@heroiclands/package-build 23.5.0 → 23.5.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/CHANGELOG.md +8 -0
- package/bin/package-build.mjs +1 -74
- package/docs/commands.md +4 -48
- package/docs/diagnostics.md +1 -2
- package/docs/guides/module-project.md +1 -1
- package/docs/guides/release-and-deploy.md +3 -3
- package/docs/project-setup.md +16 -59
- package/engine/cdn-publish.mjs +30 -5
- package/engine/project-init.mjs +4 -29
- package/package.json +2 -4
- package/types/engine/cdn-publish.d.mts +26 -25
- package/labels.mjs +0 -191
- package/types/labels.d.mts +0 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @heroiclands/package-build
|
|
2
2
|
|
|
3
|
+
## 23.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
`cdn publish` skips SVGs the CDN already holds, so a re-run uploads only what changed.
|
|
8
|
+
|
|
9
|
+
`package-build labels check` is gone, because issue labels are defined once for the whole organisation; remove any `lint:labels` script and `.github/labels.yml`. A project created by `package-build init` no longer gets a label registry.
|
|
10
|
+
|
|
3
11
|
## 23.5.0
|
|
4
12
|
|
|
5
13
|
### Minor Changes
|
package/bin/package-build.mjs
CHANGED
|
@@ -114,7 +114,6 @@ import { walkMarkdownTree } from "../engine/helpers.mjs";
|
|
|
114
114
|
import { reckoningContext } from "../engine/reckoning-markers.mjs";
|
|
115
115
|
import { SCHEMA_ARTIFACT_FILE } from "../engine/foreign-catalog.mjs";
|
|
116
116
|
import { validateLangSource } from "../lang.mjs";
|
|
117
|
-
import { checkLabelRegistry } from "../labels.mjs";
|
|
118
117
|
import { lintYaml } from "../engine/yaml-lint.mjs";
|
|
119
118
|
import { lintChangesetText, lintReleaseText } from "../engine/changelog-lint.mjs";
|
|
120
119
|
import { groupChangelogText } from "../engine/changelog-group.mjs";
|
|
@@ -1328,77 +1327,6 @@ function changelogGroup(args) {
|
|
|
1328
1327
|
console.log(`package-build: ${files.length} file(s) grouped · ${warnings} warning(s)`);
|
|
1329
1328
|
}
|
|
1330
1329
|
|
|
1331
|
-
/**
|
|
1332
|
-
* `labels check` — do the machine registry and the documented table agree?
|
|
1333
|
-
*
|
|
1334
|
-
* `.github/labels.yml` is synced to GitHub and the §3 table in
|
|
1335
|
-
* `.github/ISSUE_REPORTING.md` is what a person reads. Neither derives from the
|
|
1336
|
-
* other, so either can drift, and nothing notices until an issue is filed
|
|
1337
|
-
* against a label that does not exist.
|
|
1338
|
-
*
|
|
1339
|
-
* Every repository wants this and only the paths ever differed, which is why it
|
|
1340
|
-
* is here rather than copied into each as a `utils/` script.
|
|
1341
|
-
*
|
|
1342
|
-
* @returns {object} The yargs command module.
|
|
1343
|
-
*/
|
|
1344
|
-
function labelsCommand() {
|
|
1345
|
-
return {
|
|
1346
|
-
command: "labels <action>",
|
|
1347
|
-
describe: "Issue-label registry checks",
|
|
1348
|
-
builder: (y) =>
|
|
1349
|
-
y
|
|
1350
|
-
.positional("action", {
|
|
1351
|
-
choices: ["check"],
|
|
1352
|
-
describe: "check: the registry and the documented table name the same labels",
|
|
1353
|
-
})
|
|
1354
|
-
.option("registry", {
|
|
1355
|
-
type: "string",
|
|
1356
|
-
default: ".github/labels.yml",
|
|
1357
|
-
describe: "The machine registry synced to GitHub",
|
|
1358
|
-
})
|
|
1359
|
-
.option("doc", {
|
|
1360
|
-
type: "string",
|
|
1361
|
-
default: ".github/ISSUE_REPORTING.md",
|
|
1362
|
-
describe: "The documented reference whose §3 table lists the same labels",
|
|
1363
|
-
}),
|
|
1364
|
-
handler: handler(async (args) => labelsCheck(args)),
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
/**
|
|
1369
|
-
* Read both faces of the registry and report where they disagree.
|
|
1370
|
-
*
|
|
1371
|
-
* Findings are reported against the file each belongs to, so a missing row is
|
|
1372
|
-
* located in the documentation and a missing entry in the registry, rather than
|
|
1373
|
-
* both being attributed to whichever file was read first.
|
|
1374
|
-
*
|
|
1375
|
-
* @param {object} args - Parsed CLI arguments.
|
|
1376
|
-
*/
|
|
1377
|
-
function labelsCheck(args) {
|
|
1378
|
-
for (const target of [args.registry, args.doc]) {
|
|
1379
|
-
if (!fs.existsSync(target)) die(`labels check: ${target} does not exist.`);
|
|
1380
|
-
}
|
|
1381
|
-
const registryText = fs.readFileSync(args.registry, "utf8");
|
|
1382
|
-
const docText = fs.readFileSync(args.doc, "utf8");
|
|
1383
|
-
|
|
1384
|
-
const { registry, doc, count } = checkLabelRegistry({
|
|
1385
|
-
registryText,
|
|
1386
|
-
docText,
|
|
1387
|
-
docPath: args.doc,
|
|
1388
|
-
});
|
|
1389
|
-
|
|
1390
|
-
const errors =
|
|
1391
|
-
reportFindings(registry, { file: args.registry }) + reportFindings(doc, { file: args.doc });
|
|
1392
|
-
if (errors) {
|
|
1393
|
-
console.error(
|
|
1394
|
-
`\nThe registry and ${args.doc} §3 are edited together — ` +
|
|
1395
|
-
`change one and change the other.`,
|
|
1396
|
-
);
|
|
1397
|
-
process.exit(1);
|
|
1398
|
-
}
|
|
1399
|
-
console.log(`package-build: registry and §3 agree (${count} labels).`);
|
|
1400
|
-
}
|
|
1401
|
-
|
|
1402
1330
|
/**
|
|
1403
1331
|
* `lang <action>` — the three localization guards.
|
|
1404
1332
|
*
|
|
@@ -1641,7 +1569,7 @@ function cdnPublishCommand() {
|
|
|
1641
1569
|
.option("set", {
|
|
1642
1570
|
type: "string",
|
|
1643
1571
|
demandOption: true,
|
|
1644
|
-
describe: "The set file's name under the package's CDN root, e.g.
|
|
1572
|
+
describe: "The set file's name under the package's CDN root, e.g. alt-art.json",
|
|
1645
1573
|
})
|
|
1646
1574
|
.option("name", {
|
|
1647
1575
|
type: "string",
|
|
@@ -1972,7 +1900,6 @@ registerContentCommands(
|
|
|
1972
1900
|
.command(typesCommand())
|
|
1973
1901
|
.command(schemaCommand())
|
|
1974
1902
|
.command(langCommand())
|
|
1975
|
-
.command(labelsCommand())
|
|
1976
1903
|
.command(bumpCommand())
|
|
1977
1904
|
.command(yamlCommand())
|
|
1978
1905
|
.command(changelogCommand())
|
package/docs/commands.md
CHANGED
|
@@ -789,50 +789,6 @@ package-build: 1 template(s) fully localized and compiling.
|
|
|
789
789
|
`package-build lang check`, `package-build lang coverage`,
|
|
790
790
|
[Diagnostics](diagnostics.md), [Configuration](configuration.md).
|
|
791
791
|
|
|
792
|
-
### `package-build labels check`
|
|
793
|
-
|
|
794
|
-
**NAME**
|
|
795
|
-
|
|
796
|
-
Compare the machine label registry against the documented reference table
|
|
797
|
-
(`check` is the only action).
|
|
798
|
-
|
|
799
|
-
**SYNOPSIS**
|
|
800
|
-
|
|
801
|
-
```
|
|
802
|
-
package-build labels check [--registry <path>] [--doc <path>]
|
|
803
|
-
```
|
|
804
|
-
|
|
805
|
-
**DESCRIPTION**
|
|
806
|
-
|
|
807
|
-
Compares the machine label registry synced to GitHub against the documented
|
|
808
|
-
reference table, and reports where they disagree — a label the registry has
|
|
809
|
-
that the table does not, or the reverse. Neither derives from the other, so
|
|
810
|
-
nothing else notices when they drift. Reads both files named below; writes
|
|
811
|
-
nothing.
|
|
812
|
-
|
|
813
|
-
**OPTIONS**
|
|
814
|
-
|
|
815
|
-
| Option | Type | Default | Description |
|
|
816
|
-
| ------------ | ------ | ---------------------------- | -------------------------------------------------------------- |
|
|
817
|
-
| `--registry` | string | `.github/labels.yml` | The machine registry synced to GitHub. |
|
|
818
|
-
| `--doc` | string | `.github/ISSUE_REPORTING.md` | The documented reference whose §3 table lists the same labels. |
|
|
819
|
-
|
|
820
|
-
**EXIT STATUS**
|
|
821
|
-
|
|
822
|
-
1 if either file does not exist. 1 if the registry and the table disagree on
|
|
823
|
-
any label. Otherwise 0.
|
|
824
|
-
|
|
825
|
-
**EXAMPLES**
|
|
826
|
-
|
|
827
|
-
```
|
|
828
|
-
$ package-build labels check --registry .github/labels.yml --doc .github/ISSUE_REPORTING.md
|
|
829
|
-
package-build: registry and §3 agree (11 labels).
|
|
830
|
-
```
|
|
831
|
-
|
|
832
|
-
**SEE ALSO**
|
|
833
|
-
|
|
834
|
-
None.
|
|
835
|
-
|
|
836
792
|
### `package-build bump [packages..]`
|
|
837
793
|
|
|
838
794
|
**NAME**
|
|
@@ -1348,7 +1304,7 @@ A workflow calls the command the same way, passing the four values as named
|
|
|
1348
1304
|
secrets in the step's environment:
|
|
1349
1305
|
|
|
1350
1306
|
```yaml
|
|
1351
|
-
- run: npx package-build cdn publish --set
|
|
1307
|
+
- run: npx package-build cdn publish --set alt-art.json --name "Thalorna — illustrated"
|
|
1352
1308
|
env:
|
|
1353
1309
|
CDN_ENDPOINT: ${{ secrets.CDN_ENDPOINT }}
|
|
1354
1310
|
CDN_BUCKET: ${{ secrets.CDN_BUCKET }}
|
|
@@ -1374,12 +1330,12 @@ it. Otherwise 0.
|
|
|
1374
1330
|
**EXAMPLES**
|
|
1375
1331
|
|
|
1376
1332
|
```
|
|
1377
|
-
$ package-build cdn publish --set
|
|
1333
|
+
$ package-build cdn publish --set alt-art.json --name "Thalorna — illustrated" --dry-run
|
|
1378
1334
|
Dry run: comparing 582 pictures against https://cdn.heroiclands.org, read-only.
|
|
1379
1335
|
would upload thalorna/icons/anvilbdg.svg (969 bytes)
|
|
1380
1336
|
…
|
|
1381
|
-
would upload thalorna/
|
|
1382
|
-
Would upload 582, skipped 0, failed 0; https://cdn.heroiclands.org/thalorna/
|
|
1337
|
+
would upload thalorna/alt-art.json (582 assets)
|
|
1338
|
+
Would upload 582, skipped 0, failed 0; https://cdn.heroiclands.org/thalorna/alt-art.json lists 582 left-out pictures (would change).
|
|
1383
1339
|
|
|
1384
1340
|
$ package-build cdn publish --set default.json --name Shipped
|
|
1385
1341
|
package-build: `--set default.json` is refused: `default.json` is the shipped set's name, the fallback every other set is read against. Name the set for its art
|
package/docs/diagnostics.md
CHANGED
|
@@ -284,7 +284,7 @@ when it reports at least one `error`-severity diagnostic. A `warning` alone
|
|
|
284
284
|
never fails a run.** `bin/report.mjs`'s `reportFindings` draws this line once
|
|
285
285
|
for the commands built from the package's pure rule functions (`package-build
|
|
286
286
|
lint`, `package-build lang check`, `package-build lang coverage`,
|
|
287
|
-
`package-build yaml
|
|
287
|
+
`package-build yaml`): it emits every finding
|
|
288
288
|
and returns only the count whose `severity` is `error`, and the command sets
|
|
289
289
|
`process.exitCode = 1` exactly when that count is nonzero. Commands whose
|
|
290
290
|
findings are hand-assembled — `package-build links`, `package-build
|
|
@@ -335,7 +335,6 @@ over one would teach people to stop reading the report.
|
|
|
335
335
|
| `package-build lang check` | any localization finding is `error`-severity |
|
|
336
336
|
| `package-build lang coverage` | a referenced key is missing (`error`-severity); an unreferenced key is always advisory and never fails the run |
|
|
337
337
|
| `package-build yaml [paths..]` | any YAML finding is `error`-severity |
|
|
338
|
-
| `package-build labels check` | the label registry and the documented table disagree on any label |
|
|
339
338
|
|
|
340
339
|
Every command also fails on a thrown error unrelated to a
|
|
341
340
|
diagnostic — a missing configuration, an unreadable file, a network failure —
|
|
@@ -861,7 +861,7 @@ everything that reads the tree runs before everything that writes the stage.
|
|
|
861
861
|
|
|
862
862
|
[`project-setup.md`](../project-setup.md) turns this into npm scripts, and covers
|
|
863
863
|
everything a repository carries beyond the build itself: the git hooks, the
|
|
864
|
-
changeset directory,
|
|
864
|
+
changeset directory, and what each script in the chain is
|
|
865
865
|
for.
|
|
866
866
|
|
|
867
867
|
## What comes next
|
|
@@ -139,13 +139,13 @@ reaches a world from there as a named asset set. `package-build cdn publish`
|
|
|
139
139
|
is the one command that writes there:
|
|
140
140
|
|
|
141
141
|
```bash
|
|
142
|
-
package-build cdn publish --set
|
|
143
|
-
package-build cdn publish --set
|
|
142
|
+
package-build cdn publish --set alt-art.json --name "Thalorna — illustrated" --dry-run
|
|
143
|
+
package-build cdn publish --set alt-art.json --name "Thalorna — illustrated"
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
It uploads every icon and image flat, transformed as the stage transforms
|
|
147
147
|
them, skips what the CDN already holds at the same size, and writes
|
|
148
|
-
`<site.assets>/<contentPackage>/
|
|
148
|
+
`<site.assets>/<contentPackage>/alt-art.json` listing the left-out pictures.
|
|
149
149
|
Run it after the pictures or their provenance change; it is not part of a
|
|
150
150
|
build or a release.
|
|
151
151
|
|
package/docs/project-setup.md
CHANGED
|
@@ -30,8 +30,7 @@ acme-bestiary/
|
|
|
30
30
|
│ ├── config.json
|
|
31
31
|
│ └── *.md # one per unreleased change
|
|
32
32
|
├── .github/
|
|
33
|
-
│ ├──
|
|
34
|
-
│ ├── ISSUE_REPORTING.md # §3 lists the same labels
|
|
33
|
+
│ │ ├── ISSUE_REPORTING.md # how issues are filed
|
|
35
34
|
│ └── workflows/
|
|
36
35
|
├── assets/
|
|
37
36
|
│ └── content/ # authored Markdown notes
|
|
@@ -161,19 +160,18 @@ anything to remove.
|
|
|
161
160
|
|
|
162
161
|
### `lint:*` — the checks, one per question
|
|
163
162
|
|
|
164
|
-
|
|
163
|
+
Six checks, each answering one question and blind to what the others see.
|
|
165
164
|
They are separate scripts rather than one because a failing chain stops at its
|
|
166
165
|
first failure, and knowing _which_ question failed is most of the diagnosis.
|
|
167
166
|
|
|
168
167
|
```json
|
|
169
|
-
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:
|
|
168
|
+
"lint": "run-s lint:format lint:markdown lint:addresses lint:content-links lint:lang lint:changelog",
|
|
170
169
|
"lint:format": "package-build format",
|
|
171
170
|
"lint:markdown": "package-build markdown",
|
|
172
171
|
"lint:markdown:fix": "package-build markdown --fix",
|
|
173
172
|
"lint:addresses": "package-build lint",
|
|
174
173
|
"lint:content-links": "package-build links",
|
|
175
174
|
"lint:lang": "package-build lang check",
|
|
176
|
-
"lint:labels": "package-build labels check",
|
|
177
175
|
"lint:changelog": "package-build changelog check"
|
|
178
176
|
```
|
|
179
177
|
|
|
@@ -194,9 +192,6 @@ first failure, and knowing _which_ question failed is most of the diagnosis.
|
|
|
194
192
|
`package-build lang coverage` and `package-build lang hardcoded`; a
|
|
195
193
|
content-only package has nothing for those two to read, and each dies naming
|
|
196
194
|
the glob that matched nothing rather than passing vacuously.
|
|
197
|
-
- **`lint:labels`** — do `.github/labels.yml` and §3 of
|
|
198
|
-
`.github/ISSUE_REPORTING.md` still list the same labels? Neither derives from
|
|
199
|
-
the other, so nothing else notices when they drift.
|
|
200
195
|
- **`lint:changelog`** — does every pending `.changeset/*.md` read like a
|
|
201
196
|
release note rather than a pasted pull-request description? Passes
|
|
202
197
|
trivially when nothing is pending: no pull request is required to carry a
|
|
@@ -499,46 +494,9 @@ tool:
|
|
|
499
494
|
|
|
500
495
|
## `.github/`
|
|
501
496
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
source of truth, synced to GitHub by the org-wide `labels` action. A label on
|
|
506
|
-
GitHub that is not listed there is deleted on the next sync, which is what makes
|
|
507
|
-
the set closed and why nothing invents one.
|
|
508
|
-
|
|
509
|
-
```yaml
|
|
510
|
-
- name: documentation
|
|
511
|
-
color: "0075ca"
|
|
512
|
-
description: Documentation about this repository — README, process, authoring guides.
|
|
513
|
-
|
|
514
|
-
- name: devops
|
|
515
|
-
color: "e07b31"
|
|
516
|
-
description: Build, tooling, CI, release, repo config.
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
**`.github/ISSUE_REPORTING.md`** §3 is the same list in prose, for a human
|
|
520
|
-
filing an issue. `package-build labels check` compares the two and fails when
|
|
521
|
-
they disagree:
|
|
522
|
-
|
|
523
|
-
```console
|
|
524
|
-
$ npx package-build labels check
|
|
525
|
-
package-build: registry and §3 agree (2 labels).
|
|
526
|
-
```
|
|
527
|
-
|
|
528
|
-
Neither file derives from the other, so this check is the only thing standing
|
|
529
|
-
between them and a silent divergence. Editing the registry means editing both.
|
|
530
|
-
|
|
531
|
-
Labels are **subject matter only**. Work shape — bug, feature, epic, task, spike
|
|
532
|
-
— is an issue _type_, shared across the organization, so there is no `bug` label
|
|
533
|
-
and filtering on one returns nothing.
|
|
534
|
-
|
|
535
|
-
Neither file exists in a fresh repository, and `labels check` says so rather
|
|
536
|
-
than passing vacuously:
|
|
537
|
-
|
|
538
|
-
```console
|
|
539
|
-
$ npx package-build labels check
|
|
540
|
-
package-build: labels check: .github/labels.yml does not exist.
|
|
541
|
-
```
|
|
497
|
+
**`.github/ISSUE_REPORTING.md`** says how issues are filed. Labels are
|
|
498
|
+
organisation-level and defined in `HeroicLands/.github`, so the repository
|
|
499
|
+
carries no label registry of its own.
|
|
542
500
|
|
|
543
501
|
`.github/workflows/` then carries the repository's own CI. The org-wide
|
|
544
502
|
`HeroicLands/.github` repository supplies the shared composite actions those
|
|
@@ -549,17 +507,16 @@ site-deploy workflow, so a fix to any of them reaches every repository at once.
|
|
|
549
507
|
|
|
550
508
|
A summary, because "where does this come from" is the question that recurs.
|
|
551
509
|
|
|
552
|
-
| Read
|
|
553
|
-
|
|
|
554
|
-
| `package.json`
|
|
555
|
-
| `package-build.config.yaml`
|
|
556
|
-
| `assets/content/**/*.md`
|
|
557
|
-
| `.gitignore`
|
|
558
|
-
| `.prettierignore`
|
|
559
|
-
|
|
|
560
|
-
| `
|
|
561
|
-
| `.
|
|
562
|
-
| `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
|
|
510
|
+
| Read | By |
|
|
511
|
+
| ----------------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
512
|
+
| `package.json` | The Foundry package id, the version, the release addresses; the site's `baseURL`, description and author. |
|
|
513
|
+
| `package-build.config.yaml` | Everything else about the build. |
|
|
514
|
+
| `assets/content/**/*.md` | Every content command. |
|
|
515
|
+
| `.gitignore` | `package-build format`, `package-build markdown`. |
|
|
516
|
+
| `.prettierignore` | `package-build format`. |
|
|
517
|
+
| `lang/*.json` | `package-build lang`. |
|
|
518
|
+
| `.env.local` | `package-build deploy`. |
|
|
519
|
+
| `.github/workflows/build.yml` | The `pre-push` hook, for its step list. |
|
|
563
520
|
|
|
564
521
|
Everything written goes under `build/`. Nothing the toolchain generates is
|
|
565
522
|
committed.
|
package/engine/cdn-publish.mjs
CHANGED
|
@@ -179,7 +179,8 @@ export function cdnCredentials(env) {
|
|
|
179
179
|
*
|
|
180
180
|
* @typedef {object} CdnStore
|
|
181
181
|
* @property {(key: string) => Promise<number|undefined>} size - An object's
|
|
182
|
-
* size in bytes, `undefined` when there is none
|
|
182
|
+
* size in bytes, `undefined` when there is none or the store does not state
|
|
183
|
+
* it.
|
|
183
184
|
* @property {(key: string) => Promise<Buffer|undefined>} read - An object's
|
|
184
185
|
* bytes, `undefined` when there is none.
|
|
185
186
|
* @property {((key: string, body: Buffer, contentType: string) => Promise<void>)|undefined} write -
|
|
@@ -188,6 +189,26 @@ export function cdnCredentials(env) {
|
|
|
188
189
|
* secret.
|
|
189
190
|
*/
|
|
190
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Request headers that keep the CDN from compressing a HEAD answer: a
|
|
194
|
+
* gzip-encoded answer carries no content-length.
|
|
195
|
+
*/
|
|
196
|
+
const IDENTITY = { "accept-encoding": "identity" };
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* The object size a HEAD answer states.
|
|
200
|
+
*
|
|
201
|
+
* @param {Response} response - A successful HEAD response.
|
|
202
|
+
* @returns {number|undefined} The content-length, `undefined` when the answer
|
|
203
|
+
* states none.
|
|
204
|
+
*/
|
|
205
|
+
function contentLength(response) {
|
|
206
|
+
const header = response.headers.get("content-length");
|
|
207
|
+
if (header === null || header.trim() === "") return undefined;
|
|
208
|
+
const length = Number(header);
|
|
209
|
+
return Number.isFinite(length) && length >= 0 ? length : undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
191
212
|
/**
|
|
192
213
|
* Throw for a response the store refused.
|
|
193
214
|
*
|
|
@@ -224,10 +245,10 @@ export function s3Store({ endpoint, bucket, accessKeyId, secretAccessKey, fetch:
|
|
|
224
245
|
return {
|
|
225
246
|
describe: `bucket ${bucket} at ${new URL(endpoint).host}`,
|
|
226
247
|
async size(key) {
|
|
227
|
-
const response = await send(key, { method: "HEAD" });
|
|
248
|
+
const response = await send(key, { method: "HEAD", headers: IDENTITY });
|
|
228
249
|
if (response.status === 404) return undefined;
|
|
229
250
|
if (!response.ok) refused(`HEAD ${key}`, response);
|
|
230
|
-
return
|
|
251
|
+
return contentLength(response);
|
|
231
252
|
},
|
|
232
253
|
async read(key) {
|
|
233
254
|
const response = await send(key, { method: "GET" });
|
|
@@ -256,14 +277,18 @@ export function s3Store({ endpoint, bucket, accessKeyId, secretAccessKey, fetch:
|
|
|
256
277
|
*/
|
|
257
278
|
export function publicStore(host, fetchImpl) {
|
|
258
279
|
const base = host.replace(/\/+$/, "");
|
|
259
|
-
const get = (key, method) =>
|
|
280
|
+
const get = (key, method) =>
|
|
281
|
+
(fetchImpl ?? fetch)(`${base}/${key}`, {
|
|
282
|
+
method,
|
|
283
|
+
headers: method === "HEAD" ? IDENTITY : undefined,
|
|
284
|
+
});
|
|
260
285
|
return {
|
|
261
286
|
describe: `${base}, read-only`,
|
|
262
287
|
async size(key) {
|
|
263
288
|
const response = await get(key, "HEAD");
|
|
264
289
|
if (response.status === 404 || response.status === 403) return undefined;
|
|
265
290
|
if (!response.ok) refused(`HEAD ${key}`, response);
|
|
266
|
-
return
|
|
291
|
+
return contentLength(response);
|
|
267
292
|
},
|
|
268
293
|
async read(key) {
|
|
269
294
|
const response = await get(key, "GET");
|
package/engine/project-init.mjs
CHANGED
|
@@ -20,7 +20,6 @@ import prettier from "prettier";
|
|
|
20
20
|
import prettierConfig from "../prettier-config.mjs";
|
|
21
21
|
import { CONFIG_FILENAMES, configFromData } from "./pack-config.mjs";
|
|
22
22
|
import { positionOfYamlPath } from "./diagnostics.mjs";
|
|
23
|
-
import { checkLabelRegistry } from "../labels.mjs";
|
|
24
23
|
import { formatNoteFrontmatter } from "./note-format.mjs";
|
|
25
24
|
|
|
26
25
|
/** Package kinds the initializer can create. */
|
|
@@ -94,13 +93,12 @@ function projectScripts(answers) {
|
|
|
94
93
|
"serve:site": "run-s build:deps build:content-index build:site-content serve:site-html",
|
|
95
94
|
"serve:site-html": "hugo server --source build/hugo",
|
|
96
95
|
"build:book": "package-build pdf",
|
|
97
|
-
lint: "run-s lint:format lint:markdown lint:addresses lint:content-links
|
|
96
|
+
lint: "run-s lint:format lint:markdown lint:addresses lint:content-links",
|
|
98
97
|
"lint:format": "package-build format",
|
|
99
98
|
"lint:markdown": "package-build markdown",
|
|
100
99
|
"lint:markdown:fix": "package-build markdown --fix",
|
|
101
100
|
"lint:addresses": "package-build lint",
|
|
102
101
|
"lint:content-links": "package-build links",
|
|
103
|
-
"lint:labels": "package-build labels check",
|
|
104
102
|
format: "package-build format --write",
|
|
105
103
|
"format:check": "package-build format",
|
|
106
104
|
changeset: "changeset",
|
|
@@ -125,8 +123,7 @@ function projectScripts(answers) {
|
|
|
125
123
|
"push:qa": "package-build deploy qa",
|
|
126
124
|
"push:prod": "package-build deploy prod",
|
|
127
125
|
});
|
|
128
|
-
scripts.lint =
|
|
129
|
-
"run-s lint:format lint:markdown lint:lang lint:addresses lint:content-links lint:labels";
|
|
126
|
+
scripts.lint = "run-s lint:format lint:markdown lint:lang lint:addresses lint:content-links";
|
|
130
127
|
return scripts;
|
|
131
128
|
}
|
|
132
129
|
|
|
@@ -295,14 +292,6 @@ function projectText(answers) {
|
|
|
295
292
|
updateInternalDependencies: "patch",
|
|
296
293
|
ignore: [],
|
|
297
294
|
}),
|
|
298
|
-
".github/labels.yml": YAML.stringify([
|
|
299
|
-
{
|
|
300
|
-
name: "documentation",
|
|
301
|
-
color: "0075ca",
|
|
302
|
-
description: "Documentation and author guides.",
|
|
303
|
-
},
|
|
304
|
-
{ name: "devops", color: "e07b31", description: "Build, CI, and release work." },
|
|
305
|
-
]),
|
|
306
295
|
".github/ISSUE_REPORTING.md": [
|
|
307
296
|
"# Issue reporting",
|
|
308
297
|
"",
|
|
@@ -314,12 +303,9 @@ function projectText(answers) {
|
|
|
314
303
|
"",
|
|
315
304
|
"Use the organization issue type for a bug, feature, task, spike, or epic.",
|
|
316
305
|
"",
|
|
317
|
-
"## 3. Labels
|
|
306
|
+
"## 3. Labels",
|
|
318
307
|
"",
|
|
319
|
-
"
|
|
320
|
-
"| ----- | ---------- |",
|
|
321
|
-
"| `documentation` | Documentation and author guides. |",
|
|
322
|
-
"| `devops` | Build, CI, and release work. |",
|
|
308
|
+
"Labels are organization-level, defined in `HeroicLands/.github`.",
|
|
323
309
|
"",
|
|
324
310
|
].join("\n"),
|
|
325
311
|
".github/workflows/build.yml": [
|
|
@@ -437,7 +423,6 @@ export function checkProject(rootDir) {
|
|
|
437
423
|
"README.md",
|
|
438
424
|
"LICENSE.md",
|
|
439
425
|
".changeset/config.json",
|
|
440
|
-
".github/labels.yml",
|
|
441
426
|
".github/ISSUE_REPORTING.md",
|
|
442
427
|
".github/workflows/build.yml",
|
|
443
428
|
];
|
|
@@ -525,15 +510,5 @@ export function checkProject(rootDir) {
|
|
|
525
510
|
} else if (!fs.readdirSync(contentRoot).some((file) => file.endsWith(".md"))) {
|
|
526
511
|
add("assets/content", "the content tree has no notes");
|
|
527
512
|
}
|
|
528
|
-
const labelsPath = path.join(root, ".github/labels.yml");
|
|
529
|
-
const reportingPath = path.join(root, ".github/ISSUE_REPORTING.md");
|
|
530
|
-
if (fs.existsSync(labelsPath) && fs.existsSync(reportingPath)) {
|
|
531
|
-
const result = checkLabelRegistry({
|
|
532
|
-
registryText: fs.readFileSync(labelsPath, "utf8"),
|
|
533
|
-
docText: fs.readFileSync(reportingPath, "utf8"),
|
|
534
|
-
});
|
|
535
|
-
for (const finding of result.registry) add(".github/labels.yml", finding.message);
|
|
536
|
-
for (const finding of result.doc) add(".github/ISSUE_REPORTING.md", finding.message);
|
|
537
|
-
}
|
|
538
513
|
return findings;
|
|
539
514
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroiclands/package-build",
|
|
3
|
-
"version": "23.5.
|
|
3
|
+
"version": "23.5.1",
|
|
4
4
|
"description": "Shared toolchain for building and shipping a HeroicLands Foundry VTT package — content compilation, manifest, localization, staging, bundle, release and deployment.",
|
|
5
5
|
"license": "GPL-3.0-or-later",
|
|
6
6
|
"type": "module",
|
|
@@ -120,7 +120,6 @@
|
|
|
120
120
|
"hugo-theme",
|
|
121
121
|
"index.mjs",
|
|
122
122
|
"lang.mjs",
|
|
123
|
-
"labels.mjs",
|
|
124
123
|
"manifest.mjs",
|
|
125
124
|
"markdownlint-config.mjs",
|
|
126
125
|
"prettier-config.mjs",
|
|
@@ -192,11 +191,10 @@
|
|
|
192
191
|
"build:asset-index": "node ci/emit-asset-index.mjs",
|
|
193
192
|
"format": "node ci/prettier.mjs --write",
|
|
194
193
|
"format:check": "node ci/prettier.mjs --check",
|
|
195
|
-
"lint": "run-p --aggregate-output -c --max-parallel 4 format:check lint:markdown lint:yaml lint:
|
|
194
|
+
"lint": "run-p --aggregate-output -c --max-parallel 4 format:check lint:markdown lint:yaml lint:image-roles lint:banners lint:content-format lint:docs-links",
|
|
196
195
|
"lint:markdown": "node bin/package-build.mjs markdown",
|
|
197
196
|
"lint:docs-links": "node bin/package-build.mjs docs links",
|
|
198
197
|
"lint:yaml": "node bin/package-build.mjs yaml",
|
|
199
|
-
"lint:labels": "node bin/package-build.mjs labels check",
|
|
200
198
|
"lint:image-roles": "vitest run tests/theme-image-roles.test.ts",
|
|
201
199
|
"lint:banners": "node ci/check-banners.mjs",
|
|
202
200
|
"lint:markdown:fix": "node bin/package-build.mjs markdown --fix",
|
|
@@ -220,31 +220,6 @@ export const CDN_ENV: Readonly<{
|
|
|
220
220
|
accessKeyId: string;
|
|
221
221
|
secretAccessKey: string;
|
|
222
222
|
}>;
|
|
223
|
-
/**
|
|
224
|
-
* A store holding the CDN's objects.
|
|
225
|
-
*/
|
|
226
|
-
export type CdnStore = {
|
|
227
|
-
/**
|
|
228
|
-
* - An object's
|
|
229
|
-
* size in bytes, `undefined` when there is none.
|
|
230
|
-
*/
|
|
231
|
-
size: (key: string) => Promise<number | undefined>;
|
|
232
|
-
/**
|
|
233
|
-
* - An object's
|
|
234
|
-
* bytes, `undefined` when there is none.
|
|
235
|
-
*/
|
|
236
|
-
read: (key: string) => Promise<Buffer | undefined>;
|
|
237
|
-
/**
|
|
238
|
-
* -
|
|
239
|
-
* Store an object; absent on a read-only store.
|
|
240
|
-
*/
|
|
241
|
-
write: ((key: string, body: Buffer, contentType: string) => Promise<void>) | undefined;
|
|
242
|
-
/**
|
|
243
|
-
* - What the store is, for the log. Never a
|
|
244
|
-
* secret.
|
|
245
|
-
*/
|
|
246
|
-
describe: string;
|
|
247
|
-
};
|
|
248
223
|
/**
|
|
249
224
|
* One picture to publish.
|
|
250
225
|
*/
|
|
@@ -309,3 +284,29 @@ export type CdnPlan = {
|
|
|
309
284
|
*/
|
|
310
285
|
cdnRoot: string;
|
|
311
286
|
};
|
|
287
|
+
/**
|
|
288
|
+
* A store holding the CDN's objects.
|
|
289
|
+
*/
|
|
290
|
+
export type CdnStore = {
|
|
291
|
+
/**
|
|
292
|
+
* - An object's
|
|
293
|
+
* size in bytes, `undefined` when there is none or the store does not state
|
|
294
|
+
* it.
|
|
295
|
+
*/
|
|
296
|
+
size: (key: string) => Promise<number | undefined>;
|
|
297
|
+
/**
|
|
298
|
+
* - An object's
|
|
299
|
+
* bytes, `undefined` when there is none.
|
|
300
|
+
*/
|
|
301
|
+
read: (key: string) => Promise<Buffer | undefined>;
|
|
302
|
+
/**
|
|
303
|
+
* -
|
|
304
|
+
* Store an object; absent on a read-only store.
|
|
305
|
+
*/
|
|
306
|
+
write: ((key: string, body: Buffer, contentType: string) => Promise<void>) | undefined;
|
|
307
|
+
/**
|
|
308
|
+
* - What the store is, for the log. Never a
|
|
309
|
+
* secret.
|
|
310
|
+
*/
|
|
311
|
+
describe: string;
|
|
312
|
+
};
|
package/labels.mjs
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file is part of the Song of Heroic Lands (SoHL) system for Foundry VTT.
|
|
3
|
-
* Copyright (c) 2024-2026 Tom Rodriguez ("Toasty") — <toasty@heroiclands.org>
|
|
4
|
-
*
|
|
5
|
-
* This work is licensed under the GNU General Public License v3.0 (GPLv3).
|
|
6
|
-
* You may copy, modify, and distribute it under the terms of that license.
|
|
7
|
-
*
|
|
8
|
-
* For full terms, see the LICENSE.md file in the project root or visit:
|
|
9
|
-
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
10
|
-
*
|
|
11
|
-
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The label registry has two faces that must agree.
|
|
16
|
-
*
|
|
17
|
-
* `.github/labels.yml` is the machine source synced to GitHub, and the §3 table
|
|
18
|
-
* in `.github/ISSUE_REPORTING.md` is the documented reference a person reads.
|
|
19
|
-
* Neither is derived from the other, so either can drift — or invent a label
|
|
20
|
-
* the other has never heard of — and nothing notices until someone files an
|
|
21
|
-
* issue against a label that does not exist, or the sync pushes a label the
|
|
22
|
-
* documentation never mentions.
|
|
23
|
-
*
|
|
24
|
-
* **This lives here because every repository wants it and only the paths ever
|
|
25
|
-
* differed.** It was a `utils/check-labels.mjs` copied per repository, which is
|
|
26
|
-
* the shape a shared check takes just before it starts drifting between copies
|
|
27
|
-
* — the same argument that moved the no-attribution check to a shared action.
|
|
28
|
-
*
|
|
29
|
-
* The checks are pure and text-in: the caller reads the two files and decides
|
|
30
|
-
* what to do with the findings, so this module needs no filesystem and is
|
|
31
|
-
* testable without one.
|
|
32
|
-
*
|
|
33
|
-
* @module
|
|
34
|
-
*/
|
|
35
|
-
|
|
36
|
-
import { parse } from "yaml";
|
|
37
|
-
|
|
38
|
-
/** GitHub rejects a label description beyond this with a 422. */
|
|
39
|
-
export const MAX_DESCRIPTION = 100;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* The line a label's `name:` entry sits on, 1-based.
|
|
43
|
-
*
|
|
44
|
-
* Located rather than tracked, because `yaml`'s document API would have to be
|
|
45
|
-
* threaded through the parse for a value this simple to recover: a registry is
|
|
46
|
-
* a flat list of `- name: <label>`, so the first line declaring that name is
|
|
47
|
-
* the entry. A name that cannot be found yields no position, which
|
|
48
|
-
* `reportFindings` drops rather than guessing.
|
|
49
|
-
*
|
|
50
|
-
* @param {string} text - The registry file's contents.
|
|
51
|
-
* @param {string} name - The label name.
|
|
52
|
-
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
53
|
-
*/
|
|
54
|
-
function positionOfLabel(text, name) {
|
|
55
|
-
const lines = text.split("\n");
|
|
56
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
57
|
-
const m = lines[i].match(/^\s*-?\s*name:\s*["']?(.+?)["']?\s*$/);
|
|
58
|
-
if (m && m[1] === name) return { line: i + 1, column: lines[i].indexOf(name) + 1 };
|
|
59
|
-
}
|
|
60
|
-
return {};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* The line a label's row sits on in the documented table, 1-based.
|
|
65
|
-
*
|
|
66
|
-
* @param {string} text - The documentation file's contents.
|
|
67
|
-
* @param {string} name - The label name.
|
|
68
|
-
* @returns {{line?: number, column?: number}} Spreadable position fields.
|
|
69
|
-
*/
|
|
70
|
-
function positionOfDocRow(text, name) {
|
|
71
|
-
const lines = text.split("\n");
|
|
72
|
-
for (let i = 0; i < lines.length; i += 1) {
|
|
73
|
-
const m = lines[i].match(/^\|\s*`([a-z][a-z0-9-]*)`\s*\|/);
|
|
74
|
-
if (m && m[1] === name) return { line: i + 1, column: lines[i].indexOf(name) + 1 };
|
|
75
|
-
}
|
|
76
|
-
return {};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* The label names the documented §3 table lists.
|
|
81
|
-
*
|
|
82
|
-
* A registry row is a table row whose first cell is a backticked label name,
|
|
83
|
-
* which is narrow on purpose: §3 carries prose and other tables, and a looser
|
|
84
|
-
* match would read a heading or an example as a label.
|
|
85
|
-
*
|
|
86
|
-
* @param {string} text - The documentation file's contents.
|
|
87
|
-
* @returns {{names: Set<string>, found: boolean}} The names, and whether §3 was
|
|
88
|
-
* located at all — an absent section is a different failure from an empty one.
|
|
89
|
-
*/
|
|
90
|
-
export function documentedLabels(text) {
|
|
91
|
-
const lines = text.split("\n");
|
|
92
|
-
const start = lines.findIndex((l) => /^##\s+3\./.test(l));
|
|
93
|
-
if (start < 0) return { names: new Set(), found: false };
|
|
94
|
-
const after = lines.findIndex((l, i) => i > start && /^##\s+\d/.test(l));
|
|
95
|
-
const section = lines.slice(start, after < 0 ? lines.length : after);
|
|
96
|
-
const names = new Set();
|
|
97
|
-
for (const line of section) {
|
|
98
|
-
const m = line.match(/^\|\s*`([a-z][a-z0-9-]*)`\s*\|/);
|
|
99
|
-
if (m) names.add(m[1]);
|
|
100
|
-
}
|
|
101
|
-
return { names, found: true };
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check the machine registry against the documented table.
|
|
106
|
-
*
|
|
107
|
-
* Findings are returned per file rather than merged, because each names a
|
|
108
|
-
* position in a different document and the caller reports them against the
|
|
109
|
-
* file they belong to.
|
|
110
|
-
*
|
|
111
|
-
* @param {object} sources
|
|
112
|
-
* @param {string} sources.registryText - `.github/labels.yml`.
|
|
113
|
-
* @param {string} sources.docText - `.github/ISSUE_REPORTING.md`.
|
|
114
|
-
* @param {string} [sources.docPath] - The doc's path, for the message naming it.
|
|
115
|
-
* @returns {{registry: object[], doc: object[], count: number}} Findings for
|
|
116
|
-
* each file, and how many labels the registry declares.
|
|
117
|
-
*/
|
|
118
|
-
export function checkLabelRegistry({
|
|
119
|
-
registryText,
|
|
120
|
-
docText,
|
|
121
|
-
docPath = ".github/ISSUE_REPORTING.md",
|
|
122
|
-
}) {
|
|
123
|
-
const registryFindings = [];
|
|
124
|
-
const docFindings = [];
|
|
125
|
-
|
|
126
|
-
let entries;
|
|
127
|
-
try {
|
|
128
|
-
entries = parse(registryText);
|
|
129
|
-
} catch (error) {
|
|
130
|
-
registryFindings.push({
|
|
131
|
-
severity: "error",
|
|
132
|
-
message: `label registry is not valid YAML — ${error.message}`,
|
|
133
|
-
});
|
|
134
|
-
return { registry: registryFindings, doc: docFindings, count: 0 };
|
|
135
|
-
}
|
|
136
|
-
if (!Array.isArray(entries)) {
|
|
137
|
-
registryFindings.push({
|
|
138
|
-
severity: "error",
|
|
139
|
-
message: "label registry must be a list of `{ name, description }` entries",
|
|
140
|
-
});
|
|
141
|
-
return { registry: registryFindings, doc: docFindings, count: 0 };
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Caught here rather than mid-sync, where it surfaces as a bare 422 from
|
|
145
|
-
// the GitHub API naming neither the label nor the limit.
|
|
146
|
-
for (const entry of entries) {
|
|
147
|
-
const length = (entry?.description ?? "").length;
|
|
148
|
-
if (length > MAX_DESCRIPTION) {
|
|
149
|
-
registryFindings.push({
|
|
150
|
-
...positionOfLabel(registryText, entry.name),
|
|
151
|
-
severity: "error",
|
|
152
|
-
message:
|
|
153
|
-
`"${entry.name}" has a ${length}-character description; ` +
|
|
154
|
-
`GitHub rejects anything over ${MAX_DESCRIPTION}`,
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const registryNames = new Set(entries.map((e) => e?.name).filter(Boolean));
|
|
160
|
-
const { names: docNames, found } = documentedLabels(docText);
|
|
161
|
-
if (!found) {
|
|
162
|
-
docFindings.push({
|
|
163
|
-
severity: "error",
|
|
164
|
-
message: `no §3 section found in ${docPath}, so the registry has nothing to agree with`,
|
|
165
|
-
});
|
|
166
|
-
return { registry: registryFindings, doc: docFindings, count: registryNames.size };
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
for (const name of registryNames) {
|
|
170
|
-
if (docNames.has(name)) continue;
|
|
171
|
-
registryFindings.push({
|
|
172
|
-
...positionOfLabel(registryText, name),
|
|
173
|
-
severity: "error",
|
|
174
|
-
message:
|
|
175
|
-
`"${name}" is in the registry but not in ${docPath} §3; ` +
|
|
176
|
-
`the two are edited together, so add the row`,
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
for (const name of docNames) {
|
|
180
|
-
if (registryNames.has(name)) continue;
|
|
181
|
-
docFindings.push({
|
|
182
|
-
...positionOfDocRow(docText, name),
|
|
183
|
-
severity: "error",
|
|
184
|
-
message:
|
|
185
|
-
`"${name}" is documented in §3 but absent from the registry, ` +
|
|
186
|
-
`so it is never synced and an issue cannot carry it`,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
return { registry: registryFindings, doc: docFindings, count: registryNames.size };
|
|
191
|
-
}
|
package/types/labels.d.mts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The label names the documented §3 table lists.
|
|
3
|
-
*
|
|
4
|
-
* A registry row is a table row whose first cell is a backticked label name,
|
|
5
|
-
* which is narrow on purpose: §3 carries prose and other tables, and a looser
|
|
6
|
-
* match would read a heading or an example as a label.
|
|
7
|
-
*
|
|
8
|
-
* @param {string} text - The documentation file's contents.
|
|
9
|
-
* @returns {{names: Set<string>, found: boolean}} The names, and whether §3 was
|
|
10
|
-
* located at all — an absent section is a different failure from an empty one.
|
|
11
|
-
*/
|
|
12
|
-
export function documentedLabels(text: string): {
|
|
13
|
-
names: Set<string>;
|
|
14
|
-
found: boolean;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Check the machine registry against the documented table.
|
|
18
|
-
*
|
|
19
|
-
* Findings are returned per file rather than merged, because each names a
|
|
20
|
-
* position in a different document and the caller reports them against the
|
|
21
|
-
* file they belong to.
|
|
22
|
-
*
|
|
23
|
-
* @param {object} sources
|
|
24
|
-
* @param {string} sources.registryText - `.github/labels.yml`.
|
|
25
|
-
* @param {string} sources.docText - `.github/ISSUE_REPORTING.md`.
|
|
26
|
-
* @param {string} [sources.docPath] - The doc's path, for the message naming it.
|
|
27
|
-
* @returns {{registry: object[], doc: object[], count: number}} Findings for
|
|
28
|
-
* each file, and how many labels the registry declares.
|
|
29
|
-
*/
|
|
30
|
-
export function checkLabelRegistry({ registryText, docText, docPath, }: {
|
|
31
|
-
registryText: string;
|
|
32
|
-
docText: string;
|
|
33
|
-
docPath?: string | undefined;
|
|
34
|
-
}): {
|
|
35
|
-
registry: object[];
|
|
36
|
-
doc: object[];
|
|
37
|
-
count: number;
|
|
38
|
-
};
|
|
39
|
-
/** GitHub rejects a label description beyond this with a 422. */
|
|
40
|
-
export const MAX_DESCRIPTION: 100;
|