@rightkit/git 0.2.21 → 0.2.23

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.
@@ -52,6 +52,7 @@ const templatesDir = path.join(packageRoot, "templates");
52
52
 
53
53
  const FULL_MANIFEST = {
54
54
  schemaVersion: 1,
55
+ compile: "github-actions-only",
55
56
  lanes: [...KNOWN_LANES],
56
57
  matrix: { os: ["ubuntu-latest", "macos-latest", "windows-latest"], node: ["22.13", "24"] },
57
58
  packageManager: "pnpm@11.24.0",
package/install.mjs CHANGED
@@ -13,7 +13,7 @@ import { applyPlan, planSync } from "./sync.mjs";
13
13
 
14
14
  export const DEFAULT_MATRIX = { os: ["ubuntu-latest", "macos-latest", "windows-latest"], node: ["22.13", "24"] };
15
15
  export const DEFAULT_PACKAGE_MANAGER = "pnpm@11.24.0";
16
- export const DEFAULT_TEMPLATE_VERSION = "1.1.0";
16
+ export const DEFAULT_TEMPLATE_VERSION = "1.8.0";
17
17
 
18
18
  /**
19
19
  * opts: { lanes: string[], matrix?, packageManager?, templateVersion?,
@@ -38,6 +38,7 @@ export async function installCommand(repoPath, opts = {}, deps = {}) {
38
38
 
39
39
  const manifest = {
40
40
  schemaVersion: 1,
41
+ compile: "github-actions-only",
41
42
  lanes,
42
43
  matrix: opts.matrix || DEFAULT_MATRIX,
43
44
  packageManager: opts.packageManager || DEFAULT_PACKAGE_MANAGER,
package/manifest.mjs CHANGED
@@ -50,6 +50,7 @@ const WINGET_PACKAGE_IDENTIFIER_RE = /^[A-Za-z0-9][A-Za-z0-9-]*(?:\.[A-Za-z0-9][
50
50
 
51
51
  const TOP_LEVEL_KEYS = new Set([
52
52
  "schemaVersion",
53
+ "compile",
53
54
  "lanes",
54
55
  "matrix",
55
56
  "packageManager",
@@ -100,6 +101,9 @@ export function validateManifest(manifest) {
100
101
  if (manifest.schemaVersion !== 1) {
101
102
  errors.push("schemaVersion must be exactly 1");
102
103
  }
104
+ if (manifest.compile !== "github-actions-only") {
105
+ errors.push("compile must be exactly 'github-actions-only' for public repositories");
106
+ }
103
107
 
104
108
  let lanes = [];
105
109
  if (!Array.isArray(manifest.lanes) || manifest.lanes.length === 0) {
@@ -163,7 +167,7 @@ export function validateManifest(manifest) {
163
167
  errors.push("rustCache must be an object");
164
168
  } else {
165
169
  for (const key of Object.keys(rustCache)) {
166
- if (key !== "workspaces") errors.push(`unknown rustCache field: ${key}`);
170
+ if (key !== "workspaces" && key !== "requiredForGate") errors.push(`unknown rustCache field: ${key}`);
167
171
  }
168
172
  if (!Array.isArray(rustCache.workspaces) || rustCache.workspaces.length === 0) {
169
173
  errors.push("rustCache.workspaces must be a non-empty array");
@@ -172,6 +176,9 @@ export function validateManifest(manifest) {
172
176
  } else if (new Set(rustCache.workspaces).size !== rustCache.workspaces.length) {
173
177
  errors.push("rustCache.workspaces entries must be unique");
174
178
  }
179
+ if (rustCache.requiredForGate !== undefined && typeof rustCache.requiredForGate !== "boolean") {
180
+ errors.push("rustCache.requiredForGate must be a boolean");
181
+ }
175
182
  }
176
183
  }
177
184
 
@@ -772,6 +779,7 @@ export function buildRenderContext(manifest, repoPath) {
772
779
  TEMPLATE_VERSION: manifest.templateVersion,
773
780
  GATE_PATH: manifest.gate?.path,
774
781
  RUST_CACHE_WORKSPACES: rustWorkspaces.map((workspace) => ` ${workspace} -> target`).join("\n"),
782
+ RUST_REQUIRED_FOR_GATE: manifest.rustCache?.requiredForGate === true ? "true" : "false",
775
783
  CANDIDATE_BUILD_SCRIPT: manifest.candidate?.buildScript,
776
784
  CANDIDATE_CHECK_SCRIPT: manifest.candidate?.checkScript,
777
785
  CANDIDATE_ARTIFACT_ROOT: manifest.candidate?.artifactRoot,
@@ -4,12 +4,15 @@
4
4
  "title": "right-git per-repo manifest (.rightgit.json)",
5
5
  "description": "Declares which right-git lanes a public repo installs and how templates should be parameterized. Hand-validated by manifest.mjs — this file documents the contract, it is not fed to a JSON Schema validator at runtime.",
6
6
  "type": "object",
7
- "required": ["schemaVersion", "lanes", "matrix", "packageManager", "templateVersion"],
7
+ "required": ["schemaVersion", "compile", "lanes", "matrix", "packageManager", "templateVersion"],
8
8
  "additionalProperties": false,
9
9
  "properties": {
10
10
  "schemaVersion": {
11
11
  "const": 1
12
12
  },
13
+ "compile": {
14
+ "const": "github-actions-only"
15
+ },
13
16
  "lanes": {
14
17
  "type": "array",
15
18
  "minItems": 1,
@@ -92,7 +95,8 @@
92
95
  "minItems": 1,
93
96
  "uniqueItems": true,
94
97
  "items": { "type": "string", "pattern": "^(?:\\.|(?!.*\\.\\.)(?!.*\\s)[A-Za-z0-9][A-Za-z0-9._/-]*)$" }
95
- }
98
+ },
99
+ "requiredForGate": { "type": "boolean" }
96
100
  }
97
101
  },
98
102
  "gate": {
package/manifest.test.mjs CHANGED
@@ -4,12 +4,18 @@ import { validateManifest } from "./manifest.mjs";
4
4
 
5
5
  const VALID_MINIMAL = {
6
6
  schemaVersion: 1,
7
+ compile: "github-actions-only",
7
8
  lanes: ["ci"],
8
9
  matrix: { os: ["ubuntu-latest"], node: ["22.13"] },
9
10
  packageManager: "pnpm@11.24.0",
10
11
  templateVersion: "1.0.0",
11
12
  };
12
13
 
14
+ test("requires GitHub Actions only compilation", () => {
15
+ assert.equal(validateManifest({ ...VALID_MINIMAL, compile: undefined }).valid, false);
16
+ assert.equal(validateManifest({ ...VALID_MINIMAL, compile: "local" }).valid, false);
17
+ });
18
+
13
19
  test("accepts a minimal valid manifest", () => {
14
20
  const { valid, errors } = validateManifest(VALID_MINIMAL);
15
21
  assert.equal(valid, true);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/git",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
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
@@ -11,6 +11,7 @@ const templatesDir = path.join(packageRoot, "templates");
11
11
 
12
12
  const FULL_MANIFEST = {
13
13
  schemaVersion: 1,
14
+ compile: "github-actions-only",
14
15
  lanes: [...KNOWN_LANES],
15
16
  matrix: { os: ["ubuntu-latest", "macos-latest", "windows-latest"], node: ["22.13", "24"] },
16
17
  packageManager: "pnpm@11.24.0",
@@ -54,6 +55,20 @@ test("rust-hybrid ci renders its pinned package manager and gate", () => {
54
55
  assert.match(content, /group: \$\{\{ github\.workflow \}\}-\$\{\{ github\.ref \}\}/);
55
56
  assert.match(content, /cancel-in-progress: true/);
56
57
  assert.match(content, /bash \.\/scripts\/gate\.sh/);
58
+ assert.match(content, /name: Detect Rust-affecting changes\n id: rust-changes/);
59
+ assert.match(content, /RIGHT_GIT_DIFF_BASE: \$\{\{ github\.event_name == 'pull_request' && github\.event\.pull_request\.base\.sha \|\| github\.event\.before \}\}/);
60
+ assert.match(content, /changed=true[\s\S]*\^0\{40\}\$[\s\S]*git cat-file -e[\s\S]*git diff --name-only --diff-filter=ACDMRTUXB/);
61
+ assert.match(content, /--diff-filter=ACDMRTUXB/, "deleted Rust inputs must trigger Rust setup");
62
+ assert.match(content, /\*\.rs\|Cargo\.toml[\s\S]*scripts\/gate\.sh/);
63
+ assert.match(content, /\.rightgit\.json\|\.github\/workflows\/\*\.yml\|\.github\/workflows\/\*\.yaml/);
64
+ assert.equal((content.match(/if: steps\.rust-changes\.outputs\.required == 'true'/g) ?? []).length, 3);
65
+ assert.match(content, /RIGHT_GIT_RUST_CHANGED: \$\{\{ steps\.rust-changes\.outputs\.changed \}\}/);
66
+ assert.match(content, /RIGHT_GIT_RUST_REQUIRED: \$\{\{ steps\.rust-changes\.outputs\.required \}\}/);
67
+ assert.match(content, /RUSTC_WRAPPER: \$\{\{ steps\.rust-changes\.outputs\.required == 'true' && 'sccache' \|\| '' \}\}/);
68
+ assert.match(content, /CARGO_INCREMENTAL: "0"/);
69
+ assert.match(content, /CARGO_PROFILE_DEV_DEBUG: "0"/);
70
+ assert.match(content, /CARGO_PROFILE_TEST_DEBUG: "0"/);
71
+ assert.doesNotMatch(content, /opt-level|codegen-units|\blto\b|strip\s*=/i);
57
72
  assert.match(content, /os: \["windows-latest"\]/);
58
73
  assert.doesNotMatch(content, /ubuntu-latest|macos-latest/);
59
74
  });
@@ -104,6 +119,9 @@ test("rust-hybrid release candidate renders product scripts, artifact handoff, a
104
119
  assert.match(content, /RIGHT_GIT_RELEASE_ARCHITECTURE: \$\{\{ matrix\.architecture \}\}/);
105
120
  assert.match(content, /actions\/upload-artifact@[a-f0-9]{40}/);
106
121
  assert.doesNotMatch(content, /secrets\.|AZURE_|APPLE_|certificate|private.key/i);
122
+ assert.doesNotMatch(content, /rust-changes|RIGHT_GIT_RUST_CHANGED/);
123
+ assert.doesNotMatch(content, /if: steps\.rust-changes/);
124
+ assert.match(content, /uses: dtolnay\/rust-toolchain@[a-f0-9]{40}\n with:/);
107
125
  });
108
126
 
109
127
  test("rust-hybrid cache contract renders every declared Cargo workspace", () => {
@@ -112,7 +130,7 @@ test("rust-hybrid cache contract renders every declared Cargo workspace", () =>
112
130
  lanes: ["ci"],
113
131
  profile: "rust-hybrid",
114
132
  gate: { path: "scripts/gate.sh" },
115
- rustCache: { workspaces: [".", "apps/studio/src-tauri"] },
133
+ rustCache: { workspaces: [".", "apps/studio/src-tauri"], requiredForGate: true },
116
134
  matrix: { os: ["windows-2025"], node: ["22.23.2"] },
117
135
  };
118
136
  const content = renderTemplate(
@@ -123,7 +141,8 @@ test("rust-hybrid cache contract renders every declared Cargo workspace", () =>
123
141
  assert.match(content, /add-job-id-key: false/);
124
142
  assert.match(content, /cache-workspace-crates: false/);
125
143
  assert.match(content, /cache-on-failure: true/);
126
- assert.match(content, /RUSTC_WRAPPER: "sccache"/);
144
+ assert.match(content, /if \[\[ "true" == "true" \]\]; then[\s\S]*required=true/);
145
+ assert.match(content, /RUSTC_WRAPPER: \$\{\{ steps\.rust-changes\.outputs\.required == 'true' && 'sccache' \|\| '' \}\}/);
127
146
  });
128
147
 
129
148
  test("rust-hybrid release candidate can opt into OIDC Windows signing without long-lived secrets", () => {
package/sync.test.mjs CHANGED
@@ -57,6 +57,7 @@ test("sync reports manifest errors instead of writing files", async () => {
57
57
  test("sync rejects a Membrane-like release chain without root @rightkit/release", async () => {
58
58
  const manifest = {
59
59
  schemaVersion: 1,
60
+ compile: "github-actions-only",
60
61
  lanes: ["ci", "release-candidate"],
61
62
  matrix: { os: ["windows-2025", "macos-15"], node: ["22.23.2"] },
62
63
  packageManager: "pnpm@11.24.0",
@@ -19,9 +19,6 @@ jobs:
19
19
  run: exit 1
20
20
  ci:
21
21
  needs: [private-repo-guard]
22
- env:
23
- SCCACHE_GHA_ENABLED: "true"
24
- RUSTC_WRAPPER: "sccache"
25
22
  strategy:
26
23
  fail-fast: false
27
24
  matrix:
@@ -34,15 +31,45 @@ jobs:
34
31
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
32
  with:
36
33
  fetch-depth: 0
34
+ - name: Detect Rust-affecting changes
35
+ id: rust-changes
36
+ shell: bash
37
+ env:
38
+ RIGHT_GIT_DIFF_BASE: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || github.event.before }}
39
+ run: |
40
+ set -euo pipefail
41
+ changed=true
42
+ base="$RIGHT_GIT_DIFF_BASE"
43
+ if [[ "$base" =~ ^[0-9a-fA-F]{40}$ ]] && [[ ! "$base" =~ ^0{40}$ ]] && git cat-file -e "$base^{commit}" 2>/dev/null; then
44
+ if changed_paths="$(git diff --name-only --diff-filter=ACDMRTUXB "$base" "$GITHUB_SHA")"; then
45
+ changed=false
46
+ while IFS= read -r changed_path; do
47
+ case "$changed_path" in
48
+ *.rs|Cargo.toml|*/Cargo.toml|Cargo.lock|*/Cargo.lock|build.rs|*/build.rs|rust-toolchain|*/rust-toolchain|rust-toolchain.toml|*/rust-toolchain.toml|.cargo/*|*/.cargo/*|.rightgit.json|.github/workflows/*.yml|.github/workflows/*.yaml|${GATE_PATH})
49
+ changed=true
50
+ break
51
+ ;;
52
+ esac
53
+ done <<< "$changed_paths"
54
+ fi
55
+ fi
56
+ printf 'changed=%s\n' "$changed" >> "$GITHUB_OUTPUT"
57
+ required="$changed"
58
+ if [[ "${RUST_REQUIRED_FOR_GATE}" == "true" ]]; then
59
+ required=true
60
+ fi
61
+ printf 'required=%s\n' "$required" >> "$GITHUB_OUTPUT"
37
62
  - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
38
63
  with:
39
64
  node-version: ${{ matrix.node }}
40
65
  package-manager-cache: false
41
66
  ${PACKAGE_MANAGER_SETUP}
42
67
  - uses: dtolnay/rust-toolchain@f8be11a05b1d4f3fcebe6410cc16743212b999b0
68
+ if: steps.rust-changes.outputs.required == 'true'
43
69
  with:
44
70
  components: rustfmt, clippy
45
71
  - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
72
+ if: steps.rust-changes.outputs.required == 'true'
46
73
  with:
47
74
  workspaces: |
48
75
  ${RUST_CACHE_WORKSPACES}
@@ -52,6 +79,15 @@ ${RUST_CACHE_WORKSPACES}
52
79
  cache-on-failure: true
53
80
  save-if: ${{ github.event_name != 'pull_request' }}
54
81
  - uses: Mozilla-Actions/sccache-action@fc920bf0ec8de6ee65d409111f7ec508035751ba # v0.0.11
82
+ if: steps.rust-changes.outputs.required == 'true'
55
83
  with:
56
84
  version: "v0.16.0"
57
85
  - run: bash ./${GATE_PATH}
86
+ env:
87
+ RIGHT_GIT_RUST_CHANGED: ${{ steps.rust-changes.outputs.changed }}
88
+ RIGHT_GIT_RUST_REQUIRED: ${{ steps.rust-changes.outputs.required }}
89
+ SCCACHE_GHA_ENABLED: "true"
90
+ RUSTC_WRAPPER: ${{ steps.rust-changes.outputs.required == 'true' && 'sccache' || '' }}
91
+ CARGO_INCREMENTAL: "0"
92
+ CARGO_PROFILE_DEV_DEBUG: "0"
93
+ CARGO_PROFILE_TEST_DEBUG: "0"
package/test-support.mjs CHANGED
@@ -26,6 +26,7 @@ export const PRIVATE_DEPS = ghDeps("PRIVATE");
26
26
 
27
27
  export const SAMPLE_MANIFEST = {
28
28
  schemaVersion: 1,
29
+ compile: "github-actions-only",
29
30
  lanes: ["ci", "package-smoke"],
30
31
  matrix: { os: ["ubuntu-latest"], node: ["22.13"] },
31
32
  packageManager: "pnpm@11.24.0",
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { syncCommand } from "./sync.mjs";
6
- import { installCommand } from "./install.mjs";
6
+ import { DEFAULT_TEMPLATE_VERSION, installCommand } from "./install.mjs";
7
7
  import { uninstallCommand } from "./uninstall.mjs";
8
8
  import { listManagedFiles } from "./sync.mjs";
9
9
  import { MANIFEST_FILENAME } from "./manifest.mjs";
@@ -77,3 +77,13 @@ test("install validates before it creates a manifest", async () => {
77
77
  assert.equal(result.stage, "manifest-invalid");
78
78
  assert.equal(fs.existsSync(path.join(dir, MANIFEST_FILENAME)), false);
79
79
  });
80
+
81
+ test("install emits GitHub-Actions-only policy at template 1.8.0", async () => {
82
+ const dir = makeTempRepo(null);
83
+ const result = await installCommand(dir, { lanes: ["ci"] }, PUBLIC_DEPS);
84
+ assert.equal(result.ok, true);
85
+ const manifest = JSON.parse(fs.readFileSync(path.join(dir, MANIFEST_FILENAME), "utf8"));
86
+ assert.equal(manifest.compile, "github-actions-only");
87
+ assert.equal(DEFAULT_TEMPLATE_VERSION, "1.8.0");
88
+ assert.equal(manifest.templateVersion, DEFAULT_TEMPLATE_VERSION);
89
+ });