# 🚀 GitHub Actions 的 SCP [English](README.md) | [繁體中文](README.zh-tw.md) [GitHub Action](https://github.com/features/actions) 用于通过 SSH 复制文件和构建产物。 [![v1 version](https://github.com/appleboy/scp-action/actions/workflows/stable.yml/badge.svg)](https://github.com/appleboy/scp-action/actions/workflows/stable.yml) [![lint and test](https://github.com/appleboy/scp-action/actions/workflows/testing.yml/badge.svg)](https://github.com/appleboy/scp-action/actions/workflows/testing.yml) > **注意:** 仅支持 **Linux** [docker](https://www.docker.com/) 容器。 --- ## ✨ 功能特性 - ✅ 通过 SSH 将文件和产物复制到一台或多台远程服务器 - ✅ 支持 SSH 密钥和密码认证 - ✅ 完全支持 SSH 代理(跳板机) - ✅ 处理 Linux ↔ Windows 路径转换 - ✅ 集成 GitHub Artifacts 工作流 - ✅ 支持增量与差异文件传输 - ✅ 丰富的高级配置选项 --- ## 📦 目录 - [🚀 GitHub Actions 的 SCP](#-github-actions-的-scp) - [✨ 功能特性](#-功能特性) - [📦 目录](#-目录) - [🚀 快速开始](#-快速开始) - [⚙️ 配置说明](#️-配置说明) - [🔌 连接设置](#-连接设置) - [📁 文件传输设置](#-文件传输设置) - [🌐 代理设置](#-代理设置) - [🛡️ 最佳实践与安全性](#️-最佳实践与安全性) - [🖥️ 跨平台注意事项](#️-跨平台注意事项) - [💡 使用示例](#-使用示例) - [🧩 场景导览](#-场景导览) - [示例 1:基本 SSH 密码](#示例-1基本-ssh-密码) - [示例 2:多台服务器](#示例-2多台服务器) - [示例 3:仅传输变更文件](#示例-3仅传输变更文件) - [示例 4:集成 Artifacts](#示例-4集成-artifacts) - [示例 5:Windows 服务器](#示例-5windows-服务器) - [🗝️ SSH 密钥设置](#️-ssh-密钥设置) - [🧰 常见错误代码](#-常见错误代码) - [🔄 工作流程图](#-工作流程图) - [FAQ 与故障排查](#faq-与故障排查) - [📝 许可证](#-许可证) --- ## 🚀 快速开始 在 GitHub Actions 工作流中通过 SSH 复制文件和产物: ```yaml name: scp files on: [push] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: 通过 SSH 复制文件 uses: appleboy/scp-action@v1 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} password: ${{ secrets.PASSWORD }} port: ${{ secrets.PORT }} source: "tests/a.txt,tests/b.txt" target: your_server_target_folder_path ``` --- ## ⚙️ 配置说明 ### 🔌 连接设置 | 变量 | 说明 | 默认值 | 必填 | | --------------- | -------------------------------- | ------ | ---- | | host | 远程主机(多台用逗号分隔) | - | ✓ | | port | SSH 端口 | 22 | | | username | SSH 用户名 | - | ✓ | | password | SSH 密码(建议优先使用密钥认证) | - | | | key | SSH 私钥内容 | - | | | key_path | SSH 私钥文件路径 | - | | | passphrase | SSH 私钥密码 | - | | | fingerprint | 主机密钥 SHA256 指纹验证 | - | | | protocol | IP 协议:'tcp'、'tcp4' 或 'tcp6' | tcp | | | timeout | SSH 连接超时 | 30s | | | command_timeout | SCP 命令超时 | 10m | | ### 📁 文件传输设置 | 变量 | 说明 | 默认值 | 安全性说明 | | ---------------- | --------------------------------- | ------ | -------------- | | source | 本地要传输的文件/目录(逗号分隔) | - | 请使用明确路径 | | target | 远程目标目录(必须为目录) | - | 避免使用根目录 | | rm | 上传前移除目标目录 | - | 谨慎使用 | | strip_components | 传输时移除前置路径元素 | - | | | overwrite | 使用 tar 覆盖现有文件 | - | | | tar_dereference | tar 传输时跟随符号链接 | - | | | tar_tmp_path | 目标端 tar 临时文件路径 | - | | | tar_exec | 目标端 tar 执行文件路径 | tar | | | debug | 启用调试输出 | - | | | curl_insecure | curl 使用 --insecure | false | 不推荐 | | capture_stdout | 将命令 stdout 作为 action 输出 | false | | | version | 指定 drone-scp 版本 | - | | ### 🌐 代理设置 | 变量 | 说明 | 默认值 | 必填 | | ------------------------- | -------------------------- | ------ | ---- | | proxy_host | SSH 代理主机 | - | | | proxy_port | SSH 代理端口 | 22 | | | proxy_username | SSH 代理用户名 | - | | | proxy_password | SSH 代理密码 | - | | | proxy_key | SSH 代理私钥内容 | - | | | proxy_key_path | SSH 代理私钥文件路径 | - | | | proxy_passphrase | SSH 代理私钥密码 | - | | | proxy_fingerprint | 代理主机 SHA256 指纹验证 | - | | | proxy_use_insecure_cipher | 启用较不安全的代理加密算法 | - | | | proxy_timeout | SSH 代理连接超时 | 30s | | --- ## 🛡️ 最佳实践与安全性 - **建议优先使用 SSH 密钥认证**,提升安全性。 - 将所有敏感信息(host、username、password、key)存放于 **GitHub Secrets**。 - 定期**更换部署密钥**(建议每 90 天一次)。 - 限制目标服务器目录的写入权限。 - 启用主机密钥指纹验证以防止中间人攻击。 - 避免使用 root 用户登录 SSH。 --- ## 🖥️ 跨平台注意事项 | 场景 | Linux 服务器 | Windows 服务器 | | -------- | -------------- | ----------------------- | | 路径格式 | `/path/to/dir` | `/c/path/to/dir` | | 必要设置 | 无 | `tar_dereference: true` | | 权限 | 保留 | 可能需手动设置 ACL | | Shell | bash(默认) | Git Bash(OpenSSH) | > 🚩 **重要提醒:** > 复制到 Windows 服务器时: > > - 安装 Git for Windows 并将 OpenSSH 默认 shell 设为 Git Bash > - 使用类 Unix 目标路径(如 `/c/Users/...`) > - 启用 `tar_dereference` 处理符号链接 --- ## 💡 使用示例 ### 🧩 场景导览 - **基本文件传输** → [示例 1](#示例-1基本-ssh-密码) - **多台服务器部署** → [示例 2](#示例-2多台服务器) - **仅传输变更文件** → [示例 3](#示例-3仅传输变更文件) - **集成 Artifacts** → [示例 4](#示例-4集成-artifacts) - **Windows 服务器设置** → [示例 5](#示例-5windows-服务器) --- #### 示例 1:基本 SSH 密码 ```yaml - name: 通过 SSH 密码复制文件 uses: appleboy/scp-action@v1 with: host: example.com username: foo password: bar port: 22 source: "tests/a.txt,tests/b.txt" target: your_server_target_folder_path ``` #### 示例 2:多台服务器 ```yaml - name: 复制到多台服务器 uses: appleboy/scp-action@v1 with: host: "foo.com,bar.com" username: foo password: bar port: 22 source: "tests/a.txt,tests/b.txt" target: your_server_target_folder_path ``` #### 示例 3:仅传输变更文件 ```yaml - name: 获取变更文件 id: changed-files uses: tj-actions/changed-files@v35 with: since_last_remote_commit: true separator: "," - name: 复制变更文件到服务器 uses: appleboy/scp-action@v1 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} port: ${{ secrets.PORT }} source: ${{ steps.changed-files.outputs.all_changed_files }} target: your_server_target_folder_path ``` #### 示例 4:集成 Artifacts ```yaml - uses: actions/upload-artifact@v4 with: name: my-artifact path: world.txt - uses: actions/download-artifact@v4 with: name: my-artifact path: distfiles - name: 复制 artifact 到服务器 uses: appleboy/scp-action@v1 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.KEY }} port: ${{ secrets.PORT }} source: distfiles/* target: your_server_target_folder_path ``` #### 示例 5:Windows 服务器 ```yaml - name: 复制到 Windows uses: appleboy/scp-action@v1 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_PRIVATE_KEY }} port: 22 source: "your_source_path" target: "/c/path/to/target/" tar_dereference: true rm: true ``` --- ## 🗝️ SSH 密钥设置 1. **生成 SSH 密钥**(在本地执行): ```bash # RSA ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # ED25519 ssh-keygen -t ed25519 -a 200 -C "your_email@example.com" ``` 2. **将公钥添加到服务器**: ```bash cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys' # 或 ed25519 cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys' ``` 3. **将私钥内容复制到 GitHub Secrets**: ```bash clip < ~/.ssh/id_rsa # 或 clip < ~/.ssh/id_ed25519 ``` > 更多细节请参考 [SSH 免密登录](http://www.linuxproblem.org/art_9.html)。 **OpenSSH 注意事项:** 如遇到 `ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]`,请确认密钥算法已被支持。 Ubuntu 20.04+ 可在 `/etc/ssh/sshd_config` 或 `/etc/ssh/sshd_config.d/` 添加: ```sh CASignatureAlgorithms +ssh-rsa ``` 或使用 ed25519 密钥(默认支持)。 --- ## 🧰 常见错误代码 | 错误代码 | 可能原因 | 解决方法 | | -------------- | ------------------- | --------------------------------- | | `ECONNREFUSED` | 端口错误/防火墙阻挡 | 检查端口和防火墙设置 | | `ENOENT` | 找不到源文件 | 使用绝对路径或检查 checkout 步骤 | | `EAUTH` | 认证失败 | 检查密钥格式和权限(需 PEM 格式) | --- ## 🔄 工作流程图 ```mermaid sequenceDiagram participant G as GitHub Runner participant S as Target Server G->>S: 建立 SSH 连接 S-->>G: 验证凭证 G->>S: (可选)移除目标目录 G->>G: 打包源文件 G->>S: 传输打包文件 S->>S: 解压和处理文件 S-->>G: 返回结果 ``` --- ## FAQ 与故障排查 - **Q: 为什么认证失败?** A: 请检查 SSH 密钥格式、权限,以及密钥是否已添加到服务器。 - **Q: 如何只复制变更文件?** A: 使用 `tj-actions/changed-files` 获取变更文件并传递给 `source`。 - **Q: 如何部署到多台服务器?** A: `host` 参数用逗号分隔多台主机,例如:`host: "foo.com,bar.com"` - **Q: 如何复制到 Windows?** A: 设置 Git Bash,使用类 Unix 路径,并启用 `tar_dereference`。 --- ## 📝 许可证 MIT License