GitHub Actions are great! Probably the best way to run little CI scripts - for free - against your repositories. I maintain several of them for work, including aws-actions/configure-aws-credentials. Unfortunately, their ease of use means that a lot of the actions out there are fairly low quality, and if you’re using actions authored by others you have to be very careful about your reliance on tooling that you don’t control.

But this has been talked about before. Precautions like pinning action versions to a specific commit SHA (instead of a branch name or a tag), and being careful about what dependencies you accept are part of software supply chain management, and GitHub Actions aren’t immune from these problems.

OK, so that brings me to my issue. This website is hosted at my home, and I’m pretty leary of running an unrestricted SFTP or SSH server open to the world on my home network, even if it’s on a different port and the web server is well-firewalled in both directions. So instead I’ve set up access to an SFTP-only server running in a chroot, so that way I can use GitHub Actions to push my built website up whenever I make a push to the main branch. All I have to do is create a workflow step that uses someone else’s pre-made SFTP action!

I tried a couple of actions out there. None of the popular ones work. They

  • are typically built as Docker actions, which means they have a longish startup time
  • assume that they can ssh into the server. They’re doing things like running a tarpipe, slinging temporary files around, or just opening a shell and rm-ing stuff. None of that works on my server, because you must connect with the sftp subsystem.

So instead I hacked together my own solution, using the sftp client. Luckily this is available as part of the openssh package that is preinstalled on the ubuntu-latest images currently in use. Protip: they also include yarn by default now, so no more installing that. The issue is that the sftp client does not have an rm -R command! And we need to remove the existing website before we shove a new one in its place (and I don’t want to go through the hassle of setting up rsync; I already had a working jailed SFTP server).

That’s where the hacky part comes in. I don’t like this, but it works.

- run: |
find public_html -type f > rm-list
sed -i rm-list -e 's/^/rm /'
echo "rm public_html/assets/css/*" >> rm-list
find public_html -type d | awk '{ print length, $0 }' | sort -nr | cut -d" " -f2- > rm-dir
sed -i rm-dir -e 's/^/rmdir /'

- run: >
echo -e "$(cat rm-list)"\\nbye |
sftp -q -C -i id_rsa -P $ -o StrictHostKeyChecking=no $@$

- run: >
echo -e "$(cat rm-dir)"\\nbye |
sftp -q -C -i id_rsa -P $ -o StrictHostKeyChecking=no $@$

- run: >
echo -e "put -R public_html"\\nbye |
sftp -q -C -i id_rsa -P $ -o StrictHostKeyChecking=no $@$

Gross. Anyways, full source is here.