@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
|
|
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@
|
|
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) {
|
package/manifest.schema.json
CHANGED
|
@@ -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
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@
|
|
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, /
|
|
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, /
|
|
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@
|
|
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:
|
|
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-
|
|
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@
|
|
24
|
-
- uses: actions/setup-node@
|
|
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
|
-
-
|
|
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-
|
|
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: ${
|
|
24
|
+
os: ${CANDIDATE_OS_MATRIX}
|
|
25
25
|
node: ${NODE_MATRIX}
|
|
26
26
|
runs-on: ${{ matrix.os }}
|
|
27
27
|
steps:
|
|
28
|
-
- uses: actions/checkout@
|
|
29
|
-
- uses: actions/setup-node@
|
|
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
|
-
-
|
|
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}/**
|