Python 写入 GITHUB_ENV 后,下一行报 Invalid format
Python把多行文本写成NAME=value,Runner会把后续行当成独立指令。用完整多行格式、整行分隔符检查和UTF8传递,再到后续步骤读取。
Python 把几条错误信息拼成一个字符串,追加到 GITHUB_ENV。脚本正常结束,Runner 随后却报 Unable to process file command 'env' successfully,并把第二条错误列为 Invalid format。问题通常出在环境文件的格式:ERROR_LIST=... 只接收这一行的值,下一行已经被当作另一条指令读取。
例如,下面的文件不能表示一个三行变量:
ERROR_LIST=build failed
missing configuration
test failed
第一行符合 名称=值,后两行既没有赋值,也没有多行起始标记。换成 Python 的三引号字符串或把 write() 换成 print(),不会改变 Runner 的读取协议。
在 Python 中写完整的多行指令
对于大小受控、不含 NUL 的普通文本,可以使用 名称<<分隔符、正文、结束分隔符这三部分。结束分隔符必须独占一行,并且不能与正文中的某一整行相同。
以下适用于 Linux Runner 的 Python 步骤。示例处理的是普通错误文本,不包含密钥:
import os
import secrets
value = "build failed\nmissing configuration\ntest failed"
while True:
delimiter = "END_" + secrets.token_hex(16)
if delimiter not in value.split("\n"):
break
with open(os.environ["GITHUB_ENV"], "a", encoding="utf-8", newline="\n") as f:
f.write(f"ERROR_LIST<<{delimiter}\n")
f.write(value)
f.write(f"\n{delimiter}\n")
随机分隔符后仍做整行碰撞检查,不把“随机”当作一定不会冲突的保证。最后一个换行用于把分隔符与文件中的下一条指令分开;不要让多次追加把结束标记和下一个变量名连在一起。
这里的 GITHUB_ENV 来自 Runner,不是项目自己创建的 .env 文件。应追加到当前步骤得到的路径,不能从上一条任务的日志里复制路径,也不要截断文件中其他动作写入的内容。
到后续步骤读取,别把文本插入脚本源码
环境文件由 Runner 在步骤结束后处理。同一个 job 的后续步骤才能自动取得新变量;当前 Python 进程若马上要使用这段内容,应继续使用 value,不能等自己的 os.environ 被文件写入自动更新。
下一步骤可以直接读取:
import os
message = os.environ["ERROR_LIST"]
print("error lines:", len(message.splitlines()))
检查时用已知、非敏感样本对比完整内容,包括中间空行;日常日志只记录必要的行数或状态。不要为了验证而打印全部环境变量。若后续是 JavaScript,读取 process.env.ERROR_LIST,不要把 ${{ env.ERROR_LIST }} 直接插进引号或模板字符串:错误文本中的反引号、引号和换行可能改变生成的代码。
这一传递只覆盖同一个 job 的步骤。另一个 job 不会继承这里新增的变量,需要另选合适的输出或文件传递机制,不能仅靠重复读取 GITHUB_ENV。
大文件和任意输入保留为文件
环境变量不适合承载大段日志、二进制数据或不受约束的任意字节。把内容写入任务自己控制的文件,再向后续步骤传递一个安全的文件路径,通常更容易保留原始内容和检查大小。后续步骤必须确认路径属于预期工作区,不把不可信路径拼入命令执行。
不要用删除所有换行来掩盖报错;那会改变信息的边界。也不要退回已经弃用的 ::set-env 命令。修复后的标准是 Runner 能解析文件,下一步骤收到预期文本,而且原来需要执行的消费操作也成功。环境文件通过解析,只证明传递阶段恢复,不能替后续 API 调用或业务处理作结论。


