Skip to content

Commit 267cabe

Browse files
fix(ci): save all benchmark reports and use git-based dev versioning (#196)
* fix(cli): remove branch-compare references to non-existent module The branch-compare command was registered in cli.js and its exports added to index.js, but the implementation file src/branch-compare.js was never created. This caused two issues: 1. `codegraph branch-compare` crashed with ERR_MODULE_NOT_FOUND 2. `import('@optave/codegraph')` crashed entirely because index.js has a top-level re-export from the missing file, making the programmatic API completely unusable Remove the dead references until an implementation exists. Closes #166 * fix: recover branch-compare implementation from lost worktree files The branch-compare command was registered in cli.js and index.js but src/branch-compare.js was never committed — it was lost as untracked files in the fix/complexity-sql-sanitize worktree. Recovered the full implementation (568 lines) and integration test (192 lines, 7 tests) from git object 22c8185. This restores the cli.js command and index.js exports that were removed in 746aa65, now that the implementation file exists. Updated the dogfood report to reflect the recovery (no bugs found, rating 8.5/10). Closes #166 Impact: 13 functions changed, 4 affected * fix(ci): use commit-count versioning for dev builds and benchmarks Dev builds always produced 2.4.1-dev.<sha> because PATCH was hardcoded to +1 from package.json. Now uses git rev-list to count commits since the release tag, giving monotonically increasing versions like 2.4.5, 2.4.15, etc. Benchmark scripts now use getBenchmarkVersion() (via git describe) to detect dev vs release state, preventing dev runs from overwriting release benchmark data in historical reports. Impact: 2 functions changed, 6 affected * fix(bench): integrate getBenchmarkVersion into shared bench-config The bench-config.js local mode returned 'dev' as the version string. Now uses getBenchmarkVersion() to derive a proper semver from git state, so all 4 benchmark scripts that use bench-config get dev-aware versions. Impact: 1 functions changed, 4 affected * fix(ci): align version strategy between publish.yml and bench-version.js Both now use git describe --tags --match "v*" to find the nearest release tag and count commits from it. Previously publish.yml constructed the tag from package.json (v${CURRENT}) while bench-version.js used git describe, which would diverge if package.json was bumped but not yet tagged. Impact: 1 functions changed, 0 affected * fix(bench): align no-tags fallback with publish.yml behavior When no git tags exist, bench-version.js now produces PATCH+1 (e.g. "2.5.1-dev") matching publish.yml's fallback, instead of the bare "2.5.0-dev" which diverged. Impact: 1 functions changed, 0 affected * fix(bench): use same two-step git strategy as publish.yml Refactor getBenchmarkVersion to use --abbrev=0 + git rev-list (same as publish.yml) instead of parsing full git describe output. Both now use identical steps: find tag, count commits, get short SHA. Impact: 1 functions changed, 0 affected * fix(bench): include SHA in no-tags fallback to match publish.yml Impact: 1 functions changed, 0 affected * fix(ci): save all benchmark reports and use git-based dev versioning Merges PR #173 (commit-count versioning for dev builds) and fixes a bug where embedding, query, and incremental benchmark reports were never persisted. The "Check for changes" step used `git diff` which only detects modifications to tracked files — newly created report files (untracked) were invisible, so the PR was never created. Now also checks for untracked files via `git ls-files --others`. --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 8d7416b commit 267cabe

File tree

5 files changed

+119
-22
lines changed

5 files changed

+119
-22
lines changed

.github/workflows/benchmark.yml

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,11 +92,16 @@ jobs:
9292
- name: Check for changes
9393
id: changes
9494
run: |
95-
if git diff --quiet HEAD -- generated/BUILD-BENCHMARKS.md README.md; then
96-
echo "changed=false" >> "$GITHUB_OUTPUT"
97-
else
98-
echo "changed=true" >> "$GITHUB_OUTPUT"
95+
CHANGED=false
96+
# Detect modified tracked files
97+
if ! git diff --quiet HEAD -- generated/BUILD-BENCHMARKS.md README.md 2>/dev/null; then
98+
CHANGED=true
99+
fi
100+
# Detect newly created (untracked) files
101+
if [ -n "$(git ls-files --others --exclude-standard generated/BUILD-BENCHMARKS.md)" ]; then
102+
CHANGED=true
99103
fi
104+
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"
100105
101106
- name: Commit and push via PR
102107
if: steps.changes.outputs.changed == 'true'
@@ -212,11 +217,16 @@ jobs:
212217
- name: Check for changes
213218
id: changes
214219
run: |
215-
if git diff --quiet HEAD -- generated/EMBEDDING-BENCHMARKS.md; then
216-
echo "changed=false" >> "$GITHUB_OUTPUT"
217-
else
218-
echo "changed=true" >> "$GITHUB_OUTPUT"
220+
CHANGED=false
221+
# Detect modified tracked files
222+
if ! git diff --quiet HEAD -- generated/EMBEDDING-BENCHMARKS.md 2>/dev/null; then
223+
CHANGED=true
224+
fi
225+
# Detect newly created (untracked) files
226+
if [ -n "$(git ls-files --others --exclude-standard generated/EMBEDDING-BENCHMARKS.md)" ]; then
227+
CHANGED=true
219228
fi
229+
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"
220230
221231
- name: Commit and push via PR
222232
if: steps.changes.outputs.changed == 'true'
@@ -320,11 +330,16 @@ jobs:
320330
- name: Check for changes
321331
id: changes
322332
run: |
323-
if git diff --quiet HEAD -- generated/QUERY-BENCHMARKS.md; then
324-
echo "changed=false" >> "$GITHUB_OUTPUT"
325-
else
326-
echo "changed=true" >> "$GITHUB_OUTPUT"
333+
CHANGED=false
334+
# Detect modified tracked files
335+
if ! git diff --quiet HEAD -- generated/QUERY-BENCHMARKS.md 2>/dev/null; then
336+
CHANGED=true
337+
fi
338+
# Detect newly created (untracked) files
339+
if [ -n "$(git ls-files --others --exclude-standard generated/QUERY-BENCHMARKS.md)" ]; then
340+
CHANGED=true
327341
fi
342+
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"
328343
329344
- name: Commit and push via PR
330345
if: steps.changes.outputs.changed == 'true'
@@ -428,11 +443,16 @@ jobs:
428443
- name: Check for changes
429444
id: changes
430445
run: |
431-
if git diff --quiet HEAD -- generated/INCREMENTAL-BENCHMARKS.md; then
432-
echo "changed=false" >> "$GITHUB_OUTPUT"
433-
else
434-
echo "changed=true" >> "$GITHUB_OUTPUT"
446+
CHANGED=false
447+
# Detect modified tracked files
448+
if ! git diff --quiet HEAD -- generated/INCREMENTAL-BENCHMARKS.md 2>/dev/null; then
449+
CHANGED=true
450+
fi
451+
# Detect newly created (untracked) files
452+
if [ -n "$(git ls-files --others --exclude-standard generated/INCREMENTAL-BENCHMARKS.md)" ]; then
453+
CHANGED=true
435454
fi
455+
echo "changed=$CHANGED" >> "$GITHUB_OUTPUT"
436456
437457
- name: Commit and push via PR
438458
if: steps.changes.outputs.changed == 'true'

.github/workflows/publish.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ jobs:
4444
npm_tag: ${{ steps.compute.outputs.npm_tag }}
4545
steps:
4646
- uses: actions/checkout@v6
47+
with:
48+
fetch-depth: 0
4749

4850
- name: Compute version
4951
id: compute
@@ -65,12 +67,21 @@ jobs:
6567
NPM_TAG="latest"
6668
echo "Stable release (manual retry): $VERSION"
6769
else
68-
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
69-
DEV_PATCH=$((PATCH + 1))
70+
# Use git describe to find the nearest release tag — same strategy
71+
# as scripts/bench-version.js so versions are always consistent.
72+
RELEASE_TAG=$(git describe --tags --match "v*" --abbrev=0 2>/dev/null || echo "")
73+
if [ -n "$RELEASE_TAG" ]; then
74+
COMMITS=$(git rev-list "${RELEASE_TAG}..HEAD" --count)
75+
IFS='.' read -r MAJOR MINOR PATCH <<< "${RELEASE_TAG#v}"
76+
else
77+
COMMITS=1
78+
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
79+
fi
80+
DEV_PATCH=$((PATCH + COMMITS))
7081
SHORT_SHA=$(echo "${{ github.sha }}" | cut -c1-7)
7182
VERSION="${MAJOR}.${MINOR}.${DEV_PATCH}-dev.${SHORT_SHA}"
7283
NPM_TAG="dev"
73-
echo "Dev release: $VERSION"
84+
echo "Dev release: $VERSION (${COMMITS} commits since ${RELEASE_TAG:-none})"
7485
fi
7586
7687
echo "version=$VERSION" >> "$GITHUB_OUTPUT"

scripts/bench-version.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/**
2+
* Compute the benchmark version string from git state.
3+
*
4+
* Uses the same two-step strategy as publish.yml's compute-version job:
5+
* 1. `git describe --tags --match "v*" --abbrev=0` → find nearest release tag
6+
* 2. `git rev-list <tag>..HEAD --count` → count commits since that tag
7+
*
8+
* - If HEAD is exactly tagged (0 commits): returns "2.5.0"
9+
* - Otherwise: returns "2.5.N-dev.hash" (e.g. "2.5.3-dev.c50f7f5")
10+
* where N = PATCH + commits since tag, hash = short commit SHA
11+
*
12+
* This prevents dev/dogfood benchmark runs from overwriting release data
13+
* in the historical benchmark reports (which deduplicate by version).
14+
*/
15+
16+
import { execFileSync } from 'node:child_process';
17+
18+
const GIT_OPTS = { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] };
19+
20+
export function getBenchmarkVersion(pkgVersion, cwd) {
21+
try {
22+
// Step 1: find the nearest release tag (mirrors publish.yml --abbrev=0)
23+
const tag = execFileSync('git', ['describe', '--tags', '--match', 'v*', '--abbrev=0'], {
24+
cwd,
25+
...GIT_OPTS,
26+
}).trim();
27+
28+
// Step 2: count commits since that tag (mirrors publish.yml git rev-list)
29+
const commits = Number(
30+
execFileSync('git', ['rev-list', `${tag}..HEAD`, '--count'], { cwd, ...GIT_OPTS }).trim(),
31+
);
32+
33+
const m = tag.match(/^v(\d+)\.(\d+)\.(\d+)$/);
34+
if (!m) return `${pkgVersion}-dev`;
35+
36+
const [, major, minor, patch] = m;
37+
38+
// Exact tag (0 commits since tag): return clean release version
39+
if (commits === 0) return `${major}.${minor}.${patch}`;
40+
41+
// Dev build: MAJOR.MINOR.(PATCH+COMMITS)-dev.SHORT_SHA
42+
const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim();
43+
const devPatch = Number(patch) + commits;
44+
return `${major}.${minor}.${devPatch}-dev.${hash}`;
45+
} catch {
46+
/* git not available or no tags */
47+
}
48+
49+
// Fallback: no git or no tags — match publish.yml's no-tags behavior (PATCH+1-dev.SHA)
50+
const parts = pkgVersion.split('.');
51+
if (parts.length === 3) {
52+
const [major, minor, patch] = parts;
53+
try {
54+
const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim();
55+
return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`;
56+
} catch {
57+
return `${major}.${minor}.${Number(patch) + 1}-dev`;
58+
}
59+
}
60+
return `${pkgVersion}-dev`;
61+
}

scripts/lib/bench-config.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import os from 'node:os';
1313
import path from 'node:path';
1414
import { pathToFileURL } from 'node:url';
1515

16+
import { getBenchmarkVersion } from '../bench-version.js';
17+
1618
/**
1719
* Parse `--version <v>` and `--npm` from process.argv.
1820
*/
@@ -44,10 +46,11 @@ export async function resolveBenchmarkSource() {
4446
const { version: cliVersion, npm } = parseArgs();
4547

4648
if (!npm) {
47-
// Local mode — use repo src/, label as "dev" unless overridden
49+
// Local mode — use repo src/, version derived from git state
4850
const root = path.resolve(path.dirname(new URL(import.meta.url).pathname.replace(/^\/([A-Z]:)/, '$1')), '..', '..');
51+
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
4952
return {
50-
version: cliVersion || 'dev',
53+
version: cliVersion || getBenchmarkVersion(pkg.version, root),
5154
srcDir: path.join(root, 'src'),
5255
cleanup() {},
5356
};

scripts/token-benchmark.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,12 @@ import { fileURLToPath } from 'node:url';
2626
import { parseArgs } from 'node:util';
2727

2828
import { ISSUES, extractAgentOutput, validateResult } from './token-benchmark-issues.js';
29+
import { getBenchmarkVersion } from './bench-version.js';
2930

3031
const __dirname = path.dirname(fileURLToPath(import.meta.url));
3132
const root = path.resolve(__dirname, '..');
3233
const pkg = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8'));
34+
const benchVersion = getBenchmarkVersion(pkg.version, root);
3335

3436
// Redirect console.log to stderr so only JSON goes to stdout
3537
const origLog = console.log;
@@ -590,7 +592,7 @@ async function main() {
590592
console.log = origLog;
591593

592594
const output = {
593-
version: pkg.version,
595+
version: benchVersion,
594596
date: new Date().toISOString().slice(0, 10),
595597
model: MODEL,
596598
runsPerIssue: RUNS,

0 commit comments

Comments
 (0)