From 71e7f87a8152ff90d6388cb0eef5b602c1bfe2cd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Sat, 15 Oct 2022 08:43:36 -0400 Subject: [PATCH] prepare release pipeline Signed-off-by: Alex Goodman --- .bouncer.yaml | 4 ++ .github/workflows/release.yaml | 75 +++++++++--------------------- .github/workflows/validations.yaml | 52 ++++++++++++--------- .goreleaser.yaml | 52 ++++++++++++++++++--- Makefile | 14 +++--- cmd/quill/cli/options/notary.go | 2 +- cmd/quill/cli/options/signing.go | 2 +- quill/pem/load_bytes.go | 9 +++- 8 files changed, 115 insertions(+), 95 deletions(-) diff --git a/.bouncer.yaml b/.bouncer.yaml index a51f8876..c5b5bda5 100644 --- a/.bouncer.yaml +++ b/.bouncer.yaml @@ -5,3 +5,7 @@ permit: - MPL.* - ISC +ignore-packages: + # at the current version there is no license, however, the MIT license was eventually added + # https://github.com/mattn/go-localereader/blob/2491eb6c1c75720122ef321ed7acc3a8d9de95b1/LICENSE + - github.com/mattn/go-localereader \ No newline at end of file diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 174b3280..8739ab10 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,13 +10,14 @@ on: env: GO_VERSION: "1.18.x" + GO_CACHE_KEY: efa04b89c1b1 jobs: quality-gate: environment: release runs-on: ubuntu-latest # This OS choice is arbitrary. None of the steps in this job are specific to either Linux or macOS. steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # we don't want to release commits that have been pushed and tagged, but not necessarily merged onto main - name: Ensure tagged commit is on main @@ -26,7 +27,7 @@ jobs: git merge-base --is-ancestor ${GITHUB_REF##*/} origin/main && echo "${GITHUB_REF##*/} is a commit on main!" - name: Check static analysis results - uses: fountainhead/action-wait-for-check@v1.0.0 + uses: fountainhead/action-wait-for-check@v1.1.0 id: static-analysis with: token: ${{ secrets.GITHUB_TOKEN }} @@ -35,7 +36,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check unit test results - uses: fountainhead/action-wait-for-check@v1.0.0 + uses: fountainhead/action-wait-for-check@v1.1.0 id: unit with: token: ${{ secrets.GITHUB_TOKEN }} @@ -43,81 +44,44 @@ jobs: checkName: "Unit tests" ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Check integration test results - uses: fountainhead/action-wait-for-check@v1.0.0 - id: integration - with: - token: ${{ secrets.GITHUB_TOKEN }} - # This check name is defined as the github action job name (in .github/workflows/testing.yaml) - checkName: "Integration tests" - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Check acceptance test results (linux) - uses: fountainhead/action-wait-for-check@v1.0.0 - id: acceptance-linux - with: - token: ${{ secrets.GITHUB_TOKEN }} - # This check name is defined as the github action job name (in .github/workflows/testing.yaml) - checkName: "Acceptance tests (Linux)" - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Check acceptance test results (mac) - uses: fountainhead/action-wait-for-check@v1.0.0 - id: acceptance-mac - with: - token: ${{ secrets.GITHUB_TOKEN }} - # This check name is defined as the github action job name (in .github/workflows/testing.yaml) - checkName: "Acceptance tests (Mac)" - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - - name: Check cli test results (linux) - uses: fountainhead/action-wait-for-check@v1.0.0 - id: cli-linux - with: - token: ${{ secrets.GITHUB_TOKEN }} - # This check name is defined as the github action job name (in .github/workflows/testing.yaml) - checkName: "CLI tests (Linux)" - ref: ${{ github.event.pull_request.head.sha || github.sha }} - - name: Quality gate - if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success' + if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' run: | echo "Static Analysis Status: ${{ steps.static-analysis.conclusion }}" echo "Unit Test Status: ${{ steps.unit.outputs.conclusion }}" - echo "Integration Test Status: ${{ steps.integration.outputs.conclusion }}" - echo "Acceptance Test (Linux) Status: ${{ steps.acceptance-linux.outputs.conclusion }}" - echo "Acceptance Test (Mac) Status: ${{ steps.acceptance-mac.outputs.conclusion }}" - echo "CLI Test (Linux) Status: ${{ steps.cli-linux.outputs.conclusion }}" false release: needs: [quality-gate] runs-on: ubuntu-latest + permissions: + packages: write + contents: write steps: - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Restore tool cache id: tool-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ${{ github.workspace }}/.tmp key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - name: Restore go cache id: go-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}- + ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- - name: (cache-miss) Bootstrap all project dependencies if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' @@ -126,15 +90,18 @@ jobs: - name: Build & publish release artifacts run: make release env: - DOCKER_USERNAME: ${{ secrets.TOOLBOX_DOCKER_USER }} - DOCKER_PASSWORD: ${{ secrets.TOOLBOX_DOCKER_PASS }} - GITHUB_TOKEN: ${{ secrets.ANCHORE_GIT_READ_TOKEN }} + QUILL_SIGNING_P12: ${{ secrets.APPLE_SIGNING_P12 }} + QUILL_SIGNING_PASSWORD: ${{ secrets.APPLE_SIGNING_P12_PASSWORD }} + QUILL_NOTARY_ISSUER: ${{ secrets.APPLE_NOTARY_ISSUER }} + QUILL_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }} + QUILL_NOTARY_KEY: ${{ secrets.APPLE_NOTARY_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - uses: anchore/sbom-action@v0 with: artifact-name: sbom.spdx.json - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: dist/**/* diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 0fa68ae5..f05e4208 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -8,6 +8,7 @@ on: env: GO_VERSION: "1.18.x" + GO_CACHE_KEY: efa04b89c1b1 jobs: @@ -16,27 +17,27 @@ jobs: name: "Static analysis" runs-on: ubuntu-20.04 steps: - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Restore tool cache id: tool-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ${{ github.workspace }}/.tmp key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - name: Restore go cache id: go-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}- + ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- - name: (cache-miss) Bootstrap all project dependencies if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' @@ -48,29 +49,36 @@ jobs: Unit-Test: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Unit tests" - runs-on: macos-latest + runs-on: macOS-12 steps: - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 + with: + # this downloads and initializes LFS, but does not pull the objects + lfs: true + + - name: Checkout LFS objects + # lfs pull does a lfs fetch and lfs checkout, this is NOT the same as "git pull" + run: git lfs pull - name: Restore tool cache id: tool-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ${{ github.workspace }}/.tmp key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - name: Restore go cache id: go-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}- + ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- - name: (cache-miss) Bootstrap all project dependencies if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' @@ -79,7 +87,7 @@ jobs: - name: Run unit tests run: make unit - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: unit-test-results path: test/results/**/* @@ -88,30 +96,30 @@ jobs: name: "Build snapshot artifacts" runs-on: ubuntu-20.04 steps: - - uses: actions/setup-go@v2 + - uses: actions/setup-go@v3 with: go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up QEMU - uses: docker/setup-qemu-action@v1 + uses: docker/setup-qemu-action@v2 - name: Restore tool cache id: tool-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ${{ github.workspace }}/.tmp key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - name: Restore go cache id: go-cache - uses: actions/cache@v2.1.3 + uses: actions/cache@v3 with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ hashFiles('**/go.sum') }} + key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}- + ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- - name: (cache-miss) Bootstrap all project dependencies if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' @@ -120,7 +128,7 @@ jobs: - name: Build snapshot artifacts run: make snapshot - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: artifacts path: snapshot/**/* diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 9ce90988..c05016ef 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -7,9 +7,9 @@ release: draft: true builds: - - binary: quill - dir: ./cmd/quill - env: + - binary: &name quill + dir: &dir ./cmd/quill + env: &build-env - CGO_ENABLED=0 goos: - linux @@ -17,9 +17,8 @@ builds: - darwin goarch: - amd64 - # Set the modified timestamp on the output binary to the git timestamp (to ensure a reproducible build) - mod_timestamp: '{{ .CommitTimestamp }}' - ldflags: | + mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}' + ldflags: &build-ldflags | -w -s -extldflags '-static' @@ -28,6 +27,45 @@ builds: -X github.com/anchore/quill/internal/version.buildDate={{.Date}} -X github.com/anchore/quill/internal/version.gitDescription={{.Summary}} + - id: darwin-build + dir: *dir + binary: *name + goos: + - darwin + goarch: + - amd64 + - arm64 + mod_timestamp: *build-timestamp + env: *build-env + ldflags: *build-ldflags + hooks: + post: + - cmd: .tmp/quill sign-and-notarize "{{ .Path }}" --dry-run={{ .IsSnapshot }} --ad-hoc={{ .IsSnapshot }} -vv + env: + - QUILL_LOG_FILE=/tmp/quill-{{ .Target }}.log + + - id: windows-build + dir: *dir + binary: *name + goos: + - windows + goarch: + - amd64 + mod_timestamp: *build-timestamp + env: *build-env + ldflags: *build-ldflags + + archives: - - format: tar.gz + - id: linux-archives + builds: + - linux-build + + - id: darwin-archives + builds: + - darwin-build + - id: windows-archives + format: zip + builds: + - windows-build diff --git a/Makefile b/Makefile index 6e9b8cd8..0b40cc63 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ COVER_TOTAL = $(RESULTS_DIR)/unit-coverage-summary.txt LINTCMD = $(TEMP_DIR)/golangci-lint run --tests=false --timeout=2m --config .golangci.yaml GOIMPORTS_CMD = $(TEMP_DIR)/gosimports -local github.com/anchore RELEASE_CMD = $(TEMP_DIR)/goreleaser release --rm-dist -SNAPSHOT_CMD = $(RELEASE_CMD) --skip-publish --snapshot +SNAPSHOT_CMD = $(RELEASE_CMD) --skip-publish --snapshot --skip-sign VERSION=$(shell git describe --dirty --always --tags) # formatting @@ -23,6 +23,7 @@ COVERAGE_THRESHOLD := 30 BOOTSTRAP_CACHE="c7afb99ad" # ci dependency versions +QUILL_VERSION = latest GOLANG_CI_VERSION = v1.49.0 GOBOUNCER_VERSION = v0.4.0 GORELEASER_VERSION = v1.11.5 @@ -86,6 +87,8 @@ $(TEMP_DIR): .PHONY: bootstrap-tools bootstrap-tools: $(TEMP_DIR) + #GOBIN="$(realpath $(TEMP_DIR))" go install github.com/anchore/quill/cmd/quill@$(QUILL_VERSION) + GOBIN="$(realpath $(TEMP_DIR))" go install ./cmd/quill curl -sSfL https://raw.githubusercontent.com/anchore/chronicle/main/install.sh | sh -s -- -b $(TEMP_DIR)/ $(CHRONICLE_VERSION) curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(TEMP_DIR)/ $(GOLANG_CI_VERSION) curl -sSfL https://raw.githubusercontent.com/wagoodman/go-bouncer/master/bouncer.sh | sh -s -- -b $(TEMP_DIR)/ $(GOBOUNCER_VERSION) @@ -154,14 +157,9 @@ $(SNAPSHOT_DIR): ## Build snapshot release binaries and packages # build release snapshots bash -c "\ - SKIP_SIGNING=true \ VERSION=$(VERSION:v%=%) \ - $(TEMP_DIR)/goreleaser release \ - --skip-sign \ - --skip-publish \ - --rm-dist \ - --snapshot \ - --config $(TEMP_DIR)/goreleaser.yaml" + $(SNAPSHOT_CMD) --config $(TEMP_DIR)/goreleaser.yaml \ + " .PHONY: changelog changelog: clean-changelog CHANGELOG.md diff --git a/cmd/quill/cli/options/notary.go b/cmd/quill/cli/options/notary.go index f2eefff1..23c66735 100644 --- a/cmd/quill/cli/options/notary.go +++ b/cmd/quill/cli/options/notary.go @@ -36,7 +36,7 @@ func (o *Notary) AddFlags(flags *pflag.FlagSet) { flags.StringVarP( &o.PrivateKey, "notary-key", "", o.PrivateKey, - "App Store Connect API key. File system path to the private key.", + "App Store Connect API key. File system path to the private key. This can also be the base64-encoded contents of the key file, or 'env:ENV_VAR_NAME' to read the key from a different environment variable", ) } diff --git a/cmd/quill/cli/options/signing.go b/cmd/quill/cli/options/signing.go index 6beb5b97..f44d2d3d 100644 --- a/cmd/quill/cli/options/signing.go +++ b/cmd/quill/cli/options/signing.go @@ -41,7 +41,7 @@ func (o *Signing) AddFlags(flags *pflag.FlagSet) { flags.StringVarP( &o.P12, "p12", "", o.P12, - "path to a PKCS12 file containing the private key, (leaf) signing certificate, remaining certificate chain", + "path to a PKCS12 file containing the private key, (leaf) signing certificate, remaining certificate chain. This can also be the base64-encoded contents of the p12 file, or 'env:ENV_VAR_NAME' to read the p12 from a different environment variable", ) flags.StringVarP( diff --git a/quill/pem/load_bytes.go b/quill/pem/load_bytes.go index 516fe6fb..013fb54f 100644 --- a/quill/pem/load_bytes.go +++ b/quill/pem/load_bytes.go @@ -35,10 +35,15 @@ func LoadBytesFromFileOrEnv(path string) ([]byte, error) { // comes from the config... - if _, err := os.Stat(path); os.IsNotExist(err) { + if _, err := os.Stat(path); err != nil { log.Trace("using bytes from config") - return []byte(path), nil + decodedKey, err := base64.StdEncoding.DecodeString(path) + if err != nil { + return nil, fmt.Errorf("unable to base64 decode key: %w", err) + } + + return decodedKey, nil } // comes from a file...