GITHUB_TOKEN expires with the workflow run, but `docker login` writes it to /root/.docker/config.json on the host, where it stays. Docker then presents that dead token on every subsequent pull and does NOT fall back to anonymous, so even a public image fails with "No such image" -- which reads as a missing image rather than a stale credential. This broke deploys of services that have nothing to do with this workflow. The workaround each time was to delete the ghcr.io auth entry by hand, pull, and restore it; the stale entry has now been removed from the host as well. Uses a trap on EXIT rather than a trailing command so the logout also runs when a pull or a stack deploy fails -- the failure case is exactly when a half-finished run would otherwise leave the credential behind. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YLYiaPYror1vLKApYKrxTu
47 lines
1.9 KiB
YAML
47 lines
1.9 KiB
YAML
name: Deploy stacks
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
|
|
deploy:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: docker stack deploy
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
with:
|
|
username: root
|
|
host: ${{ secrets.SSH_KNOWN_HOSTS }}
|
|
key: ${{ secrets.SSH_PRIV_KEY }}
|
|
script: |
|
|
# Always log out, including when a pull or deploy below fails.
|
|
#
|
|
# GITHUB_TOKEN dies with the workflow run, but `docker login`
|
|
# persists it to /root/.docker/config.json on the HOST, where it
|
|
# outlives the run by however long until someone notices. Docker
|
|
# then presents that dead token on every later pull and does NOT
|
|
# fall back to anonymous, so even a public image fails with
|
|
# "No such image" — which reads like the image is missing rather
|
|
# than like the credential is stale. Deploys of unrelated services
|
|
# broke this way, and the workaround each time was to delete the
|
|
# ghcr.io entry by hand, pull, and put it back.
|
|
trap 'docker logout ghcr.io >/dev/null 2>&1 || true' EXIT
|
|
cd ~/devops && git pull
|
|
echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
|
|
docker pull ghcr.io/token-for-token/hosted-gateway:main
|
|
docker pull ghcr.io/token-for-token/hosted-gateway-frontend:main
|
|
docker stack deploy -c ~/devops/hostinger/system-stack.yaml system --with-registry-auth --resolve-image always
|
|
docker stack deploy -c ~/devops/hostinger/prod-stack.yaml prod --with-registry-auth --resolve-image always
|
|
|
|
- name: prune old images
|
|
uses: appleboy/ssh-action@v1.0.3
|
|
with:
|
|
username: root
|
|
host: ${{ secrets.SSH_KNOWN_HOSTS }}
|
|
key: ${{ secrets.SSH_PRIV_KEY }}
|
|
script: |
|
|
docker image prune -f --filter "until=24h"
|