@sidebase/base-config 0.2.1 → 0.3.0

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.
@@ -1,49 +1,42 @@
1
- # Managed by streamctl. Off by default; lands only when a repo sets
2
- # `automation: { upgradePr: true }`. See the README section "Upgrade-PR workflow".
3
- #
4
- # Needs `secrets.STREAMCTL_PR_TOKEN`: a PR opened with `GITHUB_TOKEN` does not trigger
5
- # `pull_request` workflows, so the repo's own `check` gate would never run on it.
6
- #
7
- # A repo with private dependencies must opt this file out and wire its own auth:
8
- # `files: { ".github/workflows/streamctl-upgrade.yml": "off" }`.
1
+ # Managed by streamctl. Enabled by `automation.upgradePr`.
2
+ # Repos with private dependencies should own this workflow.
3
+ # Workflow updates need the configured token with Contents, Pull requests, and Workflows write.
9
4
  name: streamctl upgrade
10
5
 
11
6
  on:
12
7
  schedule:
13
- - cron: "0 6 * * 1" # Mondays 06:00 UTC
8
+ - cron: "0 6 * * 1"
14
9
  workflow_dispatch:
15
10
 
16
- # Every PR and issue write below uses STREAMCTL_PR_TOKEN, so the default token
17
- # only needs to read the repo for checkout.
18
11
  permissions:
19
- contents: read
12
+ contents: write
13
+ issues: write
14
+ pull-requests: write
20
15
 
21
16
  env:
17
+ ALLOW_WORKFLOW_UPDATES: "${ALLOW_WORKFLOW_UPDATES}"
22
18
  NODE_VERSION: "${NODE_VERSION}"
19
+ CI_APT_PACKAGES: "${CI_APT_PACKAGES}"
20
+ # Block scalar so quotes, `#` and `: ` in the command stay literal.
21
+ PREPARE_COMMAND: >-
22
+ ${PREPARE_COMMAND}
23
23
 
24
24
  jobs:
25
25
  upgrade:
26
26
  runs-on: ubuntu-latest
27
27
  steps:
28
- # To mint an App token in-workflow instead of storing a PAT, uncomment this
29
- # and use `${{ steps.bot.outputs.token }}` in place of STREAMCTL_PR_TOKEN:
30
- #
31
- # - id: bot
32
- # uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
33
- # with:
34
- # app-id: ${{ secrets.STREAMCTL_APP_ID }}
35
- # private-key: ${{ secrets.STREAMCTL_APP_PRIVATE_KEY }}
36
-
37
28
  - name: Checkout
38
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
29
+ uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
30
+ with:
31
+ persist-credentials: false
39
32
 
40
33
  - name: Set up pnpm
41
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
34
+ uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
42
35
  with:
43
36
  run_install: false
44
37
 
45
38
  - name: Set up Node.js
46
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
39
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
47
40
  with:
48
41
  node-version: ${{ env.NODE_VERSION }}
49
42
  cache: pnpm
@@ -51,70 +44,370 @@ jobs:
51
44
  - name: Install dependencies
52
45
  run: pnpm install --frozen-lockfile
53
46
 
54
- # Exit codes: 4 = update available, 0 = up to date, 3 = repo already drifted.
47
+ - name: Capture managed-file state
48
+ run: pnpm streamctl status --json > "$RUNNER_TEMP/streamctl-status.json"
49
+
55
50
  - id: check
56
51
  name: Check for a payload update
57
52
  run: |
58
53
  set +e
59
- pnpm streamctl check --fail-on outdated --json > streamctl-check.json
60
- echo "code=$?" >> "$GITHUB_OUTPUT"
54
+ pnpm streamctl check --fail-on any --json > "$RUNNER_TEMP/streamctl-check.json"
55
+ code=$?
61
56
  set -e
62
- cat streamctl-check.json
57
+ echo "code=$code" >> "$GITHUB_OUTPUT"
58
+ echo "target=$(jq -r '.error.details.updateAvailable.latest // empty' "$RUNNER_TEMP/streamctl-check.json")" >> "$GITHUB_OUTPUT"
59
+ cat "$RUNNER_TEMP/streamctl-check.json"
60
+
61
+ - name: Fail on an unexpected check result
62
+ if: ${{ steps.check.outputs.code != '0' && steps.check.outputs.code != '3' && steps.check.outputs.code != '4' }}
63
+ run: exit 1
63
64
 
64
- - name: Open a drift issue (repo already diverged)
65
+ - name: Require an update target
66
+ if: ${{ steps.check.outputs.code == '4' }}
67
+ env:
68
+ TARGET: ${{ steps.check.outputs.target }}
69
+ run: test -n "$TARGET"
70
+
71
+ - name: Ensure workflow labels
72
+ if: ${{ steps.check.outputs.code == '3' || steps.check.outputs.code == '4' }}
73
+ env:
74
+ GH_TOKEN: ${{ github.token }}
75
+ run: |
76
+ gh label create streamctl --color 0E8A16 --description "Managed by streamctl" --force
77
+ gh label create needs-interactive-upgrade --color D93F0B --description "Manual upgrade work required" --force
78
+
79
+ - name: Require the PR token
80
+ if: ${{ steps.check.outputs.code == '4' && env.ALLOW_WORKFLOW_UPDATES == 'true' }}
81
+ env:
82
+ PR_TOKEN: ${{ secrets.${PR_TOKEN_SECRET} }}
83
+ run: test -n "$PR_TOKEN"
84
+
85
+ - name: Open or update the drift issue
65
86
  if: ${{ steps.check.outputs.code == '3' }}
66
87
  env:
67
- GH_TOKEN: ${{ secrets.STREAMCTL_PR_TOKEN }}
88
+ GH_TOKEN: ${{ github.token }}
68
89
  run: |
69
- gh issue create \
70
- --title "streamctl: managed files have drifted" \
71
- --label streamctl \
72
- --body "\`streamctl check\` reports pre-existing drift, so the automated upgrade was skipped. Run \`pnpm streamctl sync\` locally, commit the result, then re-run this workflow."
90
+ title="streamctl: managed files have drifted"
91
+ body="The automated upgrade was skipped because managed files already differ. Run \`pnpm streamctl sync\` locally, commit the result, then rerun this workflow. Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
92
+ issue_number=$(gh issue list --state open --label streamctl --search "\"$title\" in:title" --json number --jq '.[0].number // empty')
93
+ if [[ -n "$issue_number" ]]; then
94
+ gh issue edit "$issue_number" --body "$body"
95
+ else
96
+ gh issue create --title "$title" --label streamctl --body "$body"
97
+ fi
73
98
 
74
- # Only upgrade when an update is actually available (check exit 4).
75
99
  - id: upgrade
76
100
  name: Upgrade the payload
77
101
  if: ${{ steps.check.outputs.code == '4' }}
102
+ env:
103
+ CI: "false"
104
+ TARGET: ${{ steps.check.outputs.target }}
78
105
  run: |
79
106
  set +e
80
- pnpm streamctl upgrade --json > streamctl-upgrade.json
81
- echo "code=$?" >> "$GITHUB_OUTPUT"
107
+ pnpm streamctl upgrade --to "$TARGET" --yes --json > "$RUNNER_TEMP/streamctl-upgrade.raw"
108
+ code=$?
82
109
  set -e
83
- cat streamctl-upgrade.json
110
+ tail -n 1 "$RUNNER_TEMP/streamctl-upgrade.raw" > "$RUNNER_TEMP/streamctl-upgrade.json"
111
+ echo "code=$code" >> "$GITHUB_OUTPUT"
112
+ cat "$RUNNER_TEMP/streamctl-upgrade.raw"
113
+
114
+ - name: Fail on an unexpected upgrade result
115
+ if: ${{ steps.upgrade.outputs.code != '' && steps.upgrade.outputs.code != '0' && steps.upgrade.outputs.code != '2' }}
116
+ run: exit 1
84
117
 
85
- # Exit 2 means the sync hit conflicts and rolled back. Bump only the pin and
86
- # devDependency so the PR carries a diff a human reconciles interactively.
87
- - name: Prepare a plan-only bump (interactive sync required)
118
+ - id: conflicts
119
+ name: Classify upgrade conflicts
88
120
  if: ${{ steps.upgrade.outputs.code == '2' }}
89
- run: pnpm streamctl upgrade --no-install --json > streamctl-upgrade.json
121
+ run: |
122
+ jq -r --slurpfile status "$RUNNER_TEMP/streamctl-status.json" '
123
+ (.error.details.conflicted // [])[]
124
+ | select((
125
+ (.kind == "edit" or .kind == "adoption")
126
+ and (.path as $path | any($status[0].data.files[];
127
+ .path == $path and .strategy == "full" and .state == "in-sync"
128
+ ))
129
+ ) | not)
130
+ | .path
131
+ ' "$RUNNER_TEMP/streamctl-upgrade.json" | sort -u > "$RUNNER_TEMP/streamctl-unsafe.txt"
132
+ if [[ ! -s "$RUNNER_TEMP/streamctl-unsafe.txt" ]] && jq -e '
133
+ .error.code == "CONFLICTS_PENDING"
134
+ and .error.details.rolledBack == true
135
+ and (.error.details.conflicted | length > 0)
136
+ ' "$RUNNER_TEMP/streamctl-upgrade.json" > /dev/null; then
137
+ echo "safe=true" >> "$GITHUB_OUTPUT"
138
+ else
139
+ echo "safe=false" >> "$GITHUB_OUTPUT"
140
+ fi
141
+
142
+ - id: force
143
+ name: Apply safe generated replacements
144
+ if: ${{ steps.conflicts.outputs.safe == 'true' }}
145
+ env:
146
+ CI: "false"
147
+ TARGET: ${{ steps.check.outputs.target }}
148
+ run: |
149
+ set +e
150
+ pnpm streamctl upgrade --to "$TARGET" --force --json > "$RUNNER_TEMP/streamctl-force.raw"
151
+ code=$?
152
+ set -e
153
+ tail -n 1 "$RUNNER_TEMP/streamctl-force.raw" > "$RUNNER_TEMP/streamctl-force.json"
154
+ echo "code=$code" >> "$GITHUB_OUTPUT"
155
+ cat "$RUNNER_TEMP/streamctl-force.raw"
156
+
157
+ - name: Fail when safe replacement fails
158
+ if: ${{ steps.conflicts.outputs.safe == 'true' && steps.force.outputs.code != '0' }}
159
+ run: exit 1
160
+
161
+ - id: outcome
162
+ name: Record the upgrade outcome
163
+ if: ${{ steps.check.outputs.code == '4' }}
164
+ env:
165
+ FORCE_CODE: ${{ steps.force.outputs.code }}
166
+ UPGRADE_CODE: ${{ steps.upgrade.outputs.code }}
167
+ run: |
168
+ if [[ "$UPGRADE_CODE" == "0" || "$FORCE_CODE" == "0" ]]; then
169
+ echo "completed=true" >> "$GITHUB_OUTPUT"
170
+ else
171
+ echo "completed=false" >> "$GITHUB_OUTPUT"
172
+ fi
173
+
174
+ - id: workflow_changes
175
+ name: Exclude workflow changes by default
176
+ if: ${{ steps.outcome.outputs.completed == 'true' }}
177
+ run: |
178
+ jq -r '
179
+ (.data.plan // .error.details.plan // [])[]
180
+ | .path
181
+ | select(startswith(".github/workflows/"))
182
+ ' "$RUNNER_TEMP/streamctl-upgrade.json" | sort -u > "$RUNNER_TEMP/streamctl-workflows.txt"
183
+ echo "blocked=false" >> "$GITHUB_OUTPUT"
184
+ if [[ "$ALLOW_WORKFLOW_UPDATES" == "true" || ! -s "$RUNNER_TEMP/streamctl-workflows.txt" ]]; then
185
+ exit 0
186
+ fi
187
+ while IFS= read -r path; do
188
+ if git cat-file -e "HEAD:$path" 2>/dev/null; then
189
+ git restore --source=HEAD --worktree -- "$path"
190
+ else
191
+ rm -f -- "$path"
192
+ fi
193
+ done < "$RUNNER_TEMP/streamctl-workflows.txt"
194
+ echo "blocked=true" >> "$GITHUB_OUTPUT"
195
+
196
+ - id: manual
197
+ name: Prepare the manual upgrade
198
+ if: ${{ steps.upgrade.outputs.code == '2' && steps.conflicts.outputs.safe == 'false' }}
199
+ env:
200
+ CI: "false"
201
+ TARGET: ${{ steps.check.outputs.target }}
202
+ run: |
203
+ set +e
204
+ pnpm streamctl upgrade --to "$TARGET" --force --json
205
+ code=$?
206
+ set -e
207
+ # Faults and malformed files cannot be forced; fall back to the pin bump.
208
+ if [[ "$code" != "0" ]]; then
209
+ pnpm streamctl upgrade --to "$TARGET" --no-install --json
210
+ pnpm install --lockfile-only --ignore-scripts
211
+ echo "applied=false" >> "$GITHUB_OUTPUT"
212
+ exit 0
213
+ fi
214
+ : > "$RUNNER_TEMP/streamctl-workflows.txt"
215
+ if [[ "$ALLOW_WORKFLOW_UPDATES" != "true" ]]; then
216
+ jq -r '
217
+ (.data.plan // .error.details.plan // [])[]
218
+ | .path
219
+ | select(startswith(".github/workflows/"))
220
+ ' "$RUNNER_TEMP/streamctl-upgrade.json" | sort -u > "$RUNNER_TEMP/streamctl-workflows.txt"
221
+ fi
222
+ sort -u "$RUNNER_TEMP/streamctl-unsafe.txt" "$RUNNER_TEMP/streamctl-workflows.txt" > "$RUNNER_TEMP/streamctl-held.txt"
223
+ while IFS= read -r path; do
224
+ if git cat-file -e "HEAD:$path" 2>/dev/null; then
225
+ git restore --source=HEAD --worktree -- "$path"
226
+ else
227
+ rm -f -- "$path"
228
+ fi
229
+ done < "$RUNNER_TEMP/streamctl-held.txt"
230
+ echo "applied=true" >> "$GITHUB_OUTPUT"
231
+
232
+ - name: Install apt packages
233
+ if: ${{ steps.outcome.outputs.completed == 'true' && env.CI_APT_PACKAGES != '' }}
234
+ run: sudo apt-get update && sudo apt-get install -y --no-install-recommends $CI_APT_PACKAGES
90
235
 
91
- - name: Open the upgrade PR (clean reconcile)
92
- if: ${{ steps.upgrade.outputs.code == '0' }}
93
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
236
+ - name: Prepare the validation
237
+ if: ${{ steps.outcome.outputs.completed == 'true' && env.PREPARE_COMMAND != '' }}
238
+ run: eval "$PREPARE_COMMAND"
239
+
240
+ - name: Validate the completed upgrade
241
+ if: ${{ steps.outcome.outputs.completed == 'true' && steps.workflow_changes.outputs.blocked == 'false' }}
242
+ run: |
243
+ pnpm streamctl check
244
+ pnpm run --if-present lint
245
+ pnpm run --if-present typecheck
246
+ pnpm run --if-present test
247
+ pnpm run --if-present build
248
+
249
+ - name: Validate the partial upgrade
250
+ if: ${{ steps.workflow_changes.outputs.blocked == 'true' }}
251
+ run: |
252
+ set +e
253
+ pnpm streamctl check --fail-on drift --json > "$RUNNER_TEMP/streamctl-partial-check.raw"
254
+ code=$?
255
+ set -e
256
+ tail -n 1 "$RUNNER_TEMP/streamctl-partial-check.raw" > "$RUNNER_TEMP/streamctl-partial-check.json"
257
+ test "$code" -eq 3
258
+ jq -Rsc 'split("\n") | map(select(length > 0)) | sort' \
259
+ "$RUNNER_TEMP/streamctl-workflows.txt" > "$RUNNER_TEMP/streamctl-workflows.json"
260
+ jq -e --slurpfile expected "$RUNNER_TEMP/streamctl-workflows.json" '
261
+ .error.code == "DRIFT_DETECTED"
262
+ and (.error.details.structuralFaults | length == 0)
263
+ and (.error.details.versionSkew | length == 0)
264
+ and (([.error.details.drift[].path] | sort) == $expected[0])
265
+ ' "$RUNNER_TEMP/streamctl-partial-check.json" > /dev/null
266
+ pnpm run --if-present lint
267
+ pnpm run --if-present typecheck
268
+ pnpm run --if-present test
269
+ pnpm run --if-present build
270
+
271
+ - name: Prepare the completed-upgrade description
272
+ if: ${{ steps.outcome.outputs.completed == 'true' && steps.workflow_changes.outputs.blocked == 'false' }}
273
+ env:
274
+ TARGET: ${{ steps.check.outputs.target }}
275
+ run: |
276
+ from=$(jq -r '.data.payload.pinned' "$RUNNER_TEMP/streamctl-status.json")
277
+ {
278
+ echo "## Summary"
279
+ echo
280
+ printf -- '- Updated the streamctl payload from `%s` to `%s`.\n' "$from" "$TARGET"
281
+ echo "- Reconciled all managed files."
282
+ echo "- Updated dependencies and the lockfile."
283
+ echo
284
+ echo "## Validation"
285
+ echo
286
+ echo '- `pnpm streamctl check`'
287
+ echo "- Available lint, typecheck, test, and build scripts"
288
+ } > "$RUNNER_TEMP/streamctl-pr.md"
289
+
290
+ - name: Open the completed upgrade PR
291
+ if: ${{ steps.outcome.outputs.completed == 'true' && steps.workflow_changes.outputs.blocked == 'false' }}
292
+ uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
94
293
  with:
95
- token: ${{ secrets.STREAMCTL_PR_TOKEN }}
96
- branch: chore/streamctl-upgrade
97
- commit-message: "chore: upgrade the streamctl payload"
98
- title: "chore: upgrade the streamctl payload"
99
- body: |
100
- Automated payload upgrade: the managed files were reconciled cleanly by
101
- `streamctl upgrade`. Review the diff; `streamctl-upgrade.json` (in the run
102
- logs) has the full sync summary and dependency bumps.
294
+ token: ${{ env.ALLOW_WORKFLOW_UPDATES == 'true' && secrets.${PR_TOKEN_SECRET} || github.token }}
295
+ branch: chore/streamctl-upgrade-${{ steps.check.outputs.target }}
296
+ commit-message: "chore: upgrade streamctl payload to ${{ steps.check.outputs.target }}"
297
+ title: "chore: upgrade streamctl payload to ${{ steps.check.outputs.target }}"
298
+ body-path: ${{ runner.temp }}/streamctl-pr.md
103
299
  labels: streamctl
104
300
 
105
- - name: Open a plan-only PR (conflicts need interactive sync)
106
- if: ${{ steps.upgrade.outputs.code == '2' }}
107
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
301
+ - name: Prepare the resolve instructions
302
+ if: ${{ steps.workflow_changes.outputs.blocked == 'true' || steps.manual.outputs.applied != '' }}
303
+ env:
304
+ TARGET: ${{ steps.check.outputs.target }}
305
+ run: |
306
+ branch="chore/streamctl-upgrade-$TARGET"
307
+ {
308
+ echo "## Resolve"
309
+ echo
310
+ echo '```sh'
311
+ printf 'git fetch origin %s\n' "$branch"
312
+ printf 'git switch --create %s --track origin/%s\n' "$branch" "$branch"
313
+ echo 'pnpm install --frozen-lockfile'
314
+ echo 'pnpm streamctl sync --interactive'
315
+ echo 'pnpm streamctl check'
316
+ echo 'pnpm run --if-present lint'
317
+ echo 'pnpm run --if-present typecheck'
318
+ echo 'pnpm run --if-present test'
319
+ echo 'pnpm run --if-present build'
320
+ echo 'git add .'
321
+ echo 'git commit -m "chore: finish streamctl upgrade"'
322
+ echo 'git push'
323
+ echo '```'
324
+ } > "$RUNNER_TEMP/streamctl-resolve.md"
325
+
326
+ - name: Prepare the workflow-update description
327
+ if: ${{ steps.workflow_changes.outputs.blocked == 'true' }}
328
+ env:
329
+ TARGET: ${{ steps.check.outputs.target }}
330
+ run: |
331
+ from=$(jq -r '.data.payload.pinned' "$RUNNER_TEMP/streamctl-status.json")
332
+ {
333
+ echo "## Status"
334
+ echo
335
+ echo "This update omits GitHub Actions workflow changes. Do not merge this pull request yet."
336
+ echo
337
+ printf -- '- Updated the streamctl payload from `%s` to `%s`.\n' "$from" "$TARGET"
338
+ echo "- Applied every other safe managed-file change."
339
+ echo "- Updated dependencies and the lockfile."
340
+ echo
341
+ echo "## Omitted workflow files"
342
+ echo
343
+ sed 's|^|- `|; s|$|`|' "$RUNNER_TEMP/streamctl-workflows.txt"
344
+ echo
345
+ cat "$RUNNER_TEMP/streamctl-resolve.md"
346
+ } > "$RUNNER_TEMP/streamctl-workflow-pr.md"
347
+
348
+ - name: Open the workflow-update PR
349
+ if: ${{ steps.workflow_changes.outputs.blocked == 'true' }}
350
+ uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
351
+ with:
352
+ token: ${{ env.ALLOW_WORKFLOW_UPDATES == 'true' && secrets.${PR_TOKEN_SECRET} || github.token }}
353
+ branch: chore/streamctl-upgrade-${{ steps.check.outputs.target }}
354
+ commit-message: "chore: prepare streamctl payload ${{ steps.check.outputs.target }}"
355
+ title: "chore: upgrade streamctl payload to ${{ steps.check.outputs.target }}"
356
+ body-path: ${{ runner.temp }}/streamctl-workflow-pr.md
357
+ draft: true
358
+ labels: |
359
+ streamctl
360
+ needs-interactive-upgrade
361
+
362
+ - name: Prepare the manual-upgrade description
363
+ if: ${{ steps.upgrade.outputs.code == '2' && steps.conflicts.outputs.safe == 'false' }}
364
+ env:
365
+ APPLIED: ${{ steps.manual.outputs.applied }}
366
+ TARGET: ${{ steps.check.outputs.target }}
367
+ run: |
368
+ from=$(jq -r '.data.payload.pinned' "$RUNNER_TEMP/streamctl-status.json")
369
+ {
370
+ echo "## Status"
371
+ echo
372
+ echo "This update needs manual conflict resolution. Do not merge this pull request yet."
373
+ echo
374
+ printf -- '- Updated the streamctl payload from `%s` to `%s`.\n' "$from" "$TARGET"
375
+ if [[ "$APPLIED" == "true" ]]; then
376
+ echo "- Applied every safe managed-file change."
377
+ echo "- Left conflicting managed files unchanged."
378
+ else
379
+ echo "- Did not update managed files because \`streamctl upgrade --force\` failed. See the workflow run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
380
+ fi
381
+ echo "- Updated dependencies and the lockfile."
382
+ echo
383
+ echo "## Conflicts"
384
+ echo
385
+ jq -r --arg applied "$APPLIED" --rawfile unsafe "$RUNNER_TEMP/streamctl-unsafe.txt" '
386
+ ($unsafe | split("\n")) as $held
387
+ | .error.details.conflicted[]
388
+ | select($applied != "true" or (.path | IN($held[])))
389
+ | "- `\(.path)` (`\(.kind)`): \(.reason)"
390
+ ' "$RUNNER_TEMP/streamctl-upgrade.json"
391
+ echo
392
+ if [[ -s "$RUNNER_TEMP/streamctl-workflows.txt" ]]; then
393
+ echo "## Omitted workflow files"
394
+ echo
395
+ sed 's|^|- `|; s|$|`|' "$RUNNER_TEMP/streamctl-workflows.txt"
396
+ echo
397
+ fi
398
+ cat "$RUNNER_TEMP/streamctl-resolve.md"
399
+ } > "$RUNNER_TEMP/streamctl-conflict-pr.md"
400
+
401
+ - name: Open the manual upgrade PR
402
+ if: ${{ steps.upgrade.outputs.code == '2' && steps.conflicts.outputs.safe == 'false' }}
403
+ uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11
108
404
  with:
109
- token: ${{ secrets.STREAMCTL_PR_TOKEN }}
110
- branch: chore/streamctl-upgrade
111
- commit-message: "chore: bump the streamctl payload pin (interactive sync required)"
112
- title: "chore: streamctl payload upgrade needs interactive sync"
113
- body: |
114
- The automated `streamctl upgrade` hit conflicts and rolled the sync back, so
115
- only the payload pin + devDependency were bumped here. Finish the upgrade by
116
- hand: check out this branch, run `pnpm streamctl sync --interactive`, resolve
117
- each conflict, and push.
405
+ token: ${{ env.ALLOW_WORKFLOW_UPDATES == 'true' && secrets.${PR_TOKEN_SECRET} || github.token }}
406
+ branch: chore/streamctl-upgrade-${{ steps.check.outputs.target }}
407
+ commit-message: "chore: prepare streamctl payload ${{ steps.check.outputs.target }}"
408
+ title: "chore: upgrade streamctl payload to ${{ steps.check.outputs.target }}"
409
+ body-path: ${{ runner.temp }}/streamctl-conflict-pr.md
410
+ draft: true
118
411
  labels: |
119
412
  streamctl
120
413
  needs-interactive-upgrade
@@ -15,6 +15,3 @@ prisma/migrations/dev
15
15
  /local.*
16
16
  /local.*/
17
17
  prisma/local.*
18
- # CLAUDE.md is managed by streamctl. A user-global gitignore matching the name
19
- # everywhere would silently untrack it, so force-allow it.
20
- !CLAUDE.md
@@ -16,5 +16,6 @@ minimumReleaseAgeExclude:
16
16
  - "@sidebase/*"
17
17
  onlyBuiltDependencies:
18
18
  - "@prisma/client"
19
+ - "@prisma/engines"
19
20
  - "esbuild"
20
21
  - "prisma"
@@ -10,13 +10,16 @@
10
10
  { "path": ".vscode/settings.json", "strategy": "merge", "source": "base/vscode/settings.json", "render": "editor", "projectFields": ["editor.fontSize", "editor.rulers", "editor.formatOnSave", "files.exclude", "search.exclude", "files.watcherExclude"] },
11
11
  { "path": ".vscode/extensions.json", "strategy": "merge", "source": "base/vscode/extensions.json", "projectFields": ["unwantedRecommendations"] },
12
12
  { "path": "AGENTS.md", "strategy": "full", "source": "base/AGENTS.md", "adoption": "expected" },
13
- { "path": "CLAUDE.md", "strategy": "scaffold", "source": "base/CLAUDE.md", "adoption": "expected" },
14
13
  { "path": ".github/workflows/streamctl-upgrade.yml", "strategy": "full", "source": "base/github/workflows/streamctl-upgrade.yml", "render": "automation-upgrade", "enabledBy": "automation.upgradePr", "adoption": "unexpected" }
15
14
  ],
16
15
  "renders": {
17
16
  "automation-upgrade": {
18
17
  "placeholders": {
19
- "NODE_VERSION": { "configPath": "versions.node", "default": "24.13.0", "pattern": "^[\\w.+-]+$" }
18
+ "ALLOW_WORKFLOW_UPDATES": { "configPath": "automation.allowWorkflowUpdates", "default": "false", "pattern": "^(?:true|false)$" },
19
+ "CI_APT_PACKAGES": { "configPath": "ci.aptPackages", "default": "", "pattern": "^$|^[a-z0-9][a-z0-9 +.-]*$", "join": "space" },
20
+ "NODE_VERSION": { "configPath": "versions.node", "default": "24.21.0", "pattern": "^[\\w.+-]+$" },
21
+ "PR_TOKEN_SECRET": { "configPath": "automation.prTokenSecret", "default": "STREAMCTL_PR_TOKEN", "pattern": "^(?![Gg][Ii][Tt][Hh][Uu][Bb]_)[A-Za-z_][A-Za-z0-9_]*$" },
22
+ "PREPARE_COMMAND": { "configPath": "automation.prepare", "default": "pnpm prisma generate", "pattern": "^[^\\r\\n]*$" }
20
23
  }
21
24
  },
22
25
  "editor": {
@@ -34,7 +37,11 @@
34
37
  }
35
38
  },
36
39
  "configKeys": {
40
+ "automation.allowWorkflowUpdates": "boolean",
41
+ "automation.prTokenSecret": "string",
42
+ "automation.prepare": "string",
37
43
  "automation.upgradePr": "boolean",
44
+ "ci.aptPackages": "string[]",
38
45
  "editor.i18nSourceLanguage": "string",
39
46
  "security.minimumReleaseAge": "string",
40
47
  "pnpm.onlyBuiltDependencies": "string[]",
@@ -10,10 +10,9 @@ export default defineNuxtBaseConfig({
10
10
 
11
11
  // Optional knobs, uncomment to enable:
12
12
  // ci: { unitTests: true, e2e: true, aptPackages: ['libxml2-utils'] }, // apt extras for the test/e2e jobs
13
- // versions: { node: '24.13.0', pnpm: '10.28.1' },
13
+ // versions: { node: '24.21.0', pnpm: '10.34.5' },
14
14
  // docker: { aptPackages: ['ffmpeg'] }, // extras; openssl is always installed
15
15
  // security: { minimumReleaseAge: '10080' }, // minutes before pnpm installs a release; '0' disables
16
- // pnpm: { onlyBuiltDependencies: ['sharp'] }, // extras on top of prisma + esbuild
16
+ // pnpm: { onlyBuiltDependencies: ['sharp'] }, // extras on top of the prisma packages + esbuild
17
17
  // editor: { i18nSourceLanguage: 'en' }, // i18n-ally source language (default: 'de')
18
- // eslint: { trpcGuard: true },
19
18
  })
@@ -1,3 +1,4 @@
1
+ # check=skip=JSONArgsRecommended
1
2
  # see https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
2
3
  ARG NODE_VERSION=node:${NODE_VERSION}
3
4
  ARG PNPM_VERSION=${PNPM_VERSION}
@@ -55,4 +56,4 @@ ${DOCKER_FINAL_STAGE}
55
56
  # Set docker.finalStage to "USER node" to opt back in.
56
57
 
57
58
  # start the app
58
- CMD [ "sh", "-c", "${DOCKER_START_COMMAND}" ]
59
+ CMD ${DOCKER_START_COMMAND}
@@ -26,15 +26,15 @@ jobs:
26
26
  lint-typecheck:
27
27
  runs-on: ubuntu-latest
28
28
  steps:
29
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
29
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
30
30
 
31
31
  - name: Set up pnpm
32
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
32
+ uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
33
33
  with:
34
34
  run_install: false
35
35
 
36
36
  - name: Use Node.js ${{ env.NODE_VERSION }}
37
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
37
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
38
38
  with:
39
39
  node-version: ${{ env.NODE_VERSION }}
40
40
  cache: pnpm
@@ -51,15 +51,15 @@ jobs:
51
51
  needs: lint-typecheck
52
52
  runs-on: ubuntu-latest
53
53
  steps:
54
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
54
+ - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
55
55
 
56
56
  - name: Set up pnpm
57
- uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9
57
+ uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
58
58
  with:
59
59
  run_install: false
60
60
 
61
61
  - name: Use Node.js ${{ env.NODE_VERSION }}
62
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
62
+ uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
63
63
  with:
64
64
  node-version: ${{ env.NODE_VERSION }}
65
65
  cache: pnpm