@homeflare/config 0.9.0 → 0.10.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.
- package/dist/repo-shape/automerge.d.ts +17 -0
- package/dist/repo-shape/automerge.d.ts.map +1 -0
- package/dist/repo-shape/ci.d.ts.map +1 -1
- package/dist/repo-shape/companions.d.ts +2 -6
- package/dist/repo-shape/companions.d.ts.map +1 -1
- package/dist/repo-shape/dependabot.d.ts +35 -0
- package/dist/repo-shape/dependabot.d.ts.map +1 -0
- package/dist/repo-shape/guards.d.ts +25 -0
- package/dist/repo-shape/guards.d.ts.map +1 -0
- package/dist/repo-shape/render.d.ts.map +1 -1
- package/dist/repo-shape/shape.d.ts +40 -2
- package/dist/repo-shape/shape.d.ts.map +1 -1
- package/dist/repo-shape.d.ts +5 -2
- package/dist/repo-shape.d.ts.map +1 -1
- package/dist/repo-shape.js +269 -105
- package/dist/repo-shape.js.map +10 -7
- package/dist/versions.js +68 -31
- package/dist/versions.js.map +6 -5
- package/docs/repo-shape-dependabot.md +126 -0
- package/docs/repo-shape-inputs.md +83 -0
- package/docs/repo-shape.md +5 -23
- package/package.json +1 -1
- package/src/repo-shape/automerge.ts +144 -0
- package/src/repo-shape/ci.ts +35 -16
- package/src/repo-shape/companions.ts +2 -77
- package/src/repo-shape/dependabot.ts +136 -0
- package/src/repo-shape/guards.ts +68 -0
- package/src/repo-shape/render.ts +5 -1
- package/src/repo-shape/shape.ts +51 -35
- package/src/repo-shape.ts +8 -4
package/dist/repo-shape.js
CHANGED
|
@@ -1,25 +1,107 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
// src/repo-shape/
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
+
`;
|
|
22
102
|
}
|
|
103
|
+
|
|
104
|
+
// src/repo-shape/guards.ts
|
|
23
105
|
var SENTENCE = 12;
|
|
24
106
|
function requireNonEmpty(field, value) {
|
|
25
107
|
const trimmed = value.trim();
|
|
@@ -40,12 +122,50 @@ function requireIsoDate(value) {
|
|
|
40
122
|
}
|
|
41
123
|
return value;
|
|
42
124
|
}
|
|
125
|
+
function requireMajor(value) {
|
|
126
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
127
|
+
throw new Error(`repo-shape: node must be a positive integer major, got ${value}`);
|
|
128
|
+
}
|
|
129
|
+
return value;
|
|
130
|
+
}
|
|
131
|
+
function requireMinutes(value) {
|
|
132
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
133
|
+
throw new Error(`repo-shape: timeout must be a positive whole minute count, got ${value}`);
|
|
134
|
+
}
|
|
135
|
+
return value;
|
|
136
|
+
}
|
|
43
137
|
function requireJobId(value) {
|
|
44
138
|
if (!/^[a-z][a-z0-9_-]*$/.test(value)) {
|
|
45
139
|
throw new Error(`repo-shape: job id must match /^[a-z][a-z0-9_-]*$/, got ${JSON.stringify(value)}`);
|
|
46
140
|
}
|
|
47
141
|
return value;
|
|
48
142
|
}
|
|
143
|
+
|
|
144
|
+
// src/repo-shape/shape.ts
|
|
145
|
+
function except(exception) {
|
|
146
|
+
return {
|
|
147
|
+
file: exception.file,
|
|
148
|
+
reason: requireSentence("reason", exception.reason),
|
|
149
|
+
since: requireIsoDate(exception.since)
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function extraJob(job) {
|
|
153
|
+
if (job.steps.length === 0) {
|
|
154
|
+
throw new Error(`repo-shape: extra job "${job.id}" has no steps`);
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
bun: job.bun ?? true,
|
|
158
|
+
id: requireJobId(job.id),
|
|
159
|
+
name: requireNonEmpty("name", job.name),
|
|
160
|
+
needs: [...job.needs ?? []],
|
|
161
|
+
reason: requireSentence("reason", job.reason),
|
|
162
|
+
steps: [...job.steps],
|
|
163
|
+
...job.timeout === undefined ? {} : { timeout: requireMinutes(job.timeout) }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function nodeMajor(shape) {
|
|
167
|
+
return shape.node === undefined ? undefined : requireMajor(shape.node);
|
|
168
|
+
}
|
|
49
169
|
function runsOn(runner) {
|
|
50
170
|
return runner === "mini" ? "[self-hosted, homeflare-mini]" : "ubuntu-latest";
|
|
51
171
|
}
|
|
@@ -101,11 +221,98 @@ function renderSteps(steps, depth) {
|
|
|
101
221
|
`);
|
|
102
222
|
}
|
|
103
223
|
|
|
224
|
+
// src/repo-shape/automerge.ts
|
|
225
|
+
var GROUP_BRANCH_PREFIX = `dependabot/bun/${HOMEFLARE_GROUP}-`;
|
|
226
|
+
var GROUP_BRANCH = `^${GROUP_BRANCH_PREFIX}[0-9a-f]{10}$`;
|
|
227
|
+
var HEADER = `# Arms auto-merge on Dependabot's @homeflare/* group, and on nothing else.
|
|
228
|
+
#
|
|
229
|
+
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
230
|
+
# Its input is this repository's \`repo-shape.ts\`; refresh with \`bun run repo-shape:refresh\`.
|
|
231
|
+
#
|
|
232
|
+
# \u2605 HOW A KIT RELEASE ARRIVES: the \`${HOMEFLARE_GROUP}\` group in .github/dependabot.yml opens one
|
|
233
|
+
# pull request; this arms GitHub's auto-merge on it; the ruleset's required checks decide.
|
|
234
|
+
# Nothing here merges anything itself, and merging deploys nothing.
|
|
235
|
+
# \u26D4 NO THIRD-PARTY ACTION. GitHub's own example uses dependabot/fetch-metadata, which its
|
|
236
|
+
# docs mark "not certified by GitHub"; the group is recognised by its branch name instead.
|
|
237
|
+
# \u26D4 NO REQUIRED CHECK ON THE BASE BRANCH, NO ARMING: there \`gh pr merge --auto\` would merge
|
|
238
|
+
# at once, unchecked. The job fails instead, so the pull request waits for a person.
|
|
239
|
+
# \u26A0\uFE0F A RENDERER CHANGE STILL NEEDS A PERSON. When a kit release changes what @homeflare/config
|
|
240
|
+
# renders, the bump fails \`check\` (the drift test) and never goes green. Run
|
|
241
|
+
# \`bun run repo-shape:refresh\` on Dependabot's branch and push. \u26D4 This workflow does not do
|
|
242
|
+
# it for you: a push made with GITHUB_TOKEN starts no workflow run, so a refreshed commit
|
|
243
|
+
# would never get the required checks and the pull request would wait forever.
|
|
244
|
+
`;
|
|
245
|
+
var TRIGGER = `name: dependabot auto-merge
|
|
246
|
+
|
|
247
|
+
on:
|
|
248
|
+
pull_request:
|
|
249
|
+
|
|
250
|
+
# \u26D4 NOTHING AT THE TOP; the one job asks for exactly what \`gh pr merge --auto\` needs.
|
|
251
|
+
permissions: {}
|
|
252
|
+
|
|
253
|
+
concurrency:
|
|
254
|
+
group: automerge-\${{ github.ref }}
|
|
255
|
+
cancel-in-progress: true
|
|
256
|
+
`;
|
|
257
|
+
var JOB_NOTE = ` # \u26D4 BOTH LOGINS, NOT EITHER. \`user.login\` is who opened the pull request; \`github.actor\` is
|
|
258
|
+
# who started this run. A person pushing to Dependabot's branch changes the actor, so
|
|
259
|
+
# their commit never arms anything; only Dependabot's own rebases do.`;
|
|
260
|
+
var PERMISSIONS_NOTE = ` # \u2605 WHAT GitHub'S OWN EXAMPLE GRANTS FOR THIS STEP, AND NO MORE. A run Dependabot starts gets
|
|
261
|
+
# a read-only GITHUB_TOKEN unless the workflow raises it ("Troubleshooting Dependabot on
|
|
262
|
+
# GitHub Actions" \u2192 "Changing GITHUB_TOKEN permissions").`;
|
|
263
|
+
var ARM = `set -euo pipefail
|
|
264
|
+
if [[ ! "$HEAD_REF" =~ ${GROUP_BRANCH} ]]; then
|
|
265
|
+
echo "::notice::$HEAD_REF is not the ${HOMEFLARE_GROUP} group's branch; auto-merge not armed"
|
|
266
|
+
exit 0
|
|
267
|
+
fi
|
|
268
|
+
# \u26D4 gh merges a CLEAN or UNSTABLE pull request at once rather than arming it. Only a branch
|
|
269
|
+
# rule that requires a status check keeps it BLOCKED until the checks have passed.
|
|
270
|
+
required=$(gh api "repos/$GITHUB_REPOSITORY/rules/branches/$BASE_REF" \\
|
|
271
|
+
--jq '[.[] | select(.type == "required_status_checks")] | length')
|
|
272
|
+
if [ "$required" = 0 ]; then
|
|
273
|
+
echo "::error::$BASE_REF requires no status check, so gh would merge at once; auto-merge not armed"
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
# \u2605 --squash: the house repositories allow squash merges only (declareRepoPolicy).
|
|
277
|
+
gh pr merge --auto --squash "$PR_URL"`;
|
|
278
|
+
function renderAutomerge(shape) {
|
|
279
|
+
const headRef = "${{ github.head_ref }}";
|
|
280
|
+
const baseRef = "${{ github.base_ref }}";
|
|
281
|
+
const prUrl = "${{ github.event.pull_request.html_url }}";
|
|
282
|
+
const token = "${{ secrets.GITHUB_TOKEN }}";
|
|
283
|
+
const condition = [
|
|
284
|
+
"github.event.pull_request.user.login == 'dependabot[bot]'",
|
|
285
|
+
"github.actor == 'dependabot[bot]'",
|
|
286
|
+
`startsWith(github.head_ref, '${GROUP_BRANCH_PREFIX}')`
|
|
287
|
+
].join(" && ");
|
|
288
|
+
return `${HEADER}
|
|
289
|
+
${TRIGGER}
|
|
290
|
+
jobs:
|
|
291
|
+
${JOB_NOTE}
|
|
292
|
+
arm:
|
|
293
|
+
name: arm auto-merge
|
|
294
|
+
if: ${condition}
|
|
295
|
+
runs-on: ${runsOn(shape.runner)}
|
|
296
|
+
${PERMISSIONS_NOTE}
|
|
297
|
+
permissions:
|
|
298
|
+
contents: write
|
|
299
|
+
pull-requests: write
|
|
300
|
+
steps:
|
|
301
|
+
${renderSteps([
|
|
302
|
+
{
|
|
303
|
+
env: { BASE_REF: baseRef, GH_TOKEN: token, HEAD_REF: headRef, PR_URL: prUrl },
|
|
304
|
+
name: "Arm auto-merge on the homeflare group",
|
|
305
|
+
run: ARM
|
|
306
|
+
}
|
|
307
|
+
], 3)}
|
|
308
|
+
`;
|
|
309
|
+
}
|
|
104
310
|
// src/repo-shape/ci.ts
|
|
105
311
|
var BUN_VERSION = "1.4.0";
|
|
106
312
|
var ACTIONLINT_VERSION = "1.7.12";
|
|
107
313
|
var CHECKOUT = "actions/checkout@v7";
|
|
108
314
|
var SETUP_BUN = "oven-sh/setup-bun@v2";
|
|
315
|
+
var SETUP_NODE = "actions/setup-node@v6";
|
|
109
316
|
function runnerNote(shape) {
|
|
110
317
|
if (shape.runner !== "mini") {
|
|
111
318
|
return "# \u26A0\uFE0F RUNS ON GitHub-HOSTED `ubuntu-latest`. Only a public repository should: hosted minutes\n# are refused for this account's private repos (billing lock, 2026-09-22).\n";
|
|
@@ -120,7 +327,7 @@ function runnerNote(shape) {
|
|
|
120
327
|
].join(`
|
|
121
328
|
`);
|
|
122
329
|
}
|
|
123
|
-
var
|
|
330
|
+
var HEADER2 = `# Pull requests: is this code correct?
|
|
124
331
|
#
|
|
125
332
|
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
126
333
|
# Its input is this repository's \`repo-shape.ts\`. Change that, then:
|
|
@@ -133,7 +340,7 @@ var HEADER = `# Pull requests: is this code correct?
|
|
|
133
340
|
# share a prologue are steps inside \`check\`, so the install is paid once.
|
|
134
341
|
# \u26D4 EVERY ACTION IS FIRST-PARTY OR THE VENDOR'S OWN, PINNED TO A MAJOR TAG.
|
|
135
342
|
`;
|
|
136
|
-
var
|
|
343
|
+
var TRIGGER2 = `name: ci
|
|
137
344
|
|
|
138
345
|
on:
|
|
139
346
|
# \u26D4 NO \`push: branches: [main]\`. Every commit reaches main through a pull request whose
|
|
@@ -222,23 +429,41 @@ var VERIFY = `if [ "\${{ contains(needs.*.result, 'failure') }}" = "true" ] || \
|
|
|
222
429
|
echo "one or more required jobs did not succeed: \${{ toJSON(needs.*.result) }}" >&2
|
|
223
430
|
exit 1
|
|
224
431
|
fi`;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
432
|
+
var NODE_NOTE = ` # \u26D4 REAL NODE, NOT BUN'S SHIM, AND ONLY WHERE THE GATE NEEDS IT. The mini's job
|
|
433
|
+
# image has no node on PATH (ubuntu-latest always did), so a repository whose own
|
|
434
|
+
# \`check\` spawns \`node\` \u2014 or whose framework demands a Node runtime \u2014 fails with
|
|
435
|
+
# \`Executable not found in $PATH: "node"\` without this. Declared as \`node:\` in
|
|
436
|
+
# repo-shape.ts, so it is one input rather than a hand-edited block per repository.
|
|
437
|
+
# \u26A0\uFE0F NO PACKAGE-MANAGER CACHE: bun does the installing, so priming npm's cache costs
|
|
438
|
+
# time and caches nothing anything here reads.`;
|
|
439
|
+
function prologue(shape) {
|
|
440
|
+
const node = nodeMajor(shape);
|
|
441
|
+
const bun = [
|
|
228
442
|
{ uses: SETUP_BUN, with: { "bun-version": BUN_VERSION } },
|
|
229
443
|
{ run: "bun install --frozen-lockfile" }
|
|
230
|
-
]
|
|
444
|
+
];
|
|
445
|
+
if (node === undefined)
|
|
446
|
+
return renderSteps([{ uses: CHECKOUT }, ...bun], 3);
|
|
447
|
+
return [
|
|
448
|
+
renderSteps([{ uses: CHECKOUT }], 3),
|
|
449
|
+
NODE_NOTE,
|
|
450
|
+
renderSteps([{ uses: SETUP_NODE, with: { "node-version": node, "package-manager-cache": "false" } }], 3),
|
|
451
|
+
renderSteps(bun, 3)
|
|
452
|
+
].join(`
|
|
453
|
+
`);
|
|
231
454
|
}
|
|
232
|
-
function renderExtraJob(job, on) {
|
|
455
|
+
function renderExtraJob(job, shape, on) {
|
|
233
456
|
const needs = (job.needs ?? []).length === 0 ? "" : ` needs: [${(job.needs ?? []).join(", ")}]
|
|
234
457
|
`;
|
|
235
|
-
const
|
|
458
|
+
const timeout = job.timeout === undefined ? "" : ` timeout-minutes: ${job.timeout}
|
|
459
|
+
`;
|
|
460
|
+
const steps = job.bun === false ? renderSteps([{ uses: CHECKOUT }, ...job.steps], 3) : [prologue(shape), renderSteps(job.steps, 3)].join(`
|
|
236
461
|
`);
|
|
237
462
|
return ` # \u2605 NOT PART OF THE STANDARD SHAPE \u2014 ${job.reason}
|
|
238
463
|
${job.id}:
|
|
239
464
|
name: ${job.name}
|
|
240
465
|
${needs} runs-on: ${on}
|
|
241
|
-
steps:
|
|
466
|
+
${timeout} steps:
|
|
242
467
|
${steps}
|
|
243
468
|
`;
|
|
244
469
|
}
|
|
@@ -246,14 +471,14 @@ function renderCi(shape) {
|
|
|
246
471
|
const on = runsOn(shape.runner);
|
|
247
472
|
const extras = shape.extraJobs ?? [];
|
|
248
473
|
const needs = ["check", "workflows", ...extras.map((job) => job.id)];
|
|
249
|
-
return `${
|
|
474
|
+
return `${HEADER2}${runnerNote(shape)}${TRIGGER2}
|
|
250
475
|
jobs:
|
|
251
476
|
${CHECK_NOTE}
|
|
252
477
|
check:
|
|
253
478
|
name: check
|
|
254
479
|
runs-on: ${on}
|
|
255
480
|
steps:
|
|
256
|
-
${prologue()}
|
|
481
|
+
${prologue(shape)}
|
|
257
482
|
${renderSteps([{ run: "bun run check" }], 3)}
|
|
258
483
|
|
|
259
484
|
${WORKFLOWS_NOTE}
|
|
@@ -266,7 +491,7 @@ ${ACTIONLINT_NOTE}
|
|
|
266
491
|
${renderSteps([{ name: "Install actionlint (checksum-verified)", run: INSTALL_ACTIONLINT }], 3)}
|
|
267
492
|
${renderSteps([{ name: "Lint workflows", run: "./actionlint -color" }], 3)}
|
|
268
493
|
|
|
269
|
-
${extras.map((job) => `${renderExtraJob(job, on)}
|
|
494
|
+
${extras.map((job) => `${renderExtraJob(job, shape, on)}
|
|
270
495
|
`).join("")}${AGGREGATE_NOTE}
|
|
271
496
|
ci:
|
|
272
497
|
name: ci
|
|
@@ -313,78 +538,9 @@ self-hosted-runner:
|
|
|
313
538
|
- homeflare-mini
|
|
314
539
|
`;
|
|
315
540
|
}
|
|
316
|
-
function renderDependabot(shape) {
|
|
317
|
-
const bunDirs = shape.publishes ? ["/", "/packages/*"] : ["/"];
|
|
318
|
-
return `# Dependabot for ${shape.repository}.
|
|
319
|
-
#
|
|
320
|
-
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
321
|
-
# Refresh with \`bun run repo-shape:refresh\`.
|
|
322
|
-
#
|
|
323
|
-
# \u26D4 THIS FILE LIVES AT .github/dependabot.yml, NOT IN .github/workflows/. Dependabot is a
|
|
324
|
-
# platform feature, not an Action \u2014 a config placed among the workflows is silently
|
|
325
|
-
# ignored, and the symptom is simply that no pull requests ever arrive. Measured
|
|
326
|
-
# 2026-09-22: 13 of 14 HomeFlare repositories had no dependabot config at all, so their
|
|
327
|
-
# Actions and their toolchain went stale invisibly, which is exactly how nothing fails.
|
|
328
|
-
#
|
|
329
|
-
# \u2605 WHY GROUPED RATHER THAN ONE PR PER DEPENDENCY. The default opens a pull request per
|
|
330
|
-
# outdated package; that is a trickle nobody reviews properly. Each group below is a set
|
|
331
|
-
# that is either safe to take together or needs deciding together.
|
|
332
|
-
version: 2
|
|
333
|
-
|
|
334
|
-
updates:
|
|
335
|
-
# \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
|
|
336
|
-
- package-ecosystem: bun
|
|
337
|
-
directories:
|
|
338
|
-
${bunDirs.map((dir) => ` - ${dir}`).join(`
|
|
339
|
-
`)}
|
|
340
|
-
schedule:
|
|
341
|
-
interval: weekly
|
|
342
|
-
day: monday
|
|
343
|
-
time: '09:00'
|
|
344
|
-
timezone: America/New_York
|
|
345
|
-
open-pull-requests-limit: 5
|
|
346
|
-
commit-message:
|
|
347
|
-
prefix: 'chore'
|
|
348
|
-
include: scope
|
|
349
|
-
labels: [dependencies]
|
|
350
|
-
groups:
|
|
351
|
-
# oxfmt and oxlint move together and only affect style. Minor and patch bumps are
|
|
352
|
-
# noise unless they fail CI, which is what CI is for.
|
|
353
|
-
lint-and-format:
|
|
354
|
-
patterns: ['oxfmt', 'oxlint']
|
|
355
|
-
update-types: [minor, patch]
|
|
356
|
-
|
|
357
|
-
# \u26A0\uFE0F MAJORS EXCLUDED DELIBERATELY. TypeScript majors change what typechecks;
|
|
358
|
-
# changesets majors have renamed inputs and dropped compatibility (the action's v2
|
|
359
|
-
# did both). These want reading, not merging on green.
|
|
360
|
-
build-tooling:
|
|
361
|
-
patterns: ['typescript', '@changesets/*', '@types/bun']
|
|
362
|
-
update-types: [minor, patch]
|
|
363
|
-
|
|
364
|
-
# \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
|
|
365
|
-
# \u2605 Actions go stale invisibly: nothing fails, they just keep running old code. The
|
|
366
|
-
# estate's first workflows pinned checkout@v5 (current: v7) and changesets/action@v1
|
|
367
|
-
# (current: v2, with every input renamed) \u2014 measured 2026-09-15.
|
|
368
|
-
- package-ecosystem: github-actions
|
|
369
|
-
directory: /
|
|
370
|
-
schedule:
|
|
371
|
-
interval: weekly
|
|
372
|
-
day: monday
|
|
373
|
-
time: '09:00'
|
|
374
|
-
timezone: America/New_York
|
|
375
|
-
open-pull-requests-limit: 5
|
|
376
|
-
commit-message:
|
|
377
|
-
prefix: 'ci'
|
|
378
|
-
labels: [dependencies, github-actions]
|
|
379
|
-
groups:
|
|
380
|
-
actions:
|
|
381
|
-
patterns: ['*']
|
|
382
|
-
update-types: [minor, patch]
|
|
383
|
-
`;
|
|
384
|
-
}
|
|
385
541
|
// src/repo-shape/security.ts
|
|
386
542
|
var CRON = "41 6 * * 1";
|
|
387
|
-
var
|
|
543
|
+
var HEADER3 = `# Secret scanning, on its own schedule and its own check.
|
|
388
544
|
#
|
|
389
545
|
# \uD83E\uDD16 RENDERED BY @homeflare/config \u2014 DO NOT EDIT THIS FILE BY HAND.
|
|
390
546
|
# Its input is this repository's \`repo-shape.ts\`; refresh with \`bun run repo-shape:refresh\`.
|
|
@@ -394,7 +550,7 @@ var HEADER2 = `# Secret scanning, on its own schedule and its own check.
|
|
|
394
550
|
# tomorrow can find a secret committed today), and it needs full history, which the CI
|
|
395
551
|
# jobs deliberately do not fetch.
|
|
396
552
|
`;
|
|
397
|
-
var
|
|
553
|
+
var TRIGGER3 = `name: security
|
|
398
554
|
|
|
399
555
|
on:
|
|
400
556
|
# \u26D4 NO \`push: branches: [main]\`, for the reason ci.yml gives: main only moves by
|
|
@@ -445,7 +601,7 @@ function renderSecurity(shape) {
|
|
|
445
601
|
# \u26D4 THE SCHEDULED RUN GOES TO THE MINI TOO: if the runner daemon is down this queues
|
|
446
602
|
# rather than failing, so a missed weekly scan is silent. homeflare-mini's
|
|
447
603
|
# \`ci-runner\` vmalert group is what notices that, not this workflow.` : "";
|
|
448
|
-
return `${
|
|
604
|
+
return `${HEADER3}${TRIGGER3}
|
|
449
605
|
jobs:
|
|
450
606
|
secrets:
|
|
451
607
|
name: secret scan${scheduled}
|
|
@@ -465,6 +621,7 @@ function renderRepoShape(shape) {
|
|
|
465
621
|
".changeset/config.json": renderChangesetConfig(shape),
|
|
466
622
|
".github/dependabot.yml": renderDependabot(shape),
|
|
467
623
|
".github/workflows/ci.yml": renderCi(shape),
|
|
624
|
+
".github/workflows/dependabot-automerge.yml": renderAutomerge(shape),
|
|
468
625
|
".github/workflows/security.yml": renderSecurity(shape)
|
|
469
626
|
};
|
|
470
627
|
const actionlint = renderActionlintConfig(shape);
|
|
@@ -480,6 +637,7 @@ var RENDERED_PATHS = [
|
|
|
480
637
|
".github/actionlint.yaml",
|
|
481
638
|
".github/dependabot.yml",
|
|
482
639
|
".github/workflows/ci.yml",
|
|
640
|
+
".github/workflows/dependabot-automerge.yml",
|
|
483
641
|
".github/workflows/security.yml"
|
|
484
642
|
];
|
|
485
643
|
|
|
@@ -593,8 +751,13 @@ async function repoShapeCli(projectDir, shape, argv) {
|
|
|
593
751
|
export {
|
|
594
752
|
ACTIONLINT_VERSION,
|
|
595
753
|
BUN_VERSION,
|
|
754
|
+
GROUP_BRANCH,
|
|
755
|
+
GROUP_BRANCH_PREFIX,
|
|
756
|
+
HOMEFLARE_GROUP,
|
|
757
|
+
HOMEFLARE_PATTERN,
|
|
596
758
|
REFRESH_COMMAND,
|
|
597
759
|
RENDERED_PATHS,
|
|
760
|
+
THIRD_PARTY_COOLDOWN_DAYS,
|
|
598
761
|
driftInRepoShape,
|
|
599
762
|
except,
|
|
600
763
|
exceptionSummary,
|
|
@@ -602,6 +765,7 @@ export {
|
|
|
602
765
|
isExcepted,
|
|
603
766
|
refreshRepoShape,
|
|
604
767
|
renderActionlintConfig,
|
|
768
|
+
renderAutomerge,
|
|
605
769
|
renderChangesetConfig,
|
|
606
770
|
renderCi,
|
|
607
771
|
renderDependabot,
|
|
@@ -611,5 +775,5 @@ export {
|
|
|
611
775
|
runsOn
|
|
612
776
|
};
|
|
613
777
|
|
|
614
|
-
//# debugId=
|
|
778
|
+
//# debugId=2943BDDA9944E35364756E2164756E21
|
|
615
779
|
//# sourceMappingURL=repo-shape.js.map
|