From 5a5a6561d65067678990e69dc3d16a711c98c048 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:31:55 -0400 Subject: [PATCH 1/6] :seedling: Bump tj-actions/changed-files from 39.1.0 to 39.1.2 (#3504) Bumps [tj-actions/changed-files](https://github.com/tj-actions/changed-files) from 39.1.0 to 39.1.2. - [Release notes](https://github.com/tj-actions/changed-files/releases) - [Changelog](https://github.com/tj-actions/changed-files/blob/main/HISTORY.md) - [Commits](https://github.com/tj-actions/changed-files/compare/8e79ba7ab9fee9984275219aeb2c8db47bcb8a2d...41960309398d165631f08c5df47a11147e14712b) --- updated-dependencies: - dependency-name: tj-actions/changed-files dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 59fc9ac4b29..e6c8736397d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -40,7 +40,7 @@ jobs: fetch-depth: 2 # needed to diff changed files - id: files name: Get changed files - uses: tj-actions/changed-files@8e79ba7ab9fee9984275219aeb2c8db47bcb8a2d #v39.1.0 + uses: tj-actions/changed-files@41960309398d165631f08c5df47a11147e14712b #v39.1.2 with: files_ignore: '**.md' - id: docs_only_check From fa31d56694c480ab961249c78663a2e292122f39 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 09:47:00 -0400 Subject: [PATCH 2/6] :seedling: Bump actions/checkout from 4.0.0 to 4.1.0 (#3511) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/3df4ab11eba7bda6032a0b82a6bb43b11571feac...8ade135a41bc03ea155e62e844d188df1ea18608) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/depsreview.yml | 2 +- .github/workflows/docker.yml | 4 ++-- .github/workflows/gitlab.yml | 2 +- .github/workflows/goreleaser.yaml | 2 +- .github/workflows/integration.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/main.yml | 18 +++++++++--------- .github/workflows/publishimage.yml | 2 +- .github/workflows/scorecard-analysis.yml | 2 +- .github/workflows/slsa-goreleaser.yml | 2 +- 11 files changed, 20 insertions(+), 20 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 73235106ba1..c7a6fa7bbae 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -57,7 +57,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Checkout repository - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/depsreview.yml b/.github/workflows/depsreview.yml index 00d442a0e6d..bc90b255cf5 100644 --- a/.github/workflows/depsreview.yml +++ b/.github/workflows/depsreview.yml @@ -22,6 +22,6 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout Repository' - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: 'Dependency Review' uses: actions/dependency-review-action@6c5ccdad469c9f8a2996bfecaec55a631a347034 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index e6c8736397d..4084f1d18be 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -35,7 +35,7 @@ jobs: docs_only: ${{ steps.docs_only_check.outputs.docs_only }} steps: - name: Check out code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 with: fetch-depth: 2 # needed to diff changed files - id: files @@ -75,7 +75,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code if: (needs.docs_only_check.outputs.docs_only != 'true') - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Go # needed for some of the Makefile evaluations, even if building happens in Docker if: (needs.docs_only_check.outputs.docs_only != 'true') uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 diff --git a/.github/workflows/gitlab.yml b/.github/workflows/gitlab.yml index 2a8bfc7a4d3..e54d5d77683 100644 --- a/.github/workflows/gitlab.yml +++ b/.github/workflows/gitlab.yml @@ -37,7 +37,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} # head SHA if PR, else fallback to push SHA - name: Setup Go diff --git a/.github/workflows/goreleaser.yaml b/.github/workflows/goreleaser.yaml index a70781eafdc..c607071baff 100644 --- a/.github/workflows/goreleaser.yaml +++ b/.github/workflows/goreleaser.yaml @@ -39,7 +39,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Set up Go diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 828438bd616..67137cb8692 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -48,7 +48,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: ref: ${{ github.event.pull_request.head.sha }} - name: Setup Go diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9256167a6d5..01140ca44f0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ jobs: - uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # v2.5.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 33b43bb28a2..073f2a9d4f7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -41,7 +41,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: @@ -117,7 +117,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go @@ -147,7 +147,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: @@ -182,7 +182,7 @@ jobs: version: ${{ env.PROTOC_VERSION }} repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go @@ -237,7 +237,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Setup Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v4.1.0 with: @@ -277,7 +277,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go @@ -324,7 +324,7 @@ jobs: restore-keys: | ${{ runner.os }}-go- - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go @@ -359,7 +359,7 @@ jobs: version: ${{ env.PROTOC_VERSION }} repo-token: ${{ secrets.GITHUB_TOKEN }} - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go @@ -388,7 +388,7 @@ jobs: with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe # v2.2.0 with: go-version: ${{ env.GO_VERSION }} diff --git a/.github/workflows/publishimage.yml b/.github/workflows/publishimage.yml index 1efae292fcc..e6f510b03bf 100644 --- a/.github/workflows/publishimage.yml +++ b/.github/workflows/publishimage.yml @@ -40,7 +40,7 @@ jobs: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs - name: Clone the code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - name: Setup Go diff --git a/.github/workflows/scorecard-analysis.yml b/.github/workflows/scorecard-analysis.yml index 6e4c9311503..1c992d48fcf 100644 --- a/.github/workflows/scorecard-analysis.yml +++ b/.github/workflows/scorecard-analysis.yml @@ -22,7 +22,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: "Run analysis" uses: ossf/scorecard-action@08b4669551908b1024bb425080c797723083c031 # v2.2.0 diff --git a/.github/workflows/slsa-goreleaser.yml b/.github/workflows/slsa-goreleaser.yml index 6a7fe8b8e8c..133e9ec360d 100644 --- a/.github/workflows/slsa-goreleaser.yml +++ b/.github/workflows/slsa-goreleaser.yml @@ -19,7 +19,7 @@ jobs: go-binary-name: ${{ steps.build.outputs.go-binary-name }} steps: - id: checkout - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 with: fetch-depth: 0 - id: ldflags From 7626a05313ce8270c856bf532d2781f8fd5e5c7a Mon Sep 17 00:00:00 2001 From: Spencer Schrock Date: Mon, 25 Sep 2023 07:12:13 -0700 Subject: [PATCH 3/6] :sparkles: scdiff: add basic stats command to count scores by buckets (#3458) * wip Signed-off-by: Spencer Schrock * output via tabwriter Signed-off-by: Spencer Schrock * specify by check. Signed-off-by: Spencer Schrock * Return aggregate score when unmarshalling. Signed-off-by: Spencer Schrock * convert from score to bucket in one place. use aggregate score from func Signed-off-by: Spencer Schrock * fix forgotten usage of ExperimentalFromJSON2 Signed-off-by: Spencer Schrock * use sentinel errors. Signed-off-by: Spencer Schrock * move counting to own func for testability Signed-off-by: Spencer Schrock * remove unneeded fields from results for readability. Signed-off-by: Spencer Schrock * add test for parse errors. Signed-off-by: Spencer Schrock * share max result size for any bufio.Scanner which reads results. Signed-off-by: Spencer Schrock * add basic overall test for calcing stats. Signed-off-by: Spencer Schrock * make missing file argument generic. Signed-off-by: Spencer Schrock * validate min args with cobra. Signed-off-by: Spencer Schrock --------- Signed-off-by: Spencer Schrock --- cmd/internal/scdiff/app/compare.go | 15 ++- cmd/internal/scdiff/app/stats.go | 127 ++++++++++++++++++++++++++ cmd/internal/scdiff/app/stats_test.go | 109 ++++++++++++++++++++++ pkg/json.go | 11 ++- 4 files changed, 249 insertions(+), 13 deletions(-) create mode 100644 cmd/internal/scdiff/app/stats.go create mode 100644 cmd/internal/scdiff/app/stats_test.go diff --git a/cmd/internal/scdiff/app/compare.go b/cmd/internal/scdiff/app/compare.go index 7947c0f721c..8ff1117ffcc 100644 --- a/cmd/internal/scdiff/app/compare.go +++ b/cmd/internal/scdiff/app/compare.go @@ -36,18 +36,15 @@ func init() { } var ( - errMissingInputFiles = errors.New("must provide at least two files from scdiff generate") - errResultsDiffer = errors.New("results differ") - errNumResults = errors.New("number of results being compared differ") + errResultsDiffer = errors.New("results differ") + errNumResults = errors.New("number of results being compared differ") compareCmd = &cobra.Command{ Use: "compare [flags] FILE1 FILE2", Short: "Compare Scorecard results", Long: `Compare Scorecard results`, + Args: cobra.MinimumNArgs(2), RunE: func(cmd *cobra.Command, args []string) error { - if len(args) < 2 { - return errMissingInputFiles - } f1, err := os.Open(args[0]) if err != nil { return fmt.Errorf("opening %q: %w", args[0], err) @@ -68,7 +65,9 @@ var ( func compareReaders(x, y io.Reader, output io.Writer) error { // results are currently newline delimited xs := bufio.NewScanner(x) + xs.Buffer(nil, maxResultSize) ys := bufio.NewScanner(y) + ys.Buffer(nil, maxResultSize) for { if shouldContinue, err := advanceScanners(xs, ys); err != nil { return err @@ -90,11 +89,11 @@ func compareReaders(x, y io.Reader, output io.Writer) error { } func loadResults(x, y *bufio.Scanner) (pkg.ScorecardResult, pkg.ScorecardResult, error) { - xResult, err := pkg.ExperimentalFromJSON2(strings.NewReader(x.Text())) + xResult, _, err := pkg.ExperimentalFromJSON2(strings.NewReader(x.Text())) if err != nil { return pkg.ScorecardResult{}, pkg.ScorecardResult{}, fmt.Errorf("parsing first result: %w", err) } - yResult, err := pkg.ExperimentalFromJSON2(strings.NewReader(y.Text())) + yResult, _, err := pkg.ExperimentalFromJSON2(strings.NewReader(y.Text())) if err != nil { return pkg.ScorecardResult{}, pkg.ScorecardResult{}, fmt.Errorf("parsing second result: %w", err) } diff --git a/cmd/internal/scdiff/app/stats.go b/cmd/internal/scdiff/app/stats.go new file mode 100644 index 00000000000..d1a09eff6b8 --- /dev/null +++ b/cmd/internal/scdiff/app/stats.go @@ -0,0 +1,127 @@ +// Copyright 2023 OpenSSF Scorecard Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + + "github.com/spf13/cobra" + "golang.org/x/exp/slices" + + "github.com/ossf/scorecard/v4/checker" + "github.com/ossf/scorecard/v4/pkg" +) + +//nolint:gochecknoinits // common for cobra apps +func init() { + rootCmd.AddCommand(statsCmd) + statsCmd.PersistentFlags().StringVarP(&statsCheck, "check", "c", "", "Analyze breakdown of a single check") +} + +// 1 MiB size limit for individual results. This currently works, +// but bufio.Scanner always has a limit, may need to change approach in the future. +const maxResultSize = 1024 * 1024 + +var ( + statsCheck string + statsCmd = &cobra.Command{ + Use: "stats [flags] FILE", + Short: "Summarize stats for a golden file", + Long: `Summarize stats for a golden file`, + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + f1, err := os.Open(args[0]) + if err != nil { + return fmt.Errorf("opening %q: %w", args[0], err) + } + defer f1.Close() + return calcStats(f1, os.Stdout) + }, + } + + errCheckNotPresent = errors.New("requested check not present") + errInvalidScore = errors.New("invalid score") +) + +// countScores quantizes the scores into 12 buckets, from [-1, 10] +// If a check is provided, that check score is used, otherwise the aggregate score is used. +func countScores(input io.Reader, check string) ([12]int, error) { + var counts [12]int // [-1, 10] inclusive + var score int + scanner := bufio.NewScanner(input) + scanner.Buffer(nil, maxResultSize) + for scanner.Scan() { + result, aggregateScore, err := pkg.ExperimentalFromJSON2(strings.NewReader(scanner.Text())) + if err != nil { + return [12]int{}, fmt.Errorf("parsing result: %w", err) + } + if check == "" { + score = int(aggregateScore) + } else { + i := slices.IndexFunc(result.Checks, func(c checker.CheckResult) bool { + return strings.EqualFold(c.Name, check) + }) + if i == -1 { + return [12]int{}, errCheckNotPresent + } + score = result.Checks[i].Score + } + if score < -1 || score > 10 { + return [12]int{}, errInvalidScore + } + bucket := score + 1 // score of -1 is index 0, score of 0 is index 1, etc. + counts[bucket]++ + } + if err := scanner.Err(); err != nil { + return [12]int{}, fmt.Errorf("parsing golden file: %w", err) + } + return counts, nil +} + +func calcStats(input io.Reader, output io.Writer) error { + counts, err := countScores(input, statsCheck) + if err != nil { + return err + } + name := statsCheck + if name == "" { + name = "Aggregate" + } + summary(name, &counts, output) + return nil +} + +func summary(name string, counts *[12]int, output io.Writer) { + const ( + minWidth = 0 + tabWidth = 4 + padding = 1 + padchar = ' ' + flags = tabwriter.AlignRight + ) + w := tabwriter.NewWriter(output, minWidth, tabWidth, padding, padchar, flags) + fmt.Fprintf(w, "%s Score\tCount\t\n", name) + for i, c := range counts { + scoreBucket := i - 1 + fmt.Fprintf(w, "%d\t%d\t\n", scoreBucket, c) + } + w.Flush() +} diff --git a/cmd/internal/scdiff/app/stats_test.go b/cmd/internal/scdiff/app/stats_test.go new file mode 100644 index 00000000000..7896a80d38a --- /dev/null +++ b/cmd/internal/scdiff/app/stats_test.go @@ -0,0 +1,109 @@ +// Copyright 2023 OpenSSF Scorecard Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package app + +import ( + "bytes" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func Test_countScores(t *testing.T) { + t.Parallel() + + common := `{"date":"0001-01-01T00:00:00Z","repo":{"name":"repo1"},"score":0,"checks":[{"score":10,"name":"Foo"}]} +{"date":"0001-01-01T00:00:00Z","repo":{"name":"repo2"},"score":-1,"checks":[{"score":9,"name":"Foo"}]} +` + tests := []struct { + name string + check string + results string + want [12]int + wantErr bool + }{ + { + name: "aggregate score used when no check specified", + results: common, + want: [12]int{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + }, + { + name: "check score used when check specified", + check: "Foo", + results: common, + want: [12]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, + }, + { + name: "check name case insensitive", + check: "fOo", + results: common, + want: [12]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1}, + }, + { + name: "non existent check", + check: "not present", + results: common, + wantErr: true, + }, + { + name: "score outside of [-1, 10] rejected", + results: `{"date":"0001-01-01T00:00:00Z","repo":{"name":"repo1"},"score":12}`, + wantErr: true, + }, + { + name: "result fails to parse", + results: `]}[{}`, + wantErr: true, + }, + } + + for _, tt := range tests { + tt := tt + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + input := strings.NewReader(tt.results) + got, err := countScores(input, tt.check) + if (err != nil) != tt.wantErr { + t.Fatalf("unexpected error: got %v, wantedErr: %t", err, tt.wantErr) + } + if got != tt.want { + t.Errorf("counts differ: %v", cmp.Diff(got, tt.want)) + } + }) + } +} + +func removeRepeatedSpaces(t *testing.T, s string) string { + t.Helper() + return strings.Join(strings.Fields(s), " ") +} + +func Test_calcStats(t *testing.T) { + t.Parallel() + input := strings.NewReader(`{"date":"0001-01-01T00:00:00Z","repo":{"name":"repo1"},"score":10}`) + var output bytes.Buffer + if err := calcStats(input, &output); err != nil { + t.Fatalf("unexepected error: %v", err) + } + got := output.String() + // this is a bit of a simplification, but keeps the test simple + // without needing to update every minor formatting change. + got = removeRepeatedSpaces(t, got) + want := "10 1" // score 10 should have count 1 + if !strings.Contains(got, want) { + t.Errorf("didn't contain expected count") + } +} diff --git a/pkg/json.go b/pkg/json.go index 3257e91dc6b..1382cc9f086 100644 --- a/pkg/json.go +++ b/pkg/json.go @@ -176,17 +176,18 @@ func (r *ScorecardResult) AsJSON2(showDetails bool, return nil } -// This function is experimental. Do not depend on it, it may be removed at any point. -func ExperimentalFromJSON2(r io.Reader) (ScorecardResult, error) { +// ExperimentalFromJSON2 is experimental. Do not depend on it, it may be removed at any point. +// Also returns the aggregate score, as the ScorecardResult field does not contain it. +func ExperimentalFromJSON2(r io.Reader) (result ScorecardResult, score float64, err error) { var jsr JSONScorecardResultV2 decoder := json.NewDecoder(r) if err := decoder.Decode(&jsr); err != nil { - return ScorecardResult{}, fmt.Errorf("decode json: %w", err) + return ScorecardResult{}, 0, fmt.Errorf("decode json: %w", err) } date, err := time.Parse(time.RFC3339, jsr.Date) if err != nil { - return ScorecardResult{}, fmt.Errorf("parse scorecard analysis time: %w", err) + return ScorecardResult{}, 0, fmt.Errorf("parse scorecard analysis time: %w", err) } sr := ScorecardResult{ @@ -216,7 +217,7 @@ func ExperimentalFromJSON2(r io.Reader) (ScorecardResult, error) { sr.Checks = append(sr.Checks, cr) } - return sr, nil + return sr, float64(jsr.AggregateScore), nil } func (r *ScorecardResult) AsFJSON(showDetails bool, From fd12f6a4e2f1d5c9b0afb3c41c67a943f13dea39 Mon Sep 17 00:00:00 2001 From: Spencer Schrock Date: Mon, 25 Sep 2023 07:26:39 -0700 Subject: [PATCH 4/6] :seedling: Switch test import to remove gotest.tools dependency. (#3501) Signed-off-by: Spencer Schrock --- checks/fileparser/github_workflow_test.go | 4 ++-- go.mod | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/checks/fileparser/github_workflow_test.go b/checks/fileparser/github_workflow_test.go index cf2912e4fc2..2c809467ac2 100644 --- a/checks/fileparser/github_workflow_test.go +++ b/checks/fileparser/github_workflow_test.go @@ -20,8 +20,8 @@ import ( "strings" "testing" + "github.com/google/go-cmp/cmp" "github.com/rhysd/actionlint" - "gotest.tools/assert/cmp" ) func TestGitHubWorkflowShell(t *testing.T) { @@ -142,7 +142,7 @@ func TestGitHubWorkflowShell(t *testing.T) { actualShells = append(actualShells, shell) } } - if !cmp.DeepEqual(tt.expectedShells, actualShells)().Success() { + if !cmp.Equal(tt.expectedShells, actualShells) { t.Errorf("%v: Got (%v) expected (%v)", tt.name, actualShells, tt.expectedShells) } }) diff --git a/go.mod b/go.mod index 5f6ec5f5506..4b93be7b75b 100644 --- a/go.mod +++ b/go.mod @@ -2,11 +2,6 @@ module github.com/ossf/scorecard/v4 go 1.19 -require ( - github.com/rhysd/actionlint v1.6.15 - gotest.tools v2.2.0+incompatible -) - require ( cloud.google.com/go/bigquery v1.55.0 cloud.google.com/go/monitoring v1.15.1 // indirect @@ -26,6 +21,7 @@ require ( github.com/moby/buildkit v0.12.2 github.com/olekukonko/tablewriter v0.0.5 github.com/onsi/gomega v1.27.10 + github.com/rhysd/actionlint v1.6.15 github.com/shurcooL/githubv4 v0.0.0-20201206200315-234843c633fa github.com/shurcooL/graphql v0.0.0-20200928012149-18c5c3165e3a github.com/sirupsen/logrus v1.9.3 From bbd673c34551d3156e2211c274992ff72d33d6cd Mon Sep 17 00:00:00 2001 From: Spencer Schrock Date: Mon, 25 Sep 2023 09:04:03 -0700 Subject: [PATCH 5/6] :bug: Set repo commit SHA in results after fetching successfully. (#3514) Signed-off-by: Spencer Schrock --- pkg/scorecard.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/scorecard.go b/pkg/scorecard.go index 6cca73482bd..b8c1a4e1691 100644 --- a/pkg/scorecard.go +++ b/pkg/scorecard.go @@ -127,6 +127,7 @@ func RunScorecard(ctx context.Context, } else if err != nil { return ScorecardResult{}, err } + ret.Repo.CommitSHA = commitSHA defaultBranch, err := repoClient.GetDefaultBranchName() if err != nil { From 6aa3bcc7f50e7c0efc293321284db15b1689b1a7 Mon Sep 17 00:00:00 2001 From: Spencer Schrock Date: Mon, 25 Sep 2023 09:21:30 -0700 Subject: [PATCH 6/6] :seedling: Don't close stale issues explicitly (#3513) Issues are still getting closed after https://github.com/ossf/scorecard/pull/3493. I assume there's a default value being used somewhere. Signed-off-by: Spencer Schrock --- .github/workflows/stale.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 43fc1369f15..b3302c6260d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -34,7 +34,7 @@ jobs: - uses: actions/stale@1160a2240286f5da8ec72b1c0816ce2481aabf84 # v3.0.18 with: repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'Stale issue message - this issue will be closed in 7 days' + stale-issue-message: 'This issue is stale because it has been open for 60 days with no activity.' stale-pr-message: 'Stale pull request message' stale-issue-label: 'no-issue-activity' exempt-issue-labels: 'priority,bug,good first issue' @@ -43,4 +43,5 @@ jobs: days-before-pr-stale: '10' days-before-pr-close: '20' days-before-issue-stale: '60' + days-before-issue-close: -1 operations-per-run: '100'