Skip to content

fix: write full repo-root-relative path in source field when fallback path resolution is used#28342

Merged
pelikhan merged 4 commits intomainfrom
copilot/fix-gh-aw-add-source-path
Apr 24, 2026
Merged

fix: write full repo-root-relative path in source field when fallback path resolution is used#28342
pelikhan merged 4 commits intomainfrom
copilot/fix-gh-aw-add-source-path

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 24, 2026

gh aw add writes a short-form source: path when the workflow is fetched via fallback path resolution (e.g. owner/repo/my-workflow.md@SHA instead of owner/repo/.github/workflows/my-workflow.md@SHA), causing gh aw update to 404.

Root cause

fetchRemoteWorkflow tries the parsed spec path first, then falls back to .github/workflows/<name>.md. The actual resolved path is stored in FetchedWorkflow.SourcePath, but buildSourceStringWithCommitSHA was called with workflowSpec.WorkflowPath (the original parsed path), discarding the fallback resolution. The short-form path was also being used for downstream include/import/dependency resolution, causing incorrect relative-path computation for sibling files.

Changes

  • add_command.go: After detecting a fallback-resolved path, reassign workflowSpec to use SourcePath so all downstream processing — buildSourceStringWithCommitSHA, processIncludesWithWorkflowSpec, and includeSourceDir — uses the canonical repo-root-relative path.

  • trial_repository.go: Normalize parsedSpec.WorkflowPath to fetched.SourcePath before the source-field addition block, so that buildSourceStringWithCommitSHA and the subsequent fetchAllRemoteDependencies call both use the correct path.

// Normalize the spec to the actual fetched path before all downstream processing
if !fetched.IsLocal && fetched.SourcePath != "" && fetched.SourcePath != parsedSpec.WorkflowPath {
    specCopy := *parsedSpec
    specCopy.WorkflowPath = fetched.SourcePath
    parsedSpec = &specCopy
}
  • add_command_test.go: Added TestAddWorkflowWithTracking_UsesActualFetchedPath — sets up a minimal git repo, invokes addWorkflowWithTracking with a ResolvedWorkflow where SourceInfo.SourcePath (.github/workflows/my-workflow.md) differs from Spec.WorkflowPath (my-workflow.md), and asserts the written source: field contains the full path.

  • add_command_test.go: Added TestAddWorkflowWithTracking_SourceFieldVariants — a table-driven test covering three additional combinations:

    • Local workflow (IsLocal=true): verifies no source: field is written
    • Remote, no fallback (SourcePath == WorkflowPath): verifies the source: field uses the already-canonical path unchanged
    • Self-referential remote with fallback: RepoSlug matches the "current" repository and fallback path resolution is triggered; verifies the source: field uses the full .github/workflows/ path and not the short-form

@github-actions

This comment has been minimized.

1 similar comment
@github-actions
Copy link
Copy Markdown
Contributor

Hey @Copilot 👋 — great start on fixing the gh aw add short-form source path bug! The issue description is detailed and the bug is clearly understood. A couple of things to address before this is ready for review:

  • No code changes yet — the PR currently has only an "Initial plan" commit with zero file changes. The actual fix (ensuring gh aw add writes the full .github/workflows/-prefixed path into the source: field) still needs to be implemented.
  • Tests missing — once the fix lands, please include test coverage for the corrected source: path generation and ideally a regression test for gh aw update resolving cross-repo paths correctly.

When you're ready to implement, here's a prompt you can use:

Fix the bug where `gh aw add` writes a short-form `source:` path that omits the `.github/workflows/` prefix, causing `gh aw update` to 404.

Steps:
1. Locate the code in the `gh aw add` command that constructs the `source:` field in the installed workflow file.
2. Update the path-construction logic to always emit the full repo-root-relative path (e.g. `my-org/shared-workflows/.github/workflows/my-workflow.md@<sha>`) instead of the short form (`my-org/shared-workflows/my-workflow.md@<sha>`).
3. Apply the same fix to any `\{\\{\#import}}` path expansion that suffers the same truncation.
4. Add unit tests covering:
   - `gh aw add` generates a fully-qualified `source:` path including `.github/workflows/`
   - `gh aw update` successfully resolves a `source:` path that was written by the fixed `gh aw add`
5. Run `make test-unit` and `make lint-errors` to verify all checks pass.

Generated by Contribution Check · ● 2.2M ·

When gh aw add fetches a remote workflow via a fallback path (e.g.
.github/workflows/my-workflow.md instead of the short-form
my-workflow.md), FetchedWorkflow.SourcePath holds the actual
repo-root-relative path. Use it so gh aw update can re-fetch from
the correct location, preventing 404 errors.

Fixes the bug where source: owner/repo/my-workflow.md@SHA was written
instead of source: owner/repo/.github/workflows/my-workflow.md@SHA.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/63414f90-ba8f-47d9-a62c-e532d598b2dc

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix gh aw add short-form source path to prevent update errors fix: write full repo-root-relative path in source field when fallback path resolution is used Apr 24, 2026
Copilot AI requested a review from pelikhan April 24, 2026 21:33
@pelikhan pelikhan marked this pull request as ready for review April 24, 2026 21:40
Copilot AI review requested due to automatic review settings April 24, 2026 21:40
@github-actions github-actions Bot mentioned this pull request Apr 24, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 80/100

Excellent test quality

Metric Value
New/modified tests analyzed 1
✅ Design tests (behavioral contracts) 1 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 1 (100%)
Duplicate test clusters 0
Test inflation detected ⚠️ Yes (7.5:1 ratio)
🚨 Coding-guideline violations None

Test Classification Details

Test File Classification Issues Detected
TestAddWorkflowWithTracking_UsesActualFetchedPath pkg/cli/add_command_test.go:356 ✅ Design Test inflation (75 test lines vs. 10 production lines); otherwise high quality

Test Inflation Note

The test adds 75 lines while the corresponding production file (add_command.go) adds only 10 lines (7.5:1 ratio, threshold 2:1). This triggers the inflation penalty, but in this case the extra lines are justified: the test requires a real git repo environment (via exec.Command("git", "init")), directory scaffolding, and multiple struct setups to exercise the end-to-end code path. No action needed — this is a legitimate case where the test harness is necessarily more complex than the fix itself.


Test Quality Highlights

The single new test is a strong behavioral contract test:

  • What it enforces: When a remote workflow is fetched via a fallback path (.github/workflows/my-workflow.md) but the spec originally contained the short-form path (my-workflow.md), the written source: field must use the full repo-root-relative path — not the short form that would cause a 404 on gh aw update.
  • Positive assertion (assert.Contains): verifies the correct full path is written.
  • Negative assertion (assert.NotContains): explicitly guards against regression to the broken short-form path. This dual assertion pattern is particularly valuable for a fix like this.
  • Real components: Uses actual git init, filesystem writes, and calls addWorkflowWithTracking directly — no mocks.
  • Assertion messages: All assertions include descriptive messages ✅
  • Build tag: //go:build !integration present on line 1 ✅

Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 1 test — unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). The test directly verifies the behavioral fix introduced in this PR with both a positive and negative assertion.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

References: §24913143079

🧪 Test quality analysis by Test Quality Sentinel · ● 392K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 80/100. Test quality is excellent — 0% of new tests are implementation tests (threshold: 30%). The single new test enforces a clear behavioral contract with both positive and negative assertions on the source: field fix.

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

Fixes incorrect source: frontmatter for workflows fetched via fallback path resolution so subsequent gh aw update calls don’t 404.

Changes:

  • Use the fetched workflow’s resolved SourcePath (repo-root-relative) when building the source: string for remote workflows in both normal add and trial install flows.
  • Add a unit test ensuring the written source: field reflects the actual fetched path when fallback resolution occurred.
Show a summary per file
File Description
pkg/cli/add_command.go Builds source: from the effective (resolved) workflow path when remote fallback resolution is used.
pkg/cli/trial_repository.go Applies the same effective-path source: behavior in trial-mode installs.
pkg/cli/add_command_test.go Adds a test asserting the source: field uses the resolved .github/workflows/... path rather than the short-form path.

Copilot's findings

Tip

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

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

Comment thread pkg/cli/add_command.go Outdated
specCopy.WorkflowPath = sourceInfo.SourcePath
effectiveSpec = &specCopy
}
sourceString := buildSourceStringWithCommitSHA(effectiveSpec, commitSHA)
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

SourcePath is used to build the source: field, but when fallback resolution happens the rest of this function still uses the original workflowSpec.WorkflowPath (e.g. for fetchAllRemoteDependencies above and processIncludesWithWorkflowSpec below). Those codepaths resolve relative include/import/dispatch/resource paths based on WorkflowPath, so keeping the short-form path can cause incorrect dependency resolution (treating siblings as repo-root files instead of .github/workflows/...). Consider propagating the effective path to downstream processing as well (e.g. update workflowSpec.WorkflowPath for the remainder of the function or pass the same effectiveSpec to dependency/include processing).

Suggested change
sourceString := buildSourceStringWithCommitSHA(effectiveSpec, commitSHA)
// Propagate the effective path for the remainder of this function so any
// downstream relative include/import resolution uses the same canonical path
// that was used to build the source field.
workflowSpec = effectiveSpec
sourceString := buildSourceStringWithCommitSHA(workflowSpec, commitSHA)

Copilot uses AI. Check for mistakes.
Comment thread pkg/cli/trial_repository.go Outdated
Comment on lines +253 to +263
// When the fetch used a fallback path (e.g. .github/workflows/my-workflow.md
// instead of the short-form my-workflow.md), SourcePath holds the actual
// repo-root-relative path. Use it so that gh aw update can later re-fetch
// from the correct location.
effectiveSpec := parsedSpec
if fetched.SourcePath != "" && fetched.SourcePath != parsedSpec.WorkflowPath {
specCopy := *parsedSpec
specCopy.WorkflowPath = fetched.SourcePath
effectiveSpec = &specCopy
}
sourceString := buildSourceStringWithCommitSHA(effectiveSpec, fetched.CommitSHA)
Copy link

Copilot AI Apr 24, 2026

Choose a reason for hiding this comment

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

This updates the source: field to use fetched.SourcePath when fallback resolution is used, but subsequent calls (e.g. remote dependency fetching later in this function) still pass parsedSpec with the original short-form WorkflowPath. Several dependency resolvers use spec.WorkflowPath to compute the base directory, so fallback resolution can still lead to incorrect relative path resolution for includes/imports/dispatch workflows/resources. Consider updating parsedSpec.WorkflowPath (or using the same effectiveSpec) for the remainder of the trial install flow when fetched.SourcePath differs.

See below for a potential fix:

	// When the fetch used a fallback path (e.g. .github/workflows/my-workflow.md
	// instead of the short-form my-workflow.md), SourcePath holds the actual
	// repo-root-relative path. Normalize parsedSpec so any later dependency
	// resolution in this function uses the same effective workflow path.
	if fetched.SourcePath != "" && fetched.SourcePath != parsedSpec.WorkflowPath {
		specCopy := *parsedSpec
		specCopy.WorkflowPath = fetched.SourcePath
		parsedSpec = &specCopy
	}

	// Add source field to frontmatter for remote workflows
	if !fetched.IsLocal && fetched.CommitSHA != "" {
		sourceString := buildSourceStringWithCommitSHA(parsedSpec, fetched.CommitSHA)

Copilot uses AI. Check for mistakes.
@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments, add tests

…ter fallback resolution

Per review feedback: after normalizing the spec to use SourcePath when
fallback resolution was triggered, reassign workflowSpec (add_command.go)
and parsedSpec (trial_repository.go) so that processIncludesWithWorkflowSpec,
includeSourceDir, and fetchAllRemoteDependencies all use the canonical
repo-root-relative path.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/c712c321-55d8-43f3-b219-f383a9e07caa

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot review all comments, add tests

Addressed both reviewer suggestions in commit 20eb234:

  • add_command.go: Changed from a local effectiveSpec variable to directly reassigning workflowSpec, so processIncludesWithWorkflowSpec, includeSourceDir, and all other uses after the normalization point automatically pick up the canonical path.
  • trial_repository.go: Moved the parsedSpec normalization to before the if !fetched.IsLocal && fetched.CommitSHA != "" block, so fetchAllRemoteDependencies (called later on line 292) also uses the correct repo-root-relative path.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/actions/github-script/git/ref/tags/v9
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot add more tests to cover combinations of local and remote specs within the same workflow, including a remote spec that references the current repository.

…mote, self-referential remote

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/52476730-c034-41e8-a1cb-3ac9d29cfe0f

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 24, 2026

@copilot add more tests to cover combinations of local and remote specs within the same workflow, including a remote spec that references the current repository.

Added TestAddWorkflowWithTracking_SourceFieldVariants (table-driven) in commit 8c14845 covering:

  • Local workflow (IsLocal=true) — verifies no source: field is written
  • Remote, no fallbackSourcePath == WorkflowPath — verifies source field uses the original canonical path as-is
  • Self-referential remoteRepoSlug matches the "current" repo with fallback path resolution — verifies the full .github/workflows/ path appears in source: and the short-form does not

Also extracted a setupMinimalGitRepo helper to avoid duplicating git init/config boilerplate across the three tests.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/repos/actions/github-script/git/ref/tags/v9
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq [.object.sha, .object.type] | @tsv GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE npx (http block)

If you need me to access, download, or install something from one of these locations, you can either:

@pelikhan pelikhan merged commit 68128a6 into main Apr 24, 2026
@pelikhan pelikhan deleted the copilot/fix-gh-aw-add-source-path branch April 24, 2026 22:52
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.

[Bug] gh aw add writes short-form source: path that breaks gh aw update for cross-repo workflows

3 participants