diff --git a/.mise.toml b/.mise.toml index d486777..81cf9a3 100644 --- a/.mise.toml +++ b/.mise.toml @@ -32,7 +32,6 @@ depends = ["vendor"] [tasks."lint"] description = "Linting automatically checks code for errors and style issues" run = "golangci-lint run" -depends = ["tidy"] [tasks.test] description = "Checks to verify code correctness" diff --git a/cmd/skpr/main.go b/cmd/skpr/main.go index 3f6c19f..c95e94b 100644 --- a/cmd/skpr/main.go +++ b/cmd/skpr/main.go @@ -80,6 +80,13 @@ func main() { os.Exit(1) } + // Experimental commands. + featureFlags, err := userConfig.LoadFeatureFlags() + if err != nil { + fmt.Println("Failed to load feature flags:", err) + os.Exit(1) + } + skprcommand.AddGroupsToCommand(cmd) cmd.AddCommand(alias.NewCommand()) @@ -99,8 +106,8 @@ func main() { cmd.AddCommand(login.NewCommand()) cmd.AddCommand(logout.NewCommand()) cmd.AddCommand(logs.NewCommand()) - cmd.AddCommand(mysql.NewCommand()) - cmd.AddCommand(pkg.NewCommand()) + cmd.AddCommand(mysql.NewCommand(featureFlags.DockerClient)) + cmd.AddCommand(pkg.NewCommand(featureFlags.DockerClient)) cmd.AddCommand(purge.NewCommand()) cmd.AddCommand(release.NewCommand()) cmd.AddCommand(restore.NewCommand()) @@ -117,13 +124,6 @@ func main() { HiddenDefaultCmd: true, } - // Experimental commands. - featureFlags, err := userConfig.LoadFeatureFlags() - if err != nil { - fmt.Println("Failed to load feature flags:", err) - os.Exit(1) - } - if featureFlags.Trace { cmd.AddCommand(trace.NewCommand()) } diff --git a/cmd/skpr/mysql/command.go b/cmd/skpr/mysql/command.go index 542041e..bff86d9 100644 --- a/cmd/skpr/mysql/command.go +++ b/cmd/skpr/mysql/command.go @@ -7,6 +7,7 @@ import ( img "github.com/skpr/cli/cmd/skpr/mysql/image" "github.com/skpr/cli/cmd/skpr/mysql/restore" skprcommand "github.com/skpr/cli/internal/command" + "github.com/skpr/cli/internal/docker" ) var ( @@ -14,7 +15,7 @@ var ( ) // NewCommand creates a new cobra.Command for 'mysql' sub command -func NewCommand() *cobra.Command { +func NewCommand(clientId docker.DockerClientId) *cobra.Command { cmd := &cobra.Command{ Use: "mysql", DisableFlagsInUseLine: true, @@ -23,7 +24,7 @@ func NewCommand() *cobra.Command { GroupID: skprcommand.GroupDataStorage, } - cmd.AddCommand(img.NewCommand()) + cmd.AddCommand(img.NewCommand(clientId)) cmd.AddCommand(backup.NewCommand()) cmd.AddCommand(restore.NewCommand()) diff --git a/cmd/skpr/mysql/image/command.go b/cmd/skpr/mysql/image/command.go index 3adf3ad..cd77362 100644 --- a/cmd/skpr/mysql/image/command.go +++ b/cmd/skpr/mysql/image/command.go @@ -6,6 +6,7 @@ import ( "github.com/skpr/cli/cmd/skpr/mysql/image/create" "github.com/skpr/cli/cmd/skpr/mysql/image/list" "github.com/skpr/cli/cmd/skpr/mysql/image/pull" + "github.com/skpr/cli/internal/docker" ) var ( @@ -20,7 +21,7 @@ var ( ) // NewCommand creates a new cobra.Command for 'image' sub command -func NewCommand() *cobra.Command { +func NewCommand(clientId docker.DockerClientId) *cobra.Command { cmd := &cobra.Command{ Use: "image", DisableFlagsInUseLine: true, @@ -31,7 +32,7 @@ func NewCommand() *cobra.Command { cmd.AddCommand(create.NewCommand()) cmd.AddCommand(list.NewCommand()) - cmd.AddCommand(pull.NewCommand()) + cmd.AddCommand(pull.NewCommand(clientId)) return cmd } diff --git a/cmd/skpr/mysql/image/pull/command.go b/cmd/skpr/mysql/image/pull/command.go index 1f994c7..e4a82e7 100644 --- a/cmd/skpr/mysql/image/pull/command.go +++ b/cmd/skpr/mysql/image/pull/command.go @@ -4,6 +4,7 @@ import ( "github.com/spf13/cobra" v1pull "github.com/skpr/cli/internal/command/mysql/image/pull" + "github.com/skpr/cli/internal/docker" ) var ( @@ -15,7 +16,7 @@ var ( ) // NewCommand creates a new cobra.Command for 'list' sub command -func NewCommand() *cobra.Command { +func NewCommand(clientId docker.DockerClientId) *cobra.Command { command := v1pull.Command{} cmd := &cobra.Command{ @@ -27,6 +28,7 @@ func NewCommand() *cobra.Command { Example: cmdExample, RunE: func(cmd *cobra.Command, args []string) error { command.Params.Environment = args[0] + command.ClientId = clientId if len(args) > 1 { command.Params.Databases = args[1:] diff --git a/cmd/skpr/package/command.go b/cmd/skpr/package/command.go index 5057969..d69bd72 100644 --- a/cmd/skpr/package/command.go +++ b/cmd/skpr/package/command.go @@ -5,6 +5,7 @@ import ( skprcommand "github.com/skpr/cli/internal/command" v1package "github.com/skpr/cli/internal/command/package" + "github.com/skpr/cli/internal/docker" ) var ( @@ -23,7 +24,7 @@ var ( ) // NewCommand creates a new cobra.Command for 'package' sub command -func NewCommand() *cobra.Command { +func NewCommand(clientId docker.DockerClientId) *cobra.Command { command := v1package.Command{} cmd := &cobra.Command{ @@ -37,6 +38,7 @@ func NewCommand() *cobra.Command { GroupID: skprcommand.GroupLifecycle, RunE: func(cmd *cobra.Command, args []string) error { command.Params.Version = args[0] + command.ClientId = clientId return command.Run(cmd.Context()) }, } @@ -47,8 +49,11 @@ func NewCommand() *cobra.Command { cmd.Flags().BoolVar(&command.PrintManifest, "print-manifest", command.PrintManifest, "Print the manifest to stdout.") cmd.Flags().StringVar(&command.PackageDir, "dir", ".skpr/package", "The location of the package directory.") cmd.Flags().StringSliceVar(&command.BuildArgs, "build-arg", []string{}, "Additional build arguments.") - cmd.Flags().StringVar(&command.Platform, "platform", "linux/amd64", "The platform to build for.") cmd.Flags().BoolVar(&command.Debug, "debug", command.Debug, "Enable debug output.") + if clientId == docker.DockerClientIdDocker { + cmd.Flags().StringVar(&command.Params.IgnoreFile, "ignore-file", ".dockerignore", "A file containing patterns to exclude from the build context.") + } + return cmd } diff --git a/go.mod b/go.mod index d74b376..accd3b4 100644 --- a/go.mod +++ b/go.mod @@ -6,14 +6,16 @@ require ( charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 github.com/aquasecurity/table v1.11.0 - github.com/aws/aws-sdk-go-v2 v1.40.1 - github.com/aws/aws-sdk-go-v2/config v1.32.3 - github.com/aws/aws-sdk-go-v2/credentials v1.19.3 - github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.15 - github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.16 - github.com/aws/aws-sdk-go-v2/service/ecr v1.54.2 + github.com/aws/aws-sdk-go-v2 v1.41.0 + github.com/aws/aws-sdk-go-v2/config v1.32.5 + github.com/aws/aws-sdk-go-v2/credentials v1.19.5 + github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.16 + github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.17 + github.com/aws/aws-sdk-go-v2/service/ecr v1.54.4 github.com/charmbracelet/bubbletea v1.3.10 github.com/charmbracelet/fang v0.4.4 + github.com/containerd/errdefs v1.0.0 + github.com/docker/docker v28.5.2+incompatible github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca github.com/fatih/color v1.18.0 github.com/fsouza/go-dockerclient v1.12.3 @@ -24,7 +26,9 @@ require ( github.com/jinzhu/now v1.1.5 github.com/jwalton/gchalk v1.3.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d - github.com/notaryproject/notation-go v1.3.2 + github.com/moby/go-archive v0.1.0 + github.com/moby/moby/api v1.52.0 + github.com/moby/patternmatcher v0.6.0 github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 github.com/pkg/errors v0.9.1 github.com/skpr/api v1.3.2 @@ -33,36 +37,36 @@ require ( github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 - golang.org/x/crypto v0.45.0 - golang.org/x/oauth2 v0.33.0 + golang.org/x/crypto v0.46.0 + golang.org/x/oauth2 v0.34.0 golang.org/x/sync v0.19.0 - golang.org/x/term v0.37.0 + golang.org/x/term v0.38.0 google.golang.org/grpc v1.77.0 gopkg.in/yaml.v2 v2.4.0 - oras.land/oras-go/v2 v2.6.0 ) require ( - github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/atotto/clipboard v0.1.4 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 // indirect - github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 // indirect + github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 // indirect github.com/aws/smithy-go v1.24.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbles v0.21.0 // indirect - github.com/charmbracelet/colorprofile v0.4.0 // indirect + github.com/charmbracelet/colorprofile v0.4.1 // indirect github.com/charmbracelet/lipgloss v1.1.0 // indirect - github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692 // indirect - github.com/charmbracelet/x/ansi v0.11.2 // indirect + github.com/charmbracelet/ultraviolet v0.0.0-20251215102126-8518113293e1 // indirect + github.com/charmbracelet/x/ansi v0.11.3 // indirect github.com/charmbracelet/x/cellbuf v0.0.14 // indirect github.com/charmbracelet/x/exp/charmtone v0.0.0-20251215102626-e0db08df7383 // indirect github.com/charmbracelet/x/exp/golden v0.0.0-20251215102626-e0db08df7383 // indirect @@ -72,29 +76,32 @@ require ( github.com/clipperhouse/displaywidth v0.6.2 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/docker/docker v28.5.2+incompatible // indirect - github.com/docker/go-connections v0.4.0 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-connections v0.6.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jwalton/go-supportscolor v1.2.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.2 // indirect github.com/lucasb-eyer/go-colorful v1.3.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/go-archive v0.1.0 // indirect - github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/user v0.4.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect - github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect - github.com/morikuni/aec v1.0.0 // indirect + github.com/moby/term v0.5.2 // indirect + github.com/morikuni/aec v1.1.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/mango v0.2.0 // indirect @@ -110,10 +117,17 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect - golang.org/x/net v0.47.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + golang.org/x/net v0.48.0 // indirect golang.org/x/sys v0.39.0 // indirect golang.org/x/text v0.32.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect - google.golang.org/protobuf v1.36.10 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 // indirect + google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 46ed177..0159980 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410 h1:D9PbaszZYp charm.land/lipgloss/v2 v2.0.0-beta.3.0.20251106193318-19329a3e8410/go.mod h1:1qZyvvVCenJO2M1ac2mX0yyiIZJoZmDM4DG4s0udJkU= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= +github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/TylerBrock/colorjson v0.0.0-20200706003622-8a50f05110d2 h1:ZBbLwSJqkHBuFDA6DUhhse0IGJ7T5bemHyNILUjvOq4= @@ -12,58 +12,62 @@ github.com/aquasecurity/table v1.11.0 h1:SzgCAv7dZcv/gyAyzxorS6OgEk7w/WU5iT2pStI github.com/aquasecurity/table v1.11.0/go.mod h1:eqOmvjjB7AhXFgFqpJUEE/ietg7RrMSJZXyTN8E/wZw= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aws/aws-sdk-go-v2 v1.40.1 h1:difXb4maDZkRH0x//Qkwcfpdg1XQVXEAEs2DdXldFFc= -github.com/aws/aws-sdk-go-v2 v1.40.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= -github.com/aws/aws-sdk-go-v2/config v1.32.3 h1:cpz7H2uMNTDa0h/5CYL5dLUEzPSLo2g0NkbxTRJtSSU= -github.com/aws/aws-sdk-go-v2/config v1.32.3/go.mod h1:srtPKaJJe3McW6T/+GMBZyIPc+SeqJsNPJsd4mOYZ6s= -github.com/aws/aws-sdk-go-v2/credentials v1.19.3 h1:01Ym72hK43hjwDeJUfi1l2oYLXBAOR8gNSZNmXmvuas= -github.com/aws/aws-sdk-go-v2/credentials v1.19.3/go.mod h1:55nWF/Sr9Zvls0bGnWkRxUdhzKqj9uRNlPvgV1vgxKc= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15 h1:utxLraaifrSBkeyII9mIbVwXXWrZdlPO7FIKmyLCEcY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.15/go.mod h1:hW6zjYUDQwfz3icf4g2O41PHi77u10oAzJ84iSzR/lo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15 h1:Y5YXgygXwDI5P4RkteB5yF7v35neH7LfJKBG+hzIons= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.15/go.mod h1:K+/1EpG42dFSY7CBj+Fruzm8PsCGWTXJ3jdeJ659oGQ= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15 h1:AvltKnW9ewxX2hFmQS0FyJH93aSvJVUEFvXfU+HWtSE= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.15/go.mod h1:3I4oCdZdmgrREhU74qS1dK9yZ62yumob+58AbFR4cQA= +github.com/aws/aws-sdk-go-v2 v1.41.0 h1:tNvqh1s+v0vFYdA1xq0aOJH+Y5cRyZ5upu6roPgPKd4= +github.com/aws/aws-sdk-go-v2 v1.41.0/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0= +github.com/aws/aws-sdk-go-v2/config v1.32.5 h1:pz3duhAfUgnxbtVhIK39PGF/AHYyrzGEyRD9Og0QrE8= +github.com/aws/aws-sdk-go-v2/config v1.32.5/go.mod h1:xmDjzSUs/d0BB7ClzYPAZMmgQdrodNjPPhd6bGASwoE= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5 h1:xMo63RlqP3ZZydpJDMBsH9uJ10hgHYfQFIk1cHDXrR4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.5/go.mod h1:hhbH6oRcou+LpXfA/0vPElh/e0M3aFeOblE1sssAAEk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16 h1:80+uETIWS1BqjnN9uJ0dBUaETh+P1XwFy5vwHwK5r9k= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16/go.mod h1:wOOsYuxYuB/7FlnVtzeBYRcjSRtQpAW0hCP7tIULMwo= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16 h1:rgGwPzb82iBYSvHMHXc8h9mRoOUBZIGFgKb9qniaZZc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.16/go.mod h1:L/UxsGeKpGoIj6DxfhOWHWQ/kGKcd4I1VncE4++IyKA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16 h1:1jtGzuV7c82xnqOVfx2F0xmJcOw5374L7N6juGW6x6U= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.16/go.mod h1:M2E5OQf+XLe+SZGmmpaI2yy+J326aFf6/+54PoxSANc= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= -github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.15 h1:1dVctbat9Rie0Aw9QjcDr/dPfDzR3r1B/NmMAuL0+VI= -github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.15/go.mod h1:1eKTGnKGdPdif+PFUn874AO6UHCxjnNXM8HQZUOz1Pw= -github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.16 h1:h0aEsnYFvKVHhCjxJ2hYVMiwWdHb9z2/H7KTwSPSPqM= -github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.16/go.mod h1:ZFDOOw1B3HGqicw7n/SXqMZT/zwLpjk56OczL5GW7xI= -github.com/aws/aws-sdk-go-v2/service/ecr v1.54.2 h1:2Mdcg3Rphkj48toLpLrckQ9T0ce08GrMfaL0l2anzLY= -github.com/aws/aws-sdk-go-v2/service/ecr v1.54.2/go.mod h1:gwUKatMqrynzKA8L0MDAlvAvGB7LIzmTm6uFRqD+4CU= +github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.16 h1:SEjKS691gctDMITrxLPxovXxEE4CXb7yOqfjgG10X8g= +github.com/aws/aws-sdk-go-v2/service/cognitoidentity v1.33.16/go.mod h1:SJttU0860z9g/ZFlAGI1vV4w44NLO6PKDHO3jFu3Jd4= +github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.17 h1:kYAxFlyBhmhdjel6MNFf5lYQlTcMUOXPC33mor8rFz0= +github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider v1.57.17/go.mod h1:NSRHRisUPKx5y8RD+HpeCjIn8SYz5m6HhNGkd0GLB1o= +github.com/aws/aws-sdk-go-v2/service/ecr v1.54.4 h1:4THfkydiKvFeOFlfY1ABHe4Nsj+Jy6S6tHBqUAojY0M= +github.com/aws/aws-sdk-go-v2/service/ecr v1.54.4/go.mod h1:8n8vVvu7LzveA0or4iWQwNndJStpKOX4HiVHM5jax2U= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15 h1:3/u/4yZOffg5jdNk1sDpOQ4Y+R6Xbh+GzpDrSZjuy3U= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.15/go.mod h1:4Zkjq0FKjE78NKjabuM4tRXKFzUJWXgP0ItEZK8l7JU= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.3 h1:d/6xOGIllc/XW1lzG9a4AUBMmpLA9PXcQnVPTuHHcik= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.3/go.mod h1:fQ7E7Qj9GiW8y0ClD7cUJk3Bz5Iw8wZkWDHsTe8vDKs= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.6 h1:8sTTiw+9yuNXcfWeqKF2x01GqCF49CpP4Z9nKrrk/ts= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.6/go.mod h1:8WYg+Y40Sn3X2hioaaWAAIngndR8n1XFdRPPX+7QBaM= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11 h1:E+KqWoVsSrj1tJ6I/fjDIu5xoS2Zacuu1zT+H7KtiIk= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.11/go.mod h1:qyWHz+4lvkXcr3+PoGlGHEI+3DLLiU6/GdrFfMaAhB0= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.3 h1:tzMkjh0yTChUqJDgGkcDdxvZDSrJ/WB6R6ymI5ehqJI= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.3/go.mod h1:T270C0R5sZNLbWUe8ueiAF42XSZxxPocTaGSgs5c/60= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16 h1:oHjJHeUy0ImIV0bsrX0X91GkV5nJAyv1l1CC9lnO0TI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.16/go.mod h1:iRSNGgOYmiYwSCXxXaKb9HfOEj40+oTKn8pTxMlYkRM= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4 h1:HpI7aMmJ+mm1wkSHIA2t5EaFFv5EFYXePW30p1EIrbQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.4/go.mod h1:C5RdGMYGlfM0gYq/tifqgn4EbyX99V15P2V3R+VHbQU= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7 h1:eYnlt6QxnFINKzwxP5/Ucs1vkG7VT3Iezmvfgc2waUw= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.7/go.mod h1:+fWt2UHSb4kS7Pu8y+BMBvJF0EWx+4H0hzNwtDNRTrg= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12 h1:AHDr0DaHIAo8c9t1emrzAlVDFp+iMMKnPdYy6XO4MCE= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12/go.mod h1:GQ73XawFFiWxyWXMHWfhiomvP3tXtdNar/fi8z18sx0= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5 h1:SciGFVNZ4mHdm7gpD1dgZYnCuVdX1s+lFTg4+4DOy70= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.5/go.mod h1:iW40X4QBmUxdP+fZNOpfmkdMZqsovezbAeO+Ubiv2pk= github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk= github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.3.1 h1:LV+qyBQ2pqe0u42ZsUEtPiCaUoqgA9gYRDs3vj1nolY= github.com/aymanbagabas/go-udiff v0.3.1/go.mod h1:G0fsKmG+P6ylD0r6N/KgQD/nWzgfnl8ZBcNLgcbrw8E= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw= github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4= -github.com/charmbracelet/colorprofile v0.4.0 h1:OyLBfRcKbGa/a92Qh7jQQmoToGYqM1vmq0rdVm2Qwh4= -github.com/charmbracelet/colorprofile v0.4.0/go.mod h1:o630BguBH8XPdFI/NcDGPCt5vmitfqYQSCJ1s1mi5Oc= +github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk= +github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk= github.com/charmbracelet/fang v0.4.4 h1:G4qKxF6or/eTPgmAolwPuRNyuci3hTUGGX1rj1YkHJY= github.com/charmbracelet/fang v0.4.4/go.mod h1:P5/DNb9DddQ0Z0dbc0P3ol4/ix5Po7Ofr2KMBfAqoCo= 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/ultraviolet v0.0.0-20251106190538-99ea45596692 h1:r/3jQZ1LjWW6ybp8HHfhrKrwHIWiJhUuY7wwYIWZulQ= -github.com/charmbracelet/ultraviolet v0.0.0-20251106190538-99ea45596692/go.mod h1:Y8B4DzWeTb0ama8l3+KyopZtkE8fZjwRQ3aEAPEXHE0= -github.com/charmbracelet/x/ansi v0.11.2 h1:XAG3FSjiVtFvgEgGrNBkCNNYrsucAt8c6bfxHyROLLs= -github.com/charmbracelet/x/ansi v0.11.2/go.mod h1:9tY2bzX5SiJCU0iWyskjBeI2BRQfvPqI+J760Mjf+Rg= +github.com/charmbracelet/ultraviolet v0.0.0-20251215102126-8518113293e1 h1:0uVEMbe5L80n1fbNAz+WSO9mEWIau6iEo6umDMstzis= +github.com/charmbracelet/ultraviolet v0.0.0-20251215102126-8518113293e1/go.mod h1:Ns3cOzzY9hEFFeGxB6VpfgRnqOJZJFhQAPfRxPqflQs= +github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI= +github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI= github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4= github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA= github.com/charmbracelet/x/exp/charmtone v0.0.0-20251215102626-e0db08df7383 h1:xGojlO6kHCDB1k6DolME79LG0u90TzVd8atGhmxFRIo= @@ -82,19 +86,25 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= -github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 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/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/egym-playground/go-prefix-writer v0.0.0-20180609083313-7326ea162eca h1:sWNMfkKG8GW1pGUyNlbsWq6f04pFgcsomY+Fly8XdB4= @@ -103,8 +113,11 @@ github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6 github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= 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/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsouza/go-dockerclient v1.12.3 h1:CEsX4/msyMEekHAR9Pf8XniZBtwGo0Kl+mLPQ/AnSys= github.com/fsouza/go-dockerclient v1.12.3/go.mod h1:gl0t2KUfrsLbm4tw5/ySsJkkFpi7Fz9gXzY2BKLEvZA= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -113,8 +126,6 @@ github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -125,6 +136,8 @@ github.com/gosuri/uilive v0.0.4 h1:hUEBpQDj8D8jXgtCdBu7sWsy5sbW/5GhuO8KBwJ2jyY= github.com/gosuri/uilive v0.0.4/go.mod h1:V/epo5LjjlDE5RJUcqx8dbw+zc93y5Ya3yg8tfZ74VI= github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f h1:7LYC+Yfkj3CTRcShK0KOL/w6iTiKyqqBA9a41Wnggw8= github.com/hokaccha/go-prettyjson v0.0.0-20211117102719-0474bc63780f/go.mod h1:pFlLw2CfqZiIBOx6BuCeRLCrfxBJipTY0nIOF/VbGcI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -136,15 +149,18 @@ github.com/jwalton/gchalk v1.3.0/go.mod h1:ytRlj60R9f7r53IAElbpq4lVuPOPNg2J4tJcC github.com/jwalton/go-supportscolor v1.1.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= github.com/jwalton/go-supportscolor v1.2.0 h1:g6Ha4u7Vm3LIsQ5wmeBpS4gazu0UP1DRDE8y6bre4H8= github.com/jwalton/go-supportscolor v1.2.0/go.mod h1:hFVUAZV2cWg+WFFC4v8pT2X/S2qUUBYMioBD9AINXGs= -github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk= +github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= @@ -157,18 +173,22 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= +github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= -github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= +github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= +github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= +github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -183,19 +203,12 @@ github.com/muesli/roff v0.1.0 h1:YD0lalCotmYuF5HhZliKWlIx7IEhiXeSfq7hNjFqGF8= github.com/muesli/roff v0.1.0/go.mod h1:pjAHQM9hdUUwm/krAfrLGgJkXJ+YuhtsfZ42kieB2Ig= 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/notaryproject/notation-core-go v1.3.0 h1:mWJaw1QBpBxpjLSiKOjzbZvB+xh2Abzk14FHWQ+9Kfs= -github.com/notaryproject/notation-core-go v1.3.0/go.mod h1:hzvEOit5lXfNATGNBT8UQRx2J6Fiw/dq/78TQL8aE64= -github.com/notaryproject/notation-go v1.3.2 h1:4223iLXOHhEV7ZPzIUJEwwMkhlgzoYFCsMJvSH1Chb8= -github.com/notaryproject/notation-go v1.3.2/go.mod h1:/1kuq5WuLF6Gaer5re0Z6HlkQRlKYO4EbWWT/L7J1Uw= -github.com/notaryproject/tspclient-go v1.0.0 h1:AwQ4x0gX8IHnyiZB1tggpn5NFqHpTEm1SDX8YNv4Dg4= -github.com/notaryproject/tspclient-go v1.0.0/go.mod h1:LGyA/6Kwd2FlM0uk8Vc5il3j0CddbWSHBj/4kxQDbjs= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0 h1:LiZB1h0GIcudcDci2bxbqI6DXV8bF8POAnArqvRrIyw= github.com/olekukonko/ts v0.0.0-20171002115256-78ecb04241c0/go.mod h1:F/7q8/HZz+TXjlsoZQQKVYvXTZaFH4QRa3y+j1p7MS0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -203,6 +216,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 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.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= @@ -216,7 +231,6 @@ github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EE github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= @@ -230,31 +244,35 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4= +go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -262,36 +280,34 @@ golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/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-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= -oras.land/oras-go/v2 v2.6.0 h1:X4ELRsiGkrbeox69+9tzTu492FMUu7zJQW6eJU+I2oc= -oras.land/oras-go/v2 v2.6.0/go.mod h1:magiQDfG6H1O9APp+rOsvCPcW1GD2MM7vgnKY0Y+u1o= diff --git a/internal/auth/auth.go b/internal/auth/auth.go new file mode 100644 index 0000000..d94d348 --- /dev/null +++ b/internal/auth/auth.go @@ -0,0 +1,7 @@ +package auth + +// Auth is a consistent object for docker authentication (package, pull) as each client does it differently. +type Auth struct { + Username string + Password string +} diff --git a/internal/buildpack/builder.go b/internal/buildpack/builder.go new file mode 100644 index 0000000..e77a644 --- /dev/null +++ b/internal/buildpack/builder.go @@ -0,0 +1,172 @@ +package buildpack + +import ( + "context" + "fmt" + "os" + "time" + + "github.com/moby/patternmatcher/ignorefile" + "golang.org/x/sync/errgroup" + + "github.com/skpr/cli/internal/buildpack/utils/finder" + "github.com/skpr/cli/internal/buildpack/utils/image" + "github.com/skpr/cli/internal/buildpack/utils/prefixer" + "github.com/skpr/cli/internal/docker" +) + +// Builder is the docker image builder. +type Builder struct { + Client docker.DockerClient +} + +// NewBuilder creates a new Builder. +func NewBuilder(c docker.DockerClient) (*Builder, error) { + return &Builder{ + Client: c, + }, nil +} + +// Build the images. +func (b *Builder) Build(ctx context.Context, dockerfiles finder.Dockerfiles, params Params) (BuildResponse, error) { + var resp BuildResponse + + excludePatterns, err := loadIgnoreFilePatterns(params.IgnoreFile) + if err != nil { + return resp, fmt.Errorf("failed to parse ignore file: %w", err) + } + + compileDockerfile, ok := dockerfiles[ImageNameCompile] + if !ok { + return resp, fmt.Errorf("%q is a required dockerfile", ImageNameCompile) + } + + // Build the build args. + buildArgs := params.BuildArgs + buildArgs[BuildArgVersion] = params.Version + + start := time.Now() + + // Build the compile image first; it's the base for others. + compileRef := image.Name(params.Registry, image.Tag(params.Version, ImageNameCompile)) + + fmt.Fprintf(params.Writer, "Building image: %s\n", compileRef) + + localOut := prefixer.WrapWriterWithPrefixer(params.Writer, ImageNameCompile, start) + err = b.Client.BuildImage(ctx, compileDockerfile, params.Context, compileRef, excludePatterns, buildArgs, localOut) + if err != nil { + return resp, err + } + + fmt.Fprintf(params.Writer, "Built %s image in %s\n", compileRef, time.Since(start).Round(time.Second)) + + // Remove compile from list of dockerfiles. + delete(dockerfiles, ImageNameCompile) + + // Adds compile image identifier to the runtime images as an arg. + buildArgs[BuildArgCompileImage] = compileRef + + // Prepare runtime builds. + type pendingBuild struct { + name string + imageRef string + dockerfile string + } + var builds []pendingBuild + for imageName, dockerfile := range dockerfiles { + ref := image.Name(params.Registry, image.Tag(params.Version, imageName)) + builds = append(builds, pendingBuild{name: imageName, imageRef: ref, dockerfile: dockerfile}) + resp.Images = append(resp.Images, Image{ + Name: imageName, + Tag: ref, + }) + } + + // Parallel runtime builds. + bg, ctx := errgroup.WithContext(ctx) + for _, pb := range builds { + pb := pb + + fmt.Fprintf(params.Writer, "Building image: %s\n", pb.imageRef) + + localStart := time.Now() + localOut := prefixer.WrapWriterWithPrefixer(params.Writer, pb.name, localStart) + bg.Go(func() error { + err := b.Client.BuildImage(ctx, pb.dockerfile, params.Context, pb.imageRef, excludePatterns, buildArgs, localOut) + if err != nil { + return err + } + + fmt.Fprintf(params.Writer, "Built %s image in %s\n", pb.imageRef, time.Since(start).Round(time.Second)) + return nil + }) + } + if err := bg.Wait(); err != nil { + return resp, err + } + + if params.NoPush { + return resp, nil + } + + // Prepare pushes (skip compile). + type pendingPush struct { + Name string + Registry string + Tag string + } + var pushes []pendingPush + for _, buildImage := range resp.Images { + pushes = append(pushes, pendingPush{ + Name: buildImage.Name, + Registry: params.Registry, + Tag: image.Tag(params.Version, buildImage.Name), + }) + } + + // Parallel pushes. + pg, ctx := errgroup.WithContext(context.TODO()) + for _, p := range pushes { + p := p + + fmt.Fprintf(params.Writer, "Pushing image: %s:%s\n", p.Registry, p.Tag) + out := prefixer.WrapWriterWithPrefixer(params.Writer, "push "+p.Name, start) + + pg.Go(func() error { + localStart := time.Now() + err := b.Client.PushImage(ctx, p.Registry, p.Tag, out) + if err != nil { + return err + } + + fmt.Fprintf(params.Writer, "Pushed %s:%s image in %s\n", p.Registry, p.Tag, time.Since(localStart).Round(time.Second)) + return nil + }) + } + if err := pg.Wait(); err != nil { + return resp, err + } + + fmt.Fprintf(params.Writer, "Build complete in: %s\n", time.Since(start).Round(time.Second)) + return resp, nil +} + +// Loads and returns a list of ignore file patterns from the specified file. +func loadIgnoreFilePatterns(filePath string) ([]string, error) { + f, err := os.Open(filePath) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + + return nil, fmt.Errorf("unable to open ignore file: %w", err) + } + defer f.Close() + + patterns, err := ignorefile.ReadAll(f) + if err != nil { + return nil, fmt.Errorf("error parsing ignore file: %w", err) + } + + return patterns, nil +} diff --git a/internal/buildpack/builder/builder.go b/internal/buildpack/builder/builder.go deleted file mode 100644 index 54d28a5..0000000 --- a/internal/buildpack/builder/builder.go +++ /dev/null @@ -1,230 +0,0 @@ -package buildpack - -import ( - "context" - "fmt" - "io" - "strings" - "time" - - "github.com/egym-playground/go-prefix-writer/prefixer" - docker "github.com/fsouza/go-dockerclient" - "golang.org/x/sync/errgroup" - - "github.com/skpr/cli/internal/buildpack/utils/image" - "github.com/skpr/cli/internal/color" -) - -// DockerClientInterface provides an interface that allows us to test the builder. -type DockerClientInterface interface { - BuildImage(options docker.BuildImageOptions) error - PushImage(options docker.PushImageOptions, auth docker.AuthConfiguration) error -} - -// Builder is the docker image builder. -type Builder struct { - dockerClient DockerClientInterface -} - -// BuildResponse is returned by the build operation. -type BuildResponse struct { - Images []Image `json:"images"` -} - -// Image build has been built. -type Image struct { - // Name of the image. - Name string `json:"name"` - // Tag used to push image. - Tag string `json:"tag"` -} - -// Params used for building the applications. -type Params struct { - Auth docker.AuthConfiguration - Writer io.Writer - Context string - Registry string - NoPush bool - Version string - BuildArgs map[string]string - Platform string -} - -// Dockerfiles the docker build files. -type Dockerfiles map[string]string - -const ( - // ImageNameCompile is used for compiling the application. - ImageNameCompile = "compile" - - // BuildArgCompileImage is used for referencing the compile image. - BuildArgCompileImage = "COMPILE_IMAGE" - // BuildArgVersion is used for providing the version identifier of the application. - BuildArgVersion = "SKPR_VERSION" -) - -// NewBuilder creates a new Builder. -func NewBuilder(dockerClient DockerClientInterface) *Builder { - return &Builder{ - dockerClient: dockerClient, - } -} - -// Build the images. -func (b *Builder) Build(dockerfiles Dockerfiles, params Params) (BuildResponse, error) { - var resp BuildResponse - - compileDockerfile, ok := dockerfiles[ImageNameCompile] - if !ok { - return resp, fmt.Errorf("%q is a required dockerfile", ImageNameCompile) - } - - args := []docker.BuildArg{ - { - Name: BuildArgVersion, - Value: params.Version, - }, - } - - for k, v := range params.BuildArgs { - args = append(args, docker.BuildArg{ - Name: k, - Value: v, - }) - } - - start := time.Now() - - // We build the compile image first, as it is the base image for other images. - compileBuild := docker.BuildImageOptions{ - Name: image.Name(params.Registry, params.Version, ImageNameCompile), - Dockerfile: compileDockerfile, - ContextDir: params.Context, - OutputStream: prefixWithTime(params.Writer, ImageNameCompile, start), - BuildArgs: args, - Platform: params.Platform, - } - - // We need to build the 'compile' image first. - fmt.Fprintf(params.Writer, "Building image: %s\n", compileBuild.Name) - err := b.dockerClient.BuildImage(compileBuild) - if err != nil { - return resp, err - } - fmt.Fprintf(params.Writer, "Built compile image in %s\n", time.Since(start).Round(time.Second)) - - // Remove compile from list of dockerfiles. - delete(dockerfiles, ImageNameCompile) - - // Adds compile image identifier to the runtime images as an arg. - // That allows runtime images to copy over the compiled code. - args = append(args, docker.BuildArg{ - Name: BuildArgCompileImage, - Value: image.Name(params.Registry, params.Version, ImageNameCompile), - }) - - var builds []docker.BuildImageOptions - - for imageName, dockerfile := range dockerfiles { - build := docker.BuildImageOptions{ - Name: image.Name(params.Registry, params.Version, imageName), - Dockerfile: dockerfile, - ContextDir: params.Context, - OutputStream: prefixWithTime(params.Writer, imageName, start), - BuildArgs: args, - Platform: params.Platform, - } - - // Add to the builder list. - builds = append(builds, build) - - // Add to the manifest. - resp.Images = append(resp.Images, Image{ - Name: imageName, - Tag: build.Name, - }) - } - - bg, ctx := errgroup.WithContext(context.Background()) - - for _, build := range builds { - // https://golang.org/doc/faq#closures_and_goroutines - build := build - - // Allows us to cancel build executions. - build.Context = ctx - - fmt.Fprintf(params.Writer, "Building image: %s\n", build.Name) - - bg.Go(func() error { - start = time.Now() - - err := b.dockerClient.BuildImage(build) - if err != nil { - return err - } - - fmt.Fprintf(params.Writer, "Built %s image in %s\n", build.Name, time.Since(start).Round(time.Second)) - - return nil - }) - } - - err = bg.Wait() - if err != nil { - return resp, err - } - - if params.NoPush { - return resp, nil - } - - var pushes []docker.PushImageOptions - - for _, buildImage := range resp.Images { - pushes = append(pushes, docker.PushImageOptions{ - Name: params.Registry, - Tag: image.Tag(params.Version, buildImage.Name), - }) - } - - pg, ctx := errgroup.WithContext(context.Background()) - - for _, push := range pushes { - // https://golang.org/doc/faq#closures_and_goroutines - push := push - - // Allows us to cancel push executions. - push.Context = ctx - - fmt.Fprintf(params.Writer, "Pushing image: %s:%s\n", push.Name, push.Tag) - - pg.Go(func() error { - start = time.Now() - - err = b.dockerClient.PushImage(push, params.Auth) - if err != nil { - return err - } - - fmt.Fprintf(params.Writer, "Pushed %s:%s image in %s\n", push.Name, push.Tag, time.Since(start).Round(time.Second)) - - return nil - }) - } - - err = pg.Wait() - if err != nil { - return resp, err - } - - fmt.Fprintf(params.Writer, "Build complete in: %s\n", time.Since(start).Round(time.Second)) - - return resp, nil -} - -// Helper function to prefix all output for a stream. -func prefixWithTime(w io.Writer, name string, start time.Time) io.Writer { - return prefixer.New(w, newPrefixer(color.Wrap(strings.ToUpper(name)), start).PrefixFunc()) -} diff --git a/internal/buildpack/builder/mock/client.go b/internal/buildpack/builder/mock/client.go deleted file mode 100644 index dc3072f..0000000 --- a/internal/buildpack/builder/mock/client.go +++ /dev/null @@ -1,50 +0,0 @@ -package mock - -import ( - "sync" - - docker "github.com/fsouza/go-dockerclient" -) - -// DockerClient provides a mock docker client. -type DockerClient struct { - BuildWg sync.WaitGroup - PushWg sync.WaitGroup - buildNum int - pushNum int -} - -// BuildImage implements the interface. -func (c *DockerClient) BuildImage(options docker.BuildImageOptions) error { - c.BuildWg.Done() - c.buildNum++ - return nil -} - -// PushImage implements the interface. -func (c *DockerClient) PushImage(options docker.PushImageOptions, auth docker.AuthConfiguration) error { - c.PushWg.Done() - c.pushNum++ - return nil -} - -// InspectImage implements the interface. -func (c *DockerClient) InspectImage(name string) (*docker.Image, error) { - return &docker.Image{ - RepoDigests: []string{ - "sha@111222333444555666", - }, - }, nil -} - -// BuildCount returns the build count. -func (c *DockerClient) BuildCount() int { - c.BuildWg.Wait() - return c.buildNum -} - -// PushCount returns the push count. -func (c *DockerClient) PushCount() int { - c.PushWg.Wait() - return c.pushNum -} diff --git a/internal/buildpack/builder/builder_test.go b/internal/buildpack/builder_test.go similarity index 61% rename from internal/buildpack/builder/builder_test.go rename to internal/buildpack/builder_test.go index f376ae9..9cf61c9 100644 --- a/internal/buildpack/builder/builder_test.go +++ b/internal/buildpack/builder_test.go @@ -2,21 +2,21 @@ package buildpack import ( "bytes" + "context" "testing" - docker "github.com/fsouza/go-dockerclient" "github.com/stretchr/testify/assert" - "github.com/skpr/cli/internal/buildpack/builder/mock" + "github.com/skpr/cli/internal/auth" + "github.com/skpr/cli/internal/buildpack/utils/finder" + "github.com/skpr/cli/internal/docker/mockclient" ) func TestBuild(t *testing.T) { - dockerClient := &mock.DockerClient{} - dockerClient.BuildWg.Add(4) - dockerClient.PushWg.Add(3) + dockerClient := mockclient.New() - dockerFiles := make(Dockerfiles) + dockerFiles := make(finder.Dockerfiles) dockerFiles["compile"] = ".skpr/package/compile/Dockerfile" dockerFiles["cli"] = ".skpr/package/cli/Dockerfile" dockerFiles["app"] = ".skpr/package/app/Dockerfile" @@ -25,16 +25,19 @@ func TestBuild(t *testing.T) { var b bytes.Buffer params := Params{ - Writer: &b, - Registry: "foo", - Version: "222", - Context: "bar", - NoPush: false, - Auth: docker.AuthConfiguration{}, + Writer: &b, + Registry: "foo", + Version: "222", + Context: "bar", + NoPush: false, + BuildArgs: map[string]string{}, + Auth: auth.Auth{}, } - builder := NewBuilder(dockerClient) - have, err := builder.Build(dockerFiles, params) + builder, err := NewBuilder(dockerClient) + assert.NoError(t, err) + + have, err := builder.Build(context.TODO(), dockerFiles, params) assert.NoError(t, err) want := BuildResponse{ diff --git a/internal/buildpack/types.go b/internal/buildpack/types.go new file mode 100644 index 0000000..53e55cb --- /dev/null +++ b/internal/buildpack/types.go @@ -0,0 +1,42 @@ +package buildpack + +import ( + "io" + + "github.com/skpr/cli/internal/auth" +) + +// BuildResponse is returned by the build operation. +type BuildResponse struct { + Images []Image `json:"images"` +} + +// Image build has been built. +type Image struct { + // Name of the image. + Name string `json:"name"` + // Tag used to push image. + Tag string `json:"tag"` +} + +// Params used for building the applications. +type Params struct { + Auth auth.Auth + Writer io.Writer + Context string + IgnoreFile string + Registry string + NoPush bool + Version string + BuildArgs map[string]string +} + +const ( + // ImageNameCompile is used for compiling the application. + ImageNameCompile = "compile" + + // BuildArgCompileImage is used for referencing the compile image. + BuildArgCompileImage = "COMPILE_IMAGE" + // BuildArgVersion is used for providing the version identifier of the application. + BuildArgVersion = "SKPR_VERSION" +) diff --git a/internal/buildpack/utils/aws/ecr/ecr.go b/internal/buildpack/utils/aws/ecr/ecr.go index 79e0b7a..57b9f22 100644 --- a/internal/buildpack/utils/aws/ecr/ecr.go +++ b/internal/buildpack/utils/aws/ecr/ecr.go @@ -8,9 +8,9 @@ import ( "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/ecr" - docker "github.com/fsouza/go-dockerclient" "github.com/pkg/errors" + "github.com/skpr/cli/internal/auth" skprcredentials "github.com/skpr/cli/internal/client/credentials" ) @@ -25,8 +25,8 @@ func IsRegistry(registry string) bool { // UpgradeAuth to use an AWS IAM token for authentication.. // https://docs.aws.amazon.com/cli/latest/reference/ecr/get-login.html -func UpgradeAuth(ctx context.Context, url string, creds skprcredentials.Credentials) (docker.AuthConfiguration, error) { - var auth docker.AuthConfiguration +func UpgradeAuth(ctx context.Context, url string, creds skprcredentials.Credentials) (auth.Auth, error) { + var auth auth.Auth region, err := extractRegionFromURL(url) if err != nil { diff --git a/internal/buildpack/utils/finder/finder.go b/internal/buildpack/utils/finder/finder.go index 31e7cc9..89754b3 100644 --- a/internal/buildpack/utils/finder/finder.go +++ b/internal/buildpack/utils/finder/finder.go @@ -5,16 +5,17 @@ import ( "os" "path/filepath" "strings" - - "github.com/skpr/cli/internal/buildpack/builder" ) +// Dockerfiles the dockerclient build files. +type Dockerfiles map[string]string + // dockerfileSuffix is the suffix for legacy dockerfiles. const dockerfileSuffix = ".dockerfile" // FindDockerfiles finds the dockerfiles in the specified directory. -func FindDockerfiles(packageDir string) (buildpack.Dockerfiles, error) { - dockerfiles := make(buildpack.Dockerfiles) +func FindDockerfiles(packageDir string) (Dockerfiles, error) { + dockerfiles := make(Dockerfiles) err := filepath.Walk(packageDir, func(path string, info os.FileInfo, err error) error { if err != nil { return fmt.Errorf("failed accessing path %q: %w", path, err) @@ -34,6 +35,14 @@ func FindDockerfiles(packageDir string) (buildpack.Dockerfiles, error) { } return nil }) + + // Print deprecation notice. + for key, path := range dockerfiles { + if strings.HasSuffix(path, ".dockerfile") { + fmt.Printf("[DEPRECATED] Dockerfile location %q is deprecated. Use \"%s/%s/Dockerfile\" instead.\n", path, filepath.Dir(path), key) + } + } + if err != nil { return dockerfiles, err } diff --git a/internal/buildpack/utils/image/build.go b/internal/buildpack/utils/image/build.go index a217691..8181119 100644 --- a/internal/buildpack/utils/image/build.go +++ b/internal/buildpack/utils/image/build.go @@ -5,8 +5,8 @@ import ( ) // Name of the Docker image. -func Name(registry, version, suffix string) string { - return fmt.Sprintf("%s:%s", registry, Tag(version, suffix)) +func Name(registry, tag string) string { + return fmt.Sprintf("%s:%s", registry, tag) } // Tag assigned to a Docker image. diff --git a/internal/buildpack/utils/notation/utils/ecr.go b/internal/buildpack/utils/notation/utils/ecr.go deleted file mode 100644 index 3c769f3..0000000 --- a/internal/buildpack/utils/notation/utils/ecr.go +++ /dev/null @@ -1,47 +0,0 @@ -package utils - -import ( - "fmt" - - notationregistry "github.com/notaryproject/notation-go/registry" - "oras.land/oras-go/v2/registry" - "oras.land/oras-go/v2/registry/remote" - "oras.land/oras-go/v2/registry/remote/auth" -) - -// GetNotationRepository creates notationregistry.Repository required to access artifacts in Amazon ECR for sign and verify operations. -// Inspired by https://github.com/aws/aws-signer-notation-plugin/blob/6e26cfd7711ad49b52c439e46bd1050c95865846/examples/utils/ecr.go -func GetNotationRepository(repository, token string) (notationregistry.Repository, error) { - ref, err := registry.ParseReference(repository) - if err != nil { - return nil, fmt.Errorf("failed to parse reference: %w", err) - } - - if err := ref.ValidateReferenceAsDigest(); err != nil { - return nil, fmt.Errorf("invalid reference: %w", err) - } - - authClient := &auth.Client{ - Credential: auth.StaticCredential(ref.Host(), auth.Credential{ - Username: "AWS", - Password: token, - }), - ClientID: "skpr", - } - - authClient.SetUserAgent("skpr") - - remoteRepo := &remote.Repository{ - Client: authClient, - Reference: ref, - } - - err = remoteRepo.SetReferrersCapability(false) - if err != nil { - return nil, err - } - - notationRepo := notationregistry.NewRepository(remoteRepo) - - return notationRepo, nil -} diff --git a/internal/buildpack/builder/prefixer.go b/internal/buildpack/utils/prefixer/prefixer.go similarity index 50% rename from internal/buildpack/builder/prefixer.go rename to internal/buildpack/utils/prefixer/prefixer.go index e784016..84cc22c 100644 --- a/internal/buildpack/builder/prefixer.go +++ b/internal/buildpack/utils/prefixer/prefixer.go @@ -1,8 +1,14 @@ -package buildpack +package prefixer import ( "fmt" + "io" + "strings" "time" + + "github.com/egym-playground/go-prefix-writer/prefixer" + + "github.com/skpr/cli/internal/color" ) // Prefixer for printing our build prefix. @@ -11,14 +17,18 @@ type Prefixer struct { start time.Time } -// New prefixer for printing out build prefix. -func newPrefixer(name string, start time.Time) Prefixer { +// NewPrefixer for printing out build prefix. +func NewPrefixer(name string, start time.Time) Prefixer { return Prefixer{ name: name, start: start, } } +func WrapWriterWithPrefixer(w io.Writer, name string, start time.Time) io.Writer { + return prefixer.New(w, NewPrefixer(color.Wrap(strings.ToUpper(name)), start).PrefixFunc()) +} + // PrefixFunc for dynamically calculating the prefix based on time since start. func (p Prefixer) PrefixFunc() func() string { return func() string { diff --git a/internal/client/config/user/config.go b/internal/client/config/user/config.go index 7b0badb..4962be3 100644 --- a/internal/client/config/user/config.go +++ b/internal/client/config/user/config.go @@ -6,6 +6,8 @@ import ( "path/filepath" "gopkg.in/yaml.v2" + + "github.com/skpr/cli/internal/docker" ) // Config represents the persistent user configuration. @@ -16,7 +18,8 @@ type Config struct { // ConfigExperimental holds experimental feature flags. type ConfigExperimental struct { - Trace bool `yaml:"trace,omitempty"` + Trace bool `yaml:"trace,omitempty"` + DockerClient docker.DockerClientId `yaml:"docker-client,omitempty"` } // Aliases maps alias names to commands. diff --git a/internal/command/mysql/image/pull/command.go b/internal/command/mysql/image/pull/command.go index 86b3234..bdf7380 100644 --- a/internal/command/mysql/image/pull/command.go +++ b/internal/command/mysql/image/pull/command.go @@ -6,13 +6,14 @@ import ( "log/slog" "os" - docker "github.com/fsouza/go-dockerclient" "github.com/gosuri/uilive" "github.com/pkg/errors" "github.com/skpr/api/pb" + "github.com/skpr/cli/internal/auth" "github.com/skpr/cli/internal/buildpack/utils/aws/ecr" "github.com/skpr/cli/internal/client" + "github.com/skpr/cli/internal/docker" skprlog "github.com/skpr/cli/internal/log" ) @@ -23,7 +24,8 @@ const ( // Command to pull a database image. type Command struct { - Params Params + Params Params + ClientId docker.DockerClientId } // Params provided to this command. @@ -56,7 +58,7 @@ func (cmd *Command) Run(ctx context.Context) error { return fmt.Errorf("failed to get repository: %w", err) } - auth := docker.AuthConfiguration{ + auth := auth.Auth{ Username: client.Credentials.Username, Password: client.Credentials.Password, } @@ -69,13 +71,14 @@ func (cmd *Command) Run(ctx context.Context) error { } } - dockerclient, err := docker.NewClientFromEnv() + c, err := docker.NewClientFromUserConfig(auth, cmd.ClientId) if err != nil { - return errors.Wrap(err, "failed to setup Docker client") + return errors.Wrap(err, "failed to create Docker client") } writer := uilive.New() writer.Start() + defer writer.Stop() for _, database := range cmd.Params.Databases { tag := fmt.Sprintf("%s-%s", database, DefaultTagSuffix) @@ -85,48 +88,39 @@ func (cmd *Command) Run(ctx context.Context) error { logger.Info(fmt.Sprintf("Pulling: %s", imageName)) // Lookup the ID of the current image so we can delete it after we pull the image one. - cleanup, err := dockerclient.InspectImage(imageName) - if err != nil && !errors.Is(err, docker.ErrNoSuchImage) { + cleanupId, err := c.ImageId(context.TODO(), imageName) + if err != nil { return err } - opts := docker.PullImageOptions{ - OutputStream: writer, - Repository: getRepositoryResp.Repository, - Tag: tag, - } - - err = dockerclient.PullImage(opts, auth) + err = c.PullImage(context.TODO(), getRepositoryResp.Repository, tag, writer) if err != nil { return err } - // Check if there was an old images which - if cleanup == nil { - continue - } - - current, err := dockerclient.InspectImage(imageName) + currentId, err := c.ImageId(context.TODO(), imageName) if err != nil { return err } - // Don't cleanup the old image if it was the latest and never needed to be updated. - if cleanup.ID == current.ID { + if cleanupId == currentId { + logger.Info(fmt.Sprintf("Image is up to date: %s", imageName)) + } else { + logger.Info(fmt.Sprintf("Successfully pulled image: %s", imageName)) + } + + // If it's a fresh image or the same image as the current one, skip deleting it. + if cleanupId == "" || cleanupId == currentId { continue } - logger.Info(fmt.Sprintf("Cleaning up old image with: %s", cleanup.ID)) + logger.Info(fmt.Sprintf("Cleaning up old image with: %s", cleanupId)) - err = dockerclient.RemoveImage(cleanup.ID) + err = c.RemoveImage(context.TODO(), cleanupId) if err != nil { return err } - - logger.Info(fmt.Sprintf("Successfully pulled image: %s:%s", getRepositoryResp.Repository, tag)) } - writer.Stop() - return nil } diff --git a/internal/command/package/command.go b/internal/command/package/command.go index d40a0ed..88df5b4 100644 --- a/internal/command/package/command.go +++ b/internal/command/package/command.go @@ -5,17 +5,16 @@ import ( "encoding/json" "fmt" "os" - "path/filepath" - "strings" - docker "github.com/fsouza/go-dockerclient" "github.com/skpr/api/pb" - buildpack "github.com/skpr/cli/internal/buildpack/builder" + "github.com/skpr/cli/internal/auth" + "github.com/skpr/cli/internal/buildpack" "github.com/skpr/cli/internal/buildpack/utils/aws/ecr" "github.com/skpr/cli/internal/buildpack/utils/finder" "github.com/skpr/cli/internal/client" "github.com/skpr/cli/internal/client/config" + "github.com/skpr/cli/internal/docker" "github.com/skpr/cli/internal/slice" ) @@ -26,8 +25,8 @@ type Command struct { Params buildpack.Params PrintManifest bool BuildArgs []string - Platform string Debug bool + ClientId docker.DockerClientId } // Run the command. @@ -39,6 +38,8 @@ func (cmd *Command) Run(ctx context.Context) error { } cmd.Params.Registry = fmt.Sprintf("localhost/skpr/%s", config.Project) + + cmd.Params.Auth = auth.Auth{} } else { ctx, client, err := client.New(ctx) if err != nil { @@ -63,12 +64,11 @@ func (cmd *Command) Run(ctx context.Context) error { cmd.Params.Registry = project.Registry.Application - cmd.Params.Auth = docker.AuthConfiguration{ + cmd.Params.Auth = auth.Auth{ Username: client.Credentials.Username, Password: client.Credentials.Password, } - // @todo, Consider abstracting this if another registry + credentials pair is required. if ecr.IsRegistry(cmd.Params.Registry) { auth, err := ecr.UpgradeAuth(ctx, cmd.Params.Registry, client.Credentials) if err != nil { @@ -80,7 +80,6 @@ func (cmd *Command) Run(ctx context.Context) error { } cmd.Params.Writer = os.Stderr - cmd.Params.Platform = cmd.Platform // Convert build args from slice to map. // eg. --build-arg=KEY=VALUE to map[string]string{"KEY": "VALUE"} @@ -98,21 +97,17 @@ func (cmd *Command) Run(ctx context.Context) error { } } - // Print deprecation notice. - for key, path := range dockerfiles { - if strings.HasSuffix(path, ".dockerfile") { - fmt.Printf("[DEPRECATED] Dockerfile location %q is deprecated. Use \"%s/%s/Dockerfile\" instead.\n", path, filepath.Dir(path), key) - } + dc, err := docker.NewClientFromUserConfig(cmd.Params.Auth, cmd.ClientId) + if err != nil { + return err } - dockerclient, err := docker.NewClientFromEnv() + builder, err := buildpack.NewBuilder(dc) if err != nil { - return fmt.Errorf("failed to setup Docker client: %w", err) + return err } - builder := buildpack.NewBuilder(dockerclient) - - resp, err := builder.Build(dockerfiles, cmd.Params) + resp, err := builder.Build(ctx, dockerfiles, cmd.Params) if err != nil { return err } diff --git a/internal/docker/dockerclient/auth.go b/internal/docker/dockerclient/auth.go new file mode 100644 index 0000000..77cb6c9 --- /dev/null +++ b/internal/docker/dockerclient/auth.go @@ -0,0 +1,17 @@ +package dockerclient + +import ( + "encoding/base64" + "encoding/json" + + "github.com/docker/docker/api/types/registry" +) + +// EncodeRegistryAuth converts a registry.AuthConfig into the base64-encoded JSON expected by the Docker Engine API. +func EncodeRegistryAuth(cfg registry.AuthConfig) (string, error) { + b, err := json.Marshal(cfg) + if err != nil { + return "", err + } + return base64.URLEncoding.EncodeToString(b), nil +} diff --git a/internal/docker/dockerclient/client.go b/internal/docker/dockerclient/client.go new file mode 100644 index 0000000..b878115 --- /dev/null +++ b/internal/docker/dockerclient/client.go @@ -0,0 +1,180 @@ +package dockerclient + +import ( + "context" + "encoding/json" + "fmt" + "io" + + "github.com/containerd/errdefs" + "github.com/docker/docker/api/types/build" + imagetypes "github.com/docker/docker/api/types/image" + dockregistry "github.com/docker/docker/api/types/registry" + dockclient "github.com/docker/docker/client" + "github.com/moby/go-archive" + "github.com/moby/moby/api/types/jsonstream" + "github.com/pkg/errors" + + "github.com/skpr/cli/internal/auth" + "github.com/skpr/cli/internal/buildpack/utils/image" +) + +type Client struct { + Auth auth.Auth + Client *dockclient.Client +} + +func New(auth auth.Auth) (*Client, error) { + client, err := dockclient.NewClientWithOpts(dockclient.FromEnv, dockclient.WithAPIVersionNegotiation()) + if err != nil { + return nil, errors.Wrap(err, "failed to setup Docker client") + } + + return &Client{ + Auth: auth, + Client: client, + }, nil +} + +func (c *Client) ImageId(ctx context.Context, name string) (string, error) { + resp, err := c.Client.ImageInspect(ctx, name) + if err == nil { + return resp.ID, nil + } else if !errdefs.IsNotFound(err) { + return "", err + } + + return "", nil +} + +func (c *Client) PullImage(ctx context.Context, registry, tag string, writer io.Writer) error { + auth := dockregistry.AuthConfig{ + Username: c.Auth.Username, + Password: c.Auth.Password, + } + authHdr, err := EncodeRegistryAuth(auth) + if err != nil { + return errors.Wrap(err, "failed to encode registry auth") + } + + imageName := image.Name(registry, tag) + + rc, err := c.Client.ImagePull(ctx, imageName, imagetypes.PullOptions{ + RegistryAuth: authHdr, + }) + if err != nil { + return err + } + defer rc.Close() + + err = handleMessages(rc, writer) + if err != nil { + return err + } + + return nil +} + +func (c *Client) PushImage(ctx context.Context, registry, tag string, writer io.Writer) error { + auth := dockregistry.AuthConfig{ + Username: c.Auth.Username, + Password: c.Auth.Password, + } + authHdr, err := EncodeRegistryAuth(auth) + if err != nil { + return errors.Wrap(err, "failed to encode registry auth") + } + + imageName := image.Name(registry, tag) + + rc, err := c.Client.ImagePush(ctx, imageName, imagetypes.PushOptions{ + RegistryAuth: authHdr, + }) + if err != nil { + return err + } + defer rc.Close() + + err = handleMessages(rc, writer) + if err != nil { + return err + } + + return nil +} + +func (c *Client) RemoveImage(ctx context.Context, id string) error { + _, err := c.Client.ImageRemove(ctx, id, imagetypes.RemoveOptions{ + PruneChildren: true, + Force: false, + }) + if err != nil && !errdefs.IsNotFound(err) { + return err + } + return nil +} + +func (c *Client) BuildImage(ctx context.Context, dockerfile string, dockerContext string, ref string, excludePatterns []string, buildArgs map[string]string, writer io.Writer) error { + buildCtx, err := archive.TarWithOptions(dockerContext, &archive.TarOptions{ + ExcludePatterns: excludePatterns, + }) + if err != nil { + return fmt.Errorf("failed to archive build context: %w", err) + } + + // Build args (Docker SDK expects map[string]*string) + args := map[string]*string{} + withArg := func(k, v string) { + val := v + args[k] = &val + } + for k, v := range buildArgs { + withArg(k, v) + } + + opts := build.ImageBuildOptions{ + Tags: []string{ref}, + Dockerfile: dockerfile, + Remove: true, + BuildArgs: args, + } + + resp, err := c.Client.ImageBuild(ctx, buildCtx, opts) + if err != nil { + return err + } + defer resp.Body.Close() + + err = handleMessages(resp.Body, writer) + if err != nil { + return err + } + + return nil +} + +func handleMessages(in io.ReadCloser, out io.Writer) error { + // Stream the daemon's JSON log stream to the provided writer. + decoder := json.NewDecoder(in) + + for { + var msg jsonstream.Message + if err := decoder.Decode(&msg); err == io.EOF { + break + } else if err != nil { + return err + } + + // TODO: Possibly stream out progress. + if msg.Stream != "" { + if _, err := io.WriteString(out, msg.Stream); err != nil { + return err + } + } + if msg.Error != nil { + return msg.Error + } + } + + return nil +} diff --git a/internal/docker/factory.go b/internal/docker/factory.go new file mode 100644 index 0000000..291f7f7 --- /dev/null +++ b/internal/docker/factory.go @@ -0,0 +1,45 @@ +package docker + +import ( + "context" + "fmt" + "io" + + "github.com/skpr/cli/internal/auth" + "github.com/skpr/cli/internal/docker/dockerclient" + "github.com/skpr/cli/internal/docker/goclient" + "github.com/skpr/cli/internal/docker/mockclient" +) + +type DockerClient interface { + ImageId(ctx context.Context, name string) (string, error) + PullImage(ctx context.Context, registry, tag string, writer io.Writer) error + PushImage(ctx context.Context, registry, tag string, writer io.Writer) error + RemoveImage(ctx context.Context, id string) error + BuildImage(ctx context.Context, dockerfile string, dockerContext string, ref string, excludePatterns []string, buildArgs map[string]string, writer io.Writer) error +} + +type DockerClientId string + +const ( + DockerClientIdLegacy DockerClientId = "legacy" + DockerClientIdDocker DockerClientId = "docker" + DockerClientIdMock DockerClientId = "mock" +) + +func NewClientFromUserConfig(auth auth.Auth, clientId DockerClientId) (DockerClient, error) { + switch clientId { + case DockerClientIdLegacy: + return goclient.New(auth) + case DockerClientIdDocker: + return dockerclient.New(auth) + case DockerClientIdMock: + return mockclient.New(), nil + } + + if clientId != "" { + return nil, fmt.Errorf("unknown docker client: %s", clientId) + } + + return goclient.New(auth) +} diff --git a/internal/docker/goclient/client.go b/internal/docker/goclient/client.go new file mode 100644 index 0000000..44f0d44 --- /dev/null +++ b/internal/docker/goclient/client.go @@ -0,0 +1,98 @@ +package goclient + +import ( + "context" + "io" + + dockerclient "github.com/fsouza/go-dockerclient" + "github.com/pkg/errors" + + "github.com/skpr/cli/internal/auth" +) + +type Client struct { + Auth auth.Auth + Client *dockerclient.Client +} + +func New(auth auth.Auth) (*Client, error) { + client, err := dockerclient.NewClientFromEnv() + if err != nil { + return nil, errors.Wrap(err, "failed to setup Docker client") + } + + return &Client{ + Auth: auth, + Client: client, + }, nil +} + +func (c *Client) ImageId(ctx context.Context, name string) (string, error) { + resp, err := c.Client.InspectImage(name) + if err != nil && !errors.Is(err, dockerclient.ErrNoSuchImage) { + return "", err + } + + if resp == nil { + return "", nil + } + + return resp.ID, nil +} + +func (c *Client) PullImage(ctx context.Context, registry, tag string, writer io.Writer) error { + opts := dockerclient.PullImageOptions{ + OutputStream: writer, + Repository: registry, + Tag: tag, + Context: ctx, + } + + clientAuth := dockerclient.AuthConfiguration{ + Username: c.Auth.Username, + Password: c.Auth.Password, + } + + return c.Client.PullImage(opts, clientAuth) +} + +func (c *Client) PushImage(ctx context.Context, registry, tag string, writer io.Writer) error { + opts := dockerclient.PushImageOptions{ + Context: ctx, + OutputStream: writer, + Name: registry, + Tag: tag, + } + + clientAuth := dockerclient.AuthConfiguration{ + Username: c.Auth.Username, + Password: c.Auth.Password, + } + + return c.Client.PushImage(opts, clientAuth) +} + +func (c *Client) RemoveImage(ctx context.Context, id string) error { + err := c.Client.RemoveImage(id) + return err +} + +func (c *Client) BuildImage(ctx context.Context, dockerfile string, dockerContext string, ref string, excludePatterns []string, buildArgs map[string]string, writer io.Writer) error { + args := []dockerclient.BuildArg{} + for k, v := range buildArgs { + args = append(args, dockerclient.BuildArg{ + Name: k, + Value: v, + }) + } + + build := dockerclient.BuildImageOptions{ + Context: ctx, + Name: ref, + Dockerfile: dockerfile, + ContextDir: dockerContext, + OutputStream: writer, + BuildArgs: args, + } + return c.Client.BuildImage(build) +} diff --git a/internal/docker/mockclient/client.go b/internal/docker/mockclient/client.go new file mode 100644 index 0000000..28fd809 --- /dev/null +++ b/internal/docker/mockclient/client.go @@ -0,0 +1,51 @@ +package mockclient + +import ( + "context" + "io" +) + +// DockerClient provides a mock client. +type DockerClient struct { + buildNum int + pushNum int +} + +func New() *DockerClient { + return &DockerClient{ + buildNum: 0, + pushNum: 0, + } +} + +func (c *DockerClient) ImageId(ctx context.Context, name string) (string, error) { + return "sha@111222333444555666", nil +} + +func (c *DockerClient) PullImage(ctx context.Context, registry, tag string, writer io.Writer) error { + return nil +} + +func (c *DockerClient) PushImage(ctx context.Context, registry, tag string, writer io.Writer) error { + c.pushNum++ + return nil +} + +func (c *DockerClient) RemoveImage(ctx context.Context, id string) error { + return nil +} + +func (c *DockerClient) BuildImage(ctx context.Context, dockerfile string, dockerContext string, ref string, excludePatterns []string, buildArgs map[string]string, writer io.Writer) error { + c.buildNum++ + return nil +} + +// BuildCount returns the build count. +func (c *DockerClient) BuildCount() int { + return c.buildNum +} + +// PushCount returns the push count. +func (c *DockerClient) PushCount() int { + return c.pushNum +}