Merge branch 'move-predicate-type-filtering' of github.com:malancas/cli into move-predicate-type-filtering
This commit is contained in:
commit
cdfb1b7279
21 changed files with 409 additions and 139 deletions
36
go.mod
36
go.mod
|
|
@ -9,9 +9,9 @@ require (
|
|||
github.com/MakeNowJust/heredoc v1.0.0
|
||||
github.com/briandowns/spinner v1.18.1
|
||||
github.com/cenkalti/backoff/v4 v4.3.0
|
||||
github.com/charmbracelet/glamour v0.8.0
|
||||
github.com/charmbracelet/lipgloss v0.12.1
|
||||
github.com/cli/go-gh/v2 v2.11.2
|
||||
github.com/charmbracelet/glamour v0.9.2-0.20250319212134-549f544650e3
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250319133953-166f707985bc
|
||||
github.com/cli/go-gh/v2 v2.12.0
|
||||
github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24
|
||||
github.com/cli/oauth v1.1.1
|
||||
github.com/cli/safeexec v1.0.1
|
||||
|
|
@ -47,9 +47,9 @@ require (
|
|||
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.11.0
|
||||
golang.org/x/term v0.29.0
|
||||
golang.org/x/text v0.22.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
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
|
|
@ -57,13 +57,20 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.1 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
github.com/alessio/shellescape v1.4.2 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/blang/semver v3.5.1+incompatible // indirect
|
||||
github.com/charmbracelet/x/ansi v0.1.4 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cli/browser v1.3.0 // indirect
|
||||
github.com/cli/shurcooL-graphql v0.0.4 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
|
|
@ -99,6 +106,7 @@ require (
|
|||
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
|
||||
github.com/itchyny/gojq v0.12.15 // indirect
|
||||
|
|
@ -110,12 +118,14 @@ require (
|
|||
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/mattn/go-runewidth v0.0.15 // 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
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
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
|
||||
|
|
@ -130,6 +140,7 @@ require (
|
|||
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
|
||||
|
|
@ -147,8 +158,9 @@ require (
|
|||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/transparency-dev/merkle v0.0.2 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // indirect
|
||||
github.com/yuin/goldmark v1.7.4 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.3 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
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
|
||||
|
|
@ -159,7 +171,7 @@ require (
|
|||
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.30.0 // indirect
|
||||
golang.org/x/sys v0.31.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
|
||||
|
|
|
|||
78
go.sum
78
go.sum
|
|
@ -13,6 +13,8 @@ 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=
|
||||
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=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=
|
||||
|
|
@ -33,6 +35,12 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1 h1:gUDtaZk8het
|
|||
github.com/AzureAD/microsoft-authentication-library-for-go v1.3.1/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=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
|
|
@ -91,19 +99,25 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3
|
|||
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs=
|
||||
github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw=
|
||||
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
|
||||
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
|
||||
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
|
||||
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4 h1:6KzMkQeAF56rggw2NZu1L+TH7j9+DM1/2Kmh7KUxg1I=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240715153702-9ba8adf781c4/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/glamour v0.9.2-0.20250319212134-549f544650e3 h1:hx6E25SvI2WiZdt/gxINcYBnHD7PE2Vr9auqwg5B05g=
|
||||
github.com/charmbracelet/glamour v0.9.2-0.20250319212134-549f544650e3/go.mod h1:ihVqv4/YOY5Fweu1cxajuQrwJFh3zU4Ukb4mHVNjq3s=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250319133953-166f707985bc h1:nFRtCfZu/zkltd2lsLUPlVNv3ej/Atod9hcdbRZtlys=
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250319133953-166f707985bc/go.mod h1:aKC/t2arECF6rNOnaKaVU6y4t4ZeHQzqfxedE/VkVhA=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/cli/browser v1.0.0/go.mod h1:IEWkHYbLjkhtjwwWlwTHW2lGxeS5gezEQBMLTwDHf5Q=
|
||||
github.com/cli/browser v1.3.0 h1:LejqCrpWr+1pRqmEPDGnTZOjsMe7sehifLynZJuqJpo=
|
||||
github.com/cli/browser v1.3.0/go.mod h1:HH8s+fOAxjhQoBUAsKuPCbqUuxZDhQ2/aD+SzsEfBTk=
|
||||
github.com/cli/go-gh/v2 v2.11.2 h1:oad1+sESTPNTiTvh3I3t8UmxuovNDxhwLzeMHk45Q9w=
|
||||
github.com/cli/go-gh/v2 v2.11.2/go.mod h1:vVFhi3TfjseIW26ED9itAR8gQK0aVThTm8sYrsZ5QTI=
|
||||
github.com/cli/go-gh/v2 v2.12.0 h1:PIurZ13fXbWDbr2//6ws4g4zDbryO+iDuTpiHgiV+6k=
|
||||
github.com/cli/go-gh/v2 v2.12.0/go.mod h1:+5aXmEOJsH9fc9mBHfincDwnS02j2AIA/DsTH0Bk5uw=
|
||||
github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24 h1:QDrhR4JA2n3ij9YQN0u5ZeuvRIIvsUGmf5yPlTS0w8E=
|
||||
github.com/cli/go-internal v0.0.0-20241025142207-6c48bcd5ce24/go.mod h1:rr9GNING0onuVw8MnracQHn7PcchnFlP882Y0II2KZk=
|
||||
github.com/cli/oauth v1.1.1 h1:459gD3hSjlKX9B1uXBuiAMdpXBUQ9QGf/NDcCpoQxPs=
|
||||
|
|
@ -266,6 +280,8 @@ github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u
|
|||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=
|
||||
github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/in-toto/attestation v1.1.1 h1:QD3d+oATQ0dFsWoNh5oT0udQ3tUrOsZZ0Fc3tSgWbzI=
|
||||
github.com/in-toto/attestation v1.1.1/go.mod h1:Dcq1zVwA2V7Qin8I7rgOi+i837wEf/mOZwRm047Sjys=
|
||||
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
|
||||
|
|
@ -308,6 +324,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1 h1:xd8bzARK3dErqkPFtoF9F3/HgN8UQk0ed1YDKpEz01A=
|
||||
github.com/leaanthony/go-ansi-parser v1.6.1/go.mod h1:+vva/2y4alzVmmIEpk9QDhA7vLC5zKDTRwfZGOp3IWU=
|
||||
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec h1:2tTW6cDth2TSgRbAhD7yjZzTQmcN25sDRPEeinR51yQ=
|
||||
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=
|
||||
|
|
@ -325,8 +343,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
|||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
|
|
@ -334,14 +352,18 @@ github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwX
|
|||
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
|
||||
github.com/microsoft/dev-tunnels v0.0.25 h1:UlMKUI+2O8cSu4RlB52ioSyn1LthYSVkJA+CSTsdKoA=
|
||||
github.com/microsoft/dev-tunnels v0.0.25/go.mod h1:frU++12T/oqxckXkDpTuYa427ncguEOodSPZcGCCrzQ=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38 h1:0FrBxrkJ0hVembTb/e4EU5Ml6vLcOusAqymmYISg5Uo=
|
||||
github.com/muhammadmuzzammil1998/jsonc v0.0.0-20201229145248-615b0916ca38/go.mod h1:saF2fIVw4banK0H4+/EuqfFLpRnoy5S+ECwTOCcRcSU=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
|
|
@ -401,6 +423,8 @@ github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
|||
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
|
||||
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
|
||||
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/githubv4 v0.0.0-20240120211514-18a1ae0e79dc h1:vH0NQbIDk+mJLvBliNGfcQgUmhlniWBDXC79oRxfZA0=
|
||||
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=
|
||||
|
|
@ -466,12 +490,14 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG
|
|||
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
|
||||
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4=
|
||||
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
|
||||
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
|
||||
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
|
||||
github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8=
|
||||
github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
|
|
@ -518,8 +544,8 @@ 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/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.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
|
||||
golang.org/x/sync v0.12.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=
|
||||
|
|
@ -529,19 +555,19 @@ 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.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.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.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
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/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.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ func NewWithWriter(w io.Writer, isTTY bool, maxWidth int, cs *iostreams.ColorSch
|
|||
tp.AddHeader(
|
||||
upperCasedHeaders,
|
||||
WithPadding(paddingFunc),
|
||||
WithColor(cs.LightGrayUnderline),
|
||||
WithColor(cs.TableHeader),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -162,6 +162,9 @@ func TestDeleteRun(t *testing.T) {
|
|||
tt.opts.IO = ios
|
||||
|
||||
cfg := config.NewFromString(tt.config)
|
||||
cfg.WriteFunc = func() error {
|
||||
return nil
|
||||
}
|
||||
tt.opts.Config = func() (gh.Config, error) {
|
||||
return cfg, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -30,58 +30,107 @@ 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.
|
||||
## Understanding Verification
|
||||
|
||||
To specify the artifact, the command requires:
|
||||
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 nature of the claim)
|
||||
|
||||
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.
|
||||
|
||||
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, among others.
|
||||
|
||||
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--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.
|
||||
|
||||
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 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://<image-uri>%[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
|
||||
|
|
@ -181,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 <owner>/<repo>")
|
||||
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 <owner>/<repo>")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Workflow that signed attestation in the format [host/]<owner>/<repo>/<path>/<to>/<workflow>")
|
||||
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 (<owner>/<repo>)")
|
||||
verifyCmd.Flags().StringVarP(&opts.SignerWorkflow, "signer-workflow", "", "", "Enforce that the workflow that signed the attestation matches the provided value ([host/]<owner>/<repo>/<path>/<to>/<workflow>)")
|
||||
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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -486,7 +486,7 @@ func Test_listRun(t *testing.T) {
|
|||
)
|
||||
},
|
||||
wantOut: heredoc.Docf(`
|
||||
%[1]s[0;2;4;37mID %[1]s[0m %[1]s[0;2;4;37mDESCRIPTION %[1]s[0m %[1]s[0;2;4;37mFILES %[1]s[0m %[1]s[0;2;4;37mVISIBILITY%[1]s[0m %[1]s[0;2;4;37mUPDATED %[1]s[0m
|
||||
%[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
|
||||
`, "\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)
|
||||
cs := iostreams.NewColorScheme(tt.color, false, false, iostreams.NoTheme)
|
||||
|
||||
matched := false
|
||||
got, err := highlightMatch(tt.input, regex, &matched, cs.Blue, cs.Highlight)
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@ func Test_viewRun(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n # foo \n\n",
|
||||
wantOut: "cicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n # foo \n\n",
|
||||
},
|
||||
{
|
||||
name: "multiple files, trailing newlines",
|
||||
|
|
@ -277,7 +277,7 @@ func Test_viewRun(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n \n • foo \n\n",
|
||||
wantOut: "some files\n\ncicada.txt\n\nbwhiizzzbwhuiiizzzz\n\nfoo.md\n\n\n \n • foo \n\n",
|
||||
},
|
||||
{
|
||||
name: "multiple files, raw",
|
||||
|
|
|
|||
|
|
@ -496,7 +496,7 @@ func Test_createRun(t *testing.T) {
|
|||
`MaintainerCanModify: false`,
|
||||
`Body:`,
|
||||
``,
|
||||
` my body `,
|
||||
` my body `,
|
||||
``,
|
||||
``,
|
||||
},
|
||||
|
|
@ -568,7 +568,7 @@ func Test_createRun(t *testing.T) {
|
|||
`MaintainerCanModify: false`,
|
||||
`Body:`,
|
||||
``,
|
||||
` BODY `,
|
||||
` BODY `,
|
||||
``,
|
||||
``,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ func TestPRReview_interactive(t *testing.T) {
|
|||
assert.Equal(t, heredoc.Doc(`
|
||||
Got:
|
||||
|
||||
cool story
|
||||
cool story
|
||||
|
||||
`), output.String())
|
||||
assert.Equal(t, "✓ Approved pull request OWNER/REPO#123\n", output.Stderr())
|
||||
|
|
|
|||
|
|
@ -144,15 +144,15 @@ func Test_viewRun(t *testing.T) {
|
|||
wantStdout: heredoc.Doc(`
|
||||
v1.2.3
|
||||
MonaLisa released this about 1 day ago
|
||||
|
||||
|
||||
• Fixed bugs
|
||||
|
||||
|
||||
|
||||
|
||||
• Fixed bugs
|
||||
|
||||
|
||||
Assets
|
||||
windows.zip 12 B
|
||||
linux.tgz 34 B
|
||||
|
||||
|
||||
View on GitHub: https://github.com/OWNER/REPO/releases/tags/v1.2.3
|
||||
`),
|
||||
wantStderr: ``,
|
||||
|
|
@ -169,8 +169,8 @@ func Test_viewRun(t *testing.T) {
|
|||
v1.2.3
|
||||
MonaLisa released this about 1 day ago
|
||||
|
||||
|
||||
• Fixed bugs
|
||||
|
||||
• Fixed bugs
|
||||
|
||||
|
||||
Assets
|
||||
|
|
|
|||
|
|
@ -260,7 +260,7 @@ func Test_ViewRun(t *testing.T) {
|
|||
social distancing
|
||||
|
||||
|
||||
# truly cool readme check it out
|
||||
# truly cool readme check it out
|
||||
|
||||
|
||||
|
||||
|
|
@ -279,7 +279,7 @@ func Test_ViewRun(t *testing.T) {
|
|||
social distancing
|
||||
|
||||
|
||||
# truly cool readme check it out
|
||||
# truly cool readme check it out
|
||||
|
||||
|
||||
|
||||
|
|
@ -297,7 +297,7 @@ func Test_ViewRun(t *testing.T) {
|
|||
social distancing
|
||||
|
||||
|
||||
# truly cool readme check it out
|
||||
# truly cool readme check it out
|
||||
|
||||
|
||||
|
||||
|
|
@ -312,7 +312,7 @@ func Test_ViewRun(t *testing.T) {
|
|||
social distancing
|
||||
|
||||
|
||||
# truly cool readme check it out
|
||||
# truly cool readme check it out
|
||||
|
||||
|
||||
|
||||
|
|
@ -650,7 +650,7 @@ func Test_ViewRun_HandlesSpecialCharacters(t *testing.T) {
|
|||
Some basic special characters " & / < > '
|
||||
|
||||
|
||||
# < is always > than & ' and "
|
||||
# < is always > than & ' and "
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ var HelpTopics = []helpTopic{
|
|||
|
||||
The %[1]s--template%[1]s flag requires a string argument in Go template syntax, and will only print
|
||||
those JSON values which match the query.
|
||||
|
||||
In addition to the Go template functions in the standard library, the following functions can be used
|
||||
with this formatting directive:
|
||||
- %[1]sautocolor%[1]s: like %[1]scolor%[1]s, but only emits color to terminals
|
||||
|
|
@ -151,6 +152,14 @@ var HelpTopics = []helpTopic{
|
|||
- %[1]struncate <length> <input>%[1]s: ensures input fits within length
|
||||
- %[1]shyperlink <url> <text>%[1]s: renders a terminal hyperlink
|
||||
|
||||
The following Sprig template library functions can also be used with this formatting directive:
|
||||
- %[1]scontains <arg> <string>%[1]s: checks if %[1]sstring%[1]s contains %[1]sarg%[1]s
|
||||
- %[1]shasPrefix <prefix> <string>%[1]s: checks if %[1]sstring%[1]s starts with %[1]sprefix%[1]s
|
||||
- %[1]shasSuffix <suffix> <string>%[1]s: checks if %[1]sstring%[1]s ends with %[1]ssuffix%[1]s
|
||||
- %[1]sregexMatch <regex> <string>%[1]s: checks if %[1]sstring%[1]s has any matches for %[1]sregex%[1]s
|
||||
|
||||
For more information about the Sprig library, see <https://masterminds.github.io/sprig/>.
|
||||
|
||||
To learn more about Go templates, see: <https://golang.org/pkg/text/template/>.
|
||||
`, "`"),
|
||||
example: heredoc.Doc(`
|
||||
|
|
|
|||
|
|
@ -62,6 +62,8 @@ func NewCmdList(f *cmdutil.Factory, runF func(*ListOptions) error) *cobra.Comman
|
|||
|
||||
Note that providing the %[1]sworkflow_name%[1]s to the %[1]s-w%[1]s flag will not fetch disabled workflows.
|
||||
Also pass the %[1]s-a%[1]s flag to fetch disabled workflow runs using the %[1]sworkflow_name%[1]s and the %[1]s-w%[1]s flag.
|
||||
|
||||
Runs created by organization and enterprise ruleset workflows will not display a workflow name due to GitHub API limitations.
|
||||
`, "`"),
|
||||
Aliases: []string{"ls"},
|
||||
Args: cobra.NoArgs,
|
||||
|
|
|
|||
|
|
@ -366,6 +366,44 @@ func TestListRun(t *testing.T) {
|
|||
completed stale cool commit CI trunk push 10 4m34s 2021-02-23T04:51:00Z
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "org ruleset workflow in runs list shows with empty workflow name",
|
||||
opts: &ListOptions{
|
||||
Limit: defaultLimit,
|
||||
now: shared.TestRunStartTime.Add(time.Minute*4 + time.Second*34),
|
||||
},
|
||||
isTTY: true,
|
||||
stubs: func(reg *httpmock.Registry) {
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "repos/OWNER/REPO/actions/runs"),
|
||||
httpmock.JSONResponse(shared.RunsPayload{
|
||||
WorkflowRuns: shared.TestRunsWithOrgRequiredWorkflows,
|
||||
}))
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows"),
|
||||
httpmock.JSONResponse(workflowShared.WorkflowsPayload{
|
||||
Workflows: []workflowShared.Workflow{
|
||||
shared.TestWorkflow,
|
||||
},
|
||||
}))
|
||||
reg.Register(
|
||||
httpmock.REST("GET", "repos/OWNER/REPO/actions/workflows/456"),
|
||||
httpmock.StatusStringResponse(404, "not found"),
|
||||
)
|
||||
},
|
||||
wantOut: heredoc.Doc(`
|
||||
STATUS TITLE WORKFLOW BRANCH EVENT ID ELAPSED AGE
|
||||
X cool commit trunk push 1 4m34s about 4 minutes ago
|
||||
* cool commit trunk push 2 4m34s about 4 minutes ago
|
||||
✓ cool commit trunk push 3 4m34s about 4 minutes ago
|
||||
X cool commit trunk push 4 4m34s about 4 minutes ago
|
||||
X cool commit CI trunk push 5 4m34s about 4 minutes ago
|
||||
- cool commit CI trunk push 6 4m34s about 4 minutes ago
|
||||
- cool commit CI trunk push 7 4m34s about 4 minutes ago
|
||||
* cool commit CI trunk push 8 4m34s about 4 minutes ago
|
||||
* cool commit CI trunk push 9 4m34s about 4 minutes ago
|
||||
`),
|
||||
},
|
||||
{
|
||||
name: "pagination",
|
||||
opts: &ListOptions{
|
||||
|
|
|
|||
|
|
@ -446,6 +446,18 @@ func preloadWorkflowNames(client *api.Client, repo ghrepo.Interface, runs []Run)
|
|||
if _, ok := workflowMap[run.WorkflowID]; !ok {
|
||||
// Look up workflow by ID because it may have been deleted
|
||||
workflow, err := workflowShared.GetWorkflow(client, repo, run.WorkflowID)
|
||||
// If the error is an httpError and it is a 404, this is likely a
|
||||
// organization or enterprise ruleset workflow. The user does not
|
||||
// have permissions to view the details of the workflow, so we cannot
|
||||
// look it up directly without receiving a 404, but it is nonetheless
|
||||
// in the workflow run list. To handle this, we set the workflow name
|
||||
// to an empty string.
|
||||
// Deciding to put this here instead of in GetWorkflow to allow
|
||||
// the caller to decide what a 404 means.
|
||||
if httpErr, ok := err.(api.HTTPError); ok && httpErr.StatusCode == 404 {
|
||||
workflowMap[run.WorkflowID] = ""
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ func TestRunWithCommit(id int64, s Status, c Conclusion, commit string) Run {
|
|||
return TestRunWithWorkflowAndCommit(123, id, s, c, commit)
|
||||
}
|
||||
|
||||
func TestRunWithOrgRequiredWorkflow(id int64, s Status, c Conclusion, commit string) Run {
|
||||
return TestRunWithWorkflowAndCommit(456, id, s, c, commit)
|
||||
}
|
||||
|
||||
func TestRunWithWorkflowAndCommit(workflowId, runId int64, s Status, c Conclusion, commit string) Run {
|
||||
return Run{
|
||||
WorkflowID: workflowId,
|
||||
|
|
@ -57,6 +61,18 @@ var TestRuns []Run = []Run{
|
|||
TestRun(10, Completed, Stale),
|
||||
}
|
||||
|
||||
var TestRunsWithOrgRequiredWorkflows []Run = []Run{
|
||||
TestRunWithOrgRequiredWorkflow(1, Completed, TimedOut, "cool commit"),
|
||||
TestRunWithOrgRequiredWorkflow(2, InProgress, "", "cool commit"),
|
||||
TestRunWithOrgRequiredWorkflow(3, Completed, Success, "cool commit"),
|
||||
TestRunWithOrgRequiredWorkflow(4, Completed, Cancelled, "cool commit"),
|
||||
TestRun(5, Completed, Failure),
|
||||
TestRun(6, Completed, Neutral),
|
||||
TestRun(7, Completed, Skipped),
|
||||
TestRun(8, Requested, ""),
|
||||
TestRun(9, Queued, ""),
|
||||
}
|
||||
|
||||
var WorkflowRuns []Run = []Run{
|
||||
TestRun(2, InProgress, ""),
|
||||
SuccessfulRun,
|
||||
|
|
|
|||
|
|
@ -9,34 +9,45 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
NoTheme = "none"
|
||||
DarkTheme = "dark"
|
||||
LightTheme = "light"
|
||||
highlightStyle = "black:yellow"
|
||||
)
|
||||
|
||||
// Special cases like darkThemeTableHeader / lightThemeTableHeader are necessary when using color and modifiers
|
||||
// (bold, underline, dim) because ansi.ColorFunc requires a foreground color and resets formats.
|
||||
var (
|
||||
magenta = ansi.ColorFunc("magenta")
|
||||
cyan = ansi.ColorFunc("cyan")
|
||||
red = ansi.ColorFunc("red")
|
||||
yellow = ansi.ColorFunc("yellow")
|
||||
blue = ansi.ColorFunc("blue")
|
||||
green = ansi.ColorFunc("green")
|
||||
gray = ansi.ColorFunc("black+h")
|
||||
lightGrayUnderline = ansi.ColorFunc("white+du")
|
||||
bold = ansi.ColorFunc("default+b")
|
||||
cyanBold = ansi.ColorFunc("cyan+b")
|
||||
greenBold = ansi.ColorFunc("green+b")
|
||||
highlightStart = ansi.ColorCode(highlightStyle)
|
||||
highlight = ansi.ColorFunc(highlightStyle)
|
||||
magenta = ansi.ColorFunc("magenta")
|
||||
cyan = ansi.ColorFunc("cyan")
|
||||
red = ansi.ColorFunc("red")
|
||||
yellow = ansi.ColorFunc("yellow")
|
||||
blue = ansi.ColorFunc("blue")
|
||||
green = ansi.ColorFunc("green")
|
||||
gray = ansi.ColorFunc("black+h")
|
||||
bold = ansi.ColorFunc("default+b")
|
||||
cyanBold = ansi.ColorFunc("cyan+b")
|
||||
greenBold = ansi.ColorFunc("green+b")
|
||||
highlightStart = ansi.ColorCode(highlightStyle)
|
||||
highlight = ansi.ColorFunc(highlightStyle)
|
||||
darkThemeTableHeader = ansi.ColorFunc("white+du")
|
||||
lightThemeTableHeader = ansi.ColorFunc("black+hu")
|
||||
noThemeTableHeader = ansi.ColorFunc("default+u")
|
||||
|
||||
gray256 = func(t string) string {
|
||||
return fmt.Sprintf("\x1b[%d;5;%dm%s\x1b[m", 38, 242, t)
|
||||
}
|
||||
)
|
||||
|
||||
func NewColorScheme(enabled, is256enabled bool, trueColor bool) *ColorScheme {
|
||||
// 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 {
|
||||
return &ColorScheme{
|
||||
enabled: enabled,
|
||||
is256enabled: is256enabled,
|
||||
hasTrueColor: trueColor,
|
||||
theme: theme,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -44,6 +55,7 @@ type ColorScheme struct {
|
|||
enabled bool
|
||||
is256enabled bool
|
||||
hasTrueColor bool
|
||||
theme string
|
||||
}
|
||||
|
||||
func (c *ColorScheme) Enabled() bool {
|
||||
|
|
@ -115,13 +127,6 @@ func (c *ColorScheme) Grayf(t string, args ...interface{}) string {
|
|||
return c.Gray(fmt.Sprintf(t, args...))
|
||||
}
|
||||
|
||||
func (c *ColorScheme) LightGrayUnderline(t string) string {
|
||||
if !c.enabled {
|
||||
return t
|
||||
}
|
||||
return lightGrayUnderline(t)
|
||||
}
|
||||
|
||||
func (c *ColorScheme) Magenta(t string) string {
|
||||
if !c.enabled {
|
||||
return t
|
||||
|
|
@ -254,3 +259,19 @@ func (c *ColorScheme) HexToRGB(hex string, x string) string {
|
|||
b, _ := strconv.ParseInt(hex[4:6], 16, 64)
|
||||
return fmt.Sprintf("\033[38;2;%d;%d;%dm%s\033[0m", r, g, b, x)
|
||||
}
|
||||
|
||||
func (c *ColorScheme) TableHeader(t string) string {
|
||||
// Table headers are only stylized if color is enabled including underline modifier.
|
||||
if !c.enabled {
|
||||
return t
|
||||
}
|
||||
|
||||
switch c.theme {
|
||||
case DarkTheme:
|
||||
return darkThemeTableHeader(t)
|
||||
case LightTheme:
|
||||
return lightThemeTableHeader(t)
|
||||
default:
|
||||
return noThemeTableHeader(t)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package iostreams
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -19,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),
|
||||
cs: NewColorScheme(true, true, true, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "no truecolor",
|
||||
hex: "fc0303",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(true, true, false),
|
||||
cs: NewColorScheme(true, true, false, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "no color",
|
||||
hex: "fc0303",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(false, false, false),
|
||||
cs: NewColorScheme(false, false, false, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "invalid hex",
|
||||
hex: "fc0",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(false, false, false),
|
||||
cs: NewColorScheme(false, false, false, NoTheme),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -63,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),
|
||||
cs: NewColorScheme(true, true, true, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "no truecolor",
|
||||
hex: "fc0303",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(true, true, false),
|
||||
cs: NewColorScheme(true, true, false, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "no color",
|
||||
hex: "fc0303",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(false, false, false),
|
||||
cs: NewColorScheme(false, false, false, NoTheme),
|
||||
},
|
||||
{
|
||||
name: "invalid hex",
|
||||
hex: "fc0",
|
||||
text: "red",
|
||||
wants: "red",
|
||||
cs: NewColorScheme(false, false, false),
|
||||
cs: NewColorScheme(false, false, false, NoTheme),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -93,3 +94,84 @@ func TestHexToRGB(t *testing.T) {
|
|||
assert.Equal(t, tt.wants, output)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTableHeader(t *testing.T) {
|
||||
reset := "\x1b[0m"
|
||||
defaultUnderline := "\x1b[0;4;39m"
|
||||
brightBlackUnderline := "\x1b[0;4;90m"
|
||||
dimBlackUnderline := "\x1b[0;2;4;37m"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cs *ColorScheme
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "when color is disabled, text is not stylized",
|
||||
cs: NewColorScheme(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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
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),
|
||||
input: "this should have light foreground color and underlined",
|
||||
expected: fmt.Sprintf("%sthis should have light foreground color and underlined%s", dimBlackUnderline, reset),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.expected, tt.cs.TableHeader(tt.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -366,7 +366,7 @@ func (s *IOStreams) TerminalWidth() int {
|
|||
}
|
||||
|
||||
func (s *IOStreams) ColorScheme() *ColorScheme {
|
||||
return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor())
|
||||
return NewColorScheme(s.ColorEnabled(), s.ColorSupport256(), s.HasTrueColor(), s.TerminalTheme())
|
||||
}
|
||||
|
||||
func (s *IOStreams) ReadUserFile(fn string) ([]byte, error) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue