@homeflare/config 0.11.1 → 0.12.1
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/dist/repo-shape/dependabot.d.ts +11 -17
- package/dist/repo-shape/dependabot.d.ts.map +1 -1
- package/dist/repo-shape/drift.d.ts +1 -1
- package/dist/repo-shape/drift.d.ts.map +1 -1
- package/dist/repo-shape/guards.d.ts +5 -0
- package/dist/repo-shape/guards.d.ts.map +1 -1
- package/dist/repo-shape/refresh.d.ts +5 -1
- package/dist/repo-shape/refresh.d.ts.map +1 -1
- package/dist/repo-shape/render.d.ts.map +1 -1
- package/dist/repo-shape/retired.d.ts +54 -0
- package/dist/repo-shape/retired.d.ts.map +1 -0
- package/dist/repo-shape/shape.d.ts +1 -1
- package/dist/repo-shape/shape.d.ts.map +1 -1
- package/dist/repo-shape.d.ts +3 -4
- package/dist/repo-shape.d.ts.map +1 -1
- package/dist/repo-shape.js +167 -205
- package/dist/repo-shape.js.map +10 -10
- package/dist/versions.js +7 -1
- package/dist/versions.js.map +4 -4
- package/docs/repo-shape-dependabot.md +31 -43
- package/docs/repo-shape-retired.md +75 -0
- package/docs/repo-shape.md +11 -0
- package/package.json +1 -1
- package/src/repo-shape/dependabot.ts +23 -38
- package/src/repo-shape/drift.ts +19 -1
- package/src/repo-shape/guards.ts +11 -0
- package/src/repo-shape/refresh.ts +55 -2
- package/src/repo-shape/render.ts +0 -3
- package/src/repo-shape/retired.ts +113 -0
- package/src/repo-shape/shape.ts +0 -1
- package/src/repo-shape.ts +9 -4
- package/dist/repo-shape/automerge.d.ts +0 -17
- package/dist/repo-shape/automerge.d.ts.map +0 -1
- package/src/repo-shape/automerge.ts +0 -144
package/dist/repo-shape.js
CHANGED
|
@@ -1,106 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/repo-shape/dependabot.ts
|
|
3
|
-
var HOMEFLARE_GROUP = "homeflare";
|
|
4
|
-
var HOMEFLARE_PATTERN = "@homeflare/*";
|
|
5
|
-
var THIRD_PARTY_COOLDOWN_DAYS = 7;
|
|
6
|
-
function renderDependabot(shape) {
|
|
7
|
-
const bunDirs = shape.publishes ? ["/", "/packages/*"] : ["/"];
|
|
8
|
-
return `# Dependabot for ${shape.repository}.
|
|
9
|
-
#
|
|
10
|
-
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
11
|
-
# Refresh with \`bun run repo-shape:refresh\`.
|
|
12
|
-
#
|
|
13
|
-
# \u26D4 THIS FILE LIVES AT .github/dependabot.yml, NOT IN .github/workflows/. Dependabot is a
|
|
14
|
-
# platform feature, not an Action \u2014 a config placed among the workflows is silently
|
|
15
|
-
# ignored, and the symptom is simply that no pull requests ever arrive. Measured
|
|
16
|
-
# 2026-09-22: 13 of 14 HomeFlare repositories had no dependabot config at all, so their
|
|
17
|
-
# Actions and their toolchain went stale invisibly, which is exactly how nothing fails.
|
|
18
|
-
#
|
|
19
|
-
# \u2605 WHY GROUPED RATHER THAN ONE PR PER DEPENDENCY. The default opens a pull request per
|
|
20
|
-
# outdated package; that is a trickle nobody reviews properly. Each group below is a set
|
|
21
|
-
# that is either safe to take together or needs deciding together.
|
|
22
|
-
#
|
|
23
|
-
# \u2605 THE BILLING LOCK DOES NOT STOP THIS FILE. Dependabot runs as a GitHub-hosted "dynamic"
|
|
24
|
-
# workflow that GitHub's docs say "does not count towards your included GitHub Actions
|
|
25
|
-
# minutes"; measured 2026-09-22, a private estate repository's Dependabot job got a hosted
|
|
26
|
-
# runner 86 minutes after that account's ordinary hosted jobs were refused for billing.
|
|
27
|
-
# It cannot use the mini anyway: a self-hosted Dependabot runner must be Linux x64 with
|
|
28
|
-
# Docker, and the mini's runners are arm64 containers with no Docker socket.
|
|
29
|
-
version: 2
|
|
30
|
-
|
|
31
|
-
updates:
|
|
32
|
-
# \u2500\u2500 The toolchain (bun.lock) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
33
|
-
# \u26A0\uFE0F BLOCKED UPSTREAM, 2026-09-22: bun 1.4 writes bun.lock \`lockfileVersion\` 2 and
|
|
34
|
-
# Dependabot's updater bundles bun 1.3.14, which reads up to 1, so this block fails in
|
|
35
|
-
# every estate repository with "Unsupported bun.lock 'lockfileVersion' 2". The fix is
|
|
36
|
-
# dependabot/dependabot-core pull request 16071. The github-actions block is unaffected.
|
|
37
|
-
# \u26D4 ONE BUN BLOCK, SO ONE SCHEDULE. Dependabot refuses two blocks for one ecosystem and
|
|
38
|
-
# target branch whose directories overlap, so the \`homeflare\` group cannot be daily
|
|
39
|
-
# while the rest stays weekly. The block is daily; \`cooldown\` slows the rest.
|
|
40
|
-
- package-ecosystem: bun
|
|
41
|
-
directories:
|
|
42
|
-
${bunDirs.map((dir) => ` - ${dir}`).join(`
|
|
43
|
-
`)}
|
|
44
|
-
schedule:
|
|
45
|
-
# \u26A0\uFE0F Dependabot's \`daily\` is Monday to Friday; a weekend kit release lands on Monday.
|
|
46
|
-
interval: daily
|
|
47
|
-
time: '09:00'
|
|
48
|
-
timezone: America/New_York
|
|
49
|
-
# \u26D4 THE EXCLUDE IS NOT OPTIONAL. Dependabot applies a 3-day cooldown to every version
|
|
50
|
-
# update even when this key is absent, so without it a kit release would wait three
|
|
51
|
-
# days before its bump opened \u2014 and "daily" would quietly mean "three days late".
|
|
52
|
-
cooldown:
|
|
53
|
-
default-days: ${THIRD_PARTY_COOLDOWN_DAYS}
|
|
54
|
-
exclude: ['${HOMEFLARE_PATTERN}']
|
|
55
|
-
open-pull-requests-limit: 5
|
|
56
|
-
commit-message:
|
|
57
|
-
prefix: 'chore'
|
|
58
|
-
include: scope
|
|
59
|
-
labels: [dependencies]
|
|
60
|
-
groups:
|
|
61
|
-
# \u2605 FIRST, AND EVERY UPDATE TYPE. A kit release is one set of packages built to work
|
|
62
|
-
# together, so they move as one pull request; \`bun run check\` is what reads it, and
|
|
63
|
-
# .github/workflows/dependabot-automerge.yml merges it when that is green.
|
|
64
|
-
# \u26A0\uFE0F A release that changes what @homeflare/config renders fails the drift test here
|
|
65
|
-
# by design; \`bun run repo-shape:refresh\` on the branch is the one-command fix.
|
|
66
|
-
${HOMEFLARE_GROUP}:
|
|
67
|
-
patterns: ['${HOMEFLARE_PATTERN}']
|
|
68
|
-
|
|
69
|
-
# oxfmt and oxlint move together and only affect style. Minor and patch bumps are
|
|
70
|
-
# noise unless they fail CI, which is what CI is for.
|
|
71
|
-
lint-and-format:
|
|
72
|
-
patterns: ['oxfmt', 'oxlint']
|
|
73
|
-
update-types: [minor, patch]
|
|
74
|
-
|
|
75
|
-
# \u26A0\uFE0F MAJORS EXCLUDED DELIBERATELY. TypeScript majors change what typechecks;
|
|
76
|
-
# changesets majors have renamed inputs and dropped compatibility (the action's v2
|
|
77
|
-
# did both). These want reading, not merging on green.
|
|
78
|
-
build-tooling:
|
|
79
|
-
patterns: ['typescript', '@changesets/*', '@types/bun']
|
|
80
|
-
update-types: [minor, patch]
|
|
81
|
-
|
|
82
|
-
# \u2500\u2500 The workflows themselves \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
83
|
-
# \u2605 Actions go stale invisibly: nothing fails, they just keep running old code. The
|
|
84
|
-
# estate's first workflows pinned checkout@v5 (current: v7) and changesets/action@v1
|
|
85
|
-
# (current: v2, with every input renamed) \u2014 measured 2026-09-15.
|
|
86
|
-
- package-ecosystem: github-actions
|
|
87
|
-
directory: /
|
|
88
|
-
schedule:
|
|
89
|
-
interval: weekly
|
|
90
|
-
day: monday
|
|
91
|
-
time: '09:00'
|
|
92
|
-
timezone: America/New_York
|
|
93
|
-
open-pull-requests-limit: 5
|
|
94
|
-
commit-message:
|
|
95
|
-
prefix: 'ci'
|
|
96
|
-
labels: [dependencies, github-actions]
|
|
97
|
-
groups:
|
|
98
|
-
actions:
|
|
99
|
-
patterns: ['*']
|
|
100
|
-
update-types: [minor, patch]
|
|
101
|
-
`;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
2
|
// src/repo-shape/guards.ts
|
|
105
3
|
var SENTENCE = 12;
|
|
106
4
|
function requireNonEmpty(field, value) {
|
|
@@ -122,6 +20,12 @@ function requireIsoDate(value) {
|
|
|
122
20
|
}
|
|
123
21
|
return value;
|
|
124
22
|
}
|
|
23
|
+
function requireSemver(value) {
|
|
24
|
+
if (!/^\d+\.\d+\.\d+$/.test(value)) {
|
|
25
|
+
throw new Error(`repo-shape: retiredIn must be a released x.y.z, got ${JSON.stringify(value)}`);
|
|
26
|
+
}
|
|
27
|
+
return value;
|
|
28
|
+
}
|
|
125
29
|
function requireMajor(value) {
|
|
126
30
|
if (!Number.isInteger(value) || value <= 0) {
|
|
127
31
|
throw new Error(`repo-shape: node must be a positive integer major, got ${value}`);
|
|
@@ -227,92 +131,6 @@ function renderSteps(steps, depth) {
|
|
|
227
131
|
`);
|
|
228
132
|
}
|
|
229
133
|
|
|
230
|
-
// src/repo-shape/automerge.ts
|
|
231
|
-
var GROUP_BRANCH_PREFIX = `dependabot/bun/${HOMEFLARE_GROUP}-`;
|
|
232
|
-
var GROUP_BRANCH = `^${GROUP_BRANCH_PREFIX}[0-9a-f]{10}$`;
|
|
233
|
-
var HEADER = `# Arms auto-merge on Dependabot's @homeflare/* group, and on nothing else.
|
|
234
|
-
#
|
|
235
|
-
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
236
|
-
# Its input is this repository's \`repo-shape.ts\`; refresh with \`bun run repo-shape:refresh\`.
|
|
237
|
-
#
|
|
238
|
-
# \u2605 HOW A KIT RELEASE ARRIVES: the \`${HOMEFLARE_GROUP}\` group in .github/dependabot.yml opens one
|
|
239
|
-
# pull request; this arms GitHub's auto-merge on it; the ruleset's required checks decide.
|
|
240
|
-
# Nothing here merges anything itself, and merging deploys nothing.
|
|
241
|
-
# \u26D4 NO THIRD-PARTY ACTION. GitHub's own example uses dependabot/fetch-metadata, which its
|
|
242
|
-
# docs mark "not certified by GitHub"; the group is recognised by its branch name instead.
|
|
243
|
-
# \u26D4 NO REQUIRED CHECK ON THE BASE BRANCH, NO ARMING: there \`gh pr merge --auto\` would merge
|
|
244
|
-
# at once, unchecked. The job fails instead, so the pull request waits for a person.
|
|
245
|
-
# \u26A0\uFE0F A RENDERER CHANGE STILL NEEDS A PERSON. When a kit release changes what @homeflare/config
|
|
246
|
-
# renders, the bump fails \`check\` (the drift test) and never goes green. Run
|
|
247
|
-
# \`bun run repo-shape:refresh\` on Dependabot's branch and push. \u26D4 This workflow does not do
|
|
248
|
-
# it for you: a push made with GITHUB_TOKEN starts no workflow run, so a refreshed commit
|
|
249
|
-
# would never get the required checks and the pull request would wait forever.
|
|
250
|
-
`;
|
|
251
|
-
var TRIGGER = `name: dependabot auto-merge
|
|
252
|
-
|
|
253
|
-
on:
|
|
254
|
-
pull_request:
|
|
255
|
-
|
|
256
|
-
# \u26D4 NOTHING AT THE TOP; the one job asks for exactly what \`gh pr merge --auto\` needs.
|
|
257
|
-
permissions: {}
|
|
258
|
-
|
|
259
|
-
concurrency:
|
|
260
|
-
group: automerge-\${{ github.ref }}
|
|
261
|
-
cancel-in-progress: true
|
|
262
|
-
`;
|
|
263
|
-
var JOB_NOTE = ` # \u26D4 BOTH LOGINS, NOT EITHER. \`user.login\` is who opened the pull request; \`github.actor\` is
|
|
264
|
-
# who started this run. A person pushing to Dependabot's branch changes the actor, so
|
|
265
|
-
# their commit never arms anything; only Dependabot's own rebases do.`;
|
|
266
|
-
var PERMISSIONS_NOTE = ` # \u2605 WHAT GitHub'S OWN EXAMPLE GRANTS FOR THIS STEP, AND NO MORE. A run Dependabot starts gets
|
|
267
|
-
# a read-only GITHUB_TOKEN unless the workflow raises it ("Troubleshooting Dependabot on
|
|
268
|
-
# GitHub Actions" \u2192 "Changing GITHUB_TOKEN permissions").`;
|
|
269
|
-
var ARM = `set -euo pipefail
|
|
270
|
-
if [[ ! "$HEAD_REF" =~ ${GROUP_BRANCH} ]]; then
|
|
271
|
-
echo "::notice::$HEAD_REF is not the ${HOMEFLARE_GROUP} group's branch; auto-merge not armed"
|
|
272
|
-
exit 0
|
|
273
|
-
fi
|
|
274
|
-
# \u26D4 gh merges a CLEAN or UNSTABLE pull request at once rather than arming it. Only a branch
|
|
275
|
-
# rule that requires a status check keeps it BLOCKED until the checks have passed.
|
|
276
|
-
required=$(gh api "repos/$GITHUB_REPOSITORY/rules/branches/$BASE_REF" \\
|
|
277
|
-
--jq '[.[] | select(.type == "required_status_checks")] | length')
|
|
278
|
-
if [ "$required" = 0 ]; then
|
|
279
|
-
echo "::error::$BASE_REF requires no status check, so gh would merge at once; auto-merge not armed"
|
|
280
|
-
exit 1
|
|
281
|
-
fi
|
|
282
|
-
# \u2605 --squash: the house repositories allow squash merges only (declareRepoPolicy).
|
|
283
|
-
gh pr merge --auto --squash "$PR_URL"`;
|
|
284
|
-
function renderAutomerge(shape) {
|
|
285
|
-
const headRef = "${{ github.head_ref }}";
|
|
286
|
-
const baseRef = "${{ github.base_ref }}";
|
|
287
|
-
const prUrl = "${{ github.event.pull_request.html_url }}";
|
|
288
|
-
const token = "${{ secrets.GITHUB_TOKEN }}";
|
|
289
|
-
const condition = [
|
|
290
|
-
"github.event.pull_request.user.login == 'dependabot[bot]'",
|
|
291
|
-
"github.actor == 'dependabot[bot]'",
|
|
292
|
-
`startsWith(github.head_ref, '${GROUP_BRANCH_PREFIX}')`
|
|
293
|
-
].join(" && ");
|
|
294
|
-
return `${HEADER}
|
|
295
|
-
${TRIGGER}
|
|
296
|
-
jobs:
|
|
297
|
-
${JOB_NOTE}
|
|
298
|
-
arm:
|
|
299
|
-
name: arm auto-merge
|
|
300
|
-
if: ${condition}
|
|
301
|
-
runs-on: ${runsOn(shape.runner)}
|
|
302
|
-
${PERMISSIONS_NOTE}
|
|
303
|
-
permissions:
|
|
304
|
-
contents: write
|
|
305
|
-
pull-requests: write
|
|
306
|
-
steps:
|
|
307
|
-
${renderSteps([
|
|
308
|
-
{
|
|
309
|
-
env: { BASE_REF: baseRef, GH_TOKEN: token, HEAD_REF: headRef, PR_URL: prUrl },
|
|
310
|
-
name: "Arm auto-merge on the homeflare group",
|
|
311
|
-
run: ARM
|
|
312
|
-
}
|
|
313
|
-
], 3)}
|
|
314
|
-
`;
|
|
315
|
-
}
|
|
316
134
|
// src/repo-shape/ci.ts
|
|
317
135
|
var BUN_VERSION = "1.4.0";
|
|
318
136
|
var ACTIONLINT_VERSION = "1.7.12";
|
|
@@ -333,7 +151,7 @@ function runnerNote(shape) {
|
|
|
333
151
|
].join(`
|
|
334
152
|
`);
|
|
335
153
|
}
|
|
336
|
-
var
|
|
154
|
+
var HEADER = `# Pull requests: is this code correct?
|
|
337
155
|
#
|
|
338
156
|
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
339
157
|
# Its input is this repository's \`repo-shape.ts\`. Change that, then:
|
|
@@ -346,7 +164,7 @@ var HEADER2 = `# Pull requests: is this code correct?
|
|
|
346
164
|
# share a prologue are steps inside \`check\`, so the install is paid once.
|
|
347
165
|
# \u26D4 EVERY ACTION IS FIRST-PARTY OR THE VENDOR'S OWN, PINNED TO A MAJOR TAG.
|
|
348
166
|
`;
|
|
349
|
-
var
|
|
167
|
+
var TRIGGER = `name: ci
|
|
350
168
|
|
|
351
169
|
on:
|
|
352
170
|
# \u26D4 NO \`push: branches: [main]\`. Every commit reaches main through a pull request whose
|
|
@@ -477,7 +295,7 @@ function renderCi(shape) {
|
|
|
477
295
|
const on = runsOn(shape.runner);
|
|
478
296
|
const extras = shape.extraJobs ?? [];
|
|
479
297
|
const needs = ["check", "workflows", ...extras.map((job) => job.id)];
|
|
480
|
-
return `${
|
|
298
|
+
return `${HEADER}${runnerNote(shape)}${TRIGGER}
|
|
481
299
|
jobs:
|
|
482
300
|
${CHECK_NOTE}
|
|
483
301
|
check:
|
|
@@ -544,9 +362,101 @@ self-hosted-runner:
|
|
|
544
362
|
- homeflare-mini
|
|
545
363
|
`;
|
|
546
364
|
}
|
|
365
|
+
// src/repo-shape/dependabot.ts
|
|
366
|
+
var HOMEFLARE_PATTERN = "@homeflare/*";
|
|
367
|
+
var THIRD_PARTY_COOLDOWN_DAYS = 7;
|
|
368
|
+
function renderDependabot(shape) {
|
|
369
|
+
const bunDirs = shape.publishes ? ["/", "/packages/*"] : ["/"];
|
|
370
|
+
return `# Dependabot for ${shape.repository}.
|
|
371
|
+
#
|
|
372
|
+
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
373
|
+
# Refresh with \`bun run repo-shape:refresh\`.
|
|
374
|
+
#
|
|
375
|
+
# \u26D4 THIS FILE LIVES AT .github/dependabot.yml, NOT IN .github/workflows/. Dependabot is a
|
|
376
|
+
# platform feature, not an Action \u2014 a config placed among the workflows is silently
|
|
377
|
+
# ignored, and the symptom is simply that no pull requests ever arrive. Measured
|
|
378
|
+
# 2026-09-22: 13 of 14 HomeFlare repositories had no dependabot config at all, so their
|
|
379
|
+
# Actions and their toolchain went stale invisibly, which is exactly how nothing fails.
|
|
380
|
+
#
|
|
381
|
+
# \u2605 WHY GROUPED RATHER THAN ONE PR PER DEPENDENCY. The default opens a pull request per
|
|
382
|
+
# outdated package; that is a trickle nobody reviews properly. Each group below is a set
|
|
383
|
+
# that is either safe to take together or needs deciding together.
|
|
384
|
+
#
|
|
385
|
+
# \u2605 THE BILLING LOCK DOES NOT STOP THIS FILE. Dependabot runs as a GitHub-hosted "dynamic"
|
|
386
|
+
# workflow that GitHub's docs say "does not count towards your included GitHub Actions
|
|
387
|
+
# minutes"; measured 2026-09-22, a private estate repository's Dependabot job got a hosted
|
|
388
|
+
# runner 86 minutes after that account's ordinary hosted jobs were refused for billing.
|
|
389
|
+
# It cannot use the mini anyway: a self-hosted Dependabot runner must be Linux x64 with
|
|
390
|
+
# Docker, and the mini's runners are arm64 containers with no Docker socket.
|
|
391
|
+
version: 2
|
|
392
|
+
|
|
393
|
+
updates:
|
|
394
|
+
# \u2500\u2500 The toolchain (bun.lock) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
395
|
+
# \u26A0\uFE0F STILL BLOCKED UPSTREAM, 2026-09-22 \u2014 unrelated to the ignore below. bun 1.4 writes
|
|
396
|
+
# bun.lock \`lockfileVersion\` 2 and Dependabot's updater bundles bun 1.3.14, which reads
|
|
397
|
+
# up to 1, so this whole block fails in every estate repository with "Unsupported
|
|
398
|
+
# bun.lock 'lockfileVersion' 2" before it reads a single manifest. The fix is
|
|
399
|
+
# dependabot/dependabot-core pull request 16071. The github-actions block is unaffected.
|
|
400
|
+
- package-ecosystem: bun
|
|
401
|
+
directories:
|
|
402
|
+
${bunDirs.map((dir) => ` - ${dir}`).join(`
|
|
403
|
+
`)}
|
|
404
|
+
schedule:
|
|
405
|
+
interval: weekly
|
|
406
|
+
day: monday
|
|
407
|
+
time: '09:00'
|
|
408
|
+
timezone: America/New_York
|
|
409
|
+
cooldown:
|
|
410
|
+
default-days: ${THIRD_PARTY_COOLDOWN_DAYS}
|
|
411
|
+
# \u26D4 NEVER PROPOSED HERE. \`homeflare-bumper\` opens the one pull request that bumps
|
|
412
|
+
# \`@homeflare/*\` (kit auto-bumper design, Tim 2026-09-23) \u2014 a Dependabot
|
|
413
|
+
# update for the same package would race it and, on the weeks they disagree, leave
|
|
414
|
+
# two open pull requests fighting over the same \`package.json\` line.
|
|
415
|
+
ignore:
|
|
416
|
+
- dependency-name: '${HOMEFLARE_PATTERN}'
|
|
417
|
+
open-pull-requests-limit: 5
|
|
418
|
+
commit-message:
|
|
419
|
+
prefix: 'chore'
|
|
420
|
+
include: scope
|
|
421
|
+
labels: [dependencies]
|
|
422
|
+
groups:
|
|
423
|
+
# oxfmt and oxlint move together and only affect style. Minor and patch bumps are
|
|
424
|
+
# noise unless they fail CI, which is what CI is for.
|
|
425
|
+
lint-and-format:
|
|
426
|
+
patterns: ['oxfmt', 'oxlint']
|
|
427
|
+
update-types: [minor, patch]
|
|
428
|
+
|
|
429
|
+
# \u26A0\uFE0F MAJORS EXCLUDED DELIBERATELY. TypeScript majors change what typechecks;
|
|
430
|
+
# changesets majors have renamed inputs and dropped compatibility (the action's v2
|
|
431
|
+
# did both). These want reading, not merging on green.
|
|
432
|
+
build-tooling:
|
|
433
|
+
patterns: ['typescript', '@changesets/*', '@types/bun']
|
|
434
|
+
update-types: [minor, patch]
|
|
435
|
+
|
|
436
|
+
# \u2500\u2500 The workflows themselves \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
437
|
+
# \u2605 Actions go stale invisibly: nothing fails, they just keep running old code. The
|
|
438
|
+
# estate's first workflows pinned checkout@v5 (current: v7) and changesets/action@v1
|
|
439
|
+
# (current: v2, with every input renamed) \u2014 measured 2026-09-15.
|
|
440
|
+
- package-ecosystem: github-actions
|
|
441
|
+
directory: /
|
|
442
|
+
schedule:
|
|
443
|
+
interval: weekly
|
|
444
|
+
day: monday
|
|
445
|
+
time: '09:00'
|
|
446
|
+
timezone: America/New_York
|
|
447
|
+
open-pull-requests-limit: 5
|
|
448
|
+
commit-message:
|
|
449
|
+
prefix: 'ci'
|
|
450
|
+
labels: [dependencies, github-actions]
|
|
451
|
+
groups:
|
|
452
|
+
actions:
|
|
453
|
+
patterns: ['*']
|
|
454
|
+
update-types: [minor, patch]
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
547
457
|
// src/repo-shape/security.ts
|
|
548
458
|
var CRON = "41 6 * * 1";
|
|
549
|
-
var
|
|
459
|
+
var HEADER2 = `# Secret scanning, on its own schedule and its own check.
|
|
550
460
|
#
|
|
551
461
|
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
552
462
|
# Its input is this repository's \`repo-shape.ts\`; refresh with \`bun run repo-shape:refresh\`.
|
|
@@ -556,7 +466,7 @@ var HEADER3 = `# Secret scanning, on its own schedule and its own check.
|
|
|
556
466
|
# tomorrow can find a secret committed today), and it needs full history, which the CI
|
|
557
467
|
# jobs deliberately do not fetch.
|
|
558
468
|
`;
|
|
559
|
-
var
|
|
469
|
+
var TRIGGER2 = `name: security
|
|
560
470
|
|
|
561
471
|
on:
|
|
562
472
|
# \u26D4 NO \`push: branches: [main]\`, for the reason ci.yml gives: main only moves by
|
|
@@ -607,7 +517,7 @@ function renderSecurity(shape) {
|
|
|
607
517
|
# \u26D4 THE SCHEDULED RUN GOES TO THE MINI TOO: if the runner daemon is down this queues
|
|
608
518
|
# rather than failing, so a missed weekly scan is silent. homeflare-mini's
|
|
609
519
|
# \`ci-runner\` vmalert group is what notices that, not this workflow.` : "";
|
|
610
|
-
return `${
|
|
520
|
+
return `${HEADER2}${TRIGGER2}
|
|
611
521
|
jobs:
|
|
612
522
|
secrets:
|
|
613
523
|
name: secret scan${scheduled}
|
|
@@ -627,7 +537,6 @@ function renderRepoShape(shape) {
|
|
|
627
537
|
".changeset/config.json": renderChangesetConfig(shape),
|
|
628
538
|
".github/dependabot.yml": renderDependabot(shape),
|
|
629
539
|
".github/workflows/ci.yml": renderCi(shape),
|
|
630
|
-
".github/workflows/dependabot-automerge.yml": renderAutomerge(shape),
|
|
631
540
|
".github/workflows/security.yml": renderSecurity(shape)
|
|
632
541
|
};
|
|
633
542
|
const actionlint = renderActionlintConfig(shape);
|
|
@@ -643,10 +552,33 @@ var RENDERED_PATHS = [
|
|
|
643
552
|
".github/actionlint.yaml",
|
|
644
553
|
".github/dependabot.yml",
|
|
645
554
|
".github/workflows/ci.yml",
|
|
646
|
-
".github/workflows/dependabot-automerge.yml",
|
|
647
555
|
".github/workflows/security.yml"
|
|
648
556
|
];
|
|
649
557
|
|
|
558
|
+
// src/repo-shape/retired.ts
|
|
559
|
+
function retire(file) {
|
|
560
|
+
return {
|
|
561
|
+
path: file.path,
|
|
562
|
+
reason: requireSentence("retired reason", file.reason),
|
|
563
|
+
retiredIn: requireSemver(file.retiredIn)
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
var RETIRED_FILES = [
|
|
567
|
+
retire({
|
|
568
|
+
path: ".github/workflows/dependabot-automerge.yml",
|
|
569
|
+
reason: "kit PR 199 retired the Dependabot @homeflare group; taslabs-net/homeflare-bumper " + "carries a kit release into each consumer now, over the kit's own release workflow",
|
|
570
|
+
retiredIn: "0.12.0"
|
|
571
|
+
})
|
|
572
|
+
];
|
|
573
|
+
var GENERATED_FILE_MARKER = "\uD83E\uDD16 RENDERED BY @homeflare/config";
|
|
574
|
+
function wasRenderedByUs(content) {
|
|
575
|
+
return content.split(`
|
|
576
|
+
`).some((line) => line.trimStart().startsWith(`# ${GENERATED_FILE_MARKER}`));
|
|
577
|
+
}
|
|
578
|
+
function retiredFileProblem(file) {
|
|
579
|
+
return `${file.path}: retired in @homeflare/config@${file.retiredIn} (${file.reason}) ` + "but still present \u2014 run `bun run repo-shape:refresh` to remove it";
|
|
580
|
+
}
|
|
581
|
+
|
|
650
582
|
// src/repo-shape/drift.ts
|
|
651
583
|
var REFRESH_COMMAND = "bun run repo-shape:refresh";
|
|
652
584
|
function normalize(text) {
|
|
@@ -671,13 +603,23 @@ function duplicateExceptions(exceptions) {
|
|
|
671
603
|
}
|
|
672
604
|
return [...duplicated].map((file) => `${file}: declared as an exception more than once \u2014 keep one, with one reason`);
|
|
673
605
|
}
|
|
606
|
+
async function retiredFilesPresent(projectDir) {
|
|
607
|
+
const problems = [];
|
|
608
|
+
for (const file of RETIRED_FILES) {
|
|
609
|
+
if (await readIfPresent(`${projectDir}/${file.path}`) !== undefined) {
|
|
610
|
+
problems.push(retiredFileProblem(file));
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
return problems;
|
|
614
|
+
}
|
|
674
615
|
async function driftInRepoShape(projectDir, shape) {
|
|
675
616
|
const rendered = renderRepoShape(shape);
|
|
676
617
|
const exceptions = shape.exceptions ?? [];
|
|
677
618
|
const excepted = new Map(exceptions.map((exception) => [exception.file, exception]));
|
|
678
619
|
const problems = [
|
|
679
620
|
...duplicateExceptions(exceptions),
|
|
680
|
-
...staleExceptions(rendered, exceptions)
|
|
621
|
+
...staleExceptions(rendered, exceptions),
|
|
622
|
+
...await retiredFilesPresent(projectDir)
|
|
681
623
|
];
|
|
682
624
|
const drifted = [];
|
|
683
625
|
for (const [path, expected] of Object.entries(rendered.files)) {
|
|
@@ -719,7 +661,25 @@ async function refreshRepoShape(projectDir, shape) {
|
|
|
719
661
|
await Bun.write(target, contents);
|
|
720
662
|
written.push(path);
|
|
721
663
|
}
|
|
722
|
-
|
|
664
|
+
const { refused, removed } = await clearRetiredFiles(projectDir);
|
|
665
|
+
return { refused, removed, skipped, unchanged, written };
|
|
666
|
+
}
|
|
667
|
+
async function clearRetiredFiles(projectDir) {
|
|
668
|
+
const removed = [];
|
|
669
|
+
const refused = [];
|
|
670
|
+
for (const retiredFile of RETIRED_FILES) {
|
|
671
|
+
const target = `${projectDir}/${retiredFile.path}`;
|
|
672
|
+
const file = Bun.file(target);
|
|
673
|
+
if (!await file.exists())
|
|
674
|
+
continue;
|
|
675
|
+
if (wasRenderedByUs(await file.text())) {
|
|
676
|
+
await file.delete();
|
|
677
|
+
removed.push(retiredFile.path);
|
|
678
|
+
} else {
|
|
679
|
+
refused.push(retiredFile.path);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
return { refused, removed };
|
|
723
683
|
}
|
|
724
684
|
async function say(stream, lines) {
|
|
725
685
|
if (lines.length === 0)
|
|
@@ -750,19 +710,20 @@ async function repoShapeCli(projectDir, shape, argv) {
|
|
|
750
710
|
await say(Bun.stdout, [
|
|
751
711
|
...result.written.map((path) => `wrote ${path}`),
|
|
752
712
|
...result.unchanged.map((path) => `ok ${path}`),
|
|
753
|
-
...result.skipped.map((path) => `excepted ${path}`)
|
|
713
|
+
...result.skipped.map((path) => `excepted ${path}`),
|
|
714
|
+
...result.removed.map((path) => `removed ${path} (retired; carried our header)`)
|
|
754
715
|
]);
|
|
716
|
+
await say(Bun.stderr, result.refused.map((path) => `refused ${path} \u2014 present but not provably ours; not deleted. See docs/repo-shape-retired.md.`));
|
|
755
717
|
return 0;
|
|
756
718
|
}
|
|
757
719
|
export {
|
|
758
720
|
ACTIONLINT_VERSION,
|
|
759
721
|
BUN_VERSION,
|
|
760
|
-
|
|
761
|
-
GROUP_BRANCH_PREFIX,
|
|
762
|
-
HOMEFLARE_GROUP,
|
|
722
|
+
GENERATED_FILE_MARKER,
|
|
763
723
|
HOMEFLARE_PATTERN,
|
|
764
724
|
REFRESH_COMMAND,
|
|
765
725
|
RENDERED_PATHS,
|
|
726
|
+
RETIRED_FILES,
|
|
766
727
|
THIRD_PARTY_COOLDOWN_DAYS,
|
|
767
728
|
driftInRepoShape,
|
|
768
729
|
except,
|
|
@@ -771,15 +732,16 @@ export {
|
|
|
771
732
|
isExcepted,
|
|
772
733
|
refreshRepoShape,
|
|
773
734
|
renderActionlintConfig,
|
|
774
|
-
renderAutomerge,
|
|
775
735
|
renderChangesetConfig,
|
|
776
736
|
renderCi,
|
|
777
737
|
renderDependabot,
|
|
778
738
|
renderRepoShape,
|
|
779
739
|
renderSecurity,
|
|
780
740
|
repoShapeCli,
|
|
781
|
-
|
|
741
|
+
retiredFileProblem,
|
|
742
|
+
runsOn,
|
|
743
|
+
wasRenderedByUs
|
|
782
744
|
};
|
|
783
745
|
|
|
784
|
-
//# debugId=
|
|
746
|
+
//# debugId=B40D338DD6A7FD1664756E2164756E21
|
|
785
747
|
//# sourceMappingURL=repo-shape.js.map
|