Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: test
on:
push:
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: node --test test/*.test.js
- run: node scripts/check-versions.js ${{ startsWith(github.ref, 'refs/tags/v') && github.ref_name || '' }}
- run: pip install pytest && python3 -m pytest test/test_compress.py -q
33 changes: 33 additions & 0 deletions scripts/check-versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node
'use strict';
// Fails if comb's four manifests disagree on version (the ponytail v4.8.0 bug:
// manifests shipped stale, so plugins reported the old version after update).
// Optional: pass a release tag (v1.2.3) as argv[2] to also match it.
const fs = require('fs');
const path = require('path');

function readVersions(root) {
const read = (f) => fs.readFileSync(path.join(root, f), 'utf8');
const yaml = /^version:\s*["']?([^"'\s#]+)/m.exec(read('plugin.yaml'));
const market = JSON.parse(read('.claude-plugin/marketplace.json'));
return {
'package.json': JSON.parse(read('package.json')).version,
'plugin.yaml': yaml && yaml[1],
'.claude-plugin/plugin.json': JSON.parse(read('.claude-plugin/plugin.json')).version,
'.claude-plugin/marketplace.json': market.plugins && market.plugins[0] && market.plugins[0].version,
};
}

function checkVersions(root, tag) {
const v = readVersions(root);
const want = tag ? tag.replace(/^v/, '') : Object.values(v)[0];
const bad = Object.entries(v).filter(([, x]) => x !== want);
return { versions: v, expected: want, mismatches: bad.map(([f, x]) => `${f}: ${x === undefined || x === null ? 'missing' : x} (expected ${want})`) };
}

if (require.main === module) {
const r = checkVersions(path.join(__dirname, '..'), process.argv[2]);
if (r.mismatches.length) { console.error('version mismatch:\n ' + r.mismatches.join('\n ')); process.exit(1); }
console.log(`versions consistent: ${r.expected}`);
}
module.exports = { readVersions, checkVersions };
43 changes: 43 additions & 0 deletions test/versions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';
const test = require('node:test');
const assert = require('node:assert');
const fs = require('fs');
const os = require('os');
const path = require('path');
const { checkVersions } = require('../scripts/check-versions.js');

const repo = path.join(__dirname, '..');

function fixture(v) {
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'comb-ver-'));
fs.mkdirSync(path.join(d, '.claude-plugin'));
const w = (f, s) => fs.writeFileSync(path.join(d, f), s);
w('package.json', JSON.stringify({ version: v.pkg }));
w('plugin.yaml', `name: comb\nversion: "${v.yaml}"\n`);
w('.claude-plugin/plugin.json', JSON.stringify({ version: v.plugin }));
w('.claude-plugin/marketplace.json', JSON.stringify({ plugins: [{ version: v.market }] }));
return d;
}

test('repo manifests agree on one version', () => {
const r = checkVersions(repo);
assert.deepStrictEqual(r.mismatches, []);
});

test('detects a stale manifest', () => {
const r = checkVersions(fixture({ pkg: '0.2.0', yaml: '0.2.0', plugin: '0.1.0', market: '0.2.0' }));
assert.strictEqual(r.mismatches.length, 1);
assert.match(r.mismatches[0], /plugin\.json/);
});

test('release tag must match manifests', () => {
const d = fixture({ pkg: '1.0.0', yaml: '1.0.0', plugin: '1.0.0', market: '1.0.0' });
assert.deepStrictEqual(checkVersions(d, 'v1.0.0').mismatches, []);
assert.strictEqual(checkVersions(d, 'v1.0.1').mismatches.length, 4);
});

test('missing marketplace entry is reported', () => {
const d = fixture({ pkg: '1.0.0', yaml: '1.0.0', plugin: '1.0.0', market: '1.0.0' });
fs.writeFileSync(path.join(d, '.claude-plugin/marketplace.json'), JSON.stringify({ plugins: [] }));
assert.match(checkVersions(d).mismatches[0], /marketplace\.json: missing/);
});
Loading