Skip to content

Commit 0ac7efd

Browse files
authored
refactor: reorganize misplaced functions per semantic clustering analysis (#30770)
1 parent dee0177 commit 0ac7efd

7 files changed

Lines changed: 147 additions & 140 deletions

pkg/workflow/action_sha_checker.go

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"os"
77
"regexp"
88

9-
"github.com/github/gh-aw/pkg/console"
109
"github.com/github/gh-aw/pkg/logger"
1110
"github.com/goccy/go-yaml"
1211
)
@@ -144,68 +143,3 @@ func CheckActionSHAUpdates(actions []ActionUsage, resolver *ActionResolver) []Ac
144143

145144
return results
146145
}
147-
148-
// ValidateActionSHAsInLockFile validates action SHAs in a lock file and emits warnings
149-
func ValidateActionSHAsInLockFile(lockFilePath string, cache *ActionCache, verbose bool) error {
150-
actionSHACheckerLog.Printf("Validating action SHAs in: %s", lockFilePath)
151-
152-
// Extract actions from lock file
153-
actions, err := ExtractActionsFromLockFile(lockFilePath)
154-
if err != nil {
155-
return fmt.Errorf("failed to extract actions: %w", err)
156-
}
157-
158-
if len(actions) == 0 {
159-
actionSHACheckerLog.Print("No pinned actions found in lock file")
160-
if verbose {
161-
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No pinned actions to validate"))
162-
}
163-
return nil
164-
}
165-
166-
// Create resolver for checking latest SHAs
167-
resolver := NewActionResolver(cache)
168-
169-
// Check for updates
170-
checks := CheckActionSHAUpdates(actions, resolver)
171-
172-
// Count and report updates
173-
updateCount := 0
174-
for _, check := range checks {
175-
if check.NeedsUpdate {
176-
updateCount++
177-
// Emit warning (FormatWarningMessage adds the warning emoji)
178-
warningMsg := fmt.Sprintf("%s@%s has a newer SHA available: %s → %s",
179-
check.Action.Repo,
180-
check.Action.Version,
181-
check.Action.SHA[:7],
182-
check.LatestSHA[:7])
183-
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(warningMsg))
184-
185-
// Show full SHA in verbose mode
186-
if verbose {
187-
fmt.Fprintf(os.Stderr, " Current: %s\n", check.Action.SHA)
188-
fmt.Fprintf(os.Stderr, " Latest: %s\n", check.LatestSHA)
189-
}
190-
}
191-
}
192-
193-
if updateCount > 0 {
194-
actionSHACheckerLog.Printf("Found %d actions that need updating", updateCount)
195-
// Save the cache with updated SHAs so the next compilation will use them
196-
if err := cache.Save(); err != nil {
197-
actionSHACheckerLog.Printf("Warning: failed to save action cache: %v", err)
198-
} else {
199-
actionSHACheckerLog.Print("Saved updated action cache")
200-
}
201-
// Provide suggestion to fix the issue
202-
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("To apply updated action SHAs, recompile with: gh aw compile"))
203-
if verbose {
204-
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Found %d action(s) with available updates", updateCount)))
205-
}
206-
} else {
207-
actionSHACheckerLog.Print("All actions are up to date")
208-
}
209-
210-
return nil
211-
}

pkg/workflow/engine_helpers.go

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -360,24 +360,3 @@ func FilterEnvForSecrets(env map[string]string, allowedNamesAndKeys []string) ma
360360
engineHelpersLog.Printf("Filtered environment variables: kept=%d, removed=%d", len(filtered), secretsRemoved)
361361
return filtered
362362
}
363-
364-
// EngineHasValidateSecretStep checks if the engine provides a validate-secret step.
365-
// This is used to determine whether the secret_verification_result job output should be added.
366-
//
367-
// The validate-secret step is provided by engines that override GetSecretValidationStep():
368-
// - Copilot engine: Adds step unless copilot-requests feature is enabled or custom command is set
369-
// - Claude engine: Adds step unless custom command is set
370-
// - Codex engine: Adds step unless custom command is set
371-
// - Gemini engine: Adds step unless custom command is set
372-
// - Custom engine: Never adds this step (uses BaseEngine default which returns empty)
373-
//
374-
// Parameters:
375-
// - engine: The agentic engine to check
376-
// - data: The workflow data (needed for GetSecretValidationStep)
377-
//
378-
// Returns:
379-
// - bool: true if the engine provides a validate-secret step, false otherwise
380-
func EngineHasValidateSecretStep(engine CodingAgentEngine, data *WorkflowData) bool {
381-
step := engine.GetSecretValidationStep(data)
382-
return len(step) > 0
383-
}

pkg/workflow/engine_validation.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,3 +399,24 @@ func (c *Compiler) validatePiEngineRequirements(workflowData *WorkflowData) erro
399399
engineValidationLog.Print("Pi engine requirements satisfied: gh-proxy and cli-proxy are enabled")
400400
return nil
401401
}
402+
403+
// EngineHasValidateSecretStep checks if the engine provides a validate-secret step.
404+
// This is used to determine whether the secret_verification_result job output should be added.
405+
//
406+
// The validate-secret step is provided by engines that override GetSecretValidationStep():
407+
// - Copilot engine: Adds step unless copilot-requests feature is enabled or custom command is set
408+
// - Claude engine: Adds step unless custom command is set
409+
// - Codex engine: Adds step unless custom command is set
410+
// - Gemini engine: Adds step unless custom command is set
411+
// - Custom engine: Never adds this step (uses BaseEngine default which returns empty)
412+
//
413+
// Parameters:
414+
// - engine: The agentic engine to check
415+
// - data: The workflow data (needed for GetSecretValidationStep)
416+
//
417+
// Returns:
418+
// - bool: true if the engine provides a validate-secret step, false otherwise
419+
func EngineHasValidateSecretStep(engine CodingAgentEngine, data *WorkflowData) bool {
420+
step := engine.GetSecretValidationStep(data)
421+
return len(step) > 0
422+
}

pkg/workflow/lock_validation.go

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,34 @@
1-
// This file provides validation for workflow lock file schema compatibility.
1+
// This file provides validation for workflow lock files.
22
//
3-
// # Lock File Schema Validation
3+
// # Lock File Validation
44
//
5-
// This file validates that lock files use a schema version that the current
6-
// build of gh-aw supports. It provides actionable error messages when a
7-
// lock file was generated by an incompatible version.
5+
// This file validates lock files at two levels:
6+
// - Schema compatibility: ensures the lock file's schema version is supported by the
7+
// current build of gh-aw, providing actionable error messages when a lock file
8+
// was generated by an incompatible version.
9+
// - Action SHA freshness: checks whether pinned GitHub Actions SHAs are up to date
10+
// and emits warnings when newer SHAs are available.
811
//
912
// # Validation Functions
1013
//
1114
// - ValidateLockSchemaCompatibility() - Validates lock file schema version
15+
// - ValidateActionSHAsInLockFile() - Validates action SHAs and emits update warnings
1216
//
1317
// # When to Add Validation Here
1418
//
1519
// Add validation to this file when:
1620
// - Adding new lock file format constraints
1721
// - Adding migration validation for schema upgrades
22+
// - Adding user-facing orchestration that validates lock file content
1823

1924
package workflow
2025

2126
import (
2227
"fmt"
28+
"os"
2329
"strings"
30+
31+
"github.com/github/gh-aw/pkg/console"
2432
)
2533

2634
// ValidateLockSchemaCompatibility validates that a lock file's schema is compatible.
@@ -57,3 +65,68 @@ func ValidateLockSchemaCompatibility(content string, lockFilePath string) error
5765
lockSchemaLog.Printf("Lock file schema validated: %s (version=%s)", lockFilePath, metadata.SchemaVersion)
5866
return nil
5967
}
68+
69+
// ValidateActionSHAsInLockFile validates action SHAs in a lock file and emits warnings
70+
func ValidateActionSHAsInLockFile(lockFilePath string, cache *ActionCache, verbose bool) error {
71+
actionSHACheckerLog.Printf("Validating action SHAs in: %s", lockFilePath)
72+
73+
// Extract actions from lock file
74+
actions, err := ExtractActionsFromLockFile(lockFilePath)
75+
if err != nil {
76+
return fmt.Errorf("failed to extract actions: %w", err)
77+
}
78+
79+
if len(actions) == 0 {
80+
actionSHACheckerLog.Print("No pinned actions found in lock file")
81+
if verbose {
82+
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No pinned actions to validate"))
83+
}
84+
return nil
85+
}
86+
87+
// Create resolver for checking latest SHAs
88+
resolver := NewActionResolver(cache)
89+
90+
// Check for updates
91+
checks := CheckActionSHAUpdates(actions, resolver)
92+
93+
// Count and report updates
94+
updateCount := 0
95+
for _, check := range checks {
96+
if check.NeedsUpdate {
97+
updateCount++
98+
// Emit warning (FormatWarningMessage adds the warning emoji)
99+
warningMsg := fmt.Sprintf("%s@%s has a newer SHA available: %s → %s",
100+
check.Action.Repo,
101+
check.Action.Version,
102+
check.Action.SHA[:7],
103+
check.LatestSHA[:7])
104+
fmt.Fprintln(os.Stderr, console.FormatWarningMessage(warningMsg))
105+
106+
// Show full SHA in verbose mode
107+
if verbose {
108+
fmt.Fprintf(os.Stderr, " Current: %s\n", check.Action.SHA)
109+
fmt.Fprintf(os.Stderr, " Latest: %s\n", check.LatestSHA)
110+
}
111+
}
112+
}
113+
114+
if updateCount > 0 {
115+
actionSHACheckerLog.Printf("Found %d actions that need updating", updateCount)
116+
// Save the cache with updated SHAs so the next compilation will use them
117+
if err := cache.Save(); err != nil {
118+
actionSHACheckerLog.Printf("Warning: failed to save action cache: %v", err)
119+
} else {
120+
actionSHACheckerLog.Print("Saved updated action cache")
121+
}
122+
// Provide suggestion to fix the issue
123+
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("To apply updated action SHAs, recompile with: gh aw compile"))
124+
if verbose {
125+
fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Found %d action(s) with available updates", updateCount)))
126+
}
127+
} else {
128+
actionSHACheckerLog.Print("All actions are up to date")
129+
}
130+
131+
return nil
132+
}

pkg/workflow/model_alias_validation.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,63 @@ package workflow
2222

2323
import (
2424
"fmt"
25+
"math"
2526
"os"
2627
"sort"
28+
"strconv"
2729
"strings"
2830

2931
"github.com/github/gh-aw/pkg/console"
3032
)
3133

3234
var modelAliasValidationLog = newValidationLogger("model_alias")
3335

36+
// ─── Known-parameter validation ───────────────────────────────────────────────
37+
38+
// ValidateEffortParam validates the "effort" parameter value (V-MAF-002).
39+
// Allowed values: low, medium, high.
40+
func ValidateEffortParam(value string) error {
41+
switch value {
42+
case "low", "medium", "high":
43+
return nil
44+
default:
45+
return fmt.Errorf("model parameter 'effort': value %q is not valid; allowed values are: low, medium, high (V-MAF-002)", value)
46+
}
47+
}
48+
49+
// ValidateTemperatureParam validates the "temperature" parameter value (V-MAF-003).
50+
// Must be a finite decimal float in [0.0, 2.0].
51+
func ValidateTemperatureParam(value string) error {
52+
f, err := strconv.ParseFloat(value, 64)
53+
if err != nil {
54+
return fmt.Errorf("model parameter 'temperature': value %q cannot be parsed as a decimal float (V-MAF-003)", value)
55+
}
56+
if math.IsNaN(f) || math.IsInf(f, 0) {
57+
return fmt.Errorf("model parameter 'temperature': value %q is not a finite number (V-MAF-003)", value)
58+
}
59+
if f < 0.0 || f > 2.0 {
60+
return fmt.Errorf("model parameter 'temperature': value %q is out of range; must be in [0.0, 2.0] (V-MAF-003)", value)
61+
}
62+
return nil
63+
}
64+
65+
// ValidateKnownParams validates the known parameters in a parsed identifier.
66+
// Unknown parameters are tolerated (V-MAF-011 emits a warning, not an error).
67+
// Returns an error if a known parameter has an invalid value.
68+
func ValidateKnownParams(params map[string]string) error {
69+
if v, ok := params[modelParamEffort]; ok {
70+
if err := ValidateEffortParam(v); err != nil {
71+
return err
72+
}
73+
}
74+
if v, ok := params[modelParamTemperature]; ok {
75+
if err := ValidateTemperatureParam(v); err != nil {
76+
return err
77+
}
78+
}
79+
return nil
80+
}
81+
3482
// validateModelAliasMap is the main entry point for compile-time model-alias validation.
3583
// It validates:
3684
// - The user-supplied alias map entries in frontmatterModels (V-MAF-001..006, V-MAF-011).

pkg/workflow/model_identifier.go

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,7 @@ package workflow
4747
import (
4848
"errors"
4949
"fmt"
50-
"math"
5150
"regexp"
52-
"strconv"
5351
"strings"
5452

5553
"github.com/github/gh-aw/pkg/logger"
@@ -354,52 +352,6 @@ func validateParamValue(value string) error {
354352
return nil
355353
}
356354

357-
// ─── Known-parameter validation ───────────────────────────────────────────────
358-
359-
// ValidateEffortParam validates the "effort" parameter value (V-MAF-002).
360-
// Allowed values: low, medium, high.
361-
func ValidateEffortParam(value string) error {
362-
switch value {
363-
case "low", "medium", "high":
364-
return nil
365-
default:
366-
return fmt.Errorf("model parameter 'effort': value %q is not valid; allowed values are: low, medium, high (V-MAF-002)", value)
367-
}
368-
}
369-
370-
// ValidateTemperatureParam validates the "temperature" parameter value (V-MAF-003).
371-
// Must be a finite decimal float in [0.0, 2.0].
372-
func ValidateTemperatureParam(value string) error {
373-
f, err := strconv.ParseFloat(value, 64)
374-
if err != nil {
375-
return fmt.Errorf("model parameter 'temperature': value %q cannot be parsed as a decimal float (V-MAF-003)", value)
376-
}
377-
if math.IsNaN(f) || math.IsInf(f, 0) {
378-
return fmt.Errorf("model parameter 'temperature': value %q is not a finite number (V-MAF-003)", value)
379-
}
380-
if f < 0.0 || f > 2.0 {
381-
return fmt.Errorf("model parameter 'temperature': value %q is out of range; must be in [0.0, 2.0] (V-MAF-003)", value)
382-
}
383-
return nil
384-
}
385-
386-
// ValidateKnownParams validates the known parameters in a parsed identifier.
387-
// Unknown parameters are tolerated (V-MAF-011 emits a warning, not an error).
388-
// Returns an error if a known parameter has an invalid value.
389-
func ValidateKnownParams(params map[string]string) error {
390-
if v, ok := params[modelParamEffort]; ok {
391-
if err := ValidateEffortParam(v); err != nil {
392-
return err
393-
}
394-
}
395-
if v, ok := params[modelParamTemperature]; ok {
396-
if err := ValidateTemperatureParam(v); err != nil {
397-
return err
398-
}
399-
}
400-
return nil
401-
}
402-
403355
// UnrecognizedParams returns the list of parameter keys in params that are not
404356
// defined in Section 6 (i.e., not effort or temperature).
405357
// Reserved future parameters (Section 6.3) are returned here as well since they

0 commit comments

Comments
 (0)