skip to content
blog.metters.dev
Table of Contents

Prerequisites

  • Basics are explained in the previous post about deploying an artefact
  • As you might have guessed already: a GitHub Action workflow requires your repository to be on GitHub.
  • Some of the steps in that workflow are specific to my site/project, e.g., the node version, the package manager (yarn, pnpm, npm, …), and so on.
  • Set up environment/repository secrets and variables in GitHub

This is actually very similar to the previous post, but instead of on your laptop/pc this happens on a GitHub runner. It deals differently with variables and secrets, to protect your private key or other sensitive information from being leaked accidentally. It also helps with managing those for several environments, but let’s pretend there is no staging first.

Step by step

  1. Create .github/workflows/deploy.yml in your project
  2. Add trigger
name: 'Deploy artefact'
on:
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
  1. Add step: check out the repository
- name: 'Check out repository'
uses: actions/checkout@v4
  1. Add steps: install node and pnpm
- name: 'Install node'
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: 'https://npm.pkg.github.com'
- name: 'Install pnpm'
uses: pnpm/action-setup@v4
with:
version: 8.6.1
run_install: false
  1. Add steps: install dependencies and build artefact
- name: 'Install dependencies'
run: pnpm install
- name: 'Build artefact'
run: pnpm build
  1. Add step: prepare deployment and execute deployment via rsync
- name: 'Prepare secrets for ssh connection and execute deployment'
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
SSH_PASS: ${{ secrets.SSH_PASS }}
USER: ${{ vars.REMOTE_USER }}
HOST: ${{ vars.REMOTE_HOST }}
DIR: ${{ vars.REMOTE_DIR }}
run: |
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -t ed25519, "$HOST" >> ~/.ssh/known_hosts
eval "$(ssh-agent -s)"
echo "$SSH_PASS" | ssh-add <(echo "$DEPLOY_KEY")
rsync -avzr -e 'ssh -o StrictHostKeyChecking=no -v' --delete ./dist/ "${USER}"@"${HOST}":/var/www/html/"${DIR}"

Obviously, it is the step that is the most complex one. Let’s analyse what happens there:

Variable assignment
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
SSH_PASS: ${{ secrets.SSH_PASS }}
USER: ${{ vars.REMOTE_USER }}
HOST: ${{ vars.REMOTE_HOST }}
DIR: ${{ vars.REMOTE_DIR }}

Additionally to the action, I stored two secrets and three variables in GitHub.

  1. DEPLOY_KEY: the private ssh key necessary to access the vps via ssh
  2. SSH_PASS: the corresponding passphrase for the private key
  3. USER: name of the user the private ssh key belongs to
  4. HOST: ip address of the vps
  5. DIR: name of the directory on the vps that contains the artefact

Next, the ssh key needs to be stored on the runner (which is also a Ubuntu machine):

Add ssh key to runner
mkdir -p ~/.ssh
echo "$DEPLOY_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
  • After that, the ssh key is added to the known_hosts file and associated to the remote host ($HOST):
Terminal window
ssh-keyscan -t ed25519, "$HOST" >> ~/.ssh/known_hosts

Before we can open a ssh connection we need to start the ssh agent and add the private key together with its corresponding passphrase to the ssh agent:

Terminal window
eval "$(ssh-agent -s)"
echo "$SSH_PASS" | ssh-add <(echo "$DEPLOY_KEY")

Finally, the artefact can be deployed via rsync to the configured :

Terminal window
rsync -avzr -e 'ssh -o StrictHostKeyChecking=no -v' --delete ./dist/ "${USER}"@"${HOST}":/var/www/html/"${DIR}"

Troubleshooting

Why can I not find my manually triggered workflow?

An ‘instance’ of the workflow must first exist on the main branch of your repository. After you have merged your workflow into your main branch, it will show up in Actions

But I don’t want to test my workflow on my main branch

  • Register dummy file with manual trigger on main
  • Then work on ti branch

The deployment step does not work as expected

  • Please check whether you have properly set up your environment/repository variables and secrets.
  • Please check whether the user that technically executes the deployment (the owner of the private key) has ownership of the directories on your vps.
  • Please read the error logs of the failed run.