Skip to content

fix: upload_assets looks in RUNNER_TEMP but artifact is downloaded to /tmp#40062

Merged
pelikhan merged 2 commits into
mainfrom
copilot/investigate-image-upload-issue
Jun 18, 2026
Merged

fix: upload_assets looks in RUNNER_TEMP but artifact is downloaded to /tmp#40062
pelikhan merged 2 commits into
mainfrom
copilot/investigate-image-upload-issue

Conversation

Copilot AI commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

The upload_assets job downloads the safe-outputs-assets artifact to /tmp/gh-aw/safeoutputs/assets/, but upload_assets.cjs constructed the lookup path from RUNNER_TEMP (/home/runner/work/_temp on Actions runners) — causing all assets to appear missing and the job to fail.

Changes

  • upload_assets.cjs: derive assetsDir base from path.dirname(GH_AW_AGENT_OUTPUT) (already set to /tmp/gh-aw/agent_output.json in the job), falling back to RUNNER_TEMP when unset
// before
const assetsDir = path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw", "safeoutputs", "assets");

// after — follows the actual artifact download location
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
const baseDir = agentOutputFile ? path.dirname(agentOutputFile) : path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw");
const assetsDir = path.join(baseDir, "safeoutputs", "assets");
  • upload_assets.test.cjs: replace ad-hoc RUNNER_TEMP-based asset paths with a per-test tempBase dir created in beforeEach, so asset paths in tests are always co-located with GH_AW_AGENT_OUTPUT — matching the new production layout

pr-sous-chef: requested branch refresh for run https://github.com/github/gh-aw/actions/runs/27762885695

Generated by 👨‍🍳 PR Sous Chef ·

…in upload_assets

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title fix: derive asset dir from GH_AW_AGENT_OUTPUT instead of RUNNER_TEMP in upload_assets fix: upload_assets looks in RUNNER_TEMP but artifact is downloaded to /tmp Jun 18, 2026
Copilot AI requested a review from pelikhan June 18, 2026 13:19
@pelikhan pelikhan marked this pull request as ready for review June 18, 2026 13:19
Copilot AI review requested due to automatic review settings June 18, 2026 13:19
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #40062 does not have the 'implementation' label and has 0 new lines of code in business logic directories (≤100 threshold).

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 the upload_assets post-processing job by aligning upload_assets.cjs’s asset lookup directory with the actual artifact download location (co-located with GH_AW_AGENT_OUTPUT), and updates the associated tests to use a per-test temp base directory that mirrors the production layout.

Changes:

  • Derive assetsDir from path.dirname(GH_AW_AGENT_OUTPUT) (with a fallback) instead of assuming RUNNER_TEMP.
  • Update upload_assets.test.cjs to create assets under a per-test temp directory aligned with the agent output location.
Show a summary per file
File Description
actions/setup/js/upload_assets.cjs Fixes asset discovery by basing safeoutputs/assets on the agent output parent directory.
actions/setup/js/upload_assets.test.cjs Adjusts tests to create assets alongside GH_AW_AGENT_OUTPUT in a per-test temp base directory.

Copilot's findings

Tip

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

  • Files reviewed: 2/2 changed files
  • Comments generated: 1

Comment on lines +93 to +95
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
const baseDir = agentOutputFile ? path.dirname(agentOutputFile) : path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw");
const assetsDir = path.join(baseDir, "safeoutputs", "assets");
@github-actions github-actions Bot mentioned this pull request Jun 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 100/100 — Excellent

Analyzed 6 modified test(s): 6 design, 0 implementation, 0 guideline violation(s). Test infrastructure was correctly refactored to model the production fix (asset dir derived from GH_AW_AGENT_OUTPUT instead of RUNNER_TEMP).

📊 Metrics & Test Classification (6 tests analyzed)
Metric Value
New/modified tests analyzed 6
✅ Design tests (behavioral contracts) 6 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 6 (100%)
Duplicate test clusters 0
Test inflation detected No (1.43:1 ratio — within 2:1 limit)
🚨 Coding-guideline violations 0
Test File Classification Issues Detected
should not wrap commit message in extra quotes upload_assets.test.cjs ✅ Design
should normalize branch names correctly upload_assets.test.cjs ✅ Design
should allow creating orphaned branch with assets/ prefix upload_assets.test.cjs ✅ Design
should fail when creating orphaned branch without assets/ prefix upload_assets.test.cjs ✅ Design
should allow using existing branch regardless of prefix upload_assets.test.cjs ✅ Design
should skip missing assets while uploading present assets upload_assets.test.cjs ✅ Design

Go: 0 (*_test.go); JavaScript: 6 (*.test.cjs). No other languages detected.

Scoring breakdown:

Component Score
Behavioral Coverage (40 pts) 40
Error/Edge Case Coverage (30 pts) 30
Low Duplication (20 pts) 20
Proportional Growth (10 pts) 10
Total 100

Verdict

Check passed. 0% implementation tests (threshold: 30%). The test refactoring accurately mirrors the production fix: setAgentOutput() now places agent_output.json inside a shared tempBase dir so GH_AW_AGENT_OUTPUT points there, and getAssetsDir() returns tempBase/safeoutputs/assets — exactly matching how production derives assetsDir = path.join(path.dirname(agentOutputFile), "safeoutputs", "assets").

🧪 Test quality analysis by Test Quality Sentinel ·

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 100/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

COMMENT — Fix is correct; two non-blocking maintainability concerns.

Review themes

Implicit path coupling in baseDir derivation

The new logic extracts the asset directory from path.dirname(GH_AW_AGENT_OUTPUT), creating a structural dependency: it only works correctly if safeoutputs/assets/ is a direct sibling of agent_output.json. No validation or clear error distinguishes a path mismatch from genuinely absent assets — both produce per-item warnings and a silently low upload_count. See inline comment on upload_assets.cjs:94.

RUNNER_TEMP fallback is now untested

The old "from RUNNER_TEMP" test specifically exercised the fallback path. The replacement test only exercises the GH_AW_AGENT_OUTPUT branch. The fallback code is live but untested. See inline comment on upload_assets.test.cjs:144.

🔎 Code quality review by PR Code Quality Reviewer

// is downloaded to the same parent directory as agent_output.json, which may
// differ from RUNNER_TEMP when the download path is explicitly set to /tmp/gh-aw/.
const agentOutputFile = process.env.GH_AW_AGENT_OUTPUT;
const baseDir = agentOutputFile ? path.dirname(agentOutputFile) : path.join(process.env.RUNNER_TEMP || "/tmp", "gh-aw");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fragile path derivation — wrong GH_AW_AGENT_OUTPUT location silently breaks all asset uploads: if the env var ever points to a path more than one level below the artifact root, baseDir will resolve to the wrong directory and every asset will appear missing, with no error distinguishing this failure from "no assets found".

💡 Details and suggestion

The fix works today because GH_AW_AGENT_OUTPUT is set to /tmp/gh-aw/agent_output.json, so path.dirname() correctly yields /tmp/gh-aw/. But this is an implicit structural contract: the code assumes safeoutputs/assets/ is always a direct sibling of agent_output.json. Anything that changes the download layout (a nested subdirectory, a renamed artifact path, a self-hosted runner with a non-standard workspace) will silently break uploads.

A more robust alternative is a dedicated env var, or at minimum a log warning when the resolved assetsDir does not exist:

// Or at minimum: log clearly when the dir is missing
if (!fs.existsSync(assetsDir)) {
  core.warning(`Assets directory not found: ${assetsDir} — verify GH_AW_AGENT_OUTPUT path layout`);
}

The per-item "missing asset" warning is too granular to diagnose a path resolution bug in CI logs.

}));
describe("missing asset handling", () => {
it("should skip missing assets while uploading present assets from RUNNER_TEMP", async () => {
it("should skip missing assets while uploading present assets", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

RUNNER_TEMP fallback path is now untested: the renamed test only exercises the GH_AW_AGENT_OUTPUT-derived path; there is no test that verifies the fallback branch (process.env.RUNNER_TEMP || '/tmp') still works correctly.

💡 Details and suggestion

The old test ("should skip missing assets while uploading present assets from RUNNER_TEMP") explicitly set process.env.RUNNER_TEMP and verified asset lookup through that path. The new test removes that and only exercises the GH_AW_AGENT_OUTPUT branch.

The fallback is the code path that runs when GH_AW_AGENT_OUTPUT is unset — a misconfigured job, a manual invocation, or any environment that doesn't set that variable. Any regression in the fallback will now go undetected.

Suggest adding a dedicated test like:

it('should derive asset dir from RUNNER_TEMP when GH_AW_AGENT_OUTPUT is not set', async () => {
  const runnerTempDir = fs.mkdtempSync(path.join('/tmp', 'runner-temp-'));
  process.env.RUNNER_TEMP = runnerTempDir;
  delete process.env.GH_AW_AGENT_OUTPUT;

  const assetDir = path.join(runnerTempDir, 'gh-aw', 'safeoutputs', 'assets');
  fs.mkdirSync(assetDir, { recursive: true });
  // ... write a test asset and verify upload ...

  fs.rmSync(runnerTempDir, { recursive: true, force: true });
  delete process.env.RUNNER_TEMP;
});

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnose and /tdd — no blocking issues; changes are a clean, well-scoped fix.

📋 Key Themes & Highlights

Key Themes

  • Root cause correctly addressed: the path derivation now follows GH_AW_AGENT_OUTPUT instead of RUNNER_TEMP, which is exactly where the artifact lands at runtime.
  • Fallback is dead code: because loadAgentOutput() returns {success: false} when GH_AW_AGENT_OUTPUT is unset and main() returns early, line 94's ternary false-branch is unreachable in practice. The existing review comment on line 95 covers this well — consider a path.resolve() and/or removing the fallback to make the invariant explicit.
  • Test isolation significantly improved: migrating from manual RUNNER_TEMP env manipulation to per-test mkdtempSync + rmSync(recursive) in afterEach is a meaningful quality improvement — no more global state leakage between tests.

Positive Highlights

  • ✅ Minimal blast radius: 2 files, +17/-12 lines, no API surface changes
  • ✅ Test rename removes the RUNNER_TEMP implementation detail from the test name — good spec hygiene
  • tempFilePath cleanup in afterEach is correctly handled by deleting the whole tempBase dir, avoiding double-unlink

One nit

  • The fs.existsSync(assetDir) || guard in the missing-asset test is redundant (see inline comment) — minor clarity issue only.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

Comment thread actions/setup/js/upload_assets.test.cjs
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions

Copy link
Copy Markdown
Contributor

``
pr-sous-chef: branch refresh requested.
Please rerun checks and summarize any remaining blockers.

Generated by 👨‍🍳 PR Sous Chef ·

Copilot AI commented Jun 18, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Ran pr-finisher triage on HEAD 717be57.

  • Reviews: no new @copilot-targeted actionable requests in this pass.
  • Checks (local): not re-run because no code changes were made in this pass.
  • Checks (CI): latest branch-refresh runs are action_required with zero failed jobs; CI for current HEAD still needs maintainer re-trigger.
  • Mergeable: PR remains open and not draft.

Hand-off: please re-trigger CI from a maintainer action (close/reopen PR, workflow_dispatch, or maintainer push) before merge.

@pelikhan pelikhan merged commit ae0eb9b into main Jun 18, 2026
14 checks passed
@pelikhan pelikhan deleted the copilot/investigate-image-upload-issue branch June 18, 2026 14:05
dsyme added a commit that referenced this pull request Jun 18, 2026
…IR (#40122)

* fix(upload_assets): search all candidate staging dirs to resolve path-prefix mismatch (#39885)

The upload_assets (Push assets) job was failing with
  ERR_SYSTEM: Asset file not found: .../safeoutputs/assets/<file>.png
even though the agent job succeeded, because assets were staged under a
different base prefix than the job was reading from.

Prior fixes (#39900, #40062) aligned individual paths, but the consumer
still derived a single assetsDir — so any remaining producer/consumer
prefix disagreement (e.g. RUNNER_TEMP=/home/runner/work/_temp vs the
artifact download path /tmp/gh-aw) still hard-failed the whole job.

Fix: build a de-duplicated list of candidate staging directories
  1. parent of GH_AW_AGENT_OUTPUT (where the artifact was downloaded)
  2. RUNNER_TEMP/gh-aw (where the MCP handler staged the file at runtime)
  3. /tmp/gh-aw (canonical fallback)

The first candidate that contains the file wins. The existing fail-soft
behaviour (warn per missing file, only fail when all are missing) is
preserved. The missing-file warning now lists every directory searched.

Adds a regression test for the cross-prefix case.

Closes #39885

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* refactor(upload_assets): read single GH_AW_ASSETS_DIR instead of searching

Replace the multi-candidate staging-dir search with a single source of
truth. The download-artifact step in the upload_assets job writes the
safe-outputs assets to a fixed path, and the Go generator now passes that
exact path to the consumer via GH_AW_ASSETS_DIR. Add constants.TmpGhAwAssetsDir
so the download path and the env var can never drift.

This removes the GH_AW_AGENT_OUTPUT-derived path indirection that caused the
phantom-asset failures (#39885): GH_AW_AGENT_OUTPUT belongs to a separate
artifact and its directory is decoupled from where assets are downloaded.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+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.

3 participants