From db257c226e274504cad761256cc68af92e959d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:11:57 +0200 Subject: [PATCH 01/53] green: policy model with YAML generation (slice 1) Add internal/policy package with Policy structs and ToYAML(). Wildcard name "*" is normalized to omit from YAML output. --- TODO.md | 19 +++ internal/policy/policy.go | 62 ++++++++++ internal/policy/policy_test.go | 209 +++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) create mode 100644 internal/policy/policy.go create mode 100644 internal/policy/policy_test.go diff --git a/TODO.md b/TODO.md index b8fd42d6f..e95f42afd 100644 --- a/TODO.md +++ b/TODO.md @@ -54,3 +54,22 @@ - [x] Slice 2: Add `--params` flag across all three commands - [x] Slice 3: Show params in `--show-input` output - [x] Slice 4: Update help text and examples + +## kosli create policy-file + +- [ ] Slice 1: Policy model + YAML generation (`internal/policy/`) ← active + - [ ] Empty policy (just `_schema`) produces valid YAML + - [ ] Provenance required=true + - [ ] Provenance with exceptions + - [ ] Trail-compliance required + exceptions + - [ ] Single attestation rule (type only) + - [ ] Attestation with name + if condition + - [ ] Multiple attestations + - [ ] Full policy with all fields + - [ ] `name: "*"` is omitted from YAML output +- [ ] Slice 2: Expression builder +- [ ] Slice 3: Skeleton Cobra command + huh dependency +- [ ] Slice 4: Attestation loop in wizard +- [ ] Slice 5: Expression builder wizard +- [ ] Slice 6: API lookups for flows and custom attestation types +- [ ] Slice 7: Preview screen + polish diff --git a/internal/policy/policy.go b/internal/policy/policy.go new file mode 100644 index 000000000..7fa6848ec --- /dev/null +++ b/internal/policy/policy.go @@ -0,0 +1,62 @@ +package policy + +import "gopkg.in/yaml.v3" + +const SchemaURL = "https://kosli.mintlify.app/schemas/policy/v1" + +type Policy struct { + Schema string `yaml:"_schema"` + Artifacts *ArtifactRules `yaml:"artifacts,omitempty"` +} + +type ArtifactRules struct { + Provenance *BooleanRule `yaml:"provenance,omitempty"` + TrailCompliance *BooleanRule `yaml:"trail-compliance,omitempty"` + Attestations []AttestationRule `yaml:"attestations,omitempty"` +} + +type BooleanRule struct { + Required bool `yaml:"required"` + Exceptions []ExceptionRule `yaml:"exceptions,omitempty"` +} + +type ExceptionRule struct { + If string `yaml:"if"` +} + +type AttestationRule struct { + Type string `yaml:"type"` + Name string `yaml:"name,omitempty"` + If string `yaml:"if,omitempty"` +} + +func NewPolicy() *Policy { + return &Policy{ + Schema: SchemaURL, + } +} + +func (p *Policy) ToYAML() ([]byte, error) { + out := p.normalized() + return yaml.Marshal(out) +} + +// normalized returns a copy with wildcard "*" names cleared so they are omitted from YAML. +func (p *Policy) normalized() *Policy { + cp := *p + if cp.Artifacts != nil { + arts := *cp.Artifacts + if len(arts.Attestations) > 0 { + normalized := make([]AttestationRule, len(arts.Attestations)) + for i, a := range arts.Attestations { + if a.Name == "*" { + a.Name = "" + } + normalized[i] = a + } + arts.Attestations = normalized + } + cp.Artifacts = &arts + } + return &cp +} diff --git a/internal/policy/policy_test.go b/internal/policy/policy_test.go new file mode 100644 index 000000000..f2a6f4439 --- /dev/null +++ b/internal/policy/policy_test.go @@ -0,0 +1,209 @@ +package policy + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestToYAML_EmptyPolicy(t *testing.T) { + p := NewPolicy() + out, err := p.ToYAML() + require.NoError(t, err) + + expected := "_schema: https://kosli.mintlify.app/schemas/policy/v1\n" + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_ProvenanceRequired(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Provenance: &BooleanRule{Required: true}, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + provenance: + required: true +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_ProvenanceWithExceptions(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Provenance: &BooleanRule{ + Required: true, + Exceptions: []ExceptionRule{ + {If: `${{ matches(artifact.name, "^datadog:.*") }}`}, + }, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + provenance: + required: true + exceptions: + - if: ${{ matches(artifact.name, "^datadog:.*") }} +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_TrailComplianceWithExceptions(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + TrailCompliance: &BooleanRule{ + Required: true, + Exceptions: []ExceptionRule{ + {If: `${{ flow.name == "legacy" }}`}, + }, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + trail-compliance: + required: true + exceptions: + - if: ${{ flow.name == "legacy" }} +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_SingleAttestation(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Attestations: []AttestationRule{ + {Type: "snyk"}, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + attestations: + - type: snyk +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_AttestationWithNameAndIf(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Attestations: []AttestationRule{ + { + Type: "pull_request", + Name: "pr-check", + If: `${{ flow.tags.risk-level == "high" }}`, + }, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + attestations: + - type: pull_request + name: pr-check + if: ${{ flow.tags.risk-level == "high" }} +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_MultipleAttestations(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Attestations: []AttestationRule{ + {Type: "snyk", Name: "security-scan"}, + {Type: "junit"}, + {Type: "custom:coverage-metrics", Name: "coverage"}, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + attestations: + - type: snyk + name: security-scan + - type: junit + - type: custom:coverage-metrics + name: coverage +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_FullPolicy(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Provenance: &BooleanRule{ + Required: true, + Exceptions: []ExceptionRule{ + {If: `${{ matches(artifact.name, "^datadog:.*") }}`}, + }, + }, + TrailCompliance: &BooleanRule{ + Required: true, + }, + Attestations: []AttestationRule{ + {Type: "snyk", Name: "security-scan"}, + { + Type: "pull_request", + Name: "pull-request", + If: `${{ flow.tags.risk-level == "high" }}`, + }, + {Type: "custom:coverage-metrics", Name: "coverage"}, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + provenance: + required: true + exceptions: + - if: ${{ matches(artifact.name, "^datadog:.*") }} + trail-compliance: + required: true + attestations: + - type: snyk + name: security-scan + - type: pull_request + name: pull-request + if: ${{ flow.tags.risk-level == "high" }} + - type: custom:coverage-metrics + name: coverage +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_WildcardNameOmitted(t *testing.T) { + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Attestations: []AttestationRule{ + {Type: "snyk", Name: "*"}, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + // name: "*" should be omitted from output + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + attestations: + - type: snyk +` + assert.Equal(t, expected, string(out)) +} From 7ee86f36869208931efd009dd14c82fac2d131c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:12:35 +0200 Subject: [PATCH 02/53] green: expression builder helpers (slice 2) Add FlowNameExpr, FlowNameInExpr, ArtifactNameMatchExpr, ComparisonExpr, CombineExprs, and WrapExpr functions for constructing ${{ }} policy expressions from structured inputs. --- TODO.md | 13 ++----- internal/policy/expression.go | 50 +++++++++++++++++++++++++++ internal/policy/expression_test.go | 54 ++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 11 deletions(-) create mode 100644 internal/policy/expression.go create mode 100644 internal/policy/expression_test.go diff --git a/TODO.md b/TODO.md index e95f42afd..792c60323 100644 --- a/TODO.md +++ b/TODO.md @@ -57,17 +57,8 @@ ## kosli create policy-file -- [ ] Slice 1: Policy model + YAML generation (`internal/policy/`) ← active - - [ ] Empty policy (just `_schema`) produces valid YAML - - [ ] Provenance required=true - - [ ] Provenance with exceptions - - [ ] Trail-compliance required + exceptions - - [ ] Single attestation rule (type only) - - [ ] Attestation with name + if condition - - [ ] Multiple attestations - - [ ] Full policy with all fields - - [ ] `name: "*"` is omitted from YAML output -- [ ] Slice 2: Expression builder +- [x] Slice 1: Policy model + YAML generation (`internal/policy/`) +- [x] Slice 2: Expression builder - [ ] Slice 3: Skeleton Cobra command + huh dependency - [ ] Slice 4: Attestation loop in wizard - [ ] Slice 5: Expression builder wizard diff --git a/internal/policy/expression.go b/internal/policy/expression.go new file mode 100644 index 000000000..14ccf9b17 --- /dev/null +++ b/internal/policy/expression.go @@ -0,0 +1,50 @@ +package policy + +import ( + "fmt" + "strings" +) + +// FlowNameExpr returns a policy expression matching a single flow name. +func FlowNameExpr(name string) string { + return fmt.Sprintf(`${{ flow.name == "%s" }}`, name) +} + +// FlowNameInExpr returns a policy expression matching any of the given flow names. +// For a single name, it returns an equality expression instead. +func FlowNameInExpr(names []string) string { + if len(names) == 1 { + return FlowNameExpr(names[0]) + } + quoted := make([]string, len(names)) + for i, n := range names { + quoted[i] = fmt.Sprintf(`"%s"`, n) + } + return fmt.Sprintf(`${{ flow.name in [%s] }}`, strings.Join(quoted, ", ")) +} + +// ArtifactNameMatchExpr returns a policy expression matching artifact names by regex. +func ArtifactNameMatchExpr(regex string) string { + return fmt.Sprintf(`${{ matches(artifact.name, "%s") }}`, regex) +} + +// ComparisonExpr returns a policy expression comparing a context field to a value. +func ComparisonExpr(context, op, value string) string { + return fmt.Sprintf(`${{ %s %s "%s" }}`, context, op, value) +} + +// CombineExprs joins inner expressions (without ${{ }} wrappers) with a logical operator. +func CombineExprs(op string, exprs ...string) string { + if len(exprs) == 1 { + return WrapExpr(exprs[0]) + } + return fmt.Sprintf("${{ %s }}", strings.Join(exprs, " "+op+" ")) +} + +// WrapExpr adds the ${{ }} wrapper if not already present. +func WrapExpr(raw string) string { + if strings.HasPrefix(raw, "${{") && strings.HasSuffix(raw, "}}") { + return raw + } + return fmt.Sprintf("${{ %s }}", raw) +} diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go new file mode 100644 index 000000000..8ba20c090 --- /dev/null +++ b/internal/policy/expression_test.go @@ -0,0 +1,54 @@ +package policy + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFlowNameExpr(t *testing.T) { + result := FlowNameExpr("prod") + assert.Equal(t, `${{ flow.name == "prod" }}`, result) +} + +func TestFlowNameInExpr(t *testing.T) { + result := FlowNameInExpr([]string{"runner", "saver"}) + assert.Equal(t, `${{ flow.name in ["runner", "saver"] }}`, result) +} + +func TestFlowNameInExpr_Single(t *testing.T) { + result := FlowNameInExpr([]string{"prod"}) + assert.Equal(t, `${{ flow.name == "prod" }}`, result) +} + +func TestArtifactNameMatchExpr(t *testing.T) { + result := ArtifactNameMatchExpr("^datadog:.*") + assert.Equal(t, `${{ matches(artifact.name, "^datadog:.*") }}`, result) +} + +func TestComparisonExpr(t *testing.T) { + result := ComparisonExpr("flow.tags.risk", "==", "high") + assert.Equal(t, `${{ flow.tags.risk == "high" }}`, result) +} + +func TestCombineExprs(t *testing.T) { + e1 := `flow.name == "prod"` + e2 := `artifact.name == "svc"` + result := CombineExprs("and", e1, e2) + assert.Equal(t, `${{ flow.name == "prod" and artifact.name == "svc" }}`, result) +} + +func TestCombineExprs_Single(t *testing.T) { + result := CombineExprs("or", `flow.name == "prod"`) + assert.Equal(t, `${{ flow.name == "prod" }}`, result) +} + +func TestWrapExpr_AddsWrapper(t *testing.T) { + result := WrapExpr(`flow.name == "prod"`) + assert.Equal(t, `${{ flow.name == "prod" }}`, result) +} + +func TestWrapExpr_Idempotent(t *testing.T) { + result := WrapExpr(`${{ flow.name == "prod" }}`) + assert.Equal(t, `${{ flow.name == "prod" }}`, result) +} From 307b8f424ea3ee8090cecb6bb538211431d73546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:14:24 +0200 Subject: [PATCH 03/53] green: skeleton create policy-file command with huh (slice 3) Wire up `kosli create policy-file` as a new subcommand. Adds charmbracelet/huh dependency for interactive TUI wizard. Minimal wizard: provenance + trail-compliance confirms, YAML to stdout. --- TODO.md | 2 +- cmd/kosli/create.go | 1 + cmd/kosli/createPolicyFile.go | 114 ++++ go.mod | 114 ++-- go.sum | 971 ++++++++++++++++++++++++++++++---- 5 files changed, 1054 insertions(+), 148 deletions(-) create mode 100644 cmd/kosli/createPolicyFile.go diff --git a/TODO.md b/TODO.md index 792c60323..858aedfbb 100644 --- a/TODO.md +++ b/TODO.md @@ -59,7 +59,7 @@ - [x] Slice 1: Policy model + YAML generation (`internal/policy/`) - [x] Slice 2: Expression builder -- [ ] Slice 3: Skeleton Cobra command + huh dependency +- [x] Slice 3: Skeleton Cobra command + huh dependency - [ ] Slice 4: Attestation loop in wizard - [ ] Slice 5: Expression builder wizard - [ ] Slice 6: API lookups for flows and custom attestation types diff --git a/cmd/kosli/create.go b/cmd/kosli/create.go index da9ffe9b2..700c920ed 100644 --- a/cmd/kosli/create.go +++ b/cmd/kosli/create.go @@ -20,6 +20,7 @@ func newCreateCmd(out io.Writer) *cobra.Command { newCreateEnvironmentCmd(out), newCreateFlowCmd(out), newCreatePolicyCmd(out), + newCreatePolicyFileCmd(out), newCreateAttestationTypeCmd(out), ) return cmd diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go new file mode 100644 index 000000000..f3b18be39 --- /dev/null +++ b/cmd/kosli/createPolicyFile.go @@ -0,0 +1,114 @@ +package main + +import ( + "fmt" + "io" + "os" + + "github.com/charmbracelet/huh" + "github.com/kosli-dev/cli/internal/policy" + "github.com/spf13/cobra" + "golang.org/x/term" +) + +const createPolicyFileShortDesc = `Interactively create a Kosli environment policy YAML file.` + +const createPolicyFileLongDesc = createPolicyFileShortDesc + ` +Launches an interactive wizard that guides you through building a policy file +conforming to the Kosli environment policy schema. The generated YAML is +written to stdout by default, or to a file with ^--output-file^. + +This command does not upload the policy to Kosli. Use ^kosli create policy^ +to upload the generated file. + +If ^--api-token^ and ^--org^ are set, the wizard will fetch flow names and +custom attestation types from the Kosli API to offer as suggestions. +` + +const createPolicyFileExample = ` +# create a policy file interactively (output to stdout): +kosli create policy-file + +# create a policy file and write to a file: +kosli create policy-file --output-file policy.yml +` + +type createPolicyFileOptions struct { + outputFile string +} + +func newCreatePolicyFileCmd(out io.Writer) *cobra.Command { + o := new(createPolicyFileOptions) + cmd := &cobra.Command{ + Use: "policy-file", + Short: createPolicyFileShortDesc, + Long: createPolicyFileLongDesc, + Example: createPolicyFileExample, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + return o.run(out) + }, + } + + cmd.Flags().StringVarP(&o.outputFile, "output-file", "o", "", "write policy YAML to this file instead of stdout") + + return cmd +} + +func (o *createPolicyFileOptions) run(out io.Writer) error { + if !term.IsTerminal(int(os.Stdin.Fd())) { + return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") + } + + p := policy.NewPolicy() + + var requireProvenance bool + var requireTrailCompliance bool + + err := huh.NewForm( + huh.NewGroup( + huh.NewConfirm(). + Title("Require artifact provenance?"). + Description("All artifacts must belong to a Kosli flow"). + Value(&requireProvenance). + Affirmative("Yes"). + Negative("No"), + huh.NewConfirm(). + Title("Require trail compliance?"). + Description("All artifacts must be part of compliant trails"). + Value(&requireTrailCompliance). + Affirmative("Yes"). + Negative("No"), + ), + ).Run() + if err != nil { + return err + } + + if requireProvenance || requireTrailCompliance { + p.Artifacts = &policy.ArtifactRules{} + if requireProvenance { + p.Artifacts.Provenance = &policy.BooleanRule{Required: true} + } + if requireTrailCompliance { + p.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + } + } + + yamlBytes, err := p.ToYAML() + if err != nil { + return fmt.Errorf("failed to generate policy YAML: %w", err) + } + + if o.outputFile != "" { + err = os.WriteFile(o.outputFile, yamlBytes, 0644) + if err != nil { + return fmt.Errorf("failed to write policy file: %w", err) + } + logger.Info("policy file written to %s", o.outputFile) + return nil + } + + _, err = out.Write(yamlBytes) + return err +} diff --git a/go.mod b/go.mod index 7d658caf4..ffd98ad31 100644 --- a/go.mod +++ b/go.mod @@ -1,35 +1,36 @@ module github.com/kosli-dev/cli -go 1.25.9 +go 1.25.0 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 - github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 + github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 github.com/Masterminds/semver/v3 v3.4.0 - github.com/andygrunwald/go-jira v1.17.0 + github.com/andygrunwald/go-jira v1.16.0 github.com/aws/aws-sdk-go-v2 v1.41.5 github.com/aws/aws-sdk-go-v2/config v1.32.14 github.com/aws/aws-sdk-go-v2/credentials v1.19.14 github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.15 - github.com/aws/aws-sdk-go-v2/service/ecs v1.78.0 - github.com/aws/aws-sdk-go-v2/service/lambda v1.89.0 + github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6 + github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 - github.com/aws/smithy-go v1.24.3 - github.com/containers/image/v5 v5.36.2 - github.com/docker/docker v28.5.2+incompatible + github.com/aws/smithy-go v1.24.2 + github.com/charmbracelet/huh v1.0.0 + github.com/containers/image/v5 v5.34.3 + github.com/docker/docker v28.0.4+incompatible github.com/go-git/go-billy/v5 v5.8.0 - github.com/go-git/go-git/v5 v5.17.2 - github.com/go-playground/validator/v10 v10.30.2 + github.com/go-git/go-git/v5 v5.17.1 + github.com/go-playground/validator/v10 v10.26.0 github.com/google/go-github/v42 v42.0.0 github.com/hashicorp/go-retryablehttp v0.7.8 github.com/joshdk/go-junit v1.0.0 - github.com/mattn/go-shellwords v1.0.13 + github.com/mattn/go-shellwords v1.0.12 github.com/maxcnunes/httpfake v1.2.4 github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 github.com/mitchellh/go-homedir v1.1.0 - github.com/open-policy-agent/opa v1.15.2 + github.com/open-policy-agent/opa v1.13.2 github.com/otiai10/copy v1.14.1 github.com/owenrumney/go-sarif/v2 v2.3.3 github.com/pkg/errors v0.9.1 @@ -41,29 +42,31 @@ require ( github.com/stretchr/testify v1.11.1 github.com/xeonx/timeago v1.0.0-rc5 github.com/yargevad/filepathx v1.0.0 - github.com/zalando/go-keyring v0.2.8 + github.com/zalando/go-keyring v0.2.6 gitlab.com/gitlab-org/api/client-go v1.46.0 - golang.org/x/oauth2 v0.36.0 - golang.org/x/term v0.42.0 + golang.org/x/oauth2 v0.34.0 + golang.org/x/term v0.40.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.35.0 k8s.io/apimachinery v0.35.0 k8s.io/client-go v1.5.2 - k8s.io/kubernetes v1.35.3 - sigs.k8s.io/kind v0.31.0 + k8s.io/kubernetes v1.35.0 + sigs.k8s.io/kind v0.11.1 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.1 // indirect dario.cat/mergo v1.0.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.2.0 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect + github.com/alessio/shellescape v1.4.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // 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 @@ -78,16 +81,24 @@ require ( 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/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect + github.com/catppuccin/go v0.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect + github.com/charmbracelet/bubbletea v1.3.10 // indirect + github.com/charmbracelet/colorprofile v0.3.1 // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.10.1 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.6.3 // indirect - github.com/containerd/errdefs v1.0.0 // indirect - github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.1 // indirect - github.com/containers/storage v1.59.1 // indirect + github.com/containers/storage v1.57.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/danieljoos/wincred v1.2.3 // indirect @@ -98,14 +109,16 @@ require ( github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/evanphx/json-patch/v5 v5.2.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.13 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -119,8 +132,9 @@ require ( github.com/gobwas/glob v0.2.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/godbus/dbus/v5 v5.2.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/cel-go v0.27.0 // indirect github.com/google/gnostic-models v0.7.0 // indirect @@ -130,7 +144,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -146,17 +160,23 @@ require ( github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect - github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/morikuni/aec v1.0.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/onsi/ginkgo/v2 v2.27.2 // indirect @@ -175,6 +195,7 @@ require ( github.com/prometheus/common v0.66.1 // indirect github.com/prometheus/procfs v0.17.0 // indirect github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sagikazarmark/locafero v0.11.0 // indirect @@ -190,38 +211,41 @@ require ( github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/trivago/tgo v1.0.7 // indirect github.com/valyala/fastjson v1.6.7 // indirect - github.com/vektah/gqlparser/v2 v2.5.32 // indirect + github.com/vektah/gqlparser/v2 v2.5.31 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yashtewari/glob-intersection v0.2.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect - go.opentelemetry.io/otel v1.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 // indirect - go.opentelemetry.io/otel/metric v1.43.0 // indirect - go.opentelemetry.io/otel/sdk v1.43.0 // indirect - go.opentelemetry.io/otel/trace v1.43.0 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.49.0 // indirect + golang.org/x/crypto v0.48.0 // indirect golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect - golang.org/x/mod v0.33.0 // indirect + golang.org/x/mod v0.32.0 // indirect golang.org/x/net v0.51.0 // indirect - golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.43.0 // indirect - golang.org/x/text v0.35.0 // indirect - golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.42.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect + golang.org/x/text v0.34.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.41.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect k8s.io/apiextensions-apiserver v0.0.0 // indirect k8s.io/apiserver v0.35.0 // indirect diff --git a/go.sum b/go.sum index 06a48083c..a87b7398a 100644 --- a/go.sum +++ b/go.sum @@ -2,41 +2,66 @@ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeX al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 h1:ldKsKtEIblsgsr6mPwrd9yRntoX6uLz/K89wsldwx/k= -github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3/go.mod h1:MAm7bk0oDLmD8yIkvfbxPW04fxzphPyL+7GzwHxOp6Y= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2 h1:wBx10efdJcl8FSewgc41kAW4AvHPgmJZmN7fpNxn8rc= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2/go.mod h1:zzmu18cpAinSbhC86oWd47nmgbb91Fl+Yac2PE8NdYk= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 h1:JI8PcWOImyvIUEZ0Bbmfe05FOlWkMi2KhjG+cAKaUms= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0/go.mod h1:nJLFPGJkyKfDDyJiPuHIXsCi/gpJkm07EvRgiX7SGlI= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs= github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= +github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= +github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andygrunwald/go-jira v1.17.0 h1:bbu5H676l6MaNcV6A7VDIAjIOQVgzNGEhNAwNI/Cjgo= -github.com/andygrunwald/go-jira v1.17.0/go.mod h1:tiZsPUu9824bwcI2BUXatE4hJbs9rUOif0nv1lkq1hQ= +github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ= +github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= @@ -44,8 +69,13 @@ github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmO github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY= github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= @@ -66,8 +96,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= -github.com/aws/aws-sdk-go-v2/service/ecs v1.78.0 h1:P8s4jrrYr9CUPhoYXS0dI4Zi5oKXa6DWHUkeJ9m/gDQ= -github.com/aws/aws-sdk-go-v2/service/ecs v1.78.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw= +github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6 h1:TE4XBXeHvTTnD4rISqqMET4TwE7St4MrZvnJp+Gg5tY= +github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6/go.mod h1:wAtdeFanDuF9Re/ge4DRDaYe3Wy1OGrU7jG042UcuI4= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= @@ -76,8 +106,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3x github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= -github.com/aws/aws-sdk-go-v2/service/lambda v1.89.0 h1:e4NAllPs/ygQ7W4dTlAuP5N7QpCT+rTij3S8UOv2DD4= -github.com/aws/aws-sdk-go-v2/service/lambda v1.89.0/go.mod h1:6HBXRyFFqOw+ALkJ6YGHfrr20/YXYv6X9pcZErXRvCA= +github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 h1:z926KZ1Ysi8Mbi4biJSAIRFdKemwQpO9M0QUTRLDaXA= +github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2/go.mod h1:c27kk10S36lBYgbG1jR3opn4OAS5Y/4wjJa1GiHK/X4= github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 h1:hlSuz394kV0vhv9drL5lhuEFbEOEP1VyQpy15qWh1Pk= github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= @@ -88,37 +118,98 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6f github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= -github.com/aws/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg= -github.com/aws/smithy-go v1.24.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= +github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= +github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= +github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bytecodealliance/wasmtime-go/v39 v39.0.1 h1:RibaT47yiyCRxMOj/l2cvL8cWiWBSqDXHyqsa9sGcCE= github.com/bytecodealliance/wasmtime-go/v39 v39.0.1/go.mod h1:miR4NYIEBXeDNamZIzpskhJ0z/p8al+lwMWylQ/ZJb4= +github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= +github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws= +github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw= +github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= +github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= +github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= +github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw= +github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= +github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= +github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= +github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= +github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= +github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= +github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= +github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= +github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= +github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= +github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= +github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= +github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containers/image/v5 v5.36.2 h1:GcxYQyAHRF/pLqR4p4RpvKllnNL8mOBn0eZnqJbfTwk= -github.com/containers/image/v5 v5.36.2/go.mod h1:b4GMKH2z/5t6/09utbse2ZiLK/c72GuGLFdp7K69eA4= +github.com/containers/image/v5 v5.34.3 h1:/cMgfyA4Y7ILH7nzWP/kqpkE5Df35Ek4bp5ZPvJOVmI= +github.com/containers/image/v5 v5.34.3/go.mod h1:MG++slvQSZVq5ejAcLdu4APGsKGMb0YHHnAo7X28fdE= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= -github.com/containers/storage v1.59.1 h1:11Zu68MXsEQGBBd+GadPrHPpWeqjKS8hJDGiAHgIqDs= -github.com/containers/storage v1.59.1/go.mod h1:KoAYHnAjP3/cTsRS+mmWZGkufSY2GACiKQ4V3ZLQnR0= +github.com/containers/storage v1.57.2 h1:2roCtTyE9pzIaBDHibK72DTnYkPmwWaq5uXxZdaWK4U= +github.com/containers/storage v1.57.2/go.mod h1:i/Hb4lu7YgFr9G0K6BMjqW0BLJO1sFsnWQwj2UoWCUM= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc= github.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ= @@ -129,20 +220,24 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8NC8w= -github.com/dgraph-io/badger/v4 v4.9.1/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= +github.com/dgraph-io/badger/v4 v4.9.0 h1:tpqWb0NewSrCYqTvywbcXOhQdWcqephkVkbBmaaqHzc= +github.com/dgraph-io/badger/v4 v4.9.0/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= -github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v27.5.1+incompatible h1:JB9cieUT9YNiMITtIsguaN55PLOHhBSz3LKVc6cqWaY= +github.com/docker/cli v27.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= +github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -155,12 +250,16 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/evanphx/json-patch/v5 v5.2.0 h1:8ozOH5xxoMYDt5/u+yMTsVXydVCbTORFnOOoq2lumco= +github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -173,12 +272,15 @@ github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZ github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= -github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -193,19 +295,35 @@ github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDz github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.17.2 h1:B+nkdlxdYrvyFK4GPXVU8w1U+YkbsgciIR7f2sZJ104= -github.com/go-git/go-git/v5 v5.17.2/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= +github.com/go-git/go-git/v5 v5.17.1 h1:WnljyxIzSj9BRRUlnmAU35ohDsjRK0EKmL0evDqi5Jk= +github.com/go-git/go-git/v5 v5.17.1/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= +github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -214,86 +332,174 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= -github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= +github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= +github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec= github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls= github.com/graph-gophers/graphql-go v1.9.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -302,6 +508,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= @@ -318,66 +525,145 @@ github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0 github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU= github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-shellwords v1.0.13 h1:DC0OMEpGjm6LfNFU4ckYcvbQKyp2vE8atyFGXNtDcf4= -github.com/mattn/go-shellwords v1.0.13/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= +github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxcnunes/httpfake v1.2.4 h1:l7s/N7zuG6XpzG+5dUolg5SSoR3hANQxqzAkv+lREko= github.com/maxcnunes/httpfake v1.2.4/go.mod h1:rWVxb0bLKtOUM/5hN3UO1VEdEitz1hfcTXs7UyiK6r0= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= -github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= -github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= +github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= +github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= +github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= +github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= +github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= +github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= +github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= +github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= +github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= +github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= +github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= +github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= +github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= +github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= +github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= +github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= +github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= +github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= +github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/open-policy-agent/opa v1.15.2 h1:dS9q+0Yvruq/VNvWJc5qCvCchn715OWc3HLHXn/UCCc= -github.com/open-policy-agent/opa v1.15.2/go.mod h1:c6SN+7jSsUcKJLQc5P4yhwx8YYDRbjpAiGkBOTqxaa4= +github.com/open-policy-agent/opa v1.13.2 h1:c72l7DhxP4g8DEUBOdaU9QBKyA24dZxCcIuZNRZ0yP4= +github.com/open-policy-agent/opa v1.13.2/go.mod h1:M3Asy9yp1YTusUU5VQuENDe92GLmamIuceqjw+C8PHY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= @@ -385,6 +671,9 @@ github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAq github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.3.3 h1:ubWDJcF5i3L/EIOER+ZyQ03IfplbSU1BLOE26uKQIIU= github.com/owenrumney/go-sarif/v2 v2.3.3/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -393,88 +682,140 @@ github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM= github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= -github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= -github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= -github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= +github.com/vbatts/tar-split v0.11.7 h1:ixZ93pO/GmvaZw4Vq9OwmfZK/kc2zKdPfu0B+gYqs3U= +github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= +github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -487,119 +828,535 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeonx/timeago v1.0.0-rc5 h1:pwcQGpaH3eLfPtXeyPA4DmHWjoQt0Ea7/++FwpxqLxg= github.com/xeonx/timeago v1.0.0-rc5/go.mod h1:qDLrYEFynLO7y5Ho7w3GwgtYgpy5UfhcXIIQvMKVDkA= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA= github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg= github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= -github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= -github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= +github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= gitlab.com/gitlab-org/api/client-go v1.46.0 h1:YxBWFZIFYKcGESCb9fpkwzouo+apyB9pr/XTWzNoL24= gitlab.com/gitlab-org/api/client-go v1.46.0/go.mod h1:FtgyU6g2HS5+fMhw6nLK96GBEEBx5MzntOiJWfIaiN8= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= -go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= -go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= -go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= -go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= -go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= -go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= -go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= -go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= -go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= -go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= +go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= -golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= -golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= +golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= +golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= +golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY= -golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY= +golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= +golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= +golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= +golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= +golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= +golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= -golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= -golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= +golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= +golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.34.1-0.20250613162507-3f93fece84c7/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= +golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= -google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= +google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= +gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= -gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= +gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= @@ -616,6 +1373,9 @@ k8s.io/component-helpers v0.35.0 h1:wcXv7HJRksgVjM4VlXJ1CNFBpyDHruRI99RrBtrJceA= k8s.io/component-helpers v0.35.0/go.mod h1:ahX0m/LTYmu7fL3W8zYiIwnQ/5gT28Ex4o2pymF63Co= k8s.io/controller-manager v0.35.0 h1:KteodmfVIRzfZ3RDaxhnHb72rswBxEngvdL9vuZOA9A= k8s.io/controller-manager v0.35.0/go.mod h1:1bVuPNUG6/dpWpevsJpXioS0E0SJnZ7I/Wqc9Awyzm4= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= @@ -624,21 +1384,28 @@ k8s.io/kubectl v0.35.0 h1:cL/wJKHDe8E8+rP3G7avnymcMg6bH6JEcR5w5uo06wc= k8s.io/kubectl v0.35.0/go.mod h1:VR5/TSkYyxZwrRwY5I5dDq6l5KXmiCb+9w8IKplk3Qo= k8s.io/kubelet v0.35.0 h1:8cgJHCBCKLYuuQ7/Pxb/qWbJfX1LXIw7790ce9xHq7c= k8s.io/kubelet v0.35.0/go.mod h1:ciRzAXn7C4z5iB7FhG1L2CGPPXLTVCABDlbXt/Zz8YA= -k8s.io/kubernetes v1.35.3 h1:J3dk2wybKFHwoH4eydDUGHJo4HAD+9CZbSlvk/YQuao= -k8s.io/kubernetes v1.35.3/go.mod h1:AaPpCpiS8oAqRbEwpY5r3RitLpwpVp5lVXKFkJril58= +k8s.io/kubernetes v1.35.0 h1:PUOojD8c8E3csMP5NX+nLLne6SGqZjrYCscptyBfWMY= +k8s.io/kubernetes v1.35.0/go.mod h1:Tzk9Y9W/XUFFFgTUVg+BAowoFe+Pc7koGLuaiLHdcFg= k8s.io/pod-security-admission v0.35.0 h1:tT3UHC+Q1mpFRe4IoVTu20ZAx+kqgKBZnewRnsDcyfc= k8s.io/pod-security-admission v0.35.0/go.mod h1:S+57PAqNo6DaUYjmtINiiXlYnEdShrOVMwSc7C4oYPg= +k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= -sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= +sigs.k8s.io/kind v0.11.1 h1:pVzOkhUwMBrCB0Q/WllQDO3v14Y+o2V0tFgjTqIUjwA= +sigs.k8s.io/kind v0.11.1/go.mod h1:fRpgVhtqAWrtLB9ED7zQahUimpUXuG/iHT88xYqEGIA= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From f1c894155e7e75c06e571c555f7292131217d76d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:15:07 +0200 Subject: [PATCH 04/53] green: attestation loop in wizard (slice 4) Add interactive attestation collection with type select (6 built-in types) and name input. Users can add multiple attestations in a loop. --- TODO.md | 2 +- cmd/kosli/createPolicyFile.go | 91 +++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index 858aedfbb..52fa812b8 100644 --- a/TODO.md +++ b/TODO.md @@ -60,7 +60,7 @@ - [x] Slice 1: Policy model + YAML generation (`internal/policy/`) - [x] Slice 2: Expression builder - [x] Slice 3: Skeleton Cobra command + huh dependency -- [ ] Slice 4: Attestation loop in wizard +- [x] Slice 4: Attestation loop in wizard - [ ] Slice 5: Expression builder wizard - [ ] Slice 6: API lookups for flows and custom attestation types - [ ] Slice 7: Preview screen + polish diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index f3b18be39..3519e8af3 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -95,6 +95,18 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { } } + // Attestation loop + attestations, err := collectAttestations() + if err != nil { + return err + } + if len(attestations) > 0 { + if p.Artifacts == nil { + p.Artifacts = &policy.ArtifactRules{} + } + p.Artifacts.Attestations = attestations + } + yamlBytes, err := p.ToYAML() if err != nil { return fmt.Errorf("failed to generate policy YAML: %w", err) @@ -112,3 +124,82 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { _, err = out.Write(yamlBytes) return err } + +var builtInAttestationTypes = []string{ + "generic", + "junit", + "snyk", + "pull_request", + "jira", + "sonar", +} + +func collectAttestations() ([]policy.AttestationRule, error) { + var attestations []policy.AttestationRule + + for { + prompt := "Add a required attestation?" + if len(attestations) > 0 { + prompt = "Add another required attestation?" + } + + var addAttestation bool + err := huh.NewConfirm(). + Title(prompt). + Value(&addAttestation). + Affirmative("Yes"). + Negative("No"). + Run() + if err != nil { + return nil, err + } + if !addAttestation { + break + } + + rule, err := collectOneAttestation() + if err != nil { + return nil, err + } + attestations = append(attestations, rule) + } + return attestations, nil +} + +func collectOneAttestation() (policy.AttestationRule, error) { + var attType string + var attName string + + typeOptions := make([]huh.Option[string], len(builtInAttestationTypes)) + for i, t := range builtInAttestationTypes { + typeOptions[i] = huh.NewOption(t, t) + } + + err := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title("Attestation type"). + Options(typeOptions...). + Value(&attType), + huh.NewInput(). + Title("Attestation name"). + Description("Use * to match any name for this type"). + Placeholder("*"). + Value(&attName), + ), + ).Run() + if err != nil { + return policy.AttestationRule{}, err + } + + if attName == "" { + attName = "*" + } + + rule := policy.AttestationRule{ + Type: attType, + Name: attName, + } + + return rule, nil +} From 636024c899c8b5771e540c9c2fbde99bc2126bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:16:02 +0200 Subject: [PATCH 05/53] green: expression builder wizard for conditions and exceptions (slice 5) Add guided expression builder sub-wizard with four modes: - Match by flow name - Match by artifact name pattern (regex) - Custom comparison (context + operator + value) - Raw expression input Exceptions can now be added to provenance and trail-compliance rules. Attestations can have optional `if` conditions. --- TODO.md | 2 +- cmd/kosli/createPolicyFile.go | 229 +++++++++++++++++++++++++++++++++- 2 files changed, 228 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 52fa812b8..e41bc7a59 100644 --- a/TODO.md +++ b/TODO.md @@ -61,6 +61,6 @@ - [x] Slice 2: Expression builder - [x] Slice 3: Skeleton Cobra command + huh dependency - [x] Slice 4: Attestation loop in wizard -- [ ] Slice 5: Expression builder wizard +- [x] Slice 5: Expression builder wizard - [ ] Slice 6: API lookups for flows and custom attestation types - [ ] Slice 7: Preview screen + polish diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 3519e8af3..fd58b8f46 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -88,10 +88,18 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { if requireProvenance || requireTrailCompliance { p.Artifacts = &policy.ArtifactRules{} if requireProvenance { - p.Artifacts.Provenance = &policy.BooleanRule{Required: true} + exceptions, exErr := collectExceptions("provenance") + if exErr != nil { + return exErr + } + p.Artifacts.Provenance = &policy.BooleanRule{Required: true, Exceptions: exceptions} } if requireTrailCompliance { - p.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + exceptions, exErr := collectExceptions("trail compliance") + if exErr != nil { + return exErr + } + p.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true, Exceptions: exceptions} } } @@ -201,5 +209,222 @@ func collectOneAttestation() (policy.AttestationRule, error) { Name: attName, } + var addCondition bool + err = huh.NewConfirm(). + Title("Add a condition for this attestation?"). + Description("Only require this attestation when the condition is met"). + Value(&addCondition). + Affirmative("Yes"). + Negative("No"). + Run() + if err != nil { + return policy.AttestationRule{}, err + } + if addCondition { + expr, exprErr := collectExpression() + if exprErr != nil { + return policy.AttestationRule{}, exprErr + } + rule.If = expr + } + return rule, nil } + +func collectExceptions(ruleName string) ([]policy.ExceptionRule, error) { + var exceptions []policy.ExceptionRule + + for { + prompt := fmt.Sprintf("Add an exception to %s?", ruleName) + if len(exceptions) > 0 { + prompt = fmt.Sprintf("Add another exception to %s?", ruleName) + } + + var addException bool + err := huh.NewConfirm(). + Title(prompt). + Description("Exceptions waive this requirement for matching artifacts"). + Value(&addException). + Affirmative("Yes"). + Negative("No"). + Run() + if err != nil { + return nil, err + } + if !addException { + break + } + + expr, err := collectExpression() + if err != nil { + return nil, err + } + exceptions = append(exceptions, policy.ExceptionRule{If: expr}) + } + return exceptions, nil +} + +const ( + exprModeFlowName = "flow_name" + exprModeArtifactName = "artifact_name" + exprModeCustom = "custom" + exprModeRaw = "raw" +) + +func collectExpression() (string, error) { + var mode string + err := huh.NewSelect[string](). + Title("How do you want to define this condition?"). + Options( + huh.NewOption("Match by flow name", exprModeFlowName), + huh.NewOption("Match by artifact name pattern", exprModeArtifactName), + huh.NewOption("Custom comparison", exprModeCustom), + huh.NewOption("Write raw expression", exprModeRaw), + ). + Value(&mode). + Run() + if err != nil { + return "", err + } + + switch mode { + case exprModeFlowName: + return collectFlowNameExpr() + case exprModeArtifactName: + return collectArtifactNameExpr() + case exprModeCustom: + return collectCustomExpr() + case exprModeRaw: + return collectRawExpr() + } + return "", fmt.Errorf("unknown expression mode: %s", mode) +} + +func collectFlowNameExpr() (string, error) { + var flowName string + err := huh.NewInput(). + Title("Flow name"). + Description("The flow name to match"). + Value(&flowName). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("flow name is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } + return policy.FlowNameExpr(flowName), nil +} + +func collectArtifactNameExpr() (string, error) { + var regex string + err := huh.NewInput(). + Title("Artifact name regex"). + Description("Regular expression to match artifact names (e.g. ^datadog:.*)"). + Placeholder("^datadog:.*"). + Value(®ex). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("regex is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } + return policy.ArtifactNameMatchExpr(regex), nil +} + +var exprContexts = []string{ + "flow.name", + "flow.tags.", + "artifact.name", + "artifact.fingerprint", +} + +var exprOperators = []string{"==", "!=", "in", "matches"} + +func collectCustomExpr() (string, error) { + var context string + var operator string + var value string + + err := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title("Context field"). + Options(huh.NewOptions(exprContexts...)...). + Value(&context), + ), + ).Run() + if err != nil { + return "", err + } + + if context == "flow.tags." { + var tagKey string + err = huh.NewInput(). + Title("Tag key"). + Description("The flow tag key (e.g. team, risk-level)"). + Value(&tagKey). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("tag key is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } + context = "flow.tags." + tagKey + } + + err = huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title("Operator"). + Options(huh.NewOptions(exprOperators...)...). + Value(&operator), + huh.NewInput(). + Title("Value"). + Description("The value to compare against"). + Value(&value). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("value is required") + } + return nil + }), + ), + ).Run() + if err != nil { + return "", err + } + + return policy.ComparisonExpr(context, operator, value), nil +} + +func collectRawExpr() (string, error) { + var raw string + err := huh.NewInput(). + Title("Raw expression"). + Description("Enter a policy expression (e.g. flow.name == \"prod\" and artifact.name == \"svc\")"). + Placeholder(`flow.name == "prod"`). + Value(&raw). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("expression is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } + return policy.WrapExpr(raw), nil +} From 1ee753e3f2e4f85f55a84fb4ef1196ae28156331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:18:27 +0200 Subject: [PATCH 06/53] green: optional API lookups for flows and custom attestation types (slice 6) When --api-token and --org are set, fetch flow names and custom attestation types from the API to populate wizard selects. Degrades gracefully when credentials are absent or API calls fail. --- TODO.md | 2 +- cmd/kosli/createPolicyFile.go | 160 ++++++++++++++++++++++++++++------ 2 files changed, 134 insertions(+), 28 deletions(-) diff --git a/TODO.md b/TODO.md index e41bc7a59..e02122446 100644 --- a/TODO.md +++ b/TODO.md @@ -62,5 +62,5 @@ - [x] Slice 3: Skeleton Cobra command + huh dependency - [x] Slice 4: Attestation loop in wizard - [x] Slice 5: Expression builder wizard -- [ ] Slice 6: API lookups for flows and custom attestation types +- [x] Slice 6: API lookups for flows and custom attestation types - [ ] Slice 7: Preview screen + polish diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index fd58b8f46..cae566d76 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -1,16 +1,26 @@ package main import ( + "encoding/json" "fmt" "io" + "net/http" + "net/url" "os" "github.com/charmbracelet/huh" "github.com/kosli-dev/cli/internal/policy" + "github.com/kosli-dev/cli/internal/requests" "github.com/spf13/cobra" "golang.org/x/term" ) +// wizardContext holds data fetched from the API to populate wizard options. +type wizardContext struct { + flowNames []string + customAttestTypes []string // e.g. ["custom:coverage-metrics", "custom:compliance-check"] +} + const createPolicyFileShortDesc = `Interactively create a Kosli environment policy YAML file.` const createPolicyFileLongDesc = createPolicyFileShortDesc + ` @@ -60,6 +70,11 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") } + wctx := &wizardContext{} + if global.ApiToken != "" && global.Org != "" { + wctx.fetchFromAPI() + } + p := policy.NewPolicy() var requireProvenance bool @@ -88,14 +103,14 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { if requireProvenance || requireTrailCompliance { p.Artifacts = &policy.ArtifactRules{} if requireProvenance { - exceptions, exErr := collectExceptions("provenance") + exceptions, exErr := collectExceptions("provenance", wctx) if exErr != nil { return exErr } p.Artifacts.Provenance = &policy.BooleanRule{Required: true, Exceptions: exceptions} } if requireTrailCompliance { - exceptions, exErr := collectExceptions("trail compliance") + exceptions, exErr := collectExceptions("trail compliance", wctx) if exErr != nil { return exErr } @@ -104,7 +119,7 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { } // Attestation loop - attestations, err := collectAttestations() + attestations, err := collectAttestations(wctx) if err != nil { return err } @@ -142,7 +157,7 @@ var builtInAttestationTypes = []string{ "sonar", } -func collectAttestations() ([]policy.AttestationRule, error) { +func collectAttestations(wctx *wizardContext) ([]policy.AttestationRule, error) { var attestations []policy.AttestationRule for { @@ -165,7 +180,7 @@ func collectAttestations() ([]policy.AttestationRule, error) { break } - rule, err := collectOneAttestation() + rule, err := collectOneAttestation(wctx) if err != nil { return nil, err } @@ -174,12 +189,16 @@ func collectAttestations() ([]policy.AttestationRule, error) { return attestations, nil } -func collectOneAttestation() (policy.AttestationRule, error) { +func collectOneAttestation(wctx *wizardContext) (policy.AttestationRule, error) { var attType string var attName string - typeOptions := make([]huh.Option[string], len(builtInAttestationTypes)) - for i, t := range builtInAttestationTypes { + allTypes := builtInAttestationTypes + if len(wctx.customAttestTypes) > 0 { + allTypes = append(allTypes, wctx.customAttestTypes...) + } + typeOptions := make([]huh.Option[string], len(allTypes)) + for i, t := range allTypes { typeOptions[i] = huh.NewOption(t, t) } @@ -221,7 +240,7 @@ func collectOneAttestation() (policy.AttestationRule, error) { return policy.AttestationRule{}, err } if addCondition { - expr, exprErr := collectExpression() + expr, exprErr := collectExpression(wctx) if exprErr != nil { return policy.AttestationRule{}, exprErr } @@ -231,7 +250,7 @@ func collectOneAttestation() (policy.AttestationRule, error) { return rule, nil } -func collectExceptions(ruleName string) ([]policy.ExceptionRule, error) { +func collectExceptions(ruleName string, wctx *wizardContext) ([]policy.ExceptionRule, error) { var exceptions []policy.ExceptionRule for { @@ -255,7 +274,7 @@ func collectExceptions(ruleName string) ([]policy.ExceptionRule, error) { break } - expr, err := collectExpression() + expr, err := collectExpression(wctx) if err != nil { return nil, err } @@ -271,7 +290,7 @@ const ( exprModeRaw = "raw" ) -func collectExpression() (string, error) { +func collectExpression(wctx *wizardContext) (string, error) { var mode string err := huh.NewSelect[string](). Title("How do you want to define this condition?"). @@ -289,7 +308,7 @@ func collectExpression() (string, error) { switch mode { case exprModeFlowName: - return collectFlowNameExpr() + return collectFlowNameExpr(wctx) case exprModeArtifactName: return collectArtifactNameExpr() case exprModeCustom: @@ -300,21 +319,37 @@ func collectExpression() (string, error) { return "", fmt.Errorf("unknown expression mode: %s", mode) } -func collectFlowNameExpr() (string, error) { +func collectFlowNameExpr(wctx *wizardContext) (string, error) { var flowName string - err := huh.NewInput(). - Title("Flow name"). - Description("The flow name to match"). - Value(&flowName). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("flow name is required") - } - return nil - }). - Run() - if err != nil { - return "", err + + if len(wctx.flowNames) > 0 { + options := make([]huh.Option[string], len(wctx.flowNames)) + for i, name := range wctx.flowNames { + options[i] = huh.NewOption(name, name) + } + err := huh.NewSelect[string](). + Title("Select a flow"). + Options(options...). + Value(&flowName). + Run() + if err != nil { + return "", err + } + } else { + err := huh.NewInput(). + Title("Flow name"). + Description("The flow name to match"). + Value(&flowName). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("flow name is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } } return policy.FlowNameExpr(flowName), nil } @@ -428,3 +463,74 @@ func collectRawExpr() (string, error) { } return policy.WrapExpr(raw), nil } + +func (wctx *wizardContext) fetchFromAPI() { + wctx.flowNames = fetchFlowNames() + wctx.customAttestTypes = fetchCustomAttestationTypes() +} + +func fetchFlowNames() []string { + u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) + if err != nil { + logger.Debug("failed to build flows URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch flows: %v", err) + return nil + } + + var flows []map[string]any + if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { + logger.Debug("failed to parse flows response: %v", err) + return nil + } + + names := make([]string, 0, len(flows)) + for _, flow := range flows { + if name, ok := flow["name"].(string); ok { + names = append(names, name) + } + } + return names +} + +func fetchCustomAttestationTypes() []string { + u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) + if err != nil { + logger.Debug("failed to build attestation types URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch attestation types: %v", err) + return nil + } + + var types []map[string]any + if err := json.Unmarshal([]byte(response.Body), &types); err != nil { + logger.Debug("failed to parse attestation types response: %v", err) + return nil + } + + names := make([]string, 0, len(types)) + for _, t := range types { + if name, ok := t["name"].(string); ok { + names = append(names, "custom:"+name) + } + } + return names +} From 9a908c6ec27c4107acb4ad555d3ae0760b25e870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 21:19:21 +0200 Subject: [PATCH 07/53] green: preview screen before writing policy YAML (slice 7) Show generated YAML in a Note and ask for confirmation before writing. User can cancel to discard the generated policy. --- TODO.md | 2 +- cmd/kosli/createPolicyFile.go | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index e02122446..ecd870dbc 100644 --- a/TODO.md +++ b/TODO.md @@ -63,4 +63,4 @@ - [x] Slice 4: Attestation loop in wizard - [x] Slice 5: Expression builder wizard - [x] Slice 6: API lookups for flows and custom attestation types -- [ ] Slice 7: Preview screen + polish +- [x] Slice 7: Preview screen + polish diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index cae566d76..20e549679 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -135,6 +135,28 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { return fmt.Errorf("failed to generate policy YAML: %w", err) } + // Preview the generated YAML + var confirm bool + err = huh.NewForm( + huh.NewGroup( + huh.NewNote(). + Title("Generated policy YAML"). + Description(string(yamlBytes)), + huh.NewConfirm(). + Title("Write this policy?"). + Value(&confirm). + Affirmative("Yes"). + Negative("No"), + ), + ).Run() + if err != nil { + return err + } + if !confirm { + logger.Info("policy file creation cancelled") + return nil + } + if o.outputFile != "" { err = os.WriteFile(o.outputFile, yamlBytes, 0644) if err != nil { From 0e4e41526432cd7ab3e56ecfbcaae15eb350bb50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:04:20 +0200 Subject: [PATCH 08/53] green: add "Match by flow tag" mode to expression builder Add FlowTagExpr helper and a wizard mode that prompts for tag key, operator (==, !=, >, <, >=, <=), and value. Supports dotted keys like flow.tags.key.with.dots per the policy reference. --- cmd/kosli/createPolicyFile.go | 51 ++++++++++++++++++++++++++++++ internal/policy/expression.go | 5 +++ internal/policy/expression_test.go | 10 ++++++ 3 files changed, 66 insertions(+) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 20e549679..5b11cce72 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -307,6 +307,7 @@ func collectExceptions(ruleName string, wctx *wizardContext) ([]policy.Exception const ( exprModeFlowName = "flow_name" + exprModeFlowTag = "flow_tag" exprModeArtifactName = "artifact_name" exprModeCustom = "custom" exprModeRaw = "raw" @@ -318,6 +319,7 @@ func collectExpression(wctx *wizardContext) (string, error) { Title("How do you want to define this condition?"). Options( huh.NewOption("Match by flow name", exprModeFlowName), + huh.NewOption("Match by flow tag", exprModeFlowTag), huh.NewOption("Match by artifact name pattern", exprModeArtifactName), huh.NewOption("Custom comparison", exprModeCustom), huh.NewOption("Write raw expression", exprModeRaw), @@ -331,6 +333,8 @@ func collectExpression(wctx *wizardContext) (string, error) { switch mode { case exprModeFlowName: return collectFlowNameExpr(wctx) + case exprModeFlowTag: + return collectFlowTagExpr() case exprModeArtifactName: return collectArtifactNameExpr() case exprModeCustom: @@ -376,6 +380,53 @@ func collectFlowNameExpr(wctx *wizardContext) (string, error) { return policy.FlowNameExpr(flowName), nil } +var flowTagOperators = []string{"==", "!=", ">", "<", ">=", "<="} + +func collectFlowTagExpr() (string, error) { + var tagKey string + var operator string + var value string + + err := huh.NewInput(). + Title("Tag key"). + Description("The flow tag key (e.g. team, risk-level, key.with.dots)"). + Value(&tagKey). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("tag key is required") + } + return nil + }). + Run() + if err != nil { + return "", err + } + + err = huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Title("Operator"). + Options(huh.NewOptions(flowTagOperators...)...). + Value(&operator), + huh.NewInput(). + Title("Value"). + Description("The value to compare against"). + Value(&value). + Validate(func(s string) error { + if s == "" { + return fmt.Errorf("value is required") + } + return nil + }), + ), + ).Run() + if err != nil { + return "", err + } + + return policy.FlowTagExpr(tagKey, operator, value), nil +} + func collectArtifactNameExpr() (string, error) { var regex string err := huh.NewInput(). diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 14ccf9b17..3d3ae788a 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -23,6 +23,11 @@ func FlowNameInExpr(names []string) string { return fmt.Sprintf(`${{ flow.name in [%s] }}`, strings.Join(quoted, ", ")) } +// FlowTagExpr returns a policy expression comparing a flow tag to a value. +func FlowTagExpr(key, op, value string) string { + return fmt.Sprintf(`${{ flow.tags.%s %s "%s" }}`, key, op, value) +} + // ArtifactNameMatchExpr returns a policy expression matching artifact names by regex. func ArtifactNameMatchExpr(regex string) string { return fmt.Sprintf(`${{ matches(artifact.name, "%s") }}`, regex) diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 8ba20c090..bdbe71f9d 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -21,6 +21,16 @@ func TestFlowNameInExpr_Single(t *testing.T) { assert.Equal(t, `${{ flow.name == "prod" }}`, result) } +func TestFlowTagExpr(t *testing.T) { + result := FlowTagExpr("risk-level", "==", "high") + assert.Equal(t, `${{ flow.tags.risk-level == "high" }}`, result) +} + +func TestFlowTagExpr_DottedKey(t *testing.T) { + result := FlowTagExpr("key.with.dots", "!=", "bad") + assert.Equal(t, `${{ flow.tags.key.with.dots != "bad" }}`, result) +} + func TestArtifactNameMatchExpr(t *testing.T) { result := ArtifactNameMatchExpr("^datadog:.*") assert.Equal(t, `${{ matches(artifact.name, "^datadog:.*") }}`, result) From c4309bc807fcad8821ff48079735772f29fdab81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:15:26 +0200 Subject: [PATCH 09/53] refactor: rewrite wizard as bubbletea model with side-by-side layout Replace sequential huh.Form.Run() calls with a single bubbletea program. The TUI now shows the form on the left and a live YAML preview on the right, updating as the user makes selections. Uses a step-based state machine for wizard flow. Each step builds its own huh.Form which is embedded in the bubbletea update loop. Promotes bubbletea and lipgloss from indirect to direct dependencies. --- cmd/kosli/createPolicyFile.go | 1010 ++++++++++++++++++++------------- go.mod | 4 +- 2 files changed, 630 insertions(+), 384 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 5b11cce72..fccc0a566 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -7,20 +7,17 @@ import ( "net/http" "net/url" "os" + "strings" + tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" "github.com/kosli-dev/cli/internal/policy" "github.com/kosli-dev/cli/internal/requests" "github.com/spf13/cobra" "golang.org/x/term" ) -// wizardContext holds data fetched from the API to populate wizard options. -type wizardContext struct { - flowNames []string - customAttestTypes []string // e.g. ["custom:coverage-metrics", "custom:compliance-check"] -} - const createPolicyFileShortDesc = `Interactively create a Kosli environment policy YAML file.` const createPolicyFileLongDesc = createPolicyFileShortDesc + ` @@ -75,88 +72,23 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { wctx.fetchFromAPI() } - p := policy.NewPolicy() - - var requireProvenance bool - var requireTrailCompliance bool - - err := huh.NewForm( - huh.NewGroup( - huh.NewConfirm(). - Title("Require artifact provenance?"). - Description("All artifacts must belong to a Kosli flow"). - Value(&requireProvenance). - Affirmative("Yes"). - Negative("No"), - huh.NewConfirm(). - Title("Require trail compliance?"). - Description("All artifacts must be part of compliant trails"). - Value(&requireTrailCompliance). - Affirmative("Yes"). - Negative("No"), - ), - ).Run() + m := newPolicyWizardModel(wctx) + finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { - return err + return fmt.Errorf("wizard error: %w", err) } - if requireProvenance || requireTrailCompliance { - p.Artifacts = &policy.ArtifactRules{} - if requireProvenance { - exceptions, exErr := collectExceptions("provenance", wctx) - if exErr != nil { - return exErr - } - p.Artifacts.Provenance = &policy.BooleanRule{Required: true, Exceptions: exceptions} - } - if requireTrailCompliance { - exceptions, exErr := collectExceptions("trail compliance", wctx) - if exErr != nil { - return exErr - } - p.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true, Exceptions: exceptions} - } - } - - // Attestation loop - attestations, err := collectAttestations(wctx) - if err != nil { - return err - } - if len(attestations) > 0 { - if p.Artifacts == nil { - p.Artifacts = &policy.ArtifactRules{} - } - p.Artifacts.Attestations = attestations + wm := finalModel.(policyWizardModel) + if wm.cancelled || !wm.confirmed { + logger.Info("policy file creation cancelled") + return nil } - yamlBytes, err := p.ToYAML() + yamlBytes, err := wm.policy.ToYAML() if err != nil { return fmt.Errorf("failed to generate policy YAML: %w", err) } - // Preview the generated YAML - var confirm bool - err = huh.NewForm( - huh.NewGroup( - huh.NewNote(). - Title("Generated policy YAML"). - Description(string(yamlBytes)), - huh.NewConfirm(). - Title("Write this policy?"). - Value(&confirm). - Affirmative("Yes"). - Negative("No"), - ), - ).Run() - if err != nil { - return err - } - if !confirm { - logger.Info("policy file creation cancelled") - return nil - } - if o.outputFile != "" { err = os.WriteFile(o.outputFile, yamlBytes, 0644) if err != nil { @@ -170,373 +102,687 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { return err } -var builtInAttestationTypes = []string{ - "generic", - "junit", - "snyk", - "pull_request", - "jira", - "sonar", -} +// --------------------------------------------------------------------------- +// Wizard step enum +// --------------------------------------------------------------------------- -func collectAttestations(wctx *wizardContext) ([]policy.AttestationRule, error) { - var attestations []policy.AttestationRule +type wizardStep int - for { - prompt := "Add a required attestation?" - if len(attestations) > 0 { - prompt = "Add another required attestation?" - } +const ( + stepBasics wizardStep = iota // provenance + trail compliance + stepProvExcConfirm // add provenance exception? + stepTrailExcConfirm // add trail compliance exception? + stepAttConfirm // add attestation? + stepAttDetails // attestation type + name + stepAttCondConfirm // add condition for attestation? + stepExprMode // choose expression mode + stepExprFlowName // flow name input/select + stepExprFlowTag // tag key input + stepExprFlowTagOp // tag operator + value + stepExprArtifactName // artifact regex input + stepExprCustomCtx // custom context select + stepExprCustomTagKey // tag key for custom context + stepExprCustomOp // custom operator + value + stepExprRaw // raw expression input + stepFinalConfirm // write policy? + stepDone +) - var addAttestation bool - err := huh.NewConfirm(). - Title(prompt). - Value(&addAttestation). - Affirmative("Yes"). - Negative("No"). - Run() - if err != nil { - return nil, err - } - if !addAttestation { - break - } +// exprTarget tracks what we're building an expression for. +type exprTarget int - rule, err := collectOneAttestation(wctx) - if err != nil { - return nil, err - } - attestations = append(attestations, rule) - } - return attestations, nil +const ( + targetProvException exprTarget = iota + targetTrailException + targetAttCondition +) + +// --------------------------------------------------------------------------- +// Wizard context (API data) +// --------------------------------------------------------------------------- + +type wizardContext struct { + flowNames []string + customAttestTypes []string } -func collectOneAttestation(wctx *wizardContext) (policy.AttestationRule, error) { - var attType string - var attName string +// --------------------------------------------------------------------------- +// Styles +// --------------------------------------------------------------------------- + +type wizardStyles struct { + base lipgloss.Style + title lipgloss.Style + preview lipgloss.Style + previewText lipgloss.Style + footer lipgloss.Style + accent lipgloss.Style +} - allTypes := builtInAttestationTypes - if len(wctx.customAttestTypes) > 0 { - allTypes = append(allTypes, wctx.customAttestTypes...) - } - typeOptions := make([]huh.Option[string], len(allTypes)) - for i, t := range allTypes { - typeOptions[i] = huh.NewOption(t, t) +func newWizardStyles() wizardStyles { + accent := lipgloss.Color("#7571F9") + green := lipgloss.Color("#02BF87") + return wizardStyles{ + base: lipgloss.NewStyle().Padding(1, 2), + title: lipgloss.NewStyle(). + Bold(true). + Foreground(accent). + Padding(0, 1), + preview: lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(accent). + Padding(1, 2), + previewText: lipgloss.NewStyle(). + Foreground(green), + footer: lipgloss.NewStyle(). + Foreground(lipgloss.Color("240")). + Padding(1, 1, 0, 1), + accent: lipgloss.NewStyle(). + Foreground(accent), } +} - err := huh.NewForm( - huh.NewGroup( - huh.NewSelect[string](). - Title("Attestation type"). - Options(typeOptions...). - Value(&attType), - huh.NewInput(). - Title("Attestation name"). - Description("Use * to match any name for this type"). - Placeholder("*"). - Value(&attName), - ), - ).Run() - if err != nil { - return policy.AttestationRule{}, err +// --------------------------------------------------------------------------- +// Bubbletea model +// --------------------------------------------------------------------------- + +const ( + formWidth = 50 + previewWidth = 40 + maxWidth = 100 +) + +type policyWizardModel struct { + step wizardStep + form *huh.Form + policy *policy.Policy + wctx *wizardContext + styles wizardStyles + width int + height int + + // State for loops and expression building + exprTarget exprTarget + exprMode string + exprContext string // for custom expressions + exprTagKey string // for flow tag / custom tag + currentAttRule policy.AttestationRule + cancelled bool + confirmed bool + + // Form-bound values + requireProv bool + requireTrail bool + confirmBool bool + inputStr string + selectStr string +} + +func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { + m := policyWizardModel{ + step: stepBasics, + policy: policy.NewPolicy(), + wctx: wctx, + styles: newWizardStyles(), + width: maxWidth, } + m.form = m.buildForm() + return m +} + +func (m policyWizardModel) Init() tea.Cmd { + return m.form.Init() +} - if attName == "" { - attName = "*" +func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + return m, nil + case tea.KeyMsg: + if msg.String() == "ctrl+c" { + m.cancelled = true + return m, tea.Quit + } } - rule := policy.AttestationRule{ - Type: attType, - Name: attName, + // Forward to form + form, cmd := m.form.Update(msg) + if f, ok := form.(*huh.Form); ok { + m.form = f } - var addCondition bool - err = huh.NewConfirm(). - Title("Add a condition for this attestation?"). - Description("Only require this attestation when the condition is met"). - Value(&addCondition). - Affirmative("Yes"). - Negative("No"). - Run() - if err != nil { - return policy.AttestationRule{}, err + if m.form.State == huh.StateAborted { + m.cancelled = true + return m, tea.Quit } - if addCondition { - expr, exprErr := collectExpression(wctx) - if exprErr != nil { - return policy.AttestationRule{}, exprErr + + if m.form.State == huh.StateCompleted { + m.processFormResults() + m.advanceStep() + if m.step == stepDone { + return m, tea.Quit } - rule.If = expr + m.form = m.buildForm() + return m, m.form.Init() } - return rule, nil + return m, cmd } -func collectExceptions(ruleName string, wctx *wizardContext) ([]policy.ExceptionRule, error) { - var exceptions []policy.ExceptionRule +func (m policyWizardModel) View() string { + if m.cancelled { + return "" + } + if m.step == stepDone { + return "" + } - for { - prompt := fmt.Sprintf("Add an exception to %s?", ruleName) - if len(exceptions) > 0 { - prompt = fmt.Sprintf("Add another exception to %s?", ruleName) + s := m.styles + fw := formWidth + pw := previewWidth + available := m.width - s.base.GetHorizontalFrameSize() + if available < fw+pw+4 { + pw = available - fw - 4 + if pw < 20 { + pw = 0 } + } - var addException bool - err := huh.NewConfirm(). - Title(prompt). - Description("Exceptions waive this requirement for matching artifacts"). - Value(&addException). - Affirmative("Yes"). - Negative("No"). - Run() - if err != nil { - return nil, err - } - if !addException { - break - } + // Header + header := s.title.Render("Kosli Policy Builder") - expr, err := collectExpression(wctx) - if err != nil { - return nil, err + // Form (left) + formView := lipgloss.NewStyle(). + Width(fw). + Render(m.form.View()) + + var body string + if pw > 0 { + // YAML preview (right) + yamlBytes, _ := m.policy.ToYAML() + yamlStr := strings.TrimRight(string(yamlBytes), "\n") + if yamlStr == "" { + yamlStr = "(empty)" } - exceptions = append(exceptions, policy.ExceptionRule{If: expr}) + previewContent := s.previewText.Render(yamlStr) + previewTitle := s.accent.Bold(true).Render("Live Preview") + previewPanel := s.preview. + Width(pw). + Render(previewTitle + "\n\n" + previewContent) + + body = lipgloss.JoinHorizontal(lipgloss.Top, formView, " ", previewPanel) + } else { + body = formView } - return exceptions, nil -} -const ( - exprModeFlowName = "flow_name" - exprModeFlowTag = "flow_tag" - exprModeArtifactName = "artifact_name" - exprModeCustom = "custom" - exprModeRaw = "raw" -) + // Footer + footer := s.footer.Render("ctrl+c to cancel • enter to confirm") -func collectExpression(wctx *wizardContext) (string, error) { - var mode string - err := huh.NewSelect[string](). - Title("How do you want to define this condition?"). - Options( - huh.NewOption("Match by flow name", exprModeFlowName), - huh.NewOption("Match by flow tag", exprModeFlowTag), - huh.NewOption("Match by artifact name pattern", exprModeArtifactName), - huh.NewOption("Custom comparison", exprModeCustom), - huh.NewOption("Write raw expression", exprModeRaw), - ). - Value(&mode). - Run() - if err != nil { - return "", err - } - - switch mode { - case exprModeFlowName: - return collectFlowNameExpr(wctx) - case exprModeFlowTag: - return collectFlowTagExpr() - case exprModeArtifactName: - return collectArtifactNameExpr() - case exprModeCustom: - return collectCustomExpr() - case exprModeRaw: - return collectRawExpr() - } - return "", fmt.Errorf("unknown expression mode: %s", mode) + return s.base.Render(header + "\n\n" + body + "\n" + footer) } -func collectFlowNameExpr(wctx *wizardContext) (string, error) { - var flowName string +// --------------------------------------------------------------------------- +// Form builders — one per step +// --------------------------------------------------------------------------- - if len(wctx.flowNames) > 0 { - options := make([]huh.Option[string], len(wctx.flowNames)) - for i, name := range wctx.flowNames { - options[i] = huh.NewOption(name, name) - } - err := huh.NewSelect[string](). - Title("Select a flow"). - Options(options...). - Value(&flowName). - Run() - if err != nil { - return "", err - } - } else { - err := huh.NewInput(). - Title("Flow name"). - Description("The flow name to match"). - Value(&flowName). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("flow name is required") - } - return nil - }). - Run() - if err != nil { - return "", err - } - } - return policy.FlowNameExpr(flowName), nil +var builtInAttestationTypes = []string{ + "generic", "junit", "snyk", "pull_request", "jira", "sonar", } -var flowTagOperators = []string{"==", "!=", ">", "<", ">=", "<="} +func (m *policyWizardModel) buildForm() *huh.Form { + var f *huh.Form + switch m.step { + case stepBasics: + m.requireProv = true + m.requireTrail = true + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title("Require artifact provenance?"). + Description("All artifacts must belong to a Kosli flow"). + Value(&m.requireProv). + Affirmative("Yes").Negative("No"), + huh.NewConfirm(). + Title("Require trail compliance?"). + Description("All artifacts must be part of compliant trails"). + Value(&m.requireTrail). + Affirmative("Yes").Negative("No"), + )) -func collectFlowTagExpr() (string, error) { - var tagKey string - var operator string - var value string + case stepProvExcConfirm: + m.confirmBool = false + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title(m.excConfirmTitle("provenance")). + Description("Exceptions waive this requirement for matching artifacts"). + Value(&m.confirmBool). + Affirmative("Yes").Negative("No"), + )) + + case stepTrailExcConfirm: + m.confirmBool = false + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title(m.excConfirmTitle("trail compliance")). + Description("Exceptions waive this requirement for matching artifacts"). + Value(&m.confirmBool). + Affirmative("Yes").Negative("No"), + )) + + case stepAttConfirm: + m.confirmBool = false + title := "Add a required attestation?" + if m.policy.Artifacts != nil && len(m.policy.Artifacts.Attestations) > 0 { + title = "Add another required attestation?" + } + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title(title). + Value(&m.confirmBool). + Affirmative("Yes").Negative("No"), + )) + + case stepAttDetails: + m.selectStr = "" + m.inputStr = "" + allTypes := append([]string{}, builtInAttestationTypes...) + allTypes = append(allTypes, m.wctx.customAttestTypes...) + opts := make([]huh.Option[string], len(allTypes)) + for i, t := range allTypes { + opts[i] = huh.NewOption(t, t) + } + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string](). + Title("Attestation type"). + Options(opts...). + Value(&m.selectStr), + huh.NewInput(). + Title("Attestation name"). + Description("Use * to match any name for this type"). + Placeholder("*"). + Value(&m.inputStr), + )) - err := huh.NewInput(). - Title("Tag key"). - Description("The flow tag key (e.g. team, risk-level, key.with.dots)"). - Value(&tagKey). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("tag key is required") + case stepAttCondConfirm: + m.confirmBool = false + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title("Add a condition for this attestation?"). + Description("Only require when condition is met"). + Value(&m.confirmBool). + Affirmative("Yes").Negative("No"), + )) + + case stepExprMode: + m.selectStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string](). + Title("How do you want to define this condition?"). + Options( + huh.NewOption("Match by flow name", "flow_name"), + huh.NewOption("Match by flow tag", "flow_tag"), + huh.NewOption("Match by artifact name pattern", "artifact_name"), + huh.NewOption("Custom comparison", "custom"), + huh.NewOption("Write raw expression", "raw"), + ). + Value(&m.selectStr), + )) + + case stepExprFlowName: + m.inputStr = "" + m.selectStr = "" + if len(m.wctx.flowNames) > 0 { + opts := make([]huh.Option[string], len(m.wctx.flowNames)) + for i, n := range m.wctx.flowNames { + opts[i] = huh.NewOption(n, n) } - return nil - }). - Run() - if err != nil { - return "", err - } + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string](). + Title("Select a flow"). + Options(opts...). + Value(&m.selectStr), + )) + } else { + f = huh.NewForm(huh.NewGroup( + huh.NewInput(). + Title("Flow name"). + Description("The flow name to match"). + Value(&m.inputStr). + Validate(notEmpty("flow name")), + )) + } - err = huh.NewForm( - huh.NewGroup( + case stepExprFlowTag: + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewInput(). + Title("Tag key"). + Description("e.g. team, risk-level, key.with.dots"). + Value(&m.inputStr). + Validate(notEmpty("tag key")), + )) + + case stepExprFlowTagOp: + m.selectStr = "" + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( huh.NewSelect[string](). Title("Operator"). - Options(huh.NewOptions(flowTagOperators...)...). - Value(&operator), + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...). + Value(&m.selectStr), huh.NewInput(). Title("Value"). Description("The value to compare against"). - Value(&value). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("value is required") - } - return nil - }), - ), - ).Run() - if err != nil { - return "", err + Value(&m.inputStr). + Validate(notEmpty("value")), + )) + + case stepExprArtifactName: + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewInput(). + Title("Artifact name regex"). + Description("e.g. ^datadog:.*"). + Placeholder("^datadog:.*"). + Value(&m.inputStr). + Validate(notEmpty("regex")), + )) + + case stepExprCustomCtx: + m.selectStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string](). + Title("Context field"). + Options( + huh.NewOption("flow.name", "flow.name"), + huh.NewOption("flow.tags.", "flow.tags."), + huh.NewOption("artifact.name", "artifact.name"), + huh.NewOption("artifact.fingerprint", "artifact.fingerprint"), + ). + Value(&m.selectStr), + )) + + case stepExprCustomTagKey: + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewInput(). + Title("Tag key"). + Description("The flow tag key (e.g. team, risk-level)"). + Value(&m.inputStr). + Validate(notEmpty("tag key")), + )) + + case stepExprCustomOp: + m.selectStr = "" + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string](). + Title("Operator"). + Options(huh.NewOptions("==", "!=", "in", "matches")...). + Value(&m.selectStr), + huh.NewInput(). + Title("Value"). + Description("The value to compare against"). + Value(&m.inputStr). + Validate(notEmpty("value")), + )) + + case stepExprRaw: + m.inputStr = "" + f = huh.NewForm(huh.NewGroup( + huh.NewInput(). + Title("Raw expression"). + Description(`e.g. flow.name == "prod" and artifact.name == "svc"`). + Placeholder(`flow.name == "prod"`). + Value(&m.inputStr). + Validate(notEmpty("expression")), + )) + + case stepFinalConfirm: + m.confirmBool = true + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm(). + Title("Write this policy?"). + Value(&m.confirmBool). + Affirmative("Yes").Negative("No"), + )) + + default: + f = huh.NewForm(huh.NewGroup()) } - return policy.FlowTagExpr(tagKey, operator, value), nil + return f.WithWidth(formWidth).WithShowHelp(false).WithShowErrors(true) } -func collectArtifactNameExpr() (string, error) { - var regex string - err := huh.NewInput(). - Title("Artifact name regex"). - Description("Regular expression to match artifact names (e.g. ^datadog:.*)"). - Placeholder("^datadog:.*"). - Value(®ex). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("regex is required") - } - return nil - }). - Run() - if err != nil { - return "", err +func notEmpty(field string) func(string) error { + return func(s string) error { + if s == "" { + return fmt.Errorf("%s is required", field) + } + return nil } - return policy.ArtifactNameMatchExpr(regex), nil } -var exprContexts = []string{ - "flow.name", - "flow.tags.", - "artifact.name", - "artifact.fingerprint", +func (m *policyWizardModel) excConfirmTitle(rule string) string { + var count int + if rule == "provenance" && m.policy.Artifacts != nil && m.policy.Artifacts.Provenance != nil { + count = len(m.policy.Artifacts.Provenance.Exceptions) + } + if rule == "trail compliance" && m.policy.Artifacts != nil && m.policy.Artifacts.TrailCompliance != nil { + count = len(m.policy.Artifacts.TrailCompliance.Exceptions) + } + if count > 0 { + return fmt.Sprintf("Add another exception to %s?", rule) + } + return fmt.Sprintf("Add an exception to %s?", rule) } -var exprOperators = []string{"==", "!=", "in", "matches"} +// --------------------------------------------------------------------------- +// State transitions +// --------------------------------------------------------------------------- -func collectCustomExpr() (string, error) { - var context string - var operator string - var value string +func (m *policyWizardModel) processFormResults() { + switch m.step { + case stepBasics: + if m.requireProv || m.requireTrail { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} + } + if m.requireProv { + m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} + } + if m.requireTrail { + m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + } + } - err := huh.NewForm( - huh.NewGroup( - huh.NewSelect[string](). - Title("Context field"). - Options(huh.NewOptions(exprContexts...)...). - Value(&context), - ), - ).Run() - if err != nil { - return "", err - } - - if context == "flow.tags." { - var tagKey string - err = huh.NewInput(). - Title("Tag key"). - Description("The flow tag key (e.g. team, risk-level)"). - Value(&tagKey). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("tag key is required") - } - return nil - }). - Run() - if err != nil { - return "", err + case stepAttDetails: + name := m.inputStr + if name == "" { + name = "*" + } + m.currentAttRule = policy.AttestationRule{ + Type: m.selectStr, + Name: name, } - context = "flow.tags." + tagKey + + case stepExprMode: + m.exprMode = m.selectStr + + case stepExprFlowName: + name := m.selectStr + if name == "" { + name = m.inputStr + } + m.applyExpression(policy.FlowNameExpr(name)) + + case stepExprFlowTag: + m.exprTagKey = m.inputStr + + case stepExprFlowTagOp: + m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.selectStr, m.inputStr)) + + case stepExprArtifactName: + m.applyExpression(policy.ArtifactNameMatchExpr(m.inputStr)) + + case stepExprCustomCtx: + m.exprContext = m.selectStr + + case stepExprCustomTagKey: + m.exprContext = "flow.tags." + m.inputStr + + case stepExprCustomOp: + m.applyExpression(policy.ComparisonExpr(m.exprContext, m.selectStr, m.inputStr)) + + case stepExprRaw: + m.applyExpression(policy.WrapExpr(m.inputStr)) + + case stepFinalConfirm: + m.confirmed = m.confirmBool } +} - err = huh.NewForm( - huh.NewGroup( - huh.NewSelect[string](). - Title("Operator"). - Options(huh.NewOptions(exprOperators...)...). - Value(&operator), - huh.NewInput(). - Title("Value"). - Description("The value to compare against"). - Value(&value). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("value is required") - } - return nil - }), - ), - ).Run() - if err != nil { - return "", err +func (m *policyWizardModel) applyExpression(expr string) { + switch m.exprTarget { + case targetProvException: + m.policy.Artifacts.Provenance.Exceptions = append( + m.policy.Artifacts.Provenance.Exceptions, + policy.ExceptionRule{If: expr}, + ) + case targetTrailException: + m.policy.Artifacts.TrailCompliance.Exceptions = append( + m.policy.Artifacts.TrailCompliance.Exceptions, + policy.ExceptionRule{If: expr}, + ) + case targetAttCondition: + m.currentAttRule.If = expr + m.commitAttestation() } +} - return policy.ComparisonExpr(context, operator, value), nil +func (m *policyWizardModel) commitAttestation() { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} + } + m.policy.Artifacts.Attestations = append(m.policy.Artifacts.Attestations, m.currentAttRule) + m.currentAttRule = policy.AttestationRule{} } -func collectRawExpr() (string, error) { - var raw string - err := huh.NewInput(). - Title("Raw expression"). - Description("Enter a policy expression (e.g. flow.name == \"prod\" and artifact.name == \"svc\")"). - Placeholder(`flow.name == "prod"`). - Value(&raw). - Validate(func(s string) error { - if s == "" { - return fmt.Errorf("expression is required") - } - return nil - }). - Run() - if err != nil { - return "", err +func (m *policyWizardModel) advanceStep() { + switch m.step { + case stepBasics: + if m.requireProv { + m.exprTarget = targetProvException + m.step = stepProvExcConfirm + } else if m.requireTrail { + m.exprTarget = targetTrailException + m.step = stepTrailExcConfirm + } else { + m.step = stepAttConfirm + } + + case stepProvExcConfirm: + if m.confirmBool { + m.exprTarget = targetProvException + m.step = stepExprMode + } else if m.requireTrail { + m.exprTarget = targetTrailException + m.step = stepTrailExcConfirm + } else { + m.step = stepAttConfirm + } + + case stepTrailExcConfirm: + if m.confirmBool { + m.exprTarget = targetTrailException + m.step = stepExprMode + } else { + m.step = stepAttConfirm + } + + case stepAttConfirm: + if m.confirmBool { + m.step = stepAttDetails + } else { + m.step = stepFinalConfirm + } + + case stepAttDetails: + m.step = stepAttCondConfirm + + case stepAttCondConfirm: + if m.confirmBool { + m.exprTarget = targetAttCondition + m.step = stepExprMode + } else { + m.commitAttestation() + m.step = stepAttConfirm + } + + case stepExprMode: + switch m.exprMode { + case "flow_name": + m.step = stepExprFlowName + case "flow_tag": + m.step = stepExprFlowTag + case "artifact_name": + m.step = stepExprArtifactName + case "custom": + m.step = stepExprCustomCtx + case "raw": + m.step = stepExprRaw + } + + case stepExprFlowName: + m.advanceAfterExpr() + + case stepExprFlowTag: + m.step = stepExprFlowTagOp + + case stepExprFlowTagOp: + m.advanceAfterExpr() + + case stepExprArtifactName: + m.advanceAfterExpr() + + case stepExprCustomCtx: + if m.exprContext == "flow.tags." { + m.step = stepExprCustomTagKey + } else { + m.step = stepExprCustomOp + } + + case stepExprCustomTagKey: + m.step = stepExprCustomOp + + case stepExprCustomOp: + m.advanceAfterExpr() + + case stepExprRaw: + m.advanceAfterExpr() + + case stepFinalConfirm: + m.step = stepDone + } +} + +func (m *policyWizardModel) advanceAfterExpr() { + switch m.exprTarget { + case targetProvException: + m.step = stepProvExcConfirm + case targetTrailException: + m.step = stepTrailExcConfirm + case targetAttCondition: + // attestation was committed in applyExpression + m.step = stepAttConfirm } - return policy.WrapExpr(raw), nil } +// --------------------------------------------------------------------------- +// API fetching +// --------------------------------------------------------------------------- + func (wctx *wizardContext) fetchFromAPI() { wctx.flowNames = fetchFlowNames() wctx.customAttestTypes = fetchCustomAttestationTypes() diff --git a/go.mod b/go.mod index ffd98ad31..f99abc278 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,9 @@ require ( github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 github.com/aws/smithy-go v1.24.2 + github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/huh v1.0.0 + github.com/charmbracelet/lipgloss v1.1.0 github.com/containers/image/v5 v5.34.3 github.com/docker/docker v28.0.4+incompatible github.com/go-git/go-billy/v5 v5.8.0 @@ -88,9 +90,7 @@ require ( github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect - github.com/charmbracelet/bubbletea v1.3.10 // indirect github.com/charmbracelet/colorprofile v0.3.1 // indirect - github.com/charmbracelet/lipgloss v1.1.0 // indirect github.com/charmbracelet/x/ansi v0.10.1 // indirect github.com/charmbracelet/x/cellbuf v0.0.13 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect From 0966799e402800a18a341c137347bb4c9ac8c6c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:18:48 +0200 Subject: [PATCH 10/53] fix: confirm options not rendering + preview panel too narrow - Forward WindowSizeMsg to huh form so Confirm fields render their Yes/No options correctly (was returning early before form got resize) - Preview panel now takes all remaining terminal width instead of being capped at 40 chars - Enable help text on forms so navigation keys are visible --- cmd/kosli/createPolicyFile.go | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index fccc0a566..7932d73d5 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -186,11 +186,7 @@ func newWizardStyles() wizardStyles { // Bubbletea model // --------------------------------------------------------------------------- -const ( - formWidth = 50 - previewWidth = 40 - maxWidth = 100 -) +const formWidth = 55 type policyWizardModel struct { step wizardStep @@ -224,7 +220,7 @@ func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { policy: policy.NewPolicy(), wctx: wctx, styles: newWizardStyles(), - width: maxWidth, + width: 120, // sensible default until WindowSizeMsg arrives } m.form = m.buildForm() return m @@ -239,7 +235,6 @@ func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height - return m, nil case tea.KeyMsg: if msg.String() == "ctrl+c" { m.cancelled = true @@ -247,7 +242,7 @@ func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } } - // Forward to form + // Always forward to form (including WindowSizeMsg) form, cmd := m.form.Update(msg) if f, ok := form.(*huh.Form); ok { m.form = f @@ -281,13 +276,11 @@ func (m policyWizardModel) View() string { s := m.styles fw := formWidth - pw := previewWidth available := m.width - s.base.GetHorizontalFrameSize() - if available < fw+pw+4 { - pw = available - fw - 4 - if pw < 20 { - pw = 0 - } + // Give remaining width to preview, with a gap of 2 + pw := available - fw - 2 + if pw < 30 { + pw = 0 // hide preview if terminal too narrow } // Header @@ -552,7 +545,7 @@ func (m *policyWizardModel) buildForm() *huh.Form { f = huh.NewForm(huh.NewGroup()) } - return f.WithWidth(formWidth).WithShowHelp(false).WithShowErrors(true) + return f.WithWidth(formWidth).WithShowHelp(true).WithShowErrors(true) } func notEmpty(field string) func(string) error { From df1f6b0f447ee4b17ad680f6163ea7c7f0426ed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:22:48 +0200 Subject: [PATCH 11/53] fix: confirm selections ignored due to bubbletea value semantics Replace pointer-based Value(&field) bindings with Key-based form access (form.GetBool/GetString). Bubbletea passes the model by value in Update(), so pointers taken in buildForm() point to stale copies. Using form.Get*() reads directly from the form's internal state which survives copies. --- cmd/kosli/createPolicyFile.go | 159 ++++++++++++---------------------- 1 file changed, 55 insertions(+), 104 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 7932d73d5..62bac260e 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -198,20 +198,15 @@ type policyWizardModel struct { height int // State for loops and expression building - exprTarget exprTarget - exprMode string - exprContext string // for custom expressions - exprTagKey string // for flow tag / custom tag - currentAttRule policy.AttestationRule - cancelled bool - confirmed bool - - // Form-bound values - requireProv bool - requireTrail bool - confirmBool bool - inputStr string - selectStr string + exprTarget exprTarget + exprMode string + exprContext string // for custom expressions + exprTagKey string // for flow tag / custom tag + currentAttRule policy.AttestationRule + cancelled bool + confirmed bool + requireProv bool + requireTrail bool } func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { @@ -328,57 +323,45 @@ func (m *policyWizardModel) buildForm() *huh.Form { var f *huh.Form switch m.step { case stepBasics: - m.requireProv = true - m.requireTrail = true f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("prov"). Title("Require artifact provenance?"). Description("All artifacts must belong to a Kosli flow"). - Value(&m.requireProv). Affirmative("Yes").Negative("No"), - huh.NewConfirm(). + huh.NewConfirm().Key("trail"). Title("Require trail compliance?"). Description("All artifacts must be part of compliant trails"). - Value(&m.requireTrail). Affirmative("Yes").Negative("No"), )) case stepProvExcConfirm: - m.confirmBool = false f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("confirm"). Title(m.excConfirmTitle("provenance")). Description("Exceptions waive this requirement for matching artifacts"). - Value(&m.confirmBool). Affirmative("Yes").Negative("No"), )) case stepTrailExcConfirm: - m.confirmBool = false f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("confirm"). Title(m.excConfirmTitle("trail compliance")). Description("Exceptions waive this requirement for matching artifacts"). - Value(&m.confirmBool). Affirmative("Yes").Negative("No"), )) case stepAttConfirm: - m.confirmBool = false title := "Add a required attestation?" if m.policy.Artifacts != nil && len(m.policy.Artifacts.Attestations) > 0 { title = "Add another required attestation?" } f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("confirm"). Title(title). - Value(&m.confirmBool). Affirmative("Yes").Negative("No"), )) case stepAttDetails: - m.selectStr = "" - m.inputStr = "" allTypes := append([]string{}, builtInAttestationTypes...) allTypes = append(allTypes, m.wctx.customAttestTypes...) opts := make([]huh.Option[string], len(allTypes)) @@ -386,31 +369,26 @@ func (m *policyWizardModel) buildForm() *huh.Form { opts[i] = huh.NewOption(t, t) } f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("type"). Title("Attestation type"). - Options(opts...). - Value(&m.selectStr), - huh.NewInput(). + Options(opts...), + huh.NewInput().Key("name"). Title("Attestation name"). Description("Use * to match any name for this type"). - Placeholder("*"). - Value(&m.inputStr), + Placeholder("*"), )) case stepAttCondConfirm: - m.confirmBool = false f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("confirm"). Title("Add a condition for this attestation?"). Description("Only require when condition is met"). - Value(&m.confirmBool). Affirmative("Yes").Negative("No"), )) case stepExprMode: - m.selectStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("mode"). Title("How do you want to define this condition?"). Options( huh.NewOption("Match by flow name", "flow_name"), @@ -418,126 +396,101 @@ func (m *policyWizardModel) buildForm() *huh.Form { huh.NewOption("Match by artifact name pattern", "artifact_name"), huh.NewOption("Custom comparison", "custom"), huh.NewOption("Write raw expression", "raw"), - ). - Value(&m.selectStr), + ), )) case stepExprFlowName: - m.inputStr = "" - m.selectStr = "" if len(m.wctx.flowNames) > 0 { opts := make([]huh.Option[string], len(m.wctx.flowNames)) for i, n := range m.wctx.flowNames { opts[i] = huh.NewOption(n, n) } f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("value"). Title("Select a flow"). - Options(opts...). - Value(&m.selectStr), + Options(opts...), )) } else { f = huh.NewForm(huh.NewGroup( - huh.NewInput(). + huh.NewInput().Key("value"). Title("Flow name"). Description("The flow name to match"). - Value(&m.inputStr). Validate(notEmpty("flow name")), )) } case stepExprFlowTag: - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewInput(). + huh.NewInput().Key("value"). Title("Tag key"). Description("e.g. team, risk-level, key.with.dots"). - Value(&m.inputStr). Validate(notEmpty("tag key")), )) case stepExprFlowTagOp: - m.selectStr = "" - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("op"). Title("Operator"). - Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...). - Value(&m.selectStr), - huh.NewInput(). + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...), + huh.NewInput().Key("value"). Title("Value"). Description("The value to compare against"). - Value(&m.inputStr). Validate(notEmpty("value")), )) case stepExprArtifactName: - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewInput(). + huh.NewInput().Key("value"). Title("Artifact name regex"). Description("e.g. ^datadog:.*"). Placeholder("^datadog:.*"). - Value(&m.inputStr). Validate(notEmpty("regex")), )) case stepExprCustomCtx: - m.selectStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("value"). Title("Context field"). Options( huh.NewOption("flow.name", "flow.name"), huh.NewOption("flow.tags.", "flow.tags."), huh.NewOption("artifact.name", "artifact.name"), huh.NewOption("artifact.fingerprint", "artifact.fingerprint"), - ). - Value(&m.selectStr), + ), )) case stepExprCustomTagKey: - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewInput(). + huh.NewInput().Key("value"). Title("Tag key"). Description("The flow tag key (e.g. team, risk-level)"). - Value(&m.inputStr). Validate(notEmpty("tag key")), )) case stepExprCustomOp: - m.selectStr = "" - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string](). + huh.NewSelect[string]().Key("op"). Title("Operator"). - Options(huh.NewOptions("==", "!=", "in", "matches")...). - Value(&m.selectStr), - huh.NewInput(). + Options(huh.NewOptions("==", "!=", "in", "matches")...), + huh.NewInput().Key("value"). Title("Value"). Description("The value to compare against"). - Value(&m.inputStr). Validate(notEmpty("value")), )) case stepExprRaw: - m.inputStr = "" f = huh.NewForm(huh.NewGroup( - huh.NewInput(). + huh.NewInput().Key("value"). Title("Raw expression"). Description(`e.g. flow.name == "prod" and artifact.name == "svc"`). Placeholder(`flow.name == "prod"`). - Value(&m.inputStr). Validate(notEmpty("expression")), )) case stepFinalConfirm: - m.confirmBool = true f = huh.NewForm(huh.NewGroup( - huh.NewConfirm(). + huh.NewConfirm().Key("confirm"). Title("Write this policy?"). - Value(&m.confirmBool). Affirmative("Yes").Negative("No"), )) @@ -578,6 +531,8 @@ func (m *policyWizardModel) excConfirmTitle(rule string) string { func (m *policyWizardModel) processFormResults() { switch m.step { case stepBasics: + m.requireProv = m.form.GetBool("prov") + m.requireTrail = m.form.GetBool("trail") if m.requireProv || m.requireTrail { if m.policy.Artifacts == nil { m.policy.Artifacts = &policy.ArtifactRules{} @@ -591,48 +546,44 @@ func (m *policyWizardModel) processFormResults() { } case stepAttDetails: - name := m.inputStr + name := m.form.GetString("name") if name == "" { name = "*" } m.currentAttRule = policy.AttestationRule{ - Type: m.selectStr, + Type: m.form.GetString("type"), Name: name, } case stepExprMode: - m.exprMode = m.selectStr + m.exprMode = m.form.GetString("mode") case stepExprFlowName: - name := m.selectStr - if name == "" { - name = m.inputStr - } - m.applyExpression(policy.FlowNameExpr(name)) + m.applyExpression(policy.FlowNameExpr(m.form.GetString("value"))) case stepExprFlowTag: - m.exprTagKey = m.inputStr + m.exprTagKey = m.form.GetString("value") case stepExprFlowTagOp: - m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.selectStr, m.inputStr)) + m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.form.GetString("op"), m.form.GetString("value"))) case stepExprArtifactName: - m.applyExpression(policy.ArtifactNameMatchExpr(m.inputStr)) + m.applyExpression(policy.ArtifactNameMatchExpr(m.form.GetString("value"))) case stepExprCustomCtx: - m.exprContext = m.selectStr + m.exprContext = m.form.GetString("value") case stepExprCustomTagKey: - m.exprContext = "flow.tags." + m.inputStr + m.exprContext = "flow.tags." + m.form.GetString("value") case stepExprCustomOp: - m.applyExpression(policy.ComparisonExpr(m.exprContext, m.selectStr, m.inputStr)) + m.applyExpression(policy.ComparisonExpr(m.exprContext, m.form.GetString("op"), m.form.GetString("value"))) case stepExprRaw: - m.applyExpression(policy.WrapExpr(m.inputStr)) + m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) case stepFinalConfirm: - m.confirmed = m.confirmBool + m.confirmed = m.form.GetBool("confirm") } } @@ -676,7 +627,7 @@ func (m *policyWizardModel) advanceStep() { } case stepProvExcConfirm: - if m.confirmBool { + if m.form.GetBool("confirm") { m.exprTarget = targetProvException m.step = stepExprMode } else if m.requireTrail { @@ -687,7 +638,7 @@ func (m *policyWizardModel) advanceStep() { } case stepTrailExcConfirm: - if m.confirmBool { + if m.form.GetBool("confirm") { m.exprTarget = targetTrailException m.step = stepExprMode } else { @@ -695,7 +646,7 @@ func (m *policyWizardModel) advanceStep() { } case stepAttConfirm: - if m.confirmBool { + if m.form.GetBool("confirm") { m.step = stepAttDetails } else { m.step = stepFinalConfirm @@ -705,7 +656,7 @@ func (m *policyWizardModel) advanceStep() { m.step = stepAttCondConfirm case stepAttCondConfirm: - if m.confirmBool { + if m.form.GetBool("confirm") { m.exprTarget = targetAttCondition m.step = stepExprMode } else { From 18126228e54777031743d74d1366d6bc9e1071f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:35:48 +0200 Subject: [PATCH 12/53] fix: configure each rule fully before moving to the next MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split stepBasics into stepProvConfirm and stepTrailConfirm so the wizard flow is: provenance? → exceptions? → trail compliance? → exceptions? → attestations. Each rule is fully configured before the next is presented. --- cmd/kosli/createPolicyFile.go | 54 +++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 62bac260e..9cc347b5e 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -109,9 +109,10 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { type wizardStep int const ( - stepBasics wizardStep = iota // provenance + trail compliance - stepProvExcConfirm // add provenance exception? - stepTrailExcConfirm // add trail compliance exception? + stepProvConfirm wizardStep = iota // require provenance? + stepProvExcConfirm // add provenance exception? + stepTrailConfirm // require trail compliance? + stepTrailExcConfirm // add trail compliance exception? stepAttConfirm // add attestation? stepAttDetails // attestation type + name stepAttCondConfirm // add condition for attestation? @@ -211,7 +212,7 @@ type policyWizardModel struct { func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { m := policyWizardModel{ - step: stepBasics, + step: stepProvConfirm, policy: policy.NewPolicy(), wctx: wctx, styles: newWizardStyles(), @@ -322,13 +323,17 @@ var builtInAttestationTypes = []string{ func (m *policyWizardModel) buildForm() *huh.Form { var f *huh.Form switch m.step { - case stepBasics: + case stepProvConfirm: f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("prov"). + huh.NewConfirm().Key("confirm"). Title("Require artifact provenance?"). Description("All artifacts must belong to a Kosli flow"). Affirmative("Yes").Negative("No"), - huh.NewConfirm().Key("trail"). + )) + + case stepTrailConfirm: + f = huh.NewForm(huh.NewGroup( + huh.NewConfirm().Key("confirm"). Title("Require trail compliance?"). Description("All artifacts must be part of compliant trails"). Affirmative("Yes").Negative("No"), @@ -530,19 +535,22 @@ func (m *policyWizardModel) excConfirmTitle(rule string) string { func (m *policyWizardModel) processFormResults() { switch m.step { - case stepBasics: - m.requireProv = m.form.GetBool("prov") - m.requireTrail = m.form.GetBool("trail") - if m.requireProv || m.requireTrail { + case stepProvConfirm: + m.requireProv = m.form.GetBool("confirm") + if m.requireProv { if m.policy.Artifacts == nil { m.policy.Artifacts = &policy.ArtifactRules{} } - if m.requireProv { - m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} - } - if m.requireTrail { - m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} + } + + case stepTrailConfirm: + m.requireTrail = m.form.GetBool("confirm") + if m.requireTrail { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} } + m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} } case stepAttDetails: @@ -615,22 +623,24 @@ func (m *policyWizardModel) commitAttestation() { func (m *policyWizardModel) advanceStep() { switch m.step { - case stepBasics: + case stepProvConfirm: if m.requireProv { m.exprTarget = targetProvException m.step = stepProvExcConfirm - } else if m.requireTrail { - m.exprTarget = targetTrailException - m.step = stepTrailExcConfirm } else { - m.step = stepAttConfirm + m.step = stepTrailConfirm } case stepProvExcConfirm: if m.form.GetBool("confirm") { m.exprTarget = targetProvException m.step = stepExprMode - } else if m.requireTrail { + } else { + m.step = stepTrailConfirm + } + + case stepTrailConfirm: + if m.requireTrail { m.exprTarget = targetTrailException m.step = stepTrailExcConfirm } else { From 596f607114b9a698c490b9ae8e91ddf3740eb419 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:41:53 +0200 Subject: [PATCH 13/53] refactor: wizard asks for output filename instead of --output-file flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the final confirm step with a filename input (defaulting to policy.yaml). The file is always saved to disk — no more stdout output. Remove the --output-file flag since the wizard handles it interactively. --- cmd/kosli/createPolicyFile.go | 59 +++++++++++++++-------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 9cc347b5e..7d11e1333 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -33,19 +33,11 @@ custom attestation types from the Kosli API to offer as suggestions. ` const createPolicyFileExample = ` -# create a policy file interactively (output to stdout): +# create a policy file interactively: kosli create policy-file - -# create a policy file and write to a file: -kosli create policy-file --output-file policy.yml ` -type createPolicyFileOptions struct { - outputFile string -} - func newCreatePolicyFileCmd(out io.Writer) *cobra.Command { - o := new(createPolicyFileOptions) cmd := &cobra.Command{ Use: "policy-file", Short: createPolicyFileShortDesc, @@ -53,16 +45,14 @@ func newCreatePolicyFileCmd(out io.Writer) *cobra.Command { Example: createPolicyFileExample, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return o.run(out) + return runCreatePolicyFile() }, } - cmd.Flags().StringVarP(&o.outputFile, "output-file", "o", "", "write policy YAML to this file instead of stdout") - return cmd } -func (o *createPolicyFileOptions) run(out io.Writer) error { +func runCreatePolicyFile() error { if !term.IsTerminal(int(os.Stdin.Fd())) { return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") } @@ -79,7 +69,7 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { } wm := finalModel.(policyWizardModel) - if wm.cancelled || !wm.confirmed { + if wm.cancelled { logger.Info("policy file creation cancelled") return nil } @@ -89,17 +79,16 @@ func (o *createPolicyFileOptions) run(out io.Writer) error { return fmt.Errorf("failed to generate policy YAML: %w", err) } - if o.outputFile != "" { - err = os.WriteFile(o.outputFile, yamlBytes, 0644) - if err != nil { - return fmt.Errorf("failed to write policy file: %w", err) - } - logger.Info("policy file written to %s", o.outputFile) - return nil + filename := wm.outputFile + if filename == "" { + filename = "policy.yaml" } - - _, err = out.Write(yamlBytes) - return err + err = os.WriteFile(filename, yamlBytes, 0644) + if err != nil { + return fmt.Errorf("failed to write policy file: %w", err) + } + logger.Info("policy file written to %s", filename) + return nil } // --------------------------------------------------------------------------- @@ -125,7 +114,7 @@ const ( stepExprCustomTagKey // tag key for custom context stepExprCustomOp // custom operator + value stepExprRaw // raw expression input - stepFinalConfirm // write policy? + stepSaveFile // ask for filename stepDone ) @@ -205,7 +194,7 @@ type policyWizardModel struct { exprTagKey string // for flow tag / custom tag currentAttRule policy.AttestationRule cancelled bool - confirmed bool + outputFile string requireProv bool requireTrail bool } @@ -492,11 +481,13 @@ func (m *policyWizardModel) buildForm() *huh.Form { Validate(notEmpty("expression")), )) - case stepFinalConfirm: + case stepSaveFile: f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title("Write this policy?"). - Affirmative("Yes").Negative("No"), + huh.NewInput().Key("filename"). + Title("Save policy to file"). + Description("Enter filename (e.g. policy.yaml)"). + Placeholder("policy.yaml"). + Validate(notEmpty("filename")), )) default: @@ -590,8 +581,8 @@ func (m *policyWizardModel) processFormResults() { case stepExprRaw: m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) - case stepFinalConfirm: - m.confirmed = m.form.GetBool("confirm") + case stepSaveFile: + m.outputFile = m.form.GetString("filename") } } @@ -659,7 +650,7 @@ func (m *policyWizardModel) advanceStep() { if m.form.GetBool("confirm") { m.step = stepAttDetails } else { - m.step = stepFinalConfirm + m.step = stepSaveFile } case stepAttDetails: @@ -716,7 +707,7 @@ func (m *policyWizardModel) advanceStep() { case stepExprRaw: m.advanceAfterExpr() - case stepFinalConfirm: + case stepSaveFile: m.step = stepDone } } From 11bba479a3b14f1c7bd176342a63d50f5c2d1f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 22:50:28 +0200 Subject: [PATCH 14/53] fix: always emit name explicitly, add wildcard type with validation - Remove omitempty on AttestationRule.Name so name: '*' is always written explicitly in the YAML output - Add '*' to the attestation type options in the wizard - When type is '*', validate that name is not '*' (schema constraint) - Show validation error inline and loop back to the form on failure --- cmd/kosli/createPolicyFile.go | 23 ++++++++++++++++++++--- internal/policy/policy.go | 25 ++----------------------- internal/policy/policy_test.go | 31 +++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 30 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 7d11e1333..d15cc696a 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -197,6 +197,7 @@ type policyWizardModel struct { outputFile string requireProv bool requireTrail bool + validationErr string } func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { @@ -240,6 +241,11 @@ func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.form.State == huh.StateCompleted { m.processFormResults() + if m.validationErr != "" { + // Stay on the same step, rebuild the form + m.form = m.buildForm() + return m, m.form.Init() + } m.advanceStep() if m.step == stepDone { return m, tea.Quit @@ -272,9 +278,14 @@ func (m policyWizardModel) View() string { header := s.title.Render("Kosli Policy Builder") // Form (left) + formContent := m.form.View() + if m.validationErr != "" { + errStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FE5F86")).Bold(true) + formContent = errStyle.Render("⚠ "+m.validationErr) + "\n\n" + formContent + } formView := lipgloss.NewStyle(). Width(fw). - Render(m.form.View()) + Render(formContent) var body string if pw > 0 { @@ -306,7 +317,7 @@ func (m policyWizardModel) View() string { // --------------------------------------------------------------------------- var builtInAttestationTypes = []string{ - "generic", "junit", "snyk", "pull_request", "jira", "sonar", + "generic", "junit", "snyk", "pull_request", "jira", "sonar", "*", } func (m *policyWizardModel) buildForm() *huh.Form { @@ -545,12 +556,18 @@ func (m *policyWizardModel) processFormResults() { } case stepAttDetails: + attType := m.form.GetString("type") name := m.form.GetString("name") if name == "" { name = "*" } + if attType == "*" && name == "*" { + m.validationErr = "when type is *, name must not be * — please specify a name" + return + } + m.validationErr = "" m.currentAttRule = policy.AttestationRule{ - Type: m.form.GetString("type"), + Type: attType, Name: name, } diff --git a/internal/policy/policy.go b/internal/policy/policy.go index 7fa6848ec..042bcb97c 100644 --- a/internal/policy/policy.go +++ b/internal/policy/policy.go @@ -26,7 +26,7 @@ type ExceptionRule struct { type AttestationRule struct { Type string `yaml:"type"` - Name string `yaml:"name,omitempty"` + Name string `yaml:"name"` If string `yaml:"if,omitempty"` } @@ -37,26 +37,5 @@ func NewPolicy() *Policy { } func (p *Policy) ToYAML() ([]byte, error) { - out := p.normalized() - return yaml.Marshal(out) -} - -// normalized returns a copy with wildcard "*" names cleared so they are omitted from YAML. -func (p *Policy) normalized() *Policy { - cp := *p - if cp.Artifacts != nil { - arts := *cp.Artifacts - if len(arts.Attestations) > 0 { - normalized := make([]AttestationRule, len(arts.Attestations)) - for i, a := range arts.Attestations { - if a.Name == "*" { - a.Name = "" - } - normalized[i] = a - } - arts.Attestations = normalized - } - cp.Artifacts = &arts - } - return &cp + return yaml.Marshal(p) } diff --git a/internal/policy/policy_test.go b/internal/policy/policy_test.go index f2a6f4439..c2f6e04ff 100644 --- a/internal/policy/policy_test.go +++ b/internal/policy/policy_test.go @@ -82,7 +82,7 @@ func TestToYAML_SingleAttestation(t *testing.T) { p := NewPolicy() p.Artifacts = &ArtifactRules{ Attestations: []AttestationRule{ - {Type: "snyk"}, + {Type: "snyk", Name: "*"}, }, } out, err := p.ToYAML() @@ -92,6 +92,7 @@ func TestToYAML_SingleAttestation(t *testing.T) { artifacts: attestations: - type: snyk + name: '*' ` assert.Equal(t, expected, string(out)) } @@ -125,7 +126,7 @@ func TestToYAML_MultipleAttestations(t *testing.T) { p.Artifacts = &ArtifactRules{ Attestations: []AttestationRule{ {Type: "snyk", Name: "security-scan"}, - {Type: "junit"}, + {Type: "junit", Name: "*"}, {Type: "custom:coverage-metrics", Name: "coverage"}, }, } @@ -138,6 +139,7 @@ artifacts: - type: snyk name: security-scan - type: junit + name: '*' - type: custom:coverage-metrics name: coverage ` @@ -189,7 +191,7 @@ artifacts: assert.Equal(t, expected, string(out)) } -func TestToYAML_WildcardNameOmitted(t *testing.T) { +func TestToYAML_WildcardNameExplicit(t *testing.T) { p := NewPolicy() p.Artifacts = &ArtifactRules{ Attestations: []AttestationRule{ @@ -199,11 +201,32 @@ func TestToYAML_WildcardNameOmitted(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - // name: "*" should be omitted from output + // name: "*" should always be explicit in output expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 artifacts: attestations: - type: snyk + name: '*' +` + assert.Equal(t, expected, string(out)) +} + +func TestToYAML_WildcardTypeRequiresNonWildcardName(t *testing.T) { + // When type is "*", name must not be "*" per the schema + p := NewPolicy() + p.Artifacts = &ArtifactRules{ + Attestations: []AttestationRule{ + {Type: "*", Name: "security-scan"}, + }, + } + out, err := p.ToYAML() + require.NoError(t, err) + + expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 +artifacts: + attestations: + - type: '*' + name: security-scan ` assert.Equal(t, expected, string(out)) } From a9898075202bd9c1e1e8a02ca915ceb15cf83a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 23:28:44 +0200 Subject: [PATCH 15/53] refactor: split createPolicyFile into focused files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - createPolicyFile.go (89 lines) — Cobra command + file writing - policyWizardModel.go (133 lines) — bubbletea Init/Update/View - policyWizardForms.go (432 lines) — step enums, form builders, state transitions, form result processing - policyWizardContext.go (126 lines) — API fetching, styles Also extracted confirmForm() and inputForm() helpers to reduce repetition in the form builder switch. --- cmd/kosli/createPolicyFile.go | 748 +------------------------------ cmd/kosli/policyWizardContext.go | 126 ++++++ cmd/kosli/policyWizardForms.go | 432 ++++++++++++++++++ cmd/kosli/policyWizardModel.go | 133 ++++++ 4 files changed, 701 insertions(+), 738 deletions(-) create mode 100644 cmd/kosli/policyWizardContext.go create mode 100644 cmd/kosli/policyWizardForms.go create mode 100644 cmd/kosli/policyWizardModel.go diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index d15cc696a..040ec00a9 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -1,19 +1,12 @@ package main import ( - "encoding/json" "fmt" "io" - "net/http" - "net/url" "os" - "strings" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/huh" - "github.com/charmbracelet/lipgloss" "github.com/kosli-dev/cli/internal/policy" - "github.com/kosli-dev/cli/internal/requests" "github.com/spf13/cobra" "golang.org/x/term" ) @@ -23,7 +16,7 @@ const createPolicyFileShortDesc = `Interactively create a Kosli environment poli const createPolicyFileLongDesc = createPolicyFileShortDesc + ` Launches an interactive wizard that guides you through building a policy file conforming to the Kosli environment policy schema. The generated YAML is -written to stdout by default, or to a file with ^--output-file^. +written to a file you specify at the end of the wizard. This command does not upload the policy to Kosli. Use ^kosli create policy^ to upload the generated file. @@ -74,744 +67,23 @@ func runCreatePolicyFile() error { return nil } - yamlBytes, err := wm.policy.ToYAML() - if err != nil { - return fmt.Errorf("failed to generate policy YAML: %w", err) - } + return writePolicyFile(wm.policy, wm.outputFile) +} - filename := wm.outputFile +func writePolicyFile(p *policy.Policy, filename string) error { if filename == "" { filename = "policy.yaml" } - err = os.WriteFile(filename, yamlBytes, 0644) - if err != nil { - return fmt.Errorf("failed to write policy file: %w", err) - } - logger.Info("policy file written to %s", filename) - return nil -} - -// --------------------------------------------------------------------------- -// Wizard step enum -// --------------------------------------------------------------------------- - -type wizardStep int - -const ( - stepProvConfirm wizardStep = iota // require provenance? - stepProvExcConfirm // add provenance exception? - stepTrailConfirm // require trail compliance? - stepTrailExcConfirm // add trail compliance exception? - stepAttConfirm // add attestation? - stepAttDetails // attestation type + name - stepAttCondConfirm // add condition for attestation? - stepExprMode // choose expression mode - stepExprFlowName // flow name input/select - stepExprFlowTag // tag key input - stepExprFlowTagOp // tag operator + value - stepExprArtifactName // artifact regex input - stepExprCustomCtx // custom context select - stepExprCustomTagKey // tag key for custom context - stepExprCustomOp // custom operator + value - stepExprRaw // raw expression input - stepSaveFile // ask for filename - stepDone -) - -// exprTarget tracks what we're building an expression for. -type exprTarget int - -const ( - targetProvException exprTarget = iota - targetTrailException - targetAttCondition -) - -// --------------------------------------------------------------------------- -// Wizard context (API data) -// --------------------------------------------------------------------------- - -type wizardContext struct { - flowNames []string - customAttestTypes []string -} - -// --------------------------------------------------------------------------- -// Styles -// --------------------------------------------------------------------------- - -type wizardStyles struct { - base lipgloss.Style - title lipgloss.Style - preview lipgloss.Style - previewText lipgloss.Style - footer lipgloss.Style - accent lipgloss.Style -} - -func newWizardStyles() wizardStyles { - accent := lipgloss.Color("#7571F9") - green := lipgloss.Color("#02BF87") - return wizardStyles{ - base: lipgloss.NewStyle().Padding(1, 2), - title: lipgloss.NewStyle(). - Bold(true). - Foreground(accent). - Padding(0, 1), - preview: lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(accent). - Padding(1, 2), - previewText: lipgloss.NewStyle(). - Foreground(green), - footer: lipgloss.NewStyle(). - Foreground(lipgloss.Color("240")). - Padding(1, 1, 0, 1), - accent: lipgloss.NewStyle(). - Foreground(accent), - } -} - -// --------------------------------------------------------------------------- -// Bubbletea model -// --------------------------------------------------------------------------- - -const formWidth = 55 - -type policyWizardModel struct { - step wizardStep - form *huh.Form - policy *policy.Policy - wctx *wizardContext - styles wizardStyles - width int - height int - - // State for loops and expression building - exprTarget exprTarget - exprMode string - exprContext string // for custom expressions - exprTagKey string // for flow tag / custom tag - currentAttRule policy.AttestationRule - cancelled bool - outputFile string - requireProv bool - requireTrail bool - validationErr string -} - -func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { - m := policyWizardModel{ - step: stepProvConfirm, - policy: policy.NewPolicy(), - wctx: wctx, - styles: newWizardStyles(), - width: 120, // sensible default until WindowSizeMsg arrives - } - m.form = m.buildForm() - return m -} - -func (m policyWizardModel) Init() tea.Cmd { - return m.form.Init() -} - -func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.width = msg.Width - m.height = msg.Height - case tea.KeyMsg: - if msg.String() == "ctrl+c" { - m.cancelled = true - return m, tea.Quit - } - } - - // Always forward to form (including WindowSizeMsg) - form, cmd := m.form.Update(msg) - if f, ok := form.(*huh.Form); ok { - m.form = f - } - - if m.form.State == huh.StateAborted { - m.cancelled = true - return m, tea.Quit - } - - if m.form.State == huh.StateCompleted { - m.processFormResults() - if m.validationErr != "" { - // Stay on the same step, rebuild the form - m.form = m.buildForm() - return m, m.form.Init() - } - m.advanceStep() - if m.step == stepDone { - return m, tea.Quit - } - m.form = m.buildForm() - return m, m.form.Init() - } - - return m, cmd -} - -func (m policyWizardModel) View() string { - if m.cancelled { - return "" - } - if m.step == stepDone { - return "" - } - - s := m.styles - fw := formWidth - available := m.width - s.base.GetHorizontalFrameSize() - // Give remaining width to preview, with a gap of 2 - pw := available - fw - 2 - if pw < 30 { - pw = 0 // hide preview if terminal too narrow - } - - // Header - header := s.title.Render("Kosli Policy Builder") - - // Form (left) - formContent := m.form.View() - if m.validationErr != "" { - errStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FE5F86")).Bold(true) - formContent = errStyle.Render("⚠ "+m.validationErr) + "\n\n" + formContent - } - formView := lipgloss.NewStyle(). - Width(fw). - Render(formContent) - - var body string - if pw > 0 { - // YAML preview (right) - yamlBytes, _ := m.policy.ToYAML() - yamlStr := strings.TrimRight(string(yamlBytes), "\n") - if yamlStr == "" { - yamlStr = "(empty)" - } - previewContent := s.previewText.Render(yamlStr) - previewTitle := s.accent.Bold(true).Render("Live Preview") - previewPanel := s.preview. - Width(pw). - Render(previewTitle + "\n\n" + previewContent) - - body = lipgloss.JoinHorizontal(lipgloss.Top, formView, " ", previewPanel) - } else { - body = formView - } - - // Footer - footer := s.footer.Render("ctrl+c to cancel • enter to confirm") - - return s.base.Render(header + "\n\n" + body + "\n" + footer) -} - -// --------------------------------------------------------------------------- -// Form builders — one per step -// --------------------------------------------------------------------------- - -var builtInAttestationTypes = []string{ - "generic", "junit", "snyk", "pull_request", "jira", "sonar", "*", -} - -func (m *policyWizardModel) buildForm() *huh.Form { - var f *huh.Form - switch m.step { - case stepProvConfirm: - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title("Require artifact provenance?"). - Description("All artifacts must belong to a Kosli flow"). - Affirmative("Yes").Negative("No"), - )) - - case stepTrailConfirm: - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title("Require trail compliance?"). - Description("All artifacts must be part of compliant trails"). - Affirmative("Yes").Negative("No"), - )) - - case stepProvExcConfirm: - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title(m.excConfirmTitle("provenance")). - Description("Exceptions waive this requirement for matching artifacts"). - Affirmative("Yes").Negative("No"), - )) - - case stepTrailExcConfirm: - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title(m.excConfirmTitle("trail compliance")). - Description("Exceptions waive this requirement for matching artifacts"). - Affirmative("Yes").Negative("No"), - )) - - case stepAttConfirm: - title := "Add a required attestation?" - if m.policy.Artifacts != nil && len(m.policy.Artifacts.Attestations) > 0 { - title = "Add another required attestation?" - } - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title(title). - Affirmative("Yes").Negative("No"), - )) - - case stepAttDetails: - allTypes := append([]string{}, builtInAttestationTypes...) - allTypes = append(allTypes, m.wctx.customAttestTypes...) - opts := make([]huh.Option[string], len(allTypes)) - for i, t := range allTypes { - opts[i] = huh.NewOption(t, t) - } - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("type"). - Title("Attestation type"). - Options(opts...), - huh.NewInput().Key("name"). - Title("Attestation name"). - Description("Use * to match any name for this type"). - Placeholder("*"), - )) - - case stepAttCondConfirm: - f = huh.NewForm(huh.NewGroup( - huh.NewConfirm().Key("confirm"). - Title("Add a condition for this attestation?"). - Description("Only require when condition is met"). - Affirmative("Yes").Negative("No"), - )) - - case stepExprMode: - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("mode"). - Title("How do you want to define this condition?"). - Options( - huh.NewOption("Match by flow name", "flow_name"), - huh.NewOption("Match by flow tag", "flow_tag"), - huh.NewOption("Match by artifact name pattern", "artifact_name"), - huh.NewOption("Custom comparison", "custom"), - huh.NewOption("Write raw expression", "raw"), - ), - )) - - case stepExprFlowName: - if len(m.wctx.flowNames) > 0 { - opts := make([]huh.Option[string], len(m.wctx.flowNames)) - for i, n := range m.wctx.flowNames { - opts[i] = huh.NewOption(n, n) - } - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("value"). - Title("Select a flow"). - Options(opts...), - )) - } else { - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("value"). - Title("Flow name"). - Description("The flow name to match"). - Validate(notEmpty("flow name")), - )) - } - - case stepExprFlowTag: - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("value"). - Title("Tag key"). - Description("e.g. team, risk-level, key.with.dots"). - Validate(notEmpty("tag key")), - )) - - case stepExprFlowTagOp: - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("op"). - Title("Operator"). - Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...), - huh.NewInput().Key("value"). - Title("Value"). - Description("The value to compare against"). - Validate(notEmpty("value")), - )) - - case stepExprArtifactName: - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("value"). - Title("Artifact name regex"). - Description("e.g. ^datadog:.*"). - Placeholder("^datadog:.*"). - Validate(notEmpty("regex")), - )) - - case stepExprCustomCtx: - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("value"). - Title("Context field"). - Options( - huh.NewOption("flow.name", "flow.name"), - huh.NewOption("flow.tags.", "flow.tags."), - huh.NewOption("artifact.name", "artifact.name"), - huh.NewOption("artifact.fingerprint", "artifact.fingerprint"), - ), - )) - - case stepExprCustomTagKey: - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("value"). - Title("Tag key"). - Description("The flow tag key (e.g. team, risk-level)"). - Validate(notEmpty("tag key")), - )) - - case stepExprCustomOp: - f = huh.NewForm(huh.NewGroup( - huh.NewSelect[string]().Key("op"). - Title("Operator"). - Options(huh.NewOptions("==", "!=", "in", "matches")...), - huh.NewInput().Key("value"). - Title("Value"). - Description("The value to compare against"). - Validate(notEmpty("value")), - )) - - case stepExprRaw: - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("value"). - Title("Raw expression"). - Description(`e.g. flow.name == "prod" and artifact.name == "svc"`). - Placeholder(`flow.name == "prod"`). - Validate(notEmpty("expression")), - )) - - case stepSaveFile: - f = huh.NewForm(huh.NewGroup( - huh.NewInput().Key("filename"). - Title("Save policy to file"). - Description("Enter filename (e.g. policy.yaml)"). - Placeholder("policy.yaml"). - Validate(notEmpty("filename")), - )) - - default: - f = huh.NewForm(huh.NewGroup()) - } - - return f.WithWidth(formWidth).WithShowHelp(true).WithShowErrors(true) -} -func notEmpty(field string) func(string) error { - return func(s string) error { - if s == "" { - return fmt.Errorf("%s is required", field) - } - return nil - } -} - -func (m *policyWizardModel) excConfirmTitle(rule string) string { - var count int - if rule == "provenance" && m.policy.Artifacts != nil && m.policy.Artifacts.Provenance != nil { - count = len(m.policy.Artifacts.Provenance.Exceptions) - } - if rule == "trail compliance" && m.policy.Artifacts != nil && m.policy.Artifacts.TrailCompliance != nil { - count = len(m.policy.Artifacts.TrailCompliance.Exceptions) - } - if count > 0 { - return fmt.Sprintf("Add another exception to %s?", rule) - } - return fmt.Sprintf("Add an exception to %s?", rule) -} - -// --------------------------------------------------------------------------- -// State transitions -// --------------------------------------------------------------------------- - -func (m *policyWizardModel) processFormResults() { - switch m.step { - case stepProvConfirm: - m.requireProv = m.form.GetBool("confirm") - if m.requireProv { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} - } - m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} - } - - case stepTrailConfirm: - m.requireTrail = m.form.GetBool("confirm") - if m.requireTrail { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} - } - m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} - } - - case stepAttDetails: - attType := m.form.GetString("type") - name := m.form.GetString("name") - if name == "" { - name = "*" - } - if attType == "*" && name == "*" { - m.validationErr = "when type is *, name must not be * — please specify a name" - return - } - m.validationErr = "" - m.currentAttRule = policy.AttestationRule{ - Type: attType, - Name: name, - } - - case stepExprMode: - m.exprMode = m.form.GetString("mode") - - case stepExprFlowName: - m.applyExpression(policy.FlowNameExpr(m.form.GetString("value"))) - - case stepExprFlowTag: - m.exprTagKey = m.form.GetString("value") - - case stepExprFlowTagOp: - m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.form.GetString("op"), m.form.GetString("value"))) - - case stepExprArtifactName: - m.applyExpression(policy.ArtifactNameMatchExpr(m.form.GetString("value"))) - - case stepExprCustomCtx: - m.exprContext = m.form.GetString("value") - - case stepExprCustomTagKey: - m.exprContext = "flow.tags." + m.form.GetString("value") - - case stepExprCustomOp: - m.applyExpression(policy.ComparisonExpr(m.exprContext, m.form.GetString("op"), m.form.GetString("value"))) - - case stepExprRaw: - m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) - - case stepSaveFile: - m.outputFile = m.form.GetString("filename") - } -} - -func (m *policyWizardModel) applyExpression(expr string) { - switch m.exprTarget { - case targetProvException: - m.policy.Artifacts.Provenance.Exceptions = append( - m.policy.Artifacts.Provenance.Exceptions, - policy.ExceptionRule{If: expr}, - ) - case targetTrailException: - m.policy.Artifacts.TrailCompliance.Exceptions = append( - m.policy.Artifacts.TrailCompliance.Exceptions, - policy.ExceptionRule{If: expr}, - ) - case targetAttCondition: - m.currentAttRule.If = expr - m.commitAttestation() - } -} - -func (m *policyWizardModel) commitAttestation() { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} - } - m.policy.Artifacts.Attestations = append(m.policy.Artifacts.Attestations, m.currentAttRule) - m.currentAttRule = policy.AttestationRule{} -} - -func (m *policyWizardModel) advanceStep() { - switch m.step { - case stepProvConfirm: - if m.requireProv { - m.exprTarget = targetProvException - m.step = stepProvExcConfirm - } else { - m.step = stepTrailConfirm - } - - case stepProvExcConfirm: - if m.form.GetBool("confirm") { - m.exprTarget = targetProvException - m.step = stepExprMode - } else { - m.step = stepTrailConfirm - } - - case stepTrailConfirm: - if m.requireTrail { - m.exprTarget = targetTrailException - m.step = stepTrailExcConfirm - } else { - m.step = stepAttConfirm - } - - case stepTrailExcConfirm: - if m.form.GetBool("confirm") { - m.exprTarget = targetTrailException - m.step = stepExprMode - } else { - m.step = stepAttConfirm - } - - case stepAttConfirm: - if m.form.GetBool("confirm") { - m.step = stepAttDetails - } else { - m.step = stepSaveFile - } - - case stepAttDetails: - m.step = stepAttCondConfirm - - case stepAttCondConfirm: - if m.form.GetBool("confirm") { - m.exprTarget = targetAttCondition - m.step = stepExprMode - } else { - m.commitAttestation() - m.step = stepAttConfirm - } - - case stepExprMode: - switch m.exprMode { - case "flow_name": - m.step = stepExprFlowName - case "flow_tag": - m.step = stepExprFlowTag - case "artifact_name": - m.step = stepExprArtifactName - case "custom": - m.step = stepExprCustomCtx - case "raw": - m.step = stepExprRaw - } - - case stepExprFlowName: - m.advanceAfterExpr() - - case stepExprFlowTag: - m.step = stepExprFlowTagOp - - case stepExprFlowTagOp: - m.advanceAfterExpr() - - case stepExprArtifactName: - m.advanceAfterExpr() - - case stepExprCustomCtx: - if m.exprContext == "flow.tags." { - m.step = stepExprCustomTagKey - } else { - m.step = stepExprCustomOp - } - - case stepExprCustomTagKey: - m.step = stepExprCustomOp - - case stepExprCustomOp: - m.advanceAfterExpr() - - case stepExprRaw: - m.advanceAfterExpr() - - case stepSaveFile: - m.step = stepDone - } -} - -func (m *policyWizardModel) advanceAfterExpr() { - switch m.exprTarget { - case targetProvException: - m.step = stepProvExcConfirm - case targetTrailException: - m.step = stepTrailExcConfirm - case targetAttCondition: - // attestation was committed in applyExpression - m.step = stepAttConfirm - } -} - -// --------------------------------------------------------------------------- -// API fetching -// --------------------------------------------------------------------------- - -func (wctx *wizardContext) fetchFromAPI() { - wctx.flowNames = fetchFlowNames() - wctx.customAttestTypes = fetchCustomAttestationTypes() -} - -func fetchFlowNames() []string { - u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) - if err != nil { - logger.Debug("failed to build flows URL: %v", err) - return nil - } - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: u, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - logger.Debug("failed to fetch flows: %v", err) - return nil - } - - var flows []map[string]any - if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { - logger.Debug("failed to parse flows response: %v", err) - return nil - } - - names := make([]string, 0, len(flows)) - for _, flow := range flows { - if name, ok := flow["name"].(string); ok { - names = append(names, name) - } - } - return names -} - -func fetchCustomAttestationTypes() []string { - u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) + yamlBytes, err := p.ToYAML() if err != nil { - logger.Debug("failed to build attestation types URL: %v", err) - return nil + return fmt.Errorf("failed to generate policy YAML: %w", err) } - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: u, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) + err = os.WriteFile(filename, yamlBytes, 0644) if err != nil { - logger.Debug("failed to fetch attestation types: %v", err) - return nil - } - - var types []map[string]any - if err := json.Unmarshal([]byte(response.Body), &types); err != nil { - logger.Debug("failed to parse attestation types response: %v", err) - return nil - } - - names := make([]string, 0, len(types)) - for _, t := range types { - if name, ok := t["name"].(string); ok { - names = append(names, "custom:"+name) - } + return fmt.Errorf("failed to write policy file: %w", err) } - return names + logger.Info("policy file written to %s", filename) + return nil } diff --git a/cmd/kosli/policyWizardContext.go b/cmd/kosli/policyWizardContext.go new file mode 100644 index 000000000..c4b5cb437 --- /dev/null +++ b/cmd/kosli/policyWizardContext.go @@ -0,0 +1,126 @@ +package main + +import ( + "encoding/json" + "net/http" + "net/url" + + "github.com/charmbracelet/lipgloss" + "github.com/kosli-dev/cli/internal/requests" +) + +// --------------------------------------------------------------------------- +// Wizard context (API data) +// --------------------------------------------------------------------------- + +type wizardContext struct { + flowNames []string + customAttestTypes []string +} + +func (wctx *wizardContext) fetchFromAPI() { + wctx.flowNames = fetchFlowNames() + wctx.customAttestTypes = fetchCustomAttestationTypes() +} + +func fetchFlowNames() []string { + u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) + if err != nil { + logger.Debug("failed to build flows URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch flows: %v", err) + return nil + } + + var flows []map[string]any + if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { + logger.Debug("failed to parse flows response: %v", err) + return nil + } + + names := make([]string, 0, len(flows)) + for _, flow := range flows { + if name, ok := flow["name"].(string); ok { + names = append(names, name) + } + } + return names +} + +func fetchCustomAttestationTypes() []string { + u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) + if err != nil { + logger.Debug("failed to build attestation types URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch attestation types: %v", err) + return nil + } + + var types []map[string]any + if err := json.Unmarshal([]byte(response.Body), &types); err != nil { + logger.Debug("failed to parse attestation types response: %v", err) + return nil + } + + names := make([]string, 0, len(types)) + for _, t := range types { + if name, ok := t["name"].(string); ok { + names = append(names, "custom:"+name) + } + } + return names +} + +// --------------------------------------------------------------------------- +// Styles +// --------------------------------------------------------------------------- + +type wizardStyles struct { + base lipgloss.Style + title lipgloss.Style + preview lipgloss.Style + previewText lipgloss.Style + footer lipgloss.Style + accent lipgloss.Style +} + +func newWizardStyles() wizardStyles { + accent := lipgloss.Color("#7571F9") + green := lipgloss.Color("#02BF87") + return wizardStyles{ + base: lipgloss.NewStyle().Padding(1, 2), + title: lipgloss.NewStyle(). + Bold(true). + Foreground(accent). + Padding(0, 1), + preview: lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(accent). + Padding(1, 2), + previewText: lipgloss.NewStyle(). + Foreground(green), + footer: lipgloss.NewStyle(). + Foreground(lipgloss.Color("240")). + Padding(1, 1, 0, 1), + accent: lipgloss.NewStyle(). + Foreground(accent), + } +} diff --git a/cmd/kosli/policyWizardForms.go b/cmd/kosli/policyWizardForms.go new file mode 100644 index 000000000..cde10d683 --- /dev/null +++ b/cmd/kosli/policyWizardForms.go @@ -0,0 +1,432 @@ +package main + +import ( + "fmt" + + "github.com/charmbracelet/huh" + "github.com/kosli-dev/cli/internal/policy" +) + +// --------------------------------------------------------------------------- +// Step and target enums +// --------------------------------------------------------------------------- + +type wizardStep int + +const ( + stepProvConfirm wizardStep = iota // require provenance? + stepProvExcConfirm // add provenance exception? + stepTrailConfirm // require trail compliance? + stepTrailExcConfirm // add trail compliance exception? + stepAttConfirm // add attestation? + stepAttDetails // attestation type + name + stepAttCondConfirm // add condition for attestation? + stepExprMode // choose expression mode + stepExprFlowName // flow name input/select + stepExprFlowTag // tag key input + stepExprFlowTagOp // tag operator + value + stepExprArtifactName // artifact regex input + stepExprCustomCtx // custom context select + stepExprCustomTagKey // tag key for custom context + stepExprCustomOp // custom operator + value + stepExprRaw // raw expression input + stepSaveFile // ask for filename + stepDone +) + +type exprTarget int + +const ( + targetProvException exprTarget = iota + targetTrailException + targetAttCondition +) + +// --------------------------------------------------------------------------- +// Form builders +// --------------------------------------------------------------------------- + +var builtInAttestationTypes = []string{ + "generic", "junit", "snyk", "pull_request", "jira", "sonar", "*", +} + +func (m *policyWizardModel) buildForm() *huh.Form { + var f *huh.Form + switch m.step { + case stepProvConfirm: + f = confirmForm("Require artifact provenance?", + "All artifacts must belong to a Kosli flow") + + case stepTrailConfirm: + f = confirmForm("Require trail compliance?", + "All artifacts must be part of compliant trails") + + case stepProvExcConfirm: + f = confirmForm(m.excConfirmTitle("provenance"), + "Exceptions waive this requirement for matching artifacts") + + case stepTrailExcConfirm: + f = confirmForm(m.excConfirmTitle("trail compliance"), + "Exceptions waive this requirement for matching artifacts") + + case stepAttConfirm: + title := "Add a required attestation?" + if m.policy.Artifacts != nil && len(m.policy.Artifacts.Attestations) > 0 { + title = "Add another required attestation?" + } + f = confirmForm(title, "") + + case stepAttDetails: + allTypes := append([]string{}, builtInAttestationTypes...) + allTypes = append(allTypes, m.wctx.customAttestTypes...) + opts := make([]huh.Option[string], len(allTypes)) + for i, t := range allTypes { + opts[i] = huh.NewOption(t, t) + } + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("type"). + Title("Attestation type"). + Options(opts...), + huh.NewInput().Key("name"). + Title("Attestation name"). + Description("Use * to match any name for this type"). + Placeholder("*"), + )) + + case stepAttCondConfirm: + f = confirmForm("Add a condition for this attestation?", + "Only require when condition is met") + + case stepExprMode: + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("mode"). + Title("How do you want to define this condition?"). + Options( + huh.NewOption("Match by flow name", "flow_name"), + huh.NewOption("Match by flow tag", "flow_tag"), + huh.NewOption("Match by artifact name pattern", "artifact_name"), + huh.NewOption("Custom comparison", "custom"), + huh.NewOption("Write raw expression", "raw"), + ), + )) + + case stepExprFlowName: + if len(m.wctx.flowNames) > 0 { + opts := make([]huh.Option[string], len(m.wctx.flowNames)) + for i, n := range m.wctx.flowNames { + opts[i] = huh.NewOption(n, n) + } + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("value"). + Title("Select a flow").Options(opts...), + )) + } else { + f = inputForm("value", "Flow name", "The flow name to match", "", "flow name") + } + + case stepExprFlowTag: + f = inputForm("value", "Tag key", "e.g. team, risk-level, key.with.dots", "", "tag key") + + case stepExprFlowTagOp: + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("op").Title("Operator"). + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...), + huh.NewInput().Key("value").Title("Value"). + Description("The value to compare against"). + Validate(notEmpty("value")), + )) + + case stepExprArtifactName: + f = inputForm("value", "Artifact name regex", "e.g. ^datadog:.*", "^datadog:.*", "regex") + + case stepExprCustomCtx: + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("value").Title("Context field"). + Options( + huh.NewOption("flow.name", "flow.name"), + huh.NewOption("flow.tags.", "flow.tags."), + huh.NewOption("artifact.name", "artifact.name"), + huh.NewOption("artifact.fingerprint", "artifact.fingerprint"), + ), + )) + + case stepExprCustomTagKey: + f = inputForm("value", "Tag key", "The flow tag key (e.g. team, risk-level)", "", "tag key") + + case stepExprCustomOp: + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("op").Title("Operator"). + Options(huh.NewOptions("==", "!=", "in", "matches")...), + huh.NewInput().Key("value").Title("Value"). + Description("The value to compare against"). + Validate(notEmpty("value")), + )) + + case stepExprRaw: + f = inputForm("value", "Raw expression", + `e.g. flow.name == "prod" and artifact.name == "svc"`, + `flow.name == "prod"`, "expression") + + case stepSaveFile: + f = inputForm("filename", "Save policy to file", + "Enter filename (e.g. policy.yaml)", "policy.yaml", "filename") + + default: + f = huh.NewForm(huh.NewGroup()) + } + + return f.WithWidth(formWidth).WithShowHelp(true).WithShowErrors(true) +} + +// --------------------------------------------------------------------------- +// Form helpers +// --------------------------------------------------------------------------- + +func confirmForm(title, description string) *huh.Form { + c := huh.NewConfirm().Key("confirm"). + Title(title). + Affirmative("Yes").Negative("No") + if description != "" { + c = c.Description(description) + } + return huh.NewForm(huh.NewGroup(c)) +} + +func inputForm(key, title, description, placeholder, requiredName string) *huh.Form { + inp := huh.NewInput().Key(key).Title(title). + Description(description). + Placeholder(placeholder). + Validate(notEmpty(requiredName)) + return huh.NewForm(huh.NewGroup(inp)) +} + +func notEmpty(field string) func(string) error { + return func(s string) error { + if s == "" { + return fmt.Errorf("%s is required", field) + } + return nil + } +} + +func (m *policyWizardModel) excConfirmTitle(rule string) string { + var count int + if rule == "provenance" && m.policy.Artifacts != nil && m.policy.Artifacts.Provenance != nil { + count = len(m.policy.Artifacts.Provenance.Exceptions) + } + if rule == "trail compliance" && m.policy.Artifacts != nil && m.policy.Artifacts.TrailCompliance != nil { + count = len(m.policy.Artifacts.TrailCompliance.Exceptions) + } + if count > 0 { + return fmt.Sprintf("Add another exception to %s?", rule) + } + return fmt.Sprintf("Add an exception to %s?", rule) +} + +// --------------------------------------------------------------------------- +// State transitions: processFormResults +// --------------------------------------------------------------------------- + +func (m *policyWizardModel) processFormResults() { + switch m.step { + case stepProvConfirm: + m.requireProv = m.form.GetBool("confirm") + if m.requireProv { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} + } + m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} + } + + case stepTrailConfirm: + m.requireTrail = m.form.GetBool("confirm") + if m.requireTrail { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} + } + m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + } + + case stepAttDetails: + attType := m.form.GetString("type") + name := m.form.GetString("name") + if name == "" { + name = "*" + } + if attType == "*" && name == "*" { + m.validationErr = "when type is *, name must not be * — please specify a name" + return + } + m.validationErr = "" + m.currentAttRule = policy.AttestationRule{ + Type: attType, + Name: name, + } + + case stepExprMode: + m.exprMode = m.form.GetString("mode") + + case stepExprFlowName: + m.applyExpression(policy.FlowNameExpr(m.form.GetString("value"))) + + case stepExprFlowTag: + m.exprTagKey = m.form.GetString("value") + + case stepExprFlowTagOp: + m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.form.GetString("op"), m.form.GetString("value"))) + + case stepExprArtifactName: + m.applyExpression(policy.ArtifactNameMatchExpr(m.form.GetString("value"))) + + case stepExprCustomCtx: + m.exprContext = m.form.GetString("value") + + case stepExprCustomTagKey: + m.exprContext = "flow.tags." + m.form.GetString("value") + + case stepExprCustomOp: + m.applyExpression(policy.ComparisonExpr(m.exprContext, m.form.GetString("op"), m.form.GetString("value"))) + + case stepExprRaw: + m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) + + case stepSaveFile: + m.outputFile = m.form.GetString("filename") + } +} + +func (m *policyWizardModel) applyExpression(expr string) { + switch m.exprTarget { + case targetProvException: + m.policy.Artifacts.Provenance.Exceptions = append( + m.policy.Artifacts.Provenance.Exceptions, + policy.ExceptionRule{If: expr}, + ) + case targetTrailException: + m.policy.Artifacts.TrailCompliance.Exceptions = append( + m.policy.Artifacts.TrailCompliance.Exceptions, + policy.ExceptionRule{If: expr}, + ) + case targetAttCondition: + m.currentAttRule.If = expr + m.commitAttestation() + } +} + +func (m *policyWizardModel) commitAttestation() { + if m.policy.Artifacts == nil { + m.policy.Artifacts = &policy.ArtifactRules{} + } + m.policy.Artifacts.Attestations = append(m.policy.Artifacts.Attestations, m.currentAttRule) + m.currentAttRule = policy.AttestationRule{} +} + +// --------------------------------------------------------------------------- +// State transitions: advanceStep +// --------------------------------------------------------------------------- + +func (m *policyWizardModel) advanceStep() { + switch m.step { + case stepProvConfirm: + if m.requireProv { + m.exprTarget = targetProvException + m.step = stepProvExcConfirm + } else { + m.step = stepTrailConfirm + } + + case stepProvExcConfirm: + if m.form.GetBool("confirm") { + m.exprTarget = targetProvException + m.step = stepExprMode + } else { + m.step = stepTrailConfirm + } + + case stepTrailConfirm: + if m.requireTrail { + m.exprTarget = targetTrailException + m.step = stepTrailExcConfirm + } else { + m.step = stepAttConfirm + } + + case stepTrailExcConfirm: + if m.form.GetBool("confirm") { + m.exprTarget = targetTrailException + m.step = stepExprMode + } else { + m.step = stepAttConfirm + } + + case stepAttConfirm: + if m.form.GetBool("confirm") { + m.step = stepAttDetails + } else { + m.step = stepSaveFile + } + + case stepAttDetails: + m.step = stepAttCondConfirm + + case stepAttCondConfirm: + if m.form.GetBool("confirm") { + m.exprTarget = targetAttCondition + m.step = stepExprMode + } else { + m.commitAttestation() + m.step = stepAttConfirm + } + + case stepExprMode: + switch m.exprMode { + case "flow_name": + m.step = stepExprFlowName + case "flow_tag": + m.step = stepExprFlowTag + case "artifact_name": + m.step = stepExprArtifactName + case "custom": + m.step = stepExprCustomCtx + case "raw": + m.step = stepExprRaw + } + + case stepExprFlowName: + m.advanceAfterExpr() + case stepExprFlowTag: + m.step = stepExprFlowTagOp + case stepExprFlowTagOp: + m.advanceAfterExpr() + case stepExprArtifactName: + m.advanceAfterExpr() + + case stepExprCustomCtx: + if m.exprContext == "flow.tags." { + m.step = stepExprCustomTagKey + } else { + m.step = stepExprCustomOp + } + + case stepExprCustomTagKey: + m.step = stepExprCustomOp + case stepExprCustomOp: + m.advanceAfterExpr() + case stepExprRaw: + m.advanceAfterExpr() + + case stepSaveFile: + m.step = stepDone + } +} + +func (m *policyWizardModel) advanceAfterExpr() { + switch m.exprTarget { + case targetProvException: + m.step = stepProvExcConfirm + case targetTrailException: + m.step = stepTrailExcConfirm + case targetAttCondition: + m.step = stepAttConfirm + } +} diff --git a/cmd/kosli/policyWizardModel.go b/cmd/kosli/policyWizardModel.go new file mode 100644 index 000000000..1f439ed5e --- /dev/null +++ b/cmd/kosli/policyWizardModel.go @@ -0,0 +1,133 @@ +package main + +import ( + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" + "github.com/kosli-dev/cli/internal/policy" +) + +const formWidth = 55 + +type policyWizardModel struct { + step wizardStep + form *huh.Form + policy *policy.Policy + wctx *wizardContext + styles wizardStyles + width int + height int + + // State for loops and expression building + exprTarget exprTarget + exprMode string + exprContext string + exprTagKey string + currentAttRule policy.AttestationRule + cancelled bool + outputFile string + requireProv bool + requireTrail bool + validationErr string +} + +func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { + m := policyWizardModel{ + step: stepProvConfirm, + policy: policy.NewPolicy(), + wctx: wctx, + styles: newWizardStyles(), + width: 120, + } + m.form = m.buildForm() + return m +} + +func (m policyWizardModel) Init() tea.Cmd { + return m.form.Init() +} + +func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.width = msg.Width + m.height = msg.Height + case tea.KeyMsg: + if msg.String() == "ctrl+c" { + m.cancelled = true + return m, tea.Quit + } + } + + form, cmd := m.form.Update(msg) + if f, ok := form.(*huh.Form); ok { + m.form = f + } + + if m.form.State == huh.StateAborted { + m.cancelled = true + return m, tea.Quit + } + + if m.form.State == huh.StateCompleted { + m.processFormResults() + if m.validationErr != "" { + m.form = m.buildForm() + return m, m.form.Init() + } + m.advanceStep() + if m.step == stepDone { + return m, tea.Quit + } + m.form = m.buildForm() + return m, m.form.Init() + } + + return m, cmd +} + +func (m policyWizardModel) View() string { + if m.cancelled || m.step == stepDone { + return "" + } + + s := m.styles + fw := formWidth + available := m.width - s.base.GetHorizontalFrameSize() + pw := available - fw - 2 + if pw < 30 { + pw = 0 + } + + header := s.title.Render("Kosli Policy Builder") + + formContent := m.form.View() + if m.validationErr != "" { + errStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FE5F86")).Bold(true) + formContent = errStyle.Render("⚠ "+m.validationErr) + "\n\n" + formContent + } + formView := lipgloss.NewStyle().Width(fw).Render(formContent) + + var body string + if pw > 0 { + yamlBytes, _ := m.policy.ToYAML() + yamlStr := strings.TrimRight(string(yamlBytes), "\n") + if yamlStr == "" { + yamlStr = "(empty)" + } + previewContent := s.previewText.Render(yamlStr) + previewTitle := s.accent.Bold(true).Render("Live Preview") + previewPanel := s.preview.Width(pw). + Render(previewTitle + "\n\n" + previewContent) + + body = lipgloss.JoinHorizontal(lipgloss.Top, formView, " ", previewPanel) + } else { + body = formView + } + + footer := s.footer.Render("ctrl+c to cancel • enter to confirm") + + return s.base.Render(header + "\n\n" + body + "\n" + footer) +} From 4370127be72d0a05ba62345a18f527b84463e533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 23:34:28 +0200 Subject: [PATCH 16/53] refactor: move policy wizard to internal/policywizard The wizard (bubbletea model, forms, styles) is now in internal/policywizard with no dependency on cmd/kosli globals. API fetching stays in cmd/kosli/createPolicyFile.go and populates policywizard.Context before launching the wizard. --- cmd/kosli/createPolicyFile.go | 90 +++++++++++-- cmd/kosli/policyWizardContext.go | 126 ------------------ internal/policywizard/context.go | 41 ++++++ .../policywizard/forms.go | 96 ++++++------- .../policywizard/model.go | 44 +++--- 5 files changed, 192 insertions(+), 205 deletions(-) delete mode 100644 cmd/kosli/policyWizardContext.go create mode 100644 internal/policywizard/context.go rename cmd/kosli/policyWizardForms.go => internal/policywizard/forms.go (78%) rename cmd/kosli/policyWizardModel.go => internal/policywizard/model.go (76%) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 040ec00a9..ee3698209 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -1,12 +1,16 @@ package main import ( + "encoding/json" "fmt" "io" + "net/http" + "net/url" "os" tea "github.com/charmbracelet/bubbletea" - "github.com/kosli-dev/cli/internal/policy" + "github.com/kosli-dev/cli/internal/policywizard" + "github.com/kosli-dev/cli/internal/requests" "github.com/spf13/cobra" "golang.org/x/term" ) @@ -50,32 +54,30 @@ func runCreatePolicyFile() error { return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") } - wctx := &wizardContext{} + ctx := &policywizard.Context{} if global.ApiToken != "" && global.Org != "" { - wctx.fetchFromAPI() + ctx.FlowNames = fetchFlowNames() + ctx.CustomAttestTypes = fetchCustomAttestationTypes() } - m := newPolicyWizardModel(wctx) + m := policywizard.NewModel(ctx) finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { return fmt.Errorf("wizard error: %w", err) } - wm := finalModel.(policyWizardModel) - if wm.cancelled { + wm := finalModel.(policywizard.Model) + if wm.Cancelled { logger.Info("policy file creation cancelled") return nil } - return writePolicyFile(wm.policy, wm.outputFile) -} - -func writePolicyFile(p *policy.Policy, filename string) error { + filename := wm.OutputFile if filename == "" { filename = "policy.yaml" } - yamlBytes, err := p.ToYAML() + yamlBytes, err := wm.Policy.ToYAML() if err != nil { return fmt.Errorf("failed to generate policy YAML: %w", err) } @@ -87,3 +89,69 @@ func writePolicyFile(p *policy.Policy, filename string) error { logger.Info("policy file written to %s", filename) return nil } + +func fetchFlowNames() []string { + u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) + if err != nil { + logger.Debug("failed to build flows URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch flows: %v", err) + return nil + } + + var flows []map[string]any + if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { + logger.Debug("failed to parse flows response: %v", err) + return nil + } + + names := make([]string, 0, len(flows)) + for _, flow := range flows { + if name, ok := flow["name"].(string); ok { + names = append(names, name) + } + } + return names +} + +func fetchCustomAttestationTypes() []string { + u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) + if err != nil { + logger.Debug("failed to build attestation types URL: %v", err) + return nil + } + + reqParams := &requests.RequestParams{ + Method: http.MethodGet, + URL: u, + Token: global.ApiToken, + } + response, err := kosliClient.Do(reqParams) + if err != nil { + logger.Debug("failed to fetch attestation types: %v", err) + return nil + } + + var types []map[string]any + if err := json.Unmarshal([]byte(response.Body), &types); err != nil { + logger.Debug("failed to parse attestation types response: %v", err) + return nil + } + + names := make([]string, 0, len(types)) + for _, t := range types { + if name, ok := t["name"].(string); ok { + names = append(names, "custom:"+name) + } + } + return names +} diff --git a/cmd/kosli/policyWizardContext.go b/cmd/kosli/policyWizardContext.go deleted file mode 100644 index c4b5cb437..000000000 --- a/cmd/kosli/policyWizardContext.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -import ( - "encoding/json" - "net/http" - "net/url" - - "github.com/charmbracelet/lipgloss" - "github.com/kosli-dev/cli/internal/requests" -) - -// --------------------------------------------------------------------------- -// Wizard context (API data) -// --------------------------------------------------------------------------- - -type wizardContext struct { - flowNames []string - customAttestTypes []string -} - -func (wctx *wizardContext) fetchFromAPI() { - wctx.flowNames = fetchFlowNames() - wctx.customAttestTypes = fetchCustomAttestationTypes() -} - -func fetchFlowNames() []string { - u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) - if err != nil { - logger.Debug("failed to build flows URL: %v", err) - return nil - } - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: u, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - logger.Debug("failed to fetch flows: %v", err) - return nil - } - - var flows []map[string]any - if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { - logger.Debug("failed to parse flows response: %v", err) - return nil - } - - names := make([]string, 0, len(flows)) - for _, flow := range flows { - if name, ok := flow["name"].(string); ok { - names = append(names, name) - } - } - return names -} - -func fetchCustomAttestationTypes() []string { - u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) - if err != nil { - logger.Debug("failed to build attestation types URL: %v", err) - return nil - } - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: u, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - logger.Debug("failed to fetch attestation types: %v", err) - return nil - } - - var types []map[string]any - if err := json.Unmarshal([]byte(response.Body), &types); err != nil { - logger.Debug("failed to parse attestation types response: %v", err) - return nil - } - - names := make([]string, 0, len(types)) - for _, t := range types { - if name, ok := t["name"].(string); ok { - names = append(names, "custom:"+name) - } - } - return names -} - -// --------------------------------------------------------------------------- -// Styles -// --------------------------------------------------------------------------- - -type wizardStyles struct { - base lipgloss.Style - title lipgloss.Style - preview lipgloss.Style - previewText lipgloss.Style - footer lipgloss.Style - accent lipgloss.Style -} - -func newWizardStyles() wizardStyles { - accent := lipgloss.Color("#7571F9") - green := lipgloss.Color("#02BF87") - return wizardStyles{ - base: lipgloss.NewStyle().Padding(1, 2), - title: lipgloss.NewStyle(). - Bold(true). - Foreground(accent). - Padding(0, 1), - preview: lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(accent). - Padding(1, 2), - previewText: lipgloss.NewStyle(). - Foreground(green), - footer: lipgloss.NewStyle(). - Foreground(lipgloss.Color("240")). - Padding(1, 1, 0, 1), - accent: lipgloss.NewStyle(). - Foreground(accent), - } -} diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go new file mode 100644 index 000000000..27a36167f --- /dev/null +++ b/internal/policywizard/context.go @@ -0,0 +1,41 @@ +package policywizard + +import "github.com/charmbracelet/lipgloss" + +// Context holds data fetched from the API to populate wizard options. +type Context struct { + FlowNames []string + CustomAttestTypes []string +} + +type styles struct { + base lipgloss.Style + title lipgloss.Style + preview lipgloss.Style + previewText lipgloss.Style + footer lipgloss.Style + accent lipgloss.Style +} + +func newStyles() styles { + accent := lipgloss.Color("#7571F9") + green := lipgloss.Color("#02BF87") + return styles{ + base: lipgloss.NewStyle().Padding(1, 2), + title: lipgloss.NewStyle(). + Bold(true). + Foreground(accent). + Padding(0, 1), + preview: lipgloss.NewStyle(). + Border(lipgloss.RoundedBorder()). + BorderForeground(accent). + Padding(1, 2), + previewText: lipgloss.NewStyle(). + Foreground(green), + footer: lipgloss.NewStyle(). + Foreground(lipgloss.Color("240")). + Padding(1, 1, 0, 1), + accent: lipgloss.NewStyle(). + Foreground(accent), + } +} diff --git a/cmd/kosli/policyWizardForms.go b/internal/policywizard/forms.go similarity index 78% rename from cmd/kosli/policyWizardForms.go rename to internal/policywizard/forms.go index cde10d683..1f8aeeb94 100644 --- a/cmd/kosli/policyWizardForms.go +++ b/internal/policywizard/forms.go @@ -1,4 +1,4 @@ -package main +package policywizard import ( "fmt" @@ -14,23 +14,23 @@ import ( type wizardStep int const ( - stepProvConfirm wizardStep = iota // require provenance? - stepProvExcConfirm // add provenance exception? - stepTrailConfirm // require trail compliance? - stepTrailExcConfirm // add trail compliance exception? - stepAttConfirm // add attestation? - stepAttDetails // attestation type + name - stepAttCondConfirm // add condition for attestation? - stepExprMode // choose expression mode - stepExprFlowName // flow name input/select - stepExprFlowTag // tag key input - stepExprFlowTagOp // tag operator + value - stepExprArtifactName // artifact regex input - stepExprCustomCtx // custom context select - stepExprCustomTagKey // tag key for custom context - stepExprCustomOp // custom operator + value - stepExprRaw // raw expression input - stepSaveFile // ask for filename + stepProvConfirm wizardStep = iota + stepProvExcConfirm + stepTrailConfirm + stepTrailExcConfirm + stepAttConfirm + stepAttDetails + stepAttCondConfirm + stepExprMode + stepExprFlowName + stepExprFlowTag + stepExprFlowTagOp + stepExprArtifactName + stepExprCustomCtx + stepExprCustomTagKey + stepExprCustomOp + stepExprRaw + stepSaveFile stepDone ) @@ -50,7 +50,7 @@ var builtInAttestationTypes = []string{ "generic", "junit", "snyk", "pull_request", "jira", "sonar", "*", } -func (m *policyWizardModel) buildForm() *huh.Form { +func (m *Model) buildForm() *huh.Form { var f *huh.Form switch m.step { case stepProvConfirm: @@ -71,14 +71,14 @@ func (m *policyWizardModel) buildForm() *huh.Form { case stepAttConfirm: title := "Add a required attestation?" - if m.policy.Artifacts != nil && len(m.policy.Artifacts.Attestations) > 0 { + if m.Policy.Artifacts != nil && len(m.Policy.Artifacts.Attestations) > 0 { title = "Add another required attestation?" } f = confirmForm(title, "") case stepAttDetails: allTypes := append([]string{}, builtInAttestationTypes...) - allTypes = append(allTypes, m.wctx.customAttestTypes...) + allTypes = append(allTypes, m.ctx.CustomAttestTypes...) opts := make([]huh.Option[string], len(allTypes)) for i, t := range allTypes { opts[i] = huh.NewOption(t, t) @@ -111,9 +111,9 @@ func (m *policyWizardModel) buildForm() *huh.Form { )) case stepExprFlowName: - if len(m.wctx.flowNames) > 0 { - opts := make([]huh.Option[string], len(m.wctx.flowNames)) - for i, n := range m.wctx.flowNames { + if len(m.ctx.FlowNames) > 0 { + opts := make([]huh.Option[string], len(m.ctx.FlowNames)) + for i, n := range m.ctx.FlowNames { opts[i] = huh.NewOption(n, n) } f = huh.NewForm(huh.NewGroup( @@ -209,13 +209,13 @@ func notEmpty(field string) func(string) error { } } -func (m *policyWizardModel) excConfirmTitle(rule string) string { +func (m *Model) excConfirmTitle(rule string) string { var count int - if rule == "provenance" && m.policy.Artifacts != nil && m.policy.Artifacts.Provenance != nil { - count = len(m.policy.Artifacts.Provenance.Exceptions) + if rule == "provenance" && m.Policy.Artifacts != nil && m.Policy.Artifacts.Provenance != nil { + count = len(m.Policy.Artifacts.Provenance.Exceptions) } - if rule == "trail compliance" && m.policy.Artifacts != nil && m.policy.Artifacts.TrailCompliance != nil { - count = len(m.policy.Artifacts.TrailCompliance.Exceptions) + if rule == "trail compliance" && m.Policy.Artifacts != nil && m.Policy.Artifacts.TrailCompliance != nil { + count = len(m.Policy.Artifacts.TrailCompliance.Exceptions) } if count > 0 { return fmt.Sprintf("Add another exception to %s?", rule) @@ -227,24 +227,24 @@ func (m *policyWizardModel) excConfirmTitle(rule string) string { // State transitions: processFormResults // --------------------------------------------------------------------------- -func (m *policyWizardModel) processFormResults() { +func (m *Model) processFormResults() { switch m.step { case stepProvConfirm: m.requireProv = m.form.GetBool("confirm") if m.requireProv { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} + if m.Policy.Artifacts == nil { + m.Policy.Artifacts = &policy.ArtifactRules{} } - m.policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} + m.Policy.Artifacts.Provenance = &policy.BooleanRule{Required: true} } case stepTrailConfirm: m.requireTrail = m.form.GetBool("confirm") if m.requireTrail { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} + if m.Policy.Artifacts == nil { + m.Policy.Artifacts = &policy.ArtifactRules{} } - m.policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} + m.Policy.Artifacts.TrailCompliance = &policy.BooleanRule{Required: true} } case stepAttDetails: @@ -291,20 +291,20 @@ func (m *policyWizardModel) processFormResults() { m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) case stepSaveFile: - m.outputFile = m.form.GetString("filename") + m.OutputFile = m.form.GetString("filename") } } -func (m *policyWizardModel) applyExpression(expr string) { +func (m *Model) applyExpression(expr string) { switch m.exprTarget { case targetProvException: - m.policy.Artifacts.Provenance.Exceptions = append( - m.policy.Artifacts.Provenance.Exceptions, + m.Policy.Artifacts.Provenance.Exceptions = append( + m.Policy.Artifacts.Provenance.Exceptions, policy.ExceptionRule{If: expr}, ) case targetTrailException: - m.policy.Artifacts.TrailCompliance.Exceptions = append( - m.policy.Artifacts.TrailCompliance.Exceptions, + m.Policy.Artifacts.TrailCompliance.Exceptions = append( + m.Policy.Artifacts.TrailCompliance.Exceptions, policy.ExceptionRule{If: expr}, ) case targetAttCondition: @@ -313,11 +313,11 @@ func (m *policyWizardModel) applyExpression(expr string) { } } -func (m *policyWizardModel) commitAttestation() { - if m.policy.Artifacts == nil { - m.policy.Artifacts = &policy.ArtifactRules{} +func (m *Model) commitAttestation() { + if m.Policy.Artifacts == nil { + m.Policy.Artifacts = &policy.ArtifactRules{} } - m.policy.Artifacts.Attestations = append(m.policy.Artifacts.Attestations, m.currentAttRule) + m.Policy.Artifacts.Attestations = append(m.Policy.Artifacts.Attestations, m.currentAttRule) m.currentAttRule = policy.AttestationRule{} } @@ -325,7 +325,7 @@ func (m *policyWizardModel) commitAttestation() { // State transitions: advanceStep // --------------------------------------------------------------------------- -func (m *policyWizardModel) advanceStep() { +func (m *Model) advanceStep() { switch m.step { case stepProvConfirm: if m.requireProv { @@ -420,7 +420,7 @@ func (m *policyWizardModel) advanceStep() { } } -func (m *policyWizardModel) advanceAfterExpr() { +func (m *Model) advanceAfterExpr() { switch m.exprTarget { case targetProvException: m.step = stepProvExcConfirm diff --git a/cmd/kosli/policyWizardModel.go b/internal/policywizard/model.go similarity index 76% rename from cmd/kosli/policyWizardModel.go rename to internal/policywizard/model.go index 1f439ed5e..48879090c 100644 --- a/cmd/kosli/policyWizardModel.go +++ b/internal/policywizard/model.go @@ -1,4 +1,4 @@ -package main +package policywizard import ( "strings" @@ -11,52 +11,56 @@ import ( const formWidth = 55 -type policyWizardModel struct { +// Model is the bubbletea model for the policy wizard. +type Model struct { step wizardStep form *huh.Form - policy *policy.Policy - wctx *wizardContext - styles wizardStyles + styles styles width int height int + ctx *Context - // State for loops and expression building + // Public results — read after the program exits. + Policy *policy.Policy + OutputFile string + Cancelled bool + + // Internal state for loops and expression building. exprTarget exprTarget exprMode string exprContext string exprTagKey string currentAttRule policy.AttestationRule - cancelled bool - outputFile string requireProv bool requireTrail bool validationErr string } -func newPolicyWizardModel(wctx *wizardContext) policyWizardModel { - m := policyWizardModel{ +// NewModel creates a new policy wizard model. +func NewModel(ctx *Context) Model { + m := Model{ step: stepProvConfirm, - policy: policy.NewPolicy(), - wctx: wctx, - styles: newWizardStyles(), + Policy: policy.NewPolicy(), + ctx: ctx, + styles: newStyles(), width: 120, } m.form = m.buildForm() return m } -func (m policyWizardModel) Init() tea.Cmd { +func (m Model) Init() tea.Cmd { return m.form.Init() } -func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: m.width = msg.Width m.height = msg.Height case tea.KeyMsg: if msg.String() == "ctrl+c" { - m.cancelled = true + m.Cancelled = true return m, tea.Quit } } @@ -67,7 +71,7 @@ func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } if m.form.State == huh.StateAborted { - m.cancelled = true + m.Cancelled = true return m, tea.Quit } @@ -88,8 +92,8 @@ func (m policyWizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } -func (m policyWizardModel) View() string { - if m.cancelled || m.step == stepDone { +func (m Model) View() string { + if m.Cancelled || m.step == stepDone { return "" } @@ -112,7 +116,7 @@ func (m policyWizardModel) View() string { var body string if pw > 0 { - yamlBytes, _ := m.policy.ToYAML() + yamlBytes, _ := m.Policy.ToYAML() yamlStr := strings.TrimRight(string(yamlBytes), "\n") if yamlStr == "" { yamlStr = "(empty)" From 346ee0327c17e30098b529f36c75046b8f8088f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Tue, 7 Apr 2026 23:38:36 +0200 Subject: [PATCH 17/53] green: add 38 tests for policywizard state machine Extract formValues struct so state machine logic (applyFormValues, advanceStep) is testable without huh form internals. Store lastConfirm so advanceStep no longer reads from the form directly. Tests cover all step transitions, expression application for every target, validation rejection for wildcard type+name, and policy mutation for every form result type. --- internal/policywizard/forms.go | 75 +++-- internal/policywizard/forms_test.go | 435 ++++++++++++++++++++++++++++ internal/policywizard/model.go | 1 + 3 files changed, 491 insertions(+), 20 deletions(-) create mode 100644 internal/policywizard/forms_test.go diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 1f8aeeb94..4f2f52564 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -223,14 +223,50 @@ func (m *Model) excConfirmTitle(rule string) string { return fmt.Sprintf("Add an exception to %s?", rule) } +// --------------------------------------------------------------------------- +// Form values — extracted from huh form for testability +// --------------------------------------------------------------------------- + +type formValues struct { + confirm bool + str string // generic string: value, filename, mode + attType string + attName string + operator string +} + +func extractFormValues(f *huh.Form) formValues { + return formValues{ + confirm: f.GetBool("confirm"), + str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode")), + attType: f.GetString("type"), + attName: f.GetString("name"), + operator: f.GetString("op"), + } +} + +func firstNonEmpty(ss ...string) string { + for _, s := range ss { + if s != "" { + return s + } + } + return "" +} + // --------------------------------------------------------------------------- // State transitions: processFormResults // --------------------------------------------------------------------------- func (m *Model) processFormResults() { + m.applyFormValues(extractFormValues(m.form)) +} + +func (m *Model) applyFormValues(fv formValues) { + m.lastConfirm = fv.confirm switch m.step { case stepProvConfirm: - m.requireProv = m.form.GetBool("confirm") + m.requireProv = fv.confirm if m.requireProv { if m.Policy.Artifacts == nil { m.Policy.Artifacts = &policy.ArtifactRules{} @@ -239,7 +275,7 @@ func (m *Model) processFormResults() { } case stepTrailConfirm: - m.requireTrail = m.form.GetBool("confirm") + m.requireTrail = fv.confirm if m.requireTrail { if m.Policy.Artifacts == nil { m.Policy.Artifacts = &policy.ArtifactRules{} @@ -248,50 +284,49 @@ func (m *Model) processFormResults() { } case stepAttDetails: - attType := m.form.GetString("type") - name := m.form.GetString("name") + name := fv.attName if name == "" { name = "*" } - if attType == "*" && name == "*" { + if fv.attType == "*" && name == "*" { m.validationErr = "when type is *, name must not be * — please specify a name" return } m.validationErr = "" m.currentAttRule = policy.AttestationRule{ - Type: attType, + Type: fv.attType, Name: name, } case stepExprMode: - m.exprMode = m.form.GetString("mode") + m.exprMode = fv.str case stepExprFlowName: - m.applyExpression(policy.FlowNameExpr(m.form.GetString("value"))) + m.applyExpression(policy.FlowNameExpr(fv.str)) case stepExprFlowTag: - m.exprTagKey = m.form.GetString("value") + m.exprTagKey = fv.str case stepExprFlowTagOp: - m.applyExpression(policy.FlowTagExpr(m.exprTagKey, m.form.GetString("op"), m.form.GetString("value"))) + m.applyExpression(policy.FlowTagExpr(m.exprTagKey, fv.operator, fv.str)) case stepExprArtifactName: - m.applyExpression(policy.ArtifactNameMatchExpr(m.form.GetString("value"))) + m.applyExpression(policy.ArtifactNameMatchExpr(fv.str)) case stepExprCustomCtx: - m.exprContext = m.form.GetString("value") + m.exprContext = fv.str case stepExprCustomTagKey: - m.exprContext = "flow.tags." + m.form.GetString("value") + m.exprContext = "flow.tags." + fv.str case stepExprCustomOp: - m.applyExpression(policy.ComparisonExpr(m.exprContext, m.form.GetString("op"), m.form.GetString("value"))) + m.applyExpression(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) case stepExprRaw: - m.applyExpression(policy.WrapExpr(m.form.GetString("value"))) + m.applyExpression(policy.WrapExpr(fv.str)) case stepSaveFile: - m.OutputFile = m.form.GetString("filename") + m.OutputFile = fv.str } } @@ -336,7 +371,7 @@ func (m *Model) advanceStep() { } case stepProvExcConfirm: - if m.form.GetBool("confirm") { + if m.lastConfirm { m.exprTarget = targetProvException m.step = stepExprMode } else { @@ -352,7 +387,7 @@ func (m *Model) advanceStep() { } case stepTrailExcConfirm: - if m.form.GetBool("confirm") { + if m.lastConfirm { m.exprTarget = targetTrailException m.step = stepExprMode } else { @@ -360,7 +395,7 @@ func (m *Model) advanceStep() { } case stepAttConfirm: - if m.form.GetBool("confirm") { + if m.lastConfirm { m.step = stepAttDetails } else { m.step = stepSaveFile @@ -370,7 +405,7 @@ func (m *Model) advanceStep() { m.step = stepAttCondConfirm case stepAttCondConfirm: - if m.form.GetBool("confirm") { + if m.lastConfirm { m.exprTarget = targetAttCondition m.step = stepExprMode } else { diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go new file mode 100644 index 000000000..9e961d9e6 --- /dev/null +++ b/internal/policywizard/forms_test.go @@ -0,0 +1,435 @@ +package policywizard + +import ( + "testing" + + "github.com/kosli-dev/cli/internal/policy" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// --------------------------------------------------------------------------- +// advanceStep tests +// --------------------------------------------------------------------------- + +func TestAdvance_ProvConfirm_RequiredGoesToExceptions(t *testing.T) { + m := newTestModel() + m.step = stepProvConfirm + m.requireProv = true + + m.advanceStep() + + assert.Equal(t, stepProvExcConfirm, m.step) + assert.Equal(t, targetProvException, m.exprTarget) +} + +func TestAdvance_ProvConfirm_NotRequiredGoesToTrail(t *testing.T) { + m := newTestModel() + m.step = stepProvConfirm + m.requireProv = false + + m.advanceStep() + + assert.Equal(t, stepTrailConfirm, m.step) +} + +func TestAdvance_TrailConfirm_RequiredGoesToExceptions(t *testing.T) { + m := newTestModel() + m.step = stepTrailConfirm + m.requireTrail = true + + m.advanceStep() + + assert.Equal(t, stepTrailExcConfirm, m.step) +} + +func TestAdvance_TrailConfirm_NotRequiredGoesToAtt(t *testing.T) { + m := newTestModel() + m.step = stepTrailConfirm + m.requireTrail = false + + m.advanceStep() + + assert.Equal(t, stepAttConfirm, m.step) +} + +func TestAdvance_ProvExcConfirm_YesGoesToExprMode(t *testing.T) { + m := newTestModel() + m.step = stepProvExcConfirm + m.lastConfirm = true + + m.advanceStep() + + assert.Equal(t, stepExprMode, m.step) + assert.Equal(t, targetProvException, m.exprTarget) +} + +func TestAdvance_ProvExcConfirm_NoGoesToTrail(t *testing.T) { + m := newTestModel() + m.step = stepProvExcConfirm + m.lastConfirm = false + + m.advanceStep() + + assert.Equal(t, stepTrailConfirm, m.step) +} + +func TestAdvance_TrailExcConfirm_YesGoesToExprMode(t *testing.T) { + m := newTestModel() + m.step = stepTrailExcConfirm + m.lastConfirm = true + + m.advanceStep() + + assert.Equal(t, stepExprMode, m.step) + assert.Equal(t, targetTrailException, m.exprTarget) +} + +func TestAdvance_TrailExcConfirm_NoGoesToAtt(t *testing.T) { + m := newTestModel() + m.step = stepTrailExcConfirm + m.lastConfirm = false + + m.advanceStep() + + assert.Equal(t, stepAttConfirm, m.step) +} + +func TestAdvance_AttConfirm_YesGoesToDetails(t *testing.T) { + m := newTestModel() + m.step = stepAttConfirm + m.lastConfirm = true + + m.advanceStep() + + assert.Equal(t, stepAttDetails, m.step) +} + +func TestAdvance_AttConfirm_NoGoesToSaveFile(t *testing.T) { + m := newTestModel() + m.step = stepAttConfirm + m.lastConfirm = false + + m.advanceStep() + + assert.Equal(t, stepSaveFile, m.step) +} + +func TestAdvance_AttCondConfirm_YesGoesToExprMode(t *testing.T) { + m := newTestModel() + m.step = stepAttCondConfirm + m.lastConfirm = true + + m.advanceStep() + + assert.Equal(t, stepExprMode, m.step) + assert.Equal(t, targetAttCondition, m.exprTarget) +} + +func TestAdvance_AttCondConfirm_NoCommitsAndLoops(t *testing.T) { + m := newTestModel() + m.step = stepAttCondConfirm + m.lastConfirm = false + m.currentAttRule = policy.AttestationRule{Type: "snyk", Name: "scan"} + m.Policy.Artifacts = &policy.ArtifactRules{} + + m.advanceStep() + + assert.Equal(t, stepAttConfirm, m.step) + require.Len(t, m.Policy.Artifacts.Attestations, 1) + assert.Equal(t, "snyk", m.Policy.Artifacts.Attestations[0].Type) +} + +func TestAdvance_ExprMode_AllModes(t *testing.T) { + tests := []struct { + mode string + expected wizardStep + }{ + {"flow_name", stepExprFlowName}, + {"flow_tag", stepExprFlowTag}, + {"artifact_name", stepExprArtifactName}, + {"custom", stepExprCustomCtx}, + {"raw", stepExprRaw}, + } + for _, tt := range tests { + t.Run(tt.mode, func(t *testing.T) { + m := newTestModel() + m.step = stepExprMode + m.exprMode = tt.mode + m.advanceStep() + assert.Equal(t, tt.expected, m.step) + }) + } +} + +func TestAdvance_ExprCustomCtx_TagKeyGoesToTagKeyStep(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomCtx + m.exprContext = "flow.tags." + + m.advanceStep() + + assert.Equal(t, stepExprCustomTagKey, m.step) +} + +func TestAdvance_ExprCustomCtx_DirectGoesToOp(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomCtx + m.exprContext = "flow.name" + + m.advanceStep() + + assert.Equal(t, stepExprCustomOp, m.step) +} + +func TestAdvance_SaveFile_GoesToDone(t *testing.T) { + m := newTestModel() + m.step = stepSaveFile + + m.advanceStep() + + assert.Equal(t, stepDone, m.step) +} + +func TestAdvanceAfterExpr_AllTargets(t *testing.T) { + tests := []struct { + target exprTarget + expected wizardStep + }{ + {targetProvException, stepProvExcConfirm}, + {targetTrailException, stepTrailExcConfirm}, + {targetAttCondition, stepAttConfirm}, + } + for _, tt := range tests { + m := newTestModel() + m.exprTarget = tt.target + m.advanceAfterExpr() + assert.Equal(t, tt.expected, m.step) + } +} + +// --------------------------------------------------------------------------- +// applyFormValues tests +// --------------------------------------------------------------------------- + +func TestApply_ProvConfirm_True_SetsProvenance(t *testing.T) { + m := newTestModel() + m.step = stepProvConfirm + + m.applyFormValues(formValues{confirm: true}) + + assert.True(t, m.requireProv) + require.NotNil(t, m.Policy.Artifacts) + require.NotNil(t, m.Policy.Artifacts.Provenance) + assert.True(t, m.Policy.Artifacts.Provenance.Required) +} + +func TestApply_ProvConfirm_False_NoArtifacts(t *testing.T) { + m := newTestModel() + m.step = stepProvConfirm + + m.applyFormValues(formValues{confirm: false}) + + assert.False(t, m.requireProv) + assert.Nil(t, m.Policy.Artifacts) +} + +func TestApply_TrailConfirm_True_SetsTrailCompliance(t *testing.T) { + m := newTestModel() + m.step = stepTrailConfirm + + m.applyFormValues(formValues{confirm: true}) + + assert.True(t, m.requireTrail) + require.NotNil(t, m.Policy.Artifacts) + require.NotNil(t, m.Policy.Artifacts.TrailCompliance) + assert.True(t, m.Policy.Artifacts.TrailCompliance.Required) +} + +func TestApply_AttDetails_SetsCurrentRule(t *testing.T) { + m := newTestModel() + m.step = stepAttDetails + + m.applyFormValues(formValues{attType: "snyk", attName: "security-scan"}) + + assert.Equal(t, "snyk", m.currentAttRule.Type) + assert.Equal(t, "security-scan", m.currentAttRule.Name) + assert.Empty(t, m.validationErr) +} + +func TestApply_AttDetails_EmptyNameDefaultsToWildcard(t *testing.T) { + m := newTestModel() + m.step = stepAttDetails + + m.applyFormValues(formValues{attType: "snyk", attName: ""}) + + assert.Equal(t, "*", m.currentAttRule.Name) +} + +func TestApply_AttDetails_WildcardTypeAndName_Rejected(t *testing.T) { + m := newTestModel() + m.step = stepAttDetails + + m.applyFormValues(formValues{attType: "*", attName: "*"}) + + assert.Contains(t, m.validationErr, "name must not be *") + assert.Equal(t, policy.AttestationRule{}, m.currentAttRule) +} + +func TestApply_AttDetails_WildcardTypeEmptyName_Rejected(t *testing.T) { + m := newTestModel() + m.step = stepAttDetails + + m.applyFormValues(formValues{attType: "*", attName: ""}) + + assert.Contains(t, m.validationErr, "name must not be *") +} + +func TestApply_ExprMode_StoresMode(t *testing.T) { + m := newTestModel() + m.step = stepExprMode + + m.applyFormValues(formValues{str: "flow_tag"}) + + assert.Equal(t, "flow_tag", m.exprMode) +} + +func TestApply_ExprFlowName_AppliesExpression(t *testing.T) { + m := newTestModel() + m.step = stepExprFlowName + m.exprTarget = targetProvException + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{str: "prod"}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + +func TestApply_ExprFlowTag_StoresTagKey(t *testing.T) { + m := newTestModel() + m.step = stepExprFlowTag + + m.applyFormValues(formValues{str: "risk-level"}) + + assert.Equal(t, "risk-level", m.exprTagKey) +} + +func TestApply_ExprFlowTagOp_AppliesExpression(t *testing.T) { + m := newTestModel() + m.step = stepExprFlowTagOp + m.exprTarget = targetTrailException + m.exprTagKey = "team" + m.Policy.Artifacts = &policy.ArtifactRules{ + TrailCompliance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{operator: "==", str: "backend"}) + + require.Len(t, m.Policy.Artifacts.TrailCompliance.Exceptions, 1) + assert.Equal(t, `${{ flow.tags.team == "backend" }}`, m.Policy.Artifacts.TrailCompliance.Exceptions[0].If) +} + +func TestApply_ExprArtifactName_AppliesExpression(t *testing.T) { + m := newTestModel() + m.step = stepExprArtifactName + m.exprTarget = targetProvException + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{str: "^datadog:.*"}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ matches(artifact.name, "^datadog:.*") }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + +func TestApply_ExprRaw_AppliesWrappedExpression(t *testing.T) { + m := newTestModel() + m.step = stepExprRaw + m.exprTarget = targetProvException + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{str: `flow.name == "prod"`}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + +func TestApply_ExprCustomCtx_StoresContext(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomCtx + + m.applyFormValues(formValues{str: "artifact.name"}) + + assert.Equal(t, "artifact.name", m.exprContext) +} + +func TestApply_ExprCustomTagKey_BuildsContext(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomTagKey + + m.applyFormValues(formValues{str: "risk-level"}) + + assert.Equal(t, "flow.tags.risk-level", m.exprContext) +} + +func TestApply_ExprCustomOp_AppliesExpression(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprTarget = targetProvException + m.exprContext = "artifact.name" + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{operator: "==", str: "myapp"}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ artifact.name == "myapp" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + +func TestApply_AttCondition_CommitsAttestation(t *testing.T) { + m := newTestModel() + m.step = stepExprFlowName + m.exprTarget = targetAttCondition + m.currentAttRule = policy.AttestationRule{Type: "snyk", Name: "scan"} + m.Policy.Artifacts = &policy.ArtifactRules{} + + m.applyFormValues(formValues{str: "prod"}) + + require.Len(t, m.Policy.Artifacts.Attestations, 1) + assert.Equal(t, "snyk", m.Policy.Artifacts.Attestations[0].Type) + assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Attestations[0].If) +} + +func TestApply_SaveFile_SetsOutputFile(t *testing.T) { + m := newTestModel() + m.step = stepSaveFile + + m.applyFormValues(formValues{str: "my-policy.yaml"}) + + assert.Equal(t, "my-policy.yaml", m.OutputFile) +} + +func TestApply_StoresLastConfirm(t *testing.T) { + m := newTestModel() + m.step = stepProvConfirm + + m.applyFormValues(formValues{confirm: true}) + + assert.True(t, m.lastConfirm) +} + +// --------------------------------------------------------------------------- +// helpers +// --------------------------------------------------------------------------- + +func newTestModel() *Model { + m := NewModel(&Context{}) + return &m +} diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 48879090c..0b0a9a24c 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -33,6 +33,7 @@ type Model struct { currentAttRule policy.AttestationRule requireProv bool requireTrail bool + lastConfirm bool validationErr string } From 2f0b9305854f8cd4ac50b63d6161fe4671e07887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 07:10:34 +0200 Subject: [PATCH 18/53] style: apply Kosli brand colors to policy wizard TUI Replace charmbracelet default colors with Kosli brand palette: - Blue 600 (#1C4BC6) for accent, title, borders, labels - Success Green (#45A26D) for YAML preview text - Error Red (#C13D33) for validation errors - Tertiary (#646A71) for footer/dimmed text All colors maintain good contrast on both dark and light terminals. --- internal/policywizard/context.go | 24 +++++++++++++++++------- internal/policywizard/model.go | 3 +-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index 27a36167f..00ea3e439 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -8,6 +8,14 @@ type Context struct { CustomAttestTypes []string } +// Kosli brand colors for terminal UI. +const ( + colorBlue = lipgloss.Color("#1C4BC6") // Blue 600 — primary accent + colorGreen = lipgloss.Color("#45A26D") // Success green + colorRed = lipgloss.Color("#C13D33") // Error red + colorTextDim = lipgloss.Color("#646A71") // Tertiary text +) + type styles struct { base lipgloss.Style title lipgloss.Style @@ -15,27 +23,29 @@ type styles struct { previewText lipgloss.Style footer lipgloss.Style accent lipgloss.Style + err lipgloss.Style } func newStyles() styles { - accent := lipgloss.Color("#7571F9") - green := lipgloss.Color("#02BF87") return styles{ base: lipgloss.NewStyle().Padding(1, 2), title: lipgloss.NewStyle(). Bold(true). - Foreground(accent). + Foreground(colorBlue). Padding(0, 1), preview: lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). - BorderForeground(accent). + BorderForeground(colorBlue). Padding(1, 2), previewText: lipgloss.NewStyle(). - Foreground(green), + Foreground(colorGreen), footer: lipgloss.NewStyle(). - Foreground(lipgloss.Color("240")). + Foreground(colorTextDim). Padding(1, 1, 0, 1), accent: lipgloss.NewStyle(). - Foreground(accent), + Foreground(colorBlue), + err: lipgloss.NewStyle(). + Foreground(colorRed). + Bold(true), } } diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 0b0a9a24c..840b1fcdf 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -110,8 +110,7 @@ func (m Model) View() string { formContent := m.form.View() if m.validationErr != "" { - errStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#FE5F86")).Bold(true) - formContent = errStyle.Render("⚠ "+m.validationErr) + "\n\n" + formContent + formContent = s.err.Render("⚠ "+m.validationErr) + "\n\n" + formContent } formView := lipgloss.NewStyle().Width(fw).Render(formContent) From b967714dcf9a434d7ccdce77c3b1bfbdd8c23053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 07:17:08 +0200 Subject: [PATCH 19/53] feat: optionally upload policy to Kosli after creating the file After saving the policy file, if --api-token and --org are set, the wizard asks "Upload this policy to Kosli?" If yes, prompts for a policy name and optional description, then calls the existing createPolicy upload logic. Adds 7 new tests covering upload step transitions and result fields. --- cmd/kosli/createPolicyFile.go | 22 +++++++++- internal/policywizard/context.go | 1 + internal/policywizard/forms.go | 53 ++++++++++++++++++++--- internal/policywizard/forms_test.go | 66 +++++++++++++++++++++++++++++ internal/policywizard/model.go | 9 ++-- 5 files changed, 140 insertions(+), 11 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index ee3698209..0bdb3e57e 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -54,8 +54,11 @@ func runCreatePolicyFile() error { return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") } - ctx := &policywizard.Context{} - if global.ApiToken != "" && global.Org != "" { + hasAPI := global.ApiToken != "" && global.Org != "" + ctx := &policywizard.Context{ + HasAPICredentials: hasAPI, + } + if hasAPI { ctx.FlowNames = fetchFlowNames() ctx.CustomAttestTypes = fetchCustomAttestationTypes() } @@ -87,9 +90,24 @@ func runCreatePolicyFile() error { return fmt.Errorf("failed to write policy file: %w", err) } logger.Info("policy file written to %s", filename) + + if wm.UploadPolicy && wm.UploadPolicyName != "" { + return uploadPolicy(wm.UploadPolicyName, wm.UploadDescription, filename) + } return nil } +func uploadPolicy(name, description, policyFile string) error { + o := &createPolicyOptions{ + payload: PolicyPayload{ + Name: name, + Description: description, + Type: "env", + }, + } + return o.run([]string{name, policyFile}) +} + func fetchFlowNames() []string { u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) if err != nil { diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index 00ea3e439..125228cb0 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -6,6 +6,7 @@ import "github.com/charmbracelet/lipgloss" type Context struct { FlowNames []string CustomAttestTypes []string + HasAPICredentials bool } // Kosli brand colors for terminal UI. diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 4f2f52564..83e3a7587 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -31,6 +31,8 @@ const ( stepExprCustomOp stepExprRaw stepSaveFile + stepUploadConfirm + stepUploadDetails stepDone ) @@ -171,6 +173,22 @@ func (m *Model) buildForm() *huh.Form { f = inputForm("filename", "Save policy to file", "Enter filename (e.g. policy.yaml)", "policy.yaml", "filename") + case stepUploadConfirm: + f = confirmForm("Upload this policy to Kosli?", + "Requires --api-token and --org to be set") + + case stepUploadDetails: + f = huh.NewForm(huh.NewGroup( + huh.NewInput().Key("policy_name"). + Title("Policy name"). + Description("Name for the policy in Kosli"). + Validate(notEmpty("policy name")), + huh.NewInput().Key("description"). + Title("Description"). + Description("Optional description for the policy"). + Placeholder(""), + )) + default: f = huh.NewForm(huh.NewGroup()) } @@ -228,17 +246,19 @@ func (m *Model) excConfirmTitle(rule string) string { // --------------------------------------------------------------------------- type formValues struct { - confirm bool - str string // generic string: value, filename, mode - attType string - attName string - operator string + confirm bool + str string // generic string: value, filename, mode, policy_name + str2 string // secondary string: description + attType string + attName string + operator string } func extractFormValues(f *huh.Form) formValues { return formValues{ confirm: f.GetBool("confirm"), - str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode")), + str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode"), f.GetString("policy_name")), + str2: f.GetString("description"), attType: f.GetString("type"), attName: f.GetString("name"), operator: f.GetString("op"), @@ -327,6 +347,13 @@ func (m *Model) applyFormValues(fv formValues) { case stepSaveFile: m.OutputFile = fv.str + + case stepUploadConfirm: + m.UploadPolicy = fv.confirm + + case stepUploadDetails: + m.UploadPolicyName = fv.str + m.UploadDescription = fv.str2 } } @@ -451,6 +478,20 @@ func (m *Model) advanceStep() { m.advanceAfterExpr() case stepSaveFile: + if m.ctx.HasAPICredentials { + m.step = stepUploadConfirm + } else { + m.step = stepDone + } + + case stepUploadConfirm: + if m.lastConfirm { + m.step = stepUploadDetails + } else { + m.step = stepDone + } + + case stepUploadDetails: m.step = stepDone } } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 9e961d9e6..5b68c17ab 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -416,6 +416,72 @@ func TestApply_SaveFile_SetsOutputFile(t *testing.T) { assert.Equal(t, "my-policy.yaml", m.OutputFile) } +func TestApply_UploadConfirm_SetsFlag(t *testing.T) { + m := newTestModel() + m.step = stepUploadConfirm + + m.applyFormValues(formValues{confirm: true}) + + assert.True(t, m.UploadPolicy) +} + +func TestApply_UploadDetails_SetsNameAndDescription(t *testing.T) { + m := newTestModel() + m.step = stepUploadDetails + + m.applyFormValues(formValues{str: "my-policy", str2: "A test policy"}) + + assert.Equal(t, "my-policy", m.UploadPolicyName) + assert.Equal(t, "A test policy", m.UploadDescription) +} + +func TestAdvance_SaveFile_WithAPI_GoesToUploadConfirm(t *testing.T) { + m := NewModel(&Context{HasAPICredentials: true}) + m.step = stepSaveFile + + m.advanceStep() + + assert.Equal(t, stepUploadConfirm, m.step) +} + +func TestAdvance_SaveFile_WithoutAPI_GoesToDone(t *testing.T) { + m := newTestModel() + m.step = stepSaveFile + + m.advanceStep() + + assert.Equal(t, stepDone, m.step) +} + +func TestAdvance_UploadConfirm_YesGoesToDetails(t *testing.T) { + m := newTestModel() + m.step = stepUploadConfirm + m.lastConfirm = true + + m.advanceStep() + + assert.Equal(t, stepUploadDetails, m.step) +} + +func TestAdvance_UploadConfirm_NoGoesToDone(t *testing.T) { + m := newTestModel() + m.step = stepUploadConfirm + m.lastConfirm = false + + m.advanceStep() + + assert.Equal(t, stepDone, m.step) +} + +func TestAdvance_UploadDetails_GoesToDone(t *testing.T) { + m := newTestModel() + m.step = stepUploadDetails + + m.advanceStep() + + assert.Equal(t, stepDone, m.step) +} + func TestApply_StoresLastConfirm(t *testing.T) { m := newTestModel() m.step = stepProvConfirm diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 840b1fcdf..14b74192f 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -21,9 +21,12 @@ type Model struct { ctx *Context // Public results — read after the program exits. - Policy *policy.Policy - OutputFile string - Cancelled bool + Policy *policy.Policy + OutputFile string + Cancelled bool + UploadPolicy bool + UploadPolicyName string + UploadDescription string // Internal state for loops and expression building. exprTarget exprTarget From 682c518b51137685c784598fb8ba12aa1c8320e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 07:23:51 +0200 Subject: [PATCH 20/53] feat: show spinner while fetching API data on startup Move API fetching into the bubbletea program as an async tea.Cmd. A branded spinner (bubbles/spinner with Dot style) shows "Fetching flows and attestation types from Kosli..." while the fetch runs in the background. The wizard forms appear immediately once the fetch completes. When no API credentials are set, the spinner is skipped entirely and the wizard starts at the first form step. --- cmd/kosli/createPolicyFile.go | 8 +++- go.mod | 17 +++++--- go.sum | 29 ++++++++---- internal/policywizard/context.go | 8 ++++ internal/policywizard/forms.go | 3 +- internal/policywizard/model.go | 75 ++++++++++++++++++++++++++------ 6 files changed, 107 insertions(+), 33 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 0bdb3e57e..3e365ae71 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -59,8 +59,12 @@ func runCreatePolicyFile() error { HasAPICredentials: hasAPI, } if hasAPI { - ctx.FlowNames = fetchFlowNames() - ctx.CustomAttestTypes = fetchCustomAttestationTypes() + ctx.FetchFunc = func() policywizard.FetchResult { + return policywizard.FetchResult{ + FlowNames: fetchFlowNames(), + CustomAttestTypes: fetchCustomAttestationTypes(), + } + } } m := policywizard.NewModel(ctx) diff --git a/go.mod b/go.mod index f99abc278..21456f3ba 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 github.com/aws/smithy-go v1.24.2 + github.com/charmbracelet/bubbles v1.0.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/huh v1.0.0 github.com/charmbracelet/lipgloss v1.1.0 @@ -89,12 +90,14 @@ require ( github.com/catppuccin/go v0.3.0 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect - github.com/charmbracelet/colorprofile v0.3.1 // indirect - github.com/charmbracelet/x/ansi v0.10.1 // indirect - github.com/charmbracelet/x/cellbuf v0.0.13 // indirect + github.com/charmbracelet/colorprofile v0.4.1 // indirect + github.com/charmbracelet/x/ansi v0.11.6 // indirect + github.com/charmbracelet/x/cellbuf v0.0.15 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect - github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/charmbracelet/x/term v0.2.2 // indirect + github.com/clipperhouse/displaywidth v0.9.0 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.5.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.1 // indirect @@ -160,11 +163,11 @@ require ( github.com/lestrrat-go/httprc/v3 v3.0.2 // indirect github.com/lestrrat-go/jwx/v3 v3.0.13 // indirect github.com/lestrrat-go/option/v2 v2.0.0 // indirect - github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/spdystream v0.5.0 // indirect diff --git a/go.sum b/go.sum index a87b7398a..1b6d66516 100644 --- a/go.sum +++ b/go.sum @@ -143,24 +143,26 @@ github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F9 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 h1:JFgG/xnwFfbezlUnFMJy0nusZvytYysV4SCS2cYbvws= -github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7/go.mod h1:ISC1gtLcVilLOf23wvTfoQuYbW2q0JevFxPfUzZ9Ybw= +github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= +github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40= github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= +github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= +github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw= github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= -github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ= -github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= +github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= +github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k= github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= +github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= @@ -170,8 +172,9 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payR github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= -github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= +github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= @@ -186,6 +189,12 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= +github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w94cO8U= +github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -525,8 +534,9 @@ github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0 github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU= github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= -github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= @@ -542,8 +552,9 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index 125228cb0..eed3cd936 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -2,11 +2,19 @@ package policywizard import "github.com/charmbracelet/lipgloss" +// FetchResult holds the data returned by the API fetch. +type FetchResult struct { + FlowNames []string + CustomAttestTypes []string +} + // Context holds data fetched from the API to populate wizard options. type Context struct { FlowNames []string CustomAttestTypes []string HasAPICredentials bool + // FetchFunc is called asynchronously to fetch API data. If nil, no fetch is performed. + FetchFunc func() FetchResult } // Kosli brand colors for terminal UI. diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 83e3a7587..1e68a9af4 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -14,7 +14,8 @@ import ( type wizardStep int const ( - stepProvConfirm wizardStep = iota + stepLoading wizardStep = iota + stepProvConfirm stepProvExcConfirm stepTrailConfirm stepTrailExcConfirm diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 14b74192f..06b6927f9 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -3,6 +3,7 @@ package policywizard import ( "strings" + "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" @@ -11,14 +12,20 @@ import ( const formWidth = 55 +// fetchDoneMsg is sent when the async API fetch completes. +type fetchDoneMsg struct { + result FetchResult +} + // Model is the bubbletea model for the policy wizard. type Model struct { - step wizardStep - form *huh.Form - styles styles - width int - height int - ctx *Context + step wizardStep + form *huh.Form + spinner spinner.Model + styles styles + width int + height int + ctx *Context // Public results — read after the program exits. Policy *policy.Policy @@ -42,21 +49,43 @@ type Model struct { // NewModel creates a new policy wizard model. func NewModel(ctx *Context) Model { + s := spinner.New(spinner.WithSpinner(spinner.Dot)) + s.Style = lipgloss.NewStyle().Foreground(colorBlue) + + startStep := stepProvConfirm + if ctx.FetchFunc != nil { + startStep = stepLoading + } + m := Model{ - step: stepProvConfirm, - Policy: policy.NewPolicy(), - ctx: ctx, - styles: newStyles(), - width: 120, + step: startStep, + Policy: policy.NewPolicy(), + ctx: ctx, + styles: newStyles(), + spinner: s, + width: 120, + } + if startStep != stepLoading { + m.form = m.buildForm() } - m.form = m.buildForm() return m } func (m Model) Init() tea.Cmd { + if m.step == stepLoading { + return tea.Batch(m.spinner.Tick, m.startFetch()) + } return m.form.Init() } +func (m Model) startFetch() tea.Cmd { + fetchFn := m.ctx.FetchFunc + return func() tea.Msg { + result := fetchFn() + return fetchDoneMsg{result: result} + } +} + func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: @@ -67,6 +96,19 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.Cancelled = true return m, tea.Quit } + case fetchDoneMsg: + m.ctx.FlowNames = msg.result.FlowNames + m.ctx.CustomAttestTypes = msg.result.CustomAttestTypes + m.step = stepProvConfirm + m.form = m.buildForm() + return m, m.form.Init() + } + + // During loading, only update the spinner + if m.step == stepLoading { + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) + return m, cmd } form, cmd := m.form.Update(msg) @@ -102,6 +144,13 @@ func (m Model) View() string { } s := m.styles + header := s.title.Render("Kosli Policy Builder") + + if m.step == stepLoading { + loading := m.spinner.View() + " Fetching flows and attestation types from Kosli..." + return s.base.Render(header + "\n\n" + loading) + } + fw := formWidth available := m.width - s.base.GetHorizontalFrameSize() pw := available - fw - 2 @@ -109,8 +158,6 @@ func (m Model) View() string { pw = 0 } - header := s.title.Render("Kosli Policy Builder") - formContent := m.form.View() if m.validationErr != "" { formContent = s.err.Render("⚠ "+m.validationErr) + "\n\n" + formContent From 9a9bea449362879c481a4f3bfa9974749df60146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 07:29:09 +0200 Subject: [PATCH 21/53] feat: show default org in upload details with ability to change The upload step now shows an Organization field pre-filled with the current org (from --org flag or $KOSLI_ORG). Users can accept the default or type a different org. If left empty, falls back to the context org. --- cmd/kosli/createPolicyFile.go | 4 ++++ internal/policywizard/context.go | 1 + internal/policywizard/forms.go | 14 ++++++++++++++ internal/policywizard/forms_test.go | 21 ++++++++++++++++++++- internal/policywizard/model.go | 1 + 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 3e365ae71..30cc653e2 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -57,6 +57,7 @@ func runCreatePolicyFile() error { hasAPI := global.ApiToken != "" && global.Org != "" ctx := &policywizard.Context{ HasAPICredentials: hasAPI, + Org: global.Org, } if hasAPI { ctx.FetchFunc = func() policywizard.FetchResult { @@ -96,6 +97,9 @@ func runCreatePolicyFile() error { logger.Info("policy file written to %s", filename) if wm.UploadPolicy && wm.UploadPolicyName != "" { + if wm.UploadOrg != "" { + global.Org = wm.UploadOrg + } return uploadPolicy(wm.UploadPolicyName, wm.UploadDescription, filename) } return nil diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index eed3cd936..7a67a265c 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -13,6 +13,7 @@ type Context struct { FlowNames []string CustomAttestTypes []string HasAPICredentials bool + Org string // current org (e.g. from $KOSLI_ORG) // FetchFunc is called asynchronously to fetch API data. If nil, no fetch is performed. FetchFunc func() FetchResult } diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 1e68a9af4..babfca5d1 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -179,7 +179,15 @@ func (m *Model) buildForm() *huh.Form { "Requires --api-token and --org to be set") case stepUploadDetails: + orgInput := huh.NewInput().Key("org"). + Title("Organization"). + Description("Kosli organization to upload to"). + Validate(notEmpty("organization")) + if m.ctx.Org != "" { + orgInput = orgInput.Placeholder(m.ctx.Org) + } f = huh.NewForm(huh.NewGroup( + orgInput, huh.NewInput().Key("policy_name"). Title("Policy name"). Description("Name for the policy in Kosli"). @@ -250,6 +258,7 @@ type formValues struct { confirm bool str string // generic string: value, filename, mode, policy_name str2 string // secondary string: description + str3 string // tertiary string: org attType string attName string operator string @@ -260,6 +269,7 @@ func extractFormValues(f *huh.Form) formValues { confirm: f.GetBool("confirm"), str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode"), f.GetString("policy_name")), str2: f.GetString("description"), + str3: f.GetString("org"), attType: f.GetString("type"), attName: f.GetString("name"), operator: f.GetString("op"), @@ -353,6 +363,10 @@ func (m *Model) applyFormValues(fv formValues) { m.UploadPolicy = fv.confirm case stepUploadDetails: + m.UploadOrg = fv.str3 + if m.UploadOrg == "" { + m.UploadOrg = m.ctx.Org + } m.UploadPolicyName = fv.str m.UploadDescription = fv.str2 } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 5b68c17ab..43f24713d 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -429,10 +429,29 @@ func TestApply_UploadDetails_SetsNameAndDescription(t *testing.T) { m := newTestModel() m.step = stepUploadDetails - m.applyFormValues(formValues{str: "my-policy", str2: "A test policy"}) + m.applyFormValues(formValues{str: "my-policy", str2: "A test policy", str3: "my-org"}) assert.Equal(t, "my-policy", m.UploadPolicyName) assert.Equal(t, "A test policy", m.UploadDescription) + assert.Equal(t, "my-org", m.UploadOrg) +} + +func TestApply_UploadDetails_OrgDefaultsFromContext(t *testing.T) { + m := NewModel(&Context{Org: "default-org"}) + m.step = stepUploadDetails + + m.applyFormValues(formValues{str: "my-policy", str3: ""}) + + assert.Equal(t, "default-org", m.UploadOrg) +} + +func TestApply_UploadDetails_OrgOverridesContext(t *testing.T) { + m := NewModel(&Context{Org: "default-org"}) + m.step = stepUploadDetails + + m.applyFormValues(formValues{str: "my-policy", str3: "other-org"}) + + assert.Equal(t, "other-org", m.UploadOrg) } func TestAdvance_SaveFile_WithAPI_GoesToUploadConfirm(t *testing.T) { diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 06b6927f9..826dc648d 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -34,6 +34,7 @@ type Model struct { UploadPolicy bool UploadPolicyName string UploadDescription string + UploadOrg string // Internal state for loops and expression building. exprTarget exprTarget From be20ef7ed09d3dab0c6a60ee02354bafe149811d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 07:39:36 +0200 Subject: [PATCH 22/53] fix: allow tabbing past filename and org fields to accept defaults Remove notEmpty validation from filename and org inputs so users can press enter/tab to accept the placeholder value. Defaults are applied in applyFormValues: filename defaults to "policy.yaml", org defaults to the context org ($KOSLI_ORG / --org). --- cmd/kosli/createPolicyFile.go | 11 +++-------- internal/policywizard/forms.go | 22 +++++++++++++++------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 30cc653e2..89fbcf568 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -80,27 +80,22 @@ func runCreatePolicyFile() error { return nil } - filename := wm.OutputFile - if filename == "" { - filename = "policy.yaml" - } - yamlBytes, err := wm.Policy.ToYAML() if err != nil { return fmt.Errorf("failed to generate policy YAML: %w", err) } - err = os.WriteFile(filename, yamlBytes, 0644) + err = os.WriteFile(wm.OutputFile, yamlBytes, 0644) if err != nil { return fmt.Errorf("failed to write policy file: %w", err) } - logger.Info("policy file written to %s", filename) + logger.Info("policy file written to %s", wm.OutputFile) if wm.UploadPolicy && wm.UploadPolicyName != "" { if wm.UploadOrg != "" { global.Org = wm.UploadOrg } - return uploadPolicy(wm.UploadPolicyName, wm.UploadDescription, filename) + return uploadPolicy(wm.UploadPolicyName, wm.UploadDescription, wm.OutputFile) } return nil } diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index babfca5d1..fb650c7a1 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -171,21 +171,26 @@ func (m *Model) buildForm() *huh.Form { `flow.name == "prod"`, "expression") case stepSaveFile: - f = inputForm("filename", "Save policy to file", - "Enter filename (e.g. policy.yaml)", "policy.yaml", "filename") + f = huh.NewForm(huh.NewGroup( + huh.NewInput().Key("filename"). + Title("Save policy to file"). + Description("Press enter to accept default"). + Placeholder("policy.yaml"), + )) case stepUploadConfirm: f = confirmForm("Upload this policy to Kosli?", "Requires --api-token and --org to be set") case stepUploadDetails: - orgInput := huh.NewInput().Key("org"). - Title("Organization"). - Description("Kosli organization to upload to"). - Validate(notEmpty("organization")) + orgDesc := "Kosli organization to upload to" if m.ctx.Org != "" { - orgInput = orgInput.Placeholder(m.ctx.Org) + orgDesc = "Press enter to accept default" } + orgInput := huh.NewInput().Key("org"). + Title("Organization"). + Description(orgDesc). + Placeholder(m.ctx.Org) f = huh.NewForm(huh.NewGroup( orgInput, huh.NewInput().Key("policy_name"). @@ -358,6 +363,9 @@ func (m *Model) applyFormValues(fv formValues) { case stepSaveFile: m.OutputFile = fv.str + if m.OutputFile == "" { + m.OutputFile = "policy.yaml" + } case stepUploadConfirm: m.UploadPolicy = fv.confirm From 0c10db16740ec8ae8ab0bc61022500121010ff04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 08:05:09 +0200 Subject: [PATCH 23/53] feat: show completion screen with policy URL inside the TUI Instead of abruptly exiting after save/upload, the wizard now: 1. Shows a spinner "Saving policy..." during file write + API upload 2. Displays a completion screen with checkmarks and a clickable policy URL (https:////policies/) 3. Waits for any keypress before exiting File write and upload are now performed inside the TUI via a WriteFunc callback, keeping the alt screen active throughout. --- cmd/kosli/createPolicyFile.go | 53 ++++++++++++++------- internal/policywizard/context.go | 22 +++++++++ internal/policywizard/forms.go | 9 +++- internal/policywizard/forms_test.go | 28 +++++------ internal/policywizard/model.go | 74 ++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 35 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 89fbcf568..5f960ede9 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -7,6 +7,7 @@ import ( "net/http" "net/url" "os" + "strings" tea "github.com/charmbracelet/bubbletea" "github.com/kosli-dev/cli/internal/policywizard" @@ -55,9 +56,12 @@ func runCreatePolicyFile() error { } hasAPI := global.ApiToken != "" && global.Org != "" + host := global.Host ctx := &policywizard.Context{ HasAPICredentials: hasAPI, Org: global.Org, + Host: host, + WriteFunc: writeAndUploadPolicy, } if hasAPI { ctx.FetchFunc = func() policywizard.FetchResult { @@ -69,35 +73,50 @@ func runCreatePolicyFile() error { } m := policywizard.NewModel(ctx) - finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() + _, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { return fmt.Errorf("wizard error: %w", err) } - wm := finalModel.(policywizard.Model) - if wm.Cancelled { - logger.Info("policy file creation cancelled") - return nil - } + return nil +} - yamlBytes, err := wm.Policy.ToYAML() - if err != nil { - return fmt.Errorf("failed to generate policy YAML: %w", err) +func writeAndUploadPolicy(req policywizard.WriteRequest) policywizard.WriteResult { + result := policywizard.WriteResult{ + Filename: req.Filename, + PolicyName: req.PolicyName, } - err = os.WriteFile(wm.OutputFile, yamlBytes, 0644) + // Write file + err := os.WriteFile(req.Filename, req.YAMLBytes, 0644) if err != nil { - return fmt.Errorf("failed to write policy file: %w", err) + result.Err = fmt.Errorf("failed to write policy file: %w", err) + return result } - logger.Info("policy file written to %s", wm.OutputFile) - if wm.UploadPolicy && wm.UploadPolicyName != "" { - if wm.UploadOrg != "" { - global.Org = wm.UploadOrg + // Upload if requested + if req.Upload && req.PolicyName != "" { + if req.Org != "" { + global.Org = req.Org } - return uploadPolicy(wm.UploadPolicyName, wm.UploadDescription, wm.OutputFile) + err = uploadPolicy(req.PolicyName, req.Description, req.Filename) + if err != nil { + result.Err = fmt.Errorf("policy file saved to %s but upload failed: %w", req.Filename, err) + return result + } + result.Uploaded = true + result.PolicyURL = policyURL(global.Host, req.Org, req.PolicyName) } - return nil + + return result +} + +func policyURL(host, org, policyName string) string { + // Map API host to UI host + uiHost := host + uiHost = strings.TrimSuffix(uiHost, "/") + // The API host is the same as the UI host for Kosli + return fmt.Sprintf("%s/%s/policies/%s", uiHost, org, policyName) } func uploadPolicy(name, description, policyFile string) error { diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index 7a67a265c..af1a2f910 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -9,13 +9,35 @@ type FetchResult struct { } // Context holds data fetched from the API to populate wizard options. +// WriteRequest contains the parameters for writing and uploading a policy. +type WriteRequest struct { + YAMLBytes []byte + Filename string + PolicyName string + Description string + Org string + Upload bool +} + +// WriteResult is returned after writing/uploading. +type WriteResult struct { + Filename string + PolicyName string + PolicyURL string // e.g. https://app.kosli.com/my-org/policies/my-policy + Uploaded bool + Err error +} + type Context struct { FlowNames []string CustomAttestTypes []string HasAPICredentials bool Org string // current org (e.g. from $KOSLI_ORG) + Host string // e.g. https://app.kosli.com // FetchFunc is called asynchronously to fetch API data. If nil, no fetch is performed. FetchFunc func() FetchResult + // WriteFunc writes the file and optionally uploads the policy. Called as a tea.Cmd. + WriteFunc func(WriteRequest) WriteResult } // Kosli brand colors for terminal UI. diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index fb650c7a1..5451aa102 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -34,6 +34,8 @@ const ( stepSaveFile stepUploadConfirm stepUploadDetails + stepWriting // spinner while writing/uploading + stepComplete // show success message stepDone ) @@ -504,17 +506,20 @@ func (m *Model) advanceStep() { if m.ctx.HasAPICredentials { m.step = stepUploadConfirm } else { - m.step = stepDone + m.step = stepWriting } case stepUploadConfirm: if m.lastConfirm { m.step = stepUploadDetails } else { - m.step = stepDone + m.step = stepWriting } case stepUploadDetails: + m.step = stepWriting + + case stepComplete: m.step = stepDone } } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 43f24713d..e82a8b7ad 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -182,15 +182,6 @@ func TestAdvance_ExprCustomCtx_DirectGoesToOp(t *testing.T) { assert.Equal(t, stepExprCustomOp, m.step) } -func TestAdvance_SaveFile_GoesToDone(t *testing.T) { - m := newTestModel() - m.step = stepSaveFile - - m.advanceStep() - - assert.Equal(t, stepDone, m.step) -} - func TestAdvanceAfterExpr_AllTargets(t *testing.T) { tests := []struct { target exprTarget @@ -463,13 +454,13 @@ func TestAdvance_SaveFile_WithAPI_GoesToUploadConfirm(t *testing.T) { assert.Equal(t, stepUploadConfirm, m.step) } -func TestAdvance_SaveFile_WithoutAPI_GoesToDone(t *testing.T) { +func TestAdvance_SaveFile_WithoutAPI_GoesToWriting(t *testing.T) { m := newTestModel() m.step = stepSaveFile m.advanceStep() - assert.Equal(t, stepDone, m.step) + assert.Equal(t, stepWriting, m.step) } func TestAdvance_UploadConfirm_YesGoesToDetails(t *testing.T) { @@ -482,22 +473,31 @@ func TestAdvance_UploadConfirm_YesGoesToDetails(t *testing.T) { assert.Equal(t, stepUploadDetails, m.step) } -func TestAdvance_UploadConfirm_NoGoesToDone(t *testing.T) { +func TestAdvance_UploadConfirm_NoGoesToWriting(t *testing.T) { m := newTestModel() m.step = stepUploadConfirm m.lastConfirm = false m.advanceStep() - assert.Equal(t, stepDone, m.step) + assert.Equal(t, stepWriting, m.step) } -func TestAdvance_UploadDetails_GoesToDone(t *testing.T) { +func TestAdvance_UploadDetails_GoesToWriting(t *testing.T) { m := newTestModel() m.step = stepUploadDetails m.advanceStep() + assert.Equal(t, stepWriting, m.step) +} + +func TestAdvance_Complete_GoesToDone(t *testing.T) { + m := newTestModel() + m.step = stepComplete + + m.advanceStep() + assert.Equal(t, stepDone, m.step) } diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 826dc648d..2f8abb126 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -17,6 +17,11 @@ type fetchDoneMsg struct { result FetchResult } +// writeDoneMsg is sent when the file write/upload completes. +type writeDoneMsg struct { + result WriteResult +} + // Model is the bubbletea model for the policy wizard. type Model struct { step wizardStep @@ -46,6 +51,7 @@ type Model struct { requireTrail bool lastConfirm bool validationErr string + writeResult *WriteResult } // NewModel creates a new policy wizard model. @@ -79,6 +85,46 @@ func (m Model) Init() tea.Cmd { return m.form.Init() } +func (m Model) completionView() string { + s := m.styles + if m.writeResult == nil { + return "" + } + r := m.writeResult + if r.Err != nil { + return s.err.Render("✗ Error: " + r.Err.Error()) + "\n\n" + + s.footer.Render("Press any key to exit") + } + + var b strings.Builder + b.WriteString(s.accent.Bold(true).Render("✓ Policy file saved") + " " + r.Filename + "\n") + if r.Uploaded { + b.WriteString(s.accent.Bold(true).Render("✓ Policy created") + " " + r.PolicyName + "\n") + if r.PolicyURL != "" { + b.WriteString("\n" + s.accent.Render("→") + " " + r.PolicyURL + "\n") + } + } + b.WriteString("\n" + s.footer.Render("Press any key to exit")) + return b.String() +} + +func (m Model) startWrite() tea.Cmd { + yamlBytes, _ := m.Policy.ToYAML() + req := WriteRequest{ + YAMLBytes: yamlBytes, + Filename: m.OutputFile, + PolicyName: m.UploadPolicyName, + Description: m.UploadDescription, + Org: m.UploadOrg, + Upload: m.UploadPolicy, + } + writeFn := m.ctx.WriteFunc + return func() tea.Msg { + result := writeFn(req) + return writeDoneMsg{result: result} + } +} + func (m Model) startFetch() tea.Cmd { fetchFn := m.ctx.FetchFunc return func() tea.Msg { @@ -103,15 +149,27 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.step = stepProvConfirm m.form = m.buildForm() return m, m.form.Init() + case writeDoneMsg: + m.writeResult = &msg.result + m.step = stepComplete + return m, nil } - // During loading, only update the spinner - if m.step == stepLoading { + // During loading/writing, only update the spinner + if m.step == stepLoading || m.step == stepWriting { var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd } + // On the completion screen, any key quits + if m.step == stepComplete { + if _, ok := msg.(tea.KeyMsg); ok { + return m, tea.Quit + } + return m, nil + } + form, cmd := m.form.Update(msg) if f, ok := form.(*huh.Form); ok { m.form = f @@ -132,6 +190,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.step == stepDone { return m, tea.Quit } + if m.step == stepWriting { + return m, m.startWrite() + } m.form = m.buildForm() return m, m.form.Init() } @@ -152,6 +213,15 @@ func (m Model) View() string { return s.base.Render(header + "\n\n" + loading) } + if m.step == stepWriting { + loading := m.spinner.View() + " Saving policy..." + return s.base.Render(header + "\n\n" + loading) + } + + if m.step == stepComplete { + return s.base.Render(header + "\n\n" + m.completionView() + "\n") + } + fw := formWidth available := m.width - s.base.GetHorizontalFrameSize() pw := available - fw - 2 From 7f0797ab7864bc4922b77e3b18ee04395881e082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 08:52:09 +0200 Subject: [PATCH 24/53] revert: remove policy upload feature, keep spinner and defaults Remove upload confirm/details steps, WriteFunc/WriteRequest/WriteResult, completion screen, org field, and writing spinner. The wizard now ends at the save-file step and the command writes the file after the TUI exits. Kept from the reverted range: - Loading spinner for API fetch - Filename default (tab past to accept policy.yaml) --- cmd/kosli/createPolicyFile.go | 62 ++++---------------- internal/policywizard/context.go | 23 -------- internal/policywizard/forms.go | 73 ++---------------------- internal/policywizard/forms_test.go | 87 +---------------------------- internal/policywizard/model.go | 84 ++-------------------------- 5 files changed, 24 insertions(+), 305 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 5f960ede9..5a75feff9 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -7,7 +7,6 @@ import ( "net/http" "net/url" "os" - "strings" tea "github.com/charmbracelet/bubbletea" "github.com/kosli-dev/cli/internal/policywizard" @@ -56,12 +55,8 @@ func runCreatePolicyFile() error { } hasAPI := global.ApiToken != "" && global.Org != "" - host := global.Host ctx := &policywizard.Context{ HasAPICredentials: hasAPI, - Org: global.Org, - Host: host, - WriteFunc: writeAndUploadPolicy, } if hasAPI { ctx.FetchFunc = func() policywizard.FetchResult { @@ -73,61 +68,28 @@ func runCreatePolicyFile() error { } m := policywizard.NewModel(ctx) - _, err := tea.NewProgram(m, tea.WithAltScreen()).Run() + finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { return fmt.Errorf("wizard error: %w", err) } - return nil -} - -func writeAndUploadPolicy(req policywizard.WriteRequest) policywizard.WriteResult { - result := policywizard.WriteResult{ - Filename: req.Filename, - PolicyName: req.PolicyName, + wm := finalModel.(policywizard.Model) + if wm.Cancelled { + logger.Info("policy file creation cancelled") + return nil } - // Write file - err := os.WriteFile(req.Filename, req.YAMLBytes, 0644) + yamlBytes, err := wm.Policy.ToYAML() if err != nil { - result.Err = fmt.Errorf("failed to write policy file: %w", err) - return result - } - - // Upload if requested - if req.Upload && req.PolicyName != "" { - if req.Org != "" { - global.Org = req.Org - } - err = uploadPolicy(req.PolicyName, req.Description, req.Filename) - if err != nil { - result.Err = fmt.Errorf("policy file saved to %s but upload failed: %w", req.Filename, err) - return result - } - result.Uploaded = true - result.PolicyURL = policyURL(global.Host, req.Org, req.PolicyName) + return fmt.Errorf("failed to generate policy YAML: %w", err) } - return result -} - -func policyURL(host, org, policyName string) string { - // Map API host to UI host - uiHost := host - uiHost = strings.TrimSuffix(uiHost, "/") - // The API host is the same as the UI host for Kosli - return fmt.Sprintf("%s/%s/policies/%s", uiHost, org, policyName) -} - -func uploadPolicy(name, description, policyFile string) error { - o := &createPolicyOptions{ - payload: PolicyPayload{ - Name: name, - Description: description, - Type: "env", - }, + err = os.WriteFile(wm.OutputFile, yamlBytes, 0644) + if err != nil { + return fmt.Errorf("failed to write policy file: %w", err) } - return o.run([]string{name, policyFile}) + logger.Info("policy file written to %s", wm.OutputFile) + return nil } func fetchFlowNames() []string { diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index af1a2f910..eed3cd936 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -9,35 +9,12 @@ type FetchResult struct { } // Context holds data fetched from the API to populate wizard options. -// WriteRequest contains the parameters for writing and uploading a policy. -type WriteRequest struct { - YAMLBytes []byte - Filename string - PolicyName string - Description string - Org string - Upload bool -} - -// WriteResult is returned after writing/uploading. -type WriteResult struct { - Filename string - PolicyName string - PolicyURL string // e.g. https://app.kosli.com/my-org/policies/my-policy - Uploaded bool - Err error -} - type Context struct { FlowNames []string CustomAttestTypes []string HasAPICredentials bool - Org string // current org (e.g. from $KOSLI_ORG) - Host string // e.g. https://app.kosli.com // FetchFunc is called asynchronously to fetch API data. If nil, no fetch is performed. FetchFunc func() FetchResult - // WriteFunc writes the file and optionally uploads the policy. Called as a tea.Cmd. - WriteFunc func(WriteRequest) WriteResult } // Kosli brand colors for terminal UI. diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 5451aa102..ef7d14fa3 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -32,10 +32,6 @@ const ( stepExprCustomOp stepExprRaw stepSaveFile - stepUploadConfirm - stepUploadDetails - stepWriting // spinner while writing/uploading - stepComplete // show success message stepDone ) @@ -180,31 +176,6 @@ func (m *Model) buildForm() *huh.Form { Placeholder("policy.yaml"), )) - case stepUploadConfirm: - f = confirmForm("Upload this policy to Kosli?", - "Requires --api-token and --org to be set") - - case stepUploadDetails: - orgDesc := "Kosli organization to upload to" - if m.ctx.Org != "" { - orgDesc = "Press enter to accept default" - } - orgInput := huh.NewInput().Key("org"). - Title("Organization"). - Description(orgDesc). - Placeholder(m.ctx.Org) - f = huh.NewForm(huh.NewGroup( - orgInput, - huh.NewInput().Key("policy_name"). - Title("Policy name"). - Description("Name for the policy in Kosli"). - Validate(notEmpty("policy name")), - huh.NewInput().Key("description"). - Title("Description"). - Description("Optional description for the policy"). - Placeholder(""), - )) - default: f = huh.NewForm(huh.NewGroup()) } @@ -262,21 +233,17 @@ func (m *Model) excConfirmTitle(rule string) string { // --------------------------------------------------------------------------- type formValues struct { - confirm bool - str string // generic string: value, filename, mode, policy_name - str2 string // secondary string: description - str3 string // tertiary string: org - attType string - attName string - operator string + confirm bool + str string // generic string: value, filename, mode + attType string + attName string + operator string } func extractFormValues(f *huh.Form) formValues { return formValues{ confirm: f.GetBool("confirm"), - str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode"), f.GetString("policy_name")), - str2: f.GetString("description"), - str3: f.GetString("org"), + str: firstNonEmpty(f.GetString("value"), f.GetString("filename"), f.GetString("mode")), attType: f.GetString("type"), attName: f.GetString("name"), operator: f.GetString("op"), @@ -368,17 +335,6 @@ func (m *Model) applyFormValues(fv formValues) { if m.OutputFile == "" { m.OutputFile = "policy.yaml" } - - case stepUploadConfirm: - m.UploadPolicy = fv.confirm - - case stepUploadDetails: - m.UploadOrg = fv.str3 - if m.UploadOrg == "" { - m.UploadOrg = m.ctx.Org - } - m.UploadPolicyName = fv.str - m.UploadDescription = fv.str2 } } @@ -503,23 +459,6 @@ func (m *Model) advanceStep() { m.advanceAfterExpr() case stepSaveFile: - if m.ctx.HasAPICredentials { - m.step = stepUploadConfirm - } else { - m.step = stepWriting - } - - case stepUploadConfirm: - if m.lastConfirm { - m.step = stepUploadDetails - } else { - m.step = stepWriting - } - - case stepUploadDetails: - m.step = stepWriting - - case stepComplete: m.step = stepDone } } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index e82a8b7ad..b9f29fa67 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -407,97 +407,12 @@ func TestApply_SaveFile_SetsOutputFile(t *testing.T) { assert.Equal(t, "my-policy.yaml", m.OutputFile) } -func TestApply_UploadConfirm_SetsFlag(t *testing.T) { +func TestAdvance_SaveFile_GoesToDone(t *testing.T) { m := newTestModel() - m.step = stepUploadConfirm - - m.applyFormValues(formValues{confirm: true}) - - assert.True(t, m.UploadPolicy) -} - -func TestApply_UploadDetails_SetsNameAndDescription(t *testing.T) { - m := newTestModel() - m.step = stepUploadDetails - - m.applyFormValues(formValues{str: "my-policy", str2: "A test policy", str3: "my-org"}) - - assert.Equal(t, "my-policy", m.UploadPolicyName) - assert.Equal(t, "A test policy", m.UploadDescription) - assert.Equal(t, "my-org", m.UploadOrg) -} - -func TestApply_UploadDetails_OrgDefaultsFromContext(t *testing.T) { - m := NewModel(&Context{Org: "default-org"}) - m.step = stepUploadDetails - - m.applyFormValues(formValues{str: "my-policy", str3: ""}) - - assert.Equal(t, "default-org", m.UploadOrg) -} - -func TestApply_UploadDetails_OrgOverridesContext(t *testing.T) { - m := NewModel(&Context{Org: "default-org"}) - m.step = stepUploadDetails - - m.applyFormValues(formValues{str: "my-policy", str3: "other-org"}) - - assert.Equal(t, "other-org", m.UploadOrg) -} - -func TestAdvance_SaveFile_WithAPI_GoesToUploadConfirm(t *testing.T) { - m := NewModel(&Context{HasAPICredentials: true}) m.step = stepSaveFile m.advanceStep() - assert.Equal(t, stepUploadConfirm, m.step) -} - -func TestAdvance_SaveFile_WithoutAPI_GoesToWriting(t *testing.T) { - m := newTestModel() - m.step = stepSaveFile - - m.advanceStep() - - assert.Equal(t, stepWriting, m.step) -} - -func TestAdvance_UploadConfirm_YesGoesToDetails(t *testing.T) { - m := newTestModel() - m.step = stepUploadConfirm - m.lastConfirm = true - - m.advanceStep() - - assert.Equal(t, stepUploadDetails, m.step) -} - -func TestAdvance_UploadConfirm_NoGoesToWriting(t *testing.T) { - m := newTestModel() - m.step = stepUploadConfirm - m.lastConfirm = false - - m.advanceStep() - - assert.Equal(t, stepWriting, m.step) -} - -func TestAdvance_UploadDetails_GoesToWriting(t *testing.T) { - m := newTestModel() - m.step = stepUploadDetails - - m.advanceStep() - - assert.Equal(t, stepWriting, m.step) -} - -func TestAdvance_Complete_GoesToDone(t *testing.T) { - m := newTestModel() - m.step = stepComplete - - m.advanceStep() - assert.Equal(t, stepDone, m.step) } diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 2f8abb126..a75a9ebff 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -17,11 +17,6 @@ type fetchDoneMsg struct { result FetchResult } -// writeDoneMsg is sent when the file write/upload completes. -type writeDoneMsg struct { - result WriteResult -} - // Model is the bubbletea model for the policy wizard. type Model struct { step wizardStep @@ -33,13 +28,9 @@ type Model struct { ctx *Context // Public results — read after the program exits. - Policy *policy.Policy - OutputFile string - Cancelled bool - UploadPolicy bool - UploadPolicyName string - UploadDescription string - UploadOrg string + Policy *policy.Policy + OutputFile string + Cancelled bool // Internal state for loops and expression building. exprTarget exprTarget @@ -51,7 +42,6 @@ type Model struct { requireTrail bool lastConfirm bool validationErr string - writeResult *WriteResult } // NewModel creates a new policy wizard model. @@ -85,46 +75,6 @@ func (m Model) Init() tea.Cmd { return m.form.Init() } -func (m Model) completionView() string { - s := m.styles - if m.writeResult == nil { - return "" - } - r := m.writeResult - if r.Err != nil { - return s.err.Render("✗ Error: " + r.Err.Error()) + "\n\n" + - s.footer.Render("Press any key to exit") - } - - var b strings.Builder - b.WriteString(s.accent.Bold(true).Render("✓ Policy file saved") + " " + r.Filename + "\n") - if r.Uploaded { - b.WriteString(s.accent.Bold(true).Render("✓ Policy created") + " " + r.PolicyName + "\n") - if r.PolicyURL != "" { - b.WriteString("\n" + s.accent.Render("→") + " " + r.PolicyURL + "\n") - } - } - b.WriteString("\n" + s.footer.Render("Press any key to exit")) - return b.String() -} - -func (m Model) startWrite() tea.Cmd { - yamlBytes, _ := m.Policy.ToYAML() - req := WriteRequest{ - YAMLBytes: yamlBytes, - Filename: m.OutputFile, - PolicyName: m.UploadPolicyName, - Description: m.UploadDescription, - Org: m.UploadOrg, - Upload: m.UploadPolicy, - } - writeFn := m.ctx.WriteFunc - return func() tea.Msg { - result := writeFn(req) - return writeDoneMsg{result: result} - } -} - func (m Model) startFetch() tea.Cmd { fetchFn := m.ctx.FetchFunc return func() tea.Msg { @@ -149,27 +99,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.step = stepProvConfirm m.form = m.buildForm() return m, m.form.Init() - case writeDoneMsg: - m.writeResult = &msg.result - m.step = stepComplete - return m, nil } - // During loading/writing, only update the spinner - if m.step == stepLoading || m.step == stepWriting { + // During loading, only update the spinner + if m.step == stepLoading { var cmd tea.Cmd m.spinner, cmd = m.spinner.Update(msg) return m, cmd } - // On the completion screen, any key quits - if m.step == stepComplete { - if _, ok := msg.(tea.KeyMsg); ok { - return m, tea.Quit - } - return m, nil - } - form, cmd := m.form.Update(msg) if f, ok := form.(*huh.Form); ok { m.form = f @@ -190,9 +128,6 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { if m.step == stepDone { return m, tea.Quit } - if m.step == stepWriting { - return m, m.startWrite() - } m.form = m.buildForm() return m, m.form.Init() } @@ -213,15 +148,6 @@ func (m Model) View() string { return s.base.Render(header + "\n\n" + loading) } - if m.step == stepWriting { - loading := m.spinner.View() + " Saving policy..." - return s.base.Render(header + "\n\n" + loading) - } - - if m.step == stepComplete { - return s.base.Render(header + "\n\n" + m.completionView() + "\n") - } - fw := formWidth available := m.width - s.base.GetHorizontalFrameSize() pw := available - fw - 2 From b11d2a0c74c4a664935145eb4ca25511b7a10530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 08:58:06 +0200 Subject: [PATCH 25/53] fix: show immediate feedback before TUI starts Print "Starting Kosli Policy Builder..." to stderr before tea.NewProgram initializes, so the user sees instant feedback. The alt screen overwrites it once the TUI renders. --- cmd/kosli/createPolicyFile.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 5a75feff9..e460be8cc 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -67,6 +67,10 @@ func runCreatePolicyFile() error { } } + if hasAPI { + fmt.Fprint(os.Stderr, "Starting Kosli Policy Builder...\r") + } + m := policywizard.NewModel(ctx) finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { From 42b0aaa8e17d915df221ae4084943b38079ff42b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 08:59:17 +0200 Subject: [PATCH 26/53] cleanup: remove unused HasAPICredentials field The field was only used for the upload confirm branching which was removed. FetchFunc != nil already determines whether to fetch API data. Also inlined the hasAPI variable. --- cmd/kosli/createPolicyFile.go | 12 +++--------- internal/policywizard/context.go | 1 - 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index e460be8cc..8c2597ccd 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -54,11 +54,9 @@ func runCreatePolicyFile() error { return fmt.Errorf("this command requires an interactive terminal; write policy YAML manually or use 'kosli create policy' directly") } - hasAPI := global.ApiToken != "" && global.Org != "" - ctx := &policywizard.Context{ - HasAPICredentials: hasAPI, - } - if hasAPI { + ctx := &policywizard.Context{} + if global.ApiToken != "" && global.Org != "" { + fmt.Fprint(os.Stderr, "Starting Kosli Policy Builder...\r") ctx.FetchFunc = func() policywizard.FetchResult { return policywizard.FetchResult{ FlowNames: fetchFlowNames(), @@ -67,10 +65,6 @@ func runCreatePolicyFile() error { } } - if hasAPI { - fmt.Fprint(os.Stderr, "Starting Kosli Policy Builder...\r") - } - m := policywizard.NewModel(ctx) finalModel, err := tea.NewProgram(m, tea.WithAltScreen()).Run() if err != nil { diff --git a/internal/policywizard/context.go b/internal/policywizard/context.go index eed3cd936..cd63028f8 100644 --- a/internal/policywizard/context.go +++ b/internal/policywizard/context.go @@ -12,7 +12,6 @@ type FetchResult struct { type Context struct { FlowNames []string CustomAttestTypes []string - HasAPICredentials bool // FetchFunc is called asynchronously to fetch API data. If nil, no fetch is performed. FetchFunc func() FetchResult } From 467c828f9c6f8ece1c8eac6b3141cb2c120a16d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:03:38 +0200 Subject: [PATCH 27/53] style: reduce TUI minimum width from 91 to 76 columns Shrink form width from 55 to 45 chars (content fits comfortably) and preview minimum from 30 to 25 chars. Both panels now show at 76+ columns (was 91). Preview hides below 76 columns. --- internal/policywizard/model.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index a75a9ebff..1fd13e4bf 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -10,7 +10,7 @@ import ( "github.com/kosli-dev/cli/internal/policy" ) -const formWidth = 55 +const formWidth = 45 // fetchDoneMsg is sent when the async API fetch completes. type fetchDoneMsg struct { @@ -151,7 +151,7 @@ func (m Model) View() string { fw := formWidth available := m.width - s.base.GetHorizontalFrameSize() pw := available - fw - 2 - if pw < 30 { + if pw < 25 { pw = 0 } From 715f1fc80beb4d10278943c78aed6f673d8ddeb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:16:06 +0200 Subject: [PATCH 28/53] style: fix alignment in step enum --- internal/policywizard/forms.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index ef7d14fa3..de13721ce 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -14,7 +14,7 @@ import ( type wizardStep int const ( - stepLoading wizardStep = iota + stepLoading wizardStep = iota stepProvConfirm stepProvExcConfirm stepTrailConfirm From b83939ad83b999c226333a531a86a0f164586880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:56:02 +0200 Subject: [PATCH 29/53] fix: validate output file extension and prevent overwrite Check that the output filename has .yaml or .yml extension and error if the file already exists. Validation runs both in the TUI (inline feedback) and before writing the file. --- cmd/kosli/createPolicyFile.go | 22 ++++++++++++-- cmd/kosli/createPolicyFile_test.go | 49 ++++++++++++++++++++++++++++++ internal/policywizard/forms.go | 16 +++++++++- 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 cmd/kosli/createPolicyFile_test.go diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 8c2597ccd..528e8b174 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -7,6 +7,8 @@ import ( "net/http" "net/url" "os" + "path/filepath" + "strings" tea "github.com/charmbracelet/bubbletea" "github.com/kosli-dev/cli/internal/policywizard" @@ -82,11 +84,27 @@ func runCreatePolicyFile() error { return fmt.Errorf("failed to generate policy YAML: %w", err) } - err = os.WriteFile(wm.OutputFile, yamlBytes, 0644) + outPath := filepath.Clean(wm.OutputFile) + if err := validateOutputFile(outPath); err != nil { + return err + } + + err = os.WriteFile(outPath, yamlBytes, 0644) if err != nil { return fmt.Errorf("failed to write policy file: %w", err) } - logger.Info("policy file written to %s", wm.OutputFile) + logger.Info("policy file written to %s", outPath) + return nil +} + +func validateOutputFile(path string) error { + ext := strings.ToLower(filepath.Ext(path)) + if ext != ".yaml" && ext != ".yml" { + return fmt.Errorf("output file must have a .yaml or .yml extension, got %q", filepath.Base(path)) + } + if _, err := os.Stat(path); err == nil { + return fmt.Errorf("file %q already exists; remove it or choose a different name", path) + } return nil } diff --git a/cmd/kosli/createPolicyFile_test.go b/cmd/kosli/createPolicyFile_test.go new file mode 100644 index 000000000..2a5fbee64 --- /dev/null +++ b/cmd/kosli/createPolicyFile_test.go @@ -0,0 +1,49 @@ +package main + +import ( + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValidateOutputFile_AcceptsYamlExtension(t *testing.T) { + dir := t.TempDir() + assert.NoError(t, validateOutputFile(filepath.Join(dir, "policy.yaml"))) +} + +func TestValidateOutputFile_AcceptsYmlExtension(t *testing.T) { + dir := t.TempDir() + assert.NoError(t, validateOutputFile(filepath.Join(dir, "policy.yml"))) +} + +func TestValidateOutputFile_AcceptsUppercaseExtension(t *testing.T) { + dir := t.TempDir() + assert.NoError(t, validateOutputFile(filepath.Join(dir, "policy.YAML"))) +} + +func TestValidateOutputFile_RejectsNonYamlExtension(t *testing.T) { + dir := t.TempDir() + err := validateOutputFile(filepath.Join(dir, "policy.json")) + require.Error(t, err) + assert.Contains(t, err.Error(), ".yaml or .yml") +} + +func TestValidateOutputFile_RejectsNoExtension(t *testing.T) { + dir := t.TempDir() + err := validateOutputFile(filepath.Join(dir, "policy")) + require.Error(t, err) + assert.Contains(t, err.Error(), ".yaml or .yml") +} + +func TestValidateOutputFile_RejectsExistingFile(t *testing.T) { + dir := t.TempDir() + path := filepath.Join(dir, "existing.yaml") + require.NoError(t, os.WriteFile(path, []byte("test"), 0644)) + + err := validateOutputFile(path) + require.Error(t, err) + assert.Contains(t, err.Error(), "already exists") +} diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index de13721ce..06aba46aa 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -2,6 +2,8 @@ package policywizard import ( "fmt" + "path/filepath" + "strings" "github.com/charmbracelet/huh" "github.com/kosli-dev/cli/internal/policy" @@ -173,7 +175,8 @@ func (m *Model) buildForm() *huh.Form { huh.NewInput().Key("filename"). Title("Save policy to file"). Description("Press enter to accept default"). - Placeholder("policy.yaml"), + Placeholder("policy.yaml"). + Validate(validateYAMLExtension), )) default: @@ -214,6 +217,17 @@ func notEmpty(field string) func(string) error { } } +func validateYAMLExtension(s string) error { + if s == "" { + return nil // placeholder "policy.yaml" will be used + } + ext := strings.ToLower(filepath.Ext(s)) + if ext != ".yaml" && ext != ".yml" { + return fmt.Errorf("file must have a .yaml or .yml extension") + } + return nil +} + func (m *Model) excConfirmTitle(rule string) string { var count int if rule == "provenance" && m.Policy.Artifacts != nil && m.Policy.Artifacts.Provenance != nil { From 95accabbca053b09dce25ddbd0a1b6793409b9be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:56:22 +0200 Subject: [PATCH 30/53] fix: guard against nil dereference in applyExpression --- internal/policywizard/forms.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 06aba46aa..deeaee9da 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -355,11 +355,17 @@ func (m *Model) applyFormValues(fv formValues) { func (m *Model) applyExpression(expr string) { switch m.exprTarget { case targetProvException: + if m.Policy.Artifacts == nil || m.Policy.Artifacts.Provenance == nil { + return + } m.Policy.Artifacts.Provenance.Exceptions = append( m.Policy.Artifacts.Provenance.Exceptions, policy.ExceptionRule{If: expr}, ) case targetTrailException: + if m.Policy.Artifacts == nil || m.Policy.Artifacts.TrailCompliance == nil { + return + } m.Policy.Artifacts.TrailCompliance.Exceptions = append( m.Policy.Artifacts.TrailCompliance.Exceptions, policy.ExceptionRule{If: expr}, From f5d6b2af2a8d9346a73112b0d5cd986c998237d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:56:48 +0200 Subject: [PATCH 31/53] fix: use comma-ok type assertion for wizard model --- cmd/kosli/createPolicyFile.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 528e8b174..588f76759 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -73,7 +73,10 @@ func runCreatePolicyFile() error { return fmt.Errorf("wizard error: %w", err) } - wm := finalModel.(policywizard.Model) + wm, ok := finalModel.(policywizard.Model) + if !ok { + return fmt.Errorf("unexpected model type from wizard") + } if wm.Cancelled { logger.Info("policy file creation cancelled") return nil From a56e1b5e5d5e16a61a5e5ae3c2301d787377a4d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:57:12 +0200 Subject: [PATCH 32/53] fix: guard FlowNameInExpr against empty slice --- internal/policy/expression.go | 3 +++ internal/policy/expression_test.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 3d3ae788a..591d34835 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -13,6 +13,9 @@ func FlowNameExpr(name string) string { // FlowNameInExpr returns a policy expression matching any of the given flow names. // For a single name, it returns an equality expression instead. func FlowNameInExpr(names []string) string { + if len(names) == 0 { + return "" + } if len(names) == 1 { return FlowNameExpr(names[0]) } diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index bdbe71f9d..356c930f3 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -16,6 +16,11 @@ func TestFlowNameInExpr(t *testing.T) { assert.Equal(t, `${{ flow.name in ["runner", "saver"] }}`, result) } +func TestFlowNameInExpr_Empty(t *testing.T) { + assert.Equal(t, "", FlowNameInExpr(nil)) + assert.Equal(t, "", FlowNameInExpr([]string{})) +} + func TestFlowNameInExpr_Single(t *testing.T) { result := FlowNameInExpr([]string{"prod"}) assert.Equal(t, `${{ flow.name == "prod" }}`, result) From 9409515a904ce82d66f7f3e42935512be9bc2798 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 09:57:38 +0200 Subject: [PATCH 33/53] style: use slices.Clone for clearer slice copy intent --- internal/policywizard/forms.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index deeaee9da..e71e8acce 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -3,6 +3,7 @@ package policywizard import ( "fmt" "path/filepath" + "slices" "strings" "github.com/charmbracelet/huh" @@ -80,7 +81,7 @@ func (m *Model) buildForm() *huh.Form { f = confirmForm(title, "") case stepAttDetails: - allTypes := append([]string{}, builtInAttestationTypes...) + allTypes := slices.Clone(builtInAttestationTypes) allTypes = append(allTypes, m.ctx.CustomAttestTypes...) opts := make([]huh.Option[string], len(allTypes)) for i, t := range allTypes { From c57263e7320f1ad25e406cdb0d585a1018c6a430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 10:14:06 +0200 Subject: [PATCH 34/53] fix: remove in/matches from custom comparison operators These operators need special syntax (list literals, function form) that ComparisonExpr doesn't produce. They already have dedicated wizard paths (flow name match, artifact name regex). Replace with numeric comparison operators that work correctly with the quoted-value format. --- internal/policywizard/forms.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index e71e8acce..d2b1b69ec 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -160,7 +160,7 @@ func (m *Model) buildForm() *huh.Form { case stepExprCustomOp: f = huh.NewForm(huh.NewGroup( huh.NewSelect[string]().Key("op").Title("Operator"). - Options(huh.NewOptions("==", "!=", "in", "matches")...), + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...), huh.NewInput().Key("value").Title("Value"). Description("The value to compare against"). Validate(notEmpty("value")), From e1eb928cc3b5d0f4c10706525ee0d8b3532fce65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 10:16:16 +0200 Subject: [PATCH 35/53] fix: generate matches() function form for custom comparison The matches operator needs function syntax matches(ctx, "pattern") rather than infix syntax. Extract MatchesExpr and use it when the custom comparison operator is "matches". Also replace "in" with numeric comparison operators that work with quoted values. --- internal/policy/expression.go | 7 ++++++- internal/policy/expression_test.go | 5 +++++ internal/policywizard/forms.go | 8 ++++++-- internal/policywizard/forms_test.go | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 3 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 591d34835..6576fae92 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -33,7 +33,12 @@ func FlowTagExpr(key, op, value string) string { // ArtifactNameMatchExpr returns a policy expression matching artifact names by regex. func ArtifactNameMatchExpr(regex string) string { - return fmt.Sprintf(`${{ matches(artifact.name, "%s") }}`, regex) + return MatchesExpr("artifact.name", regex) +} + +// MatchesExpr returns a policy expression using the matches() function form. +func MatchesExpr(context, regex string) string { + return fmt.Sprintf(`${{ matches(%s, "%s") }}`, context, regex) } // ComparisonExpr returns a policy expression comparing a context field to a value. diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 356c930f3..4c2fd78ed 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -41,6 +41,11 @@ func TestArtifactNameMatchExpr(t *testing.T) { assert.Equal(t, `${{ matches(artifact.name, "^datadog:.*") }}`, result) } +func TestMatchesExpr(t *testing.T) { + result := MatchesExpr("flow.name", "^prod") + assert.Equal(t, `${{ matches(flow.name, "^prod") }}`, result) +} + func TestComparisonExpr(t *testing.T) { result := ComparisonExpr("flow.tags.risk", "==", "high") assert.Equal(t, `${{ flow.tags.risk == "high" }}`, result) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index d2b1b69ec..463b891c7 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -160,7 +160,7 @@ func (m *Model) buildForm() *huh.Form { case stepExprCustomOp: f = huh.NewForm(huh.NewGroup( huh.NewSelect[string]().Key("op").Title("Operator"). - Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=")...), + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=", "matches")...), huh.NewInput().Key("value").Title("Value"). Description("The value to compare against"). Validate(notEmpty("value")), @@ -340,7 +340,11 @@ func (m *Model) applyFormValues(fv formValues) { m.exprContext = "flow.tags." + fv.str case stepExprCustomOp: - m.applyExpression(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) + if fv.operator == "matches" { + m.applyExpression(policy.MatchesExpr(m.exprContext, fv.str)) + } else { + m.applyExpression(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) + } case stepExprRaw: m.applyExpression(policy.WrapExpr(fv.str)) diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index b9f29fa67..941e9fd20 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -398,6 +398,21 @@ func TestApply_AttCondition_CommitsAttestation(t *testing.T) { assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Attestations[0].If) } +func TestApply_ExprCustomOp_MatchesUsesFunction(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprTarget = targetProvException + m.exprContext = "flow.name" + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{operator: "matches", str: "^prod"}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ matches(flow.name, "^prod") }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + func TestApply_SaveFile_SetsOutputFile(t *testing.T) { m := newTestModel() m.step = stepSaveFile From 3aa1d34fe65918cd755ce010b065012c13cb18c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 10:19:10 +0200 Subject: [PATCH 36/53] feat: support exists() function in custom comparison Add exists() as an operator choice in the custom comparison wizard path. Unlike other operators, exists() takes only a context field and no value. Generates ${{ exists(field) }} syntax per the policy reference docs. --- internal/policy/expression.go | 5 +++++ internal/policy/expression_test.go | 5 +++++ internal/policywizard/forms.go | 12 +++++++----- internal/policywizard/forms_test.go | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 6576fae92..7d34bd98d 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -41,6 +41,11 @@ func MatchesExpr(context, regex string) string { return fmt.Sprintf(`${{ matches(%s, "%s") }}`, context, regex) } +// ExistsExpr returns a policy expression checking that a context field is not null. +func ExistsExpr(context string) string { + return fmt.Sprintf(`${{ exists(%s) }}`, context) +} + // ComparisonExpr returns a policy expression comparing a context field to a value. func ComparisonExpr(context, op, value string) string { return fmt.Sprintf(`${{ %s %s "%s" }}`, context, op, value) diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 4c2fd78ed..9799d7985 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -46,6 +46,11 @@ func TestMatchesExpr(t *testing.T) { assert.Equal(t, `${{ matches(flow.name, "^prod") }}`, result) } +func TestExistsExpr(t *testing.T) { + result := ExistsExpr("flow") + assert.Equal(t, `${{ exists(flow) }}`, result) +} + func TestComparisonExpr(t *testing.T) { result := ComparisonExpr("flow.tags.risk", "==", "high") assert.Equal(t, `${{ flow.tags.risk == "high" }}`, result) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 463b891c7..8e723750b 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -160,10 +160,9 @@ func (m *Model) buildForm() *huh.Form { case stepExprCustomOp: f = huh.NewForm(huh.NewGroup( huh.NewSelect[string]().Key("op").Title("Operator"). - Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=", "matches")...), + Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=", "matches", "exists")...), huh.NewInput().Key("value").Title("Value"). - Description("The value to compare against"). - Validate(notEmpty("value")), + Description("The value to compare against (leave empty for exists)"), )) case stepExprRaw: @@ -340,9 +339,12 @@ func (m *Model) applyFormValues(fv formValues) { m.exprContext = "flow.tags." + fv.str case stepExprCustomOp: - if fv.operator == "matches" { + switch fv.operator { + case "exists": + m.applyExpression(policy.ExistsExpr(m.exprContext)) + case "matches": m.applyExpression(policy.MatchesExpr(m.exprContext, fv.str)) - } else { + default: m.applyExpression(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 941e9fd20..0cfbb7215 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -413,6 +413,21 @@ func TestApply_ExprCustomOp_MatchesUsesFunction(t *testing.T) { assert.Equal(t, `${{ matches(flow.name, "^prod") }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) } +func TestApply_ExprCustomOp_ExistsUsesFunction(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprTarget = targetProvException + m.exprContext = "flow" + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + m.applyFormValues(formValues{operator: "exists"}) + + require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) + assert.Equal(t, `${{ exists(flow) }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) +} + func TestApply_SaveFile_SetsOutputFile(t *testing.T) { m := newTestModel() m.step = stepSaveFile From 2b38e2d68a5c0c4dd9eb98a57136ed5270732b54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 10:25:06 +0200 Subject: [PATCH 37/53] feat: support and/or/not logical operators in expression builder After building a sub-expression, the wizard now asks whether to negate it (not) and whether to combine it with another condition (and/or). This allows building compound expressions like: ${{ flow.name == "prod" and not(matches(artifact.name, "^datadog:.*")) }} New steps: stepExprNegate, stepExprCombineConfirm, stepExprCombineOp. Sub-expressions accumulate in pendingExprs and are combined on finalize. --- internal/policy/expression.go | 12 ++ internal/policy/expression_test.go | 22 +++ internal/policywizard/forms.go | 97 ++++++++-- internal/policywizard/forms_test.go | 280 ++++++++++++++++++++++------ internal/policywizard/model.go | 2 + 5 files changed, 348 insertions(+), 65 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 7d34bd98d..71cc830b1 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -66,3 +66,15 @@ func WrapExpr(raw string) string { } return fmt.Sprintf("${{ %s }}", raw) } + +// UnwrapExpr strips the ${{ }} wrapper, returning the inner expression. +func UnwrapExpr(expr string) string { + s := strings.TrimPrefix(expr, "${{ ") + s = strings.TrimSuffix(s, " }}") + return s +} + +// NegateExpr wraps a raw (unwrapped) expression with not(). +func NegateExpr(raw string) string { + return fmt.Sprintf("not(%s)", raw) +} diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 9799d7985..53592bba1 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -77,3 +77,25 @@ func TestWrapExpr_Idempotent(t *testing.T) { result := WrapExpr(`${{ flow.name == "prod" }}`) assert.Equal(t, `${{ flow.name == "prod" }}`, result) } + +func TestUnwrapExpr(t *testing.T) { + result := UnwrapExpr(`${{ flow.name == "prod" }}`) + assert.Equal(t, `flow.name == "prod"`, result) +} + +func TestUnwrapExpr_AlreadyRaw(t *testing.T) { + result := UnwrapExpr(`flow.name == "prod"`) + assert.Equal(t, `flow.name == "prod"`, result) +} + +func TestNegateExpr(t *testing.T) { + result := NegateExpr(`flow.name == "prod"`) + assert.Equal(t, `not(flow.name == "prod")`, result) +} + +func TestCombineAndNegate(t *testing.T) { + a := UnwrapExpr(FlowNameExpr("prod")) + b := NegateExpr(UnwrapExpr(ArtifactNameMatchExpr("^datadog:.*"))) + result := CombineExprs("and", a, b) + assert.Equal(t, `${{ flow.name == "prod" and not(matches(artifact.name, "^datadog:.*")) }}`, result) +} diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 8e723750b..d6b16ae62 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -34,6 +34,9 @@ const ( stepExprCustomTagKey stepExprCustomOp stepExprRaw + stepExprNegate + stepExprCombineConfirm + stepExprCombineOp stepSaveFile stepDone ) @@ -170,6 +173,23 @@ func (m *Model) buildForm() *huh.Form { `e.g. flow.name == "prod" and artifact.name == "svc"`, `flow.name == "prod"`, "expression") + case stepExprNegate: + f = confirmForm("Negate this condition?", + "Wrap with not() — e.g. not(flow.name == \"prod\")") + + case stepExprCombineConfirm: + f = confirmForm("Combine with another condition?", + "Chain with and/or — e.g. expr1 and expr2") + + case stepExprCombineOp: + f = huh.NewForm(huh.NewGroup( + huh.NewSelect[string]().Key("value").Title("Logical operator"). + Options( + huh.NewOption("and — both must be true", "and"), + huh.NewOption("or — either can be true", "or"), + ), + )) + case stepSaveFile: f = huh.NewForm(huh.NewGroup( huh.NewInput().Key("filename"). @@ -321,16 +341,16 @@ func (m *Model) applyFormValues(fv formValues) { m.exprMode = fv.str case stepExprFlowName: - m.applyExpression(policy.FlowNameExpr(fv.str)) + m.storeSubExpr(policy.FlowNameExpr(fv.str)) case stepExprFlowTag: m.exprTagKey = fv.str case stepExprFlowTagOp: - m.applyExpression(policy.FlowTagExpr(m.exprTagKey, fv.operator, fv.str)) + m.storeSubExpr(policy.FlowTagExpr(m.exprTagKey, fv.operator, fv.str)) case stepExprArtifactName: - m.applyExpression(policy.ArtifactNameMatchExpr(fv.str)) + m.storeSubExpr(policy.ArtifactNameMatchExpr(fv.str)) case stepExprCustomCtx: m.exprContext = fv.str @@ -341,15 +361,29 @@ func (m *Model) applyFormValues(fv formValues) { case stepExprCustomOp: switch fv.operator { case "exists": - m.applyExpression(policy.ExistsExpr(m.exprContext)) + m.storeSubExpr(policy.ExistsExpr(m.exprContext)) case "matches": - m.applyExpression(policy.MatchesExpr(m.exprContext, fv.str)) + m.storeSubExpr(policy.MatchesExpr(m.exprContext, fv.str)) default: - m.applyExpression(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) + m.storeSubExpr(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) } case stepExprRaw: - m.applyExpression(policy.WrapExpr(fv.str)) + m.storeSubExpr(policy.WrapExpr(fv.str)) + + case stepExprNegate: + if fv.confirm && len(m.pendingExprs) > 0 { + last := len(m.pendingExprs) - 1 + m.pendingExprs[last] = policy.NegateExpr(m.pendingExprs[last]) + } + + case stepExprCombineOp: + m.combineOp = fv.str + + case stepExprCombineConfirm: + if !fv.confirm { + m.finalizeExpression() + } case stepSaveFile: m.OutputFile = fv.str @@ -359,6 +393,26 @@ func (m *Model) applyFormValues(fv formValues) { } } +// storeSubExpr unwraps a full expression and appends it to the pending list. +func (m *Model) storeSubExpr(expr string) { + m.pendingExprs = append(m.pendingExprs, policy.UnwrapExpr(expr)) +} + +// finalizeExpression combines all pending sub-expressions and applies them. +func (m *Model) finalizeExpression() { + if len(m.pendingExprs) == 0 { + return + } + op := m.combineOp + if op == "" { + op = "and" + } + combined := policy.CombineExprs(op, m.pendingExprs...) + m.applyExpression(combined) + m.pendingExprs = nil + m.combineOp = "" +} + func (m *Model) applyExpression(expr string) { switch m.exprTarget { case targetProvException: @@ -408,6 +462,8 @@ func (m *Model) advanceStep() { case stepProvExcConfirm: if m.lastConfirm { m.exprTarget = targetProvException + m.pendingExprs = nil + m.combineOp = "" m.step = stepExprMode } else { m.step = stepTrailConfirm @@ -424,6 +480,8 @@ func (m *Model) advanceStep() { case stepTrailExcConfirm: if m.lastConfirm { m.exprTarget = targetTrailException + m.pendingExprs = nil + m.combineOp = "" m.step = stepExprMode } else { m.step = stepAttConfirm @@ -442,6 +500,8 @@ func (m *Model) advanceStep() { case stepAttCondConfirm: if m.lastConfirm { m.exprTarget = targetAttCondition + m.pendingExprs = nil + m.combineOp = "" m.step = stepExprMode } else { m.commitAttestation() @@ -463,13 +523,13 @@ func (m *Model) advanceStep() { } case stepExprFlowName: - m.advanceAfterExpr() + m.step = stepExprNegate case stepExprFlowTag: m.step = stepExprFlowTagOp case stepExprFlowTagOp: - m.advanceAfterExpr() + m.step = stepExprNegate case stepExprArtifactName: - m.advanceAfterExpr() + m.step = stepExprNegate case stepExprCustomCtx: if m.exprContext == "flow.tags." { @@ -481,9 +541,22 @@ func (m *Model) advanceStep() { case stepExprCustomTagKey: m.step = stepExprCustomOp case stepExprCustomOp: - m.advanceAfterExpr() + m.step = stepExprNegate case stepExprRaw: - m.advanceAfterExpr() + m.step = stepExprNegate + + case stepExprNegate: + m.step = stepExprCombineConfirm + + case stepExprCombineConfirm: + if m.lastConfirm { + m.step = stepExprCombineOp + } else { + m.advanceAfterExpr() + } + + case stepExprCombineOp: + m.step = stepExprMode case stepSaveFile: m.step = stepDone diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 0cfbb7215..c34d68749 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -199,6 +199,44 @@ func TestAdvanceAfterExpr_AllTargets(t *testing.T) { } } +func TestAdvance_ExprFlowName_GoesToNegate(t *testing.T) { + m := newTestModel() + m.step = stepExprFlowName + m.advanceStep() + assert.Equal(t, stepExprNegate, m.step) +} + +func TestAdvance_ExprNegate_GoesToCombineConfirm(t *testing.T) { + m := newTestModel() + m.step = stepExprNegate + m.advanceStep() + assert.Equal(t, stepExprCombineConfirm, m.step) +} + +func TestAdvance_ExprCombineConfirm_Yes_GoesToCombineOp(t *testing.T) { + m := newTestModel() + m.step = stepExprCombineConfirm + m.lastConfirm = true + m.advanceStep() + assert.Equal(t, stepExprCombineOp, m.step) +} + +func TestAdvance_ExprCombineConfirm_No_Finalizes(t *testing.T) { + m := newTestModel() + m.step = stepExprCombineConfirm + m.lastConfirm = false + m.exprTarget = targetProvException + m.advanceStep() + assert.Equal(t, stepProvExcConfirm, m.step) +} + +func TestAdvance_ExprCombineOp_GoesToExprMode(t *testing.T) { + m := newTestModel() + m.step = stepExprCombineOp + m.advanceStep() + assert.Equal(t, stepExprMode, m.step) +} + // --------------------------------------------------------------------------- // applyFormValues tests // --------------------------------------------------------------------------- @@ -285,18 +323,14 @@ func TestApply_ExprMode_StoresMode(t *testing.T) { assert.Equal(t, "flow_tag", m.exprMode) } -func TestApply_ExprFlowName_AppliesExpression(t *testing.T) { +func TestApply_ExprFlowName_StoresPending(t *testing.T) { m := newTestModel() m.step = stepExprFlowName - m.exprTarget = targetProvException - m.Policy.Artifacts = &policy.ArtifactRules{ - Provenance: &policy.BooleanRule{Required: true}, - } m.applyFormValues(formValues{str: "prod"}) - require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `flow.name == "prod"`, m.pendingExprs[0]) } func TestApply_ExprFlowTag_StoresTagKey(t *testing.T) { @@ -308,47 +342,35 @@ func TestApply_ExprFlowTag_StoresTagKey(t *testing.T) { assert.Equal(t, "risk-level", m.exprTagKey) } -func TestApply_ExprFlowTagOp_AppliesExpression(t *testing.T) { +func TestApply_ExprFlowTagOp_StoresPending(t *testing.T) { m := newTestModel() m.step = stepExprFlowTagOp - m.exprTarget = targetTrailException m.exprTagKey = "team" - m.Policy.Artifacts = &policy.ArtifactRules{ - TrailCompliance: &policy.BooleanRule{Required: true}, - } m.applyFormValues(formValues{operator: "==", str: "backend"}) - require.Len(t, m.Policy.Artifacts.TrailCompliance.Exceptions, 1) - assert.Equal(t, `${{ flow.tags.team == "backend" }}`, m.Policy.Artifacts.TrailCompliance.Exceptions[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `flow.tags.team == "backend"`, m.pendingExprs[0]) } -func TestApply_ExprArtifactName_AppliesExpression(t *testing.T) { +func TestApply_ExprArtifactName_StoresPending(t *testing.T) { m := newTestModel() m.step = stepExprArtifactName - m.exprTarget = targetProvException - m.Policy.Artifacts = &policy.ArtifactRules{ - Provenance: &policy.BooleanRule{Required: true}, - } m.applyFormValues(formValues{str: "^datadog:.*"}) - require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ matches(artifact.name, "^datadog:.*") }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `matches(artifact.name, "^datadog:.*")`, m.pendingExprs[0]) } -func TestApply_ExprRaw_AppliesWrappedExpression(t *testing.T) { +func TestApply_ExprRaw_StoresPending(t *testing.T) { m := newTestModel() m.step = stepExprRaw - m.exprTarget = targetProvException - m.Policy.Artifacts = &policy.ArtifactRules{ - Provenance: &policy.BooleanRule{Required: true}, - } m.applyFormValues(formValues{str: `flow.name == "prod"`}) - require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `flow.name == "prod"`, m.pendingExprs[0]) } func TestApply_ExprCustomCtx_StoresContext(t *testing.T) { @@ -369,63 +391,215 @@ func TestApply_ExprCustomTagKey_BuildsContext(t *testing.T) { assert.Equal(t, "flow.tags.risk-level", m.exprContext) } -func TestApply_ExprCustomOp_AppliesExpression(t *testing.T) { +func TestApply_ExprCustomOp_StoresPending(t *testing.T) { m := newTestModel() m.step = stepExprCustomOp - m.exprTarget = targetProvException m.exprContext = "artifact.name" - m.Policy.Artifacts = &policy.ArtifactRules{ - Provenance: &policy.BooleanRule{Required: true}, - } m.applyFormValues(formValues{operator: "==", str: "myapp"}) - require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ artifact.name == "myapp" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `artifact.name == "myapp"`, m.pendingExprs[0]) } -func TestApply_AttCondition_CommitsAttestation(t *testing.T) { +func TestApply_ExprCustomOp_MatchesStoresPending(t *testing.T) { m := newTestModel() - m.step = stepExprFlowName - m.exprTarget = targetAttCondition - m.currentAttRule = policy.AttestationRule{Type: "snyk", Name: "scan"} - m.Policy.Artifacts = &policy.ArtifactRules{} + m.step = stepExprCustomOp + m.exprContext = "flow.name" - m.applyFormValues(formValues{str: "prod"}) + m.applyFormValues(formValues{operator: "matches", str: "^prod"}) - require.Len(t, m.Policy.Artifacts.Attestations, 1) - assert.Equal(t, "snyk", m.Policy.Artifacts.Attestations[0].Type) - assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Attestations[0].If) + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `matches(flow.name, "^prod")`, m.pendingExprs[0]) } -func TestApply_ExprCustomOp_MatchesUsesFunction(t *testing.T) { +func TestApply_ExprCustomOp_ExistsStoresPending(t *testing.T) { m := newTestModel() m.step = stepExprCustomOp + m.exprContext = "flow" + + m.applyFormValues(formValues{operator: "exists"}) + + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `exists(flow)`, m.pendingExprs[0]) +} + +// --------------------------------------------------------------------------- +// Negate tests +// --------------------------------------------------------------------------- + +func TestApply_ExprNegate_Yes_NegatesLastPending(t *testing.T) { + m := newTestModel() + m.step = stepExprNegate + m.pendingExprs = []string{`flow.name == "prod"`} + + m.applyFormValues(formValues{confirm: true}) + + require.Len(t, m.pendingExprs, 1) + assert.Equal(t, `not(flow.name == "prod")`, m.pendingExprs[0]) +} + +func TestApply_ExprNegate_No_LeavesUnchanged(t *testing.T) { + m := newTestModel() + m.step = stepExprNegate + m.pendingExprs = []string{`flow.name == "prod"`} + + m.applyFormValues(formValues{confirm: false}) + + assert.Equal(t, `flow.name == "prod"`, m.pendingExprs[0]) +} + +// --------------------------------------------------------------------------- +// Combine tests +// --------------------------------------------------------------------------- + +func TestApply_ExprCombineOp_StoresOp(t *testing.T) { + m := newTestModel() + m.step = stepExprCombineOp + + m.applyFormValues(formValues{str: "and"}) + + assert.Equal(t, "and", m.combineOp) +} + +func TestApply_ExprCombineConfirm_No_Finalizes(t *testing.T) { + m := newTestModel() + m.step = stepExprCombineConfirm m.exprTarget = targetProvException - m.exprContext = "flow.name" + m.pendingExprs = []string{`flow.name == "prod"`} m.Policy.Artifacts = &policy.ArtifactRules{ Provenance: &policy.BooleanRule{Required: true}, } - m.applyFormValues(formValues{operator: "matches", str: "^prod"}) + m.applyFormValues(formValues{confirm: false}) require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ matches(flow.name, "^prod") }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + assert.Nil(t, m.pendingExprs) } -func TestApply_ExprCustomOp_ExistsUsesFunction(t *testing.T) { +func TestApply_ExprCombineConfirm_Yes_DoesNotFinalize(t *testing.T) { m := newTestModel() - m.step = stepExprCustomOp + m.step = stepExprCombineConfirm m.exprTarget = targetProvException - m.exprContext = "flow" + m.pendingExprs = []string{`flow.name == "prod"`} m.Policy.Artifacts = &policy.ArtifactRules{ Provenance: &policy.BooleanRule{Required: true}, } - m.applyFormValues(formValues{operator: "exists"}) + m.applyFormValues(formValues{confirm: true}) + + assert.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 0) + assert.Len(t, m.pendingExprs, 1) // still pending +} + +// --------------------------------------------------------------------------- +// Full combine flow: two expressions with "and" +// --------------------------------------------------------------------------- + +func TestCombineFlow_TwoExprsWithAnd(t *testing.T) { + m := newTestModel() + m.exprTarget = targetProvException + m.Policy.Artifacts = &policy.ArtifactRules{ + Provenance: &policy.BooleanRule{Required: true}, + } + + // First expression + m.step = stepExprFlowName + m.applyFormValues(formValues{str: "prod"}) + + // Don't negate + m.step = stepExprNegate + m.applyFormValues(formValues{confirm: false}) + + // Combine with another + m.step = stepExprCombineConfirm + m.applyFormValues(formValues{confirm: true}) + + // Choose "and" + m.step = stepExprCombineOp + m.applyFormValues(formValues{str: "and"}) + + // Second expression + m.step = stepExprArtifactName + m.applyFormValues(formValues{str: "^svc:.*"}) + + // Don't negate + m.step = stepExprNegate + m.applyFormValues(formValues{confirm: false}) + + // Done combining + m.step = stepExprCombineConfirm + m.applyFormValues(formValues{confirm: false}) require.Len(t, m.Policy.Artifacts.Provenance.Exceptions, 1) - assert.Equal(t, `${{ exists(flow) }}`, m.Policy.Artifacts.Provenance.Exceptions[0].If) + assert.Equal(t, + `${{ flow.name == "prod" and matches(artifact.name, "^svc:.*") }}`, + m.Policy.Artifacts.Provenance.Exceptions[0].If, + ) +} + +func TestCombineFlow_NegatedExprWithOr(t *testing.T) { + m := newTestModel() + m.exprTarget = targetTrailException + m.Policy.Artifacts = &policy.ArtifactRules{ + TrailCompliance: &policy.BooleanRule{Required: true}, + } + + // First expression + m.step = stepExprFlowName + m.applyFormValues(formValues{str: "staging"}) + + // Negate it + m.step = stepExprNegate + m.applyFormValues(formValues{confirm: true}) + + // Combine + m.step = stepExprCombineConfirm + m.applyFormValues(formValues{confirm: true}) + + m.step = stepExprCombineOp + m.applyFormValues(formValues{str: "or"}) + + // Second expression + m.step = stepExprCustomOp + m.exprContext = "flow" + m.applyFormValues(formValues{operator: "exists"}) + + // Don't negate + m.step = stepExprNegate + m.applyFormValues(formValues{confirm: false}) + + // Done + m.step = stepExprCombineConfirm + m.applyFormValues(formValues{confirm: false}) + + require.Len(t, m.Policy.Artifacts.TrailCompliance.Exceptions, 1) + assert.Equal(t, + `${{ not(flow.name == "staging") or exists(flow) }}`, + m.Policy.Artifacts.TrailCompliance.Exceptions[0].If, + ) +} + +func TestCombineFlow_AttCondition_CommitsAttestation(t *testing.T) { + m := newTestModel() + m.exprTarget = targetAttCondition + m.currentAttRule = policy.AttestationRule{Type: "snyk", Name: "scan"} + m.Policy.Artifacts = &policy.ArtifactRules{} + + // Single expression, no combine + m.step = stepExprFlowName + m.applyFormValues(formValues{str: "prod"}) + + m.step = stepExprNegate + m.applyFormValues(formValues{confirm: false}) + + m.step = stepExprCombineConfirm + m.applyFormValues(formValues{confirm: false}) + + require.Len(t, m.Policy.Artifacts.Attestations, 1) + assert.Equal(t, "snyk", m.Policy.Artifacts.Attestations[0].Type) + assert.Equal(t, `${{ flow.name == "prod" }}`, m.Policy.Artifacts.Attestations[0].If) } func TestApply_SaveFile_SetsOutputFile(t *testing.T) { diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index 1fd13e4bf..b7120fd42 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -37,6 +37,8 @@ type Model struct { exprMode string exprContext string exprTagKey string + pendingExprs []string // raw (unwrapped) sub-expressions being combined + combineOp string // "and" or "or" currentAttRule policy.AttestationRule requireProv bool requireTrail bool From 324137a1e052e5e117192f369795412bfe2c55a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:12:00 +0200 Subject: [PATCH 38/53] fix: not is a prefix operator, not a function Change NegateExpr to produce "not expr" instead of "not(expr)" to match the policy expression language syntax. --- internal/policy/expression.go | 4 ++-- internal/policy/expression_test.go | 4 ++-- internal/policywizard/forms.go | 2 +- internal/policywizard/forms_test.go | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 71cc830b1..f0f25243f 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -74,7 +74,7 @@ func UnwrapExpr(expr string) string { return s } -// NegateExpr wraps a raw (unwrapped) expression with not(). +// NegateExpr prefixes a raw (unwrapped) expression with the not operator. func NegateExpr(raw string) string { - return fmt.Sprintf("not(%s)", raw) + return fmt.Sprintf("not %s", raw) } diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 53592bba1..0f3d7d19b 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -90,12 +90,12 @@ func TestUnwrapExpr_AlreadyRaw(t *testing.T) { func TestNegateExpr(t *testing.T) { result := NegateExpr(`flow.name == "prod"`) - assert.Equal(t, `not(flow.name == "prod")`, result) + assert.Equal(t, `not flow.name == "prod"`, result) } func TestCombineAndNegate(t *testing.T) { a := UnwrapExpr(FlowNameExpr("prod")) b := NegateExpr(UnwrapExpr(ArtifactNameMatchExpr("^datadog:.*"))) result := CombineExprs("and", a, b) - assert.Equal(t, `${{ flow.name == "prod" and not(matches(artifact.name, "^datadog:.*")) }}`, result) + assert.Equal(t, `${{ flow.name == "prod" and not matches(artifact.name, "^datadog:.*") }}`, result) } diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index d6b16ae62..4c73e4b5c 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -175,7 +175,7 @@ func (m *Model) buildForm() *huh.Form { case stepExprNegate: f = confirmForm("Negate this condition?", - "Wrap with not() — e.g. not(flow.name == \"prod\")") + "Prefix with not — e.g. not flow.name == \"prod\"") case stepExprCombineConfirm: f = confirmForm("Combine with another condition?", diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index c34d68749..47a5cfa43 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -436,7 +436,7 @@ func TestApply_ExprNegate_Yes_NegatesLastPending(t *testing.T) { m.applyFormValues(formValues{confirm: true}) require.Len(t, m.pendingExprs, 1) - assert.Equal(t, `not(flow.name == "prod")`, m.pendingExprs[0]) + assert.Equal(t, `not flow.name == "prod"`, m.pendingExprs[0]) } func TestApply_ExprNegate_No_LeavesUnchanged(t *testing.T) { @@ -576,7 +576,7 @@ func TestCombineFlow_NegatedExprWithOr(t *testing.T) { require.Len(t, m.Policy.Artifacts.TrailCompliance.Exceptions, 1) assert.Equal(t, - `${{ not(flow.name == "staging") or exists(flow) }}`, + `${{ not flow.name == "staging" or exists(flow) }}`, m.Policy.Artifacts.TrailCompliance.Exceptions[0].If, ) } From 858caea36e352251cb2a5200506cf405f7336af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:21:57 +0200 Subject: [PATCH 39/53] style: clarify that value input is ignored for exists operator --- internal/policywizard/forms.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 4c73e4b5c..4a803b471 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -165,7 +165,7 @@ func (m *Model) buildForm() *huh.Form { huh.NewSelect[string]().Key("op").Title("Operator"). Options(huh.NewOptions("==", "!=", ">", "<", ">=", "<=", "matches", "exists")...), huh.NewInput().Key("value").Title("Value"). - Description("The value to compare against (leave empty for exists)"), + Description("The value to compare against (ignored for exists)"), )) case stepExprRaw: From 38c5434db7c7b7dd1c7e91a2df3bde304972fa4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:22:06 +0200 Subject: [PATCH 40/53] docs: note that ComparisonExpr always quotes values --- internal/policy/expression.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index f0f25243f..fe7ca9fc7 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -47,6 +47,8 @@ func ExistsExpr(context string) string { } // ComparisonExpr returns a policy expression comparing a context field to a value. +// The value is always quoted as a string. For operators like > or <, the policy +// engine must handle string-to-numeric coercion if needed. func ComparisonExpr(context, op, value string) string { return fmt.Sprintf(`${{ %s %s "%s" }}`, context, op, value) } From 5c8c65f661828b609ed16c72e5336129b8db38d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:22:39 +0200 Subject: [PATCH 41/53] refactor: extract fetchNameList to reduce duplication in API fetchers --- cmd/kosli/createPolicyFile.go | 60 ++++++++++++----------------------- 1 file changed, 20 insertions(+), 40 deletions(-) diff --git a/cmd/kosli/createPolicyFile.go b/cmd/kosli/createPolicyFile.go index 588f76759..c5143c2c8 100644 --- a/cmd/kosli/createPolicyFile.go +++ b/cmd/kosli/createPolicyFile.go @@ -112,42 +112,19 @@ func validateOutputFile(path string) error { } func fetchFlowNames() []string { - u, err := url.JoinPath(global.Host, "api/v2/flows", global.Org) - if err != nil { - logger.Debug("failed to build flows URL: %v", err) - return nil - } - - reqParams := &requests.RequestParams{ - Method: http.MethodGet, - URL: u, - Token: global.ApiToken, - } - response, err := kosliClient.Do(reqParams) - if err != nil { - logger.Debug("failed to fetch flows: %v", err) - return nil - } - - var flows []map[string]any - if err := json.Unmarshal([]byte(response.Body), &flows); err != nil { - logger.Debug("failed to parse flows response: %v", err) - return nil - } - - names := make([]string, 0, len(flows)) - for _, flow := range flows { - if name, ok := flow["name"].(string); ok { - names = append(names, name) - } - } - return names + return fetchNameList("api/v2/flows", nil) } func fetchCustomAttestationTypes() []string { - u, err := url.JoinPath(global.Host, "api/v2/custom-attestation-types", global.Org) + return fetchNameList("api/v2/custom-attestation-types", func(name string) string { + return "custom:" + name + }) +} + +func fetchNameList(apiPath string, transform func(string) string) []string { + u, err := url.JoinPath(global.Host, apiPath, global.Org) if err != nil { - logger.Debug("failed to build attestation types URL: %v", err) + logger.Debug("failed to build URL for %s: %v", apiPath, err) return nil } @@ -158,20 +135,23 @@ func fetchCustomAttestationTypes() []string { } response, err := kosliClient.Do(reqParams) if err != nil { - logger.Debug("failed to fetch attestation types: %v", err) + logger.Debug("failed to fetch %s: %v", apiPath, err) return nil } - var types []map[string]any - if err := json.Unmarshal([]byte(response.Body), &types); err != nil { - logger.Debug("failed to parse attestation types response: %v", err) + var items []map[string]any + if err := json.Unmarshal([]byte(response.Body), &items); err != nil { + logger.Debug("failed to parse %s response: %v", apiPath, err) return nil } - names := make([]string, 0, len(types)) - for _, t := range types { - if name, ok := t["name"].(string); ok { - names = append(names, "custom:"+name) + names := make([]string, 0, len(items)) + for _, item := range items { + if name, ok := item["name"].(string); ok { + if transform != nil { + name = transform(name) + } + names = append(names, name) } } return names From 476bef95ea2c4722638a99df8d058b9b1eb3a477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:22:48 +0200 Subject: [PATCH 42/53] docs: add comment explaining default case in buildForm --- internal/policywizard/forms.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 4a803b471..bc041221f 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -200,6 +200,8 @@ func (m *Model) buildForm() *huh.Form { )) default: + // Every step must have a form. If we reach here, a new step was + // added without a matching case — show an empty form rather than crash. f = huh.NewForm(huh.NewGroup()) } From 872c07dfbd01d9afdea688f1e26793c88b4035f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:23:27 +0200 Subject: [PATCH 43/53] fix: detect terminal width at startup instead of hard-coding 120 Use term.GetSize to read the actual terminal width, falling back to 80. This prevents a brief layout glitch on narrow terminals before the first WindowSizeMsg arrives from bubbletea. --- internal/policywizard/model.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/internal/policywizard/model.go b/internal/policywizard/model.go index b7120fd42..2834ad6dc 100644 --- a/internal/policywizard/model.go +++ b/internal/policywizard/model.go @@ -1,6 +1,7 @@ package policywizard import ( + "os" "strings" "github.com/charmbracelet/bubbles/spinner" @@ -8,6 +9,7 @@ import ( "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" "github.com/kosli-dev/cli/internal/policy" + "golang.org/x/term" ) const formWidth = 45 @@ -56,13 +58,18 @@ func NewModel(ctx *Context) Model { startStep = stepLoading } + w := 80 + if tw, _, err := term.GetSize(int(os.Stdout.Fd())); err == nil && tw > 0 { + w = tw + } + m := Model{ step: startStep, Policy: policy.NewPolicy(), ctx: ctx, styles: newStyles(), spinner: s, - width: 120, + width: w, } if startStep != stepLoading { m.form = m.buildForm() From e3f0cb9b93dba75c4a5cc2f573d8cbcac50c0524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:32:41 +0200 Subject: [PATCH 44/53] fix: make UnwrapExpr tolerant of varying whitespace Handle expressions like ${{expr}} (no inner spaces) in addition to the standard ${{ expr }} format. --- internal/policy/expression.go | 8 +++++--- internal/policy/expression_test.go | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index fe7ca9fc7..9ae5122b5 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -70,10 +70,12 @@ func WrapExpr(raw string) string { } // UnwrapExpr strips the ${{ }} wrapper, returning the inner expression. +// Tolerates varying whitespace inside the delimiters. func UnwrapExpr(expr string) string { - s := strings.TrimPrefix(expr, "${{ ") - s = strings.TrimSuffix(s, " }}") - return s + s := strings.TrimSpace(expr) + s = strings.TrimPrefix(s, "${{") + s = strings.TrimSuffix(s, "}}") + return strings.TrimSpace(s) } // NegateExpr prefixes a raw (unwrapped) expression with the not operator. diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 0f3d7d19b..c7ecbf21e 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -83,6 +83,11 @@ func TestUnwrapExpr(t *testing.T) { assert.Equal(t, `flow.name == "prod"`, result) } +func TestUnwrapExpr_NoInnerSpaces(t *testing.T) { + result := UnwrapExpr(`${{flow.name == "prod"}}`) + assert.Equal(t, `flow.name == "prod"`, result) +} + func TestUnwrapExpr_AlreadyRaw(t *testing.T) { result := UnwrapExpr(`flow.name == "prod"`) assert.Equal(t, `flow.name == "prod"`, result) From 2c51003a5756601e0cb3b8d36d278b763f631fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Wed, 8 Apr 2026 11:33:28 +0200 Subject: [PATCH 45/53] fix: validate regex in artifact name input Catch invalid regex patterns (e.g. [unclosed) at input time rather than letting them silently end up in the policy file. --- internal/policywizard/forms.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index bc041221f..0b657c117 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -3,6 +3,7 @@ package policywizard import ( "fmt" "path/filepath" + "regexp" "slices" "strings" @@ -144,7 +145,13 @@ func (m *Model) buildForm() *huh.Form { )) case stepExprArtifactName: - f = inputForm("value", "Artifact name regex", "e.g. ^datadog:.*", "^datadog:.*", "regex") + f = huh.NewForm(huh.NewGroup( + huh.NewInput().Key("value"). + Title("Artifact name regex"). + Description("e.g. ^datadog:.*"). + Placeholder("^datadog:.*"). + Validate(validateRegex), + )) case stepExprCustomCtx: f = huh.NewForm(huh.NewGroup( @@ -239,6 +246,16 @@ func notEmpty(field string) func(string) error { } } +func validateRegex(s string) error { + if s == "" { + return fmt.Errorf("regex is required") + } + if _, err := regexp.Compile(s); err != nil { + return fmt.Errorf("invalid regex: %s", err) + } + return nil +} + func validateYAMLExtension(s string) error { if s == "" { return nil // placeholder "policy.yaml" will be used From c47ae440f28c404fb536577c6bde6ea27f956edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 12:18:02 +0200 Subject: [PATCH 46/53] fix: restore sigs.k8s.io/kind to v0.31.0 after rebase The go.mod conflict resolution during rebase accidentally downgraded kind from v0.31.0 to v0.11.1, breaking ExportKubeConfig API. --- go.mod | 94 +++--- go.sum | 896 +++++++-------------------------------------------------- 2 files changed, 155 insertions(+), 835 deletions(-) diff --git a/go.mod b/go.mod index 21456f3ba..1a5368b9d 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,31 @@ module github.com/kosli-dev/cli -go 1.25.0 +go 1.25.9 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 - github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 + github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 github.com/Masterminds/semver/v3 v3.4.0 - github.com/andygrunwald/go-jira v1.16.0 + github.com/andygrunwald/go-jira v1.17.0 github.com/aws/aws-sdk-go-v2 v1.41.5 - github.com/aws/aws-sdk-go-v2/config v1.32.14 - github.com/aws/aws-sdk-go-v2/credentials v1.19.14 - github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.15 - github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6 - github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 - github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 + github.com/aws/aws-sdk-go-v2/config v1.32.13 + github.com/aws/aws-sdk-go-v2/credentials v1.19.13 + github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.13 + github.com/aws/aws-sdk-go-v2/service/ecs v1.75.0 + github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5 + github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0 github.com/aws/smithy-go v1.24.2 github.com/charmbracelet/bubbles v1.0.0 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/huh v1.0.0 github.com/charmbracelet/lipgloss v1.1.0 - github.com/containers/image/v5 v5.34.3 - github.com/docker/docker v28.0.4+incompatible + github.com/containers/image/v5 v5.36.2 + github.com/docker/docker v28.3.2+incompatible github.com/go-git/go-billy/v5 v5.8.0 - github.com/go-git/go-git/v5 v5.17.1 - github.com/go-playground/validator/v10 v10.26.0 + github.com/go-git/go-git/v5 v5.17.2 + github.com/go-playground/validator/v10 v10.30.2 github.com/google/go-github/v42 v42.0.0 github.com/hashicorp/go-retryablehttp v0.7.8 github.com/joshdk/go-junit v1.0.0 @@ -33,7 +33,7 @@ require ( github.com/maxcnunes/httpfake v1.2.4 github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 github.com/mitchellh/go-homedir v1.1.0 - github.com/open-policy-agent/opa v1.13.2 + github.com/open-policy-agent/opa v1.15.1 github.com/otiai10/copy v1.14.1 github.com/owenrumney/go-sarif/v2 v2.3.3 github.com/pkg/errors v0.9.1 @@ -45,29 +45,28 @@ require ( github.com/stretchr/testify v1.11.1 github.com/xeonx/timeago v1.0.0-rc5 github.com/yargevad/filepathx v1.0.0 - github.com/zalando/go-keyring v0.2.6 + github.com/zalando/go-keyring v0.2.8 gitlab.com/gitlab-org/api/client-go v1.46.0 - golang.org/x/oauth2 v0.34.0 - golang.org/x/term v0.40.0 + golang.org/x/oauth2 v0.36.0 + golang.org/x/term v0.41.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.35.0 k8s.io/apimachinery v0.35.0 k8s.io/client-go v1.5.2 - k8s.io/kubernetes v1.35.0 - sigs.k8s.io/kind v0.11.1 + k8s.io/kubernetes v1.35.3 + sigs.k8s.io/kind v0.31.0 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.1 // indirect dario.cat/mergo v1.0.2 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/ProtonMail/go-crypto v1.2.0 // indirect github.com/agnivade/levenshtein v1.2.1 // indirect - github.com/alessio/shellescape v1.4.2 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/atotto/clipboard v0.1.4 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect @@ -81,8 +80,8 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // 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/sso v1.30.14 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -99,9 +98,11 @@ require ( github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.5.0 // indirect github.com/cloudflare/circl v1.6.3 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect github.com/containers/ocicrypt v1.2.1 // indirect - github.com/containers/storage v1.57.2 // indirect + github.com/containers/storage v1.59.1 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect github.com/cyphar/filepath-securejoin v0.6.0 // indirect github.com/danieljoos/wincred v1.2.3 // indirect @@ -116,12 +117,12 @@ require ( github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect - github.com/evanphx/json-patch/v5 v5.2.0 // indirect + github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fatih/structs v1.1.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect @@ -147,7 +148,7 @@ require ( github.com/google/uuid v1.6.0 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect @@ -173,6 +174,7 @@ require ( github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect @@ -214,7 +216,7 @@ require ( github.com/tchap/go-patricia/v2 v2.3.3 // indirect github.com/trivago/tgo v1.0.7 // indirect github.com/valyala/fastjson v1.6.7 // indirect - github.com/vektah/gqlparser/v2 v2.5.31 // indirect + github.com/vektah/gqlparser/v2 v2.5.32 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect @@ -223,32 +225,30 @@ require ( github.com/yashtewari/glob-intersection v0.2.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/sdk v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/otel v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 // indirect + go.opentelemetry.io/otel/metric v1.41.0 // indirect + go.opentelemetry.io/otel/sdk v1.41.0 // indirect + go.opentelemetry.io/otel/trace v1.41.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect + golang.org/x/crypto v0.49.0 // indirect golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect - golang.org/x/mod v0.32.0 // indirect + golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.51.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/time v0.14.0 // indirect - golang.org/x/tools v0.41.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.42.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/grpc v1.79.3 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.1 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect k8s.io/apiextensions-apiserver v0.0.0 // indirect k8s.io/apiserver v0.35.0 // indirect diff --git a/go.sum b/go.sum index 1b6d66516..df668b822 100644 --- a/go.sum +++ b/go.sum @@ -2,31 +2,18 @@ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeX al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 h1:OVoM452qUFBrX+URdH3VpR299ma4kfom0yB0URYky9g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0/go.mod h1:kUjrAo8bgEwLeZ/CmHqNl3Z/kPm7y6FKfxxK0izYUg4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= -github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2 h1:wBx10efdJcl8FSewgc41kAW4AvHPgmJZmN7fpNxn8rc= -github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.2/go.mod h1:zzmu18cpAinSbhC86oWd47nmgbb91Fl+Yac2PE8NdYk= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3 h1:ldKsKtEIblsgsr6mPwrd9yRntoX6uLz/K89wsldwx/k= +github.com/Azure/azure-sdk-for-go/sdk/containers/azcontainerregistry v0.2.3/go.mod h1:MAm7bk0oDLmD8yIkvfbxPW04fxzphPyL+7GzwHxOp6Y= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0 h1:JI8PcWOImyvIUEZ0Bbmfe05FOlWkMi2KhjG+cAKaUms= github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/appservice/armappservice/v2 v2.3.0/go.mod h1:nJLFPGJkyKfDDyJiPuHIXsCi/gpJkm07EvRgiX7SGlI= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= @@ -35,33 +22,23 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= -github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs= github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/agnivade/levenshtein v1.2.1 h1:EHBY3UOn1gwdy/VbFwgo4cxecRznFk7fKWN1KOX7eoM= github.com/agnivade/levenshtein v1.2.1/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= -github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= -github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ= -github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU= +github.com/andygrunwald/go-jira v1.17.0 h1:bbu5H676l6MaNcV6A7VDIAjIOQVgzNGEhNAwNI/Cjgo= +github.com/andygrunwald/go-jira v1.17.0/go.mod h1:tiZsPUu9824bwcI2BUXatE4hJbs9rUOif0nv1lkq1hQ= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= @@ -69,9 +46,6 @@ github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmO github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= @@ -80,14 +54,14 @@ github.com/aws/aws-sdk-go-v2 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI= -github.com/aws/aws-sdk-go-v2/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI= -github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI= -github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w= +github.com/aws/aws-sdk-go-v2/config v1.32.13 h1:5KgbxMaS2coSWRrx9TX/QtWbqzgQkOdEa3sZPhBhCSg= +github.com/aws/aws-sdk-go-v2/config v1.32.13/go.mod h1:8zz7wedqtCbw5e9Mi2doEwDyEgHcEE9YOJp6a8jdSMY= +github.com/aws/aws-sdk-go-v2/credentials v1.19.13 h1:mA59E3fokBvyEGHKFdnpNNrvaR351cqiHgRg+JzOSRI= +github.com/aws/aws-sdk-go-v2/credentials v1.19.13/go.mod h1:yoTXOQKea18nrM69wGF9jBdG4WocSZA1h38A+t/MAsk= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI= -github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.15 h1:92MfpwB6KjsPIEq9g3DniRPxOe92ew5hUz1h8W8cX7E= -github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.15/go.mod h1:7O129SmOn4acM++3oVfTLAeHmNOsj0y7AA7zmbgnGOk= +github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.13 h1:KJBtvxjIEtZe0YZ1gxIvs00Bmm+UU+Yu3SXhrDLgjEY= +github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.13/go.mod h1:FpZJum2niivv57xOCCCLfGsR4m3K/ofeAJe70g0oRQY= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A= @@ -96,8 +70,8 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 h1:rWyie/PxDRIdhNf4DzRk0lvjVOqFJuNnO8WwaIRVxzQ= github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22/go.mod h1:zd/JsJ4P7oGfUhXn1VyLqaRZwPmZwg44Jf2dS84Dm3Y= -github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6 h1:TE4XBXeHvTTnD4rISqqMET4TwE7St4MrZvnJp+Gg5tY= -github.com/aws/aws-sdk-go-v2/service/ecs v1.54.6/go.mod h1:wAtdeFanDuF9Re/ge4DRDaYe3Wy1OGrU7jG042UcuI4= +github.com/aws/aws-sdk-go-v2/service/ecs v1.75.0 h1:VhatEa6ut7SjvJCW6Gx4keUMGMusOleGmiiNH6UiiTU= +github.com/aws/aws-sdk-go-v2/service/ecs v1.75.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 h1:JRaIgADQS/U6uXDqlPiefP32yXTda7Kqfx+LgspooZM= @@ -106,32 +80,26 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3x github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 h1:ZlvrNcHSFFWURB8avufQq9gFsheUgjVD9536obIknfM= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21/go.mod h1:cv3TNhVrssKR0O/xxLJVRfd2oazSnZnkUeTf6ctUwfQ= -github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2 h1:z926KZ1Ysi8Mbi4biJSAIRFdKemwQpO9M0QUTRLDaXA= -github.com/aws/aws-sdk-go-v2/service/lambda v1.71.2/go.mod h1:c27kk10S36lBYgbG1jR3opn4OAS5Y/4wjJa1GiHK/X4= -github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0 h1:hlSuz394kV0vhv9drL5lhuEFbEOEP1VyQpy15qWh1Pk= -github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= +github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5 h1:HWN7xwaV7Zwrn3Jlauio4u4aTMFgRzG2fblHWQeir/k= +github.com/aws/aws-sdk-go-v2/service/lambda v1.88.5/go.mod h1:6HBXRyFFqOw+ALkJ6YGHfrr20/YXYv6X9pcZErXRvCA= +github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0 h1:foqo/ocQ7WqKwy3FojGtZQJo0FR4vto9qnz9VaumbCo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.98.0/go.mod h1:uoA43SdFwacedBfSgfFSjjCvYe8aYBS7EnU5GZ/YKMM= github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.14 h1:GcLE9ba5ehAQma6wlopUesYg/hbcOhFNWTjELkiWkh4= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.14/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 h1:mP49nTpfKtpXLt5SLn8Uv8z6W+03jYVoOSAl/c02nog= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w= github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U= github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw= github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng= github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA= github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bytecodealliance/wasmtime-go/v39 v39.0.1 h1:RibaT47yiyCRxMOj/l2cvL8cWiWBSqDXHyqsa9sGcCE= @@ -140,55 +108,36 @@ github.com/catppuccin/go v0.3.0 h1:d+0/YicIq+hSTo5oPuRi5kOpqkVA5tAsU6dNhvRu+aY= github.com/catppuccin/go v0.3.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5fXonfvc= github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= -github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0= github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= github.com/charmbracelet/huh v1.0.0 h1:wOnedH8G4qzJbmhftTqrpppyqHakl/zbbNdXIWJyIxw= github.com/charmbracelet/huh v1.0.0/go.mod h1:5YVc+SlZ1IhQALxRPpkGwwEKftN/+OlJlnJYlDRFqN4= github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= -github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= -github.com/charmbracelet/x/ansi v0.9.2/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE= github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8= github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ= -github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= -github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI= github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q= github.com/charmbracelet/x/conpty v0.1.0 h1:4zc8KaIcbiL4mghEON8D72agYtSeIgq8FSThSPQIb+U= github.com/charmbracelet/x/conpty v0.1.0/go.mod h1:rMFsDJoDwVmiYM10aD4bH2XiRgwI7NYJtQgl5yskjEQ= github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9iqk37QUU2Rvb6DSBYRLtWqFqfxf8l5hOZUA= github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= -github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ= github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U= github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 h1:qko3AQ4gK1MTS/de7F5hPGx6/k1u0w4TeYmBFwzYVP4= github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0/go.mod h1:pBhA0ybfXv6hDjQUZ7hk1lVxBiUbupdw5R31yPUViVQ= -github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk= github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI= github.com/charmbracelet/x/termios v0.1.1 h1:o3Q2bT8eqzGnGPOYheoYS8eEleT5ZVNYNy8JawjaNZY= github.com/charmbracelet/x/termios v0.1.1/go.mod h1:rB7fnv1TgOPOyyKRJ9o+AsTU/vK5WHJ2ivHeut/Pcwo= github.com/charmbracelet/x/xpty v0.1.2 h1:Pqmu4TEJ8KeA9uSkISKMU3f+C1F6OGBn8ABuGlqCbtI= github.com/charmbracelet/x/xpty v0.1.2/go.mod h1:XK2Z0id5rtLWcpeNiMYBccNNBrP2IJnzHI0Lq13Xzq4= -github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs= -github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/clipperhouse/displaywidth v0.9.0 h1:Qb4KOhYwRiN3viMv1v/3cTBlz3AcAZX3+y9OLhMtAtA= github.com/clipperhouse/displaywidth v0.9.0/go.mod h1:aCAAqTlh4GIVkhQnJpbL0T/WfcrJXHcj8C0yjYcjOZA= github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= @@ -197,26 +146,23 @@ github.com/clipperhouse/uax29/v2 v2.5.0 h1:x7T0T4eTHDONxFJsL94uKNKPHrclyFI0lm7+w github.com/clipperhouse/uax29/v2 v2.5.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8= github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containers/image/v5 v5.34.3 h1:/cMgfyA4Y7ILH7nzWP/kqpkE5Df35Ek4bp5ZPvJOVmI= -github.com/containers/image/v5 v5.34.3/go.mod h1:MG++slvQSZVq5ejAcLdu4APGsKGMb0YHHnAo7X28fdE= +github.com/containers/image/v5 v5.36.2 h1:GcxYQyAHRF/pLqR4p4RpvKllnNL8mOBn0eZnqJbfTwk= +github.com/containers/image/v5 v5.36.2/go.mod h1:b4GMKH2z/5t6/09utbse2ZiLK/c72GuGLFdp7K69eA4= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA= github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= github.com/containers/ocicrypt v1.2.1 h1:0qIOTT9DoYwcKmxSt8QJt+VzMY18onl9jUXsxpVhSmM= github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= -github.com/containers/storage v1.57.2 h1:2roCtTyE9pzIaBDHibK72DTnYkPmwWaq5uXxZdaWK4U= -github.com/containers/storage v1.57.2/go.mod h1:i/Hb4lu7YgFr9G0K6BMjqW0BLJO1sFsnWQwj2UoWCUM= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/containers/storage v1.59.1 h1:11Zu68MXsEQGBBd+GadPrHPpWeqjKS8hJDGiAHgIqDs= +github.com/containers/storage v1.59.1/go.mod h1:KoAYHnAjP3/cTsRS+mmWZGkufSY2GACiKQ4V3ZLQnR0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is= @@ -229,24 +175,22 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/dgraph-io/badger/v4 v4.9.0 h1:tpqWb0NewSrCYqTvywbcXOhQdWcqephkVkbBmaaqHzc= -github.com/dgraph-io/badger/v4 v4.9.0/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= +github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8NC8w= +github.com/dgraph-io/badger/v4 v4.9.1/go.mod h1:5/MEx97uzdPUHR4KtkNt8asfI2T4JiEiQlV7kWUo8c0= github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM= github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo= github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v27.5.1+incompatible h1:JB9cieUT9YNiMITtIsguaN55PLOHhBSz3LKVc6cqWaY= -github.com/docker/cli v27.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS3hP2huFsY= +github.com/docker/cli v28.3.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= -github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= +github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -259,16 +203,14 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= -github.com/evanphx/json-patch/v5 v5.2.0 h1:8ozOH5xxoMYDt5/u+yMTsVXydVCbTORFnOOoq2lumco= -github.com/evanphx/json-patch/v5 v5.2.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= +github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= @@ -281,15 +223,12 @@ github.com/foxcpp/go-mockdns v1.2.0 h1:omK3OrHRD1IWJz1FuFBCFquhXslXoF17OvBS6JPzZ github.com/foxcpp/go-mockdns v1.2.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM= +github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs= github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -304,35 +243,19 @@ github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDz github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.17.1 h1:WnljyxIzSj9BRRUlnmAU35ohDsjRK0EKmL0evDqi5Jk= -github.com/go-git/go-git/v5 v5.17.1/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-git/go-git/v5 v5.17.2 h1:B+nkdlxdYrvyFK4GPXVU8w1U+YkbsgciIR7f2sZJ104= +github.com/go-git/go-git/v5 v5.17.2/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -341,174 +264,90 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= -github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-playground/validator/v10 v10.30.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ= +github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc= github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= github.com/godbus/dbus/v5 v5.2.2 h1:TUR3TgtSVDmjiXOgAAyaZbYmIeP3DPkld3jgKGV8mXQ= github.com/godbus/dbus/v5 v5.2.2/go.mod h1:3AAv2+hPq5rdnr5txxxRwiGjPXamgoIHgz9FPBfOp3c= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/cel-go v0.27.0 h1:e7ih85+4qVrBuqQWTW4FKSqZYokVuc3HnhH5keboFTo= github.com/google/cel-go v0.27.0/go.mod h1:tTJ11FWqnhw5KKpnWpvW9CJC3Y9GK4EIS0WXnBbebzw= github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q= github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github/v42 v42.0.0 h1:YNT0FwjPrEysRkLIiKuEfSvBPCGKphW5aS5PxwaoLec= github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/google/go-querystring v1.2.0 h1:yhqkPbu2/OH+V9BfpCVPZkNmUXhb2gBxJArfhIxNtP0= github.com/google/go-querystring v1.2.0/go.mod h1:8IFJqpSRITyJ8QhQ13bmbeMBDfmeEJZD5A0egEOmkqU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= -github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= -github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/graph-gophers/graphql-go v1.9.0 h1:yu0ucKHLc5qGpRwLYKIWtr9bOoxovkWasuBrPQwlHls= github.com/graph-gophers/graphql-go v1.9.0/go.mod h1:23olKZ7duEvHlF/2ELEoSZaY1aNPfShjP782SOoNTyM= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 h1:X+2YciYSxvMQK0UZ7sg45ZVabVZBeBuvMkmuI2V3Fak= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7/go.mod h1:lW34nIZuQ8UDPdkon5fmfp2l3+ZkQ2me/+oecHYLOII= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE= github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -517,7 +356,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= @@ -534,66 +372,53 @@ github.com/lestrrat-go/jwx/v3 v3.0.13 h1:AdHKiPIYeCSnOJtvdpipPg/0SuFh9rdkN+HF3O0 github.com/lestrrat-go/jwx/v3 v3.0.13/go.mod h1:2m0PV1A9tM4b/jVLMx8rh6rBl7F6WGb3EG2hufN9OQU= github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= -github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/maxcnunes/httpfake v1.2.4 h1:l7s/N7zuG6XpzG+5dUolg5SSoR3hANQxqzAkv+lREko= github.com/maxcnunes/httpfake v1.2.4/go.mod h1:rWVxb0bLKtOUM/5hN3UO1VEdEitz1hfcTXs7UyiK6r0= github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE= github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5 h1:YH424zrwLTlyHSH/GzLMJeu5zhYVZSx5RQxGKm1h96s= github.com/microsoft/azure-devops-go-api/azuredevops v1.0.0-b5/go.mod h1:PoGiBqKSQK1vIfQ+yVaFcGjDySHvym6FM1cNYnwzbrY= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM= github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= @@ -607,74 +432,20 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0= -github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo= -github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw= -github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo= -github.com/onsi/ginkgo/v2 v2.8.1/go.mod h1:N1/NbDngAFcSLdyZ+/aYTYGSlq9qMCS/cNKGJjy+csc= -github.com/onsi/ginkgo/v2 v2.9.0/go.mod h1:4xkjoL/tZv4SMWeww56BU5kAt19mVB47gTWxmrTcxyk= -github.com/onsi/ginkgo/v2 v2.9.1/go.mod h1:FEcmzVcCHl+4o9bQZVab+4dC9+j+91t2FHSzmGAPfuo= -github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= -github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= -github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= -github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= -github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= -github.com/onsi/ginkgo/v2 v2.25.1/go.mod h1:ppTWQ1dh9KM/F1XgpeRqelR+zHVwV81DGRSDnFxK7Sk= github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns= github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= -github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= -github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM= -github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= -github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= -github.com/onsi/gomega v1.27.1/go.mod h1:aHX5xOykVYzWOV4WqQy0sy8BQptgukenXpCXfadcIAw= -github.com/onsi/gomega v1.27.3/go.mod h1:5vG284IBtfDAmDyrK+eGyZmUgUlmi+Wngqo557cZ6Gw= -github.com/onsi/gomega v1.27.4/go.mod h1:riYq/GJKh8hhoM01HN6Vmuy93AarCXCBGpvFDK3q3fQ= -github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/onsi/gomega v1.27.8/go.mod h1:2J8vzI/s+2shY9XHRApDkdgPo1TKT7P2u6fXeJKFnNQ= -github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= -github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= -github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= -github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= -github.com/open-policy-agent/opa v1.13.2 h1:c72l7DhxP4g8DEUBOdaU9QBKyA24dZxCcIuZNRZ0yP4= -github.com/open-policy-agent/opa v1.13.2/go.mod h1:M3Asy9yp1YTusUU5VQuENDe92GLmamIuceqjw+C8PHY= +github.com/open-policy-agent/opa v1.15.1 h1:ZE4JaXsVUzDiHFSlOMBS3nJohR5BRGB/RNz6gTNugzE= +github.com/open-policy-agent/opa v1.15.1/go.mod h1:c6SN+7jSsUcKJLQc5P4yhwx8YYDRbjpAiGkBOTqxaa4= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= @@ -682,9 +453,6 @@ github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAq github.com/owenrumney/go-sarif v1.1.1/go.mod h1:dNDiPlF04ESR/6fHlPyq7gHKmrM0sHUvAGjsoh8ZH0U= github.com/owenrumney/go-sarif/v2 v2.3.3 h1:ubWDJcF5i3L/EIOER+ZyQ03IfplbSU1BLOE26uKQIIU= github.com/owenrumney/go-sarif/v2 v2.3.3/go.mod h1:MSqMMx9WqlBSY7pXoOZWgEsVB4FDNfhcaXDA1j6Sr+w= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= @@ -693,140 +461,92 @@ github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs= github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0= github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg= github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= -github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rjeczalik/notify v0.9.3 h1:6rJAzHTGKXGj76sbRgDiDcYj/HniypXmSJo1SWakZeY= github.com/rjeczalik/notify v0.9.3/go.mod h1:gF3zSOrafR9DQEWSE8TjfI9NkooDxbyT4UgRGKZA0lc= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc= github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw= github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= -github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM= github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc= github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/valyala/fastjson v1.6.7 h1:ZE4tRy0CIkh+qDc5McjatheGX2czdn8slQjomexVpBM= github.com/valyala/fastjson v1.6.7/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= -github.com/vbatts/tar-split v0.11.7 h1:ixZ93pO/GmvaZw4Vq9OwmfZK/kc2zKdPfu0B+gYqs3U= -github.com/vbatts/tar-split v0.11.7/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= -github.com/vektah/gqlparser/v2 v2.5.31 h1:YhWGA1mfTjID7qJhd1+Vxhpk5HTgydrGU9IgkWBTJ7k= -github.com/vektah/gqlparser/v2 v2.5.31/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vektah/gqlparser/v2 v2.5.32 h1:k9QPJd4sEDTL+qB4ncPLflqTJ3MmjB9SrVzJrawpFSc= +github.com/vektah/gqlparser/v2 v2.5.32/go.mod h1:c1I28gSOVNzlfc4WuDlqU7voQnsqI6OG2amkBAFmgts= github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= @@ -839,7 +559,6 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeonx/timeago v1.0.0-rc5 h1:pwcQGpaH3eLfPtXeyPA4DmHWjoQt0Ea7/++FwpxqLxg= github.com/xeonx/timeago v1.0.0-rc5/go.mod h1:qDLrYEFynLO7y5Ho7w3GwgtYgpy5UfhcXIIQvMKVDkA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc= @@ -848,526 +567,137 @@ github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBe github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s= -github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI= +github.com/zalando/go-keyring v0.2.8 h1:6sD/Ucpl7jNq10rM2pgqTs0sZ9V3qMrqfIIy5YPccHs= +github.com/zalando/go-keyring v0.2.8/go.mod h1:tsMo+VpRq5NGyKfxoBVjCuMrG47yj8cmakZDO5QGii0= github.com/zclconf/go-cty v1.10.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= gitlab.com/gitlab-org/api/client-go v1.46.0 h1:YxBWFZIFYKcGESCb9fpkwzouo+apyB9pr/XTWzNoL24= gitlab.com/gitlab-org/api/client-go v1.46.0/go.mod h1:FtgyU6g2HS5+fMhw6nLK96GBEEBx5MzntOiJWfIaiN8= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0 h1:DvJDOPmSWQHWywQS6lKL+pb8s3gBLOZUtw4N+mavW1I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.40.0/go.mod h1:EtekO9DEJb4/jRyN4v4Qjc2yA7AtfCBuz2FynRUWTXs= +go.opentelemetry.io/otel v1.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= +go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0 h1:ao6Oe+wSebTlQ1OEht7jlYTzQKE+pnx/iNywFvTbuuI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.41.0/go.mod h1:u3T6vz0gh/NVzgDgiwkgLxpsSF6PaPmo2il0apGJbls= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0 h1:mq/Qcf28TWz719lE3/hMB4KkyDuLJIvgJnFGcd0kEUI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.41.0/go.mod h1:yk5LXEYhsL2htyDNJbEq7fWzNEigeEdV5xBF/Y+kAv0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0 h1:wVZXIWjQSeSmMoxF74LzAnpVQOAFDo3pPji9Y4SOFKc= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.40.0/go.mod h1:khvBS2IggMFNwZK/6lEeHg/W57h/IX6J4URh57fuI40= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/otel/metric v1.41.0 h1:rFnDcs4gRzBcsO9tS8LCpgR0dxg4aaxWlJxCno7JlTQ= +go.opentelemetry.io/otel/metric v1.41.0/go.mod h1:xPvCwd9pU0VN8tPZYzDZV/BMj9CM9vs00GuBjeKhJps= +go.opentelemetry.io/otel/sdk v1.41.0 h1:YPIEXKmiAwkGl3Gu1huk1aYWwtpRLeskpV+wPisxBp8= +go.opentelemetry.io/otel/sdk v1.41.0/go.mod h1:ahFdU0G5y8IxglBf0QBJXgSe7agzjE4GiTJ6HT9ud90= +go.opentelemetry.io/otel/sdk/metric v1.41.0 h1:siZQIYBAUd1rlIWQT2uCxWJxcCO7q3TriaMlf08rXw8= +go.opentelemetry.io/otel/sdk/metric v1.41.0/go.mod h1:HNBuSvT7ROaGtGI50ArdRLUnvRTRGniSUZbxiWxSO8Y= +go.opentelemetry.io/otel/trace v1.41.0 h1:Vbk2co6bhj8L59ZJ6/xFTskY+tGAbOnCtQGVVa9TIN0= +go.opentelemetry.io/otel/trace v1.41.0/go.mod h1:U1NU4ULCoxeDKc09yCWdWe+3QoyweJcISEVa1RBzOis= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= -golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= -golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= -golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= -golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 h1:SbTAbRFnd5kjQXbczszQ0hdk3ctwYf3qBNH9jIsGclE= golang.org/x/exp v0.0.0-20250813145105-42675adae3e6/go.mod h1:4QTo5u+SEIbbKW1RacMZq1YEfOBqeXa19JeshGi+zc4= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ= -golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= -golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= -golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= +golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= -golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= -golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= -golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/net v0.51.0 h1:94R/GTO7mt3/4wIKpcR5gkGmRLOuE/2hNGeWq/GBIFo= golang.org/x/net v0.51.0/go.mod h1:aamm+2QF5ogm02fjy5Bb7CQ0WMt1/WVM7FtyaTLlA9Y= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= -golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20180926160741-c2ed4eda69e7/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= -golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= -golang.org/x/telemetry v0.0.0-20250807160809-1a19826ec488/go.mod h1:fGb/2+tgXXjhjHsTNdVEEMZNWA0quBnfrO+AfoDSAKw= -golang.org/x/telemetry v0.0.0-20250908211612-aef8a434d053/go.mod h1:+nZKN+XVh4LCiA9DV3ywrzN4gumyCnKjau3NGb9SGoE= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= -golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= -golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= -golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= -golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0= -golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= -golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= -golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= -golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= -golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= -golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= -golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= -golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= -golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= -golang.org/x/tools v0.34.1-0.20250613162507-3f93fece84c7/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= -golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= -golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= -golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= -golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= -golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= -golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= +golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= +golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M= -google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409 h1:H86B94AW+VfJWDqFeEbBPhEtHzJwJfTbgE2lZa54ZAQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20260128011058-8636f8732409/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0= +google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE= google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= -google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.1 h1:tVBILHy0R6e4wkYOn3XmiITt/hEVH4TFMYvAX2Ytz6k= -gopkg.in/ini.v1 v1.67.1/go.mod h1:x/cyOwCgZqOkJoDIJ3c1KNHMo10+nLGAhh+kn3Zizss= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= -gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= @@ -1384,9 +714,6 @@ k8s.io/component-helpers v0.35.0 h1:wcXv7HJRksgVjM4VlXJ1CNFBpyDHruRI99RrBtrJceA= k8s.io/component-helpers v0.35.0/go.mod h1:ahX0m/LTYmu7fL3W8zYiIwnQ/5gT28Ex4o2pymF63Co= k8s.io/controller-manager v0.35.0 h1:KteodmfVIRzfZ3RDaxhnHb72rswBxEngvdL9vuZOA9A= k8s.io/controller-manager v0.35.0/go.mod h1:1bVuPNUG6/dpWpevsJpXioS0E0SJnZ7I/Wqc9Awyzm4= -k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= @@ -1395,28 +722,21 @@ k8s.io/kubectl v0.35.0 h1:cL/wJKHDe8E8+rP3G7avnymcMg6bH6JEcR5w5uo06wc= k8s.io/kubectl v0.35.0/go.mod h1:VR5/TSkYyxZwrRwY5I5dDq6l5KXmiCb+9w8IKplk3Qo= k8s.io/kubelet v0.35.0 h1:8cgJHCBCKLYuuQ7/Pxb/qWbJfX1LXIw7790ce9xHq7c= k8s.io/kubelet v0.35.0/go.mod h1:ciRzAXn7C4z5iB7FhG1L2CGPPXLTVCABDlbXt/Zz8YA= -k8s.io/kubernetes v1.35.0 h1:PUOojD8c8E3csMP5NX+nLLne6SGqZjrYCscptyBfWMY= -k8s.io/kubernetes v1.35.0/go.mod h1:Tzk9Y9W/XUFFFgTUVg+BAowoFe+Pc7koGLuaiLHdcFg= +k8s.io/kubernetes v1.35.3 h1:J3dk2wybKFHwoH4eydDUGHJo4HAD+9CZbSlvk/YQuao= +k8s.io/kubernetes v1.35.3/go.mod h1:AaPpCpiS8oAqRbEwpY5r3RitLpwpVp5lVXKFkJril58= k8s.io/pod-security-admission v0.35.0 h1:tT3UHC+Q1mpFRe4IoVTu20ZAx+kqgKBZnewRnsDcyfc= k8s.io/pod-security-admission v0.35.0/go.mod h1:S+57PAqNo6DaUYjmtINiiXlYnEdShrOVMwSc7C4oYPg= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kind v0.11.1 h1:pVzOkhUwMBrCB0Q/WllQDO3v14Y+o2V0tFgjTqIUjwA= -sigs.k8s.io/kind v0.11.1/go.mod h1:fRpgVhtqAWrtLB9ED7zQahUimpUXuG/iHT88xYqEGIA= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= +sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v6 v6.2.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 1aba1591f0018289f7aee8bec01c2c49c1d4ad91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 13:13:09 +0200 Subject: [PATCH 47/53] fix: validate regex when matches operator is used in custom comparison --- internal/policywizard/forms.go | 4 ++++ internal/policywizard/forms_test.go | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index 0b657c117..ed363fad8 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -382,6 +382,10 @@ func (m *Model) applyFormValues(fv formValues) { case "exists": m.storeSubExpr(policy.ExistsExpr(m.exprContext)) case "matches": + if err := validateRegex(fv.str); err != nil { + m.validationErr = err.Error() + return + } m.storeSubExpr(policy.MatchesExpr(m.exprContext, fv.str)) default: m.storeSubExpr(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 47a5cfa43..2627f2190 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -413,6 +413,17 @@ func TestApply_ExprCustomOp_MatchesStoresPending(t *testing.T) { assert.Equal(t, `matches(flow.name, "^prod")`, m.pendingExprs[0]) } +func TestApply_ExprCustomOp_MatchesInvalidRegex(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprContext = "flow.name" + + m.applyFormValues(formValues{operator: "matches", str: "[unclosed"}) + + assert.Empty(t, m.pendingExprs) + assert.Contains(t, m.validationErr, "invalid regex") +} + func TestApply_ExprCustomOp_ExistsStoresPending(t *testing.T) { m := newTestModel() m.step = stepExprCustomOp From 47ebc6607054cb3113c57e7bc3bf551af081f363 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 13:14:05 +0200 Subject: [PATCH 48/53] fix: parenthesize NegateExpr to avoid operator precedence ambiguity not flow.name == "prod" could be parsed as (not flow.name) == "prod". Using not (flow.name == "prod") is unambiguous. --- internal/policy/expression.go | 3 ++- internal/policy/expression_test.go | 4 ++-- internal/policywizard/forms_test.go | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 9ae5122b5..76e458b59 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -79,6 +79,7 @@ func UnwrapExpr(expr string) string { } // NegateExpr prefixes a raw (unwrapped) expression with the not operator. +// Parentheses ensure correct evaluation regardless of operator precedence. func NegateExpr(raw string) string { - return fmt.Sprintf("not %s", raw) + return fmt.Sprintf("not (%s)", raw) } diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index c7ecbf21e..4d46ca940 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -95,12 +95,12 @@ func TestUnwrapExpr_AlreadyRaw(t *testing.T) { func TestNegateExpr(t *testing.T) { result := NegateExpr(`flow.name == "prod"`) - assert.Equal(t, `not flow.name == "prod"`, result) + assert.Equal(t, `not (flow.name == "prod")`, result) } func TestCombineAndNegate(t *testing.T) { a := UnwrapExpr(FlowNameExpr("prod")) b := NegateExpr(UnwrapExpr(ArtifactNameMatchExpr("^datadog:.*"))) result := CombineExprs("and", a, b) - assert.Equal(t, `${{ flow.name == "prod" and not matches(artifact.name, "^datadog:.*") }}`, result) + assert.Equal(t, `${{ flow.name == "prod" and not (matches(artifact.name, "^datadog:.*")) }}`, result) } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 2627f2190..e2d546850 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -447,7 +447,7 @@ func TestApply_ExprNegate_Yes_NegatesLastPending(t *testing.T) { m.applyFormValues(formValues{confirm: true}) require.Len(t, m.pendingExprs, 1) - assert.Equal(t, `not flow.name == "prod"`, m.pendingExprs[0]) + assert.Equal(t, `not (flow.name == "prod")`, m.pendingExprs[0]) } func TestApply_ExprNegate_No_LeavesUnchanged(t *testing.T) { @@ -587,7 +587,7 @@ func TestCombineFlow_NegatedExprWithOr(t *testing.T) { require.Len(t, m.Policy.Artifacts.TrailCompliance.Exceptions, 1) assert.Equal(t, - `${{ not flow.name == "staging" or exists(flow) }}`, + `${{ not (flow.name == "staging") or exists(flow) }}`, m.Policy.Artifacts.TrailCompliance.Exceptions[0].If, ) } From aeae08518e70cd81943d6c90dcfb6c29eaec8bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 13:14:33 +0200 Subject: [PATCH 49/53] fix: guard CombineExprs against zero args --- internal/policy/expression.go | 3 +++ internal/policy/expression_test.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/internal/policy/expression.go b/internal/policy/expression.go index 76e458b59..38ad3f65c 100644 --- a/internal/policy/expression.go +++ b/internal/policy/expression.go @@ -55,6 +55,9 @@ func ComparisonExpr(context, op, value string) string { // CombineExprs joins inner expressions (without ${{ }} wrappers) with a logical operator. func CombineExprs(op string, exprs ...string) string { + if len(exprs) == 0 { + return "" + } if len(exprs) == 1 { return WrapExpr(exprs[0]) } diff --git a/internal/policy/expression_test.go b/internal/policy/expression_test.go index 4d46ca940..0026a1e44 100644 --- a/internal/policy/expression_test.go +++ b/internal/policy/expression_test.go @@ -68,6 +68,11 @@ func TestCombineExprs_Single(t *testing.T) { assert.Equal(t, `${{ flow.name == "prod" }}`, result) } +func TestCombineExprs_Empty(t *testing.T) { + result := CombineExprs("and") + assert.Equal(t, "", result) +} + func TestWrapExpr_AddsWrapper(t *testing.T) { result := WrapExpr(`flow.name == "prod"`) assert.Equal(t, `${{ flow.name == "prod" }}`, result) From 2c2846472883d731552fab74871998bf30142133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 13:47:44 +0200 Subject: [PATCH 50/53] fix: clear validationErr on successful retry in stepExprCustomOp Without clearing, a failed matches regex validation would leave validationErr set, causing an infinite loop when the user retried with a valid regex or switched to a different operator. --- internal/policywizard/forms.go | 1 + internal/policywizard/forms_test.go | 30 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index ed363fad8..fb32acdd2 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -378,6 +378,7 @@ func (m *Model) applyFormValues(fv formValues) { m.exprContext = "flow.tags." + fv.str case stepExprCustomOp: + m.validationErr = "" switch fv.operator { case "exists": m.storeSubExpr(policy.ExistsExpr(m.exprContext)) diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index e2d546850..43a78c5f6 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -424,6 +424,36 @@ func TestApply_ExprCustomOp_MatchesInvalidRegex(t *testing.T) { assert.Contains(t, m.validationErr, "invalid regex") } +func TestApply_ExprCustomOp_MatchesValidRetryClears(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprContext = "flow.name" + + // First attempt: invalid regex + m.applyFormValues(formValues{operator: "matches", str: "[unclosed"}) + assert.Contains(t, m.validationErr, "invalid regex") + + // Second attempt: valid regex should clear error + m.applyFormValues(formValues{operator: "matches", str: "^prod"}) + assert.Empty(t, m.validationErr) + require.Len(t, m.pendingExprs, 1) +} + +func TestApply_ExprCustomOp_SwitchOperatorClearsError(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprContext = "flow.name" + + // First attempt: invalid regex with matches + m.applyFormValues(formValues{operator: "matches", str: "[unclosed"}) + assert.Contains(t, m.validationErr, "invalid regex") + + // Switch to == operator should clear error + m.applyFormValues(formValues{operator: "==", str: "prod"}) + assert.Empty(t, m.validationErr) + require.Len(t, m.pendingExprs, 1) +} + func TestApply_ExprCustomOp_ExistsStoresPending(t *testing.T) { m := newTestModel() m.step = stepExprCustomOp From 2dd5d568b5f5add2c99f4f7cdba1da19fab9d50c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 9 Apr 2026 14:00:08 +0200 Subject: [PATCH 51/53] fix: reject empty value for comparison and matches operators Empty values produced expressions like ${{ flow.name == "" }} which are technically valid but likely unintended. The exists operator correctly allows empty values since it takes no argument. --- internal/policywizard/forms.go | 8 ++++++++ internal/policywizard/forms_test.go | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index fb32acdd2..f89fd7bb7 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -383,12 +383,20 @@ func (m *Model) applyFormValues(fv formValues) { case "exists": m.storeSubExpr(policy.ExistsExpr(m.exprContext)) case "matches": + if fv.str == "" { + m.validationErr = "regex pattern is required for matches" + return + } if err := validateRegex(fv.str); err != nil { m.validationErr = err.Error() return } m.storeSubExpr(policy.MatchesExpr(m.exprContext, fv.str)) default: + if fv.str == "" { + m.validationErr = "value is required" + return + } m.storeSubExpr(policy.ComparisonExpr(m.exprContext, fv.operator, fv.str)) } diff --git a/internal/policywizard/forms_test.go b/internal/policywizard/forms_test.go index 43a78c5f6..33308583a 100644 --- a/internal/policywizard/forms_test.go +++ b/internal/policywizard/forms_test.go @@ -454,6 +454,28 @@ func TestApply_ExprCustomOp_SwitchOperatorClearsError(t *testing.T) { require.Len(t, m.pendingExprs, 1) } +func TestApply_ExprCustomOp_EmptyValueRejected(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprContext = "flow.name" + + m.applyFormValues(formValues{operator: "==", str: ""}) + + assert.Empty(t, m.pendingExprs) + assert.Contains(t, m.validationErr, "value is required") +} + +func TestApply_ExprCustomOp_MatchesEmptyRegexRejected(t *testing.T) { + m := newTestModel() + m.step = stepExprCustomOp + m.exprContext = "flow.name" + + m.applyFormValues(formValues{operator: "matches", str: ""}) + + assert.Empty(t, m.pendingExprs) + assert.Contains(t, m.validationErr, "regex pattern is required") +} + func TestApply_ExprCustomOp_ExistsStoresPending(t *testing.T) { m := newTestModel() m.step = stepExprCustomOp From aaa321fac3945ce946742cdb1f8a8f62f6c5953b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 16 Apr 2026 11:23:09 -0400 Subject: [PATCH 52/53] fix: check file existence during wizard instead of only after Move the os.Stat existence check into validateYAMLExtension so users get immediate feedback at stepSaveFile time, avoiding lost work if the target file already exists. --- internal/policywizard/forms.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/policywizard/forms.go b/internal/policywizard/forms.go index f89fd7bb7..fbaaf23bd 100644 --- a/internal/policywizard/forms.go +++ b/internal/policywizard/forms.go @@ -2,6 +2,7 @@ package policywizard import ( "fmt" + "os" "path/filepath" "regexp" "slices" @@ -264,6 +265,9 @@ func validateYAMLExtension(s string) error { if ext != ".yaml" && ext != ".yml" { return fmt.Errorf("file must have a .yaml or .yml extension") } + if _, err := os.Stat(s); err == nil { + return fmt.Errorf("file %q already exists; choose a different name", s) + } return nil } From 9a48e7d0289375740031a7c9ac55e507d817a0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dan=20Gr=C3=B8ndahl?= Date: Thu, 23 Apr 2026 10:44:24 +0200 Subject: [PATCH 53/53] fix: update policy schema URL to docs.kosli.com --- internal/policy/policy.go | 2 +- internal/policy/policy_test.go | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/policy/policy.go b/internal/policy/policy.go index 042bcb97c..ab7cf277b 100644 --- a/internal/policy/policy.go +++ b/internal/policy/policy.go @@ -2,7 +2,7 @@ package policy import "gopkg.in/yaml.v3" -const SchemaURL = "https://kosli.mintlify.app/schemas/policy/v1" +const SchemaURL = "https://docs.kosli.com/schemas/policy/v1" type Policy struct { Schema string `yaml:"_schema"` diff --git a/internal/policy/policy_test.go b/internal/policy/policy_test.go index c2f6e04ff..e71680350 100644 --- a/internal/policy/policy_test.go +++ b/internal/policy/policy_test.go @@ -12,7 +12,7 @@ func TestToYAML_EmptyPolicy(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := "_schema: https://kosli.mintlify.app/schemas/policy/v1\n" + expected := "_schema: https://docs.kosli.com/schemas/policy/v1\n" assert.Equal(t, expected, string(out)) } @@ -24,7 +24,7 @@ func TestToYAML_ProvenanceRequired(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: provenance: required: true @@ -45,7 +45,7 @@ func TestToYAML_ProvenanceWithExceptions(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: provenance: required: true @@ -68,7 +68,7 @@ func TestToYAML_TrailComplianceWithExceptions(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: trail-compliance: required: true @@ -88,7 +88,7 @@ func TestToYAML_SingleAttestation(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: attestations: - type: snyk @@ -111,7 +111,7 @@ func TestToYAML_AttestationWithNameAndIf(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: attestations: - type: pull_request @@ -133,7 +133,7 @@ func TestToYAML_MultipleAttestations(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: attestations: - type: snyk @@ -171,7 +171,7 @@ func TestToYAML_FullPolicy(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: provenance: required: true @@ -202,7 +202,7 @@ func TestToYAML_WildcardNameExplicit(t *testing.T) { require.NoError(t, err) // name: "*" should always be explicit in output - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: attestations: - type: snyk @@ -222,7 +222,7 @@ func TestToYAML_WildcardTypeRequiresNonWildcardName(t *testing.T) { out, err := p.ToYAML() require.NoError(t, err) - expected := `_schema: https://kosli.mintlify.app/schemas/policy/v1 + expected := `_schema: https://docs.kosli.com/schemas/policy/v1 artifacts: attestations: - type: '*'