mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-08-11 01:36:35 +00:00
* feat(ci): add stale failure patrol * fix(ci): harden failure patrol * refactor(ci): simplify flaky rerun patrol * docs(ci): clarify flaky patrol skill boundary * feat(ci): patrol stale PR failures * fix(ci): prefilter failed PRs * fix(ci): isolate patrol classification * fix(ci): revalidate stale patrol actions * fix(ci): verify main before branch update * fix(ci): classify all stale PR failures * fix(ci): bound patrol batches * fix(ci): persist patrol failure state * fix(ci): harden patrol state transitions * fix(ci): harden stale failure patrol * fix(ci): continue patrol after expired logs * fix(ci): tighten patrol guardrails * fix(ci): preserve failure context in patrol logs * fix(ci): harden stale patrol closeout * fix(ci): paginate patrol marker comments * fix(ci): harden patrol action guards * test(ci): cover patrol guard rails * fix(ci): harden patrol marker parsing * fix(ci): address patrol review followups * test(ci): cover patrol review edges * fix(ci): record patrol rerun marker first * fix(ci): harden patrol review edge cases * fix(ci): tighten stale failure patrol markers * refactor(ci): simplify flaky rerun patrol (2838→1258 lines) - Remove classification guards from actOnDecision (confidence check, action enum validation, boundedReason, update_branch multi-guard chain) - Move classification rules to SKILL.md prompt - Delete 40 source-code text matching tests, keep 20 behavior tests - Merge identity job into classify, remove SHA verification - Change scan sort order from oldest-first to newest-first - Remove unused functions: writeSkillInputs, failureKey, boundedReason, canAct, skillCandidate, mainRunSucceeded * fix(ci): show gh stderr in top-level error output When gh CLI returns non-zero exit, execFile rejects with an error whose .stderr contains the actual GitHub API diagnostic. Previously only one of stderr or message was shown; now both are printed. * fix(ci): address patrol review findings * fix(ci): make stale patrol actions recoverable * refactor(ci): simplify flaky rerun patrol * fix(ci): close flaky patrol review gaps * fix(ci): restore PR failure patrol actions * fix(ci): harden failure patrol scanning * fix(ci): address patrol review follow-ups * fix(ci): harden patrol parsing and coverage * fix(ci): classify failures against PR changes * fix(ci): harden patrol script input handling * fix(ci): remove unsafe auto branch update * test(ci): exercise patrol action limit * fix(ci): bind patrol actions to current evidence * fix(ci): count patrol actions per PR * fix(ci): redact quoted secret labels --------- Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
71 lines
2.7 KiB
JavaScript
71 lines
2.7 KiB
JavaScript
import { readFileSync } from 'node:fs';
|
|
import { describe, expect, it } from 'vitest';
|
|
import { parse } from 'yaml';
|
|
|
|
const workflow = readFileSync(
|
|
'.github/workflows/qwen-ci-flaky-rerun.yml',
|
|
'utf8',
|
|
);
|
|
const yml = parse(workflow);
|
|
const skill = readFileSync('.qwen/skills/ci-flaky-patrol/SKILL.md', 'utf8');
|
|
|
|
describe('ci failure patrol workflow', () => {
|
|
it('runs every ten minutes with serialized configurable batches', () => {
|
|
expect(yml.on.schedule[0].cron).toBe('*/10 * * * *');
|
|
expect(yml.concurrency).toEqual({
|
|
group: 'qwen-ci-flaky-rerun',
|
|
'cancel-in-progress': false,
|
|
});
|
|
expect(yml.env.ACTIVE_DAYS).toBe('7');
|
|
expect(yml.env.MAX_CANDIDATES_PER_RUN).toBe('5');
|
|
expect(workflow).toContain('--active-days "${ACTIVE_DAYS}"');
|
|
expect(workflow).toContain('--max-candidates "${MAX_CANDIDATES_PER_RUN}"');
|
|
});
|
|
|
|
it('keeps classifier credentials isolated and PAT writes explicit', () => {
|
|
const classifier = yml.jobs.classify.steps.find((step) =>
|
|
step.uses?.includes('qwen-code-action'),
|
|
);
|
|
expect(classifier.env).toEqual({ GH_TOKEN: '', GITHUB_TOKEN: '' });
|
|
expect(JSON.stringify(classifier)).not.toContain('CI_BOT_PAT');
|
|
expect(yml.jobs.act.permissions).toEqual({
|
|
actions: 'read',
|
|
contents: 'read',
|
|
'pull-requests': 'read',
|
|
});
|
|
expect(JSON.stringify(yml.jobs.act)).toContain('CI_BOT_PAT');
|
|
});
|
|
|
|
it('passes a decision batch through a trusted act job and always resets', () => {
|
|
expect(yml.jobs.classify.outputs).toHaveProperty('bot_login');
|
|
expect(workflow).toContain('ci-flaky-decisions.json');
|
|
expect(workflow).toContain('--input-sha');
|
|
expect(workflow).toContain('--trusted-marker-login');
|
|
const reset = yml.jobs.act.steps.find(
|
|
(step) => step.name === 'Reset successful failure state',
|
|
);
|
|
expect(reset.if).toContain('always()');
|
|
expect(reset['continue-on-error']).toBe(true);
|
|
expect(reset.env.GH_TOKEN).toContain('CI_BOT_PAT');
|
|
});
|
|
|
|
it('runs scan and act with the same trusted event commit', () => {
|
|
for (const job of [yml.jobs.classify, yml.jobs.act]) {
|
|
const checkout = job.steps.find((step) =>
|
|
step.uses?.includes('actions/checkout'),
|
|
);
|
|
expect(checkout.with.ref).toBe('${{ github.sha }}');
|
|
expect(checkout.with['persist-credentials']).toBe(false);
|
|
}
|
|
});
|
|
|
|
it('keeps judgment in the skill and GitHub writes in the driver', () => {
|
|
for (const action of ['rerun', 'comment', 'no_action']) {
|
|
expect(skill).toContain(action);
|
|
}
|
|
expect(skill).toContain('maximum of 3 actions');
|
|
expect(skill).toContain('main-branch failures');
|
|
expect(skill).toContain('changedFiles');
|
|
expect(skill).toContain('ci-flaky-decisions.json');
|
|
});
|
|
});
|