diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 0000000..30820a1 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,21 @@ +name: Validate + +on: + pull_request: + branches: [ main ] + +jobs: + validate-commit-messages: + name: Validate commit messages + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + steps: + - name: Run the validation + uses: rainstormy/github-action-validate-commit-messages@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + rules: 'acknowledged-author-email-addresses, acknowledged-author-names, acknowledged-committer-email-addresses, acknowledged-committer-names, capitalised-subject-lines, empty-line-after-subject-lines, imperative-subject-lines, limit-length-of-body-lines, limit-length-of-subject-lines, multi-word-subject-lines, no-co-authors, no-merge-commits, no-revert-revert-commits, no-squash-commits, no-trailing-punctuation-in-subject-lines, no-unexpected-whitespace' + acknowledged-author-email-addresses--patterns: '\d+\+.+@users\.noreply\.github\.com' + acknowledged-author-names--patterns: '\p{Lu}.*\s.+' + acknowledged-committer-email-addresses--patterns: '\d+\+.+@users\.noreply\.github\.com' + acknowledged-committer-names--patterns: '\p{Lu}.*\s.+' diff --git a/.idea/github-action-deploy-with-rsync-over-ssh.iml b/.idea/github-action-deploy-with-rsync-over-ssh.iml index d6ebd48..c0d5928 100644 --- a/.idea/github-action-deploy-with-rsync-over-ssh.iml +++ b/.idea/github-action-deploy-with-rsync-over-ssh.iml @@ -2,7 +2,10 @@ - + + + + diff --git a/CONTRIBUTING.adoc b/CONTRIBUTING.adoc index 8c320be..ed273eb 100644 --- a/CONTRIBUTING.adoc +++ b/CONTRIBUTING.adoc @@ -49,7 +49,7 @@ $ ssh-add ~/.ssh/ $ git config --global core.sshCommand "C:/Windows/System32/OpenSSH/ssh.exe" ---- -. Grab the **_public_** key. +. Grab the *_public_* key. It appears like `ssh-ed25519 AAAAC3(...)`. + [source,shell,subs="+quotes,+replacements"] diff --git a/README.adoc b/README.adoc index 0026dd4..0ea4ed8 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,84 @@ = Deploy with Rsync over SSH :experimental: :source-highlighter: highlight.js -:toc: preamble -This repository implements an Ubuntu-based composite GitHub Action that copies files from the GitHub Actions runner to a remote host using the https://manpages.ubuntu.com/manpages/bionic/man1/rsync.1.html[Rsync] tool over SSH. +This repository implements an Ubuntu-based https://docs.github.com/en/actions/creating-actions/about-custom-actions#composite-actions[composite GitHub Action] that copies files from the GitHub Actions runner to a remote host using the https://manpages.ubuntu.com/manpages/jammy/en/man1/rsync.1.html[Rsync] tool over an SSH connection. +This provides a way to deploy static files to a web server. + +Please read the `action.yml` file to review the specific steps performed by this action: +In broad terms, it saves the private SSH key and the list of known hosts on the GitHub Actions runner for the duration of the action and executes the `rsync` command with the given input parameters. + +== Usage +. Define a GitHub Actions workflow. +For example: ++ +[source,yaml] +---- +# .github/workflows/deploy.yml +name: Deploy +on: [ workflow_dispatch ] +jobs: + copy-static-files-to-web-server: + name: Copy static files to the web server + runs-on: ubuntu-latest + steps: + - name: Check out the repository + uses: actions/checkout@v3 + - name: Copy the files with Rsync over SSH + uses: rainstormy/github-action-deploy-with-rsync-over-ssh@v1 + with: + rsync-options: --archive --compress --verbose --rsh="ssh -l ${{ secrets.SSH_USERNAME }}" + rsync-host: ${{ secrets.RSYNC_HOST }} + rsync-source-path: www/ + rsync-destination-path: ${{ secrets.RSYNC_DESTINATION_PATH }} + ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }} + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + ssh-private-key-filename: ${{ secrets.SSH_PRIVATE_KEY_FILENAME }} +---- ++ +Here is another example that uses https://manpages.ubuntu.com/manpages/jammy/en/man1/sshpass.1.html[sshpass] to perform password-based authentication in addition to key-based authentication: ++ +[source,yaml] +---- +with: + rsync-options: --archive --compress --verbose --rsh="sshpass -e ssh -l ${{ secrets.SSH_USERNAME }}" + rsync-host: ${{ secrets.RSYNC_HOST }} + rsync-source-path: www/ + rsync-destination-path: ${{ secrets.RSYNC_DESTINATION_PATH }} + ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }} + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + ssh-private-key-filename: ${{ secrets.SSH_PRIVATE_KEY_FILENAME }} +env: + SSHPASS: ${{ secrets.SSH_PASSWORD }} +---- + +. Create https://docs.github.com/en/actions/security-guides/encrypted-secrets[GitHub Actions secrets] for the SSH username, the remote host (e.g. `server123.company.com`), and the destination pathname on the remote (e.g. `~/www`). +They are to be accessed through input parameters `rsync-options` (in the `--rsh` option), `rsync-host`, and `rsync-destination-path`, respectively. + +. Optionally, for password-based authentication, create a GitHub Actions secret for the SSH password to be accessed through the `SSHPASS` environment variable. + +. From your own computer, gather the remote host keys and verify that they are authentic to establish a https://developer.mozilla.org/en-US/docs/Glossary/TOFU[trust-on-first-use] relationship: ++ +[source,shell,subs="+quotes,+replacements"] +---- +$ ssh-keyscan -H *__* > "$HOME/.ssh/known_hosts" +---- + +. Save the remote host keys in a GitHub Actions secret to be accessed through the `ssh-known-hosts` input parameter. + +. From your own computer, generate an SSH key with the *Ed25519* algorithm, for example using https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent[ssh-keygen] or https://developer.1password.com/docs/ssh/manage-keys[1Password], and optionally choose a name for the key or use the default name of `id_ed25519` as `*__*`: ++ +[source,shell,subs="+quotes,+replacements"] +---- +$ ssh-keygen -t ed25519 -f "$HOME/.ssh/**__**" +---- + +. Grab the *_public_* key and add it to the list of authorised SSH keys on the remote host. +It appears like `ssh-ed25519 AAAAC3(...)`. + +. Save the private key in a GitHub Actions secret to be accessed through the `ssh-private-key` input parameter. +It appears like `-----BEGIN OPENSSH PRIVATE KEY-----(...)-----END OPENSSH PRIVATE KEY-----`. + +. Save the name that you have chosen as `*__*` in a GitHub Actions secret to be accessed through the `ssh-private-key-filename` input parameter. +It defines the name of the file in which the private key is stored on the GitHub Actions runner for the duration of the action. +You may skip this step if you have chosen the default name of `id_ed25519`. diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..a01333e --- /dev/null +++ b/action.yml @@ -0,0 +1,48 @@ +name: Deploy with Rsync over SSH +description: Copies files from the GitHub Actions runner to a remote host using the Rsync tool over an SSH connection. +author: Steffen Diswal +inputs: + rsync-destination-path: + description: The pathname on the remote host to which the files are to be copied. + required: true + rsync-host: + description: The hostname of the remote server onto which the files are to be copied. + required: true + rsync-options: + description: The options to pass to the Rsync tool. Include the `--rsh` option to set the SSH credentials for Rsync. + required: true + rsync-source-path: + description: The pathname on the GitHub Actions runner from which the files are to be copied. + required: true + ssh-known-hosts: + description: A list of trusted remote host keys gathered by the ssh-keyscan utility to be stored temporarily on the GitHub Actions runner. + required: true + ssh-private-key: + description: The private SSH key to be stored temporarily on the GitHub Actions runner in the file designated by the `ssh-private-key-filename` input parameter. It is used by the Rsync tool to establish an SSH connection to the remote host which should know the public SSH key in advance. + required: true + ssh-private-key-filename: + description: The name of the file in the `.ssh` directory on the GitHub Actions runner in which the value of the `ssh-private-key` input parameter is temporarily stored. + required: false + default: 'id_ed25519' +runs: + using: composite + steps: + - name: Prepare the SSH configuration + run: | + mkdir ~/.ssh + echo "${{ inputs.ssh-known-hosts }}" > ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts + echo "${{ inputs.ssh-private-key }}" > ~/.ssh/${{ inputs.ssh-private-key-filename }} + chmod 600 ~/.ssh/${{ inputs.ssh-private-key-filename }} + shell: bash + + - name: Deploy with Rsync + run: | + rsync ${{ inputs.rsync-options }} ${{ inputs.rsync-source-path }} ${{ inputs.rsync-host }}:${{ inputs.rsync-destination-path }} + shell: bash + + - if: always() + name: Erase the SSH configuration + run: | + rm --recursive --force ~/.ssh + shell: bash