mirror of
https://ghproxy.net/https://github.com/appleboy/scp-action.git
synced 2025-05-01 20:43:54 +00:00
Compare commits
No commits in common. "master" and "v0.0.10" have entirely different histories.
13
.github/FUNDING.yml
vendored
13
.github/FUNDING.yml
vendored
@ -1,13 +0,0 @@
|
|||||||
# These are supported funding model platforms
|
|
||||||
|
|
||||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
|
||||||
patreon: # Replace with a single Patreon username
|
|
||||||
open_collective: # Replace with a single Open Collective username
|
|
||||||
ko_fi: # Replace with a single Ko-fi username
|
|
||||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
|
||||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
|
||||||
liberapay: # Replace with a single Liberapay username
|
|
||||||
issuehunt: # Replace with a single IssueHunt username
|
|
||||||
otechie: # Replace with a single Otechie username
|
|
||||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
|
||||||
custom: ['https://www.paypal.me/appleboy46']
|
|
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
@ -1,10 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: github-actions
|
|
||||||
directory: /
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
||||||
- package-ecosystem: gomod
|
|
||||||
directory: /
|
|
||||||
schedule:
|
|
||||||
interval: weekly
|
|
52
.github/workflows/ci.yml
vendored
Normal file
52
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
name: scp files
|
||||||
|
on: [push]
|
||||||
|
jobs:
|
||||||
|
|
||||||
|
build:
|
||||||
|
name: Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: copy file via ssh password
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
password: ${{ secrets.PASSWORD }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "test"
|
||||||
|
|
||||||
|
- name: copy file via ssh key
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "test"
|
||||||
|
|
||||||
|
- name: remove the specified number of leading path elements
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "foobar"
|
||||||
|
strip_components: 1
|
||||||
|
|
||||||
|
- name: ssh key with passphrase
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
key: ${{ secrets.SSH2 }}
|
||||||
|
passphrase: ${{ secrets.PASSPHRASE }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "test"
|
33
.github/workflows/goreleaser.yml
vendored
33
.github/workflows/goreleaser.yml
vendored
@ -1,33 +0,0 @@
|
|||||||
name: Goreleaser
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "*"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
goreleaser:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version: "^1"
|
|
||||||
|
|
||||||
- name: Run GoReleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v6
|
|
||||||
with:
|
|
||||||
# either 'goreleaser' (default) or 'goreleaser-pro'
|
|
||||||
distribution: goreleaser
|
|
||||||
version: latest
|
|
||||||
args: release --clean
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
150
.github/workflows/stable.yml
vendored
150
.github/workflows/stable.yml
vendored
@ -1,150 +0,0 @@
|
|||||||
name: v1 version
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
testing:
|
|
||||||
name: test scp action
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file via ssh password
|
|
||||||
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: "test"
|
|
||||||
|
|
||||||
- name: copy file via ssh key
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
|
|
||||||
- name: remove the specified number of leading path elements
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "foobar"
|
|
||||||
strip_components: 1
|
|
||||||
|
|
||||||
- name: ssh key with passphrase
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.SSH2 }}
|
|
||||||
passphrase: ${{ secrets.PASSPHRASE }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
|
|
||||||
- name: use insecure cipher
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.SSH2 }}
|
|
||||||
passphrase: ${{ secrets.PASSPHRASE }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
use_insecure_cipher: true
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: test deploy artifact
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- run: echo hello > world.txt
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: my-artifact
|
|
||||||
path: world.txt
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: my-artifact
|
|
||||||
path: distfiles
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: distfiles/*
|
|
||||||
target: test
|
|
||||||
|
|
||||||
changes:
|
|
||||||
name: test changed-files
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get changed files
|
|
||||||
id: changed-files
|
|
||||||
uses: tj-actions/changed-files@v45
|
|
||||||
with:
|
|
||||||
since_last_remote_commit: true
|
|
||||||
separator: ","
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
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: test
|
|
||||||
|
|
||||||
target:
|
|
||||||
name: test target folder
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: tests/a.txt,tests/b.txt
|
|
||||||
target: foobar foobar 1234
|
|
||||||
|
|
||||||
multipleHost:
|
|
||||||
name: test Multiple Host
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}:${{ secrets.PORT }},${{ secrets.HOST }}:${{ secrets.PORT }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: 1024
|
|
||||||
source: tests/a.txt,tests/b.txt
|
|
||||||
target: foobar
|
|
150
.github/workflows/testing.yml
vendored
150
.github/workflows/testing.yml
vendored
@ -1,150 +0,0 @@
|
|||||||
name: lint and test
|
|
||||||
on: [push]
|
|
||||||
jobs:
|
|
||||||
testing:
|
|
||||||
name: test scp action
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file via ssh password
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
password: ${{ secrets.PASSWORD }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
|
|
||||||
- name: copy file via ssh key
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
|
|
||||||
- name: remove the specified number of leading path elements
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "foobar"
|
|
||||||
strip_components: 1
|
|
||||||
|
|
||||||
- name: ssh key with passphrase
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.SSH2 }}
|
|
||||||
passphrase: ${{ secrets.PASSPHRASE }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
|
|
||||||
- name: use insecure cipher
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.SSH2 }}
|
|
||||||
passphrase: ${{ secrets.PASSPHRASE }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: "tests/a.txt,tests/b.txt"
|
|
||||||
target: "test"
|
|
||||||
use_insecure_cipher: true
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
name: test deploy artifact
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- run: echo hello > world.txt
|
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: my-artifact
|
|
||||||
path: world.txt
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
name: my-artifact
|
|
||||||
path: distfiles
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: distfiles/*
|
|
||||||
target: test
|
|
||||||
|
|
||||||
changes:
|
|
||||||
name: test changed-files
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Get changed files
|
|
||||||
id: changed-files
|
|
||||||
uses: tj-actions/changed-files@v45
|
|
||||||
with:
|
|
||||||
since_last_remote_commit: true
|
|
||||||
separator: ","
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: ${{ steps.changed-files.outputs.all_changed_files }}
|
|
||||||
target: test
|
|
||||||
|
|
||||||
target:
|
|
||||||
name: test target folder
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: ${{ secrets.PORT }}
|
|
||||||
source: tests/a.txt,tests/b.txt
|
|
||||||
target: foobar foobar 1234
|
|
||||||
|
|
||||||
multipleHost:
|
|
||||||
name: test Multiple Host
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: copy file to server
|
|
||||||
uses: ./
|
|
||||||
with:
|
|
||||||
host: ${{ secrets.HOST }}:${{ secrets.PORT }},${{ secrets.HOST }}:${{ secrets.PORT }}
|
|
||||||
username: ${{ secrets.USERNAME }}
|
|
||||||
key: ${{ secrets.KEY }}
|
|
||||||
port: 1024
|
|
||||||
source: tests/a.txt,tests/b.txt
|
|
||||||
target: foobar
|
|
@ -1,28 +0,0 @@
|
|||||||
builds:
|
|
||||||
- # If true, skip the build.
|
|
||||||
# Useful for library projects.
|
|
||||||
# Default is false
|
|
||||||
skip: true
|
|
||||||
|
|
||||||
changelog:
|
|
||||||
use: github
|
|
||||||
groups:
|
|
||||||
- title: Features
|
|
||||||
regexp: "^.*feat[(\\w)]*:+.*$"
|
|
||||||
order: 0
|
|
||||||
- title: "Bug fixes"
|
|
||||||
regexp: "^.*fix[(\\w)]*:+.*$"
|
|
||||||
order: 1
|
|
||||||
- title: "Enhancements"
|
|
||||||
regexp: "^.*chore[(\\w)]*:+.*$"
|
|
||||||
order: 2
|
|
||||||
- title: "Refactor"
|
|
||||||
regexp: "^.*refactor[(\\w)]*:+.*$"
|
|
||||||
order: 3
|
|
||||||
- title: "Build process updates"
|
|
||||||
regexp: ^.*?(build|ci)(\(.+\))??!?:.+$
|
|
||||||
order: 4
|
|
||||||
- title: "Documentation updates"
|
|
||||||
regexp: ^.*?docs?(\(.+\))??!?:.+$
|
|
||||||
order: 4
|
|
||||||
- title: Others
|
|
5
Dockerfile
Normal file
5
Dockerfile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
FROM appleboy/drone-scp:1.5.9-linux-amd64
|
||||||
|
|
||||||
|
ADD entrypoint.sh /entrypoint.sh
|
||||||
|
RUN chmod +x /entrypoint.sh
|
||||||
|
ENTRYPOINT ["/entrypoint.sh"]
|
386
README.md
386
README.md
@ -1,348 +1,168 @@
|
|||||||
# 🚀 SCP for GitHub Actions
|
# 🚀 SCP for GitHub Actions
|
||||||
|
|
||||||
[繁體中文](README.zh-tw.md) | [简体中文](README.zh-cn.md)
|
|
||||||
|
|
||||||
[GitHub Action](https://github.com/features/actions) for copying files and artifacts via SSH.
|
[GitHub Action](https://github.com/features/actions) for copying files and artifacts via SSH.
|
||||||
|
|
||||||
[](https://github.com/appleboy/scp-action/actions/workflows/stable.yml)
|

|
||||||
[](https://github.com/appleboy/scp-action/actions/workflows/testing.yml)
|
|
||||||
|
|
||||||
> **Note:** Only supports **Linux** [docker](https://www.docker.com/) containers.
|
[](https://github.com/appleboy/scp-action/actions)
|
||||||
|
|
||||||
---
|
## Usage
|
||||||
|
|
||||||
## ✨ Features
|
copy files and artifacts via SSH as blow.
|
||||||
|
|
||||||
- ✅ Copy files and artifacts to one or multiple remote servers via SSH
|
|
||||||
- ✅ Supports both SSH key and password authentication
|
|
||||||
- ✅ Full SSH Proxy (jump host) support
|
|
||||||
- ✅ Handles Linux ↔ Windows path conversion
|
|
||||||
- ✅ Integrates with GitHub Artifacts workflow
|
|
||||||
- ✅ Incremental and differential file transfer
|
|
||||||
- ✅ Rich configuration options for advanced use cases
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 Table of Contents
|
|
||||||
|
|
||||||
- [🚀 SCP for GitHub Actions](#-scp-for-github-actions)
|
|
||||||
- [✨ Features](#-features)
|
|
||||||
- [📦 Table of Contents](#-table-of-contents)
|
|
||||||
- [🚀 Quick Start](#-quick-start)
|
|
||||||
- [⚙️ Configuration](#️-configuration)
|
|
||||||
- [🔌 Connection Settings](#-connection-settings)
|
|
||||||
- [📁 File Transfer Settings](#-file-transfer-settings)
|
|
||||||
- [🌐 Proxy Settings](#-proxy-settings)
|
|
||||||
- [🛡️ Best Practices \& Security](#️-best-practices--security)
|
|
||||||
- [🖥️ Cross-Platform Notes](#️-cross-platform-notes)
|
|
||||||
- [💡 Usage Examples](#-usage-examples)
|
|
||||||
- [🧩 Scenario Guide](#-scenario-guide)
|
|
||||||
- [Example 1: Basic SSH Password](#example-1-basic-ssh-password)
|
|
||||||
- [Example 2: Multi-server](#example-2-multi-server)
|
|
||||||
- [Example 3: Changed Files Only](#example-3-changed-files-only)
|
|
||||||
- [Example 4: Artifacts Integration](#example-4-artifacts-integration)
|
|
||||||
- [Example 5: Windows Server](#example-5-windows-server)
|
|
||||||
- [🗝️ SSH Key Setup](#️-ssh-key-setup)
|
|
||||||
- [🧰 Common Error Codes](#-common-error-codes)
|
|
||||||
- [🔄 Workflow Diagram](#-workflow-diagram)
|
|
||||||
- [FAQ \& Troubleshooting](#faq--troubleshooting)
|
|
||||||
- [📝 License](#-license)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
Copy files and artifacts via SSH in your GitHub Actions workflow:
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
name: scp files
|
name: scp files
|
||||||
on: [push]
|
on: [push]
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
build:
|
||||||
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@master
|
||||||
- name: Copy files via SSH
|
- name: copy file via ssh password
|
||||||
uses: appleboy/scp-action@v1
|
uses: appleboy/scp-action@master
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.HOST }}
|
host: ${{ secrets.HOST }}
|
||||||
username: ${{ secrets.USERNAME }}
|
username: ${{ secrets.USERNAME }}
|
||||||
password: ${{ secrets.PASSWORD }}
|
password: ${{ secrets.PASSWORD }}
|
||||||
port: ${{ secrets.PORT }}
|
port: ${{ secrets.PORT }}
|
||||||
source: "tests/a.txt,tests/b.txt"
|
source: "tests/a.txt,tests/b.txt"
|
||||||
target: your_server_target_folder_path
|
target: "test"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
## Input variables
|
||||||
|
|
||||||
## ⚙️ Configuration
|
see the [action.yml](./action.yml) file for more detail imformation.
|
||||||
|
|
||||||
### 🔌 Connection Settings
|
* host - scp remote host
|
||||||
|
* port - scp remote port, default is `22`
|
||||||
|
* username - scp username
|
||||||
|
* password - scp password
|
||||||
|
* passphrase - the passphrase is usually to encrypt the private key
|
||||||
|
* timeout - timeout for ssh to remote host, default is `30s`
|
||||||
|
* command_timeout - timeout for scp command, default is `10m`
|
||||||
|
* key - content of ssh private key. ex raw content of ~/.ssh/id_rsa
|
||||||
|
* key_path - path of ssh private key
|
||||||
|
* target - target path on the server
|
||||||
|
* source - scp file list
|
||||||
|
* rm - remove target folder before upload data, default is `false`
|
||||||
|
* strip_components - remove the specified number of leading path elements.
|
||||||
|
* overwrite - use `--overwrite` flag with tar
|
||||||
|
* tar_tmp_path - temporary path for tar file on the dest host
|
||||||
|
|
||||||
| Variable | Description | Default | Required |
|
SSH Proxy Setting:
|
||||||
| --------------- | -------------------------------------------- | ------- | -------- |
|
|
||||||
| host | Remote host(s), comma-separated for multiple | - | ✓ |
|
|
||||||
| port | SSH port | 22 | |
|
|
||||||
| username | SSH username | - | ✓ |
|
|
||||||
| password | SSH password (prefer SSH key for security) | - | |
|
|
||||||
| key | SSH private key content | - | |
|
|
||||||
| key_path | Path to SSH private key file | - | |
|
|
||||||
| passphrase | Passphrase for SSH private key | - | |
|
|
||||||
| fingerprint | SHA256 fingerprint for host key verification | - | |
|
|
||||||
| protocol | IP protocol: 'tcp', 'tcp4', or 'tcp6' | tcp | |
|
|
||||||
| timeout | SSH connection timeout | 30s | |
|
|
||||||
| command_timeout | SCP command timeout | 10m | |
|
|
||||||
|
|
||||||
### 📁 File Transfer Settings
|
* proxy_host - proxy host
|
||||||
|
* proxy_port - proxy port, default is `22`
|
||||||
|
* proxy_username - proxy username
|
||||||
|
* proxy_password - proxy password
|
||||||
|
* proxy_passphrase - the passphrase is usually to encrypt the private key
|
||||||
|
* proxy_timeout - timeout for ssh to proxy host, default is `30s`
|
||||||
|
* proxy_key - content of ssh proxy private key.
|
||||||
|
* proxy_key_path - path of ssh proxy private key
|
||||||
|
|
||||||
| Variable | Description | Default | Security Note |
|
### Example
|
||||||
| ---------------- | ------------------------------------------------------- | ------- | ---------------------- |
|
|
||||||
| source | Local files/directories to transfer (comma-separated) | - | Use explicit paths |
|
|
||||||
| target | Target directory on remote server (must be a directory) | - | Avoid root directories |
|
|
||||||
| rm | Remove target directory before upload | - | Use with caution |
|
|
||||||
| strip_components | Remove leading path elements when extracting | - | |
|
|
||||||
| overwrite | Overwrite existing files with tar | - | |
|
|
||||||
| tar_dereference | Follow symlinks with tar | - | |
|
|
||||||
| tar_tmp_path | Temp path for tar file on destination | - | |
|
|
||||||
| tar_exec | Path to tar executable on destination | tar | |
|
|
||||||
| debug | Enable debug output | - | |
|
|
||||||
| curl_insecure | Use --insecure with curl | false | Not recommended |
|
|
||||||
| capture_stdout | Capture command stdout as action output | false | |
|
|
||||||
| version | Version of drone-scp to use | - | |
|
|
||||||
|
|
||||||
### 🌐 Proxy Settings
|
Copy file via ssh password
|
||||||
|
|
||||||
| Variable | Description | Default | Required |
|
|
||||||
| ------------------------- | ------------------------------------ | ------- | -------- |
|
|
||||||
| proxy_host | SSH proxy host | - | |
|
|
||||||
| proxy_port | SSH proxy port | 22 | |
|
|
||||||
| proxy_username | SSH proxy username | - | |
|
|
||||||
| proxy_password | SSH proxy password | - | |
|
|
||||||
| proxy_key | SSH proxy private key content | - | |
|
|
||||||
| proxy_key_path | Path to SSH proxy private key file | - | |
|
|
||||||
| proxy_passphrase | Passphrase for SSH proxy private key | - | |
|
|
||||||
| proxy_fingerprint | SHA256 fingerprint for proxy host | - | |
|
|
||||||
| proxy_use_insecure_cipher | Enable less secure ciphers for proxy | - | |
|
|
||||||
| proxy_timeout | SSH proxy connection timeout | 30s | |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛡️ Best Practices & Security
|
|
||||||
|
|
||||||
- **Prefer SSH key authentication** over passwords for better security.
|
|
||||||
- Store all sensitive values (host, username, password, key) in **GitHub Secrets**.
|
|
||||||
- Regularly **rotate deployment keys** (suggested every 90 days).
|
|
||||||
- Restrict write permissions on the target server directory.
|
|
||||||
- Enable host key fingerprint verification to prevent MITM attacks.
|
|
||||||
- Avoid using root as the SSH user.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🖥️ Cross-Platform Notes
|
|
||||||
|
|
||||||
| Scenario | Linux Server | Windows Server |
|
|
||||||
| ---------------- | -------------- | ----------------------- |
|
|
||||||
| Path Format | `/path/to/dir` | `/c/path/to/dir` |
|
|
||||||
| Required Setting | None | `tar_dereference: true` |
|
|
||||||
| Permissions | Preserved | May require manual ACL |
|
|
||||||
| Shell | bash (default) | Git Bash via OpenSSH |
|
|
||||||
|
|
||||||
> 🚩 **Important:**
|
|
||||||
> When copying to Windows servers:
|
|
||||||
>
|
|
||||||
> - Install Git for Windows and set OpenSSH default shell to Git Bash
|
|
||||||
> - Use Unix-style target paths (e.g., `/c/Users/...`)
|
|
||||||
> - Enable `tar_dereference` for symlink handling
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💡 Usage Examples
|
|
||||||
|
|
||||||
### 🧩 Scenario Guide
|
|
||||||
|
|
||||||
- **Basic file transfer** → [Example 1](#example-1-basic-ssh-password)
|
|
||||||
- **Multi-server deployment** → [Example 2](#example-2-multi-server)
|
|
||||||
- **Incremental/changed files only** → [Example 3](#example-3-changed-files-only)
|
|
||||||
- **Artifacts integration** → [Example 4](#example-4-artifacts-integration)
|
|
||||||
- **Windows server setup** → [Example 5](#example-5-windows-server)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
#### Example 1: Basic SSH Password
|
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Copy file via SSH password
|
- name: copy file via ssh password
|
||||||
uses: appleboy/scp-action@v1
|
uses: appleboy/scp-action@master
|
||||||
with:
|
with:
|
||||||
host: example.com
|
host: example.com
|
||||||
username: foo
|
username: foo
|
||||||
password: bar
|
password: bar
|
||||||
port: 22
|
port: 22
|
||||||
source: "tests/a.txt,tests/b.txt"
|
source: "tests/a.txt,tests/b.txt"
|
||||||
target: your_server_target_folder_path
|
target: "test"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example 2: Multi-server
|
Copy file via ssh key
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Copy to multiple servers
|
- name: copy file via ssh key
|
||||||
uses: appleboy/scp-action@v1
|
uses: appleboy/scp-action@master
|
||||||
with:
|
with:
|
||||||
host: "foo.com,bar.com"
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "test"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example configuration for ignore list:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: copy file via ssh key
|
||||||
|
uses: appleboy/scp-action@master
|
||||||
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
|
username: ${{ secrets.USERNAME }}
|
||||||
|
port: ${{ secrets.PORT }}
|
||||||
|
key: ${{ secrets.KEY }}
|
||||||
|
source: "tests/*.txt,!tests/a.txt"
|
||||||
|
target: "test"
|
||||||
|
```
|
||||||
|
|
||||||
|
Example configuration for multiple server
|
||||||
|
|
||||||
|
```diff
|
||||||
|
uses: appleboy/scp-action@master
|
||||||
|
with:
|
||||||
|
- host: "example.com"
|
||||||
|
+ host: "foo.com,bar.com"
|
||||||
username: foo
|
username: foo
|
||||||
password: bar
|
password: bar
|
||||||
port: 22
|
port: 22
|
||||||
source: "tests/a.txt,tests/b.txt"
|
source: "tests/a.txt,tests/b.txt"
|
||||||
target: your_server_target_folder_path
|
target: "test"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example 3: Changed Files Only
|
remove the specified number of leading path elements
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- name: Get changed files
|
- name: remove the specified number of leading path elements
|
||||||
id: changed-files
|
uses: appleboy/scp-action@master
|
||||||
uses: tj-actions/changed-files@v35
|
|
||||||
with:
|
|
||||||
since_last_remote_commit: true
|
|
||||||
separator: ","
|
|
||||||
|
|
||||||
- name: Copy changed files to server
|
|
||||||
uses: appleboy/scp-action@v1
|
|
||||||
with:
|
with:
|
||||||
host: ${{ secrets.HOST }}
|
host: ${{ secrets.HOST }}
|
||||||
username: ${{ secrets.USERNAME }}
|
username: ${{ secrets.USERNAME }}
|
||||||
key: ${{ secrets.KEY }}
|
key: ${{ secrets.KEY }}
|
||||||
port: ${{ secrets.PORT }}
|
port: ${{ secrets.PORT }}
|
||||||
source: ${{ steps.changed-files.outputs.all_changed_files }}
|
source: "tests/a.txt,tests/b.txt"
|
||||||
target: your_server_target_folder_path
|
target: "foobar"
|
||||||
|
strip_components: 1
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Example 4: Artifacts Integration
|
old target structure:
|
||||||
|
|
||||||
```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: Copy artifact to server
|
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Example 5: Windows Server
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- name: Copy to 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 Key Setup
|
|
||||||
|
|
||||||
1. **Generate SSH Key** (on your local machine):
|
|
||||||
|
|
||||||
```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. **Add Public Key to Server**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat .ssh/id_rsa.pub | ssh user@host 'cat >> .ssh/authorized_keys'
|
|
||||||
# or for ed25519
|
|
||||||
cat .ssh/id_ed25519.pub | ssh user@host 'cat >> .ssh/authorized_keys'
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Copy Private Key Content to GitHub Secrets**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
clip < ~/.ssh/id_rsa
|
|
||||||
# or
|
|
||||||
clip < ~/.ssh/id_ed25519
|
|
||||||
```
|
|
||||||
|
|
||||||
> See [SSH login without password](http://www.linuxproblem.org/art_9.html) for more details.
|
|
||||||
|
|
||||||
**OpenSSH Note:**
|
|
||||||
If you see `ssh: handshake failed: ssh: unable to authenticate, attempted methods [none publickey]`, ensure your key algorithm is supported.
|
|
||||||
On Ubuntu 20.04+, add to `/etc/ssh/sshd_config` or `/etc/ssh/sshd_config.d/`:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
CASignatureAlgorithms +ssh-rsa
|
foobar
|
||||||
|
└── tests
|
||||||
|
├── a.txt
|
||||||
|
└── b.txt
|
||||||
```
|
```
|
||||||
|
|
||||||
Or use ed25519 keys, which are accepted by default.
|
new target structure:
|
||||||
|
|
||||||
---
|
```sh
|
||||||
|
foobar
|
||||||
## 🧰 Common Error Codes
|
├── a.txt
|
||||||
|
└── b.txt
|
||||||
| Error Code | Possible Cause | Solution |
|
|
||||||
| -------------- | ---------------------------- | --------------------------------------------- |
|
|
||||||
| `ECONNREFUSED` | Wrong port / firewall blocks | Check port and firewall settings |
|
|
||||||
| `ENOENT` | Source file not found | Use absolute path or check checkout step |
|
|
||||||
| `EAUTH` | Authentication failed | Check key format and permissions (PEM format) |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔄 Workflow Diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant G as GitHub Runner
|
|
||||||
participant S as Target Server
|
|
||||||
G->>S: Establish SSH connection
|
|
||||||
S-->>G: Authenticate credentials
|
|
||||||
G->>S: (Optional) Remove target directory
|
|
||||||
G->>G: Archive source files
|
|
||||||
G->>S: Transfer archive
|
|
||||||
S->>S: Extract and process files
|
|
||||||
S-->>G: Return result
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
Protecting a Private Key. The purpose of the passphrase is usually to encrypt the private key. This makes the key file by itself useless to an attacker. It is not uncommon for files to leak from backups or decommissioned hardware, and hackers commonly exfiltrate files from compromised systems.
|
||||||
|
|
||||||
## FAQ & Troubleshooting
|
```diff
|
||||||
|
- name: ssh key with passphrase
|
||||||
- **Q: Why does authentication fail?**
|
uses: appleboy/scp-action@master
|
||||||
A: Check SSH key format, permissions, and that the key is added to the server.
|
with:
|
||||||
|
host: ${{ secrets.HOST }}
|
||||||
- **Q: How do I copy only changed files?**
|
username: ${{ secrets.USERNAME }}
|
||||||
A: Use `tj-actions/changed-files` to get changed files and pass to `source`.
|
key: ${{ secrets.SSH2 }}
|
||||||
|
+ passphrase: ${{ secrets.PASSPHRASE }}
|
||||||
- **Q: How to deploy to multiple servers?**
|
port: ${{ secrets.PORT }}
|
||||||
A: Use comma-separated host list: `host: "foo.com,bar.com"`
|
source: "tests/a.txt,tests/b.txt"
|
||||||
|
target: "test"
|
||||||
- **Q: How to copy to Windows?**
|
```
|
||||||
A: Set up Git Bash, use Unix-style paths, and enable `tar_dereference`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 License
|
|
||||||
|
|
||||||
MIT License
|
|
||||||
|
348
README.zh-cn.md
348
README.zh-cn.md
@ -1,348 +0,0 @@
|
|||||||
# 🚀 GitHub Actions 的 SCP
|
|
||||||
|
|
||||||
[English](README.md) | [繁體中文](README.zh-tw.md)
|
|
||||||
|
|
||||||
[GitHub Action](https://github.com/features/actions) 用于通过 SSH 复制文件和构建产物。
|
|
||||||
|
|
||||||
[](https://github.com/appleboy/scp-action/actions/workflows/stable.yml)
|
|
||||||
[](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
|
|
348
README.zh-tw.md
348
README.zh-tw.md
@ -1,348 +0,0 @@
|
|||||||
# 🚀 GitHub Actions 的 SCP
|
|
||||||
|
|
||||||
[English](README.md) | [简体中文](README.zh-cn.md)
|
|
||||||
|
|
||||||
[GitHub Action](https://github.com/features/actions) 用於透過 SSH 複製檔案與產物。
|
|
||||||
|
|
||||||
[](https://github.com/appleboy/scp-action/actions/workflows/stable.yml)
|
|
||||||
[](https://github.com/appleboy/scp-action/actions/workflows/testing.yml)
|
|
||||||
|
|
||||||
> **注意:** 只支援 **Linux** [docker](https://www.docker.com/) 容器。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✨ 功能特色
|
|
||||||
|
|
||||||
- ✅ 透過 SSH 將檔案與產物複製到一台或多台遠端伺服器
|
|
||||||
- ✅ 支援 SSH 金鑰與密碼驗證
|
|
||||||
- ✅ 完整支援 SSH Proxy(跳板機)
|
|
||||||
- ✅ 處理 Linux ↔ Windows 路徑轉換
|
|
||||||
- ✅ 整合 GitHub Artifacts 工作流程
|
|
||||||
- ✅ 支援增量與差異檔案傳輸
|
|
||||||
- ✅ 豐富的進階設定選項
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 目錄
|
|
||||||
|
|
||||||
- [🚀 GitHub Actions 的 SCP](#-github-actions-的-scp)
|
|
||||||
- [✨ 功能特色](#-功能特色)
|
|
||||||
- [📦 目錄](#-目錄)
|
|
||||||
- [🚀 快速開始](#-快速開始)
|
|
||||||
- [⚙️ 設定說明](#️-設定說明)
|
|
||||||
- [🔌 連線設定](#-連線設定)
|
|
||||||
- [📁 檔案傳輸設定](#-檔案傳輸設定)
|
|
||||||
- [🌐 Proxy 設定](#-proxy-設定)
|
|
||||||
- [🛡️ 最佳實踐與安全性](#️-最佳實踐與安全性)
|
|
||||||
- [🖥️ 跨平台注意事項](#️-跨平台注意事項)
|
|
||||||
- [💡 使用範例](#-使用範例)
|
|
||||||
- [🧩 情境導覽](#-情境導覽)
|
|
||||||
- [範例 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 設定
|
|
||||||
|
|
||||||
| 變數 | 說明 | 預設值 | 必填 |
|
|
||||||
| ------------------------- | ------------------------------- | ------ | ---- |
|
|
||||||
| proxy_host | SSH Proxy 主機 | - | |
|
|
||||||
| proxy_port | SSH Proxy 連接埠 | 22 | |
|
|
||||||
| proxy_username | SSH Proxy 使用者名稱 | - | |
|
|
||||||
| proxy_password | SSH Proxy 密碼 | - | |
|
|
||||||
| proxy_key | SSH Proxy 私鑰內容 | - | |
|
|
||||||
| proxy_key_path | SSH Proxy 私鑰檔案路徑 | - | |
|
|
||||||
| proxy_passphrase | SSH Proxy 私鑰密碼 | - | |
|
|
||||||
| proxy_fingerprint | Proxy 主機 SHA256 指紋驗證 | - | |
|
|
||||||
| proxy_use_insecure_cipher | 啟用較不安全的 Proxy 加密演算法 | - | |
|
|
||||||
| proxy_timeout | SSH Proxy 連線逾時 | 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
|
|
147
action.yml
147
action.yml
@ -1,142 +1,65 @@
|
|||||||
name: "SCP Command to Transfer Files"
|
name: 'SCP Command to Transfer Files'
|
||||||
description: "Easily transfer files and folders using the SCP command in Linux."
|
description: 'How to Use SCP Command to Transfer Files/Folders in Linux'
|
||||||
author: "Bo-Yi Wu"
|
author: 'Bo-Yi Wu'
|
||||||
inputs:
|
inputs:
|
||||||
host:
|
host:
|
||||||
description: "Remote host address for SCP (e.g., example.com or 192.168.1.1)."
|
description: 'scp remote host'
|
||||||
port:
|
port:
|
||||||
description: "Remote SSH port for SCP. Default: 22."
|
description: 'scp remote port'
|
||||||
default: "22"
|
default: 22
|
||||||
username:
|
username:
|
||||||
description: "Username for SSH authentication."
|
description: 'scp username'
|
||||||
password:
|
password:
|
||||||
description: "Password for SSH authentication (not recommended; use SSH keys if possible)."
|
description: 'scp password'
|
||||||
protocol:
|
|
||||||
description: "IP protocol to use. Valid values: 'tcp', 'tcp4', or 'tcp6'. Default: tcp."
|
|
||||||
default: "tcp"
|
|
||||||
timeout:
|
timeout:
|
||||||
description: "Timeout for establishing SSH connection to the remote host. Default: 30s."
|
description: 'timeout for ssh to remote host'
|
||||||
default: "30s"
|
default: "30s"
|
||||||
command_timeout:
|
command_timeout:
|
||||||
description: "Timeout for the SCP command execution. Default: 10m."
|
description: 'timeout for scp command'
|
||||||
default: "10m"
|
default: "10m"
|
||||||
key:
|
key:
|
||||||
description: "Content of the SSH private key (e.g., the raw content of ~/.ssh/id_rsa)."
|
description: 'content of ssh private key. ex raw content of ~/.ssh/id_rsa'
|
||||||
key_path:
|
key_path:
|
||||||
description: "Path to the SSH private key file."
|
description: 'path of ssh private key'
|
||||||
passphrase:
|
passphrase:
|
||||||
description: "Passphrase for the SSH private key, if required."
|
description: 'ssh key passphrase'
|
||||||
fingerprint:
|
|
||||||
description: "SHA256 fingerprint of the host's public key. If not set, host key verification is skipped (not recommended for production)."
|
|
||||||
use_insecure_cipher:
|
|
||||||
description: "Enable additional, less secure ciphers for compatibility. Not recommended unless required."
|
|
||||||
target:
|
target:
|
||||||
description: "Target directory path on the remote server. Must be a directory."
|
description: 'target path on the server'
|
||||||
source:
|
source:
|
||||||
description: "List of files or directories to transfer (local paths)."
|
description: 'scp file list'
|
||||||
rm:
|
rm:
|
||||||
description: "Remove the target directory on the server before uploading new data."
|
description: 'remove target folder before upload data'
|
||||||
debug:
|
default: false
|
||||||
description: "Enable debug messages for troubleshooting."
|
|
||||||
strip_components:
|
strip_components:
|
||||||
description: "Remove the specified number of leading path elements when extracting files."
|
description: 'remove the specified number of leading path elements'
|
||||||
|
default: 0
|
||||||
overwrite:
|
overwrite:
|
||||||
description: "Use the --overwrite flag with tar to overwrite existing files."
|
description: 'use `--overwrite` flag with tar'
|
||||||
tar_dereference:
|
default: false
|
||||||
description: "Use the --dereference flag with tar to follow symlinks."
|
|
||||||
tar_tmp_path:
|
tar_tmp_path:
|
||||||
description: "Temporary path for the tar file on the destination host."
|
description: 'temporary path for tar file on the dest host'
|
||||||
tar_exec:
|
|
||||||
description: "Path to the tar executable on the destination host. Default: tar."
|
|
||||||
default: "tar"
|
|
||||||
proxy_host:
|
proxy_host:
|
||||||
description: "Remote host address for SSH proxy."
|
description: 'ssh proxy remote host'
|
||||||
proxy_port:
|
proxy_port:
|
||||||
description: "SSH proxy port. Default: 22."
|
description: 'ssh proxy remote port'
|
||||||
default: "22"
|
default: 22
|
||||||
proxy_username:
|
proxy_username:
|
||||||
description: "Username for SSH proxy authentication."
|
description: 'ssh proxy username'
|
||||||
proxy_password:
|
proxy_password:
|
||||||
description: "Password for SSH proxy authentication."
|
description: 'ssh proxy password'
|
||||||
proxy_passphrase:
|
proxy_passphrase:
|
||||||
description: "Passphrase for the SSH proxy private key, if required."
|
description: 'ssh proxy key passphrase'
|
||||||
proxy_timeout:
|
proxy_timeout:
|
||||||
description: "Timeout for establishing SSH connection to the proxy host. Default: 30s."
|
description: 'timeout for ssh to proxy host'
|
||||||
default: "30s"
|
default: "30s"
|
||||||
proxy_key:
|
proxy_key:
|
||||||
description: "Content of the SSH proxy private key (e.g., the raw content of ~/.ssh/id_rsa)."
|
description: 'content of ssh proxy private key. ex raw content of ~/.ssh/id_rsa'
|
||||||
proxy_key_path:
|
proxy_key_path:
|
||||||
description: "Path to the SSH proxy private key file."
|
description: 'path of ssh proxy private key'
|
||||||
proxy_fingerprint:
|
|
||||||
description: "SHA256 fingerprint of the proxy host's public key. If not set, host key verification is skipped (not recommended for production)."
|
|
||||||
proxy_use_insecure_cipher:
|
|
||||||
description: "Enable additional, less secure ciphers for the proxy connection. Not recommended unless required."
|
|
||||||
curl_insecure:
|
|
||||||
description: "When true, uses the --insecure option with curl for insecure downloads."
|
|
||||||
default: "false"
|
|
||||||
capture_stdout:
|
|
||||||
description: "When true, captures and returns standard output from the commands as action output."
|
|
||||||
default: "false"
|
|
||||||
version:
|
|
||||||
description: |
|
|
||||||
The version of drone-scp to use.
|
|
||||||
|
|
||||||
outputs:
|
|
||||||
stdout:
|
|
||||||
description: "Standard output of the executed commands when capture_stdout is enabled."
|
|
||||||
value: ${{ steps.entrypoint.outputs.stdout }}
|
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: 'docker'
|
||||||
steps:
|
image: 'Dockerfile'
|
||||||
- name: Set GitHub Path
|
|
||||||
run: echo "$GITHUB_ACTION_PATH" >> $GITHUB_PATH
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GITHUB_ACTION_PATH: ${{ github.action_path }}
|
|
||||||
- id: entrypoint
|
|
||||||
name: Run entrypoint.sh
|
|
||||||
run: entrypoint.sh
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GITHUB_ACTION_PATH: ${{ github.action_path }}
|
|
||||||
INPUT_HOST: ${{ inputs.host }}
|
|
||||||
INPUT_PORT: ${{ inputs.port }}
|
|
||||||
INPUT_PROTOCOL: ${{ inputs.protocol }}
|
|
||||||
INPUT_USERNAME: ${{ inputs.username }}
|
|
||||||
INPUT_PASSWORD: ${{ inputs.password }}
|
|
||||||
INPUT_PASSPHRASE: ${{ inputs.passphrase }}
|
|
||||||
INPUT_KEY: ${{ inputs.key }}
|
|
||||||
INPUT_KEY_PATH: ${{ inputs.key_path }}
|
|
||||||
INPUT_FINGERPRINT: ${{ inputs.fingerprint }}
|
|
||||||
INPUT_PROXY_HOST: ${{ inputs.proxy_host }}
|
|
||||||
INPUT_PROXY_PORT: ${{ inputs.proxy_port }}
|
|
||||||
INPUT_PROXY_USERNAME: ${{ inputs.proxy_username }}
|
|
||||||
INPUT_PROXY_PASSWORD: ${{ inputs.proxy_password }}
|
|
||||||
INPUT_PROXY_PASSPHRASE: ${{ inputs.proxy_passphrase }}
|
|
||||||
INPUT_PROXY_KEY: ${{ inputs.proxy_key }}
|
|
||||||
INPUT_PROXY_KEY_PATH: ${{ inputs.proxy_key_path }}
|
|
||||||
INPUT_PROXY_FINGERPRINT: ${{ inputs.proxy_fingerprint }}
|
|
||||||
INPUT_USE_INSECURE_CIPHER: ${{ inputs.use_insecure_cipher }}
|
|
||||||
INPUT_CIPHER: ${{ inputs.cipher }}
|
|
||||||
INPUT_PROXY_USE_INSECURE_CIPHER: ${{ inputs.proxy_use_insecure_cipher }}
|
|
||||||
INPUT_PROXY_CIPHER: ${{ inputs.proxy_cipher }}
|
|
||||||
INPUT_DEBUG: ${{ inputs.debug }}
|
|
||||||
INPUT_TIMEOUT: ${{ inputs.timeout }}
|
|
||||||
INPUT_COMMAND_TIMEOUT: ${{ inputs.command_timeout }}
|
|
||||||
INPUT_TARGET: ${{ inputs.target }}
|
|
||||||
INPUT_SOURCE: ${{ inputs.source }}
|
|
||||||
INPUT_RM: ${{ inputs.rm }}
|
|
||||||
INPUT_STRIP_COMPONENTS: ${{ inputs.strip_components }}
|
|
||||||
INPUT_OVERWRITE: ${{ inputs.overwrite }}
|
|
||||||
INPUT_TAR_DEREFERENCE: ${{ inputs.tar_dereference }}
|
|
||||||
INPUT_TAR_TMP_PATH: ${{ inputs.tar_tmp_path }}
|
|
||||||
INPUT_TAR_EXEC: ${{ inputs.tar_exec }}
|
|
||||||
INPUT_PROXY_TIMEOUT: ${{ inputs.proxy_timeout }}
|
|
||||||
INPUT_CAPTURE_STDOUT: ${{ inputs.capture_stdout }}
|
|
||||||
INPUT_CURL_INSECURE: ${{ inputs.curl_insecure }}
|
|
||||||
DRONE_SCP_VERSION: ${{ inputs.version }}
|
|
||||||
|
|
||||||
branding:
|
branding:
|
||||||
icon: "copy"
|
icon: 'copy'
|
||||||
color: "gray-dark"
|
color: 'gray-dark'
|
||||||
|
@ -1,56 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/bin/sh
|
||||||
|
|
||||||
set -euo pipefail
|
set -eu
|
||||||
|
|
||||||
export GITHUB="true"
|
export GITHUB="true"
|
||||||
|
|
||||||
GITHUB_ACTION_PATH="${GITHUB_ACTION_PATH%/}"
|
[ -n "$INPUT_STRIP_COMPONENTS" ] && export INPUT_STRIP_COMPONENTS=$((INPUT_STRIP_COMPONENTS + 0))
|
||||||
DRONE_SCP_RELEASE_URL="${DRONE_SCP_RELEASE_URL:-https://github.com/appleboy/drone-scp/releases/download}"
|
|
||||||
DRONE_SCP_VERSION="${DRONE_SCP_VERSION:-1.8.0}"
|
|
||||||
|
|
||||||
function log_error() {
|
sh -c "/bin/drone-scp $*"
|
||||||
echo "$1" >&2
|
|
||||||
exit "$2"
|
|
||||||
}
|
|
||||||
|
|
||||||
function detect_client_info() {
|
|
||||||
CLIENT_PLATFORM="${SCP_CLIENT_OS:-$(uname -s | tr '[:upper:]' '[:lower:]')}"
|
|
||||||
CLIENT_ARCH="${SCP_CLIENT_ARCH:-$(uname -m)}"
|
|
||||||
|
|
||||||
case "${CLIENT_PLATFORM}" in
|
|
||||||
darwin | linux | windows) ;;
|
|
||||||
*) log_error "Unknown or unsupported platform: ${CLIENT_PLATFORM}. Supported platforms are Linux, Darwin, and Windows." 2 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "${CLIENT_ARCH}" in
|
|
||||||
x86_64* | i?86_64* | amd64*) CLIENT_ARCH="amd64" ;;
|
|
||||||
aarch64* | arm64*) CLIENT_ARCH="arm64" ;;
|
|
||||||
*) log_error "Unknown or unsupported architecture: ${CLIENT_ARCH}. Supported architectures are x86_64, i686, and arm64." 3 ;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
detect_client_info
|
|
||||||
DOWNLOAD_URL_PREFIX="${DRONE_SCP_RELEASE_URL}/v${DRONE_SCP_VERSION}"
|
|
||||||
CLIENT_BINARY="drone-scp-${DRONE_SCP_VERSION}-${CLIENT_PLATFORM}-${CLIENT_ARCH}"
|
|
||||||
TARGET="${GITHUB_ACTION_PATH}/${CLIENT_BINARY}"
|
|
||||||
echo "Downloading ${CLIENT_BINARY} from ${DOWNLOAD_URL_PREFIX}"
|
|
||||||
INSECURE_OPTION=""
|
|
||||||
if [[ "${INPUT_CURL_INSECURE}" == 'true' ]]; then
|
|
||||||
INSECURE_OPTION="--insecure"
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -fsSL --retry 5 --keepalive-time 2 ${INSECURE_OPTION} "${DOWNLOAD_URL_PREFIX}/${CLIENT_BINARY}" -o "${TARGET}"
|
|
||||||
chmod +x "${TARGET}"
|
|
||||||
|
|
||||||
echo "======= CLI Version Information ======="
|
|
||||||
"${TARGET}" --version
|
|
||||||
echo "======================================="
|
|
||||||
if [[ "${INPUT_CAPTURE_STDOUT}" == 'true' ]]; then
|
|
||||||
{
|
|
||||||
echo 'stdout<<EOF'
|
|
||||||
"${TARGET}" "$@" | tee -a "${GITHUB_OUTPUT}"
|
|
||||||
echo 'EOF'
|
|
||||||
} >>"${GITHUB_OUTPUT}"
|
|
||||||
else
|
|
||||||
"${TARGET}" "$@"
|
|
||||||
fi
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
foo
|
foo
|
||||||
foobar
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
bar
|
bar
|
||||||
foobar
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
c
|
c
|
||||||
foobar
|
|
||||||
test1234
|
|
||||||
|
@ -1,3 +1 @@
|
|||||||
d
|
d
|
||||||
foobar
|
|
||||||
foobar
|
|
||||||
|
Loading…
Reference in New Issue
Block a user