Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions .github/agents/auth-expert.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,4 @@ When reviewing or writing auth code:
- Windows: `GIT_ASKPASS` must be `'echo'` not empty string
- Classic PATs (`ghp_`) work cross-org but are being deprecated — prefer fine-grained
- ADO uses Basic auth with base64-encoded `:PAT` — different from GitHub bearer token flow
- ADO also supports AAD bearer tokens via `az account get-access-token` (resource `499b84ac-1321-427f-aa17-267ca6975798`); precedence is `ADO_APM_PAT` -> az bearer -> fail. Stale PATs (401) silently fall back to the bearer with a `[!]` warning. See the auth skill for the four diagnostic cases.
Comment thread
danielmeppiel marked this conversation as resolved.
Outdated
31 changes: 31 additions & 0 deletions .github/skills/auth/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,34 @@ All auth flows MUST go through `AuthResolver`. No direct `os.getenv()` for token
## Canonical reference

The full per-org -> global -> credential-fill -> fallback resolution flow is in [`docs/src/content/docs/getting-started/authentication.md`](../../../docs/src/content/docs/getting-started/authentication.md) (mermaid flowchart). Treat it as the single source of truth; if behavior diverges, fix the diagram in the same PR.

## Bearer-token authentication for ADO

ADO hosts (`dev.azure.com`, `*.visualstudio.com`) resolve auth in this order:

1. `ADO_APM_PAT` env var if set
2. AAD bearer via `az account get-access-token --resource 499b84ac-1321-427f-aa17-267ca6975798` if `az` is installed and `az account show` succeeds
3. Otherwise: auth-failed error from `build_error_context`

Token source constants live in `src/apm_cli/core/token_manager.py`: `ADO_APM_PAT = "ADO_APM_PAT"`, `ADO_BEARER_SOURCE = "AAD_BEARER_AZ_CLI"`.
Comment thread
danielmeppiel marked this conversation as resolved.
Outdated

**Stale-PAT silent fallback:** if `ADO_APM_PAT` is rejected with HTTP 401, APM retries with the az bearer and emits:

```
[!] ADO_APM_PAT was rejected for {host} (HTTP 401); fell back to az cli bearer.
[!] Consider unsetting the stale variable.
```

**Verbose source line** (one per host, emitted under `--verbose`):

```
[i] dev.azure.com -- using bearer from az cli (source: AAD_BEARER_AZ_CLI)
[i] dev.azure.com -- token from ADO_APM_PAT
```

**Diagnostic cases** (`_emit_stale_pat_diagnostic` + `build_error_context` in `src/apm_cli/core/auth.py`):

1. No PAT, no `az`: `No ADO_APM_PAT was set and az CLI is not installed.` -> install `az`, run `az login --tenant <tenant>`, or set `ADO_APM_PAT`.
2. No PAT, `az` not signed in: `az CLI is installed but no active session was found.` -> run `az login --tenant <tenant>` against the tenant that owns the org, or set `ADO_APM_PAT`.
3. No PAT, wrong tenant: `az CLI returned a token but the org does not accept it (likely a tenant mismatch).` -> run `az login --tenant <correct-tenant>`, or set `ADO_APM_PAT`.
4. PAT 401, no `az` fallback: `ADO_APM_PAT was rejected (HTTP 401) and no az cli fallback was available.` -> rotate the PAT, or install `az` and run `az login --tenant <tenant>`.
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,45 @@ jobs:
include-hidden-files: true
retention-days: 30
if-no-files-found: error

# Dogfood the two CI gates we ship and document to users:
# - Gate A (consumer-side): `apm audit --ci` -- lockfile / install fidelity.
# - Gate B (producer-side): regeneration drift -- did someone hand-edit
# a regenerated file under .github/ without updating canonical .apm/?
# See microsoft/apm#883 for context. Tier 1 (no secrets needed).
apm-self-check:
name: APM Self-Check
runs-on: ubuntu-24.04
permissions:
contents: read
steps:
- uses: actions/checkout@v4

# Installs the APM CLI (latest stable) and runs `apm install` against
# this repo's apm.yml. Auto-detects target from the existing .github/
# directory and re-integrates local .apm/ content, regenerating
# .github/instructions/, .github/agents/, .github/skills/, etc.
# Adds `apm` to PATH for subsequent steps.
- uses: microsoft/apm-action@v1

# Gate A: lockfile / install fidelity (consumer-side).
# Verifies every file in lockfile.deployed_files exists, ref consistency
# between apm.yml and apm.lock.yaml, no orphan packages, and
# content-integrity (hidden Unicode) on deployed package content.
# Does NOT verify deployed-file content vs lockfile (see #684).
- name: apm audit --ci
run: apm audit --ci

# Gate B: regeneration drift (producer-side).
# The action's `apm install` step re-integrated local .apm/ into
# .github/ via target auto-detection. If anything in the governed
# integration directories changed, someone edited the regenerated
# output without updating the canonical .apm/ source.
- name: Check APM integration drift
run: |
if [ -n "$(git status --porcelain -- .github/ .claude/ .cursor/ .opencode/)" ]; then
echo "::error::APM integration files are out of date."
echo "Run 'apm install' locally (with .github/ present) and commit the result."
git --no-pager diff -- .github/ .claude/ .cursor/ .opencode/
exit 1
fi
6 changes: 3 additions & 3 deletions .github/workflows/merge-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,11 @@ jobs:
SHA: ${{ steps.sha.outputs.sha }}
# All PR-time checks the gate aggregates. Keep this in sync with
# the underlying workflows. Currently only ci.yml emits PR-time
# checks ('Build & Test (Linux)'); ci-integration.yml is
# merge_group-only and is NOT polled here.
# checks ('Build & Test (Linux)', 'APM Self-Check');
# ci-integration.yml is merge_group-only and is NOT polled here.
# NOTE: 'gate' (this job) MUST NOT appear here -- it would
# deadlock waiting for itself.
EXPECTED_CHECKS: 'Build & Test (Linux)'
EXPECTED_CHECKS: 'Build & Test (Linux),APM Self-Check'
TIMEOUT_MIN: '30'
POLL_SEC: '30'
run: |
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- CI: new `APM Self-Check` job in `ci.yml` dogfoods the two CI gates we ship to users via `microsoft/apm-action@v1` -- `apm audit --ci` (consumer-side install fidelity) plus a `git status --porcelain` regeneration-drift check (producer-side, catches hand-edits to `.github/` that bypass canonical `.apm/`). Wired into `merge-gate.yml`'s `EXPECTED_CHECKS`. Includes the precursor regen of `.github/agents/auth-expert.agent.md` and `.github/skills/auth/SKILL.md` so the gate is green on first run. Closes #883.
Comment thread
danielmeppiel marked this conversation as resolved.
Outdated

### Changed

- CI: smoke tests in `build-release.yml`'s `build-and-test` job (Linux x86_64, Linux arm64, Windows) are now gated to promotion boundaries (tag/schedule/dispatch) instead of running on every push to main. Push-time smoke duplicated the merge-time smoke gate in `ci-integration.yml` and burned ~15 redundant codex-binary downloads/day. Tag-cut releases still run smoke as a pre-ship gate; nightly catches upstream codex URL drift; merge-time still gates merges into main. (#878)
Expand Down
4 changes: 4 additions & 0 deletions docs/src/content/docs/integrations/ci-cd.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ To ensure `.github/`, `.claude/`, `.cursor/`, and `.opencode/` integration files

This catches cases where a developer updates `apm.yml` but forgets to re-run `apm install`.

:::tip[We dogfood this]
APM's own repo runs both gates on every PR via the `APM Self-Check` job in [`microsoft/apm`'s `ci.yml`](https://github.com/microsoft/apm/blob/main/.github/workflows/ci.yml). It's the same `microsoft/apm-action@v1` + `apm audit --ci` + `git status --porcelain` pattern shown above -- copy it as a reference implementation.
Comment thread
danielmeppiel marked this conversation as resolved.
Outdated
:::

## Azure Pipelines

```yaml
Expand Down
Loading