|
| 1 | +--- |
| 2 | +name: "codespace-multi-repo" |
| 3 | +description: "Clone, pull, and push across multiple repositories from a Codespace/dev container without PATs — GitHub repos via GitHub CLI web login (avoiding GITHUB_TOKEN/GH_TOKEN interference) and Azure DevOps repos via a short-lived az CLI OAuth token. Use when working across more than one repo, gh auth login fails, git push/fetch returns 403/401, or when testing apiops init/pipeline changes across repos." |
| 4 | +domain: "developer-workflow" |
| 5 | +confidence: "high" |
| 6 | +source: "manual + repeated Codespaces troubleshooting" |
| 7 | +--- |
| 8 | + |
| 9 | +## Context |
| 10 | + |
| 11 | +Use this skill when working with **multiple repositories** from a Codespace or |
| 12 | +dev container and you need to **clone, pull, and push** to each without PAT-based |
| 13 | +auth or environment-provided `GITHUB_TOKEN`/`GH_TOKEN` credentials. |
| 14 | + |
| 15 | +Repos may live on different hosts (github.com and Azure DevOps), each with its |
| 16 | +own auth path: |
| 17 | + |
| 18 | +| Host | Clone / pull / push auth | |
| 19 | +|------|--------------------------| |
| 20 | +| github.com | GitHub CLI account login (`gh auth login -w`), token-sanitized git | |
| 21 | +| dev.azure.com | Short-lived OAuth token from `az account get-access-token` | |
| 22 | + |
| 23 | +This pattern is useful for cross-repo validation such as `apiops init` pipeline |
| 24 | +testing, where the CLI repo lives on GitHub and the scaffolded sample repo lives |
| 25 | +on Azure DevOps. |
| 26 | + |
| 27 | +## GitHub Repos: Clone, Pull, Push |
| 28 | + |
| 29 | +Run these commands in order: |
| 30 | + |
| 31 | +```bash |
| 32 | +unset GITHUB_TOKEN GH_TOKEN |
| 33 | + |
| 34 | +# Optional but recommended to clear stale account state |
| 35 | +gh auth logout -h github.com |
| 36 | + |
| 37 | +# Web/device login, no PAT |
| 38 | +env -u GITHUB_TOKEN -u GH_TOKEN gh auth login \ |
| 39 | + -h github.com \ |
| 40 | + -p https \ |
| 41 | + -w \ |
| 42 | + --clipboard \ |
| 43 | + --insecure-storage |
| 44 | + |
| 45 | +# Verify auth source is account login, not env token |
| 46 | +env -u GITHUB_TOKEN -u GH_TOKEN gh auth status |
| 47 | +``` |
| 48 | + |
| 49 | +Then clone to `/workspaces`: |
| 50 | + |
| 51 | +```bash |
| 52 | +cd /workspaces |
| 53 | +gh clone <owner>/<repo> |
| 54 | +``` |
| 55 | + |
| 56 | +After cloning, open workspaces so all repos can be opened: |
| 57 | + |
| 58 | +1. In VS Code, go to **File → Open Folder...** |
| 59 | +2. Navigate to and select `/workspaces` |
| 60 | +3. Click **Open** |
| 61 | + |
| 62 | +VS Code will reload with the cloned repository as your workspace. |
| 63 | + |
| 64 | +Pull and push use the same account auth. If the Codespace-injected env token |
| 65 | +overrides it (see Common Failure Modes), force gh credentials per operation: |
| 66 | + |
| 67 | +```bash |
| 68 | +cd /workspaces/<repo> |
| 69 | + |
| 70 | +# Pull |
| 71 | +env -u GITHUB_TOKEN -u GH_TOKEN git \ |
| 72 | + -c credential.helper= \ |
| 73 | + -c credential.helper='!gh auth git-credential' \ |
| 74 | + pull origin <branch> |
| 75 | + |
| 76 | +# Push |
| 77 | +env -u GITHUB_TOKEN -u GH_TOKEN git \ |
| 78 | + -c credential.helper= \ |
| 79 | + -c credential.helper='!gh auth git-credential' \ |
| 80 | + push origin <branch> |
| 81 | +``` |
| 82 | + |
| 83 | +## If Login Appears Stuck |
| 84 | + |
| 85 | +If `gh auth login` shows: |
| 86 | + |
| 87 | +- `First copy your one-time code: XXXX-YYYY` |
| 88 | +- `Press Enter to open https://github.com/login/device in your browser...` |
| 89 | + |
| 90 | +Use a second terminal: |
| 91 | + |
| 92 | +```bash |
| 93 | +$BROWSER https://github.com/login/device |
| 94 | +``` |
| 95 | + |
| 96 | +Enter the one-time code in the browser and authorize, then return to the original terminal and press Enter if needed. |
| 97 | + |
| 98 | +## Common Failure Modes |
| 99 | + |
| 100 | +- `gh auth status` shows `(GITHUB_TOKEN)`: |
| 101 | + environment token is still active. Re-run with `env -u GITHUB_TOKEN -u GH_TOKEN`. |
| 102 | + |
| 103 | +- Login exits with code `130`: |
| 104 | + usually interrupted (`Ctrl+C`) while waiting for device authorization. Restart login and complete browser step. |
| 105 | + |
| 106 | +- Browser does not launch from container: |
| 107 | + run `$BROWSER https://github.com/login/device` manually from a separate terminal. |
| 108 | + |
| 109 | +- Clone prompts for credentials unexpectedly: |
| 110 | + verify git protocol is HTTPS in `gh auth status` and re-run `gh auth login -p https` if necessary. |
| 111 | + |
| 112 | +- `git push`/`git fetch` returns `403` even though `gh auth status` looks correct: |
| 113 | + Codespaces can inject `GITHUB_TOKEN`/`GH_TOKEN` that override account auth for git operations. |
| 114 | + Use token-sanitized commands and force gh credentials for the operation: |
| 115 | + |
| 116 | + ```bash |
| 117 | + env -u GITHUB_TOKEN -u GH_TOKEN git \ |
| 118 | + -c credential.helper= \ |
| 119 | + -c credential.helper='!gh auth git-credential' \ |
| 120 | + push origin <branch> |
| 121 | + ``` |
| 122 | + |
| 123 | + For fetch/pull, use the same pattern with `fetch` or `pull`. |
| 124 | + |
| 125 | +## Azure DevOps Repos: Clone, Pull, Push (no PAT) |
| 126 | + |
| 127 | +Azure DevOps remotes (`https://dev.azure.com/<org>/<project>/_git/<repo>`) are |
| 128 | +not covered by the GitHub credential helper. In a Codespace the only configured |
| 129 | +helper is for github.com, so `git push`/`pull`/`clone` hangs or fails auth. |
| 130 | +Instead of a PAT, mint a short-lived OAuth token from the Azure CLI and pass it |
| 131 | +inline — never echo or persist it. |
| 132 | + |
| 133 | +```bash |
| 134 | +# Ensure the Azure CLI is logged in (interactive if needed — do NOT pass secrets via chat) |
| 135 | +az account show --query "{user:user.name, tenant:tenantId}" -o json |
| 136 | + |
| 137 | +# Well-known, public Azure DevOps resource (application) ID — the same constant |
| 138 | +# for every org worldwide; NOT a secret and not specific to your repo/tenant. |
| 139 | +# It tells Entra ID to issue a token scoped to Azure DevOps. |
| 140 | +ADO_RESOURCE_ID=499b84ac-1321-427f-aa17-267ca6975798 |
| 141 | +ADO_TOKEN=$(az account get-access-token \ |
| 142 | + --resource "$ADO_RESOURCE_ID" \ |
| 143 | + --query accessToken -o tsv) |
| 144 | + |
| 145 | +# Push using the token as a bearer header; token stays in a variable, never printed |
| 146 | +git -c http.extraheader="AUTHORIZATION: Bearer $ADO_TOKEN" push origin <branch> |
| 147 | +``` |
| 148 | + |
| 149 | +Clone and pull use the identical header pattern: |
| 150 | + |
| 151 | +```bash |
| 152 | +# Clone |
| 153 | +git -c http.extraheader="AUTHORIZATION: Bearer $ADO_TOKEN" \ |
| 154 | + clone https://dev.azure.com/<org>/<project>/_git/<repo> |
| 155 | + |
| 156 | +# Pull |
| 157 | +git -c http.extraheader="AUTHORIZATION: Bearer $ADO_TOKEN" pull origin <branch> |
| 158 | +``` |
| 159 | + |
| 160 | +Notes: |
| 161 | +- `499b84ac-1321-427f-aa17-267ca6975798` is the fixed, public Azure DevOps |
| 162 | + application ID in Microsoft Entra ID — identical for all organizations and not |
| 163 | + a credential. Use it as-is for `--resource` (or `.../.default` for scope-based |
| 164 | + requests). It is required so Entra ID issues a token Azure DevOps will accept. |
| 165 | +- The same `-c http.extraheader=...` pattern works for `fetch`/`pull`/`clone`. |
| 166 | +- The az login tenant must be the tenant that backs the Azure DevOps org. If the |
| 167 | + push returns `TF400813`/`401`, the logged-in identity has no access to that org |
| 168 | + — `az login --tenant <tenant>` against the correct tenant first. |
| 169 | +- Keep the token in a shell variable (`ADO_TOKEN=$(...)`) and reference it; do not |
| 170 | + print it or place it directly in a literal command. |
| 171 | + |
| 172 | +## Safety Notes |
| 173 | + |
| 174 | +- Never request a PAT for this workflow unless the user explicitly asks for a PAT-based approach. |
| 175 | +- Do not route secrets through chat prompts. |
| 176 | +- Keep authentication interactive in user terminal when account sign-in is required. |
| 177 | + |
| 178 | +## Example End-to-End |
| 179 | + |
| 180 | +```bash |
| 181 | +unset GITHUB_TOKEN GH_TOKEN |
| 182 | +gh auth logout -h github.com |
| 183 | +env -u GITHUB_TOKEN -u GH_TOKEN gh auth login -h github.com -p https -w --clipboard --insecure-storage |
| 184 | +env -u GITHUB_TOKEN -u GH_TOKEN gh auth status |
| 185 | +cd /workspaces |
| 186 | +gh clone <owner>/<repo> |
| 187 | +``` |
| 188 | + |
| 189 | +If you later need to push from a Codespace, use: |
| 190 | + |
| 191 | +```bash |
| 192 | +cd /workspaces/<repo> |
| 193 | +env -u GITHUB_TOKEN -u GH_TOKEN git \ |
| 194 | + -c credential.helper= \ |
| 195 | + -c credential.helper='!gh auth git-credential' \ |
| 196 | + push origin <branch> |
| 197 | +``` |
| 198 | + |
| 199 | +Then use **File → Open Folder...** to open `/workspaces` in VS Code. |
0 commit comments