fix(agent-team): dispatch via GitHub App so P3 reaches CI (run_id resolves) #63

Merged
amoussa1229 merged 8 commits from fix/agent-team-dispatch-run-id into main 2026-06-24 22:22:19 +00:00
2 changed files with 23 additions and 11 deletions
Showing only changes of commit 265e32d9c9 - Show all commits

View file

@ -604,21 +604,27 @@ def app_branch_pusher(token_provider: Any, *, _run: Any = None) -> BranchPusher:
token = token_provider.token()
# Plain remote — the token is NEVER in the URL/argv/stored origin.
remote = f"https://github.com/{owner}/{repo}.git"
# Auth is injected via http.extraHeader through git's GIT_CONFIG_* env
# vars (NOT argv) on the network steps (clone + push). Env is visible
# only to the same uid via /proc/<pid>/environ, never via ps argv. The
# header key is SCOPED to the github.com host
# GitHub's git smart-HTTP transport authenticates an installation token via
# BASIC auth (username ``x-access-token``), NOT Bearer — Bearer is the REST
# API form and git rejects it ("could not read Username" → exit 128). Encode
# ``x-access-token:<token>`` and inject it as an Authorization header through
# git's GIT_CONFIG_* env vars (NOT argv) on the network steps (clone + push).
# Env is visible only to the same uid via /proc/<pid>/environ, never via ps
# argv. The header key is SCOPED to the github.com host
# (``http.https://github.com/.extraHeader``, the GitHub-Actions checkout
# pattern) so git never sends the Authorization header to any other host
# it might be redirected to.
# pattern) so git never sends the Authorization header to any other host it
# might be redirected to.
basic = base64.b64encode(f"x-access-token:{token}".encode()).decode("ascii")
auth_env = {
"GIT_CONFIG_COUNT": "1",
"GIT_CONFIG_KEY_0": "http.https://github.com/.extraHeader",
"GIT_CONFIG_VALUE_0": f"Authorization: Bearer {token}",
"GIT_CONFIG_VALUE_0": f"Authorization: Basic {basic}",
}
def _scrub(s: str) -> str:
return s.replace(token, "***")
# Scrub BOTH the raw token and the base64 credential blob (which decodes
# to the token) so neither can survive in any surfaced error message.
return s.replace(token, "***").replace(basic, "***")
with tempfile.TemporaryDirectory() as tmp:
tmpdir = Path(tmp)

View file

@ -374,12 +374,18 @@ def test_app_branch_pusher_keeps_token_out_of_argv_and_uses_plain_remote() -> No
for arg in call["cmd"]:
assert "ghs_TESTTOKEN" not in arg
# Auth rides in GIT_CONFIG_* env on the network steps only (clone + push).
# Auth rides in GIT_CONFIG_* env on the network steps only (clone + push), as
# BASIC auth (username x-access-token) — git smart-HTTP rejects Bearer.
expected_basic = "Authorization: Basic " + base64.b64encode(
b"x-access-token:ghs_TESTTOKEN"
).decode("ascii")
clone_env = recorded[0]["env"]
assert clone_env["GIT_CONFIG_KEY_0"] == "http.https://github.com/.extraHeader"
assert clone_env["GIT_CONFIG_VALUE_0"] == "Authorization: Bearer ghs_TESTTOKEN"
assert clone_env["GIT_CONFIG_VALUE_0"] == expected_basic
push_env = recorded[-1]["env"]
assert push_env["GIT_CONFIG_VALUE_0"] == "Authorization: Bearer ghs_TESTTOKEN"
assert push_env["GIT_CONFIG_VALUE_0"] == expected_basic
# The raw token never appears literally in the auth header (it is base64'd).
assert "ghs_TESTTOKEN" not in clone_env["GIT_CONFIG_VALUE_0"]
# The non-network steps (checkout/apply/commit) carry no auth env.
for call in recorded[1:4]:
assert call["env"] is None