@crossdelta/platform-sdk 0.22.0 โ 0.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/bin/cli.mjs +110 -109
- package/bin/templates/workspace/.claude/rules/cloudevents.md +85 -0
- package/bin/templates/workspace/.github/README.md +67 -0
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +27 -0
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +179 -0
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +21 -0
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +370 -0
- package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +167 -0
- package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +57 -0
- package/bin/templates/workspace/.github/dependabot.yml +18 -0
- package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +409 -0
- package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +83 -0
- package/bin/templates/workspace/.github/workflows/publish-packages.yml +228 -0
- package/bin/templates/workspace/.vscode/extensions.json +8 -0
- package/bin/templates/workspace/.vscode/settings.json +20 -0
- package/bin/templates/workspace/apps/.gitkeep +0 -0
- package/bin/templates/workspace/docs/.gitkeep +0 -0
- package/bin/templates/workspace/infra/services/.gitkeep +0 -0
- package/bin/templates/workspace/packages/.gitkeep +0 -0
- package/bin/templates/workspace/services/.gitkeep +0 -0
- package/package.json +1 -1
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
name: ๐ Build and Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
paths:
|
|
7
|
+
- 'apps/*/**'
|
|
8
|
+
- 'services/*/**'
|
|
9
|
+
- 'infra/**'
|
|
10
|
+
- 'bun.lock'
|
|
11
|
+
- 'bunfig.toml'
|
|
12
|
+
- '.github/workflows/**'
|
|
13
|
+
workflow_run:
|
|
14
|
+
workflows: ['๐ฆ Publish Packages']
|
|
15
|
+
types: [completed]
|
|
16
|
+
branches: [main]
|
|
17
|
+
workflow_dispatch:
|
|
18
|
+
inputs:
|
|
19
|
+
environment:
|
|
20
|
+
description: 'Target environment'
|
|
21
|
+
required: true
|
|
22
|
+
default: 'stage'
|
|
23
|
+
type: choice
|
|
24
|
+
options:
|
|
25
|
+
- stage
|
|
26
|
+
- production
|
|
27
|
+
|
|
28
|
+
permissions:
|
|
29
|
+
contents: read
|
|
30
|
+
packages: write
|
|
31
|
+
|
|
32
|
+
env:
|
|
33
|
+
SCOPE_ROOTS: apps,services
|
|
34
|
+
REGISTRY: ghcr.io/$\{{ github.repository_owner }}/$\{{ github.event.repository.name }}
|
|
35
|
+
PULUMI_STACK: $\{{ vars.PULUMI_STACK_BASE }}/$\{{ github.event.inputs.environment || 'stage' }}
|
|
36
|
+
WORKFLOW_HEAD_SHA: $\{{ github.event.workflow_run.head_sha || github.sha }}
|
|
37
|
+
WORKFLOW_BASE_SHA_INPUT: $\{{ github.event.before || '' }}
|
|
38
|
+
|
|
39
|
+
jobs:
|
|
40
|
+
gate:
|
|
41
|
+
name: ๐ฆ Publish Gate
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
outputs:
|
|
44
|
+
proceed: $\{{ steps.decide.outputs.proceed }}
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/checkout@v6
|
|
47
|
+
if: github.event_name == 'push'
|
|
48
|
+
with:
|
|
49
|
+
fetch-depth: 0
|
|
50
|
+
|
|
51
|
+
- name: Check for package changes
|
|
52
|
+
if: github.event_name == 'push'
|
|
53
|
+
id: pkg-check
|
|
54
|
+
run: |
|
|
55
|
+
BASE="$\{{ github.event.before }}"
|
|
56
|
+
NULL_SHA="0000000000000000000000000000000000000000"
|
|
57
|
+
if [ -z "$BASE" ] || [ "$BASE" = "$NULL_SHA" ]; then
|
|
58
|
+
BASE=$(git rev-parse HEAD^ 2>/dev/null || git rev-parse HEAD)
|
|
59
|
+
fi
|
|
60
|
+
CHANGED=$(git diff --name-only "$BASE" "$\{{ github.sha }}" -- packages/ | head -1)
|
|
61
|
+
if [ -n "$CHANGED" ]; then
|
|
62
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
63
|
+
else
|
|
64
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
- name: Decide whether to proceed
|
|
68
|
+
id: decide
|
|
69
|
+
run: |
|
|
70
|
+
EVENT="$\{{ github.event_name }}"
|
|
71
|
+
if [ "$EVENT" = "workflow_run" ]; then
|
|
72
|
+
CONCLUSION="$\{{ github.event.workflow_run.conclusion }}"
|
|
73
|
+
if [ "$CONCLUSION" = "success" ]; then
|
|
74
|
+
echo "โ
Publish completed โ proceeding with build & deploy"
|
|
75
|
+
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
|
76
|
+
else
|
|
77
|
+
echo "โ Publish workflow $CONCLUSION โ skipping"
|
|
78
|
+
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
|
79
|
+
fi
|
|
80
|
+
elif [ "$EVENT" = "push" ]; then
|
|
81
|
+
if [ "$\{{ steps.pkg-check.outputs.changed }}" = "true" ]; then
|
|
82
|
+
echo "๐ฆ Package changes detected โ deferring to publish workflow"
|
|
83
|
+
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
|
84
|
+
else
|
|
85
|
+
echo "โ
No package changes โ proceeding"
|
|
86
|
+
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
|
87
|
+
fi
|
|
88
|
+
else
|
|
89
|
+
echo "โ
Manual dispatch โ proceeding"
|
|
90
|
+
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
prepare-scopes:
|
|
94
|
+
name: ๐งช Prepare Scopes
|
|
95
|
+
needs: gate
|
|
96
|
+
if: needs.gate.outputs.proceed == 'true'
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
outputs:
|
|
99
|
+
scopes: $\{{ steps.scope-generator.outputs.scopes }}
|
|
100
|
+
scope_count: $\{{ steps.scope-generator.outputs.scopes_count }}
|
|
101
|
+
infrastructure_changed: $\{{ steps.infra-eval.outputs.changed }}
|
|
102
|
+
steps:
|
|
103
|
+
- uses: actions/checkout@v6
|
|
104
|
+
with:
|
|
105
|
+
fetch-depth: 0
|
|
106
|
+
ref: $\{{ env.WORKFLOW_HEAD_SHA }}
|
|
107
|
+
|
|
108
|
+
- name: Set commit range
|
|
109
|
+
run: |
|
|
110
|
+
set -euo pipefail
|
|
111
|
+
HEAD_SHA="${WORKFLOW_HEAD_SHA}"
|
|
112
|
+
BASE_SHA="${WORKFLOW_BASE_SHA_INPUT}"
|
|
113
|
+
NULL_SHA="0000000000000000000000000000000000000000"
|
|
114
|
+
if [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = "$NULL_SHA" ]; then
|
|
115
|
+
if git rev-parse "${HEAD_SHA}^" >/dev/null 2>&1; then
|
|
116
|
+
BASE_SHA=$(git rev-parse "${HEAD_SHA}^")
|
|
117
|
+
else
|
|
118
|
+
BASE_SHA="$HEAD_SHA"
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
echo "GITHUB_SHA=$HEAD_SHA" >> "$GITHUB_ENV"
|
|
122
|
+
echo "GITHUB_EVENT_BEFORE=$BASE_SHA" >> "$GITHUB_ENV"
|
|
123
|
+
|
|
124
|
+
- name: Check for infrastructure changes
|
|
125
|
+
id: infra-check
|
|
126
|
+
run: |
|
|
127
|
+
CHANGED=$(git diff --name-only "$GITHUB_EVENT_BEFORE" "$GITHUB_SHA" -- infra/ | head -1)
|
|
128
|
+
if [ -n "$CHANGED" ]; then
|
|
129
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
130
|
+
else
|
|
131
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
- name: Evaluate infrastructure change
|
|
135
|
+
id: infra-eval
|
|
136
|
+
run: |
|
|
137
|
+
if [ "$\{{ github.event_name }}" = "workflow_run" ] || [ "$\{{ github.event_name }}" = "workflow_dispatch" ] || [ "$\{{ steps.infra-check.outputs.changed }}" = "true" ]; then
|
|
138
|
+
echo "changed=true" >> "$GITHUB_OUTPUT"
|
|
139
|
+
else
|
|
140
|
+
echo "changed=false" >> "$GITHUB_OUTPUT"
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
- name: Generate scope matrix
|
|
144
|
+
id: scope-generator
|
|
145
|
+
uses: ./.github/actions/generate-scope-matrix
|
|
146
|
+
with:
|
|
147
|
+
scope-roots: $\{{ env.SCOPE_ROOTS }}
|
|
148
|
+
force-all: $\{{ github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run' }}
|
|
149
|
+
|
|
150
|
+
build:
|
|
151
|
+
if: needs.prepare-scopes.outputs.scope_count && needs.prepare-scopes.outputs.scope_count != '0'
|
|
152
|
+
needs: prepare-scopes
|
|
153
|
+
runs-on: ubuntu-latest
|
|
154
|
+
strategy:
|
|
155
|
+
matrix:
|
|
156
|
+
scope: $\{{ fromJson(needs.prepare-scopes.outputs.scopes) }}
|
|
157
|
+
name: ๐ณ Build $\{{ matrix.scope.shortName }}
|
|
158
|
+
environment: $\{{ github.event.inputs.environment || 'stage' }}
|
|
159
|
+
env:
|
|
160
|
+
DOCKER_BUILDKIT: 1
|
|
161
|
+
IMAGE_TAG: $\{{ github.sha }}
|
|
162
|
+
|
|
163
|
+
steps:
|
|
164
|
+
- uses: actions/checkout@v6
|
|
165
|
+
with:
|
|
166
|
+
fetch-depth: 2
|
|
167
|
+
ref: $\{{ env.WORKFLOW_HEAD_SHA }}
|
|
168
|
+
|
|
169
|
+
- name: Extract Bun version from package.json
|
|
170
|
+
run: echo "BUN_VERSION=$(jq -r '.packageManager // empty' package.json | sed 's/bun@//')" >> $GITHUB_ENV
|
|
171
|
+
|
|
172
|
+
- name: Set scope metadata
|
|
173
|
+
run: |
|
|
174
|
+
echo "SCOPE_DIR=$\{{ matrix.scope.dir }}" >> $GITHUB_ENV
|
|
175
|
+
echo "scope_name=$\{{ matrix.scope.shortName }}" >> $GITHUB_OUTPUT
|
|
176
|
+
|
|
177
|
+
- name: Setup Bun and install dependencies
|
|
178
|
+
uses: ./.github/actions/setup-bun-install
|
|
179
|
+
with:
|
|
180
|
+
enable-cache: 'true'
|
|
181
|
+
cache-key: bun-v4-$\{{ runner.os }}-$\{{ hashFiles('bun.lock', 'bunfig.toml') }}
|
|
182
|
+
cache-restore-keys: bun-v4-$\{{ runner.os }}-
|
|
183
|
+
npm-token: $\{{ github.token }}
|
|
184
|
+
|
|
185
|
+
- name: Prune
|
|
186
|
+
run: |
|
|
187
|
+
bunx turbo prune --scope=$\{{ matrix.scope.name }} --docker --out-dir out/$\{{ matrix.scope.shortName }}
|
|
188
|
+
# turbo prune omits the bun patchedDependencies patch files. Copy them into the
|
|
189
|
+
# pruned context so `bun install` can apply them (rxdb patch for studio/storefront).
|
|
190
|
+
# Done before the scope checksum so a patch change busts the image tag.
|
|
191
|
+
if [ -d patches ]; then
|
|
192
|
+
cp -rT patches "out/$\{{ matrix.scope.shortName }}/full/patches"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
- name: Compute scope checksum
|
|
196
|
+
id: scope-checksum
|
|
197
|
+
run: |
|
|
198
|
+
set -euo pipefail
|
|
199
|
+
cd "out/$\{{ matrix.scope.shortName }}"
|
|
200
|
+
HASH=$(find . -type f -print0 | sort -z | xargs -0 sha256sum | sha256sum)
|
|
201
|
+
HASH=${HASH%% *}
|
|
202
|
+
echo "scope_hash=$HASH" >> $GITHUB_OUTPUT
|
|
203
|
+
|
|
204
|
+
- name: Use scope checksum as image tag
|
|
205
|
+
run: echo "IMAGE_TAG=$\{{ steps.scope-checksum.outputs.scope_hash }}" >> $GITHUB_ENV
|
|
206
|
+
|
|
207
|
+
- name: Check if image tag exists
|
|
208
|
+
id: tag-check
|
|
209
|
+
uses: ./.github/actions/check-image-tag-exists
|
|
210
|
+
with:
|
|
211
|
+
scope-short-name: $\{{ matrix.scope.shortName }}
|
|
212
|
+
image-tag: $\{{ env.IMAGE_TAG }}
|
|
213
|
+
github-token: $\{{ secrets.GITHUB_TOKEN }}
|
|
214
|
+
|
|
215
|
+
- name: Set up Docker Buildx
|
|
216
|
+
if: steps.tag-check.outputs.exists != 'true'
|
|
217
|
+
uses: docker/setup-buildx-action@v4
|
|
218
|
+
|
|
219
|
+
- name: Login to GitHub Packages (GHCR)
|
|
220
|
+
if: steps.tag-check.outputs.exists != 'true'
|
|
221
|
+
uses: docker/login-action@v4
|
|
222
|
+
with:
|
|
223
|
+
registry: ghcr.io
|
|
224
|
+
username: $\{{ github.actor }}
|
|
225
|
+
password: $\{{ secrets.GITHUB_TOKEN }}
|
|
226
|
+
|
|
227
|
+
- name: Prepare build context
|
|
228
|
+
if: steps.tag-check.outputs.exists != 'true'
|
|
229
|
+
id: prepare-context
|
|
230
|
+
uses: ./.github/actions/prepare-build-context
|
|
231
|
+
with:
|
|
232
|
+
scope-short-name: $\{{ matrix.scope.shortName }}
|
|
233
|
+
scope-dir: $\{{ matrix.scope.dir }}
|
|
234
|
+
npm-token: $\{{ github.token }}
|
|
235
|
+
|
|
236
|
+
- name: Build and push image
|
|
237
|
+
if: steps.tag-check.outputs.exists != 'true'
|
|
238
|
+
uses: docker/build-push-action@v7
|
|
239
|
+
env:
|
|
240
|
+
NPM_TOKEN: $\{{ github.token }}
|
|
241
|
+
with:
|
|
242
|
+
context: out/$\{{ matrix.scope.shortName }}/full
|
|
243
|
+
file: out/$\{{ matrix.scope.shortName }}/full/$\{{ steps.prepare-context.outputs.dockerfile }}
|
|
244
|
+
push: true
|
|
245
|
+
tags: |
|
|
246
|
+
$\{{ env.REGISTRY }}/$\{{ matrix.scope.shortName }}:$\{{ env.IMAGE_TAG }}
|
|
247
|
+
$\{{ env.REGISTRY }}/$\{{ matrix.scope.shortName }}:latest
|
|
248
|
+
cache-from: type=gha,scope=$\{{ matrix.scope.shortName }}
|
|
249
|
+
cache-to: type=gha,scope=$\{{ matrix.scope.shortName }},mode=max
|
|
250
|
+
build-args: |
|
|
251
|
+
BUN_VERSION=$\{{ env.BUN_VERSION }}
|
|
252
|
+
secrets: |
|
|
253
|
+
NPM_TOKEN=$\{{ env.NPM_TOKEN }}
|
|
254
|
+
|
|
255
|
+
- name: Mark this scope as changed
|
|
256
|
+
run: |
|
|
257
|
+
mkdir -p .changed/$\{{ matrix.scope.shortName }}
|
|
258
|
+
echo "$\{{ matrix.scope.shortName }}" > .changed/$\{{ matrix.scope.shortName }}/scope.txt
|
|
259
|
+
printf '{"shortName":"%s","imageTag":"%s"}\n' "$\{{ matrix.scope.shortName }}" "$\{{ env.IMAGE_TAG }}" > .changed/$\{{ matrix.scope.shortName }}/metadata.json
|
|
260
|
+
|
|
261
|
+
- name: Upload change marker
|
|
262
|
+
uses: actions/upload-artifact@v6
|
|
263
|
+
with:
|
|
264
|
+
name: changed-$\{{ matrix.scope.shortName }}
|
|
265
|
+
path: .changed/$\{{ matrix.scope.shortName }}
|
|
266
|
+
|
|
267
|
+
deploy:
|
|
268
|
+
runs-on: ubuntu-latest
|
|
269
|
+
concurrency:
|
|
270
|
+
group: deploy-job-$\{{ github.ref }}
|
|
271
|
+
cancel-in-progress: false
|
|
272
|
+
needs: [gate, build, prepare-scopes]
|
|
273
|
+
if: |
|
|
274
|
+
always() &&
|
|
275
|
+
needs.gate.outputs.proceed == 'true' &&
|
|
276
|
+
needs.prepare-scopes.result == 'success' &&
|
|
277
|
+
needs.build.result != 'failure' &&
|
|
278
|
+
(needs.prepare-scopes.outputs.scope_count != '0' || needs.prepare-scopes.outputs.infrastructure_changed == 'true')
|
|
279
|
+
name: ๐ Deploy ($\{{ github.event.inputs.environment || 'stage' }})
|
|
280
|
+
environment: $\{{ github.event.inputs.environment || 'stage' }}
|
|
281
|
+
steps:
|
|
282
|
+
- uses: actions/checkout@v6
|
|
283
|
+
with:
|
|
284
|
+
ref: $\{{ env.WORKFLOW_HEAD_SHA }}
|
|
285
|
+
fetch-depth: 2
|
|
286
|
+
|
|
287
|
+
- name: Download all change markers
|
|
288
|
+
uses: actions/download-artifact@v7
|
|
289
|
+
with:
|
|
290
|
+
pattern: changed-*
|
|
291
|
+
path: .artifacts
|
|
292
|
+
|
|
293
|
+
- name: Resolve scope image tags
|
|
294
|
+
id: resolve_scope_tags
|
|
295
|
+
shell: bash
|
|
296
|
+
env:
|
|
297
|
+
REGISTRY: $\{{ env.REGISTRY }}
|
|
298
|
+
GH_TOKEN: $\{{ secrets.GITHUB_TOKEN }}
|
|
299
|
+
REPO_OWNER: $\{{ github.repository_owner }}
|
|
300
|
+
REPO_PREFIX: $\{{ github.event.repository.name }}
|
|
301
|
+
run: |
|
|
302
|
+
set -euo pipefail
|
|
303
|
+
RESULT="{}"
|
|
304
|
+
|
|
305
|
+
# 1. Read markers from build job
|
|
306
|
+
if [ -d .artifacts ]; then
|
|
307
|
+
for marker_dir in .artifacts/changed-*/; do
|
|
308
|
+
[ -f "$marker_dir/metadata.json" ] || continue
|
|
309
|
+
SHORT=$(jq -r '.shortName' "$marker_dir/metadata.json")
|
|
310
|
+
TAG=$(jq -r '.imageTag' "$marker_dir/metadata.json")
|
|
311
|
+
[ -n "$SHORT" ] && [ -n "$TAG" ] && \
|
|
312
|
+
RESULT=$(echo "$RESULT" | jq --arg k "$SHORT" --arg v "$REGISTRY/$SHORT:$TAG" '. + {($k): $v}')
|
|
313
|
+
done
|
|
314
|
+
fi
|
|
315
|
+
|
|
316
|
+
# 2. For scopes without markers, query GHCR for latest checksum tag
|
|
317
|
+
MISSING=""
|
|
318
|
+
for scope_dir in apps/*/Dockerfile services/*/Dockerfile; do
|
|
319
|
+
[ -f "$scope_dir" ] || continue
|
|
320
|
+
SHORT=$(basename "$(dirname "$scope_dir")")
|
|
321
|
+
echo "$RESULT" | jq -e --arg k "$SHORT" '.[$k]' >/dev/null 2>&1 && continue
|
|
322
|
+
|
|
323
|
+
# Query GHCR for the latest version with a checksum tag (retry transient API errors)
|
|
324
|
+
CHECKSUM_TAG=""
|
|
325
|
+
for attempt in 1 2 3; do
|
|
326
|
+
CHECKSUM_TAG=$(curl -sf \
|
|
327
|
+
-H "Authorization: Bearer $GH_TOKEN" \
|
|
328
|
+
-H "Accept: application/vnd.github+json" \
|
|
329
|
+
"https://api.github.com/orgs/$REPO_OWNER/packages/container/${REPO_PREFIX}%2F${SHORT}/versions?per_page=50" \
|
|
330
|
+
| jq -r '[.[].metadata.container.tags[]? | select(test("^[0-9a-f]{64}$"))][0] // empty' 2>/dev/null || true)
|
|
331
|
+
[ -n "$CHECKSUM_TAG" ] && break
|
|
332
|
+
sleep 2
|
|
333
|
+
done
|
|
334
|
+
|
|
335
|
+
if [ -n "$CHECKSUM_TAG" ]; then
|
|
336
|
+
RESULT=$(echo "$RESULT" | jq --arg k "$SHORT" --arg v "$REGISTRY/$SHORT:$CHECKSUM_TAG" '. + {($k): $v}')
|
|
337
|
+
echo " $SHORT โ $CHECKSUM_TAG (from GHCR)"
|
|
338
|
+
else
|
|
339
|
+
MISSING="$MISSING $SHORT"
|
|
340
|
+
fi
|
|
341
|
+
done
|
|
342
|
+
|
|
343
|
+
if [ -n "$MISSING" ]; then
|
|
344
|
+
echo "::error::No checksum-tagged image found in GHCR for:$MISSING. Refusing to deploy :latest โ trigger a build for these scopes first (workflow_dispatch builds all)."
|
|
345
|
+
exit 1
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
echo "scope_image_tags=$(echo "$RESULT" | jq -c .)" >> "$GITHUB_OUTPUT"
|
|
349
|
+
echo "Resolved image tags: $RESULT"
|
|
350
|
+
|
|
351
|
+
- name: Setup Bun and install dependencies
|
|
352
|
+
uses: ./.github/actions/setup-bun-install
|
|
353
|
+
with:
|
|
354
|
+
enable-cache: 'true'
|
|
355
|
+
cache-key: bun-v4-$\{{ runner.os }}-$\{{ hashFiles('bun.lock', 'bunfig.toml') }}
|
|
356
|
+
cache-restore-keys: bun-v4-$\{{ runner.os }}-
|
|
357
|
+
npm-token: $\{{ github.token }}
|
|
358
|
+
|
|
359
|
+
- name: Build infrastructure dependencies
|
|
360
|
+
run: |
|
|
361
|
+
bunx turbo run build --filter=@crossdelta/infrastructure --filter={{scope}}/contracts
|
|
362
|
+
|
|
363
|
+
- name: Pulumi up
|
|
364
|
+
uses: pulumi/actions@v7
|
|
365
|
+
timeout-minutes: 5
|
|
366
|
+
with:
|
|
367
|
+
command: up
|
|
368
|
+
suppress-progress: true
|
|
369
|
+
suppress-outputs: true
|
|
370
|
+
comment-on-summary: true
|
|
371
|
+
stack-name: $\{{ env.PULUMI_STACK }}
|
|
372
|
+
work-dir: infra
|
|
373
|
+
env:
|
|
374
|
+
PULUMI_ACCESS_TOKEN: $\{{ secrets.PULUMI_ACCESS_TOKEN }}
|
|
375
|
+
DIGITALOCEAN_TOKEN: $\{{ secrets.DIGITALOCEAN_TOKEN }}
|
|
376
|
+
SCOPE_IMAGE_TAGS: $\{{ steps.resolve_scope_tags.outputs.scope_image_tags }}
|
|
377
|
+
|
|
378
|
+
- name: Check if Studio was deployed
|
|
379
|
+
id: studio-deployed
|
|
380
|
+
run: |
|
|
381
|
+
if [ -f .artifacts/changed-studio/metadata.json ]; then
|
|
382
|
+
echo "deployed=true" >> "$GITHUB_OUTPUT"
|
|
383
|
+
else
|
|
384
|
+
echo "deployed=false" >> "$GITHUB_OUTPUT"
|
|
385
|
+
fi
|
|
386
|
+
|
|
387
|
+
- name: Get Pulumi outputs
|
|
388
|
+
id: pulumi-outputs
|
|
389
|
+
if: steps.studio-deployed.outputs.deployed == 'true'
|
|
390
|
+
working-directory: infra
|
|
391
|
+
run: |
|
|
392
|
+
URL=$(pulumi stack output studioUrl --stack "$PULUMI_STACK" --non-interactive)
|
|
393
|
+
echo "studio_url=${URL}" >> "$GITHUB_OUTPUT"
|
|
394
|
+
env:
|
|
395
|
+
PULUMI_ACCESS_TOKEN: $\{{ secrets.PULUMI_ACCESS_TOKEN }}
|
|
396
|
+
|
|
397
|
+
- name: Smoke-check new Studio deployment
|
|
398
|
+
id: studio-smoke
|
|
399
|
+
if: steps.studio-deployed.outputs.deployed == 'true'
|
|
400
|
+
env:
|
|
401
|
+
STUDIO_URL: $\{{ steps.pulumi-outputs.outputs.studio_url }}
|
|
402
|
+
run: |
|
|
403
|
+
curl_opts=(-sf)
|
|
404
|
+
for i in $(seq 1 30); do
|
|
405
|
+
if curl "${curl_opts[@]}" "${STUDIO_URL}/health"; then break; fi
|
|
406
|
+
sleep 2
|
|
407
|
+
done
|
|
408
|
+
curl "${curl_opts[@]}" "${STUDIO_URL}/health" || exit 1
|
|
409
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
name: Pull Request Checks
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
concurrency:
|
|
9
|
+
group: $\{{ github.workflow }}-$\{{ github.ref }}
|
|
10
|
+
cancel-in-progress: true
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
lint:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- name: Checkout code
|
|
17
|
+
uses: actions/checkout@v6
|
|
18
|
+
|
|
19
|
+
- name: Setup Bun and install dependencies
|
|
20
|
+
uses: ./.github/actions/setup-bun-install
|
|
21
|
+
with:
|
|
22
|
+
enable-cache: 'true'
|
|
23
|
+
cache-key: bun-v4-$\{{ runner.os }}-$\{{ hashFiles('bun.lock', 'bunfig.toml') }}
|
|
24
|
+
cache-restore-keys: bun-v4-$\{{ runner.os }}-
|
|
25
|
+
|
|
26
|
+
- name: Run Linter
|
|
27
|
+
run: bun run --if-present lint
|
|
28
|
+
|
|
29
|
+
test:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
env:
|
|
32
|
+
steps:
|
|
33
|
+
- name: Checkout code
|
|
34
|
+
uses: actions/checkout@v6
|
|
35
|
+
|
|
36
|
+
- name: Setup Bun and install dependencies
|
|
37
|
+
uses: ./.github/actions/setup-bun-install
|
|
38
|
+
with:
|
|
39
|
+
enable-cache: 'true'
|
|
40
|
+
cache-key: bun-v4-$\{{ runner.os }}-$\{{ hashFiles('bun.lock', 'bunfig.toml') }}
|
|
41
|
+
cache-restore-keys: bun-v4-$\{{ runner.os }}-
|
|
42
|
+
|
|
43
|
+
- name: Run Tests
|
|
44
|
+
run: bun run --if-present test
|
|
45
|
+
|
|
46
|
+
e2e:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
needs: test
|
|
49
|
+
if: false # disabled until E2E tests are stable
|
|
50
|
+
env:
|
|
51
|
+
E2E_STUDIO_EMAIL: $\{{ secrets.E2E_STUDIO_EMAIL }}
|
|
52
|
+
E2E_STUDIO_PASSWORD: $\{{ secrets.E2E_STUDIO_PASSWORD }}
|
|
53
|
+
steps:
|
|
54
|
+
- name: Checkout code
|
|
55
|
+
uses: actions/checkout@v6
|
|
56
|
+
|
|
57
|
+
- name: Setup Bun and install dependencies
|
|
58
|
+
uses: ./.github/actions/setup-bun-install
|
|
59
|
+
with:
|
|
60
|
+
enable-cache: 'true'
|
|
61
|
+
cache-key: bun-v4-$\{{ runner.os }}-$\{{ hashFiles('bun.lock', 'bunfig.toml') }}
|
|
62
|
+
cache-restore-keys: bun-v4-$\{{ runner.os }}-
|
|
63
|
+
|
|
64
|
+
- name: Cache Playwright browsers
|
|
65
|
+
id: playwright-cache
|
|
66
|
+
uses: actions/cache@v4
|
|
67
|
+
with:
|
|
68
|
+
path: ~/.cache/ms-playwright
|
|
69
|
+
key: playwright-$\{{ runner.os }}-$\{{ hashFiles('apps/studio/package.json', 'apps/storefront/package.json') }}
|
|
70
|
+
|
|
71
|
+
- name: Install Playwright Browsers
|
|
72
|
+
if: env.E2E_STUDIO_EMAIL != '' && steps.playwright-cache.outputs.cache-hit != 'true'
|
|
73
|
+
run: bunx playwright install --with-deps chromium
|
|
74
|
+
|
|
75
|
+
- name: Install Playwright deps (cached)
|
|
76
|
+
if: env.E2E_STUDIO_EMAIL != '' && steps.playwright-cache.outputs.cache-hit == 'true'
|
|
77
|
+
run: bunx playwright install-deps chromium
|
|
78
|
+
|
|
79
|
+
- name: Run E2E Tests
|
|
80
|
+
if: env.E2E_STUDIO_EMAIL != ''
|
|
81
|
+
run: bunx turbo run test.e2e --concurrency=1
|
|
82
|
+
env:
|
|
83
|
+
CI: true
|