From 321e5687a6349fd6acae53c054e5e95c1eeda977 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Wed, 26 Mar 2025 17:40:35 -0400 Subject: [PATCH 01/24] Rewrote the gh at verify --help text to a) clarify and b) document the verificationResult object. --- pkg/cmd/attestation/verify/verify.go | 115 ++++++++++++++++++--------- 1 file changed, 79 insertions(+), 36 deletions(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 65ae8ca3e..469b2f453 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -30,58 +30,101 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command Verify the integrity and provenance of an artifact using its associated cryptographically signed attestations. - In order to verify an attestation, you must validate the identity of the Actions - workflow that produced the attestation (a.k.a. the signer workflow). Given this - identity, the verification process checks the signatures in the attestations, - and confirms that the attestation refers to provided artifact. + ## Verification - To specify the artifact, the command requires: + In order to verify an attestation, you must provide an artifact and validate: + * the identity of the actor that produced the attestation + * the expected attestation predicate type + + By default, this command enforces the "%[2]s" + predicate type. To verify other attestation predicate types use the + %[1]s--predicate-type%[1]s flag. + + The "actor identity" consists of: + * the repository or the repository owner the artifact is linked with + * the Actions workflow that produced the attestation (a.k.a the + signer workflow) + + This identity is then validated against the attestation's certificate's + SourceRepository, SourceRepositoryOwner, and SubjectAlternativeName + (SAN) fields. + + It is up to you to decide how precisely you want to enforce this identity. + + At a minimum, this command requires either: + * the %[1]s--repo%[1]s flag (e.g. --repo github/example), or + * the %[1]s--owner%[1]s flag (e.g. --owner github) + + Ideally, the path of the signer workflow is also validated using the + %[1]s--signer-workflow%[1]s or %[1]s--cert-identity%[1]s flags. + + Please note: if your attestation was generated via a reusable workflow then + that reusable workflow is the signer whose identity needs to be validated. + In this situation, you must also use either the %[1]s--signer-workflow%[1]s or + the %[1]s--signer-repo%[1]s flag. + + For more options, see the other available flags. + + ## Loading Artifacts And Attestations + + To specify the artifact, this command requires: * a file path to an artifact, or * a container image URI (e.g. %[1]soci://%[1]s) * (note that if you provide an OCI URL, you must already be authenticated with its container registry) - To fetch the attestation, and validate the identity of the signer, the command - requires either: - * the %[1]s--repo%[1]s flag (e.g. --repo github/example). - * the %[1]s--owner%[1]s flag (e.g. --owner github), or + By default, this command will attempt to fetch relevant attestations via the + GitHub API using the values provided to %[1]s--owner%[1]s or %[1]s--repo%[1]s. - The %[1]s--repo%[1]s flag value must match the name of the GitHub repository - that the artifact is linked with. + To instead fetch attestations from your artifact's OCI registry, use the + %[1]s--bundle-from-oci%[1]s flag. - The %[1]s--owner%[1]s flag value must match the name of the GitHub organization - that the artifact's linked repository belongs to. + For offline verification using attestations stored on disk (c.f. the download command) + provide a path to the %[1]s--bundle%[1]s flag. - By default, the verify command will: - - only verify provenance attestations - - attempt to fetch relevant attestations via the GitHub API. + ## Additional Policy Enforcement - To verify other types of attestations, use the %[1]s--predicate-type%[1]s flag. + Given the %[1]s--format=json%[1]s flag, upon successful verification this + command will output a JSON array containing one entry per verified attestation. - To use your artifact's OCI registry instead of GitHub's API, use the - %[1]s--bundle-from-oci%[1]s flag. For offline verification, using attestations - stored on desk (c.f. the download command), provide a path to the %[1]s--bundle%[1]s flag. + This output can then be used for additional policy enforcement, i.e. by being + piped into a policy engine. - To see the full results that are generated upon successful verification, i.e. - for use with a policy engine, provide the %[1]s--format=json%[1]s flag. + Each object in the array contains two properties: + * an %[1]sattestation%[1]s object, which contains the bundle that was verified + * a %[1]sverificationResult%[1]s object, which is a parsed representation of the + contents of the bundle that was verified. - The signer workflow's identity is validated against the Subject Alternative Name (SAN) - within the attestation certificate. Often, the signer workflow is the - same workflow that started the run and generated the attestation, and will be - located inside your repository. For this reason, by default this command uses - either the %[1]s--repo%[1]s or the %[1]s--owner%[1]s flag value to validate the SAN. + Within the %[1]sverificationResult%[1]s object you will find: + * %[1]ssignature.certificate%[1]s, which is a parsed representation of the X.509 + certificate embedded in the attestation, + * %[1]sverifiedTimestamps%[1]s, an array of objects denoting when the attestation + was witnessed by a transparency log or a timestamp authority + * %[1]sstatement%[1]s, which contains the %[1]ssubject%[1]s array referencing artifacts, + the %[1]spredicateType%[1]s field, and the %[1]spredicate%[1]s object which contains + additional, often user-controllable, metadata - However, sometimes the caller workflow is not the same workflow that - performed the signing. If your attestation was generated via a reusable - workflow, then that reusable workflow is the signer whose identity needs to be - validated. In this situation, the signer workflow may or may not be located - inside your %[1]s--repo%[1]s or %[1]s--owner%[1]s. + IMPORTANT: please note that only the %[1]ssignature.certificate%[1]s and the + %[1]sverifiedTimestamps%[1]s properties contain values that cannot be + manipulated by the workflow that originated the attestation. - When using reusable workflows, use the %[1]s--signer-repo%[1]s, %[1]s--signer-workflow%[1]s, - or %[1]s--cert-identity%[1]s flags to validate the signer workflow's identity. + When dealing with attestations created within GitHub Actions, the contents of + %[1]ssignature.certificate%[1]s are populated directly from the OpenID Connect + token that GitHub has generated. The contents of the %[1]sverifiedTimestamps%[1]s + array are populated from the signed timestamps originating from either a + transparency log or a timestamp authority – and likewise cannot be forged by users. - For more policy verification options, see the other available flags. - `, "`"), + When designing policy enforcement using this output, special care must be taken + when examining the contents of the %[1]sstatement.predicate%[1]s property: + should an attacker gain access to your workflow's execution context, they + could then falsify the contents of the %[1]sstatement.predicate%[1]s. + + To mitigate this attack vector, consider using a "trusted builder": when generating + an artifact, have the build and attestation signing occur within a reusable workflow + whose execution cannot be influenced by input provided through the caller workflow. + + See above re: %[1]s--signer-workflow%[1]s. + `, "`", verification.SLSAPredicateV1), Example: heredoc.Doc(` # Verify an artifact linked with a repository $ gh attestation verify example.bin --repo github/example From 33ab0b8f3b3558f2b8134089664bbdc418173bba Mon Sep 17 00:00:00 2001 From: Phill MV Date: Thu, 27 Mar 2025 09:47:11 -0400 Subject: [PATCH 02/24] Tweaked language a bit, improved error message. --- pkg/cmd/attestation/verify/policy.go | 2 +- pkg/cmd/attestation/verify/verify.go | 40 ++++++++++++++++------------ 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/pkg/cmd/attestation/verify/policy.go b/pkg/cmd/attestation/verify/policy.go index 1060a781e..1d1595eca 100644 --- a/pkg/cmd/attestation/verify/policy.go +++ b/pkg/cmd/attestation/verify/policy.go @@ -161,7 +161,7 @@ func validateSignerWorkflow(hostname, signerWorkflow string) (string, error) { // if the provided workflow did not match the expect format // we move onto creating a signer workflow using the provided host name if hostname == "" { - return "", errors.New("unknown host") + return "", errors.New("unknown signer workflow host") } return fmt.Sprintf("^https://%s/%s", hostname, signerWorkflow), nil diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 469b2f453..0acea06c7 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -30,13 +30,16 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command Verify the integrity and provenance of an artifact using its associated cryptographically signed attestations. - ## Verification + ## Understanding Verification + + An attestation is a claim (i.e. a provenance statement) made by an actor + (i.e. a GitHub Actions workflow) regarding a subject (i.e. an artifact). In order to verify an attestation, you must provide an artifact and validate: * the identity of the actor that produced the attestation - * the expected attestation predicate type + * the expected attestation predicate type (the nature of the claim) - By default, this command enforces the "%[2]s" + By default, this command enforces the %[1]s%[2]s%[1]s predicate type. To verify other attestation predicate types use the %[1]s--predicate-type%[1]s flag. @@ -52,8 +55,11 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command It is up to you to decide how precisely you want to enforce this identity. At a minimum, this command requires either: - * the %[1]s--repo%[1]s flag (e.g. --repo github/example), or - * the %[1]s--owner%[1]s flag (e.g. --owner github) + * the %[1]s--owner%[1]s flag (e.g. --owner github), or + * the %[1]s--repo%[1]s flag (e.g. --repo github/example) + + The more precisely you specify the identity, the more control you will + have over the security guarantees offered by the verification process. Ideally, the path of the signer workflow is also validated using the %[1]s--signer-workflow%[1]s or %[1]s--cert-identity%[1]s flags. @@ -224,23 +230,23 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command verifyCmd.Flags().StringVarP(&opts.Repo, "repo", "R", "", "Repository name in the format /") verifyCmd.MarkFlagsMutuallyExclusive("owner", "repo") verifyCmd.MarkFlagsOneRequired("owner", "repo") - verifyCmd.Flags().StringVarP(&opts.PredicateType, "predicate-type", "", verification.SLSAPredicateV1, "Filter attestations by provided predicate type") verifyCmd.Flags().BoolVarP(&opts.NoPublicGood, "no-public-good", "", false, "Do not verify attestations signed with Sigstore public good instance") verifyCmd.Flags().StringVarP(&opts.TrustedRoot, "custom-trusted-root", "", "", "Path to a trusted_root.jsonl file; likely for offline verification") verifyCmd.Flags().IntVarP(&opts.Limit, "limit", "L", api.DefaultLimit, "Maximum number of attestations to fetch") cmdutil.AddFormatFlags(verifyCmd, &opts.exporter) - // policy enforcement flags - verifyCmd.Flags().BoolVarP(&opts.DenySelfHostedRunner, "deny-self-hosted-runners", "", false, "Fail verification for attestations generated on self-hosted runners") - verifyCmd.Flags().StringVarP(&opts.SAN, "cert-identity", "", "", "Enforce that the certificate's subject alternative name matches the provided value exactly") - verifyCmd.Flags().StringVarP(&opts.SANRegex, "cert-identity-regex", "i", "", "Enforce that the certificate's subject alternative name matches the provided regex") - verifyCmd.Flags().StringVarP(&opts.SignerRepo, "signer-repo", "", "", "Repository of reusable workflow that signed attestation in the format /") - verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Workflow that signed attestation in the format [host/]////") - verifyCmd.MarkFlagsMutuallyExclusive("cert-identity", "cert-identity-regex", "signer-repo", "signer-workflow") - verifyCmd.Flags().StringVarP(&opts.OIDCIssuer, "cert-oidc-issuer", "", verification.GitHubOIDCIssuer, "Issuer of the OIDC token") verifyCmd.Flags().StringVarP(&opts.Hostname, "hostname", "", "", "Configure host to use") - verifyCmd.Flags().StringVarP(&opts.SignerDigest, "signer-digest", "", "", "Digest associated with the signer workflow") - verifyCmd.Flags().StringVarP(&opts.SourceRef, "source-ref", "", "", "Ref associated with the source workflow") - verifyCmd.Flags().StringVarP(&opts.SourceDigest, "source-digest", "", "", "Digest associated with the source workflow") + // policy enforcement flags + verifyCmd.Flags().StringVarP(&opts.PredicateType, "predicate-type", "", verification.SLSAPredicateV1, "Enforce that verified attestations' predicate type matches the provided value") + verifyCmd.Flags().BoolVarP(&opts.DenySelfHostedRunner, "deny-self-hosted-runners", "", false, "Fail verification for attestations generated on self-hosted runners") + verifyCmd.Flags().StringVarP(&opts.SAN, "cert-identity", "", "", "Enforce that the certificate's SubjectAlternativeName matches the provided value exactly") + verifyCmd.Flags().StringVarP(&opts.SANRegex, "cert-identity-regex", "i", "", "Enforce that the certificate's SubjectAlternativeName matches the provided regex") + verifyCmd.Flags().StringVarP(&opts.SignerRepo, "signer-repo", "", "", "Enforce that the workflow that signed the attestation's repository matches the provided value (/)") + verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Enforce that the workflow that signed the attestation matches the provided value ([host/]////)") + verifyCmd.MarkFlagsMutuallyExclusive("cert-identity", "cert-identity-regex", "signer-repo", "signer-workflow") + verifyCmd.Flags().StringVarP(&opts.OIDCIssuer, "cert-oidc-issuer", "", verification.GitHubOIDCIssuer, "Enforce that the issuer of the OIDC token matches the provided value") + verifyCmd.Flags().StringVarP(&opts.SignerDigest, "signer-digest", "", "", "Enforce that the digest associated with the signer workflow matches the provided value") + verifyCmd.Flags().StringVarP(&opts.SourceRef, "source-ref", "", "", "Enforce that the git ref associated with the source repository matches the provided value") + verifyCmd.Flags().StringVarP(&opts.SourceDigest, "source-digest", "", "", "Enforce that the digest associated with the source repository matches the provided value") return verifyCmd } From 9c9b158d12f0ae735391373c63d102f2322b3f64 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Thu, 27 Mar 2025 09:55:14 -0400 Subject: [PATCH 03/24] added minor caveat --- pkg/cmd/attestation/verify/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 0acea06c7..34e538612 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -50,7 +50,7 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command This identity is then validated against the attestation's certificate's SourceRepository, SourceRepositoryOwner, and SubjectAlternativeName - (SAN) fields. + (SAN) fields, among others. It is up to you to decide how precisely you want to enforce this identity. From f099a542438bd12b7cdca56b39150bc7226d2438 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Thu, 27 Mar 2025 09:57:00 -0400 Subject: [PATCH 04/24] updated test --- pkg/cmd/attestation/verify/policy_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/policy_test.go b/pkg/cmd/attestation/verify/policy_test.go index d376498b6..ff10cad11 100644 --- a/pkg/cmd/attestation/verify/policy_test.go +++ b/pkg/cmd/attestation/verify/policy_test.go @@ -275,7 +275,7 @@ func TestValidateSignerWorkflow(t *testing.T) { name: "workflow with no host specified", providedSignerWorkflow: "github/artifact-attestations-workflows/.github/workflows/attest.yml", expectErr: true, - errContains: "unknown host", + errContains: "unknown signer workflow host", }, { name: "workflow with default host", From 0427f2688440ef515078a03d81ee64a6d4d9fdd2 Mon Sep 17 00:00:00 2001 From: Phill MV Date: Mon, 31 Mar 2025 11:05:23 -0400 Subject: [PATCH 05/24] Update pkg/cmd/attestation/verify/verify.go Co-authored-by: Meredith Lancaster --- pkg/cmd/attestation/verify/verify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/cmd/attestation/verify/verify.go b/pkg/cmd/attestation/verify/verify.go index 34e538612..3affdfabb 100644 --- a/pkg/cmd/attestation/verify/verify.go +++ b/pkg/cmd/attestation/verify/verify.go @@ -66,7 +66,7 @@ func NewVerifyCmd(f *cmdutil.Factory, runF func(*Options) error) *cobra.Command Please note: if your attestation was generated via a reusable workflow then that reusable workflow is the signer whose identity needs to be validated. - In this situation, you must also use either the %[1]s--signer-workflow%[1]s or + In this situation, you must use either the %[1]s--signer-workflow%[1]s or the %[1]s--signer-repo%[1]s flag. For more options, see the other available flags. From cf9ac4447aee06259acd43786c7f6e20dcd08681 Mon Sep 17 00:00:00 2001 From: William Martin Date: Mon, 31 Mar 2025 14:06:24 +0200 Subject: [PATCH 06/24] Acceptance test issue/pr create/edit with project --- .../issue-create-edit-with-project.txtar | 43 ++++++++++++++++ .../pr/pr-create-edit-with-project.txtar | 51 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 acceptance/testdata/issue/issue-create-edit-with-project.txtar create mode 100644 acceptance/testdata/pr/pr-create-edit-with-project.txtar diff --git a/acceptance/testdata/issue/issue-create-edit-with-project.txtar b/acceptance/testdata/issue/issue-create-edit-with-project.txtar new file mode 100644 index 000000000..568530cb9 --- /dev/null +++ b/acceptance/testdata/issue/issue-create-edit-with-project.txtar @@ -0,0 +1,43 @@ +env REPO=${SCRIPT_NAME}-${RANDOM_STRING} + +# Create a repository with a file so it has a default branch +exec gh repo create ${ORG}/${REPO} --add-readme --private + +# Defer repo cleanup +defer gh repo delete --yes ${ORG}/${REPO} + +# Create a project +env PROJECT_TITLE=${REPO}-project +exec gh project create --owner=${ORG} --title=${PROJECT_TITLE} --format='json' --jq='.number' +stdout2env PROJECT_NUMBER + +defer gh project delete --owner=${ORG} ${PROJECT_NUMBER} + +# Clone the repo +exec gh repo clone ${ORG}/${REPO} + +# Create an issue in the repo +cd ${REPO} +exec gh issue create --title 'Feature Request' --body 'Feature Body' --project ${PROJECT_TITLE} +stdout2env ISSUE_URL + +# Check that default issue view is working +exec gh issue view ${ISSUE_URL} + +# Check the issue was added to the project +exec gh issue view ${ISSUE_URL} --json projectItems --jq '.projectItems[0].title' +stdout ${PROJECT_TITLE} + +# Remove the issue from the project +exec gh issue edit ${ISSUE_URL} --remove-project ${PROJECT_TITLE} + +# Check the issue was removed from the project +exec gh issue view ${ISSUE_URL} --json projectItems --jq '.projectItems[0].title' +! stdout ${PROJECT_TITLE} + +# Re add the issue to the project +exec gh issue edit ${ISSUE_URL} --add-project ${PROJECT_TITLE} + +# Check the issue was added to the project +exec gh issue view ${ISSUE_URL} --json projectItems --jq '.projectItems[0].title' +stdout ${PROJECT_TITLE} diff --git a/acceptance/testdata/pr/pr-create-edit-with-project.txtar b/acceptance/testdata/pr/pr-create-edit-with-project.txtar new file mode 100644 index 000000000..9850313f0 --- /dev/null +++ b/acceptance/testdata/pr/pr-create-edit-with-project.txtar @@ -0,0 +1,51 @@ +# Use gh as a credential helper +exec gh auth setup-git + +env REPO=${SCRIPT_NAME}-${RANDOM_STRING} + +# Create a repository with a file so it has a default branch +exec gh repo create ${ORG}/${REPO} --add-readme --private + +# Defer repo cleanup +defer gh repo delete --yes ${ORG}/${REPO} + +# Create a project +env PROJECT_TITLE=${REPO}-project +exec gh project create --owner=${ORG} --title=${PROJECT_TITLE} --format='json' --jq='.number' +stdout2env PROJECT_NUMBER + +defer gh project delete --owner=${ORG} ${PROJECT_NUMBER} + +# Clone the repo +exec gh repo clone ${ORG}/${REPO} + +# Prepare a branch to PR +cd ${REPO} +exec git checkout -b feature-branch +exec git commit --allow-empty -m 'Empty Commit' +exec git push -u origin feature-branch + +# Create the PR +exec gh pr create --title 'Feature Title' --body 'Feature Body' --project ${PROJECT_TITLE} +stdout2env PR_URL + +# Check that default pr view is working +exec gh pr view ${PR_URL} + +# Check the pr was added to the project +exec gh pr view ${PR_URL} --json projectItems --jq '.projectItems[0].title' +stdout ${PROJECT_TITLE} + +# Remove the pr from the project +exec gh pr edit ${PR_URL} --remove-project ${PROJECT_TITLE} + +# Check the pr was removed from the project +exec gh pr view ${PR_URL} --json projectItems --jq '.projectItems[0].title' +! stdout ${PROJECT_TITLE} + +# Re add the pr to the project +exec gh pr edit ${PR_URL} --add-project ${PROJECT_TITLE} + +# Check the pr was added to the project +exec gh pr view ${PR_URL} --json projectItems --jq '.projectItems[0].title' +stdout ${PROJECT_TITLE} From 346fab212b177386de12beb8a6dc0f7406b96b88 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 11:19:51 -0400 Subject: [PATCH 07/24] Accessible color config boilerplate, colors update This commit is focused on incorporating cli/go-gh accessible colors configuration setting into GitHub CLI experience along with new color function to supersede ColorScheme.Gray and ColorScheme.Grayf. Originally, I was considering having all use of ColorScheme.Gray and ColorScheme.Grayf fallback to the new muted logic if accessible colors were enabled, however I decided not being that it exceeds the acceptance criteria. This means that every command using ColorScheme.Gray needs to be updated to use ColorScheme.Muted --- internal/config/config.go | 18 ++++++++++++++ internal/gh/gh.go | 2 ++ internal/gh/mock/config.go | 44 ++++++++++++++++++++++++++++++++++ pkg/cmd/factory/default.go | 3 +++ pkg/iostreams/color.go | 47 ++++++++++++++++++++++++++++++------- pkg/iostreams/color_test.go | 36 ++++++++++++++-------------- pkg/iostreams/iostreams.go | 15 +++++++++--- 7 files changed, 135 insertions(+), 30 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 476154d66..51116df6c 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,9 +12,11 @@ import ( o "github.com/cli/cli/v2/pkg/option" ghauth "github.com/cli/go-gh/v2/pkg/auth" ghConfig "github.com/cli/go-gh/v2/pkg/config" + xcolor "github.com/cli/go-gh/v2/pkg/x/color" ) const ( + accessibleColorsKey = xcolor.AccessibleColorsSetting aliasesKey = "aliases" browserKey = "browser" editorKey = "editor" @@ -108,6 +110,11 @@ func (c *cfg) Authentication() gh.AuthConfig { return &AuthConfig{cfg: c.cfg} } +func (c *cfg) AccessibleColors(hostname string) gh.ConfigEntry { + // Intentionally panic if there is no user provided value or default value (which would be a programmer error) + return c.GetOrDefault(hostname, accessibleColorsKey).Unwrap() +} + func (c *cfg) Browser(hostname string) gh.ConfigEntry { // Intentionally panic if there is no user provided value or default value (which would be a programmer error) return c.GetOrDefault(hostname, browserKey).Unwrap() @@ -532,6 +539,8 @@ aliases: http_unix_socket: # What web browser gh should use when opening URLs. If blank, will refer to environment. browser: +# Preference for accessible colors that can be customized. This is a global config that cannot be overridden by hostname. Supported values: enabled, disabled +accessible_colors: disabled ` type ConfigOption struct { @@ -602,6 +611,15 @@ var Options = []ConfigOption{ return c.Browser(hostname).Value }, }, + { + Key: accessibleColorsKey, + Description: "toggle preference for accessible colors that can be customized", + DefaultValue: "disabled", + AllowedValues: []string{"enabled", "disabled"}, + CurrentValue: func(c gh.Config, hostname string) string { + return c.AccessibleColors(hostname).Value + }, + }, } func HomeDirPath(subdir string) (string, error) { diff --git a/internal/gh/gh.go b/internal/gh/gh.go index 8e640c41a..4f3ece204 100644 --- a/internal/gh/gh.go +++ b/internal/gh/gh.go @@ -35,6 +35,8 @@ type Config interface { // Set provides primitive access for setting configuration values, optionally scoped by host. Set(hostname string, key string, value string) + // AccessibleColors returns the configured accessible colors preference, optionally scoped by host. + AccessibleColors(hostname string) ConfigEntry // Browser returns the configured browser, optionally scoped by host. Browser(hostname string) ConfigEntry // Editor returns the configured editor, optionally scoped by host. diff --git a/internal/gh/mock/config.go b/internal/gh/mock/config.go index 569af1fac..ecb8814b8 100644 --- a/internal/gh/mock/config.go +++ b/internal/gh/mock/config.go @@ -19,6 +19,9 @@ var _ gh.Config = &ConfigMock{} // // // make and configure a mocked gh.Config // mockedConfig := &ConfigMock{ +// AccessibleColorsFunc: func(hostname string) gh.ConfigEntry { +// panic("mock out the AccessibleColors method") +// }, // AliasesFunc: func() gh.AliasConfig { // panic("mock out the Aliases method") // }, @@ -71,6 +74,9 @@ var _ gh.Config = &ConfigMock{} // // } type ConfigMock struct { + // AccessibleColorsFunc mocks the AccessibleColors method. + AccessibleColorsFunc func(hostname string) gh.ConfigEntry + // AliasesFunc mocks the Aliases method. AliasesFunc func() gh.AliasConfig @@ -118,6 +124,11 @@ type ConfigMock struct { // calls tracks calls to the methods. calls struct { + // AccessibleColors holds details about calls to the AccessibleColors method. + AccessibleColors []struct { + // Hostname is the hostname argument value. + Hostname string + } // Aliases holds details about calls to the Aliases method. Aliases []struct { } @@ -190,6 +201,7 @@ type ConfigMock struct { Write []struct { } } + lockAccessibleColors sync.RWMutex lockAliases sync.RWMutex lockAuthentication sync.RWMutex lockBrowser sync.RWMutex @@ -207,6 +219,38 @@ type ConfigMock struct { lockWrite sync.RWMutex } +// AccessibleColors calls AccessibleColorsFunc. +func (mock *ConfigMock) AccessibleColors(hostname string) gh.ConfigEntry { + if mock.AccessibleColorsFunc == nil { + panic("ConfigMock.AccessibleColorsFunc: method is nil but Config.AccessibleColors was just called") + } + callInfo := struct { + Hostname string + }{ + Hostname: hostname, + } + mock.lockAccessibleColors.Lock() + mock.calls.AccessibleColors = append(mock.calls.AccessibleColors, callInfo) + mock.lockAccessibleColors.Unlock() + return mock.AccessibleColorsFunc(hostname) +} + +// AccessibleColorsCalls gets all the calls that were made to AccessibleColors. +// Check the length with: +// +// len(mockedConfig.AccessibleColorsCalls()) +func (mock *ConfigMock) AccessibleColorsCalls() []struct { + Hostname string +} { + var calls []struct { + Hostname string + } + mock.lockAccessibleColors.RLock() + calls = mock.calls.AccessibleColors + mock.lockAccessibleColors.RUnlock() + return calls +} + // Aliases calls AliasesFunc. func (mock *ConfigMock) Aliases() gh.AliasConfig { if mock.AliasesFunc == nil { diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index 923651487..598be0747 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -19,6 +19,7 @@ import ( "github.com/cli/cli/v2/pkg/cmd/extension" "github.com/cli/cli/v2/pkg/cmdutil" "github.com/cli/cli/v2/pkg/iostreams" + xcolor "github.com/cli/go-gh/v2/pkg/x/color" ) var ssoHeader string @@ -292,6 +293,8 @@ func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams { io.SetPager(pager.Value) } + io.SetAccessibleColorsEnabled(xcolor.IsAccessibleColorsEnabled()) + return io } diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 07fdcd79f..3e2d08ac2 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -30,7 +30,9 @@ var ( greenBold = ansi.ColorFunc("green+b") highlightStart = ansi.ColorCode(highlightStyle) highlight = ansi.ColorFunc(highlightStyle) + darkThemeMuted = ansi.ColorFunc("white+d") darkThemeTableHeader = ansi.ColorFunc("white+du") + lightThemeMuted = ansi.ColorFunc("black+h") lightThemeTableHeader = ansi.ColorFunc("black+hu") noThemeTableHeader = ansi.ColorFunc("default+u") @@ -42,20 +44,22 @@ var ( // NewColorScheme initializes color logic based on provided terminal capabilities. // Logic dealing with terminal theme detected, such as whether color is enabled, 8-bit color supported, true color supported, // and terminal theme detected. -func NewColorScheme(enabled, is256enabled, trueColor bool, theme string) *ColorScheme { +func NewColorScheme(enabled, is256enabled, trueColor, accessibleColors bool, theme string) *ColorScheme { return &ColorScheme{ - enabled: enabled, - is256enabled: is256enabled, - hasTrueColor: trueColor, - theme: theme, + enabled: enabled, + is256enabled: is256enabled, + hasTrueColor: trueColor, + accessibleColors: accessibleColors, + theme: theme, } } type ColorScheme struct { - enabled bool - is256enabled bool - hasTrueColor bool - theme string + enabled bool + is256enabled bool + hasTrueColor bool + accessibleColors bool + theme string } func (c *ColorScheme) Enabled() bool { @@ -73,6 +77,29 @@ func (c *ColorScheme) Boldf(t string, args ...interface{}) string { return c.Bold(fmt.Sprintf(t, args...)) } +func (c *ColorScheme) Muted(t string) string { + // Fallback to previous logic if accessible colors preview is disabled. + if !c.accessibleColors { + return c.Gray(t) + } + + // Muted text is only stylized if color is enabled. + if !c.enabled { + return t + } + + switch c.theme { + case LightTheme: + return lightThemeMuted(t) + default: + return darkThemeMuted(t) + } +} + +func (c *ColorScheme) Mutedf(t string, args ...interface{}) string { + return c.Muted(fmt.Sprintf(t, args...)) +} + func (c *ColorScheme) Red(t string) string { if !c.enabled { return t @@ -113,6 +140,7 @@ func (c *ColorScheme) GreenBold(t string) string { return greenBold(t) } +// Deprecated: Use Muted instead for thematically contrasting color. func (c *ColorScheme) Gray(t string) string { if !c.enabled { return t @@ -123,6 +151,7 @@ func (c *ColorScheme) Gray(t string) string { return gray(t) } +// Deprecated: Use Mutedf instead for thematically contrasting color. func (c *ColorScheme) Grayf(t string, args ...interface{}) string { return c.Gray(fmt.Sprintf(t, args...)) } diff --git a/pkg/iostreams/color_test.go b/pkg/iostreams/color_test.go index b35c2eb73..a399e6d58 100644 --- a/pkg/iostreams/color_test.go +++ b/pkg/iostreams/color_test.go @@ -20,28 +20,28 @@ func TestColorFromRGB(t *testing.T) { hex: "fc0303", text: "red", wants: "\033[38;2;252;3;3mred\033[0m", - cs: NewColorScheme(true, true, true, NoTheme), + cs: NewColorScheme(true, true, true, false, NoTheme), }, { name: "no truecolor", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(true, true, false, NoTheme), + cs: NewColorScheme(true, true, false, false, NoTheme), }, { name: "no color", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, false, NoTheme), }, { name: "invalid hex", hex: "fc0", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, false, NoTheme), }, } @@ -64,28 +64,28 @@ func TestHexToRGB(t *testing.T) { hex: "fc0303", text: "red", wants: "\033[38;2;252;3;3mred\033[0m", - cs: NewColorScheme(true, true, true, NoTheme), + cs: NewColorScheme(true, true, true, false, NoTheme), }, { name: "no truecolor", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(true, true, false, NoTheme), + cs: NewColorScheme(true, true, false, false, NoTheme), }, { name: "no color", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, false, NoTheme), }, { name: "invalid hex", hex: "fc0", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, false, NoTheme), }, } @@ -109,61 +109,61 @@ func TestTableHeader(t *testing.T) { }{ { name: "when color is disabled, text is not stylized", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, true, NoTheme), input: "this should not be stylized", expected: "this should not be stylized", }, { name: "when 4-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, false, false, NoTheme), + cs: NewColorScheme(true, false, false, true, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 4-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, false, false, LightTheme), + cs: NewColorScheme(true, false, false, true, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 4-bit color is enabled and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, false, false, DarkTheme), + cs: NewColorScheme(true, false, false, true, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { name: "when 8-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, false, NoTheme), + cs: NewColorScheme(true, true, false, true, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 8-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, false, LightTheme), + cs: NewColorScheme(true, true, false, true, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 8-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, false, DarkTheme), + cs: NewColorScheme(true, true, false, true, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { name: "when 24-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, true, NoTheme), + cs: NewColorScheme(true, true, true, true, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 24-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, true, LightTheme), + cs: NewColorScheme(true, true, true, true, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 24-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, true, DarkTheme), + cs: NewColorScheme(true, true, true, true, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index 6c12d7911..30981386b 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -70,8 +70,9 @@ type IOStreams struct { stderrTTYOverride bool stderrIsTTY bool - colorOverride bool - colorEnabled bool + colorOverride bool + colorEnabled bool + accessibleColorsEnabled bool pagerCommand string pagerProcess *os.Process @@ -366,7 +367,7 @@ func (s *IOStreams) TerminalWidth() int { } func (s *IOStreams) ColorScheme() *ColorScheme { - return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.TerminalTheme()) + return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.AccessibleColorsEnabled(), s.TerminalTheme()) } func (s *IOStreams) ReadUserFile(fn string) ([]byte, error) { @@ -391,6 +392,14 @@ func (s *IOStreams) TempFile(dir, pattern string) (*os.File, error) { return os.CreateTemp(dir, pattern) } +func (s *IOStreams) SetAccessibleColorsEnabled(enabled bool) { + s.accessibleColorsEnabled = enabled +} + +func (s *IOStreams) AccessibleColorsEnabled() bool { + return s.accessibleColorsEnabled +} + func System() *IOStreams { terminal := ghTerm.FromEnv() From d2cd14b4cdb32d5edf97294dac9a462672450924 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 11:58:30 -0400 Subject: [PATCH 08/24] Remove out of scope changes, update list commands After discussing this with the team, the `gh config` changes to display `accessible_colors` have been removed from this branch being outside of acceptance criteria. This will be moved to a separate issue along with any other work needed to finalize the public preview such as `gh help` entries for `GH_ACCESSIBLE_COLORS` environment variable. List commands that use ColorScheme.Gray have been updated to use ColorScheme.Muted. --- internal/config/config.go | 14 -------------- internal/gh/gh.go | 2 -- pkg/cmd/cache/list/list.go | 9 +++++---- pkg/cmd/codespace/list.go | 4 ++-- pkg/cmd/gist/list/list.go | 2 +- pkg/cmd/gpg-key/list/list.go | 4 ++-- pkg/cmd/pr/list/list.go | 2 +- pkg/cmd/release/list/list.go | 10 +++++----- pkg/cmd/repo/deploy-key/list/list.go | 2 +- pkg/cmd/repo/list/list.go | 4 ++-- pkg/cmd/run/list/list.go | 2 +- pkg/cmd/ssh-key/list/list.go | 4 ++-- 12 files changed, 22 insertions(+), 37 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 51116df6c..a3849b9af 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -110,11 +110,6 @@ func (c *cfg) Authentication() gh.AuthConfig { return &AuthConfig{cfg: c.cfg} } -func (c *cfg) AccessibleColors(hostname string) gh.ConfigEntry { - // Intentionally panic if there is no user provided value or default value (which would be a programmer error) - return c.GetOrDefault(hostname, accessibleColorsKey).Unwrap() -} - func (c *cfg) Browser(hostname string) gh.ConfigEntry { // Intentionally panic if there is no user provided value or default value (which would be a programmer error) return c.GetOrDefault(hostname, browserKey).Unwrap() @@ -611,15 +606,6 @@ var Options = []ConfigOption{ return c.Browser(hostname).Value }, }, - { - Key: accessibleColorsKey, - Description: "toggle preference for accessible colors that can be customized", - DefaultValue: "disabled", - AllowedValues: []string{"enabled", "disabled"}, - CurrentValue: func(c gh.Config, hostname string) string { - return c.AccessibleColors(hostname).Value - }, - }, } func HomeDirPath(subdir string) (string, error) { diff --git a/internal/gh/gh.go b/internal/gh/gh.go index 4f3ece204..8e640c41a 100644 --- a/internal/gh/gh.go +++ b/internal/gh/gh.go @@ -35,8 +35,6 @@ type Config interface { // Set provides primitive access for setting configuration values, optionally scoped by host. Set(hostname string, key string, value string) - // AccessibleColors returns the configured accessible colors preference, optionally scoped by host. - AccessibleColors(hostname string) ConfigEntry // Browser returns the configured browser, optionally scoped by host. Browser(hostname string) ConfigEntry // Editor returns the configured editor, optionally scoped by host. diff --git a/pkg/cmd/cache/list/list.go b/pkg/cmd/cache/list/list.go index 9f39876cb..d699e2c3d 100644 --- a/pkg/cmd/cache/list/list.go +++ b/pkg/cmd/cache/list/list.go @@ -99,11 +99,12 @@ func listRun(opts *ListOptions) error { } client := api.NewClientFromHTTP(httpClient) + cs := opts.IO.ColorScheme() opts.IO.StartProgressIndicator() result, err := shared.GetCaches(client, repo, shared.GetCachesOptions{Limit: opts.Limit, Sort: opts.Sort, Order: opts.Order, Key: opts.Key, Ref: opts.Ref}) opts.IO.StopProgressIndicator() if err != nil { - return fmt.Errorf("%s Failed to get caches: %w", opts.IO.ColorScheme().FailureIcon(), err) + return fmt.Errorf("%s Failed to get caches: %w", cs.FailureIcon(), err) } if len(result.ActionsCaches) == 0 && opts.Exporter == nil { @@ -130,11 +131,11 @@ func listRun(opts *ListOptions) error { tp := tableprinter.New(opts.IO, tableprinter.WithHeader("ID", "KEY", "SIZE", "CREATED", "ACCESSED")) for _, cache := range result.ActionsCaches { - tp.AddField(opts.IO.ColorScheme().Cyan(fmt.Sprintf("%d", cache.Id))) + tp.AddField(cs.Cyanf("%d", cache.Id)) tp.AddField(cache.Key) tp.AddField(humanFileSize(cache.SizeInBytes)) - tp.AddTimeField(opts.Now, cache.CreatedAt, opts.IO.ColorScheme().Gray) - tp.AddTimeField(opts.Now, cache.LastAccessedAt, opts.IO.ColorScheme().Gray) + tp.AddTimeField(opts.Now, cache.CreatedAt, cs.Muted) + tp.AddTimeField(opts.Now, cache.LastAccessedAt, cs.Muted) tp.EndRow() } diff --git a/pkg/cmd/codespace/list.go b/pkg/cmd/codespace/list.go index c1acccabb..238c4a6a7 100644 --- a/pkg/cmd/codespace/list.go +++ b/pkg/cmd/codespace/list.go @@ -152,7 +152,7 @@ func (a *App) List(ctx context.Context, opts *listOptions, exporter cmdutil.Expo case false: nameColor = cs.Yellow case true: - nameColor = cs.Gray + nameColor = cs.Muted } tp.AddField(formattedName, tableprinter.WithColor(nameColor)) @@ -172,7 +172,7 @@ func (a *App) List(ctx context.Context, opts *listOptions, exporter cmdutil.Expo if err != nil { return fmt.Errorf("error parsing date %q: %w", c.CreatedAt, err) } - tp.AddTimeField(time.Now(), ct, cs.Gray) + tp.AddTimeField(time.Now(), ct, cs.Muted) if hasNonProdVSCSTarget { tp.AddField(c.VSCSTarget) diff --git a/pkg/cmd/gist/list/list.go b/pkg/cmd/gist/list/list.go index d46d4da02..a4fd245f6 100644 --- a/pkg/cmd/gist/list/list.go +++ b/pkg/cmd/gist/list/list.go @@ -206,7 +206,7 @@ func printTable(io *iostreams.IOStreams, gists []shared.Gist, filter *regexp.Reg tableprinter.WithColor(highlightFilesFunc(&gist)), ) tp.AddField(visibility, tableprinter.WithColor(visColor)) - tp.AddTimeField(time.Now(), gist.UpdatedAt, cs.Gray) + tp.AddTimeField(time.Now(), gist.UpdatedAt, cs.Muted) tp.EndRow() } diff --git a/pkg/cmd/gpg-key/list/list.go b/pkg/cmd/gpg-key/list/list.go index 9acf1d7b6..4244ba349 100644 --- a/pkg/cmd/gpg-key/list/list.go +++ b/pkg/cmd/gpg-key/list/list.go @@ -78,7 +78,7 @@ func listRun(opts *ListOptions) error { t.AddField(gpgKey.Emails.String()) t.AddField(gpgKey.KeyID) t.AddField(gpgKey.PublicKey, tableprinter.WithTruncate(truncateMiddle)) - t.AddTimeField(now, gpgKey.CreatedAt, cs.Gray) + t.AddTimeField(now, gpgKey.CreatedAt, cs.Muted) expiresAt := gpgKey.ExpiresAt.Format(time.RFC3339) if t.IsTTY() { if gpgKey.ExpiresAt.IsZero() { @@ -87,7 +87,7 @@ func listRun(opts *ListOptions) error { expiresAt = gpgKey.ExpiresAt.Format("2006-01-02") } } - t.AddField(expiresAt, tableprinter.WithColor(cs.Gray)) + t.AddField(expiresAt, tableprinter.WithColor(cs.Muted)) t.EndRow() } diff --git a/pkg/cmd/pr/list/list.go b/pkg/cmd/pr/list/list.go index ee708f161..0b86d5e11 100644 --- a/pkg/cmd/pr/list/list.go +++ b/pkg/cmd/pr/list/list.go @@ -224,7 +224,7 @@ func listRun(opts *ListOptions) error { if !isTTY { table.AddField(shared.PrStateWithDraft(&pr)) } - table.AddTimeField(opts.Now(), pr.CreatedAt, cs.Gray) + table.AddTimeField(opts.Now(), pr.CreatedAt, cs.Muted) table.EndRow() } err = table.Render() diff --git a/pkg/cmd/release/list/list.go b/pkg/cmd/release/list/list.go index 24ec41840..dfc3ba8c1 100644 --- a/pkg/cmd/release/list/list.go +++ b/pkg/cmd/release/list/list.go @@ -88,7 +88,7 @@ func listRun(opts *ListOptions) error { } table := tableprinter.New(opts.IO, tableprinter.WithHeader("Title", "Type", "Tag name", "Published")) - iofmt := opts.IO.ColorScheme() + cs := opts.IO.ColorScheme() for _, rel := range releases { title := text.RemoveExcessiveWhitespace(rel.Name) if title == "" { @@ -100,13 +100,13 @@ func listRun(opts *ListOptions) error { var badgeColor func(string) string if rel.IsLatest { badge = "Latest" - badgeColor = iofmt.Green + badgeColor = cs.Green } else if rel.IsDraft { badge = "Draft" - badgeColor = iofmt.Red + badgeColor = cs.Red } else if rel.IsPrerelease { badge = "Pre-release" - badgeColor = iofmt.Yellow + badgeColor = cs.Yellow } table.AddField(badge, tableprinter.WithColor(badgeColor)) @@ -116,7 +116,7 @@ func listRun(opts *ListOptions) error { if rel.PublishedAt.IsZero() { pubDate = rel.CreatedAt } - table.AddTimeField(time.Now(), pubDate, iofmt.Gray) + table.AddTimeField(time.Now(), pubDate, cs.Muted) table.EndRow() } err = table.Render() diff --git a/pkg/cmd/repo/deploy-key/list/list.go b/pkg/cmd/repo/deploy-key/list/list.go index 0cf2861c1..79d00c912 100644 --- a/pkg/cmd/repo/deploy-key/list/list.go +++ b/pkg/cmd/repo/deploy-key/list/list.go @@ -90,7 +90,7 @@ func listRun(opts *ListOptions) error { } t.AddField(sshType) t.AddField(deployKey.Key, tableprinter.WithTruncate(truncateMiddle)) - t.AddTimeField(now, deployKey.CreatedAt, cs.Gray) + t.AddTimeField(now, deployKey.CreatedAt, cs.Muted) t.EndRow() } diff --git a/pkg/cmd/repo/list/list.go b/pkg/cmd/repo/list/list.go index 2cee2c480..116439290 100644 --- a/pkg/cmd/repo/list/list.go +++ b/pkg/cmd/repo/list/list.go @@ -181,7 +181,7 @@ func listRun(opts *ListOptions) error { totalMatchCount := len(listResult.Repositories) for _, repo := range listResult.Repositories { info := repoInfo(repo) - infoColor := cs.Gray + infoColor := cs.Muted if repo.IsPrivate { infoColor = cs.Yellow @@ -195,7 +195,7 @@ func listRun(opts *ListOptions) error { tp.AddField(repo.NameWithOwner, tableprinter.WithColor(cs.Bold)) tp.AddField(text.RemoveExcessiveWhitespace(repo.Description)) tp.AddField(info, tableprinter.WithColor(infoColor)) - tp.AddTimeField(opts.Now(), *t, cs.Gray) + tp.AddTimeField(opts.Now(), *t, cs.Muted) tp.EndRow() } diff --git a/pkg/cmd/run/list/list.go b/pkg/cmd/run/list/list.go index 7b18c391d..7128ae253 100644 --- a/pkg/cmd/run/list/list.go +++ b/pkg/cmd/run/list/list.go @@ -176,7 +176,7 @@ func listRun(opts *ListOptions) error { tp.AddField(string(run.Event)) tp.AddField(fmt.Sprintf("%d", run.ID), tableprinter.WithColor(cs.Cyan)) tp.AddField(run.Duration(opts.now).String()) - tp.AddTimeField(opts.now, run.StartedTime(), cs.Gray) + tp.AddTimeField(opts.now, run.StartedTime(), cs.Muted) tp.EndRow() } diff --git a/pkg/cmd/ssh-key/list/list.go b/pkg/cmd/ssh-key/list/list.go index eebc82f90..f69e57ea3 100644 --- a/pkg/cmd/ssh-key/list/list.go +++ b/pkg/cmd/ssh-key/list/list.go @@ -89,11 +89,11 @@ func listRun(opts *ListOptions) error { t.AddField(id) t.AddField(sshKey.Key, tableprinter.WithTruncate(truncateMiddle)) t.AddField(sshKey.Type) - t.AddTimeField(now, sshKey.CreatedAt, cs.Gray) + t.AddTimeField(now, sshKey.CreatedAt, cs.Muted) } else { t.AddField(sshKey.Title) t.AddField(sshKey.Key) - t.AddTimeField(now, sshKey.CreatedAt, cs.Gray) + t.AddTimeField(now, sshKey.CreatedAt, cs.Muted) t.AddField(id) t.AddField(sshKey.Type) } From 3c38cedaf3949018cbcbab63fc7c280481e39fa0 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 15:33:52 -0400 Subject: [PATCH 09/24] Implement tests for muted logic, standardize reset This commit covers testing around the new ColorScheme.Muted logic based on various situations to gain confidence we get the accessible colors expected when enabled. Additionally, this commit includes a small change to the existing 8-bit color logic to standardize on the same reset sequence for testing purposes. Essentially, `ESC[m` and `ESC[0m` are equivalent but this inconsistency with our other libraries makes setting up tests a little extra confusing and difficult. --- pkg/cmd/gist/list/list_test.go | 6 ++-- pkg/iostreams/color.go | 6 ++-- pkg/iostreams/color_test.go | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/pkg/cmd/gist/list/list_test.go b/pkg/cmd/gist/list/list_test.go index 4f6c8a9f7..ad6d8cdb5 100644 --- a/pkg/cmd/gist/list/list_test.go +++ b/pkg/cmd/gist/list/list_test.go @@ -487,8 +487,8 @@ func Test_listRun(t *testing.T) { }, wantOut: heredoc.Docf(` %[1]s[0;4;39mID %[1]s[0m %[1]s[0;4;39mDESCRIPTION %[1]s[0m %[1]s[0;4;39mFILES %[1]s[0m %[1]s[0;4;39mVISIBILITY%[1]s[0m %[1]s[0;4;39mUPDATED %[1]s[0m - 1234 %[1]s[0;30;43mocto%[1]s[0m%[1]s[0;1;39m match in the description%[1]s[0m 1 file %[1]s[0;32mpublic %[1]s[0m %[1]s[38;5;242mabout 6 hours ago%[1]s[m - 2345 %[1]s[0;1;39mmatch in the file name %[1]s[0m %[1]s[0;30;43m2 files%[1]s[0m %[1]s[0;31msecret %[1]s[0m %[1]s[38;5;242mabout 6 hours ago%[1]s[m + 1234 %[1]s[0;30;43mocto%[1]s[0m%[1]s[0;1;39m match in the description%[1]s[0m 1 file %[1]s[0;32mpublic %[1]s[0m %[1]s[38;5;242mabout 6 hours ago%[1]s[0m + 2345 %[1]s[0;1;39mmatch in the file name %[1]s[0m %[1]s[0;30;43m2 files%[1]s[0m %[1]s[0;31msecret %[1]s[0m %[1]s[38;5;242mabout 6 hours ago%[1]s[0m `, "\x1b"), }, { @@ -694,7 +694,7 @@ func Test_highlightMatch(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cs := iostreams.NewColorScheme(tt.color, false, false, iostreams.NoTheme) + cs := iostreams.NewColorScheme(tt.color, false, false, false, iostreams.NoTheme) matched := false got, err := highlightMatch(tt.input, regex, &matched, cs.Blue, cs.Highlight) diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 3e2d08ac2..9e6c4f7ca 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -37,7 +37,7 @@ var ( noThemeTableHeader = ansi.ColorFunc("default+u") gray256 = func(t string) string { - return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[m", 38, 242, t) + return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[0m", 38, 242, t) } ) @@ -91,8 +91,10 @@ func (c *ColorScheme) Muted(t string) string { switch c.theme { case LightTheme: return lightThemeMuted(t) - default: + case DarkTheme: return darkThemeMuted(t) + default: + return t } } diff --git a/pkg/iostreams/color_test.go b/pkg/iostreams/color_test.go index a399e6d58..b0cf994e3 100644 --- a/pkg/iostreams/color_test.go +++ b/pkg/iostreams/color_test.go @@ -175,3 +175,67 @@ func TestTableHeader(t *testing.T) { }) } } + +func TestMuted(t *testing.T) { + reset := "\x1b[0m" + gray4bit := "\x1b[0;90m" + gray8bit := "\x1b[38;5;242m" + brightBlack4bit := "\x1b[0;90m" + dimBlack4bit := "\x1b[0;2;37m" + + tests := []struct { + name string + cs *ColorScheme + input string + expected string + }{ + { + name: "when color is disabled but accessible colors are disabled, text is not stylized", + cs: NewColorScheme(false, false, false, false, NoTheme), + input: "this should not be stylized", + expected: "this should not be stylized", + }, + { + name: "when 4-bit color is enabled but accessible colors are disabled, legacy 4-bit gray color is used", + cs: NewColorScheme(true, false, false, false, NoTheme), + input: "this should be 4-bit gray", + expected: fmt.Sprintf("%sthis should be 4-bit gray%s", gray4bit, reset), + }, + { + name: "when 8-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", + cs: NewColorScheme(true, true, false, false, NoTheme), + input: "this should be 8-bit gray", + expected: fmt.Sprintf("%sthis should be 8-bit gray%s", gray8bit, reset), + }, + { + name: "when 24-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", + cs: NewColorScheme(true, true, true, false, NoTheme), + input: "this should be 8-bit gray", + expected: fmt.Sprintf("%sthis should be 8-bit gray%s", gray8bit, reset), + }, + { + name: "when 4-bit color is enabled and theme is dark, 4-bit light color is used", + cs: NewColorScheme(true, true, true, true, DarkTheme), + input: "this should be 4-bit dim black", + expected: fmt.Sprintf("%sthis should be 4-bit dim black%s", dimBlack4bit, reset), + }, + { + name: "when 4-bit color is enabled and theme is light, 4-bit dark color is used", + cs: NewColorScheme(true, true, true, true, LightTheme), + input: "this should be 4-bit bright black", + expected: fmt.Sprintf("%sthis should be 4-bit bright black%s", brightBlack4bit, reset), + }, + { + name: "when 4-bit color is enabled but no theme, 4-bit default color is used", + cs: NewColorScheme(true, true, true, true, NoTheme), + input: "this should have no explicit color", + expected: "this should have no explicit color", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, tt.cs.Muted(tt.input)) + }) + } +} From de86cc6c150d1ec969f935bf85b3ae8a7046f42f Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 15:57:37 -0400 Subject: [PATCH 10/24] Remove out of scope configuration setting change --- internal/config/config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index a3849b9af..350163cba 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -534,8 +534,6 @@ aliases: http_unix_socket: # What web browser gh should use when opening URLs. If blank, will refer to environment. browser: -# Preference for accessible colors that can be customized. This is a global config that cannot be overridden by hostname. Supported values: enabled, disabled -accessible_colors: disabled ` type ConfigOption struct { From 1bf1ad282b5bbdbe8c3ebac55e8632cfc2cf5d09 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 15:58:37 -0400 Subject: [PATCH 11/24] Remove configuration setting vestage --- internal/config/config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 350163cba..476154d66 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,11 +12,9 @@ import ( o "github.com/cli/cli/v2/pkg/option" ghauth "github.com/cli/go-gh/v2/pkg/auth" ghConfig "github.com/cli/go-gh/v2/pkg/config" - xcolor "github.com/cli/go-gh/v2/pkg/x/color" ) const ( - accessibleColorsKey = xcolor.AccessibleColorsSetting aliasesKey = "aliases" browserKey = "browser" editorKey = "editor" From e36d795629ba1e3f6a51fdb6a0718037a08b4d0b Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 15:59:18 -0400 Subject: [PATCH 12/24] Regenerate mocks --- internal/gh/mock/config.go | 44 -------------------------------------- 1 file changed, 44 deletions(-) diff --git a/internal/gh/mock/config.go b/internal/gh/mock/config.go index ecb8814b8..569af1fac 100644 --- a/internal/gh/mock/config.go +++ b/internal/gh/mock/config.go @@ -19,9 +19,6 @@ var _ gh.Config = &ConfigMock{} // // // make and configure a mocked gh.Config // mockedConfig := &ConfigMock{ -// AccessibleColorsFunc: func(hostname string) gh.ConfigEntry { -// panic("mock out the AccessibleColors method") -// }, // AliasesFunc: func() gh.AliasConfig { // panic("mock out the Aliases method") // }, @@ -74,9 +71,6 @@ var _ gh.Config = &ConfigMock{} // // } type ConfigMock struct { - // AccessibleColorsFunc mocks the AccessibleColors method. - AccessibleColorsFunc func(hostname string) gh.ConfigEntry - // AliasesFunc mocks the Aliases method. AliasesFunc func() gh.AliasConfig @@ -124,11 +118,6 @@ type ConfigMock struct { // calls tracks calls to the methods. calls struct { - // AccessibleColors holds details about calls to the AccessibleColors method. - AccessibleColors []struct { - // Hostname is the hostname argument value. - Hostname string - } // Aliases holds details about calls to the Aliases method. Aliases []struct { } @@ -201,7 +190,6 @@ type ConfigMock struct { Write []struct { } } - lockAccessibleColors sync.RWMutex lockAliases sync.RWMutex lockAuthentication sync.RWMutex lockBrowser sync.RWMutex @@ -219,38 +207,6 @@ type ConfigMock struct { lockWrite sync.RWMutex } -// AccessibleColors calls AccessibleColorsFunc. -func (mock *ConfigMock) AccessibleColors(hostname string) gh.ConfigEntry { - if mock.AccessibleColorsFunc == nil { - panic("ConfigMock.AccessibleColorsFunc: method is nil but Config.AccessibleColors was just called") - } - callInfo := struct { - Hostname string - }{ - Hostname: hostname, - } - mock.lockAccessibleColors.Lock() - mock.calls.AccessibleColors = append(mock.calls.AccessibleColors, callInfo) - mock.lockAccessibleColors.Unlock() - return mock.AccessibleColorsFunc(hostname) -} - -// AccessibleColorsCalls gets all the calls that were made to AccessibleColors. -// Check the length with: -// -// len(mockedConfig.AccessibleColorsCalls()) -func (mock *ConfigMock) AccessibleColorsCalls() []struct { - Hostname string -} { - var calls []struct { - Hostname string - } - mock.lockAccessibleColors.RLock() - calls = mock.calls.AccessibleColors - mock.lockAccessibleColors.RUnlock() - return calls -} - // Aliases calls AliasesFunc. func (mock *ConfigMock) Aliases() gh.AliasConfig { if mock.AliasesFunc == nil { From b3c8c70cba824ea4d0ded0c47fcb88838d58a5e8 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Mon, 31 Mar 2025 17:00:40 -0400 Subject: [PATCH 13/24] Remove deprecated note on gray color functions Without fixing all ColorScheme.Gray and ColorScheme.Grayf usage in this pull request, golangci-lint throws errors for using deprecated functions. As that code should be replaced within github/cli#833, I'm removing the deprecation indicator for now to get this PR passing. --- pkg/iostreams/color.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 9e6c4f7ca..58d13e6ef 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -142,7 +142,7 @@ func (c *ColorScheme) GreenBold(t string) string { return greenBold(t) } -// Deprecated: Use Muted instead for thematically contrasting color. +// Use Muted instead for thematically contrasting color. func (c *ColorScheme) Gray(t string) string { if !c.enabled { return t @@ -153,7 +153,7 @@ func (c *ColorScheme) Gray(t string) string { return gray(t) } -// Deprecated: Use Mutedf instead for thematically contrasting color. +// Use Mutedf instead for thematically contrasting color. func (c *ColorScheme) Grayf(t string, args ...interface{}) string { return c.Gray(fmt.Sprintf(t, args...)) } From 3eca268a7f60f909afe8ad4cb77b2708d94a2525 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Wed, 2 Apr 2025 18:24:20 -0400 Subject: [PATCH 14/24] Introduce `color_labels` support, update commands This commit implements the actual changes around configuration setting / environment variable logic for displaying labels using their RGB hex color code in terminals with truecolor support. One of the subtler changes in this commit is renaming generic ColorScheme.HexToRGB logic to render truecolor to ColorScheme.Label as this feature was being used exclusively for labels. This is due to confusion about introducing the new `color_labels` config on top of generic coloring logic. --- internal/config/config.go | 17 +++++++ internal/config/config_test.go | 2 + internal/config/stub.go | 3 ++ internal/gh/gh.go | 2 + internal/gh/mock/config.go | 44 +++++++++++++++++ pkg/cmd/config/list/list_test.go | 19 +++++--- pkg/cmd/factory/default.go | 6 +++ pkg/cmd/factory/default_test.go | 47 ++++++++++++++++++ pkg/cmd/gist/list/list_test.go | 2 +- pkg/cmd/issue/shared/display.go | 2 +- pkg/cmd/issue/view/view.go | 2 +- pkg/cmd/label/list.go | 7 ++- pkg/cmd/pr/view/view.go | 2 +- pkg/cmd/search/shared/shared.go | 2 +- pkg/iostreams/color.go | 20 +++----- pkg/iostreams/color_test.go | 83 +++++++++----------------------- pkg/iostreams/iostreams.go | 11 ++++- 17 files changed, 183 insertions(+), 88 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 476154d66..e7534dfdb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -17,6 +17,7 @@ import ( const ( aliasesKey = "aliases" browserKey = "browser" + colorLabelsKey = "color_labels" editorKey = "editor" gitProtocolKey = "git_protocol" hostsKey = "hosts" @@ -113,6 +114,11 @@ func (c *cfg) Browser(hostname string) gh.ConfigEntry { return c.GetOrDefault(hostname, browserKey).Unwrap() } +func (c *cfg) ColorLabels(hostname string) gh.ConfigEntry { + // Intentionally panic if there is no user provided value or default value (which would be a programmer error) + return c.GetOrDefault(hostname, colorLabelsKey).Unwrap() +} + func (c *cfg) Editor(hostname string) gh.ConfigEntry { // Intentionally panic if there is no user provided value or default value (which would be a programmer error) return c.GetOrDefault(hostname, editorKey).Unwrap() @@ -532,6 +538,8 @@ aliases: http_unix_socket: # What web browser gh should use when opening URLs. If blank, will refer to environment. browser: +# Whether to display labels using their RGB hex color codes in terminals that support truecolor. Supported values: enabled, disabled +color_labels: disabled ` type ConfigOption struct { @@ -602,6 +610,15 @@ var Options = []ConfigOption{ return c.Browser(hostname).Value }, }, + { + Key: colorLabelsKey, + Description: "whether to display labels using their RGB hex color codes in terminals that support truecolor", + DefaultValue: "disabled", + AllowedValues: []string{"enabled", "disabled"}, + CurrentValue: func(c gh.Config, hostname string) string { + return c.ColorLabels(hostname).Value + }, + }, } func HomeDirPath(subdir string) (string, error) { diff --git a/internal/config/config_test.go b/internal/config/config_test.go index fef87ddc6..67a9a98d1 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -32,6 +32,7 @@ func TestNewConfigProvidesFallback(t *testing.T) { requireKeyWithValue(t, spiedCfg, []string{aliasesKey, "co"}, "pr checkout") requireKeyWithValue(t, spiedCfg, []string{httpUnixSocketKey}, "") requireKeyWithValue(t, spiedCfg, []string{browserKey}, "") + requireKeyWithValue(t, spiedCfg, []string{colorLabelsKey}, "disabled") } func TestGetOrDefaultApplicationDefaults(t *testing.T) { @@ -137,6 +138,7 @@ func TestFallbackConfig(t *testing.T) { requireKeyWithValue(t, cfg, []string{aliasesKey, "co"}, "pr checkout") requireKeyWithValue(t, cfg, []string{httpUnixSocketKey}, "") requireKeyWithValue(t, cfg, []string{browserKey}, "") + requireKeyWithValue(t, cfg, []string{colorLabelsKey}, "disabled") requireNoKey(t, cfg, []string{"unknown"}) } diff --git a/internal/config/stub.go b/internal/config/stub.go index 71d44556d..78073da4a 100644 --- a/internal/config/stub.go +++ b/internal/config/stub.go @@ -55,6 +55,9 @@ func NewFromString(cfgStr string) *ghmock.ConfigMock { mock.BrowserFunc = func(hostname string) gh.ConfigEntry { return cfg.Browser(hostname) } + mock.ColorLabelsFunc = func(hostname string) gh.ConfigEntry { + return cfg.ColorLabels(hostname) + } mock.EditorFunc = func(hostname string) gh.ConfigEntry { return cfg.Editor(hostname) } diff --git a/internal/gh/gh.go b/internal/gh/gh.go index 8e640c41a..8f94d7f0d 100644 --- a/internal/gh/gh.go +++ b/internal/gh/gh.go @@ -37,6 +37,8 @@ type Config interface { // Browser returns the configured browser, optionally scoped by host. Browser(hostname string) ConfigEntry + // ColorLabels returns the configured colorize labels setting, optionally scoped by host. + ColorLabels(hostname string) ConfigEntry // Editor returns the configured editor, optionally scoped by host. Editor(hostname string) ConfigEntry // GitProtocol returns the configured git protocol, optionally scoped by host. diff --git a/internal/gh/mock/config.go b/internal/gh/mock/config.go index 569af1fac..b94cb084d 100644 --- a/internal/gh/mock/config.go +++ b/internal/gh/mock/config.go @@ -31,6 +31,9 @@ var _ gh.Config = &ConfigMock{} // CacheDirFunc: func() string { // panic("mock out the CacheDir method") // }, +// ColorLabelsFunc: func(hostname string) gh.ConfigEntry { +// panic("mock out the ColorLabels method") +// }, // EditorFunc: func(hostname string) gh.ConfigEntry { // panic("mock out the Editor method") // }, @@ -83,6 +86,9 @@ type ConfigMock struct { // CacheDirFunc mocks the CacheDir method. CacheDirFunc func() string + // ColorLabelsFunc mocks the ColorLabels method. + ColorLabelsFunc func(hostname string) gh.ConfigEntry + // EditorFunc mocks the Editor method. EditorFunc func(hostname string) gh.ConfigEntry @@ -132,6 +138,11 @@ type ConfigMock struct { // CacheDir holds details about calls to the CacheDir method. CacheDir []struct { } + // ColorLabels holds details about calls to the ColorLabels method. + ColorLabels []struct { + // Hostname is the hostname argument value. + Hostname string + } // Editor holds details about calls to the Editor method. Editor []struct { // Hostname is the hostname argument value. @@ -194,6 +205,7 @@ type ConfigMock struct { lockAuthentication sync.RWMutex lockBrowser sync.RWMutex lockCacheDir sync.RWMutex + lockColorLabels sync.RWMutex lockEditor sync.RWMutex lockGetOrDefault sync.RWMutex lockGitProtocol sync.RWMutex @@ -320,6 +332,38 @@ func (mock *ConfigMock) CacheDirCalls() []struct { return calls } +// ColorLabels calls ColorLabelsFunc. +func (mock *ConfigMock) ColorLabels(hostname string) gh.ConfigEntry { + if mock.ColorLabelsFunc == nil { + panic("ConfigMock.ColorLabelsFunc: method is nil but Config.ColorLabels was just called") + } + callInfo := struct { + Hostname string + }{ + Hostname: hostname, + } + mock.lockColorLabels.Lock() + mock.calls.ColorLabels = append(mock.calls.ColorLabels, callInfo) + mock.lockColorLabels.Unlock() + return mock.ColorLabelsFunc(hostname) +} + +// ColorLabelsCalls gets all the calls that were made to ColorLabels. +// Check the length with: +// +// len(mockedConfig.ColorLabelsCalls()) +func (mock *ConfigMock) ColorLabelsCalls() []struct { + Hostname string +} { + var calls []struct { + Hostname string + } + mock.lockColorLabels.RLock() + calls = mock.calls.ColorLabels + mock.lockColorLabels.RUnlock() + return calls +} + // Editor calls EditorFunc. func (mock *ConfigMock) Editor(hostname string) gh.ConfigEntry { if mock.EditorFunc == nil { diff --git a/pkg/cmd/config/list/list_test.go b/pkg/cmd/config/list/list_test.go index 65f83d659..2184d0f16 100644 --- a/pkg/cmd/config/list/list_test.go +++ b/pkg/cmd/config/list/list_test.go @@ -4,6 +4,7 @@ import ( "bytes" "testing" + "github.com/MakeNowJust/heredoc" "github.com/cli/cli/v2/internal/config" "github.com/cli/cli/v2/internal/gh" "github.com/cli/cli/v2/pkg/cmdutil" @@ -91,14 +92,16 @@ func Test_listRun(t *testing.T) { return cfg }(), input: &ListOptions{Hostname: "HOST"}, - stdout: `git_protocol=ssh -editor=/usr/bin/vim -prompt=disabled -prefer_editor_prompt=enabled -pager=less -http_unix_socket= -browser=brave -`, + stdout: heredoc.Doc(` + git_protocol=ssh + editor=/usr/bin/vim + prompt=disabled + prefer_editor_prompt=enabled + pager=less + http_unix_socket= + browser=brave + color_labels=disabled + `), }, } diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index 923651487..5e9c25ab2 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -292,6 +292,12 @@ func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams { io.SetPager(pager.Value) } + if _, ghColorLabels := os.LookupEnv("GH_COLOR_LABELS"); ghColorLabels { + io.SetColorLabels(true) // TODO: should this be a truthy value? + } else if prompt := cfg.ColorLabels(""); prompt.Value == "enabled" { + io.SetColorLabels(true) + } + return io } diff --git a/pkg/cmd/factory/default_test.go b/pkg/cmd/factory/default_test.go index 94955bb30..b1730d6e6 100644 --- a/pkg/cmd/factory/default_test.go +++ b/pkg/cmd/factory/default_test.go @@ -432,6 +432,49 @@ func Test_ioStreams_prompt(t *testing.T) { } } +func Test_ioStreams_colorLabels(t *testing.T) { + tests := []struct { + name string + config gh.Config + colorLabelsEnabled bool + env map[string]string + }{ + { + name: "default config", + colorLabelsEnabled: false, + }, + { + name: "config with colorLabels enabled", + config: enableColorLabelsConfig(), + colorLabelsEnabled: true, + }, + { + name: "colorLabels enabled via GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "1"}, + colorLabelsEnabled: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.env != nil { + for k, v := range tt.env { + t.Setenv(k, v) + } + } + f := New("1") + f.Config = func() (gh.Config, error) { + if tt.config == nil { + return config.NewBlankConfig(), nil + } else { + return tt.config, nil + } + } + io := ioStreams(f) + assert.Equal(t, tt.colorLabelsEnabled, io.ColorLabels()) + }) + } +} + func TestSSOURL(t *testing.T) { tests := []struct { name string @@ -537,3 +580,7 @@ func pagerConfig() gh.Config { func disablePromptConfig() gh.Config { return config.NewFromString("prompt: disabled") } + +func enableColorLabelsConfig() gh.Config { + return config.NewFromString("color_labels: enabled") +} diff --git a/pkg/cmd/gist/list/list_test.go b/pkg/cmd/gist/list/list_test.go index 4f6c8a9f7..1b55478b3 100644 --- a/pkg/cmd/gist/list/list_test.go +++ b/pkg/cmd/gist/list/list_test.go @@ -694,7 +694,7 @@ func Test_highlightMatch(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cs := iostreams.NewColorScheme(tt.color, false, false, iostreams.NoTheme) + cs := iostreams.NewColorScheme(tt.color, false, false, false, iostreams.NoTheme) matched := false got, err := highlightMatch(tt.input, regex, &matched, cs.Blue, cs.Highlight) diff --git a/pkg/cmd/issue/shared/display.go b/pkg/cmd/issue/shared/display.go index 0c56ffd2c..bfb9fc2c6 100644 --- a/pkg/cmd/issue/shared/display.go +++ b/pkg/cmd/issue/shared/display.go @@ -56,7 +56,7 @@ func issueLabelList(issue *api.Issue, cs *iostreams.ColorScheme, colorize bool) labelNames := make([]string, 0, len(issue.Labels.Nodes)) for _, label := range issue.Labels.Nodes { if colorize { - labelNames = append(labelNames, cs.HexToRGB(label.Color, label.Name)) + labelNames = append(labelNames, cs.Label(label.Color, label.Name)) } else { labelNames = append(labelNames, label.Name) } diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index b188c6a4c..e2420a820 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -317,7 +317,7 @@ func issueLabelList(issue *api.Issue, cs *iostreams.ColorScheme) string { if cs == nil { labelNames[i] = label.Name } else { - labelNames[i] = cs.HexToRGB(label.Color, label.Name) + labelNames[i] = cs.Label(label.Color, label.Name) } } diff --git a/pkg/cmd/label/list.go b/pkg/cmd/label/list.go index fe1e9cdb7..4fe2f5ec9 100644 --- a/pkg/cmd/label/list.go +++ b/pkg/cmd/label/list.go @@ -137,7 +137,12 @@ func printLabels(io *iostreams.IOStreams, labels []label) error { table := tableprinter.New(io, tableprinter.WithHeader("NAME", "DESCRIPTION", "COLOR")) for _, label := range labels { - table.AddField(label.Name, tableprinter.WithColor(cs.ColorFromRGB(label.Color))) + // Colorize the label using tableprinter's WithColor function for it to handle non-TTY situations + labelColor := tableprinter.WithColor(func(s string) string { + return cs.Label(label.Color, s) + }) + + table.AddField(label.Name, labelColor) table.AddField(label.Description) table.AddField("#" + label.Color) diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index ccad4fa77..719a8fc7b 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -423,7 +423,7 @@ func prLabelList(pr api.PullRequest, cs *iostreams.ColorScheme) string { labelNames := make([]string, 0, len(pr.Labels.Nodes)) for _, label := range pr.Labels.Nodes { - labelNames = append(labelNames, cs.HexToRGB(label.Color, label.Name)) + labelNames = append(labelNames, cs.Label(label.Color, label.Name)) } list := strings.Join(labelNames, ", ") diff --git a/pkg/cmd/search/shared/shared.go b/pkg/cmd/search/shared/shared.go index f0a346fc8..1e3d0069b 100644 --- a/pkg/cmd/search/shared/shared.go +++ b/pkg/cmd/search/shared/shared.go @@ -158,7 +158,7 @@ func listIssueLabels(issue *search.Issue, cs *iostreams.ColorScheme, colorize bo labelNames := make([]string, 0, len(issue.Labels)) for _, label := range issue.Labels { if colorize { - labelNames = append(labelNames, cs.HexToRGB(label.Color, label.Name)) + labelNames = append(labelNames, cs.Label(label.Color, label.Name)) } else { labelNames = append(labelNames, label.Name) } diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 07fdcd79f..49f9496f5 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -41,12 +41,13 @@ var ( // NewColorScheme initializes color logic based on provided terminal capabilities. // Logic dealing with terminal theme detected, such as whether color is enabled, 8-bit color supported, true color supported, -// and terminal theme detected. -func NewColorScheme(enabled, is256enabled, trueColor bool, theme string) *ColorScheme { +// labels are colored, and terminal theme detected. +func NewColorScheme(enabled, is256enabled, trueColor, colorLabels bool, theme string) *ColorScheme { return &ColorScheme{ enabled: enabled, is256enabled: is256enabled, hasTrueColor: trueColor, + colorLabels: colorLabels, theme: theme, } } @@ -55,6 +56,7 @@ type ColorScheme struct { enabled bool is256enabled bool hasTrueColor bool + colorLabels bool theme string } @@ -240,17 +242,9 @@ func (c *ColorScheme) ColorFromString(s string) func(string) string { return fn } -// ColorFromRGB returns a function suitable for TablePrinter.AddField -// that calls HexToRGB, coloring text if supported by the terminal. -func (c *ColorScheme) ColorFromRGB(hex string) func(string) string { - return func(s string) string { - return c.HexToRGB(hex, s) - } -} - -// HexToRGB uses the given hex to color x if supported by the terminal. -func (c *ColorScheme) HexToRGB(hex string, x string) string { - if !c.enabled || !c.hasTrueColor || len(hex) != 6 { +// Label stylizes text based on label's RGB hex color. +func (c *ColorScheme) Label(hex string, x string) string { + if !c.enabled || !c.hasTrueColor || !c.colorLabels || len(hex) != 6 { return x } diff --git a/pkg/iostreams/color_test.go b/pkg/iostreams/color_test.go index b35c2eb73..c477891ce 100644 --- a/pkg/iostreams/color_test.go +++ b/pkg/iostreams/color_test.go @@ -7,7 +7,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestColorFromRGB(t *testing.T) { +func TestLabel(t *testing.T) { tests := []struct { name string hex string @@ -20,77 +20,40 @@ func TestColorFromRGB(t *testing.T) { hex: "fc0303", text: "red", wants: "\033[38;2;252;3;3mred\033[0m", - cs: NewColorScheme(true, true, true, NoTheme), + cs: NewColorScheme(true, true, true, true, NoTheme), }, { name: "no truecolor", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(true, true, false, NoTheme), + cs: NewColorScheme(true, true, false, true, NoTheme), }, { name: "no color", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, true, NoTheme), }, { name: "invalid hex", hex: "fc0", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, true, NoTheme), + }, + { + name: "no color labels", + hex: "fc0303", + text: "red", + wants: "red", + cs: NewColorScheme(true, true, true, false, NoTheme), }, } for _, tt := range tests { - fn := tt.cs.ColorFromRGB(tt.hex) - assert.Equal(t, tt.wants, fn(tt.text)) - } -} - -func TestHexToRGB(t *testing.T) { - tests := []struct { - name string - hex string - text string - wants string - cs *ColorScheme - }{ - { - name: "truecolor", - hex: "fc0303", - text: "red", - wants: "\033[38;2;252;3;3mred\033[0m", - cs: NewColorScheme(true, true, true, NoTheme), - }, - { - name: "no truecolor", - hex: "fc0303", - text: "red", - wants: "red", - cs: NewColorScheme(true, true, false, NoTheme), - }, - { - name: "no color", - hex: "fc0303", - text: "red", - wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), - }, - { - name: "invalid hex", - hex: "fc0", - text: "red", - wants: "red", - cs: NewColorScheme(false, false, false, NoTheme), - }, - } - - for _, tt := range tests { - output := tt.cs.HexToRGB(tt.hex, tt.text) + output := tt.cs.Label(tt.hex, tt.text) assert.Equal(t, tt.wants, output) } } @@ -109,61 +72,61 @@ func TestTableHeader(t *testing.T) { }{ { name: "when color is disabled, text is not stylized", - cs: NewColorScheme(false, false, false, NoTheme), + cs: NewColorScheme(false, false, false, false, NoTheme), input: "this should not be stylized", expected: "this should not be stylized", }, { name: "when 4-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, false, false, NoTheme), + cs: NewColorScheme(true, false, false, false, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 4-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, false, false, LightTheme), + cs: NewColorScheme(true, false, false, false, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 4-bit color is enabled and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, false, false, DarkTheme), + cs: NewColorScheme(true, false, false, false, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { name: "when 8-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, false, NoTheme), + cs: NewColorScheme(true, true, false, false, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 8-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, false, LightTheme), + cs: NewColorScheme(true, true, false, false, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 8-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, false, DarkTheme), + cs: NewColorScheme(true, true, false, false, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { name: "when 24-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, true, NoTheme), + cs: NewColorScheme(true, true, true, false, NoTheme), input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { name: "when 24-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, true, LightTheme), + cs: NewColorScheme(true, true, true, false, LightTheme), input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { name: "when 24-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, true, DarkTheme), + cs: NewColorScheme(true, true, true, false, DarkTheme), input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index 6c12d7911..e78575b9c 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -72,6 +72,7 @@ type IOStreams struct { colorOverride bool colorEnabled bool + colorLabels bool pagerCommand string pagerProcess *os.Process @@ -102,6 +103,10 @@ func (s *IOStreams) HasTrueColor() bool { return s.term.IsTrueColorSupported() } +func (s *IOStreams) ColorLabels() bool { + return s.colorLabels +} + // DetectTerminalTheme is a utility to call before starting the output pager so that the terminal background // can be reliably detected. func (s *IOStreams) DetectTerminalTheme() { @@ -134,6 +139,10 @@ func (s *IOStreams) SetColorEnabled(colorEnabled bool) { s.colorEnabled = colorEnabled } +func (s *IOStreams) SetColorLabels(colorLabels bool) { + s.colorLabels = colorLabels +} + func (s *IOStreams) SetStdinTTY(isTTY bool) { s.stdinTTYOverride = true s.stdinIsTTY = isTTY @@ -366,7 +375,7 @@ func (s *IOStreams) TerminalWidth() int { } func (s *IOStreams) ColorScheme() *ColorScheme { - return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.TerminalTheme()) + return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.ColorLabels(), s.TerminalTheme()) } func (s *IOStreams) ReadUserFile(fn string) ([]byte, error) { From 5f03c208a1d1dcd862b62d4497fdbfe91f486e77 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Wed, 2 Apr 2025 18:32:37 -0400 Subject: [PATCH 15/24] Fix comment language --- internal/gh/gh.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/gh/gh.go b/internal/gh/gh.go index 8f94d7f0d..b17c6bd67 100644 --- a/internal/gh/gh.go +++ b/internal/gh/gh.go @@ -37,7 +37,7 @@ type Config interface { // Browser returns the configured browser, optionally scoped by host. Browser(hostname string) ConfigEntry - // ColorLabels returns the configured colorize labels setting, optionally scoped by host. + // ColorLabels returns the configured color_label setting, optionally scoped by host. ColorLabels(hostname string) ConfigEntry // Editor returns the configured editor, optionally scoped by host. Editor(hostname string) ConfigEntry From addbc6ac5c0dc3ed4611904972d5b122e4426756 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Fri, 4 Apr 2025 11:02:44 -0400 Subject: [PATCH 16/24] Add label color env var to help topic, unused fix This commit adds the new environment variable to the `gh environment` help topic. Additionally, there is a small fix for Go linter for an unused variable raised as a problem. --- pkg/cmd/root/help.go | 2 +- pkg/cmd/root/help_topic.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/cmd/root/help.go b/pkg/cmd/root/help.go index a7daa7b84..7f8fb1c2e 100644 --- a/pkg/cmd/root/help.go +++ b/pkg/cmd/root/help.go @@ -87,7 +87,7 @@ func isRootCmd(command *cobra.Command) bool { return command != nil && !command.HasParent() } -func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, args []string) { +func rootHelpFunc(f *cmdutil.Factory, command *cobra.Command, _ []string) { flags := command.Flags() if isRootCmd(command) { diff --git a/pkg/cmd/root/help_topic.go b/pkg/cmd/root/help_topic.go index db0ef098d..51fb0662a 100644 --- a/pkg/cmd/root/help_topic.go +++ b/pkg/cmd/root/help_topic.go @@ -81,6 +81,9 @@ var HelpTopics = []helpTopic{ %[1]sCLICOLOR_FORCE%[1]s: set to a value other than %[1]s0%[1]s to keep ANSI colors in output even when the output is piped. + %[1]sGH_COLOR_LABELS%[1]s: set to any value to display labels using their RGB hex color codes in terminals that + support truecolor. + %[1]sGH_FORCE_TTY%[1]s: set to any value to force terminal-style output even when the output is redirected. When the value is a number, it is interpreted as the number of columns available in the viewport. When the value is a percentage, it will be applied against From e067eacd8114e0880c79e256eb11a119b647a9de Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Fri, 4 Apr 2025 11:57:37 -0400 Subject: [PATCH 17/24] Refactor ColorScheme initializer This commit completely removes the iostreams.NewColorScheme() initializer function in favor of exporting the type fields for greater clarity in its use. The result being code specifying only the fields that matter to test cases. --- internal/tableprinter/table_printer.go | 2 +- pkg/cmd/gist/list/list_test.go | 27 ++-- pkg/iostreams/color.go | 77 +++++------- pkg/iostreams/color_test.go | 168 +++++++++++++++++++------ pkg/iostreams/iostreams.go | 9 +- 5 files changed, 189 insertions(+), 94 deletions(-) diff --git a/internal/tableprinter/table_printer.go b/internal/tableprinter/table_printer.go index 69b22be12..47128afb4 100644 --- a/internal/tableprinter/table_printer.go +++ b/internal/tableprinter/table_printer.go @@ -73,7 +73,7 @@ func NewWithWriter(w io.Writer, isTTY bool, maxWidth int, cs *iostreams.ColorSch // was not padded. In tests cs.Enabled() is false which allows us to avoid having to fix up // numerous tests that verify header padding. var paddingFunc func(int, string) string - if cs.Enabled() { + if cs.Enabled { paddingFunc = text.PadRight } diff --git a/pkg/cmd/gist/list/list_test.go b/pkg/cmd/gist/list/list_test.go index 8fbf4d6c9..14351418f 100644 --- a/pkg/cmd/gist/list/list_test.go +++ b/pkg/cmd/gist/list/list_test.go @@ -654,50 +654,57 @@ func Test_highlightMatch(t *testing.T) { tests := []struct { name string input string - color bool + cs *iostreams.ColorScheme want string }{ { name: "single match", input: "Octo", + cs: &iostreams.ColorScheme{}, want: "Octo", }, { name: "single match (color)", input: "Octo", - color: true, - want: "\x1b[0;30;43mOcto\x1b[0m", + cs: &iostreams.ColorScheme{ + Enabled: true, + }, + want: "\x1b[0;30;43mOcto\x1b[0m", }, { name: "single match with extra", input: "Hello, Octocat!", + cs: &iostreams.ColorScheme{}, want: "Hello, Octocat!", }, { name: "single match with extra (color)", input: "Hello, Octocat!", - color: true, - want: "\x1b[0;34mHello, \x1b[0m\x1b[0;30;43mOcto\x1b[0m\x1b[0;34mcat!\x1b[0m", + cs: &iostreams.ColorScheme{ + Enabled: true, + }, + want: "\x1b[0;34mHello, \x1b[0m\x1b[0;30;43mOcto\x1b[0m\x1b[0;34mcat!\x1b[0m", }, { name: "multiple matches", input: "Octocat/octo", + cs: &iostreams.ColorScheme{}, want: "Octocat/octo", }, { name: "multiple matches (color)", input: "Octocat/octo", - color: true, - want: "\x1b[0;30;43mOcto\x1b[0m\x1b[0;34mcat/\x1b[0m\x1b[0;30;43mocto\x1b[0m", + cs: &iostreams.ColorScheme{ + Enabled: true, + }, + want: "\x1b[0;30;43mOcto\x1b[0m\x1b[0;34mcat/\x1b[0m\x1b[0;30;43mocto\x1b[0m", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - cs := iostreams.NewColorScheme(tt.color, false, false, false, false, iostreams.NoTheme) - matched := false - got, err := highlightMatch(tt.input, regex, &matched, cs.Blue, cs.Highlight) + got, err := highlightMatch(tt.input, regex, &matched, tt.cs.Blue, tt.cs.Highlight) assert.NoError(t, err) assert.True(t, matched) assert.Equal(t, tt.want, got) diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 92b94c360..86512df4e 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -41,35 +41,24 @@ var ( } ) -// NewColorScheme initializes color logic based on provided terminal capabilities. -// Logic dealing with terminal theme detected, such as whether color is enabled, 8-bit color supported, true color supported, -// labels are colored, and terminal theme detected. -func NewColorScheme(enabled, is256enabled, trueColor, accessibleColors, colorLabels bool, theme string) *ColorScheme { - return &ColorScheme{ - enabled: enabled, - is256enabled: is256enabled, - hasTrueColor: trueColor, - accessibleColors: accessibleColors, - colorLabels: colorLabels, - theme: theme, - } -} - +// ColorScheme controls how text is colored based upon terminal capabilities and user preferences. type ColorScheme struct { - enabled bool - is256enabled bool - hasTrueColor bool - accessibleColors bool - colorLabels bool - theme string -} - -func (c *ColorScheme) Enabled() bool { - return c.enabled + // Enabled is whether color is used at all. + Enabled bool + // EightBitColor is whether the terminal supports 8-bit, 256 colors. + EightBitColor bool + // TrueColor is whether the terminal supports 24-bit, 16 million colors. + TrueColor bool + // Accessible is whether colors must be base 16 colors that users can customize in terminal preferences. + Accessible bool + // ColorLabels is whether labels are colored based on their truecolor RGB hex color. + ColorLabels bool + // Theme is the terminal background color theme used to contextually color text for light, dark, or none at all. + Theme string } func (c *ColorScheme) Bold(t string) string { - if !c.enabled { + if !c.Enabled { return t } return bold(t) @@ -81,16 +70,16 @@ func (c *ColorScheme) Boldf(t string, args ...interface{}) string { func (c *ColorScheme) Muted(t string) string { // Fallback to previous logic if accessible colors preview is disabled. - if !c.accessibleColors { + if !c.Accessible { return c.Gray(t) } // Muted text is only stylized if color is enabled. - if !c.enabled { + if !c.Enabled { return t } - switch c.theme { + switch c.Theme { case LightTheme: return lightThemeMuted(t) case DarkTheme: @@ -105,7 +94,7 @@ func (c *ColorScheme) Mutedf(t string, args ...interface{}) string { } func (c *ColorScheme) Red(t string) string { - if !c.enabled { + if !c.Enabled { return t } return red(t) @@ -116,7 +105,7 @@ func (c *ColorScheme) Redf(t string, args ...interface{}) string { } func (c *ColorScheme) Yellow(t string) string { - if !c.enabled { + if !c.Enabled { return t } return yellow(t) @@ -127,7 +116,7 @@ func (c *ColorScheme) Yellowf(t string, args ...interface{}) string { } func (c *ColorScheme) Green(t string) string { - if !c.enabled { + if !c.Enabled { return t } return green(t) @@ -138,7 +127,7 @@ func (c *ColorScheme) Greenf(t string, args ...interface{}) string { } func (c *ColorScheme) GreenBold(t string) string { - if !c.enabled { + if !c.Enabled { return t } return greenBold(t) @@ -146,10 +135,10 @@ func (c *ColorScheme) GreenBold(t string) string { // Use Muted instead for thematically contrasting color. func (c *ColorScheme) Gray(t string) string { - if !c.enabled { + if !c.Enabled { return t } - if c.is256enabled { + if c.EightBitColor { return gray256(t) } return gray(t) @@ -161,7 +150,7 @@ func (c *ColorScheme) Grayf(t string, args ...interface{}) string { } func (c *ColorScheme) Magenta(t string) string { - if !c.enabled { + if !c.Enabled { return t } return magenta(t) @@ -172,7 +161,7 @@ func (c *ColorScheme) Magentaf(t string, args ...interface{}) string { } func (c *ColorScheme) Cyan(t string) string { - if !c.enabled { + if !c.Enabled { return t } return cyan(t) @@ -183,14 +172,14 @@ func (c *ColorScheme) Cyanf(t string, args ...interface{}) string { } func (c *ColorScheme) CyanBold(t string) string { - if !c.enabled { + if !c.Enabled { return t } return cyanBold(t) } func (c *ColorScheme) Blue(t string) string { - if !c.enabled { + if !c.Enabled { return t } return blue(t) @@ -221,7 +210,7 @@ func (c *ColorScheme) FailureIconWithColor(colo func(string) string) string { } func (c *ColorScheme) HighlightStart() string { - if !c.enabled { + if !c.Enabled { return "" } @@ -229,7 +218,7 @@ func (c *ColorScheme) HighlightStart() string { } func (c *ColorScheme) Highlight(t string) string { - if !c.enabled { + if !c.Enabled { return t } @@ -237,7 +226,7 @@ func (c *ColorScheme) Highlight(t string) string { } func (c *ColorScheme) Reset() string { - if !c.enabled { + if !c.Enabled { return "" } @@ -275,7 +264,7 @@ func (c *ColorScheme) ColorFromString(s string) func(string) string { // Label stylizes text based on label's RGB hex color. func (c *ColorScheme) Label(hex string, x string) string { - if !c.enabled || !c.hasTrueColor || !c.colorLabels || len(hex) != 6 { + if !c.Enabled || !c.TrueColor || !c.ColorLabels || len(hex) != 6 { return x } @@ -287,11 +276,11 @@ func (c *ColorScheme) Label(hex string, x string) string { func (c *ColorScheme) TableHeader(t string) string { // Table headers are only stylized if color is enabled including underline modifier. - if !c.enabled { + if !c.Enabled { return t } - switch c.theme { + switch c.Theme { case DarkTheme: return darkThemeTableHeader(t) case LightTheme: diff --git a/pkg/iostreams/color_test.go b/pkg/iostreams/color_test.go index 2adacd63f..f6a72e2a7 100644 --- a/pkg/iostreams/color_test.go +++ b/pkg/iostreams/color_test.go @@ -20,35 +20,52 @@ func TestLabel(t *testing.T) { hex: "fc0303", text: "red", wants: "\033[38;2;252;3;3mred\033[0m", - cs: NewColorScheme(true, true, true, false, true, NoTheme), + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + ColorLabels: true, + }, }, { name: "no truecolor", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(true, true, false, false, true, NoTheme), + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + ColorLabels: true, + }, }, { name: "no color", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, false, true, NoTheme), + cs: &ColorScheme{ + ColorLabels: true, + }, }, { name: "invalid hex", hex: "fc0", text: "red", wants: "red", - cs: NewColorScheme(false, false, false, false, true, NoTheme), + cs: &ColorScheme{ + ColorLabels: true, + }, }, { name: "no color labels", hex: "fc0303", text: "red", wants: "red", - cs: NewColorScheme(true, true, true, false, false, NoTheme), + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + ColorLabels: true, + }, }, } @@ -71,62 +88,110 @@ func TestTableHeader(t *testing.T) { expected string }{ { - name: "when color is disabled, text is not stylized", - cs: NewColorScheme(false, false, false, true, false, NoTheme), + name: "when color is disabled, text is not stylized", + cs: &ColorScheme{ + Accessible: true, + Theme: NoTheme, + }, input: "this should not be stylized", expected: "this should not be stylized", }, { - name: "when 4-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, false, false, true, false, NoTheme), + name: "when 4-bit color is enabled but no theme, 4-bit default color and underline are used", + cs: &ColorScheme{ + Enabled: true, + Accessible: true, + Theme: NoTheme, + }, input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { - name: "when 4-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, false, false, true, false, LightTheme), + name: "when 4-bit color is enabled and theme is light, 4-bit dark color and underline are used", + cs: &ColorScheme{ + Enabled: true, + Accessible: true, + Theme: LightTheme, + }, input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { - name: "when 4-bit color is enabled and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, false, false, true, false, DarkTheme), + name: "when 4-bit color is enabled and theme is dark, 4-bit light color and underline are used", + cs: &ColorScheme{ + Enabled: true, + Accessible: true, + Theme: DarkTheme, + }, input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { - name: "when 8-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, false, true, false, NoTheme), + name: "when 8-bit color is enabled but no theme, 4-bit default color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + Accessible: true, + Theme: NoTheme, + }, input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { - name: "when 8-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, false, true, false, LightTheme), + name: "when 8-bit color is enabled and theme is light, 4-bit dark color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + Accessible: true, + Theme: LightTheme, + }, input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { - name: "when 8-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, false, true, false, DarkTheme), + name: "when 8-bit color is true and theme is dark, 4-bit light color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + Accessible: true, + Theme: DarkTheme, + }, input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, { - name: "when 24-bit color is enabled but no theme, 4-bit default color and underline are used", - cs: NewColorScheme(true, true, true, true, false, NoTheme), + name: "when 24-bit color is enabled but no theme, 4-bit default color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: NoTheme, + }, input: "this should have no explicit color but underlined", expected: fmt.Sprintf("%sthis should have no explicit color but underlined%s", defaultUnderline, reset), }, { - name: "when 24-bit color is enabled and theme is light, 4-bit dark color and underline are used", - cs: NewColorScheme(true, true, true, true, false, LightTheme), + name: "when 24-bit color is enabled and theme is light, 4-bit dark color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: LightTheme, + }, input: "this should have dark foreground color and underlined", expected: fmt.Sprintf("%sthis should have dark foreground color and underlined%s", brightBlackUnderline, reset), }, { - name: "when 24-bit color is true and theme is dark, 4-bit light color and underline are used", - cs: NewColorScheme(true, true, true, true, false, DarkTheme), + name: "when 24-bit color is true and theme is dark, 4-bit light color and underline are used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: DarkTheme, + }, input: "this should have light foreground color and underlined", expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset), }, @@ -154,43 +219,70 @@ func TestMuted(t *testing.T) { }{ { name: "when color is disabled but accessible colors are disabled, text is not stylized", - cs: NewColorScheme(false, false, false, false, false, NoTheme), + cs: &ColorScheme{}, input: "this should not be stylized", expected: "this should not be stylized", }, { - name: "when 4-bit color is enabled but accessible colors are disabled, legacy 4-bit gray color is used", - cs: NewColorScheme(true, false, false, false, false, NoTheme), + name: "when 4-bit color is enabled but accessible colors are disabled, legacy 4-bit gray color is used", + cs: &ColorScheme{ + Enabled: true, + }, input: "this should be 4-bit gray", expected: fmt.Sprintf("%sthis should be 4-bit gray%s", gray4bit, reset), }, { - name: "when 8-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", - cs: NewColorScheme(true, true, false, false, false, NoTheme), + name: "when 8-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + }, input: "this should be 8-bit gray", expected: fmt.Sprintf("%sthis should be 8-bit gray%s", gray8bit, reset), }, { - name: "when 24-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", - cs: NewColorScheme(true, true, true, false, false, NoTheme), + name: "when 24-bit color is enabled but accessible colors are disabled, legacy 8-bit gray color is used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + }, input: "this should be 8-bit gray", expected: fmt.Sprintf("%sthis should be 8-bit gray%s", gray8bit, reset), }, { - name: "when 4-bit color is enabled and theme is dark, 4-bit light color is used", - cs: NewColorScheme(true, true, true, true, false, DarkTheme), + name: "when 4-bit color is enabled and theme is dark, 4-bit light color is used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: DarkTheme, + }, input: "this should be 4-bit dim black", expected: fmt.Sprintf("%sthis should be 4-bit dim black%s", dimBlack4bit, reset), }, { - name: "when 4-bit color is enabled and theme is light, 4-bit dark color is used", - cs: NewColorScheme(true, true, true, true, false, LightTheme), + name: "when 4-bit color is enabled and theme is light, 4-bit dark color is used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: LightTheme, + }, input: "this should be 4-bit bright black", expected: fmt.Sprintf("%sthis should be 4-bit bright black%s", brightBlack4bit, reset), }, { - name: "when 4-bit color is enabled but no theme, 4-bit default color is used", - cs: NewColorScheme(true, true, true, true, false, NoTheme), + name: "when 4-bit color is enabled but no theme, 4-bit default color is used", + cs: &ColorScheme{ + Enabled: true, + EightBitColor: true, + TrueColor: true, + Accessible: true, + Theme: NoTheme, + }, input: "this should have no explicit color", expected: "this should have no explicit color", }, diff --git a/pkg/iostreams/iostreams.go b/pkg/iostreams/iostreams.go index 28e81abba..f5e3c2aee 100644 --- a/pkg/iostreams/iostreams.go +++ b/pkg/iostreams/iostreams.go @@ -376,7 +376,14 @@ func (s *IOStreams) TerminalWidth() int { } func (s *IOStreams) ColorScheme() *ColorScheme { - return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.AccessibleColorsEnabled(), s.ColorLabels(), s.TerminalTheme()) + return &ColorScheme{ + Enabled: s.ColorEnabled(), + EightBitColor: s.ColorSupport256(), + TrueColor: s.HasTrueColor(), + Accessible: s.AccessibleColorsEnabled(), + ColorLabels: s.ColorLabels(), + Theme: s.TerminalTheme(), + } } func (s *IOStreams) ReadUserFile(fn string) ([]byte, error) { From e21243fe9b4db0f4ba837caa149a5120d7209773 Mon Sep 17 00:00:00 2001 From: Kynan Ware <47394200+BagToad@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:45:54 -0600 Subject: [PATCH 18/24] ci: pin third party actions to commit sha --- .github/workflows/deployment.yml | 10 +++++----- .github/workflows/homebrew-bump.yml | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deployment.yml b/.github/workflows/deployment.yml index 60354a953..a7b03f40d 100644 --- a/.github/workflows/deployment.yml +++ b/.github/workflows/deployment.yml @@ -50,7 +50,7 @@ jobs: with: go-version-file: 'go.mod' - name: Install GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 with: version: "~1.17.1" install-only: true @@ -103,7 +103,7 @@ jobs: security set-key-partition-list -S "apple-tool:,apple:,codesign:" -s -k "$keychain_password" "$keychain" rm "$RUNNER_TEMP/cert.p12" - name: Install GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 with: version: "~1.17.1" install-only: true @@ -157,7 +157,7 @@ jobs: with: go-version-file: 'go.mod' - name: Install GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 with: version: "~1.17.1" install-only: true @@ -196,7 +196,7 @@ jobs: run: script/release --local "$TAG_NAME" --platform windows - name: Set up MSBuild id: setupmsbuild - uses: microsoft/setup-msbuild@v2.0.0 + uses: microsoft/setup-msbuild@6fb02220983dee41ce7ae257b6f4d8f9bf5ed4ce - name: Build MSI shell: bash env: @@ -384,7 +384,7 @@ jobs: git diff --name-status @{upstream}.. fi - name: Bump homebrew-core formula - uses: mislav/bump-homebrew-formula-action@v3 + uses: mislav/bump-homebrew-formula-action@942e550c6344cfdb9e1ab29b9bb9bf0c43efa19b if: inputs.environment == 'production' && !contains(inputs.tag_name, '-') with: formula-name: gh diff --git a/.github/workflows/homebrew-bump.yml b/.github/workflows/homebrew-bump.yml index 2bc395a1e..228f1a345 100644 --- a/.github/workflows/homebrew-bump.yml +++ b/.github/workflows/homebrew-bump.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Bump homebrew-core formula - uses: mislav/bump-homebrew-formula-action@v3 + uses: mislav/bump-homebrew-formula-action@942e550c6344cfdb9e1ab29b9bb9bf0c43efa19b if: inputs.environment == 'production' && !contains(inputs.tag_name, '-') with: formula-name: gh From 918cafc222aa328c1fc932672c51027f9b39060d Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Sun, 6 Apr 2025 10:18:48 -0400 Subject: [PATCH 19/24] Deprecate ColorScheme.Gray for ColorScheme.Muted This commit converts all of the places using ColorScheme.Gray and ColorScheme.Grayf to Muted and Mutedf. There is a little extra tidying up with local variable names or converting code to use Mutedf format. --- pkg/cmd/gist/create/create.go | 2 +- pkg/cmd/gist/shared/shared.go | 2 +- pkg/cmd/gist/view/view.go | 4 ++-- pkg/cmd/issue/shared/display.go | 4 ++-- pkg/cmd/issue/view/view.go | 4 ++-- pkg/cmd/pr/checks/output.go | 2 +- pkg/cmd/pr/create/create.go | 26 +++++++++++++------------- pkg/cmd/pr/review/review.go | 2 +- pkg/cmd/pr/shared/comments.go | 8 ++++---- pkg/cmd/pr/shared/display.go | 4 ++-- pkg/cmd/pr/status/status.go | 2 +- pkg/cmd/pr/view/view.go | 4 ++-- pkg/cmd/release/view/view.go | 16 ++++++++-------- pkg/cmd/repo/license/view/view.go | 6 +++--- pkg/cmd/repo/view/view.go | 6 +++--- pkg/cmd/run/shared/presentation.go | 2 +- pkg/cmd/run/shared/shared.go | 2 +- pkg/cmd/run/view/view.go | 6 +++--- pkg/cmd/search/commits/commits.go | 2 +- pkg/cmd/search/repos/repos.go | 4 ++-- pkg/cmd/search/shared/shared.go | 2 +- pkg/cmd/status/status.go | 2 +- pkg/cmd/workflow/view/view.go | 2 +- pkg/iostreams/color.go | 6 +++--- 24 files changed, 60 insertions(+), 60 deletions(-) diff --git a/pkg/cmd/gist/create/create.go b/pkg/cmd/gist/create/create.go index 5392b997e..4f51bed25 100644 --- a/pkg/cmd/gist/create/create.go +++ b/pkg/cmd/gist/create/create.go @@ -138,7 +138,7 @@ func createRun(opts *CreateOptions) error { processMessage = fmt.Sprintf("Creating gist %s", gistName) } } - fmt.Fprintf(errOut, "%s %s\n", cs.Gray("-"), processMessage) + fmt.Fprintf(errOut, "%s %s\n", cs.Muted("-"), processMessage) httpClient, err := opts.HttpClient() if err != nil { diff --git a/pkg/cmd/gist/shared/shared.go b/pkg/cmd/gist/shared/shared.go index 53b577e4c..fc63f56ce 100644 --- a/pkg/cmd/gist/shared/shared.go +++ b/pkg/cmd/gist/shared/shared.go @@ -230,7 +230,7 @@ func PromptGists(prompter prompter.Prompter, client *http.Client, host string, c for i, gist := range gists { gistTime := text.FuzzyAgo(time.Now(), gist.UpdatedAt) // TODO: support dynamic maxWidth - opts[i] = fmt.Sprintf("%s %s %s", cs.Bold(gist.Filename()), gist.TruncDescription(), cs.Gray(gistTime)) + opts[i] = fmt.Sprintf("%s %s %s", cs.Bold(gist.Filename()), gist.TruncDescription(), cs.Muted(gistTime)) } result, err := prompter.Select("Select a gist", "", opts) diff --git a/pkg/cmd/gist/view/view.go b/pkg/cmd/gist/view/view.go index 705f8f703..f789c5b04 100644 --- a/pkg/cmd/gist/view/view.go +++ b/pkg/cmd/gist/view/view.go @@ -140,7 +140,7 @@ func viewRun(opts *ViewOptions) error { if len(gist.Files) == 1 || opts.Filename != "" { return fmt.Errorf("error: file is binary") } - _, err = fmt.Fprintln(opts.IO.Out, cs.Gray("(skipping rendering binary content)")) + _, err = fmt.Fprintln(opts.IO.Out, cs.Muted("(skipping rendering binary content)")) return nil } @@ -197,7 +197,7 @@ func viewRun(opts *ViewOptions) error { for i, fn := range filenames { if showFilenames { - fmt.Fprintf(opts.IO.Out, "%s\n\n", cs.Gray(fn)) + fmt.Fprintf(opts.IO.Out, "%s\n\n", cs.Muted(fn)) } if err := render(gist.Files[fn]); err != nil { return err diff --git a/pkg/cmd/issue/shared/display.go b/pkg/cmd/issue/shared/display.go index 0c56ffd2c..08ec484d6 100644 --- a/pkg/cmd/issue/shared/display.go +++ b/pkg/cmd/issue/shared/display.go @@ -38,13 +38,13 @@ func PrintIssues(io *iostreams.IOStreams, now time.Time, prefix string, totalCou } table.AddField(text.RemoveExcessiveWhitespace(issue.Title)) table.AddField(issueLabelList(&issue, cs, isTTY)) - table.AddTimeField(now, issue.UpdatedAt, cs.Gray) + table.AddTimeField(now, issue.UpdatedAt, cs.Muted) table.EndRow() } _ = table.Render() remaining := totalCount - len(issues) if remaining > 0 { - fmt.Fprintf(io.Out, cs.Gray("%sAnd %d more\n"), prefix, remaining) + fmt.Fprintf(io.Out, cs.Muted("%sAnd %d more\n"), prefix, remaining) } } diff --git a/pkg/cmd/issue/view/view.go b/pkg/cmd/issue/view/view.go index b188c6a4c..f61c603b4 100644 --- a/pkg/cmd/issue/view/view.go +++ b/pkg/cmd/issue/view/view.go @@ -228,7 +228,7 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue var md string var err error if issue.Body == "" { - md = fmt.Sprintf("\n %s\n\n", cs.Gray("No description provided")) + md = fmt.Sprintf("\n %s\n\n", cs.Muted("No description provided")) } else { md, err = markdown.Render(issue.Body, markdown.WithTheme(opts.IO.TerminalTheme()), @@ -250,7 +250,7 @@ func printHumanIssuePreview(opts *ViewOptions, baseRepo ghrepo.Interface, issue } // Footer - fmt.Fprintf(out, cs.Gray("View this issue on GitHub: %s\n"), issue.URL) + fmt.Fprintf(out, cs.Muted("View this issue on GitHub: %s\n"), issue.URL) return nil } diff --git a/pkg/cmd/pr/checks/output.go b/pkg/cmd/pr/checks/output.go index 5d2f080c6..24105c3e2 100644 --- a/pkg/cmd/pr/checks/output.go +++ b/pkg/cmd/pr/checks/output.go @@ -30,7 +30,7 @@ func addRow(tp *tableprinter.TablePrinter, io *iostreams.IOStreams, o check) { markColor = cs.Yellow case "skipping", "cancel": mark = "-" - markColor = cs.Gray + markColor = cs.Muted } if io.IsStdoutTTY() { diff --git a/pkg/cmd/pr/create/create.go b/pkg/cmd/pr/create/create.go index 0066bbc6e..5f8979c11 100644 --- a/pkg/cmd/pr/create/create.go +++ b/pkg/cmd/pr/create/create.go @@ -879,37 +879,37 @@ func renderPullRequestPlain(w io.Writer, params map[string]interface{}, state *s } func renderPullRequestTTY(io *iostreams.IOStreams, params map[string]interface{}, state *shared.IssueMetadataState) error { - iofmt := io.ColorScheme() + cs := io.ColorScheme() out := io.Out fmt.Fprint(out, "Would have created a Pull Request with:\n") - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Title"), params["title"].(string)) - fmt.Fprintf(out, "%s: %t\n", iofmt.Bold("Draft"), params["draft"]) - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Base"), params["baseRefName"]) - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Head"), params["headRefName"]) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Title"), params["title"].(string)) + fmt.Fprintf(out, "%s: %t\n", cs.Bold("Draft"), params["draft"]) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Base"), params["baseRefName"]) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Head"), params["headRefName"]) if len(state.Labels) != 0 { - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Labels"), strings.Join(state.Labels, ", ")) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Labels"), strings.Join(state.Labels, ", ")) } if len(state.Reviewers) != 0 { - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Reviewers"), strings.Join(state.Reviewers, ", ")) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Reviewers"), strings.Join(state.Reviewers, ", ")) } if len(state.Assignees) != 0 { - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Assignees"), strings.Join(state.Assignees, ", ")) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Assignees"), strings.Join(state.Assignees, ", ")) } if len(state.Milestones) != 0 { - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Milestones"), strings.Join(state.Milestones, ", ")) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Milestones"), strings.Join(state.Milestones, ", ")) } if len(state.Projects) != 0 { - fmt.Fprintf(out, "%s: %s\n", iofmt.Bold("Projects"), strings.Join(state.Projects, ", ")) + fmt.Fprintf(out, "%s: %s\n", cs.Bold("Projects"), strings.Join(state.Projects, ", ")) } - fmt.Fprintf(out, "%s: %t\n", iofmt.Bold("MaintainerCanModify"), params["maintainerCanModify"]) + fmt.Fprintf(out, "%s: %t\n", cs.Bold("MaintainerCanModify"), params["maintainerCanModify"]) - fmt.Fprintf(out, "%s\n", iofmt.Bold("Body:")) + fmt.Fprintf(out, "%s\n", cs.Bold("Body:")) // Body var md string var err error if len(params["body"].(string)) == 0 { - md = fmt.Sprintf("%s\n", iofmt.Gray("No description provided")) + md = fmt.Sprintf("%s\n", cs.Muted("No description provided")) } else { md, err = markdown.Render(params["body"].(string), markdown.WithTheme(io.TerminalTheme()), diff --git a/pkg/cmd/pr/review/review.go b/pkg/cmd/pr/review/review.go index 25f81d973..cafa6ce8f 100644 --- a/pkg/cmd/pr/review/review.go +++ b/pkg/cmd/pr/review/review.go @@ -191,7 +191,7 @@ func reviewRun(opts *ReviewOptions) error { switch reviewData.State { case api.ReviewComment: - fmt.Fprintf(opts.IO.ErrOut, "%s Reviewed pull request %s#%d\n", cs.Gray("-"), ghrepo.FullName(baseRepo), pr.Number) + fmt.Fprintf(opts.IO.ErrOut, "%s Reviewed pull request %s#%d\n", cs.Muted("-"), ghrepo.FullName(baseRepo), pr.Number) case api.ReviewApprove: fmt.Fprintf(opts.IO.ErrOut, "%s Approved pull request %s#%d\n", cs.SuccessIcon(), ghrepo.FullName(baseRepo), pr.Number) case api.ReviewRequestChanges: diff --git a/pkg/cmd/pr/shared/comments.go b/pkg/cmd/pr/shared/comments.go index a05108d7b..7c6e9154c 100644 --- a/pkg/cmd/pr/shared/comments.go +++ b/pkg/cmd/pr/shared/comments.go @@ -62,7 +62,7 @@ func CommentList(io *iostreams.IOStreams, comments api.Comments, reviews api.Pul hiddenCount := totalCount - retrievedCount if preview && hiddenCount > 0 { - fmt.Fprint(&b, cs.Gray(fmt.Sprintf("———————— Not showing %s ————————", text.Pluralize(hiddenCount, "comment")))) + fmt.Fprint(&b, cs.Muted(fmt.Sprintf("———————— Not showing %s ————————", text.Pluralize(hiddenCount, "comment")))) fmt.Fprintf(&b, "\n\n\n") } @@ -79,7 +79,7 @@ func CommentList(io *iostreams.IOStreams, comments api.Comments, reviews api.Pul } if preview && hiddenCount > 0 { - fmt.Fprint(&b, cs.Gray("Use --comments to view the full conversation")) + fmt.Fprint(&b, cs.Muted("Use --comments to view the full conversation")) fmt.Fprintln(&b) } @@ -122,7 +122,7 @@ func formatComment(io *iostreams.IOStreams, comment Comment, newest bool) (strin var md string var err error if comment.Content() == "" { - md = fmt.Sprintf("\n %s\n\n", cs.Gray("No body provided")) + md = fmt.Sprintf("\n %s\n\n", cs.Muted("No body provided")) } else { md, err = markdown.Render(comment.Content(), markdown.WithTheme(io.TerminalTheme()), @@ -135,7 +135,7 @@ func formatComment(io *iostreams.IOStreams, comment Comment, newest bool) (strin // Footer if comment.Link() != "" { - fmt.Fprintf(&b, cs.Gray("View the full review: %s\n\n"), comment.Link()) + fmt.Fprintf(&b, cs.Muted("View the full review: %s\n\n"), comment.Link()) } return b.String(), nil diff --git a/pkg/cmd/pr/shared/display.go b/pkg/cmd/pr/shared/display.go index 02482951c..b4d83c719 100644 --- a/pkg/cmd/pr/shared/display.go +++ b/pkg/cmd/pr/shared/display.go @@ -60,7 +60,7 @@ func PrintHeader(io *iostreams.IOStreams, s string) { } func PrintMessage(io *iostreams.IOStreams, s string) { - fmt.Fprintln(io.Out, io.ColorScheme().Gray(s)) + fmt.Fprintln(io.Out, io.ColorScheme().Muted(s)) } func ListNoResults(repoName string, itemName string, hasFilters bool) error { @@ -83,7 +83,7 @@ func ListHeader(repoName string, itemName string, matchCount int, totalMatchCoun } func PrCheckStatusSummaryWithColor(cs *iostreams.ColorScheme, checks api.PullRequestChecksStatus) string { - var summary = cs.Gray("No checks") + var summary = cs.Muted("No checks") if checks.Total > 0 { if checks.Failing > 0 { if checks.Failing == checks.Total { diff --git a/pkg/cmd/pr/status/status.go b/pkg/cmd/pr/status/status.go index b7b390bf2..d20522d04 100644 --- a/pkg/cmd/pr/status/status.go +++ b/pkg/cmd/pr/status/status.go @@ -316,6 +316,6 @@ func printPrs(io *iostreams.IOStreams, totalCount int, prs ...api.PullRequest) { } remaining := totalCount - len(prs) if remaining > 0 { - fmt.Fprintf(w, cs.Gray(" And %d more\n"), remaining) + fmt.Fprintf(w, cs.Muted(" And %d more\n"), remaining) } } diff --git a/pkg/cmd/pr/view/view.go b/pkg/cmd/pr/view/view.go index ccad4fa77..ed5984b46 100644 --- a/pkg/cmd/pr/view/view.go +++ b/pkg/cmd/pr/view/view.go @@ -260,7 +260,7 @@ func printHumanPrPreview(opts *ViewOptions, baseRepo ghrepo.Interface, pr *api.P var md string var err error if pr.Body == "" { - md = fmt.Sprintf("\n %s\n\n", cs.Gray("No description provided")) + md = fmt.Sprintf("\n %s\n\n", cs.Muted("No description provided")) } else { md, err = markdown.Render(pr.Body, markdown.WithTheme(opts.IO.TerminalTheme()), @@ -282,7 +282,7 @@ func printHumanPrPreview(opts *ViewOptions, baseRepo ghrepo.Interface, pr *api.P } // Footer - fmt.Fprintf(out, cs.Gray("View this pull request on GitHub: %s\n"), pr.URL) + fmt.Fprintf(out, cs.Muted("View this pull request on GitHub: %s\n"), pr.URL) return nil } diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go index a32482e65..db0e6ae1d 100644 --- a/pkg/cmd/release/view/view.go +++ b/pkg/cmd/release/view/view.go @@ -129,19 +129,19 @@ func viewRun(opts *ViewOptions) error { } func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error { - iofmt := io.ColorScheme() + cs := io.ColorScheme() w := io.Out - fmt.Fprintf(w, "%s\n", iofmt.Bold(release.TagName)) + fmt.Fprintf(w, "%s\n", cs.Bold(release.TagName)) if release.IsDraft { - fmt.Fprintf(w, "%s • ", iofmt.Red("Draft")) + fmt.Fprintf(w, "%s • ", cs.Red("Draft")) } else if release.IsPrerelease { - fmt.Fprintf(w, "%s • ", iofmt.Yellow("Pre-release")) + fmt.Fprintf(w, "%s • ", cs.Yellow("Pre-release")) } if release.IsDraft { - fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s created this %s", release.Author.Login, text.FuzzyAgo(time.Now(), release.CreatedAt)))) + fmt.Fprintf(w, "%s\n", cs.Mutedf("%s created this %s", release.Author.Login, text.FuzzyAgo(time.Now(), release.CreatedAt))) } else { - fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("%s released this %s", release.Author.Login, text.FuzzyAgo(time.Now(), *release.PublishedAt)))) + fmt.Fprintf(w, "%s\n", cs.Mutedf("%s released this %s", release.Author.Login, text.FuzzyAgo(time.Now(), *release.PublishedAt))) } renderedDescription, err := markdown.Render(release.Body, @@ -153,7 +153,7 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error { fmt.Fprintln(w, renderedDescription) if len(release.Assets) > 0 { - fmt.Fprintf(w, "%s\n", iofmt.Bold("Assets")) + fmt.Fprintf(w, "%s\n", cs.Bold("Assets")) //nolint:staticcheck // SA1019: Showing NAME|SIZE headers adds nothing to table. table := tableprinter.New(io, tableprinter.NoHeader) for _, a := range release.Assets { @@ -168,7 +168,7 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error { fmt.Fprint(w, "\n") } - fmt.Fprintf(w, "%s\n", iofmt.Gray(fmt.Sprintf("View on GitHub: %s", release.URL))) + fmt.Fprintln(w, cs.Mutedf("View on GitHub: %s", release.URL)) return nil } diff --git a/pkg/cmd/repo/license/view/view.go b/pkg/cmd/repo/license/view/view.go index d3e63c241..14228ba36 100644 --- a/pkg/cmd/repo/license/view/view.go +++ b/pkg/cmd/repo/license/view/view.go @@ -119,9 +119,9 @@ func renderLicense(license *api.License, opts *ViewOptions) error { cs := opts.IO.ColorScheme() var out strings.Builder if opts.IO.IsStdoutTTY() { - out.WriteString(fmt.Sprintf("\n%s\n", cs.Gray(license.Description))) - out.WriteString(fmt.Sprintf("\n%s\n", cs.Grayf("To implement: %s", license.Implementation))) - out.WriteString(fmt.Sprintf("\n%s\n\n", cs.Grayf("For more information, see: %s", license.HTMLURL))) + out.WriteString(fmt.Sprintf("\n%s\n", cs.Muted(license.Description))) + out.WriteString(fmt.Sprintf("\n%s\n", cs.Mutedf("To implement: %s", license.Implementation))) + out.WriteString(fmt.Sprintf("\n%s\n\n", cs.Mutedf("For more information, see: %s", license.HTMLURL))) } out.WriteString(license.Body) _, err := opts.IO.Out.Write([]byte(out.String())) diff --git a/pkg/cmd/repo/view/view.go b/pkg/cmd/repo/view/view.go index 06f85d048..b13276c80 100644 --- a/pkg/cmd/repo/view/view.go +++ b/pkg/cmd/repo/view/view.go @@ -181,7 +181,7 @@ func viewRun(opts *ViewOptions) error { var readmeContent string if readme == nil { - readmeContent = cs.Gray("This repository does not have a README") + readmeContent = cs.Muted("This repository does not have a README") } else if isMarkdownFile(readme.Filename) { var err error readmeContent, err = markdown.Render(readme.Content, @@ -197,7 +197,7 @@ func viewRun(opts *ViewOptions) error { description := repo.Description if description == "" { - description = cs.Gray("No description provided") + description = cs.Muted("No description provided") } repoData := struct { @@ -209,7 +209,7 @@ func viewRun(opts *ViewOptions) error { FullName: cs.Bold(fullName), Description: description, Readme: readmeContent, - View: cs.Gray(fmt.Sprintf("View this repository on GitHub: %s", openURL)), + View: cs.Mutedf("View this repository on GitHub: %s", openURL), } return tmpl.Execute(stdout, repoData) diff --git a/pkg/cmd/run/shared/presentation.go b/pkg/cmd/run/shared/presentation.go index 2ec149729..699ea120f 100644 --- a/pkg/cmd/run/shared/presentation.go +++ b/pkg/cmd/run/shared/presentation.go @@ -52,7 +52,7 @@ func RenderAnnotations(cs *iostreams.ColorScheme, annotations []Annotation) stri for _, a := range annotations { lines = append(lines, fmt.Sprintf("%s %s", AnnotationSymbol(cs, a), a.Message)) - lines = append(lines, cs.Grayf("%s: %s#%d\n", a.JobName, a.Path, a.StartLine)) + lines = append(lines, cs.Mutedf("%s: %s#%d\n", a.JobName, a.Path, a.StartLine)) } return strings.Join(lines, "\n") diff --git a/pkg/cmd/run/shared/shared.go b/pkg/cmd/run/shared/shared.go index ce909fd77..8dbf59c41 100644 --- a/pkg/cmd/run/shared/shared.go +++ b/pkg/cmd/run/shared/shared.go @@ -575,7 +575,7 @@ func Symbol(cs *iostreams.ColorScheme, status Status, conclusion Conclusion) (st case Success: return cs.SuccessIconWithColor(noColor), cs.Green case Skipped, Neutral: - return "-", cs.Gray + return "-", cs.Muted default: return cs.FailureIconWithColor(noColor), cs.Red } diff --git a/pkg/cmd/run/view/view.go b/pkg/cmd/run/view/view.go index c794cff9a..d308962ed 100644 --- a/pkg/cmd/run/view/view.go +++ b/pkg/cmd/run/view/view.go @@ -397,7 +397,7 @@ func runView(opts *ViewOptions) error { for _, a := range artifacts { expiredBadge := "" if a.Expired { - expiredBadge = cs.Gray(" (expired)") + expiredBadge = cs.Muted(" (expired)") } fmt.Fprintf(out, "%s%s\n", a.Name, expiredBadge) } @@ -411,7 +411,7 @@ func runView(opts *ViewOptions) error { } else { fmt.Fprintf(out, "For more information about a job, try: gh run view --job=\n") } - fmt.Fprintf(out, cs.Gray("View this run on GitHub: %s\n"), run.URL) + fmt.Fprintln(out, cs.Mutedf("View this run on GitHub: %s", run.URL)) if opts.ExitStatus && shared.IsFailureState(run.Conclusion) { return cmdutil.SilentError @@ -423,7 +423,7 @@ func runView(opts *ViewOptions) error { } else { fmt.Fprintf(out, "To see the full job log, try: gh run view --log --job=%d\n", selectedJob.ID) } - fmt.Fprintf(out, cs.Gray("View this run on GitHub: %s\n"), run.URL) + fmt.Fprintln(out, cs.Mutedf("View this run on GitHub: %s", run.URL)) if opts.ExitStatus && shared.IsFailureState(selectedJob.Conclusion) { return cmdutil.SilentError diff --git a/pkg/cmd/search/commits/commits.go b/pkg/cmd/search/commits/commits.go index bc37684a2..fb1742dc9 100644 --- a/pkg/cmd/search/commits/commits.go +++ b/pkg/cmd/search/commits/commits.go @@ -161,7 +161,7 @@ func displayResults(io *iostreams.IOStreams, now time.Time, results search.Commi tp.AddField(commit.Sha) tp.AddField(text.RemoveExcessiveWhitespace(commit.Info.Message)) tp.AddField(commit.Author.Login) - tp.AddTimeField(now, commit.Info.Author.Date, cs.Gray) + tp.AddTimeField(now, commit.Info.Author.Date, cs.Muted) tp.EndRow() } if io.IsStdoutTTY() { diff --git a/pkg/cmd/search/repos/repos.go b/pkg/cmd/search/repos/repos.go index 0bad650d3..2815ee6dc 100644 --- a/pkg/cmd/search/repos/repos.go +++ b/pkg/cmd/search/repos/repos.go @@ -171,14 +171,14 @@ func displayResults(io *iostreams.IOStreams, now time.Time, results search.Repos tags = append(tags, "archived") } info := strings.Join(tags, ", ") - infoColor := cs.Gray + infoColor := cs.Muted if repo.IsPrivate { infoColor = cs.Yellow } tp.AddField(repo.FullName, tableprinter.WithColor(cs.Bold)) tp.AddField(text.RemoveExcessiveWhitespace(repo.Description)) tp.AddField(info, tableprinter.WithColor(infoColor)) - tp.AddTimeField(now, repo.UpdatedAt, cs.Gray) + tp.AddTimeField(now, repo.UpdatedAt, cs.Muted) tp.EndRow() } if io.IsStdoutTTY() { diff --git a/pkg/cmd/search/shared/shared.go b/pkg/cmd/search/shared/shared.go index f0a346fc8..aefd0c054 100644 --- a/pkg/cmd/search/shared/shared.go +++ b/pkg/cmd/search/shared/shared.go @@ -132,7 +132,7 @@ func displayIssueResults(io *iostreams.IOStreams, now time.Time, et EntityType, } tp.AddField(text.RemoveExcessiveWhitespace(issue.Title)) tp.AddField(listIssueLabels(&issue, cs, tp.IsTTY())) - tp.AddTimeField(now, issue.UpdatedAt, cs.Gray) + tp.AddTimeField(now, issue.UpdatedAt, cs.Muted) tp.EndRow() } diff --git a/pkg/cmd/status/status.go b/pkg/cmd/status/status.go index 0ed7dd3a7..c9acce8bd 100644 --- a/pkg/cmd/status/status.go +++ b/pkg/cmd/status/status.go @@ -740,7 +740,7 @@ func statusRun(opts *StatusOptions) error { errs := sg.authErrors.ToSlice() sort.Strings(errs) for _, msg := range errs { - fmt.Fprintln(out, cs.Gray(fmt.Sprintf("warning: %s", msg))) + fmt.Fprintln(out, cs.Mutedf("warning: %s", msg)) } } diff --git a/pkg/cmd/workflow/view/view.go b/pkg/cmd/workflow/view/view.go index 188d79e22..2e550f496 100644 --- a/pkg/cmd/workflow/view/view.go +++ b/pkg/cmd/workflow/view/view.go @@ -175,7 +175,7 @@ func viewWorkflowContent(opts *ViewOptions, client *api.Client, repo ghrepo.Inte out := opts.IO.Out fileName := workflow.Base() - fmt.Fprintf(out, "%s - %s\n", cs.Bold(workflow.Name), cs.Gray(fileName)) + fmt.Fprintf(out, "%s - %s\n", cs.Bold(workflow.Name), cs.Muted(fileName)) fmt.Fprintf(out, "ID: %s", cs.Cyanf("%d", workflow.ID)) codeBlock := fmt.Sprintf("```yaml\n%s\n```", yaml) diff --git a/pkg/iostreams/color.go b/pkg/iostreams/color.go index 58d13e6ef..a92fdcab3 100644 --- a/pkg/iostreams/color.go +++ b/pkg/iostreams/color.go @@ -142,7 +142,7 @@ func (c *ColorScheme) GreenBold(t string) string { return greenBold(t) } -// Use Muted instead for thematically contrasting color. +// Deprecated: Use Muted instead for thematically contrasting color. func (c *ColorScheme) Gray(t string) string { if !c.enabled { return t @@ -153,7 +153,7 @@ func (c *ColorScheme) Gray(t string) string { return gray(t) } -// Use Mutedf instead for thematically contrasting color. +// Deprecated: Use Mutedf instead for thematically contrasting color. func (c *ColorScheme) Grayf(t string, args ...interface{}) string { return c.Gray(fmt.Sprintf(t, args...)) } @@ -255,7 +255,7 @@ func (c *ColorScheme) ColorFromString(s string) func(string) string { case "green": fn = c.Green case "gray": - fn = c.Gray + fn = c.Muted case "magenta": fn = c.Magenta case "cyan": From c5206109ca9db2d2215b1de68c5ccc0cb9bfa548 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Tue, 8 Apr 2025 15:24:18 -0400 Subject: [PATCH 20/24] Use truthy value for `GH_COLOR_LABELS` --- pkg/cmd/factory/default.go | 9 +++++++-- pkg/cmd/factory/default_test.go | 32 +++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pkg/cmd/factory/default.go b/pkg/cmd/factory/default.go index 68df7a316..6286c999d 100644 --- a/pkg/cmd/factory/default.go +++ b/pkg/cmd/factory/default.go @@ -293,8 +293,13 @@ func ioStreams(f *cmdutil.Factory) *iostreams.IOStreams { io.SetPager(pager.Value) } - if _, ghColorLabels := os.LookupEnv("GH_COLOR_LABELS"); ghColorLabels { - io.SetColorLabels(true) // TODO: should this be a truthy value? + if ghColorLabels, ghColorLabelsExists := os.LookupEnv("GH_COLOR_LABELS"); ghColorLabelsExists { + switch ghColorLabels { + case "", "0", "false", "no": + io.SetColorLabels(false) + default: + io.SetColorLabels(true) + } } else if prompt := cfg.ColorLabels(""); prompt.Value == "enabled" { io.SetColorLabels(true) } diff --git a/pkg/cmd/factory/default_test.go b/pkg/cmd/factory/default_test.go index b1730d6e6..407c2fcdb 100644 --- a/pkg/cmd/factory/default_test.go +++ b/pkg/cmd/factory/default_test.go @@ -449,10 +449,40 @@ func Test_ioStreams_colorLabels(t *testing.T) { colorLabelsEnabled: true, }, { - name: "colorLabels enabled via GH_COLOR_LABELS env var", + name: "colorLabels enabled via `1` in GH_COLOR_LABELS env var", env: map[string]string{"GH_COLOR_LABELS": "1"}, colorLabelsEnabled: true, }, + { + name: "colorLabels enabled via `true` in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "true"}, + colorLabelsEnabled: true, + }, + { + name: "colorLabels enabled via `yes` in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "yes"}, + colorLabelsEnabled: true, + }, + { + name: "colorLabels disable via empty string in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": ""}, + colorLabelsEnabled: false, + }, + { + name: "colorLabels disabled via `0` in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "0"}, + colorLabelsEnabled: false, + }, + { + name: "colorLabels disabled via `false` in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "false"}, + colorLabelsEnabled: false, + }, + { + name: "colorLabels disabled via `no` in GH_COLOR_LABELS env var", + env: map[string]string{"GH_COLOR_LABELS": "no"}, + colorLabelsEnabled: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { From 5c12f5633e3beee887be089a82b0ec52469ad087 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Tue, 8 Apr 2025 15:27:36 -0400 Subject: [PATCH 21/24] Test for explicit config disable of label colors --- pkg/cmd/factory/default_test.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/cmd/factory/default_test.go b/pkg/cmd/factory/default_test.go index 407c2fcdb..c0275d1de 100644 --- a/pkg/cmd/factory/default_test.go +++ b/pkg/cmd/factory/default_test.go @@ -448,6 +448,11 @@ func Test_ioStreams_colorLabels(t *testing.T) { config: enableColorLabelsConfig(), colorLabelsEnabled: true, }, + { + name: "config with colorLabels disabled", + config: disableColorLabelsConfig(), + colorLabelsEnabled: false, + }, { name: "colorLabels enabled via `1` in GH_COLOR_LABELS env var", env: map[string]string{"GH_COLOR_LABELS": "1"}, @@ -611,6 +616,10 @@ func disablePromptConfig() gh.Config { return config.NewFromString("prompt: disabled") } +func disableColorLabelsConfig() gh.Config { + return config.NewFromString("color_labels: disabled") +} + func enableColorLabelsConfig() gh.Config { return config.NewFromString("color_labels: enabled") } From 644dbe6275861cec50b744330576f97b78f978b5 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Tue, 8 Apr 2025 15:56:23 -0400 Subject: [PATCH 22/24] Apply suggestions from code review Co-authored-by: Babak K. Shandiz --- pkg/cmd/pr/status/status.go | 2 +- pkg/cmd/release/view/view.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/cmd/pr/status/status.go b/pkg/cmd/pr/status/status.go index d20522d04..eb120e5a7 100644 --- a/pkg/cmd/pr/status/status.go +++ b/pkg/cmd/pr/status/status.go @@ -316,6 +316,6 @@ func printPrs(io *iostreams.IOStreams, totalCount int, prs ...api.PullRequest) { } remaining := totalCount - len(prs) if remaining > 0 { - fmt.Fprintf(w, cs.Muted(" And %d more\n"), remaining) + fmt.Fprintln(w, cs.Mutedf(" And %d more", remaining)) } } diff --git a/pkg/cmd/release/view/view.go b/pkg/cmd/release/view/view.go index db0e6ae1d..c9030f299 100644 --- a/pkg/cmd/release/view/view.go +++ b/pkg/cmd/release/view/view.go @@ -139,9 +139,9 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error { fmt.Fprintf(w, "%s • ", cs.Yellow("Pre-release")) } if release.IsDraft { - fmt.Fprintf(w, "%s\n", cs.Mutedf("%s created this %s", release.Author.Login, text.FuzzyAgo(time.Now(), release.CreatedAt))) + fmt.Fprintln(w, cs.Mutedf("%s created this %s", release.Author.Login, text.FuzzyAgo(time.Now(), release.CreatedAt))) } else { - fmt.Fprintf(w, "%s\n", cs.Mutedf("%s released this %s", release.Author.Login, text.FuzzyAgo(time.Now(), *release.PublishedAt))) + fmt.Fprintln(w, cs.Mutedf("%s released this %s", release.Author.Login, text.FuzzyAgo(time.Now(), *release.PublishedAt))) } renderedDescription, err := markdown.Render(release.Body, @@ -153,7 +153,7 @@ func renderReleaseTTY(io *iostreams.IOStreams, release *shared.Release) error { fmt.Fprintln(w, renderedDescription) if len(release.Assets) > 0 { - fmt.Fprintf(w, "%s\n", cs.Bold("Assets")) + fmt.Fprintln(w, cs.Bold("Assets")) //nolint:staticcheck // SA1019: Showing NAME|SIZE headers adds nothing to table. table := tableprinter.New(io, tableprinter.NoHeader) for _, a := range release.Assets { From 93e51c583b7f1cfa85c096f83d3abc7589836076 Mon Sep 17 00:00:00 2001 From: Andy Feller Date: Tue, 8 Apr 2025 16:40:39 -0400 Subject: [PATCH 23/24] Update pkg/cmd/run/shared/presentation.go --- pkg/cmd/run/shared/presentation.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/cmd/run/shared/presentation.go b/pkg/cmd/run/shared/presentation.go index 699ea120f..a3556d743 100644 --- a/pkg/cmd/run/shared/presentation.go +++ b/pkg/cmd/run/shared/presentation.go @@ -52,6 +52,7 @@ func RenderAnnotations(cs *iostreams.ColorScheme, annotations []Annotation) stri for _, a := range annotations { lines = append(lines, fmt.Sprintf("%s %s", AnnotationSymbol(cs, a), a.Message)) + // Following newline is essential for spacing between annotations lines = append(lines, cs.Mutedf("%s: %s#%d\n", a.JobName, a.Path, a.StartLine)) } From 7b20ee5549454db7007a6314e002d2e5ab683eca Mon Sep 17 00:00:00 2001 From: Meredith Lancaster Date: Wed, 9 Apr 2025 03:58:20 -0600 Subject: [PATCH 24/24] Merge pull request #10749 from malancas/update-to-sigstore-go-v0.7.1 Update github.com/sigstore/sigstore-go to 0.7.1 and fix breaking function change --- go.mod | 65 +++-- go.sum | 326 +++++++++++++------------- pkg/cmd/attestation/test/data/data.go | 10 +- 3 files changed, 196 insertions(+), 205 deletions(-) diff --git a/go.mod b/go.mod index bea712a2d..c24bd57cc 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/gabriel-vasile/mimetype v1.4.8 github.com/gdamore/tcell/v2 v2.5.4 github.com/golang/snappy v0.0.4 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.20.3 github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 github.com/gorilla/websocket v1.5.3 @@ -40,18 +40,18 @@ require ( github.com/opentracing/opentracing-go v1.2.0 github.com/rivo/tview v0.0.0-20221029100920-c4a7e501810d github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc - github.com/sigstore/protobuf-specs v0.3.3 - github.com/sigstore/sigstore-go v0.7.0 - github.com/spf13/cobra v1.8.1 + github.com/sigstore/protobuf-specs v0.4.1 + github.com/sigstore/sigstore-go v0.7.1 + github.com/spf13/cobra v1.9.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 github.com/zalando/go-keyring v0.2.5 - golang.org/x/crypto v0.35.0 - golang.org/x/sync v0.12.0 - golang.org/x/term v0.30.0 - golang.org/x/text v0.23.0 - google.golang.org/grpc v1.69.4 - google.golang.org/protobuf v1.36.5 + golang.org/x/crypto v0.37.0 + golang.org/x/sync v0.13.0 + golang.org/x/term v0.31.0 + golang.org/x/text v0.24.0 + google.golang.org/grpc v1.71.0 + google.golang.org/protobuf v1.36.6 gopkg.in/h2non/gock.v1 v1.1.2 gopkg.in/yaml.v3 v3.0.1 ) @@ -83,29 +83,29 @@ require ( github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/fatih/color v1.16.0 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/gdamore/encoding v1.0.0 // indirect github.com/go-chi/chi v4.1.2+incompatible // indirect github.com/go-jose/go-jose/v4 v4.0.5 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/validate v0.24.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/google/certificate-transparency-go v1.3.1 // indirect github.com/google/uuid v1.6.0 // indirect github.com/gorilla/css v1.0.1 // indirect github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -116,8 +116,7 @@ require ( github.com/klauspost/compress v1.17.11 // indirect github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/microcosm-cc/bluemonday v1.0.27 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect @@ -129,27 +128,26 @@ require ( github.com/oklog/ulid v1.3.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rodaine/table v1.0.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sassoftware/relic v7.2.1+incompatible // indirect github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 // indirect - github.com/sigstore/rekor v1.3.8 // indirect - github.com/sigstore/sigstore v1.8.12 // indirect - github.com/sigstore/timestamp-authority v1.2.4 // indirect + github.com/sigstore/rekor v1.3.9 // indirect + github.com/sigstore/sigstore v1.9.1 // indirect + github.com/sigstore/timestamp-authority v1.2.5 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.11.0 // indirect - github.com/spf13/cast v1.7.0 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/cast v1.7.1 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/theupdateframework/go-tuf v0.7.0 // indirect @@ -163,18 +161,17 @@ require ( github.com/yuin/goldmark-emoji v1.0.5 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/otel v1.34.0 // indirect + go.opentelemetry.io/otel/metric v1.34.0 // indirect + go.opentelemetry.io/otel/trace v1.34.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect - golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.36.0 // indirect - golang.org/x/sys v0.31.0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/sys v0.32.0 // indirect golang.org/x/tools v0.29.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d // indirect - gopkg.in/ini.v1 v1.67.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect k8s.io/klog/v2 v2.130.1 // indirect ) diff --git a/go.sum b/go.sum index 2b5a31212..a0f64b334 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,17 @@ -cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= -cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= -cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs= -cloud.google.com/go/auth v0.13.0/go.mod h1:COOjD9gwfKNKz+IIduatIhYJQIc0mG3H102r/EMxX6Q= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= -cloud.google.com/go/compute v1.24.0 h1:phWcR2eWzRJaL/kOiJwfFsPs4BaKq1j6vnpZrc1YlVg= +cloud.google.com/go v0.118.3 h1:jsypSnrE/w4mJysioGdMBg4MiW/hHx/sArFpaBWHdME= +cloud.google.com/go v0.118.3/go.mod h1:Lhs3YLnBlwJ4KA6nuObNMZ/fCbOQBPuWKPoE0Wa/9Vc= +cloud.google.com/go/auth v0.15.0 h1:Ly0u4aA5vG/fsSsxu98qCQBemXtAtJf+95z9HK+cxps= +cloud.google.com/go/auth v0.15.0/go.mod h1:WJDGqZ1o9E9wKIL+IwStfyn/+s59zl4Bi+1KQNVXLZ8= +cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M= +cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= -cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= -cloud.google.com/go/kms v1.20.4 h1:CJ0hMpOg1ANN9tx/a/GPJ+Uxudy8k6f3fvGFuTHiE5A= -cloud.google.com/go/kms v1.20.4/go.mod h1:gPLsp1r4FblUgBYPOcvI/bUPpdMg2Jm1ZVKU4tQUfcc= -cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc= -cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI= +cloud.google.com/go/iam v1.4.1 h1:cFC25Nv+u5BkTR/BT1tXdoF2daiVbZ1RLx2eqfQ9RMM= +cloud.google.com/go/iam v1.4.1/go.mod h1:2vUEJpUG3Q9p2UdsyksaKpDzlwOrnMzS30isdReIcLM= +cloud.google.com/go/kms v1.21.1 h1:r1Auo+jlfJSf8B7mUnVw5K0fI7jWyoUy65bV53VjKyk= +cloud.google.com/go/kms v1.21.1/go.mod h1:s0wCyByc9LjTdCjG88toVs70U9W+cc6RKFc8zAqX7nE= +cloud.google.com/go/longrunning v0.6.5 h1:sD+t8DO8j4HKW4QfouCklg7ZC1qC4uzVZt8iz3uTW+Q= +cloud.google.com/go/longrunning v0.6.5/go.mod h1:Et04XK+0TTLKa5IPYryKf5DkpwImy6TluQ1QTLwlKmY= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= @@ -21,18 +20,18 @@ github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjq github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM= github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1 h1:DSDNVxqkoXJiko6x8a90zidoYqnYYa6c1MTzDKzKkTo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.1/go.mod h1:zGqV2R4Cr/k8Uye5w+dgQ06WJtEcbQG/8J7BB6hnCr4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2 h1:F0gBpfdPLGsw+nsgk6aqqkZS1jiixa5WwFe3fk/T3Ys= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.2/go.mod h1:SqINnQ9lVVdRlyC8cd1lCI0SdX4n2paeABd2K8ggfnE= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0 h1:7rKG7UmnrxX4N53TFhkYqjc+kVUZuw0fL8I3Fh+Ld9E= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.0/go.mod h1:Wjo+24QJVhhl/L7jy6w9yzFF2yDOf3cKECAa8ecf9vE= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 h1:eXnN9kaS8TiDwXjoie3hMRLuwdUBUMW9KRgOqB3mCaw= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0/go.mod h1:XIpam8wumeZ5rVMuhdDQLMfIPDf1WO3IzrCRO3e3e3o= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1 h1:gUDtaZk8heteyfdmv+pcfHvhR9llnh7c7GMwZ8RVG04= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3 h1:H5xDQaE3XowWfhZRUpnfC+rGZMEVoSiji+b+/HFAPU4= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.3/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -53,36 +52,36 @@ github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4u github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.8 h1:cZV+NUS/eGxKXMtmyhtYPJ7Z4YLoI/V8bkTdRZfYhGo= -github.com/aws/aws-sdk-go-v2 v1.32.8/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/config v1.28.10 h1:fKODZHfqQu06pCzR69KJ3GuttraRJkhlC8g80RZ0Dfg= -github.com/aws/aws-sdk-go-v2/config v1.28.10/go.mod h1:PvdxRYZ5Um9QMq9PQ0zHHNdtKK+he2NHtFCUFMXWXeg= -github.com/aws/aws-sdk-go-v2/credentials v1.17.51 h1:F/9Sm6Y6k4LqDesZDPJCLxQGXNNHd/ZtJiWd0lCZKRk= -github.com/aws/aws-sdk-go-v2/credentials v1.17.51/go.mod h1:TKbzCHm43AoPyA+iLGGcruXd4AFhF8tOmLex2R9jWNQ= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23 h1:IBAoD/1d8A8/1aA8g4MBVtTRHhXRiNAgwdbo/xRM2DI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.23/go.mod h1:vfENuCM7dofkgKpYzuzf1VT1UKkA/YL3qanfBn7HCaA= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27 h1:jSJjSBzw8VDIbWv+mmvBSP8ezsztMYJGH+eKqi9AmNs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.27/go.mod h1:/DAhLbFRgwhmvJdOfSm+WwikZrCuUJiA4WgJG0fTNSw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27 h1:l+X4K77Dui85pIj5foXDhPlnqcNRG2QUyvca300lXh8= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.27/go.mod h1:KvZXSFEXm6x84yE8qffKvT3x8J5clWnVFXphpohhzJ8= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8 h1:cWno7lefSH6Pp+mSznagKCgfDGeZRin66UvYUqAkyeA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.8/go.mod h1:tPD+VjU3ABTBoEJ3nctu5Nyg4P4yjqSH5bJGGkY4+XE= -github.com/aws/aws-sdk-go-v2/service/kms v1.37.8 h1:KbLZjYqhQ9hyB4HwXiheiflTlYQa0+Fz0Ms/rh5f3mk= -github.com/aws/aws-sdk-go-v2/service/kms v1.37.8/go.mod h1:ANs9kBhK4Ghj9z1W+bsr3WsNaPF71qkgd6eE6Ekol/Y= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.9 h1:YqtxripbjWb2QLyzRK9pByfEDvgg95gpC2AyDq4hFE8= -github.com/aws/aws-sdk-go-v2/service/sso v1.24.9/go.mod h1:lV8iQpg6OLOfBnqbGMBKYjilBlf633qwHnBEiMSPoHY= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8 h1:6dBT1Lz8fK11m22R+AqfRsFn8320K0T5DTGxxOQBSMw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.8/go.mod h1:/kiBvRQXBc6xeJTYzhSdGvJ5vm1tjaDEjH+MSeRJnlY= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.6 h1:VwhTrsTuVn52an4mXx29PqRzs2Dvu921NpGk7y43tAM= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.6/go.mod h1:+8h7PZb3yY5ftmVLD7ocEoE98hdc8PoKS0H3wfx1dlc= -github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= -github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk= +github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= +github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= +github.com/aws/aws-sdk-go-v2/config v1.29.10 h1:yNjgjiGBp4GgaJrGythyBXg2wAs+Im9fSWIUwvi1CAc= +github.com/aws/aws-sdk-go-v2/config v1.29.10/go.mod h1:A0mbLXSdtob/2t59n1X0iMkPQ5d+YzYZB4rwu7SZ7aA= +github.com/aws/aws-sdk-go-v2/credentials v1.17.63 h1:rv1V3kIJ14pdmTu01hwcMJ0WAERensSiD9rEWEBb1Tk= +github.com/aws/aws-sdk-go-v2/credentials v1.17.63/go.mod h1:EJj+yDf0txT26Ulo0VWTavBl31hOsaeuMxIHu2m0suY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= +github.com/aws/aws-sdk-go-v2/service/kms v1.38.1 h1:tecq7+mAav5byF+Mr+iONJnCBf4B4gon8RSp4BrweSc= +github.com/aws/aws-sdk-go-v2/service/kms v1.38.1/go.mod h1:cQn6tAF77Di6m4huxovNM7NVAozWTZLsDRp9t8Z/WYk= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= +github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2 h1:wK8O+j2dOolmpNVY1EWIbLgxrGCHJKVPm08Hv/u80M8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.2/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= +github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= +github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= +github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8= @@ -131,7 +130,6 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= @@ -167,8 +165,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= @@ -177,8 +175,6 @@ github.com/gdamore/tcell/v2 v2.5.4 h1:TGU4tSjD3sCL788vFNeJnTdzpNKIw1H5dgLnJRQVv/ github.com/gdamore/tcell/v2 v2.5.4/go.mod h1:dZgRy5v4iMobMEcWNYBtREnDZAT9DYmfqIkrgEMxLyw= github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= -github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE= github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -188,8 +184,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= +github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -202,32 +198,34 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= 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/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go= github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= -github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= @@ -236,8 +234,8 @@ github.com/google/trillian v1.7.1 h1:+zX8jLM3524bAMPS+VxaDIDgsMv3/ty6DuLWerHXcek github.com/google/trillian v1.7.1/go.mod h1:E1UMAHqpZCA8AQdrKdWmHmtUfSeiD0sDWD1cv00Xa+c= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= +github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q= github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= @@ -265,13 +263,12 @@ github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0S github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw= github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= -github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= +github.com/hashicorp/vault/api v1.16.0 h1:nbEYGJiAPGzT9U4oWgaaB0g+Rj8E59QuHKyA5LhwQN4= +github.com/hashicorp/vault/api v1.16.0/go.mod h1:KhuUhzOD8lDSk29AtzNjgAu2kxRA9jL9NAbkFlqvkBA= github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU= github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= @@ -330,10 +327,8 @@ github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2T github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec/go.mod h1:TmwEoGCwIti7BCeJ9hescZgRtatxRE+A72pCoPfmcfk= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -378,8 +373,8 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -387,12 +382,12 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= -github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rivo/tview v0.0.0-20221029100920-c4a7e501810d h1:jKIUJdMcIVGOSHi6LSqJqw9RqblyblE2ZrHvFbWR3S0= @@ -409,10 +404,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= github.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4= @@ -429,51 +422,44 @@ github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJL github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc/go.mod h1:zqMwyHmnN/eDOZOdiTohqIUKUrTFX62PNlu7IJdu0q8= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0= github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE= -github.com/sigstore/protobuf-specs v0.3.3 h1:RMZQgXTD/pF7KW6b5NaRLYxFYZ/wzx44PQFXN2PEo5g= -github.com/sigstore/protobuf-specs v0.3.3/go.mod h1:vIhZ6Uor1a38+wvRrKcqL2PtYNlgoIW9lhzYzkyy4EU= -github.com/sigstore/rekor v1.3.8 h1:B8kJI8mpSIXova4Jxa6vXdJyysRxFGsEsLKBDl0rRjA= -github.com/sigstore/rekor v1.3.8/go.mod h1:/dHFYKSuxEygfDRnEwyJ+ZD6qoVYNXQdi1mJrKvKWsI= -github.com/sigstore/sigstore v1.8.12 h1:S8xMVZbE2z9ZBuQUEG737pxdLjnbOIcFi5v9UFfkJFc= -github.com/sigstore/sigstore v1.8.12/go.mod h1:+PYQAa8rfw0QdPpBcT+Gl3egKD9c+TUgAlF12H3Nmjo= -github.com/sigstore/sigstore-go v0.7.0 h1:bIGPc2IbnbxnzlqQcKlh1o96bxVJ4yRElpP1gHrOH48= -github.com/sigstore/sigstore-go v0.7.0/go.mod h1:4RrCK+i+jhx7lyOG2Vgef0/kFLbKlDI1hrioUYvkxxA= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12 h1:EC3UmIaa7nV9sCgSpVevmvgvTYTkMqyrRbj5ojPp7tE= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.12/go.mod h1:aw60vs3crnQdM/DYH+yF2P0MVKtItwAX34nuaMrY7Lk= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12 h1:FPpliDTywSy0woLHMAdmTSZ5IS/lVBZ0dY0I+2HmnSY= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.12/go.mod h1:NkPiz4XA0JcBSXzJUrjMj7Xi7oSTew1Ip3Zmt56mHlw= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.12 h1:kweBChR6M9FEvmxN3BMEcl7SNnwxTwKF7THYFKLOE5U= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.12/go.mod h1:6+d+A6oYt1W5OgtzgEVb21V7tAZ/C2Ihtzc5MNJbayY= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.12 h1:jvY1B9bjP+tKzdKDyuq5K7O19CG2IKzGJNTy5tuL2Gs= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.8.12/go.mod h1:2uEeOb8xE2RC6OvzxKux1wkS39Zv8gA27z92m49xUTc= -github.com/sigstore/timestamp-authority v1.2.4 h1:RjXZxOWorEiem/uSr0pFHVtQpyzpcFxgugo5jVqm3mw= -github.com/sigstore/timestamp-authority v1.2.4/go.mod h1:ExrbobKdEuwuBptZIiKp1IaVBRiUeKbiuSyZTO8Okik= +github.com/sigstore/protobuf-specs v0.4.1 h1:5SsMqZbdkcO/DNHudaxuCUEjj6x29tS2Xby1BxGU7Zc= +github.com/sigstore/protobuf-specs v0.4.1/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc= +github.com/sigstore/rekor v1.3.9 h1:sUjRpKVh/hhgqGMs0t+TubgYsksArZ6poLEC3MsGAzU= +github.com/sigstore/rekor v1.3.9/go.mod h1:xThNUhm6eNEmkJ/SiU/FVU7pLY2f380fSDZFsdDWlcM= +github.com/sigstore/sigstore v1.9.1 h1:bNMsfFATsMPaagcf+uppLk4C9rQZ2dh5ysmCxQBYWaw= +github.com/sigstore/sigstore v1.9.1/go.mod h1:zUoATYzR1J3rLNp3jmp4fzIJtWdhC3ZM6MnpcBtnsE4= +github.com/sigstore/sigstore-go v0.7.1 h1:lyzi3AjO6+BHc5zCf9fniycqPYOt3RaC08M/FRmQhVY= +github.com/sigstore/sigstore-go v0.7.1/go.mod h1:AIRj4I3LC82qd07VFm3T2zXYiddxeBV1k/eoS8nTz0E= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1 h1:/YcNq687WnXpIRXl04nLfJX741G4iW+w+7Nem2Zy0f4= +github.com/sigstore/sigstore/pkg/signature/kms/aws v1.9.1/go.mod h1:ApL9RpKsi7gkSYN0bMNdm/3jZ9EefxMmfYHfUmq2ZYM= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1 h1:FnusXyTIInnwfIOzzl5PFilRm1I97dxMSOcCkZBu9Kc= +github.com/sigstore/sigstore/pkg/signature/kms/azure v1.9.1/go.mod h1:d5m5LOa/69a+t2YC9pDPwS1n2i/PhqB4cUKbpVDlKKE= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1 h1:LFiYK1DEWQ6Hf/nroFzBMM+s5rVSjVL45Alpb5Ctl5A= +github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.9.1/go.mod h1:GFyFmDsE2wDuIHZD+4+JErGpA0S4zJsKNz5l2JVJd8s= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1 h1:sIW6xe4yU5eIMH8fve2C78d+r29KmHnIb+7po+80bsY= +github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.9.1/go.mod h1:3pNf99GnK9eu3XUa5ebHzgEQSVYf9hqAoPFwbwD6O6M= +github.com/sigstore/timestamp-authority v1.2.5 h1:W22JmwRv1Salr/NFFuP7iJuhytcZszQjldoB8GiEdnw= +github.com/sigstore/timestamp-authority v1.2.5/go.mod h1:gWPKWq4HMWgPCETre0AakgBzcr9DRqHrsgbrRqsigOs= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= -github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= +github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -484,6 +470,12 @@ github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if37 github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= +github.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI= +github.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0= +github.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw= +github.com/tink-crypto/tink-go/v2 v2.3.0 h1:4/TA0lw0lA/iVKBL9f8R5eP7397bfc4antAMXF5JRhs= +github.com/tink-crypto/tink-go/v2 v2.3.0/go.mod h1:kfPOtXIadHlekBTeBtJrHWqoGL+Fm3JQg0wtltPuxLU= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= @@ -504,22 +496,22 @@ go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= -go.step.sm/crypto v0.57.0 h1:YjoRQDaJYAxHLVwjst0Bl0xcnoKzVwuHCJtEo2VSHYU= -go.step.sm/crypto v0.57.0/go.mod h1:+Lwp5gOVPaTa3H/Ul/TzGbxQPXZZcKIUGMS0lG6n9Go= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I= +go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= +go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= +go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.step.sm/crypto v0.60.0 h1:UgSw8DFG5xUOGB3GUID17UA32G4j1iNQ4qoMhBmsVFw= +go.step.sm/crypto v0.60.0/go.mod h1:Ep83Lv818L4gV0vhFTdPWRKnL6/5fRMpi8SaoP5ArSw= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= @@ -528,24 +520,24 @@ go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= -golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw= golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= -golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA= -golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -555,46 +547,44 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.216.0 h1:xnEHy+xWFrtYInWPy8OdGFsyIfWJjtVnO39g7pz2BFY= -google.golang.org/api v0.216.0/go.mod h1:K9wzQMvWi47Z9IU7OgdOofvZuw75Ge3PPITImZR/UyI= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d h1:xJJRGY7TJcvIlpSrN3K6LAWgNFUILlO+OMAqtg9aqnw= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250102185135-69823020774d/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc= +google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE= +google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950= +google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= gopkg.in/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/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/h2non/gock.v1 v1.1.2/go.mod h1:n7UGz/ckNChHiK05rDoiC4MYSunEC/lyaUm2WWaDva0= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 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= diff --git a/pkg/cmd/attestation/test/data/data.go b/pkg/cmd/attestation/test/data/data.go index b33efaa28..ef3c35c20 100644 --- a/pkg/cmd/attestation/test/data/data.go +++ b/pkg/cmd/attestation/test/data/data.go @@ -5,13 +5,17 @@ import ( "testing" "github.com/sigstore/sigstore-go/pkg/bundle" - sgData "github.com/sigstore/sigstore-go/pkg/testing/data" ) //go:embed sigstore-js-2.1.0-bundle.json var SigstoreBundleRaw []byte -// SigstoreBundle returns a test *sigstore.Bundle +// SigstoreBundle returns a test sigstore-go bundle.Bundle func SigstoreBundle(t *testing.T) *bundle.Bundle { - return sgData.TestBundle(t, SigstoreBundleRaw) + b := &bundle.Bundle{} + err := b.UnmarshalJSON(SigstoreBundleRaw) + if err != nil { + t.Fatalf("failed to unmarshal sigstore bundle: %v", err) + } + return b }