Skip to content
Closed
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
10 changes: 10 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ linters:
- G703 # Path traversal via taint - paths come from admin config or are internal CI/CD pipeline paths, not user input
- G705 # XSS via taint - raw-response/static-file steps write admin-configured content by design
- G706 # Log injection via taint - step.log and publish steps are designed to log arbitrary admin-configured content
- G710 # Open redirect via taint - OAuth2 redirect URLs come from the authorization server (AuthCodeURL); intentional by design
gocritic:
enabled-tags:
- diagnostic
Expand All @@ -53,6 +54,15 @@ linters:
linters:
- staticcheck
text: "SA5011"
# ui/node_modules contains third-party Go code that should not be linted
- path: ui/node_modules/
linters:
- govet
- staticcheck
- unused
- gocritic
- gosec
- errcheck
presets:
- std-error-handling

Expand Down
45 changes: 0 additions & 45 deletions cmd/iac-codemod/add_validate_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import (
"go/token"
"io"
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
Expand Down Expand Up @@ -643,50 +642,6 @@ func qualifierFromProviderMethods(methods []*ast.FuncDecl) string {
return ""
}

// siblingUsesInterfacesImport returns true if any non-test .go file
// in dir (other than excludePath) imports
// github.com/GoCodeAlone/workflow/interfaces. Used to decide whether
// to inject an interfaces import into a file that doesn't have one
// when emitting a qualified ValidatePlan stub (review round-4 #1).
func siblingUsesInterfacesImport(dir, excludePath string) bool {
const wantPath = "github.com/GoCodeAlone/workflow/interfaces"
entries, err := os.ReadDir(dir)
if err != nil {
return false
}
for _, e := range entries {
if e.IsDir() {
continue
}
name := e.Name()
if !strings.HasSuffix(name, ".go") || strings.HasSuffix(name, "_test.go") {
continue
}
fpath := filepath.Join(dir, name)
if fpath == excludePath {
continue
}
src, err := readFile(fpath)
if err != nil {
continue
}
fs := token.NewFileSet()
sib, err := parser.ParseFile(fs, fpath, src, parser.ImportsOnly)
if err != nil {
continue
}
for _, imp := range sib.Imports {
if imp.Path == nil {
continue
}
if strings.Trim(imp.Path.Value, `"`) == wantPath {
return true
}
}
}
return false
}

// interfacesQualifier returns the package alias `file` uses for
// github.com/GoCodeAlone/workflow/interfaces. If the import is
// renamed (`alias "github.com/.../interfaces"`), the alias name is
Expand Down
33 changes: 0 additions & 33 deletions cmd/iac-codemod/lint.go
Original file line number Diff line number Diff line change
Expand Up @@ -1039,39 +1039,6 @@ func receiverTypeName(fn *ast.FuncDecl) string {
return id.Name
}

// bodyCallsSelector reports whether the function body contains a
// CallExpr whose callee is a SelectorExpr with the given X.Name and
// Sel.Name, e.g. `wfctlhelpers.Plan(...)`.
func bodyCallsSelector(body *ast.BlockStmt, pkgIdent, selName string) bool {
if body == nil {
return false
}
found := false
ast.Inspect(body, func(n ast.Node) bool {
if found {
return false
}
call, ok := n.(*ast.CallExpr)
if !ok {
return true
}
sel, ok := call.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
x, ok := sel.X.(*ast.Ident)
if !ok {
return true
}
if x.Name == pkgIdent && sel.Sel.Name == selName {
found = true
return false
}
return true
})
return found
}

// bodyReferencesField reports whether the function body references any
// SelectorExpr with the given Sel.Name, e.g. any `<X>.ForceNew`.
func bodyReferencesField(body *ast.BlockStmt, fieldName string) bool {
Expand Down
12 changes: 4 additions & 8 deletions cmd/iac-codemod/refactor_apply.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ func init() {
modes["refactor-apply"] = runRefactorApply
}

// applyCanonicalCallExpr is the canonical replacement-body expression
// emitted by refactor-apply.
const applyCanonicalCallExpr = "wfctlhelpers.ApplyPlan(ctx, p, plan)"

// applyClassification labels the disposition of a single Apply()
// method site. The non-canonical idioms are surfaced as distinct
// classes so the report can suggest the right hand-port handling.
Expand Down Expand Up @@ -123,7 +119,7 @@ func runRefactorApply(args []string, opts *Options, stdout, stderr io.Writer) in
if *reportFile != "" {
var buf bytes.Buffer
report.print(&buf, opts)
if err := os.WriteFile(*reportFile, buf.Bytes(), 0o644); err != nil {
if err := os.WriteFile(*reportFile, buf.Bytes(), 0o600); err != nil {
fmt.Fprintf(stderr, "iac-codemod refactor-apply: write report-file %s: %v\n", *reportFile, err)
return 1
}
Expand Down Expand Up @@ -476,7 +472,7 @@ func isCanonicalApplyLoopAssign(a *ast.AssignStmt, recvName string) bool {
if !ok {
return false
}
if !((recvName != "" && x.Name == recvName) || (recvName == "" && x.Name == "p")) {
if (recvName == "" || x.Name != recvName) && (recvName != "" || x.Name != "p") {
return false
}
// Round-12 #7: also verify the lookup KEY is `action.Resource.Type`.
Expand Down Expand Up @@ -1162,7 +1158,7 @@ func isProviderIDGuard(ifs *ast.IfStmt) bool {
if id, ok := be.Y.(*ast.Ident); ok && id.Name == "nil" {
yIsNil = true
}
if !(xIsCurrent && yIsNil) {
if !xIsCurrent || !yIsNil {
// Allow the reverse order too (`nil != action.Current`),
// though it's not idiomatic Go.
yIsCurrent := false
Expand All @@ -1173,7 +1169,7 @@ func isProviderIDGuard(ifs *ast.IfStmt) bool {
if id, ok := be.X.(*ast.Ident); ok && id.Name == "nil" {
xIsNil = true
}
if !(yIsCurrent && xIsNil) {
if !yIsCurrent || !xIsNil {
return false
}
}
Expand Down
7 changes: 0 additions & 7 deletions cmd/iac-codemod/refactor_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ const helperImportPath = "github.com/GoCodeAlone/workflow/iac/wfctlhelpers"
// files would fail to compile".
const planHelperImportPath = "github.com/GoCodeAlone/workflow/platform"

// planCanonicalCallExpr is the canonical replacement-body expression
// emitted by refactor-plan. Calls platform.ComputePlan (the real helper);
// see planHelperImportPath above for the review-correction rationale.
const planCanonicalCallExpr = "platform.ComputePlan(ctx, p, desired, current)"

// planClassification labels the disposition of a single Plan() method
// site. Each report entry carries one classification; the rewriter
// honors only `planCanonical`.
Expand Down Expand Up @@ -1108,13 +1103,11 @@ func rewritePlanBody(fn *ast.FuncDecl, file *ast.File) {
ast.NewIdent(currentName),
},
}
// plan, err := platform.ComputePlan(ctx, p, desired, current)
planAssign := &ast.AssignStmt{
Lhs: []ast.Expr{ast.NewIdent("plan"), ast.NewIdent("err")},
Tok: token.DEFINE,
Rhs: []ast.Expr{call},
}
// return &plan, err
returnStmt := &ast.ReturnStmt{
Results: []ast.Expr{
&ast.UnaryExpr{Op: token.AND, X: ast.NewIdent("plan")},
Expand Down
2 changes: 1 addition & 1 deletion cmd/wfctl/infra.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ func runInfraPlan(args []string) error {
// cmd/wfctl/main.go's top-level wrapper. errors.New (rather than
// fmt.Errorf) avoids govet's no-verbs noise and is canonical for
// fixed-string error literals per Go convention.
return errors.New("this plan requires JIT resolution; persisted plan.json is not supported. Run 'wfctl infra apply' directly without -o/--plan.")
return errors.New("this plan requires JIT resolution; persisted plan.json is not supported. Run 'wfctl infra apply' directly without -o/--plan")
}
// Embed a hash of the desired-state inputs so wfctl infra apply --plan
// can detect stale plans when the config changes after plan generation.
Expand Down
11 changes: 4 additions & 7 deletions cmd/wfctl/infra_bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,10 @@ func bootstrapSecrets(ctx context.Context, provider secrets.Provider, cfg *Secre
if forceRotate[gen.Key] {
deleteKey := gen.Key
if delErr := provider.Delete(ctx, deleteKey); delErr != nil {
if errors.Is(delErr, secrets.ErrNotFound) || errors.Is(delErr, secrets.ErrUnsupported) {
// Secret was already absent or provider doesn't support Delete.
// Log a warning and continue — the create path handles this.
fmt.Fprintf(os.Stderr, "warn: rotate-pre-delete %s: %v (continuing)\n", deleteKey, delErr)
} else {
fmt.Fprintf(os.Stderr, "warn: rotate-pre-delete %s: %v (continuing)\n", deleteKey, delErr)
}
// Secret was already absent, provider doesn't support Delete,
// or another error. Log a warning and continue — the create
// path handles this.
fmt.Fprintf(os.Stderr, "warn: rotate-pre-delete %s: %v (continuing)\n", deleteKey, delErr)
}
// Invalidate the List cache so the existence check below reflects the
// deletion. A fresh List call after Delete is the safe path for
Expand Down
2 changes: 1 addition & 1 deletion cmd/wfctl/infra_plan_jit_reject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import (
// prefix from cmd/wfctl/main.go's top-level error wrapper; the value
// returned by runInfraPlan (and asserted here) does NOT include that
// prefix.
const expectedJITRejectError = "this plan requires JIT resolution; persisted plan.json is not supported. Run 'wfctl infra apply' directly without -o/--plan."
const expectedJITRejectError = "this plan requires JIT resolution; persisted plan.json is not supported. Run 'wfctl infra apply' directly without -o/--plan"

// TestInfraPlan_RejectsPersistedJITPlan_WithExactErrorString is the
// canonical T5.5 scenario: a config whose env_vars carry a
Expand Down
31 changes: 15 additions & 16 deletions example/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,32 @@ require (
github.com/Workiva/go-datastructures v1.1.7 // indirect
github.com/andybalholm/brotli v1.2.1 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.6 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.14 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.14 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/config v1.32.16 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.15 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.23 // indirect
github.com/aws/aws-sdk-go-v2/service/apigatewayv2 v1.34.1 // indirect
github.com/aws/aws-sdk-go-v2/service/applicationautoscaling v1.41.14 // indirect
github.com/aws/aws-sdk-go-v2/service/codebuild v1.68.13 // indirect
github.com/aws/aws-sdk-go-v2/service/ec2 v1.297.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ecs v1.78.0 // indirect
github.com/aws/aws-sdk-go-v2/service/eks v1.82.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.8 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.22 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
github.com/aws/aws-sdk-go-v2/service/kinesis v1.43.5 // indirect
github.com/aws/aws-sdk-go-v2/service/route53 v1.62.5 // indirect
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
github.com/aws/smithy-go v1.24.3 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.0.10 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.30.16 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.20 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.42.0 // indirect
github.com/aws/smithy-go v1.25.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
Expand All @@ -78,7 +77,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/digitalocean/godo v1.184.0 // indirect
github.com/digitalocean/godo v1.186.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v28.5.2+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
Expand Down
Loading
Loading