diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml new file mode 100644 index 00000000..b3e288ca --- /dev/null +++ b/.github/actions/bootstrap/action.yaml @@ -0,0 +1,80 @@ +name: "Bootstrap" +description: "Bootstrap all tools and dependencies" +inputs: + go-version: + description: "Go version to install" + required: true + default: "1.18.x" + use-go-cache: + description: "Restore go cache" + required: true + default: "true" + cache-key-prefix: + description: "Prefix all cache keys with this value" + required: true + default: "efa04b89c1b1" + build-cache-key-prefix: + description: "Prefix build cache key with this value" + required: true + default: "f8b6d31dea" + bootstrap-apt-packages: + description: "Space delimited list of tools to install via apt" + default: "" + +runs: + using: "composite" + steps: + - uses: actions/setup-go@v3 + with: + go-version: ${{ inputs.go-version }} + + - name: Restore tool cache + id: tool-cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/.tmp + key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }} + + # note: we need to keep restoring the go mod cache before bootstrapping tools since `go install` is used in + # some installations of project tools. + - name: Restore go module cache + id: go-mod-cache + if: inputs.use-go-cache == 'true' + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + + - name: (cache-miss) Bootstrap project tools + shell: bash + if: steps.tool-cache.outputs.cache-hit != 'true' + run: make bootstrap-tools + + - name: Restore go build cache + id: go-cache + if: inputs.use-go-cache == 'true' + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + + - name: (cache-miss) Bootstrap go dependencies + shell: bash + if: steps.go-mod-cache.outputs.cache-hit != 'true' && inputs.use-go-cache == 'true' + run: make bootstrap-go + + - name: Install apt packages + if: inputs.bootstrap-apt-packages != '' + shell: bash + run: | + DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y ${{ inputs.bootstrap-apt-packages }} + + - name: Create all cache fingerprints + shell: bash + run: make fingerprints diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4214c32c..2bf1e63b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,10 +8,6 @@ on: tags: - "v*" -env: - GO_VERSION: "1.18.x" - GO_CACHE_KEY: efa04b89c1b1 - jobs: quality-gate: environment: release @@ -69,33 +65,12 @@ jobs: contents: write steps: - - uses: actions/setup-go@v4 - with: - go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v3 with: fetch-depth: 0 - - name: Restore tool cache - id: tool-cache - 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@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ 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' - run: make bootstrap + - name: Bootstrap environment + uses: ./.github/actions/bootstrap - name: Build & publish release artifacts run: make release diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 52c27122..61aa116a 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -6,9 +6,6 @@ on: - main pull_request: -env: - GO_VERSION: "1.18.x" - GO_CACHE_KEY: efa04b89c1b1 jobs: @@ -17,31 +14,10 @@ jobs: name: "Static analysis" runs-on: ubuntu-20.04 steps: - - uses: actions/setup-go@v4 - with: - go-version: ${{ env.GO_VERSION }} - - uses: actions/checkout@v3 - - name: Restore tool cache - id: tool-cache - 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@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ 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' - run: make bootstrap + - name: Bootstrap environment + uses: ./.github/actions/bootstrap - name: Run static analysis run: make static-analysis @@ -51,9 +27,6 @@ jobs: name: "Unit tests" runs-on: macOS-12 steps: - - uses: actions/setup-go@v4 - with: - go-version: ${{ env.GO_VERSION }} - uses: actions/checkout@v3 with: @@ -64,25 +37,8 @@ jobs: # 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@v3 - with: - path: ${{ github.workspace }}/.tmp - key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - - - name: Restore go cache - id: go-cache - uses: actions/cache@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ 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' - run: make bootstrap + - name: Bootstrap environment + uses: ./.github/actions/bootstrap - name: Run unit tests run: make unit @@ -96,42 +52,53 @@ jobs: name: "Build snapshot artifacts" runs-on: ubuntu-20.04 steps: - - uses: actions/setup-go@v4 + - uses: actions/checkout@v3 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + + - name: Build snapshot artifacts + run: make snapshot + + # why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach). + # see https://github.com/actions/upload-artifact/issues/199 for more info + - name: Upload snapshot artifacts + uses: actions/cache/save@v3 with: - go-version: ${{ env.GO_VERSION }} + path: snapshot + key: snapshot-build-${{ github.run_id }} + Acceptance-Linux: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Acceptance tests (Linux)" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-20.04 + steps: - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - - name: Restore tool cache - id: tool-cache - uses: actions/cache@v3 + - name: Download snapshot build + uses: actions/cache/restore@v3 with: - path: ${{ github.workspace }}/.tmp - key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} + path: snapshot + key: snapshot-build-${{ github.run_id }} - - name: Restore go cache - id: go-cache + - name: Restore install.sh test image cache + id: install-test-image-cache uses: actions/cache@v3 with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- + path: ${{ github.workspace }}/test/install/cache + key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }} - - name: (cache-miss) Bootstrap all project dependencies - if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' - run: make bootstrap + - name: Load test image cache + if: steps.install-test-image-cache.outputs.cache-hit == 'true' + run: make install-test-cache-load - - name: Build snapshot artifacts - run: make snapshot + - name: Run install.sh tests (Linux) + run: make install-test - - uses: actions/upload-artifact@v3 - with: - name: artifacts - path: snapshot/**/* + - name: (cache-miss) Create test image cache + if: steps.install-test-image-cache.outputs.cache-hit != 'true' + run: make install-test-cache-save Cli-Linux: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline @@ -139,9 +106,6 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/setup-go@v4 - with: - go-version: ${{ env.GO_VERSION }} - uses: actions/checkout@v3 with: @@ -152,30 +116,14 @@ jobs: # lfs pull does a lfs fetch and lfs checkout, this is NOT the same as "git pull" run: git lfs pull - - name: Restore go cache - id: go-cache - uses: actions/cache@v3 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go-${{ env.GO_VERSION }}-${{ env.GO_CACHE_KEY }}- - - - name: Restore tool cache - id: tool-cache - uses: actions/cache@v3 - with: - path: ${{ github.workspace }}/.tmp - key: ${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - - - name: (cache-miss) Bootstrap all project dependencies - if: steps.tool-cache.outputs.cache-hit != 'true' || steps.go-cache.outputs.cache-hit != 'true' - run: make bootstrap + - name: Bootstrap environment + uses: ./.github/actions/bootstrap - - uses: actions/download-artifact@v3 + - name: Download snapshot build + uses: actions/cache/restore@v3 with: - name: artifacts path: snapshot + key: snapshot-build-${{ github.run_id }} - name: Run CLI Tests run: make cli diff --git a/.goreleaser.yaml b/.goreleaser.yaml index 0eb2778e..9c0f9b3a 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -17,6 +17,8 @@ builds: goarch: - amd64 - arm64 + - ppc64le + - s390x mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}' ldflags: &build-ldflags | -w @@ -55,8 +57,31 @@ builds: env: *build-env ldflags: *build-ldflags + - id: netbsd-build + dir: *dir + binary: *name + goos: + - netbsd + goarch: + - amd64 + mod_timestamp: *build-timestamp + env: *build-env + ldflags: *build-ldflags + +nfpms: + - license: "Apache 2.0" + maintainer: "Anchore, Inc" + homepage: &website "https://github.com/anchore/quill" + description: &description "Simple mac binary signing and notarization from any platform (replacing the codesign utility for simple use cases)." + formats: + - rpm + - deb archives: + - id: netbsd-archives + builds: + - netbsd-build + - id: linux-archives builds: - linux-build diff --git a/Makefile b/Makefile index f2793760..64a36a84 100644 --- a/Makefile +++ b/Makefile @@ -82,6 +82,9 @@ $(RESULTS_DIR): $(TEMP_DIR): mkdir -p $(TEMP_DIR) + +## Bootstrapping targets ################################# + .PHONY: bootstrap-tools bootstrap-tools: $(TEMP_DIR) #GOBIN="$(realpath $(TEMP_DIR))" go install github.com/anchore/quill/cmd/quill@$(QUILL_VERSION) @@ -100,6 +103,9 @@ bootstrap-go: bootstrap: $(RESULTS_DIR) bootstrap-go bootstrap-tools ## Download and install all go dependencies (+ prep tooling in the ./tmp dir) $(call title,Bootstrapping dependencies) + +## Static analysis targets ################################# + .PHONY: static-analysis static-analysis: lint check-go-mod-tidy check-licenses @@ -133,6 +139,9 @@ check-licenses: check-go-mod-tidy: @ .github/scripts/go-mod-tidy-check.sh && echo "go.mod and go.sum are tidy!" + +## Testing targets ################################# + .PHONY: unit unit: $(RESULTS_DIR) ## Run unit tests (with coverage) $(call title,Running unit tests) @@ -142,11 +151,42 @@ unit: $(RESULTS_DIR) ## Run unit tests (with coverage) @echo "Coverage: $$(cat $(COVER_TOTAL))" @if [ $$(echo "$$(cat $(COVER_TOTAL)) >= $(COVERAGE_THRESHOLD)" | bc -l) -ne 1 ]; then echo "$(RED)$(BOLD)Failed coverage quality gate (> $(COVERAGE_THRESHOLD)%)$(RESET)" && false; fi +## Test-fixture-related targets ################################# + +# note: this is used by CI to determine if various test fixture cache should be restored or recreated +fingerprints: + $(call title,Creating all test cache input fingerprints) + + # for INSTALL integration test fixtures + cd test/install && \ + make cache.fingerprint + + +## install.sh testing targets ################################# + +install-test: $(SNAPSHOT_DIR) + cd test/install && \ + make + +install-test-cache-save: $(SNAPSHOT_DIR) + cd test/install && \ + make save + +install-test-cache-load: $(SNAPSHOT_DIR) + cd test/install && \ + make load + + +## Code generation targets ################################# + .PHONY: update-apple-certs update-apple-certs: ## Update the apple certs checked into the repo $(call title,Updating Apple certs) go generate ./... + +## Build-related targets ################################# + .PHONY: build build: $(SNAPSHOT_DIR) ## Build release snapshot binaries and packages @@ -190,6 +230,9 @@ release: clean-dist clean-changelog CHANGELOG.md ## Build and publish final bina bash -c "$(RELEASE_CMD) --release-notes <(cat CHANGELOG.md) --config $(TEMP_DIR)/goreleaser.yaml" + +## Cleanup targets ################################# + .PHONY: clean clean: clean-dist clean-snapshot ## Remove previous builds, result reports, and test cache rm -rf $(RESULTS_DIR)/* @@ -206,6 +249,9 @@ clean-dist: clean-changelog clean-changelog: rm -f CHANGELOG.md + +## Halp! ################################# + .PHONY: help help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "$(BOLD)$(CYAN)%-25s$(RESET)%s\n", $$1, $$2}' diff --git a/test/install/.dockerignore b/test/install/.dockerignore new file mode 100644 index 00000000..fa29cdff --- /dev/null +++ b/test/install/.dockerignore @@ -0,0 +1 @@ +** \ No newline at end of file diff --git a/test/install/.gitignore b/test/install/.gitignore new file mode 100644 index 00000000..6e25fa8f --- /dev/null +++ b/test/install/.gitignore @@ -0,0 +1 @@ +cache/ \ No newline at end of file diff --git a/test/install/0_search_for_asset_test.sh b/test/install/0_search_for_asset_test.sh new file mode 100755 index 00000000..b5cf8c60 --- /dev/null +++ b/test/install/0_search_for_asset_test.sh @@ -0,0 +1,40 @@ +. test_harness.sh + +# search for an asset in a release checksums file +test_search_for_asset_release() { + fixture=./test-fixtures/quill_0.36.0_checksums.txt + + # search_for_asset [checksums-file-path] [name] [os] [arch] [format] + + # positive case + actual=$(search_for_asset "${fixture}" "quill" "linux" "amd64" "tar.gz") + assertEquals "quill_0.36.0_linux_amd64.tar.gz" "${actual}" "unable to find release asset" + + # negative cases + actual=$(search_for_asset "${fixture}" "quill" "Linux" "amd64" "tar.gz") + assertEquals "" "${actual}" "found a release asset but did not expect to (os)" + + actual=$(search_for_asset "${fixture}" "quill" "darwin" "amd64" "rpm") + assertEquals "" "${actual}" "found a release asset but did not expect to (format)" + +} + +run_test_case test_search_for_asset_release + + +# search for an asset in a snapshot checksums file +test_search_for_asset_snapshot() { + fixture=./test-fixtures/quill_0.35.1-SNAPSHOT-d461f63_checksums.txt + + # search_for_asset [checksums-file-path] [name] [os] [arch] [format] + + # positive case + actual=$(search_for_asset "${fixture}" "quill" "linux" "amd64" "rpm") + assertEquals "quill_0.35.1-SNAPSHOT-d461f63_linux_amd64.rpm" "${actual}" "unable to find snapshot asset" + + # negative case + actual=$(search_for_asset "${fixture}" "quill" "linux" "amd64" "zip") + assertEquals "" "${actual}" "found a snapshot asset but did not expect to (format)" +} + +run_test_case test_search_for_asset_snapshot diff --git a/test/install/1_download_snapshot_asset_test.sh b/test/install/1_download_snapshot_asset_test.sh new file mode 100755 index 00000000..44afecb2 --- /dev/null +++ b/test/install/1_download_snapshot_asset_test.sh @@ -0,0 +1,96 @@ +. test_harness.sh + +DOWNLOAD_SNAPSHOT_POSITIVE_CASES=0 + +# helper for asserting test_positive_snapshot_download_asset positive cases +test_positive_snapshot_download_asset() { + os="$1" + arch="$2" + format="$3" + + # for troubleshooting + # log_set_priority 10 + + name=${PROJECT_NAME} + github_download=$(snapshot_download_url) + version=$(snapshot_version) + + tmpdir=$(mktemp -d) + + actual_filepath=$(download_asset "${github_download}" "${tmpdir}" "${name}" "${os}" "${arch}" "${version}" "${format}" ) + + assertFileExists "${actual_filepath}" "download_asset os=${os} arch=${arch} format=${format}" + + assertFilesEqual \ + "$(snapshot_dir)/${name}_${version}_${os}_${arch}.${format}" \ + "${actual_filepath}" \ + "unable to download os=${os} arch=${arch} format=${format}" + + ((DOWNLOAD_SNAPSHOT_POSITIVE_CASES++)) + + rm -rf -- "$tmpdir" +} + + +test_download_snapshot_asset_exercised_all_assets() { + expected=$(snapshot_assets_count) + + assertEquals "${expected}" "${DOWNLOAD_SNAPSHOT_POSITIVE_CASES}" "did not download all possible assets (missing an os/arch/format variant?)" +} + +# helper for asserting download_asset negative cases +test_negative_snapshot_download_asset() { + os="$1" + arch="$2" + format="$3" + + # for troubleshooting + # log_set_priority 10 + + name=${PROJECT_NAME} + github_download=$(snapshot_download_url) + version=$(snapshot_version) + + tmpdir=$(mktemp -d) + + actual_filepath=$(download_asset "${github_download}" "${tmpdir}" "${name}" "${os}" "${arch}" "${version}" "${format}") + + assertEquals "" "${actual_filepath}" "unable to download os=${os} arch=${arch} format=${format}" + + rm -rf -- "$tmpdir" +} + + +worker_pid=$(setup_snapshot_server) +trap 'teardown_snapshot_server ${worker_pid}' EXIT + +# exercise all possible assets +run_test_case test_positive_snapshot_download_asset "netbsd" "amd64" "tar.gz" + +run_test_case test_positive_snapshot_download_asset "linux" "amd64" "tar.gz" +run_test_case test_positive_snapshot_download_asset "linux" "amd64" "rpm" +run_test_case test_positive_snapshot_download_asset "linux" "amd64" "deb" +run_test_case test_positive_snapshot_download_asset "linux" "arm64" "tar.gz" +run_test_case test_positive_snapshot_download_asset "linux" "arm64" "rpm" +run_test_case test_positive_snapshot_download_asset "linux" "arm64" "deb" +run_test_case test_positive_snapshot_download_asset "linux" "ppc64le" "tar.gz" +run_test_case test_positive_snapshot_download_asset "linux" "ppc64le" "rpm" +run_test_case test_positive_snapshot_download_asset "linux" "ppc64le" "deb" +run_test_case test_positive_snapshot_download_asset "linux" "s390x" "tar.gz" +run_test_case test_positive_snapshot_download_asset "linux" "s390x" "rpm" +run_test_case test_positive_snapshot_download_asset "linux" "s390x" "deb" + +run_test_case test_positive_snapshot_download_asset "darwin" "amd64" "tar.gz" +run_test_case test_positive_snapshot_download_asset "darwin" "arm64" "tar.gz" + +run_test_case test_positive_snapshot_download_asset "windows" "amd64" "zip" +# note: the mac signing process produces a dmg which is not part of the snapshot process (thus is not exercised here) + +# let's make certain we covered all assets that were expected +run_test_case test_download_snapshot_asset_exercised_all_assets + +# make certain we handle missing assets alright +run_test_case test_negative_snapshot_download_asset "bogus" "amd64" "zip" + +trap - EXIT +teardown_snapshot_server "${worker_pid}" diff --git a/test/install/2_download_release_asset_test.sh b/test/install/2_download_release_asset_test.sh new file mode 100755 index 00000000..e11df25c --- /dev/null +++ b/test/install/2_download_release_asset_test.sh @@ -0,0 +1,37 @@ +. test_harness.sh + +test_download_release_asset() { + release="$1" + os="$2" + arch="$3" + format="$4" + expected_mime_type="$5" + + # for troubleshooting + # log_set_priority 10 + + name=${PROJECT_NAME} + version=$(tag_to_version ${release}) + github_download="https://github.com/${OWNER}/${REPO}/releases/download/${release}" + + tmpdir=$(mktemp -d) + + actual_filepath=$(download_asset "${github_download}" "${tmpdir}" "${name}" "${os}" "${arch}" "${version}" "${format}" ) + + assertFileExists "${actual_filepath}" "download_asset os=${os} arch=${arch} format=${format}" + + actual_mime_type=$(file -b --mime-type ${actual_filepath}) + + assertEquals "${expected_mime_type}" "${actual_mime_type}" "unexpected mimetype for os=${os} arch=${arch} format=${format}" + + rm -rf -- "$tmpdir" +} + +# always test against the latest release +release=$(get_release_tag "${OWNER}" "${REPO}" "latest" ) + +# exercise all possible assets against a real github release (based on asset listing from https://github.com/anchore/quill/releases/tag/v0.2.0) +run_test_case test_download_release_asset "${release}" "darwin" "amd64" "tar.gz" "application/gzip" +run_test_case test_download_release_asset "${release}" "darwin" "arm64" "tar.gz" "application/gzip" +run_test_case test_download_release_asset "${release}" "linux" "amd64" "tar.gz" "application/gzip" +run_test_case test_download_release_asset "${release}" "linux" "arm64" "tar.gz" "application/gzip" diff --git a/test/install/3_install_asset_test.sh b/test/install/3_install_asset_test.sh new file mode 100755 index 00000000..e3b2b8de --- /dev/null +++ b/test/install/3_install_asset_test.sh @@ -0,0 +1,99 @@ +. test_harness.sh + +INSTALL_ARCHIVE_POSITIVE_CASES=0 + +# helper for asserting install_asset positive cases +test_positive_snapshot_install_asset() { + os="$1" + arch="$2" + format="$3" + + # for troubleshooting + # log_set_priority 10 + + name=${PROJECT_NAME} + binary=$(get_binary_name "${os}" "${arch}" "${PROJECT_NAME}") + github_download=$(snapshot_download_url) + version=$(snapshot_version) + + download_dir=$(mktemp -d) + install_dir=$(mktemp -d) + + download_and_install_asset "${github_download}" "${download_dir}" "${install_dir}" "${name}" "${os}" "${arch}" "${version}" "${format}" "${binary}" + + assertEquals "0" "$?" "download/install did not succeed" + + expected_path="${install_dir}/${binary}" + assertFileExists "${expected_path}" "install_asset os=${os} arch=${arch} format=${format}" + + # directory structure for arch has been updated as of go 1.18 + # https://goreleaser.com/customization/build/#why-is-there-a-_v1-suffix-on-amd64-buildsjk + if [ $arch == "amd64" ]; then + arch="amd64_v1" + fi + + assertFilesEqual \ + "$(snapshot_dir)/${os}-build_${os}_${arch}/${binary}" \ + "${expected_path}" \ + "unable to verify installation of os=${os} arch=${arch} format=${format}" + + ((INSTALL_ARCHIVE_POSITIVE_CASES++)) + + rm -rf -- "$download_dir" + rm -rf -- "$install_dir" +} + +# helper for asserting install_asset negative cases +test_negative_snapshot_install_asset() { + os="$1" + arch="$2" + format="$3" + + # for troubleshooting + # log_set_priority 10 + + name=${PROJECT_NAME} + binary=$(get_binary_name "${os}" "${arch}" "${PROJECT_NAME}") + github_download=$(snapshot_download_url) + version=$(snapshot_version) + + download_dir=$(mktemp -d) + install_dir=$(mktemp -d) + + download_and_install_asset "${github_download}" "${download_dir}" "${install_dir}" "${name}" "${os}" "${arch}" "${version}" "${format}" "${binary}" + + assertNotEquals "0" "$?" "download/install should have failed but did not" + + rm -rf -- "$download_dir" + rm -rf -- "$install_dir" +} + + +test_install_asset_exercised_all_archive_assets() { + expected=$(snapshot_assets_archive_count) + + assertEquals "${expected}" "${INSTALL_ARCHIVE_POSITIVE_CASES}" "did not download all possible archive assets (missing an os/arch/format variant?)" +} + + +worker_pid=$(setup_snapshot_server) +trap 'teardown_snapshot_server ${worker_pid}' EXIT + +# exercise all possible archive assets (not rpm/deb/dmg) against a snapshot build +run_test_case test_positive_snapshot_install_asset "netbsd" "amd64" "tar.gz" +run_test_case test_positive_snapshot_install_asset "linux" "amd64" "tar.gz" +run_test_case test_positive_snapshot_install_asset "linux" "arm64" "tar.gz" +run_test_case test_positive_snapshot_install_asset "linux" "ppc64le" "tar.gz" +run_test_case test_positive_snapshot_install_asset "linux" "s390x" "tar.gz" +run_test_case test_positive_snapshot_install_asset "darwin" "amd64" "tar.gz" +run_test_case test_positive_snapshot_install_asset "darwin" "arm64" "tar.gz" +run_test_case test_positive_snapshot_install_asset "windows" "amd64" "zip" + +# let's make certain we covered all assets that were expected +run_test_case test_install_asset_exercised_all_archive_assets + +# make certain we handle missing assets alright +run_test_case test_negative_snapshot_install_asset "bogus" "amd64" "zip" + +trap - EXIT +teardown_snapshot_server "${worker_pid}" diff --git a/test/install/Makefile b/test/install/Makefile new file mode 100644 index 00000000..0a3749a4 --- /dev/null +++ b/test/install/Makefile @@ -0,0 +1,127 @@ +NAME=quill + +IMAGE_NAME=$(NAME)-install.sh-env +UBUNTU_IMAGE=$(IMAGE_NAME):ubuntu-20.04 +ALPINE_IMAGE=$(IMAGE_NAME):alpine-3.6 +BUSYBOX_IMAGE=busybox:1.35 + +ENVS=./environments +DOCKER_RUN=docker run --rm -t -w /project/test/install -v $(shell pwd)/../../:/project +UNIT=make unit-local + +# acceptance testing is running the current install.sh against the latest release. Note: this could be a problem down +# the line if there are breaking changes made that don't align with the latest release (but will be OK with the next +# release) +ACCEPTANCE_CMD=sh -c '../../install.sh -b /usr/local/bin && quill version' +# we also want to test against a previous release to ensure that install.sh defers execution to a former install.sh +PREVIOUS_RELEASE=v0.33.0 +ACCEPTANCE_PREVIOUS_RELEASE_CMD=sh -c "../../install.sh -b /usr/local/bin $(PREVIOUS_RELEASE) && quill version" + +# CI cache busting values; change these if you want CI to not use previous stored cache +INSTALL_TEST_CACHE_BUSTER=894d8ca + +define title + @printf '\n≡≡≡[ $(1) ]≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡\n' +endef + +.PHONY: test +test: unit acceptance + +.PHONY: ci-test-mac +ci-test-mac: unit-local acceptance-local + +# note: do not add acceptance-local to this list +.PHONY: acceptance +acceptance: acceptance-ubuntu-20.04 acceptance-alpine-3.6 acceptance-busybox-1.35 + +.PHONY: unit +unit: unit-ubuntu-20.04 + +.PHONY: unit-local +unit-local: + $(call title,unit tests) + @for f in $(shell ls *_test.sh); do echo "Running unit test suite '$${f}'"; bash $${f} || exit 1; done + +.PHONY: acceptance-local +acceptance-local: acceptance-current-release-local acceptance-previous-release-local + +.PHONY: acceptance-current-release-local +acceptance-current-release-local: + $(ACCEPTANCE_CMD) + +.PHONY: acceptance-previous-release-local +acceptance-previous-release-local: + $(ACCEPTANCE_PREVIOUS_RELEASE_CMD) + quill version | grep $(shell echo $(PREVIOUS_RELEASE)| tr -d "v") + +.PHONY: save +save: ubuntu-20.04 alpine-3.6 busybox-1.35 + @mkdir cache || true + docker image save -o cache/ubuntu-env.tar $(UBUNTU_IMAGE) + docker image save -o cache/alpine-env.tar $(ALPINE_IMAGE) + docker image save -o cache/busybox-env.tar $(BUSYBOX_IMAGE) + +.PHONY: load +load: + docker image load -i cache/ubuntu-env.tar + docker image load -i cache/alpine-env.tar + docker image load -i cache/busybox-env.tar + +## UBUNTU ####################################################### + +.PHONY: acceptance-ubuntu-20.04 +acceptance-ubuntu-20.04: ubuntu-20.04 + $(call title,ubuntu:20.04 - acceptance) + $(DOCKER_RUN) $(UBUNTU_IMAGE) \ + $(ACCEPTANCE_CMD) + +.PHONY: unit-ubuntu-20.04 +unit-ubuntu-20.04: ubuntu-20.04 + $(call title,ubuntu:20.04 - unit) + $(DOCKER_RUN) $(UBUNTU_IMAGE) \ + $(UNIT) + +.PHONY: ubuntu-20.04 +ubuntu-20.04: + $(call title,ubuntu:20.04 - build environment) + docker build -t $(UBUNTU_IMAGE) -f $(ENVS)/Dockerfile-ubuntu-20.04 . + +## ALPINE ####################################################### + +# note: unit tests cannot be run with sh (alpine dosn't have bash by default) + +.PHONY: acceptance-alpine-3.6 +acceptance-alpine-3.6: alpine-3.6 + $(call title,alpine:3.6 - acceptance) + $(DOCKER_RUN) $(ALPINE_IMAGE) \ + $(ACCEPTANCE_CMD) + +.PHONY: alpine-3.6 +alpine-3.6: + $(call title,alpine:3.6 - build environment) + docker build -t $(ALPINE_IMAGE) -f $(ENVS)/Dockerfile-alpine-3.6 . + +## BUSYBOX ####################################################### + +# note: unit tests cannot be run with sh (busybox dosn't have bash by default) + +# note: busybox by default will not have cacerts, so you will get TLS warnings (we want to test under these conditions) + +.PHONY: acceptance-busybox-1.35 +acceptance-busybox-1.35: busybox-1.35 + $(call title,busybox-1.35 - acceptance) + $(DOCKER_RUN) $(BUSYBOX_IMAGE) \ + $(ACCEPTANCE_CMD) + @echo "\n*** test note: you should see quill spit out a 'x509: certificate signed by unknown authority' error --this is expected ***" + +.PHONY: busybox-1.35 +busybox-1.35: + $(call title,busybox-1.35 - build environment) + docker pull $(BUSYBOX_IMAGE) + +## For CI ######################################################## + +.PHONY: cache.fingerprint +cache.fingerprint: + $(call title,Install test fixture fingerprint) + @find ./environments/* -type f -exec md5sum {} + | awk '{print $1}' | sort | tee /dev/stderr | md5sum | tee cache.fingerprint && echo "$(INSTALL_TEST_CACHE_BUSTER)" >> cache.fingerprint diff --git a/test/install/environments/Dockerfile-alpine-3.6 b/test/install/environments/Dockerfile-alpine-3.6 new file mode 100644 index 00000000..982e5402 --- /dev/null +++ b/test/install/environments/Dockerfile-alpine-3.6 @@ -0,0 +1,2 @@ +FROM alpine:3.6 +RUN apk update && apk add python3 wget unzip make ca-certificates \ No newline at end of file diff --git a/test/install/environments/Dockerfile-ubuntu-20.04 b/test/install/environments/Dockerfile-ubuntu-20.04 new file mode 100644 index 00000000..dafb64ed --- /dev/null +++ b/test/install/environments/Dockerfile-ubuntu-20.04 @@ -0,0 +1,2 @@ +FROM ubuntu:20.04 +RUN apt update -y && apt install make python3 curl unzip -y \ No newline at end of file diff --git a/test/install/github_test.sh b/test/install/github_test.sh new file mode 100755 index 00000000..75516c9c --- /dev/null +++ b/test/install/github_test.sh @@ -0,0 +1,68 @@ +. test_harness.sh + +# check that we can extract single json values +test_extract_json_value() { + fixture=./test-fixtures/github-api-quill-v0.2.0-release.json + content=$(cat ${fixture}) + + actual=$(extract_json_value "${content}" "tag_name") + assertEquals "v0.2.0" "${actual}" "unable to find tag_name" + + actual=$(extract_json_value "${content}" "id") + assertEquals "57501596" "${actual}" "unable to find tag_name" +} + +run_test_case test_extract_json_value + + +# check that we can extract github release tag from github api json +test_github_release_tag() { + fixture=./test-fixtures/github-api-quill-v0.2.0-release.json + content=$(cat ${fixture}) + + actual=$(github_release_tag "${content}") + assertEquals "v0.2.0" "${actual}" "unable to find release tag" +} + +run_test_case test_github_release_tag + + +# download a known good github release checksums and compare against a test-fixture +test_download_github_release_checksums() { + tmpdir=$(mktemp -d) + + tag=v0.2.0 + github_download="https://github.com/anchore/quill/releases/download/${tag}" + name=${PROJECT_NAME} + version=$(tag_to_version "${tag}") + + actual_filepath=$(download_github_release_checksums "${github_download}" "${name}" "${version}" "${tmpdir}") + assertFilesEqual \ + "./test-fixtures/quill_0.2.0_checksums.txt" \ + "${actual_filepath}" \ + "unable to find release tag" + + rm -rf -- "$tmpdir" +} + +run_test_case test_download_github_release_checksums + + +# download a checksums file from a locally served-up snapshot directory and compare against the file in the snapshot dir +test_download_github_release_checksums_snapshot() { + tmpdir=$(mktemp -d) + + github_download=$(snapshot_download_url) + name=${PROJECT_NAME} + version=$(snapshot_version) + + actual_filepath=$(download_github_release_checksums "${github_download}" "${name}" "${version}" "${tmpdir}") + assertFilesEqual \ + "$(snapshot_checksums_path)" \ + "${actual_filepath}" \ + "unable to find release tag" + + rm -rf -- "$tmpdir" +} + +run_test_case_with_snapshot_release test_download_github_release_checksums_snapshot \ No newline at end of file diff --git a/test/install/test-fixtures/github-api-quill-v0.2.0-release.json b/test/install/test-fixtures/github-api-quill-v0.2.0-release.json new file mode 100644 index 00000000..c4e870f7 --- /dev/null +++ b/test/install/test-fixtures/github-api-quill-v0.2.0-release.json @@ -0,0 +1 @@ +{"id":57501596,"tag_name":"v0.2.0","update_url":"/anchore/quill/releases/tag/v0.2.0","update_authenticity_token":"7XbNZgRHpbHegdv-xRlbe84Y983YgyXa3YKWwv_e0ocqTHagsHq5dxCTQUQnuX3vbsgdWQU3A3__hkVNhKGHSg","delete_url":"/anchore/quill/releases/tag/v0.2.0","delete_authenticity_token":"6tLaRtXKUc-zz4tHIwCbbD7CksxIHK5imZE1gnA39oVCe6fYux5a8cPD9J52kGUzM1Hs9JPBjceG7yyszBk_2A","edit_url":"/anchore/quill/releases/edit/v0.2.0"} diff --git a/test/install/test-fixtures/quill_0.2.0_checksums.txt b/test/install/test-fixtures/quill_0.2.0_checksums.txt new file mode 100644 index 00000000..87e645d5 --- /dev/null +++ b/test/install/test-fixtures/quill_0.2.0_checksums.txt @@ -0,0 +1,5 @@ +068ae1bb57cc8fef8269961a85f80c91c5dc115ae626d22f0e941e1fdde7e984 quill_0.2.0_windows_amd64.zip +0e1e5c6e5fee45d9490d8d8acc390d0977fd98fce965854c1d4f5ee660abd15a quill_0.2.0_linux_arm64.tar.gz +6d6d373ee32ea115ce64bd184ea24386795cf6bef50620905d677461f502bbff quill_0.2.0_darwin_amd64.tar.gz +85a4a16a4206874c42acee4c5e5031f70ff61251778d101ec7ba866167c4bca4 quill_0.2.0_darwin_arm64.tar.gz +c6020a2f95a9a2e53b5d08d9b39c62a5fb9990fc15ad67bd5bb1374feab059c2 quill_0.2.0_linux_amd64.tar.gz diff --git a/test/install/test-fixtures/quill_0.35.1-SNAPSHOT-d461f63_checksums.txt b/test/install/test-fixtures/quill_0.35.1-SNAPSHOT-d461f63_checksums.txt new file mode 100644 index 00000000..19a9826c --- /dev/null +++ b/test/install/test-fixtures/quill_0.35.1-SNAPSHOT-d461f63_checksums.txt @@ -0,0 +1,17 @@ +0761f645009cea20aa45f6ef56ce7a5dac987f76f53acc7432fca65c0493cb06 quill_0.35.1-SNAPSHOT-d461f63_windows_amd64.zip +209e9fc173f384b87412f8e98d0b342853085c6e73b4b51792b34c25aa7741ac quill_0.35.1-SNAPSHOT-d461f63_linux_ppc64le.rpm +2e60386cd4a174ff064a9048e75217704fa5c381d6e53b86d96b02389629c6ce quill_0.35.1-SNAPSHOT-d461f63_linux_amd64.tar.gz +36e9b867b600fa2532c9de50f70a712eee107b719ad1813d1ed5a8fe40392a12 quill_0.35.1-SNAPSHOT-d461f63_darwin_arm64.tar.gz +3cba7a2afa708225b8720e5db47212d65361303a39bd44508e06f22544257428 quill_0.35.1-SNAPSHOT-d461f63_linux_ppc64le.deb +4f5183deafbdc38610d911cccb457bbe0de1b6ac867a9b24f563e87d5e786e6a quill_0.35.1-SNAPSHOT-d461f63_netbsd_amd64.tar.gz +55cfee449511bae90726f125667c744393079317dcaea7852e4fe9ea24a299b4 quill_0.35.1-SNAPSHOT-d461f63_linux_arm64.rpm +9d38ddbe70ed94e24136534f747ff36b723d374ff87356903163eb8be7244238 quill_0.35.1-SNAPSHOT-d461f63_linux_amd64.deb +a4e59963f74a1be145028c3e7c01ecaeeff7eeb320da1182e2b7bb251b1013e6 quill_0.35.1-SNAPSHOT-d461f63_linux_arm64.deb +a68fdb7c95a596f4199d963966b03e7b47088b910fce6333c443a0d3468b879a quill_0.35.1-SNAPSHOT-d461f63_linux_amd64.rpm +a962b09118b659c30e2bc2f7985a10cc18ac41640f619cb6d924bc94a05cbf0c quill_0.35.1-SNAPSHOT-d461f63_linux_s390x.tar.gz +b7b2110396c382aebefe0f68b9d5d724320c6433fea5927b61186890896675a5 quill_0.35.1-SNAPSHOT-d461f63_linux_arm64.tar.gz +c1c19ad1f64118c50ff68f2076882807de738c1a3e2ff8813639f340bbbe6c0a quill_0.35.1-SNAPSHOT-d461f63_linux_s390x.rpm +df2011664dc1f5e443ae4ad522ea28d046c549a9c21e1954d9d89f52a1dd53c8 quill_0.35.1-SNAPSHOT-d461f63_darwin_amd64.tar.gz +f8b7eb24e2c74e72bff3a5b1998cf096a395a9bf9c1a61db15a2e9a4e83c348c quill_0.35.1-SNAPSHOT-d461f63_linux_ppc64le.tar.gz +fda586625976befc21ce15af61a2046ac254286fa30e906b10aa452c9deb1d00 quill_0.35.1-SNAPSHOT-d461f63_linux_s390x.deb + diff --git a/test/install/test-fixtures/quill_0.36.0_checksums.txt b/test/install/test-fixtures/quill_0.36.0_checksums.txt new file mode 100644 index 00000000..e75d78f3 --- /dev/null +++ b/test/install/test-fixtures/quill_0.36.0_checksums.txt @@ -0,0 +1,16 @@ +0761f645009cea20aa45f6ef56ce7a5dac987f76f53acc7432fca65c0493cb06 quill_0.36.0_windows_amd64.zip +209e9fc173f384b87412f8e98d0b342853085c6e73b4b51792b34c25aa7741ac quill_0.36.0_linux_ppc64le.rpm +2e60386cd4a174ff064a9048e75217704fa5c381d6e53b86d96b02389629c6ce quill_0.36.0_linux_amd64.tar.gz +36e9b867b600fa2532c9de50f70a712eee107b719ad1813d1ed5a8fe40392a12 quill_0.36.0_darwin_arm64.tar.gz +3cba7a2afa708225b8720e5db47212d65361303a39bd44508e06f22544257428 quill_0.36.0_linux_ppc64le.deb +4f5183deafbdc38610d911cccb457bbe0de1b6ac867a9b24f563e87d5e786e6a quill_0.36.0_netbsd_amd64.tar.gz +55cfee449511bae90726f125667c744393079317dcaea7852e4fe9ea24a299b4 quill_0.36.0_linux_arm64.rpm +9d38ddbe70ed94e24136534f747ff36b723d374ff87356903163eb8be7244238 quill_0.36.0_linux_amd64.deb +a4e59963f74a1be145028c3e7c01ecaeeff7eeb320da1182e2b7bb251b1013e6 quill_0.36.0_linux_arm64.deb +a68fdb7c95a596f4199d963966b03e7b47088b910fce6333c443a0d3468b879a quill_0.36.0_linux_amd64.rpm +a962b09118b659c30e2bc2f7985a10cc18ac41640f619cb6d924bc94a05cbf0c quill_0.36.0_linux_s390x.tar.gz +b7b2110396c382aebefe0f68b9d5d724320c6433fea5927b61186890896675a5 quill_0.36.0_linux_arm64.tar.gz +c1c19ad1f64118c50ff68f2076882807de738c1a3e2ff8813639f340bbbe6c0a quill_0.36.0_linux_s390x.rpm +df2011664dc1f5e443ae4ad522ea28d046c549a9c21e1954d9d89f52a1dd53c8 quill_0.36.0_darwin_amd64.tar.gz +f8b7eb24e2c74e72bff3a5b1998cf096a395a9bf9c1a61db15a2e9a4e83c348c quill_0.36.0_linux_ppc64le.tar.gz +fda586625976befc21ce15af61a2046ac254286fa30e906b10aa452c9deb1d00 quill_0.36.0_linux_s390x.deb diff --git a/test/install/test_harness.sh b/test/install/test_harness.sh new file mode 100644 index 00000000..65ac7956 --- /dev/null +++ b/test/install/test_harness.sh @@ -0,0 +1,190 @@ +# disable using the install.sh entrypoint such that we can unit test +# script functions without invoking main() +TEST_INSTALL_SH=true + +. ../../install.sh +set -u + +echoerr() { + echo "$@" 1>&2 +} + +printferr() { + printf "%s" "$*" >&2 +} + + +assertTrue() { + if eval "$1"; then + echo "assertTrue failed: $2" + exit 2 + fi +} + +assertFalse() { + if eval "$1"; then + echo "assertFalse failed: $2" + exit 2 + fi +} + +assertEquals() { + want=$1 + got=$2 + msg=$3 + if [ "$want" != "$got" ]; then + echo "assertEquals failed: want='$want' got='$got' $msg" + exit 2 + fi +} + +assertFilesDoesNotExist() { + path="$1" + msg=$2 + if [ -f "${path}" ]; then + echo "assertFilesDoesNotExist failed: path exists '$path': $msg" + exit 2 + fi +} + +assertFileExists() { + path="$1" + msg=$2 + if [ ! -f "${path}" ]; then + echo "assertFileExists failed: path does not exist '$path': $msg" + exit 2 + fi +} + +assertFilesEqual() { + want=$1 + got=$2 + msg=$3 + + diff "$1" "$2" + if [ $? -ne 0 ]; then + echo "assertFilesEqual failed: $msg" + exit 2 + fi +} + +assertNotEquals() { + want=$1 + got=$2 + msg=$3 + if [ "$want" = "$got" ]; then + echo "assertNotEquals failed: want='$want' got='$got' $msg" + exit 2 + fi +} + +log_test_case() { + echo " running $@" +} + +run_test_case_with_snapshot_release() { + log_test_case ${@:1} + + worker_pid=$(setup_snapshot_server) + trap "teardown_snapshot_server $worker_pid" EXIT + + # run test function with all arguments + ${@:1} + + trap - EXIT + teardown_snapshot_server "${worker_pid}" +} + +serve_port=8000 + +setup_snapshot_server() { + # if you want to see proof in the logs, feel free to adjust the redirection + python3 -m http.server --directory "$(snapshot_dir)" $serve_port &> /dev/null & + worker_pid=$! + + echoerr "serving up $(snapshot_dir) on port $serve_port" + + echoerr "$(ls -1 $(snapshot_dir) | sed 's/^/ ▕―― /')" + + check_snapshots_server_ready + + echoerr "snapshot server ready! (worker=${worker_pid})" + + echo "$worker_pid" +} + +check_snapshots_server_ready() { + i=0 + until $(curl -m 3 --output /dev/null --silent --head --fail localhost:$serve_port/); do + sleep 1 + ((i=i+1)) + if [ "$i" -gt "30" ]; then + echoerr "could not connect to local snapshot server! bailing..." + exit 1 + fi + printferr '.' + done +} + +teardown_snapshot_server() { + worker_pid="$1" + echoerr "stopping worker=${worker_pid}" + kill "$worker_pid" +} + +snapshot_version() { + partial=$(ls ../../snapshot/*_checksums.txt | grep -o "_.*_checksums.txt") + partial="${partial%_checksums.txt}" + echo "${partial#_}" +} + +snapshot_download_url() { + echo "localhost:${serve_port}" +} + +snapshot_dir() { + echo "../../snapshot" +} + +snapshot_checksums_path() { + echo "$(ls $(snapshot_dir)/*_checksums.txt)" +} + +snapshot_assets_count() { + # example output before wc -l: + + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_arm64.deb + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_arm64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_amd64.rpm + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_arm64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_amd64.deb + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_arm64.rpm + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_amd64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_windows_amd64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_arm64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_amd64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_amd64.tar.gz + + echo "$(find ../../snapshot -maxdepth 1 -type f | grep 'quill_' | grep -v checksums | wc -l | tr -d '[:space:]')" +} + + +snapshot_assets_archive_count() { + # example output before wc -l: + + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_arm64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_arm64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_amd64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_windows_amd64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_arm64.zip + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_linux_amd64.tar.gz + # ../../snapshot/quill_0.36.0-SNAPSHOT-e5e847a_darwin_amd64.tar.gz + + echo "$(find ../../snapshot -maxdepth 1 -type f | grep 'quill_' | grep 'tar\|zip' | wc -l | tr -d '[:space:]')" +} + + +run_test_case() { + log_test_case ${@:1} + ${@:1} +}