Skip to content

fix(isolated): inject target input into generated apm.yml#33

Merged
danielmeppiel merged 4 commits into
mainfrom
fix/isolated-target-in-manifest
May 7, 2026
Merged

fix(isolated): inject target input into generated apm.yml#33
danielmeppiel merged 4 commits into
mainfrom
fix/isolated-target-in-manifest

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

TL;DR

APM v0.12.3 (microsoft/apm#1154) made target detection strict. In isolated mode, this action generates apm.yml from inline dependencies: but never persisted the target: input into that manifest, so every workflow upgrading past v0.12.3 now exits 2 with Error: No harness detected for installation.

This PR injects target: <value> into the generated manifest when the input is set.

Symptom

Broken run on microsoft/apm itself (uses apm-action@v1 from a gh-aw workflow):

Generated apm.yml with 1 dependencies (isolated mode)
[STEP 1/3] Resolving dependencies...
Error: No harness detected for installation.

The workflow set target: claude but the generated apm.yml only contained name / version / dependencies.

Root cause

src/runner.ts generateManifest() wrote:

name: inline-workflow
version: 1.0.0
dependencies:
  apm:
    - microsoft/...

with no harness signal. Pre-v0.12.3 APM silently defaulted to copilot; v0.12.3 (microsoft/apm#1154) requires an explicit signal — apm.yml target:, --target, or an on-disk marker like .github/copilot-instructions.md — and exits 2 otherwise.

The action's target: input was only consumed by the pack/bundle step, never by isolated install.

Fix

  • generateManifest() now accepts an optional target and writes a target: <value> line into the generated apm.yml when set.
  • Call site reads core.getInput('target') and forwards it.
  • When target: input is empty, manifest is unchanged (back-compat — workflows that rely on an on-disk harness marker keep working).
  • action.yml: expanded target: input description to cover the install-time use, list all supported harnesses (copilot/vscode/claude/cursor/codex/opencode/gemini/windsurf, also accepts CSV), and note the APM v0.12.3+ requirement.

Tests

src/__tests__/runner.test.ts — two new cases under the existing isolated-mode block:

  1. writes target into generated apm.yml when target input set (isolated mode) — asserts the generated apm.yml contains ^target: claude$ when the workflow passes target: claude.
  2. omits target line when target input not set (isolated mode) — asserts no spurious target: line is written when the input is empty (back-compat).

Local validation (mirrors CI):

npm run lint        # silent
npm run typecheck   # silent
npm test            # 99 passed (4 suites), incl. the 2 new cases
npm run build       # dist/index.js regenerated and committed

Trade-offs

  • Scope: only isolated mode is touched. Non-isolated mode reads an existing apm.yml from the checkout, which should already declare its target — passing --target there would be redundant and risk overriding the project's intent.
  • No new input: reusing the existing target: input keeps the surface flat. The input was already documented for pack; we now also document its install-time meaning.

How to test

After merge, re-run the previously failing job in microsoft/apm: https://github.com/microsoft/apm/actions/runs/25484800900/job/74779405094 — it should resolve and install without the No harness detected error.

Or locally, with the rebuilt dist/:

- uses: microsoft/apm-action@fix/isolated-target-in-manifest
  with:
    isolated: true
    dependencies: microsoft/apm-sample-package
    target: claude

The generated apm.yml will include target: claude, and apm install will deploy to .claude/.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

APM v0.12.3 (microsoft/apm#1154) introduced strict harness detection:
`apm install` exits 2 with "No harness detected" when there is no
target signal (apm.yml `target:`, --target flag, or on-disk marker
like .github/copilot-instructions.md).

The action's isolated mode generates apm.yml from inline `dependencies`
input but never propagated the workflow's `target:` input into the
manifest. Result: every gh-aw / inline-deps workflow upgrading past
v0.12.3 broke with exit 2.

Fix: when `target:` input is set, write `target: <value>` into the
generated apm.yml. Manifest stays unchanged when target is not set so
callers that rely on on-disk markers keep working.

Also expands the `target:` input description in action.yml to cover
the install-time use, lists all supported harnesses, and notes the
APM v0.12.3+ requirement.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 7, 2026 09:52
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes isolated-mode installs for APM v0.12.3+ by persisting the action’s target input into the generated apm.yml, ensuring apm install has an explicit harness signal.

Changes:

  • Read target input in isolated mode and write target: <value> into the generated apm.yml when provided.
  • Extend isolated-mode tests to cover both “target present” and “target absent” manifest generation.
  • Update action.yml input description and regenerate dist/index.js.
Show a summary per file
File Description
src/runner.ts Injects optional target into generated apm.yml during isolated installs and logs it.
src/__tests__/runner.test.ts Adds regression tests verifying target: is written/omitted correctly in isolated mode.
dist/index.js Compiled output reflecting the runner changes.
action.yml Documents target as both a pack-time and isolated-install-time harness signal.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 3/4 changed files
  • Comments generated: 1

Comment thread src/runner.ts
Comment on lines +567 to +571
const targetLine = target ? `target: ${target}\n` : '';
const content = `name: inline-workflow\nversion: 1.0.0\n${targetLine}dependencies:\n apm:\n${depEntries.join('\n')}\n`;
fs.writeFileSync(apmYmlPath, content, 'utf-8');
core.info(`Generated apm.yml with ${deps.length} dependencies (isolated mode)`);
const targetSuffix = target ? ` (target: ${target})` : '';
core.info(`Generated apm.yml with ${deps.length} dependencies (isolated mode)${targetSuffix}`);
Two related changes prompted by the v0.12.3 strict-detection rollout:

1. Pin the action's default `apm-version` from `latest` to `0.12.3`.
   Floating on `latest` means every APM release ships into every
   downstream workflow without review -- exactly how v0.12.3 broke
   gh-aw and apm-action's own CI in the same hour. The new default
   gives users a known-good baseline and forces an explicit, reviewed
   bump (or an explicit `apm-version: latest` opt-in) for upgrades.

2. Add `target:` to every CI test fixture (apm.yml + isolated mode
   `with:` blocks). v0.12.3 requires an explicit harness signal --
   without it `apm install` exits 2 with "No harness detected". The
   action now correctly propagates the input into isolated-mode
   manifests; the test workflows just needed to declare a target
   like any real consumer would.

Both action.yml input description and the existing tests in
runner.test.ts already cover the new behaviour. dist/ unchanged
(action.yml + workflow files only).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel
Copy link
Copy Markdown
Collaborator Author

Pushed c5372d5 to address CI failures and add the version-pinning hardening you asked for.

Two changes in this follow-up commit:

  1. Pin apm-version default from latest to 0.12.3 in action.yml. Floating on latest is exactly what made v0.12.3's strict-detection change ship into every gh-aw workflow (and into this action's own CI) without review. New default gives a known-good baseline; users opt in to floating with apm-version: latest, or pin to a different tag for reproducibility.

  2. Declare target: in every CI test fixture (ci.yml, test-multibundle.yml, test-action.yml) — both in apm.yml files and in isolated-mode with: blocks. The CI failures (test-isolated, test-manifest, test-pack, test-restore-*, test-compile, test-setup-only, Pack bundle (alpha/beta/gamma)) all hit exit 2 / No harness detected because the fixtures predated the v0.12.3 contract. Now they exercise the new contract correctly — including isolated mode, which validates the runtime fix in this PR end-to-end.

dist/ is unchanged (only action.yml + workflow files touched). 99/99 unit tests still pass locally.

danielmeppiel and others added 2 commits May 7, 2026 11:59
v0.12.3 dropped the 'vscode' target alias. Valid targets are now:
agent-skills, claude, codex, copilot, cursor, gemini, opencode,
windsurf. The 'copilot' target deploys to .github/ -- same outcome
as the old 'vscode' alias, just under its canonical name.

Updates pack/restore CI fixtures and trims 'vscode' from the
`target:` input description in action.yml.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Two assertions in ci.yml predated v0.12.3 and no longer reflect the
shipped behaviour:

1. Skills under the `copilot` target now deploy to `.agents/skills/`
   (universal AGENTS-style fallback) rather than `.github/skills/`.
   Other primitive types (agents, instructions, prompts) still land
   under `.github/`. Update the isolated and setup-only checks to
   look for the skill at the new path.

2. The `plugin` bundle format now ships `apm.lock.yaml` alongside
   `plugin.json` (lockfile is needed to reproduce the deployed set
   when the plugin is restored). Drop the obsolete "plugin must NOT
   contain apm.lock.yaml" assertion; keep the positive plugin.json
   presence check.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel danielmeppiel merged commit 6c6327f into main May 7, 2026
20 checks passed
danielmeppiel added a commit that referenced this pull request May 7, 2026
…#36)

Mirror of #33's fix for the second install path. Isolated mode writes
`target:` into the generated apm.yml; the non-isolated additive path
(`apm install <dep>` per inline dep, no apm.yml present) had no such
signal and so APM v0.12.3+ strict harness detection rejected with
exit code 2 ("No harness detected") on every workspace without an
on-disk marker like `.github/copilot-instructions.md`.

`installDeps` now accepts the validated target and appends
`--target <value>` to every `apm install <dep>` call. The bare
`apm install` (apm.yml-driven) path is intentionally left alone -
the project's apm.yml is the source of truth for that case, and
overriding it from the action input would be surprising.

Caught by test-action.yml's `Install with inline dependencies
(additive)` job on the v1.7.0 tag run.

Regression test asserts the per-dep call sequence is
`apm install <pkg> --target <value>`. 111/111 unit tests pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants