# Deploys main to the Hetzner host once this commit's test and check have # passed. The appview image is built from this checkout on the host, over # SSH, as a candidate, grain:build-, while the checks run; once # they pass it becomes grain:current and the grain service is recreated # from it, leaving the rest of the stack as it is. grain-deploy on the # host rebuilds the whole stack by hand. # # When main has moved past this commit by the time the checks pass, the # candidate is dropped and the run passes: the newer commit deploys. Once # a deploy starts it finishes; a newer one waits for it (superseded: # finish). # # It runs on ci.knotless.dev alone, the runner on knotless.dev's own host, # so the key never reaches the hosted runner that checks pulls. There # DEPLOY_SSH_KEY is sealed to pushes to main alone (`knotless secret # knotless://grain.social/grain set DEPLOY_SSH_KEY --runner ci.knotless.dev # --ref refs/heads/main`). Its public half is in root's authorized_keys on # the host. on: push: { refs: [refs/heads/main] } superseded: finish image: docker:28-cli runners: [ci.knotless.dev] secrets: [DEPLOY_SSH_KEY] env: GRAIN_HOST: root@167.233.237.163 steps: - name: tools run: | apk add --no-cache curl openssh-client curl -fsSL https://knotless.dev/install.sh | KNOTLESS_INSTALL_DIR=/usr/local/bin sh - name: key run: | mkdir -p ~/.ssh && chmod 700 ~/.ssh printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519 && chmod 600 ~/.ssh/id_ed25519 printf 'Host *\n StrictHostKeyChecking accept-new\n' > ~/.ssh/config - name: build timeout: 30 run: | DOCKER_HOST="ssh://$GRAIN_HOST" docker build -t "grain:build-$KNOTLESS_RUN_SHA" . - name: checks timeout: 30 run: | repo="knotless://$KNOTLESS_RUN_REPO" end=$(($(date +%s) + 1500)) for wf in test check; do left=$((end - $(date +%s))) [ "$left" -gt 0 ] || left=1 out=$(knotless checks "$repo" --ref "$KNOTLESS_RUN_REF" --commit "$KNOTLESS_RUN_SHA" --workflow "$wf" --wait "$left") echo "$out" case "$out" in success*) ;; *) echo "$wf has not passed: not deploying" DOCKER_HOST="ssh://$GRAIN_HOST" docker rmi "grain:build-$KNOTLESS_RUN_SHA" exit 1 ;; esac done - name: deploy run: | export DOCKER_HOST="ssh://$GRAIN_HOST" tip=$(knotless refs "knotless://$KNOTLESS_RUN_REPO" | awk -F '\t' -v ref="$KNOTLESS_RUN_REF" '$2 == ref { print $1 }') if [ "$tip" != "$KNOTLESS_RUN_SHA" ]; then echo "$KNOTLESS_RUN_REF is at ${tip:-nothing} now, not $KNOTLESS_RUN_SHA: a newer commit will deploy" docker rmi "grain:build-$KNOTLESS_RUN_SHA" exit 0 fi docker tag "grain:build-$KNOTLESS_RUN_SHA" grain:current docker rmi "grain:build-$KNOTLESS_RUN_SHA" ssh "$GRAIN_HOST" 'docker compose -f /etc/grain/docker-compose.yml up -d --no-build grain && docker image prune -f'