From 790260276fa0eeade4a776de21f9325d0ca28d1a Mon Sep 17 00:00:00 2001 From: reugn Date: Fri, 2 Jan 2026 13:36:20 +0200 Subject: [PATCH] refactor(config): rename version fields in upgrade config --- README.md | 6 +- docs/configuration/index.md | 10 +-- docs/configuration/upgrade.md | 48 +++++++-------- docs/index.md | 2 +- docs/usage/init.md | 8 +-- docs/usage/upgrade.md | 12 ++-- internal/actions/cache.go | 2 +- internal/actions/cache_key.go | 22 +++---- internal/actions/cache_test.go | 4 +- internal/actions/client.go | 34 +++++------ internal/actions/client_test.go | 24 ++++---- internal/actions/resolver.go | 2 +- internal/actions/resolver_mock.go | 6 +- internal/cmd/init.go | 6 +- internal/config/config.go | 42 ++++++------- internal/config/config_test.go | 98 +++++++++++++++--------------- internal/config/upgrade_config.go | 20 +++--- internal/upgrader/upgrader.go | 16 ++--- internal/upgrader/upgrader_test.go | 30 ++++----- 19 files changed, 196 insertions(+), 196 deletions(-) diff --git a/README.md b/README.md index ac33621..a3feae7 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ A CLI tool for managing GitHub Actions workflows. It helps lint workflows for be - **style**: Naming conventions and style best practices - **Auto-fix Issues**: Automatically fix formatting issues and replace version tags with commit hashes - **Upgrade Actions**: Discover and upgrade GitHub Actions to their latest versions based on semantic versioning patterns -- **Config Management**: Configure linters and version update patterns via `.github-ci.yaml` +- **Config Management**: Configure linters and version patterns via `.github-ci.yaml` ## Quick Start @@ -136,10 +136,10 @@ linters: max-line-length: 120 upgrade: - version: tag # or 'major', 'hash' + format: tag # or 'major', 'hash' actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 ``` See the [Configuration Guide](https://reugn.github.io/github-ci/configuration/) for all options. diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 837b3dd..0eadfa6 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -42,12 +42,12 @@ linters: max-line-length: 120 upgrade: - version: tag # 'tag', 'major', or 'hash' + format: tag # 'tag', 'major', or 'hash' actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 actions/setup-go: - version: ~1.0.0 + constraint: ~1.0.0 ``` ## Sections @@ -56,14 +56,14 @@ upgrade: |---------|-------------| | [run](run) | Runtime settings (timeout, exit codes) | | [linters](linters) | Which linters to enable and their settings | -| [upgrade](upgrade) | Version patterns for action upgrades | +| [upgrade](upgrade) | Version constraints for action upgrades | ## Defaults If no configuration file exists: - All linters are enabled -- Actions use `^1.0.0` version pattern (allow minor/patch updates) +- Actions use `^1.0.0` version constraint (allow minor/patch updates) - Timeout is 5 minutes - Exit code for issues is 1 - Version format is `tag` diff --git a/docs/configuration/upgrade.md b/docs/configuration/upgrade.md index 3169ad2..2eb227e 100644 --- a/docs/configuration/upgrade.md +++ b/docs/configuration/upgrade.md @@ -13,15 +13,15 @@ The `upgrade` section controls how actions are upgraded. ```yaml upgrade: - version: tag + format: tag actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 actions/setup-go: - version: ~1.0.0 + constraint: ~1.0.0 ``` -### version +### format Controls the format of action references after upgrade. @@ -33,19 +33,19 @@ Controls the format of action references after upgrade. ### actions -Per-action version patterns controlling which versions are allowed. +Per-action version constraints controlling which versions are allowed. -## Version Patterns +## Version Constraints ### Caret (`^`) - Allow Minor Updates ```yaml actions: actions/checkout: - version: ^1.0.0 # Allows 1.x.x but not 2.x.x + constraint: ^1.0.0 # Allows 1.x.x but not 2.x.x ``` -| Pattern | Allowed | Not Allowed | +| Constraint | Allowed | Not Allowed | |---------|---------|-------------| | `^1.0.0` | `1.0.1`, `1.2.0`, `1.99.0` | `2.0.0` | | `^2.0.0` | `2.0.1`, `2.5.0` | `3.0.0` | @@ -58,10 +58,10 @@ actions: ```yaml actions: actions/checkout: - version: ~1.2.0 # Allows 1.2.x but not 1.3.x + constraint: ~1.2.0 # Allows 1.2.x but not 1.3.x ``` -| Pattern | Allowed | Not Allowed | +| Constraint | Allowed | Not Allowed | |---------|---------|-------------| | `~1.2.0` | `1.2.1`, `1.2.5` | `1.3.0`, `2.0.0` | | `~2.5.0` | `2.5.1`, `2.5.99` | `2.6.0` | @@ -78,12 +78,12 @@ Only allow patch updates for stability: ```yaml upgrade: - version: tag + format: tag actions: actions/checkout: - version: ~4.0.0 + constraint: ~4.0.0 actions/setup-go: - version: ~5.0.0 + constraint: ~5.0.0 ``` ### Major Version Pinning @@ -92,10 +92,10 @@ Use major version tags for cleaner workflow files: ```yaml upgrade: - version: major + format: major actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 ``` Result: @@ -109,10 +109,10 @@ Pin to exact commits for maximum security: ```yaml upgrade: - version: hash + format: hash actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 ``` Result: @@ -122,23 +122,23 @@ Result: ### Mixed Strategies -Different patterns for different actions: +Different constraints for different actions: ```yaml upgrade: - version: tag + format: tag actions: # Critical actions - patch updates only actions/checkout: - version: ~4.0.0 + constraint: ~4.0.0 # Less critical - minor updates allowed actions/cache: - version: ^4.0.0 + constraint: ^4.0.0 # Third-party - more conservative docker/build-push-action: - version: ~5.0.0 + constraint: ~5.0.0 ``` ## Upgrade Process @@ -146,8 +146,8 @@ upgrade: 1. **Discover**: Scan workflows for action usages 2. **Resolve**: Get current version (resolve hash to tag if needed) 3. **Fetch**: Get latest version from GitHub API -4. **Compare**: Check if update matches version pattern -5. **Update**: Apply update based on `version` format setting +4. **Compare**: Check if update matches version constraint +5. **Update**: Apply update based on `format` setting ## See Also diff --git a/docs/index.md b/docs/index.md index 3807166..b00ef2a 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,7 @@ A CLI tool for managing GitHub Actions workflows. It helps you lint workflows fo - **Lint Workflows**: Check workflows for best practices with multiple configurable linters - **Auto-fix Issues**: Automatically fix formatting issues and replace version tags with commit hashes - **Upgrade Actions**: Discover and upgrade GitHub Actions to their latest versions based on semantic versioning patterns -- **Config Management**: Configure linters and version update patterns via `.github-ci.yaml` +- **Config Management**: Configure linters and version patterns via `.github-ci.yaml` ## Available Linters diff --git a/docs/usage/init.md b/docs/usage/init.md index 87cf9e5..c513304 100644 --- a/docs/usage/init.md +++ b/docs/usage/init.md @@ -87,7 +87,7 @@ linters: disable: [] upgrade: actions: {} - version: tag + format: tag ``` With `--defaults`, all linter settings and discovered actions are included: @@ -116,12 +116,12 @@ linters: max-run-lines: 0 upgrade: - version: tag + format: tag actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 actions/setup-go: - version: ^1.0.0 + constraint: ^1.0.0 ``` See [Configuration](../configuration/) for details on customizing the config. diff --git a/docs/usage/upgrade.md b/docs/usage/upgrade.md index 124e32b..c997d78 100644 --- a/docs/usage/upgrade.md +++ b/docs/usage/upgrade.md @@ -17,13 +17,13 @@ github-ci upgrade [flags] ## Description -The `upgrade` command checks for newer versions of actions in all workflows and updates them based on configured version patterns. +The `upgrade` command checks for newer versions of actions in all workflows and updates them based on configured version constraints. This command: 1. Scans all workflows to discover actions 2. Updates `.github-ci.yaml` if it exists (use `init` command to create one) 3. Checks for newer versions of each action -4. Updates actions based on version patterns defined in the config +4. Updates actions based on version constraints defined in the config ## Flags @@ -63,7 +63,7 @@ GitHub API: 4 call(s), 2 from cache ## Version Format -The `upgrade.version` config option controls how actions are referenced after upgrade: +The `upgrade.format` config option controls how actions are referenced after upgrade: | Format | Example | Description | |--------|---------|-------------| @@ -89,11 +89,11 @@ The `upgrade.version` config option controls how actions are referenced after up - uses: actions/checkout@8f4b7f84856dbbe3f95729c4cd48d901b28810a # v4.1.1 ``` -## Version Patterns +## Version Constraints Control which versions are allowed for each action: -| Pattern | Behavior | Example | +| Constraint | Behavior | Example | |---------|----------|---------| | `^1.0.0` | Same major, any minor/patch | `1.x.x` | | `~1.2.0` | Same major.minor, any patch | `1.2.x` | @@ -116,5 +116,5 @@ This appears when: ## See Also -- [Configuration](../configuration/) - Configure version patterns +- [Configuration](../configuration/) - Configure version constraints - [init](init) - Create configuration file diff --git a/internal/actions/cache.go b/internal/actions/cache.go index b8957f1..7fc3e85 100644 --- a/internal/actions/cache.go +++ b/internal/actions/cache.go @@ -12,7 +12,7 @@ type CacheStats struct { // when the same action appears in multiple workflows. type Cache struct { mu sync.Mutex - constrained map[string]VersionResult // Results for configured actions with version patterns + constrained map[string]VersionResult // Results for configured actions with version constraints unconstrained map[string]VersionResult // Results for unconfigured actions (absolute latest) hits int64 misses int64 diff --git a/internal/actions/cache_key.go b/internal/actions/cache_key.go index 494cadf..0b02030 100644 --- a/internal/actions/cache_key.go +++ b/internal/actions/cache_key.go @@ -4,19 +4,19 @@ import "fmt" // VersionKey represents a cache key for version lookups. type VersionKey struct { - Owner string - Repo string - Ref string // Current version reference (e.g., "v1.2.0"); empty for unconstrained - Pattern string // Version constraint (e.g., "^1.0.0"); empty for unconstrained + Owner string + Repo string + Ref string // Current version reference (e.g., "v1.2.0"); empty for unconstrained + Constraint string // Version constraint (e.g., "^1.0.0"); empty for unconstrained } // NewConstrainedKey creates a key for constrained version lookups. -func NewConstrainedKey(owner, repo, ref, pattern string) VersionKey { +func NewConstrainedKey(owner, repo, ref, constraint string) VersionKey { return VersionKey{ - Owner: owner, - Repo: repo, - Ref: ref, - Pattern: pattern, + Owner: owner, + Repo: repo, + Ref: ref, + Constraint: constraint, } } @@ -34,10 +34,10 @@ func (k VersionKey) String() string { if !k.IsConstrained() { return fmt.Sprintf("%s/%s", k.Owner, k.Repo) } - return fmt.Sprintf("%s/%s:%s:%s", k.Owner, k.Repo, k.Ref, k.Pattern) + return fmt.Sprintf("%s/%s:%s:%s", k.Owner, k.Repo, k.Ref, k.Constraint) } // IsConstrained returns true if this is a constrained key. func (k VersionKey) IsConstrained() bool { - return k.Ref != "" || k.Pattern != "" + return k.Ref != "" || k.Constraint != "" } diff --git a/internal/actions/cache_test.go b/internal/actions/cache_test.go index 7df5241..50b2a2a 100644 --- a/internal/actions/cache_test.go +++ b/internal/actions/cache_test.go @@ -50,7 +50,7 @@ func TestCache_ConstrainedGetSet(t *testing.T) { differentKey := NewConstrainedKey("owner", "repo", "v1.0.0", "^2.0.0") _, ok = cache.GetConstrained(differentKey) if ok { - t.Error("GetConstrained returned ok=true for different pattern") + t.Error("GetConstrained returned ok=true for different constraint") } } @@ -228,7 +228,7 @@ func TestVersionKey_IsConstrained(t *testing.T) { expected: true, }, { - name: "constrained with pattern only", + name: "constrained with constraint only", key: NewConstrainedKey("owner", "repo", "", "^1.0.0"), expected: true, }, diff --git a/internal/actions/client.go b/internal/actions/client.go index 46276c0..f7ba5cf 100644 --- a/internal/actions/client.go +++ b/internal/actions/client.go @@ -149,21 +149,21 @@ func (c *Client) GetCommitHash(owner, repo, ref string) (string, error) { // GetLatestVersion fetches the latest compatible tag and commit hash. // Results are cached. -func (c *Client) GetLatestVersion(owner, repo, currentVersion, versionPattern string) (string, string, error) { - key := NewConstrainedKey(owner, repo, currentVersion, versionPattern) +func (c *Client) GetLatestVersion(owner, repo, currentVersion, versionConstraint string) (string, string, error) { + key := NewConstrainedKey(owner, repo, currentVersion, versionConstraint) if result, ok := c.cache.GetConstrained(key); ok { return result.Tag, result.Hash, result.Err } - tags, err := c.fetchMatchingTags(owner, repo, versionPattern) + tags, err := c.fetchMatchingTags(owner, repo, versionConstraint) if err != nil { c.cache.SetConstrained(key, NewVersionResult("", "", err)) return "", "", err } if len(tags) == 0 { - err := fmt.Errorf("no compatible tags found for pattern %s", versionPattern) + err := fmt.Errorf("no compatible tags found for constraint %s", versionConstraint) c.cache.SetConstrained(key, NewVersionResult("", "", err)) return "", "", err } @@ -180,12 +180,12 @@ func (c *Client) GetLatestVersion(owner, repo, currentVersion, versionPattern st return latest.tag, latest.hash, nil } -// fetchMatchingTags retrieves all tags matching the version pattern. -func (c *Client) fetchMatchingTags(owner, repo, pattern string) ([]tagInfo, error) { +// fetchMatchingTags retrieves all tags matching the version constraint. +func (c *Client) fetchMatchingTags(owner, repo, constraint string) ([]tagInfo, error) { var matching []tagInfo err := c.paginateTags(owner, repo, func(tag *github.RepositoryTag) bool { - if matchesVersionPattern(tag.GetName(), pattern) { + if matchesVersionConstraint(tag.GetName(), constraint) { matching = append(matching, tagInfo{ tag: tag.GetName(), hash: tag.GetCommit().GetSHA(), @@ -307,27 +307,27 @@ func (c *Client) GetLatestMinorVersion(owner, repo, majorVersion string) (string return latest.tag, latest.hash, nil } -// matchesVersionPattern checks if a tag matches the version pattern. -func matchesVersionPattern(tagVersion, pattern string) bool { - if pattern == "" { +// matchesVersionConstraint checks if a tag matches the version constraint. +func matchesVersionConstraint(tagVersion, constraint string) bool { + if constraint == "" { return true } - if after, ok := strings.CutPrefix(pattern, "^"); ok { - patternMajor := version.ExtractMajor(after) + if after, ok := strings.CutPrefix(constraint, "^"); ok { + constraintMajor := version.ExtractMajor(after) tagMajor := version.ExtractMajor(tagVersion) // ^1.0.0 allows any version >= 1 - if patternMajor == 1 { + if constraintMajor == 1 { return tagMajor >= 1 } - return tagMajor == patternMajor + return tagMajor == constraintMajor } - if after, ok := strings.CutPrefix(pattern, "~"); ok { - patternMajor, patternMinor := version.ExtractMajorMinor(after) + if after, ok := strings.CutPrefix(constraint, "~"); ok { + constraintMajor, constraintMinor := version.ExtractMajorMinor(after) tagMajor, tagMinor := version.ExtractMajorMinor(tagVersion) - return tagMajor == patternMajor && tagMinor == patternMinor + return tagMajor == constraintMajor && tagMinor == constraintMinor } return false diff --git a/internal/actions/client_test.go b/internal/actions/client_test.go index 83b63e6..fb0cd7f 100644 --- a/internal/actions/client_test.go +++ b/internal/actions/client_test.go @@ -2,42 +2,42 @@ package actions import "testing" -func TestMatchesVersionPattern(t *testing.T) { +func TestMatchesVersionConstraint(t *testing.T) { tests := []struct { name string tagVersion string - pattern string + constraint string expected bool }{ - // Empty pattern - allow all - {"empty pattern allows any", "2.0.0", "", true}, + // Empty constraint - allow all + {"empty constraint allows any", "2.0.0", "", true}, - // ^1.0.0 pattern - latest overall + // ^1.0.0 constraint - latest overall {"^1.0.0 allows v1", "v1.5.0", "^1.0.0", true}, {"^1.0.0 allows v2", "v2.0.0", "^1.0.0", true}, {"^1.0.0 allows v3", "v3.5.0", "^1.0.0", true}, - // ^2.0.0 pattern - same major version + // ^2.0.0 constraint - same major version {"^2.0.0 allows v2.x", "v2.5.0", "^2.0.0", true}, {"^2.0.0 rejects v3.x", "v3.0.0", "^2.0.0", false}, {"^2.0.0 rejects v1.x", "v1.9.0", "^2.0.0", false}, - // ~2.5.0 pattern - same major.minor version + // ~2.5.0 constraint - same major.minor version {"~2.5.0 allows v2.5.x", "v2.5.1", "~2.5.0", true}, {"~2.5.0 rejects v2.6.x", "v2.6.0", "~2.5.0", false}, {"~2.5.0 rejects v2.4.x", "v2.4.9", "~2.5.0", false}, {"~2.5.0 rejects v3.5.x", "v3.5.0", "~2.5.0", false}, - // Unknown pattern - {"unknown pattern", "2.0.0", "invalid", false}, + // Unknown constraint + {"unknown constraint", "2.0.0", "invalid", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := matchesVersionPattern(tt.tagVersion, tt.pattern) + result := matchesVersionConstraint(tt.tagVersion, tt.constraint) if result != tt.expected { - t.Errorf("matchesVersionPattern(%q, %q) = %v, want %v", - tt.tagVersion, tt.pattern, result, tt.expected) + t.Errorf("matchesVersionConstraint(%q, %q) = %v, want %v", + tt.tagVersion, tt.constraint, result, tt.expected) } }) } diff --git a/internal/actions/resolver.go b/internal/actions/resolver.go index 901fedf..b21b0ef 100644 --- a/internal/actions/resolver.go +++ b/internal/actions/resolver.go @@ -3,7 +3,7 @@ package actions // Resolver defines the interface for GitHub Actions operations. type Resolver interface { GetCommitHash(owner, repo, ref string) (string, error) - GetLatestVersion(owner, repo, currentVersion, versionPattern string) (string, string, error) + GetLatestVersion(owner, repo, currentVersion, versionConstraint string) (string, string, error) GetLatestVersionUnconstrained(owner, repo string) (string, string, error) GetTagForCommit(owner, repo, commitHash string) (string, error) GetLatestMinorVersion(owner, repo, majorVersion string) (string, string, error) diff --git a/internal/actions/resolver_mock.go b/internal/actions/resolver_mock.go index 2a403c7..0bb6781 100644 --- a/internal/actions/resolver_mock.go +++ b/internal/actions/resolver_mock.go @@ -3,7 +3,7 @@ package actions // MockResolver is a mock implementation of the Resolver interface for testing. type MockResolver struct { GetCommitHashFunc func(owner, repo, ref string) (string, error) - GetLatestVersionFunc func(owner, repo, currentVersion, versionPattern string) (string, string, error) + GetLatestVersionFunc func(owner, repo, currentVersion, versionConstraint string) (string, string, error) GetLatestVersionUnconstrFunc func(owner, repo string) (string, string, error) GetTagForCommitFunc func(owner, repo, commitHash string) (string, error) GetLatestMinorVersionFunc func(owner, repo, majorVersion string) (string, string, error) @@ -19,9 +19,9 @@ func (m *MockResolver) GetCommitHash(owner, repo, ref string) (string, error) { return "", nil } -func (m *MockResolver) GetLatestVersion(owner, repo, currentVersion, versionPattern string) (string, string, error) { +func (m *MockResolver) GetLatestVersion(owner, repo, currentVersion, versionConstraint string) (string, string, error) { if m.GetLatestVersionFunc != nil { - return m.GetLatestVersionFunc(owner, repo, currentVersion, versionPattern) + return m.GetLatestVersionFunc(owner, repo, currentVersion, versionConstraint) } return "", "", nil } diff --git a/internal/cmd/init.go b/internal/cmd/init.go index f2fb700..4859f5f 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -25,7 +25,7 @@ If the configuration file already exists: - With --update: adds any new actions found in workflows to the config Use --defaults to include all linter settings and scan workflows to discover -actions with default version patterns.`, +actions with default version constraints.`, RunE: runInit, SilenceUsage: true, } @@ -137,8 +137,8 @@ func discoverActions(cfg *config.Config, workflows []*workflow.Workflow) []strin continue } - // Check if action already exists in config - if cfg.Upgrade.Actions[name].Version == "" { + // Add action if not already configured + if cfg.Upgrade.Actions[name].Constraint == "" { cfg.SetActionConfig(name, config.DefaultActionConfig) newActions = append(newActions, name) } diff --git a/internal/config/config.go b/internal/config/config.go index c499a48..e6d0b64 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,7 +16,7 @@ import ( const DefaultConfigFileName = ".github-ci.yaml" // DefaultActionConfig is the default configuration for newly discovered actions. -var DefaultActionConfig = ActionConfig{Version: defaultVersionPattern} +var DefaultActionConfig = ActionConfig{Constraint: defaultVersionConstraint} // Config represents the GitHub CI configuration file structure. type Config struct { @@ -171,7 +171,7 @@ func (c *Config) GetActionConfig(actionName string) ActionConfig { return cfg } } - return ActionConfig{Version: defaultVersionPattern} + return ActionConfig{Constraint: defaultVersionConstraint} } // SetActionConfig sets the configuration for an action. @@ -183,10 +183,10 @@ func (c *Config) SetActionConfig(actionName string, cfg ActionConfig) { // GetVersionFormat returns the version format for upgrades: "tag", "hash", or "major". // Defaults to "tag" if not specified. func (c *Config) GetVersionFormat() string { - if c.Upgrade == nil || c.Upgrade.Version == "" { - return defaultUpgradeVersion + if c.Upgrade == nil || c.Upgrade.Format == "" { + return defaultUpgradeFormat } - return c.Upgrade.Version + return c.Upgrade.Format } // IsLinterEnabled checks if a linter is enabled based on configuration. @@ -215,48 +215,48 @@ func NormalizeActionName(uses string) string { } // ShouldUpdate determines if a version update should be applied. -// Patterns: +// Constraints: // - "": any newer version // - "^X.0.0": same major version (X.y.z) // - "~X.Y.0": same major.minor version (X.Y.z) -func ShouldUpdate(currentVersion, newVersion, pattern string) bool { +func ShouldUpdate(currentVersion, newVersion, constraint string) bool { if version.Compare(newVersion, currentVersion) <= 0 { return false } - if pattern == "" { + if constraint == "" { return true } - if after, ok := strings.CutPrefix(pattern, "^"); ok { - return matchesMajorPattern(newVersion, after) + if after, ok := strings.CutPrefix(constraint, "^"); ok { + return matchesMajorConstraint(newVersion, after) } - if after, ok := strings.CutPrefix(pattern, "~"); ok { - return matchesMinorPattern(newVersion, after) + if after, ok := strings.CutPrefix(constraint, "~"); ok { + return matchesMinorConstraint(newVersion, after) } return false } -// matchesMajorPattern checks if version matches ^X.0.0 pattern. -func matchesMajorPattern(newVersion, patternVersion string) bool { - patternMajor := version.ExtractMajor(patternVersion) +// matchesMajorConstraint checks if version matches ^X.0.0 constraint. +func matchesMajorConstraint(newVersion, constraintVersion string) bool { + constraintMajor := version.ExtractMajor(constraintVersion) newMajor := version.ExtractMajor(newVersion) // ^1.0.0 is special: allows any version >= 1.0.0 - if patternMajor == 1 { + if constraintMajor == 1 { return newMajor >= 1 } // Otherwise: same major version only - return newMajor == patternMajor + return newMajor == constraintMajor } -// matchesMinorPattern checks if version matches ~X.Y.0 pattern. -func matchesMinorPattern(newVersion, patternVersion string) bool { - patternMajor, patternMinor := version.ExtractMajorMinor(patternVersion) +// matchesMinorConstraint checks if version matches ~X.Y.0 constraint. +func matchesMinorConstraint(newVersion, constraintVersion string) bool { + constraintMajor, constraintMinor := version.ExtractMajorMinor(constraintVersion) newMajor, newMinor := version.ExtractMajorMinor(newVersion) - return newMajor == patternMajor && newMinor == patternMinor + return newMajor == constraintMajor && newMinor == constraintMinor } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 10fce7a..cecdfc7 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -23,8 +23,8 @@ func TestLoadConfig_NonExistent(t *testing.T) { if cfg.Upgrade == nil { t.Error("cfg.Upgrade is nil, want non-nil") } - if cfg.Upgrade.Version != "tag" { - t.Errorf("cfg.Upgrade.Version = %q, want %q", cfg.Upgrade.Version, "tag") + if cfg.Upgrade.Format != "tag" { + t.Errorf("cfg.Upgrade.Format = %q, want %q", cfg.Upgrade.Format, "tag") } } @@ -44,10 +44,10 @@ linters: format: indent-width: 4 upgrade: - version: hash + format: hash actions: actions/checkout: - version: ^2.0.0 + constraint: ^2.0.0 ` if err := os.WriteFile(configPath, []byte(content), 0600); err != nil { t.Fatalf("Failed to write test config: %v", err) @@ -70,15 +70,15 @@ upgrade: } // Check upgrade config - if cfg.Upgrade.Version != "hash" { - t.Errorf("cfg.Upgrade.Version = %q, want %q", cfg.Upgrade.Version, "hash") + if cfg.Upgrade.Format != "hash" { + t.Errorf("cfg.Upgrade.Format = %q, want %q", cfg.Upgrade.Format, "hash") } actionCfg, ok := cfg.Upgrade.Actions["actions/checkout"] if !ok { t.Fatal("actions/checkout not found in config") } - if actionCfg.Version != "^2.0.0" { - t.Errorf("actions/checkout version = %q, want %q", actionCfg.Version, "^2.0.0") + if actionCfg.Constraint != "^2.0.0" { + t.Errorf("actions/checkout constraint = %q, want %q", actionCfg.Constraint, "^2.0.0") } } @@ -107,9 +107,9 @@ func TestSaveConfig(t *testing.T) { Enable: []string{"permissions"}, }, Upgrade: &UpgradeConfig{ - Version: "tag", + Format: "tag", Actions: map[string]ActionConfig{ - "actions/checkout": {Version: "^1.0.0"}, + "actions/checkout": {Constraint: "^1.0.0"}, }, }, } @@ -128,8 +128,8 @@ func TestSaveConfig(t *testing.T) { if loaded.Linters.Default != cfg.Linters.Default { t.Errorf("Loaded default = %q, want %q", loaded.Linters.Default, cfg.Linters.Default) } - if loaded.Upgrade.Version != cfg.Upgrade.Version { - t.Errorf("Loaded version = %q, want %q", loaded.Upgrade.Version, cfg.Upgrade.Version) + if loaded.Upgrade.Format != cfg.Upgrade.Format { + t.Errorf("Loaded format = %q, want %q", loaded.Upgrade.Format, cfg.Upgrade.Format) } } @@ -137,21 +137,21 @@ func TestConfig_GetActionConfig(t *testing.T) { cfg := &Config{ Upgrade: &UpgradeConfig{ Actions: map[string]ActionConfig{ - "actions/checkout": {Version: "^2.0.0"}, + "actions/checkout": {Constraint: "^2.0.0"}, }, }, } // Test existing action actionCfg := cfg.GetActionConfig("actions/checkout") - if actionCfg.Version != "^2.0.0" { - t.Errorf("GetActionConfig() version = %q, want %q", actionCfg.Version, "^2.0.0") + if actionCfg.Constraint != "^2.0.0" { + t.Errorf("GetActionConfig() constraint = %q, want %q", actionCfg.Constraint, "^2.0.0") } // Test non-existing action (should return default) actionCfg = cfg.GetActionConfig("actions/setup-go") - if actionCfg.Version != "^1.0.0" { - t.Errorf("GetActionConfig() default version = %q, want %q", actionCfg.Version, "^1.0.0") + if actionCfg.Constraint != "^1.0.0" { + t.Errorf("GetActionConfig() default constraint = %q, want %q", actionCfg.Constraint, "^1.0.0") } } @@ -159,15 +159,15 @@ func TestConfig_GetActionConfig_NilUpgrade(t *testing.T) { cfg := &Config{} actionCfg := cfg.GetActionConfig("actions/checkout") - if actionCfg.Version != "^1.0.0" { - t.Errorf("GetActionConfig() with nil Upgrade = %q, want %q", actionCfg.Version, "^1.0.0") + if actionCfg.Constraint != "^1.0.0" { + t.Errorf("GetActionConfig() with nil Upgrade constraint = %q, want %q", actionCfg.Constraint, "^1.0.0") } } func TestConfig_SetActionConfig(t *testing.T) { cfg := &Config{} - cfg.SetActionConfig("actions/checkout", ActionConfig{Version: "^3.0.0"}) + cfg.SetActionConfig("actions/checkout", ActionConfig{Constraint: "^3.0.0"}) if cfg.Upgrade == nil { t.Fatal("SetActionConfig() did not initialize Upgrade") @@ -177,8 +177,8 @@ func TestConfig_SetActionConfig(t *testing.T) { } actionCfg := cfg.Upgrade.Actions["actions/checkout"] - if actionCfg.Version != "^3.0.0" { - t.Errorf("SetActionConfig() version = %q, want %q", actionCfg.Version, "^3.0.0") + if actionCfg.Constraint != "^3.0.0" { + t.Errorf("SetActionConfig() constraint = %q, want %q", actionCfg.Constraint, "^3.0.0") } } @@ -194,30 +194,30 @@ func TestConfig_GetVersionFormat(t *testing.T) { expected: "tag", }, { - name: "version is tag", + name: "format is tag", cfg: &Config{ - Upgrade: &UpgradeConfig{Version: "tag"}, + Upgrade: &UpgradeConfig{Format: "tag"}, }, expected: "tag", }, { - name: "version is hash", + name: "format is hash", cfg: &Config{ - Upgrade: &UpgradeConfig{Version: "hash"}, + Upgrade: &UpgradeConfig{Format: "hash"}, }, expected: "hash", }, { - name: "version is major", + name: "format is major", cfg: &Config{ - Upgrade: &UpgradeConfig{Version: "major"}, + Upgrade: &UpgradeConfig{Format: "major"}, }, expected: "major", }, { - name: "empty version defaults to tag", + name: "empty format defaults to tag", cfg: &Config{ - Upgrade: &UpgradeConfig{Version: ""}, + Upgrade: &UpgradeConfig{Format: ""}, }, expected: "tag", }, @@ -592,36 +592,36 @@ func TestShouldUpdate(t *testing.T) { name string currentVersion string newVersion string - pattern string + constraint string expected bool }{ // New version must be greater {"same version", "1.0.0", "1.0.0", "", false}, {"older version", "2.0.0", "1.0.0", "", false}, - {"newer version, empty pattern", "1.0.0", "2.0.0", "", true}, + {"newer version, empty constraint", "1.0.0", "2.0.0", "", true}, - // ^1.0.0 pattern + // ^1.0.0 constraint {"^1.0.0 allows v2", "1.0.0", "v2.0.0", "^1.0.0", true}, {"^1.0.0 allows v5", "1.0.0", "v5.0.0", "^1.0.0", true}, - // ^2.0.0 pattern (same major) + // ^2.0.0 constraint (same major) {"^2.0.0 allows v2.5", "2.0.0", "v2.5.0", "^2.0.0", true}, {"^2.0.0 rejects v3", "2.0.0", "v3.0.0", "^2.0.0", false}, - // ~2.5.0 pattern (same major.minor) + // ~2.5.0 constraint (same major.minor) {"~2.5.0 allows v2.5.1", "2.5.0", "v2.5.1", "~2.5.0", true}, {"~2.5.0 rejects v2.6.0", "2.5.0", "v2.6.0", "~2.5.0", false}, - // Invalid pattern - {"invalid pattern", "1.0.0", "2.0.0", "invalid", false}, + // Invalid constraint + {"invalid constraint", "1.0.0", "2.0.0", "invalid", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := ShouldUpdate(tt.currentVersion, tt.newVersion, tt.pattern) + result := ShouldUpdate(tt.currentVersion, tt.newVersion, tt.constraint) if result != tt.expected { t.Errorf("ShouldUpdate(%q, %q, %q) = %v, want %v", - tt.currentVersion, tt.newVersion, tt.pattern, result, tt.expected) + tt.currentVersion, tt.newVersion, tt.constraint, result, tt.expected) } }) } @@ -662,19 +662,19 @@ func TestUpgradeConfig_EnsureDefaults(t *testing.T) { wantVer string }{ { - name: "nil actions and empty version", + name: "nil actions and empty format", config: &UpgradeConfig{}, wantVer: "tag", }, { - name: "nil actions with existing version", - config: &UpgradeConfig{Version: "hash"}, + name: "nil actions with existing format", + config: &UpgradeConfig{Format: "hash"}, wantVer: "hash", }, { - name: "existing actions with empty version", + name: "existing actions with empty format", config: &UpgradeConfig{ - Actions: map[string]ActionConfig{"test": {Version: "^1.0.0"}}, + Actions: map[string]ActionConfig{"test": {Constraint: "^1.0.0"}}, }, wantVer: "tag", }, @@ -687,8 +687,8 @@ func TestUpgradeConfig_EnsureDefaults(t *testing.T) { if tt.config.Actions == nil { t.Error("Actions is nil after EnsureDefaults") } - if tt.config.Version != tt.wantVer { - t.Errorf("Version = %q, want %q", tt.config.Version, tt.wantVer) + if tt.config.Format != tt.wantVer { + t.Errorf("Format = %q, want %q", tt.config.Format, tt.wantVer) } }) } @@ -710,7 +710,7 @@ func TestConfig_Validate(t *testing.T) { config: &Config{ Run: &RunConfig{Timeout: "5m", IssuesExitCode: 2}, Linters: &LinterConfig{Default: "all", Enable: []string{"versions"}}, - Upgrade: &UpgradeConfig{Version: "tag"}, + Upgrade: &UpgradeConfig{Format: "tag"}, }, wantErr: false, }, @@ -745,8 +745,8 @@ func TestConfig_Validate(t *testing.T) { wantErr: true, }, { - name: "invalid upgrade version format", - config: &Config{Upgrade: &UpgradeConfig{Version: "invalid"}}, + name: "invalid upgrade output format", + config: &Config{Upgrade: &UpgradeConfig{Format: "invalid"}}, wantErr: true, }, { diff --git a/internal/config/upgrade_config.go b/internal/config/upgrade_config.go index 478ad1e..7684788 100644 --- a/internal/config/upgrade_config.go +++ b/internal/config/upgrade_config.go @@ -6,8 +6,8 @@ import ( ) const ( - defaultVersionPattern = "^1.0.0" - defaultUpgradeVersion = "tag" + defaultVersionConstraint = "^1.0.0" + defaultUpgradeFormat = "tag" ) // Valid version formats for upgrades. @@ -16,12 +16,12 @@ var validVersionFormats = []string{"tag", "hash", "major"} // UpgradeConfig specifies settings for the upgrade command. type UpgradeConfig struct { Actions map[string]ActionConfig `yaml:"actions"` - Version string `yaml:"version"` // "tag", "hash", or "major" + Format string `yaml:"format"` // "tag", "hash", or "major" } -// ActionConfig specifies the version update pattern for a GitHub Action. +// ActionConfig specifies the version constraint for a GitHub Action. type ActionConfig struct { - Version string `yaml:"version"` + Constraint string `yaml:"constraint"` } // Validate checks UpgradeConfig for invalid values. @@ -29,8 +29,8 @@ func (u *UpgradeConfig) Validate() error { if u == nil { return nil } - if u.Version != "" && !slices.Contains(validVersionFormats, u.Version) { - return fmt.Errorf("upgrade.version must be one of %v, got %q", validVersionFormats, u.Version) + if u.Format != "" && !slices.Contains(validVersionFormats, u.Format) { + return fmt.Errorf("upgrade.format must be one of %v, got %q", validVersionFormats, u.Format) } return nil } @@ -39,7 +39,7 @@ func (u *UpgradeConfig) Validate() error { func DefaultUpgradeConfig() *UpgradeConfig { return &UpgradeConfig{ Actions: make(map[string]ActionConfig), - Version: defaultUpgradeVersion, + Format: defaultUpgradeFormat, } } @@ -48,7 +48,7 @@ func (u *UpgradeConfig) EnsureDefaults() { if u.Actions == nil { u.Actions = make(map[string]ActionConfig) } - if u.Version == "" { - u.Version = defaultUpgradeVersion + if u.Format == "" { + u.Format = defaultUpgradeFormat } } diff --git a/internal/upgrader/upgrader.go b/internal/upgrader/upgrader.go index 6ea96ee..acc99a0 100644 --- a/internal/upgrader/upgrader.go +++ b/internal/upgrader/upgrader.go @@ -122,7 +122,7 @@ func (u *Upgrader) loadAndInitConfig() (*config.Config, error) { for _, action := range wfActions { name := config.NormalizeActionName(action.Uses) - if cfg.Upgrade.Actions[name].Version == "" { + if cfg.Upgrade.Actions[name].Constraint == "" { cfg.SetActionConfig(name, config.DefaultActionConfig) } } @@ -170,7 +170,7 @@ func (u *Upgrader) checkForUpdate(cfg *config.Config, wf *workflow.Workflow, currentVersion, warning := u.resolveCurrentVersion(actionInfo) latestTag, latestHash, err := u.getLatestVersion(cfg, actionInfo, actionName, - currentVersion, actionCfg.Version) + currentVersion, actionCfg.Constraint) if err != nil { return nil, fmt.Errorf("failed to check %s: %w", actionName, err) } @@ -184,13 +184,13 @@ func (u *Upgrader) checkForUpdate(cfg *config.Config, wf *workflow.Workflow, return nil, nil } - // Determine version pattern for update check - pattern := actionCfg.Version + // Determine version constraint for update check + constraint := actionCfg.Constraint if _, exists := cfg.Upgrade.Actions[actionName]; !exists { - pattern = "" // Allow any newer version if not in config + constraint = "" // Allow any newer version if not in config } - versionNeedsUpdate := config.ShouldUpdate(currentVersion, latestTag, pattern) + versionNeedsUpdate := config.ShouldUpdate(currentVersion, latestTag, constraint) // Update if either version or format needs changing if !versionNeedsUpdate && !formatNeedsUpdate { @@ -231,11 +231,11 @@ func (u *Upgrader) resolveCurrentVersion(info *actions.ActionInfo) (string, stri // getLatestVersion fetches the latest version based on config constraints. func (u *Upgrader) getLatestVersion(cfg *config.Config, info *actions.ActionInfo, actionName, - currentVersion, pattern string) (string, string, error) { + currentVersion, constraint string) (string, string, error) { if _, exists := cfg.Upgrade.Actions[actionName]; !exists { return u.client.GetLatestVersionUnconstrained(info.Owner, info.Repo) } - return u.client.GetLatestVersion(info.Owner, info.Repo, currentVersion, pattern) + return u.client.GetLatestVersion(info.Owner, info.Repo, currentVersion, constraint) } // applyUpdate applies a single update to the workflow file. diff --git a/internal/upgrader/upgrader_test.go b/internal/upgrader/upgrader_test.go index 24e26ce..e518bc1 100644 --- a/internal/upgrader/upgrader_test.go +++ b/internal/upgrader/upgrader_test.go @@ -81,10 +81,10 @@ jobs: configPath := testutil.CreateConfig(t, tmpDir, ` upgrade: - version: tag + format: tag actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 `) wf, err := workflow.LoadWorkflow(workflowPath) @@ -120,10 +120,10 @@ jobs: configPath := testutil.CreateConfig(t, tmpDir, ` upgrade: - version: tag + format: tag actions: actions/checkout: - version: ^1.0.0 + constraint: ^1.0.0 `) wf, err := workflow.LoadWorkflow(workflowPath) @@ -158,10 +158,10 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: tag + format: tag actions: actions/checkout: - version: "^1.0.0" + constraint: "^1.0.0" ` configPath := testutil.CreateConfig(t, tmpDir, configContent) @@ -217,10 +217,10 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: hash + format: hash actions: actions/checkout: - version: "^1.0.0" + constraint: "^1.0.0" ` configPath := testutil.CreateConfig(t, tmpDir, configContent) @@ -277,10 +277,10 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: tag + format: tag actions: actions/checkout: - version: "^4.0.0" + constraint: "^4.0.0" ` configPath := testutil.CreateConfig(t, tmpDir, configContent) @@ -343,10 +343,10 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: major + format: major actions: actions/checkout: - version: "^1.0.0" + constraint: "^1.0.0" ` configPath := testutil.CreateConfig(t, tmpDir, configContent) @@ -403,10 +403,10 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: tag + format: tag actions: actions/checkout: - version: "^4.0.0" + constraint: "^4.0.0" ` configPath := testutil.CreateConfig(t, tmpDir, configContent) @@ -448,7 +448,7 @@ jobs: workflowPath := testutil.CreateWorkflow(t, tmpDir, "test.yml", workflowContent) configContent := `upgrade: - version: tag + format: tag ` configPath := testutil.CreateConfig(t, tmpDir, configContent)