@kungfu-tech/buildchain 2.10.3 → 2.10.4
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/README.md +1 -0
- package/actions/promote-buildchain-ref/README.md +10 -7
- package/dist/site/badge-endpoint-registry.json +160 -0
- package/dist/site/badges/v1/kfd-4/aligned.json +14 -0
- package/dist/site/badges/v1/kfd-4/declared.json +14 -0
- package/dist/site/badges/v1/kfd-4/downgraded.json +14 -0
- package/dist/site/badges/v1/kfd-4/draft.json +14 -0
- package/dist/site/badges/v1/kfd-4/failed.json +14 -0
- package/dist/site/badges/v1/kfd-4/missing.json +14 -0
- package/dist/site/badges/v1/kfd-4/passed.json +14 -0
- package/dist/site/badges/v1/kfd-4/planned.json +14 -0
- package/dist/site/buildchain-contract.json +13 -13
- package/dist/site/buildchain-site.json +24 -24
- package/dist/site/capability-registry.json +1 -1
- package/dist/site/kfd-claims.json +4 -4
- package/dist/site/manual-registry.json +5 -5
- package/dist/site/node-api-registry.json +9 -9
- package/dist/site/page-registry.json +14 -14
- package/dist/site/public-surface-audit.json +1 -1
- package/dist/site/site-manifest.json +9 -9
- package/docs/readme-badges.md +14 -10
- package/docs/release-candidate.md +4 -1
- package/docs/release-governance.md +5 -2
- package/docs/release-passport.md +5 -2
- package/docs/reusable-build-surface.md +5 -2
- package/package.json +2 -2
- package/packages/core/artifact-passport.js +37 -11
- package/packages/core/badges.js +1 -0
- package/packages/core/buildchain-contract.js +1 -1
- package/packages/core/buildchain-kfd-claims.js +1 -1
- package/packages/core/index.js +1 -0
- package/packages/core/readme-badges.js +98 -39
- package/packages/core/release-passport.js +1 -0
- package/scripts/check-inventory.mjs +2 -0
- package/scripts/generate-site-bundle.mjs +11 -6
- package/scripts/web-surface-production-release-pr.mjs +88 -3
- package/scripts/web-surface.mjs +39 -1
|
@@ -2063,7 +2063,7 @@
|
|
|
2063
2063
|
"pageRegistryPath": "dist/site/page-registry.json",
|
|
2064
2064
|
"cliRegistryDigest": "281c186e9dbdf45e55e8afcd59cfee66518f42c146d1b79dfd8837048c00bec8",
|
|
2065
2065
|
"workflowRegistryDigest": "b24b2a68ae83941cbb6a719a62b4eafbd0f5e859e5810ae766ce9a5db69ddfae",
|
|
2066
|
-
"pageRegistryDigest": "
|
|
2066
|
+
"pageRegistryDigest": "3f1337aa8d9a1ef1d9c607612db76705479d5a1b270360d001dfcbb1a68a69cb"
|
|
2067
2067
|
},
|
|
2068
2068
|
"comparison": {
|
|
2069
2069
|
"missingCliRegistry": [],
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"contract": "kungfu-buildchain-site-manifest",
|
|
4
|
-
"generatedAt": "2026-07-
|
|
5
|
-
"publishedAt": "2026-07-
|
|
4
|
+
"generatedAt": "2026-07-08T12:25:38.748Z",
|
|
5
|
+
"publishedAt": "2026-07-08T12:25:38.748Z",
|
|
6
6
|
"reproducible": true,
|
|
7
7
|
"timestampPolicy": "ci-injected",
|
|
8
8
|
"deterministicInputs": [
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"declared Buildchain surface manifest contract"
|
|
20
20
|
],
|
|
21
21
|
"sourceDateEpoch": "0",
|
|
22
|
-
"sourceRevision": "
|
|
22
|
+
"sourceRevision": "2c2936458d8b1d9af67f4421af00a647de6284bb",
|
|
23
23
|
"timestampPolicyDetails": {
|
|
24
24
|
"contract": "kungfu-buildchain-surface-timestamp-policy",
|
|
25
25
|
"timestampFields": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
},
|
|
38
38
|
"package": {
|
|
39
39
|
"name": "@kungfu-tech/buildchain",
|
|
40
|
-
"version": "2.10.
|
|
40
|
+
"version": "2.10.4",
|
|
41
41
|
"versionSource": "package.json#version"
|
|
42
42
|
},
|
|
43
43
|
"entrypoint": "buildchain-site.json",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"path": "docs/release-passport.md",
|
|
70
70
|
"plane": "verify",
|
|
71
71
|
"exists": true,
|
|
72
|
-
"digest": "sha256:
|
|
72
|
+
"digest": "sha256:ca764740841b364ff7cfea79b87221d29c09df09fee6d19e78ed4b0b173bfad3"
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
"id": "release-candidate",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"path": "docs/release-candidate.md",
|
|
78
78
|
"plane": "verify",
|
|
79
79
|
"exists": true,
|
|
80
|
-
"digest": "sha256:
|
|
80
|
+
"digest": "sha256:391f14569c7d2a43dd9986c6a29462d674a77338dac6a4ac89c79a4c3d6c4947"
|
|
81
81
|
},
|
|
82
82
|
{
|
|
83
83
|
"id": "release-propagation",
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"path": "docs/readme-badges.md",
|
|
94
94
|
"plane": "use",
|
|
95
95
|
"exists": true,
|
|
96
|
-
"digest": "sha256:
|
|
96
|
+
"digest": "sha256:ddaf49a7d6e6c11aea8d21d1ba736159897e16207cd74af0044014425faf8a2c"
|
|
97
97
|
},
|
|
98
98
|
{
|
|
99
99
|
"id": "homebrew",
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"path": "docs/reusable-build-surface.md",
|
|
206
206
|
"plane": "use",
|
|
207
207
|
"exists": true,
|
|
208
|
-
"digest": "sha256:
|
|
208
|
+
"digest": "sha256:a3f979fcede4450dabe5b8d6182395993c9f28559f7176b071a6ff9db565a355"
|
|
209
209
|
},
|
|
210
210
|
{
|
|
211
211
|
"id": "publish-transaction",
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
"path": "docs/release-governance.md",
|
|
222
222
|
"plane": "why",
|
|
223
223
|
"exists": true,
|
|
224
|
-
"digest": "sha256:
|
|
224
|
+
"digest": "sha256:69218a4749170e1f84b82f211d57187a7ff33f1a510aced587e240d1d901b9e7"
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
"id": "release-flow",
|
package/docs/readme-badges.md
CHANGED
|
@@ -24,6 +24,7 @@ Use the public package export:
|
|
|
24
24
|
import {
|
|
25
25
|
collectBadgeBundleFacts,
|
|
26
26
|
collectReadmeBadgeFacts,
|
|
27
|
+
createKfdBadgeSpecsFromStandards,
|
|
27
28
|
renderBadgeBundleBlock,
|
|
28
29
|
renderReadmeBadgeBlock,
|
|
29
30
|
checkBadgeBundleBlock,
|
|
@@ -60,9 +61,11 @@ The Node API is the implementation source. The CLI delegates to it.
|
|
|
60
61
|
`collectBadgeBundleFacts({ cwd, claims })` is the trust-badge bundle API. It
|
|
61
62
|
uses the same repository facts, but returns contract
|
|
62
63
|
`kungfu-buildchain-badge-bundle-facts` and only renders the Buildchain trust
|
|
63
|
-
claims:
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
claims: every active `kfd-*` standard discovered from KFD standards metadata,
|
|
65
|
+
plus `release-passport`. KFD-1, KFD-2, KFD-3, KFD-4, and release passport are
|
|
66
|
+
enabled by default with current KFD metadata. Callers can pass
|
|
67
|
+
`claims: "kfd-1,release-passport"` or an array to narrow the bundle without
|
|
68
|
+
hand-writing badge Markdown.
|
|
66
69
|
|
|
67
70
|
## CLI
|
|
68
71
|
|
|
@@ -114,11 +117,12 @@ kfd_standards = "node_modules/@kungfu-tech/kfd/standards.json"
|
|
|
114
117
|
kfd_1 = "declared"
|
|
115
118
|
kfd_2 = "planned"
|
|
116
119
|
kfd_3 = "aligned"
|
|
120
|
+
kfd_4 = "declared"
|
|
117
121
|
platforms = ["macOS", "Linux", "Windows"]
|
|
118
122
|
workflows = ["verify.yml", "build.yml"]
|
|
119
123
|
|
|
120
124
|
[badges.bundle]
|
|
121
|
-
claims = ["kfd-1", "kfd-2", "kfd-3", "release-passport"]
|
|
125
|
+
claims = ["kfd-1", "kfd-2", "kfd-3", "kfd-4", "release-passport"]
|
|
122
126
|
```
|
|
123
127
|
|
|
124
128
|
`release_passport` may be a local path or URL. If omitted, Buildchain tries
|
|
@@ -161,12 +165,12 @@ may display a KFD state as `passed`.
|
|
|
161
165
|
|
|
162
166
|
## KFD Badge Rules
|
|
163
167
|
|
|
164
|
-
KFD passed is evidence-backed. A repository may display `KFD-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
`@kungfu-tech/kfd/standards.json`.
|
|
168
|
+
KFD passed is evidence-backed. A repository may display `KFD-N passed` only when
|
|
169
|
+
its own release passport verifies successfully and the corresponding passport
|
|
170
|
+
section has `status: "passed"`. The KFD badge vocabulary comes from KFD
|
|
171
|
+
standards metadata, not Buildchain private strings: for example KFD-2 uses the
|
|
172
|
+
`releaseTrustPassport` concept and KFD-4 uses the `observerPerspective` concept
|
|
173
|
+
from `@kungfu-tech/kfd/standards.json`.
|
|
170
174
|
|
|
171
175
|
When no release passport exists yet, or when the passport cannot be verified,
|
|
172
176
|
Buildchain downgrades each KFD badge to the explicit local declaration such as
|
|
@@ -84,12 +84,15 @@ generation scripts.
|
|
|
84
84
|
|
|
85
85
|
By default, the wrapper forwards GitHub Release publication to the underlying
|
|
86
86
|
`promote-buildchain-ref` semver model. Once the release transaction is complete,
|
|
87
|
-
the action creates or updates the
|
|
87
|
+
the action creates or updates the public GitHub Release, applies
|
|
88
88
|
prerelease/latest metadata from the semver tag, and uploads the publish evidence
|
|
89
89
|
file together with the generated release passport assets. This keeps
|
|
90
90
|
npm/registry publication, Buildchain release passport persistence, and
|
|
91
91
|
`release.published` propagation in one declarative reusable workflow. Consumers
|
|
92
92
|
that do not publish GitHub Releases can opt out with `github-release: false`.
|
|
93
|
+
For anchored/manual package releases, the public GitHub Release tag defaults to
|
|
94
|
+
`v<publishedVersion>` while the internal transaction exact tag remains recorded
|
|
95
|
+
in the release passport.
|
|
93
96
|
|
|
94
97
|
Products that publish KFD release trust evidence can keep that path declarative
|
|
95
98
|
too. Pass KFD-1 self contract witnesses, KFD-2 public claim files, and KFD-3
|
|
@@ -479,10 +479,13 @@ consumer shell glue. Consumers normally use the `release-candidate-promote.yml`
|
|
|
479
479
|
wrapper, where GitHub Release publication is enabled by default and can be
|
|
480
480
|
disabled with `github-release: false`; the wrapper passes that declaration to
|
|
481
481
|
the action. After the publish transaction reaches `complete`, Buildchain creates
|
|
482
|
-
or updates the
|
|
482
|
+
or updates the public GitHub Release and uploads the generated
|
|
483
483
|
`buildchain.release.json`, release-passport assets, and publish evidence. Semver
|
|
484
484
|
prerelease tags are marked `prerelease=true` and `make_latest=false`; stable
|
|
485
|
-
semver tags are marked latest.
|
|
485
|
+
semver tags are marked latest. For anchored/manual package releases, the public
|
|
486
|
+
release tag defaults to `v<publishedVersion>` while the internal exact
|
|
487
|
+
transaction tag remains in the release passport and release-state ref. This is
|
|
488
|
+
the supported path for downstream
|
|
486
489
|
`release.published` propagation across semver, major, and promote-only release
|
|
487
490
|
candidate publication models.
|
|
488
491
|
|
package/docs/release-passport.md
CHANGED
|
@@ -34,8 +34,11 @@ digests, trusted publishing evidence, and transaction result.
|
|
|
34
34
|
|
|
35
35
|
Additive passport sections:
|
|
36
36
|
|
|
37
|
-
- `release`:
|
|
38
|
-
|
|
37
|
+
- `release`: public release tag, internal transaction tag, line, channel,
|
|
38
|
+
source SHA, target ref, release SHA, release material SHA, publish tooling
|
|
39
|
+
SHA, and durable release-state ref. Anchored/manual package releases use the
|
|
40
|
+
published package version for the public tag while preserving the internal
|
|
41
|
+
exact transaction tag for Buildchain recovery.
|
|
39
42
|
- `versionImpact`: final patch/minor/major classification, source, and
|
|
40
43
|
rationale.
|
|
41
44
|
- `surfaceImpacts`: per registered surface classification. The final impact is
|
|
@@ -628,13 +628,16 @@ resolution and before publish. A compatible `v2` drift leaves an issue in the
|
|
|
628
628
|
consumer repository but does not trigger a second heavy build; an incompatible
|
|
629
629
|
drift fails before publish side effects.
|
|
630
630
|
|
|
631
|
-
The wrapper publishes the
|
|
631
|
+
The wrapper publishes the public release tag as a GitHub Release by default.
|
|
632
632
|
After `promote-buildchain-ref` reports a complete release transaction, the
|
|
633
|
-
wrapper creates or updates the
|
|
633
|
+
wrapper creates or updates the public release, marks semver prerelease tags
|
|
634
634
|
such as `v1.2.3-alpha.0`, `v1.2.3-rc.1`, or `v22.22.3-kf.3-alpha.7` as
|
|
635
635
|
`prerelease=true` and `make_latest=false`, marks stable semver tags as latest,
|
|
636
636
|
and uploads the publish evidence file plus every file in the generated release
|
|
637
637
|
passport directory, including `buildchain.release.json` and `check-report.json`.
|
|
638
|
+
For anchored/manual package releases, the public release tag is derived from the
|
|
639
|
+
published package version and the internal exact transaction tag remains
|
|
640
|
+
available in the release passport.
|
|
638
641
|
Consumers do not need to hand-write `gh release` logic to trigger
|
|
639
642
|
`release.published` propagation. Set `github-release: false` only for
|
|
640
643
|
repositories that intentionally do not maintain GitHub Releases.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kungfu-tech/buildchain",
|
|
3
|
-
"version": "2.10.
|
|
3
|
+
"version": "2.10.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Buildchain Release Passport, release governance, CLI toolkit, and site facts.",
|
|
6
6
|
"repository": "https://github.com/kungfu-systems/buildchain",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"vitest": "^4.1.9"
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
|
-
"@kungfu-tech/kfd": "1.0.0-alpha.
|
|
92
|
+
"@kungfu-tech/kfd": "1.0.0-alpha.19",
|
|
93
93
|
"smol-toml": "1.7.0"
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -297,14 +297,31 @@ function embeddedPointerCandidates(subject) {
|
|
|
297
297
|
: [];
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
-
function
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
if (!repository || !tag) {
|
|
300
|
+
function releaseTagForArtifactVersion(version = "") {
|
|
301
|
+
const value = optionalString(version).trim();
|
|
302
|
+
if (!value) {
|
|
304
303
|
return "";
|
|
305
304
|
}
|
|
305
|
+
return value.startsWith("v") ? value : `v${value}`;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function githubReleasePassportLocations(subject, options) {
|
|
309
|
+
const repository = options.repository || subject.githubRelease?.repository || "";
|
|
310
|
+
if (!repository) {
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
const tags = [
|
|
314
|
+
subject.githubRelease?.tag,
|
|
315
|
+
releaseTagForArtifactVersion(subject.version),
|
|
316
|
+
options.tag,
|
|
317
|
+
].map(optionalString).filter(Boolean);
|
|
318
|
+
const uniqueTags = [...new Set(tags)];
|
|
306
319
|
const baseUrl = (options.githubReleaseBaseUrl || "https://github.com").replace(/\/$/, "");
|
|
307
|
-
return
|
|
320
|
+
return uniqueTags.map((tag) => ({
|
|
321
|
+
tag,
|
|
322
|
+
repository,
|
|
323
|
+
location: `${baseUrl}/${repository}/releases/download/${encodeURIComponent(tag)}/buildchain.release.json`,
|
|
324
|
+
}));
|
|
308
325
|
}
|
|
309
326
|
|
|
310
327
|
export async function resolveArtifactSubject(subject, {
|
|
@@ -455,18 +472,27 @@ export async function discoverArtifactPassport({
|
|
|
455
472
|
return found("local-config-index", matched.passportLocation, { locator: matched.locator });
|
|
456
473
|
}
|
|
457
474
|
}
|
|
458
|
-
const
|
|
459
|
-
|
|
475
|
+
const githubLocations = githubReleasePassportLocations(subject, { repository, tag, githubReleaseBaseUrl });
|
|
476
|
+
for (const githubLocation of githubLocations) {
|
|
460
477
|
try {
|
|
461
|
-
await readJsonFromLocation(githubLocation);
|
|
462
|
-
attempts.push({
|
|
463
|
-
|
|
478
|
+
await readJsonFromLocation(githubLocation.location);
|
|
479
|
+
attempts.push({
|
|
480
|
+
method: "github-release-default",
|
|
481
|
+
location: githubLocation.location,
|
|
482
|
+
status: "found",
|
|
483
|
+
details: { repository: githubLocation.repository, tag: githubLocation.tag },
|
|
484
|
+
});
|
|
485
|
+
return found("github-release-default", githubLocation.location, {
|
|
486
|
+
repository: githubLocation.repository,
|
|
487
|
+
tag: githubLocation.tag,
|
|
488
|
+
});
|
|
464
489
|
} catch (error) {
|
|
465
490
|
attempts.push({
|
|
466
491
|
method: "github-release-default",
|
|
467
|
-
location: githubLocation,
|
|
492
|
+
location: githubLocation.location,
|
|
468
493
|
status: "miss",
|
|
469
494
|
error: error.message,
|
|
495
|
+
details: { repository: githubLocation.repository, tag: githubLocation.tag },
|
|
470
496
|
});
|
|
471
497
|
}
|
|
472
498
|
}
|
package/packages/core/badges.js
CHANGED
|
@@ -207,7 +207,7 @@ export function createBuildchainContractWorld({ root = process.cwd(), packageJso
|
|
|
207
207
|
"protected release refs and durable release-state are finalized by Buildchain",
|
|
208
208
|
"release passport finalization is idempotent after publish side effects",
|
|
209
209
|
"publish transactions can require a resolved publish-gate source lock to prevent floating-ref drift",
|
|
210
|
-
"semver GitHub Releases are created or updated only after transaction completion, with prerelease/latest metadata derived from the
|
|
210
|
+
"semver GitHub Releases are created or updated only after transaction completion, with prerelease/latest metadata derived from the public release tag",
|
|
211
211
|
],
|
|
212
212
|
}),
|
|
213
213
|
surface(root, {
|
|
@@ -353,7 +353,7 @@ export function createBuildchainPublicClaimDefinitions() {
|
|
|
353
353
|
},
|
|
354
354
|
{
|
|
355
355
|
id: "claim:buildchain-semver-github-release",
|
|
356
|
-
claim: "Semver promotion can create or update
|
|
356
|
+
claim: "Semver promotion can create or update the public GitHub Release only after the publish transaction completes, and uploads Buildchain release passport/evidence assets while preserving the internal transaction exact tag.",
|
|
357
357
|
sourcePaths: [
|
|
358
358
|
"actions/promote-buildchain-ref/index.js",
|
|
359
359
|
"actions/promote-buildchain-ref/action.yml",
|
package/packages/core/index.js
CHANGED
|
@@ -18,19 +18,22 @@ export const BADGE_BUNDLE_DEFAULT_CLAIMS = [
|
|
|
18
18
|
"kfd-1",
|
|
19
19
|
"kfd-2",
|
|
20
20
|
"kfd-3",
|
|
21
|
+
"kfd-4",
|
|
21
22
|
"release-passport",
|
|
22
23
|
];
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
+
const KFD_FALLBACK_KEYS = [
|
|
25
26
|
{ key: "kfd-1", id: "kfd1", label: "KFD-1", text: "contract world" },
|
|
26
27
|
{ key: "kfd-2", id: "kfd2", label: "KFD-2", text: "trust passport" },
|
|
27
28
|
{ key: "kfd-3", id: "kfd3", label: "KFD-3", text: "collaboration interface" },
|
|
29
|
+
{ key: "kfd-4", id: "kfd4", label: "KFD-4", text: "timeline observer" },
|
|
28
30
|
];
|
|
29
31
|
|
|
30
32
|
const KFD_BADGE_CONCEPTS = {
|
|
31
33
|
"kfd-1": "contractWorld",
|
|
32
34
|
"kfd-2": "releaseTrustPassport",
|
|
33
35
|
"kfd-3": "collaborationInterface",
|
|
36
|
+
"kfd-4": "observerPerspective",
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
const requireFromHere = createRequire(import.meta.url);
|
|
@@ -47,23 +50,7 @@ const STATE_COLORS = {
|
|
|
47
50
|
unknown: "6e7781",
|
|
48
51
|
};
|
|
49
52
|
|
|
50
|
-
const
|
|
51
|
-
"kfd-1",
|
|
52
|
-
"kfd-2",
|
|
53
|
-
"kfd-3",
|
|
54
|
-
"buildchain-release-passport",
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const BADGE_BUNDLE_CLAIM_ALIASES = {
|
|
58
|
-
kfd1: "kfd-1",
|
|
59
|
-
"kfd_1": "kfd-1",
|
|
60
|
-
"kfd-1": "kfd-1",
|
|
61
|
-
kfd2: "kfd-2",
|
|
62
|
-
"kfd_2": "kfd-2",
|
|
63
|
-
"kfd-2": "kfd-2",
|
|
64
|
-
kfd3: "kfd-3",
|
|
65
|
-
"kfd_3": "kfd-3",
|
|
66
|
-
"kfd-3": "kfd-3",
|
|
53
|
+
const STATIC_BADGE_BUNDLE_CLAIM_ALIASES = {
|
|
67
54
|
passport: "release-passport",
|
|
68
55
|
"release_passport": "release-passport",
|
|
69
56
|
"release-passport": "release-passport",
|
|
@@ -136,10 +123,20 @@ function hostedBadgeUrl({ badgeConfig = {}, id, state }) {
|
|
|
136
123
|
return `${badgeEndpointBaseUrl(badgeConfig)}/${encodeURIComponent(id)}/${encodeURIComponent(state || "unknown")}.svg`;
|
|
137
124
|
}
|
|
138
125
|
|
|
139
|
-
export function
|
|
126
|
+
export function createKfdBadgeSpecsFromStandards(standardsMetadata) {
|
|
127
|
+
return kfdBadgeSpecs(summarizeKfdStandards({
|
|
128
|
+
value: standardsMetadata && typeof standardsMetadata === "object" ? standardsMetadata : undefined,
|
|
129
|
+
source: "provided",
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function createReadmeBadgeEndpointRegistry({ kfdSpecs = undefined, kfdStandards = undefined } = {}) {
|
|
134
|
+
const effectiveKfdSpecs = Array.isArray(kfdSpecs) && kfdSpecs.length > 0
|
|
135
|
+
? kfdSpecs
|
|
136
|
+
: createKfdBadgeSpecsFromStandards(kfdStandards);
|
|
140
137
|
const states = Object.keys(STATE_COLORS).filter((state) => state !== "unknown");
|
|
141
138
|
const specs = [
|
|
142
|
-
...
|
|
139
|
+
...effectiveKfdSpecs.map((entry) => ({
|
|
143
140
|
id: entry.key,
|
|
144
141
|
label: entry.label,
|
|
145
142
|
messageTemplate: `${entry.text} {state}`,
|
|
@@ -211,15 +208,55 @@ function splitClaimList(value) {
|
|
|
211
208
|
return [];
|
|
212
209
|
}
|
|
213
210
|
|
|
214
|
-
function
|
|
211
|
+
function fallbackKfdEntryForKey(key) {
|
|
212
|
+
return KFD_FALLBACK_KEYS.find((entry) => entry.key === key) || {};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function normalizeKfdKey(value) {
|
|
216
|
+
const normalized = String(value || "").trim().toLowerCase().replace(/_/g, "-");
|
|
217
|
+
const match = normalized.match(/^kfd-?(\d+)$/);
|
|
218
|
+
return match ? `kfd-${match[1]}` : normalized;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function kfdSortValue(key) {
|
|
222
|
+
const match = String(key || "").match(/^kfd-(\d+)$/);
|
|
223
|
+
return match ? Number(match[1]) : Number.MAX_SAFE_INTEGER;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function sortedKfdKeys(keys = []) {
|
|
227
|
+
return [...new Set(keys.map(normalizeKfdKey).filter((key) => /^kfd-\d+$/.test(key)))]
|
|
228
|
+
.sort((left, right) => {
|
|
229
|
+
const numeric = kfdSortValue(left) - kfdSortValue(right);
|
|
230
|
+
return numeric || left.localeCompare(right);
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function defaultBadgeBundleClaimsForKfd(kfdSpecs = KFD_FALLBACK_KEYS) {
|
|
235
|
+
const claims = sortedKfdKeys(kfdSpecs.map((entry) => entry.key));
|
|
236
|
+
return [...claims, "release-passport"];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function badgeBundleClaimAliases(kfdSpecs = KFD_FALLBACK_KEYS) {
|
|
240
|
+
const aliases = { ...STATIC_BADGE_BUNDLE_CLAIM_ALIASES };
|
|
241
|
+
for (const key of sortedKfdKeys(kfdSpecs.map((entry) => entry.key))) {
|
|
242
|
+
const number = key.replace(/^kfd-/, "");
|
|
243
|
+
aliases[key] = key;
|
|
244
|
+
aliases[`kfd${number}`] = key;
|
|
245
|
+
aliases[`kfd_${number}`] = key;
|
|
246
|
+
}
|
|
247
|
+
return aliases;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function normalizeBadgeBundleClaims(value, fallback = BADGE_BUNDLE_DEFAULT_CLAIMS, kfdSpecs = KFD_FALLBACK_KEYS) {
|
|
215
251
|
const selected = [];
|
|
216
252
|
const unknown = [];
|
|
253
|
+
const aliases = badgeBundleClaimAliases(kfdSpecs);
|
|
217
254
|
for (const claim of splitClaimList(value)) {
|
|
218
255
|
const key = String(claim || "").trim().toLowerCase();
|
|
219
256
|
if (!key) {
|
|
220
257
|
continue;
|
|
221
258
|
}
|
|
222
|
-
const normalized =
|
|
259
|
+
const normalized = aliases[key.replace(/_/g, "-")] || aliases[key];
|
|
223
260
|
if (!normalized) {
|
|
224
261
|
unknown.push(key);
|
|
225
262
|
continue;
|
|
@@ -234,13 +271,18 @@ function normalizeBadgeBundleClaims(value, fallback = BADGE_BUNDLE_DEFAULT_CLAIM
|
|
|
234
271
|
return selected.length > 0 ? selected : [...fallback];
|
|
235
272
|
}
|
|
236
273
|
|
|
237
|
-
function badgeBundleConfig(badgeConfig = {}) {
|
|
274
|
+
function badgeBundleConfig(badgeConfig = {}, kfdSpecs = KFD_FALLBACK_KEYS) {
|
|
238
275
|
const configured = badgeConfig.bundle && typeof badgeConfig.bundle === "object"
|
|
239
276
|
? badgeConfig.bundle
|
|
240
277
|
: {};
|
|
278
|
+
const defaultClaims = defaultBadgeBundleClaimsForKfd(kfdSpecs);
|
|
241
279
|
return {
|
|
242
280
|
enabled: configured.enabled === undefined ? true : Boolean(configured.enabled),
|
|
243
|
-
claims: normalizeBadgeBundleClaims(
|
|
281
|
+
claims: normalizeBadgeBundleClaims(
|
|
282
|
+
configured.claims || badgeConfig.bundle_claims || badgeConfig.bundleClaims,
|
|
283
|
+
defaultClaims,
|
|
284
|
+
kfdSpecs,
|
|
285
|
+
),
|
|
244
286
|
mode: String(configured.mode || "readme"),
|
|
245
287
|
hosted: configured.hosted === undefined ? true : Boolean(configured.hosted),
|
|
246
288
|
};
|
|
@@ -320,6 +362,13 @@ function summarizeKfdStandards(loaded) {
|
|
|
320
362
|
const standards = loaded?.value?.standards && typeof loaded.value.standards === "object"
|
|
321
363
|
? loaded.value.standards
|
|
322
364
|
: {};
|
|
365
|
+
const discoveredKeys = sortedKfdKeys(Object.keys(standards).filter((key) => {
|
|
366
|
+
const standard = standards[key];
|
|
367
|
+
return standard && typeof standard === "object" && standard.status !== "retired";
|
|
368
|
+
}));
|
|
369
|
+
const keys = discoveredKeys.length > 0
|
|
370
|
+
? discoveredKeys
|
|
371
|
+
: sortedKfdKeys(KFD_FALLBACK_KEYS.map((entry) => entry.key));
|
|
323
372
|
const summary = {
|
|
324
373
|
contract: loaded?.value?.contract || "",
|
|
325
374
|
schemaVersion: loaded?.value?.schemaVersion || undefined,
|
|
@@ -331,20 +380,22 @@ function summarizeKfdStandards(loaded) {
|
|
|
331
380
|
error: loaded?.error || "",
|
|
332
381
|
standards: {},
|
|
333
382
|
};
|
|
334
|
-
for (const
|
|
335
|
-
const
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
383
|
+
for (const key of keys) {
|
|
384
|
+
const fallback = fallbackKfdEntryForKey(key);
|
|
385
|
+
const standard = standards[key] || {};
|
|
386
|
+
const conceptKey = KFD_BADGE_CONCEPTS[key] || Object.keys(standard.interfaces || {})[0] || Object.keys(standard.concepts || {})[0] || "";
|
|
387
|
+
const label = standard.label || fallback.label || String(key).toUpperCase();
|
|
388
|
+
summary.standards[key] = {
|
|
389
|
+
key: standard.key || key,
|
|
390
|
+
id: standard.id || fallback.id || label,
|
|
391
|
+
label,
|
|
341
392
|
title: standard.title || "",
|
|
342
393
|
status: standard.status || "",
|
|
343
394
|
revision: standard.revision || undefined,
|
|
344
395
|
documentUrl: standard.document?.url || "",
|
|
345
396
|
documentPath: standard.document?.path || "",
|
|
346
397
|
documentSha256: standard.document?.sha256 || "",
|
|
347
|
-
badgeText: standard.concepts?.[conceptKey] || fallback.text,
|
|
398
|
+
badgeText: standard.concepts?.[conceptKey] || fallback.text || label,
|
|
348
399
|
interfaceContract: conceptKey ? standard.interfaces?.[conceptKey]?.contract || "" : "",
|
|
349
400
|
schemaId: conceptKey ? standard.interfaces?.[conceptKey]?.schemaId || standard.schemaIds?.[conceptKey] || "" : "",
|
|
350
401
|
};
|
|
@@ -353,12 +404,15 @@ function summarizeKfdStandards(loaded) {
|
|
|
353
404
|
}
|
|
354
405
|
|
|
355
406
|
function kfdBadgeSpecs(kfdStandards) {
|
|
356
|
-
|
|
357
|
-
|
|
407
|
+
const keys = sortedKfdKeys(Object.keys(kfdStandards?.standards || {}));
|
|
408
|
+
return keys.map((key) => {
|
|
409
|
+
const fallback = fallbackKfdEntryForKey(key);
|
|
410
|
+
const standard = kfdStandards?.standards?.[key] || {};
|
|
358
411
|
return {
|
|
359
412
|
...fallback,
|
|
413
|
+
key,
|
|
360
414
|
label: standard.label || fallback.label,
|
|
361
|
-
text: standard.badgeText || fallback.text,
|
|
415
|
+
text: standard.badgeText || fallback.text || standard.label || key.toUpperCase(),
|
|
362
416
|
title: standard.title || "",
|
|
363
417
|
standardDocumentUrl: standard.documentUrl || "",
|
|
364
418
|
standardDocumentSha256: standard.documentSha256 || "",
|
|
@@ -664,7 +718,8 @@ export async function collectReadmeBadgeFacts({ cwd = process.cwd() } = {}) {
|
|
|
664
718
|
: releasePassportLocation;
|
|
665
719
|
const releaseState = releasePassportState({ report, error, badgeConfig });
|
|
666
720
|
const kfdStandards = summarizeKfdStandards(await loadKfdStandards({ cwd: resolvedCwd, badgeConfig }));
|
|
667
|
-
const
|
|
721
|
+
const kfdSpecs = kfdBadgeSpecs(kfdStandards);
|
|
722
|
+
const kfd = kfdSpecs.map((entry) => {
|
|
668
723
|
const passed = kfdSectionPassed({ passport, report, key: entry.key });
|
|
669
724
|
const state = passed ? "passed" : declaredKfdState({ badgeConfig, key: entry.key, id: entry.id });
|
|
670
725
|
return {
|
|
@@ -719,10 +774,13 @@ export async function collectReadmeBadgeFacts({ cwd = process.cwd() } = {}) {
|
|
|
719
774
|
badgeRuntime: {
|
|
720
775
|
provider: "buildchain-hosted",
|
|
721
776
|
endpointBaseUrl: badgeEndpointBaseUrl(badgeConfig),
|
|
722
|
-
hostedBadgeIds:
|
|
777
|
+
hostedBadgeIds: [
|
|
778
|
+
...defaultBadgeBundleClaimsForKfd(kfdSpecs).filter((claim) => claim !== "release-passport"),
|
|
779
|
+
"buildchain-release-passport",
|
|
780
|
+
],
|
|
723
781
|
logoPolicy: BUILDCHAIN_BADGE_LOGO_PLACEHOLDER,
|
|
724
782
|
},
|
|
725
|
-
badgeBundle: badgeBundleConfig(badgeConfig),
|
|
783
|
+
badgeBundle: badgeBundleConfig(badgeConfig, kfdSpecs),
|
|
726
784
|
badges: [],
|
|
727
785
|
};
|
|
728
786
|
facts.badges = buildBadgeEntries(facts, { badgeConfig });
|
|
@@ -733,7 +791,7 @@ export async function collectBadgeBundleFacts({ cwd = process.cwd(), claims = un
|
|
|
733
791
|
const readmeFacts = await collectReadmeBadgeFacts({ cwd });
|
|
734
792
|
const config = {
|
|
735
793
|
...readmeFacts.badgeBundle,
|
|
736
|
-
claims: normalizeBadgeBundleClaims(claims, readmeFacts.badgeBundle.claims),
|
|
794
|
+
claims: normalizeBadgeBundleClaims(claims, readmeFacts.badgeBundle.claims, readmeFacts.kfd),
|
|
737
795
|
};
|
|
738
796
|
const badges = buildBadgeBundleEntries(readmeFacts, config);
|
|
739
797
|
return {
|
|
@@ -746,6 +804,7 @@ export async function collectBadgeBundleFacts({ cwd = process.cwd(), claims = un
|
|
|
746
804
|
sourceFactsSha256: hashText(JSON.stringify(readmeFacts)),
|
|
747
805
|
policy: {
|
|
748
806
|
defaultClaims: [...BADGE_BUNDLE_DEFAULT_CLAIMS],
|
|
807
|
+
discoveredDefaultClaims: defaultBadgeBundleClaimsForKfd(readmeFacts.kfd),
|
|
749
808
|
claims: config.claims,
|
|
750
809
|
hosted: config.hosted,
|
|
751
810
|
mode: config.mode,
|
|
@@ -1032,6 +1032,7 @@ export function createReleasePassport({
|
|
|
1032
1032
|
},
|
|
1033
1033
|
release: {
|
|
1034
1034
|
tag: normalizedTag,
|
|
1035
|
+
publicTag: optionalString(release.publicTag || release.public_tag || normalizedTag),
|
|
1035
1036
|
internalTag: optionalString(release.internalTag || release.internal_tag || normalizedTag),
|
|
1036
1037
|
internalVersion,
|
|
1037
1038
|
publishedVersion,
|
|
@@ -271,6 +271,7 @@ for (const requiredSnippet of [
|
|
|
271
271
|
"renderReadmeBadgeBlock",
|
|
272
272
|
"checkReadmeBadgeBlock",
|
|
273
273
|
"updateReadmeBadgeBlock",
|
|
274
|
+
"createKfdBadgeSpecsFromStandards",
|
|
274
275
|
"createReadmeBadgeEndpointRegistry",
|
|
275
276
|
]) {
|
|
276
277
|
if (!coreIndexSource.includes(requiredSnippet)) {
|
|
@@ -761,6 +762,7 @@ for (const requiredSnippet of [
|
|
|
761
762
|
"github-release:",
|
|
762
763
|
"github-release-title:",
|
|
763
764
|
"github-release-notes:",
|
|
765
|
+
"public-release-tag:",
|
|
764
766
|
"github-release-url:",
|
|
765
767
|
"github-release-action:",
|
|
766
768
|
]) {
|