From 8ae2fc0241085f0112d4ad81d3dc536d7f273502 Mon Sep 17 00:00:00 2001 From: coseto6125 <80243681+coseto6125@users.noreply.github.com> Date: Sun, 24 May 2026 01:32:19 +0800 Subject: [PATCH 1/2] diagnostic(ci): instrument all 5 checkout sites to capture flake state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'could not read Username for https://github.com' / 'Bad credentials' failures across today's runs (#388 macos, #393 main-push ubuntu, #401 ubuntu test, etc.) all happen inside actions/checkout@v6.0.2's 'Fetching the repository' step right after a successful 'Setting up auth' that writes an includeIf-scoped credentials config. Failure looks consistent with includeIf gitdir path resolution mismatch, but we have no direct evidence yet — the hypothesis ought to be confirmed before we swap to a workaround that downgrades security (persist-credentials: true) or replaces actions/checkout outright. This PR adds .github/actions/diagnose-checkout-failure (composite action) and wires every actions/checkout call site in ci.yml to: 1. continue-on-error: true on the checkout step (id: checkout) 2. follow-up step that runs only on steps.checkout.conclusion == 'failure' and dumps: - git version - workspace path / readlink chain (catches symlink mismatches) - .git/config contents (raw) - resolved gitdir from git rev-parse --absolute-git-dir - effective config after includeIf evaluation - existence of every includeIf-referenced path - leftover credentials files in RUNNER_TEMP / /tmp / /github/runner_temp - per-component readlink of GITHUB_WORKSPACE (symlink detection) 3. Re-fails the job at the end so the diagnostic doesn't silently convert a real failure into a pass. Once the next flake fires, the dump will tell us which of: - includeIf path doesn't match actual gitdir - credentials file got cleaned up between setup and fetch - workspace path has a symlink we didn't anticipate - the runner had no token in the first place …actually caused the failure, and we can apply the proportionate fix. This PR is intentionally diagnostic-only: no behavior change beyond the failure-path noise. Once root cause is identified and fixed, revert by removing the diagnostic step / continue-on-error from each checkout block. --- .../diagnose-checkout-failure/action.yml | 116 ++++++++++++++++++ .github/workflows/ci.yml | 32 +++++ 2 files changed, 148 insertions(+) create mode 100644 .github/actions/diagnose-checkout-failure/action.yml diff --git a/.github/actions/diagnose-checkout-failure/action.yml b/.github/actions/diagnose-checkout-failure/action.yml new file mode 100644 index 00000000..62ce4033 --- /dev/null +++ b/.github/actions/diagnose-checkout-failure/action.yml @@ -0,0 +1,116 @@ +name: Diagnose checkout failure +description: | + Dumps the runner environment, git config, includeIf path resolution, + and credentials file existence when actions/checkout fails. Used to + pin down the intermittent "could not read Username" / "Bad credentials" + flake. Re-fails the job at the end so the diagnostic doesn't silently + convert a failure into a pass. + + Wire-up: + - uses: actions/checkout@ + id: checkout + continue-on-error: true + with: ... + - uses: ./.github/actions/diagnose-checkout-failure + if: steps.checkout.conclusion == 'failure' + +runs: + using: composite + steps: + - shell: bash + run: | + set +e + echo "::group::Runtime environment" + echo "PWD: $(pwd)" + echo "HOME: ${HOME:-(unset)}" + echo "GIT_DIR: ${GIT_DIR:-(unset)}" + echo "GITHUB_WORKSPACE: ${GITHUB_WORKSPACE:-(unset)}" + echo "RUNNER_OS: ${RUNNER_OS:-(unset)}" + echo "RUNNER_TEMP: ${RUNNER_TEMP:-(unset)}" + echo "RUNNER_TOOL_CACHE: ${RUNNER_TOOL_CACHE:-(unset)}" + echo "GITHUB_EVENT_NAME: ${GITHUB_EVENT_NAME:-(unset)}" + echo "::endgroup::" + + echo "::group::Tool versions" + git --version + echo "git config diagnostics:" + git config --show-origin --show-scope --list 2>&1 | head -40 || true + echo "::endgroup::" + + echo "::group::Workspace state" + ls -la "$GITHUB_WORKSPACE" 2>&1 | head -20 + echo "" + echo "readlink -f \$GITHUB_WORKSPACE: $(readlink -f "$GITHUB_WORKSPACE")" + echo "readlink -f \$GITHUB_WORKSPACE/.git: $(readlink -f "$GITHUB_WORKSPACE/.git" 2>&1)" + echo "::endgroup::" + + echo "::group::.git/config (if exists)" + if [ -f "$GITHUB_WORKSPACE/.git/config" ]; then + cat "$GITHUB_WORKSPACE/.git/config" + else + echo "(no .git/config)" + fi + echo "::endgroup::" + + echo "::group::Resolved gitdir from workspace" + if [ -d "$GITHUB_WORKSPACE/.git" ]; then + cd "$GITHUB_WORKSPACE" + echo "git rev-parse --git-dir: $(git rev-parse --git-dir 2>&1)" + echo "git rev-parse --absolute-git-dir: $(git rev-parse --absolute-git-dir 2>&1)" + echo "Effective config (after includeIf evaluation):" + git config --list --show-origin 2>&1 | head -60 + else + echo "(no .git directory)" + fi + echo "::endgroup::" + + echo "::group::includeIf credentials files referenced" + if [ -f "$GITHUB_WORKSPACE/.git/config" ]; then + # Pull out every includeIf .path = ; check if the file exists + # and (for visibility) show whether it's the expected extraheader. + # Sed strips the leading whitespace+key= so we get just the path. + grep -E '^\s*path = ' "$GITHUB_WORKSPACE/.git/config" 2>/dev/null | \ + sed -E 's/^\s*path = //' | while IFS= read -r p; do + if [ -f "$p" ]; then + echo " ✓ EXISTS: $p" + # Mask the token but show structure (extraheader presence) + grep -E '(\[|extraheader)' "$p" 2>&1 | sed 's/AUTHORIZATION:.*/AUTHORIZATION: ***/' + else + echo " ✗ MISSING: $p" + fi + done + else + echo "(no .git/config to inspect)" + fi + echo "::endgroup::" + + echo "::group::Any leftover credentials files in runner temp" + ls -la "${RUNNER_TEMP:-/tmp}/git-credentials-"*.config 2>&1 || echo "(none in RUNNER_TEMP)" + ls -la /tmp/git-credentials-*.config 2>&1 || echo "(none in /tmp)" + ls -la /github/runner_temp/git-credentials-*.config 2>&1 || echo "(none in /github/runner_temp)" + echo "::endgroup::" + + echo "::group::Mount layout (symlink detection)" + # If workspace path contains symlinks, includeIf's gitdir pattern + # may match before resolution but not after — surface this. + echo "df -h on workspace:" + df -h "$GITHUB_WORKSPACE" 2>&1 | head -3 + echo "" + echo "Component-by-component readlink:" + IFS=/ read -ra parts <<< "$GITHUB_WORKSPACE" + path="" + for part in "${parts[@]}"; do + [ -z "$part" ] && continue + path="$path/$part" + resolved=$(readlink -f "$path" 2>&1) + if [ "$path" != "$resolved" ]; then + echo " $path → $resolved (DIFFERENT)" + else + echo " $path" + fi + done + echo "::endgroup::" + + echo "" + echo "::error::actions/checkout failed; diagnostic captured above. Re-failing job intentionally." + exit 1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e3817ea..72b32942 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,12 +65,20 @@ jobs: egress-policy: audit - name: Checkout code + id: checkout + continue-on-error: true uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: # Need history to diff PR head against base ref. fetch-depth: 0 persist-credentials: false + # Diagnostic only fires on checkout failure; re-fails the job at end + # so the green/red signal stays accurate. See action.yml for context. + - name: Diagnose checkout failure + if: steps.checkout.conclusion == 'failure' + uses: ./.github/actions/diagnose-checkout-failure + - name: Classify changed paths id: filter shell: bash @@ -145,10 +153,16 @@ jobs: - name: Checkout code if: needs.detect-changes.outputs.code == 'true' + id: checkout + continue-on-error: true uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Diagnose checkout failure + if: needs.detect-changes.outputs.code == 'true' && steps.checkout.conclusion == 'failure' + uses: ./.github/actions/diagnose-checkout-failure + - name: Install Rust toolchain if: needs.detect-changes.outputs.code == 'true' uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @@ -236,10 +250,16 @@ jobs: - name: Checkout code if: needs.detect-changes.outputs.code == 'true' + id: checkout + continue-on-error: true uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Diagnose checkout failure + if: needs.detect-changes.outputs.code == 'true' && steps.checkout.conclusion == 'failure' + uses: ./.github/actions/diagnose-checkout-failure + - name: Install Rust toolchain if: needs.detect-changes.outputs.code == 'true' uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @@ -336,10 +356,16 @@ jobs: - name: Checkout code if: needs.detect-changes.outputs.code == 'true' + id: checkout + continue-on-error: true uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Diagnose checkout failure + if: needs.detect-changes.outputs.code == 'true' && steps.checkout.conclusion == 'failure' + uses: ./.github/actions/diagnose-checkout-failure + - name: Install Rust toolchain (audit 需要 cargo metadata 來解 Cargo.lock) if: needs.detect-changes.outputs.code == 'true' uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable @@ -402,10 +428,16 @@ jobs: egress-policy: audit - name: Checkout code + id: checkout + continue-on-error: true uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false + - name: Diagnose checkout failure + if: steps.checkout.conclusion == 'failure' + uses: ./.github/actions/diagnose-checkout-failure + - name: Run actionlint uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0 with: From bd778011b3a0f0ff02a463c6076846f52a3be850 Mon Sep 17 00:00:00 2001 From: coseto6125 <80243681+coseto6125@users.noreply.github.com> Date: Sun, 24 May 2026 02:27:20 +0800 Subject: [PATCH 2/2] ci: re-trigger checks for merge commit 53e93a7 The 'Update branch' merge commit was authored by the GitHub UI using GITHUB_TOKEN, which by GH Actions design does not trigger downstream pull_request workflows on the resulting SHA. Only the Mergify check (lazy-evaluated) registered against 53e93a7; CI / CodeQL / ecp PR analyze all targeted the prior SHA (8ae2fc0). This empty commit emits a user-authored push so pull_request:synchronize fires and the full check matrix runs against the actual PR head.