@rightkit/release 0.2.76 → 0.2.78

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.
@@ -1,6 +1,6 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
3
- import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { basename, dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { spawnSync } from "node:child_process";
5
5
  import { RIGHTRELEASE_MANIFEST_SIGNER, signReleaseManifestWithAzure } from "./sign-release-manifest.mjs";
6
6
  import { validateCycloneDxSbom, validateInTotoSlsaProvenance } from "./supply-chain-evidence.mjs";
@@ -73,8 +73,13 @@ function assertGitHubAssetUrl(url) {
73
73
 
74
74
  export function createPortableArchive({ sourceDir, outputPath, commandRunner = spawnSync }) {
75
75
  const source = realpathSync(sourceDir);
76
- mkdirSync(dirname(outputPath), { recursive: true });
77
- const result = commandRunner("tar", ["-a", "-c", "-f", resolve(outputPath), "-C", source, "."], {
76
+ const outputDir = resolve(dirname(outputPath));
77
+ mkdirSync(outputDir, { recursive: true });
78
+ // GNU tar reads a colon in -f as a remote host spec, so an absolute
79
+ // Windows path (C:\...) fails with "Cannot connect to C:". Run from the
80
+ // output directory and pass a bare filename instead.
81
+ const result = commandRunner("tar", ["-a", "-c", "-f", basename(outputPath), "-C", source, "."], {
82
+ cwd: outputDir,
78
83
  encoding: "utf8",
79
84
  windowsHide: true,
80
85
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.76",
3
+ "version": "0.2.78",
4
4
  "description": "Portable Right Suite release CLI/SDK: signed native archives, direct bootstrap transactions, immutable GitHub Release upload, R2 bootstrap publication, and add-on adoption.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
package/release.mjs CHANGED
@@ -619,9 +619,6 @@ async function validateHostedWorkflows(root, appName, workflows, policy) {
619
619
  if (policy !== "right-git-ci-only") {
620
620
  fail(`${appName} hosted workflow files are forbidden: ${workflows.join(", ")}`);
621
621
  }
622
- if (workflows.length !== 1 || workflows[0] !== "ci.yml") {
623
- fail(`${appName} right-git-ci-only policy permits only ci.yml, got: ${workflows.join(", ")}`);
624
- }
625
622
  const manifestPath = path.join(root, ".rightgit.json");
626
623
  let manifest;
627
624
  try {
@@ -629,16 +626,30 @@ async function validateHostedWorkflows(root, appName, workflows, policy) {
629
626
  } catch {
630
627
  fail(`${appName} right-git-ci-only policy requires a valid .rightgit.json`);
631
628
  }
632
- if (manifest?.schemaVersion !== 1 || JSON.stringify(manifest?.lanes) !== JSON.stringify(["ci"])) {
633
- fail(`${appName} right-git-ci-only policy requires .rightgit.json lanes=["ci"]`);
634
- }
635
- const source = await readFile(path.join(root, ".github", "workflows", "ci.yml"), "utf8");
636
- if (!source.startsWith("# Managed by right-git")) {
637
- fail(`${appName} ci.yml is not marked as right-git managed`);
638
- }
639
- if (/^\s*(?:actions|attestations|contents|id-token|packages|security-events):\s*write\s*$/mi.test(source)
640
- || /\bsecrets\s*\./i.test(source)) {
641
- fail(`${appName} right-git managed ci.yml requests release-capable permissions or secrets`);
629
+ const lanes = manifest?.lanes;
630
+ const safeLanes = new Set(["ci", "release-candidate"]);
631
+ if (manifest?.schemaVersion !== 1
632
+ || !Array.isArray(lanes)
633
+ || lanes.length === 0
634
+ || !lanes.includes("ci")
635
+ || new Set(lanes).size !== lanes.length
636
+ || lanes.some((lane) => !safeLanes.has(lane))) {
637
+ fail(`${appName} right-git-ci-only policy requires unique .rightgit.json lanes drawn from ["ci", "release-candidate"] and including "ci"`);
638
+ }
639
+ const expectedWorkflows = lanes.map((lane) => `${lane}.yml`).sort();
640
+ if (JSON.stringify(workflows) !== JSON.stringify(expectedWorkflows)) {
641
+ fail(`${appName} right-git-ci-only policy requires workflows declared by .rightgit.json; expected ${expectedWorkflows.join(", ")}, got: ${workflows.join(", ")}`);
642
+ }
643
+ for (const workflow of expectedWorkflows) {
644
+ const source = await readFile(path.join(root, ".github", "workflows", workflow), "utf8");
645
+ if (!source.startsWith("# Managed by right-git")) {
646
+ fail(`${appName} ${workflow} is not marked as right-git managed`);
647
+ }
648
+ if (/^\s*permissions:\s*write-all\s*$/mi.test(source)
649
+ || /^\s*(?:actions|attestations|checks|contents|deployments|discussions|id-token|issues|models|packages|pages|pull-requests|security-events|statuses):\s*write\s*$/mi.test(source)
650
+ || /\bsecrets\s*(?:\.|\[)/i.test(source)) {
651
+ fail(`${appName} right-git managed ${workflow} requests release-capable permissions or secrets`);
652
+ }
642
653
  }
643
654
  }
644
655
 
@@ -14,7 +14,7 @@
14
14
  },
15
15
  "stagedNpm": {
16
16
  "@rightkit/ax": "0.2.1",
17
- "@rightkit/git": "0.2.1",
17
+ "@rightkit/git": "0.2.5",
18
18
  "@rightkit/hooks": "0.1.1",
19
19
  "@rightkit/legal": "0.3.1",
20
20
  "@rightkit/legal-ui": "0.1.1",
@@ -22,7 +22,7 @@
22
22
  "@rightkit/logs": "0.1.4",
23
23
  "@rightkit/platform-ui": "0.1.1",
24
24
  "@rightkit/qa": "0.2.1",
25
- "@rightkit/release": "0.2.76",
25
+ "@rightkit/release": "0.2.78",
26
26
  "@rightkit/tauri": "0.1.1",
27
27
  "@rightkit/updates": "0.2.4"
28
28
  },
@@ -30,6 +30,12 @@
30
30
  "@rightkit/ax": [
31
31
  "0.2.0"
32
32
  ],
33
+ "@rightkit/git": [
34
+ "0.2.1",
35
+ "0.2.2",
36
+ "0.2.3",
37
+ "0.2.4"
38
+ ],
33
39
  "@rightkit/legal-ui": [
34
40
  "0.1.0"
35
41
  ],
@@ -74,7 +80,8 @@
74
80
  "0.2.72",
75
81
  "0.2.73",
76
82
  "0.2.74",
77
- "0.2.75"
83
+ "0.2.75",
84
+ "0.2.76"
78
85
  ],
79
86
  "@rightkit/qa": [
80
87
  "0.1.0",