The automation of file movement within a Continuous Integration and Continuous Deployment (CI/CD) pipeline is a critical requirement for modern software engineering. Whether the objective is to synchronize artifacts between repositories, deploy build outputs to a remote production server, or manage the transition of code from private development environments to public-facing repositories, the selection of the appropriate GitHub Action determines the reliability and security of the pipeline. File copying in GitHub Actions is not a monolithic task; it spans three primary domains: intra-repository movement, cross-repository synchronization, and remote server deployment via secure protocols.
The complexity of these operations often involves managing authentication tokens, handling glob patterns for file selection, and configuring secure shell (SSH) tunnels. For developers operating in restricted environments—such as those who cannot develop openly on public repositories—these actions provide a vital bridge, allowing for a controlled flow of approved content from private silos to public visibility. The technical execution of these transfers relies on various underlying mechanisms, ranging from the cpy package for local filesystem operations to the scp protocol for remote transfers and the GitHub REST API for cross-repository commits.
Cross-Repository Synchronization and Pull Request Automation
One of the most sophisticated use cases for file copying is the movement of data between two distinct GitHub repositories. This is often required when a project follows a strict "private-to-public" promotion workflow.
The copy-pr-action is specifically engineered for developers who maintain private content that must be approved before being published to a public repository. In this workflow, the source repository acts as the staging area, and the destination repository acts as the final public version.
The operational flow is as follows:
1. A push event is triggered on a specified branch of the source repository.
2. The action identifies the files within a specific directory.
3. These files are copied to a new branch in the destination repository.
4. A pull request is automatically raised in the destination repository to merge those changes.
To prevent the leakage of sensitive configuration or internal automation logic, this action allows for the exclusion of specific files. This is managed through a configuration file located at .github/workflows/ci_ignore.txt. Any file path listed in this text file will be skipped during the copy process, ensuring that GitHub workflow files or private secrets are not inadvertently pushed to the public domain.
The security model for this operation requires a Personal Access Token (PAT) because standard GitHub Actions tokens are generally scoped to the local repository and cannot push to external repositories.
The process for generating and implementing this token involves:
- Navigating to GitHub Personal Access Tokens settings.
- Generating a new token with the "Repo" scope selected.
- Copying the token and adding it as a secret in the source repository.
- Specifically, the token must be stored in the source repository's settings under Secrets and Variables > Actions.
Another alternative for cross-repository movement is the nkoppel/push-files-to-another-repository action. This tool is ideal for scenarios such as updating a github.io website with a webpage generated by a separate build action. Unlike the PR-based approach, this action commits changes directly to the destination repository.
The configuration for this action requires several parameters to ensure the commit is attributed correctly and placed in the right directory:
| Parameter | Description | Default Value |
|---|---|---|
| source-files | Files or directories to copy (supports globbing) | N/A |
| destination-username | The owner of the destination repository | N/A |
| destination-repository | The target repository name | N/A |
| destination-branch | The target branch | master |
| destination-directory | Target folder in the destination repo | Root |
| commit-username | Username for the commit | destination-username |
| commit-email | Email for the commit | N/especified |
| commit-message | The message for the commit | Update from [url]@[commit] |
For the API_TOKEN_GITHUB secret, users have two choices: granting access to "All Repositories" (which increases security risk) or selecting specific repositories and setting the "Contents" permission to "Read and Write".
Local Filesystem Operations and Glob Pattern Copying
When the requirement is simply to move files from one directory to another within the same runner environment (such as moving source files to a build folder), specialized local copy actions are utilized.
The nearform-actions/github-action-copy-files action leverages the cpy package. This allows for highly flexible file selection using glob patterns via globby. This is particularly useful for filtering files by extension or path while ignoring others.
The action is configured with three primary inputs:
- source: The path to the files or directories to be copied.
- destination: The target path where files should be placed.
- options: A JSON object passed to the cpy function to modify behavior.
The options input allows for advanced controls such as overwrite (to replace existing files) and flat (to flatten the directory structure).
Example implementation:
yaml
name: Copy files
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: nearform-actions/github-action-copy-files@v1
with:
source: './src/**'
destination: './build/'
options: |
{
"overwrite": true,
"flat": true
}
For simpler, single-file operations, the canastro/copy-file-action provides a lightweight alternative. It maps a specific source file to a target filename, though it is provided by a third party and is not officially certified by GitHub.
Remote Deployment via SSH and SCP
Transporting files from a GitHub runner to a remote server requires secure protocols. This is typically achieved through Secure Copy Protocol (SCP) over SSH.
The appleboy/scp-action is a comprehensive tool for this purpose, specifically designed for Linux docker containers. It supports a wide array of enterprise-grade features:
- Use of SSH keys or password authentication.
- Support for SSH Proxy (jump hosts) to reach servers in private networks.
- Automatic conversion between Linux and Windows path formats.
- Integration with GitHub Artifacts.
- Support for incremental and differential transfers to reduce bandwidth.
The configuration variables for appleboy/scp-action are detailed in the following table:
| Variable | Description | Default | Required |
|---|---|---|---|
| host | Remote host(s), comma-separated for multiple servers | N/A | Yes |
| port | SSH port | 22 | No |
| username | SSH username | N/A | Yes |
| password | SSH password (less secure) | N/A | No |
| key | SSH private key content | N/A | No |
| key_path | Path to the SSH private key file | N/A | No |
| passphrase | Passphrase for the SSH key | N/A | No |
Implementation example:
yaml
name: scp files
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Copy files via 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
Alternatively, the garygrossgarten/github-action-scp provides a streamlined approach for both single files and recursive folder copying.
The parameters for this action include:
- local: The path to the local folder or file.
- remote: The destination path on the server.
- concurrency: The number of simultaneous file transfers (Default: 1).
- recursive: Whether to copy directory contents recursively (Default: true).
- verbose: Whether to output the status of every single file transfer (Default: true).
- host: Hostname or IP (Default: 'localhost').
- port: Server port (Default: 22).
- username: Authentication username.
- password: Password for authentication.
Example for recursive folder copy:
yaml
- name: Copy folder content recursively to remote
uses: garygrossgarten/github-action-scp@release
with:
local: test
remote: scp/directory
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.PASSWORD }}
Example for a single file copy:
yaml
- name: Copy single file to remote
uses: garygrossgarten/github-action-scp@release
with:
local: test/oof.txt
remote: scp/single/oof.txt
host: ${{ secrets.HOST }}
username: ${{ secrets.SSH_USER }}
password: ${{ secrets.PASSWORD }}
Conclusion: Strategic Analysis of File Copying Methods
The selection of a file-copying action is not merely a matter of preference but a technical decision based on the destination of the data and the security requirements of the environment. When analyzing the available tools, a clear hierarchy of use cases emerges.
For internal build processes where files must be reorganized on the runner, the nearform-actions/github-action-copy-files is the superior choice due to its integration with the cpy package and support for globbing, which allows for surgical precision in file selection.
For deployment to infrastructure, the appleboy/scp-action stands out as the most robust option. Its support for jump hosts and differential transfers makes it suitable for complex enterprise architectures where servers are hidden behind bastion hosts. In contrast, garygrossgarten/github-action-scp is better suited for simpler, linear deployments where a minimalist configuration is preferred.
The most complex challenge remains cross-repository synchronization. The choice between copy-pr-action and nkoppel/push-files-to-another-repository depends on the desired level of oversight. The copy-pr-action is designed for a "governed" workflow, where the creation of a pull request ensures that a human reviewer can audit the changes before they are merged into the public repository. The push-files action is designed for "automated" workflows, such as static site generators, where the speed of update is more important than a manual review process.
Across all these methods, the common thread is the critical importance of Secret Management. Whether using API_TOKEN_GITHUB for repository access or SSH_USER and PASSWORD for server access, the use of GitHub Secrets is mandatory to prevent the exposure of credentials in the workflow YAML files.