Repository navigation
fix(ci): keep qwen-autofix.yml under GitHub's 500 KB start-runs limit #9517
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| #!/usr/bin/env bash | ||
| # GitHub does not START RUNS for a workflow file larger than 500 KB (512,000 | ||
| # bytes), and it says nothing when it stops: schedule ticks vanish, dispatches | ||
| # sit "queued" forever with zero jobs, `issues`/`issue_comment` go quiet, and | ||
| # only PR-event runs keep working — those resolve the workflow from the PR's | ||
| # own branch, so an older, smaller copy runs and the workflow looks half-alive. | ||
| # qwen-autofix.yml crossed the line on 2026-08-19 and the autofix loop went | ||
| # dark for a day before anyone read the size. | ||
| # | ||
| # The gate below sits well under the real ceiling on purpose: a PR that trips | ||
| # it still has room to land the fix, instead of discovering the wall with no | ||
| # space left to move prose out. | ||
| set -uo pipefail | ||
|
|
||
| GITHUB_LIMIT_BYTES=512000 | ||
| GATE_BYTES="${WORKFLOW_SIZE_GATE_BYTES:-470000}" | ||
| WARN_BYTES=$((GATE_BYTES - 25000)) | ||
|
|
||
| status=0 | ||
| shopt -s nullglob | ||
| for file in .github/workflows/*.yml .github/workflows/*.yaml; do | ||
| if ! size="$(wc -c <"${file}")"; then | ||
| echo "::error file=${file}::unable to read ${file}" | ||
| status=1 | ||
| continue | ||
| fi | ||
| pct=$((size * 100 / GITHUB_LIMIT_BYTES)) | ||
| if ((size > GATE_BYTES)); then | ||
| echo "::error file=${file}::${file} is ${size} bytes — ${pct}% of GitHub's ${GITHUB_LIMIT_BYTES}-byte start-runs limit, past this repo's ${GATE_BYTES}-byte gate. Move prose into a sibling .md and long steps into .github/scripts/; do not raise the gate." | ||
| status=1 | ||
| elif ((size > WARN_BYTES)); then | ||
| echo "::warning file=${file}::${file} is ${size} bytes (${pct}% of GitHub's limit) — approaching the ${GATE_BYTES}-byte gate." | ||
| fi | ||
| done | ||
|
|
||
| if ((status == 0)); then | ||
| echo "✅ every workflow file is under the ${GATE_BYTES}-byte gate" | ||
| fi | ||
| exit "${status}" |
This file was deleted.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -22,6 +22,10 @@ import { describe, expect, it } from 'vitest'; | |||||||||||||||||
| import { getWorkflowJob } from './workflow-helpers.js'; | ||||||||||||||||||
|
|
||||||||||||||||||
| const workflow = readFileSync('.github/workflows/qwen-autofix.yml', 'utf8'); | ||||||||||||||||||
| // Long-form rationale moved out of the YAML when the file approached | ||||||||||||||||||
| // GitHub's 500 KB start-runs limit; assertions that pin a REASON (rather | ||||||||||||||||||
| // than a code line) read it here. | ||||||||||||||||||
| const designDoc = readFileSync('.github/workflows/qwen-autofix.md', 'utf8'); | ||||||||||||||||||
| const ciWorkflow = readFileSync('.github/workflows/ci.yml', 'utf8'); | ||||||||||||||||||
| const releaseWorkflow = readFileSync('.github/workflows/release.yml', 'utf8'); | ||||||||||||||||||
| const sandboxImageResolverScript = readFileSync( | ||||||||||||||||||
|
|
@@ -3864,13 +3868,11 @@ describe('qwen-autofix workflow', () => { | |||||||||||||||||
| // idle candidates hit `continue` before the TARGETS append, so they | ||||||||||||||||||
| // never contend for it; the real win is inspection budget + walk | ||||||||||||||||||
| // latency, and the comment says so. | ||||||||||||||||||
| expect(reviewScanJob).toContain( | ||||||||||||||||||
| 'Idle PRs never reach the\n # 10-target budget', | ||||||||||||||||||
| ); | ||||||||||||||||||
| expect(designDoc).toContain('Idle PRs never reach the\n10-target budget'); | ||||||||||||||||||
| // The two scan-only signals updatedAt cannot see are named, not | ||||||||||||||||||
| // papered over by an absolute invariant. | ||||||||||||||||||
| expect(reviewScanJob).toContain('base conflict'); | ||||||||||||||||||
| expect(reviewScanJob).toContain('still-red checks'); | ||||||||||||||||||
| expect(designDoc).toContain('base conflict'); | ||||||||||||||||||
| expect(designDoc).toContain('still-red checks'); | ||||||||||||||||||
|
Comment on lines
+3874
to
+3875
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] The migrated assertion lost its scope: it previously matched only
Suggested change
中文说明迁移后的断言丢失了作用域:原先只匹配 — qwen3.8-max via Qwen Code /review (v0.21.11) |
||||||||||||||||||
| }); | ||||||||||||||||||
|
|
||||||||||||||||||
| it('fails closed on busy-enumeration failure, keeps explicit dispatches, and signals the issue phase', () => { | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /** | ||
| * @license | ||
| * Copyright 2026 Qwen Team | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| import { readFileSync, readdirSync } from 'node:fs'; | ||
| import { join } from 'node:path'; | ||
| import { describe, expect, it } from 'vitest'; | ||
|
|
||
| // GitHub does not start runs for a workflow file over 500 KB (512,000 bytes) | ||
| // and reports nothing when it stops — see .github/scripts/check-workflow-size.sh | ||
| // and .github/workflows/qwen-autofix.md for the incident this encodes. | ||
| const GITHUB_LIMIT_BYTES = 512_000; | ||
| const WORKFLOW_DIR = '.github/workflows'; | ||
| const gateScript = readFileSync( | ||
| '.github/scripts/check-workflow-size.sh', | ||
| 'utf8', | ||
| ); | ||
| const ciWorkflow = readFileSync(join(WORKFLOW_DIR, 'ci.yml'), 'utf8'); | ||
|
|
||
| const gateBytes = Number( | ||
| gateScript.match(/GATE_BYTES="\$\{WORKFLOW_SIZE_GATE_BYTES:-(\d+)\}"/)?.[1], | ||
| ); | ||
|
Comment on lines
+22
to
+24
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] No test executes 中文说明没有任何测试会执行 — qwen3.8-max via Qwen Code /review (v0.21.11) |
||
|
|
||
| const workflowFiles = readdirSync(WORKFLOW_DIR) | ||
| .filter((name) => name.endsWith('.yml') || name.endsWith('.yaml')) | ||
| .map((name) => join(WORKFLOW_DIR, name)); | ||
|
|
||
| describe('workflow file size', () => { | ||
| it('keeps the gate below GitHub 500 KB start-runs limit', () => { | ||
| expect(gateBytes).toBeGreaterThan(0); | ||
| expect(gateBytes).toBeLessThan(GITHUB_LIMIT_BYTES); | ||
| }); | ||
|
|
||
| it.each(workflowFiles)('%s stays under the gate', (file) => { | ||
| const bytes = Buffer.byteLength(readFileSync(file)); | ||
| expect(bytes).toBeLessThan(gateBytes); | ||
| }); | ||
|
|
||
| it('runs the gate on every CI profile, not just full', () => { | ||
| // A .github-only PR classifies as `github_ci_only`; gating the check on the | ||
| // `full` profile would skip it for exactly the changes that can trip it. | ||
|
Comment on lines
+42
to
+43
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Suggestion] Same false classifier claim as the ci.yml comment discussed in this review: a 中文说明与 ci.yml 中讨论过的注释是同一个错误的分类器断言:只改 — qwen3.8-max via Qwen Code /review (v0.21.11) |
||
| const step = ciWorkflow.match( | ||
| /- name: 'Check workflow file size'[\s\S]*?run: '(.+?)'/, | ||
| ); | ||
| expect(step?.[1]).toBe('.github/scripts/check-workflow-size.sh'); | ||
| expect(step?.[0]).toContain( | ||
| 'if: "${{ needs.classify_pr.outputs.skip_ci != \'true\' }}"', | ||
| ); | ||
| expect(step?.[0]).not.toContain('ci_profile'); | ||
| }); | ||
| }); | ||
|
|
||
| describe('qwen-autofix.yml design-record pointers', () => { | ||
| const workflow = readFileSync(join(WORKFLOW_DIR, 'qwen-autofix.yml'), 'utf8'); | ||
| const doc = readFileSync(join(WORKFLOW_DIR, 'qwen-autofix.md'), 'utf8'); | ||
|
|
||
| const pointers = [...workflow.matchAll(/qwen-autofix\.md#(af-\d+)/g)].map( | ||
| (m) => m[1], | ||
| ); | ||
| const anchors = [...doc.matchAll(/<a id="(af-\d+)"><\/a>/g)].map((m) => m[1]); | ||
|
|
||
| it('every pointer resolves to a section', () => { | ||
| expect(pointers.length).toBeGreaterThan(0); | ||
| expect( | ||
| [...new Set(pointers)].filter((id) => !anchors.includes(id)), | ||
| ).toEqual([]); | ||
| }); | ||
|
|
||
| it('every section is still pointed at from the workflow', () => { | ||
| expect(anchors.filter((id) => !pointers.includes(id))).toEqual([]); | ||
| }); | ||
|
|
||
| it('lists every section in the contents table', () => { | ||
| const listed = [...doc.matchAll(/^- \[\d+\..*?\]\(#(af-\d+)\)$/gm)].map( | ||
| (m) => m[1], | ||
| ); | ||
| expect(listed).toEqual(anchors); | ||
| }); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Suggestion] This comment (and its twin in
scripts/tests/workflow-size.test.js) claims a PR touching nothing but.github/classifies asgithub_ci_only, butclassify-profile.mjsdefinesGITHUB_CI_ONLY_FILESas a hardcoded 3-file allowlist (pr-safety-precheck.mjs, its test, andqwen-pr-safety-precheck.yml), andclassify-profile.test.mjspinsclassifyChangedFiles(['.github/workflows/ci.yml']) === 'full'. Probe against the real classifier at this commit:ci.yml only → full,qwen-autofix.yml only → full,all 3 allowlisted → github_ci_only. The step's actual gating (skip_ci only, runs on every profile) is correct and test-pinned — only the recorded rationale is wrong. Failure scenario: a maintainer trusting this comment re-gates the step onto thegithub_ci_onlyprofile and silently removes the size gate from every workflow-editing PR — exactly the PRs that can trip the 500 KB limit. Reword to match the classifier:github_ci_onlyis a 3-file allowlist (one of which is a workflow file — the narrow case this gate additionally covers), while workflow-file PRs classify asfull.中文说明
这条注释(以及
scripts/tests/workflow-size.test.js中的孪生注释)声称只改.github/的 PR 会被分类为github_ci_only,但classify-profile.mjs把GITHUB_CI_ONLY_FILES定义为硬编码的 3 文件白名单(pr-safety-precheck.mjs、其测试、qwen-pr-safety-precheck.yml),且classify-profile.test.mjs钉住了classifyChangedFiles(['.github/workflows/ci.yml']) === 'full'。在本提交上对真实分类器探测:仅 ci.yml → full、仅 qwen-autofix.yml → full、三个白名单文件 → github_ci_only。该 step 的实际门控(仅 skip_ci、所有 profile 都跑)是正确且有测试钉住的——只是注释记录的理由错了。失败场景:维护者相信这条注释,把该 step 改为仅在github_ci_onlyprofile 下运行,就会静默地让所有改动 workflow 的 PR 失去大小门禁——而那恰恰是最可能触发 500 KB 限制的 PR。请按分类器实情改写:github_ci_only是 3 文件白名单(其中之一是 workflow 文件——正是本门禁额外覆盖的窄场景),而改 workflow 的 PR 分类为full。— qwen3.8-max via Qwen Code /review (v0.21.11)