YAML · 5409 bytes Raw Blame History
1 name: deploy
2 on:
3 # Auto-deploy on every push to trunk, but only after CI succeeds —
4 # workflow_run waits for the named workflow to complete.
5 workflow_run:
6 workflows: [ci]
7 branches: [trunk]
8 types: [completed]
9 # Escape hatch: redeploy current trunk without pushing.
10 workflow_dispatch:
11
12 permissions:
13 contents: read
14
15 # GH Actions is migrating its built-in Node from 20 to 24 by 2026-09-16.
16 # Force our pinned action versions onto Node 24 now so the deprecation
17 # warning clears and the eventual default flip is a no-op for us. Drop
18 # this once every action below is bumped to a Node-24-native release.
19 env:
20 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
21
22 concurrency:
23 # Serialize deploys so two pushes in flight don't race on the
24 # remote git reset / binary swap.
25 group: deploy-prod
26 cancel-in-progress: false
27
28 jobs:
29 deploy:
30 # Skip when the upstream CI run failed; workflow_dispatch always runs.
31 if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
32 runs-on: ubuntu-latest
33 environment: production
34 steps:
35 - uses: actions/checkout@v4
36 with:
37 # Full history so the Mirror-to-shithub.sh step can prove
38 # ancestry against whatever the mirror's tip is. Default
39 # depth=1 makes any push that isn't the very first commit
40 # fail "non-fast-forward" because the runner can't see
41 # the parent chain.
42 fetch-depth: 0
43
44 - uses: actions/setup-go@v5
45 with:
46 go-version-file: go.mod
47 cache: true
48
49 # Delegate to `make build` so the version-injection ldflags
50 # (Version/Commit/BuiltAt → internal/version) are the same set
51 # the local Makefile uses. The previous inline `go build` here
52 # passed only `-s -w`, which left the homepage stamps showing
53 # "dev (unknown, built unknown)" on every deployed binary.
54 # The runner is linux/amd64 so no cross-compile is needed.
55 # Output goes to ./shithubd (overriding BIN) so the next step's
56 # `< shithubd` redirect picks it up.
57 - name: Build shithubd
58 env:
59 CGO_ENABLED: "0"
60 run: make build BIN=shithubd
61
62 - name: Configure SSH
63 env:
64 DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
65 DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
66 run: |
67 mkdir -p ~/.ssh
68 chmod 700 ~/.ssh
69 printf '%s\n' "$DEPLOY_SSH_KEY" > ~/.ssh/id_ed25519
70 printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > ~/.ssh/known_hosts
71 chmod 600 ~/.ssh/id_ed25519 ~/.ssh/known_hosts
72
73 - name: Ship binary
74 env:
75 DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
76 DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
77 # Stream over an exec channel rather than scp/sftp — the
78 # hardened sshd on the droplet has the sftp-server subsystem
79 # disabled, and modern scp insists on sftp.
80 run: |
81 ssh -o BatchMode=yes "${DEPLOY_USER}@${DEPLOY_HOST}" \
82 'cat > /tmp/shithubd-new && chmod 0755 /tmp/shithubd-new' < shithubd
83
84 - name: Redeploy
85 env:
86 DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
87 DEPLOY_USER: ${{ secrets.DEPLOY_USER }}
88 run: |
89 ssh -o BatchMode=yes "${DEPLOY_USER}@${DEPLOY_HOST}" \
90 'cd /root/src/shithub && git fetch --quiet origin trunk && git reset --hard origin/trunk && bash deploy/redeploy.sh'
91
92 # Mirror the just-deployed SHA to the self-hosted shithub.sh
93 # instance so anyone reading the source on shithub.sh sees the
94 # same tip as github. Runs only after the redeploy succeeds, so
95 # a broken commit never appears canonical on the dogfood mirror.
96 # The credential helper feeds the PAT via stdin to avoid leaking
97 # it via the process listing or git's url-with-credentials log.
98 #
99 # Plain `push` (no --force-with-lease): if shithub.sh ever
100 # diverges from origin/trunk, the push fails non-fast-forward
101 # and a human reconciles. We never want a runner to silently
102 # overwrite a human edit on the mirror.
103 #
104 # The app may still be coming back through Caddy immediately
105 # after the systemd restart. Wait for the public HTTP surface
106 # and retry the mirror push so a transient 502 does not mark a
107 # successful deploy as failed. Persistent auth, divergence, or
108 # server errors still fail the job.
109 - name: Mirror to shithub.sh
110 if: success()
111 env:
112 SHITHUB_PUSH_USER: ${{ secrets.SHITHUB_PUSH_USER }}
113 SHITHUB_PUSH_PAT: ${{ secrets.SHITHUB_PUSH_PAT }}
114 run: |
115 curl --fail --silent --show-error \
116 --retry 6 --retry-delay 5 --retry-all-errors \
117 https://shithub.sh/ \
118 --output /dev/null
119
120 mirror_push() {
121 git -c "credential.helper=!f() { echo username=$SHITHUB_PUSH_USER; echo password=$SHITHUB_PUSH_PAT; }; f" \
122 push \
123 https://shithub.sh/tenseleyflow/shithub.git \
124 HEAD:trunk
125 }
126
127 status=0
128 for attempt in 1 2 3 4 5; do
129 if mirror_push; then
130 exit 0
131 fi
132 status=$?
133 if [ "$attempt" -eq 5 ]; then
134 break
135 fi
136 sleep "$((attempt * 10))"
137 done
138 exit "$status"
139