diff --git a/AGENTS.md b/AGENTS.md index de00e9ce874..c0c36126713 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -480,6 +480,35 @@ When developing a new command: ### Go Code Style - **ALWAYS use `any` instead of `interface{}`** - Use the modern `any` type alias (Go 1.18+) for consistency across the codebase +### YAML Library Usage + +**Primary YAML Library**: `goccy/go-yaml` v1.19.1 + +gh-aw uses `goccy/go-yaml` for YAML 1.1/1.2 compatibility with GitHub Actions. See specs/yaml-version-gotchas.md for details on YAML version differences. + +**Standard YAML Library**: `go.yaml.in/yaml/v3` v3.0.4 + +For simple YAML marshaling/unmarshaling operations where YAML 1.1/1.2 compatibility is not critical, use the canonical `go.yaml.in/yaml/v3` import path (not the deprecated `gopkg.in/yaml.v3`). + +**Migration from `gopkg.in/yaml.v3`**: +- The deprecated `gopkg.in/yaml.v3` path has been migrated to the canonical `go.yaml.in/yaml/v3` path +- Both paths provide identical APIs (`Marshal`, `Unmarshal`, `Encoder`, `Decoder`) +- The canonical path provides better supply chain security and aligns with modern Go ecosystem practices +- Transitive dependencies may still use `gopkg.in/yaml.v3` (e.g., `github.com/cli/go-gh/v2`) - this is acceptable + +**When to use each library**: +- **`goccy/go-yaml`**: Workflow frontmatter parsing, GitHub Actions YAML generation, any YAML 1.1/1.2 sensitive operations +- **`go.yaml.in/yaml/v3`**: Campaign specs, workflow statistics, simple configuration marshaling + +**Example**: +```go +import "go.yaml.in/yaml/v3" // ✅ Use canonical path + +// Simple marshaling example +data := map[string]any{"key": "value"} +yamlBytes, err := yaml.Marshal(data) +``` + ### Type Patterns and Best Practices Use appropriate type patterns to improve code clarity, maintainability, and type safety: diff --git a/go.mod b/go.mod index 868dbfb26a6..b283faef69a 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,11 @@ require ( github.com/sourcegraph/conc v0.3.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 + go.yaml.in/yaml/v3 v3.0.4 golang.org/x/crypto v0.45.0 golang.org/x/term v0.38.0 golang.org/x/tools/gopls v0.21.0 golang.org/x/vuln v1.1.4 - gopkg.in/yaml.v3 v3.0.1 ) require ( @@ -260,7 +260,6 @@ require ( go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect - go.yaml.in/yaml/v3 v3.0.4 // indirect go.yaml.in/yaml/v4 v4.0.0-rc.3 // indirect golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect golang.org/x/mod v0.30.0 // indirect @@ -277,6 +276,7 @@ require ( google.golang.org/protobuf v1.36.8 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect honnef.co/go/tools v0.7.0-0.dev.0.20251022135355-8273271481d0 // indirect mvdan.cc/gofumpt v0.9.2 // indirect mvdan.cc/unparam v0.0.0-20251027182757-5beb8c8f8f15 // indirect diff --git a/pkg/campaign/loader.go b/pkg/campaign/loader.go index 7b6fee6f6c0..54a5cfbe224 100644 --- a/pkg/campaign/loader.go +++ b/pkg/campaign/loader.go @@ -9,7 +9,7 @@ import ( "github.com/githubnext/gh-aw/pkg/logger" "github.com/githubnext/gh-aw/pkg/parser" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) var log = logger.New("campaign:loader") diff --git a/pkg/cli/compile_orchestrator.go b/pkg/cli/compile_orchestrator.go index abe4d862f67..a0ef2b63aec 100644 --- a/pkg/cli/compile_orchestrator.go +++ b/pkg/cli/compile_orchestrator.go @@ -10,7 +10,7 @@ import ( "github.com/githubnext/gh-aw/pkg/console" "github.com/githubnext/gh-aw/pkg/logger" "github.com/githubnext/gh-aw/pkg/workflow" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) var compileOrchestratorLog = logger.New("cli:compile_orchestrator") diff --git a/pkg/cli/compile_stats.go b/pkg/cli/compile_stats.go index afa67e93593..c7c40835813 100644 --- a/pkg/cli/compile_stats.go +++ b/pkg/cli/compile_stats.go @@ -10,7 +10,7 @@ import ( "github.com/githubnext/gh-aw/pkg/logger" "github.com/githubnext/gh-aw/pkg/styles" "github.com/githubnext/gh-aw/pkg/tty" - "gopkg.in/yaml.v3" + "go.yaml.in/yaml/v3" ) var compileStatsLog = logger.New("cli:compile_stats") diff --git a/pkg/workflow/compiler_jobs.go b/pkg/workflow/compiler_jobs.go index 4ed345eadc9..3a4eb3f4c41 100644 --- a/pkg/workflow/compiler_jobs.go +++ b/pkg/workflow/compiler_jobs.go @@ -336,7 +336,7 @@ func (c *Compiler) buildCustomJobs(data *WorkflowData, activationJobCreated bool // Extract permissions if permissions, hasPermissions := configMap["permissions"]; hasPermissions { if permsMap, ok := permissions.(map[string]any); ok { - // Use gopkg.in/yaml.v3 to marshal permissions + // Use goccy/go-yaml to marshal permissions yamlBytes, err := yaml.Marshal(permsMap) if err != nil { return fmt.Errorf("failed to convert permissions to YAML for job '%s': %w", jobName, err)