diff --git a/cmd/all.go b/cmd/all.go index e30def3b..e0d98346 100644 --- a/cmd/all.go +++ b/cmd/all.go @@ -10,7 +10,8 @@ import ( var allCmd = &cobra.Command{ Use: "all", Short: "Run sync and render", - Long: "Run sync and render", + Long: "Run sync and render for specified environments and applications", + Args: cobra.RangeArgs(0, 2), Annotations: map[string]string{ ANNOTATION_SMART_MODE: ANNOTATION_TRUE, }, diff --git a/cmd/render.go b/cmd/render.go index 9dde34a0..57e1dd99 100644 --- a/cmd/render.go +++ b/cmd/render.go @@ -10,7 +10,7 @@ import ( var renderCmd = &cobra.Command{ Use: "render", Short: "Render manifests", - Long: "Render manifests", + Long: "Render manifests for specified environments and applications", Annotations: map[string]string{ ANNOTATION_SMART_MODE: ANNOTATION_TRUE, }, diff --git a/cmd/root.go b/cmd/root.go index 61bbc46b..3e965c6c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -27,38 +27,97 @@ var ( asyncLevel int ) +// Use this template for commands that accept environment and application arguments +const envAppCommandUsageTemplate = `Usage: + {{.CommandPath}} [environments [applications]] [flags] + +Arguments: + 0. When no arguments are provided, myks uses the Smart Mode to determine the environments and applications to process. + In Smart Mode, myks relies on git to only processes applications with changes. + + 1. environments (Optional) Comma-separated list of environments or ALL + ALL will process all environments + Examples: ALL + prod,stage,dev + prod/region1,stage/region1 + dev + + 2. applications (Optional) Comma-separated list of applications or ALL + ALL will process all applications + Example: app1,app2 or ALL + +{{if .HasAvailableFlags}}Flags: +{{.Flags.FlagUsages | trimTrailingWhitespaces}}{{end}} + +Examples: + # Process all apps in production and staging + {{.CommandPath}} prod,stage ALL + + # Process specific apps in all environments + {{.CommandPath}} ALL app1,app2 + + # Process specific apps in specific environments + {{.CommandPath}} prod,stage app1,app2 +` + func NewMyksCmd(version, commit, date string) *cobra.Command { cobra.OnInitialize(initLogger) zerolog.SetGlobalLevel(zerolog.InfoLevel) cmd := newRootCmd(version, commit, date) cmd.AddCommand(allCmd) cmd.AddCommand(renderCmd) + cmd.AddCommand(syncCmd) cmd.AddCommand(newCleanupCmd()) cmd.AddCommand(newInitCmd()) cmd.AddCommand(newPrintConfigCmd()) - cmd.AddCommand(newSyncCmd()) cmd.AddCommand(embedded.EmbeddedCmd("vendir", "Vendir is embedded in myks to manage vendir.yaml files.")) cmd.AddCommand(embedded.EmbeddedCmd("ytt", "Ytt is embedded in myks to manage yaml files.")) initConfig() addPlugins(cmd) + + allCmd.SetUsageTemplate(envAppCommandUsageTemplate) + renderCmd.SetUsageTemplate(envAppCommandUsageTemplate) + syncCmd.SetUsageTemplate(envAppCommandUsageTemplate) + return cmd } func newRootCmd(version, commit, date string) *cobra.Command { rootCmd := &cobra.Command{ Use: "myks", - Short: "Myks helps to manage configuration for kubernetes clusters", - Long: `Myks fetches K8s workloads from a variety of sources, e.g. Helm charts or Git Repositories. It renders their respective yaml files to the file system in a structure of environments and their applications. - - It supports prototype applications that can be shared between environments and inheritance of configuration from parent environments to their "children". - - Myks supports two positional arguments: - - - A comma-separated list of environments to render. If you provide "ALL", all environments will be rendered. - - A comma-separated list of applications to render. If you don't provide this argument or provide "ALL", all applications will be rendered. - - If you do not provide any positional arguments, myks will run in "Smart Mode". In Smart Mode, myks will only render environments and applications that have changed since the last run. - `, + Short: "Myks generates Kubernetes manifests", + // TODO: Launch the documentation website and add a link here + Long: ` +Myks - Kubernetes Manifest Generator + +OVERVIEW + Myks simplifies Kubernetes manifest management through a standardized toolset + and GitOps-ready conventions. + +CORE FEATURES + • External source management (via vendir) + • Helm chart rendering + • YAML templating and validation (via ytt) + • Idempotent output + • Automatic ArgoCD resource generation + • Environment-based configuration inheritance + • Intelligent change detection + +BASIC COMMANDS + init Create a new Myks project in the current directory + sync Download external dependencies + render Generate Kubernetes manifests + all Perform sync and render in one step + +GETTING STARTED + 1. Create a new project: myks init + 2. Download dependencies: myks sync + 3. Generate manifests: myks render + +LEARN MORE + • Use 'myks --help' for detailed information about a command + • Report issues at https://github.com/example/myks/issues +`, } rootCmd.Version = fmt.Sprintf(`%s diff --git a/cmd/sync.go b/cmd/sync.go index 4e1255b5..8f980df1 100644 --- a/cmd/sync.go +++ b/cmd/sync.go @@ -7,36 +7,32 @@ import ( "github.com/mykso/myks/internal/myks" ) -func newSyncCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "sync", - Short: "Sync vendir configs", - Long: `Sync vendir configs. This will run vendir sync for all applications. +var syncCmd = &cobra.Command{ + Use: "sync", + Short: "Download external sources", + Long: `Download external sources for specified environments and applications. Authentication against protected repositories is achieved with environment variables prefixed with "VENDIR_SECRET_". For example, if you reference a secret named "mycreds" in your vendir.yaml, you need to export the variables "VENDIR_SECRET_MYCREDS_USERNAME" and "VENDIR_SECRET_MYCREDS_PASSWORD" in your environment.`, - Annotations: map[string]string{ - ANNOTATION_SMART_MODE: ANNOTATION_TRUE, - }, - Run: func(cmd *cobra.Command, args []string) { - log.Info().Msg("Syncing vendir configs") - g := myks.New(".") - - if err := g.ValidateRootDir(); err != nil { - log.Fatal().Err(err).Msg("Root directory is not suitable for myks") - } - - if err := g.Init(asyncLevel, envAppMap); err != nil { - log.Fatal().Err(err).Msg("Unable to initialize myks's globe") - } - - if err := g.Sync(asyncLevel); err != nil { - log.Fatal().Err(err).Msg("Unable to sync vendir configs") - } - }, - ValidArgsFunction: shellCompletion, - } - - return cmd + Annotations: map[string]string{ + ANNOTATION_SMART_MODE: ANNOTATION_TRUE, + }, + Run: func(cmd *cobra.Command, args []string) { + log.Info().Msg("Syncing vendir configs") + g := myks.New(".") + + if err := g.ValidateRootDir(); err != nil { + log.Fatal().Err(err).Msg("Root directory is not suitable for myks") + } + + if err := g.Init(asyncLevel, envAppMap); err != nil { + log.Fatal().Err(err).Msg("Unable to initialize myks's globe") + } + + if err := g.Sync(asyncLevel); err != nil { + log.Fatal().Err(err).Msg("Unable to sync vendir configs") + } + }, + ValidArgsFunction: shellCompletion, } diff --git a/go.mod b/go.mod index 19b10bf3..63079029 100644 --- a/go.mod +++ b/go.mod @@ -61,7 +61,6 @@ require ( github.com/dlclark/regexp2 v1.11.0 // indirect github.com/docker/cli v27.1.1+incompatible // indirect github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v25.0.6+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/fatih/color v1.15.0 // indirect github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect diff --git a/go.sum b/go.sum index d0537e85..67cd49ac 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,7 @@ -carvel.dev/imgpkg v0.43.1 h1:AjvY2Rpz7FIRd1jxOHMyAZwAV6MiqKOLyjT+gzhznbI= -carvel.dev/imgpkg v0.43.1/go.mod h1:mLKnJumG8xtUY4ItDHZCj8XFfCwCdIr26pyxSvP3EPo= carvel.dev/imgpkg v0.44.0 h1:5f7LZKn8MALx2xldwNXm5TD6vn9NDQuHJs8Nag2Fr0Y= carvel.dev/imgpkg v0.44.0/go.mod h1:DmoiVp9De4Om38wrc1ooOC12alHytoWd4dWtqYwo4wQ= -carvel.dev/vendir v0.42.0 h1:5uz9rGq7n1ylwDBBB1tYutfimowGBAYKpHMVyxHGH68= -carvel.dev/vendir v0.42.0/go.mod h1:yE0A4PAEM7YSxV8Q0VIlXHNqISOJ/LnqMluMi7tX1C8= carvel.dev/vendir v0.43.0 h1:TEqLRQK4S/4rzyJEKB+ULrkS1BQSGUG5ZucBFCbZFxI= carvel.dev/vendir v0.43.0/go.mod h1:pK7tOW8jwz2CdwL4VirKwu4G7FoOn5HQ38++T0I0E/I= -carvel.dev/ytt v0.51.0 h1:mkH/7+WBqYEEv4MAU5CRmjte82NvSwMiMhF4sHfATlA= -carvel.dev/ytt v0.51.0/go.mod h1:1mkMIMghLgdMp8Lqa0pj92aDyVo3vOd33XplyAxYHM0= carvel.dev/ytt v0.51.1 h1:XWM+9tA+rJnhSel3dJf1jgrCES2wHw68cclkQhZcNRM= carvel.dev/ytt v0.51.1/go.mod h1:bvN7TWCAHhpPgdulWTQ+gi80aR6TOZ1PJPOEHcKtvQY= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -117,14 +111,10 @@ github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TR github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= 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/docker/cli v25.0.6+incompatible h1:F1mCw1kUGixOkM8WQbcG5kniPvP8XCFxreFxl4b/UnY= -github.com/docker/cli v25.0.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v25.0.6+incompatible h1:5cPwbwriIcsua2REJe8HqQV+6WlWc1byg2QSXzBxBGg= -github.com/docker/docker v25.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= @@ -184,8 +174,6 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9/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-containerregistry v0.20.0 h1:wRqHpOeVh3DnenOrPy9xDOLdnLatiGuuNRVelR2gSbg= -github.com/google/go-containerregistry v0.20.0/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= @@ -344,7 +332,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -366,8 +353,6 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -395,8 +380,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= @@ -407,10 +392,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= -golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -441,19 +422,11 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= -golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -461,8 +434,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=