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
2 changes: 2 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ linters:
# godot linter disabled - too pedantic about comment punctuation
- unconvert # Remove unnecessary conversions
- testifylint # Enforce testify best practices
- intrange # Suggest integer range in for loops
- modernize # Modernize Go code using modern language features
disable:
- errcheck # Disabled due to exclude-functions not working properly in golangci-lint v2
- gocritic # Disabled due to disabled-checks not working properly in golangci-lint v2
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/add_interactive_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ func (c *AddInteractiveConfig) checkExistingSecrets() error {
}

// Parse the output - each secret name is on its own line
secretNames := strings.Split(strings.TrimSpace(string(output)), "\n")
for _, name := range secretNames {
secretNames := strings.SplitSeq(strings.TrimSpace(string(output)), "\n")
for name := range secretNames {
name = strings.TrimSpace(name)
if name != "" {
c.existingSecrets[name] = true
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/add_interactive_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func (c *AddInteractiveConfig) checkStatusAndOfferRun(ctx context.Context) error

// Try a few times to see the workflow in status
var workflowFound bool
for i := 0; i < 5; i++ {
for i := range 5 {
// Wait 2 seconds before each check (including the first)
select {
case <-ctx.Done():
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/audit_input_size_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func TestAuditReportIncludesInputSizes(t *testing.T) {

// Verify the table has the correct number of columns (5: Tool, Calls, Max Input, Max Output, Max Duration)
headerLine := ""
for _, line := range strings.Split(report, "\n") {
for line := range strings.SplitSeq(report, "\n") {
if strings.Contains(line, "Max Input") {
headerLine = line
break
Expand Down
9 changes: 3 additions & 6 deletions pkg/cli/audit_report.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ type OverviewData struct {
Status string `json:"status" console:"header:Status"`
Conclusion string `json:"conclusion,omitempty" console:"header:Conclusion,omitempty"`
CreatedAt time.Time `json:"created_at" console:"header:Created At"`
StartedAt time.Time `json:"started_at,omitempty" console:"header:Started At,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitempty" console:"header:Updated At,omitempty"`
StartedAt time.Time `json:"started_at,omitzero" console:"header:Started At,omitempty"`
UpdatedAt time.Time `json:"updated_at,omitzero" console:"header:Updated At,omitempty"`
Duration string `json:"duration,omitempty" console:"header:Duration,omitempty"`
Event string `json:"event" console:"header:Event"`
Branch string `json:"branch" console:"header:Branch"`
Expand Down Expand Up @@ -627,10 +627,7 @@ func stripGHALogTimestamps(content string) string {
// in a generous window (positions 11-35) to handle any fractional seconds length.
if len(line) > 19 && line[4] == '-' && line[7] == '-' && line[10] == 'T' {
// Find the Z that ends the timestamp within a reasonable range
searchBound := 35
if searchBound > len(line) {
searchBound = len(line)
}
searchBound := min(35, len(line))
if zIdx := strings.IndexByte(line[11:searchBound], 'Z'); zIdx >= 0 {
zPos := 11 + zIdx
if zPos+1 <= len(line) {
Expand Down
9 changes: 2 additions & 7 deletions pkg/cli/codemod_bots_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package cli

import (
"slices"
"strings"
"testing"

Expand Down Expand Up @@ -124,13 +125,7 @@ engine: copilot
assert.Contains(t, result, "bots: [dependabot, renovate]")
// Ensure on: block is created
lines := strings.Split(result, "\n")
foundOn := false
for _, line := range lines {
if line == "on:" {
foundOn = true
break
}
}
foundOn := slices.Contains(lines, "on:")
assert.True(t, foundOn, "Should create new on: block")
}

Expand Down
9 changes: 2 additions & 7 deletions pkg/cli/codemod_roles_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package cli

import (
"slices"
"strings"
"testing"

Expand Down Expand Up @@ -168,13 +169,7 @@ engine: copilot
assert.Contains(t, result, "roles: [admin, write]")
// Ensure on: block is created
lines := strings.Split(result, "\n")
foundOn := false
for _, line := range lines {
if line == "on:" {
foundOn = true
break
}
}
foundOn := slices.Contains(lines, "on:")
assert.True(t, foundOn, "Should create new on: block")
}

Expand Down
6 changes: 2 additions & 4 deletions pkg/cli/commands_utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,7 @@ This is a test workflow with some content.`
b.Fatalf("Failed to create test file: %v", err)
}

b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_, _ = extractWorkflowNameFromFile(filePath)
}
}
Expand All @@ -278,8 +277,7 @@ More content here.
@include another/file.md
Final content.`

b.ResetTimer()
for i := 0; i < b.N; i++ {
for b.Loop() {
_, _ = findIncludesInContent(content, "", false)
}
}
Expand Down
21 changes: 7 additions & 14 deletions pkg/cli/compile_security_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,8 @@ PR Number: ${{ github.event.pull_request.number }}

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile with actionlint enabled (per-file mode for benchmarking)
_ = CompileWorkflowWithValidation(compiler, testFile, false, false, false, true, false, false)
}
Expand Down Expand Up @@ -107,9 +106,8 @@ Issue: ${{ needs.activation.outputs.text }}

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile with zizmor enabled
_ = CompileWorkflowWithValidation(compiler, testFile, false, true, false, false, false, false)
}
Expand Down Expand Up @@ -153,9 +151,8 @@ Repository: ${{ github.repository }}

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile with poutine enabled
_ = CompileWorkflowWithValidation(compiler, testFile, false, false, true, false, false, false)
}
Expand Down Expand Up @@ -224,9 +221,8 @@ PR Details:

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile with all security tools enabled (zizmor, poutine, actionlint)
_ = CompileWorkflowWithValidation(compiler, testFile, false, true, true, true, false, false)
}
Expand Down Expand Up @@ -276,9 +272,8 @@ PR Number: ${{ github.event.pull_request.number }}

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile without any security tools
_ = CompileWorkflowWithValidation(compiler, testFile, false, false, false, false, false, false)
}
Expand Down Expand Up @@ -340,9 +335,8 @@ Process issue.
lockFiles = append(lockFiles, lockFile)
}

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Run batch actionlint on all lock files
_ = RunActionlintOnFiles(lockFiles, false, false)
}
Expand Down Expand Up @@ -433,9 +427,8 @@ Triggered by: ${{ github.actor }}

compiler := workflow.NewCompiler()

b.ResetTimer()
b.ReportAllocs()
for i := 0; i < b.N; i++ {
for b.Loop() {
// Compile with all security tools enabled
_ = CompileWorkflowWithValidation(compiler, testFile, false, true, true, true, false, false)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/cli/compile_stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func TestDisplayStatsTable_LessThan10(t *testing.T) {
func TestDisplayStatsTable_Exactly10(t *testing.T) {
// Create test stats with exactly 10 workflows
statsList := make([]*WorkflowStats, 10)
for i := 0; i < 10; i++ {
for i := range 10 {
statsList[i] = &WorkflowStats{
Workflow: fmt.Sprintf("workflow%d.lock.yml", i+1),
FileSize: int64((10 - i) * 1000), // Descending sizes
Expand Down Expand Up @@ -121,7 +121,7 @@ func TestDisplayStatsTable_Exactly10(t *testing.T) {
func TestDisplayStatsTable_MoreThan10(t *testing.T) {
// Create test stats with 15 workflows
statsList := make([]*WorkflowStats, 15)
for i := 0; i < 15; i++ {
for i := range 15 {
statsList[i] = &WorkflowStats{
Workflow: fmt.Sprintf("workflow%d.lock.yml", i+1),
FileSize: int64((15 - i) * 1000), // Descending sizes
Expand All @@ -146,7 +146,7 @@ func TestDisplayStatsTable_MoreThan10(t *testing.T) {
output := buf.String()

// Should show top 10 workflows (largest sizes)
for i := 0; i < 10; i++ {
for i := range 10 {
if !strings.Contains(output, statsList[i].Workflow) {
t.Errorf("Expected top 10 workflow %s to be displayed, but it wasn't found", statsList[i].Workflow)
}
Expand Down
9 changes: 2 additions & 7 deletions pkg/cli/completions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package cli
import (
"os"
"path/filepath"
"slices"
"strings"
"testing"

Expand Down Expand Up @@ -207,13 +208,7 @@ No description workflow

// Verify the name part matches expected names
name := parts[0]
found := false
for _, expectedName := range tt.wantNames {
if name == expectedName {
found = true
break
}
}
found := slices.Contains(tt.wantNames, name)
assert.True(t, found, "Expected name %s to be in %v", name, tt.wantNames)
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/dependency_graph_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ description: MCP Tool

// Create multiple top-level workflows that import the deep shared workflow
workflows := make([]string, 3)
for i := 0; i < 3; i++ {
for i := range 3 {
workflows[i] = filepath.Join(workflowsDir, fmt.Sprintf("workflow%d.md", i))
content := fmt.Sprintf(`---
description: Workflow %d
Expand Down
5 changes: 2 additions & 3 deletions pkg/cli/devcontainer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"encoding/json"
"fmt"
"maps"
"os"
"path/filepath"
"strings"
Expand Down Expand Up @@ -312,9 +313,7 @@ func mergeFeatures(existing DevcontainerFeatures, toAdd map[string]any) {
}

// Add new features
for key, value := range toAdd {
existing[key] = value
}
maps.Copy(existing, toAdd)
}

// getCurrentRepoName gets the current repository name from git remote in owner/repo format
Expand Down
12 changes: 6 additions & 6 deletions pkg/cli/docker_images_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ func TestStartDockerImageDownload_ConcurrentCalls(t *testing.T) {
doneChan := make(chan int, numGoroutines)

// Launch multiple goroutines that all try to start downloading the same image
for i := 0; i < numGoroutines; i++ {
for i := range numGoroutines {
go func(index int) {
<-startChan // Wait for the signal to start
started[index] = StartDockerImageDownload(context.Background(), testImage)
Expand All @@ -296,7 +296,7 @@ func TestStartDockerImageDownload_ConcurrentCalls(t *testing.T) {
close(startChan)

// Wait for all goroutines to finish
for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
<-doneChan
}

Expand Down Expand Up @@ -340,7 +340,7 @@ func TestStartDockerImageDownload_ConcurrentCallsWithAvailableImage(t *testing.T
doneChan := make(chan int, numGoroutines)

// Launch multiple goroutines
for i := 0; i < numGoroutines; i++ {
for i := range numGoroutines {
go func(index int) {
<-startChan
started[index] = StartDockerImageDownload(context.Background(), testImage)
Expand All @@ -352,7 +352,7 @@ func TestStartDockerImageDownload_ConcurrentCallsWithAvailableImage(t *testing.T
close(startChan)

// Wait for all to finish
for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
<-doneChan
}

Expand Down Expand Up @@ -393,15 +393,15 @@ func TestStartDockerImageDownload_RaceWithExternalDownload(t *testing.T) {
const numGoroutines = 5
results := make(chan bool, numGoroutines)

for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
go func() {
results <- StartDockerImageDownload(context.Background(), testImage)
}()
}

// Collect results
downloadStarts := 0
for i := 0; i < numGoroutines; i++ {
for range numGoroutines {
if <-results {
downloadStarts++
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/engine_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -434,8 +434,8 @@ func uploadSecretToRepo(secretName, secretValue, repoSlug string, verbose bool)

// stringContainsSecretName checks if the gh secret list output contains a secret name
func stringContainsSecretName(output, secretName string) bool {
lines := strings.Split(output, "\n")
for _, line := range lines {
lines := strings.SplitSeq(output, "\n")
for line := range lines {
if len(line) >= len(secretName) {
if line[:len(secretName)] == secretName && (len(line) == len(secretName) || line[len(secretName)] == '\t' || line[len(secretName)] == ' ') {
return true
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/error_formatting_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,8 @@ func TestErrorFormattingDoesNotMangle(t *testing.T) {

// All essential parts of the message should be preserved
// (formatting may add prefixes/styling but shouldn't lose content)
essentialParts := strings.Fields(tt.message)
for _, part := range essentialParts {
essentialParts := strings.FieldsSeq(tt.message)
for part := range essentialParts {
// Skip very short parts like ":", "at", etc.
if len(part) > 2 {
assert.Contains(t, formatted, part,
Expand Down
9 changes: 2 additions & 7 deletions pkg/cli/file_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"
"os/exec"
"path/filepath"
"slices"
"testing"
)

Expand Down Expand Up @@ -315,13 +316,7 @@ This uses reaction.

// Should track the lock file
lockFile := filepath.Join(tempDir, "test-workflow-with-reaction.lock.yml")
found := false
for _, file := range allFiles {
if file == lockFile {
found = true
break
}
}
found := slices.Contains(allFiles, lockFile)
if !found {
t.Errorf("Lock file %s should be tracked", lockFile)
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/fix_command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -532,8 +532,8 @@ This is a test workflow with slash command.
}

// Check that standalone "command" field was replaced (not part of slash_command)
lines := strings.Split(updatedStr, "\n")
for _, line := range lines {
lines := strings.SplitSeq(updatedStr, "\n")
for line := range lines {
trimmed := strings.TrimSpace(line)
if strings.HasPrefix(trimmed, "command:") && !strings.Contains(line, "slash_command") {
t.Errorf("Found unreplaced 'command:' field: %s", line)
Expand Down
Loading
Loading