From 291a4042c2b4ede8cb9af757b5ec7df5b33cce2c Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:11:00 +0100 Subject: [PATCH 01/41] refactor: extract LambdaAPI interface from concrete *lambda.Client MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a narrow LambdaAPI interface scoped to the two SDK methods we actually use (ListFunctions, GetFunctionConfiguration). Refactor getFilteredLambdaFuncs, getAndProcessOneLambdaFunc, and the new getLambdaPackageDataFromClient to accept the interface instead of *lambda.Client. The public GetLambdaPackageData creates the real client and delegates — command layer is untouched. This is Slice 1 of the fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 14 ++++++++++++++ internal/aws/aws.go | 20 ++++++++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/TODO.md b/TODO.md index b8fd42d6f..fa2e22e87 100644 --- a/TODO.md +++ b/TODO.md @@ -54,3 +54,17 @@ - [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 + +## Fakes & contract tests for cloud provider integrations (#758) + +- [ ] Slice 1: Define `LambdaAPI` interface and refactor signatures ← active + - [ ] Define `LambdaAPI` interface with `ListFunctions` and `GetFunctionConfiguration` + - [ ] Change `getFilteredLambdaFuncs` to accept `LambdaAPI` + - [ ] Change `getAndProcessOneLambdaFunc` to accept `LambdaAPI` + - [ ] Extract `getLambdaPackageDataFromClient(LambdaAPI, filter)` from `GetLambdaPackageData` + - [ ] All existing tests still pass +- [ ] Slice 2: Contract test suite against real AWS +- [ ] Slice 3: Build `FakeLambdaClient` that passes the contract +- [ ] Slice 4: Fake-backed unit tests for filtering and pagination +- [ ] Slice 5: Fake-backed unit tests for orchestration +- [ ] Slice 6: Trim existing integration tests diff --git a/internal/aws/aws.go b/internal/aws/aws.go index e18694011..27865d308 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -139,6 +139,13 @@ func (staticCreds *AWSStaticCreds) NewLambdaClient() (*lambda.Client, error) { return lambda.NewFromConfig(cfg), nil } +// LambdaAPI is the interface for AWS Lambda operations used by this package. +// The real *lambda.Client satisfies this implicitly. +type LambdaAPI interface { + ListFunctions(ctx context.Context, params *lambda.ListFunctionsInput, optFns ...func(*lambda.Options)) (*lambda.ListFunctionsOutput, error) + GetFunctionConfiguration(ctx context.Context, params *lambda.GetFunctionConfigurationInput, optFns ...func(*lambda.Options)) (*lambda.GetFunctionConfigurationOutput, error) +} + // NewECSClient returns a new ECS API client func (staticCreds *AWSStaticCreds) NewECSClient() (*ecs.Client, error) { cfg, err := staticCreds.NewAWSConfigFromEnvOrFlags() @@ -149,7 +156,7 @@ func (staticCreds *AWSStaticCreds) NewECSClient() (*ecs.Client, error) { } // getFilteredLambdaFuncs fetches a filtered set of lambda functions recursively (50 at a time) and returns a list of FunctionConfiguration -func getFilteredLambdaFuncs(client *lambda.Client, nextMarker *string, allFunctions *[]types.FunctionConfiguration, +func getFilteredLambdaFuncs(client LambdaAPI, nextMarker *string, allFunctions *[]types.FunctionConfiguration, filter *filters.ResourceFilterOptions) (*[]types.FunctionConfiguration, error) { params := &lambda.ListFunctionsInput{} if nextMarker != nil { @@ -187,11 +194,16 @@ func getFilteredLambdaFuncs(client *lambda.Client, nextMarker *string, allFuncti // GetLambdaPackageData returns a digest and metadata of a Lambda function package func (staticCreds *AWSStaticCreds) GetLambdaPackageData(filter *filters.ResourceFilterOptions) ([]*LambdaData, error) { - lambdaData := []*LambdaData{} client, err := staticCreds.NewLambdaClient() if err != nil { - return lambdaData, err + return []*LambdaData{}, err } + return getLambdaPackageDataFromClient(client, filter) +} + +// getLambdaPackageDataFromClient fetches Lambda function data using the provided LambdaAPI client. +func getLambdaPackageDataFromClient(client LambdaAPI, filter *filters.ResourceFilterOptions) ([]*LambdaData, error) { + lambdaData := []*LambdaData{} filteredFunctions, err := getFilteredLambdaFuncs(client, nil, &[]types.FunctionConfiguration{}, filter) if err != nil { @@ -247,7 +259,7 @@ func (staticCreds *AWSStaticCreds) GetLambdaPackageData(filter *filters.Resource } // getAndProcessOneLambdaFunc get a lambda function by its name and return a LambdaData object from it -func getAndProcessOneLambdaFunc(client *lambda.Client, functionName string) (*LambdaData, error) { +func getAndProcessOneLambdaFunc(client LambdaAPI, functionName string) (*LambdaData, error) { params := &lambda.GetFunctionConfigurationInput{ FunctionName: aws.String(functionName), } From 9a6eca076f149038500b8cf57e5d8f9ca327298c Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:12:54 +0100 Subject: [PATCH 02/41] test: add LambdaAPI contract test suite against real AWS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Shared runLambdaContractTests function exercises the behaviours we depend on: listing functions, marker-based pagination, getting function config, and error on missing function. Wired to real *lambda.Client in TestLambdaContract_RealAWS, env-gated behind AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY. This establishes the contract grounded in real AWS behaviour — the fake (next slice) must pass the same suite. Slice 2 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 15 +++--- internal/aws/lambda_contract_test.go | 80 ++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 internal/aws/lambda_contract_test.go diff --git a/TODO.md b/TODO.md index fa2e22e87..9a1ccfdd6 100644 --- a/TODO.md +++ b/TODO.md @@ -57,13 +57,14 @@ ## Fakes & contract tests for cloud provider integrations (#758) -- [ ] Slice 1: Define `LambdaAPI` interface and refactor signatures ← active - - [ ] Define `LambdaAPI` interface with `ListFunctions` and `GetFunctionConfiguration` - - [ ] Change `getFilteredLambdaFuncs` to accept `LambdaAPI` - - [ ] Change `getAndProcessOneLambdaFunc` to accept `LambdaAPI` - - [ ] Extract `getLambdaPackageDataFromClient(LambdaAPI, filter)` from `GetLambdaPackageData` - - [ ] All existing tests still pass -- [ ] Slice 2: Contract test suite against real AWS +- [x] Slice 1: Define `LambdaAPI` interface and refactor signatures +- [ ] Slice 2: Contract test suite against real AWS ← active + - [ ] Create `runLambdaContractTests(t, client LambdaAPI)` shared test function + - [ ] Test: ListFunctions returns results (non-empty) + - [ ] Test: ListFunctions pagination — marker-based, returns all functions across pages + - [ ] Test: GetFunctionConfiguration for existing function returns config + - [ ] Test: GetFunctionConfiguration for missing function returns error + - [ ] Run suite against real `*lambda.Client`, env-gated - [ ] Slice 3: Build `FakeLambdaClient` that passes the contract - [ ] Slice 4: Fake-backed unit tests for filtering and pagination - [ ] Slice 5: Fake-backed unit tests for orchestration diff --git a/internal/aws/lambda_contract_test.go b/internal/aws/lambda_contract_test.go new file mode 100644 index 000000000..322fd8ddc --- /dev/null +++ b/internal/aws/lambda_contract_test.go @@ -0,0 +1,80 @@ +package aws + +import ( + "context" + "testing" + + "github.com/aws/aws-sdk-go-v2/service/lambda" + "github.com/kosli-dev/cli/internal/testHelpers" + "github.com/stretchr/testify/require" +) + +// runLambdaContractTests exercises the LambdaAPI contract. It verifies the +// behaviours we depend on — pagination, function config retrieval, and error +// responses for missing functions. +// +// Any implementation that passes this suite is a valid stand-in for the real +// AWS Lambda API as far as this codebase is concerned. +// +// existingFunctionName must name a function that the client can see. +func runLambdaContractTests(t *testing.T, client LambdaAPI, existingFunctionName string) { + t.Helper() + + t.Run("ListFunctions returns without error", func(t *testing.T) { + out, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{}) + require.NoError(t, err) + require.NotNil(t, out) + }) + + t.Run("ListFunctions with MaxItems paginates via Marker", func(t *testing.T) { + // Request one function per page to force pagination + maxItems := int32(1) + out, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ + MaxItems: &maxItems, + }) + require.NoError(t, err) + require.NotNil(t, out) + require.LessOrEqual(t, len(out.Functions), 1) + + if out.NextMarker != nil { + // Follow the marker to prove pagination works + out2, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ + MaxItems: &maxItems, + Marker: out.NextMarker, + }) + require.NoError(t, err) + require.NotNil(t, out2) + require.LessOrEqual(t, len(out2.Functions), 1) + } + }) + + t.Run("GetFunctionConfiguration returns config for existing function", func(t *testing.T) { + out, err := client.GetFunctionConfiguration(context.TODO(), &lambda.GetFunctionConfigurationInput{ + FunctionName: &existingFunctionName, + }) + require.NoError(t, err) + require.NotNil(t, out) + require.NotNil(t, out.FunctionName) + require.Equal(t, existingFunctionName, *out.FunctionName) + require.NotNil(t, out.CodeSha256, "CodeSha256 should be present") + require.NotNil(t, out.LastModified, "LastModified should be present") + }) + + t.Run("GetFunctionConfiguration errors for missing function", func(t *testing.T) { + missingName := "nonexistent-function-that-should-not-exist-" + t.Name() + _, err := client.GetFunctionConfiguration(context.TODO(), &lambda.GetFunctionConfigurationInput{ + FunctionName: &missingName, + }) + require.Error(t, err) + }) +} + +func TestLambdaContract_RealAWS(t *testing.T) { + testHelpers.SkipIfEnvVarUnset(t, []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"}) + + creds := &AWSStaticCreds{Region: "eu-central-1"} + client, err := creds.NewLambdaClient() + require.NoError(t, err) + + runLambdaContractTests(t, client, "cli-tests") +} From cd4e54a7520939cf95159946c8cfc00e174b2885 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:15:01 +0100 Subject: [PATCH 03/41] feat: add FakeLambdaClient that passes the LambdaAPI contract FakeLambdaClient is an in-memory implementation of LambdaAPI with marker-based pagination and error responses for missing functions. It passes the same runLambdaContractTests suite that validates the real *lambda.Client, proving it is a trustworthy stand-in. Slice 3 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 14 +++--- internal/aws/fake_lambda.go | 74 ++++++++++++++++++++++++++++ internal/aws/lambda_contract_test.go | 51 ++++++++++++++----- 3 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 internal/aws/fake_lambda.go diff --git a/TODO.md b/TODO.md index 9a1ccfdd6..cee90c128 100644 --- a/TODO.md +++ b/TODO.md @@ -58,14 +58,12 @@ ## Fakes & contract tests for cloud provider integrations (#758) - [x] Slice 1: Define `LambdaAPI` interface and refactor signatures -- [ ] Slice 2: Contract test suite against real AWS ← active - - [ ] Create `runLambdaContractTests(t, client LambdaAPI)` shared test function - - [ ] Test: ListFunctions returns results (non-empty) - - [ ] Test: ListFunctions pagination — marker-based, returns all functions across pages - - [ ] Test: GetFunctionConfiguration for existing function returns config - - [ ] Test: GetFunctionConfiguration for missing function returns error - - [ ] Run suite against real `*lambda.Client`, env-gated -- [ ] Slice 3: Build `FakeLambdaClient` that passes the contract +- [x] Slice 2: Contract test suite against real AWS +- [ ] Slice 3: Build `FakeLambdaClient` that passes the contract ← active + - [ ] Create `FakeLambdaClient` struct with in-memory function list + - [ ] Implement `ListFunctions` with marker-based pagination + - [ ] Implement `GetFunctionConfiguration` with error for missing functions + - [ ] Pass `runLambdaContractTests` against the fake - [ ] Slice 4: Fake-backed unit tests for filtering and pagination - [ ] Slice 5: Fake-backed unit tests for orchestration - [ ] Slice 6: Trim existing integration tests diff --git a/internal/aws/fake_lambda.go b/internal/aws/fake_lambda.go new file mode 100644 index 000000000..4372496bb --- /dev/null +++ b/internal/aws/fake_lambda.go @@ -0,0 +1,74 @@ +package aws + +import ( + "context" + "fmt" + "strconv" + + "github.com/aws/aws-sdk-go-v2/service/lambda" + "github.com/aws/aws-sdk-go-v2/service/lambda/types" +) + +// FakeLambdaClient is an in-memory implementation of LambdaAPI for testing. +// It simulates marker-based pagination and returns errors for missing functions. +type FakeLambdaClient struct { + Functions []types.FunctionConfiguration + // PageSize controls how many functions are returned per ListFunctions call. + // Defaults to 50 (matching the AWS default) if zero. + PageSize int +} + +func (f *FakeLambdaClient) pageSize() int { + if f.PageSize > 0 { + return f.PageSize + } + return 50 +} + +func (f *FakeLambdaClient) ListFunctions(_ context.Context, params *lambda.ListFunctionsInput, _ ...func(*lambda.Options)) (*lambda.ListFunctionsOutput, error) { + pageSize := f.pageSize() + if params.MaxItems != nil && int(*params.MaxItems) < pageSize { + pageSize = int(*params.MaxItems) + } + + start := 0 + if params.Marker != nil { + parsed, err := strconv.Atoi(*params.Marker) + if err != nil { + return nil, fmt.Errorf("invalid marker: %s", *params.Marker) + } + start = parsed + } + + end := start + pageSize + if end > len(f.Functions) { + end = len(f.Functions) + } + + out := &lambda.ListFunctionsOutput{ + Functions: f.Functions[start:end], + } + if end < len(f.Functions) { + marker := strconv.Itoa(end) + out.NextMarker = &marker + } + + return out, nil +} + +func (f *FakeLambdaClient) GetFunctionConfiguration(_ context.Context, params *lambda.GetFunctionConfigurationInput, _ ...func(*lambda.Options)) (*lambda.GetFunctionConfigurationOutput, error) { + if params.FunctionName == nil { + return nil, fmt.Errorf("FunctionName is required") + } + for _, fn := range f.Functions { + if fn.FunctionName != nil && *fn.FunctionName == *params.FunctionName { + return &lambda.GetFunctionConfigurationOutput{ + FunctionName: fn.FunctionName, + CodeSha256: fn.CodeSha256, + LastModified: fn.LastModified, + PackageType: fn.PackageType, + }, nil + } + } + return nil, fmt.Errorf("function not found: %s", *params.FunctionName) +} diff --git a/internal/aws/lambda_contract_test.go b/internal/aws/lambda_contract_test.go index 322fd8ddc..21b6d82ca 100644 --- a/internal/aws/lambda_contract_test.go +++ b/internal/aws/lambda_contract_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/aws/aws-sdk-go-v2/service/lambda" + "github.com/aws/aws-sdk-go-v2/service/lambda/types" "github.com/kosli-dev/cli/internal/testHelpers" "github.com/stretchr/testify/require" ) @@ -27,7 +28,10 @@ func runLambdaContractTests(t *testing.T, client LambdaAPI, existingFunctionName }) t.Run("ListFunctions with MaxItems paginates via Marker", func(t *testing.T) { - // Request one function per page to force pagination + // Request one function per page to force pagination. + // The test setup must seed at least 2 functions for this to exercise + // the marker path — if there's only one, NextMarker will be nil and + // the test degrades to a no-op. maxItems := int32(1) out, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ MaxItems: &maxItems, @@ -36,16 +40,16 @@ func runLambdaContractTests(t *testing.T, client LambdaAPI, existingFunctionName require.NotNil(t, out) require.LessOrEqual(t, len(out.Functions), 1) - if out.NextMarker != nil { - // Follow the marker to prove pagination works - out2, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ - MaxItems: &maxItems, - Marker: out.NextMarker, - }) - require.NoError(t, err) - require.NotNil(t, out2) - require.LessOrEqual(t, len(out2.Functions), 1) - } + // Follow the marker to prove pagination works + require.NotNil(t, out.NextMarker, "expected NextMarker when more functions exist") + out2, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ + MaxItems: &maxItems, + Marker: out.NextMarker, + }) + require.NoError(t, err) + require.NotNil(t, out2) + require.LessOrEqual(t, len(out2.Functions), 1) + require.NotEmpty(t, out2.Functions, "second page should return at least one function") }) t.Run("GetFunctionConfiguration returns config for existing function", func(t *testing.T) { @@ -69,6 +73,31 @@ func runLambdaContractTests(t *testing.T, client LambdaAPI, existingFunctionName }) } +func TestLambdaContract_Fake(t *testing.T) { + fnName1 := "my-function" + fnName2 := "other-function" + lastModified := "2024-01-15T10:30:00.000+0000" + codeSha256 := "abc123" + client := &FakeLambdaClient{ + Functions: []types.FunctionConfiguration{ + { + FunctionName: &fnName1, + CodeSha256: &codeSha256, + LastModified: &lastModified, + PackageType: types.PackageTypeZip, + }, + { + FunctionName: &fnName2, + CodeSha256: &codeSha256, + LastModified: &lastModified, + PackageType: types.PackageTypeZip, + }, + }, + PageSize: 1, + } + runLambdaContractTests(t, client, fnName1) +} + func TestLambdaContract_RealAWS(t *testing.T) { testHelpers.SkipIfEnvVarUnset(t, []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"}) From 96e6043d0a03c9994bbf9f2587c85755d97aa238 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:16:08 +0100 Subject: [PATCH 04/41] test: add fake-backed unit tests for Lambda filtering and pagination Tests getFilteredLambdaFuncs with the FakeLambdaClient covering: IncludeNames, IncludeNamesRegex, ExcludeNames, ExcludeNamesRegex, combined exclude filters, multi-page pagination with filtering, empty function lists, and invalid regex error handling. These tests run without AWS credentials and complete in milliseconds. Slice 4 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 16 +++-- internal/aws/aws_test.go | 123 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index cee90c128..97f4ebedb 100644 --- a/TODO.md +++ b/TODO.md @@ -59,11 +59,15 @@ - [x] Slice 1: Define `LambdaAPI` interface and refactor signatures - [x] Slice 2: Contract test suite against real AWS -- [ ] Slice 3: Build `FakeLambdaClient` that passes the contract ← active - - [ ] Create `FakeLambdaClient` struct with in-memory function list - - [ ] Implement `ListFunctions` with marker-based pagination - - [ ] Implement `GetFunctionConfiguration` with error for missing functions - - [ ] Pass `runLambdaContractTests` against the fake -- [ ] Slice 4: Fake-backed unit tests for filtering and pagination +- [x] Slice 3: Build `FakeLambdaClient` that passes the contract +- [ ] Slice 4: Fake-backed unit tests for filtering and pagination ← active + - [ ] Test: no filter returns all functions + - [ ] Test: IncludeNames filter + - [ ] Test: IncludeNamesRegex filter + - [ ] Test: ExcludeNames filter + - [ ] Test: ExcludeNamesRegex filter + - [ ] Test: combined exclude + exclude-regex + - [ ] Test: multi-page results with filtering + - [ ] Test: empty function list returns empty result - [ ] Slice 5: Fake-backed unit tests for orchestration - [ ] Slice 6: Trim existing integration tests diff --git a/internal/aws/aws_test.go b/internal/aws/aws_test.go index 210298ab7..cdb438533 100644 --- a/internal/aws/aws_test.go +++ b/internal/aws/aws_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/aws/aws-sdk-go-v2/service/lambda/types" "github.com/kosli-dev/cli/internal/filters" "github.com/kosli-dev/cli/internal/logger" "github.com/kosli-dev/cli/internal/testHelpers" @@ -668,6 +669,128 @@ func (suite *AWSTestSuite) TestGetEcsTasksData() { } } +// helper to build a FakeLambdaClient with named functions for testing +func fakeLambdaClientWithFunctions(names ...string) *FakeLambdaClient { + fns := make([]types.FunctionConfiguration, len(names)) + lastModified := "2024-01-15T10:30:00.000+0000" + codeSha256 := "abc123" + for i, name := range names { + n := name + fns[i] = types.FunctionConfiguration{ + FunctionName: &n, + CodeSha256: &codeSha256, + LastModified: &lastModified, + PackageType: types.PackageTypeZip, + } + } + return &FakeLambdaClient{Functions: fns} +} + +func functionNames(result *[]types.FunctionConfiguration) []string { + names := make([]string, len(*result)) + for i, f := range *result { + names[i] = *f.FunctionName + } + return names +} + +func (suite *AWSTestSuite) TestGetFilteredLambdaFuncs() { + for _, t := range []struct { + name string + functions []string + filter *filters.ResourceFilterOptions + pageSize int + expectedNames []string + wantErr bool + }{ + { + name: "no filter returns all functions", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{}, + expectedNames: []string{"alpha", "beta", "gamma"}, + }, + { + name: "empty function list returns empty result", + functions: []string{}, + filter: &filters.ResourceFilterOptions{}, + expectedNames: []string{}, + }, + { + name: "IncludeNames filters to matching functions", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{IncludeNames: []string{"beta"}}, + expectedNames: []string{"beta"}, + }, + { + name: "IncludeNames with multiple names", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{IncludeNames: []string{"alpha", "gamma"}}, + expectedNames: []string{"alpha", "gamma"}, + }, + { + name: "IncludeNamesRegex filters by pattern", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{IncludeNamesRegex: []string{"^a.*"}}, + expectedNames: []string{"alpha"}, + }, + { + name: "ExcludeNames removes matching functions", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{ExcludeNames: []string{"beta"}}, + expectedNames: []string{"alpha", "gamma"}, + }, + { + name: "ExcludeNamesRegex removes matching pattern", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{ExcludeNamesRegex: []string{"^[bg].*"}}, + expectedNames: []string{"alpha"}, + }, + { + name: "combined ExcludeNames and ExcludeNamesRegex", + functions: []string{"alpha", "beta", "gamma", "delta"}, + filter: &filters.ResourceFilterOptions{ + ExcludeNames: []string{"alpha"}, + ExcludeNamesRegex: []string{"^d.*"}, + }, + expectedNames: []string{"beta", "gamma"}, + }, + { + name: "multi-page results with filtering across pages", + functions: []string{"alpha", "beta", "gamma", "delta"}, + filter: &filters.ResourceFilterOptions{IncludeNamesRegex: []string{"^[ag].*"}}, + pageSize: 2, + expectedNames: []string{"alpha", "gamma"}, + }, + { + name: "multi-page results without filtering", + functions: []string{"alpha", "beta", "gamma"}, + filter: &filters.ResourceFilterOptions{}, + pageSize: 1, + expectedNames: []string{"alpha", "beta", "gamma"}, + }, + { + name: "invalid regex causes an error", + functions: []string{"alpha"}, + filter: &filters.ResourceFilterOptions{IncludeNamesRegex: []string{"invalid["}}, + wantErr: true, + }, + } { + suite.Run(t.name, func() { + client := fakeLambdaClientWithFunctions(t.functions...) + if t.pageSize > 0 { + client.PageSize = t.pageSize + } + result, err := getFilteredLambdaFuncs(client, nil, &[]types.FunctionConfiguration{}, t.filter) + if t.wantErr { + require.Error(suite.T(), err) + return + } + require.NoError(suite.T(), err) + require.ElementsMatch(suite.T(), t.expectedNames, functionNames(result)) + }) + } +} + func skipOrSetCreds(T *testing.T, requireEnvVars bool, creds *AWSStaticCreds) { if requireEnvVars { // skips the test case if it requires env vars and they are not set From 1f1784d7ac6c20cf6c63204bca37999e54aef857 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:17:45 +0100 Subject: [PATCH 05/41] test: add fake-backed unit tests for Lambda orchestration Tests getLambdaPackageDataFromClient with the FakeLambdaClient covering: Zip fingerprint decoding, Image raw CodeSha256, concurrent multi-function processing, empty function list, and error propagation from GetFunctionConfiguration. Also adds GetFunctionConfigurationErr field to FakeLambdaClient for error injection in tests. Slice 5 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 15 +++--- internal/aws/aws_test.go | 97 +++++++++++++++++++++++++++++++++++++ internal/aws/fake_lambda.go | 6 +++ 3 files changed, 109 insertions(+), 9 deletions(-) diff --git a/TODO.md b/TODO.md index 97f4ebedb..896457cad 100644 --- a/TODO.md +++ b/TODO.md @@ -60,14 +60,11 @@ - [x] Slice 1: Define `LambdaAPI` interface and refactor signatures - [x] Slice 2: Contract test suite against real AWS - [x] Slice 3: Build `FakeLambdaClient` that passes the contract -- [ ] Slice 4: Fake-backed unit tests for filtering and pagination ← active - - [ ] Test: no filter returns all functions - - [ ] Test: IncludeNames filter - - [ ] Test: IncludeNamesRegex filter - - [ ] Test: ExcludeNames filter - - [ ] Test: ExcludeNamesRegex filter - - [ ] Test: combined exclude + exclude-regex - - [ ] Test: multi-page results with filtering +- [x] Slice 4: Fake-backed unit tests for filtering and pagination +- [ ] Slice 5: Fake-backed unit tests for orchestration ← active + - [ ] Test: single Zip function — returns decoded fingerprint + - [ ] Test: single Image function — returns raw CodeSha256 + - [ ] Test: multiple functions processed concurrently + - [ ] Test: error in GetFunctionConfiguration propagates - [ ] Test: empty function list returns empty result -- [ ] Slice 5: Fake-backed unit tests for orchestration - [ ] Slice 6: Trim existing integration tests diff --git a/internal/aws/aws_test.go b/internal/aws/aws_test.go index cdb438533..020997be0 100644 --- a/internal/aws/aws_test.go +++ b/internal/aws/aws_test.go @@ -2,6 +2,7 @@ package aws import ( "context" + "fmt" "testing" "time" @@ -791,6 +792,102 @@ func (suite *AWSTestSuite) TestGetFilteredLambdaFuncs() { } } +func (suite *AWSTestSuite) TestGetLambdaPackageDataFromClient() { + // base64-encoded SHA256 that decodes to a known hex fingerprint + zipCodeSha256 := "Mh48OOkSYuXHLfS9QF6bF3tvTXUOGvC3jKLiuF1vkbQ=" + expectedZipFingerprint := "321e3c38e91262e5c72df4bd405e9b177b6f4d750e1af0b78ca2e2b85d6f91b4" + // Image package types use the raw CodeSha256 (not base64-decoded) + imageCodeSha256 := "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e" + lastModified := "2024-01-15T10:30:00.000+0000" + + for _, t := range []struct { + name string + client *FakeLambdaClient + filter *filters.ResourceFilterOptions + expectedDigests map[string]string // functionName -> fingerprint + wantErr bool + wantErrMsgSubstring string + }{ + { + name: "single Zip function returns decoded fingerprint", + client: func() *FakeLambdaClient { + fnName := "zip-func" + return &FakeLambdaClient{Functions: []types.FunctionConfiguration{ + {FunctionName: &fnName, CodeSha256: &zipCodeSha256, LastModified: &lastModified, PackageType: types.PackageTypeZip}, + }} + }(), + filter: &filters.ResourceFilterOptions{}, + expectedDigests: map[string]string{"zip-func": expectedZipFingerprint}, + }, + { + name: "single Image function returns raw CodeSha256", + client: func() *FakeLambdaClient { + fnName := "image-func" + return &FakeLambdaClient{Functions: []types.FunctionConfiguration{ + {FunctionName: &fnName, CodeSha256: &imageCodeSha256, LastModified: &lastModified, PackageType: types.PackageTypeImage}, + }} + }(), + filter: &filters.ResourceFilterOptions{}, + expectedDigests: map[string]string{"image-func": imageCodeSha256}, + }, + { + name: "multiple functions processed concurrently", + client: func() *FakeLambdaClient { + fn1 := "zip-func" + fn2 := "image-func" + return &FakeLambdaClient{Functions: []types.FunctionConfiguration{ + {FunctionName: &fn1, CodeSha256: &zipCodeSha256, LastModified: &lastModified, PackageType: types.PackageTypeZip}, + {FunctionName: &fn2, CodeSha256: &imageCodeSha256, LastModified: &lastModified, PackageType: types.PackageTypeImage}, + }} + }(), + filter: &filters.ResourceFilterOptions{}, + expectedDigests: map[string]string{ + "zip-func": expectedZipFingerprint, + "image-func": imageCodeSha256, + }, + }, + { + name: "empty function list returns empty result", + client: &FakeLambdaClient{ + Functions: []types.FunctionConfiguration{}, + }, + filter: &filters.ResourceFilterOptions{}, + expectedDigests: map[string]string{}, + }, + { + name: "GetFunctionConfiguration error propagates", + client: func() *FakeLambdaClient { + fnName := "will-fail" + return &FakeLambdaClient{ + Functions: []types.FunctionConfiguration{ + {FunctionName: &fnName, CodeSha256: &zipCodeSha256, LastModified: &lastModified, PackageType: types.PackageTypeZip}, + }, + GetFunctionConfigurationErr: fmt.Errorf("simulated AWS error"), + } + }(), + filter: &filters.ResourceFilterOptions{}, + wantErr: true, + }, + } { + suite.Run(t.name, func() { + data, err := getLambdaPackageDataFromClient(t.client, t.filter) + if t.wantErr { + require.Error(suite.T(), err) + return + } + require.NoError(suite.T(), err) + + gotDigests := map[string]string{} + for _, d := range data { + for name, fp := range d.Digests { + gotDigests[name] = fp + } + } + require.Equal(suite.T(), t.expectedDigests, gotDigests) + }) + } +} + func skipOrSetCreds(T *testing.T, requireEnvVars bool, creds *AWSStaticCreds) { if requireEnvVars { // skips the test case if it requires env vars and they are not set diff --git a/internal/aws/fake_lambda.go b/internal/aws/fake_lambda.go index 4372496bb..86c5ea7a3 100644 --- a/internal/aws/fake_lambda.go +++ b/internal/aws/fake_lambda.go @@ -16,6 +16,9 @@ type FakeLambdaClient struct { // PageSize controls how many functions are returned per ListFunctions call. // Defaults to 50 (matching the AWS default) if zero. PageSize int + // GetFunctionConfigurationErr, if set, is returned by GetFunctionConfiguration + // for any function. Useful for testing error propagation. + GetFunctionConfigurationErr error } func (f *FakeLambdaClient) pageSize() int { @@ -60,6 +63,9 @@ func (f *FakeLambdaClient) GetFunctionConfiguration(_ context.Context, params *l if params.FunctionName == nil { return nil, fmt.Errorf("FunctionName is required") } + if f.GetFunctionConfigurationErr != nil { + return nil, f.GetFunctionConfigurationErr + } for _, fn := range f.Functions { if fn.FunctionName != nil && *fn.FunctionName == *params.FunctionName { return &lambda.GetFunctionConfigurationOutput{ From 93b13dc993c9b244df4d5f5c1d48a85cee045fd8 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:19:03 +0100 Subject: [PATCH 06/41] test: trim Lambda integration tests to focused smoke tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove filtering-focused integration test cases (IncludeNamesRegex, ExcludeNames, ExcludeNamesRegex, combined filters, wrong region, invalid regex) — these are now covered by fake-backed unit tests in TestGetFilteredLambdaFuncs and TestGetLambdaPackageDataFromClient. Keep three smoke tests: invalid credentials error, one Zip function happy path, and one Image function happy path. These prove real AWS SDK wiring works without duplicating logic tests. Reduces Lambda integration tests from 8 cases to 3. Slice 6 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 11 ++--- internal/aws/aws_test.go | 100 +++++---------------------------------- 2 files changed, 17 insertions(+), 94 deletions(-) diff --git a/TODO.md b/TODO.md index 896457cad..0cd30f4fe 100644 --- a/TODO.md +++ b/TODO.md @@ -61,10 +61,7 @@ - [x] Slice 2: Contract test suite against real AWS - [x] Slice 3: Build `FakeLambdaClient` that passes the contract - [x] Slice 4: Fake-backed unit tests for filtering and pagination -- [ ] Slice 5: Fake-backed unit tests for orchestration ← active - - [ ] Test: single Zip function — returns decoded fingerprint - - [ ] Test: single Image function — returns raw CodeSha256 - - [ ] Test: multiple functions processed concurrently - - [ ] Test: error in GetFunctionConfiguration propagates - - [ ] Test: empty function list returns empty result -- [ ] Slice 6: Trim existing integration tests +- [x] Slice 5: Fake-backed unit tests for orchestration +- [ ] Slice 6: Trim existing integration tests ← active + - [ ] Remove filtering-focused integration tests (now covered by fakes) + - [ ] Keep: invalid credentials error, one Zip happy path, one Image happy path diff --git a/internal/aws/aws_test.go b/internal/aws/aws_test.go index 020997be0..130b943af 100644 --- a/internal/aws/aws_test.go +++ b/internal/aws/aws_test.go @@ -251,6 +251,10 @@ func (suite *AWSTestSuite) TestAWSClients() { // they are skipped // All cases will run in CI +// TestGetLambdaPackageData is a smoke test for the real AWS integration. +// Filtering logic and fingerprint processing are covered by fake-backed tests +// (TestGetFilteredLambdaFuncs, TestGetLambdaPackageDataFromClient). +// These cases prove the real SDK wiring works. func (suite *AWSTestSuite) TestGetLambdaPackageData() { type expectedFunction struct { name string @@ -258,7 +262,7 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { } for _, t := range []struct { name string - requireEnvVars bool // indicates that a test case needs real credentials from env vars + requireEnvVars bool creds *AWSStaticCreds filter *filters.ResourceFilterOptions expectedFunctions []expectedFunction @@ -275,16 +279,7 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { wantErr: true, }, { - name: "providing the wrong region gives an empty result", - creds: &AWSStaticCreds{ - Region: "ap-south-1", - }, - filter: &filters.ResourceFilterOptions{IncludeNames: []string{"cli-tests"}}, - requireEnvVars: true, - expectedFunctions: []expectedFunction{}, - }, - { - name: "can get zip package lambda function data from name", + name: "can get zip package lambda function data", creds: &AWSStaticCreds{ Region: "eu-central-1", }, @@ -294,7 +289,7 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { requireEnvVars: true, }, { - name: "can get image package lambda function data from name", + name: "can get image package lambda function data", creds: &AWSStaticCreds{ Region: "eu-central-1", }, @@ -303,67 +298,6 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { fingerprint: "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e"}}, requireEnvVars: true, }, - { - name: "can get a list of lambda functions data from names", - creds: &AWSStaticCreds{ - Region: "eu-central-1", - }, - filter: &filters.ResourceFilterOptions{IncludeNames: []string{"cli-tests-docker", "cli-tests"}}, - expectedFunctions: []expectedFunction{ - {name: "cli-tests", - fingerprint: "321e3c38e91262e5c72df4bd405e9b177b6f4d750e1af0b78ca2e2b85d6f91b4"}, - {name: "cli-tests-docker", - fingerprint: "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e"}}, - requireEnvVars: true, - }, - { - name: "can get a list of lambda functions data from names regex", - creds: &AWSStaticCreds{ - Region: "eu-central-1", - }, - filter: &filters.ResourceFilterOptions{IncludeNamesRegex: []string{"^cli-test.*"}}, - expectedFunctions: []expectedFunction{ - {name: "cli-tests", - fingerprint: "321e3c38e91262e5c72df4bd405e9b177b6f4d750e1af0b78ca2e2b85d6f91b4"}, - {name: "cli-tests-docker", - fingerprint: "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e"}}, - requireEnvVars: true, - }, - { - name: "can exclude lambda functions matching a regex pattern", - creds: &AWSStaticCreds{ - Region: "eu-central-1", - }, - filter: &filters.ResourceFilterOptions{ExcludeNamesRegex: []string{"^([^c]|c[^l]|cl[^i]|cli[^-]).*$"}}, - expectedFunctions: []expectedFunction{ - {name: "cli-tests", - fingerprint: "321e3c38e91262e5c72df4bd405e9b177b6f4d750e1af0b78ca2e2b85d6f91b4"}, - {name: "cli-tests-docker", - fingerprint: "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e"}}, - requireEnvVars: true, - }, - { - name: "invalid exclude name regex pattern causes an error", - creds: &AWSStaticCreds{ - Region: "eu-central-1", - }, - filter: &filters.ResourceFilterOptions{ExcludeNamesRegex: []string{"invalid["}}, - requireEnvVars: true, - wantErr: true, - }, - { - name: "can combine exclude and exclude-regex and they are joined", - creds: &AWSStaticCreds{ - Region: "eu-central-1", - }, - filter: &filters.ResourceFilterOptions{ - ExcludeNames: []string{"cli-tests"}, - ExcludeNamesRegex: []string{"^([^c]|c[^l]|cl[^i]|cli[^-]).*$"}}, - expectedFunctions: []expectedFunction{ - {name: "cli-tests-docker", - fingerprint: "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e"}}, - requireEnvVars: true, - }, } { suite.Run(t.name, func() { skipOrSetCreds(suite.T(), t.requireEnvVars, t.creds) @@ -371,25 +305,17 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { require.False(suite.T(), (err != nil) != t.wantErr, "GetLambdaPackageData() error = %v, wantErr %v", err, t.wantErr) if !t.wantErr { - matchFound := false require.Len(suite.T(), data, len(t.expectedFunctions)) - if len(t.expectedFunctions) == 0 { - matchFound = true - } - loop1: - for _, expectedFunction := range t.expectedFunctions { + for _, expected := range t.expectedFunctions { + found := false for _, item := range data { - if fingerprint, ok := item.Digests[expectedFunction.name]; ok { - if expectedFunction.fingerprint == fingerprint { - matchFound = true - break loop1 - } else { - suite.T().Logf("fingerprint did not match: GOT %s -- WANT %s", fingerprint, expectedFunction.fingerprint) - } + if fingerprint, ok := item.Digests[expected.name]; ok && fingerprint == expected.fingerprint { + found = true + break } } + require.True(suite.T(), found, "expected function %s with fingerprint %s not found", expected.name, expected.fingerprint) } - require.True(suite.T(), matchFound) } }) } From d07be837a772d0011ec45229784111ee1dbd657f Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:19:18 +0100 Subject: [PATCH 07/41] chore: mark all Lambda fakes & contract slices complete in TODO Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 0cd30f4fe..ffa4919dd 100644 --- a/TODO.md +++ b/TODO.md @@ -62,6 +62,4 @@ - [x] Slice 3: Build `FakeLambdaClient` that passes the contract - [x] Slice 4: Fake-backed unit tests for filtering and pagination - [x] Slice 5: Fake-backed unit tests for orchestration -- [ ] Slice 6: Trim existing integration tests ← active - - [ ] Remove filtering-focused integration tests (now covered by fakes) - - [ ] Keep: invalid credentials error, one Zip happy path, one Image happy path +- [x] Slice 6: Trim existing integration tests From 7eac7cd0e9620674d228e5530824280b642b48b5 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:34:46 +0100 Subject: [PATCH 08/41] feat: inject FakeLambdaClient into command tests via factory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add NewLambdaClientFunc package-level factory to internal/aws. GetLambdaPackageData uses the factory instead of creating a client directly. Tests replace the factory to inject a FakeLambdaClient. snapshotLambda_test.go now injects the fake in SetupTest and resets in TearDownTest. All test cases run without AWS credentials — the requireAuthToBeSet/SkipIfEnvVarUnset pattern is removed entirely. Also adds make test_smoke_aws target for running contract and smoke tests against real AWS before release. Slice 7 of fakes & contract tests work (#758). Co-Authored-By: Claude Opus 4.6 (1M context) --- Makefile | 5 ++ TODO.md | 6 ++ cmd/kosli/snapshotLambda_test.go | 95 ++++++++++++++++---------------- internal/aws/aws.go | 16 +++++- 4 files changed, 73 insertions(+), 49 deletions(-) diff --git a/Makefile b/Makefile index f5be6dd61..b6e7d8132 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,11 @@ test_integration_restart_server: test_setup_restart_server test_integration_single: test_setup @export KOSLI_TESTS=true $(FAKE_CI_ENV) && $(GOTESTSUM) -- -p=1 ./... -run "${TARGET}" +test_smoke_aws: ## Run AWS contract and smoke tests against real AWS (requires AWS creds) + @echo "Running AWS contract and smoke tests against real AWS..." + @echo "Requires AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY to be set" + @$(GOTESTSUM) -- -v -p=1 -run "LambdaContract_RealAWS|AWSTestSuite/TestGetLambdaPackageData|AWSTestSuite/TestGetEcsTasksData|AWSTestSuite/TestGetS3Data" ./internal/aws/ + test_docs: deps vet ensure_network test_setup ## Test docs ./bin/test_docs_cmds.sh docs.kosli.com/content/use_cases/simulating_a_devops_system/_index.md diff --git a/TODO.md b/TODO.md index ffa4919dd..f4b391ded 100644 --- a/TODO.md +++ b/TODO.md @@ -63,3 +63,9 @@ - [x] Slice 4: Fake-backed unit tests for filtering and pagination - [x] Slice 5: Fake-backed unit tests for orchestration - [x] Slice 6: Trim existing integration tests +- [ ] Slice 7: Package-level factory + fake-backed command tests ← active + - [ ] Add `NewLambdaClientFunc` factory variable to `internal/aws` + - [ ] `GetLambdaPackageData` uses factory instead of direct client creation + - [ ] `snapshotLambda_test.go` injects fake in SetupTest, resets in TearDownTest + - [ ] Remove `requireAuthToBeSet` from command test cases — they always run + - [ ] Add Makefile target for AWS smoke tests diff --git a/cmd/kosli/snapshotLambda_test.go b/cmd/kosli/snapshotLambda_test.go index de3b3a65f..d469e0992 100644 --- a/cmd/kosli/snapshotLambda_test.go +++ b/cmd/kosli/snapshotLambda_test.go @@ -4,7 +4,8 @@ import ( "fmt" "testing" - "github.com/kosli-dev/cli/internal/testHelpers" + "github.com/aws/aws-sdk-go-v2/service/lambda/types" + "github.com/kosli-dev/cli/internal/aws" "github.com/stretchr/testify/suite" ) @@ -19,10 +20,6 @@ type SnapshotLambdaTestSuite struct { imageFunctionName string } -type snapshotLambdaTestConfig struct { - requireAuthToBeSet bool -} - func (suite *SnapshotLambdaTestSuite) SetupTest() { suite.envName = "snapshot-lambda-env" suite.zipFunctionName = "cli-tests" @@ -35,67 +32,75 @@ func (suite *SnapshotLambdaTestSuite) SetupTest() { suite.defaultKosliArguments = fmt.Sprintf(" --host %s --org %s --api-token %s", global.Host, global.Org, global.ApiToken) CreateEnv(global.Org, suite.envName, "lambda", suite.T()) + + // Inject fake Lambda client so tests run without AWS credentials. + // The fake is seeded with two functions matching the names used in test cases. + zipCodeSha256 := "Mh48OOkSYuXHLfS9QF6bF3tvTXUOGvC3jKLiuF1vkbQ=" + imageCodeSha256 := "e908950659e56bb886acbb0ecf9b8f38bf6e0382ede71095e166269ee4db601e" + lastModified := "2024-01-15T10:30:00.000+0000" + zipName := suite.zipFunctionName + imageName := suite.imageFunctionName + aws.NewLambdaClientFunc = func(_ *aws.AWSStaticCreds) (aws.LambdaAPI, error) { + return &aws.FakeLambdaClient{ + Functions: []types.FunctionConfiguration{ + { + FunctionName: &zipName, + CodeSha256: &zipCodeSha256, + LastModified: &lastModified, + PackageType: types.PackageTypeZip, + }, + { + FunctionName: &imageName, + CodeSha256: &imageCodeSha256, + LastModified: &lastModified, + PackageType: types.PackageTypeImage, + }, + }, + }, nil + } +} + +func (suite *SnapshotLambdaTestSuite) TearDownTest() { + aws.ResetLambdaClientFactory() } func (suite *SnapshotLambdaTestSuite) TestSnapshotLambdaCmd() { tests := []cmdTestCase{ { - name: "snapshot lambda works with deprecated --function-name for Zip package type", - cmd: fmt.Sprintf(`snapshot lambda %s %s --function-name %s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works with deprecated --function-name for Zip package type", + cmd: fmt.Sprintf(`snapshot lambda %s %s --function-name %s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), golden: fmt.Sprintf("Flag --function-name has been deprecated, use --function-names instead\n1 lambda functions were reported to environment %s\n", suite.envName), }, { - name: "snapshot lambda works with --function-names for Zip package type", - cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works with --function-names for Zip package type", + cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), golden: fmt.Sprintf("1 lambda functions were reported to environment %s\n", suite.envName), }, { - name: "snapshot lambda works with --function-names taking a list of functions", - cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s,%s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName, suite.imageFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works with --function-names taking a list of functions", + cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s,%s`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName, suite.imageFunctionName), golden: fmt.Sprintf("2 lambda functions were reported to environment %s\n", suite.envName), }, { - name: "snapshot lambda works with --function-names for Image package type", - cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s`, suite.envName, suite.defaultKosliArguments, suite.imageFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works with --function-names for Image package type", + cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s`, suite.envName, suite.defaultKosliArguments, suite.imageFunctionName), golden: fmt.Sprintf("1 lambda functions were reported to environment %s\n", suite.envName), }, { - name: "snapshot lambda works with --function-names and deprecated --function-version which is ignored", - cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s --function-version 317`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works with --function-names and deprecated --function-version which is ignored", + cmd: fmt.Sprintf(`snapshot lambda %s %s --function-names %s --function-version 317`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), golden: fmt.Sprintf("Flag --function-version has been deprecated, --function-version is no longer supported. It will be removed in a future release.\n1 lambda functions were reported to environment %s\n", suite.envName), }, { - wantError: false, - name: "snapshot lambda without --function-names will report all lambdas in the AWS account", - cmd: fmt.Sprintf(`snapshot lambda %s %s`, suite.envName, suite.defaultKosliArguments), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda without --function-names will report all lambdas", + cmd: fmt.Sprintf(`snapshot lambda %s %s`, suite.envName, suite.defaultKosliArguments), goldenRegex: fmt.Sprintf("[0-9]+ lambda functions were reported to environment %s\n", suite.envName), }, { wantError: true, name: "snapshot lambda fails when both of --function-name and --function-names are set", cmd: fmt.Sprintf(`snapshot lambda %s --function-name foo --function-names foo %s`, suite.envName, suite.defaultKosliArguments), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, - golden: "Flag --function-name has been deprecated, use --function-names instead\nError: only one of --function-name, --function-names, --exclude is allowed\n", + golden: "Flag --function-name has been deprecated, use --function-names instead\nError: only one of --function-name, --function-names, --exclude is allowed\n", }, { wantError: true, @@ -122,19 +127,13 @@ func (suite *SnapshotLambdaTestSuite) TestSnapshotLambdaCmd() { golden: "Error: only one of --function-name, --function-names, --exclude-regex is allowed\n", }, { - name: "snapshot lambda works if both --exclude and --exclude-regex are set", - cmd: fmt.Sprintf(`snapshot lambda %s %s --exclude %s --exclude-regex function1`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), - additionalConfig: snapshotLambdaTestConfig{ - requireAuthToBeSet: true, - }, + name: "snapshot lambda works if both --exclude and --exclude-regex are set", + cmd: fmt.Sprintf(`snapshot lambda %s %s --exclude %s --exclude-regex function1`, suite.envName, suite.defaultKosliArguments, suite.zipFunctionName), goldenRegex: fmt.Sprintf("[0-9]+ lambda functions were reported to environment %s\n", suite.envName), }, } for _, t := range tests { - if t.additionalConfig != nil && t.additionalConfig.(snapshotLambdaTestConfig).requireAuthToBeSet { - testHelpers.SkipIfEnvVarUnset(suite.T(), []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"}) - } runTestCmd(suite.T(), []cmdTestCase{t}) } } diff --git a/internal/aws/aws.go b/internal/aws/aws.go index 27865d308..589fc3561 100644 --- a/internal/aws/aws.go +++ b/internal/aws/aws.go @@ -146,6 +146,20 @@ type LambdaAPI interface { GetFunctionConfiguration(ctx context.Context, params *lambda.GetFunctionConfigurationInput, optFns ...func(*lambda.Options)) (*lambda.GetFunctionConfigurationOutput, error) } +// defaultNewLambdaClient creates a real Lambda client from credentials. +func defaultNewLambdaClient(creds *AWSStaticCreds) (LambdaAPI, error) { + return creds.NewLambdaClient() +} + +// NewLambdaClientFunc is the factory used by GetLambdaPackageData to create a +// LambdaAPI client. Tests can replace this to inject a FakeLambdaClient. +var NewLambdaClientFunc = defaultNewLambdaClient + +// ResetLambdaClientFactory restores the default (real AWS) client factory. +func ResetLambdaClientFactory() { + NewLambdaClientFunc = defaultNewLambdaClient +} + // NewECSClient returns a new ECS API client func (staticCreds *AWSStaticCreds) NewECSClient() (*ecs.Client, error) { cfg, err := staticCreds.NewAWSConfigFromEnvOrFlags() @@ -194,7 +208,7 @@ func getFilteredLambdaFuncs(client LambdaAPI, nextMarker *string, allFunctions * // GetLambdaPackageData returns a digest and metadata of a Lambda function package func (staticCreds *AWSStaticCreds) GetLambdaPackageData(filter *filters.ResourceFilterOptions) ([]*LambdaData, error) { - client, err := staticCreds.NewLambdaClient() + client, err := NewLambdaClientFunc(staticCreds) if err != nil { return []*LambdaData{}, err } From f744999beb06ffc125418c498644466f440504b6 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 08:34:57 +0100 Subject: [PATCH 09/41] chore: mark Slice 7 complete in TODO Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index f4b391ded..37a5860ad 100644 --- a/TODO.md +++ b/TODO.md @@ -63,9 +63,4 @@ - [x] Slice 4: Fake-backed unit tests for filtering and pagination - [x] Slice 5: Fake-backed unit tests for orchestration - [x] Slice 6: Trim existing integration tests -- [ ] Slice 7: Package-level factory + fake-backed command tests ← active - - [ ] Add `NewLambdaClientFunc` factory variable to `internal/aws` - - [ ] `GetLambdaPackageData` uses factory instead of direct client creation - - [ ] `snapshotLambda_test.go` injects fake in SetupTest, resets in TearDownTest - - [ ] Remove `requireAuthToBeSet` from command test cases — they always run - - [ ] Add Makefile target for AWS smoke tests +- [x] Slice 7: Package-level factory + fake-backed command tests From 2ff108a7243031060980c17ae049d2cdd78da323 Mon Sep 17 00:00:00 2001 From: Steve Tooke Date: Fri, 3 Apr 2026 10:53:28 +0100 Subject: [PATCH 10/41] docs: add TODO checklist for remaining cloud provider fakes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lists the next steps for ECS, S3, Azure Apps, Docker, and Kubernetes following the pattern established by the Lambda work: interface → contract tests (real first) → fake → unit tests → factory injection into command tests → trim integration tests. Co-Authored-By: Claude Opus 4.6 (1M context) --- TODO.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/TODO.md b/TODO.md index 37a5860ad..2c3ed9d3f 100644 --- a/TODO.md +++ b/TODO.md @@ -57,6 +57,8 @@ ## Fakes & contract tests for cloud provider integrations (#758) +### Lambda (done — this PR) + - [x] Slice 1: Define `LambdaAPI` interface and refactor signatures - [x] Slice 2: Contract test suite against real AWS - [x] Slice 3: Build `FakeLambdaClient` that passes the contract @@ -64,3 +66,51 @@ - [x] Slice 5: Fake-backed unit tests for orchestration - [x] Slice 6: Trim existing integration tests - [x] Slice 7: Package-level factory + fake-backed command tests + +### ECS (next) + +- [ ] Define `ECSAPI` interface (`ListClusters`, `DescribeClusters`, `ListServices`, `ListTasks`, `DescribeTasks`) and refactor signatures +- [ ] Contract test suite against real AWS (env-gated) +- [ ] Build `FakeECSClient` that passes the contract (nested pagination: clusters → services → tasks) +- [ ] Fake-backed unit tests for filtering (cluster names, service names, regex, exclude patterns) +- [ ] Fake-backed unit tests for orchestration (concurrent cluster/service/task fetching, error propagation) +- [ ] `NewECSClientFunc` factory + inject fake into `snapshotECS_test.go` command tests +- [ ] Trim existing ECS integration tests to smoke tests +- [ ] Add ECS to `make test_smoke_aws` + +### S3 + +- [ ] Define `S3API` interface (decide: fake at paginator level or raw `ListObjectsV2` level) +- [ ] Contract test suite against real AWS (env-gated) +- [ ] Build `FakeS3Client` that passes the contract +- [ ] Fake-backed unit tests for path include/exclude filtering and digest computation +- [ ] `NewS3ClientFunc` factory + inject fake into `snapshotS3_test.go` command tests +- [ ] Trim existing S3 integration tests to smoke tests +- [ ] Add S3 to `make test_smoke_aws` + +### Azure Apps + +- [ ] Define interfaces for ARM AppService + Azure Container Registry clients +- [ ] Contract test suite against real Azure (env-gated) +- [ ] Build fakes that pass the contracts +- [ ] Fake-backed unit tests for app listing, image fingerprinting, error propagation +- [ ] Factory + inject fakes into `snapshotAzureApps_test.go` command tests +- [ ] Trim existing Azure integration tests to smoke tests + +### Docker + +- [ ] Define `DockerAPI` interface (Pull, Push, Tag, Remove, Run, container operations) +- [ ] Contract test suite against real Docker daemon +- [ ] Build `FakeDockerClient` that passes the contract +- [ ] Fake-backed unit tests +- [ ] Factory + inject fake into `snapshotDocker_test.go` command tests +- [ ] Trim existing Docker integration tests to smoke tests + +### Kubernetes + +- [ ] Define interface for Kubernetes clientset operations (pod listing, namespace listing) +- [ ] Contract test suite against real cluster (KIND, env-gated) +- [ ] Build fake that passes the contract (semaphore pattern, namespace filtering) +- [ ] Fake-backed unit tests for filtering, large-scale concurrency, error propagation +- [ ] Factory + inject fake into `snapshotK8S_test.go` command tests +- [ ] Trim existing Kube integration tests to smoke tests From 4bd004f98646bfac013f61c9ade0c30a3fa3e308 Mon Sep 17 00:00:00 2001 From: SimonC Date: Mon, 20 Apr 2026 16:06:28 +0200 Subject: [PATCH 11/41] Apply suggestions from code review Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- internal/aws/fake_lambda.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/aws/fake_lambda.go b/internal/aws/fake_lambda.go index 86c5ea7a3..a855baa4f 100644 --- a/internal/aws/fake_lambda.go +++ b/internal/aws/fake_lambda.go @@ -30,7 +30,12 @@ func (f *FakeLambdaClient) pageSize() int { func (f *FakeLambdaClient) ListFunctions(_ context.Context, params *lambda.ListFunctionsInput, _ ...func(*lambda.Options)) (*lambda.ListFunctionsOutput, error) { pageSize := f.pageSize() - if params.MaxItems != nil && int(*params.MaxItems) < pageSize { + if params.MaxItems != nil { + maxItems := int(*params.MaxItems) + if maxItems < pageSize { + pageSize = maxItems + } + } pageSize = int(*params.MaxItems) } From 828e7260c7bed9398ef6d91debc01dc767834638 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 20 Apr 2026 16:08:54 +0200 Subject: [PATCH 12/41] test: skip pagination contract test when account has only 1 Lambda function Replace hard require.NotNil on NextMarker with a t.Skip guard so the real-AWS contract test skips cleanly instead of failing with a confusing nil-pointer message if the account ever has only one function. Co-Authored-By: Claude Sonnet 4.6 --- internal/aws/lambda_contract_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/aws/lambda_contract_test.go b/internal/aws/lambda_contract_test.go index 21b6d82ca..b210e2445 100644 --- a/internal/aws/lambda_contract_test.go +++ b/internal/aws/lambda_contract_test.go @@ -41,7 +41,9 @@ func runLambdaContractTests(t *testing.T, client LambdaAPI, existingFunctionName require.LessOrEqual(t, len(out.Functions), 1) // Follow the marker to prove pagination works - require.NotNil(t, out.NextMarker, "expected NextMarker when more functions exist") + if out.NextMarker == nil { + t.Skip("only 1 function in account; pagination not exercisable") + } out2, err := client.ListFunctions(context.TODO(), &lambda.ListFunctionsInput{ MaxItems: &maxItems, Marker: out.NextMarker, From 1f033512a3c13c459cb2f889b7ed4a5ef705b1a9 Mon Sep 17 00:00:00 2001 From: SimonC Date: Thu, 9 Apr 2026 15:34:07 +0200 Subject: [PATCH 13/41] Remove latest_activity from repo list and get commands (#770) The field always returns null in production and is being removed from the API response (kosli-dev/server#5180). Co-authored-by: Claude Sonnet 4.6 --- cmd/kosli/getRepo.go | 1 - cmd/kosli/listRepos.go | 8 ++------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/cmd/kosli/getRepo.go b/cmd/kosli/getRepo.go index eb477dedb..ac45c7eca 100644 --- a/cmd/kosli/getRepo.go +++ b/cmd/kosli/getRepo.go @@ -130,7 +130,6 @@ func printRepoAsTable(raw string, out io.Writer, page int) error { fmt.Sprintf("Name:\t%s", repo["name"]), fmt.Sprintf("URL:\t%s", repo["url"]), fmt.Sprintf("Provider:\t%s", repo["provider"]), - fmt.Sprintf("Latest Activity:\t%s", repo["latest_activity"]), } tabFormattedPrint(out, []string{}, rows) diff --git a/cmd/kosli/listRepos.go b/cmd/kosli/listRepos.go index 6658480c8..29264064f 100644 --- a/cmd/kosli/listRepos.go +++ b/cmd/kosli/listRepos.go @@ -102,14 +102,10 @@ func printReposListAsTable(raw string, out io.Writer, page int) error { return nil } - header := []string{"NAME", "URL", "PROVIDER", "LAST_ACTIVITY"} + header := []string{"NAME", "URL", "PROVIDER"} rows := []string{} for _, repo := range repos { - latestActivity := "" - if v := repo["latest_activity"]; v != nil { - latestActivity = fmt.Sprintf("%s", v) - } - row := fmt.Sprintf("%s\t%s\t%s\t%s", repo["name"], repo["url"], repo["provider"], latestActivity) + row := fmt.Sprintf("%s\t%s\t%s", repo["name"], repo["url"], repo["provider"]) rows = append(rows, row) } tabFormattedPrint(out, header, rows) From 7b2f4067c30d69d8886838e3f1faee6f5098d24e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:17:35 +0100 Subject: [PATCH 14/41] chore(deps): bump the go-dependencies group across 1 directory with 10 updates (#769) Bumps the go-dependencies group with 8 updates in the / directory: | Package | From | To | | --- | --- | --- | | [github.com/Azure/azure-sdk-for-go/sdk/azidentity](https://github.com/Azure/azure-sdk-for-go) | `1.10.1` | `1.13.1` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.32.13` | `1.32.14` | | [github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager](https://github.com/aws/aws-sdk-go-v2) | `0.1.13` | `0.1.15` | | [github.com/aws/aws-sdk-go-v2/service/ecs](https://github.com/aws/aws-sdk-go-v2) | `1.75.0` | `1.77.0` | | [github.com/aws/aws-sdk-go-v2/service/lambda](https://github.com/aws/aws-sdk-go-v2) | `1.88.5` | `1.89.0` | | [github.com/aws/smithy-go](https://github.com/aws/smithy-go) | `1.24.2` | `1.24.3` | | [github.com/docker/docker](https://github.com/docker/docker) | `28.3.2+incompatible` | `28.5.2+incompatible` | | [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) | `1.15.1` | `1.15.2` | Updates `github.com/Azure/azure-sdk-for-go/sdk/azidentity` from 1.10.1 to 1.13.1 - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azidentity/v1.10.1...sdk/azidentity/v1.13.1) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.13 to 1.32.14 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.32.13...config/v1.32.14) Updates `github.com/aws/aws-sdk-go-v2/credentials` from 1.19.13 to 1.19.14 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/credentials/v1.19.13...credentials/v1.19.14) Updates `github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager` from 0.1.13 to 0.1.15 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/feature/s3/transfermanager/v0.1.13...feature/s3/transfermanager/v0.1.15) Updates `github.com/aws/aws-sdk-go-v2/service/ecs` from 1.75.0 to 1.77.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.75.0...service/s3/v1.77.0) Updates `github.com/aws/aws-sdk-go-v2/service/lambda` from 1.88.5 to 1.89.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.88.5...service/s3/v1.89.0) Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.98.0 to 1.99.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.98.0...service/s3/v1.99.0) Updates `github.com/aws/smithy-go` from 1.24.2 to 1.24.3 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.24.2...v1.24.3) Updates `github.com/docker/docker` from 28.3.2+incompatible to 28.5.2+incompatible - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.3.2...v28.5.2) Updates `github.com/open-policy-agent/opa` from 1.15.1 to 1.15.2 - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/v1.15.2/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v1.15.1...v1.15.2) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azidentity dependency-version: 1.13.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.32.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/credentials dependency-version: 1.19.14 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager dependency-version: 0.1.15 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/service/ecs dependency-version: 1.77.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/service/lambda dependency-version: 1.89.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/service/s3 dependency-version: 1.99.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/aws/smithy-go dependency-version: 1.24.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/docker/docker dependency-version: 28.5.2+incompatible dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/open-policy-agent/opa dependency-version: 1.15.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 29 ++++++++++---------- go.sum | 87 +++++++++++++++++++--------------------------------------- 2 files changed, 42 insertions(+), 74 deletions(-) diff --git a/go.mod b/go.mod index 9da0156f4..c7eb01ee2 100644 --- a/go.mod +++ b/go.mod @@ -4,20 +4,20 @@ go 1.25.9 require ( 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/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/resourcemanager/appservice/armappservice/v2 v2.3.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.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/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.77.0 + github.com/aws/aws-sdk-go-v2/service/lambda v1.89.0 + 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.3.2+incompatible + github.com/docker/docker v28.5.2+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 @@ -28,7 +28,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.15.1 + github.com/open-policy-agent/opa v1.15.2 github.com/otiai10/copy v1.14.1 github.com/owenrumney/go-sarif/v2 v2.3.3 github.com/pkg/errors v0.9.1 @@ -57,7 +57,7 @@ require ( 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.4.2 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Masterminds/semver/v3 v3.4.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -75,8 +75,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.14 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect @@ -119,9 +119,8 @@ 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.2.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // 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 diff --git a/go.sum b/go.sum index 0b64abe58..db7385ba2 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ 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.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 v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= 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= @@ -20,8 +20,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 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.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= -github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +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/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= @@ -50,14 +50,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.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/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/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.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/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/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= @@ -66,8 +66,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.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/ecs v1.77.0 h1:g3RYQmK6uRU5kOuwDthemuiiTbmwyGd8Wzf+k7cYWtk= +github.com/aws/aws-sdk-go-v2/service/ecs v1.77.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= @@ -76,20 +76,20 @@ 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.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/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/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= 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.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/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/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/aws/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg= +github.com/aws/smithy-go v1.24.3/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -133,8 +133,6 @@ github.com/dgraph-io/badger/v4 v4.9.1 h1:DocZXZkg5JJHJPtUErA0ibyHxOVUDVoXLSCV6t8 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/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/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= @@ -143,8 +141,8 @@ github.com/docker/cli v28.3.2+incompatible h1:mOt9fcLE7zaACbxW1GeS65RI67wIJrTnqS 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.3.2+incompatible h1:wn66NJ6pWB1vBZIilP8G3qQPqHy5XymfYn5vsqeA5oA= -github.com/docker/docker v28.3.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +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-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= @@ -230,12 +228,10 @@ 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.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 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-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/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/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -293,8 +289,6 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4 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.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= @@ -376,8 +370,8 @@ 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.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.1 h1:ZE4JaXsVUzDiHFSlOMBS3nJohR5BRGB/RNz6gTNugzE= -github.com/open-policy-agent/opa v1.15.1/go.mod h1:c6SN+7jSsUcKJLQc5P4yhwx8YYDRbjpAiGkBOTqxaa4= +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/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= @@ -415,8 +409,6 @@ github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7D github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw= 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.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/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= @@ -499,8 +491,6 @@ github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5 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/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 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= @@ -539,38 +529,25 @@ go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -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-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/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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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.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.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -585,7 +562,6 @@ 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.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= @@ -593,15 +569,8 @@ 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-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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= From 5840ef9e287cf72c6e91c9f6156ae41ce79adfb8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:18:41 +0100 Subject: [PATCH 15/41] chore(deps): bump actions/download-artifact from 7 to 8 (#765) Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8. - [Release notes](https://github.com/actions/download-artifact/releases) - [Commits](https://github.com/actions/download-artifact/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/download-artifact dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 5eded4c46..c5ae2c1b1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -202,7 +202,7 @@ jobs: check-latest: true - name: Download all digest artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digest-* From e8af1593cdf10c44dfde1429d21571c3997f7a37 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 21:00:46 +0100 Subject: [PATCH 16/41] chore(deps): bump go.opentelemetry.io/otel/sdk from 1.41.0 to 1.43.0 (#771) Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.41.0 to 1.43.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.41.0...v1.43.0) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/sdk dependency-version: 1.43.0 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 20 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index c7eb01ee2..7efc5ff71 100644 --- a/go.mod +++ b/go.mod @@ -198,12 +198,12 @@ 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.41.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.41.0 // indirect - go.opentelemetry.io/otel/sdk v1.41.0 // indirect - go.opentelemetry.io/otel/trace 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/proto/otlp v1.9.0 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect diff --git a/go.sum b/go.sum index db7385ba2..5ea01a4bf 100644 --- a/go.sum +++ b/go.sum @@ -500,22 +500,22 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ 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.41.0 h1:YlEwVsGAlCvczDILpUXpIpPSL/VPugt7zHThEMLce1c= -go.opentelemetry.io/otel v1.41.0/go.mod h1:Yt4UwgEKeT05QbLwbyHXEwhnjxNO6D8L5PQP51/46dE= +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/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.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/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/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/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= From dc055d335444d6b32ac23d0b7ce184718f76415b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 20:32:12 +0000 Subject: [PATCH 17/41] chore(deps): bump golang.org/x/term in the go-dependencies group (#772) Bumps the go-dependencies group with 1 update: [golang.org/x/term](https://github.com/golang/term). Updates `golang.org/x/term` from 0.41.0 to 0.42.0 - [Commits](https://github.com/golang/term/compare/v0.41.0...v0.42.0) --- updated-dependencies: - dependency-name: golang.org/x/term dependency-version: 0.42.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7efc5ff71..9db3810a4 100644 --- a/go.mod +++ b/go.mod @@ -43,7 +43,7 @@ require ( github.com/zalando/go-keyring v0.2.8 gitlab.com/gitlab-org/api/client-go v1.46.0 golang.org/x/oauth2 v0.36.0 - golang.org/x/term v0.41.0 + golang.org/x/term v0.42.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.35.0 k8s.io/apimachinery v0.35.0 @@ -212,7 +212,7 @@ require ( golang.org/x/mod v0.33.0 // indirect golang.org/x/net v0.51.0 // indirect golang.org/x/sync v0.20.0 // indirect - golang.org/x/sys v0.42.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 diff --git a/go.sum b/go.sum index 5ea01a4bf..c8d508a88 100644 --- a/go.sum +++ b/go.sum @@ -555,11 +555,11 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -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/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -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/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/text v0.3.0/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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= From 022a757f79393ba41361acc879707495e21ce23b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:39:36 +0000 Subject: [PATCH 18/41] chore(deps): bump github.com/aws/aws-sdk-go-v2/service/ecs (#773) Bumps the go-dependencies group with 1 update: [github.com/aws/aws-sdk-go-v2/service/ecs](https://github.com/aws/aws-sdk-go-v2). Updates `github.com/aws/aws-sdk-go-v2/service/ecs` from 1.77.0 to 1.78.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.77.0...service/s3/v1.78.0) --- updated-dependencies: - dependency-name: github.com/aws/aws-sdk-go-v2/service/ecs dependency-version: 1.78.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9db3810a4..20f7de030 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( 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.77.0 + 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/s3 v1.99.0 github.com/aws/smithy-go v1.24.3 diff --git a/go.sum b/go.sum index c8d508a88..5c87d0510 100644 --- a/go.sum +++ b/go.sum @@ -66,8 +66,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.77.0 h1:g3RYQmK6uRU5kOuwDthemuiiTbmwyGd8Wzf+k7cYWtk= -github.com/aws/aws-sdk-go-v2/service/ecs v1.77.0/go.mod h1:QkWmubOYmjj3cHn7A4CoUU7BKJhVeo39Gp6NH7IyhZw= +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/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= From eac9b4e1deed2dacde88637b29e4101c1707abfe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 11 Apr 2026 20:40:52 +0000 Subject: [PATCH 19/41] chore(deps): bump step-security/harden-runner (#774) Bumps the github-actions-dependencies group with 1 update: [step-security/harden-runner](https://github.com/step-security/harden-runner). Updates `step-security/harden-runner` from 2.16.1 to 2.17.0 - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](https://github.com/step-security/harden-runner/compare/fe104658747b27e96e4f7e80cd0a94068e53901d...f808768d1510423e83855289c910610ca9b43176) --- updated-dependencies: - dependency-name: step-security/harden-runner dependency-version: 2.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/binary_provenance.yml | 2 +- .github/workflows/claude-pr-review.yml | 4 ++-- .github/workflows/daily-cli-tests.yml | 4 ++-- .github/workflows/docker.yml | 6 +++--- .github/workflows/helm-chart.yml | 2 +- .github/workflows/init_kosli.yml | 2 +- .github/workflows/install-script-tests.yml | 4 ++-- .github/workflows/main.yml | 4 ++-- .github/workflows/never_alone_trail.yml | 2 +- .github/workflows/publish_branch_docs.yml | 2 +- .github/workflows/publish_docs.yml | 2 +- .github/workflows/release.yml | 16 ++++++++-------- .github/workflows/test.yml | 8 ++++---- .github/workflows/upload-cli-layer.yml | 2 +- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/binary_provenance.yml b/.github/workflows/binary_provenance.yml index 2365391ab..e093cbad6 100644 --- a/.github/workflows/binary_provenance.yml +++ b/.github/workflows/binary_provenance.yml @@ -35,7 +35,7 @@ jobs: artifact: ${{fromJson(inputs.artifacts)}} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml index 09a0f0d58..299bf81ed 100644 --- a/.github/workflows/claude-pr-review.yml +++ b/.github/workflows/claude-pr-review.yml @@ -27,7 +27,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -80,7 +80,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/daily-cli-tests.yml b/.github/workflows/daily-cli-tests.yml index 9546f382d..ec9cc4646 100644 --- a/.github/workflows/daily-cli-tests.yml +++ b/.github/workflows/daily-cli-tests.yml @@ -12,7 +12,7 @@ jobs: trail_name: ${{ steps.prep.outputs.trail_name }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -67,7 +67,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c5ae2c1b1..2a6da0251 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -63,7 +63,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -120,7 +120,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -187,7 +187,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml index d2a8d55c4..5c86b8c46 100644 --- a/.github/workflows/helm-chart.yml +++ b/.github/workflows/helm-chart.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/init_kosli.yml b/.github/workflows/init_kosli.yml index f185c6ded..fabafa3af 100644 --- a/.github/workflows/init_kosli.yml +++ b/.github/workflows/init_kosli.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/install-script-tests.yml b/.github/workflows/install-script-tests.yml index e8a6a1b9f..7a6f08467 100644 --- a/.github/workflows/install-script-tests.yml +++ b/.github/workflows/install-script-tests.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -52,7 +52,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 672fe0161..a98af2ae7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: report_to_kosli: ${{ steps.prep.outputs.report_to_kosli }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -136,7 +136,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/never_alone_trail.yml b/.github/workflows/never_alone_trail.yml index 0867f05a9..00867b6b5 100644 --- a/.github/workflows/never_alone_trail.yml +++ b/.github/workflows/never_alone_trail.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/publish_branch_docs.yml b/.github/workflows/publish_branch_docs.yml index 6980d2322..c1ace8505 100644 --- a/.github/workflows/publish_branch_docs.yml +++ b/.github/workflows/publish_branch_docs.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml index 15685705a..3546bbbba 100644 --- a/.github/workflows/publish_docs.yml +++ b/.github/workflows/publish_docs.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 150f4dd85..3feb00c03 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: trail_template_file: ${{ steps.prep.outputs.trail_template_file }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -116,7 +116,7 @@ jobs: artifacts: ${{ steps.prepare-artifacts-list.outputs.artifacts }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -216,7 +216,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -234,7 +234,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -276,7 +276,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -293,7 +293,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -310,7 +310,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -354,7 +354,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fcc42ba0..65101c21b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -120,7 +120,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -195,7 +195,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit @@ -239,7 +239,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit diff --git a/.github/workflows/upload-cli-layer.yml b/.github/workflows/upload-cli-layer.yml index 1edaeeb52..42e655df5 100644 --- a/.github/workflows/upload-cli-layer.yml +++ b/.github/workflows/upload-cli-layer.yml @@ -21,7 +21,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@fe104658747b27e96e4f7e80cd0a94068e53901d # v2.16.1 + uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 with: egress-policy: audit From a0506e8633fd4f0a7dc1abe2213df9526f49ceb4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 15:03:11 +0100 Subject: [PATCH 20/41] chore(deps): bump github.com/mattn/go-shellwords (#775) Bumps the go-dependencies group with 1 update: [github.com/mattn/go-shellwords](https://github.com/mattn/go-shellwords). Updates `github.com/mattn/go-shellwords` from 1.0.12 to 1.0.13 - [Commits](https://github.com/mattn/go-shellwords/compare/v1.0.12...v1.0.13) --- updated-dependencies: - dependency-name: github.com/mattn/go-shellwords dependency-version: 1.0.13 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 20f7de030..d349d0a8e 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( 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.12 + github.com/mattn/go-shellwords v1.0.13 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 diff --git a/go.sum b/go.sum index 5c87d0510..06a48083c 100644 --- a/go.sum +++ b/go.sum @@ -326,8 +326,8 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +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/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= From 3825c452e26de704dd78fc4aa365bb6d513e5493 Mon Sep 17 00:00:00 2001 From: AlexKantor87 Date: Wed, 15 Apr 2026 11:16:09 +0100 Subject: [PATCH 21/41] fix: skip legacy_ref self-references in checklinks plugin (#778) * fix: skip legacy_ref self-references in checklinks plugin Hugo renders the navigation menu with absolute URLs to docs.kosli.com. Newly added legacy_ref versions cause checklinks to fail because the self-referencing links 404 against the live production site until the deploy succeeds (chicken-and-egg). Add https://docs.kosli.com/legacy_ref/ to skipPatterns so any future legacy_ref version addition does not block the deploy. * fix: add trailing newline to netlify.toml --- docs.kosli.com/netlify.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs.kosli.com/netlify.toml b/docs.kosli.com/netlify.toml index 0497a65d8..9839c84d4 100644 --- a/docs.kosli.com/netlify.toml +++ b/docs.kosli.com/netlify.toml @@ -20,4 +20,4 @@ [[context.production.plugins]] package = "netlify-plugin-checklinks" [context.production.plugins.inputs] - skipPatterns = [".netlify.app", "https://docs.kosli.com/ci-defaults%29", "https://fonts.googleapis.com/"] \ No newline at end of file + skipPatterns = [".netlify.app", "https://docs.kosli.com/ci-defaults%29", "https://fonts.googleapis.com/", "https://docs.kosli.com/legacy_ref/"] From 04db728a1107767b1a891a45eadeb0e56c7bf1e0 Mon Sep 17 00:00:00 2001 From: AlexKantor87 Date: Wed, 15 Apr 2026 11:41:23 +0100 Subject: [PATCH 22/41] feat(k8s-reporter): add extraVolumes, extraVolumeMounts, extraEnvVars, customCA (#777) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(k8s-reporter): add extraVolumes, extraVolumeMounts, extraEnvVars, customCA Adds two layers of support for customers running the k8s-reporter behind a TLS-inspecting proxy that requires a corporate CA bundle: 1. Generic escape hatch — extraVolumes, extraVolumeMounts, extraEnvVars following Bitnami / prometheus-community naming conventions. 2. customCA convenience wrapper — single config block that mounts the CA via subPath into /etc/ssl/certs/ so Go's stdlib picks it up additively alongside the system bundle (avoids the SSL_CERT_FILE footgun that would otherwise replace the system CAs). The Kosli CLI is Go and uses net/http's default transport; no CLI changes are needed because crypto/x509 reads /etc/ssl/certs/ automatically. Refs kosli-dev/cli#776 Co-Authored-By: Claude Opus 4.6 * review fixes: defaultMode, x509 keyword, value-table cleanup, env deprecation Addresses parallel review feedback on PR #777: - Add explicit defaultMode: 0644 to customCA Secret volume so the permission posture is self-documenting for auditors. - Tighten the docs description of Go's cert-pool loading: it's two independent passes (cert file + dir scan), not one unified scan. - Add the literal "x509: certificate signed by unknown authority" string to the docs section so customers searching the error message find it. - Replace the SSL_CERT_FILE example in the extraEnvVars block with HTTPS_PROXY — SSL_CERT_FILE is the documented footgun and shouldn't be the first thing copy-pasters see. - Shorten the customCA parent comment so the helm-docs values table reads cleanly; defer detail to the README section. - Mark the existing `env:` map as DEPRECATED in favour of extraEnvVars to remove cross-reference confusion. Both still work. Refs kosli-dev/cli#776 Co-Authored-By: Claude Opus 4.6 * revert env deprecation; document env and extraEnvVars as siblings Backing out the env-map deprecation from the previous commit — that was scope creep beyond the customCA feature, made without auditing who relies on env: today or whether the team wants to phase it out. Instead: surface env: in the helm-docs values table (it was previously invisible because the values.yaml entry was fully commented out) and describe env: vs extraEnvVars as sibling options. Behaviour unchanged — env: {} is functionally identical to the previous fully-commented form. Co-Authored-By: Claude Opus 4.6 * address Claude bot review on PR #777 - Add `required` guard on customCA.key to prevent the silent empty-subPath footgun (an empty subPath causes K8s to mount the whole Secret as a directory at the mountPath, which would break Go trying to read it as a cert file). - Tighten the env range loop with `{{- end }}` so the rendered env block doesn't carry a trailing blank line before extraEnvVars. Co-Authored-By: Claude Opus 4.6 * restore env: comment as instruction (not example) Reverting the editorial framing in the env: comment. The original comment was a directive for single-tenant Kosli instances, not an example. Also dropping the extraEnvVars cross-reference — that was scope creep added without justification. Co-Authored-By: Claude Opus 4.6 * Apply suggestions from code review Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * address remaining Claude bot review on PR #777 Picking up safe-to-fix nits regardless of strict scope: - Quote customCA.secretName and subPath, matching the pattern used for cronSchedule and image fields. mbevc1 also asked for this. - Quote $value in the env range so user-supplied values containing YAML special chars (colons, brackets) render safely. - Tighten the podLabels range with `{{- end }}` for consistency with the env-range fix in commit b44df210. Pre-existing whitespace nit, not introduced by this PR, but cheap to fix while I'm here. - Collapse the env: helm-docs comment to a single line so it reads cleanly in the rendered values table (the multi-line YAML example collapsed badly after helm-docs concatenation). Co-Authored-By: Claude Opus 4.6 * quote podLabels values; fix double-space typo in range loops Same fix as the env value quoting in f2af5bfa, applied to podLabels for consistency. Also fixes the double-space typo (`:= .Values.`) in both range loops — harmless cosmetic carry-over from the original template. Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 Co-authored-by: Marko Bevc Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- charts/k8s-reporter/Chart.yaml | 2 +- charts/k8s-reporter/README.md | 63 +++++++++++++++++++++- charts/k8s-reporter/README.md.gotmpl | 2 + charts/k8s-reporter/_templates.gotmpl | 55 +++++++++++++++++++ charts/k8s-reporter/templates/cronjob.yaml | 33 +++++++++--- charts/k8s-reporter/values.yaml | 44 +++++++++++++-- docs.kosli.com/content/helm/_index.md | 63 +++++++++++++++++++++- 7 files changed, 250 insertions(+), 12 deletions(-) diff --git a/charts/k8s-reporter/Chart.yaml b/charts/k8s-reporter/Chart.yaml index 8b1d76c7a..4fece5f6f 100644 --- a/charts/k8s-reporter/Chart.yaml +++ b/charts/k8s-reporter/Chart.yaml @@ -15,7 +15,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 2.1.0 +version: 2.2.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index 3d2a12b63..5161b8713 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -4,7 +4,7 @@ title: Kubernetes Reporter Helm Chart # k8s-reporter -![Version: 2.1.0](https://img.shields.io/badge/Version-2.1.0-informational?style=flat-square) +![Version: 2.2.0](https://img.shields.io/badge/Version-2.2.0-informational?style=flat-square) A Helm chart for installing the Kosli K8S reporter as a cronjob. The chart allows you to create a Kubernetes cronjob and all its necessary RBAC to report running images to Kosli at a given cron schedule. @@ -96,11 +96,72 @@ helm upgrade kosli-reporter kosli/k8s-reporter -f values.yaml helm uninstall kosli-reporter ``` +## Running behind a TLS-inspecting proxy (corporate / custom CA bundle) + +If your network sits behind a TLS-inspecting appliance (Zscaler, Netskope, Palo Alto, etc.) that re-signs HTTPS traffic with a corporate CA certificate, the reporter will fail with `x509: certificate signed by unknown authority`. To fix this, make the appliance's CA bundle available to the reporter. + +The chart offers two ways to do this. Use whichever fits your deployment flow. + +### Option 1 — `customCA` convenience wrapper (recommended for the common case) + +1. Create a Secret containing the corporate CA certificate (PEM format, single cert or bundle): + +```shell {.command} +kubectl create secret generic corporate-ca-bundle --from-file=ca.crt=/path/to/corporate-ca.crt +``` + +2. Enable the wrapper in `values.yaml`: + +```yaml +customCA: + enabled: true + secretName: corporate-ca-bundle + key: ca.crt +``` + +The chart mounts the certificate as a single file at `/etc/ssl/certs/kosli-custom-ca.crt` using `subPath`. Go's standard library on Linux loads CA roots in two independent passes — it reads the system bundle file (e.g. `/etc/ssl/certs/ca-certificates.crt`) and **also** scans `/etc/ssl/certs/` for additional certificate files. The mounted file is picked up by the directory scan and added to the trust store alongside the system roots, so no `SSL_CERT_FILE` env var is needed. + +The wrapper deliberately does **not** set `SSL_CERT_FILE`. Setting it would replace the system bundle entirely with the customer's file, breaking trust for any public CAs the bundle does not include. + +### Option 2 — generic `extraVolumes` / `extraVolumeMounts` / `extraEnvVars` + +Use these when you need a non-default mount path, a ConfigMap instead of a Secret, multiple volumes, or any other shape the wrapper does not cover: + +```yaml +extraVolumes: + - name: corporate-ca + secret: + secretName: corporate-ca-bundle + +extraVolumeMounts: + - name: corporate-ca + mountPath: /etc/ssl/certs/corporate + readOnly: true +``` + +Note: if you mount the CA outside `/etc/ssl/certs/` and set `SSL_CERT_FILE` via `extraEnvVars`, your bundle must include the public CAs you also need to trust — Go uses only that file when `SSL_CERT_FILE` is set. + +### Pod Security Standards + +Both options use `secret`-backed volumes, which are permitted under the Pod Security Standards `restricted` profile. `hostPath` mounts are not permitted under that profile and should not be used here. + +### Cluster-wide alternative + +If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/trust/trust-manager/) to distribute a corporate CA bundle into a well-known ConfigMap in every namespace, point `extraVolumes` / `extraVolumeMounts` at that ConfigMap instead of creating a per-namespace Secret. + ## Configurations | Key | Type | Default | Description | |-----|------|---------|-------------| | concurrencyPolicy | string | `"Replace"` | specifies how to treat concurrent executions of a Job that is created by this CronJob | | cronSchedule | string | `"*/5 * * * *"` | the cron schedule at which the reporter is triggered to report to Kosli | +| customCA | object | `{"enabled":false,"key":"ca.crt","secretName":""}` | convenience wrapper for mounting a corporate / custom CA bundle. See the "Running behind a TLS-inspecting proxy" section of the README for usage. | +| customCA.enabled | bool | `false` | enable mounting a corporate/custom CA bundle into the trust store | +| customCA.key | string | `"ca.crt"` | key within the Secret that holds the PEM-formatted CA certificate (single cert or multi-cert PEM bundle) | +| customCA.secretName | string | `""` | name of an existing Secret in the same namespace containing the CA bundle | +| env | object | `{}` | map of plain environment variables to inject into the reporter container. For a single-tenant Kosli instance, set KOSLI_HOST to https://.kosli.com. | +| extraEnvVars | list | `[]` | additional environment variables to inject into the reporter container. List of {name, value} or {name, valueFrom} entries, rendered verbatim into the container env. Supports plain values and valueFrom (secretKeyRef / configMapKeyRef). Note: entries here are appended after the chart's own env entries; on duplicate names the later entry wins. | +| extraVolumeMounts | list | `[]` | additional container-level volumeMounts for the reporter container. Rendered verbatim into the container spec alongside the chart's own mounts. | +| extraVolumes | list | `[]` | additional Pod-level volumes to attach to the reporter pod. Rendered verbatim into the Pod spec alongside the chart's own volumes. Use together with `extraVolumeMounts` to mount Secrets, ConfigMaps, or other volumes into the container. | | failedJobsHistoryLimit | int | `1` | specifies the number of failed finished jobs to keep | | fullnameOverride | string | `""` | overrides the fullname used for the created k8s resources. It has higher precedence than `nameOverride` | | image.pullPolicy | string | `"IfNotPresent"` | the kosli reporter image pull policy | diff --git a/charts/k8s-reporter/README.md.gotmpl b/charts/k8s-reporter/README.md.gotmpl index 71035bdec..c6c82ee37 100644 --- a/charts/k8s-reporter/README.md.gotmpl +++ b/charts/k8s-reporter/README.md.gotmpl @@ -17,6 +17,8 @@ title: Kubernetes Reporter Helm Chart {{ template "extra.uninstall" . }} +{{ template "extra.customCA" . }} + {{ template "extra.valuesHeader" . }} {{ template "chart.valuesTable" . }} diff --git a/charts/k8s-reporter/_templates.gotmpl b/charts/k8s-reporter/_templates.gotmpl index a8cd8fa04..5ce980faf 100644 --- a/charts/k8s-reporter/_templates.gotmpl +++ b/charts/k8s-reporter/_templates.gotmpl @@ -98,6 +98,61 @@ helm uninstall kosli-reporter ``` {{- end }} +{{ define "extra.customCA" -}} +## Running behind a TLS-inspecting proxy (corporate / custom CA bundle) + +If your network sits behind a TLS-inspecting appliance (Zscaler, Netskope, Palo Alto, etc.) that re-signs HTTPS traffic with a corporate CA certificate, the reporter will fail with `x509: certificate signed by unknown authority`. To fix this, make the appliance's CA bundle available to the reporter. + +The chart offers two ways to do this. Use whichever fits your deployment flow. + +### Option 1 — `customCA` convenience wrapper (recommended for the common case) + +1. Create a Secret containing the corporate CA certificate (PEM format, single cert or bundle): + +```shell {.command} +kubectl create secret generic corporate-ca-bundle --from-file=ca.crt=/path/to/corporate-ca.crt +``` + +2. Enable the wrapper in `values.yaml`: + +```yaml +customCA: + enabled: true + secretName: corporate-ca-bundle + key: ca.crt +``` + +The chart mounts the certificate as a single file at `/etc/ssl/certs/kosli-custom-ca.crt` using `subPath`. Go's standard library on Linux loads CA roots in two independent passes — it reads the system bundle file (e.g. `/etc/ssl/certs/ca-certificates.crt`) and **also** scans `/etc/ssl/certs/` for additional certificate files. The mounted file is picked up by the directory scan and added to the trust store alongside the system roots, so no `SSL_CERT_FILE` env var is needed. + +The wrapper deliberately does **not** set `SSL_CERT_FILE`. Setting it would replace the system bundle entirely with the customer's file, breaking trust for any public CAs the bundle does not include. + +### Option 2 — generic `extraVolumes` / `extraVolumeMounts` / `extraEnvVars` + +Use these when you need a non-default mount path, a ConfigMap instead of a Secret, multiple volumes, or any other shape the wrapper does not cover: + +```yaml +extraVolumes: + - name: corporate-ca + secret: + secretName: corporate-ca-bundle + +extraVolumeMounts: + - name: corporate-ca + mountPath: /etc/ssl/certs/corporate + readOnly: true +``` + +Note: if you mount the CA outside `/etc/ssl/certs/` and set `SSL_CERT_FILE` via `extraEnvVars`, your bundle must include the public CAs you also need to trust — Go uses only that file when `SSL_CERT_FILE` is set. + +### Pod Security Standards + +Both options use `secret`-backed volumes, which are permitted under the Pod Security Standards `restricted` profile. `hostPath` mounts are not permitted under that profile and should not be used here. + +### Cluster-wide alternative + +If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/trust/trust-manager/) to distribute a corporate CA bundle into a well-known ConfigMap in every namespace, point `extraVolumes` / `extraVolumeMounts` at that ConfigMap instead of creating a per-namespace Secret. +{{- end }} + {{ define "extra.valuesHeader" -}} ## Configurations {{- end }} diff --git a/charts/k8s-reporter/templates/cronjob.yaml b/charts/k8s-reporter/templates/cronjob.yaml index a3fc7bb9f..b54f6e7be 100644 --- a/charts/k8s-reporter/templates/cronjob.yaml +++ b/charts/k8s-reporter/templates/cronjob.yaml @@ -18,15 +18,24 @@ spec: template: metadata: labels: - {{- range $key, $value := .Values.podLabels }} - {{ $key }}: {{ $value }} - {{ end }} + {{- range $key, $value := .Values.podLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} spec: serviceAccountName: {{ include "reporter.serviceAccountName" . }} volumes: - name: environments-config configMap: name: {{ include "reporter.fullname" . }}-environments-config + {{- if .Values.customCA.enabled }} + - name: custom-ca + secret: + secretName: {{ required "customCA.secretName is required when customCA.enabled is true" .Values.customCA.secretName | quote }} + defaultMode: 0644 + {{- end }} + {{- with .Values.extraVolumes }} + {{- toYaml . | nindent 10 }} + {{- end }} containers: - name: reporter image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" @@ -35,6 +44,15 @@ spec: - name: environments-config mountPath: /config readOnly: true + {{- if .Values.customCA.enabled }} + - name: custom-ca + mountPath: /etc/ssl/certs/kosli-custom-ca.crt + subPath: {{ required "customCA.key is required when customCA.enabled is true" .Values.customCA.key | quote }} + readOnly: true + {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} {{- if .Values.reporterConfig.securityContext }} securityContext: {{- if hasKey .Values.reporterConfig.securityContext "allowPrivilegeEscalation" }} @@ -61,10 +79,13 @@ spec: secretKeyRef: name: {{ required ".Values.kosliApiToken.secretName is required." .Values.kosliApiToken.secretName }} key: {{ .Values.kosliApiToken.secretKey | default "token" }} - {{- range $key, $value := .Values.env }} + {{- range $key, $value := .Values.env }} - name: {{ $key }} - value: {{ $value }} - {{ end }} + value: {{ $value | quote }} + {{- end }} + {{- with .Values.extraEnvVars }} + {{- toYaml . | nindent 14 }} + {{- end }} args: - snapshot - k8s diff --git a/charts/k8s-reporter/values.yaml b/charts/k8s-reporter/values.yaml index ca1742e82..a13f74495 100644 --- a/charts/k8s-reporter/values.yaml +++ b/charts/k8s-reporter/values.yaml @@ -82,9 +82,47 @@ reporterConfig: # Omit this field for OpenShift environments to allow automatic UID assignment runAsUser: 1000 -# Uncomment the env variable below and replace , if you are on a single tenant Kosli instance -# env: -# KOSLI_HOST: https://.kosli.com +# -- map of plain environment variables to inject into the reporter container. +# For a single-tenant Kosli instance, set KOSLI_HOST to https://.kosli.com. +env: {} + +# -- additional environment variables to inject into the reporter container. +# List of {name, value} or {name, valueFrom} entries, rendered verbatim into the container env. +# Supports plain values and valueFrom (secretKeyRef / configMapKeyRef). +# Note: entries here are appended after the chart's own env entries; on duplicate names the later entry wins. +extraEnvVars: [] + # - name: HTTPS_PROXY + # value: http://proxy.corp:8080 + # - name: MY_SECRET + # valueFrom: + # secretKeyRef: + # name: my-secret + # key: token + +# -- additional Pod-level volumes to attach to the reporter pod. +# Rendered verbatim into the Pod spec alongside the chart's own volumes. +# Use together with `extraVolumeMounts` to mount Secrets, ConfigMaps, or other volumes into the container. +extraVolumes: [] + # - name: corporate-ca + # secret: + # secretName: corporate-ca-bundle + +# -- additional container-level volumeMounts for the reporter container. +# Rendered verbatim into the container spec alongside the chart's own mounts. +extraVolumeMounts: [] + # - name: corporate-ca + # mountPath: /etc/ssl/certs/corporate + # readOnly: true + +# -- convenience wrapper for mounting a corporate / custom CA bundle. See the +# "Running behind a TLS-inspecting proxy" section of the README for usage. +customCA: + # -- enable mounting a corporate/custom CA bundle into the trust store + enabled: false + # -- name of an existing Secret in the same namespace containing the CA bundle + secretName: "" + # -- key within the Secret that holds the PEM-formatted CA certificate (single cert or multi-cert PEM bundle) + key: "ca.crt" # -- any custom annotations to be added to the cronjob podAnnotations: {} diff --git a/docs.kosli.com/content/helm/_index.md b/docs.kosli.com/content/helm/_index.md index 3d2a12b63..5161b8713 100644 --- a/docs.kosli.com/content/helm/_index.md +++ b/docs.kosli.com/content/helm/_index.md @@ -4,7 +4,7 @@ title: Kubernetes Reporter Helm Chart # k8s-reporter -![Version: 2.1.0](https://img.shields.io/badge/Version-2.1.0-informational?style=flat-square) +![Version: 2.2.0](https://img.shields.io/badge/Version-2.2.0-informational?style=flat-square) A Helm chart for installing the Kosli K8S reporter as a cronjob. The chart allows you to create a Kubernetes cronjob and all its necessary RBAC to report running images to Kosli at a given cron schedule. @@ -96,11 +96,72 @@ helm upgrade kosli-reporter kosli/k8s-reporter -f values.yaml helm uninstall kosli-reporter ``` +## Running behind a TLS-inspecting proxy (corporate / custom CA bundle) + +If your network sits behind a TLS-inspecting appliance (Zscaler, Netskope, Palo Alto, etc.) that re-signs HTTPS traffic with a corporate CA certificate, the reporter will fail with `x509: certificate signed by unknown authority`. To fix this, make the appliance's CA bundle available to the reporter. + +The chart offers two ways to do this. Use whichever fits your deployment flow. + +### Option 1 — `customCA` convenience wrapper (recommended for the common case) + +1. Create a Secret containing the corporate CA certificate (PEM format, single cert or bundle): + +```shell {.command} +kubectl create secret generic corporate-ca-bundle --from-file=ca.crt=/path/to/corporate-ca.crt +``` + +2. Enable the wrapper in `values.yaml`: + +```yaml +customCA: + enabled: true + secretName: corporate-ca-bundle + key: ca.crt +``` + +The chart mounts the certificate as a single file at `/etc/ssl/certs/kosli-custom-ca.crt` using `subPath`. Go's standard library on Linux loads CA roots in two independent passes — it reads the system bundle file (e.g. `/etc/ssl/certs/ca-certificates.crt`) and **also** scans `/etc/ssl/certs/` for additional certificate files. The mounted file is picked up by the directory scan and added to the trust store alongside the system roots, so no `SSL_CERT_FILE` env var is needed. + +The wrapper deliberately does **not** set `SSL_CERT_FILE`. Setting it would replace the system bundle entirely with the customer's file, breaking trust for any public CAs the bundle does not include. + +### Option 2 — generic `extraVolumes` / `extraVolumeMounts` / `extraEnvVars` + +Use these when you need a non-default mount path, a ConfigMap instead of a Secret, multiple volumes, or any other shape the wrapper does not cover: + +```yaml +extraVolumes: + - name: corporate-ca + secret: + secretName: corporate-ca-bundle + +extraVolumeMounts: + - name: corporate-ca + mountPath: /etc/ssl/certs/corporate + readOnly: true +``` + +Note: if you mount the CA outside `/etc/ssl/certs/` and set `SSL_CERT_FILE` via `extraEnvVars`, your bundle must include the public CAs you also need to trust — Go uses only that file when `SSL_CERT_FILE` is set. + +### Pod Security Standards + +Both options use `secret`-backed volumes, which are permitted under the Pod Security Standards `restricted` profile. `hostPath` mounts are not permitted under that profile and should not be used here. + +### Cluster-wide alternative + +If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/trust/trust-manager/) to distribute a corporate CA bundle into a well-known ConfigMap in every namespace, point `extraVolumes` / `extraVolumeMounts` at that ConfigMap instead of creating a per-namespace Secret. + ## Configurations | Key | Type | Default | Description | |-----|------|---------|-------------| | concurrencyPolicy | string | `"Replace"` | specifies how to treat concurrent executions of a Job that is created by this CronJob | | cronSchedule | string | `"*/5 * * * *"` | the cron schedule at which the reporter is triggered to report to Kosli | +| customCA | object | `{"enabled":false,"key":"ca.crt","secretName":""}` | convenience wrapper for mounting a corporate / custom CA bundle. See the "Running behind a TLS-inspecting proxy" section of the README for usage. | +| customCA.enabled | bool | `false` | enable mounting a corporate/custom CA bundle into the trust store | +| customCA.key | string | `"ca.crt"` | key within the Secret that holds the PEM-formatted CA certificate (single cert or multi-cert PEM bundle) | +| customCA.secretName | string | `""` | name of an existing Secret in the same namespace containing the CA bundle | +| env | object | `{}` | map of plain environment variables to inject into the reporter container. For a single-tenant Kosli instance, set KOSLI_HOST to https://.kosli.com. | +| extraEnvVars | list | `[]` | additional environment variables to inject into the reporter container. List of {name, value} or {name, valueFrom} entries, rendered verbatim into the container env. Supports plain values and valueFrom (secretKeyRef / configMapKeyRef). Note: entries here are appended after the chart's own env entries; on duplicate names the later entry wins. | +| extraVolumeMounts | list | `[]` | additional container-level volumeMounts for the reporter container. Rendered verbatim into the container spec alongside the chart's own mounts. | +| extraVolumes | list | `[]` | additional Pod-level volumes to attach to the reporter pod. Rendered verbatim into the Pod spec alongside the chart's own volumes. Use together with `extraVolumeMounts` to mount Secrets, ConfigMaps, or other volumes into the container. | | failedJobsHistoryLimit | int | `1` | specifies the number of failed finished jobs to keep | | fullnameOverride | string | `""` | overrides the fullname used for the created k8s resources. It has higher precedence than `nameOverride` | | image.pullPolicy | string | `"IfNotPresent"` | the kosli reporter image pull policy | From 5b5b9792945d067825caa78f8f23627e45d524c1 Mon Sep 17 00:00:00 2001 From: AlexKantor87 Date: Wed, 15 Apr 2026 13:04:17 +0100 Subject: [PATCH 23/41] Remove dead Pipedrive chat widget from docs footer (#783) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We moved off Pipedrive to HubSpot almost a year ago, but the LeadBooster loader script was still embedded on every docs page — silently pinging an abandoned Pipedrive workspace (companyId 7960593) on every visit and rendering nothing. Also blocking Netlify deploys: the link checker times out on leadbooster-chat.pipedrive.com from Netlify's build infra. Removing the script tag fixes the deploy as a side effect. --- docs.kosli.com/layouts/partials/docs/footer.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs.kosli.com/layouts/partials/docs/footer.html b/docs.kosli.com/layouts/partials/docs/footer.html index 552e1cb15..06b3691ad 100644 --- a/docs.kosli.com/layouts/partials/docs/footer.html +++ b/docs.kosli.com/layouts/partials/docs/footer.html @@ -49,5 +49,3 @@ {{ with $script.Content }} {{ end }} - - \ No newline at end of file From 106b2ae31eb654065a2b4f1d96982a644ddce544 Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen <93583343+ToreMerkely@users.noreply.github.com> Date: Wed, 15 Apr 2026 14:31:22 +0200 Subject: [PATCH 24/41] 5348 sonar qube pr problem (#780) * Added hard coded test for sonarqube PR scan to reproduce error server/#5348 * Fixed problem and change to use kosli attest sonar for test * Added sonar test data * green: add --sonar-ce-task-url flag for CI environments without report-task.txt access Allows users to pass the SonarQube CE task URL directly, bypassing the need for the .scannerwork/report-task.txt file. This solves the case where the scanner and the Kosli CLI run in different containers (e.g. Jenkins with Kubernetes pod agents) and don't share a filesystem. Co-Authored-By: Claude Opus 4.6 (1M context) * Updated docmentation * fix: check error before using http.NewRequest result in GetCETaskData Reorder the error check to come before taskRequest.Header.Add() to prevent a nil pointer dereference if NewRequest fails. Pre-existing bug, but now more reachable via the new --sonar-ce-task-url flag. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: check error before using http.NewRequest result in all sonar functions Same nil pointer dereference pattern as fixed in GetCETaskData, now fixed in GetProjectAnalysisFromRevision, GetProjectAnalysisFromAnalysisID, GetQualityGate, and GetTaskID. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: handle error return from Body.Close to satisfy errcheck linter Co-Authored-By: Claude Opus 4.6 (1M context) * docs: add PR fixture update instructions and fix inaccurate comment Add instructions for updating the PR scan test fixture to update-sonarqube-test-data.txt. Fix comment on readFile that incorrectly claimed it extracts the project key. Co-Authored-By: Claude Opus 4.6 (1M context) * fix: add missing defer Body.Close in GetProjectAnalysisFromAnalysisID Response body was never closed, leaking connections. Also moved the error check before the loop for consistency with GetPRAnalysisData. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- cmd/kosli/attestSonar.go | 27 +++- cmd/kosli/attestSonar_test.go | 10 ++ cmd/kosli/root.go | 1 + .../.scannerwork-pr/report-task.txt | 7 ++ .../sonar/update-sonarqube-test-data.txt | 17 +++ internal/sonar/sonar.go | 116 +++++++++++++++--- 6 files changed, 157 insertions(+), 21 deletions(-) create mode 100644 cmd/kosli/testdata/sonar/sonarcloud/.scannerwork-pr/report-task.txt diff --git a/cmd/kosli/attestSonar.go b/cmd/kosli/attestSonar.go index 9a8e6b4e1..418e716b5 100644 --- a/cmd/kosli/attestSonar.go +++ b/cmd/kosli/attestSonar.go @@ -33,19 +33,25 @@ const attestSonarShortDesc = `Report a SonarQube attestation to an artifact or a const attestSonarLongDesc = attestSonarShortDesc + ` Retrieves results for the specified scan from SonarQube Cloud or SonarQube Server and attests them to Kosli. The results are parsed to find the status of the project's quality gate which is used to determine the attestation's compliance status. +Both branch scans and pull request scans are supported. -The scan to be retrieved can be specified in two ways: +The scan to be retrieved can be specified in three ways: 1. (Default) Using metadata created by the Sonar scanner. By default this is located within a temporary ^.scannerwork^ folder in the repo base directory. If you have overriden the location of this folder by passing parameters to the Sonar scanner, or are running Kosli's CLI locally outside the repo's base directory, -you can provide the correct path using the ^--sonar-working-dir^ flag. This metadata is generated by a specific scan, allowing Kosli to retrieve the results of that scan. -If there are delays in the scan processing (either because the scanned project is very large, or because SonarQube is experiencing processing delays), it may happen that -the scan results are not available by the time the attest sonar command is executed. In this case you can use the ^--max-wait^ flag to retry the command while waiting for the scan to be processed. -This flag takes the maximum number of seconds to wait for the results to be available. The Kosli CLI will then attempt to retrieve the scan results until the maximum wait time is reached, with +you can provide the correct path using the ^--sonar-working-dir^ flag. This metadata is generated by a specific scan, allowing Kosli to retrieve the results of that scan. +If there are delays in the scan processing (either because the scanned project is very large, or because SonarQube is experiencing processing delays), it may happen that +the scan results are not available by the time the attest sonar command is executed. In this case you can use the ^--max-wait^ flag to retry the command while waiting for the scan to be processed. +This flag takes the maximum number of seconds to wait for the results to be available. The Kosli CLI will then attempt to retrieve the scan results until the maximum wait time is reached, with exponential backoff between retries. Once the results are available they are attested to Kosli as usual. 2. Providing the Sonar project key and the revision of the scan (plus the SonarQube server URL if relevant). If running the Kosli CLI in some CI/CD pipeline, the revision is defaulted to the commit SHA. If you are running the command locally, or have overriden the revision in SonarQube via parameters to the Sonar scanner, you can provide the correct revision using the ^--sonar-revision^ flag. Kosli then finds the scan results for the specified project key and revision. +Note that this method does not support pull request scans; use method 1 or 3 instead. + +3. Providing the CE task URL directly via ^--sonar-ce-task-url^. The CE task URL can be found in the ^report-task.txt^ file +generated by the Sonar scanner (the ^ceTaskUrl^ field). This is useful in CI/CD environments where the Sonar scanner and the Kosli CLI +run in different containers that do not share a filesystem, making the ^report-task.txt^ file inaccessible to the CLI. Note that if your project is very large and you are using SonarQube Cloud's automatic analysis, it is possible for the attest sonar command to run before the SonarQube Cloud scan is completed. In this case, we recommend using Kosli's Sonar webhook integration ( https://docs.kosli.com/integrations/sonar/ ) rather than the CLI to attest the scan results. @@ -107,6 +113,16 @@ kosli attest sonar \ --api-token yourAPIToken \ --org yourOrgName \ --max-wait 300 + +# report a SonarQube attestation using the CE task URL directly (useful when report-task.txt is not accessible): +kosli attest sonar \ + --name yourAttestationName \ + --flow yourFlowName \ + --trail yourTrailName \ + --sonar-api-token yourSonarAPIToken \ + --sonar-ce-task-url yourCETaskURL \ + --api-token yourAPIToken \ + --org yourOrgName ` func newAttestSonarCmd(out io.Writer) *cobra.Command { @@ -163,6 +179,7 @@ func newAttestSonarCmd(out io.Writer) *cobra.Command { cmd.Flags().StringVar(&o.projectKey, "sonar-project-key", "", sonarProjectKeyFlag) cmd.Flags().StringVar(&o.serverURL, "sonar-server-url", "https://sonarcloud.io", sonarServerURLFlag) cmd.Flags().StringVar(&o.revision, "sonar-revision", o.commitSHA, sonarRevisionFlag) + cmd.Flags().StringVar(&o.ceTaskURL, "sonar-ce-task-url", "", sonarCETaskURLFlag) cmd.Flags().IntVar(&o.maxWait, "max-wait", 30, sonarMaxWaitFlag) err := RequireFlags(cmd, []string{"flow", "trail", "name", "sonar-api-token"}) diff --git a/cmd/kosli/attestSonar_test.go b/cmd/kosli/attestSonar_test.go index 8b467768c..e2a217376 100644 --- a/cmd/kosli/attestSonar_test.go +++ b/cmd/kosli/attestSonar_test.go @@ -155,6 +155,16 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --sonar-project-key anyKey --sonar-revision anyRevision --sonar-server-url http://example.com %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, + { + name: "can attest sonar for a pull request scan using report-task.txt", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork-pr %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, + { + name: "can attest sonar using --sonar-ce-task-url without report-task.txt", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZrs5eywBfkZKeU0sde9 %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, { wantError: true, name: "if outdated task given (i.e. we try to get results for an older scan that SonarCloud has deleted), we get an error", diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index 0e44586f8..f7a851a88 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -241,6 +241,7 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file, sonarServerURLFlag = "[conditional] The URL of your SonarQube server. Only required if you are using SonarQube and not using SonarQube's metadata file to get scan results." sonarRevisionFlag = "[conditional] The revision of the SonarCloud/SonarQube project. Only required if you want to use the project key/revision to get the scan results rather than using Sonar's metadata file and you have overridden the default revision, or you aren't using a CI. Defaults to the value of the git commit flag." sonarMaxWaitFlag = "[optional] Allow the command to wait and retry fetching the scan results from SonarQube, up to the maximum number of seconds provided, with exponential backoff. Useful when using SonarQube's metadata file to retrieve and attest scans that take a long time to process . Defaults to 30 seconds." + sonarCETaskURLFlag = "[conditional] The URL of the SonarQube CE task. Can be used instead of --sonar-working-dir when the report-task.txt file is not accessible, e.g. due to container isolation in CI/CD pipelines." logicalEnvFlag = "[required] The logical environment." physicalEnvFlag = "[required] The physical environment." attestationTypeDescriptionFlag = "[optional] The attestation type description." diff --git a/cmd/kosli/testdata/sonar/sonarcloud/.scannerwork-pr/report-task.txt b/cmd/kosli/testdata/sonar/sonarcloud/.scannerwork-pr/report-task.txt new file mode 100644 index 000000000..7c015d438 --- /dev/null +++ b/cmd/kosli/testdata/sonar/sonarcloud/.scannerwork-pr/report-task.txt @@ -0,0 +1,7 @@ +organization=cyber-dojo +projectKey=cyber-dojo_differ +serverUrl=https://sonarcloud.io +serverVersion=8.0.0.56246 +dashboardUrl=https://sonarcloud.io/dashboard?id=cyber-dojo_differ&pullRequest=359 +ceTaskId=AZ2Qge89T7Y829rQbv87 +ceTaskUrl=https://sonarcloud.io/api/ce/task?id=AZ2Qge89T7Y829rQbv87 diff --git a/cmd/kosli/testdata/sonar/update-sonarqube-test-data.txt b/cmd/kosli/testdata/sonar/update-sonarqube-test-data.txt index 57e1a5fec..9c7845e76 100644 --- a/cmd/kosli/testdata/sonar/update-sonarqube-test-data.txt +++ b/cmd/kosli/testdata/sonar/update-sonarqube-test-data.txt @@ -37,6 +37,23 @@ After 5 years all snapshots are deleted, including snapshots marked by version e 7. In report-task.txt, replace the value of ceTaskId with the ID you just copied. - Also replace the id at the end of the ceTaskUrl with that ID. +## Instructions for updating PR scan test data in .scannerwork-pr/report-task.txt + +The PR scan fixture works the same way as the main branch fixture, but the CE task must be from a pull request scan +(i.e. the CE task API response will have a `pullRequest` field instead of `branch`). + +1. Find a recent pull request analysis for the project. You can use the project_pull_requests/list API: + - The endpoint is https://sonarcloud.io/api/project_pull_requests/list?project=cyber-dojo_differ + - This returns a JSON object with an array of pull requests, each with a key, analysisDate, and commit SHA. +2. Choose a pull request that has a recent analysis. +3. Find the CE task for that PR analysis using the CE activity API: + - The endpoint is https://sonarcloud.io/api/ce/activity?component=cyber-dojo_differ + - Look for a task with a `pullRequest` field matching the PR key from step 2. +4. Copy the task ID and update .scannerwork-pr/report-task.txt: + - Replace the ceTaskId value with the new task ID. + - Replace the id at the end of the ceTaskUrl with the same ID. + - Update the pullRequest parameter in the dashboardUrl if the PR number has changed. + ## Instructions for updating test data passed as CLI command arguments Instead of using the report-task.txt file, some of our sonar tests take the key of the SonarQube project and a git commit as arguments to the CLI command, which are then used to find the relevant analysis in SonarQube. This should rarely need to be updated, since once an analysis snapshot is 4 weeks old in SonarQube, it will remain in the database until at least 2 years. diff --git a/internal/sonar/sonar.go b/internal/sonar/sonar.go index 33f495875..e26322710 100644 --- a/internal/sonar/sonar.go +++ b/internal/sonar/sonar.go @@ -33,6 +33,7 @@ type SonarResults struct { Revision string `json:"revision"` Project Project `json:"project"` Branch *Branch `json:"branch,omitempty"` + PullRequest *PullRequest `json:"pullRequest,omitempty"` QualityGate *QualityGate `json:"qualityGate,omitempty"` } @@ -47,6 +48,10 @@ type Branch struct { Type string `json:"type,omitempty"` } +type PullRequest struct { + Key string `json:"key"` +} + type QualityGate struct { Status string `json:"status"` Conditions []Condition `json:"conditions"` @@ -92,6 +97,7 @@ type Task struct { Status string `json:"status"` Branch string `json:"branch"` BranchType string `json:"branchType"` + PullRequest string `json:"pullRequest"` } type ActivityResponse struct { @@ -110,6 +116,22 @@ type Analysis struct { Revision string `json:"revision"` } +// These are the structs for the response from the project_pull_requests/list API +type PullRequestsResponse struct { + PullRequests []PullRequestInfo `json:"pullRequests"` + Errors []Error `json:"errors,omitempty"` +} + +type PullRequestInfo struct { + Key string `json:"key"` + AnalysisDate string `json:"analysisDate"` + Commit PRCommit `json:"commit"` +} + +type PRCommit struct { + SHA string `json:"sha"` +} + // Struct for error messages from sonar APIs type Error struct { Msg string `json:"msg"` @@ -152,10 +174,18 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error return nil, fmt.Errorf("API token must be given to retrieve data from SonarQube") } - // Read the report-task.txt file (if it exists) to get the project key, server URL, dashboard URL and ceTaskURL + // Read the report-task.txt file (if it exists) to get the server URL, dashboard URL and ceTaskURL err = sc.readFile(project, sonarResults, logger) if err != nil { - if sc.projectKey == "" || sc.revision == "" { + if sc.CETaskUrl != "" { + // If the CE task URL is provided directly (e.g. via --sonar-ce-task-url), we can skip the report-task.txt + // and use the CE task URL to get the data. Extract the server URL from the CE task URL. + parsedURL, parseErr := url.Parse(sc.CETaskUrl) + if parseErr != nil { + return nil, fmt.Errorf("failed to parse CE task URL: %s", parseErr) + } + sonarResults.ServerUrl = fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host) + } else if sc.projectKey == "" || sc.revision == "" { return nil, fmt.Errorf("%s. Alternatively provide the project key and revision for the scan to attest", err) // If the report-task.txt does not exist, but we've been given the project key and revision, we can still get the data } else { @@ -184,10 +214,19 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error return nil, err } - //Get project revision and scan date/time from the projectAnalyses API - err = GetProjectAnalysisFromAnalysisID(httpClient, sonarResults, project, analysisID, tokenHeader) - if err != nil { - return nil, err + if sonarResults.PullRequest != nil { + // For PR scans, project_analyses/search does not return PR analyses on SonarCloud. + // Use the project_pull_requests/list API to get the revision and analysis date instead. + err = GetPRAnalysisData(httpClient, sonarResults, project, sonarResults.PullRequest.Key, tokenHeader) + if err != nil { + return nil, err + } + } else { + //Get project revision and scan date/time from the projectAnalyses API + err = GetProjectAnalysisFromAnalysisID(httpClient, sonarResults, project, analysisID, tokenHeader) + if err != nil { + return nil, err + } } } @@ -237,10 +276,10 @@ func (sc *SonarConfig) readFile(project *Project, results *SonarResults, logger func GetCETaskData(httpClient *http.Client, project *Project, sonarResults *SonarResults, ceTaskURL, tokenHeader string, maxWait int, logger *log.Logger) (string, error) { taskRequest, err := http.NewRequest("GET", ceTaskURL, nil) - taskRequest.Header.Add("Authorization", tokenHeader) if err != nil { return "", err } + taskRequest.Header.Add("Authorization", tokenHeader) wait := 1 // start wait period retries := 0 // number of retries so far @@ -312,7 +351,10 @@ func GetCETaskData(httpClient *http.Client, project *Project, sonarResults *Sona } } - if taskResponseData.Task.Branch != "" { + if taskResponseData.Task.PullRequest != "" { + sonarResults.PullRequest = &PullRequest{Key: taskResponseData.Task.PullRequest} + sonarResults.Branch = nil + } else if taskResponseData.Task.Branch != "" { sonarResults.Branch = &Branch{} sonarResults.Branch.Name = taskResponseData.Task.Branch sonarResults.Branch.Type = taskResponseData.Task.BranchType @@ -331,10 +373,10 @@ func GetProjectAnalysisFromRevision(httpClient *http.Client, sonarResults *Sonar return "", err } projectAnalysesRequest, err := http.NewRequest("GET", projectAnalysesURL, nil) - projectAnalysesRequest.Header.Add("Authorization", tokenHeader) if err != nil { return "", err } + projectAnalysesRequest.Header.Add("Authorization", tokenHeader) projectAnalysesResponse, err := httpClient.Do(projectAnalysesRequest) if err != nil { @@ -376,15 +418,16 @@ func GetProjectAnalysisFromAnalysisID(httpClient *http.Client, sonarResults *Son return err } projectAnalysesRequest, err := http.NewRequest("GET", projectAnalysesURL, nil) - projectAnalysesRequest.Header.Add("Authorization", tokenHeader) if err != nil { return err } + projectAnalysesRequest.Header.Add("Authorization", tokenHeader) projectAnalysesResponse, err := httpClient.Do(projectAnalysesRequest) if err != nil { return err } + defer func() { _ = projectAnalysesResponse.Body.Close() }() projectAnalysesData := &ProjectAnalyses{} err = json.NewDecoder(projectAnalysesResponse.Body).Decode(projectAnalysesData) @@ -392,6 +435,10 @@ func GetProjectAnalysisFromAnalysisID(httpClient *http.Client, sonarResults *Son return fmt.Errorf("please check your API token is correct and you have the correct permissions in SonarQube") } + if projectAnalysesData.Errors != nil { + return fmt.Errorf("SonarQube error: %s", projectAnalysesData.Errors[0].Msg) + } + for analysis := range projectAnalysesData.Analyses { if projectAnalysesData.Analyses[analysis].Key == analysisID { sonarResults.AnalaysedAt = projectAnalysesData.Analyses[analysis].Date @@ -400,10 +447,6 @@ func GetProjectAnalysisFromAnalysisID(httpClient *http.Client, sonarResults *Son } } - if projectAnalysesData.Errors != nil { - return fmt.Errorf("SonarQube error: %s", projectAnalysesData.Errors[0].Msg) - } - if sonarResults.AnalaysedAt == "" { return fmt.Errorf("analysis with ID %s not found on %s. Snapshot may have been deleted by SonarQube", analysisID, sonarResults.ServerUrl) } @@ -411,16 +454,57 @@ func GetProjectAnalysisFromAnalysisID(httpClient *http.Client, sonarResults *Son return nil } +// GetPRAnalysisData retrieves the revision and analysis date for a pull request scan +// from the project_pull_requests/list API. This is needed because the project_analyses/search +// API does not return PR analyses on SonarCloud. +func GetPRAnalysisData(httpClient *http.Client, sonarResults *SonarResults, project *Project, prKey, tokenHeader string) error { + prURL, err := sonarURL(sonarResults.ServerUrl, "api/project_pull_requests/list", url.Values{"project": {project.Key}}) + if err != nil { + return err + } + prRequest, err := http.NewRequest("GET", prURL, nil) + if err != nil { + return err + } + prRequest.Header.Add("Authorization", tokenHeader) + + prResponse, err := httpClient.Do(prRequest) + if err != nil { + return err + } + defer func() { _ = prResponse.Body.Close() }() + + prData := &PullRequestsResponse{} + err = json.NewDecoder(prResponse.Body).Decode(prData) + if err != nil { + return fmt.Errorf("please check your API token is correct and you have the correct permissions in SonarQube") + } + + if prData.Errors != nil { + return fmt.Errorf("SonarQube error: %s", prData.Errors[0].Msg) + } + + for _, pr := range prData.PullRequests { + if pr.Key == prKey { + sonarResults.AnalaysedAt = pr.AnalysisDate + sonarResults.Revision = pr.Commit.SHA + return nil + } + } + + return fmt.Errorf("pull request %s not found for project %s on %s", prKey, project.Key, sonarResults.ServerUrl) +} + func GetQualityGate(httpClient *http.Client, sonarResults *SonarResults, qualityGate *QualityGate, analysisID, tokenHeader string) (*QualityGate, error) { qualityGateURL, err := sonarURL(sonarResults.ServerUrl, "api/qualitygates/project_status", url.Values{"analysisId": {analysisID}}) if err != nil { return nil, err } qualityGateRequest, err := http.NewRequest("GET", qualityGateURL, nil) - qualityGateRequest.Header.Add("Authorization", tokenHeader) if err != nil { return nil, err } + qualityGateRequest.Header.Add("Authorization", tokenHeader) qualityGateResponse, err := httpClient.Do(qualityGateRequest) if err != nil { @@ -460,10 +544,10 @@ func GetTaskID(httpClient *http.Client, sonarResults *SonarResults, project *Pro return err } CEActivityRequest, err := http.NewRequest("GET", CEActivityURL, nil) - CEActivityRequest.Header.Add("Authorization", tokenHeader) if err != nil { return err } + CEActivityRequest.Header.Add("Authorization", tokenHeader) CEActivityResponse, err := httpClient.Do(CEActivityRequest) if err != nil { From 200bf5351e139a11c3792343ea8525e8eda489dd Mon Sep 17 00:00:00 2001 From: Marko Bevc Date: Wed, 15 Apr 2026 16:48:10 +0100 Subject: [PATCH 25/41] feat: use appVersion as a default CLI version (#785) * feat: use appVersion as a default CLI version * feat: use appVersion as a default CLI version - add tag comment * feat: use appVersion as a default CLI version - bump Chart version --- charts/k8s-reporter/Chart.yaml | 13 ++++++------- charts/k8s-reporter/templates/cronjob.yaml | 2 +- charts/k8s-reporter/values.yaml | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/charts/k8s-reporter/Chart.yaml b/charts/k8s-reporter/Chart.yaml index 4fece5f6f..f6db3aa15 100644 --- a/charts/k8s-reporter/Chart.yaml +++ b/charts/k8s-reporter/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 name: k8s-reporter -description: A Helm chart for installing the Kosli K8S reporter as a cronjob. +description: A Helm chart for installing the Kosli K8S reporter as a CronJob. # A chart can be either an 'application' or a 'library' chart. # @@ -15,10 +15,9 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 2.2.0 +version: 2.2.1 -# This is the version number of the application being deployed. This version number should be -# incremented each time you make changes to the application. Versions are not expected to -# follow Semantic Versioning. They should reflect the version the application is using. -# It is recommended to use it with quotes. -appVersion: "2.12.0" +# This is the version number of the (CLI) application being deployed. This version number should be +# incremented each time you make changes to the application. They should reflect the version the +# application is using. It is recommended to use it with quotes. +appVersion: "v2.12.0" diff --git a/charts/k8s-reporter/templates/cronjob.yaml b/charts/k8s-reporter/templates/cronjob.yaml index b54f6e7be..a5a11b813 100644 --- a/charts/k8s-reporter/templates/cronjob.yaml +++ b/charts/k8s-reporter/templates/cronjob.yaml @@ -38,7 +38,7 @@ spec: {{- end }} containers: - name: reporter - image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" imagePullPolicy: {{ .Values.image.pullPolicy }} volumeMounts: - name: environments-config diff --git a/charts/k8s-reporter/values.yaml b/charts/k8s-reporter/values.yaml index a13f74495..191e7176a 100644 --- a/charts/k8s-reporter/values.yaml +++ b/charts/k8s-reporter/values.yaml @@ -8,7 +8,7 @@ image: # -- the kosli reporter image pull policy pullPolicy: IfNotPresent # -- the kosli reporter image tag, overrides the image tag whose default is the chart appVersion. - tag: "v2.12.0" + tag: "" # defaults to chart appVersion # -- overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one nameOverride: "" From 9d55770cc27aace8502ab543db92a045491fbc0a Mon Sep 17 00:00:00 2001 From: Faye <108031168+FayeSGW@users.noreply.github.com> Date: Wed, 15 Apr 2026 18:03:30 +0200 Subject: [PATCH 26/41] Enable retrieving sonarqube scan results for PRs (#784) * Enable retrieving sonarqube scan results for PRs * Fix failing test * Resolve merge conflicts and update docs for --pull-request flag Merge both --pull-request and --sonar-ce-task-url flags, update long description to document method 2 supporting PR scans, add key+PR example, fix test golden output, and renumber tests. Co-Authored-By: Claude Opus 4.6 (1M context) * improve PR support and fix resource leaks in sonar attestation --------- Co-authored-by: Tore Martin Hagen Co-authored-by: Claude Opus 4.6 (1M context) --- cmd/kosli/attestSonar.go | 45 ++++++--- cmd/kosli/attestSonar_test.go | 110 ++++++++++++++++------ cmd/kosli/root.go | 11 ++- internal/sonar/sonar.go | 170 ++++++++++++++++++++-------------- 4 files changed, 221 insertions(+), 115 deletions(-) diff --git a/cmd/kosli/attestSonar.go b/cmd/kosli/attestSonar.go index 418e716b5..962b76645 100644 --- a/cmd/kosli/attestSonar.go +++ b/cmd/kosli/attestSonar.go @@ -18,14 +18,15 @@ type SonarAttestationPayload struct { type attestSonarOptions struct { *CommonAttestationOptions - apiToken string - workingDir string - ceTaskURL string - projectKey string - serverURL string - revision string - maxWait int - payload SonarAttestationPayload + apiToken string + workingDir string + ceTaskURL string + projectKey string + serverURL string + revision string + pullRequest string + maxWait int + payload SonarAttestationPayload } const attestSonarShortDesc = `Report a SonarQube attestation to an artifact or a trail in a Kosli flow. ` @@ -44,10 +45,11 @@ the scan results are not available by the time the attest sonar command is execu This flag takes the maximum number of seconds to wait for the results to be available. The Kosli CLI will then attempt to retrieve the scan results until the maximum wait time is reached, with exponential backoff between retries. Once the results are available they are attested to Kosli as usual. -2. Providing the Sonar project key and the revision of the scan (plus the SonarQube server URL if relevant). If running the Kosli CLI in some CI/CD pipeline, the revision -is defaulted to the commit SHA. If you are running the command locally, or have overriden the revision in SonarQube via parameters to the Sonar scanner, you can -provide the correct revision using the ^--sonar-revision^ flag. Kosli then finds the scan results for the specified project key and revision. -Note that this method does not support pull request scans; use method 1 or 3 instead. +2. Providing the Sonar project key and either the revision or the pull-request ID of the scan (plus the SonarQube server URL if relevant). +For branch scans: if running the Kosli CLI in some CI/CD pipeline, the revision is defaulted to the commit SHA. If you are running the command locally, +or have overriden the revision in SonarQube via parameters to the Sonar scanner, you can provide the correct revision using the ^--sonar-revision^ flag. +For pull request scans: provide the pull-request ID using the ^--pull-request^ flag instead of the revision. +Kosli then finds the scan results for the specified project key and revision or pull-request ID. 3. Providing the CE task URL directly via ^--sonar-ce-task-url^. The CE task URL can be found in the ^report-task.txt^ file generated by the Sonar scanner (the ^ceTaskUrl^ field). This is useful in CI/CD environments where the Sonar scanner and the Kosli CLI @@ -102,6 +104,17 @@ kosli attest sonar \ --api-token yourAPIToken \ --org yourOrgName \ +# report a SonarQube Cloud attestation about a trail for a pull request scan using key/pull-request: +kosli attest sonar \ + --name yourAttestationName \ + --flow yourFlowName \ + --trail yourTrailName \ + --sonar-api-token yourSonarAPIToken \ + --sonar-project-key yourSonarProjectKey \ + --pull-request yourPullRequestID \ + --api-token yourAPIToken \ + --org yourOrgName \ + # report a SonarQube Cloud attestation about a trail with an attachment using SonarQube's metadata, waiting for up to 300 seconds for the results to be available: kosli attest sonar \ --name yourAttestationName \ @@ -158,6 +171,11 @@ func newAttestSonarCmd(out io.Writer) *cobra.Command { return err } + err = MuXRequiredFlags(cmd, []string{"sonar-revision", "pull-request"}, false) + if err != nil { + return err + } + err = ValidateAttestationArtifactArg(args, o.fingerprintOptions.artifactType, o.payload.ArtifactFingerprint) if err != nil { return ErrorBeforePrintingUsage(cmd, err.Error()) @@ -179,6 +197,7 @@ func newAttestSonarCmd(out io.Writer) *cobra.Command { cmd.Flags().StringVar(&o.projectKey, "sonar-project-key", "", sonarProjectKeyFlag) cmd.Flags().StringVar(&o.serverURL, "sonar-server-url", "https://sonarcloud.io", sonarServerURLFlag) cmd.Flags().StringVar(&o.revision, "sonar-revision", o.commitSHA, sonarRevisionFlag) + cmd.Flags().StringVar(&o.pullRequest, "pull-request", "", sonarPRFlag) cmd.Flags().StringVar(&o.ceTaskURL, "sonar-ce-task-url", "", sonarCETaskURLFlag) cmd.Flags().IntVar(&o.maxWait, "max-wait", 30, sonarMaxWaitFlag) @@ -201,7 +220,7 @@ func (o *attestSonarOptions) run(args []string) error { return err } - sc := sonar.NewSonarConfig(o.apiToken, o.workingDir, o.ceTaskURL, o.projectKey, o.serverURL, o.revision, o.maxWait) + sc := sonar.NewSonarConfig(o.apiToken, o.workingDir, o.ceTaskURL, o.projectKey, o.serverURL, o.revision, o.pullRequest, o.maxWait) o.payload.SonarResults, err = sc.GetSonarResults(logger) if err != nil { diff --git a/cmd/kosli/attestSonar_test.go b/cmd/kosli/attestSonar_test.go index e2a217376..0ee1b7375 100644 --- a/cmd/kosli/attestSonar_test.go +++ b/cmd/kosli/attestSonar_test.go @@ -81,107 +81,158 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { tests := []cmdTestCase{ { wantError: true, - name: "fails when more arguments are provided", + name: "01 fails when more arguments are provided", cmd: fmt.Sprintf("attest sonar foo bar --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "Error: accepts at most 1 arg(s), received 2 [foo bar]\n", }, { wantError: true, - name: "fails when both --fingerprint and --artifact-type", + name: "02 fails when both --fingerprint and --artifact-type", cmd: fmt.Sprintf("attest sonar testdata/file1 --fingerprint xxxx --artifact-type file --name bar --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "Error: only one of --fingerprint, --artifact-type is allowed\n", }, { wantError: true, - name: "fails when --fingerprint is not valid", + name: "03 fails when --fingerprint is not valid", cmd: fmt.Sprintf("attest sonar --name foo-s --fingerprint xxxx --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "Error: xxxx is not a valid SHA256 fingerprint. It should match the pattern ^([a-f0-9]{64})$\nUsage: kosli attest sonar [IMAGE-NAME | FILE-PATH | DIR-PATH] [flags]\n", }, { wantError: true, - name: "attesting against an artifact that does not exist fails", + name: "04 attesting against an artifact that does not exist fails", cmd: fmt.Sprintf("attest sonar --fingerprint 1234e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 --name foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "Error: Artifact with fingerprint 1234e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 does not exist in trail \"test-123\" of flow \"attest-sonar\" belonging to organization \"docs-cmd-test-user\"\n", }, { - name: "can attest sonar against an artifact using artifact name and --artifact-type", + name: "05 can attest sonar against an artifact using artifact name and --artifact-type", cmd: fmt.Sprintf("attest sonar testdata/file1 --artifact-type file --name foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { - name: "can attest sonar against an artifact using artifact name and --artifact-type when --name does not exist in the trail template", + name: "06 can attest sonar against an artifact using artifact name and --artifact-type when --name does not exist in the trail template", cmd: fmt.Sprintf("attest sonar testdata/file1 --artifact-type file --name bar --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'bar' is reported to trail: test-123\n", }, { - name: "can attest sonar against an artifact using --fingerprint", + name: "07 can attest sonar against an artifact using --fingerprint", cmd: fmt.Sprintf("attest sonar --fingerprint 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 --name foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { - name: "can attest sonar against a trail", + name: "08 can attest sonar against a trail", cmd: fmt.Sprintf("attest sonar --name bar --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'bar' is reported to trail: test-123\n", }, { - name: "can attest sonar against a trail when name is not found in the trail template", + name: "09 can attest sonar against a trail when name is not found in the trail template", cmd: fmt.Sprintf("attest sonar --name additional --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'additional' is reported to trail: test-123\n", }, { - name: "can attest sonar against an artifact it is created using dot syntax in --name", + name: "10 can attest sonar against an artifact it is created using dot syntax in --name", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { wantError: true, - name: "trying to fetch data from SonarCloud with incorrect API token gives error", + name: "11 trying to fetch data from SonarCloud with incorrect API token gives error", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-api-token xxxx --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork %s", suite.defaultKosliArguments), golden: "Error: please check your API token is correct and you have the correct permissions in SonarQube\n", }, { wantError: true, - name: "if no path to the scannerwork directory is provided and the command is not being run in the same base directory, we get an error", + name: "12 if no path to the scannerwork directory is provided and the command is not being run in the same base directory, and project key/revision/pull request not provided, we get an error", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com %s", suite.defaultKosliArguments), - golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and revision for the scan to attest\n", + golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and either revision or pull-request ID for the scan to attest\n", }, { - name: "can retrieve scan results using project key and revision and attest them", + name: "13 can retrieve scan results using project key and revision and attest them", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --sonar-revision 38f3dc8b63abb632ac94a12b3f818b49f8047fa1 %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { - name: "if report-task.txt file found, we don't use the sonar-project-key, sonar-revision or sonar-server-url flags", + name: "14 if report-task.txt file found, we don't use the sonar-project-key, sonar-revision or sonar-server-url flags", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --sonar-project-key anyKey --sonar-revision anyRevision --sonar-server-url http://example.com %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { - name: "can attest sonar for a pull request scan using report-task.txt", + wantError: true, + name: "15 if outdated task given (i.e. we try to get results for an older scan that SonarCloud has deleted), we get an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork-old %s", suite.defaultKosliArguments), + golden: "Error: No activity found for task 'AZERk4uWpzGpahwkB9ac' on https://sonarcloud.io. \nSonarQube may be experiencing problems, please check https://status.sonarqube.com/ and try again later. \nOtherwise if you are attesting an older scan, the snapshot may have been deleted by SonarQube\n", + }, + { + wantError: true, + name: "16 if incorrect revision given (or the scan for the given revision has been deleted by SonarCloud)", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --sonar-revision b4d1053f2aac18c9fb4b9a289a8289199c932e12 %s", suite.defaultKosliArguments), + golden: "Error: analysis for revision b4d1053f2aac18c9fb4b9a289a8289199c932e12 of project cyber-dojo_differ not found. Check the revision is correct. \nThe scan may still be being processed by SonarQube, try again later.\n Otherwise if you are attesting an older scan, the snapshot may also have been deleted by SonarQube\n", + }, + { + wantError: true, + name: "17 if incorrect project key given, we get an error message from Sonar", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo-differ --sonar-revision 38f3dc8b63abb632ac94a12b3f818b49f8047fa1 %s", suite.defaultKosliArguments), + golden: "Error: SonarQube error: Component key 'cyber-dojo-differ' not found\n", + }, + { + name: "18 can retrieve scan results using report-task.txt file and pull-request flag", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --pull-request 359 %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, + { + wantError: true, + name: "19 providing an incorrect pull-request ID gives an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --pull-request 1 %s", suite.defaultKosliArguments), + golden: "Error: pull request 1 not found for project cyber-dojo_differ on https://sonarcloud.io\n", + }, + { + name: "20 can retrieve scan results using project key and pull-request flag", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --pull-request 359 %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, + { + name: "21 can attest sonar for a pull request scan using report-task.txt without --pull-request flag", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork-pr %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { - name: "can attest sonar using --sonar-ce-task-url without report-task.txt", + name: "22 can attest sonar using --sonar-ce-task-url without report-task.txt", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZrs5eywBfkZKeU0sde9 %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { wantError: true, - name: "if outdated task given (i.e. we try to get results for an older scan that SonarCloud has deleted), we get an error", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork-old %s", suite.defaultKosliArguments), - golden: "Error: No activity found for task 'AZERk4uWpzGpahwkB9ac' on https://sonarcloud.io. \nSonarQube may be experiencing problems, please check https://status.sonarqube.com/ and try again later. \nOtherwise if you are attesting an older scan, the snapshot may have been deleted by SonarQube\n", + name: "23 if no report-task.txt is available, and project key is not provided, we get an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com %s", suite.defaultKosliArguments), + golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and either revision or pull-request ID for the scan to attest\n", }, { wantError: true, - name: "if incorrect revision given (or the scan for the given revision has been deleted by SonarCloud)", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --sonar-revision b4d1053f2aac18c9fb4b9a289a8289199c932e12 %s", suite.defaultKosliArguments), - golden: "Error: analysis for revision b4d1053f2aac18c9fb4b9a289a8289199c932e12 of project cyber-dojo_differ not found. Check the revision is correct. \nThe scan may still be being processed by SonarQube, try again later.\n Otherwise if you are attesting an older scan, the snapshot may also have been deleted by SonarQube\n", + name: "24 if no report-task.txt is available, and neither revision or pull-request is given, we get an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ %s", suite.defaultKosliArguments), + golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and either revision or pull-request ID for the scan to attest\n", }, { wantError: true, - name: "if incorrect project key given, we get an error message from Sonar", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo-differ --sonar-revision 38f3dc8b63abb632ac94a12b3f818b49f8047fa1 %s", suite.defaultKosliArguments), - golden: "Error: SonarQube error: Component key 'cyber-dojo-differ' not found\n", + name: "25 can't provide both revision and pull-request", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --sonar-revision xxx --pull-request 5 %s", suite.defaultKosliArguments), + golden: "Error: only one of --sonar-revision, --pull-request is allowed\n", + }, + { + name: "26 can attest sonar for a pull request scan using --sonar-ce-task-url", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZ2Qge89T7Y829rQbv87 %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, + { + wantError: true, + name: "27 providing an incorrect pull-request ID with project key gives an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --pull-request 1 %s", suite.defaultKosliArguments), + golden: "Error: pull request 1 not found for project cyber-dojo_differ on https://sonarcloud.io\n", + }, + { + wantError: true, + name: "28 if expired CE task URL is provided, we get an error", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZERk4uWpzGpahwkB9ac %s", suite.defaultKosliArguments), + golden: "Error: No activity found for task 'AZERk4uWpzGpahwkB9ac' on https://sonarcloud.io. \nSonarQube may be experiencing problems, please check https://status.sonarqube.com/ and try again later. \nOtherwise if you are attesting an older scan, the snapshot may have been deleted by SonarQube\n", }, } @@ -254,7 +305,7 @@ func (suite *AttestSonarQubeCommandTestSuite) TestAttestSonarQubeCmd() { wantError: true, name: "if no path to the scannerwork directory is provided and the command is not being run in the same base directory, we get an error", cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com %s", suite.defaultKosliArguments), - golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and revision for the scan to attest\n", + golden: "Error: open .scannerwork/report-task.txt: no such file or directory. Check your working directory is set correctly. Alternatively provide the project key and either revision or pull-request ID for the scan to attest\n", }, { name: "can retrieve scan results using project key and revision and attest them", @@ -284,6 +335,11 @@ func (suite *AttestSonarQubeCommandTestSuite) TestAttestSonarQubeCmd() { cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarqube/.scannerwork --sonar-project-key anyKey --sonar-revision anyRevision --sonar-server-url http://example.com %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, + { + name: "can retrieve scan results using report-task.txt file and pull-request flag", + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarqube/.scannerwork --pull-request 359 --sonar-server-url http://example.com %s", suite.defaultKosliArguments), + golden: "sonar attestation 'foo' is reported to trail: test-123\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index f7a851a88..86b35e0a1 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -235,11 +235,12 @@ The ^.kosli_ignore^ will be treated as part of the artifact like any other file, policyName = "[optional] policy name (can be specified multiple times)" attachPolicyEnvFlag = "the list of environment names to attach the policy to" detachPolicyEnvFlag = "the list of environment names to detach the policy from" - sonarAPITokenFlag = "[required] SonarCloud/SonarQube API token." - sonarWorkingDirFlag = "[conditional] The base directory of the repo scanned by SonarCloud/SonarQube. Only required if you have overriden the default in the sonar scanner or you are running the CLI locally in a separate folder from the repo." - sonarProjectKeyFlag = "[conditional] The project key of the SonarCloud/SonarQube project. Only required if you want to use the project key/revision to get the scan results rather than using Sonar's metadata file." - sonarServerURLFlag = "[conditional] The URL of your SonarQube server. Only required if you are using SonarQube and not using SonarQube's metadata file to get scan results." - sonarRevisionFlag = "[conditional] The revision of the SonarCloud/SonarQube project. Only required if you want to use the project key/revision to get the scan results rather than using Sonar's metadata file and you have overridden the default revision, or you aren't using a CI. Defaults to the value of the git commit flag." + sonarAPITokenFlag = "[required] SonarQube API token." + sonarWorkingDirFlag = "[conditional] The base directory of the repo scanned by SonarQube. Only required if you have overriden the default in the sonar scanner or you are running the CLI locally in a separate folder from the repo." + sonarProjectKeyFlag = "[conditional] The project key of the SonarQube project. Only required if you want to use the project key/revision/pull-request to get the scan results rather than using Sonar's metadata file." + sonarServerURLFlag = "[conditional] The URL of your SonarQube server. Only required if you are using SonarQube Server and not using SonarQube's metadata file to get scan results." + sonarRevisionFlag = "[conditional] The revision of the SonarQube project. Only required if you want to use the project key/revision to get the scan results rather than using Sonar's metadata file and you have overridden the default revision, or you aren't using a CI. Defaults to the value of the git commit flag. Cannot be used with --pull-request." + sonarPRFlag = "[conditional] The ID of the pull-request. Only required if you want to use the project key/pull-request to get the scan results rather than using Sonar's metadata file. Cannot be used with --sonar-revision." sonarMaxWaitFlag = "[optional] Allow the command to wait and retry fetching the scan results from SonarQube, up to the maximum number of seconds provided, with exponential backoff. Useful when using SonarQube's metadata file to retrieve and attest scans that take a long time to process . Defaults to 30 seconds." sonarCETaskURLFlag = "[conditional] The URL of the SonarQube CE task. Can be used instead of --sonar-working-dir when the report-task.txt file is not accessible, e.g. due to container isolation in CI/CD pipelines." logicalEnvFlag = "[required] The logical environment." diff --git a/internal/sonar/sonar.go b/internal/sonar/sonar.go index e26322710..5bf2a8c15 100644 --- a/internal/sonar/sonar.go +++ b/internal/sonar/sonar.go @@ -15,13 +15,14 @@ import ( ) type SonarConfig struct { - APIToken string - WorkingDir string - CETaskUrl string - revision string - projectKey string - serverURL string - maxWait int + APIToken string + WorkingDir string + CETaskUrl string + revision string + projectKey string + serverURL string + pullRequest string + maxWait int } // Structs to build the JSON for our attestation payload @@ -29,11 +30,11 @@ type SonarResults struct { ServerUrl string `json:"serverUrl"` TaskID string `json:"taskId"` Status string `json:"status"` - AnalaysedAt string `json:"analysedAt"` + AnalysedAt string `json:"analysedAt"` Revision string `json:"revision"` Project Project `json:"project"` Branch *Branch `json:"branch,omitempty"` - PullRequest *PullRequest `json:"pullRequest,omitempty"` + PullRequest string `json:"pullRequest,omitempty"` QualityGate *QualityGate `json:"qualityGate,omitempty"` } @@ -48,10 +49,6 @@ type Branch struct { Type string `json:"type,omitempty"` } -type PullRequest struct { - Key string `json:"key"` -} - type QualityGate struct { Status string `json:"status"` Conditions []Condition `json:"conditions"` @@ -124,6 +121,7 @@ type PullRequestsResponse struct { type PullRequestInfo struct { Key string `json:"key"` + Branch string `json:"branch"` AnalysisDate string `json:"analysisDate"` Commit PRCommit `json:"commit"` } @@ -137,15 +135,16 @@ type Error struct { Msg string `json:"msg"` } -func NewSonarConfig(apiToken, workingDir, ceTaskUrl, projectKey, serverURL, revision string, maxWait int) *SonarConfig { +func NewSonarConfig(apiToken, workingDir, ceTaskUrl, projectKey, serverURL, revision, pullRequest string, maxWait int) *SonarConfig { return &SonarConfig{ - APIToken: apiToken, - WorkingDir: workingDir, - CETaskUrl: ceTaskUrl, - revision: revision, - projectKey: projectKey, - serverURL: serverURL, - maxWait: maxWait, + APIToken: apiToken, + WorkingDir: workingDir, + CETaskUrl: ceTaskUrl, + revision: revision, + projectKey: projectKey, + serverURL: serverURL, + pullRequest: pullRequest, + maxWait: maxWait, } } @@ -174,6 +173,11 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error return nil, fmt.Errorf("API token must be given to retrieve data from SonarQube") } + // If explicit pull-request flag was given, set it on the results + if sc.pullRequest != "" { + sonarResults.PullRequest = sc.pullRequest + } + // Read the report-task.txt file (if it exists) to get the server URL, dashboard URL and ceTaskURL err = sc.readFile(project, sonarResults, logger) if err != nil { @@ -185,10 +189,10 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error return nil, fmt.Errorf("failed to parse CE task URL: %s", parseErr) } sonarResults.ServerUrl = fmt.Sprintf("%s://%s", parsedURL.Scheme, parsedURL.Host) - } else if sc.projectKey == "" || sc.revision == "" { - return nil, fmt.Errorf("%s. Alternatively provide the project key and revision for the scan to attest", err) - // If the report-task.txt does not exist, but we've been given the project key and revision, we can still get the data + } else if sc.projectKey == "" || (sc.revision == "" && sc.pullRequest == "") { + return nil, fmt.Errorf("%s. Alternatively provide the project key and either revision or pull-request ID for the scan to attest", err) } else { + // If the report-task.txt does not exist, but we've been given the project key and revision (or PR ID), we can still get the data project.Key = sc.projectKey sonarResults.ServerUrl = sc.serverURL sonarResults.Revision = sc.revision @@ -196,10 +200,13 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error if err != nil { return nil, err } - analysisID, err = GetProjectAnalysisFromRevision(httpClient, sonarResults, project, sc.revision, tokenHeader, logger) - if err != nil { - return nil, err + if sonarResults.PullRequest == "" { + analysisID, err = GetProjectAnalysisFromRevision(httpClient, sonarResults, project, sc.revision, tokenHeader, logger) + if err != nil { + return nil, err + } } + err = GetTaskID(httpClient, sonarResults, project, analysisID, tokenHeader, logger) if err != nil { return nil, err @@ -207,31 +214,33 @@ func (sc *SonarConfig) GetSonarResults(logger *log.Logger) (*SonarResults, error } } - if analysisID == "" { + if analysisID == "" && sc.CETaskUrl != "" { //Get the analysis ID, status, project name and branch data from the ceTaskURL (ce API) analysisID, err = GetCETaskData(httpClient, project, sonarResults, sc.CETaskUrl, tokenHeader, sc.maxWait, logger) if err != nil { return nil, err } - if sonarResults.PullRequest != nil { - // For PR scans, project_analyses/search does not return PR analyses on SonarCloud. - // Use the project_pull_requests/list API to get the revision and analysis date instead. - err = GetPRAnalysisData(httpClient, sonarResults, project, sonarResults.PullRequest.Key, tokenHeader) - if err != nil { - return nil, err - } - } else { + if sonarResults.PullRequest == "" { //Get project revision and scan date/time from the projectAnalyses API err = GetProjectAnalysisFromAnalysisID(httpClient, sonarResults, project, analysisID, tokenHeader) if err != nil { return nil, err } } + // PR case falls through to the block below + } + + // If we have a PR get PR analysis data + if sonarResults.PullRequest != "" { + err = GetPRAnalysisData(httpClient, sonarResults, project, sonarResults.PullRequest, tokenHeader) + if err != nil { + return nil, err + } } //Get the quality gate status from the qualitygates/project_status API - qualityGate, err = GetQualityGate(httpClient, sonarResults, qualityGate, analysisID, tokenHeader) + qualityGate, err = GetQualityGate(httpClient, sonarResults, qualityGate, analysisID, project.Key, sonarResults.PullRequest, tokenHeader) if err != nil { return nil, err } @@ -293,13 +302,19 @@ func GetCETaskData(httpClient *http.Client, project *Project, sonarResults *Sona } err = json.NewDecoder(taskResponse.Body).Decode(taskResponseData) if err != nil { + _ = taskResponse.Body.Close() return "", fmt.Errorf("please check your API token is correct and you have the correct permissions in SonarQube") } // If the CETaskURL from the report-task.txt file gives a 404, the CE task does not exist, or SonarQube is down. if taskResponseData.Errors != nil { + _ = taskResponse.Body.Close() return "", fmt.Errorf("%s on %s. \nSonarQube may be experiencing problems, please check https://status.sonarqube.com/ and try again later. \nOtherwise if you are attesting an older scan, the snapshot may have been deleted by SonarQube", taskResponseData.Errors[0].Msg, sonarResults.ServerUrl) } + if err := taskResponse.Body.Close(); err != nil { + logger.Warn("failed to close task response body: %v", err) + } + if taskResponseData.Task.Status == "PENDING" || taskResponseData.Task.Status == "IN_PROGRESS" { // So that we don't wait longer than maxWait if (elapsed + wait) > maxWait { @@ -319,9 +334,6 @@ func GetCETaskData(httpClient *http.Client, project *Project, sonarResults *Sona wait *= 2 } } else { - if err := taskResponse.Body.Close(); err != nil { - logger.Warn("failed to close task response body: %v", err) - } break } } @@ -352,7 +364,7 @@ func GetCETaskData(httpClient *http.Client, project *Project, sonarResults *Sona } if taskResponseData.Task.PullRequest != "" { - sonarResults.PullRequest = &PullRequest{Key: taskResponseData.Task.PullRequest} + sonarResults.PullRequest = taskResponseData.Task.PullRequest sonarResults.Branch = nil } else if taskResponseData.Task.Branch != "" { sonarResults.Branch = &Branch{} @@ -382,6 +394,11 @@ func GetProjectAnalysisFromRevision(httpClient *http.Client, sonarResults *Sonar if err != nil { return "", err } + defer func() { + if err := projectAnalysesResponse.Body.Close(); err != nil { + logger.Warn("failed to close project analyses response body: %v", err) + } + }() projectAnalysesData := &ProjectAnalyses{} err = json.NewDecoder(projectAnalysesResponse.Body).Decode(projectAnalysesData) @@ -389,25 +406,21 @@ func GetProjectAnalysisFromRevision(httpClient *http.Client, sonarResults *Sonar return "", fmt.Errorf("please check your API token and SonarQube server URL are correct and you have the correct permissions in SonarQube") } + if projectAnalysesData.Errors != nil { + return "", fmt.Errorf("SonarQube error: %s", projectAnalysesData.Errors[0].Msg) + } + for analysis := range projectAnalysesData.Analyses { if projectAnalysesData.Analyses[analysis].Revision == revision { - sonarResults.AnalaysedAt = projectAnalysesData.Analyses[analysis].Date + sonarResults.AnalysedAt = projectAnalysesData.Analyses[analysis].Date analysisID = projectAnalysesData.Analyses[analysis].Key break } } - if projectAnalysesData.Errors != nil { - return "", fmt.Errorf("SonarQube error: %s", projectAnalysesData.Errors[0].Msg) - } - - if sonarResults.AnalaysedAt == "" { + if sonarResults.AnalysedAt == "" { return "", fmt.Errorf("analysis for revision %s of project %s not found. Check the revision is correct. \nThe scan may still be being processed by SonarQube, try again later.\n Otherwise if you are attesting an older scan, the snapshot may also have been deleted by SonarQube", revision, project.Key) } - if err := projectAnalysesResponse.Body.Close(); err != nil { - // Log warning for cleanup error - logger.Warn("failed to close project analyses response body: %v", err) - } return analysisID, nil } @@ -441,13 +454,13 @@ func GetProjectAnalysisFromAnalysisID(httpClient *http.Client, sonarResults *Son for analysis := range projectAnalysesData.Analyses { if projectAnalysesData.Analyses[analysis].Key == analysisID { - sonarResults.AnalaysedAt = projectAnalysesData.Analyses[analysis].Date + sonarResults.AnalysedAt = projectAnalysesData.Analyses[analysis].Date sonarResults.Revision = projectAnalysesData.Analyses[analysis].Revision break } } - if sonarResults.AnalaysedAt == "" { + if sonarResults.AnalysedAt == "" { return fmt.Errorf("analysis with ID %s not found on %s. Snapshot may have been deleted by SonarQube", analysisID, sonarResults.ServerUrl) } @@ -486,7 +499,7 @@ func GetPRAnalysisData(httpClient *http.Client, sonarResults *SonarResults, proj for _, pr := range prData.PullRequests { if pr.Key == prKey { - sonarResults.AnalaysedAt = pr.AnalysisDate + sonarResults.AnalysedAt = pr.AnalysisDate sonarResults.Revision = pr.Commit.SHA return nil } @@ -495,10 +508,19 @@ func GetPRAnalysisData(httpClient *http.Client, sonarResults *SonarResults, proj return fmt.Errorf("pull request %s not found for project %s on %s", prKey, project.Key, sonarResults.ServerUrl) } -func GetQualityGate(httpClient *http.Client, sonarResults *SonarResults, qualityGate *QualityGate, analysisID, tokenHeader string) (*QualityGate, error) { - qualityGateURL, err := sonarURL(sonarResults.ServerUrl, "api/qualitygates/project_status", url.Values{"analysisId": {analysisID}}) - if err != nil { - return nil, err +func GetQualityGate(httpClient *http.Client, sonarResults *SonarResults, qualityGate *QualityGate, analysisID, projectKey, pullRequest, tokenHeader string) (*QualityGate, error) { + var qualityGateURL string + var err error + if pullRequest == "" { + qualityGateURL, err = sonarURL(sonarResults.ServerUrl, "api/qualitygates/project_status", url.Values{"analysisId": {analysisID}}) + if err != nil { + return nil, err + } + } else { + qualityGateURL, err = sonarURL(sonarResults.ServerUrl, "api/qualitygates/project_status", url.Values{"projectKey": {projectKey}, "pullRequest": {pullRequest}}) + if err != nil { + return nil, err + } } qualityGateRequest, err := http.NewRequest("GET", qualityGateURL, nil) if err != nil { @@ -510,6 +532,7 @@ func GetQualityGate(httpClient *http.Client, sonarResults *SonarResults, quality if err != nil { return nil, err } + defer func() { _ = qualityGateResponse.Body.Close() }() qualityGateData := &QualityGateResponse{} err = json.NewDecoder(qualityGateResponse.Body).Decode(qualityGateData) @@ -553,6 +576,11 @@ func GetTaskID(httpClient *http.Client, sonarResults *SonarResults, project *Pro if err != nil { return err } + defer func() { + if err := CEActivityResponse.Body.Close(); err != nil { + logger.Warn("failed to close CE activity response body: %v", err) + } + }() CEActivityData := &ActivityResponse{} err = json.NewDecoder(CEActivityResponse.Body).Decode(CEActivityData) @@ -560,25 +588,27 @@ func GetTaskID(httpClient *http.Client, sonarResults *SonarResults, project *Pro return fmt.Errorf("please check your API token is correct and you have the correct permissions in SonarQube") } - for task := range CEActivityData.Tasks { - if CEActivityData.Tasks[task].AnalysisID == analysisID { - sonarResults.TaskID = CEActivityData.Tasks[task].TaskID - sonarResults.Status = CEActivityData.Tasks[task].Status - project.Name = CEActivityData.Tasks[task].ComponentName - if CEActivityData.Tasks[task].Branch != "" { + for t := range CEActivityData.Tasks { + task := CEActivityData.Tasks[t] + matched := (analysisID != "" && task.AnalysisID == analysisID) || + (analysisID == "" && sonarResults.PullRequest != "" && task.PullRequest == sonarResults.PullRequest) + if matched { + sonarResults.TaskID = task.TaskID + sonarResults.Status = task.Status + project.Name = task.ComponentName + if task.PullRequest != "" { + sonarResults.PullRequest = task.PullRequest + sonarResults.Branch = nil + } else if task.Branch != "" { sonarResults.Branch = &Branch{} - sonarResults.Branch.Name = CEActivityData.Tasks[task].Branch - sonarResults.Branch.Type = CEActivityData.Tasks[task].BranchType + sonarResults.Branch.Name = task.Branch + sonarResults.Branch.Type = task.BranchType } else { sonarResults.Branch = nil } break } } - if err := CEActivityResponse.Body.Close(); err != nil { - // Log warning for cleanup error - logger.Warn("failed to close CE activity response body: %v", err) - } return nil } From 87d43aca1aa751a15a3c47b9e5e9ff93fc9b0da0 Mon Sep 17 00:00:00 2001 From: "ci-signed-commit-bot[bot]" <247774526+ci-signed-commit-bot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:10:51 +0000 Subject: [PATCH 27/41] Update helm docs (#786) Co-authored-by: ci-signed-commit-bot[bot] <247774526+ci-signed-commit-bot[bot]@users.noreply.github.com> --- charts/k8s-reporter/README.md | 10 +++++----- docs.kosli.com/content/helm/_index.md | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index 5161b8713..d9b77585e 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -4,9 +4,9 @@ title: Kubernetes Reporter Helm Chart # k8s-reporter -![Version: 2.2.0](https://img.shields.io/badge/Version-2.2.0-informational?style=flat-square) +![Version: 2.2.1](https://img.shields.io/badge/Version-2.2.1-informational?style=flat-square) -A Helm chart for installing the Kosli K8S reporter as a cronjob. +A Helm chart for installing the Kosli K8S reporter as a CronJob. The chart allows you to create a Kubernetes cronjob and all its necessary RBAC to report running images to Kosli at a given cron schedule. Configuration is done via **reporterConfig.environments**: a list of Kosli environments to report to. Each entry has a required `name` and optional namespace selectors. Use one entry for a single environment, or multiple entries to report to different environments with different selectors. @@ -166,13 +166,13 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | fullnameOverride | string | `""` | overrides the fullname used for the created k8s resources. It has higher precedence than `nameOverride` | | image.pullPolicy | string | `"IfNotPresent"` | the kosli reporter image pull policy | | image.repository | string | `"ghcr.io/kosli-dev/cli"` | the kosli reporter image repository | -| image.tag | string | `"v2.12.0"` | the kosli reporter image tag, overrides the image tag whose default is the chart appVersion. | +| image.tag | string | `""` | the kosli reporter image tag, overrides the image tag whose default is the chart appVersion. | | kosliApiToken.secretKey | string | `"key"` | the name of the key in the secret data which contains the Kosli API token | | kosliApiToken.secretName | string | `"kosli-api-token"` | the name of the secret containing the kosli API token | | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | | podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | podLabels | object | `{}` | custom labels to add to pods | -| reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | +| reporterConfig.dryRun | bool | `false` | | | reporterConfig.environments | list | `[]` | List of Kosli environments to report to. Each entry has required 'name' and optional namespace selectors. Use one entry to report a single environment; use multiple entries to report to multiple environments with different selectors. Per entry: name (required), namespaces, namespacesRegex, excludeNamespaces, excludeNamespacesRegex (optional). Leave namespace fields unset for an entry to report the entire cluster to that environment. | | reporterConfig.httpProxy | string | `""` | the http proxy url | | reporterConfig.kosliOrg | string | `""` | the name of the Kosli org | @@ -190,5 +190,5 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | successfulJobsHistoryLimit | int | `3` | specifies the number of successful finished jobs to keep | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) +Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) diff --git a/docs.kosli.com/content/helm/_index.md b/docs.kosli.com/content/helm/_index.md index 5161b8713..d9b77585e 100644 --- a/docs.kosli.com/content/helm/_index.md +++ b/docs.kosli.com/content/helm/_index.md @@ -4,9 +4,9 @@ title: Kubernetes Reporter Helm Chart # k8s-reporter -![Version: 2.2.0](https://img.shields.io/badge/Version-2.2.0-informational?style=flat-square) +![Version: 2.2.1](https://img.shields.io/badge/Version-2.2.1-informational?style=flat-square) -A Helm chart for installing the Kosli K8S reporter as a cronjob. +A Helm chart for installing the Kosli K8S reporter as a CronJob. The chart allows you to create a Kubernetes cronjob and all its necessary RBAC to report running images to Kosli at a given cron schedule. Configuration is done via **reporterConfig.environments**: a list of Kosli environments to report to. Each entry has a required `name` and optional namespace selectors. Use one entry for a single environment, or multiple entries to report to different environments with different selectors. @@ -166,13 +166,13 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | fullnameOverride | string | `""` | overrides the fullname used for the created k8s resources. It has higher precedence than `nameOverride` | | image.pullPolicy | string | `"IfNotPresent"` | the kosli reporter image pull policy | | image.repository | string | `"ghcr.io/kosli-dev/cli"` | the kosli reporter image repository | -| image.tag | string | `"v2.12.0"` | the kosli reporter image tag, overrides the image tag whose default is the chart appVersion. | +| image.tag | string | `""` | the kosli reporter image tag, overrides the image tag whose default is the chart appVersion. | | kosliApiToken.secretKey | string | `"key"` | the name of the key in the secret data which contains the Kosli API token | | kosliApiToken.secretName | string | `"kosli-api-token"` | the name of the secret containing the kosli API token | | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | | podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | podLabels | object | `{}` | custom labels to add to pods | -| reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | +| reporterConfig.dryRun | bool | `false` | | | reporterConfig.environments | list | `[]` | List of Kosli environments to report to. Each entry has required 'name' and optional namespace selectors. Use one entry to report a single environment; use multiple entries to report to multiple environments with different selectors. Per entry: name (required), namespaces, namespacesRegex, excludeNamespaces, excludeNamespacesRegex (optional). Leave namespace fields unset for an entry to report the entire cluster to that environment. | | reporterConfig.httpProxy | string | `""` | the http proxy url | | reporterConfig.kosliOrg | string | `""` | the name of the Kosli org | @@ -190,5 +190,5 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | successfulJobsHistoryLimit | int | `3` | specifies the number of successful finished jobs to keep | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) +Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) From 3a857a61892f783ae4769a6eeebcbd402f911e2a Mon Sep 17 00:00:00 2001 From: AlexKantor87 Date: Thu, 16 Apr 2026 09:16:53 +0100 Subject: [PATCH 28/41] Guard against lightweight tags in release workflow (#787) Add a fail-fast check in the release workflow pre-build job that rejects lightweight tags with a clear error message pointing to 'make release'. Also document the correct release process in CLAUDE.md. Discovered during the v2.16.0 release when 'gh release create' was used instead of 'make release', causing never-alone-trail failures. --- .github/workflows/release.yml | 10 ++++++++++ CLAUDE.md | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3feb00c03..791ca022a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,6 +27,16 @@ jobs: id: tag uses: dawidd6/action-get-tag@v1 + - name: Verify annotated tag + env: + GH_TOKEN: ${{ github.token }} + run: | + TAG_TYPE=$(gh api repos/${{ github.repository }}/git/refs/tags/${{ steps.tag.outputs.tag }} --jq '.object.type') + if [ "$TAG_TYPE" != "tag" ]; then + echo "::error::Tag ${{ steps.tag.outputs.tag }} is lightweight, not annotated. Use 'make release' to cut releases." + exit 1 + fi + - name: Prepare id: prep run: | diff --git a/CLAUDE.md b/CLAUDE.md index e59dc60bd..5a7a82eb7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -81,6 +81,19 @@ All command tests in `cmd/kosli/` use the **testify suite** pattern: - Multi-platform releases via GoReleaser (darwin/linux/windows × amd64/arm64) - Semantic versioning; releases created with `make release tag=v` +## Releasing + +**NEVER use `gh release create` to cut a release.** It creates a lightweight tag and a premature GitHub Release, both of which break the release pipeline (never-alone-trail fails, GoReleaser conflicts with the pre-existing release). + +The release process is documented in [release-guide.md](/release-guide.md). The correct way to release: + +```bash +make release # Interactive: AI suggests version + changelog, you confirm +make release tag=v2.x.y # Fallback: explicit tag, no AI +``` + +Both paths create an **annotated tag** and push it. The `release.yml` workflow then runs GoReleaser, which creates the GitHub Release at the correct point in the pipeline. Do not create tags or releases via any other method. + ## Working Style: TDD Follow a strict Red-Green-Refactor loop for every change: From ee994de18e0cc47ddb1bcfe7e18f1035411b74bc Mon Sep 17 00:00:00 2001 From: AlexKantor87 Date: Thu, 16 Apr 2026 10:13:47 +0100 Subject: [PATCH 29/41] docs: clarify OpenShift runAsUser must be set to null, not omitted (#789) Helm deep-merges values overrides with chart defaults, so simply omitting runAsUser from a values file does not remove it from the rendered spec. The default of 1000 always survives. For OpenShift environments with SCC, users must explicitly set runAsUser: null. Updated values.yaml comments with a concrete example and explanation. Regenerated README.md and docs site via helm-docs. --- charts/k8s-reporter/README.md | 8 ++++---- charts/k8s-reporter/values.yaml | 19 ++++++++++++++----- docs.kosli.com/content/helm/_index.md | 8 ++++---- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index d9b77585e..4045d19fd 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -172,14 +172,14 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | | podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | podLabels | object | `{}` | custom labels to add to pods | -| reporterConfig.dryRun | bool | `false` | | +| reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | | reporterConfig.environments | list | `[]` | List of Kosli environments to report to. Each entry has required 'name' and optional namespace selectors. Use one entry to report a single environment; use multiple entries to report to multiple environments with different selectors. Per entry: name (required), namespaces, namespacesRegex, excludeNamespaces, excludeNamespacesRegex (optional). Leave namespace fields unset for an entry to report the entire cluster to that environment. | | reporterConfig.httpProxy | string | `""` | the http proxy url | | reporterConfig.kosliOrg | string | `""` | the name of the Kosli org | -| reporterConfig.securityContext | object | `{"allowPrivilegeEscalation":false,"runAsNonRoot":true,"runAsUser":1000}` | the security context for the reporter cronjob Set to null or {} to disable security context entirely (not recommended) For OpenShift, you can omit runAsUser to let OpenShift assign the UID | +| reporterConfig.securityContext | object | `{"allowPrivilegeEscalation":false,"runAsNonRoot":true,"runAsUser":1000}` | the security context for the reporter cronjob. Set to null or {} to disable security context entirely (not recommended). For OpenShift with SCC, explicitly set runAsUser to null to let OpenShift assign the UID from the allowed range. Simply omitting runAsUser from your values override will not work because Helm deep-merges with these defaults. Example OpenShift override: securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: null | | reporterConfig.securityContext.allowPrivilegeEscalation | bool | `false` | whether to allow privilege escalation | | reporterConfig.securityContext.runAsNonRoot | bool | `true` | whether to run as non root | -| reporterConfig.securityContext.runAsUser | int | `1000` | the user id to run as Omit this field for OpenShift environments to allow automatic UID assignment | +| reporterConfig.securityContext.runAsUser | int | `1000` | the user id to run as. For OpenShift environments with SCC, set to null (runAsUser: null) to allow automatic UID assignment. Simply omitting this field will not work due to Helm's deep merge with chart defaults. | | resources.limits.cpu | string | `"100m"` | the cpu limit | | resources.limits.memory | string | `"256Mi"` | the memory limit | | resources.requests.memory | string | `"64Mi"` | the memory request | @@ -190,5 +190,5 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | successfulJobsHistoryLimit | int | `3` | specifies the number of successful finished jobs to keep | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/k8s-reporter/values.yaml b/charts/k8s-reporter/values.yaml index 191e7176a..9ba1339b9 100644 --- a/charts/k8s-reporter/values.yaml +++ b/charts/k8s-reporter/values.yaml @@ -70,16 +70,25 @@ reporterConfig: # -- the http proxy url httpProxy: "" - # -- the security context for the reporter cronjob - # Set to null or {} to disable security context entirely (not recommended) - # For OpenShift, you can omit runAsUser to let OpenShift assign the UID + # -- the security context for the reporter cronjob. + # Set to null or {} to disable security context entirely (not recommended). + # For OpenShift with SCC, explicitly set runAsUser to null to let OpenShift + # assign the UID from the allowed range. Simply omitting runAsUser from your + # values override will not work because Helm deep-merges with these defaults. + # Example OpenShift override: + # securityContext: + # allowPrivilegeEscalation: false + # runAsNonRoot: true + # runAsUser: null securityContext: # -- whether to allow privilege escalation allowPrivilegeEscalation: false # -- whether to run as non root runAsNonRoot: true - # -- the user id to run as - # Omit this field for OpenShift environments to allow automatic UID assignment + # -- the user id to run as. + # For OpenShift environments with SCC, set to null (runAsUser: null) to allow + # automatic UID assignment. Simply omitting this field will not work due to + # Helm's deep merge with chart defaults. runAsUser: 1000 # -- map of plain environment variables to inject into the reporter container. diff --git a/docs.kosli.com/content/helm/_index.md b/docs.kosli.com/content/helm/_index.md index d9b77585e..4045d19fd 100644 --- a/docs.kosli.com/content/helm/_index.md +++ b/docs.kosli.com/content/helm/_index.md @@ -172,14 +172,14 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | nameOverride | string | `""` | overrides the name used for the created k8s resources. If `fullnameOverride` is provided, it has higher precedence than this one | | podAnnotations | object | `{}` | any custom annotations to be added to the cronjob | | podLabels | object | `{}` | custom labels to add to pods | -| reporterConfig.dryRun | bool | `false` | | +| reporterConfig.dryRun | bool | `false` | whether the dry run mode is enabled or not. In dry run mode, the reporter logs the reports to stdout and does not send them to kosli. | | reporterConfig.environments | list | `[]` | List of Kosli environments to report to. Each entry has required 'name' and optional namespace selectors. Use one entry to report a single environment; use multiple entries to report to multiple environments with different selectors. Per entry: name (required), namespaces, namespacesRegex, excludeNamespaces, excludeNamespacesRegex (optional). Leave namespace fields unset for an entry to report the entire cluster to that environment. | | reporterConfig.httpProxy | string | `""` | the http proxy url | | reporterConfig.kosliOrg | string | `""` | the name of the Kosli org | -| reporterConfig.securityContext | object | `{"allowPrivilegeEscalation":false,"runAsNonRoot":true,"runAsUser":1000}` | the security context for the reporter cronjob Set to null or {} to disable security context entirely (not recommended) For OpenShift, you can omit runAsUser to let OpenShift assign the UID | +| reporterConfig.securityContext | object | `{"allowPrivilegeEscalation":false,"runAsNonRoot":true,"runAsUser":1000}` | the security context for the reporter cronjob. Set to null or {} to disable security context entirely (not recommended). For OpenShift with SCC, explicitly set runAsUser to null to let OpenShift assign the UID from the allowed range. Simply omitting runAsUser from your values override will not work because Helm deep-merges with these defaults. Example OpenShift override: securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: null | | reporterConfig.securityContext.allowPrivilegeEscalation | bool | `false` | whether to allow privilege escalation | | reporterConfig.securityContext.runAsNonRoot | bool | `true` | whether to run as non root | -| reporterConfig.securityContext.runAsUser | int | `1000` | the user id to run as Omit this field for OpenShift environments to allow automatic UID assignment | +| reporterConfig.securityContext.runAsUser | int | `1000` | the user id to run as. For OpenShift environments with SCC, set to null (runAsUser: null) to allow automatic UID assignment. Simply omitting this field will not work due to Helm's deep merge with chart defaults. | | resources.limits.cpu | string | `"100m"` | the cpu limit | | resources.limits.memory | string | `"256Mi"` | the memory limit | | resources.requests.memory | string | `"64Mi"` | the memory request | @@ -190,5 +190,5 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t | successfulJobsHistoryLimit | int | `3` | specifies the number of successful finished jobs to keep | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.5.0](https://github.com/norwoodj/helm-docs/releases/v1.5.0) +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) From 9e3ba82e3dcb3d5c4e8fce2a40b72782f49dc8c2 Mon Sep 17 00:00:00 2001 From: Tore Martin Hagen <93583343+ToreMerkely@users.noreply.github.com> Date: Thu, 16 Apr 2026 15:43:16 +0200 Subject: [PATCH 30/41] Sonar qube pr test now uses the lastest sonar-qube-pr scan and not a hard coded value server/#5354 (#792) * Dynamically fetch PR scan test data from GitHub artifact Test 21 was hardcoded to a specific SonarCloud PR scan that would break when SonarCloud housekeeping deletes old data. Instead, download the report-task.txt from a GitHub Actions artifact uploaded by the sonar-pr-trigger workflow in cyber-dojo/differ. Resolves kosli-dev/server#5354 (test 21) Co-Authored-By: Claude Opus 4.6 (1M context) * Use dynamic PR scan data for tests 18, 20, and 26 Parse the downloaded report-task.txt to extract the PR key and CE task URL, replacing the hardcoded PR 359 references in tests that use --pull-request and --sonar-ce-task-url flags. Resolves kosli-dev/server#5354 Co-Authored-By: Claude Opus 4.6 (1M context) * Add GH_TOKEN to test workflow for sonar PR scan tests Sonar tests download a report-task.txt artifact from cyber-dojo/differ, which requires a GitHub token with actions:read scope on that repo. Co-Authored-By: Claude Opus 4.6 (1M context) --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .github/workflows/test.yml | 1 + cmd/kosli/attestSonar_test.go | 173 +++++++++++++++++++++++++++++++++- 2 files changed, 170 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 65101c21b..0ac65c1aa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -164,6 +164,7 @@ jobs: INTEGRATION_TEST_AZURE_CLIENT_ID: ${{ secrets.azure_client_id }} KOSLI_SONAR_API_TOKEN: ${{ secrets.sonarqube_token }} KOSLI_API_TOKEN_PROD: ${{ secrets.kosli_querying_api_token }} + GH_TOKEN: ${{ secrets.github_access_token }} # sonar tests download artifacts from cyber-dojo/differ run: | # some tests use git operations, therefore the git author on the CI VM needs to be set git config --global user.name "John Doe" diff --git a/cmd/kosli/attestSonar_test.go b/cmd/kosli/attestSonar_test.go index 0ee1b7375..52584d9fe 100644 --- a/cmd/kosli/attestSonar_test.go +++ b/cmd/kosli/attestSonar_test.go @@ -1,7 +1,15 @@ package main import ( + "archive/zip" + "bufio" + "encoding/json" "fmt" + "io" + "net/http" + "os" + "path/filepath" + "strings" "testing" "github.com/kosli-dev/cli/internal/testHelpers" @@ -31,6 +39,9 @@ type AttestSonarCommandTestSuite struct { flowName string trailName string artifactFingerprint string + prScannerWorkDir string + prKey string + prCETaskURL string suite.Suite defaultKosliArguments string } @@ -59,6 +70,8 @@ func (suite *AttestSonarCommandTestSuite) SetupTest() { CreateFlowWithTemplate(suite.flowName, "testdata/valid_template.yml", suite.T()) BeginTrail(suite.trailName, suite.flowName, "", suite.T()) CreateArtifactOnTrail(suite.flowName, suite.trailName, "cli", suite.artifactFingerprint, "file1", suite.T()) + + suite.prScannerWorkDir, suite.prKey, suite.prCETaskURL = downloadPRScanData(suite.T()) } func (suite *AttestSonarQubeCommandTestSuite) SetupTest() { @@ -175,7 +188,7 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { }, { name: "18 can retrieve scan results using report-task.txt file and pull-request flag", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --pull-request 359 %s", suite.defaultKosliArguments), + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork --pull-request %s %s", suite.prKey, suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { @@ -186,12 +199,12 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { }, { name: "20 can retrieve scan results using project key and pull-request flag", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --pull-request 359 %s", suite.defaultKosliArguments), + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-project-key cyber-dojo_differ --pull-request %s %s", suite.prKey, suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { name: "21 can attest sonar for a pull request scan using report-task.txt without --pull-request flag", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarcloud/.scannerwork-pr %s", suite.defaultKosliArguments), + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir %s %s", suite.prScannerWorkDir, suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { @@ -219,7 +232,7 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { }, { name: "26 can attest sonar for a pull request scan using --sonar-ce-task-url", - cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZ2Qge89T7Y829rQbv87 %s", suite.defaultKosliArguments), + cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url %s %s", suite.prCETaskURL, suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, { @@ -345,6 +358,158 @@ func (suite *AttestSonarQubeCommandTestSuite) TestAttestSonarQubeCmd() { runTestCmd(suite.T(), tests) } +// downloadPRScanData downloads the report-task.txt from the latest +// SonarCloud PR scan of cyber-dojo_differ. The file is uploaded as a GitHub +// Actions artifact by the sonar-pr-trigger workflow in that repo. +// Returns the directory containing report-task.txt, the PR key, and the CE task URL. +func downloadPRScanData(t *testing.T) (workDir, prKey, ceTaskURL string) { + t.Helper() + + tmpDir := t.TempDir() + httpClient := &http.Client{} + + // GitHub token is required to download workflow artifacts. + // Check GH_TOKEN (gh CLI), GITHUB_TOKEN (CI), in that order. + ghToken := os.Getenv("GH_TOKEN") + if ghToken == "" { + ghToken = os.Getenv("GITHUB_TOKEN") + } + if ghToken == "" { + t.Fatalf("GH_TOKEN or GITHUB_TOKEN must be set to download artifacts from GitHub") + } + + // Find the artifact ID via GitHub API + req, err := http.NewRequest("GET", + "https://api.github.com/repos/cyber-dojo/differ/actions/artifacts?name=sonar-pr-report-task&per_page=1", nil) + if err != nil { + t.Fatalf("failed to create artifact list request: %v", err) + } + req.Header.Add("Authorization", "Bearer "+ghToken) + + resp, err := httpClient.Do(req) + if err != nil { + t.Fatalf("failed to list artifacts from GitHub: %v", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + t.Fatalf("GitHub artifacts API returned status %d", resp.StatusCode) + } + + var result struct { + Artifacts []struct { + ID int `json:"id"` + Expired bool `json:"expired"` + Name string `json:"name"` + } `json:"artifacts"` + } + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + t.Fatalf("failed to decode artifacts response: %v", err) + } + if len(result.Artifacts) == 0 { + t.Fatalf("no sonar-pr-report-task artifact found in cyber-dojo/differ") + } + if result.Artifacts[0].Expired { + t.Fatalf("sonar-pr-report-task artifact has expired") + } + + // Download the artifact zip + downloadURL := fmt.Sprintf("https://api.github.com/repos/cyber-dojo/differ/actions/artifacts/%d/zip", result.Artifacts[0].ID) + dlReq, err := http.NewRequest("GET", downloadURL, nil) + if err != nil { + t.Fatalf("failed to create artifact download request: %v", err) + } + dlReq.Header.Add("Authorization", "Bearer "+ghToken) + + dlResp, err := httpClient.Do(dlReq) + if err != nil { + t.Fatalf("failed to download artifact: %v", err) + } + defer func() { _ = dlResp.Body.Close() }() + + if dlResp.StatusCode != http.StatusOK { + t.Fatalf("artifact download returned status %d", dlResp.StatusCode) + } + + // Save zip to temp file, then extract report-task.txt + zipPath := filepath.Join(tmpDir, "artifact.zip") + zipFile, err := os.Create(zipPath) + if err != nil { + t.Fatalf("failed to create temp zip file: %v", err) + } + if _, err := io.Copy(zipFile, dlResp.Body); err != nil { + _ = zipFile.Close() + t.Fatalf("failed to write artifact zip: %v", err) + } + _ = zipFile.Close() + + zipReader, err := zip.OpenReader(zipPath) + if err != nil { + t.Fatalf("failed to open artifact zip: %v", err) + } + defer func() { _ = zipReader.Close() }() + + for _, f := range zipReader.File { + if f.Name == "report-task.txt" { + rc, err := f.Open() + if err != nil { + t.Fatalf("failed to open report-task.txt in zip: %v", err) + } + outPath := filepath.Join(tmpDir, "report-task.txt") + outFile, err := os.Create(outPath) + if err != nil { + _ = rc.Close() + t.Fatalf("failed to create report-task.txt: %v", err) + } + _, err = io.Copy(outFile, rc) + _ = rc.Close() + _ = outFile.Close() + if err != nil { + t.Fatalf("failed to extract report-task.txt: %v", err) + } + + prKey, ceTaskURL = parsePRReportTask(t, outPath) + return tmpDir, prKey, ceTaskURL + } + } + + t.Fatalf("report-task.txt not found in artifact zip") + return "", "", "" +} + +// parsePRReportTask extracts the PR key and CE task URL from a report-task.txt file. +func parsePRReportTask(t *testing.T, path string) (prKey, ceTaskURL string) { + t.Helper() + + f, err := os.Open(path) + if err != nil { + t.Fatalf("failed to open %s: %v", path, err) + } + defer func() { _ = f.Close() }() + + scanner := bufio.NewScanner(f) + for scanner.Scan() { + line := scanner.Text() + if strings.HasPrefix(line, "ceTaskUrl=") { + ceTaskURL = strings.TrimPrefix(line, "ceTaskUrl=") + } + if strings.HasPrefix(line, "dashboardUrl=") { + // dashboardUrl contains ...&pullRequest= + if i := strings.Index(line, "pullRequest="); i != -1 { + prKey = line[i+len("pullRequest="):] + } + } + } + + if prKey == "" { + t.Fatalf("pullRequest not found in %s", path) + } + if ceTaskURL == "" { + t.Fatalf("ceTaskUrl not found in %s", path) + } + return prKey, ceTaskURL +} + // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestAttestSonarCommandTestSuite(t *testing.T) { From f506975667da920c9c4c9c71d3aa1a4848910ef1 Mon Sep 17 00:00:00 2001 From: Marko Bevc Date: Thu, 16 Apr 2026 15:13:17 +0100 Subject: [PATCH 31/41] feat: check for latest version (#781) * feat: check for latest version * feat: check for latest version - fix issues * feat: check for latest version - address feedback * feat: check for latest version - address feedback2 * feat: check for latest version - add version flag and don't check versions for internal commands * feat: check for latest version - minor fixes * Update internal/version/update_check_test.go Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * feat: check for latest version - minor improvements * feat: check for latest version - minor nits * feat: check for latest version - add comment * feat: check for latest version - add more comments * feat: check for latest version - add more tests --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- cmd/kosli/root.go | 31 ++++++++- cmd/kosli/version.go | 24 ++++--- go.mod | 2 +- internal/version/update_check.go | 86 ++++++++++++++++++++++++ internal/version/update_check_test.go | 95 +++++++++++++++++++++++++++ 5 files changed, 228 insertions(+), 10 deletions(-) create mode 100644 internal/version/update_check.go create mode 100644 internal/version/update_check_test.go diff --git a/cmd/kosli/root.go b/cmd/kosli/root.go index 86b35e0a1..655e610c1 100644 --- a/cmd/kosli/root.go +++ b/cmd/kosli/root.go @@ -9,6 +9,7 @@ import ( "github.com/kosli-dev/cli/internal/requests" "github.com/kosli-dev/cli/internal/security" + "github.com/kosli-dev/cli/internal/version" homedir "github.com/mitchellh/go-homedir" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -306,10 +307,14 @@ func getConfigFileFlagDefault() string { func newRootCmd(out, errOut io.Writer, args []string) (*cobra.Command, error) { global = new(GlobalOpts) + + var updateNoticeCh = make(chan string, 1) // buffered — goroutine never blocks + cmd := &cobra.Command{ Use: "kosli", Short: "The Kosli CLI.", Long: globalUsage, + Version: fmt.Sprintf("%#v", version.Get()), SilenceUsage: true, SilenceErrors: true, TraverseChildren: true, @@ -320,6 +325,19 @@ func newRootCmd(out, errOut io.Writer, args []string) (*cobra.Command, error) { return err } + // Fire update check in background — result collected in PostRun. + // Skip when: + // - "version" subcommand: runs the check synchronously itself + // - "__complete*": Cobra shell-completion commands fire on every Tab press + // - --version flag: Cobra handles it internally and skips PersistentPostRun, + // so the goroutine result would always be silently discarded + if cmd.Name() != "version" && !strings.HasPrefix(cmd.Name(), "__") && !cmd.Root().Flags().Changed("version") { + go func() { + notice, _ := version.CheckForUpdate(version.GetVersion()) + updateNoticeCh <- notice + }() + } + if global.ApiToken == "DRY_RUN" { global.DryRun = true } @@ -342,7 +360,18 @@ func newRootCmd(out, errOut io.Writer, args []string) (*cobra.Command, error) { return flagError }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + select { + case notice := <-updateNoticeCh: + if notice != "" { + _, _ = fmt.Fprint(errOut, notice) // stderr — doesn't pollute piped stdout + } + default: + // goroutine not done yet (took > command duration) — skip silently + } + }, } + cmd.SetVersionTemplate("{{.Version}}\n") cmd.PersistentFlags().StringVarP(&global.ApiToken, "api-token", "a", "", apiTokenFlag) cmd.PersistentFlags().StringVar(&global.Org, "org", "", orgFlag) cmd.PersistentFlags().StringVarP(&global.Host, "host", "H", defaultHost, hostFlag) @@ -354,7 +383,7 @@ func newRootCmd(out, errOut io.Writer, args []string) (*cobra.Command, error) { // Add subcommands cmd.AddCommand( - newVersionCmd(out), + newVersionCmd(out, errOut), newFingerprintCmd(out), newAssertCmd(out), newStatusCmd(out), diff --git a/cmd/kosli/version.go b/cmd/kosli/version.go index dbfb707a8..44af2cb01 100644 --- a/cmd/kosli/version.go +++ b/cmd/kosli/version.go @@ -10,7 +10,7 @@ import ( const versionShortDesc = `Print the version of a Kosli CLI. ` const versionLongDesc = versionShortDesc + ` -The output will look something like this: +The output will look something like this: version.BuildInfo{Version:"v0.0.1", GitCommit:"fe51cd1e31e6a202cba7dead9552a6d418ded79a", GitTreeState:"clean", GoVersion:"go1.16.3"} - Version is the semantic version of the release. @@ -24,15 +24,16 @@ type versionOptions struct { short bool } -func newVersionCmd(out io.Writer) *cobra.Command { +func newVersionCmd(out, errOut io.Writer) *cobra.Command { o := new(versionOptions) cmd := &cobra.Command{ - Use: "version", - Short: versionShortDesc, - Long: versionLongDesc, - Args: cobra.NoArgs, + Use: "version", + Aliases: []string{"ver"}, + Short: versionShortDesc, + Long: versionLongDesc, + Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - o.run(out) + o.run(out, errOut) }, } @@ -41,8 +42,15 @@ func newVersionCmd(out io.Writer) *cobra.Command { return cmd } -func (o *versionOptions) run(out io.Writer) { +func (o *versionOptions) run(out, errOut io.Writer) { logger.Info(formatVersion(o.short)) + + // Synchronous check — version command always shows the update notice, + // unlike other commands where the check may be skipped if slower than the command. + notice, _ := version.CheckForUpdate(version.GetVersion()) + if notice != "" { + _, _ = fmt.Fprint(errOut, notice) // stderr — doesn't pollute piped stdout + } } func formatVersion(short bool) string { diff --git a/go.mod b/go.mod index d349d0a8e..7d658caf4 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( 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/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/aws/aws-sdk-go-v2 v1.41.5 github.com/aws/aws-sdk-go-v2/config v1.32.14 @@ -59,7 +60,6 @@ require ( 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/BurntSushi/toml v1.5.0 // indirect - github.com/Masterminds/semver/v3 v3.4.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 diff --git a/internal/version/update_check.go b/internal/version/update_check.go new file mode 100644 index 000000000..1cc9ea2e0 --- /dev/null +++ b/internal/version/update_check.go @@ -0,0 +1,86 @@ +package version + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + "time" + + semver "github.com/Masterminds/semver/v3" +) + +const ( + githubLatestReleaseURL = "https://api.github.com/repos/kosli-dev/cli/releases/latest" + updateCheckTimeout = 2 * time.Second +) + +type githubRelease struct { + TagName string `json:"tag_name"` +} + +// CheckForUpdate is the public entry point — uses the real GitHub URL +func CheckForUpdate(currentVersion string) (string, error) { + return checkForUpdateWithURL(currentVersion, githubLatestReleaseURL) +} + +// checkForUpdateWithURL is the internal, testable implementation. +// It queries the given URL for the latest release and returns a non-empty +// notice string if the current version is outdated. +// It returns silently (empty string, nil) on any network or parse error, +// so it never blocks or fails a command. +// Set KOSLI_NO_UPDATE_CHECK=1 to skip entirely. +func checkForUpdateWithURL(currentVersion string, apiURL string) (string, error) { + if os.Getenv("KOSLI_NO_UPDATE_CHECK") != "" { + return "", nil + } + if currentVersion == "" || strings.HasPrefix(currentVersion, "main") || strings.Contains(currentVersion, "+unreleased") { + return "", nil // dev build — skip + } + + // context provides the timeout and not http.Client + ctx, cancel := context.WithTimeout(context.Background(), updateCheckTimeout) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, http.MethodGet, apiURL, nil) + if err != nil { + return "", nil + } + req.Header.Set("Accept", "application/vnd.github+json") + req.Header.Set("User-Agent", fmt.Sprintf("kosli-cli/%s", currentVersion)) + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + return "", nil + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + return "", nil + } + + var release githubRelease + if err := json.NewDecoder(resp.Body).Decode(&release); err != nil { + return "", nil + } + + latestVer, err := semver.NewVersion(release.TagName) + if err != nil { + return "", nil + } + currentVer, err := semver.NewVersion(currentVersion) + if err != nil { + return "", nil + } + + if latestVer.GreaterThan(currentVer) { + return fmt.Sprintf( + "\nA new version of the Kosli CLI is available: %s (you have %s)\nUpgrade: https://docs.kosli.com/getting_started/install/\n", + release.TagName, currentVersion, + ), nil + } + return "", nil +} diff --git a/internal/version/update_check_test.go b/internal/version/update_check_test.go new file mode 100644 index 000000000..31cd503a9 --- /dev/null +++ b/internal/version/update_check_test.go @@ -0,0 +1,95 @@ +package version + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestCheckForUpdate_NewVersionAvailable(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = fmt.Fprintf(w, `{"tag_name":"v9.99.0"}`) + })) + defer srv.Close() + + notice, err := checkForUpdateWithURL("v0.1.0", srv.URL) + assert.NoError(t, err) + assert.Contains(t, notice, "v9.99.0") +} + +func TestCheckForUpdate_AlreadyLatest(t *testing.T) { + current := "v9.99.0" + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = fmt.Fprintf(w, `{"tag_name":"%s"}`, current) + })) + defer srv.Close() + + notice, err := checkForUpdateWithURL(current, srv.URL) + assert.NoError(t, err) + assert.Empty(t, notice) +} + +func TestCheckForUpdate_NewerThanLatest(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + _, _ = fmt.Fprintf(w, `{"tag_name":"v1.0.0"}`) + })) + defer srv.Close() + + // User has a newer version — should NOT show an update notice + notice, err := checkForUpdateWithURL("v2.0.0", srv.URL) + assert.NoError(t, err) + assert.Empty(t, notice) +} + +func TestCheckForUpdate_OptOut(t *testing.T) { + t.Setenv("KOSLI_NO_UPDATE_CHECK", "1") + notice, _ := CheckForUpdate("v0.1.0") + assert.Empty(t, notice) +} + +func TestCheckForUpdate_DevBuild(t *testing.T) { + // dev builds should be skipped without any HTTP call + notice, err := checkForUpdateWithURL("main", "http://should-not-be-called") + assert.NoError(t, err) + assert.Empty(t, notice) +} + +func TestCheckForUpdate_UnreleasedBuild(t *testing.T) { + notice, err := checkForUpdateWithURL("v1.0.0+unreleased", "http://should-not-be-called") + assert.NoError(t, err) + assert.Empty(t, notice) +} + +func TestCheckForUpdate_NetworkError(t *testing.T) { + notice, err := checkForUpdateWithURL("v0.1.0", "http://localhost:1") // nothing listening + assert.NoError(t, err) // must be silent + assert.Empty(t, notice) +} + +func TestCheckForUpdate_BadJSON(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = fmt.Fprint(w, `not json`) + })) + defer srv.Close() + + notice, err := checkForUpdateWithURL("v0.1.0", srv.URL) + assert.NoError(t, err) + assert.Empty(t, notice) +} + +func TestCheckForUpdate_Non200(t *testing.T) { + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusServiceUnavailable) + })) + defer srv.Close() + + notice, err := checkForUpdateWithURL("v0.1.0", srv.URL) + assert.NoError(t, err) + assert.Empty(t, notice) +} From 4caec2faa39dc87c982ce089b8150d51afad7099 Mon Sep 17 00:00:00 2001 From: Marko Bevc Date: Thu, 16 Apr 2026 16:41:14 +0100 Subject: [PATCH 32/41] chore: helm-docs GHA and use more modern version (#793) * chore: helm-docs GHA and use more modern version * Update .github/workflows/helm-chart.yml Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> * chore: helm-docs GHA and use more modern version - ping action --------- Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com> --- .github/workflows/helm-chart.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml index 5c86b8c46..c7f1a811d 100644 --- a/.github/workflows/helm-chart.yml +++ b/.github/workflows/helm-chart.yml @@ -1,7 +1,7 @@ name: helm chart on: - workflow_dispatch: + workflow_dispatch: {} push: branches: - main @@ -41,13 +41,15 @@ jobs: - name: Lint run: make helm-lint + - name: Setup Helm Docs + uses: gabe565/setup-helm-docs-action@d5c35bdc9133cfbea3b671acadf50a29029e87c2 # v1.0.4 + with: + version: 'latest' # v1.14.2 + - name: Generate Helm Docs run: | - curl -L https://github.com/norwoodj/helm-docs/releases/download/v1.5.0/helm-docs_1.5.0_linux_amd64.deb --output helm-docs.dep - sudo dpkg -i helm-docs.dep - rm helm-docs.dep - cd charts/k8s-reporter - helm-docs --template-files README.md.gotmpl,_templates.gotmpl --output-file README.md + cd charts/k8s-reporter + helm-docs --template-files README.md.gotmpl,_templates.gotmpl --output-file README.md helm-docs --template-files README.md.gotmpl,_templates.gotmpl --output-file ../../docs.kosli.com/content/helm/_index.md - name: Helm Package @@ -72,11 +74,11 @@ jobs: - name: Upload new chart and the update index.yaml to S3 run: | rm package/old-index.yaml - aws s3 cp --recursive package s3://kosli-helm-charts-repo/stable/k8s-reporter/ + aws s3 cp --recursive package s3://kosli-helm-charts-repo/stable/k8s-reporter/ - name: cleanup run: | - rm -rf package + rm -rf package - name: Create branch via GitHub API run: | From e395714edec4e9b82e45baf3c633248dec0ed528 Mon Sep 17 00:00:00 2001 From: Marko Bevc Date: Thu, 16 Apr 2026 17:01:08 +0100 Subject: [PATCH 33/41] chore: re-trigger helm-docs (#795) --- charts/k8s-reporter/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index 4045d19fd..6c8b5265b 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -191,4 +191,3 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) - From c97b03ef583e5eede91dc8633cd2d1358d774149 Mon Sep 17 00:00:00 2001 From: "ci-signed-commit-bot[bot]" <247774526+ci-signed-commit-bot[bot]@users.noreply.github.com> Date: Thu, 16 Apr 2026 16:09:53 +0000 Subject: [PATCH 34/41] Update helm docs (#796) Co-authored-by: ci-signed-commit-bot[bot] <247774526+ci-signed-commit-bot[bot]@users.noreply.github.com> --- charts/k8s-reporter/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/charts/k8s-reporter/README.md b/charts/k8s-reporter/README.md index 6c8b5265b..4045d19fd 100644 --- a/charts/k8s-reporter/README.md +++ b/charts/k8s-reporter/README.md @@ -191,3 +191,4 @@ If you already run [cert-manager's trust-manager](https://cert-manager.io/docs/t ---------------------------------------------- Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) + From a480ac2dd0fa140c0c0868bce3216b2af462bd27 Mon Sep 17 00:00:00 2001 From: Faye <108031168+FayeSGW@users.noreply.github.com> Date: Fri, 17 Apr 2026 10:24:03 +0200 Subject: [PATCH 35/41] 4808 fix attestation name check (#794) * Don't allow empty strings on either side of the dot in attestation name * Add tests for attestation name check * Add tests for name check to all attest commands * Fix PR tests --- cmd/kosli/attestCustom_test.go | 6 ++++ cmd/kosli/attestGeneric_test.go | 6 ++++ cmd/kosli/attestJira_test.go | 6 ++++ cmd/kosli/attestJunit_test.go | 6 ++++ cmd/kosli/attestPRAzure_test.go | 6 ++++ cmd/kosli/attestPRBitbucket_test.go | 6 ++++ cmd/kosli/attestPRGithub_test.go | 6 ++++ cmd/kosli/attestPRGitlab_test.go | 6 ++++ cmd/kosli/attestSnyk_test.go | 6 ++++ cmd/kosli/attestSonar_test.go | 12 +++++++ cmd/kosli/attestation.go | 14 ++++---- cmd/kosli/attestation_test.go | 51 +++++++++++++++++++++++++++++ 12 files changed, 125 insertions(+), 6 deletions(-) diff --git a/cmd/kosli/attestCustom_test.go b/cmd/kosli/attestCustom_test.go index 7982c9f69..8c0921888 100644 --- a/cmd/kosli/attestCustom_test.go +++ b/cmd/kosli/attestCustom_test.go @@ -158,6 +158,12 @@ func (suite *AttestCustomCommandTestSuite) TestAttestCustomCmd() { cmd: fmt.Sprintf("attest custom --name bar --annotate foo.baz=bar %s", suite.defaultKosliArguments), golden: "Error: --annotate flag should be in the format key=value. Invalid key: 'foo.baz'. Key can only contain [A-Za-z0-9_]\n", }, + { + wantError: true, + name: "fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest custom --name .foo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestGeneric_test.go b/cmd/kosli/attestGeneric_test.go index f698f3800..df2956fbe 100644 --- a/cmd/kosli/attestGeneric_test.go +++ b/cmd/kosli/attestGeneric_test.go @@ -190,6 +190,12 @@ func (suite *AttestGenericCommandTestSuite) TestAttestGenericCmd() { cmd: fmt.Sprintf("attest generic --name foo --fingerprint 7509e5bda0c762d2bac7f90d758b5b2263fa01ccbc542ab5e3df163be08e6ca9 --repo-id test-repo-id --repository test-repo-name --repo-url https://github.com/org/repo --repo-provider github %s", suite.defaultKosliArguments), golden: "generic attestation 'foo' is reported to trail: test-123\n", }, + { + wantError: true, + name: "fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest generic --name .foo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestJira_test.go b/cmd/kosli/attestJira_test.go index fd301bb81..6676932a2 100644 --- a/cmd/kosli/attestJira_test.go +++ b/cmd/kosli/attestJira_test.go @@ -325,6 +325,12 @@ func (suite *AttestJiraCommandTestSuite) TestAttestJiraCmd() { commitMessage: "SAMI-1 test commit", }, }, + { + wantError: true, + name: "26 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest jira --name .foo --commit HEAD --jira-base-url https://kosli-test.atlassian.net %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } for _, test := range tests { diff --git a/cmd/kosli/attestJunit_test.go b/cmd/kosli/attestJunit_test.go index cae491473..b9bcdd5d1 100644 --- a/cmd/kosli/attestJunit_test.go +++ b/cmd/kosli/attestJunit_test.go @@ -122,6 +122,12 @@ func (suite *AttestJunitCommandTestSuite) TestAttestJunitCmd() { --annotate foo.bar=bar %s`, suite.defaultKosliArguments), golden: "Error: --annotate flag should be in the format key=value. Invalid key: 'foo.bar'. Key can only contain [A-Za-z0-9_]\n", }, + { + wantError: true, + name: "fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest junit --name .foo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestPRAzure_test.go b/cmd/kosli/attestPRAzure_test.go index 4582b580d..63fc71d24 100644 --- a/cmd/kosli/attestPRAzure_test.go +++ b/cmd/kosli/attestPRAzure_test.go @@ -131,6 +131,12 @@ func (suite *AttestAzurePRCommandTestSuite) TestAttestAzurePRCmd() { cmd: fmt.Sprintf("attest pullrequest azure --name foo --commit HEAD --azure-token fake --azure-org-url https://dev.azure.com/myorg --project myproject --repository myrepo --repo-provider jenkins %s", suite.defaultKosliArguments), golden: "Error: --repo-provider 'jenkins' is not allowed. Must be one of: github, gitlab, bitbucket, azure-devops\n", }, + { + wantError: true, + name: "16 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest pullrequest azure --name .foo --commit HEAD --azure-org-url https://dev.azure.com/myorg --project myproject --repository myrepo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestPRBitbucket_test.go b/cmd/kosli/attestPRBitbucket_test.go index 176d9bd73..e47cd8e86 100644 --- a/cmd/kosli/attestPRBitbucket_test.go +++ b/cmd/kosli/attestPRBitbucket_test.go @@ -170,6 +170,12 @@ func (suite *AttestBitbucketPRCommandTestSuite) TestAttestBitbucketPRCmd() { cmd: fmt.Sprintf("attest pullrequest bitbucket --name foo --commit %s --bitbucket-access-token fake --bitbucket-workspace myworkspace --repository myrepo --repo-provider jenkins %s", suite.commitWithPR, suite.defaultKosliArguments), golden: "Error: --repo-provider 'jenkins' is not allowed. Must be one of: github, gitlab, bitbucket, azure-devops\n", }, + { + wantError: true, + name: "19 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest pullrequest bitbucket --name .foo --bitbucket-workspace myworkspace --commit %s --bitbucket-access-token fake --repository myrepo %s", suite.commitWithPR, suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestPRGithub_test.go b/cmd/kosli/attestPRGithub_test.go index 2cfc18ec4..0d33646dd 100644 --- a/cmd/kosli/attestPRGithub_test.go +++ b/cmd/kosli/attestPRGithub_test.go @@ -161,6 +161,12 @@ func (suite *AttestGithubPRCommandTestSuite) TestAttestGithubPRCmd() { cmd: fmt.Sprintf("attest pullrequest github --name foo --commit %s --github-token fake --github-org myorg --repository myrepo --repo-provider jenkins %s", suite.commitWithPR, suite.defaultKosliArguments), golden: "Error: --repo-provider 'jenkins' is not allowed. Must be one of: github, gitlab, bitbucket, azure-devops\n", }, + { + wantError: true, + name: "20 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest pullrequest github --name .foo --github-org myorg --commit %s --github-token fake --repository myrepo %s", suite.commitWithPR, suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestPRGitlab_test.go b/cmd/kosli/attestPRGitlab_test.go index 129112053..4fb8abeb1 100644 --- a/cmd/kosli/attestPRGitlab_test.go +++ b/cmd/kosli/attestPRGitlab_test.go @@ -163,6 +163,12 @@ func (suite *AttestGitlabPRCommandTestSuite) TestAttestGitlabPRCmd() { cmd: fmt.Sprintf("attest pullrequest gitlab --name foo --commit %s --gitlab-token fake --gitlab-org myorg --repository myrepo --repo-provider jenkins %s", suite.commitWithPR, suite.defaultKosliArguments), golden: "Error: --repo-provider 'jenkins' is not allowed. Must be one of: github, gitlab, bitbucket, azure-devops\n", }, + { + wantError: true, + name: "18 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest pullrequest gitlab --name .foo --gitlab-org myorg --commit %s --gitlab-token fake --repository myrepo %s", suite.commitWithPR, suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestSnyk_test.go b/cmd/kosli/attestSnyk_test.go index c20f7651b..0493cf990 100644 --- a/cmd/kosli/attestSnyk_test.go +++ b/cmd/kosli/attestSnyk_test.go @@ -124,6 +124,12 @@ func (suite *AttestSnykCommandTestSuite) TestAttestSnykCmd() { --scan-results testdata/snyk_sarif.json %s`, suite.defaultKosliArguments), golden: "Error: --annotate flag should be in the format key=value. Invalid key: 'foo.baz'. Key can only contain [A-Za-z0-9_]\n", }, + { + wantError: true, + name: "fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest snyk --name .foo --scan-results testdata/snyk_sarif.json %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestSonar_test.go b/cmd/kosli/attestSonar_test.go index 52584d9fe..8b07acec3 100644 --- a/cmd/kosli/attestSonar_test.go +++ b/cmd/kosli/attestSonar_test.go @@ -247,6 +247,12 @@ func (suite *AttestSonarCommandTestSuite) TestAttestSonarCmd() { cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-ce-task-url https://sonarcloud.io/api/ce/task?id=AZERk4uWpzGpahwkB9ac %s", suite.defaultKosliArguments), golden: "Error: No activity found for task 'AZERk4uWpzGpahwkB9ac' on https://sonarcloud.io. \nSonarQube may be experiencing problems, please check https://status.sonarqube.com/ and try again later. \nOtherwise if you are attesting an older scan, the snapshot may have been deleted by SonarQube\n", }, + { + wantError: true, + name: "29 fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest sonar --name .foo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) @@ -353,6 +359,12 @@ func (suite *AttestSonarQubeCommandTestSuite) TestAttestSonarQubeCmd() { cmd: fmt.Sprintf("attest sonar --name cli.foo --commit HEAD --origin-url http://www.example.com --sonar-working-dir testdata/sonar/sonarqube/.scannerwork --pull-request 359 --sonar-server-url http://example.com %s", suite.defaultKosliArguments), golden: "sonar attestation 'foo' is reported to trail: test-123\n", }, + { + wantError: true, + name: "fails when --name has invalid dot format", + cmd: fmt.Sprintf("attest sonar --name .foo %s", suite.defaultKosliArguments), + golden: "Error: failed to parse attestation name: invalid attestation name format: .foo\n", + }, } runTestCmd(suite.T(), tests) diff --git a/cmd/kosli/attestation.go b/cmd/kosli/attestation.go index 48b880bd5..9736dae93 100644 --- a/cmd/kosli/attestation.go +++ b/cmd/kosli/attestation.go @@ -195,14 +195,16 @@ func prepareAttestationForm(payload interface{}, evidencePaths []string) ([]requ } func parseAttestationNameTemplate(template string) (string, string, error) { - parts := strings.Split(template, ".") - if len(parts) == 1 { - return parts[0], "", nil - } else if len(parts) == 2 { - return parts[0], parts[1], nil - } else { + p1, p2, found := strings.Cut(template, ".") + // No dot: treat the whole string as the attestation name + if !found { + return template, "", nil + } + // Reject empty sides (e.g. ".foo", "foo.") or multiple dots (e.g. "foo.bar.baz") + if p1 == "" || p2 == "" || strings.Contains(p2, ".") { return "", "", fmt.Errorf("invalid attestation name format: %s", template) } + return p1, p2, nil } // newAttestationForm constructs a list of FormItems for an attestation diff --git a/cmd/kosli/attestation_test.go b/cmd/kosli/attestation_test.go index ebf6b5bf8..c48810f90 100644 --- a/cmd/kosli/attestation_test.go +++ b/cmd/kosli/attestation_test.go @@ -116,3 +116,54 @@ func TestMergeGitRepoInfo(t *testing.T) { }) } } + +func TestParseAttestationNameTemplate(t *testing.T) { + tests := []struct { + name string + template string + wantP1 string + wantP2 string + wantError bool + }{ + { + name: "no dot returns whole string as p1", + template: "myattestation", + wantP1: "myattestation", + wantP2: "", + }, + { + name: "dot separates flow and attestation name", + template: "myflow.myattestation", + wantP1: "myflow", + wantP2: "myattestation", + }, + { + name: "leading dot is invalid", + template: ".myattestation", + wantError: true, + }, + { + name: "trailing dot is invalid", + template: "myflow.", + wantError: true, + }, + { + name: "multiple dots are invalid", + template: "myflow.myattestation.extra", + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + p1, p2, err := parseAttestationNameTemplate(tt.template) + if tt.wantError { + assert.Error(t, err) + return + } + assert.NoError(t, err) + assert.Equal(t, tt.wantP1, p1) + assert.Equal(t, tt.wantP2, p2) + }) + } +} From bebcccdcfab26e6b529039f0e4a7aec2c9b4ddd3 Mon Sep 17 00:00:00 2001 From: Faye <108031168+FayeSGW@users.noreply.github.com> Date: Fri, 17 Apr 2026 12:09:41 +0200 Subject: [PATCH 36/41] Make slack failure webhook trigger on main instead of master (#797) --- .github/workflows/daily-cli-tests.yml | 2 +- .github/workflows/main.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/daily-cli-tests.yml b/.github/workflows/daily-cli-tests.yml index ec9cc4646..0cc3d4ad6 100644 --- a/.github/workflows/daily-cli-tests.yml +++ b/.github/workflows/daily-cli-tests.yml @@ -64,7 +64,7 @@ jobs: set-trail-name, test, ] - if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} + if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a98af2ae7..0ac962cb6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -133,7 +133,7 @@ jobs: test, docker ] - if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} + if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 791ca022a..2e6d0199b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -361,7 +361,7 @@ jobs: environment-reporter-upload-package-and-deploy, environment-reporter-upload-layer ] - if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/master' }} + if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 From 29ebc42909bf29eb1db00f8cc769e498d9aacbca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 18:05:49 +0100 Subject: [PATCH 37/41] chore(deps): bump github.com/moby/spdystream from 0.5.0 to 0.5.1 (#798) Bumps [github.com/moby/spdystream](https://github.com/moby/spdystream) from 0.5.0 to 0.5.1. - [Release notes](https://github.com/moby/spdystream/releases) - [Commits](https://github.com/moby/spdystream/compare/v0.5.0...v0.5.1) --- updated-dependencies: - dependency-name: github.com/moby/spdystream dependency-version: 0.5.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 7d658caf4..d801cef99 100644 --- a/go.mod +++ b/go.mod @@ -149,7 +149,7 @@ require ( github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/spdystream v0.5.1 // 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 diff --git a/go.sum b/go.sum index 06a48083c..a68f9ee82 100644 --- a/go.sum +++ b/go.sum @@ -340,8 +340,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/spdystream v0.5.1 h1:9sNYeYZUcci9R6/w7KDaFWEWeV4LStVG78Mpyq/Zm/Y= +github.com/moby/spdystream v0.5.1/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= From 293155b0721aca16cacf1c8e09ba06bed7074e9c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:23:49 +0100 Subject: [PATCH 38/41] chore(deps): bump the go-dependencies group with 6 updates (#800) Bumps the go-dependencies group with 6 updates: | Package | From | To | | --- | --- | --- | | [github.com/Azure/azure-sdk-for-go/sdk/azcore](https://github.com/Azure/azure-sdk-for-go) | `1.21.0` | `1.21.1` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.32.14` | `1.32.15` | | [github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager](https://github.com/aws/aws-sdk-go-v2) | `0.1.15` | `0.1.16` | | [github.com/aws/smithy-go](https://github.com/aws/smithy-go) | `1.24.3` | `1.25.0` | | [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) | `5.17.2` | `5.18.0` | | [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes) | `1.35.3` | `1.35.4` | Updates `github.com/Azure/azure-sdk-for-go/sdk/azcore` from 1.21.0 to 1.21.1 - [Release notes](https://github.com/Azure/azure-sdk-for-go/releases) - [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.21.0...sdk/azcore/v1.21.1) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.32.14 to 1.32.15 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.32.14...config/v1.32.15) Updates `github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager` from 0.1.15 to 0.1.16 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/feature/s3/transfermanager/v0.1.15...feature/s3/transfermanager/v0.1.16) Updates `github.com/aws/smithy-go` from 1.24.3 to 1.25.0 - [Release notes](https://github.com/aws/smithy-go/releases) - [Changelog](https://github.com/aws/smithy-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/aws/smithy-go/compare/v1.24.3...v1.25.0) Updates `github.com/go-git/go-git/v5` from 5.17.2 to 5.18.0 - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.17.2...v5.18.0) Updates `k8s.io/kubernetes` from 1.35.3 to 1.35.4 - [Release notes](https://github.com/kubernetes/kubernetes/releases) - [Commits](https://github.com/kubernetes/kubernetes/compare/v1.35.3...v1.35.4) --- updated-dependencies: - dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azcore dependency-version: 1.21.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-version: 1.32.15 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager dependency-version: 0.1.16 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies - dependency-name: github.com/aws/smithy-go dependency-version: 1.25.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: github.com/go-git/go-git/v5 dependency-version: 5.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-dependencies - dependency-name: k8s.io/kubernetes dependency-version: 1.35.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 25 ++++++++++++------------- go.sum | 50 ++++++++++++++++++++++++-------------------------- 2 files changed, 36 insertions(+), 39 deletions(-) diff --git a/go.mod b/go.mod index d801cef99..c7c51428d 100644 --- a/go.mod +++ b/go.mod @@ -3,24 +3,24 @@ module github.com/kosli-dev/cli go 1.25.9 require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1 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/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/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/config v1.32.15 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/feature/s3/transfermanager v0.1.16 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/s3 v1.99.0 - github.com/aws/smithy-go v1.24.3 + github.com/aws/smithy-go v1.25.0 github.com/containers/image/v5 v5.36.2 github.com/docker/docker v28.5.2+incompatible github.com/go-git/go-billy/v5 v5.8.0 - github.com/go-git/go-git/v5 v5.17.2 + github.com/go-git/go-git/v5 v5.18.0 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 @@ -49,7 +49,7 @@ require ( 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 + k8s.io/kubernetes v1.35.4 sigs.k8s.io/kind v0.31.0 ) @@ -57,7 +57,7 @@ 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/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/BurntSushi/toml v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect @@ -68,7 +68,6 @@ require ( github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect @@ -207,15 +206,15 @@ require ( 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.50.0 // indirect golang.org/x/exp v0.0.0-20250813145105-42675adae3e6 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.51.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.53.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/text v0.36.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.42.0 // indirect + golang.org/x/tools v0.43.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 diff --git a/go.sum b/go.sum index a68f9ee82..af37640be 100644 --- a/go.sum +++ b/go.sum @@ -4,16 +4,16 @@ cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= 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/azcore v1.21.1 h1:jHb/wfvRikGdxMXYV3QG/SzUOPYN9KEUUuC0Yd0/vC0= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.1/go.mod h1:pzBXCYn05zvYIrwLgtK8Ap8QcjRg+0i76tMQdWN6wOk= 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= 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/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY= 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= @@ -50,20 +50,18 @@ 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/config v1.32.15 h1:i7rHbaySnBXGvCkDndaBU8f3EAlRVgViwNfkwFUrXgE= +github.com/aws/aws-sdk-go-v2/config v1.32.15/go.mod h1:yLJzL0IkI9+4BwjPSOueyHzppJj3t0dhK5tbmmcFk5Q= 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/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.16 h1:n8TmP5vlknh1B/mVNrNgQfSvQy0isR9B9IgADdwuhhY= +github.com/aws/aws-sdk-go-v2/feature/s3/transfermanager v0.1.16/go.mod h1:Iu9wL4lqscFF6ByhqyDO8mgvCUwGn5bqWr7fuOgUjTA= 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= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw= -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= @@ -88,8 +86,8 @@ 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.25.0 h1:Sz/XJ64rwuiKtB6j98nDIPyYrV1nVNJ4YU74gttcl5U= +github.com/aws/smithy-go v1.25.0/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc= 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/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -193,8 +191,8 @@ 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.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM= +github.com/go-git/go-git/v5 v5.18.0/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.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -530,17 +528,17 @@ 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-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI= +golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q= 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/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -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/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= 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= @@ -564,13 +562,13 @@ golang.org/x/text v0.3.0/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.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/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= 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.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= 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/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= @@ -624,8 +622,8 @@ 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.4 h1:o/8dBC/pHVpYoGV4OAIytAlNPZbdYVTXJHoYvXC4qzM= +k8s.io/kubernetes v1.35.4/go.mod h1:fPfnQs8GtfrLQ+KuOcpvwQ+mV17jVcgdvPL6ZHxKp10= 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-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= From f4a72c51848377d0376e38a39a2b1ce1a43de4ec Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:25:19 +0100 Subject: [PATCH 39/41] chore(deps): bump step-security/harden-runner (#801) Bumps the github-actions-dependencies group with 1 update: [step-security/harden-runner](https://github.com/step-security/harden-runner). Updates `step-security/harden-runner` from 2.17.0 to 2.18.0 - [Release notes](https://github.com/step-security/harden-runner/releases) - [Commits](https://github.com/step-security/harden-runner/compare/f808768d1510423e83855289c910610ca9b43176...6c3c2f2c1c457b00c10c4848d6f5491db3b629df) --- updated-dependencies: - dependency-name: step-security/harden-runner dependency-version: 2.18.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions-dependencies ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/binary_provenance.yml | 2 +- .github/workflows/claude-pr-review.yml | 4 ++-- .github/workflows/daily-cli-tests.yml | 4 ++-- .github/workflows/docker.yml | 6 +++--- .github/workflows/helm-chart.yml | 2 +- .github/workflows/init_kosli.yml | 2 +- .github/workflows/install-script-tests.yml | 4 ++-- .github/workflows/main.yml | 4 ++-- .github/workflows/never_alone_trail.yml | 2 +- .github/workflows/publish_branch_docs.yml | 2 +- .github/workflows/publish_docs.yml | 2 +- .github/workflows/release.yml | 16 ++++++++-------- .github/workflows/test.yml | 8 ++++---- .github/workflows/upload-cli-layer.yml | 2 +- 14 files changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/binary_provenance.yml b/.github/workflows/binary_provenance.yml index e093cbad6..e7353640c 100644 --- a/.github/workflows/binary_provenance.yml +++ b/.github/workflows/binary_provenance.yml @@ -35,7 +35,7 @@ jobs: artifact: ${{fromJson(inputs.artifacts)}} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/claude-pr-review.yml b/.github/workflows/claude-pr-review.yml index 299bf81ed..6c5e65ee4 100644 --- a/.github/workflows/claude-pr-review.yml +++ b/.github/workflows/claude-pr-review.yml @@ -27,7 +27,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -80,7 +80,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/daily-cli-tests.yml b/.github/workflows/daily-cli-tests.yml index 0cc3d4ad6..1d109be2b 100644 --- a/.github/workflows/daily-cli-tests.yml +++ b/.github/workflows/daily-cli-tests.yml @@ -12,7 +12,7 @@ jobs: trail_name: ${{ steps.prep.outputs.trail_name }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -67,7 +67,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 2a6da0251..7bde3e659 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -63,7 +63,7 @@ jobs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -120,7 +120,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -187,7 +187,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/helm-chart.yml b/.github/workflows/helm-chart.yml index c7f1a811d..cd70d05bf 100644 --- a/.github/workflows/helm-chart.yml +++ b/.github/workflows/helm-chart.yml @@ -17,7 +17,7 @@ jobs: pull-requests: write steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/init_kosli.yml b/.github/workflows/init_kosli.yml index fabafa3af..7cb79b59f 100644 --- a/.github/workflows/init_kosli.yml +++ b/.github/workflows/init_kosli.yml @@ -40,7 +40,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/install-script-tests.yml b/.github/workflows/install-script-tests.yml index 7a6f08467..8d4f1716d 100644 --- a/.github/workflows/install-script-tests.yml +++ b/.github/workflows/install-script-tests.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -52,7 +52,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0ac962cb6..0867280e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,7 +22,7 @@ jobs: report_to_kosli: ${{ steps.prep.outputs.report_to_kosli }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -136,7 +136,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/never_alone_trail.yml b/.github/workflows/never_alone_trail.yml index 00867b6b5..033ab44f0 100644 --- a/.github/workflows/never_alone_trail.yml +++ b/.github/workflows/never_alone_trail.yml @@ -41,7 +41,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/publish_branch_docs.yml b/.github/workflows/publish_branch_docs.yml index c1ace8505..a29b2b552 100644 --- a/.github/workflows/publish_branch_docs.yml +++ b/.github/workflows/publish_branch_docs.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml index 3546bbbba..5a3b562a1 100644 --- a/.github/workflows/publish_docs.yml +++ b/.github/workflows/publish_docs.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2e6d0199b..e999f8e46 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: trail_template_file: ${{ steps.prep.outputs.trail_template_file }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -126,7 +126,7 @@ jobs: artifacts: ${{ steps.prepare-artifacts-list.outputs.artifacts }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -226,7 +226,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -244,7 +244,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -286,7 +286,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -303,7 +303,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -320,7 +320,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -364,7 +364,7 @@ jobs: if: ${{ always() && contains(join(needs.*.result, ','), 'failure') && github.ref == 'refs/heads/main' }} steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0ac65c1aa..06e96a2a4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -74,7 +74,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -120,7 +120,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -196,7 +196,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit @@ -240,7 +240,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit diff --git a/.github/workflows/upload-cli-layer.yml b/.github/workflows/upload-cli-layer.yml index 42e655df5..03974d283 100644 --- a/.github/workflows/upload-cli-layer.yml +++ b/.github/workflows/upload-cli-layer.yml @@ -21,7 +21,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@f808768d1510423e83855289c910610ca9b43176 # v2.17.0 + uses: step-security/harden-runner@6c3c2f2c1c457b00c10c4848d6f5491db3b629df # v2.18.0 with: egress-policy: audit From cc362f9c987b7e83967f5f321f39f69ecb9c8fb9 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Mon, 20 Apr 2026 16:16:13 +0200 Subject: [PATCH 40/41] fix: remove duplicate/orphaned line in FakeLambdaClient.ListFunctions A stray `pageSize = int(*params.MaxItems)` and closing brace outside the if-block caused a syntax error. Remove the dangling lines. Co-Authored-By: Claude Sonnet 4.6 --- internal/aws/fake_lambda.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/aws/fake_lambda.go b/internal/aws/fake_lambda.go index a855baa4f..a232dc26c 100644 --- a/internal/aws/fake_lambda.go +++ b/internal/aws/fake_lambda.go @@ -36,8 +36,6 @@ func (f *FakeLambdaClient) ListFunctions(_ context.Context, params *lambda.ListF pageSize = maxItems } } - pageSize = int(*params.MaxItems) - } start := 0 if params.Marker != nil { From 2b926ef6ec0bfb7a00bb4bfb36d98dc132a47a32 Mon Sep 17 00:00:00 2001 From: Simon Castagna Date: Tue, 21 Apr 2026 11:53:16 +0200 Subject: [PATCH 41/41] address review comments: add error-type comment and rename skipOrSetCreds - fake_lambda.go: note that real AWS returns *types.ResourceNotFoundException - aws_test.go: rename skipOrSetCreds -> skipIfCredsUnset (it only skips) Co-Authored-By: Claude Sonnet 4.6 --- internal/aws/aws_test.go | 8 ++++---- internal/aws/fake_lambda.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/aws/aws_test.go b/internal/aws/aws_test.go index 130b943af..f87edeaa3 100644 --- a/internal/aws/aws_test.go +++ b/internal/aws/aws_test.go @@ -300,7 +300,7 @@ func (suite *AWSTestSuite) TestGetLambdaPackageData() { }, } { suite.Run(t.name, func() { - skipOrSetCreds(suite.T(), t.requireEnvVars, t.creds) + skipIfCredsUnset(suite.T(), t.requireEnvVars, t.creds) data, err := t.creds.GetLambdaPackageData(t.filter) require.False(suite.T(), (err != nil) != t.wantErr, "GetLambdaPackageData() error = %v, wantErr %v", err, t.wantErr) @@ -436,7 +436,7 @@ func (suite *AWSTestSuite) TestGetS3Data() { }, } { suite.Run(t.name, func() { - skipOrSetCreds(suite.T(), t.requireEnvVars, t.creds) + skipIfCredsUnset(suite.T(), t.requireEnvVars, t.creds) data, err := t.creds.GetS3Data(t.bucketName, t.includePaths, t.excludePaths, logger.NewStandardLogger()) require.False(suite.T(), (err != nil) != t.wantErr, "GetS3Data() error = %v, wantErr %v", err, t.wantErr) @@ -579,7 +579,7 @@ func (suite *AWSTestSuite) TestGetEcsTasksData() { }, } { suite.Run(t.name, func() { - skipOrSetCreds(suite.T(), t.requireEnvVars, t.creds) + skipIfCredsUnset(suite.T(), t.requireEnvVars, t.creds) if t.clusterFilter == nil { t.clusterFilter = new(filters.ResourceFilterOptions) } @@ -814,7 +814,7 @@ func (suite *AWSTestSuite) TestGetLambdaPackageDataFromClient() { } } -func skipOrSetCreds(T *testing.T, requireEnvVars bool, creds *AWSStaticCreds) { +func skipIfCredsUnset(T *testing.T, requireEnvVars bool, creds *AWSStaticCreds) { if requireEnvVars { // skips the test case if it requires env vars and they are not set testHelpers.SkipIfEnvVarUnset(T, []string{"AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"}) diff --git a/internal/aws/fake_lambda.go b/internal/aws/fake_lambda.go index a232dc26c..3db2fc0cd 100644 --- a/internal/aws/fake_lambda.go +++ b/internal/aws/fake_lambda.go @@ -79,5 +79,5 @@ func (f *FakeLambdaClient) GetFunctionConfiguration(_ context.Context, params *l }, nil } } - return nil, fmt.Errorf("function not found: %s", *params.FunctionName) + return nil, fmt.Errorf("function not found: %s", *params.FunctionName) // Real AWS returns *types.ResourceNotFoundException }