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
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ repos:
- yaml
args: ["--schemafile", "https://goreleaser.com/static/schema.json"]

- repo: local
hooks:
- id: make-modernize
name: "Make Modernize"
entry: "make modernize"
language: system
pass_filenames: false

- repo: local
hooks:
- id: make-lint
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ lint:
test:
go test ./... -cover

.PHONY: modernize
modernize:
go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix -test ./...

.PHONY: tidy
tidy:
go mod tidy
Expand Down
98 changes: 49 additions & 49 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ type WorkflowAction struct {
// Workflow represents the GitHub Actions workflow file structure
// This matches the YAML structure of GitHub Actions workflow files.
type Workflow struct {
Name string `yaml:"name,omitempty"` // Name of the workflow
RunName string `yaml:"run-name,omitempty"` // Dynamic name for workflow runs
On interface{} `yaml:"on"` // Event triggers for the workflow
Permissions interface{} `yaml:"permissions,omitempty"` // Workflow-level permissions
Env map[string]interface{} `yaml:"env,omitempty"` // Workflow-level environment variables
Defaults *Defaults `yaml:"defaults,omitempty"` // Default settings for all jobs
Concurrency interface{} `yaml:"concurrency,omitempty"` // Concurrency group settings
Jobs map[string]Job `yaml:"jobs"` // The jobs that make up the workflow
Name string `yaml:"name,omitempty"` // Name of the workflow
RunName string `yaml:"run-name,omitempty"` // Dynamic name for workflow runs
On any `yaml:"on"` // Event triggers for the workflow
Permissions any `yaml:"permissions,omitempty"` // Workflow-level permissions
Env map[string]any `yaml:"env,omitempty"` // Workflow-level environment variables
Defaults *Defaults `yaml:"defaults,omitempty"` // Default settings for all jobs
Concurrency any `yaml:"concurrency,omitempty"` // Concurrency group settings
Jobs map[string]Job `yaml:"jobs"` // The jobs that make up the workflow
}

// Defaults represents default settings for all jobs
Expand All @@ -48,57 +48,57 @@ type RunDefaults struct {

// Job represents a job within a workflow
type Job struct {
Name string `yaml:"name,omitempty"` // Display name of the job
Needs interface{} `yaml:"needs,omitempty"` // Dependencies on other jobs
Permissions interface{} `yaml:"permissions,omitempty"` // Job-level permissions
RunsOn interface{} `yaml:"runs-on,omitempty"` // Runner type(s) to use
Environment interface{} `yaml:"environment,omitempty"` // Deployment environment
Outputs map[string]string `yaml:"outputs,omitempty"` // Job outputs for other jobs
Env map[string]interface{} `yaml:"env,omitempty"` // Job-level environment variables
Defaults *Defaults `yaml:"defaults,omitempty"` // Job-specific default settings
If interface{} `yaml:"if,omitempty"` // Conditional execution
Steps []Step `yaml:"steps,omitempty"` // Steps to execute in the job
TimeoutMinutes interface{} `yaml:"timeout-minutes,omitempty"` // Job timeout
Strategy *Strategy `yaml:"strategy,omitempty"` // Build matrix strategy
ContinueOnError interface{} `yaml:"continue-on-error,omitempty"` // Whether to continue on failure
Container interface{} `yaml:"container,omitempty"` // Container to run the job in
Services map[string]Container `yaml:"services,omitempty"` // Service containers
Concurrency interface{} `yaml:"concurrency,omitempty"` // Job-level concurrency
Uses string `yaml:"uses,omitempty"` // Reusable workflow reference
With map[string]interface{} `yaml:"with,omitempty"` // Inputs for reusable workflow
Secrets interface{} `yaml:"secrets,omitempty"` // Secrets for reusable workflow
Name string `yaml:"name,omitempty"` // Display name of the job
Needs any `yaml:"needs,omitempty"` // Dependencies on other jobs
Permissions any `yaml:"permissions,omitempty"` // Job-level permissions
RunsOn any `yaml:"runs-on,omitempty"` // Runner type(s) to use
Environment any `yaml:"environment,omitempty"` // Deployment environment
Outputs map[string]string `yaml:"outputs,omitempty"` // Job outputs for other jobs
Env map[string]any `yaml:"env,omitempty"` // Job-level environment variables
Defaults *Defaults `yaml:"defaults,omitempty"` // Job-specific default settings
If any `yaml:"if,omitempty"` // Conditional execution
Steps []Step `yaml:"steps,omitempty"` // Steps to execute in the job
TimeoutMinutes any `yaml:"timeout-minutes,omitempty"` // Job timeout
Strategy *Strategy `yaml:"strategy,omitempty"` // Build matrix strategy
ContinueOnError any `yaml:"continue-on-error,omitempty"` // Whether to continue on failure
Container any `yaml:"container,omitempty"` // Container to run the job in
Services map[string]Container `yaml:"services,omitempty"` // Service containers
Concurrency any `yaml:"concurrency,omitempty"` // Job-level concurrency
Uses string `yaml:"uses,omitempty"` // Reusable workflow reference
With map[string]any `yaml:"with,omitempty"` // Inputs for reusable workflow
Secrets any `yaml:"secrets,omitempty"` // Secrets for reusable workflow
}

// Step represents a step within a job
type Step struct {
ID string `yaml:"id,omitempty"` // Step identifier
If interface{} `yaml:"if,omitempty"` // Conditional execution
Name string `yaml:"name,omitempty"` // Display name of the step
Uses string `yaml:"uses,omitempty"` // Action reference
Run string `yaml:"run,omitempty"` // Command to run
WorkingDirectory string `yaml:"working-directory,omitempty"` // Step-specific working directory
Shell string `yaml:"shell,omitempty"` // Step-specific shell
With map[string]interface{} `yaml:"with,omitempty"` // Inputs for the action
Env map[string]interface{} `yaml:"env,omitempty"` // Step-level environment variables
ContinueOnError interface{} `yaml:"continue-on-error,omitempty"` // Whether to continue on failure
TimeoutMinutes interface{} `yaml:"timeout-minutes,omitempty"` // Step timeout
ID string `yaml:"id,omitempty"` // Step identifier
If any `yaml:"if,omitempty"` // Conditional execution
Name string `yaml:"name,omitempty"` // Display name of the step
Uses string `yaml:"uses,omitempty"` // Action reference
Run string `yaml:"run,omitempty"` // Command to run
WorkingDirectory string `yaml:"working-directory,omitempty"` // Step-specific working directory
Shell string `yaml:"shell,omitempty"` // Step-specific shell
With map[string]any `yaml:"with,omitempty"` // Inputs for the action
Env map[string]any `yaml:"env,omitempty"` // Step-level environment variables
ContinueOnError any `yaml:"continue-on-error,omitempty"` // Whether to continue on failure
TimeoutMinutes any `yaml:"timeout-minutes,omitempty"` // Step timeout
}

// Strategy represents a build matrix strategy
type Strategy struct {
Matrix interface{} `yaml:"matrix"` // Matrix configuration
FailFast interface{} `yaml:"fail-fast,omitempty"` // Whether to cancel all jobs if any fail
MaxParallel interface{} `yaml:"max-parallel,omitempty"` // Maximum parallel jobs
Matrix any `yaml:"matrix"` // Matrix configuration
FailFast any `yaml:"fail-fast,omitempty"` // Whether to cancel all jobs if any fail
MaxParallel any `yaml:"max-parallel,omitempty"` // Maximum parallel jobs
}

// Container represents a container configuration
type Container struct {
Image string `yaml:"image"` // Container image to use
Credentials *ContainerCredentials `yaml:"credentials,omitempty"` // Registry credentials
Env map[string]interface{} `yaml:"env,omitempty"` // Container environment variables
Ports []interface{} `yaml:"ports,omitempty"` // Ports to expose
Volumes []string `yaml:"volumes,omitempty"` // Volumes to mount
Options string `yaml:"options,omitempty"` // Additional Docker options
Image string `yaml:"image"` // Container image to use
Credentials *ContainerCredentials `yaml:"credentials,omitempty"` // Registry credentials
Env map[string]any `yaml:"env,omitempty"` // Container environment variables
Ports []any `yaml:"ports,omitempty"` // Ports to expose
Volumes []string `yaml:"volumes,omitempty"` // Volumes to mount
Options string `yaml:"options,omitempty"` // Additional Docker options
}

// ContainerCredentials represents credentials for a container
Expand All @@ -115,8 +115,8 @@ type Environment struct {

// Concurrency represents concurrency settings
type Concurrency struct {
Group string `yaml:"group"` // Concurrency group name
CancelInProgress interface{} `yaml:"cancel-in-progress,omitempty"` // Whether to cancel in-progress runs
Group string `yaml:"group"` // Concurrency group name
CancelInProgress any `yaml:"cancel-in-progress,omitempty"` // Whether to cancel in-progress runs
}

// ParseActionReference parses a "uses:" line into owner, repo, and ref
Expand Down
9 changes: 4 additions & 5 deletions utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"fmt"
"os"
"path/filepath"
"slices"
"strings"
)

Expand Down Expand Up @@ -42,11 +43,9 @@ func ValidateFilePath(path string) (string, error) {
// filepath.Clean("../a") -> "../a"
// We must ensure no ".." component exists anywhere in the path.
parts := strings.Split(cleanedPath, string(filepath.Separator))
for _, part := range parts {
if part == ".." {
// Return original path and error
return path, fmt.Errorf("invalid file path %q: contains '..'", path)
}
if slices.Contains(parts, "..") {
// Return original path and error
return path, fmt.Errorf("invalid file path %q: contains '..'", path)
}

// 4. Check if the cleaned path contains any directory separators.
Expand Down