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
30 changes: 15 additions & 15 deletions pkg/workflow/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1932,7 +1932,7 @@ func (c *Compiler) buildAddReactionJob(data *WorkflowData, taskJobCreated bool)
Permissions: "permissions:\n issues: write\n pull-requests: write",
Steps: steps,
Outputs: outputs,
Depends: depends,
Needs: depends,
}

return job, nil
Expand Down Expand Up @@ -1993,7 +1993,7 @@ func (c *Compiler) buildCreateOutputIssueJob(data *WorkflowData, mainJobName str
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2052,7 +2052,7 @@ func (c *Compiler) buildCreateOutputDiscussionJob(data *WorkflowData, mainJobNam
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2129,7 +2129,7 @@ func (c *Compiler) buildCreateOutputAddIssueCommentJob(data *WorkflowData, mainJ
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2193,7 +2193,7 @@ func (c *Compiler) buildCreateOutputPullRequestReviewCommentJob(data *WorkflowDa
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2281,7 +2281,7 @@ func (c *Compiler) buildCreateOutputSecurityReportJob(data *WorkflowData, mainJo
TimeoutMinutes: 10, // 10-minute timeout
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2376,7 +2376,7 @@ func (c *Compiler) buildCreateOutputPullRequestJob(data *WorkflowData, mainJobNa
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down Expand Up @@ -2417,7 +2417,7 @@ func (c *Compiler) buildMainJob(data *WorkflowData, jobName string, taskJobCreat
RunsOn: c.indentYAMLLines(data.RunsOn, " "),
Permissions: c.indentYAMLLines(data.Permissions, " "),
Steps: steps,
Depends: depends,
Needs: depends,
Outputs: outputs,
}

Expand Down Expand Up @@ -3580,16 +3580,16 @@ func (c *Compiler) buildCustomJobs(data *WorkflowData) error {
}

// Extract job dependencies
if depends, hasDeps := configMap["depends"]; hasDeps {
if depsList, ok := depends.([]any); ok {
for _, dep := range depsList {
if depStr, ok := dep.(string); ok {
job.Depends = append(job.Depends, depStr)
if needs, hasNeeds := configMap["needs"]; hasNeeds {
if needsList, ok := needs.([]any); ok {
for _, need := range needsList {
if needStr, ok := need.(string); ok {
job.Needs = append(job.Needs, needStr)
}
}
} else if depStr, ok := depends.(string); ok {
} else if needStr, ok := needs.(string); ok {
// Single dependency as string
job.Depends = append(job.Depends, depStr)
job.Needs = append(job.Needs, needStr)
}
}

Expand Down
24 changes: 12 additions & 12 deletions pkg/workflow/job_dependencies_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ func TestJobDependenciesWithCycleDetection(t *testing.T) {
name: "valid job dependencies",
jobs: []*Job{
{Name: "build", RunsOn: "ubuntu-latest"},
{Name: "test", RunsOn: "ubuntu-latest", Depends: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Depends: []string{"build", "test"}},
{Name: "test", RunsOn: "ubuntu-latest", Needs: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Needs: []string{"build", "test"}},
},
expectError: false,
description: "Valid linear job dependencies should work",
},
{
name: "simple cycle detection",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
expectError: true,
errorMsg: "cycle detected",
Expand All @@ -37,9 +37,9 @@ func TestJobDependenciesWithCycleDetection(t *testing.T) {
{
name: "complex cycle detection",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job3"}},
{Name: "job3", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job3"}},
{Name: "job3", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
expectError: true,
errorMsg: "cycle detected",
Expand All @@ -48,7 +48,7 @@ func TestJobDependenciesWithCycleDetection(t *testing.T) {
{
name: "dependency on non-existent job",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"nonexistent_job"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"nonexistent_job"}},
},
expectError: true,
errorMsg: "depends on non-existent job",
Expand All @@ -57,7 +57,7 @@ func TestJobDependenciesWithCycleDetection(t *testing.T) {
{
name: "self-dependency cycle",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
expectError: true,
errorMsg: "cycle detected",
Expand Down Expand Up @@ -146,9 +146,9 @@ func TestJobDependencyTopologicalOrder(t *testing.T) {
// Create a complex dependency graph: build -> [unit-test, integration-test] -> deploy
jobs := []*Job{
{Name: "build", RunsOn: "ubuntu-latest"},
{Name: "unit-test", RunsOn: "ubuntu-latest", Depends: []string{"build"}},
{Name: "integration-test", RunsOn: "ubuntu-latest", Depends: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Depends: []string{"unit-test", "integration-test"}},
{Name: "unit-test", RunsOn: "ubuntu-latest", Needs: []string{"build"}},
{Name: "integration-test", RunsOn: "ubuntu-latest", Needs: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Needs: []string{"unit-test", "integration-test"}},
}

for _, job := range jobs {
Expand Down
18 changes: 9 additions & 9 deletions pkg/workflow/jobs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Job struct {
Permissions string
TimeoutMinutes int
Steps []string
Depends []string // Job dependencies (needs clause)
Needs []string // Job dependencies (needs clause)
Outputs map[string]string
}

Expand Down Expand Up @@ -67,7 +67,7 @@ func (jm *JobManager) GetAllJobs() map[string]*Job {
func (jm *JobManager) ValidateDependencies() error {
// First check that all dependencies reference existing jobs
for jobName, job := range jm.jobs {
for _, dep := range job.Depends {
for _, dep := range job.Needs {
if _, exists := jm.jobs[dep]; !exists {
return fmt.Errorf("job '%s' depends on non-existent job '%s'", jobName, dep)
}
Expand Down Expand Up @@ -105,7 +105,7 @@ func (jm *JobManager) dfsVisit(jobName string, visitState map[string]int) error
visitState[jobName] = 1 // Mark as visiting

job := jm.jobs[jobName]
for _, dep := range job.Depends {
for _, dep := range job.Needs {
if visitState[dep] == 1 {
// Found a back edge - cycle detected
return fmt.Errorf("cycle detected in job dependencies: job '%s' has circular dependency through '%s'", jobName, dep)
Expand Down Expand Up @@ -146,12 +146,12 @@ func (jm *JobManager) renderJob(job *Job) string {
yaml.WriteString(fmt.Sprintf(" %s:\n", job.Name))

// Add needs clause if there are dependencies
if len(job.Depends) > 0 {
if len(job.Depends) == 1 {
yaml.WriteString(fmt.Sprintf(" needs: %s\n", job.Depends[0]))
if len(job.Needs) > 0 {
if len(job.Needs) == 1 {
yaml.WriteString(fmt.Sprintf(" needs: %s\n", job.Needs[0]))
} else {
yaml.WriteString(" needs:\n")
for _, dep := range job.Depends {
for _, dep := range job.Needs {
yaml.WriteString(fmt.Sprintf(" - %s\n", dep))
}
}
Expand Down Expand Up @@ -222,7 +222,7 @@ func (jm *JobManager) GetTopologicalOrder() ([]string, error) {

// Calculate in-degrees: count how many dependencies each job has
for _, job := range jm.jobs {
inDegree[job.Name] = len(job.Depends)
inDegree[job.Name] = len(job.Needs)
}

// Start with jobs that have no dependencies (in-degree = 0)
Expand All @@ -247,7 +247,7 @@ func (jm *JobManager) GetTopologicalOrder() ([]string, error) {

// For each job that depends on the current job, reduce its in-degree
for jobName, job := range jm.jobs {
for _, dep := range job.Depends {
for _, dep := range job.Needs {
if dep == currentJob {
inDegree[jobName]--
if inDegree[jobName] == 0 {
Expand Down
48 changes: 24 additions & 24 deletions pkg/workflow/jobs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,43 +81,43 @@ func TestJobManager_ValidateDependencies(t *testing.T) {
name: "valid dependencies",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest"},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job3", RunsOn: "ubuntu-latest", Depends: []string{"job1", "job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
{Name: "job3", RunsOn: "ubuntu-latest", Needs: []string{"job1", "job2"}},
},
wantErr: false,
},
{
name: "missing dependency",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest"},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"nonexistent"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"nonexistent"}},
},
wantErr: true,
errMsg: "depends on non-existent job 'nonexistent'",
},
{
name: "simple cycle",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
wantErr: true,
errMsg: "cycle detected",
},
{
name: "complex cycle",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job3"}},
{Name: "job3", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job3"}},
{Name: "job3", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
wantErr: true,
errMsg: "cycle detected",
},
{
name: "self-dependency cycle",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
wantErr: true,
errMsg: "cycle detected",
Expand Down Expand Up @@ -172,8 +172,8 @@ func TestJobManager_GetTopologicalOrder(t *testing.T) {
name: "linear dependencies",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest"},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job3", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
{Name: "job3", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
},
expected: []string{"job1", "job2", "job3"},
wantErr: false,
Expand All @@ -182,18 +182,18 @@ func TestJobManager_GetTopologicalOrder(t *testing.T) {
name: "complex dependencies",
jobs: []*Job{
{Name: "build", RunsOn: "ubuntu-latest"},
{Name: "test", RunsOn: "ubuntu-latest", Depends: []string{"build"}},
{Name: "lint", RunsOn: "ubuntu-latest", Depends: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Depends: []string{"test", "lint"}},
{Name: "test", RunsOn: "ubuntu-latest", Needs: []string{"build"}},
{Name: "lint", RunsOn: "ubuntu-latest", Needs: []string{"build"}},
{Name: "deploy", RunsOn: "ubuntu-latest", Needs: []string{"test", "lint"}},
},
expected: []string{"build", "lint", "test", "deploy"}, // build first, then lint/test (alphabetical), then deploy
wantErr: false,
},
{
name: "cycle should error",
jobs: []*Job{
{Name: "job1", RunsOn: "ubuntu-latest", Depends: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Depends: []string{"job1"}},
{Name: "job1", RunsOn: "ubuntu-latest", Needs: []string{"job2"}},
{Name: "job2", RunsOn: "ubuntu-latest", Needs: []string{"job1"}},
},
wantErr: true,
errMsg: "cycle detected",
Expand Down Expand Up @@ -272,10 +272,10 @@ func TestJobManager_RenderToYAML(t *testing.T) {
name: "job with dependencies",
jobs: []*Job{
{
Name: "job1",
RunsOn: "runs-on: ubuntu-latest",
Depends: []string{"job2"},
Steps: []string{" - name: Step1\n run: echo step1\n"},
Name: "job1",
RunsOn: "runs-on: ubuntu-latest",
Needs: []string{"job2"},
Steps: []string{" - name: Step1\n run: echo step1\n"},
},
{
Name: "job2",
Expand All @@ -296,10 +296,10 @@ func TestJobManager_RenderToYAML(t *testing.T) {
name: "job with multiple dependencies",
jobs: []*Job{
{
Name: "deploy",
RunsOn: "runs-on: ubuntu-latest",
Depends: []string{"build", "test"},
Steps: []string{" - name: Deploy\n run: echo deploy\n"},
Name: "deploy",
RunsOn: "runs-on: ubuntu-latest",
Needs: []string{"build", "test"},
Steps: []string{" - name: Deploy\n run: echo deploy\n"},
},
},
expected: []string{
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/output_labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (c *Compiler) buildCreateOutputLabelJob(data *WorkflowData, mainJobName str
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/output_missing_tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (c *Compiler) buildCreateOutputMissingToolJob(data *WorkflowData, mainJobNa
TimeoutMinutes: 5, // Short timeout since it's just processing output
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down
4 changes: 2 additions & 2 deletions pkg/workflow/output_missing_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ func TestMissingToolSafeOutput(t *testing.T) {
if job.Name != "missing_tool" {
t.Errorf("Expected job name to be 'missing_tool', got '%s'", job.Name)
}
if len(job.Depends) != 1 || job.Depends[0] != "main-job" {
t.Errorf("Expected job to depend on 'main-job', got %v", job.Depends)
if len(job.Needs) != 1 || job.Needs[0] != "main-job" {
t.Errorf("Expected job to depend on 'main-job', got %v", job.Needs)
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/output_push_to_branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ func (c *Compiler) buildCreateOutputPushToBranchJob(data *WorkflowData, mainJobN
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/workflow/output_update_issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (c *Compiler) buildCreateOutputUpdateIssueJob(data *WorkflowData, mainJobNa
TimeoutMinutes: 10, // 10-minute timeout as required
Steps: steps,
Outputs: outputs,
Depends: []string{mainJobName}, // Depend on the main workflow job
Needs: []string{mainJobName}, // Depend on the main workflow job
}

return job, nil
Expand Down
4 changes: 2 additions & 2 deletions pkg/workflow/security_reports_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ func TestBuildCreateOutputSecurityReportJob(t *testing.T) {
t.Errorf("Expected timeout 10 minutes, got %d", job.TimeoutMinutes)
}

if len(job.Depends) != 1 || job.Depends[0] != "main_job" {
t.Errorf("Expected dependency on 'main_job', got %v", job.Depends)
if len(job.Needs) != 1 || job.Needs[0] != "main_job" {
t.Errorf("Expected dependency on 'main_job', got %v", job.Needs)
}

// Check that job has necessary permissions
Expand Down