From d57cc1a53c022d3f87c4820bc6b64384a06c8a07 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Thu, 25 Apr 2024 12:13:22 -0700 Subject: [PATCH] Refactor formatting adjustments (#81) Instead of depending on a diff to restore newlines, this completely changes the algorithm to compare the input contents with the raw render YAML contents (before any mutations) to identify changes. We compute the places where newlines need to be injected, run Ratchet, and then insert those newlines back after modification. There are also now many more tests and a test harness that is more sustainable for adding tests and edge cases. This also introduces an intentionally-undocumented environment variable that dumps debugging information about whitespace computations when set. This will help with issue debugging. This results in a slight behavior change: YAML contents _will_ have consistently-formatted indentation now, but all newline whitespace changes are preserved, including block scalars. Fixes GH-80 --- .github/workflows/release.yml | 2 +- .github/workflows/test.yml | 9 +- command/check.go | 2 +- command/command.go | 164 +++---- command/command_test.go | 578 +++++++----------------- command/pin.go | 5 +- command/unpin.go | 5 +- command/update.go | 5 +- command/upgrade.go | 5 +- go.mod | 24 +- go.sum | 69 +-- testdata/a.golden.yml | 16 + testdata/a.yml | 16 + testdata/b.golden.yml | 33 ++ testdata/b.yml | 33 ++ testdata/c.golden.yml | 21 + testdata/c.yml | 26 ++ testdata/cloudbuild.yml | 6 +- testdata/github-issue-80.yml | 46 ++ testdata/github.yml | 20 +- testdata/no-trailing-newline.golden.yml | 1 + testdata/no-trailing-newline.yml | 1 + testdata/tekton.yml | 2 +- 23 files changed, 491 insertions(+), 598 deletions(-) create mode 100644 testdata/a.golden.yml create mode 100644 testdata/a.yml create mode 100644 testdata/b.golden.yml create mode 100644 testdata/b.yml create mode 100644 testdata/c.golden.yml create mode 100644 testdata/c.yml create mode 100644 testdata/github-issue-80.yml create mode 100644 testdata/no-trailing-newline.golden.yml create mode 100644 testdata/no-trailing-newline.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0e97de7cad..24c99bc96b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -21,7 +21,7 @@ jobs: - uses: 'actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491' # ratchet:actions/setup-go@v5 with: - go-version: '1.21' + go-version-file: 'go.mod' - uses: 'docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20' # ratchet:docker/login-action@v3 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 0a45e6844a..257ab51d9f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,8 +21,13 @@ jobs: - uses: 'actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491' # ratchet:actions/setup-go@v5 with: - go-version: '1.21' + go-version-file: 'go.mod' - name: 'Run tests' run: |- - go test -count=1 -shuffle=on -timeout=10m -race ./... + go test \ + -count=1 \ + -shuffle=on \ + -timeout=10m \ + -race \ + ./... diff --git a/command/check.go b/command/check.go index 40753f2029..e41f1aa81b 100644 --- a/command/check.go +++ b/command/check.go @@ -62,7 +62,7 @@ func (c *CheckCommand) Run(ctx context.Context, originalArgs []string) error { fsys := os.DirFS(".") - files, err := loadYAMLFiles(fsys, args, false) + files, err := loadYAMLFiles(fsys, args) if err != nil { return err } diff --git a/command/command.go b/command/command.go index 203e33a5af..f09d35a865 100644 --- a/command/command.go +++ b/command/command.go @@ -9,14 +9,13 @@ import ( "fmt" "io/fs" "os" + "slices" + "strconv" "strings" // Using banydonk/yaml instead of the default yaml pkg because the default // pkg incorrectly escapes unicode. https://github.com/go-yaml/yaml/issues/737 "github.com/braydonk/yaml" - "github.com/hexops/gotextdiff" - "github.com/hexops/gotextdiff/myers" - "github.com/hexops/gotextdiff/span" "github.com/sethvargo/ratchet/internal/version" ) @@ -93,24 +92,48 @@ func extractCommandAndArgs(args []string) (string, []string) { } // marshalYAML encodes the yaml node into the given writer. -func marshalYAML(m *yaml.Node) ([]byte, error) { +func marshalYAML(m *yaml.Node) (string, error) { var b bytes.Buffer enc := yaml.NewEncoder(&b) enc.SetIndent(2) + enc.SetAssumeBlockAsLiteral(true) if err := enc.Encode(m); err != nil { - return nil, fmt.Errorf("failed to encode yaml: %w", err) + return "", fmt.Errorf("failed to encode yaml: %w", err) } if err := enc.Close(); err != nil { - return nil, fmt.Errorf("failed to finalize yaml: %w", err) + return "", fmt.Errorf("failed to finalize yaml: %w", err) } - return b.Bytes(), nil + return b.String(), nil } type loadResult struct { path string node *yaml.Node - contents []byte + contents string + newlines []int +} + +func (r *loadResult) marshalYAML() (string, error) { + contents, err := marshalYAML(r.node) + if err != nil { + return "", err + } + + // Restore newlines + lines := strings.Split(contents, "\n") + + for _, v := range r.newlines { + lines = slices.Insert(lines, v, "") + } + + // Handle the edge case where a document starts with "---", which the Go YAML + // parser discards. + if strings.HasPrefix(strings.TrimSpace(r.contents), "---") && !strings.HasPrefix(contents, "---") { + lines = slices.Insert(lines, 0, "---") + } + + return strings.Join(lines, "\n"), nil } type loadResults []*loadResult @@ -123,7 +146,7 @@ func (r loadResults) nodes() []*yaml.Node { return n } -func loadYAMLFiles(fsys fs.FS, paths []string, format bool) (loadResults, error) { +func loadYAMLFiles(fsys fs.FS, paths []string) (loadResults, error) { r := make(loadResults, 0, len(paths)) for _, pth := range paths { @@ -134,94 +157,75 @@ func loadYAMLFiles(fsys fs.FS, paths []string, format bool) (loadResults, error) } var node yaml.Node - if err := yaml.Unmarshal(contents, &node); err != nil { + dec := yaml.NewDecoder(bytes.NewReader(contents)) + dec.SetScanBlockScalarAsLiteral(true) + if err := dec.Decode(&node); err != nil { return nil, fmt.Errorf("failed to parse yaml for %s: %w", pth, err) } - lr := &loadResult{ - path: pth, - node: &node, - contents: contents, - } - if format { - if err := fixIndentation(lr); err != nil { - return nil, fmt.Errorf("failed to format indentation: %w", err) - } + // Remarshal the content before any modification so we can compute the + // places where a newline should be inserted post-rendering. + remarshaled, err := marshalYAML(&node) + if err != nil { + return nil, fmt.Errorf("failed to remarshal yaml for %s: %w", pth, err) } - r = append(r, lr) + newlines := computeNewlineTargets(string(contents), remarshaled) + + r = append(r, &loadResult{ + path: pth, + node: &node, + contents: string(contents), + newlines: newlines, + }) } return r, nil } -// fixIndentation corrects the indentation for the given loadResult and edits it in-place. -func fixIndentation(f *loadResult) error { - updated, err := marshalYAML(f.node) - if err != nil { - return fmt.Errorf("failed to marshal yaml for %s: %w", f.path, err) - } - lines := strings.Split(string(f.contents), "\n") - afterLines := strings.Split(string(updated), "\n") - - editedLines := []string{} - afterIndex := 0 - // Loop through both lists line by line using a two-pointer technique. - for _, l := range lines { - token := strings.TrimSpace(l) - if token == "" { - editedLines = append(editedLines, l) - continue +func computeNewlineTargets(before, after string) []int { + before = strings.TrimPrefix(before, "---\n") + + debug, _ := strconv.ParseBool(os.Getenv("RATCHET_DEBUG_NEWLINE_PARSING")) + if debug { + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "Original content:\n") + for i, l := range strings.Split(string(before), "\n") { + fmt.Fprintf(os.Stderr, "%3d: %s\n", i, l) } - currentAfterLine := afterLines[afterIndex] - indexInAfterLine := strings.Index(currentAfterLine, token) - for indexInAfterLine == -1 { - afterIndex++ - currentAfterLine = afterLines[afterIndex] - indexInAfterLine = strings.Index(currentAfterLine, token) + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "Rendered content:\n") + for i, l := range strings.Split(after, "\n") { + fmt.Fprintf(os.Stderr, "%3d: %s\n", i, l) } - - lineWithCorrectIndent := currentAfterLine[:indexInAfterLine] + token - editedLines = append(editedLines, lineWithCorrectIndent) - afterIndex++ + fmt.Fprintf(os.Stderr, "\n") } - f.contents = []byte(strings.Join(editedLines, "\n")) - return nil -} + result := make([]int, 0, 8) + afteri, afterLines := 0, strings.Split(after, "\n") + beforeLines := strings.Split(before, "\n") -func removeNewLineChanges(beforeContent, afterContent string) string { - lines := strings.Split(beforeContent, "\n") - edits := myers.ComputeEdits(span.URIFromPath("before.txt"), beforeContent, afterContent) - unified := gotextdiff.ToUnified("before.txt", "after.txt", beforeContent, edits) - - editedLines := make(map[int]string) - // Iterates through all changes and only keep changes to lines that are not empty. - for _, h := range unified.Hunks { - // Changes are in-order of delete line followed by insert line for lines that were modified. - // We want to locate the position of all deletes of non-empty lines and replace - // these in the original content with the modified line. - var deletePositions []int - inserts := 0 - for i, l := range h.Lines { - if l.Kind == gotextdiff.Delete && l.Content != "\n" { - deletePositions = append(deletePositions, h.FromLine+i-1-inserts) - } - if l.Kind == gotextdiff.Insert && l.Content != "" { - pos := deletePositions[0] - deletePositions = deletePositions[1:] - editedLines[pos] = strings.TrimSuffix(l.Content, "\n") - inserts++ - } + for beforei := 0; beforei < len(beforeLines); beforei++ { + if afteri >= len(afterLines) { + result = append(result, beforei) + continue } - } - var formattedLines []string - for i, line := range lines { - if editedLine, ok := editedLines[i]; ok { - formattedLines = append(formattedLines, editedLine) + + beforeLine := strings.TrimSpace(beforeLines[beforei]) + afterLine := strings.TrimSpace(afterLines[afteri]) + + if beforeLine != afterLine && beforeLine == "" { + result = append(result, beforei) } else { - formattedLines = append(formattedLines, line) + afteri++ } } - return strings.Join(formattedLines, "\n") + + if debug { + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "newline indicies: %v\n", result) + fmt.Fprintf(os.Stderr, "\n") + } + + return result } diff --git a/command/command_test.go b/command/command_test.go index cb780d2cff..c0aa4f9d02 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -1,310 +1,115 @@ package command import ( + "io/fs" "os" - "reflect" "testing" + "testing/fstest" "github.com/google/go-cmp/cmp" ) -const ( - yamlA = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 - - - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 - - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlAChanges = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlAChangesFormatted = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlB = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 - - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlBChanges = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlBChangesFormatted = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlC = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - name: 'Checkout' - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 +func Test_loadYAMLFiles(t *testing.T) { + t.Parallel() - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlCChanges = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - name: 'Checkout' - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlCChangesFormatted = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - id : 'print' - runs: 'echo "hello"' - - name: 'Checkout' - uses: 'actions/checkout@9239842384293848238sfsdf823e234234234sds' # ratchet:actions/checkout@v3 + fsys := os.DirFS("../testdata") + + cases := map[string]string{ + "a.yml": "a.golden.yml", + "b.yml": "b.golden.yml", + "c.yml": "", + "circleci.yml": "", + "cloudbuild.yml": "", + "drone.yml": "", + "github-crazy-indent.yml": "github.yml", + "github-issue-80.yml": "", + "github.yml": "", + "gitlabci.yml": "", + "no-trailing-newline.yml": "no-trailing-newline.golden.yml", + "tekton.yml": "", + } - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@sdfswdf23423423423423sdfsdfsdfsdfdsfsdf2' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' -` - yamlD = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + for input, expected := range cases { + inputFilename, expectedFilename := input, expected - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' - thing: |- - this is my string - it has many lines + t.Run(inputFilename, func(t *testing.T) { + t.Parallel() - some of them even - have new lines -` - yamlDChanges = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' - thing: |- - this is my string - it has many lines + files, err := loadYAMLFiles(fsys, []string{inputFilename}) + if err != nil { + t.Fatal(err) + } - some of them even - have new lines -` - yamlDChangesFormatted = ` -jobs: - init: - runs-on: 'ubuntu-latest' - outputs: - directories: '${{ steps.dirs.outputs.directories }}' - steps: - - name: 'Checkout' - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + got, err := files[0].marshalYAML() + if err != nil { + t.Fatal(err) + } - - name: 'Guardian Directories' - id: 'dirs' - uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main - with: - directories: '${{ inputs.directories }}' - thing: |- - this is my string - it has many lines + if expectedFilename == "" { + expectedFilename = inputFilename + } + want, err := fsys.(fs.ReadFileFS).ReadFile(expectedFilename) + if err != nil { + t.Fatal(err) + } - some of them even - have new lines -` -) + if got != string(want) { + t.Errorf("expected\n\n%s\n\nto be\n\n%s\n", got, want) + } + }) + } +} -func Test_removeNewLineChanges(t *testing.T) { +func Test_computeNewlineTargets_simple(t *testing.T) { t.Parallel() cases := []struct { - name string - yamlBefore string - yamlAfter string - want string + name string + before string + after string + want []int }{ { - name: "yamlA_multiple_empty_lines", - yamlBefore: yamlA, - yamlAfter: yamlAChanges, - want: yamlAChangesFormatted, + name: "empty", + before: "", + after: "", + want: []int{}, }, { - name: "yamlB_single_empty_line", - yamlBefore: yamlB, - yamlAfter: yamlBChanges, - want: yamlBChangesFormatted, + name: "single_newline", + before: "\n", + after: "\n", + want: []int{}, }, { - name: "yamlC_long_unchanged_section", - yamlBefore: yamlC, - yamlAfter: yamlCChanges, - want: yamlCChangesFormatted, + name: "leading_whitespace", + before: "\n\nfoo", + after: "foo", + want: []int{0, 1}, }, { - name: "yamlD_multiline_string", - yamlBefore: yamlD, - yamlAfter: yamlDChanges, - want: yamlDChangesFormatted, + name: "trailing_whitespace", + before: "foo\nbar\n\n", + after: "foo\nbar", + want: []int{2, 3}, + }, + { + name: "interior_whitespace", + before: "foo\n\nbar\n\n\nbaz", + after: "foo\nbar\nbaz", + want: []int{1, 3, 4}, + }, + { + name: "interior_whitespace_leading_lines", + before: "foo\n\n bar\n\n\nbaz", + after: "foo\nbar\nbaz", + want: []int{1, 3, 4}, + }, + { + name: "interior_whitespace_tailing_lines", + before: "foo\n\nbar \n\n\nbaz", + after: "foo\nbar\nbaz", + want: []int{1, 3, 4}, }, } @@ -314,158 +119,81 @@ func Test_removeNewLineChanges(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - got := removeNewLineChanges(tc.yamlBefore, tc.yamlAfter) - - if !reflect.DeepEqual(got, tc.want) { - t.Errorf("expected %s to be %s", got, tc.want) + got := computeNewlineTargets(tc.before, tc.after) + if diff := cmp.Diff(got, tc.want); diff != "" { + t.Errorf("unexpected diff (+got, -want):\n%s", diff) } }) } } -func Test_loadYAMLFiles(t *testing.T) { +func Test_unmarshalMarshal(t *testing.T) { t.Parallel() cases := []struct { - name string - yamlFilenames []string - format bool - fixNewlines bool - want string + name string + yaml string + want []int }{ { - name: "yamlA_multiple_empty_lines", - yamlFilenames: []string{"testdata/github.yml"}, - format: false, - fixNewlines: false, - want: `jobs: - my_job: - runs-on: 'ubuntu-latest' - container: - image: 'ubuntu:20.04' - services: - nginx: - image: 'nginx:1.21' - steps: - - uses: 'actions/checkout@v3' - - uses: 'docker://ubuntu:20.04' - with: - uses: '/path/to/user.png' - image: '/path/to/image.jpg' - - runs: |- - echo "Hello 😀" - if [ "true" == "false" ]; - echo "NOPE" - fi - other_job: - uses: 'my-org/my-repo/.github/workflows/my-workflow.yml@v0' - final_job: - uses: './local/path/to/action' -`, + name: "single", + yaml: "\nAPPARENTLY_THIS_IS_VALID_YAML\n", + want: []int{0}, }, { - name: "handles-leading-dot-slash", - yamlFilenames: []string{"./testdata/github.yml"}, - format: false, - fixNewlines: false, - want: `jobs: - my_job: - runs-on: 'ubuntu-latest' - container: - image: 'ubuntu:20.04' - services: - nginx: - image: 'nginx:1.21' - steps: - - uses: 'actions/checkout@v3' - - uses: 'docker://ubuntu:20.04' - with: - uses: '/path/to/user.png' - image: '/path/to/image.jpg' - - runs: |- - echo "Hello 😀" - if [ "true" == "false" ]; - echo "NOPE" - fi - other_job: - uses: 'my-org/my-repo/.github/workflows/my-workflow.yml@v0' - final_job: - uses: './local/path/to/action' + name: "multiline", + yaml: `--- +stages: + - build + - test + +build-code-job: + stage: build + image: + name: gcr.io/distroless/static-debian11:nonroot + entrypoint: [""] + script: + - echo "Job 1" + +test-code-job1: + stage: test + image: node:12 + script: + - echo "Job 2" `, + want: []int{3, 11}, }, { - name: "yaml_steps_indent_change", - yamlFilenames: []string{"testdata/github.yml"}, - format: true, - fixNewlines: true, - want: `jobs: - my_job: - runs-on: 'ubuntu-latest' - - container: - image: 'ubuntu:20.04' - - services: - nginx: - image: 'nginx:1.21' - - steps: - - uses: 'actions/checkout@v3' - - - uses: 'docker://ubuntu:20.04' - with: - uses: '/path/to/user.png' - image: '/path/to/image.jpg' - - - runs: |- - echo "Hello 😀" - if [ "true" == "false" ]; - echo "NOPE" - fi - - other_job: - uses: 'my-org/my-repo/.github/workflows/my-workflow.yml@v0' - - final_job: - uses: './local/path/to/action' + name: "folded_block_scalar", + yaml: `this: + is: >- + a multiline + + string that + spans lines + + that: + has: >- + other multline + folded scalars `, + want: []int{6}, }, { - name: "yaml_all_indent_change", - yamlFilenames: []string{"testdata/github-crazy-indent.yml"}, - format: true, - fixNewlines: true, - want: `jobs: - my_job: - runs-on: 'ubuntu-latest' - - container: - image: 'ubuntu:20.04' - - services: - nginx: - image: 'nginx:1.21' - - steps: - - uses: 'actions/checkout@v3' - - - uses: 'docker://ubuntu:20.04' - with: - uses: '/path/to/user.png' - image: '/path/to/image.jpg' - - - runs: |- - echo "Hello 😀" - if [ "true" == "false" ]; - echo "NOPE" - fi - - other_job: - uses: 'my-org/my-repo/.github/workflows/my-workflow.yml@v0' - - final_job: - uses: './local/path/to/action' + name: "literal_block_scalar", + yaml: `this: + is: |- + a multiline + + string that + spans lines + + that: + has: |- + other multline + literal scalars `, + want: []int{6}, }, } @@ -475,22 +203,28 @@ func Test_loadYAMLFiles(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() - files, err := loadYAMLFiles(os.DirFS(".."), tc.yamlFilenames, tc.format) - if err != nil { - t.Fatalf("loadYAMLFiles() returned error: %s", err) + fsys := fstest.MapFS{ + "file.yml": &fstest.MapFile{ + Data: []byte(tc.yaml), + }, } - b, err := marshalYAML(files[0].node) + r, err := loadYAMLFiles(fsys, []string{"file.yml"}) if err != nil { - t.Fatalf("marshalYAML() returned error: %s", err) + t.Fatal(err) } - got := string(b) - if tc.fixNewlines { - got = removeNewLineChanges(string(files[0].contents), got) + + f := r[0] + if diff := cmp.Diff(f.newlines, tc.want); diff != "" { + t.Errorf("unexpected newlines diff (+got, -want):\n%s", diff) } - if diff := cmp.Diff(tc.want, got); diff != "" { - t.Errorf("returned diff (-want, +got):\n%s", diff) + s, err := f.marshalYAML() + if err != nil { + t.Fatal(err) + } + if diff := cmp.Diff(s, tc.yaml); diff != "" { + t.Errorf("unexpected render diff (+got, -want):\n%s", diff) } }) } diff --git a/command/pin.go b/command/pin.go index de9b97cc84..41735f4499 100644 --- a/command/pin.go +++ b/command/pin.go @@ -80,7 +80,7 @@ func (c *PinCommand) Run(ctx context.Context, originalArgs []string) error { fsys := os.DirFS(".") - files, err := loadYAMLFiles(fsys, args, true) + files, err := loadYAMLFiles(fsys, args) if err != nil { return err } @@ -102,12 +102,11 @@ func (c *PinCommand) Run(ctx context.Context, originalArgs []string) error { outFile = f.path } - updated, err := marshalYAML(f.node) + final, err := f.marshalYAML() if err != nil { return fmt.Errorf("failed to marshal yaml for %s: %w", f.path, err) } - final := removeNewLineChanges(string(f.contents), string(updated)) if err := atomic.Write(f.path, outFile, strings.NewReader(final)); err != nil { return fmt.Errorf("failed to save file %s: %w", outFile, err) } diff --git a/command/unpin.go b/command/unpin.go index b007dfd3ee..6f620bc217 100644 --- a/command/unpin.go +++ b/command/unpin.go @@ -64,7 +64,7 @@ func (c *UnpinCommand) Run(ctx context.Context, originalArgs []string) error { fsys := os.DirFS(".") - files, err := loadYAMLFiles(fsys, args, true) + files, err := loadYAMLFiles(fsys, args) if err != nil { return err } @@ -86,12 +86,11 @@ func (c *UnpinCommand) Run(ctx context.Context, originalArgs []string) error { outFile = f.path } - updated, err := marshalYAML(f.node) + final, err := f.marshalYAML() if err != nil { return fmt.Errorf("failed to marshal yaml for %s: %w", f.path, err) } - final := removeNewLineChanges(string(f.contents), string(updated)) if err := atomic.Write(f.path, outFile, strings.NewReader(final)); err != nil { return fmt.Errorf("failed to save file %s: %w", outFile, err) } diff --git a/command/update.go b/command/update.go index e9f3a9060d..002682c875 100644 --- a/command/update.go +++ b/command/update.go @@ -69,7 +69,7 @@ func (c *UpdateCommand) Run(ctx context.Context, originalArgs []string) error { fsys := os.DirFS(".") - files, err := loadYAMLFiles(fsys, args, true) + files, err := loadYAMLFiles(fsys, args) if err != nil { return err } @@ -95,12 +95,11 @@ func (c *UpdateCommand) Run(ctx context.Context, originalArgs []string) error { outFile = f.path } - updated, err := marshalYAML(f.node) + final, err := f.marshalYAML() if err != nil { return fmt.Errorf("failed to marshal yaml for %s: %w", f.path, err) } - final := removeNewLineChanges(string(f.contents), string(updated)) if err := atomic.Write(f.path, outFile, strings.NewReader(final)); err != nil { return fmt.Errorf("failed to save file %s: %w", outFile, err) } diff --git a/command/upgrade.go b/command/upgrade.go index edf308d6a0..17b47dbd27 100644 --- a/command/upgrade.go +++ b/command/upgrade.go @@ -77,7 +77,7 @@ func (c *UpgradeCommand) Run(ctx context.Context, originalArgs []string) error { fsys := os.DirFS(".") - files, err := loadYAMLFiles(fsys, args, true) + files, err := loadYAMLFiles(fsys, args) if err != nil { return err } @@ -107,12 +107,11 @@ func (c *UpgradeCommand) Run(ctx context.Context, originalArgs []string) error { outFile = f.path } - upgraded, err := marshalYAML(f.node) + final, err := f.marshalYAML() if err != nil { return fmt.Errorf("failed to marshal yaml for %s: %w", f.path, err) } - final := removeNewLineChanges(string(f.contents), string(upgraded)) if err := atomic.Write(f.path, outFile, strings.NewReader(final)); err != nil { return fmt.Errorf("failed to save file %s: %w", outFile, err) } diff --git a/go.mod b/go.mod index 43a5fcbb20..9e4aa0f0e3 100644 --- a/go.mod +++ b/go.mod @@ -1,34 +1,32 @@ module github.com/sethvargo/ratchet -go 1.21 +go 1.22 + +toolchain go1.22.2 require ( github.com/braydonk/yaml v0.7.0 github.com/google/go-cmp v0.6.0 - github.com/google/go-containerregistry v0.19.0 + github.com/google/go-containerregistry v0.19.1 github.com/google/go-github/v58 v58.0.0 - github.com/hexops/gotextdiff v1.0.3 - golang.org/x/oauth2 v0.16.0 - golang.org/x/sync v0.6.0 + golang.org/x/oauth2 v0.19.0 + golang.org/x/sync v0.7.0 ) require ( github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect - github.com/docker/cli v25.0.2+incompatible // indirect + github.com/docker/cli v26.0.2+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v25.0.5+incompatible // indirect + github.com/docker/docker v26.0.2+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.1 // indirect - github.com/golang/protobuf v1.5.3 // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/klauspost/compress v1.17.5 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc6 // indirect + github.com/opencontainers/image-spec v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/vbatts/tar-split v0.11.5 // indirect - golang.org/x/sys v0.16.0 // indirect - google.golang.org/appengine v1.6.8 // indirect - google.golang.org/protobuf v1.33.0 // indirect + golang.org/x/sys v0.19.0 // indirect ) diff --git a/go.sum b/go.sum index d0281f1492..d5d4843e2b 100644 --- a/go.sum +++ b/go.sum @@ -6,32 +6,25 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docker/cli v25.0.2+incompatible h1:6GEdvxwEA451/+Y3GtqIGn/MNjujQazUlxC6uGu8Tog= -github.com/docker/cli v25.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v26.0.2+incompatible h1:4C4U8ZqrlNDe/R1U1zFFX+YsCFiVUicJqo4WVdInJas= +github.com/docker/cli v26.0.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v25.0.5+incompatible h1:UmQydMduGkrD5nQde1mecF/YnSbTOaPeFIeP5C4W+DE= -github.com/docker/docker v25.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE= +github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo= github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.19.0 h1:uIsMRBV7m/HDkDxE/nXMnv1q+lOOSPlQ/ywc5JbB8Ic= -github.com/google/go-containerregistry v0.19.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= +github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY= +github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI= github.com/google/go-github/v58 v58.0.0 h1:Una7GGERlF/37XfkPwpzYJe0Vp4dt2k1kCjlxwjIvzw= github.com/google/go-github/v58 v58.0.0/go.mod h1:k4hxDKEfoWpSqFlc8LTpGd9fu2KrV1YAa6Hi6FmDNY4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= -github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= -github.com/klauspost/compress v1.17.5 h1:d4vBd+7CHydUqpFBgUEKkSdtSugf9YFmSkvUYPquI5E= -github.com/klauspost/compress v1.17.5/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -40,8 +33,8 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc6 h1:XDqvyKsJEbRtATzkgItUqBA7QHk58yxX1Ov9HERHNqU= -github.com/opencontainers/image-spec v1.1.0-rc6/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -56,44 +49,14 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts= github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -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/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= -golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg= +golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= -google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 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= diff --git a/testdata/a.golden.yml b/testdata/a.golden.yml new file mode 100644 index 0000000000..8f785a8163 --- /dev/null +++ b/testdata/a.golden.yml @@ -0,0 +1,16 @@ +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + + steps: + - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' diff --git a/testdata/a.yml b/testdata/a.yml new file mode 100644 index 0000000000..1a6451ac27 --- /dev/null +++ b/testdata/a.yml @@ -0,0 +1,16 @@ +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + + steps: + - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' diff --git a/testdata/b.golden.yml b/testdata/b.golden.yml new file mode 100644 index 0000000000..2d9dc4281d --- /dev/null +++ b/testdata/b.golden.yml @@ -0,0 +1,33 @@ +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + steps: + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - id: 'print' + runs: 'echo "hello"' + - name: 'Checkout' + uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' diff --git a/testdata/b.yml b/testdata/b.yml new file mode 100644 index 0000000000..c05854d487 --- /dev/null +++ b/testdata/b.yml @@ -0,0 +1,33 @@ +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + steps: + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - id : 'print' + runs: 'echo "hello"' + - name: 'Checkout' + uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' diff --git a/testdata/c.golden.yml b/testdata/c.golden.yml new file mode 100644 index 0000000000..cccd1da9dc --- /dev/null +++ b/testdata/c.golden.yml @@ -0,0 +1,21 @@ +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' + thing: |- + this is my string + it has many lines + + some of them even + have new lines diff --git a/testdata/c.yml b/testdata/c.yml new file mode 100644 index 0000000000..5ac87d84f3 --- /dev/null +++ b/testdata/c.yml @@ -0,0 +1,26 @@ + + + +jobs: + init: + runs-on: 'ubuntu-latest' + outputs: + directories: '${{ steps.dirs.outputs.directories }}' + + steps: + - name: 'Checkout' + uses: 'actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab' # ratchet:actions/checkout@v3 + + - name: 'Guardian Directories' + id: 'dirs' + uses: 'abcxyz/guardian/.github/actions/directories@52a8396df1c40bde244947c887d2c5dfbd36e4ce' # ratchet:abcxyz/guardian/.github/actions/directories@main + with: + directories: '${{ inputs.directories }}' + thing: |- + this is my string + it has many lines + + some of them even + have new lines + + diff --git a/testdata/cloudbuild.yml b/testdata/cloudbuild.yml index 347ad98483..2554d0b4fb 100644 --- a/testdata/cloudbuild.yml +++ b/testdata/cloudbuild.yml @@ -1,4 +1,4 @@ steps: -- name: 'ubuntu:20.04' - args: - - 'name: foo' + - name: 'ubuntu:20.04' + args: + - 'name: foo' diff --git a/testdata/github-issue-80.yml b/testdata/github-issue-80.yml new file mode 100644 index 0000000000..7b3e9145dc --- /dev/null +++ b/testdata/github-issue-80.yml @@ -0,0 +1,46 @@ +--- +name: mega-linter + +on: + workflow_dispatch: + push: + branches-ignore: + - main + +permissions: read-all + +jobs: + mega-linter: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout Code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + - name: Restore lychee cache + uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + with: + path: .lycheecache + key: cache-lychee-${{ github.sha }} + restore-keys: cache-lychee- + + - name: Extract commands from markdown files + run: | + set -euxo pipefail + echo '#!/usr/bin/env bash' > README.sh + find . -name '*.md' -print0 | while IFS= read -r -d '' FILE; do + # Extract: ... + sed -n "/^\`\`\`\(bash\|shell\)$/,/^\`\`\`$/p" "${FILE}" | sed '/^```*/d' >> README.sh + # Extract: ```bash ... ``` + sed -n "/^ \`\`\`\(bash\|shell\)$/,/^ \`\`\`$/p" "${FILE}" | sed '/^ ```*/d; s/^ //' >> README.sh + done + ls -la README.sh + chmod a+x README.sh + + - name: 💡 MegaLinter + uses: oxsecurity/megalinter@a7a0163b6c8ff7474a283d99a706e27483ddd80f # v7.10.0 + env: + GITHUB_COMMENT_REPORTER: false + # Disabled due to error: [GitHub Status Reporter] Error posting Status for REPOSITORY with ...: 403 + GITHUB_STATUS_REPORTER: false + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/testdata/github.yml b/testdata/github.yml index 2819213e5f..9bdb8dee54 100644 --- a/testdata/github.yml +++ b/testdata/github.yml @@ -10,18 +10,18 @@ jobs: image: 'nginx:1.21' steps: - - uses: 'actions/checkout@v3' + - uses: 'actions/checkout@v3' - - uses: 'docker://ubuntu:20.04' - with: - uses: '/path/to/user.png' - image: '/path/to/image.jpg' + - uses: 'docker://ubuntu:20.04' + with: + uses: '/path/to/user.png' + image: '/path/to/image.jpg' - - runs: |- - echo "Hello 😀" - if [ "true" == "false" ]; - echo "NOPE" - fi + - runs: |- + echo "Hello 😀" + if [ "true" == "false" ]; + echo "NOPE" + fi other_job: uses: 'my-org/my-repo/.github/workflows/my-workflow.yml@v0' diff --git a/testdata/no-trailing-newline.golden.yml b/testdata/no-trailing-newline.golden.yml new file mode 100644 index 0000000000..07f7d83e6b --- /dev/null +++ b/testdata/no-trailing-newline.golden.yml @@ -0,0 +1 @@ +job: 'my-job' diff --git a/testdata/no-trailing-newline.yml b/testdata/no-trailing-newline.yml new file mode 100644 index 0000000000..c1bce0c679 --- /dev/null +++ b/testdata/no-trailing-newline.yml @@ -0,0 +1 @@ +job: 'my-job' \ No newline at end of file diff --git a/testdata/tekton.yml b/testdata/tekton.yml index 5d8664c180..689aab9deb 100644 --- a/testdata/tekton.yml +++ b/testdata/tekton.yml @@ -8,4 +8,4 @@ spec: type: string steps: - name: build - image: golang:1.12 \ No newline at end of file + image: golang:1.12