From 5a769c78ddb3b1d9d0d995a675ea3ac1a0139a72 Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Mon, 27 Apr 2026 10:03:44 +0200 Subject: [PATCH 1/3] feat(shared/apm): support github-app token minting for cross-org packages Add app-id, private-key, owner, repositories inputs to the shared/apm.md gh-aw workflow. When app-id is set, mint an installation access token via actions/create-github-app-token before the apm-action pack step and use it in place of the default GH_AW_PLUGINS_TOKEN / GH_AW_GITHUB_TOKEN / GITHUB_TOKEN cascade. When app-id is empty, behavior is unchanged. Restores parity with the deprecated dependencies.github-app frontmatter form so users migrating from dependencies: to imports: shared/apm.md can keep fetching cross-org private APM packages with a GitHub App. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/shared/apm.md | 61 +++++++++++++++++++++++++++++++-- CHANGELOG.md | 4 +++ 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/.github/workflows/shared/apm.md b/.github/workflows/shared/apm.md index 1d2b7b396..f5b4e666f 100644 --- a/.github/workflows/shared/apm.md +++ b/.github/workflows/shared/apm.md @@ -15,6 +15,17 @@ # packages: # - microsoft/apm-sample-package # - github/awesome-copilot/skills/review-and-refactor +# +# Cross-org private packages with GitHub App authentication: +# imports: +# - uses: shared/apm.md +# with: +# app-id: ${{ vars.APP_ID }} +# private-key: ${{ secrets.APP_PRIVATE_KEY }} +# owner: acme-platform-org +# repositories: acme-skills +# packages: +# - acme-platform-org/acme-skills/skills/code-review import-schema: packages: @@ -26,6 +37,36 @@ import-schema: List of APM package references to install. Format: owner/repo or owner/repo/path/to/skill. Examples: microsoft/apm-sample-package, github/awesome-copilot/skills/review-and-refactor + app-id: + type: string + required: false + description: > + GitHub App ID used to mint an installation access token for fetching + cross-org private APM packages. When set, app-id and private-key together + override the default GH_AW_PLUGINS_TOKEN / GH_AW_GITHUB_TOKEN / GITHUB_TOKEN + cascading fallback. + private-key: + type: string + required: false + description: > + GitHub App private key (PEM) corresponding to app-id. Required when app-id + is set. Should be passed via a repository or organization secret. + owner: + type: string + required: false + description: > + GitHub App installation owner. Defaults to the current repository owner + when omitted. Only used when app-id is set. + repositories: + type: string + required: false + description: > + Repositories the minted installation token should be scoped to. Accepts a + single repo name, a comma-separated list, or a newline-separated block. + When omitted, the token defaults to the calling repository (per + actions/create-github-app-token). For org-wide access, configure the + GitHub App installation with all-repositories access and leave this + empty. Only used when app-id is set. jobs: apm: @@ -33,6 +74,15 @@ jobs: needs: [activation] permissions: {} steps: + - name: Generate GitHub App token for APM packages + id: apm_token + if: ${{ github.aw.import-inputs.app-id != '' }} + uses: actions/create-github-app-token@v3.1.1 + with: + app-id: ${{ github.aw.import-inputs.app-id }} + private-key: ${{ github.aw.import-inputs.private-key }} + owner: ${{ github.aw.import-inputs.owner || github.repository_owner }} + repositories: ${{ github.aw.import-inputs.repositories }} - name: Prepare APM package list id: apm_prep env: @@ -48,7 +98,7 @@ jobs: id: apm_pack uses: microsoft/apm-action@v1.4.2 env: - GITHUB_TOKEN: ${{ secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.apm_token.outputs.token || secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: dependencies: ${{ steps.apm_prep.outputs.deps }} isolated: 'true' @@ -100,6 +150,11 @@ Packages use the format `owner/repo` or `owner/repo/path/to/skill`: ### Authentication -Packages are fetched using the cascading token fallback: -`GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN` +By default, packages are fetched using the cascading token fallback: +`GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN`. + +For cross-org private packages, supply GitHub App credentials via the +`app-id`, `private-key`, `owner`, and `repositories` inputs. When `app-id` +is set, an installation access token is minted with +`actions/create-github-app-token` and used in place of the default cascade. --> diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fc61e474..121912f8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- `shared/apm.md` gh-aw workflow now accepts `app-id`, `private-key`, `owner`, and `repositories` inputs to mint a GitHub App installation token for fetching cross-org private APM packages, restoring parity with the deprecated `dependencies.github-app` form. The default `GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN` cascade still applies when no app-id is supplied. + ## [0.9.4] - 2026-04-27 ### Added From cf2e9f664f55c39b97bcf55660dee47fa6a6f1e6 Mon Sep 17 00:00:00 2001 From: danielmeppiel Date: Mon, 27 Apr 2026 11:01:47 +0200 Subject: [PATCH 2/3] feat(shared/apm): apps[] + matrix fan-out for multi-org App auth Refactor shared/apm.md to implement the v3 design ratified in #983: - import-schema gains apps[] (array of GitHub App credential groups), each entry mints its own installation token and packs only its declared packages. - single-app top-level form (app-id, private-key, owner, repositories) stays first-class as the canonical shorthand for one-org users. - new apm-prep job normalises packages + single-app + apps[] into one canonical matrix of credential groups via jq (auto-derives apps[].id from slug(owner) when omitted; rejects duplicate ids and invalid id patterns). - apm job fans out one matrix replica per group; each replica conditionally mints, packs, and uploads apm- as an artifact. The multi-bundle restore block is intentionally commented out behind a TODO(microsoft/apm-action bundles-file) marker: the upstream apm-action does not yet expose a bundles-file: input, so the matrix-restore cannot land. This commit is workflow-side-only; the diff is for design review and is NOT merge-ready until upstream PR-A ships per #983. Security: - apm-prep never echoes $groups / $matrix / any matrix.group.* value (S3 mitigation; private-key flows through env -> jq -> GITHUB_OUTPUT with GA secret masking; only id + package-count are printed). - artifact-count validation against the matrix manifest is included in the commented-out restore block (S4: defends against same-run artifact-name collision attacks); will activate alongside bundles-file. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/shared/apm.md | 347 ++++++++++++++++++++++++-------- CHANGELOG.md | 1 + 2 files changed, 263 insertions(+), 85 deletions(-) diff --git a/.github/workflows/shared/apm.md b/.github/workflows/shared/apm.md index f5b4e666f..624d46ea5 100644 --- a/.github/workflows/shared/apm.md +++ b/.github/workflows/shared/apm.md @@ -2,105 +2,244 @@ # APM (Agent Package Manager) - Shared Workflow # Install Microsoft APM packages in your agentic workflow. # -# This shared workflow creates a dedicated "apm" job (depending on activation) that -# packs packages using microsoft/apm-action and uploads the bundle as an artifact. -# The agent job then downloads and unpacks the bundle as pre-steps. +# This shared workflow normalises packages, single-app inputs, and apps[] (multi-org +# GitHub App credential groups) into one canonical list of credential groups in an +# "apm-prep" job, then fans the "apm" job out one matrix replica per group. Each +# replica mints its own installation token (when an app-id is set), packs only its +# declared packages with microsoft/apm-action, and uploads a uniquely-named artifact. +# Pre-agent-steps then download all bundles and restore them in one apm-action call. +# +# STATUS: blocked on upstream microsoft/apm-action gaining a `bundles-file:` input. +# The matrix restore block in `steps:` below is intentionally commented out until +# that input ships -- see TODO marker. Until then this workflow does not produce +# a working agent run; the diff is for design review only. # # Documentation: https://github.com/microsoft/APM # -# Usage: -# imports: -# - uses: shared/apm.md -# with: -# packages: -# - microsoft/apm-sample-package -# - github/awesome-copilot/skills/review-and-refactor +# Three user-facing forms (all valid, additive): +# +# 1. Public + default-token packages (no App credentials): +# +# imports: +# - uses: shared/apm.md +# with: +# packages: +# - microsoft/apm-sample-package +# - github/awesome-copilot/skills/review-and-refactor +# +# 2. Single GitHub App (one org) -- canonical shorthand: # -# Cross-org private packages with GitHub App authentication: -# imports: -# - uses: shared/apm.md -# with: -# app-id: ${{ vars.APP_ID }} -# private-key: ${{ secrets.APP_PRIVATE_KEY }} -# owner: acme-platform-org -# repositories: acme-skills -# packages: -# - acme-platform-org/acme-skills/skills/code-review +# imports: +# - uses: shared/apm.md +# with: +# app-id: ${{ vars.APP_ID }} +# private-key: ${{ secrets.APP_PRIVATE_KEY }} +# owner: my-org +# packages: +# - my-org/my-private-skills +# +# 3. Multiple GitHub Apps (cross-org): +# +# imports: +# - uses: shared/apm.md +# with: +# packages: +# - microsoft/apm-sample-package +# apps: +# - id: acme +# app-id: ${{ vars.ACME_APP_ID }} +# private-key: ${{ secrets.ACME_KEY }} +# owner: acme-org +# packages: +# - acme-org/acme-skills/skills/code-review +# - app-id: ${{ vars.BETA_APP_ID }} +# private-key: ${{ secrets.BETA_KEY }} +# owner: beta-org +# packages: +# - beta-org/beta-pkg import-schema: packages: type: array items: type: string - required: true + required: false description: > - List of APM package references to install. + Public APM packages or packages reachable via the default token cascade + (GH_AW_PLUGINS_TOKEN, GH_AW_GITHUB_TOKEN, GITHUB_TOKEN). Optional. At + least one of `packages`, the single-app inputs, or `apps` must be provided. Format: owner/repo or owner/repo/path/to/skill. - Examples: microsoft/apm-sample-package, github/awesome-copilot/skills/review-and-refactor + + # Single-app convenience form (canonical shorthand for one-org users) app-id: type: string required: false description: > - GitHub App ID used to mint an installation access token for fetching - cross-org private APM packages. When set, app-id and private-key together - override the default GH_AW_PLUGINS_TOKEN / GH_AW_GITHUB_TOKEN / GITHUB_TOKEN - cascading fallback. + GitHub App ID. With `private-key`, mints an installation token for the + packages listed in `packages:`. For multiple orgs, use `apps:` instead. private-key: type: string required: false description: > - GitHub App private key (PEM) corresponding to app-id. Required when app-id - is set. Should be passed via a repository or organization secret. + PEM private key matching `app-id`. Required when `app-id` is set. Pass via + a repository or organization secret. owner: type: string required: false description: > - GitHub App installation owner. Defaults to the current repository owner - when omitted. Only used when app-id is set. + App installation owner. Defaults to the current repository owner when + omitted. Only used when `app-id` is set. repositories: type: string required: false description: > - Repositories the minted installation token should be scoped to. Accepts a - single repo name, a comma-separated list, or a newline-separated block. - When omitted, the token defaults to the calling repository (per - actions/create-github-app-token). For org-wide access, configure the - GitHub App installation with all-repositories access and leave this - empty. Only used when app-id is set. + Repositories the minted token is scoped to. Comma- or newline-separated. + Empty defaults to the calling repo or the App installation default scope. + Note: literal "*" is NOT a wildcard for actions/create-github-app-token; + leave empty for org-wide access via App installation config. + + # Multi-app form (cross-org) + apps: + type: array + required: false + description: > + List of GitHub App credential groups. Each entry mints its own + installation token and packs its own packages. Use when packages span + multiple orgs requiring different App installations. + items: + type: object + properties: + id: + type: string + required: false + description: > + Stable identifier used for matrix-row and artifact naming. + Auto-derived from `owner` (slugified) when omitted. Required when + two entries share the same owner. + app-id: + type: string + required: true + private-key: + type: string + required: true + owner: + type: string + required: false + repositories: + type: string + required: false + packages: + type: array + items: + type: string + required: true jobs: - apm: + apm-prep: runs-on: ubuntu-slim needs: [activation] permissions: {} + outputs: + matrix: ${{ steps.compute.outputs.matrix }} steps: - - name: Generate GitHub App token for APM packages - id: apm_token - if: ${{ github.aw.import-inputs.app-id != '' }} + # SECURITY (S3): never echo $groups, $matrix, or any matrix.group.* value + # in any apm-prep step. private-key is a real secret string here. + - name: Compute APM credential-group matrix + id: compute + env: + AW_APM_PACKAGES: ${{ github.aw.import-inputs.packages }} + AW_APM_APPS: ${{ github.aw.import-inputs.apps }} + AW_APM_LEGACY_APP_ID: ${{ github.aw.import-inputs.app-id }} + AW_APM_LEGACY_PRIVATE_KEY: ${{ github.aw.import-inputs.private-key }} + AW_APM_LEGACY_OWNER: ${{ github.aw.import-inputs.owner }} + AW_APM_LEGACY_REPOS: ${{ github.aw.import-inputs.repositories }} + run: | + set -euo pipefail + packages_json=${AW_APM_PACKAGES:-null} + apps_json=${AW_APM_APPS:-null} + legacy_id=${AW_APM_LEGACY_APP_ID:-} + + groups=$(jq -nc \ + --argjson packages "$packages_json" \ + --argjson apps "$apps_json" \ + --arg legacy_id "$legacy_id" \ + --arg legacy_pk "${AW_APM_LEGACY_PRIVATE_KEY:-}" \ + --arg legacy_owner "${AW_APM_LEGACY_OWNER:-}" \ + --arg legacy_repos "${AW_APM_LEGACY_REPOS:-}" \ + 'def slug(s): s | gsub("[^a-zA-Z0-9-]"; "-") | ascii_downcase | .[0:32]; + def with_id(g): + g + (if (g.id // "") == "" then {id: ("auto-" + slug(g.owner // "default"))} else {} end); + [ + (if (($packages // []) | length) > 0 and $legacy_id == "" + then [{id:"default",("app-id"):"",("private-key"):"",owner:"",repositories:"",packages:$packages}] + else [] end), + (if $legacy_id != "" + then [with_id({id:"legacy",("app-id"):$legacy_id,("private-key"):$legacy_pk,owner:$legacy_owner,repositories:$legacy_repos,packages:($packages // [])})] + else [] end), + (($apps // []) | map(with_id(.))) + ] | add // []') + + count=$(echo "$groups" | jq 'length') + if [ "$count" = "0" ]; then + echo "::error::shared/apm.md import provided no packages. Add packages: , single-app inputs (app-id + private-key), or apps: in the with: block." + exit 1 + fi + + dups=$(echo "$groups" | jq -r '[.[].id] | group_by(.) | map(select(length > 1) | first) | join(", ")') + if [ -n "$dups" ]; then + echo "::error::duplicate apm group ids after auto-derivation: $dups. Set apps[].id explicitly when two entries share the same owner." + exit 1 + fi + + while IFS= read -r id; do + if ! echo "$id" | grep -Eq '^[a-z0-9-]{1,32}$'; then + echo "::error::invalid apm group id: '$id' (lowercase alphanumeric and dashes, 1-32 chars). Set apps[].id explicitly." + exit 1 + fi + done < <(echo "$groups" | jq -r '.[].id') + + # SAFE: emit only id + package-count to logs. Never $groups in full. + { + echo "matrix={\"group\":$groups}" + } >> "$GITHUB_OUTPUT" + printf "::notice::APM matrix: %d credential group(s)\n" "$count" + echo "$groups" | jq -r '.[] | " - " + .id + " (" + (.packages | length | tostring) + " package(s))"' + + apm: + runs-on: ubuntu-slim + needs: [activation, apm-prep] + permissions: {} + strategy: + fail-fast: false + matrix: ${{ fromJSON(needs.apm-prep.outputs.matrix) }} + steps: + - name: Mint installation token + id: token + if: ${{ matrix.group.app-id != '' }} uses: actions/create-github-app-token@v3.1.1 with: - app-id: ${{ github.aw.import-inputs.app-id }} - private-key: ${{ github.aw.import-inputs.private-key }} - owner: ${{ github.aw.import-inputs.owner || github.repository_owner }} - repositories: ${{ github.aw.import-inputs.repositories }} - - name: Prepare APM package list - id: apm_prep + app-id: ${{ matrix.group.app-id }} + private-key: ${{ matrix.group.private-key }} + owner: ${{ matrix.group.owner != '' && matrix.group.owner || github.repository_owner }} + repositories: ${{ matrix.group.repositories }} + - name: Render package list + id: list env: - AW_APM_PACKAGES: '${{ github.aw.import-inputs.packages }}' + AW_PKG: ${{ toJSON(matrix.group.packages) }} run: | - DEPS=$(echo "$AW_APM_PACKAGES" | jq -r '.[] | "- " + .') + DEPS=$(echo "$AW_PKG" | jq -r '.[] | "- " + .') { echo "deps<> "$GITHUB_OUTPUT" - name: Pack APM packages - id: apm_pack + id: pack uses: microsoft/apm-action@v1.4.2 env: - GITHUB_TOKEN: ${{ steps.apm_token.outputs.token || secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ steps.token.outputs.token || secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: - dependencies: ${{ steps.apm_prep.outputs.deps }} + dependencies: ${{ steps.list.outputs.deps }} isolated: 'true' pack: 'true' archive: 'true' @@ -110,51 +249,89 @@ jobs: if: success() uses: actions/upload-artifact@v7 with: - name: ${{ needs.activation.outputs.artifact_prefix }}apm - path: ${{ steps.apm_pack.outputs.bundle-path }} + name: ${{ needs.activation.outputs.artifact_prefix }}apm-${{ matrix.group.id }} + path: ${{ steps.pack.outputs.bundle-path }} retention-days: '1' -steps: - - name: Download APM bundle artifact - uses: actions/download-artifact@v8.0.1 - with: - name: ${{ needs.activation.outputs.artifact_prefix }}apm - path: /tmp/gh-aw/apm-bundle - - name: Find APM bundle path - id: apm_bundle - run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" - - name: Restore APM packages - uses: microsoft/apm-action@v1.4.2 - with: - bundle: ${{ steps.apm_bundle.outputs.path }} +# TODO(microsoft/apm-action bundles-file): the multi-bundle restore below is +# blocked on an upstream apm-action input that accepts a newline-separated list +# of bundle tarballs. Until that PR ships, this workflow produces N artifacts +# (apm-) but cannot restore them in one call. Tracked in microsoft/apm#983 +# (PR-A in the proposal). Do NOT merge this workflow until the commented block +# below is uncommented and the apm-action version pinned. +# +# steps: +# - name: Download APM bundle artifacts (all groups) +# uses: actions/download-artifact@v8.0.1 +# with: +# pattern: ${{ needs.activation.outputs.artifact_prefix }}apm-* +# path: /tmp/gh-aw/apm-bundles +# merge-multiple: false +# - name: Validate downloaded bundles match matrix manifest +# env: +# EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} +# ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} +# run: | +# set -euo pipefail +# expected=$(echo "$EXPECTED_MATRIX" | jq -r --arg prefix "$ARTIFACT_PREFIX" '.group | map($prefix + "apm-" + .id) | sort | .[]') +# actual=$(ls /tmp/gh-aw/apm-bundles | sort) +# missing=$(comm -23 <(echo "$expected") <(echo "$actual") || true) +# unexpected=$(comm -13 <(echo "$expected") <(echo "$actual") || true) +# if [ -n "$missing" ]; then +# echo "::error::missing APM bundles (group did not pack successfully): $missing" +# exit 1 +# fi +# if [ -n "$unexpected" ]; then +# echo "::error::unexpected artifact in apm bundle download (collision attack?): $unexpected" +# exit 1 +# fi +# - name: Build bundle list +# id: bundles +# run: | +# set -euo pipefail +# mapfile -t list < <(find /tmp/gh-aw/apm-bundles -name '*.tar.gz' | sort) +# [ ${#list[@]} -gt 0 ] || { echo '::error::no apm bundles found'; exit 1; } +# printf '%s\n' "${list[@]}" > /tmp/gh-aw/apm-bundle-list.txt +# - name: Restore APM packages (all bundles) +# uses: microsoft/apm-action@ # pin once bundles-file lands +# with: +# bundles-file: /tmp/gh-aw/apm-bundle-list.txt +steps: [] --- diff --git a/CHANGELOG.md b/CHANGELOG.md index 121912f8a..fbbe3724f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- `shared/apm.md` gh-aw workflow gains an `apps:` array input for cross-org private packages: each entry mints its own GitHub App installation token via `actions/create-github-app-token` and packs only its declared packages, with a matrix fan-out one replica per credential group. The single-app top-level form (`app-id`, `private-key`, `owner`, `repositories`) shipped earlier in this cycle is preserved as the canonical shorthand for one-org users; `apps[]` is purely additive. NOTE: full multi-bundle restore is blocked on an upstream `microsoft/apm-action` `bundles-file:` input (tracked in microsoft/apm#983); the matrix-restore block in `shared/apm.md` is currently emitted in commented-out form so the workflow does not silently regress. - `shared/apm.md` gh-aw workflow now accepts `app-id`, `private-key`, `owner`, and `repositories` inputs to mint a GitHub App installation token for fetching cross-org private APM packages, restoring parity with the deprecated `dependencies.github-app` form. The default `GH_AW_PLUGINS_TOKEN || GH_AW_GITHUB_TOKEN || GITHUB_TOKEN` cascade still applies when no app-id is supplied. ## [0.9.4] - 2026-04-27 From b63e366103a1add8bd57e24fda23a81d0c6c696b Mon Sep 17 00:00:00 2001 From: Daniel Meppiel Date: Tue, 28 Apr 2026 19:49:43 +0200 Subject: [PATCH 3/3] feat(shared/apm): cut over to apm-action v1.5.0 multi-bundle restore Uncomments the matrix-restore block that was previously emitted in commented-out form pending the upstream bundles-file: input. Bumps both apm-action references to @v1.5.0 (the release shipping the new input) and removes the doc-level 'blocked on upstream' notes. Land sequence: 1. Merge microsoft/apm-action#30 + tag v1.5.0 + advance v1 2. Wait for CI on this PR to flip green with @v1.5.0 live 3. Merge this PR Refs microsoft/apm-action#29 microsoft/apm-action#30 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/shared/apm.md | 90 +++++++++++++++------------------ CHANGELOG.md | 2 +- 2 files changed, 42 insertions(+), 50 deletions(-) diff --git a/.github/workflows/shared/apm.md b/.github/workflows/shared/apm.md index 624d46ea5..f528df123 100644 --- a/.github/workflows/shared/apm.md +++ b/.github/workflows/shared/apm.md @@ -235,7 +235,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Pack APM packages id: pack - uses: microsoft/apm-action@v1.4.2 + uses: microsoft/apm-action@v1.5.0 env: GITHUB_TOKEN: ${{ steps.token.outputs.token || secrets.GH_AW_PLUGINS_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} with: @@ -253,50 +253,42 @@ jobs: path: ${{ steps.pack.outputs.bundle-path }} retention-days: '1' -# TODO(microsoft/apm-action bundles-file): the multi-bundle restore below is -# blocked on an upstream apm-action input that accepts a newline-separated list -# of bundle tarballs. Until that PR ships, this workflow produces N artifacts -# (apm-) but cannot restore them in one call. Tracked in microsoft/apm#983 -# (PR-A in the proposal). Do NOT merge this workflow until the commented block -# below is uncommented and the apm-action version pinned. -# -# steps: -# - name: Download APM bundle artifacts (all groups) -# uses: actions/download-artifact@v8.0.1 -# with: -# pattern: ${{ needs.activation.outputs.artifact_prefix }}apm-* -# path: /tmp/gh-aw/apm-bundles -# merge-multiple: false -# - name: Validate downloaded bundles match matrix manifest -# env: -# EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} -# ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} -# run: | -# set -euo pipefail -# expected=$(echo "$EXPECTED_MATRIX" | jq -r --arg prefix "$ARTIFACT_PREFIX" '.group | map($prefix + "apm-" + .id) | sort | .[]') -# actual=$(ls /tmp/gh-aw/apm-bundles | sort) -# missing=$(comm -23 <(echo "$expected") <(echo "$actual") || true) -# unexpected=$(comm -13 <(echo "$expected") <(echo "$actual") || true) -# if [ -n "$missing" ]; then -# echo "::error::missing APM bundles (group did not pack successfully): $missing" -# exit 1 -# fi -# if [ -n "$unexpected" ]; then -# echo "::error::unexpected artifact in apm bundle download (collision attack?): $unexpected" -# exit 1 -# fi -# - name: Build bundle list -# id: bundles -# run: | -# set -euo pipefail -# mapfile -t list < <(find /tmp/gh-aw/apm-bundles -name '*.tar.gz' | sort) -# [ ${#list[@]} -gt 0 ] || { echo '::error::no apm bundles found'; exit 1; } -# printf '%s\n' "${list[@]}" > /tmp/gh-aw/apm-bundle-list.txt -# - name: Restore APM packages (all bundles) -# uses: microsoft/apm-action@ # pin once bundles-file lands -# with: -# bundles-file: /tmp/gh-aw/apm-bundle-list.txt -steps: [] +steps: + - name: Download APM bundle artifacts (all groups) + uses: actions/download-artifact@v8.0.1 + with: + pattern: ${{ needs.activation.outputs.artifact_prefix }}apm-* + path: /tmp/gh-aw/apm-bundles + merge-multiple: false + - name: Validate downloaded bundles match matrix manifest + env: + EXPECTED_MATRIX: ${{ needs.apm-prep.outputs.matrix }} + ARTIFACT_PREFIX: ${{ needs.activation.outputs.artifact_prefix }} + run: | + set -euo pipefail + expected=$(echo "$EXPECTED_MATRIX" | jq -r --arg prefix "$ARTIFACT_PREFIX" '.group | map($prefix + "apm-" + .id) | sort | .[]') + actual=$(ls /tmp/gh-aw/apm-bundles | sort) + missing=$(comm -23 <(echo "$expected") <(echo "$actual") || true) + unexpected=$(comm -13 <(echo "$expected") <(echo "$actual") || true) + if [ -n "$missing" ]; then + echo "::error::missing APM bundles (group did not pack successfully): $missing" + exit 1 + fi + if [ -n "$unexpected" ]; then + echo "::error::unexpected artifact in apm bundle download (collision attack?): $unexpected" + exit 1 + fi + - name: Build bundle list + id: bundles + run: | + set -euo pipefail + mapfile -t list < <(find /tmp/gh-aw/apm-bundles -name '*.tar.gz' | sort) + [ ${#list[@]} -gt 0 ] || { echo '::error::no apm bundles found'; exit 1; } + printf '%s\n' "${list[@]}" > /tmp/gh-aw/apm-bundle-list.txt + - name: Restore APM packages (all bundles) + uses: microsoft/apm-action@v1.5.0 + with: + bundles-file: /tmp/gh-aw/apm-bundle-list.txt ---