You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* feat(engine): GitHub App-backed Copilot engine auth
Add engine.github-app-token front-matter to mint a GitHub App installation
token for the Copilot engine (issue #1316), source-clean and without a VSIX
or ADO service connection.
A new github-app-token ado-script bundle builds an RS256 JWT from the App ID
+ private key (ADO secret variables), resolves the installation for the owner,
exchanges it for an installation access token (optionally repo-scoped), and
exposes it as a masked same-job GITHUB_APP_TOKEN. The compiler wires it into
GITHUB_TOKEN for the Copilot engine only, in the Agent and Detection jobs; it
is never provided to SafeOutputs, user steps, ManualReview, Teardown, or
Conclusion. A best-effort revoke step deletes the token after the run.
Schema: app-id (literal or variable name), private-key (secret variable),
owner, optional repositories, optional api-url (GHES), skip-token-revocation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(engine): pin GitHub App token output var; validate revoke step
Addresses PR review feedback:
- Pin GH_APP_OUTPUT_VAR to the compiler constant (GITHUB_APP_TOKEN) in the
mint step. ADO injects pipeline variables as env vars, so without an explicit
step-level env entry a pipeline variable named GH_APP_OUTPUT_VAR could
silently redirect the minted token, leaving $(GITHUB_APP_TOKEN) empty and
breaking auth with no mint-time error. Step env overrides the pipeline-var
injection, closing the hole.
- Add cfg.validate() to github_app_token_revoke_step_typed for symmetry with
the mint step, so neither assumes the other ran first.
- Document why the revoke step intentionally omits `set -eo pipefail`
(best-effort cleanup; bundle revoke always exits 0; continueOnError).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(engine): pass GitHub App token knobs as argv, not env
Strengthens the GH_APP_OUTPUT_VAR fix (PR review): rather than pinning the
output-variable name in the step env, move ALL non-secret github-app-token
inputs to single-quoted argv flags (--app-id, --owner, --output-var,
--repositories, --api-url). ADO injects pipeline variables into a step's env,
so any env-sourced knob is shadowable; argv comes only from the compiler-
authored script and cannot be shadowed. This eliminates the class of bug at the
root for this bundle, not just the one instance.
Only the two secrets stay in masked env (GH_APP_PRIVATE_KEY on mint,
GH_APP_TOKEN on revoke) — a secret must never appear on a command line. Values
are single-quoted (with '\'' escaping) so metacharacters and the runtime-
substituted variable-form app-id value cannot expand or word-split.
Bundle main()/revoke() now take a parsed CliArgs; env is read only for the
secret. Updated Rust + vitest tests and docs accordingly.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(engine): harden GitHub App token arg parsing and secrecy advisory
Addresses PR review feedback:
- parseArgs (github-app-token bundle): never consume a `--flag`-looking token
as the value of a preceding flag. Keeps parsing aligned if a future compiler
emits a value-less boolean flag (e.g. --debug), instead of silently swallowing
the next real flag. Added vitest cases.
- Detection job: note that its inline install_and_download_steps_typed is the
only ado-script download path there, so a future feature should hoist a single
gated download rather than add a second.
- Emit a non-blocking compile advisory when engine.github-app-token is
configured, reminding the author to store the private-key variable as a
secret (the compiler validates the name but cannot verify secrecy). Added
unit tests.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* refactor(engine): compiler-owned defaults for github-app-token variables
Aligns github-app-token with ado-aw's GITHUB_TOKEN contract ("the compiler owns
the variable name; the user owns the value"):
- private-key is now OPTIONAL, defaulting to the compiler-owned secret variable
GITHUB_APP_PRIVATE_KEY (set via `ado-aw secrets set`). Set it only to override
with a differently-named secret. The common case needs no private-key line.
- app-id is a literal-only value (numeric App ID or alphanumeric client ID). The
variable-name form is removed, which also fixes a latent bug: the old
"all-digits => literal, else variable" heuristic mis-read client IDs like
`Iv23li...` as variable names and rendered `$(Iv23li...)`, breaking auth.
Output variable stays the fixed internal constant GITHUB_APP_TOKEN. Updated
validation, the secrecy advisory (names the effective variable), tests, and
docs/engine.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(engine): reject github-app-token on non-copilot engines
Addresses PR review: engine.github-app-token was silently a no-op for a
non-Copilot engine — the mint/revoke steps emit based on
github_app_token().is_some() (engine-agnostic), but GITHUB_TOKEN is only
sourced from $(GITHUB_APP_TOKEN) on the Copilot path (copilot_env).
Add validate_engine_feature_support: a hard compile error when
github-app-token is set with a non-copilot engine.id, run early in
compile_pipeline_inner so the author gets a precise message. Mirrors gh-aw's
engine-gated config validation (e.g. max-tool-denials, which errors
"supported only with engine 'copilot'"). ado-aw only supports copilot today
(get_engine rejects others), so this is primarily future-proofing + a precise
error, and the load-bearing guard once a second engine is added.
Adds unit tests + an integration test for the non-copilot rejection.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* fix(engine): reject negative app-id; tidy detection bundle gating
Addresses PR review feedback:
- Reject a negative/leading-punctuation app-id. `de_string_or_number`
stringifies a negative YAML integer (app-id: -7654321 -> "-7654321"), which
passed the [A-Za-z0-9._-] charset check because '-' is allowed, yielding a
bad JWT `iss` at runtime. validate() now requires the first character to be
alphanumeric. Added unit tests.
- Extract detection_job_needs_ado_script_bundle() predicate so the Detection
job stages the ado-script bundle once, and future detection-only consumers
OR into it (mirrors the Agent-job predicate in AdoScriptExtension) rather
than adding a second download.
- Add BashStep::with_continue_on_error builder so the revoke step is a clean
builder chain instead of interrupting it with a direct field mutation.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: document github-app-token advisory, engine-gating, and authoring prompts
- engine.md: note the compile-time secrecy advisory and that github-app-token
is a hard error on non-copilot engines (previously undocumented behaviors).
- create/update/debug authoring prompts: add github-app-token guidance
(org-backed Copilot auth) — a create-time engine note, an update scenario,
and a debug troubleshooting entry — each linking to docs/engine.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* docs: fix stale github-app-token struct-level doc
Addresses PR review: the GithubAppTokenConfig struct-level doc comment still
described app-id (and private-key) as "ADO pipeline variable names" with an
`app-id: GH_APP_ID` example, contradicting the field-level doc and the actual
schema. app-id is a literal value (numeric App ID or client ID); only
private-key names a secret variable. Fixed the opening sentence and the example
(app-id: 1234567). IDE hover / rustdoc now match runtime behavior.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---------
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy file name to clipboardExpand all lines: docs/engine.md
+113Lines changed: 113 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -30,6 +30,7 @@ engine:
30
30
| `args` | list | `[]` | Custom CLI arguments appended after compiler-generated args. Subject to shell-safety validation and blocked from overriding compiler-controlled flags (`--prompt`, `--additional-mcp-config`, `--allow-tool`, `--allow-all-tools`, `--allow-all-paths`, `--disable-builtin-mcps`, `--no-ask-user`, `--ask-user`). |
31
31
| `env` | map | *(none)* | Engine-specific environment variables merged into the sandbox step's `env:` block. Keys must be valid env var names. Values are literal-only and must not contain ADO expressions (`$(`, `${{`, `$[`) or pipeline command injection (`##vso[`), **except** the Copilot BYOM provider keys (`COPILOT_PROVIDER_BASE_URL`, `COPILOT_PROVIDER_API_KEY`, `COPILOT_PROVIDER_BEARER_TOKEN`, `COPILOT_PROVIDER_WIRE_API`), which may carry an ADO macro (`$(...)`) expression — see [Copilot BYOM / BYOK provider configuration](#copilot-byom--byok-provider-configuration). Compiler-controlled keys (`GITHUB_TOKEN`, `PATH`, `BASH_ENV`, etc.) are blocked. |
32
32
| `command` | string | *(none)* | Custom engine executable path (skips the default engine binary installation — NuGet for `target: 1es`, GitHub Releases for all other targets). The path must be accessible inside the AWF container (e.g., `/tmp/...` or workspace-mounted paths). |
33
+
| `github-app-token` | map | *(none)* | GitHub App-backed Copilot engine authentication. When set, the compiler mints (and, by default, revokes) a GitHub App installation token in the Agent and Detection jobs and sources `GITHUB_TOKEN` from it (for Copilot only). See [GitHub App-backed Copilot engine auth](#github-app-backed-copilot-engine-auth). |
33
34
34
35
35
36
### `timeout-minutes`
@@ -42,6 +43,118 @@ The `timeout-minutes` field sets a wall-clock limit (in minutes) for the entire
42
43
43
44
When omitted, Azure DevOps uses its default job timeout (60 minutes). When set, the compiler emits `timeoutInMinutes: <value>` on the agentic job.
44
45
46
+
### GitHub App-backed Copilot engine auth
47
+
48
+
By default the Copilot engine authenticates with the `GITHUB_TOKEN` pipeline
49
+
variable you provide (`ado-aw secrets set GITHUB_TOKEN <pat>`). For
50
+
organization-backed Copilot authentication you can instead have the compiler
51
+
mint a **GitHub App installation access token** at runtime — mirroring
app-id: 1234567 # literal App ID or client ID (required)
60
+
owner: octo-org # installation owner (org or user login)
61
+
repositories: [octo-repo] # optional; scopes the token to these repos
62
+
# api-url: https://ghe.example.com/api/v3 # optional; GHES base URL
63
+
# skip-token-revocation: false # optional; revoke by default
64
+
# private-key: MY_SECRET_VAR # optional override; see below
65
+
```
66
+
67
+
Store the private key once and you're done:
68
+
69
+
```bash
70
+
ado-aw secrets set GITHUB_APP_PRIVATE_KEY "$(cat app-private-key.pem)"
71
+
```
72
+
73
+
| Field | Required | Description |
74
+
|-------|----------|-------------|
75
+
| `app-id` | yes | The GitHub App ID — a **literal** value: either a numeric App ID (e.g. `1234567`, quoted or unquoted) or an alphanumeric client ID (e.g. `Iv23liABC…`). The App ID is not secret (it is plain per-app config, like `owner`), so it is written verbatim, not indirected through a variable. |
76
+
| `owner` | yes | GitHub installation owner (organization or user login). |
77
+
| `repositories` | no | Repository names (owner-relative) to scope the installation token to. Omit to span every repository the installation grants. |
78
+
| `api-url` | no | GitHub API base URL. Defaults to `https://api.github.com` (GHEC). For GitHub Enterprise Server, set the `/api/v3` base URL (e.g. `https://ghe.example.com/api/v3`). Must be an `https://` URL. |
79
+
| `skip-token-revocation` | no | When `true`, do not revoke the minted token after the Copilot run. Defaults to `false` (the token is revoked — see below). |
80
+
| `private-key` | no | Name of the ADO **secret** pipeline variable holding the private key (PEM). **Defaults to `GITHUB_APP_PRIVATE_KEY`** — the compiler owns the name, exactly like `GITHUB_TOKEN`, so the common case sets no field and just runs `ado-aw secrets set GITHUB_APP_PRIVATE_KEY …`. Set this only to point at a differently-named secret (e.g. reusing an existing variable). |
81
+
82
+
#### Setup
83
+
84
+
1. Create the GitHub App and install it on the owning org/user, ensuring the
85
+
installation is suitable for Copilot organization-backed
86
+
authentication/billing in your tenant. Copilot capability is granted on the
87
+
**App/org side** — the installation token inherits it automatically; you do
88
+
not (and cannot) configure it here.
89
+
2. Store the private key as an ADO **secret** pipeline variable — the default
90
+
name `GITHUB_APP_PRIVATE_KEY` unless you set a `private-key` override:
91
+
92
+
```bash
93
+
ado-aw secrets set GITHUB_APP_PRIVATE_KEY "$(cat app-private-key.pem)"
94
+
```
95
+
3. Set `engine.github-app-token` (`app-id` + `owner` at minimum) and compile.
96
+
Each compile prints a non-blocking advisory reminding you to store the
97
+
private-key variable as a secret — this is expected (the compiler cannot
98
+
verify secrecy itself).
99
+
100
+
#### What the compiler generates
101
+
102
+
- A **token-mint step** (the `github-app-token` ado-script bundle) runs
103
+
immediately before the Copilot invocation in **both** the Agent and Detection
104
+
jobs. It builds a short-lived RS256 JWT from the App ID + private key,
105
+
resolves the installation for `owner`, exchanges it for an installation
106
+
access token (optionally scoped to `repositories`), and exposes it as a
107
+
**masked, same-job** `GITHUB_APP_TOKEN` variable.
108
+
- The Copilot engine's `GITHUB_TOKEN` is then sourced from `$(GITHUB_APP_TOKEN)`
109
+
instead of the operator-provided `$(GITHUB_TOKEN)` variable.
110
+
- A **token-revocation step** runs after the Copilot invocation in both jobs
111
+
(unless `skip-token-revocation: true`). It deletes the minted token
112
+
(`DELETE /installation/token`) so it does not remain valid for its full ~1h
113
+
lifetime — matching `actions/create-github-app-token`'s default. Revocation is
114
+
best-effort (`always()` + `continueOnError`) and never fails the build.
115
+
- The token is **never** provided to SafeOutputs, user-authored `steps:`,
116
+
ManualReview, Teardown, or Conclusion.
117
+
118
+
The mint/revoke steps run **outside** the AWF network sandbox (like the
119
+
ADO-token and execution-context steps), reaching the GitHub API over the build
120
+
agent pool's normal network — no AWF `network.allowed` entry is required.
121
+
122
+
#### Scope and boundaries — what this token is *not*
123
+
124
+
- **ADO API permissions** (`permissions.read` / `permissions.write`) are
125
+
entirely separate: they describe Azure DevOps OAuth/service-connection tokens
126
+
used by the pipeline and Stage 3 executor. The GitHub App token has no effect
127
+
on them.
128
+
- The GitHub App token is for **Copilot engine authentication only**. It does
129
+
**not** authenticate the `gh` CLI, grant GitHub MCP permissions, or give the
130
+
agent sandbox GitHub write access.
131
+
- There is **no `permissions:` sub-field** to scope the token. Copilot access
132
+
rides on the App's own granted capability (configured App/org-side); narrowing
133
+
the installation token's permissions at mint time cannot grant Copilot access
134
+
and risks stripping the capability it needs.
135
+
136
+
#### Notes and limitations
137
+
138
+
- **Copilot engine only.** `github-app-token` is rejected at compile time for
139
+
any other `engine.id` (the minted token is wired into `GITHUB_TOKEN` only on
140
+
the Copilot path), so a misconfiguration fails fast rather than silently
141
+
no-opping.
142
+
- **Secrecy is your responsibility.** The compiler validates the private-key
143
+
*variable name* but cannot verify the ADO variable is actually marked secret,
144
+
so every compile of a `github-app-token` workflow emits an advisory:
Copy file name to clipboardExpand all lines: prompts/create-ado-agentic-workflow.md
+19Lines changed: 19 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -81,6 +81,25 @@ engine:
81
81
timeout-minutes: 30
82
82
```
83
83
84
+
**Org-backed Copilot auth (GitHub App).** By default Copilot authenticates with
85
+
the `GITHUB_TOKEN` pipeline variable. For organization-backed authentication,
86
+
add a `github-app-token` block so the compiler mints a GitHub App installation
87
+
token at runtime (Copilot engine only):
88
+
```yaml
89
+
engine:
90
+
id: copilot
91
+
github-app-token:
92
+
app-id: 1234567 # literal App ID or client ID
93
+
owner: octo-org # installation owner (org or user)
94
+
# repositories: [octo-repo] # optional; scopes the token
95
+
```
96
+
Store the private key once with `ado-aw secrets set GITHUB_APP_PRIVATE_KEY
97
+
"$(cat key.pem)"`. Only add this when the workflow explicitly needs org-backed
98
+
Copilot auth — omit it otherwise. See
99
+
[`docs/engine.md`](../docs/engine.md#github-app-backed-copilot-engine-auth) for
100
+
the full field reference (`private-key` override, `api-url` for GHES,
101
+
`skip-token-revocation`).
102
+
84
103
### Step 3 — Schedule
85
104
86
105
Use the **fuzzy schedule syntax** (deterministic time scattering based on agent name hash prevents load spikes). Omit `on.schedule` (or omit `on:` entirely) for manual/trigger-only pipelines.
Copy file name to clipboardExpand all lines: prompts/debug-ado-agentic-workflow.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -314,6 +314,7 @@ network:
314
314
timeout-minutes: 120
315
315
```
316
316
- **API rate limiting**: The model provider is rate-limiting requests. Check Copilot CLI logs for 429 responses.
317
+
- **GitHub App auth failures** (when `engine.github-app-token` is set): if Copilot fails to authenticate, check the `Mint GitHub App token` step logs in the Agent (and Detection) job. Common causes: the `GITHUB_APP_PRIVATE_KEY` secret (or your `private-key` override) is unset or not marked secret; a wrong `app-id`/`owner` (the App must be installed on that owner); or GHES without `api-url` set to the `/api/v3` base URL. `github-app-token` is Copilot-engine only — the compiler rejects it on other engines. See [`docs/engine.md`](../docs/engine.md#github-app-backed-copilot-engine-auth).
Copy file name to clipboardExpand all lines: prompts/update-ado-agentic-workflow.md
+28Lines changed: 28 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -229,6 +229,34 @@ permissions:
229
229
230
230
`permissions.write`is **optional** — the Stage 3 executor defaults to `$(System.AccessToken)`. Set it only when you need cross-org writes or named-identity attribution instead of `Project Collection Build Service`.
231
231
232
+
### Enabling GitHub App-backed Copilot Auth
233
+
234
+
Add a `github-app-token` block under `engine:` so Copilot authenticates with an
0 commit comments