diff --git a/pkg/workflow/compiler_yaml.go b/pkg/workflow/compiler_yaml.go index 41261c24ec..30b20cdbe7 100644 --- a/pkg/workflow/compiler_yaml.go +++ b/pkg/workflow/compiler_yaml.go @@ -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:] } 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) diff --git a/pkg/workflow/compiler_yaml_test.go b/pkg/workflow/compiler_yaml_test.go index 0984b6fd57..ea02084def 100644 --- a/pkg/workflow/compiler_yaml_test.go +++ b/pkg/workflow/compiler_yaml_test.go @@ -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) + } + + // 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) + } + }) + } +}