-
Notifications
You must be signed in to change notification settings - Fork 1
159 lines (153 loc) · 5.48 KB
/
deploy-docs.yaml
File metadata and controls
159 lines (153 loc) · 5.48 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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
name: Deploy Docs
on:
push:
branches: [main, develop]
paths:
- "apps/docs/**"
- "packages/**"
- "bun.lock"
- "package.json"
- ".github/workflows/deploy-docs.yaml"
pull_request:
types: [opened, reopened, synchronize, closed]
paths:
- "apps/docs/**"
- "packages/**"
- "bun.lock"
- "package.json"
- ".github/workflows/deploy-docs.yaml"
workflow_dispatch:
inputs:
stage:
description: "Stage to deploy (e.g. production, staging, pr-42)"
required: true
type: string
concurrency:
group: deploy-docs-${{ github.ref }}
cancel-in-progress: false
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
jobs:
stage:
runs-on: ubuntu-latest
outputs:
stage: ${{ steps.resolve.outputs.stage }}
action: ${{ steps.resolve.outputs.action }}
steps:
- id: resolve
run: |
set -euo pipefail
event="${{ github.event_name }}"
if [[ "$event" == "workflow_dispatch" ]]; then
stage="${{ inputs.stage }}"
action="deploy"
elif [[ "$event" == "push" ]]; then
case "${{ github.ref_name }}" in
main) stage="production" ;;
develop) stage="staging" ;;
*) echo "Unexpected ref ${{ github.ref_name }}" >&2; exit 1 ;;
esac
action="deploy"
elif [[ "$event" == "pull_request" ]]; then
stage="pr-${{ github.event.pull_request.number }}"
if [[ "${{ github.event.action }}" == "closed" ]]; then
action="destroy"
else
action="deploy"
fi
fi
echo "stage=$stage" >> "$GITHUB_OUTPUT"
echo "action=$action" >> "$GITHUB_OUTPUT"
echo "Resolved: $action stage=$stage"
deploy:
needs: stage
if: needs.stage.outputs.action == 'deploy'
runs-on: ubuntu-latest
environment:
name: docs-${{ needs.stage.outputs.stage }}
url: ${{ steps.deploy.outputs.url }}
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
# Persist alchemy's LocalState (.alchemy/state/<stack>/<stage>/) across
# runners so the destroy job on PR close can find the resources to tear down.
# alchemy currently ships only a filesystem state backend; without this
# cache, every runner starts blind and orphans Workers.
- name: Restore alchemy state
uses: actions/cache@v4
with:
path: apps/docs/.alchemy
key: alchemy-state-docs-${{ needs.stage.outputs.stage }}
- name: Build worker
working-directory: apps/docs
run: |
set -euo pipefail
bun run build:worker
- name: Deploy
id: deploy
working-directory: apps/docs
env:
STAGE: ${{ needs.stage.outputs.stage }}
# SOPS AGE key used by `loadAppEnv` (sops-age) to decrypt the
# generated per-app payloads. `production` uses the prod key;
# everything else (staging, pr-*, dev) uses the dev key.
# All stages encrypt with the github_actions age recipient
# (SECRETS_AGE_KEY_DEV's pubkey). The previous prod/dev split
# was stale — SECRETS_AGE_KEY_PROD's pubkey isn't on the prod
# payloads, so production deploys failed at decrypt time.
SOPS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY_DEV }}
run: |
set -euo pipefail
bunx alchemy deploy --stage ${{ needs.stage.outputs.stage }} --yes
- name: Comment preview URL on PR
if: github.event_name == 'pull_request'
uses: marocchino/sticky-pull-request-comment@v2
with:
header: preview-docs
message: |
Docs preview deployed to `${{ needs.stage.outputs.stage }}`
destroy:
needs: stage
if: needs.stage.outputs.action == 'destroy'
runs-on: ubuntu-latest
permissions:
actions: write # gh cache delete
contents: read
pull-requests: write # sticky comment
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
- name: Install dependencies
run: bun install --frozen-lockfile
# Restore-only — we never want the destroy job's post-step to write the
# (now-empty) state back to the cache.
- name: Restore alchemy state
uses: actions/cache/restore@v4
with:
path: apps/docs/.alchemy
key: alchemy-state-docs-${{ needs.stage.outputs.stage }}
- name: Destroy PR preview
working-directory: apps/docs
env:
STAGE: ${{ needs.stage.outputs.stage }}
# PR previews always use the dev key.
SOPS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY_DEV }}
run: |
set -euo pipefail
bunx alchemy destroy --stage ${{ needs.stage.outputs.stage }} --yes
- name: Delete cached alchemy state
if: always()
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh cache delete "alchemy-state-docs-${{ needs.stage.outputs.stage }}" \
--repo "${{ github.repository }}" || true
- name: Mark preview comment as torn down
uses: marocchino/sticky-pull-request-comment@v2
with:
header: preview-docs
message: |
Docs preview `${{ needs.stage.outputs.stage }}` has been destroyed.