Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions pkg/workflow/compiler_yaml.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,23 +320,24 @@ func (c *Compiler) generatePrompt(yaml *strings.Builder, data *WorkflowData) {
// For a workflow at ".github/workflows/test.md", the runtime-import path should be ".github/workflows/test.md"
// This makes the path explicit and matches the actual file location in the repository
var workflowFilePath string
if strings.Contains(c.markdownPath, ".github") {

// Normalize path separators first to handle both Unix and Windows paths consistently
normalizedPath := filepath.ToSlash(c.markdownPath)

// Look for "/.github/" as a directory (not just substring in repo name like "username.github.io")
// We need to match the directory component, not arbitrary substrings
githubDirPattern := "/.github/"
githubIndex := strings.Index(normalizedPath, githubDirPattern)

if githubIndex != -1 {
// Extract everything from ".github/" onwards (inclusive)
githubIndex := strings.Index(c.markdownPath, ".github")
if githubIndex != -1 {
workflowFilePath = c.markdownPath[githubIndex:]
} else {
// Fallback
workflowFilePath = workflowBasename
}
// +1 to skip the leading slash, so we get ".github/workflows/..." not "/.github/workflows/..."
workflowFilePath = normalizedPath[githubIndex+1:]
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

The new pattern search only matches paths containing "/.github/" (with a leading slash). If the compiler is invoked with a repository-relative path like ".github/workflows/test.md" (common in CLI flows that glob ".github/workflows/*.md"), githubIndex will be -1 and this will fall back to workflowBasename, producing a runtime-import like {{#runtime-import test.md}} instead of .github/workflows/test.md.

Suggested fix: support .github/ at the start of the normalized path (and optionally ./.github/) in addition to matching /.github/ as a directory boundary (e.g., match (^|/)\.github/ after filepath.ToSlash).

Suggested change
workflowFilePath = normalizedPath[githubIndex+1:]
workflowFilePath = normalizedPath[githubIndex+1:]
} else if strings.HasPrefix(normalizedPath, ".github/") {
// Handle repository-relative paths like ".github/workflows/test.md"
workflowFilePath = normalizedPath
} else if strings.HasPrefix(normalizedPath, "./.github/") {
// Handle repository-relative paths with leading "./", like "./.github/workflows/test.md"
// Strip the leading "./" so the runtime-import path starts with ".github/"
workflowFilePath = normalizedPath[2:]

Copilot uses AI. Check for mistakes.
} else {
// For non-standard paths (like /tmp/test.md), just use the basename
workflowFilePath = workflowBasename
}

// Normalize to Unix paths (forward slashes) for cross-platform compatibility
workflowFilePath = filepath.ToSlash(workflowFilePath)

// Create a runtime-import macro for the main workflow markdown
// The runtime_import.cjs helper will extract and process the markdown body at runtime
// The path uses .github/ prefix for clarity (e.g., .github/workflows/test.md)
Expand Down
109 changes: 109 additions & 0 deletions pkg/workflow/compiler_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1111,3 +1111,112 @@ Test content.`
t.Error("Found ANSI escape sequences in generated YAML file")
}
}

// TestRuntimeImportPathGitHubIO tests that repositories ending with .github.io
// generate correct runtime-import paths without duplicating the .github.io suffix
func TestRuntimeImportPathGitHubIO(t *testing.T) {
tests := []struct {
name string
repoName string // simulated repo name in path
expected string
description string
}{
{
name: "github_pages_repo",
repoName: "testuser.github.io",
expected: "{{#runtime-import .github/workflows/translate-to-ptbr.md}}",
description: "GitHub Pages repo should not duplicate .github.io in runtime-import path",
},
{
name: "another_github_pages_repo",
repoName: "anotheruser.github.io",
expected: "{{#runtime-import .github/workflows/test.md}}",
description: "Another GitHub Pages repo should work correctly",
},
{
name: "normal_repo",
repoName: "myrepo",
expected: "{{#runtime-import .github/workflows/workflow.md}}",
description: "Normal repo without .github.io should work as before",
},
{
name: "repo_with_github_in_name",
repoName: "my-github-project",
expected: "{{#runtime-import .github/workflows/test.md}}",
description: "Repo with 'github' in name should only match .github directory",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create a temporary directory that simulates the repo structure
// with repo name in the path
tmpBase := testutil.TempDir(t, "runtime-import-path-test")
tmpDir := filepath.Join(tmpBase, tt.repoName)

// Create .github/workflows directory
workflowDir := filepath.Join(tmpDir, ".github", "workflows")
if err := os.MkdirAll(workflowDir, 0755); err != nil {
t.Fatalf("Failed to create workflow directory: %v", err)
}

// Determine workflow filename from expected path
expectedParts := strings.Split(tt.expected, " ")
if len(expectedParts) < 2 {
t.Fatalf("Invalid expected format: %s", tt.expected)
}
workflowFilePath := strings.TrimSuffix(expectedParts[1], "}}")
workflowBasename := filepath.Base(workflowFilePath)

// Create a simple workflow file
workflowPath := filepath.Join(workflowDir, workflowBasename)
workflowContent := `---
on: push
engine: copilot
---

# Test Workflow

This is a test workflow.`

if err := os.WriteFile(workflowPath, []byte(workflowContent), 0644); err != nil {
t.Fatalf("Failed to write workflow file: %v", err)
}

// Compile the workflow
compiler := NewCompiler()
if err := compiler.CompileWorkflow(workflowPath); err != nil {
t.Fatalf("%s: Compilation failed: %v", tt.description, err)
Comment on lines +1186 to +1189
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

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

This test only exercises compilation when workflowPath is an absolute path (TempDir + filepath.Join). Since the runtime-import extraction logic depends on how c.markdownPath is formatted, it would be good to add a subtest that compiles the same workflow via a repo-relative path like ".github/workflows/.md" (e.g., os.Chdir(tmpDir) then call CompileWorkflow with the relative path). That would guard against regressions in CLI code paths that pass relative paths.

Copilot uses AI. Check for mistakes.
}

// Calculate lock file path
lockFile := strings.TrimSuffix(workflowPath, ".md") + ".lock.yml"

// Read the generated lock file
lockContent, err := os.ReadFile(lockFile)
if err != nil {
t.Fatalf("Failed to read lock file: %v", err)
}

lockContentStr := string(lockContent)

// Check that the runtime-import path is correct
if !strings.Contains(lockContentStr, tt.expected) {
t.Errorf("%s: Expected to find '%s' in lock file", tt.description, tt.expected)

// Find what runtime-import was actually generated
lines := strings.Split(lockContentStr, "\n")
for _, line := range lines {
if strings.Contains(line, "{{#runtime-import") {
t.Logf("Found runtime-import: %s", strings.TrimSpace(line))
}
}
}

// Also verify that .github.io is NOT duplicated in the path
if strings.Contains(lockContentStr, ".github.io/.github/workflows") {
t.Errorf("%s: Found incorrect path with duplicated .github.io prefix", tt.description)
}
})
}
}
Loading