Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix upgrade bug for GitHub Actions container refs #85

Merged
merged 1 commit into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions command/command_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions command/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ func Test_loadYAMLFiles(t *testing.T) {
"c.yml": "",
"circleci.yml": "",
"cloudbuild.yml": "",
"docker.yml": "",
"drone.yml": "",
"github-crazy-indent.yml": "github.yml",
"github-issue-80.yml": "",
Expand Down
8 changes: 6 additions & 2 deletions command/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type UpgradeCommand struct {
flagConcurrency int64
flagParser string
flagOut string
flagPin bool
}

func (c *UpgradeCommand) Desc() string {
Expand All @@ -55,6 +56,7 @@ func (c *UpgradeCommand) Flags() *flag.FlagSet {
"maximum number of concurrent resolutions")
f.StringVar(&c.flagParser, "parser", "actions", "parser to use")
f.StringVar(&c.flagOut, "out", "", "output path (defaults to input file)")
f.BoolVar(&c.flagPin, "pin", true, "pin resolved upgraded versions (defaults to true)")

return f
}
Expand Down Expand Up @@ -94,8 +96,10 @@ func (c *UpgradeCommand) Run(ctx context.Context, originalArgs []string) error {
return fmt.Errorf("failed to upgrade refs: %w", err)
}

if err := parser.Pin(ctx, res, par, files.nodes(), c.flagConcurrency); err != nil {
return fmt.Errorf("failed to pin upgraded refs: %w", err)
if c.flagPin {
if err := parser.Pin(ctx, res, par, files.nodes(), c.flagConcurrency); err != nil {
return fmt.Errorf("failed to pin upgraded refs: %w", err)
}
}

for _, f := range files {
Expand Down
10 changes: 10 additions & 0 deletions parser/actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,16 @@ import (

type Actions struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (a *Actions) DenormalizeRef(ref string) string {
isContainer := strings.HasPrefix(ref, resolver.ContainerProtocol)
ref = resolver.DenormalizeRef(ref)
if isContainer {
return "docker://" + ref
}
return ref
}

// Parse pulls the GitHub Actions refs from the documents.
func (a *Actions) Parse(nodes []*yaml.Node) (*RefsList, error) {
var refs RefsList
Expand Down
5 changes: 5 additions & 0 deletions parser/circleci.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (

type CircleCI struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (c *CircleCI) DenormalizeRef(ref string) string {
return resolver.DenormalizeRef(ref)
}

// Parse pulls the CircleCI refs from the documents. Unfortunately it does not
// process "orbs" because there is no documented API for resolving orbs to an
// absolute version.
Expand Down
5 changes: 5 additions & 0 deletions parser/cloudbuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (

type CloudBuild struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (c *CloudBuild) DenormalizeRef(ref string) string {
return resolver.DenormalizeRef(ref)
}

// Parse pulls the Google Cloud Build refs from the documents.
func (c *CloudBuild) Parse(nodes []*yaml.Node) (*RefsList, error) {
var refs RefsList
Expand Down
5 changes: 5 additions & 0 deletions parser/drone.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (

type Drone struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (d *Drone) DenormalizeRef(ref string) string {
return resolver.DenormalizeRef(ref)
}

// Parse pulls the Drone Ci refs from the documents.
func (d *Drone) Parse(nodes []*yaml.Node) (*RefsList, error) {
var refs RefsList
Expand Down
5 changes: 5 additions & 0 deletions parser/gitlabci.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (

type GitLabCI struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (c *GitLabCI) DenormalizeRef(ref string) string {
return resolver.DenormalizeRef(ref)
}

// Parse pulls the image references from GitLab CI configuration files. It does
// not support references with variables.
func (c *GitLabCI) Parse(nodes []*yaml.Node) (*RefsList, error) {
Expand Down
6 changes: 4 additions & 2 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
// Parser defines an interface which parses references out of the given yaml
// node.
type Parser interface {
DenormalizeRef(ref string) string
Parse(nodes []*yaml.Node) (*RefsList, error)
}

Expand Down Expand Up @@ -220,11 +221,12 @@ func Upgrade(ctx context.Context, res resolver.Resolver, parser Parser, nodes []
merrLock.Unlock()
}

denormLatest := resolver.DenormalizeRef(latest)
denormRef := parser.DenormalizeRef(ref)
denormLatest := parser.DenormalizeRef(latest)

for _, node := range nodes {
node.LineComment = appendOriginalToComment(node.LineComment, denormLatest)
node.Value = denormLatest
node.Value = strings.Replace(node.Value, denormRef, denormLatest, 1)
}
}()
}
Expand Down
16 changes: 10 additions & 6 deletions parser/tekton.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ import (

type Tekton struct{}

// DenormalizeRef changes the resolved ref into a ref that the parser expects.
func (t *Tekton) DenormalizeRef(ref string) string {
return resolver.DenormalizeRef(ref)
}

// Parse pulls the Tekton Ci refs from the documents.
func (d *Tekton) Parse(nodes []*yaml.Node) (*RefsList, error) {
func (t *Tekton) Parse(nodes []*yaml.Node) (*RefsList, error) {
var refs RefsList
for i, node := range nodes {
if err := d.parseOne(&refs, node); err != nil {
if err := t.parseOne(&refs, node); err != nil {
return nil, fmt.Errorf("failed to parse node %d: %w", i, err)
}
}
Expand Down Expand Up @@ -48,17 +53,17 @@ func (d *Tekton) parseOne(refs *RefsList, node *yaml.Node) error {

return nil
}
func (d *Tekton) findSpecs(refs *RefsList, node *yaml.Node) error {

func (d *Tekton) findSpecs(refs *RefsList, node *yaml.Node) {
for i, specsMap := range node.Content {
if specsMap.Value == "spec" {
specs := node.Content[i+1]
d.findImages(refs, specs)
}
}
return nil
}

func (d *Tekton) findImages(refs *RefsList, node *yaml.Node) error {
func (d *Tekton) findImages(refs *RefsList, node *yaml.Node) {
for i, property := range node.Content {
if property.Value == "image" {
image := node.Content[i+1]
Expand All @@ -69,5 +74,4 @@ func (d *Tekton) findImages(refs *RefsList, node *yaml.Node) error {
d.findImages(refs, property)
}
}
return nil
}
6 changes: 3 additions & 3 deletions resolver/actions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
"testing"
)

func TestResolve(t *testing.T) {
func TestActions_Resolve(t *testing.T) {
t.Parallel()

ctx := context.Background()
Expand Down Expand Up @@ -57,7 +57,7 @@ func TestResolve(t *testing.T) {
}
}

func TestLatestVersion(t *testing.T) {
func TestActions_LatestVersion(t *testing.T) {
t.Parallel()

ctx := context.Background()
Expand Down Expand Up @@ -125,7 +125,7 @@ func TestLatestVersion(t *testing.T) {
}
}

func TestParseRef(t *testing.T) {
func TestParseActionRef(t *testing.T) {
t.Parallel()

cases := []struct {
Expand Down
56 changes: 56 additions & 0 deletions resolver/container_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package resolver

import (
"context"
"regexp"
"testing"
)

func TestContainer_Resolve(t *testing.T) {
t.Parallel()

ctx := context.Background()
resolver, err := NewContainer(ctx)
if err != nil {
t.Fatal(err)
}

cases := []struct {
name string
in string
exp string
}{
{
name: "default",
in: "alpine:3",
exp: "index.docker.io/library/alpine@sha256:[0-9a-f]{64}",
},
{
name: "sha",
in: "alpine@sha256:dabf91b69c191a1a0a1628fd6bdd029c0c4018041c7f052870bb13c5a222ae76",
exp: "alpine@sha256:dabf91b69c191a1a0a1628fd6bdd029c0c4018041c7f052870bb13c5a222ae76",
},
}

for _, tc := range cases {
tc := tc

t.Run(tc.name, func(t *testing.T) {
t.Parallel()

result, err := resolver.Resolve(ctx, tc.in)
if err != nil {
t.Fatal(err)
}

match, err := regexp.MatchString(tc.exp, result)
if err != nil {
t.Fatal(err)
}

if !match {
t.Errorf("expected %q to match %q", result, tc.exp)
}
})
}
}
8 changes: 8 additions & 0 deletions testdata/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
jobs:
docker:
steps:
- name: 'docker'
uses: 'docker://alpine:3'
with:
entrypoint: '/bin/sh'
args: '-euc "test -n "/bin/sh"'