本地正常但GitHub Workflow中Expect调用SFTP失败排查
以下是针对你遇到的本地Docker运行正常、GitHub Workflow同环境失败的问题,几个实际可落地的排查方向:
1. 环境变量传递失效
GitHub Workflow的环境变量在非交互式shell或子进程(比如Expect启动的SFTP会话)中可能无法正常传递,导致Expect脚本拿到的账号/密码为空或错误。
修改方案:
放弃依赖父shell环境变量,改用Expect命令行参数传递变量,彻底避免环境隔离问题:
sftp_chmod() { local file_perm=$1 local remote_file=$2 local ftp_user=$3 local ftp_host=$4 local ftp_pass=$5 expect -c " set timeout 30 set perm [lindex \$argv 0] set file [lindex \$argv 1] set user [lindex \$argv 2] set host [lindex \$argv 3] set pass [lindex \$argv 4] spawn sftp -o StrictHostKeyChecking=no \$user@\$host expect { \"Are you sure you want to continue connecting (yes/no)?\" { send \"yes\r\" exp_continue } \"password:\" { send \"\$pass\r\" } } expect \"sftp>\" send \"chmod \$perm \$file\r\" expect \"sftp>\" send \"quit\r\" expect eof " "$file_perm" "$remote_file" "$ftp_user" "$ftp_host" "$ftp_pass" }
调用时直接传Secrets变量:
sftp_chmod "$FILE_PERM" "$REMOTE_FILE" "$FTP_USER" "$FTP_HOST" "$FTP_PASS"
2. 远程路径匹配问题
本地测试的SFTP默认目录可能与GitHub Workflow中登录后的默认目录不一致,导致你指定的remote_file路径不存在,服务器返回的"权限拒绝"实际是路径错误的混淆提示。
排查方案:
在Expect脚本中增加目录检查命令,打印日志确认路径:
# 在send "chmod..."前添加 expect "sftp>" send "pwd\r" expect "sftp>" send "ls -l $remote_file\r"
查看Workflow日志中的当前目录和文件列表,确认目标文件是否存在。如果路径错误,改用绝对路径或调整相对路径。
3. 特殊字符转义错误
如果Secrets中的密码包含$、!、\等特殊字符,bash会自动解析转义,导致传递给Expect的密码不正确。
修复方案:
用bash字符串拼接的方式,直接将变量插入Expect脚本,避免转义:
expect -c ' set timeout 30 spawn sftp -o StrictHostKeyChecking=no '"$ftp_user"'@'"$ftp_host"' expect { "Are you sure you want to continue connecting (yes/no)?" { send "yes\r" exp_continue } "password:" { send "'"$ftp_pass"'\r" } } expect "sftp>" send "chmod '"$file_perm"' '"$remote_file"'\r" expect "sftp>" send "quit\r" expect eof '
这种写法通过单引号保护Expect脚本的固定部分,用双引号插入变量,确保特殊字符完整传递。
4. 主机密钥确认未处理
本地测试时已缓存主机密钥,但GitHub Workflow是全新环境,即使加了StrictHostKeyChecking=no,部分服务器仍会弹出确认提示,导致Expect脚本超时,被误认为权限拒绝。
修复方案:
在Expect的expect块中增加对密钥确认提示的处理,如上述代码中的exp_continue逻辑,覆盖所有可能的登录提示。
内容的提问来源于stack exchange,提问作者jv-k

