|
| 1 | +# 🚀 GitHub Actions 的 SCP |
| 2 | + |
| 3 | +[English](README.md) | [繁體中文](README.zh-tw.md) |
| 4 | + |
| 5 | +[GitHub Action](https://github.com/features/actions) 用于通过 SSH 复制文件和构建产物。 |
| 6 | + |
| 7 | +[](https://github.com/appleboy/scp-action/actions) |
| 8 | + |
| 9 | +> **注意:** 仅支持 **Linux** [docker](https://www.docker.com/) 容器。 |
| 10 | +
|
| 11 | +--- |
| 12 | + |
| 13 | +## ✨ 功能特性 |
| 14 | + |
| 15 | +- ✅ 通过 SSH 将文件和产物复制到一台或多台远程服务器 |
| 16 | +- ✅ 支持 SSH 密钥和密码认证 |
| 17 | +- ✅ 完全支持 SSH 代理(跳板机) |
| 18 | +- ✅ 处理 Linux ↔ Windows 路径转换 |
| 19 | +- ✅ 集成 GitHub Artifacts 工作流 |
| 20 | +- ✅ 支持增量与差异文件传输 |
| 21 | +- ✅ 丰富的高级配置选项 |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 📦 目录 |
| 26 | + |
| 27 | +- [🚀 GitHub Actions 的 SCP](#-github-actions-的-scp) |
| 28 | + - [✨ 功能特性](#-功能特性) |
| 29 | + - [📦 目录](#-目录) |
| 30 | + - [🚀 快速开始](#-快速开始) |
| 31 | + - [⚙️ 配置说明](#️-配置说明) |
| 32 | + - [🔌 连接设置](#-连接设置) |
| 33 | + - [📁 文件传输设置](#-文件传输设置) |
| 34 | + - [🌐 代理设置](#-代理设置) |
| 35 | + - [🛡️ 最佳实践与安全性](#️-最佳实践与安全性) |
| 36 | + - [🖥️ 跨平台注意事项](#️-跨平台注意事项) |
| 37 | + - [💡 使用示例](#-使用示例) |
| 38 | + - [🧩 场景导览](#-场景导览) |
| 39 | + - [示例 1:基本 SSH 密码](#示例-1基本-ssh-密码) |
| 40 | + - [示例 2:多台服务器](#示例-2多台服务器) |
| 41 | + - [示例 3:仅传输变更文件](#示例-3仅传输变更文件) |
| 42 | + - [示例 4:集成 Artifacts](#示例-4集成-artifacts) |
| 43 | + - [示例 5:Windows 服务器](#示例-5windows-服务器) |
| 44 | + - [🗝️ SSH 密钥设置](#️-ssh-密钥设置) |
| 45 | + - [🧰 常见错误代码](#-常见错误代码) |
| 46 | + - [🔄 工作流程图](#-工作流程图) |
| 47 | + - [FAQ 与故障排查](#faq-与故障排查) |
| 48 | + - [📝 许可证](#-许可证) |
| 49 | + |
| 50 | +--- |
| 51 | + |
| 52 | +## 🚀 快速开始 |
| 53 | + |
| 54 | +在 GitHub Actions 工作流中通过 SSH 复制文件和产物: |
| 55 | + |
| 56 | +```yaml |
| 57 | +name: scp files |
| 58 | +on: [push] |
| 59 | +jobs: |
| 60 | + build: |
| 61 | + runs-on: ubuntu-latest |
| 62 | + steps: |
| 63 | + - uses: actions/checkout@v4 |
| 64 | + - name: 通过 SSH 复制文件 |
| 65 | + uses: appleboy/scp-action@v0.1.7 |
| 66 | + with: |
| 67 | + host: ${{ secrets.HOST }} |
| 68 | + username: ${{ secrets.USERNAME }} |
| 69 | + password: ${{ secrets.PASSWORD }} |
| 70 | + port: ${{ secrets.PORT }} |
| 71 | + source: "tests/a.txt,tests/b.txt" |
| 72 | + target: your_server_target_folder_path |
| 73 | +``` |
| 74 | +
|
| 75 | +--- |
| 76 | +
|
| 77 | +## ⚙️ 配置说明 |
| 78 | +
|
| 79 | +### 🔌 连接设置 |
| 80 | +
|
| 81 | +| 变量 | 说明 | 默认值 | 必填 | |
| 82 | +| --------------- | -------------------------------- | ------ | ---- | |
| 83 | +| host | 远程主机(多台用逗号分隔) | - | ✓ | |
| 84 | +| port | SSH 端口 | 22 | | |
| 85 | +| username | SSH 用户名 | - | ✓ | |
| 86 | +| password | SSH 密码(建议优先使用密钥认证) | - | | |
| 87 | +| key | SSH 私钥内容 | - | | |
| 88 | +| key_path | SSH 私钥文件路径 | - | | |
| 89 | +| passphrase | SSH 私钥密码 | - | | |
| 90 | +| fingerprint | 主机密钥 SHA256 指纹验证 | - | | |
| 91 | +| protocol | IP 协议:'tcp'、'tcp4' 或 'tcp6' | tcp | | |
| 92 | +| timeout | SSH 连接超时 | 30s | | |
| 93 | +| command_timeout | SCP 命令超时 | 10m | | |
| 94 | +
|
| 95 | +### 📁 文件传输设置 |
| 96 | +
|
| 97 | +| 变量 | 说明 | 默认值 | 安全性说明 | |
| 98 | +| ---------------- | --------------------------------- | ------ | -------------- | |
| 99 | +| source | 本地要传输的文件/目录(逗号分隔) | - | 请使用明确路径 | |
| 100 | +| target | 远程目标目录(必须为目录) | - | 避免使用根目录 | |
| 101 | +| rm | 上传前移除目标目录 | - | 谨慎使用 | |
| 102 | +| strip_components | 传输时移除前置路径元素 | - | | |
| 103 | +| overwrite | 使用 tar 覆盖现有文件 | - | | |
| 104 | +| tar_dereference | tar 传输时跟随符号链接 | - | | |
| 105 | +| tar_tmp_path | 目标端 tar 临时文件路径 | - | | |
| 106 | +| tar_exec | 目标端 tar 执行文件路径 | tar | | |
| 107 | +| debug | 启用调试输出 | - | | |
| 108 | +| curl_insecure | curl 使用 --insecure | false | 不推荐 | |
| 109 | +| capture_stdout | 将命令 stdout 作为 action 输出 | false | | |
| 110 | +| version | 指定 drone-scp 版本 | - | | |
| 111 | +
|
| 112 | +### 🌐 代理设置 |
| 113 | +
|
| 114 | +| 变量 | 说明 | 默认值 | 必填 | |
| 115 | +| ------------------------- | -------------------------- | ------ | ---- | |
| 116 | +| proxy_host | SSH 代理主机 | - | | |
| 117 | +| proxy_port | SSH 代理端口 | 22 | | |
| 118 | +| proxy_username | SSH 代理用户名 | - | | |
| 119 | +| proxy_password | SSH 代理密码 | - | | |
| 120 | +| proxy_key | SSH 代理私钥内容 | - | | |
| 121 | +| proxy_key_path | SSH 代理私钥文件路径 | - | | |
| 122 | +| proxy_passphrase | SSH 代理私钥密码 | - | | |
| 123 | +| proxy_fingerprint | 代理主机 SHA256 指纹验证 | - | | |
| 124 | +| proxy_use_insecure_cipher | 启用较不安全的代理加密算法 | - | | |
| 125 | +| proxy_timeout | SSH 代理连接超时 | 30s | | |
| 126 | +
|
| 127 | +--- |
| 128 | +
|
| 129 | +## 🛡️ 最佳实践与安全性 |
| 130 | +
|
| 131 | +- **建议优先使用 SSH 密钥认证**,提升安全性。 |
| 132 | +- 将所有敏感信息(host、username、password、key)存放于 **GitHub Secrets**。 |
| 133 | +- 定期**更换部署密钥**(建议每 90 天一次)。 |
| 134 | +- 限制目标服务器目录的写入权限。 |
| 135 | +- 启用主机密钥指纹验证以防止中间人攻击。 |
| 136 | +- 避免使用 root 用户登录 SSH。 |
| 137 | +
|
| 138 | +--- |
| 139 | +
|
| 140 | +## 🖥️ 跨平台注意事项 |
| 141 | +
|
| 142 | +| 场景 | Linux 服务器 | Windows 服务器 | |
| 143 | +| -------- | -------------- | ----------------------- | |
| 144 | +| 路径格式 | `/path/to/dir` | `/c/path/to/dir` | |
| 145 | +| 必要设置 | 无 | `tar_dereference: true` | |
| 146 | +| 权限 | 保留 | 可能需手动设置 ACL | |
| 147 | +| Shell | bash(默认) | Git Bash(OpenSSH) | |
| 148 | + |
| 149 | +> 🚩 **重要提醒:** |
| 150 | +> 复制到 Windows 服务器时: |
| 151 | +> |
| 152 | +> - 安装 Git for Windows 并将 OpenSSH 默认 shell 设为 Git Bash |
| 153 | +> - 使用类 Unix 目标路径(如 `/c/Users/...`) |
| 154 | +> - 启用 `tar_dereference` 处理符号链接 |
| 155 | + |
| 156 | +--- |
| 157 | + |
| 158 | +## 💡 使用示例 |
| 159 | + |
| 160 | +### 🧩 场景导览 |
| 161 | + |
| 162 | +- **基本文件传输** → [示例 1](#示例-1基本-ssh-密码) |
| 163 | +- **多台服务器部署** → [示例 2](#示例-2多台服务器) |
| 164 | +- **仅传输变更文件** → [示例 3](#示例-3仅传输变更文件) |
| 165 | +- **集成 Artifacts** → [示例 4](#示例-4集成-artifacts) |
| 166 | +- **Windows 服务器设置** → [示例 5](#示例-5windows-服务器) |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +#### 示例 1:基本 SSH 密码 |
| 171 | + |
| 172 | +```yaml |
| 173 | +- name: 通过 SSH 密码复制文件 |
| 174 | + uses: appleboy/scp-action@v0.1.7 |
| 175 | + with: |
| 176 | + host: example.com |
| 177 | + username: foo |
| 178 | + password: bar |
| 179 | + port: 22 |
| 180 | + source: "tests/a.txt,tests/b.txt" |
| 181 | + target: your_server_target_folder_path |
| 182 | +``` |
| 183 | + |
| 184 | +#### 示例 2:多台服务器 |
| 185 | + |
| 186 | +```yaml |
| 187 | +- name: 复制到多台服务器 |
| 188 | + uses: appleboy/scp-action@v0.1.7 |
| 189 | + with: |
| 190 | + host: "foo.com,bar.com" |
| 191 | + username: foo |
| 192 | + password: bar |
| 193 | + port: 22 |
| 194 | + source: "tests/a.txt,tests/b.txt" |
| 195 | + target: your_server_target_folder_path |
| 196 | +``` |
| 197 | + |
| 198 | +#### 示例 3:仅传输变更文件 |
| 199 | + |
| 200 | +```yaml |
| 201 | +- name: 获取变更文件 |
| 202 | + id: changed-files |
| 203 | + uses: tj-actions/changed-files@v35 |
| 204 | + with: |
| 205 | + since_last_remote_commit: true |
| 206 | + separator: "," |
| 207 | +
|
| 208 | +- name: 复制变更文件到服务器 |
| 209 | + uses: appleboy/scp-action@v0.1.7 |
| 210 | + with: |
| 211 | + host: ${{ secrets.HOST }} |
| 212 | + username: ${{ secrets.USERNAME }} |
| 213 | + key: ${{ secrets.KEY }} |
| 214 | + port: ${{ secrets.PORT }} |
| 215 | + source: ${{ steps.changed-files.outputs.all_changed_files }} |
| 216 | + target: your_server_target_folder_path |
| 217 | +``` |
| 218 | + |
| 219 | +#### 示例 4:集成 Artifacts |
| 220 | + |
| 221 | +```yaml |
| 222 | +- uses: actions/upload-artifact@v4 |
| 223 | + with: |
| 224 | + name: my-artifact |
| 225 | + path: world.txt |
| 226 | +
|
| 227 | +- uses: actions/download-artifact@v4 |
| 228 | + with: |
| 229 | + name: my-artifact |
| 230 | + path: distfiles |
| 231 | +
|
| 232 | +- name: 复制 artifact 到服务器 |
| 233 | + uses: appleboy/scp-action@v0.1.7 |
| 234 | + with: |
| 235 | + host: ${{ secrets.HOST }} |
| 236 | + username: ${{ secrets.USERNAME }} |
| 237 | + key: ${{ secrets.KEY }} |
| 238 | + port: ${{ secrets.PORT }} |
| 239 | + source: distfiles/* |
| 240 | + target: your_server_target_folder_path |
| 241 | +``` |
| 242 | + |
| 243 | +#### 示例 5:Windows 服务器 |
| 244 | + |
| 245 | +```yaml |
| 246 | +- name: 复制到 Windows |
| 247 | + uses: appleboy/scp-action@v0.1.7 |
| 248 | + with: |
| 249 | + host: ${{ secrets.HOST }} |
| 250 | + username: ${{ secrets.USERNAME }} |
| 251 | + key: ${{ secrets.SSH_PRIVATE_KEY }} |
| 252 | + port: 22 |
| 253 | + source: "your_source_path" |
| 254 | + target: "/c/path/to/target/" |
| 255 | + tar_dereference: true |
| 256 | + rm: true |
| 257 | +``` |
| 258 | + |
| 259 | +--- |
| 260 | + |
| 261 | +## 🗝️ SSH 密钥设置 |
| 262 | + |
| 263 | +1. **生成 SSH 密钥**(在本地执行): |
| 264 | + |
| 265 | + ```bash |
| 266 | + # RSA |
| 267 | + ssh-keygen -t rsa -b 4096 -C "your_email@example.com" |
| 268 | + # ED25519 |
| 269 | + ssh-keygen -t ed25519 -a 200 -C "your_email@example.com" |
| 270 | + ``` |
| 271 | + |
| 272 | +2. **将公钥添加到服务器**: |
| 273 | + |
| 274 | + ```bash |
| 275 | + cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys' |
| 276 | + # 或 ed25519 |
| 277 | + cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys' |
| 278 | + ``` |
| 279 | + |
| 280 | +3. **将私钥内容复制到 GitHub Secrets**: |
| 281 | + |
| 282 | + ```bash |
| 283 | + clip < ~/.ssh/id_rsa |
| 284 | + # 或 |
| 285 | + clip < ~/.ssh/id_ed25519 |
| 286 | + ``` |
| 287 | + |
| 288 | +> 更多细节请参考 [SSH 免密登录](http://www.linuxproblem.org/art_9.html)。 |
| 289 | + |
| 290 | +**OpenSSH 注意事项:** |
| 291 | +如遇到 `ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]`,请确认密钥算法已被支持。 |
| 292 | +Ubuntu 20.04+ 可在 `/etc/ssh/sshd_config` 或 `/etc/ssh/sshd_config.d/` 添加: |
| 293 | + |
| 294 | +```sh |
| 295 | +CASignatureAlgorithms +ssh-rsa |
| 296 | +``` |
| 297 | + |
| 298 | +或使用 ed25519 密钥(默认支持)。 |
| 299 | + |
| 300 | +--- |
| 301 | + |
| 302 | +## 🧰 常见错误代码 |
| 303 | + |
| 304 | +| 错误代码 | 可能原因 | 解决方法 | |
| 305 | +| -------------- | ------------------- | --------------------------------- | |
| 306 | +| `ECONNREFUSED` | 端口错误/防火墙阻挡 | 检查端口和防火墙设置 | |
| 307 | +| `ENOENT` | 找不到源文件 | 使用绝对路径或检查 checkout 步骤 | |
| 308 | +| `EAUTH` | 认证失败 | 检查密钥格式和权限(需 PEM 格式) | |
| 309 | + |
| 310 | +--- |
| 311 | + |
| 312 | +## 🔄 工作流程图 |
| 313 | + |
| 314 | +```mermaid |
| 315 | +sequenceDiagram |
| 316 | + participant G as GitHub Runner |
| 317 | + participant S as Target Server |
| 318 | + G->>S: 建立 SSH 连接 |
| 319 | + S-->>G: 验证凭证 |
| 320 | + G->>S: (可选)移除目标目录 |
| 321 | + G->>G: 打包源文件 |
| 322 | + G->>S: 传输打包文件 |
| 323 | + S->>S: 解压和处理文件 |
| 324 | + S-->>G: 返回结果 |
| 325 | +``` |
| 326 | + |
| 327 | +--- |
| 328 | + |
| 329 | +## FAQ 与故障排查 |
| 330 | + |
| 331 | +- **Q: 为什么认证失败?** |
| 332 | + A: 请检查 SSH 密钥格式、权限,以及密钥是否已添加到服务器。 |
| 333 | + |
| 334 | +- **Q: 如何只复制变更文件?** |
| 335 | + A: 使用 `tj-actions/changed-files` 获取变更文件并传递给 `source`。 |
| 336 | + |
| 337 | +- **Q: 如何部署到多台服务器?** |
| 338 | + A: `host` 参数用逗号分隔多台主机,例如:`host: "foo.com,bar.com"` |
| 339 | + |
| 340 | +- **Q: 如何复制到 Windows?** |
| 341 | + A: 设置 Git Bash,使用类 Unix 路径,并启用 `tar_dereference`。 |
| 342 | + |
| 343 | +--- |
| 344 | + |
| 345 | +## 📝 许可证 |
| 346 | + |
| 347 | +MIT License |
0 commit comments