@rightkit/git 0.2.2 → 0.2.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 CHANGED
@@ -38,4 +38,4 @@ Node builtins only; no runtime dependencies.
38
38
 
39
39
  ## Profiles
40
40
 
41
- `profile` defaults to `node`. `rust-hybrid` requires `ci`, accepts optional `release-candidate`, installs pinned Node plus `rustup stable`, then runs product-owned package scripts. Candidate configuration is `{ buildScript, checkScript, artifactRoot }`; RightGit passes an isolated `RIGHT_GIT_ARTIFACT_ROOT`, checks it, & uploads it as an unsigned handoff. It never signs, finalizes, publishes releases, or receives signing credentials.
41
+ `profile` defaults to `node`. `rust-hybrid` requires `ci`, accepts optional `release-candidate`, installs pinned Node plus Rust, then runs product-owned package scripts. Candidate configuration is `{ buildScript, checkScript, artifactRoot, os }`; `os` selects supported release hosts from main matrix. RightGit passes isolated `RIGHT_GIT_ARTIFACT_ROOT`, checks it, & uploads it as an unsigned handoff. It never signs, finalizes, publishes releases, or receives signing credentials.
package/manifest.mjs CHANGED
@@ -141,7 +141,7 @@ export function validateManifest(manifest) {
141
141
  errors.push("candidate must be an object");
142
142
  } else {
143
143
  for (const key of Object.keys(candidate)) {
144
- if (!["buildScript", "checkScript", "artifactRoot"].includes(key)) errors.push(`unknown candidate field: ${key}`);
144
+ if (!["buildScript", "checkScript", "artifactRoot", "os"].includes(key)) errors.push(`unknown candidate field: ${key}`);
145
145
  }
146
146
  for (const key of ["buildScript", "checkScript"]) {
147
147
  if (typeof candidate[key] !== "string" || !PACKAGE_SCRIPT_RE.test(candidate[key])) {
@@ -151,6 +151,11 @@ export function validateManifest(manifest) {
151
151
  if (typeof candidate.artifactRoot !== "string" || !GATE_PATH_RE.test(candidate.artifactRoot)) {
152
152
  errors.push("candidate.artifactRoot must be one safe relative path");
153
153
  }
154
+ if (!Array.isArray(candidate.os) || candidate.os.length === 0 || candidate.os.some((value) => typeof value !== "string" || !SAFE_MATRIX_VALUE_RE.test(value))) {
155
+ errors.push("candidate.os must be a non-empty array of safe runner labels");
156
+ } else if (candidate.os.some((value) => !manifest.matrix?.os?.includes(value))) {
157
+ errors.push("candidate.os entries must also exist in matrix.os");
158
+ }
154
159
  }
155
160
  }
156
161
  if (profile === "rust-hybrid" && lanes.includes("release-candidate") && candidate === undefined) {
@@ -262,7 +267,7 @@ function parsePackageManager(spec) {
262
267
  }[name];
263
268
  const runPrefix = { pnpm: "pnpm run", npm: "npm run", yarn: "yarn run" }[name];
264
269
  const setup = {
265
- pnpm: ` - uses: pnpm/action-setup@v4\n with:\n version: ${version}`,
270
+ pnpm: ` - uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6.0.10\n with:\n version: ${version}`,
266
271
  npm: "",
267
272
  yarn: ` - run: corepack enable && corepack prepare yarn@${version} --activate`,
268
273
  }[name];
@@ -315,6 +320,7 @@ export function buildRenderContext(manifest, repoPath) {
315
320
  CANDIDATE_BUILD_SCRIPT: manifest.candidate?.buildScript,
316
321
  CANDIDATE_CHECK_SCRIPT: manifest.candidate?.checkScript,
317
322
  CANDIDATE_ARTIFACT_ROOT: manifest.candidate?.artifactRoot,
323
+ CANDIDATE_OS_MATRIX: yamlFlowStringList(manifest.candidate?.os ?? manifest.matrix.os),
318
324
  };
319
325
 
320
326
  if (manifest.publish?.npm) {
@@ -88,12 +88,13 @@
88
88
  },
89
89
  "candidate": {
90
90
  "type": "object",
91
- "required": ["buildScript", "checkScript", "artifactRoot"],
91
+ "required": ["buildScript", "checkScript", "artifactRoot", "os"],
92
92
  "additionalProperties": false,
93
93
  "properties": {
94
94
  "buildScript": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9:_-]*$" },
95
95
  "checkScript": { "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9:_-]*$" },
96
- "artifactRoot": { "type": "string", "pattern": "^(?!.*\\.\\.)(?!.*\\s)[A-Za-z0-9][A-Za-z0-9._/-]*$" }
96
+ "artifactRoot": { "type": "string", "pattern": "^(?!.*\\.\\.)(?!.*\\s)[A-Za-z0-9][A-Za-z0-9._/-]*$" },
97
+ "os": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 } }
97
98
  }
98
99
  }
99
100
  }
package/manifest.test.mjs CHANGED
@@ -26,11 +26,12 @@ test("rust-hybrid supports ci plus a configured unsigned release-candidate lane"
26
26
  const candidate = {
27
27
  ...valid,
28
28
  lanes: ["ci", "release-candidate"],
29
- candidate: { buildScript: "ci:candidate", checkScript: "ci:candidate-check", artifactRoot: "legion-unsigned" },
29
+ candidate: { buildScript: "ci:candidate", checkScript: "ci:candidate-check", artifactRoot: "legion-unsigned", os: ["ubuntu-latest"] },
30
30
  };
31
31
  assert.equal(validateManifest(candidate).valid, true);
32
32
  assert.equal(validateManifest({ ...candidate, candidate: { ...candidate.candidate, buildScript: "echo unsafe" } }).valid, false);
33
33
  assert.equal(validateManifest({ ...candidate, candidate: { ...candidate.candidate, artifactRoot: "../escape" } }).valid, false);
34
+ assert.equal(validateManifest({ ...candidate, candidate: { ...candidate.candidate, os: ["windows-latest"] } }).valid, false);
34
35
  });
35
36
 
36
37
  test("rejects non-object input", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/git",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Public-repo-only GitHub Actions workflow layer: templates, visibility gate, sync/drift/adopt/uninstall for right-git.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
package/render.test.mjs CHANGED
@@ -45,9 +45,9 @@ test("every known lane has a template file and renders without an unresolved pla
45
45
  test("rust-hybrid ci renders its pinned package manager and gate", () => {
46
46
  const manifest = { ...FULL_MANIFEST, lanes: ["ci"], profile: "rust-hybrid", gate: { path: "scripts/gate.sh" }, templateVersion: "1.1.0" };
47
47
  const content = renderTemplate(fs.readFileSync(path.join(templatesDir, "profiles/rust-hybrid/ci.yml"), "utf8"), buildRenderContext(manifest, "/repos/widgets"));
48
- assert.match(content, /uses: pnpm\/action-setup@v4/);
48
+ assert.match(content, /uses: pnpm\/action-setup@[a-f0-9]{40}/);
49
49
  assert.match(content, /version: 11\.24\.0/);
50
- assert.match(content, /rustup toolchain install stable/);
50
+ assert.match(content, /toolchain: 1\.98\.0/);
51
51
  assert.match(content, /bash \.\/scripts\/gate\.sh/);
52
52
  });
53
53
 
@@ -57,7 +57,7 @@ test("rust-hybrid release candidate renders product scripts, artifact handoff, a
57
57
  lanes: ["ci", "release-candidate"],
58
58
  profile: "rust-hybrid",
59
59
  gate: { path: "scripts/gate.sh" },
60
- candidate: { buildScript: "ci:candidate", checkScript: "ci:candidate-check", artifactRoot: "widgets-unsigned" },
60
+ candidate: { buildScript: "ci:candidate", checkScript: "ci:candidate-check", artifactRoot: "widgets-unsigned", os: ["macos-latest", "windows-latest"] },
61
61
  templateVersion: "1.2.0",
62
62
  };
63
63
  const content = renderTemplate(
@@ -67,7 +67,8 @@ test("rust-hybrid release candidate renders product scripts, artifact handoff, a
67
67
  assert.match(content, /pnpm run ci:candidate/);
68
68
  assert.match(content, /pnpm run ci:candidate-check/);
69
69
  assert.match(content, /RIGHT_GIT_ARTIFACT_ROOT/);
70
- assert.match(content, /actions\/upload-artifact@v4/);
70
+ assert.match(content, /os: \["macos-latest", "windows-latest"\]/);
71
+ assert.match(content, /actions\/upload-artifact@[a-f0-9]{40}/);
71
72
  assert.doesNotMatch(content, /secrets\.|AZURE_|APPLE_|certificate|private.key/i);
72
73
  });
73
74
 
@@ -95,7 +96,7 @@ test("rendered workflows never request pnpm or yarn setup-node caching before pa
95
96
  test("rendered workflows bootstrap each declared package manager safely", () => {
96
97
  const pnpm = renderAllLanes({ ...FULL_MANIFEST, packageManager: "pnpm@11.24.0" }, "/repos/widgets");
97
98
  for (const content of Object.values(pnpm)) {
98
- assert.match(content, /uses: pnpm\/action-setup@v4/);
99
+ assert.match(content, /uses: pnpm\/action-setup@[a-f0-9]{40}/);
99
100
  assert.match(content, /version: 11\.24\.0/);
100
101
  assert.doesNotMatch(content, /corepack enable/);
101
102
  }
@@ -1,12 +1,15 @@
1
1
  # Managed by right-git — do not hand-edit. Run `right-git sync` to update.
2
2
  # Template: profiles/rust-hybrid/ci.yml | Template version: ${TEMPLATE_VERSION}
3
- name: ci
4
- on: [push, pull_request]
3
+ name: legion-ci
4
+ on:
5
+ pull_request:
6
+ push:
7
+ branches: [main]
5
8
  permissions:
6
9
  contents: read
7
10
  jobs:
8
11
  private-repo-guard:
9
- runs-on: ubuntu-latest
12
+ runs-on: ubuntu-24.04
10
13
  steps:
11
14
  - name: Refuse private repository
12
15
  if: ${{ github.event.repository.private }}
@@ -20,11 +23,17 @@ jobs:
20
23
  node: ${NODE_MATRIX}
21
24
  runs-on: ${{ matrix.os }}
22
25
  steps:
23
- - uses: actions/checkout@v5
24
- - uses: actions/setup-node@v5
26
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
27
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
25
28
  with:
26
29
  node-version: ${{ matrix.node }}
27
30
  package-manager-cache: false
28
31
  ${PACKAGE_MANAGER_SETUP}
29
- - run: rustup toolchain install stable --profile minimal
32
+ - uses: dtolnay/rust-toolchain@f8be11a05b1d4f3fcebe6410cc16743212b999b0
33
+ with:
34
+ toolchain: 1.98.0
35
+ components: rustfmt, clippy
36
+ - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
37
+ with:
38
+ workspaces: engine
30
39
  - run: bash ./${GATE_PATH}
@@ -11,7 +11,7 @@ permissions:
11
11
  contents: read
12
12
  jobs:
13
13
  private-repo-guard:
14
- runs-on: ubuntu-latest
14
+ runs-on: ubuntu-24.04
15
15
  steps:
16
16
  - name: Refuse private repository
17
17
  if: ${{ github.event.repository.private }}
@@ -21,17 +21,23 @@ jobs:
21
21
  strategy:
22
22
  fail-fast: false
23
23
  matrix:
24
- os: ${OS_MATRIX}
24
+ os: ${CANDIDATE_OS_MATRIX}
25
25
  node: ${NODE_MATRIX}
26
26
  runs-on: ${{ matrix.os }}
27
27
  steps:
28
- - uses: actions/checkout@v5
29
- - uses: actions/setup-node@v5
28
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
29
+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
30
30
  with:
31
31
  node-version: ${{ matrix.node }}
32
32
  package-manager-cache: false
33
33
  ${PACKAGE_MANAGER_SETUP}
34
- - run: rustup toolchain install stable --profile minimal
34
+ - uses: dtolnay/rust-toolchain@f8be11a05b1d4f3fcebe6410cc16743212b999b0
35
+ with:
36
+ toolchain: 1.98.0
37
+ components: rustfmt, clippy
38
+ - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
39
+ with:
40
+ workspaces: engine
35
41
  - run: ${INSTALL_COMMAND}
36
42
  - name: Build unsigned release candidate
37
43
  run: ${PM_RUN} ${CANDIDATE_BUILD_SCRIPT}
@@ -42,7 +48,7 @@ ${PACKAGE_MANAGER_SETUP}
42
48
  env:
43
49
  RIGHT_GIT_ARTIFACT_ROOT: ${{ runner.temp }}/${CANDIDATE_ARTIFACT_ROOT}
44
50
  - name: Upload unsigned candidate handoff
45
- uses: actions/upload-artifact@v4
51
+ uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
46
52
  with:
47
53
  name: ${ARTIFACT_PREFIX}-unsigned-${{ matrix.os }}-${{ matrix.node }}
48
54
  path: ${{ runner.temp }}/${CANDIDATE_ARTIFACT_ROOT}/**