diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0fd431579e2..dc6337aaf6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -530,6 +530,11 @@ jobs: # Merge queue refs are ephemeral; check out the event head SHA directly so # slow hosted runners do not fail after the queue branch is removed. # Non-PR/non-queue events keep github.ref. + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' id: 'checkout' if: "${{ needs.classify_pr.outputs.skip_ci != 'true' }}" @@ -1017,6 +1022,11 @@ jobs: # Merge queue refs are ephemeral; check out the event head SHA directly so # slow hosted runners do not fail after the queue branch is removed. # Non-PR/non-queue events keep github.ref. + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' id: 'checkout' if: "${{ needs.classify_pr.outputs.skip_ci != 'true' }}" @@ -2047,6 +2057,11 @@ jobs: done || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: @@ -2278,6 +2293,11 @@ jobs: done || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: diff --git a/.github/workflows/qwen-autofix.yml b/.github/workflows/qwen-autofix.yml index db3046c3187..6055d181578 100644 --- a/.github/workflows/qwen-autofix.yml +++ b/.github/workflows/qwen-autofix.yml @@ -768,6 +768,11 @@ jobs: git config --local --unset-all core.hooksPath 2>/dev/null || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: @@ -3594,6 +3599,11 @@ jobs: git config --local --unset-all core.hooksPath 2>/dev/null || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout trusted base' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: @@ -3810,6 +3820,11 @@ jobs: git config --local --unset-all core.hooksPath 2>/dev/null || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout trusted base' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: diff --git a/.github/workflows/qwen-triage.yml b/.github/workflows/qwen-triage.yml index 7a20c18539e..f9f86154c26 100644 --- a/.github/workflows/qwen-triage.yml +++ b/.github/workflows/qwen-triage.yml @@ -610,6 +610,11 @@ jobs: git worktree prune -v || true echo "stale agent state cleaned" + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout repo' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: @@ -1323,6 +1328,11 @@ jobs: fi git worktree prune -v || true + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout PR merge ref' if: "steps.pr.outputs.decision == 'run'" uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 @@ -2895,6 +2905,11 @@ jobs: git worktree prune -v || true echo "stale agent state cleaned" + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout PR merge ref' if: "steps.pr.outputs.decision == 'run'" uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 diff --git a/.github/workflows/sdk-java.yml b/.github/workflows/sdk-java.yml index 89b037ada8c..095bf19e91b 100644 --- a/.github/workflows/sdk-java.yml +++ b/.github/workflows/sdk-java.yml @@ -92,7 +92,7 @@ jobs: test: name: '${{ matrix.os }} / Java ${{ matrix.java }}' runs-on: '${{ (matrix.os == ''ubuntu-latest'' && github.repository == ''QwenLM/qwen-code'' && vars.MAINTAINER_ECS_RUNNER_DISABLED != ''true'' && (github.event_name == ''push'' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON(''["OWNER","MEMBER","COLLABORATOR"]''), github.event.pull_request.author_association))) && fromJSON(''["self-hosted", "linux", "x64", "ecs-qwen"]'') || matrix.os }}' - timeout-minutes: 30 + timeout-minutes: 60 strategy: fail-fast: false matrix: @@ -215,6 +215,11 @@ jobs: done || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: @@ -272,8 +277,8 @@ jobs: run: |- mkdir -p "${HOME}/.cache/qwen-code-ci" exec 9>"${HOME}/.cache/qwen-code-ci/sdk-java-tests.lock" - if ! flock --wait 1200 9; then - echo "::error::sdk-java host lock not acquired within 20 minutes" + if ! flock --wait 2400 9; then + echo "::error::sdk-java host lock not acquired within 40 minutes" exit 1 fi mvn --batch-mode --no-transfer-progress clean test @@ -444,6 +449,11 @@ jobs: done || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 @@ -623,6 +633,11 @@ jobs: # gate certifies what merging would produce, cancel-in-progress # re-runs a displaced head, and the pool queue is seconds short. # Pinned in scripts/tests/sdk-java-workflow.test.js. + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 @@ -1010,6 +1025,11 @@ jobs: done || true fi + - name: 'Recreate runner Git metadata' + if: "${{ runner.environment == 'self-hosted' }}" + run: |- + rm -rf -- "$GITHUB_WORKSPACE/.git" + - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: diff --git a/scripts/tests/qwen-runner-checkout-workflow.test.js b/scripts/tests/qwen-runner-checkout-workflow.test.js new file mode 100644 index 00000000000..857454e7718 --- /dev/null +++ b/scripts/tests/qwen-runner-checkout-workflow.test.js @@ -0,0 +1,84 @@ +import { execFileSync } from 'node:child_process'; +import { + existsSync, + mkdirSync, + mkdtempSync, + readFileSync, + rmSync, + symlinkSync, + writeFileSync, +} from 'node:fs'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { parse } from 'yaml'; +import { expect, it } from 'vitest'; + +const jobs = [ + ...['triage', 'tmux-testing', 'verify'].map((name) => ['qwen-triage', name]), + ...['test', 'lint_and_static', 'integration_no_ak', 'integration_cli'].map( + (name) => ['ci', name], + ), + ...['test', 'flyway-migrations', 'mysql-integration', 'daemon-e2e'].map( + (name) => ['sdk-java', name], + ), + ...['issue-autofix', 'build-cli', 'review-address'].map((name) => [ + 'qwen-autofix', + name, + ]), +]; + +it.each(jobs)( + '%s / %s replaces runner Git metadata before checkout without following links', + (workflow, name) => { + const { steps } = parse( + readFileSync(`.github/workflows/${workflow}.yml`, 'utf8'), + ).jobs[name]; + const index = steps.findIndex( + (step) => step.name === 'Recreate runner Git metadata', + ); + expect(index).toBeGreaterThanOrEqual(0); + expect(steps[index].if).toBe("${{ runner.environment == 'self-hosted' }}"); + expect(steps[index + 1].uses).toMatch(/^actions\/checkout@/); + + const root = mkdtempSync(join(tmpdir(), 'qwen-checkout-')); + try { + const workspace = join(root, 'workspace'); + const external = join(root, 'external'); + mkdirSync(workspace); + mkdirSync(external); + const cached = join(workspace, 'node_modules'); + const preserved = join(external, 'keep'); + mkdirSync(cached); + writeFileSync(preserved, 'keep external repository'); + const git = (...args) => + execFileSync('git', args, { cwd: workspace, stdio: 'pipe' }); + const recover = () => + execFileSync('bash', ['-e', '-c', steps[index].run], { + cwd: workspace, + env: { ...process.env, GITHUB_WORKSPACE: workspace }, + }); + + git('init', '--quiet'); + writeFileSync( + join(workspace, '.git', 'shallow'), + 'broken shallow boundary', + ); + expect(() => git('rev-parse', '--is-shallow-repository')).toThrow(); + recover(); + expect(existsSync(join(workspace, '.git'))).toBe(false); + git('init', '--quiet'); + expect( + git('rev-parse', '--is-shallow-repository').toString().trim(), + ).toBe('false'); + + rmSync(join(workspace, '.git'), { recursive: true }); + symlinkSync(external, join(workspace, '.git')); + recover(); + expect(readFileSync(preserved, 'utf8')).toBe('keep external repository'); + expect(existsSync(cached)).toBe(true); + recover(); + } finally { + rmSync(root, { recursive: true, force: true }); + } + }, +); diff --git a/scripts/tests/sdk-java-workflow.test.js b/scripts/tests/sdk-java-workflow.test.js index ad71a5267d6..e15bc9be618 100644 --- a/scripts/tests/sdk-java-workflow.test.js +++ b/scripts/tests/sdk-java-workflow.test.js @@ -49,9 +49,9 @@ describe('SDK Java self-hosted workflow guards', () => { expect(block).toContain( 'exec 9>"${HOME}/.cache/qwen-code-ci/sdk-java-tests.lock"', ); - expect(block).toContain('flock --wait 1200 9'); + expect(block).toContain('flock --wait 2400 9'); expect(block).toContain( - '::error::sdk-java host lock not acquired within 20 minutes', + '::error::sdk-java host lock not acquired within 40 minutes', ); expect(block).toContain( 'if: "${{ runner.environment == \'github-hosted\' }}"', @@ -414,10 +414,10 @@ describe('SDK Java MariaDB lane on the ECS pool', () => { ).toBeGreaterThanOrEqual(mavenSteps.length * 2400 + 14 * 60); // The sibling test job shares the same per-host lock: its ceiling must // cover one full wait plus its own measured locked work (~5 minutes on - // the Java 21 leg of run 37743332082) — this job's holds now consume - // part of that wait budget, and nothing else asserts the headroom. + // the Java 21 leg of run 37743332082) and setup headroom. This job's holds + // now consume part of that wait budget. expect(parsed.jobs.test['timeout-minutes'] * 60).toBeGreaterThanOrEqual( - 1200 + 5 * 60, + 2400 + 10 * 60, ); });