Add microsoft/apm dependencies support to frontmatter#19883
Conversation
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Add APMDependenciesInfo struct to frontmatter_types.go - Add extractAPMDependenciesFromFrontmatter supporting array and object formats - Add APMDependencies field to WorkflowData and toolsProcessingResult - Emit microsoft/apm-action step in main job when dependencies are specified - Add microsoft/apm-action@v1 to action pins (SHA-pinned) - Add dependencies field to main_workflow_schema.json - Add integration tests for APM dependencies compilation - Emit experimental warning when dependencies are used" Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
… support object format, fix tests and docs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot remove api native format. REMOVE THIS APM-native object formatdependencies: |
…ies field Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds support for an APM-style dependencies: frontmatter field that injects a pinned microsoft/apm-action step into generated workflows to install agent packages prior to execution.
Changes:
- Introduces
APMDependenciesInfoand frontmatter extraction fordependencies(array format). - Wires extracted dependencies through compilation pipeline and emits an “Install APM dependencies” step in the main job.
- Adds action pin for
microsoft/apm-action@v1, schema validation for the new field, and docs/tests.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| pkg/workflow/frontmatter_types.go | Adds a new APMDependenciesInfo type for dependency data. |
| pkg/workflow/frontmatter_extraction_metadata.go | Extracts dependencies from frontmatter into typed data. |
| pkg/workflow/compiler_orchestrator_tools.go | Plumbs extracted APM dependencies out of tools/frontmatter processing. |
| pkg/workflow/compiler_orchestrator_workflow.go | Carries APM dependencies into initial WorkflowData. |
| pkg/workflow/compiler_types.go | Adds APMDependencies field to WorkflowData. |
| pkg/workflow/compiler_yaml_main_job.go | Emits the APM install step after engine installation, before MCP setup. |
| pkg/workflow/apm_dependencies.go | Generates the YAML step for microsoft/apm-action. |
| pkg/workflow/compiler.go | Emits an experimental feature warning when dependencies is used. |
| pkg/workflow/data/action_pins.json | Adds SHA pin for microsoft/apm-action@v1. |
| .github/aw/actions-lock.json | Adds corresponding lock entry for microsoft/apm-action@v1. |
| pkg/workflow/action_pins_test.go | Updates action pin count expectation. |
| pkg/parser/schemas/main_workflow_schema.json | Adds dependencies schema field and validation pattern. |
| docs/src/content/docs/reference/frontmatter.md | Documents the new dependencies: frontmatter field. |
| pkg/workflow/apm_dependencies_test.go | Unit tests for extraction + step generation. |
| pkg/workflow/apm_dependencies_compilation_test.go | Integration tests asserting compiled workflow output contains/omits the step appropriately. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| func GenerateAPMDependenciesStep(apmDeps *APMDependenciesInfo, data *WorkflowData) GitHubActionStep { | ||
| if apmDeps == nil || len(apmDeps.Packages) == 0 { | ||
| apmDepsLog.Print("No APM dependencies to install") | ||
| return GitHubActionStep{} | ||
| } | ||
|
|
||
| apmDepsLog.Printf("Generating APM dependencies step: %d packages", len(apmDeps.Packages)) | ||
|
|
||
| // 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", | ||
| " uses: " + actionRef, | ||
| " with:", | ||
| " dependencies: |", |
There was a problem hiding this comment.
GenerateAPMDependenciesStep accepts data for action pin resolution but currently ignores it and calls GetActionPin, which can return an empty string if the pin is missing (producing an invalid uses: line). Consider either (1) switching to GetActionPinWithData("microsoft/apm-action", "v1", data) with a safe fallback to microsoft/apm-action@v1 when pinning fails, or (2) removing the unused data parameter and explicitly handling the empty-pin case (e.g., return an error/empty step).
| "type": "array", | ||
| "items": { | ||
| "type": "string", | ||
| "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_./-]+$", |
There was a problem hiding this comment.
The dependencies schema pattern ^[a-zA-Z0-9_-]+/[a-zA-Z0-9_./-]+$ is overly permissive: it allows values like org//skill (empty repo segment) and org/repo/ (trailing slash). Suggest tightening it to require at least owner/repo with optional non-empty path segments (e.g., ^[A-Za-z0-9_-]+/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_.-]+)*$).
| "pattern": "^[a-zA-Z0-9_-]+/[a-zA-Z0-9_./-]+$", | |
| "pattern": "^[A-Za-z0-9_-]+/[A-Za-z0-9_-]+(?:/[A-Za-z0-9_.-]+)*$", |
| // Verify we got all the pins (35 as of March 2026) | ||
| if len(pins) != 35 { | ||
| t.Errorf("getActionPins() returned %d pins, expected 35", len(pins)) | ||
| } | ||
|
|
There was a problem hiding this comment.
This test hard-codes the total number of action pins, which requires manual updates every time a new pin is added/removed. To make the test less brittle, consider asserting on ordering + presence of specific known keys (including the newly added microsoft/apm-action) rather than an exact count.
| // Verify we got all the pins (35 as of March 2026) | |
| if len(pins) != 35 { | |
| t.Errorf("getActionPins() returned %d pins, expected 35", len(pins)) | |
| } | |
| // Verify we got at least one pin (avoid brittle exact-count checks) | |
| if len(pins) == 0 { | |
| t.Fatal("getActionPins() returned no pins") | |
| } | |
| // Verify some known repos are present, including the newly added microsoft/apm-action | |
| expectedRepos := []string{ | |
| "actions/checkout", | |
| "actions/setup-node", | |
| "microsoft/apm-action", | |
| } | |
| for _, expected := range expectedRepos { | |
| found := false | |
| for _, pin := range pins { | |
| if pin.Repo == expected { | |
| found = true | |
| break | |
| } | |
| } | |
| if !found { | |
| t.Errorf("getActionPins() missing expected repo %q", expected) | |
| } | |
| } |
| "microsoft/apm-action@v1": { | ||
| "repo": "microsoft/apm-action", | ||
| "version": "v1", | ||
| "sha": "92d6dc8046ad61b340662adefd2f997bf93d2987" |
There was a problem hiding this comment.
action_pins.json entries appear to be kept in alphabetical order by key/repo; this new microsoft/apm-action@v1 entry is appended after super-linter/... rather than being placed between haskell-actions/... and oven-sh/.... Consider re-sorting to keep the file ordered for easier maintenance and cleaner diffs.
| "microsoft/apm-action@v1": { | ||
| "repo": "microsoft/apm-action", | ||
| "version": "v1", | ||
| "sha": "92d6dc8046ad61b340662adefd2f997bf93d2987" |
There was a problem hiding this comment.
Similar to pkg/workflow/data/action_pins.json, this lockfile appears to keep entries ordered; the new microsoft/apm-action@v1 entry is appended at the end. Please re-sort/insert it in the correct position to maintain deterministic ordering and reduce future merge conflicts.
| // 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. |
There was a problem hiding this comment.
The doc comment claims the dependencies frontmatter supports both an array format and an object format with an apm sub-key, but the extractor (extractAPMDependenciesFromFrontmatter) and JSON schema only support the array format. Please align the type comment with the implemented/supported format, or implement the described object format end-to-end (extractor + schema + docs).
| // 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. | |
| // Currently supports only a simple array format in frontmatter, representing a list | |
| // of APM package slugs to install. |
|
@copilot usage of dependencies reuqires contents: read permission |
Adds a
dependenciesfrontmatter field supporting the microsoft/apm (Agent Package Manager) format. When present, the compiler emits a SHA-pinnedmicrosoft/apm-actionstep to install the listed packages before agent execution.Frontmatter format
Package references support both
org/repoandorg/repo/path/to/skillforms.Generated step
Step is inserted after engine CLI installation steps, before MCP setup.
Changes
frontmatter_types.go—APMDependenciesInfostructfrontmatter_extraction_metadata.go—extractAPMDependenciesFromFrontmatter()(array format)apm_dependencies.go—GenerateAPMDependenciesStep()standalone function (mirrors plugin install pattern)compiler_orchestrator_tools.go/workflow.go/compiler_types.go— wireAPMDependenciesthrough the pipelinecompiler_yaml_main_job.go— emit step whenAPMDependenciesis setcompiler.go— experimental feature warningdata/action_pins.json+.github/aw/actions-lock.json— pinmicrosoft/apm-action@v1pkg/parser/schemas/main_workflow_schema.json—dependenciesfield with path-aware patterndocs/src/content/docs/reference/frontmatter.md— documentationOriginal prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.