@rightkit/git 0.2.26 → 0.2.28
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 +41 -25
- package/manifest.mjs +50 -2
- package/manifest.schema.json +12 -0
- package/manifest.test.mjs +17 -0
- package/package.json +1 -1
- package/render.test.mjs +21 -0
- package/rightkit-ownership.json +15 -0
- package/templates/profiles/rust-hybrid/ci.yml +1 -0
- package/templates/publish-npm.yml +23 -3
package/README.md
CHANGED
|
@@ -1,43 +1,59 @@
|
|
|
1
1
|
# @rightkit/git
|
|
2
2
|
|
|
3
|
-
Public-
|
|
3
|
+
Public-repository-only GitHub Actions workflow layer for Right Suite products. The CLI `right-git` renders and installs CI lanes into `.github/workflows/` from a `.rightgit.json` manifest.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
| Item | Value |
|
|
6
|
+
|---|---|
|
|
7
|
+
| Where it runs | Build and release tooling (repository CI setup) |
|
|
8
|
+
| Rust pair | None |
|
|
9
|
+
| Version | see INDEX.md |
|
|
10
|
+
| License | MIT OR Apache-2.0 |
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
## Commands
|
|
8
13
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
- `right-git install <repo> --lanes ci,...` writes `.rightgit.json` and renders workflows.
|
|
15
|
+
- `right-git sync` re-renders installed workflows from the manifest.
|
|
16
|
+
- `right-git drift --all` checks installed workflows against templates and re-checks visibility.
|
|
17
|
+
- `right-git adopt` imports existing workflows into the template library.
|
|
18
|
+
- `right-git uninstall` removes every managed file.
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
`install` and `sync` refuse private, internal and unconfirmed-visibility repositories. There is no override flag.
|
|
14
21
|
|
|
15
|
-
|
|
16
|
-
- **`right-git drift`** — detect divergence between installed workflows and templates, and orphaned managed workflows.
|
|
17
|
-
- **`right-git adopt`** — import existing workflows into the template library.
|
|
18
|
-
- **`right-git uninstall`** — remove every managed file, leaving zero behind.
|
|
22
|
+
## Optional QA evidence upload (rust-hybrid)
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
Set `qaEvidencePath` in `.rightgit.json` to upload the gate's QA output as a workflow artifact. The path is relative to `$RUNNER_TEMP`; the rendered step uses `${{ runner.temp }}/<path>`. Absolute paths and expression syntax are rejected.
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"profile": "rust-hybrid",
|
|
29
|
+
"gate": { "path": "scripts/gate.sh" },
|
|
30
|
+
"qaEvidencePath": "pulse-hub-qa",
|
|
31
|
+
"qaEvidenceRetentionDays": 14
|
|
32
|
+
}
|
|
33
|
+
```
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
- The step runs after the gate with `if: always()` and `if-no-files-found: ignore`.
|
|
36
|
+
- Artifact name: `qa-evidence-${{ matrix.os || runner.os }}-${{ github.run_attempt }}`.
|
|
37
|
+
- `qaEvidenceRetentionDays` is an integer from 1 to 90 and defaults to 7. It requires `qaEvidencePath`.
|
|
38
|
+
- Omit the key and the rendered `ci.yml` is unchanged.
|
|
25
39
|
|
|
26
|
-
|
|
27
|
-
- **Exception, disclosed:** the optional, opt-in `publish-package-managers` lane *does* require two owner-provisioned, long-lived tokens in GitHub Actions `secrets.*` (`HOMEBREW_TAP_TOKEN`, `WINGET_CREATE_GITHUB_TOKEN`) to open tap/manifest PRs. These are real bearer tokens, public-repo-scoped by the visibility gate but not zero-exposure. The "no signing credential in CI" invariant is specifically about code-signing material; it does not mean "no credential of any kind."
|
|
40
|
+
## Entry points
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
No importable API. The package has no `main` and no `exports` map. The supported surface is the `right-git` bin.
|
|
30
43
|
|
|
31
|
-
##
|
|
44
|
+
## Peer dependencies
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
npm install -g @rightkit/git # or: npx @rightkit/git --help
|
|
35
|
-
```
|
|
46
|
+
None. No runtime dependencies (Node builtins only).
|
|
36
47
|
|
|
37
|
-
|
|
48
|
+
## Example
|
|
38
49
|
|
|
39
|
-
|
|
50
|
+
```sh
|
|
51
|
+
pnpm add -D @rightkit/git
|
|
52
|
+
pnpm exec right-git install . --lanes ci
|
|
53
|
+
pnpm exec right-git drift --all
|
|
54
|
+
```
|
|
40
55
|
|
|
41
|
-
|
|
56
|
+
## Known issues
|
|
42
57
|
|
|
43
|
-
|
|
58
|
+
- The optional `publish-package-managers` lane needs two long-lived tokens in GitHub Actions secrets. Their names and scope are in the package templates, and they are not zero-exposure. Read `docs/rules/github-access.md` in the workspace before enabling it.
|
|
59
|
+
- Manifest fields are charset-checked before they reach workflow YAML.
|
package/manifest.mjs
CHANGED
|
@@ -59,9 +59,17 @@ const TOP_LEVEL_KEYS = new Set([
|
|
|
59
59
|
"profile",
|
|
60
60
|
"gate",
|
|
61
61
|
"rustCache",
|
|
62
|
+
"qaEvidencePath",
|
|
63
|
+
"qaEvidenceRetentionDays",
|
|
62
64
|
"candidate",
|
|
63
65
|
"ownership",
|
|
64
66
|
]);
|
|
67
|
+
// Optional QA evidence upload (rust-hybrid ci only). qaEvidencePath is a safe
|
|
68
|
+
// relative path under $RUNNER_TEMP; it renders as `${{ runner.temp }}/<path>`.
|
|
69
|
+
const QA_EVIDENCE_DEFAULT_RETENTION_DAYS = 7;
|
|
70
|
+
const QA_EVIDENCE_MAX_RETENTION_DAYS = 90;
|
|
71
|
+
// Pinned to the same upload-artifact release the rust-hybrid release-candidate template uses.
|
|
72
|
+
const UPLOAD_ARTIFACT_PIN = "actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4";
|
|
65
73
|
const GATE_PATH_RE = /^(?!.*\.\.)(?!.*\s)[A-Za-z0-9][A-Za-z0-9._/-]*$/;
|
|
66
74
|
const RUST_WORKSPACE_PATH_RE = /^(?:\.|(?!.*\.\.)(?!.*\s)[A-Za-z0-9][A-Za-z0-9._/-]*)$/;
|
|
67
75
|
const PACKAGE_SCRIPT_RE = /^[A-Za-z0-9][A-Za-z0-9:_-]*$/;
|
|
@@ -75,8 +83,11 @@ const NATIVE_RUNNER_TARGETS = new Map([
|
|
|
75
83
|
["macos-15-intel", ["macos", "x86_64"]],
|
|
76
84
|
]);
|
|
77
85
|
|
|
86
|
+
// Hosted `macos-*` runners plus the self-hosted Xcode runner labels (`xcode-27`).
|
|
87
|
+
const MACOS_RUNNER_RE = /^(macos|xcode)-/i;
|
|
88
|
+
|
|
78
89
|
function inferredTarget(os) {
|
|
79
|
-
const platform = /^windows-/i.test(os) ? "windows" :
|
|
90
|
+
const platform = /^windows-/i.test(os) ? "windows" : MACOS_RUNNER_RE.test(os) ? "macos" : "linux";
|
|
80
91
|
return { os, platform, architecture: platform === "macos" ? "arm64" : "x86_64" };
|
|
81
92
|
}
|
|
82
93
|
|
|
@@ -160,6 +171,21 @@ export function validateManifest(manifest) {
|
|
|
160
171
|
errors.push("gate is only valid for profile 'rust-hybrid'");
|
|
161
172
|
}
|
|
162
173
|
|
|
174
|
+
if (manifest.qaEvidencePath !== undefined) {
|
|
175
|
+
if (profile !== "rust-hybrid") {
|
|
176
|
+
errors.push("qaEvidencePath is only valid for profile 'rust-hybrid'");
|
|
177
|
+
} else if (typeof manifest.qaEvidencePath !== "string" || !GATE_PATH_RE.test(manifest.qaEvidencePath)) {
|
|
178
|
+
errors.push("qaEvidencePath must be a safe relative path under $RUNNER_TEMP (no '..', whitespace, leading '/', or expression syntax)");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (manifest.qaEvidenceRetentionDays !== undefined) {
|
|
182
|
+
if (manifest.qaEvidencePath === undefined) {
|
|
183
|
+
errors.push("qaEvidenceRetentionDays requires qaEvidencePath");
|
|
184
|
+
} else if (!Number.isInteger(manifest.qaEvidenceRetentionDays) || manifest.qaEvidenceRetentionDays < 1 || manifest.qaEvidenceRetentionDays > QA_EVIDENCE_MAX_RETENTION_DAYS) {
|
|
185
|
+
errors.push(`qaEvidenceRetentionDays must be an integer from 1 to ${QA_EVIDENCE_MAX_RETENTION_DAYS}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
163
189
|
const rustCache = manifest.rustCache;
|
|
164
190
|
if (rustCache !== undefined) {
|
|
165
191
|
if (profile !== "rust-hybrid") {
|
|
@@ -230,7 +256,7 @@ export function validateManifest(manifest) {
|
|
|
230
256
|
if (nativeTarget && (target.platform !== nativeTarget[0] || target.architecture !== nativeTarget[1])) {
|
|
231
257
|
errors.push(`candidate.targets[] must declare ${target.os} as ${nativeTarget[0]}/${nativeTarget[1]}`);
|
|
232
258
|
}
|
|
233
|
-
if ((target.platform === "windows" && !/^windows-/i.test(target.os)) || (target.platform === "macos" &&
|
|
259
|
+
if ((target.platform === "windows" && !/^windows-/i.test(target.os)) || (target.platform === "macos" && !MACOS_RUNNER_RE.test(target.os))) {
|
|
234
260
|
errors.push("candidate.targets[].os must match its Windows or macOS platform");
|
|
235
261
|
}
|
|
236
262
|
const targetKey = `${target.os}|${target.platform}|${target.architecture}`;
|
|
@@ -1004,6 +1030,27 @@ export function ownershipStep(manifest, lane = "ci") {
|
|
|
1004
1030
|
return `\n - name: RightKit ownership (protected registry roots)\n shell: bash\n run: test "$(git rev-parse HEAD)" = "${sourceRevision}" && ${command}`;
|
|
1005
1031
|
}
|
|
1006
1032
|
|
|
1033
|
+
/**
|
|
1034
|
+
* Optional QA evidence upload, rendered after the rust-hybrid gate step.
|
|
1035
|
+
* Returns "" when qaEvidencePath is unset, so the rendered workflow stays
|
|
1036
|
+
* byte-identical to a manifest without the key.
|
|
1037
|
+
*/
|
|
1038
|
+
export function qaEvidenceStep(manifest) {
|
|
1039
|
+
if (manifest.qaEvidencePath === undefined) return "";
|
|
1040
|
+
const retentionDays = manifest.qaEvidenceRetentionDays ?? QA_EVIDENCE_DEFAULT_RETENTION_DAYS;
|
|
1041
|
+
return [
|
|
1042
|
+
" - name: Upload QA evidence",
|
|
1043
|
+
" if: always()",
|
|
1044
|
+
` uses: ${UPLOAD_ARTIFACT_PIN}`,
|
|
1045
|
+
" with:",
|
|
1046
|
+
" name: qa-evidence-${{ matrix.os || runner.os }}-${{ github.run_attempt }}",
|
|
1047
|
+
` path: \${{ runner.temp }}/${manifest.qaEvidencePath}`,
|
|
1048
|
+
` retention-days: ${retentionDays}`,
|
|
1049
|
+
" if-no-files-found: ignore",
|
|
1050
|
+
"",
|
|
1051
|
+
].join("\n");
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1007
1054
|
export function buildRenderContext(manifest, repoPath, lane = "ci") {
|
|
1008
1055
|
const pm = parsePackageManager(manifest.packageManager);
|
|
1009
1056
|
const projectName = deriveProjectName(repoPath);
|
|
@@ -1031,6 +1078,7 @@ export function buildRenderContext(manifest, repoPath, lane = "ci") {
|
|
|
1031
1078
|
MACOS_SIGNING_JOB: macosSigningJob(manifest, pm, artifactPrefix),
|
|
1032
1079
|
RELEASE_CHAIN_JOBS: releaseChainJobs(manifest, pm, artifactPrefix),
|
|
1033
1080
|
OWNERSHIP_STEP: ownershipStep(manifest, lane),
|
|
1081
|
+
QA_EVIDENCE_STEP: qaEvidenceStep(manifest),
|
|
1034
1082
|
};
|
|
1035
1083
|
|
|
1036
1084
|
// Every key below is populated ONLY when its source manifest section is
|
package/manifest.schema.json
CHANGED
|
@@ -116,6 +116,18 @@
|
|
|
116
116
|
"type": "object", "required": ["path"], "additionalProperties": false,
|
|
117
117
|
"properties": { "path": { "type": "string", "pattern": "^(?!.*\\.\\.)(?!.*\\s)[A-Za-z0-9][A-Za-z0-9._/-]*$" } }
|
|
118
118
|
},
|
|
119
|
+
"qaEvidencePath": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"description": "rust-hybrid only. Safe relative path under $RUNNER_TEMP where the gate writes QA evidence; rendered as ${{ runner.temp }}/<path> and uploaded with if: always().",
|
|
122
|
+
"pattern": "^(?!.*\\.\\.)(?!.*\\s)[A-Za-z0-9][A-Za-z0-9._/-]*$"
|
|
123
|
+
},
|
|
124
|
+
"qaEvidenceRetentionDays": {
|
|
125
|
+
"type": "integer",
|
|
126
|
+
"minimum": 1,
|
|
127
|
+
"maximum": 90,
|
|
128
|
+
"default": 7,
|
|
129
|
+
"description": "rust-hybrid only, requires qaEvidencePath. Artifact retention in days."
|
|
130
|
+
},
|
|
119
131
|
"candidate": {
|
|
120
132
|
"type": "object",
|
|
121
133
|
"required": ["buildScript", "checkScript", "admissionScript", "stageSummaryScript", "artifactRoot", "os"],
|
package/manifest.test.mjs
CHANGED
|
@@ -207,6 +207,9 @@ test("candidate targets permit native multi-architecture matrices independent of
|
|
|
207
207
|
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, targets: [{ os: "windows-2025", platform: "windows", architecture: "riscv64" }] } }).valid, false);
|
|
208
208
|
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, targets: [{ os: "windows-11-arm", platform: "windows", architecture: "x86_64" }] } }).valid, false);
|
|
209
209
|
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, targets: [{ os: "ubuntu-24.04", platform: "windows", architecture: "x86_64" }] } }).valid, false);
|
|
210
|
+
const xcodeRunner = { ...multiArchitecture, candidate: { ...multiArchitecture.candidate, os: ["xcode-27"], targets: [{ os: "xcode-27", platform: "macos", architecture: "arm64" }], signWindows: false } };
|
|
211
|
+
assert.equal(validateManifest(xcodeRunner).valid, true, JSON.stringify(validateManifest(xcodeRunner).errors));
|
|
212
|
+
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, os: ["ubuntu-24.04"], targets: [{ os: "ubuntu-24.04", platform: "macos", architecture: "arm64" }] } }).valid, false);
|
|
210
213
|
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, targets: [{ os: "${{ secrets.X }}", platform: "windows", architecture: "x86_64" }] } }).valid, false);
|
|
211
214
|
assert.equal(validateManifest({ ...multiArchitecture, candidate: { ...multiArchitecture.candidate, releaseChain: { windowsFinalizeScript: "release:finalize:win", macosFinalizeScript: "release:finalize:mac", installedQualificationScript: "release:qualify", publishScript: "release:publish", qualificationRunnerVariable: "QUALIFICATION_RUNNER_LABEL" } } }).valid, false);
|
|
212
215
|
});
|
|
@@ -389,3 +392,17 @@ test("F15 non-regression: realistic homebrew/winget/matrix values still validate
|
|
|
389
392
|
});
|
|
390
393
|
assert.equal(result.valid, true, JSON.stringify(result.errors));
|
|
391
394
|
});
|
|
395
|
+
|
|
396
|
+
test("qaEvidencePath and qaEvidenceRetentionDays are optional, rust-hybrid only, and charset-checked", () => {
|
|
397
|
+
const valid = { ...VALID_MINIMAL, profile: "rust-hybrid", gate: { path: "scripts/gate.sh" } };
|
|
398
|
+
assert.equal(validateManifest({ ...valid, qaEvidencePath: "pulse-hub-qa" }).valid, true);
|
|
399
|
+
assert.equal(validateManifest({ ...valid, qaEvidencePath: "pulse-hub-qa", qaEvidenceRetentionDays: 30 }).valid, true);
|
|
400
|
+
for (const qaEvidencePath of ["/abs", "../up", "pulse hub", "${{ runner.temp }}/x", "a;b", "", null]) {
|
|
401
|
+
assert.equal(validateManifest({ ...valid, qaEvidencePath }).valid, false, String(qaEvidencePath));
|
|
402
|
+
}
|
|
403
|
+
for (const qaEvidenceRetentionDays of [0, 91, 1.5, "7"]) {
|
|
404
|
+
assert.equal(validateManifest({ ...valid, qaEvidencePath: "pulse-hub-qa", qaEvidenceRetentionDays }).valid, false, String(qaEvidenceRetentionDays));
|
|
405
|
+
}
|
|
406
|
+
assert.equal(validateManifest({ ...valid, qaEvidenceRetentionDays: 7 }).valid, false, "retention requires a path");
|
|
407
|
+
assert.equal(validateManifest({ ...VALID_MINIMAL, qaEvidencePath: "pulse-hub-qa" }).valid, false, "node profile rejects qaEvidencePath");
|
|
408
|
+
});
|
package/package.json
CHANGED
package/render.test.mjs
CHANGED
|
@@ -829,3 +829,24 @@ test("ci.yml ownership check cannot be removed by deleting optional repo metadat
|
|
|
829
829
|
const without = renderTemplate(template, buildRenderContext(structuredClone(FULL_MANIFEST), "/repos/widgets"));
|
|
830
830
|
assert.match(without, /RightKit ownership \(protected registry roots\)/);
|
|
831
831
|
});
|
|
832
|
+
|
|
833
|
+
test("rust-hybrid ci uploads QA evidence after the gate only when qaEvidencePath is set", () => {
|
|
834
|
+
const base = { ...FULL_MANIFEST, lanes: ["ci"], profile: "rust-hybrid", gate: { path: "scripts/gate.sh" }, templateVersion: "1.1.0" };
|
|
835
|
+
const template = fs.readFileSync(path.join(templatesDir, "profiles/rust-hybrid/ci.yml"), "utf8");
|
|
836
|
+
|
|
837
|
+
const unset = renderTemplate(template, buildRenderContext(structuredClone(base), "/repos/widgets"));
|
|
838
|
+
assert.doesNotMatch(unset, /upload-artifact|QA evidence|runner\.temp/);
|
|
839
|
+
assert.match(unset, /CARGO_PROFILE_TEST_DEBUG: "0"\n$/);
|
|
840
|
+
|
|
841
|
+
const set = renderTemplate(template, buildRenderContext({ ...structuredClone(base), qaEvidencePath: "pulse-hub-qa" }, "/repos/widgets"));
|
|
842
|
+
assert.ok(set.indexOf("bash ./scripts/gate.sh") < set.indexOf("- name: Upload QA evidence"), "upload must follow the gate step");
|
|
843
|
+
assert.match(
|
|
844
|
+
set,
|
|
845
|
+
/ - name: Upload QA evidence\n if: always\(\)\n uses: actions\/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4\n with:\n name: qa-evidence-\$\{\{ matrix\.os \|\| runner\.os \}\}-\$\{\{ github\.run_attempt \}\}\n path: \$\{\{ runner\.temp \}\}\/pulse-hub-qa\n retention-days: 7\n if-no-files-found: ignore\n$/,
|
|
846
|
+
);
|
|
847
|
+
// The upload step is the only difference between the two renders.
|
|
848
|
+
assert.equal(set.replace(/ - name: Upload QA evidence[\s\S]*$/, ""), unset);
|
|
849
|
+
|
|
850
|
+
const custom = renderTemplate(template, buildRenderContext({ ...structuredClone(base), qaEvidencePath: "pulse-hub-qa", qaEvidenceRetentionDays: 30 }, "/repos/widgets"));
|
|
851
|
+
assert.match(custom, /retention-days: 30\n/);
|
|
852
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"package": "@rightkit/git",
|
|
3
|
+
"capabilities": [
|
|
4
|
+
{
|
|
5
|
+
"capability": "github-workflows",
|
|
6
|
+
"summary": "Public-repo-only GitHub Actions workflow layer: templates, visibility gate, sync/drift/adopt/uninstall for right-git.; declaration-only detection (no precise implementation fingerprint)",
|
|
7
|
+
"fingerprints": [],
|
|
8
|
+
"detection": "declaration-only",
|
|
9
|
+
"policyNotes": [
|
|
10
|
+
"Package contract retained; dependency declarations do not verify integration.",
|
|
11
|
+
"No precise implementation fingerprint; declaration-only inventory, never enforced ownership or verified integration."
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
]
|
|
15
|
+
}
|
|
@@ -43,10 +43,30 @@ ${PACKAGE_MANAGER_SETUP}
|
|
|
43
43
|
${OWNERSHIP_STEP}
|
|
44
44
|
- run: node scripts/release/build-candidate.mjs --version "${{ github.event.release.tag_name }}" --platform current --out "release/candidates/publish"
|
|
45
45
|
- run: node scripts/release/check-release.mjs release/candidates/publish
|
|
46
|
+
# check-release must supply publication-evidence.json with artifactSha256.
|
|
47
|
+
# Capture gated bytes before publication; never hash the source path during readback.
|
|
46
48
|
- id: tarball
|
|
47
49
|
shell: bash
|
|
48
50
|
run: |
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
51
|
+
node --input-type=module <<'NODE'
|
|
52
|
+
import fs from 'node:fs';
|
|
53
|
+
import { prepareNpmPublication } from '@rightkit/release/publication-gate.mjs';
|
|
54
|
+
const root = 'release/candidates/publish';
|
|
55
|
+
const c = JSON.parse(fs.readFileSync(`${root}/compatibility.json`));
|
|
56
|
+
const tarball = `${root}/${c.packageName.replace(/^@/, '').replace('/', '-')}-${c.version}.tgz`;
|
|
57
|
+
const proof = prepareNpmPublication({ name: c.packageName, version: c.version, tarball, evidencePath: `${root}/publication-evidence.json` });
|
|
58
|
+
// Only safe single-line metadata may enter GitHub outputs.
|
|
59
|
+
for (const value of [tarball, proof.name, proof.version]) if (/[\r\n]/.test(value)) throw new Error('unsafe npm publication identity');
|
|
60
|
+
fs.appendFileSync(process.env.GITHUB_OUTPUT, `path=${tarball}\nname=${proof.name}\nversion=${proof.version}\nsha1=${proof.sha1}\n`);
|
|
61
|
+
NODE
|
|
52
62
|
- run: npm publish "${{ steps.tarball.outputs.path }}"${NPM_PROVENANCE_FLAG} --access public
|
|
63
|
+
- name: Require registry byte equality
|
|
64
|
+
env:
|
|
65
|
+
NPM_PUBLICATION_NAME: ${{ steps.tarball.outputs.name }}
|
|
66
|
+
NPM_PUBLICATION_VERSION: ${{ steps.tarball.outputs.version }}
|
|
67
|
+
NPM_PUBLICATION_SHA1: ${{ steps.tarball.outputs.sha1 }}
|
|
68
|
+
run: |
|
|
69
|
+
node --input-type=module <<'NODE'
|
|
70
|
+
import { verifyNpmRegistryBytes } from '@rightkit/release/publication-gate.mjs';
|
|
71
|
+
verifyNpmRegistryBytes({ name: process.env.NPM_PUBLICATION_NAME, version: process.env.NPM_PUBLICATION_VERSION, sha1: process.env.NPM_PUBLICATION_SHA1 });
|
|
72
|
+
NODE
|