fix: reserve Grok snapshot equality for releases

This commit is contained in:
MistyStar committed 2026-10-20 10:35:37 +08:00
1 parent 5819419150
commit 08f5e15aaf
9 files changed
+162 -17

No files matched your search

+1 -1
View File
@@ -104,7 +104,7 @@ jobs:
run: node tests/plugin-manifest.test.js
- name: Validate Grok marketplace source pin
run: node scripts/ci/validate-grok-pin.js
run: node scripts/ci/validate-grok-pin.js --release
- name: Check npm publish state
id: npm_publish_state
+1 -1
View File
@@ -102,7 +102,7 @@ jobs:
run: node tests/plugin-manifest.test.js
- name: Validate Grok marketplace source pin
run: node scripts/ci/validate-grok-pin.js
run: node scripts/ci/validate-grok-pin.js --release
- name: Check npm publish state
id: npm_publish_state
+7
View File
@@ -25,6 +25,13 @@ node scripts/uninstall.js --target grok
rollback use the same canonical apply path; pass `--source-sha <40-hex>` to
reapply a previous pinned commit.
The marketplace pin advances with releases, not every source commit. Ordinary
CI validates the SHA and its ancestry. `scripts/release.sh` commits the release
metadata, pins that source commit in a separate catalog commit, and validates
the result before tagging. Release workflows also run
`node scripts/ci/validate-grok-pin.js --release` to reject unpublished source
changes after the pinned snapshot.
Grok CLI marketplace add / install / enable is **discovery only**.
`grok plugin install --trust` skips Grok's confirmation prompt; it is **not
ECC capability consent** and does not write an ECC receipt. `plugin.json`
+1 -1
View File
@@ -9,7 +9,7 @@
{
"name": "ecc",
"description": "Harness-native ECC operator layer - 68 agents, 286 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Grok Build, Claude Code, Codex, OpenCode, Cursor, and related agent harnesses",
"version": "2.2.1",
"version": "2.2.2",
"author": {
"name": "Affaan Mustafa",
"email": "me@affaanmustafa.com"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "ecc",
"version": "2.2.1",
"version": "2.2.2",
"description": "Harness-native ECC plugin for engineering teams - 68 agents, 286 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Grok Build plus adjacent agent harnesses",
"author": {
"name": "Affaan Mustafa",
+17 -7
View File
@@ -14,7 +14,7 @@ function git(root, args) {
}).trim();
}
function validateGrokPin(root = path.resolve(__dirname, '../..')) {
function validateGrokPin(root = path.resolve(__dirname, '../..'), { release = false } = {}) {
const marketplace = JSON.parse(fs.readFileSync(path.join(root, MARKETPLACE_PATH), 'utf8'));
const source = marketplace.plugins && marketplace.plugins[0] && marketplace.plugins[0].source;
const sha = source && source.sha;
@@ -30,18 +30,28 @@ function validateGrokPin(root = path.resolve(__dirname, '../..')) {
} catch {
throw new Error(`Grok marketplace pin ${sha} is not an ancestor of HEAD ${head}`);
}
try {
git(root, ['diff', '--quiet', sha, 'HEAD', '--', '.', `:(exclude)${MARKETPLACE_PATH}`]);
} catch {
throw new Error(`Grok marketplace pin ${sha} is stale; source changes exist after the pinned commit`);
// Development retains the last pinned snapshot; releases must include all source changes.
if (release) {
try {
git(root, ['diff', '--quiet', sha, 'HEAD', '--', '.', `:(exclude)${MARKETPLACE_PATH}`]);
} catch {
throw new Error(`Grok marketplace pin ${sha} is stale; source changes exist after the pinned commit`);
}
}
return { sha, head };
}
if (require.main === module) {
try {
const { sha, head } = validateGrokPin();
console.log(`Grok marketplace pin ${sha} matches source at ${head}`);
const args = process.argv.slice(2);
if (args.length > 1 || (args.length === 1 && args[0] !== '--release')) {
throw new Error('Usage: node scripts/ci/validate-grok-pin.js [--release]');
}
const release = args[0] === '--release';
const { sha, head } = validateGrokPin(undefined, { release });
console.log(release
? `Grok marketplace pin ${sha} matches source at ${head}`
: `Grok marketplace pin ${sha} is a valid ancestor of ${head}`);
} catch (error) {
console.error(error.message);
process.exitCode = 1;
+2 -1
View File
@@ -354,9 +354,10 @@ node -e '
plugin.source.sha = sha;
fs.writeFileSync(file, `${JSON.stringify(marketplace, null, 2)}\n`);
' "$GROK_MARKETPLACE_JSON" "$GROK_SOURCE_SHA"
node scripts/ci/validate-grok-pin.js
node scripts/ci/validate-grok-pin.js --release
git add "$GROK_MARKETPLACE_JSON"
git commit -m "chore: pin Grok marketplace to release source"
node scripts/ci/validate-grok-pin.js --release
git tag "v$VERSION"
git push origin main "v$VERSION"
+116 -4
View File
@@ -1,7 +1,7 @@
'use strict';
const assert = require('assert');
const { execFileSync } = require('child_process');
const { execFileSync, spawnSync } = require('child_process');
const fs = require('fs');
const os = require('os');
const path = require('path');
@@ -16,7 +16,7 @@ function writeMarketplace(root, sha) {
const directory = path.join(root, '.grok-plugin');
fs.mkdirSync(directory, { recursive: true });
fs.writeFileSync(path.join(directory, 'marketplace.json'), JSON.stringify({
plugins: [{ source: { sha } }],
plugins: [{ name: 'ecc', source: { sha } }],
}));
}
@@ -25,6 +25,9 @@ function createFixture() {
git(root, 'init', '--quiet');
git(root, 'config', 'user.name', 'ECC test');
git(root, 'config', 'user.email', 'ecc-test@example.invalid');
const validatorPath = path.join(root, 'scripts/ci/validate-grok-pin.js');
fs.mkdirSync(path.dirname(validatorPath), { recursive: true });
fs.copyFileSync(path.resolve(__dirname, '../../scripts/ci/validate-grok-pin.js'), validatorPath);
fs.writeFileSync(path.join(root, 'payload.txt'), 'verified source\n');
writeMarketplace(root, '0'.repeat(40));
git(root, 'add', '.');
@@ -57,22 +60,131 @@ test('accepts a pin commit that differs only by the pin update', () => {
const fixture = createFixture();
try {
assert.strictEqual(validateGrokPin(fixture.root).sha, fixture.sha);
assert.strictEqual(validateGrokPin(fixture.root, { release: true }).sha, fixture.sha);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('rejects source changes after the pinned snapshot', () => {
test('ordinary source commits keep a valid historical pin but cannot be released', () => {
const fixture = createFixture();
try {
fs.writeFileSync(path.join(fixture.root, 'payload.txt'), 'changed source\n');
git(fixture.root, 'add', 'payload.txt');
git(fixture.root, 'commit', '--quiet', '-m', 'change source without repinning');
assert.throws(() => validateGrokPin(fixture.root), /pin .* is stale/);
assert.strictEqual(validateGrokPin(fixture.root).sha, fixture.sha);
assert.throws(() => validateGrokPin(fixture.root, { release: true }), /pin .* is stale/);
const validator = path.join(fixture.root, 'scripts/ci/validate-grok-pin.js');
const normal = spawnSync(process.execPath, [validator], { encoding: 'utf8' });
assert.strictEqual(normal.status, 0, normal.stderr);
assert.doesNotMatch(normal.stdout, /matches source/);
const release = spawnSync(process.execPath, [validator, '--release'], { encoding: 'utf8' });
assert.strictEqual(release.status, 1);
assert.match(release.stderr, /pin .* is stale/);
const sourceSha = git(fixture.root, 'rev-parse', 'HEAD');
const releaseScript = fs.readFileSync(path.resolve(__dirname, '../../scripts/release.sh'), 'utf8');
const updater = releaseScript.slice(releaseScript.indexOf('GROK_SOURCE_SHA='))
.match(/node -e '([\s\S]*?)' "\$GROK_MARKETPLACE_JSON" "\$GROK_SOURCE_SHA"/);
assert.ok(updater, 'exercise the pin updater shipped in release.sh');
const updated = spawnSync(process.execPath, ['-e', updater[1],
path.join(fixture.root, '.grok-plugin/marketplace.json'), sourceSha,
], { encoding: 'utf8' });
assert.strictEqual(updated.status, 0, updated.stderr);
git(fixture.root, 'add', '.');
git(fixture.root, 'commit', '--quiet', '-m', 'pin updated source');
assert.strictEqual(git(fixture.root, 'diff', '--name-only', 'HEAD^', 'HEAD'),
'.grok-plugin/marketplace.json');
assert.strictEqual(validateGrokPin(fixture.root, { release: true }).sha, sourceSha);
const repinned = spawnSync(process.execPath, [validator, '--release'], { encoding: 'utf8' });
assert.strictEqual(repinned.status, 0, repinned.stderr);
assert.match(repinned.stdout, /matches source/);
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('both modes reject malformed, missing, and non-ancestor pins', () => {
const fixture = createFixture();
try {
for (const marketplace of [{}, { plugins: [] }, { plugins: [{}] }]) {
fs.writeFileSync(path.join(fixture.root, '.grok-plugin/marketplace.json'), JSON.stringify(marketplace));
for (const release of [false, true]) {
assert.throws(() => validateGrokPin(fixture.root, { release }), /must pin a 40-character/);
}
}
for (const sha of ['main', '0'.repeat(40)]) {
writeMarketplace(fixture.root, sha);
for (const release of [false, true]) {
assert.throws(() => validateGrokPin(fixture.root, { release }));
}
}
// Create a real commit that is not reachable from HEAD, without moving any refs.
const tree = git(fixture.root, 'rev-parse', 'HEAD^{tree}');
const unrelated = git(fixture.root, 'commit-tree', tree, '-m', 'unrelated history');
writeMarketplace(fixture.root, unrelated);
for (const release of [false, true]) {
assert.throws(() => validateGrokPin(fixture.root, { release }), /not an ancestor/);
}
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
test('CLI rejects unknown options instead of silently disabling release validation', () => {
for (const args of [['--relase'], ['--release', 'unexpected']]) {
const result = spawnSync(process.execPath, [
path.resolve(__dirname, '../../scripts/ci/validate-grok-pin.js'), ...args,
], { encoding: 'utf8' });
assert.strictEqual(result.status, 1);
assert.match(result.stderr, /Usage:/);
}
});
// release.sh is a POSIX entrypoint; the validator and updater tests above run on all platforms.
if (process.platform !== 'win32') {
for (const contaminate of [false, true]) {
test(contaminate ? 'release blocks tagging when a commit hook changes source'
: 'release script tags the verified pin-only commit', () => {
const fixture = createFixture();
try {
fs.writeFileSync(path.join(fixture.root, 'payload.txt'), 'new release source\n');
git(fixture.root, 'add', '.');
git(fixture.root, 'commit', '--quiet', '-m', 'release source');
const sourceSha = git(fixture.root, 'rev-parse', 'HEAD');
if (contaminate) {
fs.writeFileSync(path.join(fixture.root, '.git/hooks/pre-commit'),
'#!/bin/sh\nprintf "unexpected source change\\n" > payload.txt\ngit add payload.txt\n',
{ mode: 0o755 });
}
const source = fs.readFileSync(path.resolve(__dirname, '../../scripts/release.sh'), 'utf8');
const start = source.indexOf('GROK_SOURCE_SHA=$(git rev-parse HEAD)');
const end = source.indexOf('git push origin main', start);
assert.ok(start >= 0 && end > start);
const result = spawnSync('bash', ['-euo', 'pipefail'], {
cwd: fixture.root,
input: source.slice(start, end),
encoding: 'utf8',
env: { ...process.env, GROK_MARKETPLACE_JSON: '.grok-plugin/marketplace.json', VERSION: '9.9.9' },
});
if (contaminate) {
assert.strictEqual(result.status, 1, result.stderr);
assert.match(result.stderr, /pin .* is stale/);
assert.strictEqual(git(fixture.root, 'tag', '--list'), '');
} else {
assert.strictEqual(result.status, 0, result.stderr);
assert.strictEqual(validateGrokPin(fixture.root, { release: true }).sha, sourceSha);
assert.strictEqual(git(fixture.root, 'rev-parse', 'v9.9.9'), git(fixture.root, 'rev-parse', 'HEAD'));
assert.strictEqual(git(fixture.root, 'diff', '--name-only', sourceSha, 'HEAD'),
'.grok-plugin/marketplace.json');
}
} finally {
fs.rmSync(fixture.root, { recursive: true, force: true });
}
});
}
}
console.log(`\nResults: Passed: ${passed}, Failed: ${failed}`);
if (failed > 0) process.exitCode = 1;
+16 -1
View File
@@ -84,7 +84,7 @@ function runTests() {
const releaseCommitIndex = source.indexOf('git commit -m "chore: bump plugin version to $VERSION"');
const sourceShaIndex = source.indexOf('GROK_SOURCE_SHA=$(git rev-parse HEAD)');
const pinUpdateIndex = source.indexOf('plugin.source.sha = sha;');
const pinValidationIndex = source.indexOf('node scripts/ci/validate-grok-pin.js');
const pinValidationIndex = source.indexOf('node scripts/ci/validate-grok-pin.js --release');
const pinCommitIndex = source.indexOf('git commit -m "chore: pin Grok marketplace to release source"');
const tagIndex = source.indexOf('git tag "v$VERSION"');
@@ -96,9 +96,24 @@ function runTests() {
assert.ok(pinUpdateIndex > sourceShaIndex);
assert.ok(pinValidationIndex > pinUpdateIndex);
assert.ok(pinCommitIndex > pinValidationIndex);
const finalValidationIndex = source.lastIndexOf('node scripts/ci/validate-grok-pin.js --release');
assert.ok(finalValidationIndex > pinCommitIndex && finalValidationIndex < tagIndex,
'validate the committed release snapshot before tagging');
assert.ok(tagIndex > pinCommitIndex, 'the release tag must point at the pin-only commit');
})) passed++; else failed++;
if (test('release workflows enforce snapshot equality while ordinary CI accepts historical pins', () => {
for (const workflow of [releaseWorkflowSource, reusableReleaseWorkflowSource]) {
assert.match(workflow, /run: node scripts\/ci\/validate-grok-pin\.js --release/);
}
const reusableValidation = fs.readFileSync(path.join(
__dirname, '../../.github/workflows/reusable-validate.yml'
), 'utf8');
for (const workflow of [ciWorkflowSource, reusableValidation]) {
assert.match(workflow, /run: node scripts\/ci\/validate-grok-pin\.js\s*\n/);
}
})) passed++; else failed++;
if (test('release script supports prerelease semver and release heading sync', () => {
assert.ok(
source.includes('2.0.0-rc.1'),