Skip to content

Commit 256d6f1

Browse files
authored
fix: bridge GH token to git auth (#15)
* fix(shell): bridge gh token to git credentials * fix(shell): auto-configure git credentials after gh auth * test(docker-git): assert generated auth helper in entrypoint * refactor(lib): split entrypoint git template blocks --------- Co-authored-by: skulidropek <skulidropek@users.noreply.github.com>
1 parent 03162d8 commit 256d6f1

File tree

4 files changed

+110
-10
lines changed

4 files changed

+110
-10
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { describe, expect, it } from "@effect/vitest"
2+
import { Effect } from "effect"
3+
4+
import { defaultTemplateConfig } from "@effect-template/lib/core/domain"
5+
import { renderEntrypoint } from "@effect-template/lib/core/templates-entrypoint"
6+
7+
describe("renderEntrypoint auth bridge", () => {
8+
it.effect("maps GH token fallback to git auth and sets git credential helper", () =>
9+
Effect.sync(() => {
10+
const entrypoint = renderEntrypoint({
11+
...defaultTemplateConfig,
12+
repoUrl: "https://github.com/org/repo.git",
13+
enableMcpPlaywright: false
14+
})
15+
16+
expect(entrypoint).toContain(
17+
"GIT_AUTH_TOKEN=\"${GIT_AUTH_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}\""
18+
)
19+
expect(entrypoint).toContain("GITHUB_TOKEN=\"${GITHUB_TOKEN:-${GH_TOKEN:-}}\"")
20+
expect(entrypoint).toContain("if [[ -n \"$GH_TOKEN\" || -n \"$GITHUB_TOKEN\" ]]; then")
21+
expect(entrypoint).toContain(String.raw`printf "export GITHUB_TOKEN=%q\n" "$EFFECTIVE_GITHUB_TOKEN"`)
22+
expect(entrypoint).toContain(String.raw`printf "%s\n" "GITHUB_TOKEN=$EFFECTIVE_GITHUB_TOKEN" >> "$SSH_ENV_PATH"`)
23+
expect(entrypoint).toContain("GIT_CREDENTIAL_HELPER_PATH=\"/usr/local/bin/docker-git-credential-helper\"")
24+
expect(entrypoint).toContain("token=\"$GITHUB_TOKEN\"")
25+
expect(entrypoint).toContain("token=\"$GH_TOKEN\"")
26+
expect(entrypoint).toContain(String.raw`printf "%s\n" "password=$token"`)
27+
expect(entrypoint).toContain("git config --global credential.helper")
28+
}))
29+
})

packages/docker-git/tests/core/templates.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ describe("planFiles", () => {
3939
const dockerfileSpec = specs.find(
4040
(spec) => spec._tag === "File" && spec.relativePath === "Dockerfile"
4141
)
42+
const entrypointSpec = specs.find(
43+
(spec) => spec._tag === "File" && spec.relativePath === "entrypoint.sh"
44+
)
4245

4346
expect(composeSpec !== undefined && composeSpec._tag === "File").toBe(true)
4447
expect(ignoreSpec !== undefined && ignoreSpec._tag === "File").toBe(true)
4548
expect(configSpec !== undefined && configSpec._tag === "File").toBe(true)
4649
expect(dockerfileSpec !== undefined && dockerfileSpec._tag === "File").toBe(true)
50+
expect(entrypointSpec !== undefined && entrypointSpec._tag === "File").toBe(true)
4751

4852
if (configSpec && configSpec._tag === "File") {
4953
expect(configSpec.contents).toContain(config.repoUrl)
@@ -61,6 +65,13 @@ describe("planFiles", () => {
6165
expect(dockerfileSpec.contents).toContain("ncurses-term")
6266
expect(dockerfileSpec.contents).toContain("tag-order builtins commands")
6367
}
68+
69+
if (entrypointSpec && entrypointSpec._tag === "File") {
70+
expect(entrypointSpec.contents).toContain(
71+
"GIT_CREDENTIAL_HELPER_PATH=\"/usr/local/bin/docker-git-credential-helper\""
72+
)
73+
expect(entrypointSpec.contents).toContain("token=\"$GITHUB_TOKEN\"")
74+
}
6475
}))
6576

6677
it.effect("includes Playwright sidecar files when enabled", () =>

packages/lib/src/core/templates-entrypoint/base.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ REPO_REF="\${REPO_REF:-}"
1010
FORK_REPO_URL="\${FORK_REPO_URL:-}"
1111
TARGET_DIR="\${TARGET_DIR:-${config.targetDir}}"
1212
GIT_AUTH_USER="\${GIT_AUTH_USER:-\${GITHUB_USER:-x-access-token}}"
13-
GIT_AUTH_TOKEN="\${GIT_AUTH_TOKEN:-\${GITHUB_TOKEN:-}}"
13+
GIT_AUTH_TOKEN="\${GIT_AUTH_TOKEN:-\${GITHUB_TOKEN:-\${GH_TOKEN:-}}}"
1414
GH_TOKEN="\${GH_TOKEN:-\${GIT_AUTH_TOKEN:-}}"
15+
GITHUB_TOKEN="\${GITHUB_TOKEN:-\${GH_TOKEN:-}}"
1516
GIT_USER_NAME="\${GIT_USER_NAME:-}"
1617
GIT_USER_EMAIL="\${GIT_USER_EMAIL:-}"
1718
CODEX_AUTO_UPDATE="\${CODEX_AUTO_UPDATE:-1}"

packages/lib/src/core/templates-entrypoint/git.ts

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,72 @@
11
import type { TemplateConfig } from "../domain.js"
22

3-
export const renderEntrypointGitConfig = (config: TemplateConfig): string =>
4-
String.raw`# 2) Ensure GH_TOKEN is available for SSH sessions if provided
5-
if [[ -n "$GH_TOKEN" ]]; then
6-
printf "export GH_TOKEN=%q\n" "$GH_TOKEN" > /etc/profile.d/gh-token.sh
3+
const renderEntrypointAuthEnvBridge = (config: TemplateConfig): string =>
4+
String.raw`# 2) Ensure GitHub auth vars are available for SSH sessions if provided
5+
if [[ -n "$GH_TOKEN" || -n "$GITHUB_TOKEN" ]]; then
6+
EFFECTIVE_GITHUB_TOKEN="$GITHUB_TOKEN"
7+
if [[ -z "$EFFECTIVE_GITHUB_TOKEN" ]]; then
8+
EFFECTIVE_GITHUB_TOKEN="$GH_TOKEN"
9+
fi
10+
11+
EFFECTIVE_GH_TOKEN="$GH_TOKEN"
12+
if [[ -z "$EFFECTIVE_GH_TOKEN" ]]; then
13+
EFFECTIVE_GH_TOKEN="$EFFECTIVE_GITHUB_TOKEN"
14+
fi
15+
16+
printf "export GH_TOKEN=%q\n" "$EFFECTIVE_GH_TOKEN" > /etc/profile.d/gh-token.sh
17+
printf "export GITHUB_TOKEN=%q\n" "$EFFECTIVE_GITHUB_TOKEN" >> /etc/profile.d/gh-token.sh
718
chmod 0644 /etc/profile.d/gh-token.sh
819
SSH_ENV_PATH="/home/${config.sshUser}/.ssh/environment"
9-
printf "%s\n" "GH_TOKEN=$GH_TOKEN" > "$SSH_ENV_PATH"
10-
if [[ -n "$GITHUB_TOKEN" ]]; then
11-
printf "%s\n" "GITHUB_TOKEN=$GITHUB_TOKEN" >> "$SSH_ENV_PATH"
12-
fi
20+
printf "%s\n" "GH_TOKEN=$EFFECTIVE_GH_TOKEN" > "$SSH_ENV_PATH"
21+
printf "%s\n" "GITHUB_TOKEN=$EFFECTIVE_GITHUB_TOKEN" >> "$SSH_ENV_PATH"
1322
chmod 600 "$SSH_ENV_PATH"
1423
chown 1000:1000 "$SSH_ENV_PATH" || true
24+
25+
SAFE_GH_TOKEN="$(printf "%q" "$GH_TOKEN")"
26+
# Keep git+https auth in sync with gh auth so push/pull works without manual setup.
27+
su - ${config.sshUser} -c "GH_TOKEN=$SAFE_GH_TOKEN gh auth setup-git --hostname github.com --force" || true
28+
29+
GH_LOGIN="$(su - ${config.sshUser} -c "GH_TOKEN=$SAFE_GH_TOKEN gh api user --jq .login" 2>/dev/null || true)"
30+
GH_ID="$(su - ${config.sshUser} -c "GH_TOKEN=$SAFE_GH_TOKEN gh api user --jq .id" 2>/dev/null || true)"
31+
GH_LOGIN="$(printf "%s" "$GH_LOGIN" | tr -d '\r\n')"
32+
GH_ID="$(printf "%s" "$GH_ID" | tr -d '\r\n')"
33+
34+
if [[ -z "$GIT_USER_NAME" && -n "$GH_LOGIN" ]]; then
35+
GIT_USER_NAME="$GH_LOGIN"
36+
fi
37+
if [[ -z "$GIT_USER_EMAIL" && -n "$GH_LOGIN" && -n "$GH_ID" ]]; then
38+
GIT_USER_EMAIL="${"${"}GH_ID}+${"${"}GH_LOGIN}@users.noreply.github.com"
39+
fi
40+
fi`
41+
42+
const renderEntrypointGitCredentialHelper = (config: TemplateConfig): string =>
43+
String.raw`# 3) Configure git credential helper for HTTPS remotes
44+
GIT_CREDENTIAL_HELPER_PATH="/usr/local/bin/docker-git-credential-helper"
45+
cat <<'EOF' > "$GIT_CREDENTIAL_HELPER_PATH"
46+
#!/usr/bin/env bash
47+
set -euo pipefail
48+
49+
if [[ "$#" -lt 1 || "$1" != "get" ]]; then
50+
exit 0
51+
fi
52+
53+
token="$GITHUB_TOKEN"
54+
if [[ -z "$token" ]]; then
55+
token="$GH_TOKEN"
1556
fi
1657
17-
# 3) Configure git identity for the dev user if provided
58+
if [[ -z "$token" ]]; then
59+
exit 0
60+
fi
61+
62+
printf "%s\n" "username=x-access-token"
63+
printf "%s\n" "password=$token"
64+
EOF
65+
chmod 0755 "$GIT_CREDENTIAL_HELPER_PATH"
66+
su - ${config.sshUser} -c "git config --global credential.helper '$GIT_CREDENTIAL_HELPER_PATH'"`
67+
68+
const renderEntrypointGitIdentity = (config: TemplateConfig): string =>
69+
String.raw`# 4) Configure git identity for the dev user if provided
1870
if [[ -n "$GIT_USER_NAME" ]]; then
1971
SAFE_GIT_USER_NAME="$(printf "%q" "$GIT_USER_NAME")"
2072
su - ${config.sshUser} -c "git config --global user.name $SAFE_GIT_USER_NAME"
@@ -25,6 +77,13 @@ if [[ -n "$GIT_USER_EMAIL" ]]; then
2577
su - ${config.sshUser} -c "git config --global user.email $SAFE_GIT_USER_EMAIL"
2678
fi`
2779

80+
export const renderEntrypointGitConfig = (config: TemplateConfig): string =>
81+
[
82+
renderEntrypointAuthEnvBridge(config),
83+
renderEntrypointGitCredentialHelper(config),
84+
renderEntrypointGitIdentity(config)
85+
].join("\n\n")
86+
2887
export const renderEntrypointGitHooks = (): string =>
2988
String.raw`# 3) Install global git hooks to protect main/master
3089
HOOKS_DIR="/opt/docker-git/hooks"

0 commit comments

Comments
 (0)