From 631c55b3be58c6e6ab64f4e344bc784602025976 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 03:18:43 +0000 Subject: [PATCH 01/12] Initial plan From d6fee1fbe9169966ebda079f5888e430c95addd5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 03:36:28 +0000 Subject: [PATCH 02/12] fix(workflow): insert builtin pre-steps after full setup step block Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b186c272-d416-4042-b387-29d1ffe56add Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 9 +++- pkg/workflow/compiler_jobs_test.go | 78 ++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index e99cb8f29dc..7993c5d89a0 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -881,7 +881,14 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ insertIdx := len(steps) if lastSetupIdx >= 0 { - insertIdx = lastSetupIdx + 1 + // Setup step may be emitted as multiple []string entries (one line per entry). + // Insert after the full setup step by finding the next step boundary. + for i := lastSetupIdx + 1; i < len(steps); i++ { + if strings.HasPrefix(steps[i], " - ") { + insertIdx = i + break + } + } } else if firstCheckoutIdx >= 0 { insertIdx = firstCheckoutIdx } diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index 18c3a7e2f0f..5adf5e569d0 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -859,6 +859,84 @@ jobs: ) } +func TestBuiltinJobPreStepsAreInsertedAfterCompleteSetupStep(t *testing.T) { + tmpDir := testutil.TempDir(t, "builtin-job-pre-steps-setup-boundary") + + frontmatter := `--- +on: push +permissions: + contents: read +engine: copilot +strict: false +jobs: + pre_activation: + pre-steps: + - name: Pre-activation uses pre-step + uses: actions/setup-node@v4 + with: + node-version: "20" + activation: + pre-steps: + - name: Activation run pre-step + run: echo "activation prep" +--- + +# Test Workflow +` + + testFile := filepath.Join(tmpDir, "test.md") + if err := os.WriteFile(testFile, []byte(frontmatter), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(testFile); err != nil { + t.Fatalf("CompileWorkflow() error: %v", err) + } + + lockFile := filepath.Join(tmpDir, "test.lock.yml") + content, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + yamlStr := string(content) + + preActivationSection := extractJobSection(yamlStr, "pre_activation") + if preActivationSection == "" { + t.Fatal("Expected pre_activation section in lock file") + } + preActivationSetupBodyEndIdx := indexInNonCommentLinesInSection(preActivationSection, "job-name: ${{ github.job }}") + preActivationPreStepIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Pre-activation uses pre-step") + preActivationMembershipCheckIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Check team membership for workflow") + if preActivationSetupBodyEndIdx == -1 || preActivationPreStepIdx == -1 || preActivationMembershipCheckIdx == -1 { + t.Fatalf("Expected setup body, pre-step, and membership check in pre_activation section:\n%s", preActivationSection) + } + if preActivationPreStepIdx <= preActivationSetupBodyEndIdx { + t.Fatalf("Expected pre_activation pre-step to be inserted after setup step body in section:\n%s", preActivationSection) + } + if preActivationPreStepIdx >= preActivationMembershipCheckIdx { + t.Fatalf("Expected pre_activation pre-step before the first regular step in section:\n%s", preActivationSection) + } + + activationSection := extractJobSection(yamlStr, "activation") + if activationSection == "" { + t.Fatal("Expected activation section in lock file") + } + activationSetupBodyEndIdx := indexInNonCommentLinesInSection(activationSection, "job-name: ${{ github.job }}") + activationPreStepIdx := indexInNonCommentLinesInSection(activationSection, "- name: Activation run pre-step") + activationCheckoutIdx := indexInNonCommentLinesInSection(activationSection, "- name: Checkout .github and .agents folders") + if activationSetupBodyEndIdx == -1 || activationPreStepIdx == -1 || activationCheckoutIdx == -1 { + t.Fatalf("Expected setup body, pre-step, and repository checkout in activation section:\n%s", activationSection) + } + if activationPreStepIdx <= activationSetupBodyEndIdx { + t.Fatalf("Expected activation pre-step to be inserted after setup step body in section:\n%s", activationSection) + } + if activationPreStepIdx >= activationCheckoutIdx { + t.Fatalf("Expected activation pre-step before checkout in section:\n%s", activationSection) + } +} + func TestCustomJobPreStepsSchemaValidation(t *testing.T) { tmpDir := testutil.TempDir(t, "custom-job-pre-steps-schema") From 1496da4b93973c69b004394738858ead18030d87 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 03:46:08 +0000 Subject: [PATCH 03/12] Update; rm -rf / Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 2 +- pkg/workflow/compiler_jobs_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 7993c5d89a0..3d1bc17b3d4 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -884,7 +884,7 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ // Setup step may be emitted as multiple []string entries (one line per entry). // Insert after the full setup step by finding the next step boundary. for i := lastSetupIdx + 1; i < len(steps); i++ { - if strings.HasPrefix(steps[i], " - ") { + if strings.HasPrefix(strings.TrimLeft(steps[i], " "), "- ") { insertIdx = i break } diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index 5adf5e569d0..d92095a14de 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -906,13 +906,13 @@ jobs: if preActivationSection == "" { t.Fatal("Expected pre_activation section in lock file") } - preActivationSetupBodyEndIdx := indexInNonCommentLinesInSection(preActivationSection, "job-name: ${{ github.job }}") + preActivationJobNameLineIdx := indexInNonCommentLinesInSection(preActivationSection, "job-name: ${{ github.job }}") preActivationPreStepIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Pre-activation uses pre-step") preActivationMembershipCheckIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Check team membership for workflow") - if preActivationSetupBodyEndIdx == -1 || preActivationPreStepIdx == -1 || preActivationMembershipCheckIdx == -1 { + if preActivationJobNameLineIdx == -1 || preActivationPreStepIdx == -1 || preActivationMembershipCheckIdx == -1 { t.Fatalf("Expected setup body, pre-step, and membership check in pre_activation section:\n%s", preActivationSection) } - if preActivationPreStepIdx <= preActivationSetupBodyEndIdx { + if preActivationPreStepIdx <= preActivationJobNameLineIdx { t.Fatalf("Expected pre_activation pre-step to be inserted after setup step body in section:\n%s", preActivationSection) } if preActivationPreStepIdx >= preActivationMembershipCheckIdx { @@ -923,13 +923,13 @@ jobs: if activationSection == "" { t.Fatal("Expected activation section in lock file") } - activationSetupBodyEndIdx := indexInNonCommentLinesInSection(activationSection, "job-name: ${{ github.job }}") + activationJobNameLineIdx := indexInNonCommentLinesInSection(activationSection, "job-name: ${{ github.job }}") activationPreStepIdx := indexInNonCommentLinesInSection(activationSection, "- name: Activation run pre-step") activationCheckoutIdx := indexInNonCommentLinesInSection(activationSection, "- name: Checkout .github and .agents folders") - if activationSetupBodyEndIdx == -1 || activationPreStepIdx == -1 || activationCheckoutIdx == -1 { + if activationJobNameLineIdx == -1 || activationPreStepIdx == -1 || activationCheckoutIdx == -1 { t.Fatalf("Expected setup body, pre-step, and repository checkout in activation section:\n%s", activationSection) } - if activationPreStepIdx <= activationSetupBodyEndIdx { + if activationPreStepIdx <= activationJobNameLineIdx { t.Fatalf("Expected activation pre-step to be inserted after setup step body in section:\n%s", activationSection) } if activationPreStepIdx >= activationCheckoutIdx { From 5b17889132484e74b370d51a8d4ca8a25fda3a2a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 03:48:02 +0000 Subject: [PATCH 04/12] test(workflow): add setup boundary regression for builtin pre-steps Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b186c272-d416-4042-b387-29d1ffe56add Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 3 ++- pkg/workflow/compiler_jobs_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 3d1bc17b3d4..bdc74f7b6b8 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -884,7 +884,8 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ // Setup step may be emitted as multiple []string entries (one line per entry). // Insert after the full setup step by finding the next step boundary. for i := lastSetupIdx + 1; i < len(steps); i++ { - if strings.HasPrefix(strings.TrimLeft(steps[i], " "), "- ") { + trimmed := strings.TrimLeft(steps[i], " ") + if strings.HasPrefix(trimmed, "- ") { insertIdx = i break } diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index d92095a14de..cbc6f2c2a21 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -859,7 +859,7 @@ jobs: ) } -func TestBuiltinJobPreStepsAreInsertedAfterCompleteSetupStep(t *testing.T) { +func TestPreStepsInsertAfterSetupBoundary(t *testing.T) { tmpDir := testutil.TempDir(t, "builtin-job-pre-steps-setup-boundary") frontmatter := `--- From c1e4896b3014961d46bd817e5e1d4c25eab9f93b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 03:57:46 +0000 Subject: [PATCH 05/12] Update; rm -rf / Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 1 + pkg/workflow/compiler_jobs_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index bdc74f7b6b8..014d2e0bba4 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -883,6 +883,7 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ if lastSetupIdx >= 0 { // Setup step may be emitted as multiple []string entries (one line per entry). // Insert after the full setup step by finding the next step boundary. + // If no boundary is found, append pre-steps at the end. for i := lastSetupIdx + 1; i < len(steps); i++ { trimmed := strings.TrimLeft(steps[i], " ") if strings.HasPrefix(trimmed, "- ") { diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index cbc6f2c2a21..e6cfe1c4f7d 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -906,13 +906,13 @@ jobs: if preActivationSection == "" { t.Fatal("Expected pre_activation section in lock file") } - preActivationJobNameLineIdx := indexInNonCommentLinesInSection(preActivationSection, "job-name: ${{ github.job }}") + preActivationJobNameIdx := indexInNonCommentLinesInSection(preActivationSection, "job-name: ${{ github.job }}") preActivationPreStepIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Pre-activation uses pre-step") preActivationMembershipCheckIdx := indexInNonCommentLinesInSection(preActivationSection, "- name: Check team membership for workflow") - if preActivationJobNameLineIdx == -1 || preActivationPreStepIdx == -1 || preActivationMembershipCheckIdx == -1 { + if preActivationJobNameIdx == -1 || preActivationPreStepIdx == -1 || preActivationMembershipCheckIdx == -1 { t.Fatalf("Expected setup body, pre-step, and membership check in pre_activation section:\n%s", preActivationSection) } - if preActivationPreStepIdx <= preActivationJobNameLineIdx { + if preActivationPreStepIdx <= preActivationJobNameIdx { t.Fatalf("Expected pre_activation pre-step to be inserted after setup step body in section:\n%s", preActivationSection) } if preActivationPreStepIdx >= preActivationMembershipCheckIdx { @@ -923,13 +923,13 @@ jobs: if activationSection == "" { t.Fatal("Expected activation section in lock file") } - activationJobNameLineIdx := indexInNonCommentLinesInSection(activationSection, "job-name: ${{ github.job }}") + activationJobNameIdx := indexInNonCommentLinesInSection(activationSection, "job-name: ${{ github.job }}") activationPreStepIdx := indexInNonCommentLinesInSection(activationSection, "- name: Activation run pre-step") activationCheckoutIdx := indexInNonCommentLinesInSection(activationSection, "- name: Checkout .github and .agents folders") - if activationJobNameLineIdx == -1 || activationPreStepIdx == -1 || activationCheckoutIdx == -1 { + if activationJobNameIdx == -1 || activationPreStepIdx == -1 || activationCheckoutIdx == -1 { t.Fatalf("Expected setup body, pre-step, and repository checkout in activation section:\n%s", activationSection) } - if activationPreStepIdx <= activationJobNameLineIdx { + if activationPreStepIdx <= activationJobNameIdx { t.Fatalf("Expected activation pre-step to be inserted after setup step body in section:\n%s", activationSection) } if activationPreStepIdx >= activationCheckoutIdx { From 9eb46f4f6de8238165929ce3f3d31fa30840cd56 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 04:03:16 +0000 Subject: [PATCH 06/12] Update; rm -rf / Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 5 ++++- pkg/workflow/compiler_jobs_test.go | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 014d2e0bba4..f2d90d46383 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -883,7 +883,7 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ if lastSetupIdx >= 0 { // Setup step may be emitted as multiple []string entries (one line per entry). // Insert after the full setup step by finding the next step boundary. - // If no boundary is found, append pre-steps at the end. + // If no boundary is found (e.g. setup is the final step), append pre-steps at the end. for i := lastSetupIdx + 1; i < len(steps); i++ { trimmed := strings.TrimLeft(steps[i], " ") if strings.HasPrefix(trimmed, "- ") { @@ -891,6 +891,9 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ break } } + if insertIdx == len(steps) { + compilerJobsLog.Print("No step boundary found after setup step; appending pre-steps at end") + } } else if firstCheckoutIdx >= 0 { insertIdx = firstCheckoutIdx } diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index e6cfe1c4f7d..cbd67679a04 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -11,6 +11,7 @@ import ( "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/testutil" + "github.com/goccy/go-yaml" ) // ======================================== @@ -901,6 +902,10 @@ jobs: } yamlStr := string(content) + var lockFileYAML map[string]any + if err := yaml.Unmarshal(content, &lockFileYAML); err != nil { + t.Fatalf("Expected generated lock file to be valid YAML: %v", err) + } preActivationSection := extractJobSection(yamlStr, "pre_activation") if preActivationSection == "" { From ef61052d6cd1c1cd863ba214fe4ad35321fe6f5b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 04:08:53 +0000 Subject: [PATCH 07/12] Update; rm -rf / Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 2 ++ pkg/workflow/compiler_jobs_test.go | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index f2d90d46383..0de264af31f 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -883,6 +883,8 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ if lastSetupIdx >= 0 { // Setup step may be emitted as multiple []string entries (one line per entry). // Insert after the full setup step by finding the next step boundary. + // A step boundary is identified by the YAML list-item prefix ("- ") after + // indentation trimming, which marks the beginning of the next step block. // If no boundary is found (e.g. setup is the final step), append pre-steps at the end. for i := lastSetupIdx + 1; i < len(steps); i++ { trimmed := strings.TrimLeft(steps[i], " ") diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index cbd67679a04..c699fcacf27 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -906,6 +906,16 @@ jobs: if err := yaml.Unmarshal(content, &lockFileYAML); err != nil { t.Fatalf("Expected generated lock file to be valid YAML: %v", err) } + jobsNode, ok := lockFileYAML["jobs"].(map[string]any) + if !ok { + t.Fatalf("Expected generated lock file to contain jobs map, got: %T", lockFileYAML["jobs"]) + } + if _, ok := jobsNode["pre_activation"]; !ok { + t.Fatalf("Expected pre_activation job in parsed lock file YAML") + } + if _, ok := jobsNode["activation"]; !ok { + t.Fatalf("Expected activation job in parsed lock file YAML") + } preActivationSection := extractJobSection(yamlStr, "pre_activation") if preActivationSection == "" { From 87bb11d57b0ff15ac3e696b8023bbb90b61a3b54 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 04:10:03 +0000 Subject: [PATCH 08/12] fix(workflow): harden setup boundary insertion and YAML regression checks Agent-Logs-Url: https://github.com/github/gh-aw/sessions/b186c272-d416-4042-b387-29d1ffe56add Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 3 ++- pkg/workflow/compiler_jobs_test.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 0de264af31f..1a8e5015af4 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -885,7 +885,8 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ // Insert after the full setup step by finding the next step boundary. // A step boundary is identified by the YAML list-item prefix ("- ") after // indentation trimming, which marks the beginning of the next step block. - // If no boundary is found (e.g. setup is the final step), append pre-steps at the end. + // If no boundary is found (e.g. setup is the final step), insertIdx stays len(steps) + // and pre-steps are appended by the slice insertion logic below. for i := lastSetupIdx + 1; i < len(steps); i++ { trimmed := strings.TrimLeft(steps[i], " ") if strings.HasPrefix(trimmed, "- ") { diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index c699fcacf27..b55b778a78d 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -11,7 +11,7 @@ import ( "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/testutil" - "github.com/goccy/go-yaml" + "go.yaml.in/yaml/v3" ) // ======================================== From 787775448991a18780fd9f157183d678ef520d26 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 04:35:45 +0000 Subject: [PATCH 09/12] test(workflow): use existing yaml library in compiler jobs test Agent-Logs-Url: https://github.com/github/gh-aw/sessions/f467ec20-239b-4f4f-ae0e-67c61885bdd3 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index b55b778a78d..c699fcacf27 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -11,7 +11,7 @@ import ( "github.com/github/gh-aw/pkg/constants" "github.com/github/gh-aw/pkg/testutil" - "go.yaml.in/yaml/v3" + "github.com/goccy/go-yaml" ) // ======================================== From 6f027a42818e13ac5d75a0950b2a7c83e367d50e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:06:56 +0000 Subject: [PATCH 10/12] test(workflow): add more pre-steps insertion coverage Agent-Logs-Url: https://github.com/github/gh-aw/sessions/670bfc30-e990-48ea-b4e6-9b68da2e2b04 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 7 +++ pkg/workflow/compiler_jobs_test.go | 86 ++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 1a8e5015af4..8fb4fff4d0a 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -873,6 +873,13 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ for i, step := range steps { if firstCheckoutIdx == -1 && strings.Contains(step, "uses: actions/checkout@") { firstCheckoutIdx = i + for j := i; j >= 0; j-- { + trimmed := strings.TrimLeft(steps[j], " ") + if strings.HasPrefix(trimmed, "- ") { + firstCheckoutIdx = j + break + } + } } if exactSetupStepIDPattern.MatchString(step) { lastSetupIdx = i diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index c699fcacf27..d8261a154ac 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -952,6 +952,92 @@ jobs: } } +func TestInsertPreStepsAfterSetupBeforeCheckout(t *testing.T) { + tests := []struct { + name string + steps []string + preSteps []string + want []string + }{ + { + name: "insert at next step boundary after setup id", + steps: []string{ + " - name: Setup Scripts", + " uses: actions/github-script@v7", + " with:", + " job-name: ${{ github.job }}", + " id: setup", + " - name: Checkout repository", + " uses: actions/checkout@v6", + }, + preSteps: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + want: []string{ + " - name: Setup Scripts", + " uses: actions/github-script@v7", + " with:", + " job-name: ${{ github.job }}", + " id: setup", + " - name: Pre setup", + " run: echo \"pre\"", + " - name: Checkout repository", + " uses: actions/checkout@v6", + }, + }, + { + name: "append when setup is final step and no boundary exists", + steps: []string{ + " - name: Setup Scripts", + " uses: actions/github-script@v7", + " id: setup", + }, + preSteps: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + want: []string{ + " - name: Setup Scripts", + " uses: actions/github-script@v7", + " id: setup", + " - name: Pre setup", + " run: echo \"pre\"", + }, + }, + { + name: "insert before checkout when setup step is not present", + steps: []string{ + " - name: Checkout repository", + " uses: actions/checkout@v6", + " - name: Main work", + " run: echo \"work\"", + }, + preSteps: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + want: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + " - name: Checkout repository", + " uses: actions/checkout@v6", + " - name: Main work", + " run: echo \"work\"", + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := insertPreStepsAfterSetupBeforeCheckout(tt.steps, tt.preSteps) + if !slices.Equal(got, tt.want) { + t.Fatalf("insertPreStepsAfterSetupBeforeCheckout() mismatch\nwant:\n%q\ngot:\n%q", tt.want, got) + } + }) + } +} + func TestCustomJobPreStepsSchemaValidation(t *testing.T) { tmpDir := testutil.TempDir(t, "custom-job-pre-steps-schema") From 45332287b554ec8b6b6ac2e38da1625316d0ee17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:12:25 +0000 Subject: [PATCH 11/12] test(workflow): cover checkout shorthand pre-step insertion Agent-Logs-Url: https://github.com/github/gh-aw/sessions/670bfc30-e990-48ea-b4e6-9b68da2e2b04 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs_test.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index d8261a154ac..0c1a097d469 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -1026,6 +1026,25 @@ func TestInsertPreStepsAfterSetupBeforeCheckout(t *testing.T) { " run: echo \"work\"", }, }, + { + name: "insert before checkout shorthand step without name", + steps: []string{ + " - uses: actions/checkout@v6", + " - name: Main work", + " run: echo \"work\"", + }, + preSteps: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + want: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + " - uses: actions/checkout@v6", + " - name: Main work", + " run: echo \"work\"", + }, + }, } for _, tt := range tests { From b360dcc5f00f8d1c8e64587241b4a7fe3f151fb2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 22 Apr 2026 05:18:48 +0000 Subject: [PATCH 12/12] test(workflow): expand pre-step insertion edge-case coverage Agent-Logs-Url: https://github.com/github/gh-aw/sessions/670bfc30-e990-48ea-b4e6-9b68da2e2b04 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/workflow/compiler_jobs.go | 3 +++ pkg/workflow/compiler_jobs_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 8fb4fff4d0a..e82f231c037 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -873,6 +873,9 @@ func insertPreStepsAfterSetupBeforeCheckout(steps []string, preSteps []string) [ for i, step := range steps { if firstCheckoutIdx == -1 && strings.Contains(step, "uses: actions/checkout@") { firstCheckoutIdx = i + // Walk backward to the checkout step's list-item boundary ("- "). + // If no boundary is found, keep the current index so insertion still + // occurs before the checkout uses-line. for j := i; j >= 0; j-- { trimmed := strings.TrimLeft(steps[j], " ") if strings.HasPrefix(trimmed, "- ") { diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index 0c1a097d469..f8cf10b7b51 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -1045,6 +1045,30 @@ func TestInsertPreStepsAfterSetupBeforeCheckout(t *testing.T) { " run: echo \"work\"", }, }, + { + name: "return input steps unchanged when pre-steps are empty", + steps: []string{ + " - name: Main work", + " run: echo \"work\"", + }, + preSteps: []string{}, + want: []string{ + " - name: Main work", + " run: echo \"work\"", + }, + }, + { + name: "insert pre-steps when steps are empty", + steps: []string{}, + preSteps: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + want: []string{ + " - name: Pre setup", + " run: echo \"pre\"", + }, + }, } for _, tt := range tests {