Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
id: permissions-auth-111
title: "actions/checkout Global Auth Fails with 'Unable to replace auth placeholder' When GIT_CONFIG_GLOBAL Is Set"
category: permissions-auth
severity: error
tags:
- checkout
- submodules
- GIT_CONFIG_GLOBAL
- git-config
- self-hosted
- auth
- credential
- persist-credentials
patterns:
- regex: 'Unable to replace auth placeholder in .+\.gitconfig'
flags: 'i'
- regex: 'configureTempGlobalConfig|replaceTokenPlaceholder'
flags: 'i'
error_messages:
- "##[error]Unable to replace auth placeholder in /home/runner/_temp/<uuid>/.gitconfig"
- "Unable to replace auth placeholder in /runner/_work/_temp/.gitconfig"
root_cause: |
When `GIT_CONFIG_GLOBAL` is set in the environment (a common practice on self-hosted runners
to isolate git configuration per job), `actions/checkout` fails to inject the GITHUB_TOKEN
credential into its temporary gitconfig, producing "Unable to replace auth placeholder".

`checkout`'s `configureTempGlobalConfig` function works in three steps:
1. Copy `~/.gitconfig` into a temporary directory and override `HOME` to point there.
2. Run `git config --global` to write an auth token placeholder into the temp config.
3. Read the temp config and replace the placeholder with the real token.

The bug: **`GIT_CONFIG_GLOBAL` overrides `HOME`** when git resolves the global config file.
If `GIT_CONFIG_GLOBAL` is already set in the environment, step 2 writes the placeholder to
*that* external file instead of the temp config. Step 3 then reads the temp config, finds no
placeholder, and throws "Unable to replace auth placeholder in .../.gitconfig".

This affects any checkout scenario that uses global auth — primarily `submodules: true` and
`persist-credentials: true` (the default) combined with `submodules`. The `HOME`-only override
predates v6 so it affects v4, v5, and v6 equally.

Common trigger: self-hosted runner startup scripts that set
`GIT_CONFIG_GLOBAL=/shared/git/config` to isolate global git settings per job/runner.
Container-based runners that set `GIT_CONFIG_GLOBAL` in their base image are also affected.
fix: |
**Workaround (works today):** Unset `GIT_CONFIG_GLOBAL` in the checkout step's `env` block
so that checkout's `HOME` override takes effect:

- uses: actions/checkout@v6
with:
submodules: true
env:
GIT_CONFIG_GLOBAL: '' # unset so checkout's HOME override takes effect

**Permanent fix:** A patch is open (actions/checkout#2449) that pins `GIT_CONFIG_GLOBAL`
to the temp config alongside the `HOME` override. Use the version that ships this fix once
it is merged and released.

If you cannot modify the checkout step, as an alternative use `submodules: false` and
authenticate submodule access in a separate step after checkout.
fix_code:
- language: yaml
label: 'Broken: GIT_CONFIG_GLOBAL set by runner causes auth placeholder failure'
code: |
# Self-hosted runner has GIT_CONFIG_GLOBAL=/etc/gitconfig set in its environment.
# checkout writes the placeholder to /etc/gitconfig (via GIT_CONFIG_GLOBAL),
# then reads the temp config and finds no placeholder → error.
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v6
with:
submodules: true
# ERROR: "Unable to replace auth placeholder in /tmp/runner/_temp/.gitconfig"
- language: yaml
label: 'Fixed: unset GIT_CONFIG_GLOBAL in the checkout step env'
code: |
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v6
with:
submodules: true
env:
GIT_CONFIG_GLOBAL: '' # ← force checkout to use its own HOME override
# instead of the runner-injected global config
- language: yaml
label: 'Alternative: authenticate submodules separately without submodules: true'
code: |
jobs:
build:
runs-on: self-hosted
steps:
- uses: actions/checkout@v6
with:
submodules: false
persist-credentials: true

- name: Init and update submodules manually
run: |
git submodule init
git submodule update --recursive
env:
# Explicitly set the token for git operations in this step
GIT_CONFIG_GLOBAL: ''
prevention:
- "If your self-hosted runner sets `GIT_CONFIG_GLOBAL` in the environment (e.g. via /etc/environment or a container base image), always add `env: { GIT_CONFIG_GLOBAL: '' }` to checkout steps that use `submodules: true`."
- "The error 'Unable to replace auth placeholder in .../.gitconfig' always indicates a `GIT_CONFIG_GLOBAL` conflict — it never means the GITHUB_TOKEN is missing or invalid."
- "Audit self-hosted runner startup scripts and container base images for `GIT_CONFIG_GLOBAL` — it is a common source of intermittent checkout credential failures."
- "Use `persist-credentials: false` on checkout steps that do not need long-lived credentials after checkout, to avoid global auth setup entirely."
docs:
- url: 'https://github.com/actions/checkout/issues/2449'
label: 'actions/checkout #2449 — GIT_CONFIG_GLOBAL override breaks global auth (configureTempGlobalConfig)'
- url: 'https://git-scm.com/docs/git-config#Documentation/git-config.txt-GIT_CONFIG_GLOBAL'
label: 'git-scm.com — GIT_CONFIG_GLOBAL env var: overrides the global config file location, takes precedence over $HOME/.gitconfig'
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions'
label: 'GitHub Docs — Using secrets in GitHub Actions (credential management)'
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
id: runner-environment-432
title: "actions/checkout Fails on Windows When Repository Contains Branch Name Prefixes Differing Only by Case"
category: runner-environment
severity: error
tags:
- checkout
- windows
- case-insensitive
- git-refs
- branch-naming
- filesystem
- ntfs
- case-collision
patterns:
- regex: 'cannot lock ref.*exists.*cannot create'
flags: 'i'
- regex: 'fatal: cannot create.+directory.+[Ff]ile exists'
flags: 'i'
- regex: 'error: refs/remotes/origin/.+: cannot lock ref'
flags: 'i'
error_messages:
- "error: cannot lock ref 'refs/remotes/origin/Feature/tarun': 'refs/remotes/origin/feature' exists; cannot create 'refs/remotes/origin/Feature'"
- "fatal: cannot create directory at 'Feature': File exists."
- "error: Your local changes to the following files would be overwritten by checkout"
root_cause: |
On Windows runners, `actions/checkout` fails during the "Determining the checkout info"
or subsequent git fetch phase when the repository contains branch names whose path
components differ only by case — for example, `feature/manu` and `Feature/tarun`.

The root cause is that Windows uses the NTFS filesystem, which is case-insensitive
(case-preserving but not case-sensitive). Git stores remote-tracking refs as directory
structures: `refs/remotes/origin/feature/` and `refs/remotes/origin/Feature/` would
map to the same directory on Windows. When git attempts to create both, the second
ref creation fails with a lock error or "cannot create directory" error.

Linux runners are not affected because Linux filesystems (ext4, etc.) are case-sensitive
and can store both `feature/` and `Feature/` as distinct directories simultaneously.

This is ultimately a Git-on-Windows limitation rather than an actions/checkout bug, but
checkout surfaces it as an opaque error during the checkout phase. The issue affects
any version of actions/checkout (v4, v5, v6) on Windows runners.

Common branch naming patterns that trigger this issue:
- `feature/foo` and `Feature/bar` (mixed case in the directory component)
- `fix/issue-1` and `Fix/issue-2`
- `release/v1` and `Release/v2`
fix: |
The fix requires changing branch names in the repository to avoid case-only collisions
in path prefixes. On Windows, two branches cannot coexist if their path components
are identical when lowercased.

**Option 1 (recommended):** Rename branches to use a consistent case convention.
Most teams standardize on all-lowercase branch prefixes:

git branch -m Feature/tarun feature/tarun-2

**Option 2:** Run the Windows CI workflow only after avoiding the fetch of conflicting
refs using a sparse fetch:

- uses: actions/checkout@v6
with:
fetch-depth: 1
# fetch-depth: 1 avoids fetching all remote refs, which may avoid the collision

Note: this only helps if the checkout target branch is not itself one of the conflicting
refs. All remote-tracking refs are still fetched by default.

**Option 3:** Run Windows CI on Linux or macOS where the case collision does not apply,
and file a repository convention to standardize branch name casing.
fix_code:
- language: yaml
label: 'Broken: repo has feature/manu and Feature/tarun — fails on Windows'
code: |
# This workflow fails on windows-latest when the repo has:
# branch: feature/manu
# branch: Feature/tarun
# Git cannot create both refs/remotes/origin/feature/ and
# refs/remotes/origin/Feature/ on Windows NTFS.
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
# ERROR on Windows:
# error: cannot lock ref 'refs/remotes/origin/Feature/tarun':
# 'refs/remotes/origin/feature' exists; cannot create 'refs/remotes/origin/Feature'
- language: yaml
label: 'Workaround: use fetch-depth: 1 to limit which remote refs are fetched'
code: |
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 1 # fetch only the checked-out commit, not all remote refs
# This avoids fetching the conflicting case refs from origin.
# Note: git log history and other branch refs are unavailable.
- language: yaml
label: 'Fix: enforce lowercase branch naming convention via branch protection'
code: |
# In GitHub repository settings, add a branch name pattern rule that
# requires all new branches to match a lowercase pattern, preventing
# future case collisions:
#
# Repository Settings → Branches → Branch protection rules → Add rule
# Branch name pattern: [^A-Z]* (no uppercase letters allowed)
#
# To fix existing conflicting branches, rename them:
# git push origin :Feature/tarun # delete remote branch
# git push origin Feature/tarun:feature/tarun-v2 # recreate with lowercase
prevention:
- "Establish a branch naming convention that disallows uppercase letters in the path prefix component (e.g., always use `feature/`, never `Feature/`)."
- "Add a GitHub Actions workflow or branch protection rule that rejects branch names with uppercase in the prefix to prevent future case collisions."
- "Before migrating Windows runners into a repository's CI, audit branch names for case-insensitive prefix collisions using `git branch -r | sort -f`."
- "If your repository may have case-conflicting branches (e.g., from contributors on macOS/Linux who don't notice), use `fetch-depth: 1` on Windows checkout steps to reduce the risk."
docs:
- url: 'https://github.com/actions/checkout/issues/2448'
label: 'actions/checkout #2448 — checkout fails on Windows when branches differ only by case'
- url: 'https://git-scm.com/docs/git-fetch#Documentation/git-fetch.txt-ltrefspecgt'
label: 'git-scm.com — git fetch refspec: how remote-tracking refs are stored as directory trees'
- url: 'https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches'
label: 'GitHub Docs — Protected branches: enforcing naming conventions'
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
id: runner-environment-431
title: "macOS 15 Arm64 Runner Shows Analytics Setup Prompt — Breaks UI Focus-Dependent Tests"
category: runner-environment
severity: error
tags:
- macos
- macos-15
- analytics
- arm64
- electron
- ui-tests
- focus
- prompt
- runner-image
- regression
patterns:
- regex: 'runs-on:\s*macos-15'
flags: 'i'
- regex: 'macos-15[-_]arm64'
flags: 'i'
- regex: 'BrowserWindow.*focus|element.*not.*interactable.*macos'
flags: 'si'
error_messages:
- "Error: BrowserWindow can't grab focus on macos-15-arm64 runner"
- "element not interactable: Element is not reachable by keyboard"
- "Accessibility test failed: application window does not have focus"
- "osascript: unable to activate application: window focus stolen by system dialog"
root_cause: |
Starting with macOS 15 arm64 runner image version `20260520.0085.1`, a macOS system dialog
prompts to "Set Up Analytics" (or a similar OS-level analytics/privacy onboarding prompt)
when the runner VM session starts. This dialog appears in the foreground, captures keyboard
focus, and causes any CI test that requires the foreground window or keyboard/mouse focus
to fail.

Affected test categories include:
- Electron app tests using `BrowserWindow.focus()` or any call that expects the app window
to have foreground focus
- Eclipse IDE tests (confirmed: Eclipse GEF, Eclipse SWT)
- Browser-based UI tests using focus APIs (`element.focus()`, `sendKeys`, etc.)
- Screenshot-based or accessibility tests that depend on the active window
- Any test using macOS accessibility APIs or `osascript` to interact with windows

The prompt behavior is a regression introduced in image `20260520.0085.1` on
macOS 15 arm64. Image version `20260428.0039.1` and earlier did not show this prompt.
macOS 14 (arm64), macOS 26, and all x64 macOS runners are **not** affected.

Multiple major projects confirmed the regression:
- GitHub Electron (actions/runs/26368726651)
- Eclipse GEF (eclipse-gef/gef-classic#1123)
- Eclipse SWT (eclipse-platform/eclipse.platform.swt#3327)
fix: |
**Option 1 (recommended):** Add a step to dismiss the macOS Analytics prompt
programmatically before running UI tests:

- name: Dismiss macOS system prompts (prevents focus theft in UI tests)
run: |
osascript -e 'tell application "System Events" to key code 53' 2>/dev/null || true
sleep 1

`key code 53` is the Escape key — pressing Escape dismisses the Analytics/privacy dialog.
Adding `|| true` prevents the step from failing if no dialog is present.

**Option 2:** Switch to macOS 14 or macOS 26 (neither is affected by this regression):

runs-on: macos-14 # or: macos-26

Note: `macos-latest` will switch to macOS 26 in mid-June 2026, which also avoids
the regression.

**Option 3:** If your workflow specifically requires macOS 15 and you cannot switch,
disable analytics at the system level in a setup step:

- name: Disable macOS analytics (prevents prompts)
run: |
defaults write com.apple.assistant.support 'Assistant Enabled' -bool false
defaults write com.apple.SubmitDiagInfo AutoSubmit -bool false
fix_code:
- language: yaml
label: 'Workaround: dismiss the Analytics prompt before UI tests'
code: |
jobs:
test:
runs-on: macos-15
steps:
- uses: actions/checkout@v6

- name: Dismiss macOS system prompts (prevents focus theft in UI tests)
run: |
# Dismiss analytics/setup dialogs introduced in image 20260520.0085.1
osascript -e 'tell application "System Events" to key code 53' 2>/dev/null || true
sleep 1 # allow the dismiss to take effect

- name: Run UI tests
run: npm test # or your test command
- language: yaml
label: 'Workaround: switch to macOS 14 or macOS 26 (unaffected runners)'
code: |
jobs:
test:
# macOS 14 arm64 and macOS 26 do not show the Analytics prompt
runs-on: macos-14 # or: runs-on: macos-26
steps:
- uses: actions/checkout@v6
- name: Run UI tests
run: npm test
- language: yaml
label: 'Workaround: disable analytics via defaults write in setup step'
code: |
jobs:
test:
runs-on: macos-15
steps:
- uses: actions/checkout@v6

- name: Disable macOS analytics (prevents onboarding prompts from appearing)
run: |
defaults write com.apple.assistant.support 'Assistant Enabled' -bool false
defaults write com.apple.SubmitDiagInfo AutoSubmit -bool false
defaults write com.apple.PrivacyPreferences OptedOutForDiagnosticSubmission -bool true

- name: Run UI tests
run: npm test
prevention:
- "If your macOS 15 CI runs UI tests that require window focus, always add a prompt-dismissal step before the test runner."
- "Monitor the actions/runner-images issue tracker for macOS image regressions — subscribe to announcements before major image version bumps."
- "Consider switching to `macos-26` instead of `macos-15` for new pipelines; macOS 26 is unaffected and will become the `macos-latest` default in June 2026."
- "Pin a specific macOS image version in your runner config if you need to reproduce test failures across image versions."
docs:
- url: 'https://github.com/actions/runner-images/issues/14161'
label: 'actions/runner-images #14161 — macOS 15 arm64 runner shows Analytics prompt (regression since 20260520.0085.1)'
- url: 'https://github.com/electron/electron/actions/runs/26368726651/job/77802729527'
label: 'Electron CI example run — tests fail due to Analytics prompt stealing focus'
- url: 'https://github.com/eclipse-gef/gef-classic/issues/1123'
label: 'Eclipse GEF — several IDE tests fail on macOS due to Analytics prompt'
Loading