diff --git a/.golangci.yml b/.golangci.yml index 8c9abc90..d659ccb4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -34,7 +34,6 @@ linters: - ineffassign - interfacebloat - intrange - - ireturn - makezero - mirror - misspell diff --git a/Makefile b/Makefile index f86afaff..63f7fe32 100644 --- a/Makefile +++ b/Makefile @@ -101,7 +101,7 @@ devcheck: devchecknotest spincontainer test removetestcontainer importfmtlint: @echo -n "Running 'impi' to format import ordering..." - @if go tool impi --local . --scheme stdThirdPartyLocal ./...; then \ + @if go tool impi --skip internal/proto/pingcli_command --local . --scheme stdThirdPartyLocal ./...; then \ echo " SUCCESS"; \ else \ echo " FAILED"; \ @@ -111,7 +111,12 @@ importfmtlint: golangcilint: @echo -n "Running 'golangci-lint' to check for code quality issues... " @# Clear the cache for every run, so that the linter outputs the same results as the GH Actions workflow - @go tool golangci-lint cache clean && go tool golangci-lint run --timeout 5m ./... + @if go tool golangci-lint cache clean && go tool golangci-lint run --timeout 5m ./...; then \ + echo "SUCCESS"; \ + else \ + echo "FAILED"; \ + exit 1; \ + fi starttestcontainer: --checkpfcontainerenvvars --checkdocker --dockerrunpf --waitforpfhealthy @@ -186,3 +191,12 @@ openapp: echo " FAILED"; \ exit 1; \ fi + +protogen: + @echo -n "Running 'protoc' to generate Go code from proto files..." + @if protoc --proto_path=./internal/proto --go_out=./internal --go-grpc_out=./internal ./internal/proto/*.proto; then \ + echo " SUCCESS"; \ + else \ + echo " FAILED"; \ + exit 1; \ + fi diff --git a/cmd/config/list_keys_test.go b/cmd/config/list_keys_test.go index 46ac6dc8..c117b756 100644 --- a/cmd/config/list_keys_test.go +++ b/cmd/config/list_keys_test.go @@ -59,6 +59,7 @@ func Example_listKeysValue() { // - export.services // - noColor // - outputFormat + // - plugins // - request.accessToken // - request.accessTokenExpiry // - request.fail diff --git a/cmd/config/unset_test.go b/cmd/config/unset_test.go index f6d7cc0c..430676e6 100644 --- a/cmd/config/unset_test.go +++ b/cmd/config/unset_test.go @@ -52,6 +52,8 @@ func TestConfigUnsetCmd_CheckKoanfConfig(t *testing.T) { err := testutils_cobra.ExecutePingcli(t, "config", "unset", koanfKey) testutils.CheckExpectedError(t, err, nil) + koanfConfig = profiles.GetKoanfConfig().KoanfInstance() + koanfNewValue := koanfConfig.String(profileKoanfKey) if koanfOldValue == koanfNewValue { t.Errorf("Expected koanf configuration value to be updated. Old: %s, New: %s", koanfOldValue, koanfNewValue) diff --git a/cmd/platform/export_test.go b/cmd/platform/export_test.go index 0cafbb11..080404c6 100644 --- a/cmd/platform/export_test.go +++ b/cmd/platform/export_test.go @@ -15,6 +15,7 @@ import ( // Test Platform Export Command Executes without issue func TestPlatformExportCmd_Execute(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -25,8 +26,6 @@ func TestPlatformExportCmd_Execute(t *testing.T) { // Test Platform Export Command fails when provided too many arguments func TestPlatformExportCmd_TooManyArgs(t *testing.T) { - testutils_koanf.InitKoanfs(t) - expectedErrorPattern := `^failed to execute 'pingcli platform export': command accepts 0 arg\(s\), received 1$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "extra-arg") testutils.CheckExpectedError(t, err, &expectedErrorPattern) @@ -50,6 +49,7 @@ func TestPlatformExportCmd_HelpFlag(t *testing.T) { // Test Platform Export Command --service-group, -g flag func TestPlatformExportCmd_ServiceGroupFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -61,6 +61,8 @@ func TestPlatformExportCmd_ServiceGroupFlag(t *testing.T) { // Test Platform Export Command --service-group with non-supported service group func TestPlatformExportCmd_ServiceGroupFlagInvalidServiceGroup(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-g, --service-group" flag: unrecognized service group '.*'\. Must be one of: .*$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PlatformExportServiceGroupOption.CobraParamName, "invalid") @@ -69,6 +71,7 @@ func TestPlatformExportCmd_ServiceGroupFlagInvalidServiceGroup(t *testing.T) { // Test Platform Export Command --services flag func TestPlatformExportCmd_ServicesFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -80,6 +83,8 @@ func TestPlatformExportCmd_ServicesFlag(t *testing.T) { // Test Platform Export Command --services flag with invalid service func TestPlatformExportCmd_ServicesFlagInvalidService(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-s, --services" flag: failed to set ExportServices: Invalid service: .*\. Allowed services: .*$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PlatformExportServiceOption.CobraParamName, "invalid") @@ -88,6 +93,7 @@ func TestPlatformExportCmd_ServicesFlagInvalidService(t *testing.T) { // Test Platform Export Command --format flag func TestPlatformExportCmd_ExportFormatFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -100,6 +106,8 @@ func TestPlatformExportCmd_ExportFormatFlag(t *testing.T) { // Test Platform Export Command --format flag with invalid format func TestPlatformExportCmd_ExportFormatFlagInvalidFormat(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-f, --format" flag: unrecognized export format '.*'\. Must be one of: .*$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PlatformExportExportFormatOption.CobraParamName, "invalid") @@ -108,6 +116,7 @@ func TestPlatformExportCmd_ExportFormatFlagInvalidFormat(t *testing.T) { // Test Platform Export Command --output-directory flag func TestPlatformExportCmd_OutputDirectoryFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -119,6 +128,8 @@ func TestPlatformExportCmd_OutputDirectoryFlag(t *testing.T) { // Test Platform Export Command --output-directory flag with invalid directory func TestPlatformExportCmd_OutputDirectoryFlagInvalidDirectory(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^failed to create output directory '\/invalid': mkdir \/invalid: .+$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PlatformExportOutputDirectoryOption.CobraParamName, "/invalid") @@ -127,6 +138,7 @@ func TestPlatformExportCmd_OutputDirectoryFlagInvalidDirectory(t *testing.T) { // Test Platform Export Command --overwrite flag func TestPlatformExportCmd_OverwriteFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -139,6 +151,7 @@ func TestPlatformExportCmd_OverwriteFlag(t *testing.T) { // Test Platform Export Command --overwrite flag false with existing directory // where the directory already contains a file func TestPlatformExportCmd_OverwriteFlagFalseWithExistingDirectory(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() _, err := os.Create(outputDir + "/file") //#nosec G304 -- this is a test @@ -157,6 +170,7 @@ func TestPlatformExportCmd_OverwriteFlagFalseWithExistingDirectory(t *testing.T) // Test Platform Export Command --overwrite flag true with existing directory // where the directory already contains a file func TestPlatformExportCmd_OverwriteFlagTrueWithExistingDirectory(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() _, err := os.Create(outputDir + "/file") //#nosec G304 -- this is a test @@ -177,6 +191,7 @@ func TestPlatformExportCmd_OverwriteFlagTrueWithExistingDirectory(t *testing.T) // --pingone-worker-client-secret flag // --pingone-region flag func TestPlatformExportCmd_PingOneWorkerEnvironmentIdFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -192,6 +207,8 @@ func TestPlatformExportCmd_PingOneWorkerEnvironmentIdFlag(t *testing.T) { // Test Platform Export Command fails when not provided required pingone flags together func TestPlatformExportCmd_PingOneWorkerEnvironmentIdFlagRequiredTogether(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^if any flags in the group \[pingone-worker-environment-id pingone-worker-client-id pingone-worker-client-secret pingone-region-code] are set they must all be set; missing \[pingone-region-code pingone-worker-client-id pingone-worker-client-secret]$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PingOneAuthenticationWorkerEnvironmentIDOption.CobraParamName, os.Getenv("TEST_PINGONE_ENVIRONMENT_ID")) @@ -200,6 +217,7 @@ func TestPlatformExportCmd_PingOneWorkerEnvironmentIdFlagRequiredTogether(t *tes // Test Platform Export command with PingFederate Basic Auth flags func TestPlatformExportCmd_PingFederateBasicAuthFlags(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -215,6 +233,8 @@ func TestPlatformExportCmd_PingFederateBasicAuthFlags(t *testing.T) { // Test Platform Export Command fails when not provided required PingFederate Basic Auth flags together func TestPlatformExportCmd_PingFederateBasicAuthFlagsRequiredTogether(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^if any flags in the group \[pingfederate-username pingfederate-password] are set they must all be set; missing \[pingfederate-password]$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PingFederateBasicAuthUsernameOption.CobraParamName, "Administrator") @@ -223,6 +243,7 @@ func TestPlatformExportCmd_PingFederateBasicAuthFlagsRequiredTogether(t *testing // Test Platform Export Command fails when provided invalid PingOne Client Credential flags func TestPlatformExportCmd_PingOneClientCredentialFlagsInvalid(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() expectedErrorPattern := `^failed to initialize pingone API client\. Check worker client ID, worker client secret, worker environment ID, and pingone region code configuration values\. oauth2: \"invalid_client\" \"Request denied: Unsupported authentication method \(Correlation ID: .*\)\"$` @@ -240,6 +261,7 @@ func TestPlatformExportCmd_PingOneClientCredentialFlagsInvalid(t *testing.T) { // Test Platform Export Command fails when provided invalid PingFederate Basic Auth flags func TestPlatformExportCmd_PingFederateBasicAuthFlagsInvalid(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() expectedErrorPattern := `^failed to initialize PingFederate Go Client. Check authentication type and credentials$` @@ -256,6 +278,8 @@ func TestPlatformExportCmd_PingFederateBasicAuthFlagsInvalid(t *testing.T) { // Test Platform Export Command fails when not provided required PingFederate Client Credentials Auth flags together func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsRequiredTogether(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^if any flags in the group \[pingfederate-client-id pingfederate-client-secret pingfederate-token-url] are set they must all be set; missing \[pingfederate-client-secret pingfederate-token-url]$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PingFederateClientCredentialsAuthClientIDOption.CobraParamName, "test") @@ -264,6 +288,7 @@ func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsRequiredTogethe // Test Platform Export Command fails when provided invalid PingFederate Client Credentials Auth flags func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsInvalid(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() expectedErrorPattern := `^failed to initialize PingFederate Go Client. Check authentication type and credentials$` @@ -282,6 +307,7 @@ func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsInvalid(t *test // Test Platform Export Command fails when provided invalid PingFederate OAuth2 Token URL func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsInvalidTokenURL(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() expectedErrorPattern := `^failed to initialize PingFederate Go Client. Check authentication type and credentials$` @@ -299,6 +325,7 @@ func TestPlatformExportCmd_PingFederateClientCredentialsAuthFlagsInvalidTokenURL // Test Platform Export command with PingFederate X-Bypass Header set to true func TestPlatformExportCmd_PingFederateXBypassHeaderFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -315,6 +342,7 @@ func TestPlatformExportCmd_PingFederateXBypassHeaderFlag(t *testing.T) { // Test Platform Export command with PingFederate --pingfederate-insecure-trust-all-tls flag set to true func TestPlatformExportCmd_PingFederateTrustAllTLSFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -331,6 +359,7 @@ func TestPlatformExportCmd_PingFederateTrustAllTLSFlag(t *testing.T) { // Test Platform Export command fails with PingFederate --pingfederate-insecure-trust-all-tls flag set to false func TestPlatformExportCmd_PingFederateTrustAllTLSFlagFalse(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() expectedErrorPattern := `^failed to initialize PingFederate Go Client. Check authentication type and credentials$` @@ -350,6 +379,7 @@ func TestPlatformExportCmd_PingFederateTrustAllTLSFlagFalse(t *testing.T) { // --pingfederate-insecure-trust-all-tls=false // and --pingfederate-ca-certificate-pem-files set func TestPlatformExportCmd_PingFederateCaCertificatePemFiles(t *testing.T) { + testutils_koanf.InitKoanfs(t) outputDir := t.TempDir() err := testutils_cobra.ExecutePingcli(t, "platform", "export", @@ -367,6 +397,8 @@ func TestPlatformExportCmd_PingFederateCaCertificatePemFiles(t *testing.T) { // Test Platform Export command fails with --pingfederate-ca-certificate-pem-files set to non-existent file. func TestPlatformExportCmd_PingFederateCaCertificatePemFilesInvalid(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^failed to read CA certificate PEM file '.*': open .*: no such file or directory$` err := testutils_cobra.ExecutePingcli(t, "platform", "export", "--"+options.PlatformExportServiceOption.CobraParamName, customtypes.ENUM_EXPORT_SERVICE_PINGFEDERATE, diff --git a/cmd/plugin/add.go b/cmd/plugin/add.go new file mode 100644 index 00000000..519b7f0e --- /dev/null +++ b/cmd/plugin/add.go @@ -0,0 +1,40 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin + +import ( + "github.com/pingidentity/pingcli/cmd/common" + plugin_internal "github.com/pingidentity/pingcli/internal/commands/plugin" + "github.com/pingidentity/pingcli/internal/logger" + "github.com/spf13/cobra" +) + +const ( + addPluginCommandExamples = ` Add a plugin to use with PingCLI. + pingcli plugin add pingcli-plugin-executable` +) + +func NewPluginAddCommand() *cobra.Command { + cmd := &cobra.Command{ + Args: common.ExactArgs(1), + DisableFlagsInUseLine: true, // We write our own flags in @Use attribute + Example: addPluginCommandExamples, + Long: `Add a plugin to use with PingCLI.`, + RunE: pluginAddRunE, + Short: "Add a plugin to use with PingCLI", + Use: "add plugin-executable", + } + + return cmd +} + +func pluginAddRunE(cmd *cobra.Command, args []string) error { + l := logger.Get() + l.Debug().Msgf("Plugin Add Subcommand Called.") + + if err := plugin_internal.RunInternalPluginAdd(args[0]); err != nil { + return err + } + + return nil +} diff --git a/cmd/plugin/add_test.go b/cmd/plugin/add_test.go new file mode 100644 index 00000000..ff083a68 --- /dev/null +++ b/cmd/plugin/add_test.go @@ -0,0 +1,85 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_test + +import ( + "os" + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_cobra" +) + +// Test Plugin add Command Executes without issue +func TestPluginAddCmd_Execute(t *testing.T) { + // Create a temporary PATH for a test plugin + pathDir := t.TempDir() + t.Setenv("PATH", pathDir) + + testPlugin, err := os.CreateTemp(pathDir, "test-plugin-*.sh") + if err != nil { + t.Fatalf("Failed to create temporary plugin file: %v", err) + } + + defer func() { + err = os.Remove(testPlugin.Name()) + if err != nil { + t.Fatalf("Failed to remove temporary plugin file: %v", err) + } + }() + + _, err = testPlugin.WriteString("#!/usr/bin/env sh\necho \"Hello, world!\"\nexit 0\n") + if err != nil { + t.Fatalf("Failed to write to temporary plugin file: %v", err) + } + + err = testPlugin.Chmod(0755) + if err != nil { + t.Fatalf("Failed to set permissions on temporary plugin file: %v", err) + } + + err = testPlugin.Close() + if err != nil { + t.Fatalf("Failed to close temporary plugin file: %v", err) + } + + err = testutils_cobra.ExecutePingcli(t, "plugin", "add", testPlugin.Name()) + testutils.CheckExpectedError(t, err, nil) +} + +// Test Plugin add Command fails when provided a non-existent plugin +func TestPluginAddCmd_NonExistentPlugin(t *testing.T) { + expectedErrorPattern := `^failed to add plugin: exec: .*: executable file not found in \$PATH$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "add", "non-existent-plugin") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin add Command fails when provided too many arguments +func TestPluginAddCmd_TooManyArgs(t *testing.T) { + expectedErrorPattern := `^failed to execute 'pingcli plugin add': command accepts 1 arg\(s\), received 2$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "add", "test-plugin-name", "extra-arg") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin add Command fails when provided too few arguments +func TestPluginAddCmd_TooFewArgs(t *testing.T) { + expectedErrorPattern := `^failed to execute 'pingcli plugin add': command accepts 1 arg\(s\), received 0$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "add") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin add Command fails when provided an invalid flag +func TestPluginAddCmd_InvalidFlag(t *testing.T) { + expectedErrorPattern := `^unknown flag: --invalid$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "add", "test-plugin-name", "--invalid") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin add Command --help, -h flag +func TestPluginAddCmd_HelpFlag(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "add", "--help") + testutils.CheckExpectedError(t, err, nil) + + err = testutils_cobra.ExecutePingcli(t, "plugin", "add", "-h") + testutils.CheckExpectedError(t, err, nil) +} diff --git a/cmd/plugin/list.go b/cmd/plugin/list.go new file mode 100644 index 00000000..7f92c712 --- /dev/null +++ b/cmd/plugin/list.go @@ -0,0 +1,40 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin + +import ( + "github.com/pingidentity/pingcli/cmd/common" + plugin_internal "github.com/pingidentity/pingcli/internal/commands/plugin" + "github.com/pingidentity/pingcli/internal/logger" + "github.com/spf13/cobra" +) + +const ( + listPluginCommandExamples = ` List all plugins currently in use with PingCLI. + pingcli plugin list` +) + +func NewPluginListCommand() *cobra.Command { + cmd := &cobra.Command{ + Args: common.ExactArgs(0), + DisableFlagsInUseLine: true, // We write our own flags in @Use attribute + Example: listPluginCommandExamples, + Long: `List all plugins currently in use with PingCLI.`, + RunE: pluginListRunE, + Short: "List all plugins currently in use with PingCLI", + Use: "list", + } + + return cmd +} + +func pluginListRunE(cmd *cobra.Command, args []string) error { + l := logger.Get() + l.Debug().Msgf("Plugin List Subcommand Called.") + + if err := plugin_internal.RunInternalPluginList(); err != nil { + return err + } + + return nil +} diff --git a/cmd/plugin/list_test.go b/cmd/plugin/list_test.go new file mode 100644 index 00000000..204f9f32 --- /dev/null +++ b/cmd/plugin/list_test.go @@ -0,0 +1,39 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_cobra" +) + +// Test Plugin list Command Executes without issue +func TestPluginListCmd_Execute(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "list") + testutils.CheckExpectedError(t, err, nil) +} + +// Test Plugin list Command fails when provided too many arguments +func TestPluginListCmd_TooManyArgs(t *testing.T) { + expectedErrorPattern := `^failed to execute 'pingcli plugin list': command accepts 0 arg\(s\), received 1$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "list", "extra-arg") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin list Command fails when provided an invalid flag +func TestPluginListCmd_InvalidFlag(t *testing.T) { + expectedErrorPattern := `^unknown flag: --invalid$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "list", "--invalid") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin list Command --help, -h flag +func TestPluginListCmd_HelpFlag(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "list", "--help") + testutils.CheckExpectedError(t, err, nil) + + err = testutils_cobra.ExecutePingcli(t, "plugin", "list", "-h") + testutils.CheckExpectedError(t, err, nil) +} diff --git a/cmd/plugin/plugin.go b/cmd/plugin/plugin.go new file mode 100644 index 00000000..e2f647dc --- /dev/null +++ b/cmd/plugin/plugin.go @@ -0,0 +1,23 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin + +import ( + "github.com/spf13/cobra" +) + +func NewPluginCommand() *cobra.Command { + cmd := &cobra.Command{ + Long: `Manage PingCLI plugins.`, + Short: "Manage PingCLI plugins.", + Use: "plugin", + } + + cmd.AddCommand( + NewPluginAddCommand(), + NewPluginListCommand(), + NewPluginRemoveCommand(), + ) + + return cmd +} diff --git a/cmd/plugin/plugin_test.go b/cmd/plugin/plugin_test.go new file mode 100644 index 00000000..9739c203 --- /dev/null +++ b/cmd/plugin/plugin_test.go @@ -0,0 +1,32 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_cobra" +) + +// Test Plugin Command Executes without issue +func TestPluginCmd_Execute(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin") + testutils.CheckExpectedError(t, err, nil) +} + +// Test Plugin Command fails when provided invalid flag +func TestPluginCmd_InvalidFlag(t *testing.T) { + expectedErrorPattern := `^unknown flag: --invalid$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "--invalid") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin Command --help, -h flag +func TestPluginCmd_HelpFlag(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "--help") + testutils.CheckExpectedError(t, err, nil) + + err = testutils_cobra.ExecutePingcli(t, "plugin", "-h") + testutils.CheckExpectedError(t, err, nil) +} diff --git a/cmd/plugin/remove.go b/cmd/plugin/remove.go new file mode 100644 index 00000000..f0202687 --- /dev/null +++ b/cmd/plugin/remove.go @@ -0,0 +1,40 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin + +import ( + "github.com/pingidentity/pingcli/cmd/common" + plugin_internal "github.com/pingidentity/pingcli/internal/commands/plugin" + "github.com/pingidentity/pingcli/internal/logger" + "github.com/spf13/cobra" +) + +const ( + removePluginCommandExamples = ` Remove a plugin from PingCLI. + pingcli plugin remove pingcli-plugin-executable` +) + +func NewPluginRemoveCommand() *cobra.Command { + cmd := &cobra.Command{ + Args: common.ExactArgs(1), + DisableFlagsInUseLine: true, // We write our own flags in @Use attribute + Example: removePluginCommandExamples, + Long: `Remove a plugin from PingCLI.`, + RunE: pluginRemoveRunE, + Short: "Remove a plugin from PingCLI", + Use: "remove plugin-executable", + } + + return cmd +} + +func pluginRemoveRunE(cmd *cobra.Command, args []string) error { + l := logger.Get() + l.Debug().Msgf("Plugin Remove Subcommand Called.") + + if err := plugin_internal.RunInternalPluginRemove(args[0]); err != nil { + return err + } + + return nil +} diff --git a/cmd/plugin/remove_test.go b/cmd/plugin/remove_test.go new file mode 100644 index 00000000..b9294375 --- /dev/null +++ b/cmd/plugin/remove_test.go @@ -0,0 +1,55 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_test + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_cobra" +) + +// Test Plugin remove Command Executes without issue +func TestPluginRemoveCmd_Execute(t *testing.T) { + t.SkipNow() + + // TODO: A test plugin that responds with a valid RPC configuration is needed + // for pingcli to execute when the plugin is listed as used in pingcli. + // We can probably use a future plugin for testing once made. +} + +// Test Plugin remove Command succeeds when provided a non-existent plugin +func TestPluginRemoveCmd_NonExistentPlugin(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "remove", "non-existent-plugin") + testutils.CheckExpectedError(t, err, nil) +} + +// Test Plugin remove Command fails when provided too many arguments +func TestPluginRemoveCmd_TooManyArgs(t *testing.T) { + expectedErrorPattern := `^failed to execute 'pingcli plugin remove': command accepts 1 arg\(s\), received 2$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "remove", "test-plugin-name", "extra-arg") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin remove Command fails when provided too few arguments +func TestPluginRemoveCmd_TooFewArgs(t *testing.T) { + expectedErrorPattern := `^failed to execute 'pingcli plugin remove': command accepts 1 arg\(s\), received 0$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "remove") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin remove Command fails when provided an invalid flag +func TestPluginRemoveCmd_InvalidFlag(t *testing.T) { + expectedErrorPattern := `^unknown flag: --invalid$` + err := testutils_cobra.ExecutePingcli(t, "plugin", "remove", "test-plugin-name", "--invalid") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} + +// Test Plugin remove Command --help, -h flag +func TestPluginRemoveCmd_HelpFlag(t *testing.T) { + err := testutils_cobra.ExecutePingcli(t, "plugin", "remove", "--help") + testutils.CheckExpectedError(t, err, nil) + + err = testutils_cobra.ExecutePingcli(t, "plugin", "remove", "-h") + testutils.CheckExpectedError(t, err, nil) +} diff --git a/cmd/request/request_test.go b/cmd/request/request_test.go index e7e63b8d..55074d1c 100644 --- a/cmd/request/request_test.go +++ b/cmd/request/request_test.go @@ -13,10 +13,13 @@ import ( "github.com/pingidentity/pingcli/internal/configuration/options" "github.com/pingidentity/pingcli/internal/testing/testutils" "github.com/pingidentity/pingcli/internal/testing/testutils_cobra" + "github.com/pingidentity/pingcli/internal/testing/testutils_koanf" ) // Test Request Command Executes without issue func TestRequestCmd_Execute(t *testing.T) { + testutils_koanf.InitKoanfs(t) + originalStdout := os.Stdout pipeReader, pipeWriter, err := os.Pipe() if err != nil { @@ -93,6 +96,8 @@ func TestRequestCmd_Execute_Help(t *testing.T) { // Test Request Command with Invalid Service func TestRequestCmd_Execute_InvalidService(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-s, --service" flag: unrecognized Request Service: '.*'. Must be one of: .*$` err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "invalid-service", @@ -104,6 +109,8 @@ func TestRequestCmd_Execute_InvalidService(t *testing.T) { // Test Request Command with Invalid HTTP Method func TestRequestCmd_Execute_InvalidHTTPMethod(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-m, --http-method" flag: unrecognized HTTP Method: '.*'. Must be one of: .*$` err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "pingone", @@ -122,29 +129,37 @@ func TestRequestCmd_Execute_MissingRequiredServiceFlag(t *testing.T) { // Test Request Command with Header Flag func TestRequestCmd_Execute_HeaderFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) + err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "pingone", "--"+options.RequestHTTPMethodOption.CobraParamName, "GET", "--"+options.RequestHeaderOption.CobraParamName, "Content-Type: application/vnd.pingidentity.user.import+json", - fmt.Sprintf("environments/%s/users", os.Getenv(options.PingOneAuthenticationWorkerEnvironmentIDOption.EnvVar)), + "--"+options.RequestFailOption.CobraParamName, + fmt.Sprintf("environments/%s/users", os.Getenv("TEST_PINGONE_ENVIRONMENT_ID")), ) testutils.CheckExpectedError(t, err, nil) } // Test Request Command with Header Flag with and without spacing func TestRequestCmd_Execute_HeaderFlagSpacing(t *testing.T) { + testutils_koanf.InitKoanfs(t) + err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "pingone", "--"+options.RequestHTTPMethodOption.CobraParamName, "GET", "--"+options.RequestHeaderOption.CobraParamName, "Test-Header:TestValue", "--"+options.RequestHeaderOption.CobraParamName, "Test-Header-Two:\tTestValue", - fmt.Sprintf("environments/%s/users", os.Getenv(options.PingOneAuthenticationWorkerEnvironmentIDOption.EnvVar)), + "--"+options.RequestFailOption.CobraParamName, + fmt.Sprintf("environments/%s/users", os.Getenv("TEST_PINGONE_ENVIRONMENT_ID")), ) testutils.CheckExpectedError(t, err, nil) } // Test Request Command with invalid Header Flag func TestRequestCmd_Execute_InvalidHeaderFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-r, --header" flag: failed to set Headers: Invalid header: invalid=header. Headers must be in the proper format. Expected regex pattern: .*$` err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "pingone", @@ -156,6 +171,8 @@ func TestRequestCmd_Execute_InvalidHeaderFlag(t *testing.T) { // Test Request Command with disallowed Authorization Header Flag func TestRequestCmd_Execute_DisallowedAuthorizationFlag(t *testing.T) { + testutils_koanf.InitKoanfs(t) + expectedErrorPattern := `^invalid argument ".*" for "-r, --header" flag: failed to set Headers: Invalid header: Authorization. Authorization header is not allowed$` err := testutils_cobra.ExecutePingcli(t, "request", "--"+options.RequestServiceOption.CobraParamName, "pingone", diff --git a/cmd/root.go b/cmd/root.go index e549ffb4..1a60207b 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -13,28 +13,28 @@ import ( "github.com/pingidentity/pingcli/cmd/config" "github.com/pingidentity/pingcli/cmd/feedback" "github.com/pingidentity/pingcli/cmd/platform" + "github.com/pingidentity/pingcli/cmd/plugin" "github.com/pingidentity/pingcli/cmd/request" "github.com/pingidentity/pingcli/internal/autocompletion" "github.com/pingidentity/pingcli/internal/configuration" "github.com/pingidentity/pingcli/internal/configuration/options" "github.com/pingidentity/pingcli/internal/logger" "github.com/pingidentity/pingcli/internal/output" + "github.com/pingidentity/pingcli/internal/plugins" "github.com/pingidentity/pingcli/internal/profiles" "github.com/spf13/cobra" ) -func init() { +// rootCmd represents the base command when called without any subcommands +func NewRootCommand(version string, commit string) *cobra.Command { l := logger.Get() l.Debug().Msgf("Initializing Ping CLI options...") configuration.InitAllOptions() l.Debug().Msgf("Initializing Root command...") - cobra.OnInitialize(initKoanfProfile) -} + initKoanfProfile() -// rootCmd represents the base command when called without any subcommands -func NewRootCommand(version string, commit string) *cobra.Command { cmd := &cobra.Command{ Long: "A CLI tool for managing the configuration of Ping Identity products.", Short: "A CLI tool for managing the configuration of Ping Identity products.", @@ -49,9 +49,15 @@ func NewRootCommand(version string, commit string) *cobra.Command { config.NewConfigCommand(), feedback.NewFeedbackCommand(), platform.NewPlatformCommand(), + plugin.NewPluginCommand(), request.NewRequestCommand(), ) + err := plugins.AddAllPluginToCmd(cmd) + if err != nil { + output.SystemError(fmt.Sprintf("Failed to add plugin commands: %v", err), nil) + } + // FLAGS // // --config, -C cmd.PersistentFlags().AddFlag(options.RootConfigOption.Flag) @@ -62,7 +68,7 @@ func NewRootCommand(version string, commit string) *cobra.Command { // --profile, -P cmd.PersistentFlags().AddFlag(options.RootProfileOption.Flag) // auto-completion - err := cmd.RegisterFlagCompletionFunc(options.RootProfileOption.CobraParamName, autocompletion.RootProfileFunc) + err = cmd.RegisterFlagCompletionFunc(options.RootProfileOption.CobraParamName, autocompletion.RootProfileFunc) if err != nil { output.SystemError(fmt.Sprintf("Unable to register auto completion for pingcli global flag %s: %v", options.RootProfileOption.CobraParamName, err), nil) } @@ -194,8 +200,7 @@ func initKoanf(cfgFile string) { func loadKoanfConfig(cfgFile string) { l := logger.Get() - koanfConfig := profiles.GetKoanfConfig() - koanfConfig.SetKoanfConfigFile(cfgFile) + koanfConfig := profiles.NewKoanfConfig(cfgFile) // Use config file from the flag. if err := koanfConfig.KoanfInstance().Load(file.Provider(cfgFile), yaml.Parser()); err != nil { diff --git a/go.mod b/go.mod index 6f4fc2e3..c684b2c4 100644 --- a/go.mod +++ b/go.mod @@ -1,80 +1,92 @@ module github.com/pingidentity/pingcli -go 1.24.1 +go 1.24.4 tool ( - github.com/golangci/golangci-lint/cmd/golangci-lint + github.com/golangci/golangci-lint/v2/cmd/golangci-lint github.com/pavius/impi/cmd/impi ) require ( github.com/fatih/color v1.18.0 + github.com/hashicorp/go-hclog v1.6.3 + github.com/hashicorp/go-plugin v1.6.3 github.com/hashicorp/go-uuid v1.0.3 - github.com/knadh/koanf/parsers/yaml v0.1.0 - github.com/knadh/koanf/providers/confmap v0.1.0 - github.com/knadh/koanf/providers/file v1.1.2 - github.com/knadh/koanf/v2 v2.2.0 + github.com/knadh/koanf/parsers/yaml v1.0.0 + github.com/knadh/koanf/providers/confmap v1.0.0 + github.com/knadh/koanf/providers/file v1.2.0 + github.com/knadh/koanf/v2 v2.2.1 github.com/manifoldco/promptui v0.9.0 - github.com/patrickcping/pingone-go-sdk-v2 v0.12.13 + github.com/patrickcping/pingone-go-sdk-v2 v0.12.17 github.com/patrickcping/pingone-go-sdk-v2/authorize v0.8.0 - github.com/patrickcping/pingone-go-sdk-v2/management v0.53.0 + github.com/patrickcping/pingone-go-sdk-v2/management v0.57.0 github.com/patrickcping/pingone-go-sdk-v2/mfa v0.23.0 github.com/patrickcping/pingone-go-sdk-v2/risk v0.19.0 github.com/pingidentity/pingfederate-go-client/v1220 v1220.0.0 github.com/rs/zerolog v1.34.0 github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 - golang.org/x/mod v0.24.0 + golang.org/x/mod v0.25.0 + google.golang.org/grpc v1.73.0 + google.golang.org/protobuf v1.36.6 gopkg.in/yaml.v3 v3.0.1 ) require ( - 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect + 4d63.com/gocheckcompilerdirectives v1.3.0 // indirect 4d63.com/gochecknoglobals v0.2.2 // indirect - github.com/4meepo/tagalign v1.4.1 // indirect + github.com/4meepo/tagalign v1.4.2 // indirect github.com/Abirdcfly/dupword v0.1.3 // indirect - github.com/Antonboom/errname v1.0.0 // indirect - github.com/Antonboom/nilnil v1.0.1 // indirect - github.com/Antonboom/testifylint v1.5.2 // indirect - github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect - github.com/Crocmagnon/fatcontext v0.7.1 // indirect + github.com/Antonboom/errname v1.1.0 // indirect + github.com/Antonboom/nilnil v1.1.0 // indirect + github.com/Antonboom/testifylint v1.6.1 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect - github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.1 // indirect + github.com/alecthomas/chroma/v2 v2.17.2 // indirect github.com/alecthomas/go-check-sumtype v0.3.1 // indirect - github.com/alexkohler/nakedret/v2 v2.0.5 // indirect + github.com/alexkohler/nakedret/v2 v2.0.6 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect - github.com/alingse/nilnesserr v0.1.2 // indirect + github.com/alingse/nilnesserr v0.2.0 // indirect github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.2.0 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bkielbasa/cyclop v1.2.3 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v4 v4.5.0 // indirect - github.com/breml/bidichk v0.3.2 // indirect - github.com/breml/errchkjson v0.4.0 // indirect - github.com/butuzov/ireturn v0.3.1 // indirect + github.com/bombsimon/wsl/v4 v4.7.0 // indirect + github.com/breml/bidichk v0.3.3 // indirect + github.com/breml/errchkjson v0.4.1 // indirect + github.com/butuzov/ireturn v0.4.0 // indirect github.com/butuzov/mirror v1.3.0 // indirect - github.com/catenacyber/perfsprint v0.8.1 // indirect + github.com/catenacyber/perfsprint v0.9.1 // indirect github.com/ccojocar/zxcvbn-go v1.0.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charithe/durationcheck v0.0.10 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect + github.com/charmbracelet/lipgloss v1.1.0 // indirect + github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect github.com/chavacava/garif v0.1.0 // indirect github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e // indirect - github.com/ckaznocha/intrange v0.3.0 // indirect + github.com/ckaznocha/intrange v0.3.1 // indirect github.com/curioswitch/go-reassign v0.3.0 // indirect - github.com/daixiang0/gci v0.13.5 // indirect + github.com/daixiang0/gci v0.13.6 // indirect + github.com/dave/dst v0.27.3 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect + github.com/dlclark/regexp2 v1.11.5 // indirect github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/structtag v1.2.0 // indirect - github.com/firefart/nonamedreturns v1.0.5 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect + github.com/firefart/nonamedreturns v1.0.6 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fzipp/gocyclo v0.6.0 // indirect - github.com/ghostiam/protogetter v0.3.9 // indirect - github.com/go-critic/go-critic v0.12.0 // indirect + github.com/ghostiam/protogetter v0.3.15 // indirect + github.com/go-critic/go-critic v0.13.0 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect github.com/go-toolsmith/astequal v1.2.0 // indirect @@ -86,45 +98,49 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gofrs/flock v0.12.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 // indirect github.com/golangci/go-printf-func-name v0.1.0 // indirect github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d // indirect - github.com/golangci/golangci-lint v1.64.5 // indirect + github.com/golangci/golangci-lint/v2 v2.1.6 // indirect + github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 // indirect github.com/golangci/misspell v0.6.0 // indirect github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.8.0 // indirect - github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect - github.com/google/go-cmp v0.6.0 // indirect + github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e // indirect + github.com/google/go-cmp v0.7.0 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect - github.com/gostaticanalysis/comment v1.4.2 // indirect + github.com/gostaticanalysis/comment v1.5.0 // indirect github.com/gostaticanalysis/forcetypeassert v0.2.0 // indirect github.com/gostaticanalysis/nilerr v0.1.1 // indirect github.com/hashicorp/go-immutable-radix/v2 v2.1.0 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/hashicorp/yamux v0.1.1 // indirect github.com/hexops/gotextdiff v1.0.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jgautheron/goconst v1.7.1 // indirect + github.com/jgautheron/goconst v1.8.1 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jjti/go-spancheck v0.6.4 // indirect github.com/julz/importas v0.2.0 // indirect github.com/karamaru-alpha/copyloopvar v1.2.1 // indirect - github.com/kisielk/errcheck v1.8.0 // indirect + github.com/kisielk/errcheck v1.9.0 // indirect github.com/kisielk/gotool v1.0.0 // indirect - github.com/kkHAIKE/contextcheck v1.1.5 // indirect + github.com/kkHAIKE/contextcheck v1.1.6 // indirect github.com/knadh/koanf/maps v0.1.2 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.10 // indirect + github.com/kunwardeep/paralleltest v1.0.14 // indirect github.com/lasiar/canonicalheader v1.1.2 // indirect - github.com/ldez/exptostd v0.4.1 // indirect + github.com/ldez/exptostd v0.4.3 // indirect github.com/ldez/gomoddirectives v0.6.1 // indirect github.com/ldez/grignotin v0.9.0 // indirect github.com/ldez/tagliatelle v0.7.1 // indirect - github.com/ldez/usetesting v0.4.2 // indirect + github.com/ldez/usetesting v0.4.3 // indirect github.com/leonklingele/grouper v1.1.2 // indirect - github.com/macabu/inamedparam v0.1.3 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/macabu/inamedparam v0.2.0 // indirect + github.com/manuelarte/funcorder v0.2.1 // indirect github.com/maratori/testableexamples v1.0.0 // indirect github.com/maratori/testpackage v1.1.1 // indirect github.com/matoous/godox v1.1.0 // indirect @@ -132,49 +148,50 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect - github.com/mgechev/revive v1.6.1 // indirect + github.com/mgechev/revive v1.9.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moricho/tparallel v0.3.2 // indirect + github.com/muesli/termenv v0.16.0 // indirect github.com/nakabonne/nestif v0.3.1 // indirect github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.19.0 // indirect + github.com/nunnatsa/ginkgolinter v0.19.1 // indirect + github.com/oklog/run v1.0.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/patrickcping/pingone-go-sdk-v2/credentials v0.11.0 // indirect github.com/patrickcping/pingone-go-sdk-v2/verify v0.9.0 // indirect github.com/pavius/impi v0.0.3 // indirect - github.com/pelletier/go-toml/v2 v2.2.3 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/polyfloyd/go-errorlint v1.7.1 // indirect + github.com/polyfloyd/go-errorlint v1.8.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.32.1 // indirect github.com/prometheus/procfs v0.7.3 // indirect - github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect + github.com/quasilyte/go-ruleguard v0.4.4 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect github.com/quasilyte/gogrep v0.5.0 // indirect github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect github.com/raeperd/recvcheck v0.2.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect - github.com/ryancurrah/gomodguard v1.3.5 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/ryancurrah/gomodguard v1.4.1 // indirect github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sanposhiho/wastedassign/v2 v2.1.0 // indirect github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect github.com/sashamelentyev/usestdlibvars v1.28.0 // indirect - github.com/securego/gosec/v2 v2.22.1 // indirect + github.com/securego/gosec/v2 v2.22.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sivchari/containedctx v1.0.3 // indirect - github.com/sivchari/tenv v1.12.1 // indirect github.com/sonatard/noctx v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/sourcegraph/go-diff v0.7.0 // indirect - github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/viper v1.20.1 // indirect github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect @@ -182,36 +199,39 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/stretchr/testify v1.10.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/tdakkota/asciicheck v0.4.0 // indirect - github.com/tetafro/godot v1.4.20 // indirect - github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 // indirect - github.com/timonwong/loggercheck v0.10.1 // indirect - github.com/tomarrell/wrapcheck/v2 v2.10.0 // indirect + github.com/tdakkota/asciicheck v0.4.1 // indirect + github.com/tetafro/godot v1.5.1 // indirect + github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 // indirect + github.com/timonwong/loggercheck v0.11.0 // indirect + github.com/tomarrell/wrapcheck/v2 v2.11.0 // indirect github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ultraware/funlen v0.2.0 // indirect github.com/ultraware/whitespace v0.2.0 // indirect github.com/uudashr/gocognit v1.2.0 // indirect github.com/uudashr/iface v1.3.1 // indirect - github.com/xen0n/gosmopolitan v1.2.2 // indirect + github.com/xen0n/gosmopolitan v1.3.0 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect - go-simpler.org/musttag v0.13.0 // indirect - go-simpler.org/sloglint v0.9.0 // indirect + go-simpler.org/musttag v0.13.1 // indirect + go-simpler.org/sloglint v0.11.0 // indirect + go.augendre.info/fatcontext v0.8.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/text v0.22.0 // indirect - golang.org/x/tools v0.30.0 // indirect - google.golang.org/protobuf v1.36.4 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/tools v0.32.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect - honnef.co/go/tools v0.6.0 // indirect - mvdan.cc/gofumpt v0.7.0 // indirect - mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect + honnef.co/go/tools v0.6.1 // indirect + mvdan.cc/gofumpt v0.8.0 // indirect + mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 // indirect ) diff --git a/go.sum b/go.sum index 99d4555b..285c41f2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA= -4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs= +4d63.com/gocheckcompilerdirectives v1.3.0 h1:Ew5y5CtcAAQeTVKUVFrE7EwHMrTO6BggtEj8BZSjZ3A= +4d63.com/gocheckcompilerdirectives v1.3.0/go.mod h1:ofsJ4zx2QAuIP/NO/NAh1ig6R1Fb18/GI7RVMwz7kAY= 4d63.com/gochecknoglobals v0.2.2 h1:H1vdnwnMaZdQW/N+NrkT1SZMTBmcwHe9Vq8lJcYYTtU= 4d63.com/gochecknoglobals v0.2.2/go.mod h1:lLxwTQjL5eIesRbvnzIP3jZtG140FnTdz+AlMa+ogt0= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -35,32 +35,32 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/4meepo/tagalign v1.4.1 h1:GYTu2FaPGOGb/xJalcqHeD4il5BiCywyEYZOA55P6J4= -github.com/4meepo/tagalign v1.4.1/go.mod h1:2H9Yu6sZ67hmuraFgfZkNcg5Py9Ch/Om9l2K/2W1qS4= +github.com/4meepo/tagalign v1.4.2 h1:0hcLHPGMjDyM1gHG58cS73aQF8J4TdVR96TZViorO9E= +github.com/4meepo/tagalign v1.4.2/go.mod h1:+p4aMyFM+ra7nb41CnFG6aSDXqRxU/w1VQqScKqDARI= github.com/Abirdcfly/dupword v0.1.3 h1:9Pa1NuAsZvpFPi9Pqkd93I7LIYRURj+A//dFd5tgBeE= github.com/Abirdcfly/dupword v0.1.3/go.mod h1:8VbB2t7e10KRNdwTVoxdBaxla6avbhGzb8sCTygUMhw= -github.com/Antonboom/errname v1.0.0 h1:oJOOWR07vS1kRusl6YRSlat7HFnb3mSfMl6sDMRoTBA= -github.com/Antonboom/errname v1.0.0/go.mod h1:gMOBFzK/vrTiXN9Oh+HFs+e6Ndl0eTFbtsRTSRdXyGI= -github.com/Antonboom/nilnil v1.0.1 h1:C3Tkm0KUxgfO4Duk3PM+ztPncTFlOf0b2qadmS0s4xs= -github.com/Antonboom/nilnil v1.0.1/go.mod h1:CH7pW2JsRNFgEh8B2UaPZTEPhCMuFowP/e8Udp9Nnb0= -github.com/Antonboom/testifylint v1.5.2 h1:4s3Xhuv5AvdIgbd8wOOEeo0uZG7PbDKQyKY5lGoQazk= -github.com/Antonboom/testifylint v1.5.2/go.mod h1:vxy8VJ0bc6NavlYqjZfmp6EfqXMtBgQ4+mhCojwC1P8= +github.com/Antonboom/errname v1.1.0 h1:A+ucvdpMwlo/myWrkHEUEBWc/xuXdud23S8tmTb/oAE= +github.com/Antonboom/errname v1.1.0/go.mod h1:O1NMrzgUcVBGIfi3xlVuvX8Q/VP/73sseCaAppfjqZw= +github.com/Antonboom/nilnil v1.1.0 h1:jGxJxjgYS3VUUtOTNk8Z1icwT5ESpLH/426fjmQG+ng= +github.com/Antonboom/nilnil v1.1.0/go.mod h1:b7sAlogQjFa1wV8jUW3o4PMzDVFLbTux+xnQdvzdcIE= +github.com/Antonboom/testifylint v1.6.1 h1:6ZSytkFWatT8mwZlmRCHkWz1gPi+q6UBSbieji2Gj/o= +github.com/Antonboom/testifylint v1.6.1/go.mod h1:k+nEkathI2NFjKO6HvwmSrbzUcQ6FAnbZV+ZRrnXPLI= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs= -github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/Crocmagnon/fatcontext v0.7.1 h1:SC/VIbRRZQeQWj/TcQBS6JmrXcfA+BU4OGSVUt54PjM= -github.com/Crocmagnon/fatcontext v0.7.1/go.mod h1:1wMvv3NXEBJucFGfwOJBxSVWcoIO6emV215SMkW9MFU= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= -github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1 h1:Sz1JIXEcSfhz7fUi7xHnhpIE0thVASYjvosApmHuD2k= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.1/go.mod h1:n/LSCXNuIYqVfBlVXyHfMQkZDdp1/mmxfSjADd3z1Zg= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1 h1:vckeWVESWp6Qog7UZSARNqfu/cZqvki8zsuj3piCMx4= +github.com/OpenPeeDeeP/depguard/v2 v2.2.1/go.mod h1:q4DKzC4UcVaAvcfd41CZh0PWpGgzrVxUYBlgKNGquUo= github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0= github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI= +github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk= github.com/alecthomas/go-check-sumtype v0.3.1 h1:u9aUvbGINJxLVXiFvHUlPEaD7VDULsrxJb4Aq31NLkU= github.com/alecthomas/go-check-sumtype v0.3.1/go.mod h1:A8TSiN3UPRw3laIgWEUOHHLPa6/r9MtoigdlP5h3K/E= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= @@ -70,18 +70,20 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alexkohler/nakedret/v2 v2.0.5 h1:fP5qLgtwbx9EJE8dGEERT02YwS8En4r9nnZ71RK+EVU= -github.com/alexkohler/nakedret/v2 v2.0.5/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU= +github.com/alexkohler/nakedret/v2 v2.0.6 h1:ME3Qef1/KIKr3kWX3nti3hhgNxw6aqN5pZmQiFSsuzQ= +github.com/alexkohler/nakedret/v2 v2.0.6/go.mod h1:l3RKju/IzOMQHmsEvXwkqMDzHHvurNQfAgE1eVmT40Q= github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw= github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= -github.com/alingse/nilnesserr v0.1.2 h1:Yf8Iwm3z2hUUrP4muWfW83DF4nE3r1xZ26fGWUKCZlo= -github.com/alingse/nilnesserr v0.1.2/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= +github.com/alingse/nilnesserr v0.2.0 h1:raLem5KG7EFVb4UIDAXgrv3N2JIaffeKNtcEXkEWd/w= +github.com/alingse/nilnesserr v0.2.0/go.mod h1:1xJPrXonEtX7wyTq8Dytns5P2hNzoWymVUIaKm4HNFg= github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY= github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU= github.com/ashanbrown/makezero v1.2.0 h1:/2Lp1bypdmK9wDIq7uWBlDF1iMUpIIS4A+pF6C9IEUU= github.com/ashanbrown/makezero v1.2.0/go.mod h1:dxlPhHbDMC6N6xICzFBSK+4njQDdK8euNO0qjQMtGY4= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= @@ -92,18 +94,20 @@ github.com/bkielbasa/cyclop v1.2.3 h1:faIVMIGDIANuGPWH031CZJTi2ymOQBULs9H21HSMa5 github.com/bkielbasa/cyclop v1.2.3/go.mod h1:kHTwA9Q0uZqOADdupvcFJQtp/ksSnytRMe8ztxG8Fuo= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v4 v4.5.0 h1:iZRsEvDdyhd2La0FVi5k6tYehpOR/R7qIUjmKk7N74A= -github.com/bombsimon/wsl/v4 v4.5.0/go.mod h1:NOQ3aLF4nD7N5YPXMruR6ZXDOAqLoM0GEpLwTdvmOSc= -github.com/breml/bidichk v0.3.2 h1:xV4flJ9V5xWTqxL+/PMFF6dtJPvZLPsyixAoPe8BGJs= -github.com/breml/bidichk v0.3.2/go.mod h1:VzFLBxuYtT23z5+iVkamXO386OB+/sVwZOpIj6zXGos= -github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAdk= -github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= -github.com/butuzov/ireturn v0.3.1 h1:mFgbEI6m+9W8oP/oDdfA34dLisRFCj2G6o/yiI1yZrY= -github.com/butuzov/ireturn v0.3.1/go.mod h1:ZfRp+E7eJLC0NQmk1Nrm1LOrn/gQlOykv+cVPdiXH5M= +github.com/bombsimon/wsl/v4 v4.7.0 h1:1Ilm9JBPRczjyUs6hvOPKvd7VL1Q++PL8M0SXBDf+jQ= +github.com/bombsimon/wsl/v4 v4.7.0/go.mod h1:uV/+6BkffuzSAVYD+yGyld1AChO7/EuLrCF/8xTiapg= +github.com/breml/bidichk v0.3.3 h1:WSM67ztRusf1sMoqH6/c4OBCUlRVTKq+CbSeo0R17sE= +github.com/breml/bidichk v0.3.3/go.mod h1:ISbsut8OnjB367j5NseXEGGgO/th206dVa427kR8YTE= +github.com/breml/errchkjson v0.4.1 h1:keFSS8D7A2T0haP9kzZTi7o26r7kE3vymjZNeNDRDwg= +github.com/breml/errchkjson v0.4.1/go.mod h1:a23OvR6Qvcl7DG/Z4o0el6BRAjKnaReoPQFciAl9U3s= +github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= +github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= +github.com/butuzov/ireturn v0.4.0 h1:+s76bF/PfeKEdbG8b54aCocxXmi0wvYdOVsWxVO7n8E= +github.com/butuzov/ireturn v0.4.0/go.mod h1:ghI0FrCmap8pDWZwfPisFD1vEc56VKH4NpQUxDHta70= github.com/butuzov/mirror v1.3.0 h1:HdWCXzmwlQHdVhwvsfBb2Au0r3HyINry3bDWLYXiKoc= github.com/butuzov/mirror v1.3.0/go.mod h1:AEij0Z8YMALaq4yQj9CPPVYOyJQyiexpQEQgihajRfI= -github.com/catenacyber/perfsprint v0.8.1 h1:bGOHuzHe0IkoGeY831RW4aSlt1lPRd3WRAScSWOaV7E= -github.com/catenacyber/perfsprint v0.8.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/catenacyber/perfsprint v0.9.1 h1:5LlTp4RwTooQjJCvGEFV6XksZvWE7wCOUvjD2z0vls0= +github.com/catenacyber/perfsprint v0.9.1/go.mod h1:q//VWC2fWbcdSLEY1R3l8n0zQCDPdE4IjZwyY1HMunM= github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -113,6 +117,16 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4= github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= +github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= +github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= @@ -121,46 +135,51 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5O github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/ckaznocha/intrange v0.3.0 h1:VqnxtK32pxgkhJgYQEeOArVidIPg+ahLP7WBOXZd5ZY= -github.com/ckaznocha/intrange v0.3.0/go.mod h1:+I/o2d2A1FBHgGELbGxzIcyd3/9l9DuwjM8FsbSS3Lo= +github.com/ckaznocha/intrange v0.3.1 h1:j1onQyXvHUsPWujDH6WIjhyH26gkRt/txNlV7LspvJs= +github.com/ckaznocha/intrange v0.3.1/go.mod h1:QVepyz1AkUoFQkpEqksSYpNpUo3c5W7nWh/s6SHIJJk= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/curioswitch/go-reassign v0.3.0 h1:dh3kpQHuADL3cobV/sSGETA8DOv457dwl+fbBAhrQPs= github.com/curioswitch/go-reassign v0.3.0/go.mod h1:nApPCCTtqLJN/s8HfItCcKV0jIPwluBOvZP+dsJGA88= -github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c= -github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/daixiang0/gci v0.13.6 h1:RKuEOSkGpSadkGbvZ6hJ4ddItT3cVZ9Vn9Rybk6xjl8= +github.com/daixiang0/gci v0.13.6/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk= +github.com/dave/dst v0.27.3 h1:P1HPoMza3cMEquVf9kKy8yXsFirry4zEnWOdYPOoIzY= +github.com/dave/dst v0.27.3/go.mod h1:jHh6EOibnHgcUW3WjKHisiooEkYwqpHLBSX1iOBhEyc= +github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= +github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= -github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= -github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ= +github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4= github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94= -github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA= -github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw= +github.com/firefart/nonamedreturns v1.0.6 h1:vmiBcKV/3EqKY3ZiPxCINmpS431OcE1S47AQUwhrg8E= +github.com/firefart/nonamedreturns v1.0.6/go.mod h1:R8NisJnSIpvPWheCq0mNRXJok6D8h7fagJTF8EMEwCo= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= -github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= -github.com/ghostiam/protogetter v0.3.9 h1:j+zlLLWzqLay22Cz/aYwTHKQ88GE2DQ6GkWSYFOI4lQ= -github.com/ghostiam/protogetter v0.3.9/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= -github.com/go-critic/go-critic v0.12.0 h1:iLosHZuye812wnkEz1Xu3aBwn5ocCPfc9yqmFG9pa6w= -github.com/go-critic/go-critic v0.12.0/go.mod h1:DpE0P6OVc6JzVYzmM5gq5jMU31zLr4am5mB/VfFK64w= +github.com/ghostiam/protogetter v0.3.15 h1:1KF5sXel0HE48zh1/vn0Loiw25A9ApyseLzQuif1mLY= +github.com/ghostiam/protogetter v0.3.15/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= +github.com/go-critic/go-critic v0.13.0 h1:kJzM7wzltQasSUXtYyTl6UaPVySO6GkaR1thFnJ6afY= +github.com/go-critic/go-critic v0.13.0/go.mod h1:M/YeuJ3vOCQDnP2SU+ZhjgRzwzcBW87JqLpMJLrZDLI= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -172,6 +191,8 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -233,24 +254,26 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= -github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32 h1:WUvBfQL6EW/40l6OmeSBYQJNSif4O11+bmWEz+C7FYw= +github.com/golangci/dupl v0.0.0-20250308024227-f665c8d69b32/go.mod h1:NUw9Zr2Sy7+HxzdjIULge71wI6yEg1lWQr7Evcu8K0E= github.com/golangci/go-printf-func-name v0.1.0 h1:dVokQP+NMTO7jwO4bwsRwLWeudOVUPPyAKJuzv8pEJU= github.com/golangci/go-printf-func-name v0.1.0/go.mod h1:wqhWFH5mUdJQhweRnldEywnR5021wTdZSNgwYceV14s= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d h1:viFft9sS/dxoYY0aiOTsLKO2aZQAPT4nlQCsimGcSGE= github.com/golangci/gofmt v0.0.0-20250106114630-d62b90e6713d/go.mod h1:ivJ9QDg0XucIkmwhzCDsqcnxxlDStoTl89jDMIoNxKY= -github.com/golangci/golangci-lint v1.64.5 h1:5omC86XFBKXZgCrVdUWU+WNHKd+CWCxNx717KXnzKZY= -github.com/golangci/golangci-lint v1.64.5/go.mod h1:WZnwq8TF0z61h3jLQ7Sk5trcP7b3kUFxLD6l1ivtdvU= +github.com/golangci/golangci-lint/v2 v2.1.6 h1:LXqShFfAGM5BDzEOWD2SL1IzJAgUOqES/HRBsfKjI+w= +github.com/golangci/golangci-lint/v2 v2.1.6/go.mod h1:EPj+fgv4TeeBq3TcqaKZb3vkiV5dP4hHHKhXhEhzci8= +github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95 h1:AkK+w9FZBXlU/xUmBtSJN1+tAI4FIvy5WtnUnY8e4p8= +github.com/golangci/golines v0.0.0-20250217134842-442fd0091d95/go.mod h1:k9mmcyWKSTMcPPvQUCfRWWQ9VHJ1U9Dc0R7kaXAgtnQ= github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs= github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo= github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c= github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc= github.com/golangci/revgrep v0.8.0 h1:EZBctwbVd0aMeRnNUsFogoyayvKHyxlV3CdUA46FX2s= github.com/golangci/revgrep v0.8.0/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs= -github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e h1:gD6P7NEo7Eqtt0ssnqSJNNndxe69DOQ24A5h7+i3KpM= +github.com/golangci/unconvert v0.0.0-20250410112200-a129a6e6413e/go.mod h1:h+wZwLjUTJnm/P2rwlbJdRPZXOzaT36/FwnPnY2inzc= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -265,8 +288,8 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -280,6 +303,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= @@ -287,8 +312,9 @@ github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk= github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc= github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado= -github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q= github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM= +github.com/gostaticanalysis/comment v1.5.0 h1:X82FLl+TswsUMpMh17srGRuKaaXprTaytmEpgnKIDu8= +github.com/gostaticanalysis/comment v1.5.0/go.mod h1:V6eb3gpCv9GNVqb6amXzEUX3jXLVK/AdA+IrAMSqvEc= github.com/gostaticanalysis/forcetypeassert v0.2.0 h1:uSnWrrUEYDr86OCxWa4/Tp2jeYDlogZiZHzGkWFefTk= github.com/gostaticanalysis/forcetypeassert v0.2.0/go.mod h1:M5iPavzE9pPqWyeiVXSFghQjljW1+l/Uke3PXHS6ILY= github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk= @@ -296,8 +322,12 @@ github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M= github.com/gostaticanalysis/testutil v0.5.0 h1:Dq4wT1DdTwTGCQQv3rl3IvD5Ld0E6HiY+3Zh0sUGqw8= github.com/gostaticanalysis/testutil v0.5.0/go.mod h1:OLQSbuM6zw2EvCcXTz1lVq5unyoNft372msDY0nY5Hs= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/go-immutable-radix/v2 v2.1.0 h1:CUW5RYIcysz+D3B+l1mDeXrQ7fUvGGCwJfdASSzbrfo= github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= +github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg= +github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -307,13 +337,17 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= +github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk= -github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.8.1 h1:PPqCYp3K/xlOj5JmIe6O1Mj6r1DbkdbLtR3AJuZo414= +github.com/jgautheron/goconst v1.8.1/go.mod h1:A0oxgBCHy55NQn6sYpO7UdnA9p+h7cPtoOZUmvNIako= +github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= +github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c= github.com/jjti/go-spancheck v0.6.4 h1:Tl7gQpYf4/TMU7AT84MN83/6PutY21Nb9fuQjFTpRRc= @@ -331,22 +365,22 @@ github.com/julz/importas v0.2.0 h1:y+MJN/UdL63QbFJHws9BVC5RpA2iq0kpjrFajTGivjQ= github.com/julz/importas v0.2.0/go.mod h1:pThlt589EnCYtMnmhmRYY/qn9lCf/frPOK+WMx3xiJY= github.com/karamaru-alpha/copyloopvar v1.2.1 h1:wmZaZYIjnJ0b5UoKDjUHrikcV0zuPyyxI4SVplLd2CI= github.com/karamaru-alpha/copyloopvar v1.2.1/go.mod h1:nFmMlFNlClC2BPvNaHMdkirmTJxVCY0lhxBtlfOypMM= -github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg= -github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= +github.com/kisielk/errcheck v1.9.0 h1:9xt1zI9EBfcYBvdU1nVrzMzzUPUtPKs9bVSIM3TAb3M= +github.com/kisielk/errcheck v1.9.0/go.mod h1:kQxWMMVZgIkDq7U8xtG/n2juOjbLgZtedi0D+/VL/i8= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg= -github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA= +github.com/kkHAIKE/contextcheck v1.1.6 h1:7HIyRcnyzxL9Lz06NGhiKvenXq7Zw6Q0UQu/ttjfJCE= +github.com/kkHAIKE/contextcheck v1.1.6/go.mod h1:3dDbMRNBFaq8HFXWC1JyvDSPm43CmE6IuHam8Wr0rkg= github.com/knadh/koanf/maps v0.1.2 h1:RBfmAW5CnZT+PJ1CVc1QSJKf4Xu9kxfQgYVQSu8hpbo= github.com/knadh/koanf/maps v0.1.2/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= -github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w= -github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY= -github.com/knadh/koanf/providers/confmap v0.1.0 h1:gOkxhHkemwG4LezxxN8DMOFopOPghxRVp7JbIvdvqzU= -github.com/knadh/koanf/providers/confmap v0.1.0/go.mod h1:2uLhxQzJnyHKfxG927awZC7+fyHFdQkd697K4MdLnIU= -github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w= -github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= -github.com/knadh/koanf/v2 v2.2.0 h1:FZFwd9bUjpb8DyCWARUBy5ovuhDs1lI87dOEn2K8UVU= -github.com/knadh/koanf/v2 v2.2.0/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY= +github.com/knadh/koanf/parsers/yaml v1.0.0 h1:PXyeHCRhAMKyfLJaoTWsqUTxIFeDMmdAKz3XVEslZV4= +github.com/knadh/koanf/parsers/yaml v1.0.0/go.mod h1:Q63VAOh/s6XaQs6a0TB2w9GFUuuPGvfYrCSWb9eWAQU= +github.com/knadh/koanf/providers/confmap v1.0.0 h1:mHKLJTE7iXEys6deO5p6olAiZdG5zwp8Aebir+/EaRE= +github.com/knadh/koanf/providers/confmap v1.0.0/go.mod h1:txHYHiI2hAtF0/0sCmcuol4IDcuQbKTybiB1nOcUo1A= +github.com/knadh/koanf/providers/file v1.2.0 h1:hrUJ6Y9YOA49aNu/RSYzOTFlqzXSCpmYIDXI7OJU6+U= +github.com/knadh/koanf/providers/file v1.2.0/go.mod h1:bp1PM5f83Q+TOUu10J/0ApLBd9uIzg+n9UgthfY+nRA= +github.com/knadh/koanf/v2 v2.2.1 h1:jaleChtw85y3UdBnI0wCqcg1sj1gPoz6D3caGNHtrNE= +github.com/knadh/koanf/v2 v2.2.1/go.mod h1:PSFru3ufQgTsI7IF+95rf9s8XA1+aHxKuO/W+dPoHEY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -359,26 +393,30 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs= -github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kunwardeep/paralleltest v1.0.14 h1:wAkMoMeGX/kGfhQBPODT/BL8XhK23ol/nuQ3SwFaUw8= +github.com/kunwardeep/paralleltest v1.0.14/go.mod h1:di4moFqtfz3ToSKxhNjhOZL+696QtJGCFe132CbBLGk= github.com/lasiar/canonicalheader v1.1.2 h1:vZ5uqwvDbyJCnMhmFYimgMZnJMjwljN5VGY0VKbMXb4= github.com/lasiar/canonicalheader v1.1.2/go.mod h1:qJCeLFS0G/QlLQ506T+Fk/fWMa2VmBUiEI2cuMK4djI= -github.com/ldez/exptostd v0.4.1 h1:DIollgQ3LWZMp3HJbSXsdE2giJxMfjyHj3eX4oiD6JU= -github.com/ldez/exptostd v0.4.1/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= +github.com/ldez/exptostd v0.4.3 h1:Ag1aGiq2epGePuRJhez2mzOpZ8sI9Gimcb4Sb3+pk9Y= +github.com/ldez/exptostd v0.4.3/go.mod h1:iZBRYaUmcW5jwCR3KROEZ1KivQQp6PHXbDPk9hqJKCQ= github.com/ldez/gomoddirectives v0.6.1 h1:Z+PxGAY+217f/bSGjNZr/b2KTXcyYLgiWI6geMBN2Qc= github.com/ldez/gomoddirectives v0.6.1/go.mod h1:cVBiu3AHR9V31em9u2kwfMKD43ayN5/XDgr+cdaFaKs= github.com/ldez/grignotin v0.9.0 h1:MgOEmjZIVNn6p5wPaGp/0OKWyvq42KnzAt/DAb8O4Ow= github.com/ldez/grignotin v0.9.0/go.mod h1:uaVTr0SoZ1KBii33c47O1M8Jp3OP3YDwhZCmzT9GHEk= github.com/ldez/tagliatelle v0.7.1 h1:bTgKjjc2sQcsgPiT902+aadvMjCeMHrY7ly2XKFORIk= github.com/ldez/tagliatelle v0.7.1/go.mod h1:3zjxUpsNB2aEZScWiZTHrAXOl1x25t3cRmzfK1mlo2I= -github.com/ldez/usetesting v0.4.2 h1:J2WwbrFGk3wx4cZwSMiCQQ00kjGR0+tuuyW0Lqm4lwA= -github.com/ldez/usetesting v0.4.2/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= +github.com/ldez/usetesting v0.4.3 h1:pJpN0x3fMupdTf/IapYjnkhiY1nSTN+pox1/GyBRw3k= +github.com/ldez/usetesting v0.4.3/go.mod h1:eEs46T3PpQ+9RgN9VjpY6qWdiw2/QmfiDeWmdZdrjIQ= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= -github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= -github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/macabu/inamedparam v0.2.0 h1:VyPYpOc10nkhI2qeNUdh3Zket4fcZjEWe35poddBCpE= +github.com/macabu/inamedparam v0.2.0/go.mod h1:+Pee9/YfGe5LJ62pYXqB89lJ+0k5bsR8Wgz/C0Zlq3U= github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA= github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg= +github.com/manuelarte/funcorder v0.2.1 h1:7QJsw3qhljoZ5rH0xapIvjw31EcQeFbF31/7kQ/xS34= +github.com/manuelarte/funcorder v0.2.1/go.mod h1:BQQ0yW57+PF9ZpjpeJDKOffEsQbxDFKW8F8zSMe/Zd0= github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI= github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE= github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04= @@ -387,9 +425,13 @@ github.com/matoous/godox v1.1.0 h1:W5mqwbyWrwZv6OQ5Z1a/DHGMOvXYCBP3+Ht7KMoJhq4= github.com/matoous/godox v1.1.0/go.mod h1:jgE/3fUXiTurkdHOLT5WEkThTSuE7yxHv5iWPa80afs= github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE= github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -399,8 +441,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mgechev/revive v1.6.1 h1:ncK0ZCMWtb8GXwVAmk+IeWF2ULIDsvRxSRfg5sTwQ2w= -github.com/mgechev/revive v1.6.1/go.mod h1:/2tfHWVO8UQi/hqJsIYNEKELi+DJy/e+PQpLgTB1v88= +github.com/mgechev/revive v1.9.0 h1:8LaA62XIKrb8lM6VsBSQ92slt/o92z5+hTw3CmrvSrM= +github.com/mgechev/revive v1.9.0/go.mod h1:LAPq3+MgOf7GcL5PlWIkHb0PT7XH4NuC2LdWymhb9Mo= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= @@ -414,6 +456,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI= github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U= @@ -422,14 +466,16 @@ github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhK github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.19.0 h1:CnHRFAeBS3LdLI9h+Jidbcc5KH71GKOmaBZQk8Srnto= -github.com/nunnatsa/ginkgolinter v0.19.0/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s= +github.com/nunnatsa/ginkgolinter v0.19.1 h1:mjwbOlDQxZi9Cal+KfbEJTCz327OLNfwNvoZ70NJ+c4= +github.com/nunnatsa/ginkgolinter v0.19.1/go.mod h1:jkQ3naZDmxaZMXPWaS9rblH+i+GWXQCaS/JFIWcOH2s= +github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= +github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= -github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= -github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0= +github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/gomega v1.36.3 h1:hID7cr8t3Wp26+cYnfcjR6HpJ00fdogN6dqZ1t6IylU= +github.com/onsi/gomega v1.36.3/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= @@ -437,14 +483,14 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= -github.com/patrickcping/pingone-go-sdk-v2 v0.12.13 h1:Kyktdn+jnZ87zgfKwaPTZy+qKVQHYtkj7smPez3UKEk= -github.com/patrickcping/pingone-go-sdk-v2 v0.12.13/go.mod h1:JHqqA/Q11OK5LOyslrtczTfyO92pK/0oI25w6zX5VB8= +github.com/patrickcping/pingone-go-sdk-v2 v0.12.17 h1:XN4IpfaqGVTcmNYx9Qh42Yo9TJLCZGl+sQT5H7sUnBA= +github.com/patrickcping/pingone-go-sdk-v2 v0.12.17/go.mod h1:6Q+d1SXedSku9+64HlgZofHGTfahQmoGKRwMsWBn7qM= github.com/patrickcping/pingone-go-sdk-v2/authorize v0.8.0 h1:gEPzZToJlBcJh2Ft12dP1GCSGzsNFQFEHS7Bql86RQk= github.com/patrickcping/pingone-go-sdk-v2/authorize v0.8.0/go.mod h1:2PDrgC1ufXk2IDIk4JQHx6r34r2xpkbnzKIpXFv8gYs= github.com/patrickcping/pingone-go-sdk-v2/credentials v0.11.0 h1:pLiiBkROks/40vhFWJEcr/tiIEqqYdP4FWsHtfCLdIs= github.com/patrickcping/pingone-go-sdk-v2/credentials v0.11.0/go.mod h1:yRGf7+tsB3/AQYsNjIIs4ScJhR885mvDYMgwHiQeMl0= -github.com/patrickcping/pingone-go-sdk-v2/management v0.53.0 h1:psDOY/ARaLlJnRrPdMs/o3FGUFb5fHUCnBuwC3b4+n0= -github.com/patrickcping/pingone-go-sdk-v2/management v0.53.0/go.mod h1:oLB/jjAkn4oEA60nC5/0KAobvcNJbflOWnVaS6lKxv8= +github.com/patrickcping/pingone-go-sdk-v2/management v0.57.0 h1:no242yX2sr9BIyWoyuBGzVscZJgDaE0RseUJL0AmqPk= +github.com/patrickcping/pingone-go-sdk-v2/management v0.57.0/go.mod h1:oLB/jjAkn4oEA60nC5/0KAobvcNJbflOWnVaS6lKxv8= github.com/patrickcping/pingone-go-sdk-v2/mfa v0.23.0 h1:k133OY6PNO3tgNK3LBoEI+Uf9bRNKsvAkMMVUf99/Q0= github.com/patrickcping/pingone-go-sdk-v2/mfa v0.23.0/go.mod h1:Q+Ym6kktv5Y6VnVhDt//lWoOhmIKfyjo6ejRx5mLttY= github.com/patrickcping/pingone-go-sdk-v2/risk v0.19.0 h1:qGdwnfjsexHhTUAyBaUzheyeKWhR3Q8groqVpprzzOw= @@ -453,8 +499,8 @@ github.com/patrickcping/pingone-go-sdk-v2/verify v0.9.0 h1:Gnxvi7yx4NSBNOqBBydUP github.com/patrickcping/pingone-go-sdk-v2/verify v0.9.0/go.mod h1:bCq5fHv9mSdNsm/XiT5jb3YgYnQb8F824EYfq9eAJl4= github.com/pavius/impi v0.0.3 h1:DND6MzU+BLABhOZXbELR3FU8b+zDgcq4dOCNLhiTYuI= github.com/pavius/impi v0.0.3/go.mod h1:x/hU0bfdWIhuOT1SKwiJg++yvkk6EuOtJk8WtDZqgr8= -github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= -github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pingidentity/pingfederate-go-client/v1220 v1220.0.0 h1:rCYn/ELV74uLO9dDWNgax03pjn9sw8YVNeIK94qkDmA= github.com/pingidentity/pingfederate-go-client/v1220 v1220.0.0/go.mod h1:oA+IsXxQB8Glkq90fWakWb6tJ742An5Ca5vwn9GobM4= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -464,8 +510,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.7.1 h1:RyLVXIbosq1gBdk/pChWA8zWYLsq9UEw7a1L5TVMCnA= -github.com/polyfloyd/go-errorlint v1.7.1/go.mod h1:aXjNb1x2TNhoLsk26iv1yl7a+zTnXPhwEMtEXukiLR8= +github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q= +github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -490,8 +536,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo= -github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI= +github.com/quasilyte/go-ruleguard v0.4.4 h1:53DncefIeLX3qEpjzlS1lyUmQoUEeOWPFWqaTJq9eAQ= +github.com/quasilyte/go-ruleguard v0.4.4/go.mod h1:Vl05zJ538vcEEwu16V/Hdu7IYZWyKSwIy4c88Ro1kRE= github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE= github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU= github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo= @@ -506,14 +552,14 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU= -github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE= +github.com/ryancurrah/gomodguard v1.4.1 h1:eWC8eUMNZ/wM/PWuZBv7JxxqT5fiIKSIyTvjb7Elr+g= +github.com/ryancurrah/gomodguard v1.4.1/go.mod h1:qnMJwV1hX9m+YJseXEBhd2s90+1Xn6x9dLz11ualI1I= github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU= github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= @@ -526,8 +572,10 @@ github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tM github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= github.com/sashamelentyev/usestdlibvars v1.28.0 h1:jZnudE2zKCtYlGzLVreNp5pmCdOxXUzwsMDBkR21cyQ= github.com/sashamelentyev/usestdlibvars v1.28.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= -github.com/securego/gosec/v2 v2.22.1 h1:IcBt3TpI5Y9VN1YlwjSpM2cHu0i3Iw52QM+PQeg7jN8= -github.com/securego/gosec/v2 v2.22.1/go.mod h1:4bb95X4Jz7VSEPdVjC0hD7C/yR6kdeUBvCPOy9gDQ0g= +github.com/securego/gosec/v2 v2.22.3 h1:mRrCNmRF2NgZp4RJ8oJ6yPJ7G4x6OCiAXHd8x4trLRc= +github.com/securego/gosec/v2 v2.22.3/go.mod h1:42M9Xs0v1WseinaB/BmNGO8AVqG8vRfhC2686ACY48k= +github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ= +github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -537,16 +585,14 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4= -github.com/sivchari/tenv v1.12.1 h1:+E0QzjktdnExv/wwsnnyk4oqZBUfuh89YMQT1cyuvSY= -github.com/sivchari/tenv v1.12.1/go.mod h1:1LjSOUCc25snIr5n3DtGGrENhX3LuWefcplwVGC24mw= github.com/sonatard/noctx v0.1.0 h1:JjqOc2WN16ISWAjAk8M5ej0RfExEXtkEyExl2hLW+OM= github.com/sonatard/noctx v0.1.0/go.mod h1:0RvBxqY8D4j9cTTTWE8ylt2vqj2EPI8fHmrxHdsaZ2c= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0= github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= @@ -572,26 +618,27 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/tdakkota/asciicheck v0.4.0 h1:VZ13Itw4k1i7d+dpDSNS8Op645XgGHpkCEh/WHicgWw= -github.com/tdakkota/asciicheck v0.4.0/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= +github.com/tdakkota/asciicheck v0.4.1 h1:bm0tbcmi0jezRA2b5kg4ozmMuGAFotKI3RZfrhfovg8= +github.com/tdakkota/asciicheck v0.4.1/go.mod h1:0k7M3rCfRXb0Z6bwgvkEIMleKH3kXNz9UqJ9Xuqopr8= github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA= github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.20 h1:z/p8Ek55UdNvzt4TFn2zx2KscpW4rWqcnUrdmvWJj7E= -github.com/tetafro/godot v1.4.20/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= -github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3 h1:y4mJRFlM6fUyPhoXuFg/Yu02fg/nIPFMOY8tOqppoFg= -github.com/timakin/bodyclose v0.0.0-20241017074812-ed6a65f985e3/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= -github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= -github.com/timonwong/loggercheck v0.10.1/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= -github.com/tomarrell/wrapcheck/v2 v2.10.0 h1:SzRCryzy4IrAH7bVGG4cK40tNUhmVmMDuJujy4XwYDg= -github.com/tomarrell/wrapcheck/v2 v2.10.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo= +github.com/tetafro/godot v1.5.1 h1:PZnjCol4+FqaEzvZg5+O8IY2P3hfY9JzRBNPv1pEDS4= +github.com/tetafro/godot v1.5.1/go.mod h1:cCdPtEndkmqqrhiCfkmxDodMQJ/f3L1BCNskCUZdTwk= +github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67 h1:9LPGD+jzxMlnk5r6+hJnar67cgpDIz/iyD+rfl5r2Vk= +github.com/timakin/bodyclose v0.0.0-20241222091800-1db5c5ca4d67/go.mod h1:mkjARE7Yr8qU23YcGMSALbIxTQ9r9QBVahQOBRfU460= +github.com/timonwong/loggercheck v0.11.0 h1:jdaMpYBl+Uq9mWPXv1r8jc5fC3gyXx4/WGwTnnNKn4M= +github.com/timonwong/loggercheck v0.11.0/go.mod h1:HEAWU8djynujaAVX7QI65Myb8qgfcZ1uKbdpg3ZzKl8= +github.com/tomarrell/wrapcheck/v2 v2.11.0 h1:BJSt36snX9+4WTIXeJ7nvHBQBcm1h2SjQMSlmQ6aFSU= +github.com/tomarrell/wrapcheck/v2 v2.11.0/go.mod h1:wFL9pDWDAbXhhPZZt+nG8Fu+h29TtnZ2MW6Lx4BRXIU= github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw= github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ultraware/funlen v0.2.0 h1:gCHmCn+d2/1SemTdYMiKLAHFYxTYz7z9VIDRaTGyLkI= @@ -602,8 +649,10 @@ github.com/uudashr/gocognit v1.2.0 h1:3BU9aMr1xbhPlvJLSydKwdLN3tEUUrzPSSM8S4hDYR github.com/uudashr/gocognit v1.2.0/go.mod h1:k/DdKPI6XBZO1q7HgoV2juESI2/Ofj9AcHPZhBBdrTU= github.com/uudashr/iface v1.3.1 h1:bA51vmVx1UIhiIsQFSNq6GZ6VPTk3WNMZgRiCe9R29U= github.com/uudashr/iface v1.3.1/go.mod h1:4QvspiRd3JLPAEXBQ9AiZpLbJlrWWgRChOKDJEuQTdg= -github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU= -github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg= +github.com/xen0n/gosmopolitan v1.3.0 h1:zAZI1zefvo7gcpbCOrPSHJZJYA9ZgLfJqtKzZ5pHqQM= +github.com/xen0n/gosmopolitan v1.3.0/go.mod h1:rckfr5T6o4lBtM1ga7mLGKZmLxswUoH1zxHgNXOsEt4= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM= github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs= @@ -621,15 +670,29 @@ gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo= gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8= go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ= go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/musttag v0.13.0 h1:Q/YAW0AHvaoaIbsPj3bvEI5/QFP7w696IMUpnKXQfCE= -go-simpler.org/musttag v0.13.0/go.mod h1:FTzIGeK6OkKlUDVpj0iQUXZLUO1Js9+mvykDQy9C5yM= -go-simpler.org/sloglint v0.9.0 h1:/40NQtjRx9txvsB/RN022KsUJU+zaaSb/9q9BSefSrE= -go-simpler.org/sloglint v0.9.0/go.mod h1:G/OrAF6uxj48sHahCzrbarVMptL2kjWTaUeC8+fOGww= +go-simpler.org/musttag v0.13.1 h1:lw2sJyu7S1X8lc8zWUAdH42y+afdcCnHhWpnkWvd6vU= +go-simpler.org/musttag v0.13.1/go.mod h1:8r450ehpMLQgvpb6sg+hV5Ur47eH6olp/3yEanfG97k= +go-simpler.org/sloglint v0.11.0 h1:JlR1X4jkbeaffiyjLtymeqmGDKBDO1ikC6rjiuFAOco= +go-simpler.org/sloglint v0.11.0/go.mod h1:CFDO8R1i77dlciGfPEPvYke2ZMx4eyGiEIWkyeW2Pvw= +go.augendre.info/fatcontext v0.8.0 h1:2dfk6CQbDGeu1YocF59Za5Pia7ULeAM6friJ3LP7lmk= +go.augendre.info/fatcontext v0.8.0/go.mod h1:oVJfMgwngMsHO+KB2MdgzcO+RvtNdiCEOlWvSFtax/s= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= @@ -694,8 +757,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= -golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -736,16 +799,16 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -761,8 +824,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -781,6 +844,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -803,9 +867,12 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -816,8 +883,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= @@ -838,8 +905,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -899,8 +966,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= 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= @@ -956,6 +1023,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 h1:e0AIkUUhxyBKh6ssZNrAMeqhA7RKUj42346d1y02i2g= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -968,6 +1037,8 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= +google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -980,8 +1051,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1006,12 +1077,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.6.0 h1:TAODvD3knlq75WCp2nyGJtT4LeRV/o7NN9nYPeVJXf8= -honnef.co/go/tools v0.6.0/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= -mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU= -mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo= -mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U= -mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ= +honnef.co/go/tools v0.6.1 h1:R094WgE8K4JirYjBaOpz/AvTyUu/3wbmAoskKN/pxTI= +honnef.co/go/tools v0.6.1/go.mod h1:3puzxxljPCe8RGJX7BIy1plGbxEOZni5mR2aXe3/uk4= +mvdan.cc/gofumpt v0.8.0 h1:nZUCeC2ViFaerTcYKstMmfysj6uhQrA2vJe+2vwGU6k= +mvdan.cc/gofumpt v0.8.0/go.mod h1:vEYnSzyGPmjvFkqJWtXkh79UwPWP9/HMxQdGEXZHjpg= +mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4 h1:WjUu4yQoT5BHT1w8Zu56SP8367OuBV5jvo+4Ulppyf8= +mvdan.cc/unparam v0.0.0-20250301125049-0df0534333a4/go.mod h1:rthT7OuvRbaGcd5ginj6dA2oLE7YNlta9qhBNNdCaLE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/internal/commands/plugin/add_internal.go b/internal/commands/plugin/add_internal.go new file mode 100644 index 00000000..13cb34e6 --- /dev/null +++ b/internal/commands/plugin/add_internal.go @@ -0,0 +1,87 @@ +package plugin_internal + +import ( + "fmt" + "os/exec" + + "github.com/pingidentity/pingcli/internal/configuration/options" + "github.com/pingidentity/pingcli/internal/customtypes" + "github.com/pingidentity/pingcli/internal/output" + "github.com/pingidentity/pingcli/internal/profiles" +) + +func RunInternalPluginAdd(pluginExecutable string) error { + if pluginExecutable == "" { + return fmt.Errorf("plugin executable is required") + } + + // Check if plugin executable is in PATH + _, err := exec.LookPath(pluginExecutable) + if err != nil { + // exec error contains executable name and $PATH error message + return fmt.Errorf("failed to add plugin: %w", err) + } + + err = addPluginExecutable(pluginExecutable) + if err != nil { + return fmt.Errorf("failed to add plugin: %w", err) + } + + output.Success(fmt.Sprintf("Plugin '%s' added.", pluginExecutable), nil) + + return nil +} + +func addPluginExecutable(pluginExecutable string) error { + pName, err := readPluginAddProfileName() + if err != nil { + return fmt.Errorf("failed to read profile name: %w", err) + } + + subKoanf, err := profiles.GetKoanfConfig().GetProfileKoanf(pName) + if err != nil { + return fmt.Errorf("failed to get profile: %w", err) + } + + existingPluginExectuables, _, err := profiles.KoanfValueFromOption(options.PluginExecutablesOption, pName) + if err != nil { + return fmt.Errorf("failed to get existing plugin configuration from profile '%s': %w", pName, err) + } + + strSlice := new(customtypes.StringSlice) + if err = strSlice.Set(existingPluginExectuables); err != nil { + return err + } + if err = strSlice.Set(pluginExecutable); err != nil { + return err + } + + err = subKoanf.Set(options.PluginExecutablesOption.KoanfKey, strSlice) + if err != nil { + return err + } + + if err = profiles.GetKoanfConfig().SaveProfile(pName, subKoanf); err != nil { + return err + } + + return nil +} + +func readPluginAddProfileName() (pName string, err error) { + if !options.RootProfileOption.Flag.Changed { + pName, err = profiles.GetOptionValue(options.RootActiveProfileOption) + } else { + pName, err = profiles.GetOptionValue(options.RootProfileOption) + } + + if err != nil { + return pName, err + } + + if pName == "" { + return pName, fmt.Errorf("unable to determine active profile") + } + + return pName, nil +} diff --git a/internal/commands/plugin/add_internal_test.go b/internal/commands/plugin/add_internal_test.go new file mode 100644 index 00000000..59ca3d79 --- /dev/null +++ b/internal/commands/plugin/add_internal_test.go @@ -0,0 +1,59 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_internal + +import ( + "os" + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_koanf" +) + +// Test RunInternalPluginAdd function +func Test_RunInternalPluginAdd(t *testing.T) { + testutils_koanf.InitKoanfs(t) + + // Create a temporary $PATH for a test plugin + pathDir := t.TempDir() + t.Setenv("PATH", pathDir) + + testPlugin, err := os.CreateTemp(pathDir, "test-plugin-*.sh") + if err != nil { + t.Fatalf("Failed to create temporary plugin file: %v", err) + } + + defer func() { + err = os.Remove(testPlugin.Name()) + if err != nil { + t.Fatalf("Failed to remove temporary plugin file: %v", err) + } + }() + + _, err = testPlugin.WriteString("#!/usr/bin/env sh\necho \"Hello, world!\"\nexit 0\n") + if err != nil { + t.Fatalf("Failed to write to temporary plugin file: %v", err) + } + + err = testPlugin.Chmod(0755) + if err != nil { + t.Fatalf("Failed to set permissions on temporary plugin file: %v", err) + } + + err = testPlugin.Close() + if err != nil { + t.Fatalf("Failed to close temporary plugin file: %v", err) + } + + err = RunInternalPluginAdd(testPlugin.Name()) + if err != nil { + t.Errorf("RunInternalPluginAdd returned error: %v", err) + } +} + +// Test RunInternalPluginAdd function fails with non-existent plugin +func Test_RunInternalPluginAdd_NonExistentPlugin(t *testing.T) { + expectedErrorPattern := `^failed to add plugin: exec: .*: executable file not found in \$PATH$` + err := RunInternalPluginAdd("non-existent-plugin") + testutils.CheckExpectedError(t, err, &expectedErrorPattern) +} diff --git a/internal/commands/plugin/list_internal.go b/internal/commands/plugin/list_internal.go new file mode 100644 index 00000000..9252807f --- /dev/null +++ b/internal/commands/plugin/list_internal.go @@ -0,0 +1,30 @@ +package plugin_internal + +import ( + "fmt" + "strings" + + "github.com/pingidentity/pingcli/internal/configuration/options" + "github.com/pingidentity/pingcli/internal/output" + "github.com/pingidentity/pingcli/internal/profiles" +) + +func RunInternalPluginList() error { + existingPluginExectuables, _, err := profiles.KoanfValueFromOption(options.PluginExecutablesOption, "") + if err != nil { + return fmt.Errorf("failed to get existing plugin configuration: %w", err) + } + + listStr := "Plugins:\n" + for pluginExecutable := range strings.SplitSeq(existingPluginExectuables, ",") { + if pluginExecutable == "" { + continue + } + + listStr += "- " + pluginExecutable + "\n" + } + + output.Message(listStr, nil) + + return nil +} diff --git a/internal/commands/plugin/list_internal_test.go b/internal/commands/plugin/list_internal_test.go new file mode 100644 index 00000000..3b206529 --- /dev/null +++ b/internal/commands/plugin/list_internal_test.go @@ -0,0 +1,19 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_internal + +import ( + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils_koanf" +) + +// Test RunInternalPluginList function +func Test_RunInternalPluginList(t *testing.T) { + testutils_koanf.InitKoanfs(t) + + err := RunInternalPluginList() + if err != nil { + t.Errorf("RunInternalPluginList returned error: %v", err) + } +} diff --git a/internal/commands/plugin/remove_internal.go b/internal/commands/plugin/remove_internal.go new file mode 100644 index 00000000..88971c6f --- /dev/null +++ b/internal/commands/plugin/remove_internal.go @@ -0,0 +1,88 @@ +package plugin_internal + +import ( + "fmt" + + "github.com/pingidentity/pingcli/internal/configuration/options" + "github.com/pingidentity/pingcli/internal/customtypes" + "github.com/pingidentity/pingcli/internal/output" + "github.com/pingidentity/pingcli/internal/profiles" +) + +func RunInternalPluginRemove(pluginExecutable string) error { + if pluginExecutable == "" { + return fmt.Errorf("plugin executable is required") + } + + ok, err := removePluginExecutable(pluginExecutable) + if err != nil { + return fmt.Errorf("failed to remove plugin: %w", err) + } + + if ok { + output.Success(fmt.Sprintf("Plugin '%s' removed.", pluginExecutable), nil) + } + + return nil +} + +func removePluginExecutable(pluginExecutable string) (bool, error) { + pName, err := readPluginRemoveProfileName() + if err != nil { + return false, fmt.Errorf("failed to read profile name: %w", err) + } + + subKoanf, err := profiles.GetKoanfConfig().GetProfileKoanf(pName) + if err != nil { + return false, fmt.Errorf("failed to get profile: %w", err) + } + + existingPluginExectuables, _, err := profiles.KoanfValueFromOption(options.PluginExecutablesOption, pName) + if err != nil { + return false, fmt.Errorf("failed to get existing plugin configuration from profile '%s': %w", pName, err) + } + + strSlice := new(customtypes.StringSlice) + if err = strSlice.Set(existingPluginExectuables); err != nil { + return false, err + } + removed, err := strSlice.Remove(pluginExecutable) + if err != nil { + return false, err + } + + if !removed { + output.Warn(fmt.Sprintf("plugin executable '%s' not found in profile '%s' plugins", pluginExecutable, pName), nil) + + return false, nil + } + + err = subKoanf.Set(options.PluginExecutablesOption.KoanfKey, strSlice) + if err != nil { + return false, err + } + + if err = profiles.GetKoanfConfig().SaveProfile(pName, subKoanf); err != nil { + return false, err + } + + return true, nil +} + +func readPluginRemoveProfileName() (pName string, err error) { + if !options.RootProfileOption.Flag.Changed { + pName, err = profiles.GetOptionValue(options.RootActiveProfileOption) + } else { + pName, err = profiles.GetOptionValue(options.RootProfileOption) + } + + if err != nil { + return pName, err + } + + if pName == "" { + return pName, fmt.Errorf("unable to determine active profile") + } + + return pName, nil +} diff --git a/internal/commands/plugin/remove_internal_test.go b/internal/commands/plugin/remove_internal_test.go new file mode 100644 index 00000000..98baafcc --- /dev/null +++ b/internal/commands/plugin/remove_internal_test.go @@ -0,0 +1,65 @@ +// Copyright © 2025 Ping Identity Corporation + +package plugin_internal + +import ( + "os" + "testing" + + "github.com/pingidentity/pingcli/internal/testing/testutils" + "github.com/pingidentity/pingcli/internal/testing/testutils_koanf" +) + +// Test RunInternalPluginRemove function +func Test_RunInternalPluginRemove(t *testing.T) { + testutils_koanf.InitKoanfs(t) + + // Create a temporary $PATH for a test plugin + pathDir := t.TempDir() + t.Setenv("PATH", pathDir) + + testPlugin, err := os.CreateTemp(pathDir, "test-plugin-*.sh") + if err != nil { + t.Fatalf("Failed to create temporary plugin file: %v", err) + } + + defer func() { + err = os.Remove(testPlugin.Name()) + if err != nil { + t.Fatalf("Failed to remove temporary plugin file: %v", err) + } + }() + + _, err = testPlugin.WriteString("#!/usr/bin/env sh\necho \"Hello, world!\"\nexit 0\n") + if err != nil { + t.Fatalf("Failed to write to temporary plugin file: %v", err) + } + + err = testPlugin.Chmod(0755) + if err != nil { + t.Fatalf("Failed to set permissions on temporary plugin file: %v", err) + } + + err = testPlugin.Close() + if err != nil { + t.Fatalf("Failed to close temporary plugin file: %v", err) + } + + err = RunInternalPluginAdd(testPlugin.Name()) + if err != nil { + t.Errorf("RunInternalPluginAdd returned error: %v", err) + } + + err = RunInternalPluginRemove(testPlugin.Name()) + if err != nil { + t.Errorf("RunInternalPluginRemove returned error: %v", err) + } +} + +// Test RunInternalPluginRemove function succeeds with non-existent plugin +func Test_RunInternalPluginRemove_NonExistentPlugin(t *testing.T) { + testutils_koanf.InitKoanfs(t) + + err := RunInternalPluginRemove("non-existent-plugin") + testutils.CheckExpectedError(t, err, nil) +} diff --git a/internal/configuration/configuration.go b/internal/configuration/configuration.go index 50dc0004..9a75f1ec 100644 --- a/internal/configuration/configuration.go +++ b/internal/configuration/configuration.go @@ -10,6 +10,7 @@ import ( configuration_config "github.com/pingidentity/pingcli/internal/configuration/config" "github.com/pingidentity/pingcli/internal/configuration/options" configuration_platform "github.com/pingidentity/pingcli/internal/configuration/platform" + configuration_plugin "github.com/pingidentity/pingcli/internal/configuration/plugin" configuration_profiles "github.com/pingidentity/pingcli/internal/configuration/profiles" configuration_request "github.com/pingidentity/pingcli/internal/configuration/request" configuration_root "github.com/pingidentity/pingcli/internal/configuration/root" @@ -90,6 +91,8 @@ func InitAllOptions() { configuration_platform.InitPlatformExportOptions() + configuration_plugin.InitPluginOptions() + configuration_profiles.InitProfilesOptions() configuration_root.InitRootOptions() diff --git a/internal/configuration/options/options.go b/internal/configuration/options/options.go index 3f7c1c8b..02a2b6cb 100644 --- a/internal/configuration/options/options.go +++ b/internal/configuration/options/options.go @@ -56,6 +56,8 @@ func Options() []Option { PlatformExportOverwriteOption, PlatformExportPingOneEnvironmentIDOption, + PluginExecutablesOption, + PingFederateHTTPSHostOption, PingFederateAdminAPIPathOption, PingFederateXBypassExternalValidationHeaderOption, @@ -153,6 +155,11 @@ var ( PlatformExportPingOneEnvironmentIDOption Option ) +// 'pingcli plugin' command options +var ( + PluginExecutablesOption Option +) + // Generic koanf profile options var ( ProfileDescriptionOption Option diff --git a/internal/configuration/plugin/add.go b/internal/configuration/plugin/add.go new file mode 100644 index 00000000..a30fe107 --- /dev/null +++ b/internal/configuration/plugin/add.go @@ -0,0 +1,27 @@ +// Copyright © 2025 Ping Identity Corporation + +package configuration_plugin + +import ( + "github.com/pingidentity/pingcli/internal/configuration/options" + "github.com/pingidentity/pingcli/internal/customtypes" +) + +func InitPluginOptions() { + initPluginExecutablesOption() +} + +func initPluginExecutablesOption() { + defaultValue := customtypes.StringSlice([]string{}) + + options.PluginExecutablesOption = options.Option{ + CobraParamName: "", // No cobra param + CobraParamValue: nil, + DefaultValue: &defaultValue, + EnvVar: "", // No env var + Flag: nil, // No flag + Sensitive: false, + Type: options.ENUM_STRING_SLICE, + KoanfKey: "plugins", + } +} diff --git a/internal/configuration/request/request.go b/internal/configuration/request/request.go index 70bf779d..bd3903fc 100644 --- a/internal/configuration/request/request.go +++ b/internal/configuration/request/request.go @@ -158,8 +158,8 @@ func initAccessTokenOption() { CobraParamName: "", // No cobra param name CobraParamValue: nil, // No cobra param value DefaultValue: &defaultValue, - EnvVar: "", // No environment variable - Flag: nil, + EnvVar: "", // No environment variable + Flag: nil, // No flag Sensitive: true, Type: options.ENUM_STRING, KoanfKey: "request.accessToken", diff --git a/internal/connector/pingfederate/pingfederate_connector_test.go b/internal/connector/pingfederate/pingfederate_connector_test.go index c153a7e3..892b74e1 100644 --- a/internal/connector/pingfederate/pingfederate_connector_test.go +++ b/internal/connector/pingfederate/pingfederate_connector_test.go @@ -69,9 +69,7 @@ func TestPingFederateTerraformPlan(t *testing.T) { { name: "PingFederateCertificateCa", testableResource: pingfederate_testable_resources.CertificateCa(t, pingFederateClientInfo), - ignoredErrors: []string{ - "Error: Invalid Attribute Value Length", - }, + ignoredErrors: nil, }, { name: "PingFederateCertificatesRevocationOcspCertificate", @@ -338,9 +336,7 @@ func TestPingFederateTerraformPlan(t *testing.T) { { name: "PingFederateServerSettingsWsTrustStsSettingsIssuerCertificate", testableResource: pingfederate_testable_resources.ServerSettingsWsTrustStsSettingsIssuerCertificate(t, pingFederateClientInfo), - ignoredErrors: []string{ - "Error: Missing Configuration for Required Attribute", - }, + ignoredErrors: nil, }, { name: "PingFederateServiceAuthentication", diff --git a/internal/connector/pingone/platform/pingone_platform_connector_test.go b/internal/connector/pingone/platform/pingone_platform_connector_test.go index 76145edc..e11a7f7a 100644 --- a/internal/connector/pingone/platform/pingone_platform_connector_test.go +++ b/internal/connector/pingone/platform/pingone_platform_connector_test.go @@ -139,23 +139,23 @@ func TestPlatformTerraformPlan(t *testing.T) { name: "Language", testableResource: pingone_platform_testable_resources.Language(t, clientInfo), ignoredErrors: []string{ - "Error: The language code `pt` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `fr` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `es` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `pl` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `hu` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `cs` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `ru` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `ko` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `th` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `tr` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `fr-CA` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `de` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `it` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `ja` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `zh` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `en` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", - "Error: The language code `nl` is reserved and cannot be imported into this provider. Please use `pingone_language_override` for system-defined languages instead.", + "Error: The language code `pt` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `fr` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `es` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `pl` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `hu` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `cs` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `ru` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `ko` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `th` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `tr` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `fr-CA` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `de` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `it` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `ja` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `zh` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `en` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", + "Error: The language code `nl` is reserved and cannot be imported into this provider. Please use `pingone_language_update` for system-defined languages instead.", }, }, { diff --git a/internal/customtypes/string_slice.go b/internal/customtypes/string_slice.go index b3caf1f1..9af9fef5 100644 --- a/internal/customtypes/string_slice.go +++ b/internal/customtypes/string_slice.go @@ -4,6 +4,7 @@ package customtypes import ( "fmt" + "slices" "strings" "github.com/spf13/pflag" @@ -29,6 +30,26 @@ func (ss *StringSlice) Set(val string) error { return nil } +func (ss *StringSlice) Remove(val string) (bool, error) { + if ss == nil { + return false, fmt.Errorf("failed to remove StringSlice value: %s. StringSlice is nil", val) + } + + if val == "" || val == "[]" { + return false, nil + } + + for i, v := range *ss { + if v == val { + *ss = slices.Delete(*ss, i, i+1) + + return true, nil + } + } + + return false, nil +} + func (ss StringSlice) Type() string { return "[]string" } diff --git a/internal/output/output.go b/internal/output/output.go index 6319885a..d4b4cc39 100644 --- a/internal/output/output.go +++ b/internal/output/output.go @@ -96,13 +96,26 @@ func SystemError(message string, fields map[string]interface{}) { l := logger.Get() systemMsg := fmt.Sprintf(`FATAL: %s -This is a bug in the Ping CLI and needs reporting to our team. +This is a bug in PingCLI and needs reporting to our team. Please raise an issue at https://github.com/pingidentity/pingcli`, message) - // l.Fatal() exits the program prematurely before the message is printed - // pass nil to print the message before exiting + printOutput(systemMsg, fields, boldRed, l.Fatal) +} + +// This function is used to inform the user a plugin-level error +// has occurred. These errors should indicate a bug or bad behavior +// of the plugin. +func PluginError(message string, fields map[string]interface{}) { + l := logger.Get() + systemMsg := fmt.Sprintf(`FATAL: %s + +This is a bug in the plugin and needs reporting to the plugin maintainers. + +Consider raising an issue at the plugin's issue tracker.`, + message) + printOutput(systemMsg, fields, boldRed, l.Fatal) } diff --git a/internal/plugins/plugins.go b/internal/plugins/plugins.go new file mode 100644 index 00000000..f9929d6e --- /dev/null +++ b/internal/plugins/plugins.go @@ -0,0 +1,167 @@ +package plugins + +import ( + "errors" + "fmt" + "io" + "os/exec" + "strings" + + "github.com/hashicorp/go-hclog" + hplugin "github.com/hashicorp/go-plugin" + "github.com/pingidentity/pingcli/internal/configuration/options" + "github.com/pingidentity/pingcli/internal/logger" + "github.com/pingidentity/pingcli/internal/profiles" + "github.com/pingidentity/pingcli/shared/grpc" + shared_logger "github.com/pingidentity/pingcli/shared/logger" + "github.com/spf13/cobra" +) + +func AddAllPluginToCmd(cmd *cobra.Command) error { + l := logger.Get() + + // Plugin executables are stored in the profile configuration + // via the command 'pingcli plugin add ' + pluginExecutables, err := profiles.GetOptionValue(options.PluginExecutablesOption) + if err != nil { + return fmt.Errorf("failed to get configured plugin executables: %w", err) + } + + if pluginExecutables == "" { + return nil + } + + for pluginExecutable := range strings.SplitSeq(pluginExecutables, ",") { + if pluginExecutable == "" { + continue + } + + conf, err := pluginConfiguration(pluginExecutable) + if err != nil { + return err + } + + pluginCmd := &cobra.Command{ + Use: conf.Use, + Short: conf.Short, + Long: conf.Long, + Example: conf.Example, + DisableFlagsInUseLine: true, // We write our own flags in @Use attribute + RunE: createCmdRunE(pluginExecutable), + } + + cmd.AddCommand(pluginCmd) + + l.Info().Msgf("Loaded plugin executable: %s", pluginExecutable) + } + + return nil +} + +// createHPluginClient creates a new hplugin.Client for the given plugin executable. +// The caller is responsible for closing the client connection after use. +func createHPluginClient(pluginExecutable string) *hplugin.Client { + // We use our own logger for the plugins to communicate to the user. + // Discard any other plugin logging details to avoid user communication clutter. + logger := hclog.New(&hclog.LoggerOptions{ + Name: "plugin", + Output: io.Discard, + Level: hclog.Debug, + }) + + // PingCLI is the host process. Start the plugin process + client := hplugin.NewClient(&hplugin.ClientConfig{ + HandshakeConfig: grpc.HandshakeConfig, + Plugins: grpc.PluginMap, + Cmd: exec.Command(pluginExecutable), + AllowedProtocols: []hplugin.Protocol{ + hplugin.ProtocolGRPC, + }, + Logger: logger, + }) + + return client +} + +// dispensePlugin connects to the plugin via RPC and dispenses the grpc.PingCliCommand interface. +// the caller is responsible for closing the client connection after use. +func dispensePlugin(client *hplugin.Client, pluginExecutable string) (hplugin.ClientProtocol, grpc.PingCliCommand, error) { + // Connect via RPC + clientProtocol, err := client.Client() + if err != nil { + return nil, nil, fmt.Errorf("failed to create Plugin RPC client: %w", err) + } + + // All PingCLI plugins are expected to serve the ENUM_PINGCLI_COMMAND_GRPC plugin via + // the PluginMap within the plugin.Serve() method. + // Non-Golang plugins unable to use the shared grpc module should supply the + // raw value of ENUM_PINGCLI_COMMAND_GRPC "pingcli_command_grpc" for the PluginMap key. + raw, err := clientProtocol.Dispense(grpc.ENUM_PINGCLI_COMMAND_GRPC) + if err != nil { + return nil, nil, fmt.Errorf("the rpc client failed to dispense plugin executable '%s': %w", pluginExecutable, err) + } + + // Cast the dispensed plugin to the interface we expect to work with: grpc.PingCliCommand. + // However, this is not a normal interface, but rather implemeted over the RPC connection. + plugin, ok := raw.(grpc.PingCliCommand) + if !ok { + return nil, nil, fmt.Errorf("failed to cast plugin executable '%s' to grpc.PingCliCommand interface", pluginExecutable) + } + + return clientProtocol, plugin, nil +} + +func pluginConfiguration(pluginExecutable string) (conf *grpc.PingCliCommandConfiguration, err error) { + client := createHPluginClient(pluginExecutable) + defer client.Kill() + + clientProtocol, plugin, err := dispensePlugin(client, pluginExecutable) + defer func() { + cErr := clientProtocol.Close() + if cErr != nil { + err = errors.Join(err, cErr) + } + }() + + if err != nil { + return nil, err + } + + // The configuration method is defined by the protobuf definition. + // The plugin should return relevant cobra command information + // even if the plugin does not use cobra commands internally. + // This allows the host process PingCLI to present the plugin command + // in the help output. + resp, err := plugin.Configuration() + if err != nil { + return nil, fmt.Errorf("failed to run command from Plugin: %w", err) + } + + return resp, nil +} + +func createCmdRunE(pluginExecutable string) func(cmd *cobra.Command, args []string) (err error) { + return func(cmd *cobra.Command, args []string) error { + client := createHPluginClient(pluginExecutable) + defer client.Kill() + + clientProtocol, plugin, err := dispensePlugin(client, pluginExecutable) + defer func() { + cErr := clientProtocol.Close() + if cErr != nil { + err = errors.Join(err, cErr) + } + }() + + if err != nil { + return err + } + + err = plugin.Run(args, &shared_logger.SharedLogger{}) + if err != nil { + return fmt.Errorf("failed to execute plugin command: %w", err) + } + + return nil + } +} diff --git a/internal/profiles/koanf.go b/internal/profiles/koanf.go index 682c5fd7..70f1b650 100644 --- a/internal/profiles/koanf.go +++ b/internal/profiles/koanf.go @@ -14,7 +14,7 @@ import ( ) var ( - k *KoanfConfig = NewKoanfConfig("") + k *KoanfConfig ) type KoanfConfig struct { @@ -23,10 +23,12 @@ type KoanfConfig struct { } func NewKoanfConfig(cnfFilePath string) *KoanfConfig { - return &KoanfConfig{ + k = &KoanfConfig{ koanfInstance: koanf.New("."), configFilePath: &cnfFilePath, } + + return k } func GetKoanfConfig() *KoanfConfig { diff --git a/internal/proto/pingcli_command.pb.go b/internal/proto/pingcli_command.pb.go new file mode 100644 index 00000000..7126fd6a --- /dev/null +++ b/internal/proto/pingcli_command.pb.go @@ -0,0 +1,330 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.6 +// protoc v5.29.3 +// source: pingcli_command.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" + unsafe "unsafe" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Empty struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Empty) Reset() { + *x = Empty{} + mi := &file_pingcli_command_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Empty) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Empty) ProtoMessage() {} + +func (x *Empty) ProtoReflect() protoreflect.Message { + mi := &file_pingcli_command_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Empty.ProtoReflect.Descriptor instead. +func (*Empty) Descriptor() ([]byte, []int) { + return file_pingcli_command_proto_rawDescGZIP(), []int{0} +} + +type LoggerRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Message *string `protobuf:"bytes,1,opt,name=message" json:"message,omitempty"` + Fields map[string]string `protobuf:"bytes,2,rep,name=fields" json:"fields,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LoggerRequest) Reset() { + *x = LoggerRequest{} + mi := &file_pingcli_command_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LoggerRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LoggerRequest) ProtoMessage() {} + +func (x *LoggerRequest) ProtoReflect() protoreflect.Message { + mi := &file_pingcli_command_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LoggerRequest.ProtoReflect.Descriptor instead. +func (*LoggerRequest) Descriptor() ([]byte, []int) { + return file_pingcli_command_proto_rawDescGZIP(), []int{1} +} + +func (x *LoggerRequest) GetMessage() string { + if x != nil && x.Message != nil { + return *x.Message + } + return "" +} + +func (x *LoggerRequest) GetFields() map[string]string { + if x != nil { + return x.Fields + } + return nil +} + +type PingCliCommandConfigurationResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Example *string `protobuf:"bytes,1,opt,name=example" json:"example,omitempty"` + Long *string `protobuf:"bytes,2,opt,name=long" json:"long,omitempty"` + Short *string `protobuf:"bytes,3,opt,name=short" json:"short,omitempty"` + Use *string `protobuf:"bytes,4,opt,name=use" json:"use,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PingCliCommandConfigurationResponse) Reset() { + *x = PingCliCommandConfigurationResponse{} + mi := &file_pingcli_command_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PingCliCommandConfigurationResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingCliCommandConfigurationResponse) ProtoMessage() {} + +func (x *PingCliCommandConfigurationResponse) ProtoReflect() protoreflect.Message { + mi := &file_pingcli_command_proto_msgTypes[2] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingCliCommandConfigurationResponse.ProtoReflect.Descriptor instead. +func (*PingCliCommandConfigurationResponse) Descriptor() ([]byte, []int) { + return file_pingcli_command_proto_rawDescGZIP(), []int{2} +} + +func (x *PingCliCommandConfigurationResponse) GetExample() string { + if x != nil && x.Example != nil { + return *x.Example + } + return "" +} + +func (x *PingCliCommandConfigurationResponse) GetLong() string { + if x != nil && x.Long != nil { + return *x.Long + } + return "" +} + +func (x *PingCliCommandConfigurationResponse) GetShort() string { + if x != nil && x.Short != nil { + return *x.Short + } + return "" +} + +func (x *PingCliCommandConfigurationResponse) GetUse() string { + if x != nil && x.Use != nil { + return *x.Use + } + return "" +} + +type PingCliCommandRunRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Args []string `protobuf:"bytes,1,rep,name=args" json:"args,omitempty"` + Logger *uint32 `protobuf:"varint,2,opt,name=logger" json:"logger,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PingCliCommandRunRequest) Reset() { + *x = PingCliCommandRunRequest{} + mi := &file_pingcli_command_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PingCliCommandRunRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PingCliCommandRunRequest) ProtoMessage() {} + +func (x *PingCliCommandRunRequest) ProtoReflect() protoreflect.Message { + mi := &file_pingcli_command_proto_msgTypes[3] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use PingCliCommandRunRequest.ProtoReflect.Descriptor instead. +func (*PingCliCommandRunRequest) Descriptor() ([]byte, []int) { + return file_pingcli_command_proto_rawDescGZIP(), []int{3} +} + +func (x *PingCliCommandRunRequest) GetArgs() []string { + if x != nil { + return x.Args + } + return nil +} + +func (x *PingCliCommandRunRequest) GetLogger() uint32 { + if x != nil && x.Logger != nil { + return *x.Logger + } + return 0 +} + +var File_pingcli_command_proto protoreflect.FileDescriptor + +const file_pingcli_command_proto_rawDesc = "" + + "\n" + + "\x15pingcli_command.proto\"\a\n" + + "\x05Empty\"\x98\x01\n" + + "\rLoggerRequest\x12\x18\n" + + "\amessage\x18\x01 \x01(\tR\amessage\x122\n" + + "\x06fields\x18\x02 \x03(\v2\x1a.LoggerRequest.FieldsEntryR\x06fields\x1a9\n" + + "\vFieldsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"{\n" + + "#PingCliCommandConfigurationResponse\x12\x18\n" + + "\aexample\x18\x01 \x01(\tR\aexample\x12\x12\n" + + "\x04long\x18\x02 \x01(\tR\x04long\x12\x14\n" + + "\x05short\x18\x03 \x01(\tR\x05short\x12\x10\n" + + "\x03use\x18\x04 \x01(\tR\x03use\"F\n" + + "\x18PingCliCommandRunRequest\x12\x12\n" + + "\x04args\x18\x01 \x03(\tR\x04args\x12\x16\n" + + "\x06logger\x18\x02 \x01(\rR\x06logger2y\n" + + "\x0ePingCliCommand\x12=\n" + + "\rConfiguration\x12\x06.Empty\x1a$.PingCliCommandConfigurationResponse\x12(\n" + + "\x03Run\x12\x19.PingCliCommandRunRequest\x1a\x06.Empty2\xdf\x01\n" + + "\x06Logger\x12!\n" + + "\aMessage\x12\x0e.LoggerRequest\x1a\x06.Empty\x12!\n" + + "\aSuccess\x12\x0e.LoggerRequest\x1a\x06.Empty\x12\x1e\n" + + "\x04Warn\x12\x0e.LoggerRequest\x1a\x06.Empty\x12#\n" + + "\tUserError\x12\x0e.LoggerRequest\x1a\x06.Empty\x12#\n" + + "\tUserFatal\x12\x0e.LoggerRequest\x1a\x06.Empty\x12%\n" + + "\vPluginError\x12\x0e.LoggerRequest\x1a\x06.EmptyB\tZ\a./protob\beditionsp\xe8\a" + +var ( + file_pingcli_command_proto_rawDescOnce sync.Once + file_pingcli_command_proto_rawDescData []byte +) + +func file_pingcli_command_proto_rawDescGZIP() []byte { + file_pingcli_command_proto_rawDescOnce.Do(func() { + file_pingcli_command_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_pingcli_command_proto_rawDesc), len(file_pingcli_command_proto_rawDesc))) + }) + return file_pingcli_command_proto_rawDescData +} + +var file_pingcli_command_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_pingcli_command_proto_goTypes = []any{ + (*Empty)(nil), // 0: Empty + (*LoggerRequest)(nil), // 1: LoggerRequest + (*PingCliCommandConfigurationResponse)(nil), // 2: PingCliCommandConfigurationResponse + (*PingCliCommandRunRequest)(nil), // 3: PingCliCommandRunRequest + nil, // 4: LoggerRequest.FieldsEntry +} +var file_pingcli_command_proto_depIdxs = []int32{ + 4, // 0: LoggerRequest.fields:type_name -> LoggerRequest.FieldsEntry + 0, // 1: PingCliCommand.Configuration:input_type -> Empty + 3, // 2: PingCliCommand.Run:input_type -> PingCliCommandRunRequest + 1, // 3: Logger.Message:input_type -> LoggerRequest + 1, // 4: Logger.Success:input_type -> LoggerRequest + 1, // 5: Logger.Warn:input_type -> LoggerRequest + 1, // 6: Logger.UserError:input_type -> LoggerRequest + 1, // 7: Logger.UserFatal:input_type -> LoggerRequest + 1, // 8: Logger.PluginError:input_type -> LoggerRequest + 2, // 9: PingCliCommand.Configuration:output_type -> PingCliCommandConfigurationResponse + 0, // 10: PingCliCommand.Run:output_type -> Empty + 0, // 11: Logger.Message:output_type -> Empty + 0, // 12: Logger.Success:output_type -> Empty + 0, // 13: Logger.Warn:output_type -> Empty + 0, // 14: Logger.UserError:output_type -> Empty + 0, // 15: Logger.UserFatal:output_type -> Empty + 0, // 16: Logger.PluginError:output_type -> Empty + 9, // [9:17] is the sub-list for method output_type + 1, // [1:9] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_pingcli_command_proto_init() } +func file_pingcli_command_proto_init() { + if File_pingcli_command_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: unsafe.Slice(unsafe.StringData(file_pingcli_command_proto_rawDesc), len(file_pingcli_command_proto_rawDesc)), + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 2, + }, + GoTypes: file_pingcli_command_proto_goTypes, + DependencyIndexes: file_pingcli_command_proto_depIdxs, + MessageInfos: file_pingcli_command_proto_msgTypes, + }.Build() + File_pingcli_command_proto = out.File + file_pingcli_command_proto_goTypes = nil + file_pingcli_command_proto_depIdxs = nil +} diff --git a/internal/proto/pingcli_command.proto b/internal/proto/pingcli_command.proto new file mode 100644 index 00000000..f3e3b611 --- /dev/null +++ b/internal/proto/pingcli_command.proto @@ -0,0 +1,36 @@ +edition = "2023"; + +option go_package = "./proto"; + +message Empty {} + +message LoggerRequest { + string message = 1; + map fields = 2; +} + +message PingCliCommandConfigurationResponse { + string example = 1; + string long = 2; + string short = 3; + string use = 4; +} + +message PingCliCommandRunRequest { + repeated string args = 1; + uint32 logger = 2; +} + +service PingCliCommand { + rpc Configuration (Empty) returns (PingCliCommandConfigurationResponse); + rpc Run (PingCliCommandRunRequest) returns (Empty); +} + +service Logger { + rpc Message(LoggerRequest) returns (Empty); + rpc Success(LoggerRequest) returns (Empty); + rpc Warn(LoggerRequest) returns (Empty); + rpc UserError(LoggerRequest) returns (Empty); + rpc UserFatal(LoggerRequest) returns (Empty); + rpc PluginError(LoggerRequest) returns (Empty); +} diff --git a/internal/proto/pingcli_command_grpc.pb.go b/internal/proto/pingcli_command_grpc.pb.go new file mode 100644 index 00000000..946ede0f --- /dev/null +++ b/internal/proto/pingcli_command_grpc.pb.go @@ -0,0 +1,451 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v5.29.3 +// source: pingcli_command.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + PingCliCommand_Configuration_FullMethodName = "/PingCliCommand/Configuration" + PingCliCommand_Run_FullMethodName = "/PingCliCommand/Run" +) + +// PingCliCommandClient is the client API for PingCliCommand service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type PingCliCommandClient interface { + Configuration(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingCliCommandConfigurationResponse, error) + Run(ctx context.Context, in *PingCliCommandRunRequest, opts ...grpc.CallOption) (*Empty, error) +} + +type pingCliCommandClient struct { + cc grpc.ClientConnInterface +} + +func NewPingCliCommandClient(cc grpc.ClientConnInterface) PingCliCommandClient { + return &pingCliCommandClient{cc} +} + +func (c *pingCliCommandClient) Configuration(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*PingCliCommandConfigurationResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PingCliCommandConfigurationResponse) + err := c.cc.Invoke(ctx, PingCliCommand_Configuration_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *pingCliCommandClient) Run(ctx context.Context, in *PingCliCommandRunRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, PingCliCommand_Run_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PingCliCommandServer is the server API for PingCliCommand service. +// All implementations must embed UnimplementedPingCliCommandServer +// for forward compatibility. +type PingCliCommandServer interface { + Configuration(context.Context, *Empty) (*PingCliCommandConfigurationResponse, error) + Run(context.Context, *PingCliCommandRunRequest) (*Empty, error) + mustEmbedUnimplementedPingCliCommandServer() +} + +// UnimplementedPingCliCommandServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedPingCliCommandServer struct{} + +func (UnimplementedPingCliCommandServer) Configuration(context.Context, *Empty) (*PingCliCommandConfigurationResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Configuration not implemented") +} +func (UnimplementedPingCliCommandServer) Run(context.Context, *PingCliCommandRunRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Run not implemented") +} +func (UnimplementedPingCliCommandServer) mustEmbedUnimplementedPingCliCommandServer() {} +func (UnimplementedPingCliCommandServer) testEmbeddedByValue() {} + +// UnsafePingCliCommandServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to PingCliCommandServer will +// result in compilation errors. +type UnsafePingCliCommandServer interface { + mustEmbedUnimplementedPingCliCommandServer() +} + +func RegisterPingCliCommandServer(s grpc.ServiceRegistrar, srv PingCliCommandServer) { + // If the following call pancis, it indicates UnimplementedPingCliCommandServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&PingCliCommand_ServiceDesc, srv) +} + +func _PingCliCommand_Configuration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PingCliCommandServer).Configuration(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PingCliCommand_Configuration_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PingCliCommandServer).Configuration(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + +func _PingCliCommand_Run_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PingCliCommandRunRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PingCliCommandServer).Run(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: PingCliCommand_Run_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PingCliCommandServer).Run(ctx, req.(*PingCliCommandRunRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// PingCliCommand_ServiceDesc is the grpc.ServiceDesc for PingCliCommand service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var PingCliCommand_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "PingCliCommand", + HandlerType: (*PingCliCommandServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Configuration", + Handler: _PingCliCommand_Configuration_Handler, + }, + { + MethodName: "Run", + Handler: _PingCliCommand_Run_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pingcli_command.proto", +} + +const ( + Logger_Message_FullMethodName = "/Logger/Message" + Logger_Success_FullMethodName = "/Logger/Success" + Logger_Warn_FullMethodName = "/Logger/Warn" + Logger_UserError_FullMethodName = "/Logger/UserError" + Logger_UserFatal_FullMethodName = "/Logger/UserFatal" + Logger_PluginError_FullMethodName = "/Logger/PluginError" +) + +// LoggerClient is the client API for Logger service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type LoggerClient interface { + Message(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) + Success(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) + Warn(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) + UserError(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) + UserFatal(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) + PluginError(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) +} + +type loggerClient struct { + cc grpc.ClientConnInterface +} + +func NewLoggerClient(cc grpc.ClientConnInterface) LoggerClient { + return &loggerClient{cc} +} + +func (c *loggerClient) Message(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_Message_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loggerClient) Success(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_Success_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loggerClient) Warn(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_Warn_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loggerClient) UserError(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_UserError_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loggerClient) UserFatal(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_UserFatal_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *loggerClient) PluginError(ctx context.Context, in *LoggerRequest, opts ...grpc.CallOption) (*Empty, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(Empty) + err := c.cc.Invoke(ctx, Logger_PluginError_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// LoggerServer is the server API for Logger service. +// All implementations must embed UnimplementedLoggerServer +// for forward compatibility. +type LoggerServer interface { + Message(context.Context, *LoggerRequest) (*Empty, error) + Success(context.Context, *LoggerRequest) (*Empty, error) + Warn(context.Context, *LoggerRequest) (*Empty, error) + UserError(context.Context, *LoggerRequest) (*Empty, error) + UserFatal(context.Context, *LoggerRequest) (*Empty, error) + PluginError(context.Context, *LoggerRequest) (*Empty, error) + mustEmbedUnimplementedLoggerServer() +} + +// UnimplementedLoggerServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedLoggerServer struct{} + +func (UnimplementedLoggerServer) Message(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Message not implemented") +} +func (UnimplementedLoggerServer) Success(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Success not implemented") +} +func (UnimplementedLoggerServer) Warn(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method Warn not implemented") +} +func (UnimplementedLoggerServer) UserError(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UserError not implemented") +} +func (UnimplementedLoggerServer) UserFatal(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method UserFatal not implemented") +} +func (UnimplementedLoggerServer) PluginError(context.Context, *LoggerRequest) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method PluginError not implemented") +} +func (UnimplementedLoggerServer) mustEmbedUnimplementedLoggerServer() {} +func (UnimplementedLoggerServer) testEmbeddedByValue() {} + +// UnsafeLoggerServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to LoggerServer will +// result in compilation errors. +type UnsafeLoggerServer interface { + mustEmbedUnimplementedLoggerServer() +} + +func RegisterLoggerServer(s grpc.ServiceRegistrar, srv LoggerServer) { + // If the following call pancis, it indicates UnimplementedLoggerServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&Logger_ServiceDesc, srv) +} + +func _Logger_Message_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).Message(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_Message_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).Message(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logger_Success_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).Success(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_Success_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).Success(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logger_Warn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).Warn(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_Warn_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).Warn(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logger_UserError_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).UserError(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_UserError_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).UserError(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logger_UserFatal_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).UserFatal(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_UserFatal_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).UserFatal(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Logger_PluginError_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(LoggerRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(LoggerServer).PluginError(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Logger_PluginError_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(LoggerServer).PluginError(ctx, req.(*LoggerRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// Logger_ServiceDesc is the grpc.ServiceDesc for Logger service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var Logger_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "Logger", + HandlerType: (*LoggerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Message", + Handler: _Logger_Message_Handler, + }, + { + MethodName: "Success", + Handler: _Logger_Success_Handler, + }, + { + MethodName: "Warn", + Handler: _Logger_Warn_Handler, + }, + { + MethodName: "UserError", + Handler: _Logger_UserError_Handler, + }, + { + MethodName: "UserFatal", + Handler: _Logger_UserFatal_Handler, + }, + { + MethodName: "PluginError", + Handler: _Logger_PluginError_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pingcli_command.proto", +} diff --git a/internal/testing/testutils_cobra/cobra_utils.go b/internal/testing/testutils_cobra/cobra_utils.go index c9aea826..448c3ab4 100644 --- a/internal/testing/testutils_cobra/cobra_utils.go +++ b/internal/testing/testutils_cobra/cobra_utils.go @@ -8,6 +8,7 @@ import ( "github.com/pingidentity/pingcli/cmd" "github.com/pingidentity/pingcli/internal/configuration" + "github.com/pingidentity/pingcli/internal/configuration/options" testutils_koanf "github.com/pingidentity/pingcli/internal/testing/testutils_koanf" ) @@ -19,10 +20,13 @@ func ExecutePingcli(t *testing.T, args ...string) (err error) { // Reset options for testing individual executions of pingcli configuration.InitAllOptions() - root := cmd.NewRootCommand("test-version", "test-commit") - - // Add config location to the root command + // Create a temporary config file for the test configFilepath := testutils_koanf.CreateConfigFile(t) + + // Set the config file in the test env so it is used in creation of the root command + t.Setenv(options.RootConfigOption.EnvVar, configFilepath) + + root := cmd.NewRootCommand("test-version", "test-commit") args = append([]string{"--config", configFilepath}, args...) root.SetArgs(args) diff --git a/internal/testing/testutils_koanf/koanf_utils.go b/internal/testing/testutils_koanf/koanf_utils.go index 66862f43..4d108a14 100644 --- a/internal/testing/testutils_koanf/koanf_utils.go +++ b/internal/testing/testutils_koanf/koanf_utils.go @@ -29,7 +29,6 @@ default: outputFormat: text export: outputDirectory: %s - serviceGroup: %s services: ["%s"] service: pingOne: @@ -79,8 +78,7 @@ func configureMainKoanf(t *testing.T) { t.Helper() configFilePath = CreateConfigFile(t) - mainKoanf := profiles.GetKoanfConfig() - mainKoanf.SetKoanfConfigFile(configFilePath) + mainKoanf := profiles.NewKoanfConfig(configFilePath) if err := mainKoanf.KoanfInstance().Load(file.Provider(configFilePath), yaml.Parser()); err != nil { t.Fatalf("Failed to load configuration from file '%s': %v", configFilePath, err) @@ -107,8 +105,7 @@ func InitKoanfsCustomFile(t *testing.T, fileContents string) { func getDefaultConfigFileContents() string { return fmt.Sprintf(defaultConfigFileContentsPattern, outputDirectoryReplacement, - customtypes.ENUM_EXPORT_SERVICE_GROUP_PINGONE, - customtypes.ENUM_EXPORT_SERVICE_PINGFEDERATE, + customtypes.ENUM_EXPORT_SERVICE_PINGONE_PROTECT, os.Getenv("TEST_PINGONE_REGION_CODE"), os.Getenv("TEST_PINGONE_WORKER_CLIENT_ID"), os.Getenv("TEST_PINGONE_WORKER_CLIENT_SECRET"), diff --git a/internal/testing/testutils_terraform/terraform_utils.go b/internal/testing/testutils_terraform/terraform_utils.go index 32578a3a..784817e4 100644 --- a/internal/testing/testutils_terraform/terraform_utils.go +++ b/internal/testing/testutils_terraform/terraform_utils.go @@ -162,7 +162,7 @@ func InitPingOneTerraform(t *testing.T) { required_providers { pingone = { source = "pingidentity/pingone" - version = "1.6.0" + version = "1.8.0" } } } @@ -211,7 +211,7 @@ func InitPingFederateTerraform(t *testing.T) { required_providers { pingfederate = { source = "pingidentity/pingfederate" - version = "1.4.3" + version = "1.5.0" } } } diff --git a/shared/grpc/interface.go b/shared/grpc/interface.go new file mode 100644 index 00000000..cef3e277 --- /dev/null +++ b/shared/grpc/interface.go @@ -0,0 +1,74 @@ +// Copyright © 2025 Ping Identity Corporation + +package grpc + +import ( + "context" + + "github.com/hashicorp/go-plugin" + "github.com/pingidentity/pingcli/internal/proto" + "google.golang.org/grpc" +) + +var ( + HandshakeConfig = plugin.HandshakeConfig{ + ProtocolVersion: 1, + MagicCookieKey: "PINGCLI_COMMAND_PLUGIN_KEY", + MagicCookieValue: "8b8a9351-bef8-42fc-a642-7cb10f12a49c", + } + + ENUM_PINGCLI_COMMAND_GRPC string = "pingcli_command_grpc" + + PluginMap = map[string]plugin.Plugin{ + ENUM_PINGCLI_COMMAND_GRPC: &PingCliCommandGrpcPlugin{}, + } +) + +type PingCliCommandConfiguration struct { + Example string + Long string + Short string + Use string +} + +type LogType int32 + +const ( + LOG_TYPE_UNSPECIFIED LogType = 0 + LOG_TYPE_DEBUG LogType = 1 +) + +type Logger interface { + Message(message string, fields map[string]string) error + Success(message string, fields map[string]string) error + Warn(message string, fields map[string]string) error + UserError(message string, fields map[string]string) error + UserFatal(message string, fields map[string]string) error + PluginError(message string, fields map[string]string) error +} + +type PingCliCommand interface { + Configuration() (*PingCliCommandConfiguration, error) + Run(args []string, l Logger) error +} + +type PingCliCommandGrpcPlugin struct { + plugin.Plugin + Impl PingCliCommand +} + +func (p *PingCliCommandGrpcPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { + proto.RegisterPingCliCommandServer(s, &PingCliCommandGRPCServer{ + broker: broker, + Impl: p.Impl, + }) + + return nil +} + +func (p *PingCliCommandGrpcPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (any, error) { + return &PingCliCommandGRPCClient{ + broker: broker, + client: proto.NewPingCliCommandClient(c), + }, nil +} diff --git a/shared/grpc/logger_grpc_client.go b/shared/grpc/logger_grpc_client.go new file mode 100644 index 00000000..74124e74 --- /dev/null +++ b/shared/grpc/logger_grpc_client.go @@ -0,0 +1,69 @@ +// Copyright © 2025 Ping Identity Corporation + +package grpc + +import ( + "context" + + "github.com/pingidentity/pingcli/internal/proto" +) + +var _ Logger = (*LoggerGRPCClient)(nil) + +type LoggerGRPCClient struct { + client proto.LoggerClient +} + +func (c LoggerGRPCClient) Message(message string, fields map[string]string) error { + _, err := c.client.Message(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} + +func (c LoggerGRPCClient) Success(message string, fields map[string]string) error { + _, err := c.client.Success(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} + +func (c LoggerGRPCClient) Warn(message string, fields map[string]string) error { + _, err := c.client.Warn(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} + +func (c LoggerGRPCClient) UserError(message string, fields map[string]string) error { + _, err := c.client.UserError(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} + +func (c LoggerGRPCClient) UserFatal(message string, fields map[string]string) error { + _, err := c.client.UserFatal(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} + +func (c LoggerGRPCClient) PluginError(message string, fields map[string]string) error { + _, err := c.client.PluginError(context.Background(), &proto.LoggerRequest{ + Message: &message, + Fields: fields, + }) + + return err +} diff --git a/shared/grpc/logger_grpc_server.go b/shared/grpc/logger_grpc_server.go new file mode 100644 index 00000000..784bf694 --- /dev/null +++ b/shared/grpc/logger_grpc_server.go @@ -0,0 +1,52 @@ +// Copyright © 2025 Ping Identity Corporation + +package grpc + +import ( + "context" + + "github.com/pingidentity/pingcli/internal/proto" +) + +var _ proto.LoggerServer = (*LoggerGRPCServer)(nil) + +type LoggerGRPCServer struct { + Impl Logger + proto.UnimplementedLoggerServer +} + +func (s *LoggerGRPCServer) Message(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.Message(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} + +func (s *LoggerGRPCServer) Success(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.Success(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} + +func (s *LoggerGRPCServer) Warn(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.Warn(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} + +func (s *LoggerGRPCServer) UserError(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.UserError(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} + +func (s *LoggerGRPCServer) UserFatal(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.UserFatal(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} + +func (s *LoggerGRPCServer) PluginError(ctx context.Context, req *proto.LoggerRequest) (*proto.Empty, error) { + err := s.Impl.PluginError(req.GetMessage(), req.GetFields()) + + return &proto.Empty{}, err +} diff --git a/shared/grpc/pingcli_command_grpc_client.go b/shared/grpc/pingcli_command_grpc_client.go new file mode 100644 index 00000000..a263b26f --- /dev/null +++ b/shared/grpc/pingcli_command_grpc_client.go @@ -0,0 +1,56 @@ +// Copyright © 2025 Ping Identity Corporation + +package grpc + +import ( + "context" + + "github.com/hashicorp/go-plugin" + "github.com/pingidentity/pingcli/internal/proto" + "google.golang.org/grpc" +) + +var _ PingCliCommand = (*PingCliCommandGRPCClient)(nil) + +type PingCliCommandGRPCClient struct { + broker *plugin.GRPCBroker + client proto.PingCliCommandClient +} + +func (c *PingCliCommandGRPCClient) Configuration() (*PingCliCommandConfiguration, error) { + resp, err := c.client.Configuration(context.Background(), &proto.Empty{}) + if err != nil { + return nil, err + } + + return &PingCliCommandConfiguration{ + Example: resp.GetExample(), + Long: resp.GetLong(), + Short: resp.GetShort(), + Use: resp.GetUse(), + }, nil +} + +func (c *PingCliCommandGRPCClient) Run(args []string, l Logger) error { + loggerServer := &LoggerGRPCServer{ + Impl: l, + } + + var s *grpc.Server + serverFunc := func(opts []grpc.ServerOption) *grpc.Server { + s = grpc.NewServer(opts...) + proto.RegisterLoggerServer(s, loggerServer) + + return s + } + + brokerId := c.broker.NextId() + go c.broker.AcceptAndServe(brokerId, serverFunc) + + _, err := c.client.Run(context.Background(), &proto.PingCliCommandRunRequest{ + Args: args, + Logger: &brokerId, + }) + + return err +} diff --git a/shared/grpc/pingcli_command_grpc_server.go b/shared/grpc/pingcli_command_grpc_server.go new file mode 100644 index 00000000..361682ad --- /dev/null +++ b/shared/grpc/pingcli_command_grpc_server.go @@ -0,0 +1,54 @@ +// Copyright © 2025 Ping Identity Corporation + +package grpc + +import ( + "context" + "errors" + + "github.com/hashicorp/go-plugin" + "github.com/pingidentity/pingcli/internal/proto" +) + +var _ proto.PingCliCommandServer = (*PingCliCommandGRPCServer)(nil) + +type PingCliCommandGRPCServer struct { + Impl PingCliCommand + broker *plugin.GRPCBroker + proto.UnimplementedPingCliCommandServer +} + +func (s *PingCliCommandGRPCServer) Configuration(ctx context.Context, req *proto.Empty) (*proto.PingCliCommandConfigurationResponse, error) { + cmd, err := s.Impl.Configuration() + if err != nil { + return nil, err + } + + return &proto.PingCliCommandConfigurationResponse{ + Example: &cmd.Example, + Long: &cmd.Long, + Short: &cmd.Short, + Use: &cmd.Use, + }, nil +} + +func (s *PingCliCommandGRPCServer) Run(ctx context.Context, req *proto.PingCliCommandRunRequest) (em *proto.Empty, err error) { + conn, err := s.broker.Dial(req.GetLogger()) + if err != nil { + return nil, err + } + defer func() { + cErr := conn.Close() + if cErr != nil { + err = errors.Join(err, cErr) + } + }() + + loggerClient := &LoggerGRPCClient{ + proto.NewLoggerClient(conn), + } + + err = s.Impl.Run(req.GetArgs(), loggerClient) + + return &proto.Empty{}, err +} diff --git a/shared/logger/logger.go b/shared/logger/logger.go new file mode 100644 index 00000000..b575134e --- /dev/null +++ b/shared/logger/logger.go @@ -0,0 +1,52 @@ +package logger + +import ( + "github.com/pingidentity/pingcli/internal/output" +) + +type SharedLogger struct{} + +func (gl SharedLogger) convertFields(fields map[string]string) map[string]interface{} { + convertedFields := make(map[string]interface{}, len(fields)) + for k, v := range fields { + convertedFields[k] = v + } + + return convertedFields +} + +func (gl SharedLogger) Message(message string, fields map[string]string) error { + output.Message(message, gl.convertFields(fields)) + + return nil +} + +func (gl SharedLogger) Success(message string, fields map[string]string) error { + output.Success(message, gl.convertFields(fields)) + + return nil +} + +func (gl SharedLogger) Warn(message string, fields map[string]string) error { + output.Warn(message, gl.convertFields(fields)) + + return nil +} + +func (gl SharedLogger) UserError(message string, fields map[string]string) error { + output.UserError(message, gl.convertFields(fields)) + + return nil +} + +func (gl SharedLogger) UserFatal(message string, fields map[string]string) error { + output.UserFatal(message, gl.convertFields(fields)) + + return nil +} + +func (gl SharedLogger) PluginError(message string, fields map[string]string) error { + output.PluginError(message, gl.convertFields(fields)) + + return nil +}