diff --git a/pkg/workflow/call_workflow_permissions_test.go b/pkg/workflow/call_workflow_permissions_test.go index f4d939d304a..b67b2bf6b39 100644 --- a/pkg/workflow/call_workflow_permissions_test.go +++ b/pkg/workflow/call_workflow_permissions_test.go @@ -410,7 +410,9 @@ jobs: _, err := compiler.buildCallWorkflowJobs(workflowData, markdownPath) require.NoError(t, err, "Should build jobs without error") - yamlOutput := compiler.jobManager.RenderToYAML() + var yamlBuf strings.Builder + compiler.jobManager.WriteJobsYAML(&yamlBuf) + yamlOutput := yamlBuf.String() assert.Contains(t, yamlOutput, "uses: ./.github/workflows/worker-a.lock.yml", "Should contain uses directive") assert.Contains(t, yamlOutput, "secrets: inherit", "Should inherit secrets") diff --git a/pkg/workflow/compiler_jobs_test.go b/pkg/workflow/compiler_jobs_test.go index ee21bec0906..66363ea446e 100644 --- a/pkg/workflow/compiler_jobs_test.go +++ b/pkg/workflow/compiler_jobs_test.go @@ -3287,7 +3287,9 @@ func TestBuildCustomJobsAllNewFieldsViaWorkflowData(t *testing.T) { if err := jm.AddJob(job); err != nil { t.Fatalf("AddJob() error: %v", err) } - rendered := jm.RenderToYAML() + var renderedBuf strings.Builder + jm.WriteJobsYAML(&renderedBuf) + rendered := renderedBuf.String() renderedChecks := []string{ "name: My Display Name", diff --git a/pkg/workflow/if_expression_clean_test.go b/pkg/workflow/if_expression_clean_test.go index 30028c1b11f..ee3abca2261 100644 --- a/pkg/workflow/if_expression_clean_test.go +++ b/pkg/workflow/if_expression_clean_test.go @@ -63,7 +63,9 @@ github.event_name == 'issue_comment'`, t.Fatalf("Failed to add job: %v", err) } - yaml := jm.renderJob(job) + var yamlBuf strings.Builder + jm.renderJobTo(&yamlBuf, job) + yaml := yamlBuf.String() if tt.expression == "" { // Empty expressions should not render if condition @@ -126,7 +128,9 @@ func TestMultilineExpressionYAMLFolding(t *testing.T) { t.Fatalf("Failed to add job: %v", err) } - yaml := jm.renderJob(job) + var yamlBuf strings.Builder + jm.renderJobTo(&yamlBuf, job) + yaml := yamlBuf.String() // Should use folded style for multiline if !strings.Contains(yaml, "if: >") { @@ -232,7 +236,9 @@ func TestCustomJobIfConditionHandling(t *testing.T) { t.Fatalf("Failed to add job: %v", err) } - yaml := jm.renderJob(job) + var yamlBuf strings.Builder + jm.renderJobTo(&yamlBuf, job) + yaml := yamlBuf.String() // Ensure we don't have double prefixes in the rendered YAML if strings.Contains(yaml, "if: if:") { @@ -313,7 +319,9 @@ func TestLongExpressionBreaking(t *testing.T) { t.Fatalf("Failed to add job: %v", err) } - yaml := jm.renderJob(job) + var yamlBuf strings.Builder + jm.renderJobTo(&yamlBuf, job) + yaml := yamlBuf.String() t.Logf("Generated YAML:\n%s", yaml) // Check expected content diff --git a/pkg/workflow/jobs.go b/pkg/workflow/jobs.go index 78c9aa84044..89154c7c5ff 100644 --- a/pkg/workflow/jobs.go +++ b/pkg/workflow/jobs.go @@ -173,18 +173,6 @@ func (jm *JobManager) WriteJobsYAML(b *strings.Builder) { } } -// RenderToYAML generates the jobs section of a GitHub Actions workflow. -// Prefer WriteJobsYAML when an existing *strings.Builder is available to avoid -// an extra allocation. -func (jm *JobManager) RenderToYAML() string { - var yaml strings.Builder - // Pre-size so the builder avoids reallocation for typical workflows. - // All jobs combined are usually ~48–60 KB; 64 KB covers that range. - yaml.Grow(64 * 1024) - jm.WriteJobsYAML(&yaml) - return yaml.String() -} - // renderJobTo writes a single job to b directly, with no intermediate string allocation. func (jm *JobManager) renderJobTo(b *strings.Builder, job *Job) { jobLog.Printf("Rendering job: %s (steps=%d, needs=%d, reusable=%t)", job.Name, len(job.Steps), len(job.Needs), job.Uses != "") @@ -388,12 +376,3 @@ func (jm *JobManager) renderJobTo(b *strings.Builder, job *Job) { // Add newline after each job for proper formatting b.WriteString("\n") } - -// renderJob renders a single job to a new string. -// Prefer renderJobTo when an existing *strings.Builder is available to avoid -// the intermediate allocation. -func (jm *JobManager) renderJob(job *Job) string { - var b strings.Builder - jm.renderJobTo(&b, job) - return b.String() -} diff --git a/pkg/workflow/jobs_test.go b/pkg/workflow/jobs_test.go index 3d610f2a1c7..6fe274a1180 100644 --- a/pkg/workflow/jobs_test.go +++ b/pkg/workflow/jobs_test.go @@ -153,197 +153,8 @@ func TestJobManager_ValidateDependencies(t *testing.T) { } } -func TestJobManager_RenderToYAML(t *testing.T) { - tests := []struct { - name string - jobs []*Job - expected []string // Strings that should be present in the output - }{ - { - name: "empty job manager", - jobs: []*Job{}, - expected: []string{ - "jobs:", - }, - }, - { - name: "single simple job", - jobs: []*Job{ - { - Name: "test-job", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Test\n run: echo hello\n"}, - }, - }, - expected: []string{ - "jobs:", - " test-job:", - " runs-on: ubuntu-latest", - " steps:", - " - name: Test", - " run: echo hello", - }, - }, - { - name: "job with dependencies", - jobs: []*Job{ - { - Name: "job1", - RunsOn: "runs-on: ubuntu-latest", - Needs: []string{"job2"}, - Steps: []string{" - name: Step1\n run: echo step1\n"}, - }, - { - Name: "job2", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Step2\n run: echo step2\n"}, - }, - }, - expected: []string{ - "jobs:", - " job1:", - " needs: job2", - " runs-on: ubuntu-latest", - " job2:", - " runs-on: ubuntu-latest", - }, - }, - { - name: "job with multiple dependencies", - jobs: []*Job{ - { - Name: "deploy", - RunsOn: "runs-on: ubuntu-latest", - Needs: []string{"build", "test"}, - Steps: []string{" - name: Deploy\n run: echo deploy\n"}, - }, - }, - expected: []string{ - "jobs:", - " deploy:", - " needs:", - " - build", - " - test", - " runs-on: ubuntu-latest", - }, - }, - { - name: "job with if condition", - jobs: []*Job{ - { - Name: "conditional-job", - RunsOn: "runs-on: ubuntu-latest", - If: "github.event_name == 'push'", - Steps: []string{" - name: Conditional Step\n run: echo conditional\n"}, - }, - }, - expected: []string{ - "jobs:", - " conditional-job:", - " if: github.event_name == 'push'", - " runs-on: ubuntu-latest", - }, - }, - { - name: "job with outputs", - jobs: []*Job{ - { - Name: "output-job", - RunsOn: "runs-on: ubuntu-latest", - Outputs: map[string]string{ - "result": "${{ steps.test.outputs.result }}", - "version": "${{ steps.version.outputs.version }}", - }, - Steps: []string{" - name: Generate Output\n run: echo output\n"}, - }, - }, - expected: []string{ - "jobs:", - " output-job:", - " runs-on: ubuntu-latest", - " outputs:", - " result: ${{ steps.test.outputs.result }}", - " version: ${{ steps.version.outputs.version }}", - }, - }, - { - name: "jobs sorted alphabetically regardless of insertion order", - jobs: []*Job{ - { - Name: "zebra-job", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Zebra\n run: echo zebra\n"}, - }, - { - Name: "alpha-job", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Alpha\n run: echo alpha\n"}, - }, - { - Name: "charlie-job", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Charlie\n run: echo charlie\n"}, - }, - { - Name: "beta-job", - RunsOn: "runs-on: ubuntu-latest", - Steps: []string{" - name: Beta\n run: echo beta\n"}, - }, - }, - expected: []string{ - "jobs:", - " alpha-job:", - " beta-job:", - " charlie-job:", - " zebra-job:", - }, - }, - { - name: "job with multiple dependencies sorted alphabetically", - jobs: []*Job{ - { - Name: "deploy", - RunsOn: "runs-on: ubuntu-latest", - Needs: []string{"test", "activation", "build", "lint"}, - Steps: []string{" - name: Deploy\n run: echo deploy\n"}, - }, - }, - expected: []string{ - "jobs:", - " deploy:", - " needs:", - " - activation", - " - build", - " - lint", - " - test", - " runs-on: ubuntu-latest", - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - jm := NewJobManager() - for _, job := range tt.jobs { - if err := jm.AddJob(job); err != nil { - t.Fatalf("Failed to add job %s: %v", job.Name, err) - } - } - - result := jm.RenderToYAML() - - for _, expected := range tt.expected { - if !strings.Contains(result, expected) { - t.Errorf("RenderToYAML() result does not contain expected string: %s\nFull result:\n%s", expected, result) - } - } - }) - } -} - // TestJobManager_WriteJobsYAML verifies that WriteJobsYAML correctly appends -// the jobs section to an already-populated builder (mimicking generateWorkflowBody) -// and that the output is identical to RenderToYAML() prepended with the same prefix. +// the jobs section to an already-populated builder (mimicking generateWorkflowBody). func TestJobManager_WriteJobsYAML(t *testing.T) { tests := []struct { name string @@ -426,11 +237,6 @@ func TestJobManager_WriteJobsYAML(t *testing.T) { t.Errorf("WriteJobsYAML() result does not contain %q\nFull result:\n%s", expected, result) } } - - // WriteJobsYAML and RenderToYAML must produce the same jobs section. - if !strings.HasSuffix(result, jm.RenderToYAML()) { - t.Errorf("WriteJobsYAML() jobs section differs from RenderToYAML().\nRenderToYAML:\n%s\nWriteJobsYAML result (full):\n%s", jm.RenderToYAML(), result) - } }) } } diff --git a/pkg/workflow/main_job_env_test.go b/pkg/workflow/main_job_env_test.go index db8144ac82e..7696cf29495 100644 --- a/pkg/workflow/main_job_env_test.go +++ b/pkg/workflow/main_job_env_test.go @@ -99,7 +99,9 @@ func TestMainJobEnvironmentVariables(t *testing.T) { t.Fatalf("Failed to add job to manager: %v", err) } - yamlOutput := jobManager.RenderToYAML() + var yamlBuf strings.Builder + jobManager.WriteJobsYAML(&yamlBuf) + yamlOutput := yamlBuf.String() t.Logf("Generated YAML:\n%s", yamlOutput) // Check that env section exists in YAML diff --git a/pkg/workflow/safe_outputs_call_workflow_test.go b/pkg/workflow/safe_outputs_call_workflow_test.go index b66eb29345b..58b26c3e3ae 100644 --- a/pkg/workflow/safe_outputs_call_workflow_test.go +++ b/pkg/workflow/safe_outputs_call_workflow_test.go @@ -278,7 +278,9 @@ func TestCallWorkflowJobYAMLOutput(t *testing.T) { require.True(t, exists, "Job should exist") // Render all jobs to YAML - yamlOutput := compiler.jobManager.RenderToYAML() + var yamlBuf strings.Builder + compiler.jobManager.WriteJobsYAML(&yamlBuf) + yamlOutput := yamlBuf.String() assert.Contains(t, yamlOutput, "uses: ./.github/workflows/worker-a.lock.yml", "Should contain uses directive") assert.Contains(t, yamlOutput, "secrets: inherit", "Should inherit secrets") diff --git a/pkg/workflow/threat_detection_test.go b/pkg/workflow/threat_detection_test.go index c86ed4adc76..6e00f15a2cf 100644 --- a/pkg/workflow/threat_detection_test.go +++ b/pkg/workflow/threat_detection_test.go @@ -1343,7 +1343,9 @@ func TestDetectionJobPermissionsIndentation(t *testing.T) { t.Fatalf("AddJob() error: %v", err) } - yamlOutput := compiler.jobManager.RenderToYAML() + var yamlBuf strings.Builder + compiler.jobManager.WriteJobsYAML(&yamlBuf) + yamlOutput := yamlBuf.String() for _, expected := range tt.wantContains { if !strings.Contains(yamlOutput, expected) { diff --git a/pkg/workflow/zizmor_annotation_test.go b/pkg/workflow/zizmor_annotation_test.go index ba318c09796..e047b909283 100644 --- a/pkg/workflow/zizmor_annotation_test.go +++ b/pkg/workflow/zizmor_annotation_test.go @@ -153,7 +153,9 @@ func TestJobHasWorkflowRunSafetyChecks(t *testing.T) { // Test that the field is present in rendered YAML when true jm := NewJobManager() jm.AddJob(tt.job) - yaml := jm.RenderToYAML() + var yamlBuf strings.Builder + jm.WriteJobsYAML(&yamlBuf) + yaml := yamlBuf.String() if tt.expectField { if !strings.Contains(yaml, "# zizmor: ignore[dangerous-triggers]") {