From 7b277d62e6813b4d1350f810fe5f23f8a76d96b5 Mon Sep 17 00:00:00 2001 From: Don Syme Date: Thu, 18 Jun 2026 11:08:13 +0100 Subject: [PATCH] fix: relocate checkout manifest into safeoutputs/ so containerized safe-outputs MCP can read it The safe-outputs MCP server now runs in a container that only bind-mounts $RUNNER_TEMP/gh-aw/safeoutputs (plus the workspace and /tmp/gh-aw). The checkout manifest was written as a sibling at $RUNNER_TEMP/gh-aw/checkout-manifest.json, so it was invisible inside the container. Manifest-first checkout resolution then fell back to an unreliable git scan and failed with 'Repository / not found in workspace', breaking create_pull_request and push_to_pull_request_branch. Write and read the manifest at $RUNNER_TEMP/gh-aw/safeoutputs/checkout-manifest.json so it lives inside the mounted directory. Fixes #40018 --- ...tch-relocate-checkout-manifest-into-safeoutputs.md | 5 +++++ actions/setup/js/build_checkout_manifest.cjs | 5 ++++- actions/setup/js/build_checkout_manifest.test.cjs | 2 +- actions/setup/js/checkout_manifest.cjs | 11 +++++++---- pkg/workflow/checkout_step_generator.go | 5 +++-- 5 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 .changeset/patch-relocate-checkout-manifest-into-safeoutputs.md diff --git a/.changeset/patch-relocate-checkout-manifest-into-safeoutputs.md b/.changeset/patch-relocate-checkout-manifest-into-safeoutputs.md new file mode 100644 index 00000000000..4f1c6461e0b --- /dev/null +++ b/.changeset/patch-relocate-checkout-manifest-into-safeoutputs.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Fix `create_pull_request` and `push_to_pull_request_branch` failing with `Repository '/' not found in workspace` after the safe-outputs MCP server moved into a container. The checkout manifest is now written to and read from `$RUNNER_TEMP/gh-aw/safeoutputs/checkout-manifest.json` instead of `$RUNNER_TEMP/gh-aw/checkout-manifest.json`, so it lives inside the `safeoutputs/` directory that is bind-mounted into the containerized safe-outputs MCP server. Previously the manifest was a sibling of the mounted directory and therefore invisible inside the container, causing manifest-first checkout resolution to fail. diff --git a/actions/setup/js/build_checkout_manifest.cjs b/actions/setup/js/build_checkout_manifest.cjs index 0e77a571407..3a585eaa3e5 100644 --- a/actions/setup/js/build_checkout_manifest.cjs +++ b/actions/setup/js/build_checkout_manifest.cjs @@ -88,7 +88,10 @@ function buildCheckoutManifest(entries, options = {}) { const runGit = options.runGit; const runGH = options.runGH; - const manifestDir = path.join(runnerTemp, "gh-aw"); + // Write under safeoutputs/ because that subdirectory is the only part of + // $RUNNER_TEMP/gh-aw that is bind-mounted into the containerized safe-outputs + // MCP server, which is where the manifest is read by findRepoCheckout. + const manifestDir = path.join(runnerTemp, "gh-aw", "safeoutputs"); fs.mkdirSync(manifestDir, { recursive: true }); const manifestPath = path.join(manifestDir, "checkout-manifest.json"); const manifest = {}; diff --git a/actions/setup/js/build_checkout_manifest.test.cjs b/actions/setup/js/build_checkout_manifest.test.cjs index 1da95cb1635..a0329dd2653 100644 --- a/actions/setup/js/build_checkout_manifest.test.cjs +++ b/actions/setup/js/build_checkout_manifest.test.cjs @@ -121,7 +121,7 @@ describe("build_checkout_manifest.cjs", () => { } ); - expect(manifestPath).toBe(path.join(runnerTemp, "gh-aw", "checkout-manifest.json")); + expect(manifestPath).toBe(path.join(runnerTemp, "gh-aw", "safeoutputs", "checkout-manifest.json")); expect(manifest).toEqual({ "owner/repo": { repository: "Owner/Repo", diff --git a/actions/setup/js/checkout_manifest.cjs b/actions/setup/js/checkout_manifest.cjs index 903fcdf1b43..5cd92127046 100644 --- a/actions/setup/js/checkout_manifest.cjs +++ b/actions/setup/js/checkout_manifest.cjs @@ -19,9 +19,12 @@ const { getErrorMessage } = require("./error_helpers.cjs"); * authoritative source for resolving the on-disk checkout path and base branch * of cross-repo checkouts without any network access. * - * The default location is $RUNNER_TEMP/gh-aw/checkout-manifest.json. Override - * with GH_AW_CHECKOUT_MANIFEST when running outside of a GitHub Actions runner - * (tests, local dev). + * The default location is $RUNNER_TEMP/gh-aw/safeoutputs/checkout-manifest.json. + * It lives under safeoutputs/ specifically because that subdirectory is the only + * part of $RUNNER_TEMP/gh-aw that is bind-mounted into the containerized + * safe-outputs MCP server; a sibling file at $RUNNER_TEMP/gh-aw/ would be + * invisible inside the container. Override with GH_AW_CHECKOUT_MANIFEST when + * running outside of a GitHub Actions runner (tests, local dev). */ let cached = null; @@ -35,7 +38,7 @@ function resolveManifestPath() { if (!runnerTemp || runnerTemp.trim() === "") { return null; } - return path.join(runnerTemp, "gh-aw", "checkout-manifest.json"); + return path.join(runnerTemp, "gh-aw", "safeoutputs", "checkout-manifest.json"); } function loadManifest() { diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go index 11ef5c81d47..5b75e7ac07a 100644 --- a/pkg/workflow/checkout_step_generator.go +++ b/pkg/workflow/checkout_step_generator.go @@ -73,8 +73,9 @@ func (cm *CheckoutManager) GenerateAdditionalCheckoutSteps(getActionPin func(str // so the safe-outputs MCP server (which runs without credentials) can look up the // base branch without making any network calls. // -// The manifest file lives at $RUNNER_TEMP/gh-aw/checkout-manifest.json. The default -// branch is resolved at runtime via: +// The manifest file lives at $RUNNER_TEMP/gh-aw/safeoutputs/checkout-manifest.json +// (under safeoutputs/ so it is bind-mounted into the containerized safe-outputs MCP +// server). The default branch is resolved at runtime via: // 1. `git symbolic-ref --short refs/remotes/origin/HEAD` on the local checkout // (works when actions/checkout left the remote HEAD set, typical for fetch-depth: 0) // 2. `gh api repos// --jq .default_branch` as a credentialed fallback