diff --git a/.github/workflows/shared/apm.md b/.github/workflows/shared/apm.md index b505ce5ab17..35cc64097e2 100644 --- a/.github/workflows/shared/apm.md +++ b/.github/workflows/shared/apm.md @@ -4,7 +4,7 @@ # # 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. +# The agent job then downloads and unpacks the bundle as pre-agent-steps. # # Documentation: https://github.com/microsoft/APM # @@ -64,7 +64,7 @@ jobs: path: ${{ steps.apm_pack.outputs.bundle-path }} retention-days: '1' -steps: +pre-agent-steps: - name: Download APM bundle artifact uses: actions/download-artifact@v8.0.1 with: @@ -83,13 +83,13 @@ steps: ## APM Packages These packages are installed via a dedicated "apm" job that packs and uploads a bundle, -which the agent job then downloads and unpacks as pre-steps. +which the agent job then downloads and unpacks as pre-agent-steps. ### How it works 1. **Pack** (`apm` job): `microsoft/apm-action` installs packages and creates a bundle archive, uploaded as a GitHub Actions artifact. -2. **Unpack** (agent job pre-steps): the bundle is downloaded and unpacked via +2. **Unpack** (agent job pre-agent-steps): the bundle is downloaded and unpacked via `microsoft/apm-action` in restore mode, making all skills and tools available to the AI agent. ### Package format diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 6b9defa6dd8..431f9acfe05 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -870,19 +870,6 @@ jobs: run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh" env: GH_TOKEN: ${{ github.token }} - - name: Download APM bundle artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 - with: - name: ${{ needs.activation.outputs.artifact_prefix }}apm - path: /tmp/gh-aw/apm-bundle - - id: apm_bundle - name: Find APM bundle path - run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" - - name: Restore APM packages - uses: microsoft/apm-action@a190b0b1a91031057144dc136acf9757a59c9e4d # v1.4.1 - with: - bundle: ${{ steps.apm_bundle.outputs.path }} - # Cache memory file share configuration from frontmatter processed below - name: Create cache-memory directory run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh" @@ -2173,6 +2160,19 @@ jobs: - name: Clean git credentials continue-on-error: true run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh" + - name: Download APM bundle artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: ${{ needs.activation.outputs.artifact_prefix }}apm + path: /tmp/gh-aw/apm-bundle + - id: apm_bundle + name: Find APM bundle path + run: echo "path=$(find /tmp/gh-aw/apm-bundle -name '*.tar.gz' | head -1)" >> "$GITHUB_OUTPUT" + - name: Restore APM packages + uses: microsoft/apm-action@a190b0b1a91031057144dc136acf9757a59c9e4d # v1.4.1 + with: + bundle: ${{ steps.apm_bundle.outputs.path }} + - name: Execute Claude Code CLI id: agentic_execution # Allowed tools (sorted): diff --git a/pkg/workflow/compiler_pre_agent_steps_test.go b/pkg/workflow/compiler_pre_agent_steps_test.go index b571f8249d8..b8bb38789bf 100644 --- a/pkg/workflow/compiler_pre_agent_steps_test.go +++ b/pkg/workflow/compiler_pre_agent_steps_test.go @@ -124,3 +124,71 @@ Main workflow. t.Errorf("Main pre-agent-step (%d) should appear before AI execution step (%d)", mainIdx, aiStepIdx) } } + +func TestImportedPreAgentStepsRunAfterPRBaseRestore(t *testing.T) { + tmpDir := testutil.TempDir(t, "pre-agent-steps-pr-restore-test") + + sharedDir := filepath.Join(tmpDir, "shared") + if err := os.MkdirAll(sharedDir, 0755); err != nil { + t.Fatal(err) + } + + sharedContent := `--- +pre-agent-steps: + - name: Restore APM packages + run: echo "restore apm" +--- + +Shared APM-style steps. +` + sharedFile := filepath.Join(sharedDir, "apm.md") + if err := os.WriteFile(sharedFile, []byte(sharedContent), 0644); err != nil { + t.Fatal(err) + } + + mainContent := `--- +on: + pull_request: + types: [opened] +permissions: + contents: read + issues: read + pull-requests: read +imports: + - ./shared/apm.md +engine: claude +strict: false +--- + +Main workflow. +` + mainFile := filepath.Join(tmpDir, "main.md") + if err := os.WriteFile(mainFile, []byte(mainContent), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(mainFile); err != nil { + t.Fatalf("Unexpected error compiling workflow with imported pre-agent-steps in PR context: %v", err) + } + + lockFile := filepath.Join(tmpDir, "main.lock.yml") + content, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read generated lock file: %v", err) + } + lockContent := string(content) + + restoreBaseIdx := indexInNonCommentLines(lockContent, "- name: Restore agent config folders from base branch") + restoreAPMIdx := indexInNonCommentLines(lockContent, "- name: Restore APM packages") + aiStepIdx := indexInNonCommentLines(lockContent, "- name: Execute Claude Code CLI") + if restoreBaseIdx == -1 || restoreAPMIdx == -1 || aiStepIdx == -1 { + t.Fatal("Could not find expected PR restore, pre-agent, and AI steps in generated workflow") + } + if restoreBaseIdx >= restoreAPMIdx { + t.Errorf("PR base restore step (%d) should appear before imported pre-agent step (%d)", restoreBaseIdx, restoreAPMIdx) + } + if restoreAPMIdx >= aiStepIdx { + t.Errorf("Imported pre-agent step (%d) should appear before AI execution step (%d)", restoreAPMIdx, aiStepIdx) + } +}