mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
v1.91.122.0 fix(deps): clear the 2026-11-30 OSV suppressions, replace #3228, exempt dependabot from Version Gate rule 1 (#3239)
* fix(deps): clear the OSV suppressions that expire on 2026-11-30 Delete the hono, @anthropic-ai/sdk (#2754) and sprintf-js ignores, which the lockfiles no longer match. Override image-size to 2.0.4 (#2753) and chokidar to 4.0.3 in lib/diagram-render (drops braces), and delete those ignores. Re-date katex to 2027-01-31: its fix is outside mermaid 11's range (mermaid-js/mermaid#8388). html-to-docx 1.8.0 bundles its own image-size 1.x, which the override cannot reach and whose ICNS parser loops forever. make-pdf --to docx now hands it only PNG, JPEG, GIF, WebP or SVG bytes. A golden docx test proves the Word output is unchanged. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * build(deps-dev): @types/bun 1.4.3, prettier 3.9.10; hold the Anthropic SDKs to patches Replaces dependabot #3228 without its agent-sdk 0.3 and sdk 0.133 bumps, which break typecheck:test and overlay-sdk-cancel-eof. Their 0.x breaking releases are semver-minor to dependabot, so the ignore covers minor and major. @types/bun 1.4.3 retires 18 test type diagnostics; the baseline locks that in. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(ci): exempt dependabot dependency-manifest PRs from Version Gate rule 1 Dependabot never edits VERSION, so hard rule 1 failed every package.json PR it opened. The workflow now passes the PR author and changed-file list; the gate skips rule 1 only for dependabot[bot] when every file is a package.json, bun.lock(b) or .github/dependabot.yml. A failed file lookup keeps the rule. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(make-pdf): guard every html-to-docx sizing path, keep non-ICNS formats Review found three spellings that got past the regex guard and still hung --to docx: a data-src decoy, a percent-encoded separator, and an unquoted encoded colon. html-to-docx sizes images on three paths, and buildImage URI-decodes the whole src first. The guard now mirrors all three, drops ICNS (the only input its bundled image-size routes to the looping parser) instead of allowlisting formats, so BMP and TIFF embed as before, and re-parses the result with html-to-docx's own html-to-vdom to fail closed on anything the regex misreads. The golden test tolerates CRLF checkouts on Windows. Version Gate treats a failed file lookup as an empty list, so a partial paginated result cannot decide the exemption. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(release): stamp v1.91.121.0 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore(release): restamp v1.91.122.0 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
d258bcffb0
commit
9dea818e34
28 files changed
+554
-144
No files matched your search
@@ -262,7 +262,7 @@ test.each([
|
||||
[reviewStartLead, reviewStartLabels, 2],
|
||||
['D1 — No design doc found: run /office-hours before the review?', ['Run /office-hours now', 'Skip — proceed with review (Recommended)'], 2],
|
||||
[reviewStartLead, ['Skip — standard review', 'Run /office-hours first'], 1],
|
||||
...['Example: ', 'If approved: ', 'Do not ', '> ', ' ', '"', '`'].map(prefix => [prefix + reviewStartLead, reviewStartLabels, 1]),
|
||||
...['Example: ', 'If approved: ', 'Do not ', '> ', ' ', '"', '`'].map(prefix => [prefix + reviewStartLead, reviewStartLabels, 1] as const),
|
||||
['D1 — Discuss /office-hours in our documentation?', reviewStartLabels, 1],
|
||||
['D1 — Run /office-hours instead of this review?', reviewStartLabels, 1],
|
||||
[reviewStartLead, ['Run /office-hours first', 'Skip'], 1],
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Version Gate (scripts/compare-pr-version.ts). Hard rule 1 requires VERSION
|
||||
* above the base, which dependabot can never satisfy, so a dependabot PR that
|
||||
* touches only dependency manifests, lockfiles and dependabot config is exempt.
|
||||
* Every other author or file keeps the rule.
|
||||
*/
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const SCRIPT = path.join(ROOT, 'scripts', 'compare-pr-version.ts');
|
||||
|
||||
function gate(env: { PR_VERSION: string; PR_AUTHOR?: string; PR_CHANGED_FILES?: string }) {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'version-gate-'));
|
||||
try {
|
||||
const next = path.join(dir, 'next.json');
|
||||
fs.writeFileSync(next, JSON.stringify({ version: '1.91.117.0', base_version: '1.91.116.0', claimed: [{ pr: 3236, branch: 'fix-wave-5', version: '1.91.117.0' }] }));
|
||||
const run = Bun.spawnSync([process.execPath, 'run', SCRIPT, next, '3227'], {
|
||||
env: { PATH: process.env.PATH ?? '', ...env },
|
||||
timeout: 30_000,
|
||||
stdout: 'pipe',
|
||||
stderr: 'pipe',
|
||||
});
|
||||
return { code: run.exitCode, out: run.stdout.toString() + run.stderr.toString() };
|
||||
} finally {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
const DEPENDABOT = 'dependabot[bot]';
|
||||
|
||||
describe('version gate: dependabot dependency-manifest exemption', () => {
|
||||
test('an unbumped VERSION still fails for a human author', () => {
|
||||
const result = gate({ PR_VERSION: '1.91.116.0', PR_AUTHOR: 'garrytan', PR_CHANGED_FILES: 'package.json\nbun.lock' });
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.out).toContain('::error::VERSION not bumped');
|
||||
});
|
||||
|
||||
test('a dependabot PR behind main that touches only manifests, lockfiles and dependabot config passes', () => {
|
||||
const files = ['package.json', 'bun.lock', 'lib/diagram-render/package.json', 'lib/diagram-render/bun.lock', '.github/dependabot.yml'].join('\n');
|
||||
const result = gate({ PR_VERSION: '1.91.106.0', PR_AUTHOR: DEPENDABOT, PR_CHANGED_FILES: files + '\n' });
|
||||
expect(result.code).toBe(0);
|
||||
expect(result.out).toContain('::notice::PR #3227 is a dependabot dependency update');
|
||||
expect(result.out).not.toContain('::error::');
|
||||
});
|
||||
|
||||
test('a dependabot PR with any other file keeps hard rule 1', () => {
|
||||
for (const other of ['scripts/compare-pr-version.ts', 'VERSION', '.github/workflows/version-gate.yml', 'evil-package.json', 'package.json.bak']) {
|
||||
const result = gate({ PR_VERSION: '1.91.116.0', PR_AUTHOR: DEPENDABOT, PR_CHANGED_FILES: `package.json\nbun.lock\n${other}` });
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.out).toContain('::error::VERSION not bumped');
|
||||
}
|
||||
});
|
||||
|
||||
test('a missing file list or author keeps hard rule 1', () => {
|
||||
expect(gate({ PR_VERSION: '1.91.116.0', PR_AUTHOR: DEPENDABOT, PR_CHANGED_FILES: '' }).code).toBe(1);
|
||||
expect(gate({ PR_VERSION: '1.91.116.0', PR_AUTHOR: DEPENDABOT }).code).toBe(1);
|
||||
expect(gate({ PR_VERSION: '1.91.116.0', PR_CHANGED_FILES: 'package.json' }).code).toBe(1);
|
||||
});
|
||||
|
||||
test('a bumped dependabot VERSION goes through the collision rule as before', () => {
|
||||
const result = gate({ PR_VERSION: '1.91.117.0', PR_AUTHOR: DEPENDABOT, PR_CHANGED_FILES: 'package.json' });
|
||||
expect(result.code).toBe(1);
|
||||
expect(result.out).toContain('::error::VERSION collision');
|
||||
});
|
||||
|
||||
test('the workflow passes the PR author and its changed-file list to the gate', () => {
|
||||
const workflow = Bun.YAML.parse(fs.readFileSync(path.join(ROOT, '.github', 'workflows', 'version-gate.yml'), 'utf8')) as any;
|
||||
const step = workflow.jobs.check.steps.find((s: any) => s.name === 'Compare PR VERSION to next free slot');
|
||||
expect(step.env.PR_AUTHOR).toBe('${{ github.event.pull_request.user.login }}');
|
||||
expect(step.env.GH_TOKEN).toBe('${{ secrets.GITHUB_TOKEN }}');
|
||||
expect(step.run).toContain('pulls/${{ github.event.pull_request.number }}/files');
|
||||
expect(step.run).toContain('--paginate');
|
||||
expect(step.run).toContain('then PR_CHANGED_FILES=""; fi');
|
||||
expect(step.run).toContain('export PR_CHANGED_FILES');
|
||||
expect(workflow.jobs.check.permissions).toEqual({ contents: 'read', 'pull-requests': 'read' });
|
||||
});
|
||||
});
|
||||
@@ -50,4 +50,12 @@ describe("osv-scanner config wiring", () => {
|
||||
const page = fs.readFileSync(path.join(ROOT, "lib", "diagram-render", "dist", "diagram-render.html"), "utf8");
|
||||
expect(page).not.toMatch(/chokidar|FSWatcher/);
|
||||
});
|
||||
|
||||
test("the chokidar override keeps braces (GHSA-vfj7-8cjw-p6xm, no fixed release) out of lib/diagram-render", () => {
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, "lib", "diagram-render", "package.json"), "utf8"));
|
||||
expect(pkg.overrides.chokidar).toMatch(/^4\./);
|
||||
const lock = fs.readFileSync(path.join(ROOT, "lib", "diagram-render", "bun.lock"), "utf8");
|
||||
expect(lock).toMatch(/"chokidar": \["chokidar@4\.\d+\.\d+"/);
|
||||
expect(lock).not.toMatch(/"braces": \["braces@/);
|
||||
});
|
||||
});
|
||||
@@ -58,18 +58,15 @@ describe('OSV suppression expiry', () => {
|
||||
for (const text of ['"$SCAN_RESULT" = "failure"', 'gh issue create', 'gh issue comment', 'gh issue close']) expect(run).toContain(text);
|
||||
});
|
||||
|
||||
test('the image-size entries name the fixed release and the pin that blocks it (#2753)', () => {
|
||||
const toml = fs.readFileSync(path.join(import.meta.dir, '..', '.osv-scanner.toml'), 'utf8');
|
||||
const entries = toml.split('[[IgnoredVulns]]').slice(1).filter(e => /^id = "GHSA-(?:5p2g-fcmc-qvqq|w3rx-r6r6-pgpr)"/m.test(e));
|
||||
expect(entries).toHaveLength(2);
|
||||
for (const entry of entries) {
|
||||
expect(entry).not.toContain('No fixed version exists');
|
||||
expect(entry).toMatch(/^reason = ".*image-size 2\.0\.3.*\^1\.0\.0.*"$/m);
|
||||
expect(entry).toContain('ignoreUntil = 2026-11-30T00:00:00Z');
|
||||
}
|
||||
// The stated blocker is the lockfile's: html-to-docx asks for image-size ^1.0.0 and resolves a 1.x.
|
||||
test('image-size is overridden to a fixed 2.x and its suppressions are gone (#2753)', () => {
|
||||
const toml = fs.readFileSync(path.join(ROOT, CONFIG), 'utf8');
|
||||
expect(toml).not.toMatch(/GHSA-(?:5p2g-fcmc-qvqq|w3rx-r6r6-pgpr)/);
|
||||
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
|
||||
expect(pkg.overrides['image-size']).toBe('2.0.4');
|
||||
// html-to-docx still asks for ^1.0.0; the override is what moves the lockfile off the affected 1.x.
|
||||
const lock = fs.readFileSync(path.join(ROOT, 'bun.lock'), 'utf8');
|
||||
expect(lock).toMatch(/"html-to-docx": \["html-to-docx@[^"]+", "", \{ "dependencies": \{[^}]*"image-size": "\^1\.0\.0"/);
|
||||
expect(lock).toMatch(/"image-size": \["image-size@1\.\d+\.\d+"/);
|
||||
expect(lock).toMatch(/"image-size": \["image-size@2\.0\.4"/);
|
||||
expect(lock).not.toMatch(/"image-size": \["image-size@1\./);
|
||||
});
|
||||
});
|
||||
@@ -113,4 +113,13 @@ describe('workflow action pins', () => {
|
||||
expect(ignored[name]).toEqual({ 'dependency-name': name, 'update-types': ['version-update:semver-minor', 'version-update:semver-major'] });
|
||||
}
|
||||
});
|
||||
|
||||
test('Dependabot holds the Anthropic SDKs to patch updates; their 0.x breaking releases arrive as semver-minor', () => {
|
||||
const config = Bun.YAML.parse(fs.readFileSync(path.join(GITHUB_DIR, 'dependabot.yml'), 'utf-8')) as any;
|
||||
const bun = (config.updates ?? []).find((update: any) => update['package-ecosystem'] === 'bun');
|
||||
const ignored = Object.fromEntries((bun?.ignore ?? []).map((rule: any) => [rule['dependency-name'], rule]));
|
||||
for (const name of ['@anthropic-ai/claude-agent-sdk', '@anthropic-ai/sdk']) {
|
||||
expect(ignored[name]).toEqual({ 'dependency-name': name, 'update-types': ['version-update:semver-minor', 'version-update:semver-major'] });
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user