Skip to content

Commit f5a2c2a

Browse files
authored
fix: body-level {{#import shared/X.md}} rewritten with wrong cross-repo path in gh aw add (#23817)
1 parent e6633d8 commit f5a2c2a

4 files changed

Lines changed: 454 additions & 30 deletions

File tree

.changeset/patch-fix-body-import-path-resolution.md

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/add_command.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,13 +416,16 @@ func addWorkflowWithTracking(resolved *ResolvedWorkflow, tracker *FileTracker, o
416416
// fetchAndSaveRemoteFrontmatterImports already downloaded those files locally, so
417417
// the compiler can resolve them from disk without any GitHub API calls.
418418

419-
// Process @include directives and replace with workflowspec
420-
// For local workflows, use the workflow's directory as the base path
419+
// Process @include directives and replace with workflowspec.
420+
// For local workflows, use the workflow's directory as the package source path.
421+
// Pass githubWorkflowsDir as localWorkflowDir so that any body-level import
422+
// whose target already exists locally is preserved as a local reference rather
423+
// than being rewritten to a cross-repo workflowspec.
421424
includeSourceDir := ""
422425
if sourceInfo != nil && sourceInfo.IsLocal {
423426
includeSourceDir = filepath.Dir(workflowSpec.WorkflowPath)
424427
}
425-
processedContent, err := processIncludesWithWorkflowSpec(content, workflowSpec, commitSHA, includeSourceDir, opts.Verbose)
428+
processedContent, err := processIncludesWithWorkflowSpec(content, workflowSpec, commitSHA, includeSourceDir, githubWorkflowsDir, opts.Verbose)
426429
if err != nil {
427430
if opts.Verbose {
428431
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Failed to process includes: %v", err)))

pkg/cli/imports.go

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,11 @@ func reconstructWorkflowFileFromMap(frontmatter map[string]any, markdown string)
182182
}
183183

184184
// processIncludesWithWorkflowSpec processes @include directives in content and replaces local file references
185-
// with workflowspec format (owner/repo/path@sha) for all includes found in the package
186-
func processIncludesWithWorkflowSpec(content string, workflow *WorkflowSpec, commitSHA, packagePath string, verbose bool) (string, error) {
187-
importsLog.Printf("Processing @include directives: repo=%s, sha=%s, package=%s", workflow.RepoSlug, commitSHA, packagePath)
185+
// with workflowspec format (owner/repo/path@sha) for all includes found in the package.
186+
// If localWorkflowDir is non-empty, any relative import path whose file exists under that directory is
187+
// left as a local relative path rather than being rewritten to a cross-repo reference.
188+
func processIncludesWithWorkflowSpec(content string, workflow *WorkflowSpec, commitSHA, packagePath, localWorkflowDir string, verbose bool) (string, error) {
189+
importsLog.Printf("Processing @include directives: repo=%s, sha=%s, package=%s, localWorkflowDir=%s", workflow.RepoSlug, commitSHA, packagePath, localWorkflowDir)
188190
if verbose {
189191
fmt.Fprintln(os.Stderr, console.FormatVerboseMessage("Processing @include directives to replace with workflowspec"))
190192
}
@@ -211,6 +213,12 @@ func processIncludesWithWorkflowSpec(content string, workflow *WorkflowSpec, com
211213
isOptional := directive.IsOptional
212214
includePath := directive.Path
213215

216+
// Skip if it's already a workflowspec (owner/repo/path@sha format)
217+
if isWorkflowSpecFormat(includePath) {
218+
result.WriteString(line + "\n")
219+
continue
220+
}
221+
214222
// Handle section references (file.md#Section)
215223
var filePath, sectionName string
216224
if strings.Contains(includePath, "#") {
@@ -230,34 +238,43 @@ func processIncludesWithWorkflowSpec(content string, workflow *WorkflowSpec, com
230238
continue
231239
}
232240

233-
// Check for cycle detection
234-
if visited[filePath] {
235-
if verbose {
236-
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Cycle detected for include: %s, skipping", filePath)))
241+
// Preserve relative {{#import}} paths whose files exist in the local workflow directory.
242+
if localWorkflowDir != "" && !strings.HasPrefix(filePath, "/") {
243+
if isLocalFileForUpdate(localWorkflowDir, filePath) {
244+
importsLog.Printf("Include path exists locally, preserving: %s", filePath)
245+
result.WriteString(line + "\n")
246+
// Add file to queue for processing nested includes (first visit only)
247+
if !visited[filePath] {
248+
visited[filePath] = true
249+
queue = append(queue, fileToProcess{path: filePath})
250+
}
251+
continue
237252
}
238-
continue
239253
}
240254

241-
// Mark as visited
242-
visited[filePath] = true
255+
// Resolve the file path relative to the workflow file's directory
256+
resolvedPath := resolveImportPath(filePath, workflow.WorkflowPath)
243257

244258
// Build workflowspec for this include
245-
workflowSpec := buildWorkflowSpecRef(workflow.RepoSlug, filePath, commitSHA, workflow.Version)
259+
workflowSpec := buildWorkflowSpecRef(workflow.RepoSlug, resolvedPath, commitSHA, workflow.Version)
246260

247261
// Add section if present
248262
if sectionName != "" {
249263
workflowSpec += "#" + sectionName
250264
}
251265

252-
// Write the updated @include directive
266+
// Write the updated @include directive (even for duplicate occurrences)
253267
if isOptional {
254268
result.WriteString("{{#import? " + workflowSpec + "}}\n")
255269
} else {
256270
result.WriteString("{{#import " + workflowSpec + "}}\n")
257271
}
258272

259-
// Add file to queue for processing nested includes
260-
queue = append(queue, fileToProcess{path: filePath})
273+
// Only enqueue for nested-include processing on the first visit to prevent cycles
274+
if !visited[filePath] {
275+
visited[filePath] = true
276+
queue = append(queue, fileToProcess{path: filePath})
277+
}
261278
} else {
262279
// Regular line, pass through
263280
result.WriteString(line + "\n")
@@ -360,7 +377,7 @@ func processIncludesInContent(content string, workflow *WorkflowSpec, commitSHA
360377
isOptional := directive.IsOptional
361378
includePath := directive.Path
362379

363-
// Skip if it's already a workflowspec (contains repo/path format)
380+
// Skip if it's already a workflowspec (owner/repo/path@sha format)
364381
if isWorkflowSpecFormat(includePath) {
365382
result.WriteString(line + "\n")
366383
continue
@@ -385,7 +402,7 @@ func processIncludesInContent(content string, workflow *WorkflowSpec, commitSHA
385402
continue
386403
}
387404

388-
// Preserve relative @include paths whose files exist in the local workflow directory.
405+
// Preserve relative {{#import}} paths whose files exist in the local workflow directory.
389406
if localWorkflowDir != "" && !strings.HasPrefix(filePath, "/") {
390407
if isLocalFileForUpdate(localWorkflowDir, filePath) {
391408
importsLog.Printf("Include path exists locally, preserving: %s", filePath)

0 commit comments

Comments
 (0)