-
Notifications
You must be signed in to change notification settings - Fork 0
129 lines (114 loc) · 5.11 KB
/
Copy pathdeploy-selfhost.yml
File metadata and controls
129 lines (114 loc) · 5.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
name: Deploy production app
# Push to main → build + rsync to production app evig.orangecat.ch.
# One-time setup (Settings → Secrets → Actions):
# HETZNER_SSH_PRIVATE_KEY — private key for ubuntu@167.233.22.31
# SELFHOST_ENV — full contents of .env.selfhost.local (multiline OK)
# AUTH_TEST_USER_PASSWORD — non-admin E2E account (butaeff@gmail.com)
# AUTH_TEST_ADMIN_PASSWORD — staff E2E account (georgy.butaev@revamp-it.ch)
# Without deploy secrets the job logs a notice and exits cleanly.
# Without E2E passwords the post-deploy inventory step is skipped.
on:
workflow_dispatch: {}
push:
branches: [main]
concurrency:
group: deploy-selfhost-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
name: evig.orangecat.ch
runs-on: ubuntu-latest
timeout-minutes: 45
outputs:
deployed: ${{ steps.mark-deployed.outputs.deployed }}
env:
HETZNER_SSH_PRIVATE_KEY: ${{ secrets.HETZNER_SSH_PRIVATE_KEY }}
SELFHOST_ENV: ${{ secrets.SELFHOST_ENV }}
steps:
- name: Check secrets configured
id: secrets
run: |
if [ -z "$HETZNER_SSH_PRIVATE_KEY" ] || [ -z "$SELFHOST_ENV" ]; then
echo "::notice::HETZNER_SSH_PRIVATE_KEY and/or SELFHOST_ENV not set — skipping self-host deploy. Push from a machine with .env.selfhost.local still deploys via the pre-push hook."
echo "configured=false" >> "$GITHUB_OUTPUT"
else
echo "configured=true" >> "$GITHUB_OUTPUT"
fi
- name: Checkout code
if: steps.secrets.outputs.configured == 'true'
uses: actions/checkout@v7
- name: Setup Node.js
if: steps.secrets.outputs.configured == 'true'
uses: actions/setup-node@v7
with:
node-version: '24'
cache: npm
- name: Install dependencies
if: steps.secrets.outputs.configured == 'true'
run: npm ci
# Persist Next.js's incremental compiler cache across runs. The build runs
# on this ephemeral runner, so without this every build is COLD (full
# recompile). restore-keys falls back to the latest cache for the same
# lockfile, making subsequent builds incremental (warm).
- name: Cache Next.js build
if: steps.secrets.outputs.configured == 'true'
uses: actions/cache@v6
with:
path: .next/cache
key: nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-${{ github.sha }}
restore-keys: |
nextjs-${{ runner.os }}-${{ hashFiles('package-lock.json') }}-
# ONE definition of "verified" — the same bundle ci.yml runs, called
# verbatim. These four steps used to be re-inlined here, which quietly made
# the DEPLOY gate weaker than the MERGE gate: it omitted `lint:chrome`
# (the card-shell ratchet) and ran only the i18n subset instead of the
# full unit suite. A regression those cover could not block a merge but
# could still ship. `verify` = lint + umlauts + chrome + typecheck + test
# + build, and it already includes the i18n structure gate via `test`.
- name: Verify (lint + umlauts + chrome + typecheck + test + build)
if: steps.secrets.outputs.configured == 'true'
run: npm run verify
env:
# Same build-time placeholders ci.yml uses: this step runs BEFORE
# .env.selfhost.local is written, so strict env validation would
# otherwise fail during compile. The deploy script rebuilds after
# this with the real env and the release SHA; that second build is
# warm (the .next/cache step above), so the gate costs little.
AUTH_SECRET: ci-build-placeholder-secret-32chars
DB_HOST: localhost
DB_NAME: evig_ci
DB_USER: ci
DB_PASSWORD: ci
- name: Write selfhost env
if: steps.secrets.outputs.configured == 'true'
run: |
printf '%s' "$SELFHOST_ENV" > .env.selfhost.local
chmod 600 .env.selfhost.local
- name: Setup SSH
if: steps.secrets.outputs.configured == 'true'
run: |
mkdir -p ~/.ssh
printf '%s\n' "$HETZNER_SSH_PRIVATE_KEY" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H 167.233.22.31 >> ~/.ssh/known_hosts
- name: Deploy
if: steps.secrets.outputs.configured == 'true'
run: bash scripts/selfhost-deploy-evig.sh
- name: Mark deployed
id: mark-deployed
if: steps.secrets.outputs.configured == 'true'
run: echo "deployed=true" >> "$GITHUB_OUTPUT"
post-deploy-smoke:
name: Read-only prod smoke
runs-on: ubuntu-latest
needs: deploy
if: needs.deploy.outputs.deployed == 'true'
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v7
# Read-only: public pages render + DB-backed public APIs return success.
# No login, no prod mutation. The heavier authenticated dual-persona
# journeys (which mutate prod) are manual: npm run test:e2e:inventory:prod.
- name: Run read-only smoke (public pages + APIs)
run: bash scripts/post-deploy-smoke.sh