diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 7da26cebc6..a7253f2c08 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - go-version: ['1.17', '1.18'] + go-version: ['1.18', '1.19'] name: Build ${{ matrix.go-version }} runs-on: ubuntu-latest diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index e501692a22..0ad86ba966 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - name: Build and run ko container diff --git a/.github/workflows/go-1.18.yaml b/.github/workflows/go-1.18.yaml deleted file mode 100644 index cb58a6ba86..0000000000 --- a/.github/workflows/go-1.18.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: Go 1.18 compat test - -on: - pull_request: - branches: ['main'] - -jobs: - go118: - strategy: - fail-fast: false - matrix: - ko-go-version: ['1.17', '1.18'] - user-go-version: ['1.17', '1.18'] - name: Go 1.18 compat (ko=${{ matrix.ko-go-version }} / user=${{ matrix.user-go-version }}) - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - # Build ko using ko-go-version - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.ko-go-version }} - check-latest: true - - run: go install ./ - - # Run ko using user-go-version - - uses: actions/setup-go@v3 - with: - go-version: ${{ matrix.user-go-version }} - check-latest: true - - uses: chainguard-dev/actions/setup-registry@main - - env: - KO_DOCKER_REPO: localhost:1338 - run: ko build ./test/ diff --git a/.github/workflows/image.yaml b/.github/workflows/image.yaml index b80941e02c..d7fca63130 100644 --- a/.github/workflows/image.yaml +++ b/.github/workflows/image.yaml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: sigstore/cosign-installer@v2.5.1 diff --git a/.github/workflows/kind-e2e.yaml b/.github/workflows/kind-e2e.yaml index eaee3b118e..13abd7ffc5 100644 --- a/.github/workflows/kind-e2e.yaml +++ b/.github/workflows/kind-e2e.yaml @@ -20,7 +20,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: actions/checkout@v3 diff --git a/.github/workflows/modules-integration-test.yaml b/.github/workflows/modules-integration-test.yaml index 9a3a378ca0..785fd705a6 100644 --- a/.github/workflows/modules-integration-test.yaml +++ b/.github/workflows/modules-integration-test.yaml @@ -9,7 +9,7 @@ jobs: name: Module Tests strategy: matrix: - go-version: [1.17, 1.18] + go-version: [1.18, 1.19] runs-on: 'ubuntu-latest' steps: diff --git a/.github/workflows/sbom.yaml b/.github/workflows/sbom.yaml index b1523a8153..38b159a021 100644 --- a/.github/workflows/sbom.yaml +++ b/.github/workflows/sbom.yaml @@ -18,7 +18,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: chainguard-dev/actions/setup-registry@main - uses: actions/checkout@v3 @@ -39,7 +39,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: chainguard-dev/actions/setup-registry@main - uses: actions/checkout@v3 @@ -71,7 +71,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: chainguard-dev/actions/setup-registry@main - uses: actions/checkout@v3 @@ -104,7 +104,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: chainguard-dev/actions/setup-registry@main - uses: actions/checkout@v3 diff --git a/.github/workflows/style.yaml b/.github/workflows/style.yaml index aaee2aaf1f..318064955b 100644 --- a/.github/workflows/style.yaml +++ b/.github/workflows/style.yaml @@ -12,10 +12,10 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: actions/checkout@v3 - - uses: chainguard-dev/actions/gofmt@84c993eaf02da1c325854fb272a4df9184bd80fc # main + - uses: chainguard-dev/actions/gofmt@d886686603afb809f7ef9b734b333e20b7ce5cda with: args: -s @@ -25,10 +25,10 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: actions/checkout@v3 - - uses: chainguard-dev/actions/goimports@84c993eaf02da1c325854fb272a4df9184bd80fc # main + - uses: chainguard-dev/actions/goimports@d886686603afb809f7ef9b734b333e20b7ce5cda lint: name: Lint @@ -38,16 +38,16 @@ jobs: - name: Set up Go uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - name: Check out code uses: actions/checkout@v3 - - uses: chainguard-dev/actions/trailing-space@84c993eaf02da1c325854fb272a4df9184bd80fc # main + - uses: chainguard-dev/actions/trailing-space@d886686603afb809f7ef9b734b333e20b7ce5cda if: ${{ always() }} - - uses: chainguard-dev/actions/eof-newline@84c993eaf02da1c325854fb272a4df9184bd80fc # main + - uses: chainguard-dev/actions/eof-newline@d886686603afb809f7ef9b734b333e20b7ce5cda if: ${{ always() }} - uses: reviewdog/action-misspell@v1 diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 35dcab5b2b..95352e9ae0 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v3 - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - run: go test -coverprofile=coverage.txt -covermode=atomic -race ./... diff --git a/.github/workflows/verify.yaml b/.github/workflows/verify.yaml index ff8b5e9de5..4ca9867097 100644 --- a/.github/workflows/verify.yaml +++ b/.github/workflows/verify.yaml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/setup-go@v3 with: - go-version: 1.17 + go-version: 1.18 check-latest: true - uses: actions/checkout@v3 - name: Verify diff --git a/README.md b/README.md index 56d87505e2..825f1b2492 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ apk add ko ### Build and Install from Source -With Go 1.16+, build and install the latest released version: +With Go 1.18+, build and install the latest released version: ``` go install github.com/google/ko@latest diff --git a/internal/sbom/cyclonedx.go b/internal/sbom/cyclonedx.go index bb3132c390..fc47c498c8 100644 --- a/internal/sbom/cyclonedx.go +++ b/internal/sbom/cyclonedx.go @@ -19,6 +19,7 @@ import ( "encoding/base64" "encoding/hex" "encoding/json" + "runtime/debug" "strings" "github.com/sigstore/cosign/pkg/oci" @@ -42,7 +43,7 @@ func GenerateImageCycloneDX(mod []byte) ([]byte, error) { return nil, err } - bi, err := ParseBuildInfo(string(mod)) + bi, err := debug.ParseBuildInfo(string(mod)) if err != nil { return nil, err } diff --git a/internal/sbom/mod.go b/internal/sbom/mod.go deleted file mode 100644 index 2da560a81a..0000000000 --- a/internal/sbom/mod.go +++ /dev/null @@ -1,241 +0,0 @@ -//go:build !go1.18 -// +build !go1.18 - -// Copyright 2021 Google LLC All Rights Reserved. -// -// 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. - -// TODO: Most of this is copied from: -// https://cs.opensource.google/go/go/+/master:src/debug/buildinfo/buildinfo.go -// https://cs.opensource.google/go/go/+/master:src/runtime/debug/mod.go -// It should be replaced with runtime/buildinfo.Read on the binary file when Go 1.18 is released. - -package sbom - -import ( - "fmt" - "strconv" - "strings" -) - -func modulePackageName(mod *Module) string { - return fmt.Sprintf("SPDXRef-Package-%s-%s", - strings.ReplaceAll(mod.Path, "/", "."), - mod.Version) -} - -func bomRef(mod *Module) string { - return fmt.Sprintf("pkg:golang/%s@%s?type=module", mod.Path, mod.Version) -} - -func goRef(mod *Module) string { - path := mod.Path - // Try to lowercase the first 2 path elements to comply with spec - // https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang - p := strings.Split(path, "/") - if len(p) > 2 { - path = strings.Join( - append( - []string{strings.ToLower(p[0]), strings.ToLower(p[1])}, - p[2:]..., - ), "/", - ) - } - return fmt.Sprintf("pkg:golang/%s@%s?type=module", path, mod.Version) -} - -// BuildInfo represents the build information read from a Go binary. -// https://cs.opensource.google/go/go/+/release-branch.go1.18:src/runtime/debug/mod.go;drc=release-branch.go1.18;l=41 -type BuildInfo struct { - GoVersion string // Version of Go that produced this binary. - Path string // The main package path - Main Module // The module containing the main package - Deps []*Module // Module dependencies - Settings []BuildSetting // Other information about the build. -} - -// Module represents a module. -type Module struct { - Path string // module path - Version string // module version - Sum string // checksum - Replace *Module // replaced by this module -} - -// BuildSetting describes a setting that may be used to understand how the -// binary was built. For example, VCS commit and dirty status is stored here. -type BuildSetting struct { - // Key and Value describe the build setting. - // Key must not contain an equals sign, space, tab, or newline. - // Value must not contain newlines ('\n'). - Key, Value string -} - -// https://cs.opensource.google/go/go/+/release-branch.go1.18:src/strings/strings.go;drc=release-branch.go1.18;l=1181 -func stringsCut(s, sep string) (before, after string, found bool) { - if i := strings.Index(s, sep); i >= 0 { - return s[:i], s[i+len(sep):], true - } - return s, "", false -} - -// quoteKey reports whether key is required to be quoted. -func quoteKey(key string) bool { - return len(key) == 0 || strings.ContainsAny(key, "= \t\r\n\"`") -} - -// quoteValue reports whether value is required to be quoted. -func quoteValue(value string) bool { - return strings.ContainsAny(value, " \t\r\n\"`") -} - -// https://cs.opensource.google/go/go/+/release-branch.go1.18:src/runtime/debug/mod.go;drc=release-branch.go1.18;l=121 -func ParseBuildInfo(data string) (bi *BuildInfo, err error) { - lineNum := 1 - defer func() { - if err != nil { - err = fmt.Errorf("could not parse Go build info: line %d: %w", lineNum, err) - } - }() - - var ( - pathLine = "path\t" - modLine = "mod\t" - depLine = "dep\t" - repLine = "=>\t" - buildLine = "build\t" - newline = "\n" - tab = "\t" - ) - - readModuleLine := func(elem []string) (Module, error) { - if len(elem) != 2 && len(elem) != 3 { - return Module{}, fmt.Errorf("expected 2 or 3 columns; got %d", len(elem)) - } - version := elem[1] - sum := "" - if len(elem) == 3 { - sum = elem[2] - } - return Module{ - Path: elem[0], - Version: version, - Sum: sum, - }, nil - } - - bi = new(BuildInfo) - var ( - last *Module - line string - ok bool - ) - // Reverse of BuildInfo.String(), except for go version. - for len(data) > 0 { - line, data, ok = stringsCut(data, newline) - if !ok { - break - } - switch { - case strings.HasPrefix(line, pathLine): - elem := line[len(pathLine):] - bi.Path = string(elem) - case strings.HasPrefix(line, modLine): - elem := strings.Split(line[len(modLine):], tab) - last = &bi.Main - *last, err = readModuleLine(elem) - if err != nil { - return nil, err - } - case strings.HasPrefix(line, depLine): - elem := strings.Split(line[len(depLine):], tab) - last = new(Module) - bi.Deps = append(bi.Deps, last) - *last, err = readModuleLine(elem) - if err != nil { - return nil, err - } - case strings.HasPrefix(line, repLine): - elem := strings.Split(line[len(repLine):], tab) - if len(elem) != 3 { - return nil, fmt.Errorf("expected 3 columns for replacement; got %d", len(elem)) - } - if last == nil { - return nil, fmt.Errorf("replacement with no module on previous line") - } - last.Replace = &Module{ - Path: string(elem[0]), - Version: string(elem[1]), - Sum: string(elem[2]), - } - last = nil - case strings.HasPrefix(line, buildLine): - kv := line[len(buildLine):] - if len(kv) < 1 { - return nil, fmt.Errorf("build line missing '='") - } - - var key, rawValue string - switch kv[0] { - case '=': - return nil, fmt.Errorf("build line with missing key") - - case '`', '"': - rawKey, err := strconv.QuotedPrefix(kv) - if err != nil { - return nil, fmt.Errorf("invalid quoted key in build line") - } - if len(kv) == len(rawKey) { - return nil, fmt.Errorf("build line missing '=' after quoted key") - } - if c := kv[len(rawKey)]; c != '=' { - return nil, fmt.Errorf("unexpected character after quoted key: %q", c) - } - key, _ = strconv.Unquote(rawKey) - rawValue = kv[len(rawKey)+1:] - - default: - var ok bool - key, rawValue, ok = stringsCut(kv, "=") - if !ok { - return nil, fmt.Errorf("build line missing '=' after key") - } - if quoteKey(key) { - return nil, fmt.Errorf("unquoted key %q must be quoted", key) - } - } - - var value string - if len(rawValue) > 0 { - switch rawValue[0] { - case '`', '"': - var err error - value, err = strconv.Unquote(rawValue) - if err != nil { - return nil, fmt.Errorf("invalid quoted value in build line") - } - - default: - value = rawValue - if quoteValue(value) { - return nil, fmt.Errorf("unquoted value %q must be quoted", value) - } - } - } - - bi.Settings = append(bi.Settings, BuildSetting{Key: key, Value: value}) - } - lineNum++ - } - return bi, nil -} diff --git a/internal/sbom/mod_1.18.go b/internal/sbom/mod_1.18.go deleted file mode 100644 index 6857476540..0000000000 --- a/internal/sbom/mod_1.18.go +++ /dev/null @@ -1,61 +0,0 @@ -//go:build go1.18 -// +build go1.18 - -// Copyright 2021 Google LLC All Rights Reserved. -// -// 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 sbom - -import ( - "fmt" - "runtime/debug" - "strings" -) - -type BuildInfo debug.BuildInfo - -func ParseBuildInfo(data string) (*BuildInfo, error) { - dbi, err := debug.ParseBuildInfo(data) - if err != nil { - return nil, fmt.Errorf("parsing build info: %w", err) - } - bi := BuildInfo(*dbi) - return &bi, nil -} - -func modulePackageName(mod *debug.Module) string { - return fmt.Sprintf("SPDXRef-Package-%s-%s", - strings.ReplaceAll(mod.Path, "/", "."), - mod.Version) -} - -func bomRef(mod *debug.Module) string { - return fmt.Sprintf("pkg:golang/%s@%s?type=module", mod.Path, mod.Version) -} - -func goRef(mod *debug.Module) string { - path := mod.Path - // Try to lowercase the first 2 path elements to comply with spec - // https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang - p := strings.Split(path, "/") - if len(p) > 2 { - path = strings.Join( - append( - []string{strings.ToLower(p[0]), strings.ToLower(p[1])}, - p[2:]..., - ), "/", - ) - } - return fmt.Sprintf("pkg:golang/%s@%s?type=module", path, mod.Version) -} diff --git a/internal/sbom/sbom.go b/internal/sbom/sbom.go index d7e91b23c9..bb44f87432 100644 --- a/internal/sbom/sbom.go +++ b/internal/sbom/sbom.go @@ -18,10 +18,37 @@ import ( "bufio" "bytes" "fmt" + "runtime/debug" "strings" "unicode" ) +func modulePackageName(mod *debug.Module) string { + return fmt.Sprintf("SPDXRef-Package-%s-%s", + strings.ReplaceAll(mod.Path, "/", "."), + mod.Version) +} + +func bomRef(mod *debug.Module) string { + return fmt.Sprintf("pkg:golang/%s@%s?type=module", mod.Path, mod.Version) +} + +func goRef(mod *debug.Module) string { + path := mod.Path + // Try to lowercase the first 2 path elements to comply with spec + // https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang + p := strings.Split(path, "/") + if len(p) > 2 { + path = strings.Join( + append( + []string{strings.ToLower(p[0]), strings.ToLower(p[1])}, + p[2:]..., + ), "/", + ) + } + return fmt.Sprintf("pkg:golang/%s@%s?type=module", path, mod.Version) +} + // massageGoModVersion massages the output of `go version -m` into a form that // can be consumed by ParseBuildInfo. // diff --git a/internal/sbom/spdx.go b/internal/sbom/spdx.go index 3c9d1f8061..99f852bbdd 100644 --- a/internal/sbom/spdx.go +++ b/internal/sbom/spdx.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "net/url" + "runtime/debug" "strings" "time" @@ -59,7 +60,7 @@ func GenerateImageSPDX(koVersion string, mod []byte, img oci.SignedImage) ([]byt return nil, err } - bi, err := ParseBuildInfo(string(mod)) + bi, err := debug.ParseBuildInfo(string(mod)) if err != nil { return nil, err }