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
5 changes: 5 additions & 0 deletions .codegraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# CodeGraph data files — local to each machine, not for committing.
# Ignore everything in .codegraph/ except this file itself, so transient
# files (the database, daemon.pid, sockets, logs) never show up in git.
*
!.gitignore
46 changes: 44 additions & 2 deletions .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,15 +65,15 @@ jobs:
"github-actions[bot]":
oauth_token: stale-token
YAML
- name: Switch account exports provided token
- name: Switch account uses isolated config
id: switch-account
uses: ./
env:
GH_TOKEN: ${{ github.token }}
with:
token: ${{ github.token }}
switch-account: "true"
- run: test -n "$GH_TOKEN"
- run: test -z "${GH_TOKEN:-}"
- run: test -n "$GH_CONFIG_DIR"
- run: test "$GH_CONFIG_DIR" = "${{ steps.switch-account.outputs.gh-config-dir }}"
- run: test "$GH_CONFIG_DIR" != "$HOME/.config/gh"
Expand All @@ -89,6 +89,48 @@ jobs:
- run: test "${{ steps.switch-account.outputs.gh-config-dir }}" != "${{ steps.switch-account-again.outputs.gh-config-dir }}"
- run: test "$GH_CONFIG_DIR" != "$HOME/.config/gh"
- run: gh auth status --hostname github.com
- name: Mock gh where env token shadows stored auth
run: |
fake_bin="$(mktemp -d)"
cat > "$fake_bin/gh" <<'BASH'
#!/usr/bin/env bash
set -euo pipefail

case "$*" in
"--version")
echo "gh version 2.93.0 (mock)"
;;
"auth login --with-token --hostname github.com")
cat >/dev/null
;;
"auth status --hostname github.com")
if [ -n "${GH_TOKEN:-}" ]; then
echo "github.com" >&2
echo " X Failed to log in to github.com account github-actions[bot] (GH_TOKEN)" >&2
echo " - The token in GH_TOKEN is invalid." >&2
echo " ✓ Logged in to github.com account github-actions[bot] (${GH_CONFIG_DIR}/hosts.yml)" >&2
exit 1
fi
echo "github.com" >&2
echo " ✓ Logged in to github.com account github-actions[bot] (${GH_CONFIG_DIR}/hosts.yml)" >&2
;;
*)
echo "unexpected gh invocation: $*" >&2
exit 2
;;
esac
BASH
chmod +x "$fake_bin/gh"
echo "$fake_bin" >> "$GITHUB_PATH"
- name: Switch account clears env-token shadowing
id: env-token-shadowed
uses: ./
with:
token: mock-token
switch-account: "true"
- run: test "${{ steps.env-token-shadowed.outputs.auth }}" = "true"
- run: test -z "${GH_TOKEN:-}"
- run: gh auth status --hostname github.com
- name: Mock gh auth status failure
run: |
fake_bin="$(mktemp -d)"
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,11 @@ jobs:
`github.token`.

- **`switch-account`:** Whether to switch later `gh` commands to the provided
token, even when another account is already authenticated. This also exports
the provided token as `GH_TOKEN` and a fresh isolated `GH_CONFIG_DIR` for
later workflow steps. Each `switch-account: true` invocation creates its own
config directory. The default is `false`.
token, even when another account is already authenticated. This exports a
fresh isolated `GH_CONFIG_DIR` for later workflow steps and clears gh token
environment variables for the configured host so the isolated config is not
shadowed. Each `switch-account: true` invocation creates its own config
directory. The default is `false`.

- **`skip-matching-version`:** If `false`, installation is skipped whenever any
`gh` is already on `PATH`. If `true`, installation is skipped only when the
Expand Down
6 changes: 4 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@ inputs:
switch-account:
description: >
Whether to switch later 'gh' commands to the provided token, even when
another account is already authenticated. When true, this exports
'GH_TOKEN' and a fresh isolated 'GH_CONFIG_DIR' for later workflow steps.
another account is already authenticated. When true, this exports a
fresh isolated 'GH_CONFIG_DIR' for later workflow steps and clears gh
token environment variables for the configured host so the isolated
config is not shadowed.
default: false
skip-matching-version:
description: >
Expand Down
20 changes: 18 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,11 @@ function sanitizeAuthStatusOutput(output: string) {
}

async function getAuthStatus(hostname: string) {
const result = await $({ reject: false })`gh auth status --hostname ${hostname}`;
const result = await $({
reject: false,
env: envWithoutGhTokens(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: Stripping token env vars in getAuthStatus affects the non-switch-account path too

Because getAuthStatus is also used by the default (switch-account: false) flow at the else if ((await getAuthStatus(hostname)).ok) branch, removing GH_TOKEN/GITHUB_TOKEN from the probe is not scoped to switch-account. Previously, when a workflow provided a token input and had GH_TOKEN set in the environment, gh auth status succeeded via that env token and the action skipped login. Now the stripped probe sees no stored auth on a fresh runner, fails, and the action falls through to loginWithToken, which persists the provided token to the resolved config dir (the global $HOME/.config/gh when not switching). This can overwrite previously stored credentials on self-hosted/persistent runners and is a behavior change not exercised by the added tests. Consider scoping the env-stripping to the switch-account path, or document that the default path now writes the provided token to the user's gh config.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

extendEnv: false,
})`gh auth status --hostname ${hostname}`;
const output = sanitizeAuthStatusOutput(
[result.stdout, result.stderr].filter(Boolean).join("\n"),
);
Expand All @@ -179,6 +183,18 @@ async function setAuthOutputFromStatus(hostname: string, warning: string) {
return authStatus.ok;
}

function ghTokenEnvNamesForHost(hostname: string) {
return hostname === "github.com"
? ["GH_TOKEN", "GITHUB_TOKEN"]
: ["GH_ENTERPRISE_TOKEN", "GITHUB_ENTERPRISE_TOKEN"];
}

function clearGhTokenEnvForHost(hostname: string) {
for (const name of ghTokenEnvNamesForHost(hostname)) {
core.exportVariable(name, "");
}
}

const installedVersion = await getInstalledVersion();
if (installedVersion && !skipMatchingVersion) {
core.info(`Using existing GH CLI ${installedVersion} from PATH`);
Expand Down Expand Up @@ -214,7 +230,7 @@ if (!token) {
ghConfigDir = createGhConfigDir();
core.exportVariable("GH_CONFIG_DIR", ghConfigDir);
await loginWithToken(hostname, token);
core.exportVariable("GH_TOKEN", token);
clearGhTokenEnvForHost(hostname);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WARNING: switch-account no longer exports GH_TOKEN and also clears GITHUB_TOKEN

Two behavior changes from replacing core.exportVariable("GH_TOKEN", token) with clearGhTokenEnvForHost(hostname): (1) Workflows that previously relied on $GH_TOKEN being exported by this action after switch-account: true will now find it empty — a breaking change for existing consumers. (2) clearGhTokenEnvForHost sets both GH_TOKEN and GITHUB_TOKEN (or the enterprise equivalents) to "", which clobbers any job/workflow-level GITHUB_TOKEN/GITHUB_ENTERPRISE_TOKEN that subsequent non-gh steps may depend on, with no opt-out. The README/action.yml describe the high-level intent but don't surface these side effects; consider documenting them or verifying intended consumers are unaffected.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

await setAuthOutputFromStatus(
hostname,
`gh auth status --hostname ${hostname} failed after switch-account login; setting auth=false.`,
Expand Down
Loading