Skip to content

Commit bd72ffb

Browse files
authored
fix: stabilize compiled lock file output ordering (#17927)
1 parent 62a9499 commit bd72ffb

3 files changed

Lines changed: 54 additions & 0 deletions

File tree

pkg/parser/import_topological_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,3 +454,48 @@ tools:
454454
assert.Equal(t, "a-dependency.md", result.ImportedFiles[0])
455455
assert.Equal(t, "z-parent.md", result.ImportedFiles[1])
456456
}
457+
458+
func TestImportTopologicalSortStableAcrossRuns(t *testing.T) {
459+
tempDir := testutil.TempDir(t, "import-topo-stable-*")
460+
461+
files := map[string]string{
462+
"root.md": `---
463+
imports:
464+
- b.md
465+
- a.md
466+
---`,
467+
"a.md": `---
468+
imports:
469+
- c.md
470+
tools:
471+
a-tool: {}
472+
---`,
473+
"b.md": `---
474+
tools:
475+
b-tool: {}
476+
---`,
477+
"c.md": `---
478+
tools:
479+
c-tool: {}
480+
---`,
481+
}
482+
483+
for filename, content := range files {
484+
filePath := filepath.Join(tempDir, filename)
485+
err := os.WriteFile(filePath, []byte(content), 0644)
486+
require.NoError(t, err)
487+
}
488+
489+
frontmatter := map[string]any{"imports": []string{"root.md"}}
490+
491+
var baseline []string
492+
for i := range 5 {
493+
result, err := parser.ProcessImportsFromFrontmatterWithManifest(frontmatter, tempDir, nil)
494+
require.NoError(t, err)
495+
if i == 0 {
496+
baseline = append([]string(nil), result.ImportedFiles...)
497+
continue
498+
}
499+
assert.Equal(t, baseline, result.ImportedFiles)
500+
}
501+
}

pkg/workflow/compiler_activation_jobs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"maps"
88
"slices"
9+
"sort"
910
"strconv"
1011
"strings"
1112

@@ -801,7 +802,12 @@ func (c *Compiler) buildMainJob(data *WorkflowData, activationJobCreated bool) (
801802
// so the agent job gets them transitively through activation
802803
// Custom jobs that depend on agent should run AFTER the agent job, not before it
803804
if data.Jobs != nil {
805+
jobNames := make([]string, 0, len(data.Jobs))
804806
for jobName := range data.Jobs {
807+
jobNames = append(jobNames, jobName)
808+
}
809+
sort.Strings(jobNames)
810+
for _, jobName := range jobNames {
805811
// Skip jobs.pre-activation (or pre_activation) as it's handled specially
806812
if jobName == string(constants.PreActivationJobName) || jobName == "pre-activation" {
807813
continue

pkg/workflow/compiler_jobs.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"sort"
78
"strings"
89

910
"github.com/github/gh-aw/pkg/stringutil"
@@ -115,6 +116,7 @@ func (c *Compiler) getCustomJobsDependingOnPreActivation(customJobs map[string]a
115116
}
116117
return false
117118
})
119+
sort.Strings(deps)
118120
compilerJobsLog.Printf("Found %d custom jobs depending on pre_activation: %v", len(deps), deps)
119121
return deps
120122
}
@@ -133,6 +135,7 @@ func (c *Compiler) getReferencedCustomJobs(content string, customJobs map[string
133135
refs := sliceutil.FilterMapKeys(customJobs, func(jobName string, _ any) bool {
134136
return strings.Contains(content, fmt.Sprintf("needs.%s.", jobName))
135137
})
138+
sort.Strings(refs)
136139
if len(refs) > 0 {
137140
compilerJobsLog.Printf("Found %d custom job references: %v", len(refs), refs)
138141
}

0 commit comments

Comments
 (0)