diff --git a/.changeset/patch-move-apm-dependency-resolution.md b/.changeset/patch-move-apm-dependency-resolution.md new file mode 100644 index 0000000000..6cb35aa077 --- /dev/null +++ b/.changeset/patch-move-apm-dependency-resolution.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Pack APM dependencies during activation and restore them via the apm artifact in agent jobs to improve determinism. diff --git a/.changeset/patch-pack-apm-artifact.md b/.changeset/patch-pack-apm-artifact.md new file mode 100644 index 0000000000..aa8d4f96a8 --- /dev/null +++ b/.changeset/patch-pack-apm-artifact.md @@ -0,0 +1,4 @@ +--- +"gh-aw": patch +--- +Package APM dependencies during activation and restore them via the `apm` artifact for deterministic agent jobs. diff --git a/.changeset/patch-package-apm-artifact.md b/.changeset/patch-package-apm-artifact.md new file mode 100644 index 0000000000..c6e19f65e3 --- /dev/null +++ b/.changeset/patch-package-apm-artifact.md @@ -0,0 +1,4 @@ +--- +"gh-aw": patch +--- +Package APM dependencies during activation and restore them via the `apm` artifact so agent jobs get a deterministic, cached bundle of packages. diff --git a/.github/workflows/constraint-solving-potd.lock.yml b/.github/workflows/constraint-solving-potd.lock.yml index 37a19d704a..ba086a4839 100644 --- a/.github/workflows/constraint-solving-potd.lock.yml +++ b/.github/workflows/constraint-solving-potd.lock.yml @@ -22,7 +22,7 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"8b8e7739047a7e1a0c6540f7574b93437f0a2b7607c424e28a72fd5745c56be5","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b614d2b177f891f2474ec04ef2dadf47a16142bc2933d8a36a8e165870fae677","strict":true} name: "Constraint Solving — Problem of the Day" "on": diff --git a/.github/workflows/constraint-solving-potd.md b/.github/workflows/constraint-solving-potd.md index 87b79c4a1b..41af9d795b 100644 --- a/.github/workflows/constraint-solving-potd.md +++ b/.github/workflows/constraint-solving-potd.md @@ -18,7 +18,7 @@ safe-outputs: title-prefix: "🧩 Constraint Solving POTD:" labels: [constraint-solving, problem-of-the-day] close-older-discussions: true - expires: 7 + expires: 7d --- # Constraint Solving — Problem of the Day @@ -116,4 +116,4 @@ List 2–4 seminal or accessible references: When you have written the problem discussion, post it using `create-discussion`. -If today's category was recently covered and you cannot find a sufficiently different problem, call `noop` with an explanation of why you skipped. +If today's category was recently covered and you cannot find a sufficiently different problem, call `noop` with an explanation of why you skipped. \ No newline at end of file diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml index 2faa91806d..49ee25b752 100644 --- a/.github/workflows/contribution-check.lock.yml +++ b/.github/workflows/contribution-check.lock.yml @@ -22,7 +22,7 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4de9281fdf89dba8197d91de6339b21a8b01ddb1645d17de1f09b3a70fc4cf53","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"fad22b344bd980cb8f5ad99e373faef34327027c4d9165767e10caf7af746e3b","strict":true} name: "Contribution Check" "on": diff --git a/.github/workflows/contribution-check.md b/.github/workflows/contribution-check.md index bb821a0956..5581b51ac3 100644 --- a/.github/workflows/contribution-check.md +++ b/.github/workflows/contribution-check.md @@ -22,7 +22,7 @@ safe-outputs: labels: - contribution-report close-older-issues: true - expires: 1 + expires: 1d add-labels: allowed: [spam, needs-work, outdated, lgtm] max: 4 @@ -184,4 +184,4 @@ If any subagent call failed (❓), also apply `outdated`. ```json {"noop": {"message": "No action needed: [brief explanation of what was analyzed and why]"}} -``` +``` \ No newline at end of file diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml index 4c40729074..5094142807 100644 --- a/.github/workflows/daily-architecture-diagram.lock.yml +++ b/.github/workflows/daily-architecture-diagram.lock.yml @@ -27,7 +27,7 @@ # Imports: # - shared/reporting.md # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"15c319e318a6b9de87fc6b5acd2a3c80463bb2ac5970875c00ce5325d428a71d","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"219c5898fd299f2ed4fb902a027b8acdc19ea0405f85f6af82d20abc1a27a076","strict":true} name: "Architecture Diagram Generator" "on": diff --git a/.github/workflows/daily-architecture-diagram.md b/.github/workflows/daily-architecture-diagram.md index 3f8364e5ae..e48def3bb9 100644 --- a/.github/workflows/daily-architecture-diagram.md +++ b/.github/workflows/daily-architecture-diagram.md @@ -22,10 +22,10 @@ safe-outputs: title-prefix: "🏗️ Architecture Diagram:" labels: [architecture, diagram] close-older-issues: true - expires: 7 + expires: 7d max: 1 create-pull-request: - expires: 7 + expires: 7d title-prefix: "[architecture] " labels: [architecture, diagram, documentation] noop: @@ -207,4 +207,4 @@ This diagram shows the package structure and dependencies of the `gh-aw` codebas ```` - When the diagram **changes**: update `scratchpad/architecture.md` via `create_pull_request` with a PR titled `[architecture] Update architecture diagram - `. -- When the diagram is **unchanged** (noop path): skip the scratchpad update entirely. +- When the diagram is **unchanged** (noop path): skip the scratchpad update entirely. \ No newline at end of file diff --git a/.github/workflows/security-alert-burndown.campaign.g.lock.yml b/.github/workflows/security-alert-burndown.campaign.g.lock.yml index 201f91db55..c4aaedcdba 100644 --- a/.github/workflows/security-alert-burndown.campaign.g.lock.yml +++ b/.github/workflows/security-alert-burndown.campaign.g.lock.yml @@ -23,7 +23,7 @@ # # Orchestrator workflow for campaign 'security-alert-burndown' # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"4cabee9e7e0a3b1a2f3c07dce2b2a763a9e8aeadbb3e8228389b45f8255ac805","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"b86eef08186cbd578cec6ef9166227a187a1d951d243f31b9fc9573b33cbf012","strict":true} name: "Security Alert Burndown" "on": @@ -286,7 +286,7 @@ jobs: - env: GH_AW_CAMPAIGN_ID: security-alert-burndown GH_AW_CURSOR_PATH: /tmp/gh-aw/repo-memory/campaigns/security-alert-burndown/cursor.json - GH_AW_DISCOVERY_REPOS: githubnext/gh-aw + GH_AW_DISCOVERY_REPOS: github/gh-aw GH_AW_MAX_DISCOVERY_ITEMS: "50" GH_AW_MAX_DISCOVERY_PAGES: "3" GH_AW_PROJECT_URL: https://github.com/orgs/githubnext/projects/122 diff --git a/.github/workflows/security-alert-burndown.campaign.g.md b/.github/workflows/security-alert-burndown.campaign.g.md index d9ae3e084f..65ed9846ac 100644 --- a/.github/workflows/security-alert-burndown.campaign.g.md +++ b/.github/workflows/security-alert-burndown.campaign.g.md @@ -45,7 +45,7 @@ steps: - env: GH_AW_CAMPAIGN_ID: security-alert-burndown GH_AW_CURSOR_PATH: /tmp/gh-aw/repo-memory/campaigns/security-alert-burndown/cursor.json - GH_AW_DISCOVERY_REPOS: githubnext/gh-aw + GH_AW_DISCOVERY_REPOS: github/gh-aw GH_AW_MAX_DISCOVERY_ITEMS: "50" GH_AW_MAX_DISCOVERY_PAGES: "3" GH_AW_PROJECT_URL: https://github.com/orgs/githubnext/projects/122 @@ -697,4 +697,4 @@ The following rules are mandatory and override inferred behavior: - Do NOT overwrite fields except as explicitly allowed. - Workers are immutable and campaign-agnostic. -If any instruction conflicts, the Project Update Instructions take precedence for all writes. +If any instruction conflicts, the Project Update Instructions take precedence for all writes. \ No newline at end of file diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index b2da2b72e3..d8f34f597d 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -35,7 +35,7 @@ # # inlined-imports: true # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"ce1db450f8ac78040e972a4a41fe2c780e51d97c7d931f9ec7f883a6ae8006d7","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"cf65ca18529a16d3f7278e9f39c60eee12cb0abe10843fcb1411350b7bba20b5","strict":true} name: "Smoke Claude" "on": @@ -636,6 +636,24 @@ jobs: env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Install and pack APM dependencies + id: apm_pack + uses: microsoft/apm-action@5eac264e08ed8db603fe2c40983794f94cab49d8 # v1.3.1 + with: + dependencies: | + - microsoft/apm-sample-package + isolated: 'true' + pack: 'true' + archive: 'true' + target: claude + working-directory: /tmp/gh-aw/apm-workspace + - name: Upload APM bundle artifact + if: success() + uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + with: + name: apm + path: ${{ steps.apm_pack.outputs.bundle-path }} + retention-days: 1 - name: Upload activation artifact if: success() uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 @@ -772,6 +790,15 @@ jobs: run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0 - name: Install Claude Code CLI run: npm install -g @anthropic-ai/claude-code@latest + - name: Download APM bundle artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: apm + path: /tmp/gh-aw/apm-bundle + - name: Restore APM dependencies + uses: microsoft/apm-action@5eac264e08ed8db603fe2c40983794f94cab49d8 # v1.3.1 + with: + bundle: /tmp/gh-aw/apm-bundle/*.tar.gz - name: Determine automatic lockdown mode for GitHub MCP Server id: determine-automatic-lockdown uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 diff --git a/.github/workflows/smoke-claude.md b/.github/workflows/smoke-claude.md index 639c16f1e0..e2610c27a9 100644 --- a/.github/workflows/smoke-claude.md +++ b/.github/workflows/smoke-claude.md @@ -49,6 +49,8 @@ tools: serena: languages: go: {} +dependencies: + - microsoft/apm-sample-package runtimes: go: version: "1.25" diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml index edad982d80..fe1e4b00a4 100644 --- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml +++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml @@ -21,9 +21,9 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# Smoke test validating cross-repo pull request creation in githubnext/gh-aw-side-repo +# Smoke test validating cross-repo pull request creation in github/gh-aw-side-repo # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"7df6ac5fd0e9496ec5425a827a1abda3ebb1f52b59b4d1edc78b2358cdf1a856","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"5dcab599970500a93f445abb1b9f5080ef822dd303ba672a9ca912dbe950fd42","strict":true} name: "Smoke Create Cross-Repo PR" "on": @@ -132,7 +132,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_WORKFLOW_NAME: "Smoke Create Cross-Repo PR" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in githubnext/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in githubnext/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in github/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in github/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -196,7 +196,7 @@ jobs: - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} - **checkouts**: The following repositories have been checked out and are available in the workspace: - - `$GITHUB_WORKSPACE` → `githubnext/gh-aw-side-repo` (cwd) [shallow clone, fetch-depth=1 (default)] + - `$GITHUB_WORKSPACE` → `github/gh-aw-side-repo` (cwd) [shallow clone, fetch-depth=1 (default)] - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). @@ -318,11 +318,11 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: Checkout githubnext/gh-aw-side-repo + - name: Checkout github/gh-aw-side-repo uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - repository: githubnext/gh-aw-side-repo + repository: github/gh-aw-side-repo token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh @@ -375,7 +375,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":2},"create_issue":{"expires":2,"max":1},"create_pull_request":{"draft":true,"expires":24,"fallback_as_issue":false,"max":1,"target-repo":"githubnext/gh-aw-side-repo","title_prefix":"[smoke] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} + {"add_comment":{"max":2},"create_issue":{"expires":2,"max":1},"create_pull_request":{"draft":true,"expires":24,"fallback_as_issue":false,"max":1,"target-repo":"github/gh-aw-side-repo","title_prefix":"[smoke] "},"missing_data":{},"missing_tool":{},"noop":{"max":1}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -1062,7 +1062,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: WORKFLOW_NAME: "Smoke Create Cross-Repo PR" - WORKFLOW_DESCRIPTION: "Smoke test validating cross-repo pull request creation in githubnext/gh-aw-side-repo" + WORKFLOW_DESCRIPTION: "Smoke test validating cross-repo pull request creation in github/gh-aw-side-repo" HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | @@ -1234,7 +1234,7 @@ jobs: GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in githubnext/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in githubnext/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in github/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in github/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_TIMEOUT_MINUTES: "10" @@ -1287,7 +1287,7 @@ jobs: GH_AW_WORKFLOW_NAME: "Smoke Create Cross-Repo PR" GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in githubnext/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in githubnext/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in github/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in github/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1345,7 +1345,7 @@ jobs: env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/smoke-create-cross-repo-pr" GH_AW_ENGINE_ID: "copilot" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in githubnext/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in githubnext/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in github/gh-aw-side-repo...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in github/gh-aw-side-repo!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}\"}" GH_AW_WORKFLOW_ID: "smoke-create-cross-repo-pr" GH_AW_WORKFLOW_NAME: "Smoke Create Cross-Repo PR" outputs: @@ -1396,7 +1396,7 @@ jobs: if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: - repository: githubnext/gh-aw-side-repo + repository: github/gh-aw-side-repo ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }} token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} persist-credentials: false @@ -1404,7 +1404,7 @@ jobs: - name: Configure Git credentials if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (contains(needs.agent.outputs.output_types, 'create_pull_request')) env: - REPO_NAME: "githubnext/gh-aw-side-repo" + REPO_NAME: "github/gh-aw-side-repo" SERVER_URL: ${{ github.server_url }} GIT_TOKEN: ${{ secrets.GH_AW_SIDE_REPO_PAT }} run: | @@ -1423,7 +1423,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"create_pull_request\":{\"draft\":true,\"expires\":24,\"fallback_as_issue\":false,\"github-token\":\"${{ secrets.GH_AW_SIDE_REPO_PAT }}\",\"if_no_changes\":\"error\",\"labels\":[\"smoke-test\"],\"max\":1,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target-repo\":\"githubnext/gh-aw-side-repo\",\"title_prefix\":\"[smoke] \"},\"missing_data\":{},\"missing_tool\":{}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"create_pull_request\":{\"draft\":true,\"expires\":24,\"fallback_as_issue\":false,\"github-token\":\"${{ secrets.GH_AW_SIDE_REPO_PAT }}\",\"if_no_changes\":\"error\",\"labels\":[\"smoke-test\"],\"max\":1,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target-repo\":\"github/gh-aw-side-repo\",\"title_prefix\":\"[smoke] \"},\"missing_data\":{},\"missing_tool\":{}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_AW_SIDE_REPO_PAT }} with: diff --git a/.github/workflows/smoke-create-cross-repo-pr.md b/.github/workflows/smoke-create-cross-repo-pr.md index 4dcb424d28..ea03ee9022 100644 --- a/.github/workflows/smoke-create-cross-repo-pr.md +++ b/.github/workflows/smoke-create-cross-repo-pr.md @@ -1,6 +1,6 @@ --- name: Smoke Create Cross-Repo PR -description: Smoke test validating cross-repo pull request creation in githubnext/gh-aw-side-repo +description: Smoke test validating cross-repo pull request creation in github/gh-aw-side-repo on: schedule: every 12h workflow_dispatch: @@ -20,7 +20,7 @@ network: - github checkout: - - repository: githubnext/gh-aw-side-repo + - repository: github/gh-aw-side-repo github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} tools: @@ -33,7 +33,7 @@ tools: safe-outputs: create-pull-request: - target-repo: "githubnext/gh-aw-side-repo" + target-repo: "github/gh-aw-side-repo" github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} title-prefix: "[smoke] " labels: [smoke-test] @@ -50,8 +50,8 @@ safe-outputs: max: 2 messages: footer: "> 🔬 *Cross-repo smoke test by [{workflow_name}]({run_url})*{history_link}" - run-started: "🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in githubnext/gh-aw-side-repo..." - run-success: "✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in githubnext/gh-aw-side-repo!" + run-started: "🔬 [{workflow_name}]({run_url}) is testing cross-repo PR creation in github/gh-aw-side-repo..." + run-success: "✅ [{workflow_name}]({run_url}) successfully created a cross-repo PR in github/gh-aw-side-repo!" run-failure: "❌ [{workflow_name}]({run_url}) failed to create a cross-repo PR: {status}" timeout-minutes: 10 @@ -106,4 +106,4 @@ Status: cross-repo PR creation smoke test - "Smoke Test: Copilot - Cross-repo create PR ${{ github.run_id }}" - The line that was added to the cross-repo PR - Overall status: SUCCESS - - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} \ No newline at end of file diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml index 8676b43ee5..0c0142e7d0 100644 --- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml +++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml @@ -21,9 +21,9 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# Smoke test validating cross-repo pull request updates in githubnext/gh-aw-side-repo by adding lines from Homer's Odyssey to the README +# Smoke test validating cross-repo pull request updates in github/gh-aw-side-repo by adding lines from Homer's Odyssey to the README # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"0dbcea46ce3435c40cb5ca9fcbd9feb04e8720f1b0b3b89993c94ae5d7bf3cd0","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"9078fad1a6be9369e589feb9e92efab7f7c0f3aa6b441c8848114d6a42c2ee7b","strict":true} name: "Smoke Update Cross-Repo PR" "on": @@ -132,7 +132,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: GH_AW_WORKFLOW_NAME: "Smoke Update Cross-Repo PR" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to githubnext/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to github/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); @@ -197,7 +197,7 @@ jobs: - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ {{/if}} - **checkouts**: The following repositories have been checked out and are available in the workspace: - - `$GITHUB_WORKSPACE` → `githubnext/gh-aw-side-repo` (cwd) [full history, all branches available as remote-tracking refs] [additional refs fetched: main, refs/pulls/open/*] + - `$GITHUB_WORKSPACE` → `github/gh-aw-side-repo` (cwd) [full history, all branches available as remote-tracking refs] [additional refs fetched: main, refs/pulls/open/*] - **Note**: If a branch you need is not in the list above and is not listed as an additional fetched ref, it has NOT been checked out. For private repositories you cannot fetch it without proper authentication. If the branch is required and not available, exit with an error and ask the user to add it to the `fetch:` option of the `checkout:` configuration (e.g., `fetch: ["refs/pulls/open/*"]` for all open PR refs, or `fetch: ["main", "feature/my-branch"]` for specific branches). @@ -325,14 +325,14 @@ jobs: uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - name: Checkout githubnext/gh-aw-side-repo + - name: Checkout github/gh-aw-side-repo uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - repository: githubnext/gh-aw-side-repo + repository: github/gh-aw-side-repo token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} fetch-depth: 0 - - name: Fetch additional refs for githubnext/gh-aw-side-repo + - name: Fetch additional refs for github/gh-aw-side-repo env: GH_AW_FETCH_TOKEN: ${{ secrets.GH_AW_SIDE_REPO_PAT }} run: | @@ -1063,7 +1063,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: WORKFLOW_NAME: "Smoke Update Cross-Repo PR" - WORKFLOW_DESCRIPTION: "Smoke test validating cross-repo pull request updates in githubnext/gh-aw-side-repo by adding lines from Homer's Odyssey to the README" + WORKFLOW_DESCRIPTION: "Smoke test validating cross-repo pull request updates in github/gh-aw-side-repo by adding lines from Homer's Odyssey to the README" HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | @@ -1236,7 +1236,7 @@ jobs: GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }} GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }} GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to githubnext/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to github/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" GH_AW_GROUP_REPORTS: "false" GH_AW_FAILURE_REPORT_AS_ISSUE: "true" GH_AW_TIMEOUT_MINUTES: "10" @@ -1275,7 +1275,7 @@ jobs: GH_AW_WORKFLOW_NAME: "Smoke Update Cross-Repo PR" GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} GH_AW_DETECTION_CONCLUSION: ${{ needs.agent.outputs.detection_conclusion }} - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to githubnext/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to github/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} script: | @@ -1333,7 +1333,7 @@ jobs: env: GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/smoke-update-cross-repo-pr" GH_AW_ENGINE_ID: "copilot" - GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to githubnext/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" + GH_AW_SAFE_OUTPUT_MESSAGES: "{\"footer\":\"\\u003e 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}\",\"runStarted\":\"📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to github/gh-aw-side-repo PR #1...\",\"runSuccess\":\"✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!\",\"runFailure\":\"❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}\"}" GH_AW_WORKFLOW_ID: "smoke-update-cross-repo-pr" GH_AW_WORKFLOW_NAME: "Smoke Update Cross-Repo PR" outputs: @@ -1410,7 +1410,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"github-token\":\"${{ secrets.GH_AW_SIDE_REPO_PAT }}\",\"if_no_changes\":\"error\",\"labels\":[\"smoke-test\"],\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"1\",\"target-repo\":\"githubnext/gh-aw-side-repo\",\"title_prefix\":\"[smoke] \"}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":2},\"create_issue\":{\"close_older_issues\":true,\"expires\":2,\"labels\":[\"automation\",\"testing\"],\"max\":1},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"github-token\":\"${{ secrets.GH_AW_SIDE_REPO_PAT }}\",\"if_no_changes\":\"error\",\"labels\":[\"smoke-test\"],\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"AGENTS.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\"],\"target\":\"1\",\"target-repo\":\"github/gh-aw-side-repo\",\"title_prefix\":\"[smoke] \"}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} GITHUB_TOKEN: ${{ secrets.GH_AW_SIDE_REPO_PAT }} with: diff --git a/.github/workflows/smoke-update-cross-repo-pr.md b/.github/workflows/smoke-update-cross-repo-pr.md index 3f8ee59c4a..e201d1d7c7 100644 --- a/.github/workflows/smoke-update-cross-repo-pr.md +++ b/.github/workflows/smoke-update-cross-repo-pr.md @@ -1,6 +1,6 @@ --- name: Smoke Update Cross-Repo PR -description: Smoke test validating cross-repo pull request updates in githubnext/gh-aw-side-repo by adding lines from Homer's Odyssey to the README +description: Smoke test validating cross-repo pull request updates in github/gh-aw-side-repo by adding lines from Homer's Odyssey to the README on: schedule: every 12h @@ -21,7 +21,7 @@ network: - github checkout: - - repository: githubnext/gh-aw-side-repo + - repository: github/gh-aw-side-repo github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} fetch: ["main", "refs/pulls/open/*"] # fetch all open PR refs after checkout fetch-depth: 0 # fetch full history to ensure we can see all commits and PR details @@ -44,7 +44,7 @@ safe-outputs: hide-older-comments: true max: 2 push-to-pull-request-branch: - target-repo: "githubnext/gh-aw-side-repo" + target-repo: "github/gh-aw-side-repo" github-token: ${{ secrets.GH_AW_SIDE_REPO_PAT }} title-prefix: "[smoke] " labels: [smoke-test] @@ -52,7 +52,7 @@ safe-outputs: target: "1" # PR #1 messages: footer: "> 📜 *Cross-repo PR update smoke test by [{workflow_name}]({run_url})*{history_link}" - run-started: "📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to githubnext/gh-aw-side-repo PR #1..." + run-started: "📜 [{workflow_name}]({run_url}) is adding the next Odyssey line to github/gh-aw-side-repo PR #1..." run-success: "✅ [{workflow_name}]({run_url}) successfully updated the cross-repo PR with a new Odyssey line!" run-failure: "❌ [{workflow_name}]({run_url}) failed to update the cross-repo PR: {status}" @@ -95,4 +95,4 @@ Mark this step ✅ if the checkout succeeds, ❌ otherwise. - "Smoke Test: Copilot - Cross-repo update PR ${{ github.run_id }}" - The line that was added to the cross-repo PR - Overall status: SUCCESS - - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} + - Run URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} \ No newline at end of file diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md index a6f173a7e6..0a0a159189 100644 --- a/docs/src/content/docs/reference/frontmatter.md +++ b/docs/src/content/docs/reference/frontmatter.md @@ -151,10 +151,12 @@ Each plugin repository must be specified in `org/repo` format. The compiler gene ### APM Dependencies (`dependencies:`) -Specifies [microsoft/apm](https://github.com/microsoft/apm) packages to install before workflow execution. When present, the compiler emits a step using the `microsoft/apm-action` action to install the listed packages. +Specifies [microsoft/apm](https://github.com/microsoft/apm) packages to install before workflow execution. When present, the compiler resolves and packs dependencies in the activation job, then unpacks them in the agent job for faster, deterministic startup. APM (Agent Package Manager) manages AI agent primitives such as skills, prompts, instructions, agents, and hooks. Packages can depend on other packages and APM resolves the full dependency tree. +**Simple array format** (most common): + ```yaml wrap dependencies: - microsoft/apm-sample-package @@ -162,12 +164,22 @@ dependencies: - anthropics/skills/skills/frontend-design ``` +**Object format** with options: + +```yaml wrap +dependencies: + packages: + - microsoft/apm-sample-package + - github/awesome-copilot/skills/review-and-refactor + isolated: true # clear repo primitives before unpack (default: false) +``` + Each entry is an APM package reference. Supported formats: - `owner/repo` — full APM package - `owner/repo/path/to/skill` — individual skill or primitive from a repository -The compiler generates an `Install APM dependencies` step that runs after the engine CLI installation steps. +The compiler emits a pack step in the activation job and a restore step in the agent job. The APM target is automatically inferred from the configured engine (`copilot`, `claude`, or `all` for other engines). The `isolated` flag controls whether existing `.github/` primitive directories are cleared before the bundle is unpacked in the agent job. ### Runtimes (`runtimes:`) diff --git a/pkg/workflow/agentic_engine.go b/pkg/workflow/agentic_engine.go index c29ec83ae4..f221de38b1 100644 --- a/pkg/workflow/agentic_engine.go +++ b/pkg/workflow/agentic_engine.go @@ -148,6 +148,11 @@ type WorkflowExecutor interface { // before secret redaction runs. Engines that copy session or firewall state files should // override this; the default implementation returns an empty slice. GetFirewallLogsCollectionStep(workflowData *WorkflowData) []GitHubActionStep + + // GetAPMTarget returns the APM target value to use when packing dependencies with + // microsoft/apm-action. Supported values are "copilot", "claude", and "all". + // The default implementation returns "all" (packs all primitive types). + GetAPMTarget() string } // MCPConfigProvider handles MCP (Model Context Protocol) configuration @@ -337,6 +342,12 @@ func (e *BaseEngine) GetFirewallLogsCollectionStep(workflowData *WorkflowData) [ return []GitHubActionStep{} } +// GetAPMTarget returns "all" by default (packs all primitive types). +// CopilotEngine overrides this to return "copilot"; ClaudeEngine overrides to return "claude". +func (e *BaseEngine) GetAPMTarget() string { + return "all" +} + // ParseLogMetrics provides a default no-op implementation for log parsing // Engines can override this to provide detailed log parsing and metrics extraction func (e *BaseEngine) ParseLogMetrics(logContent string, verbose bool) LogMetrics { diff --git a/pkg/workflow/apm_dependencies.go b/pkg/workflow/apm_dependencies.go index 8a794f3b07..f4b625be7c 100644 --- a/pkg/workflow/apm_dependencies.go +++ b/pkg/workflow/apm_dependencies.go @@ -6,30 +6,29 @@ import ( var apmDepsLog = logger.New("workflow:apm_dependencies") -// GenerateAPMDependenciesStep generates a GitHub Actions step that installs APM packages -// using the microsoft/apm-action action. The step is emitted when the workflow frontmatter -// contains a non-empty `dependencies` list in microsoft/apm format. +// GenerateAPMPackStep generates the GitHub Actions step that installs APM packages and +// packs them into a bundle in the activation job. The step always uses isolated:true because +// the activation job has no repo context to preserve. // // Parameters: // - apmDeps: APM dependency configuration extracted from frontmatter -// - data: WorkflowData used for action pin resolution +// - target: APM target derived from the agentic engine (e.g. "copilot", "claude", "all") +// - data: WorkflowData used for action pin resolution // // Returns a GitHubActionStep, or an empty step if apmDeps is nil or has no packages. -func GenerateAPMDependenciesStep(apmDeps *APMDependenciesInfo, data *WorkflowData) GitHubActionStep { +func GenerateAPMPackStep(apmDeps *APMDependenciesInfo, target string, data *WorkflowData) GitHubActionStep { if apmDeps == nil || len(apmDeps.Packages) == 0 { - apmDepsLog.Print("No APM dependencies to install") + apmDepsLog.Print("No APM dependencies to pack") return GitHubActionStep{} } - apmDepsLog.Printf("Generating APM dependencies step: %d packages", len(apmDeps.Packages)) + apmDepsLog.Printf("Generating APM pack step: %d packages, target=%s", len(apmDeps.Packages), target) - // Resolve the pinned action reference for microsoft/apm-action. actionRef := GetActionPin("microsoft/apm-action") - // Build step lines. The `dependencies` input uses a YAML block scalar (`|`) - // so each package is written as an indented list item on its own line. lines := []string{ - " - name: Install APM dependencies", + " - name: Install and pack APM dependencies", + " id: apm_pack", " uses: " + actionRef, " with:", " dependencies: |", @@ -39,5 +38,45 @@ func GenerateAPMDependenciesStep(apmDeps *APMDependenciesInfo, data *WorkflowDat lines = append(lines, " - "+dep) } + lines = append(lines, + " isolated: 'true'", + " pack: 'true'", + " archive: 'true'", + " target: "+target, + " working-directory: /tmp/gh-aw/apm-workspace", + ) + + return GitHubActionStep(lines) +} + +// GenerateAPMRestoreStep generates the GitHub Actions step that restores APM packages +// from a pre-packed bundle in the agent job. +// +// Parameters: +// - apmDeps: APM dependency configuration extracted from frontmatter +// - data: WorkflowData used for action pin resolution +// +// Returns a GitHubActionStep, or an empty step if apmDeps is nil or has no packages. +func GenerateAPMRestoreStep(apmDeps *APMDependenciesInfo, data *WorkflowData) GitHubActionStep { + if apmDeps == nil || len(apmDeps.Packages) == 0 { + apmDepsLog.Print("No APM dependencies to restore") + return GitHubActionStep{} + } + + apmDepsLog.Printf("Generating APM restore step (isolated=%v)", apmDeps.Isolated) + + actionRef := GetActionPin("microsoft/apm-action") + + lines := []string{ + " - name: Restore APM dependencies", + " uses: " + actionRef, + " with:", + " bundle: /tmp/gh-aw/apm-bundle/*.tar.gz", + } + + if apmDeps.Isolated { + lines = append(lines, " isolated: 'true'") + } + return GitHubActionStep(lines) } diff --git a/pkg/workflow/apm_dependencies_compilation_test.go b/pkg/workflow/apm_dependencies_compilation_test.go index b39875fae9..e9f0c5d945 100644 --- a/pkg/workflow/apm_dependencies_compilation_test.go +++ b/pkg/workflow/apm_dependencies_compilation_test.go @@ -43,14 +43,37 @@ Test with a single APM dependency lockContent := string(content) - assert.Contains(t, lockContent, "Install APM dependencies", - "Lock file should contain APM dependencies step name") + // Activation job should have the pack step + assert.Contains(t, lockContent, "Install and pack APM dependencies", + "Lock file should contain APM pack step in activation job") assert.Contains(t, lockContent, "microsoft/apm-action", "Lock file should reference the microsoft/apm-action action") - assert.Contains(t, lockContent, "dependencies: |", - "Lock file should use block scalar for dependencies input") assert.Contains(t, lockContent, "- microsoft/apm-sample-package", "Lock file should list the dependency package") + assert.Contains(t, lockContent, "id: apm_pack", + "Lock file should have apm_pack step ID") + assert.Contains(t, lockContent, "pack: 'true'", + "Lock file should include pack input") + assert.Contains(t, lockContent, "target: copilot", + "Lock file should include target inferred from copilot engine") + + // Separate APM artifact upload in activation job + assert.Contains(t, lockContent, "Upload APM bundle artifact", + "Lock file should upload APM bundle as separate artifact") + assert.Contains(t, lockContent, "name: apm", + "Lock file should name the APM artifact 'apm'") + + // Agent job should have download + restore steps + assert.Contains(t, lockContent, "Download APM bundle artifact", + "Lock file should download APM bundle in agent job") + assert.Contains(t, lockContent, "Restore APM dependencies", + "Lock file should contain APM restore step in agent job") + assert.Contains(t, lockContent, "bundle: /tmp/gh-aw/apm-bundle/*.tar.gz", + "Lock file should restore from bundle path") + + // Old install step should NOT appear + assert.NotContains(t, lockContent, "Install APM dependencies", + "Lock file should not contain the old install step name") } func TestAPMDependenciesCompilationMultiplePackages(t *testing.T) { @@ -85,8 +108,8 @@ Test with multiple APM dependencies lockContent := string(content) - assert.Contains(t, lockContent, "Install APM dependencies", - "Lock file should contain APM dependencies step name") + assert.Contains(t, lockContent, "Install and pack APM dependencies", + "Lock file should contain APM pack step") assert.Contains(t, lockContent, "microsoft/apm-action", "Lock file should reference the microsoft/apm-action action") assert.Contains(t, lockContent, "- microsoft/apm-sample-package", @@ -95,6 +118,8 @@ Test with multiple APM dependencies "Lock file should include second dependency") assert.Contains(t, lockContent, "- anthropics/skills/skills/frontend-design", "Lock file should include third dependency") + assert.Contains(t, lockContent, "Restore APM dependencies", + "Lock file should contain APM restore step") } func TestAPMDependenciesCompilationNoDependencies(t *testing.T) { @@ -125,8 +150,85 @@ Test without APM dependencies lockContent := string(content) - assert.NotContains(t, lockContent, "Install APM dependencies", - "Lock file should not contain APM dependencies step when no dependencies specified") + assert.NotContains(t, lockContent, "Install and pack APM dependencies", + "Lock file should not contain APM pack step when no dependencies specified") + assert.NotContains(t, lockContent, "Restore APM dependencies", + "Lock file should not contain APM restore step when no dependencies specified") assert.NotContains(t, lockContent, "microsoft/apm-action", "Lock file should not reference microsoft/apm-action when no dependencies specified") } + +func TestAPMDependenciesCompilationObjectFormatIsolated(t *testing.T) { + tmpDir := testutil.TempDir(t, "apm-deps-isolated-test") + + workflow := `--- +engine: copilot +on: workflow_dispatch +permissions: + issues: read + pull-requests: read +dependencies: + packages: + - microsoft/apm-sample-package + isolated: true +--- + +Test with isolated APM dependencies +` + + testFile := filepath.Join(tmpDir, "test-apm-isolated.md") + err := os.WriteFile(testFile, []byte(workflow), 0644) + require.NoError(t, err, "Failed to write test file") + + compiler := NewCompiler() + err = compiler.CompileWorkflow(testFile) + require.NoError(t, err, "Compilation should succeed") + + lockFile := strings.Replace(testFile, ".md", ".lock.yml", 1) + content, err := os.ReadFile(lockFile) + require.NoError(t, err, "Failed to read lock file") + + lockContent := string(content) + + assert.Contains(t, lockContent, "Install and pack APM dependencies", + "Lock file should contain APM pack step") + assert.Contains(t, lockContent, "Restore APM dependencies", + "Lock file should contain APM restore step") + // Restore step should include isolated: true because frontmatter says so + assert.Contains(t, lockContent, "isolated: 'true'", + "Lock file restore step should include isolated flag") +} + +func TestAPMDependenciesCompilationClaudeEngineTarget(t *testing.T) { + tmpDir := testutil.TempDir(t, "apm-deps-claude-test") + + workflow := `--- +engine: claude +on: workflow_dispatch +permissions: + issues: read + pull-requests: read +dependencies: + - microsoft/apm-sample-package +--- + +Test with Claude engine target inference +` + + testFile := filepath.Join(tmpDir, "test-apm-claude.md") + err := os.WriteFile(testFile, []byte(workflow), 0644) + require.NoError(t, err, "Failed to write test file") + + compiler := NewCompiler() + err = compiler.CompileWorkflow(testFile) + require.NoError(t, err, "Compilation should succeed") + + lockFile := strings.Replace(testFile, ".md", ".lock.yml", 1) + content, err := os.ReadFile(lockFile) + require.NoError(t, err, "Failed to read lock file") + + lockContent := string(content) + + assert.Contains(t, lockContent, "target: claude", + "Lock file should use claude target for claude engine") +} diff --git a/pkg/workflow/apm_dependencies_test.go b/pkg/workflow/apm_dependencies_test.go index 4564bef140..77ab336218 100644 --- a/pkg/workflow/apm_dependencies_test.go +++ b/pkg/workflow/apm_dependencies_test.go @@ -12,9 +12,10 @@ import ( func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) { tests := []struct { - name string - frontmatter map[string]any - expectedDeps []string + name string + frontmatter map[string]any + expectedDeps []string + expectedIsolated bool }{ { name: "No dependencies field", @@ -53,7 +54,7 @@ func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) { expectedDeps: nil, }, { - name: "Non-array value is ignored", + name: "Non-array, non-object value is ignored", frontmatter: map[string]any{ "dependencies": "microsoft/apm-sample-package", }, @@ -66,6 +67,50 @@ func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) { }, expectedDeps: []string{"microsoft/apm-sample-package", "github/awesome-copilot"}, }, + { + name: "Object format with packages only", + frontmatter: map[string]any{ + "dependencies": map[string]any{ + "packages": []any{ + "microsoft/apm-sample-package", + "github/awesome-copilot", + }, + }, + }, + expectedDeps: []string{"microsoft/apm-sample-package", "github/awesome-copilot"}, + expectedIsolated: false, + }, + { + name: "Object format with isolated true", + frontmatter: map[string]any{ + "dependencies": map[string]any{ + "packages": []any{"microsoft/apm-sample-package"}, + "isolated": true, + }, + }, + expectedDeps: []string{"microsoft/apm-sample-package"}, + expectedIsolated: true, + }, + { + name: "Object format with isolated false", + frontmatter: map[string]any{ + "dependencies": map[string]any{ + "packages": []any{"microsoft/apm-sample-package"}, + "isolated": false, + }, + }, + expectedDeps: []string{"microsoft/apm-sample-package"}, + expectedIsolated: false, + }, + { + name: "Object format with empty packages", + frontmatter: map[string]any{ + "dependencies": map[string]any{ + "packages": []any{}, + }, + }, + expectedDeps: nil, + }, } for _, tt := range tests { @@ -76,52 +121,88 @@ func TestExtractAPMDependenciesFromFrontmatter(t *testing.T) { } else { require.NotNil(t, result, "Should return non-nil APMDependenciesInfo") assert.Equal(t, tt.expectedDeps, result.Packages, "Extracted packages should match expected") + assert.Equal(t, tt.expectedIsolated, result.Isolated, "Isolated flag should match expected") } }) } } -func TestGenerateAPMDependenciesStep(t *testing.T) { +func TestEngineGetAPMTarget(t *testing.T) { + tests := []struct { + name string + engine CodingAgentEngine + expected string + }{ + {name: "copilot engine returns copilot", engine: NewCopilotEngine(), expected: "copilot"}, + {name: "claude engine returns claude", engine: NewClaudeEngine(), expected: "claude"}, + {name: "codex engine returns all", engine: NewCodexEngine(), expected: "all"}, + {name: "gemini engine returns all", engine: NewGeminiEngine(), expected: "all"}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.engine.GetAPMTarget() + assert.Equal(t, tt.expected, result, "APM target should match for engine %s", tt.engine.GetID()) + }) + } +} + +func TestGenerateAPMPackStep(t *testing.T) { tests := []struct { name string apmDeps *APMDependenciesInfo + target string expectedContains []string expectedEmpty bool }{ { name: "Nil deps returns empty step", apmDeps: nil, + target: "copilot", expectedEmpty: true, }, { name: "Empty packages returns empty step", apmDeps: &APMDependenciesInfo{Packages: []string{}}, + target: "copilot", expectedEmpty: true, }, { - name: "Single dependency", + name: "Single dependency with copilot target", apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}}, + target: "copilot", expectedContains: []string{ - "Install APM dependencies", + "Install and pack APM dependencies", + "id: apm_pack", "microsoft/apm-action", "dependencies: |", "- microsoft/apm-sample-package", + "isolated: 'true'", + "pack: 'true'", + "archive: 'true'", + "target: copilot", + "working-directory: /tmp/gh-aw/apm-workspace", }, }, { - name: "Multiple dependencies", - apmDeps: &APMDependenciesInfo{ - Packages: []string{ - "microsoft/apm-sample-package", - "github/awesome-copilot/skills/review-and-refactor", - }, - }, + name: "Multiple dependencies with claude target", + apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package", "github/skills/review"}}, + target: "claude", expectedContains: []string{ - "Install APM dependencies", + "Install and pack APM dependencies", + "id: apm_pack", "microsoft/apm-action", - "dependencies: |", "- microsoft/apm-sample-package", - "- github/awesome-copilot/skills/review-and-refactor", + "- github/skills/review", + "target: claude", + }, + }, + { + name: "All target for non-copilot/claude engine", + apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}}, + target: "all", + expectedContains: []string{ + "target: all", }, }, } @@ -129,7 +210,7 @@ func TestGenerateAPMDependenciesStep(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { data := &WorkflowData{Name: "test-workflow"} - step := GenerateAPMDependenciesStep(tt.apmDeps, data) + step := GenerateAPMPackStep(tt.apmDeps, tt.target, data) if tt.expectedEmpty { assert.Empty(t, step, "Step should be empty for empty/nil dependencies") @@ -138,7 +219,6 @@ func TestGenerateAPMDependenciesStep(t *testing.T) { require.NotEmpty(t, step, "Step should not be empty") - // Combine all lines for easier assertion var sb strings.Builder for _, line := range step { sb.WriteString(line + "\n") @@ -152,30 +232,70 @@ func TestGenerateAPMDependenciesStep(t *testing.T) { } } -func TestAPMDependenciesStepFormat(t *testing.T) { - deps := &APMDependenciesInfo{ - Packages: []string{ - "microsoft/apm-sample-package", - "github/awesome-copilot/skills/review-and-refactor", +func TestGenerateAPMRestoreStep(t *testing.T) { + tests := []struct { + name string + apmDeps *APMDependenciesInfo + expectedContains []string + expectedNotContains []string + expectedEmpty bool + }{ + { + name: "Nil deps returns empty step", + apmDeps: nil, + expectedEmpty: true, + }, + { + name: "Empty packages returns empty step", + apmDeps: &APMDependenciesInfo{Packages: []string{}}, + expectedEmpty: true, + }, + { + name: "Non-isolated restore step", + apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}, Isolated: false}, + expectedContains: []string{ + "Restore APM dependencies", + "microsoft/apm-action", + "bundle: /tmp/gh-aw/apm-bundle/*.tar.gz", + }, + expectedNotContains: []string{"isolated"}, + }, + { + name: "Isolated restore step", + apmDeps: &APMDependenciesInfo{Packages: []string{"microsoft/apm-sample-package"}, Isolated: true}, + expectedContains: []string{ + "Restore APM dependencies", + "microsoft/apm-action", + "bundle: /tmp/gh-aw/apm-bundle/*.tar.gz", + "isolated: 'true'", + }, }, } - data := &WorkflowData{Name: "test-workflow"} - step := GenerateAPMDependenciesStep(deps, data) - require.NotEmpty(t, step, "Step should not be empty") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + data := &WorkflowData{Name: "test-workflow"} + step := GenerateAPMRestoreStep(tt.apmDeps, data) - // Combine all lines for easy assertion - var sb strings.Builder - for _, line := range step { - sb.WriteString(line + "\n") + if tt.expectedEmpty { + assert.Empty(t, step, "Step should be empty for empty/nil dependencies") + return + } + + require.NotEmpty(t, step, "Step should not be empty") + + var sb strings.Builder + for _, line := range step { + sb.WriteString(line + "\n") + } + combined := sb.String() + + for _, expected := range tt.expectedContains { + assert.Contains(t, combined, expected, "Step should contain: %s", expected) + } + for _, notExpected := range tt.expectedNotContains { + assert.NotContains(t, combined, notExpected, "Step should not contain: %s", notExpected) + } + }) } - combined := sb.String() - - // Verify the step has the correct structure - assert.Contains(t, combined, "- name: Install APM dependencies", "Should have correct step name") - assert.Contains(t, combined, "uses:", "Should have uses line") - assert.Contains(t, combined, "microsoft/apm-action", "Should reference microsoft/apm-action action") - assert.Contains(t, combined, "dependencies: |", "Should use YAML block scalar for dependencies") - assert.Contains(t, combined, " - microsoft/apm-sample-package", "First dep should be properly indented") - assert.Contains(t, combined, " - github/awesome-copilot/skills/review-and-refactor", "Second dep should be properly indented") } diff --git a/pkg/workflow/claude_engine.go b/pkg/workflow/claude_engine.go index 7bf9882beb..33651bf37f 100644 --- a/pkg/workflow/claude_engine.go +++ b/pkg/workflow/claude_engine.go @@ -40,6 +40,11 @@ func (e *ClaudeEngine) GetModelEnvVarName() string { return constants.ClaudeCLIModelEnvVar } +// GetAPMTarget returns "claude" so that apm-action packs Claude-specific primitives. +func (e *ClaudeEngine) GetAPMTarget() string { + return "claude" +} + // GetRequiredSecretNames returns the list of secrets required by the Claude engine // This includes ANTHROPIC_API_KEY and optionally MCP_GATEWAY_API_KEY func (e *ClaudeEngine) GetRequiredSecretNames(workflowData *WorkflowData) []string { diff --git a/pkg/workflow/compiler_activation_job.go b/pkg/workflow/compiler_activation_job.go index 2136d51828..a07621e6a0 100644 --- a/pkg/workflow/compiler_activation_job.go +++ b/pkg/workflow/compiler_activation_job.go @@ -326,6 +326,26 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate compilerActivationJobLog.Print("Generating prompt in activation job") c.generatePromptInActivationJob(&steps, data, preActivationJobCreated, customJobsBeforeActivation) + // Generate APM pack step if dependencies are specified. + // The pack step runs after prompt generation and uploads as a separate "apm" artifact. + if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 { + compilerActivationJobLog.Printf("Adding APM pack step: %d packages", len(data.APMDependencies.Packages)) + apmTarget := engine.GetAPMTarget() + apmPackStep := GenerateAPMPackStep(data.APMDependencies, apmTarget, data) + for _, line := range apmPackStep { + steps = append(steps, line+"\n") + } + // Upload the packed APM bundle as a separate artifact for the agent job to download + compilerActivationJobLog.Print("Adding APM bundle artifact upload step") + steps = append(steps, " - name: Upload APM bundle artifact\n") + steps = append(steps, " if: success()\n") + steps = append(steps, fmt.Sprintf(" uses: %s\n", GetActionPin("actions/upload-artifact"))) + steps = append(steps, " with:\n") + steps = append(steps, " name: apm\n") + steps = append(steps, " path: ${{ steps.apm_pack.outputs.bundle-path }}\n") + steps = append(steps, " retention-days: 1\n") + } + // Upload aw_info.json and prompt.txt as the activation artifact for the agent job to download compilerActivationJobLog.Print("Adding activation artifact upload step") steps = append(steps, " - name: Upload activation artifact\n") diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go index 2a84085051..13876fbde9 100644 --- a/pkg/workflow/compiler_yaml_main_job.go +++ b/pkg/workflow/compiler_yaml_main_job.go @@ -231,8 +231,17 @@ func (c *Compiler) generateMainJobSteps(yaml *strings.Builder, data *WorkflowDat // Add APM (Agent Package Manager) setup step if dependencies are specified if data.APMDependencies != nil && len(data.APMDependencies.Packages) > 0 { - compilerYamlLog.Printf("Adding APM setup step: %d packages", len(data.APMDependencies.Packages)) - apmStep := GenerateAPMDependenciesStep(data.APMDependencies, data) + // Download the pre-packed APM bundle from the separate "apm" artifact + compilerYamlLog.Printf("Adding APM bundle download step: %d packages", len(data.APMDependencies.Packages)) + yaml.WriteString(" - name: Download APM bundle artifact\n") + fmt.Fprintf(yaml, " uses: %s\n", GetActionPin("actions/download-artifact")) + yaml.WriteString(" with:\n") + yaml.WriteString(" name: apm\n") + yaml.WriteString(" path: /tmp/gh-aw/apm-bundle\n") + + // Restore APM dependencies from bundle + compilerYamlLog.Printf("Adding APM restore step") + apmStep := GenerateAPMRestoreStep(data.APMDependencies, data) for _, line := range apmStep { yaml.WriteString(line + "\n") } diff --git a/pkg/workflow/copilot_engine.go b/pkg/workflow/copilot_engine.go index cc09bfacaf..1331cc011f 100644 --- a/pkg/workflow/copilot_engine.go +++ b/pkg/workflow/copilot_engine.go @@ -55,6 +55,11 @@ func (e *CopilotEngine) GetDefaultDetectionModel() string { return string(constants.DefaultCopilotDetectionModel) } +// GetAPMTarget returns "copilot" so that apm-action packs Copilot-specific primitives. +func (e *CopilotEngine) GetAPMTarget() string { + return "copilot" +} + // GetModelEnvVarName returns the native environment variable name that the Copilot CLI uses // for model selection. Setting COPILOT_MODEL is equivalent to passing --model to the CLI. func (e *CopilotEngine) GetModelEnvVarName() string { diff --git a/pkg/workflow/frontmatter_extraction_metadata.go b/pkg/workflow/frontmatter_extraction_metadata.go index 4b29b2e152..944e263e15 100644 --- a/pkg/workflow/frontmatter_extraction_metadata.go +++ b/pkg/workflow/frontmatter_extraction_metadata.go @@ -360,8 +360,9 @@ func extractPluginsFromFrontmatter(frontmatter map[string]any) *PluginInfo { } // extractAPMDependenciesFromFrontmatter extracts APM (Agent Package Manager) dependency -// configuration from frontmatter. Supports array format only: +// configuration from frontmatter. Supports two formats: // - Array format: ["org/pkg1", "org/pkg2"] +// - Object format: {packages: ["org/pkg1", "org/pkg2"], isolated: true} // // Returns nil if no dependencies field is present or if the field contains no packages. func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDependenciesInfo { @@ -370,22 +371,41 @@ func extractAPMDependenciesFromFrontmatter(frontmatter map[string]any) *APMDepen return nil } - depsArray, ok := value.([]any) - if !ok { - return nil - } - var packages []string - for _, item := range depsArray { - if s, ok := item.(string); ok && s != "" { - packages = append(packages, s) + var isolated bool + + switch v := value.(type) { + case []any: + // Array format: dependencies: [pkg1, pkg2] + for _, item := range v { + if s, ok := item.(string); ok && s != "" { + packages = append(packages, s) + } + } + case map[string]any: + // Object format: dependencies: {packages: [...], isolated: true} + if pkgsAny, ok := v["packages"]; ok { + if pkgsArray, ok := pkgsAny.([]any); ok { + for _, item := range pkgsArray { + if s, ok := item.(string); ok && s != "" { + packages = append(packages, s) + } + } + } } + if iso, ok := v["isolated"]; ok { + if isoBool, ok := iso.(bool); ok { + isolated = isoBool + } + } + default: + return nil } if len(packages) == 0 { return nil } - frontmatterMetadataLog.Printf("Extracted %d APM dependency packages from frontmatter", len(packages)) - return &APMDependenciesInfo{Packages: packages} + frontmatterMetadataLog.Printf("Extracted %d APM dependency packages from frontmatter (isolated=%v)", len(packages), isolated) + return &APMDependenciesInfo{Packages: packages, Isolated: isolated} } diff --git a/pkg/workflow/frontmatter_types.go b/pkg/workflow/frontmatter_types.go index 3cd69b3a20..64ba420d7d 100644 --- a/pkg/workflow/frontmatter_types.go +++ b/pkg/workflow/frontmatter_types.go @@ -87,10 +87,11 @@ type PluginsConfig struct { } // APMDependenciesInfo encapsulates APM (Agent Package Manager) dependency configuration. -// Supports both simple array format (list of package slugs) and object format with -// an "apm" sub-key. When present, a microsoft/apm-action setup step is emitted. +// Supports simple array format and object format with packages and isolated fields. +// When present, a pack step is emitted in the activation job and a restore step in the agent job. type APMDependenciesInfo struct { Packages []string // APM package slugs to install (e.g., "org/package") + Isolated bool // If true, agent restore step clears primitive dirs before unpacking } // RateLimitConfig represents rate limiting configuration for workflow triggers