version: "3" vars: OWNER: wagoodman PROJECT: dive # static file dirs TOOL_DIR: .tool TMP_DIR: .tmp # TOOLS BINNY: "{{ .TOOL_DIR }}/binny" CHRONICLE: "{{ .TOOL_DIR }}/chronicle" GORELEASER: "{{ .TOOL_DIR }}/goreleaser" GOLANGCI_LINT: "{{ .TOOL_DIR }}/golangci-lint" TASK: "{{ .TOOL_DIR }}/task" BOUNCER: "{{ .TOOL_DIR }}/bouncer" GLOW: "{{ .TOOL_DIR }}/glow" # used for changelog generation CHANGELOG: CHANGELOG.md NEXT_VERSION: VERSION # note: the snapshot dir must be a relative path starting with ./ SNAPSHOT_DIR: ./snapshot SNAPSHOT_CMD: "{{ .GORELEASER }} release --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --skip=publish --skip=sign" BUILD_CMD: "{{ .GORELEASER }} build --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --single-target" RELEASE_CMD: "{{ .GORELEASER }} release --clean --release-notes {{ .CHANGELOG }}" VERSION: sh: git describe --dirty --always --tags # used for acceptance tests TEST_IMAGE: busybox:latest DOCKER_CLI_VERSION: 28.0.0 env: GNUMAKEFLAGS: '--no-print-directory' DOCKER_CLI_VERSION: "{{ .DOCKER_CLI_VERSION }}" tasks: ## High-level tasks ################################# default: desc: Run all validation tasks aliases: - pr-validations - validations cmds: - task: static-analysis - task: test static-analysis: desc: Run all static analysis tasks cmds: - task: check-go-mod-tidy - task: check-licenses - task: lint test: desc: Run all levels of test cmds: - task: unit - task: cli ## Bootstrap tasks ################################# binny: internal: true # desc: Get the binny tool generates: - "{{ .BINNY }}" status: - "test -f {{ .BINNY }}" cmd: "curl -sSfL https://raw.githubusercontent.com/anchore/binny/main/install.sh | sh -s -- -b .tool" silent: true tools: desc: Install all tools needed for CI and local development deps: [binny] aliases: - bootstrap generates: - ".binny.yaml" - "{{ .TOOL_DIR }}/*" status: - "{{ .BINNY }} check -v" cmd: "{{ .BINNY }} install -v" silent: true update-tools: desc: Update pinned versions of all tools to their latest available versions deps: [binny] generates: - ".binny.yaml" - "{{ .TOOL_DIR }}/*" cmd: "{{ .BINNY }} update -v" silent: true list-tools: desc: List all tools needed for CI and local development deps: [binny] cmd: "{{ .BINNY }} list" silent: true list-tool-updates: desc: List all tools that are not up to date relative to the binny config deps: [binny] cmd: "{{ .BINNY }} list --updates" silent: true tmpdir: silent: true generates: - "{{ .TMP_DIR }}" cmd: "mkdir -p {{ .TMP_DIR }}" ## Static analysis tasks ################################# format: desc: Auto-format all source code deps: [tools] cmds: - gofmt -w -s . - go mod tidy lint-fix: desc: Auto-format all source code + run golangci lint fixers deps: [tools] cmds: - task: format - "{{ .GOLANGCI_LINT }} run --tests=false --fix" lint: desc: Run gofmt + golangci lint checks vars: BAD_FMT_FILES: sh: gofmt -l -s . BAD_FILE_NAMES: sh: "find . | grep -e ':' || true" deps: [tools] cmds: # ensure there are no go fmt differences - cmd: 'test -z "{{ .BAD_FMT_FILES }}" || (echo "files with gofmt issues: [{{ .BAD_FMT_FILES }}]"; exit 1)' silent: true # ensure there are no files with ":" in it (a known back case in the go ecosystem) - cmd: 'test -z "{{ .BAD_FILE_NAMES }}" || (echo "files with bad names: [{{ .BAD_FILE_NAMES }}]"; exit 1)' silent: true # run linting - "{{ .GOLANGCI_LINT }} run --tests=false" check-licenses: # desc: Ensure transitive dependencies are compliant with the current license policy deps: [tools] cmd: "{{ .BOUNCER }} check ./..." check-go-mod-tidy: # desc: Ensure go.mod and go.sum are up to date cmds: - cmd: | if ! go mod tidy -diff; then echo "go.mod and/or go.sum need updates. Please run 'go mod tidy'" exit 1 fi silent: true ## Testing tasks ################################# unit: desc: Run unit tests deps: - tmpdir vars: TEST_PKGS: sh: "go list ./... | grep -v '^github.com/wagoodman/dive/cmd/dive/cli$' | tr '\n' ' '" # unit test coverage threshold (in % coverage) COVERAGE_THRESHOLD: 25 cmds: - "go test -coverprofile {{ .TMP_DIR }}/unit-coverage-details.txt {{ .TEST_PKGS }}" - cmd: ".github/scripts/coverage.py {{ .COVERAGE_THRESHOLD }} {{ .TMP_DIR }}/unit-coverage-details.txt" silent: true cli: desc: Run CLI tests cmds: - "go test github.com/wagoodman/dive/cmd/dive/cli -v" ## Acceptance tests ################################# ci-test-linux: cmds: - task: ci-test-linux-run - task: ci-test-docker-image - task: ci-test-deb-package-install - task: ci-test-rpm-package-install ci-test-docker-image: desc: Test using the docker image cmds: - | docker run \ --rm \ -t \ --env CLICOLOR_FORCE=true \ -v /var/run/docker.sock:/var/run/docker.sock \ 'docker.io/wagoodman/dive:latest' \ '{{ .TEST_IMAGE }}' \ --ci ci-test-deb-package-install: desc: Test debian package installation cmds: - | docker run \ --platform linux/amd64 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /${PWD}:/src \ -w /src \ --env CLICOLOR_FORCE=true \ ubuntu:latest \ /bin/bash -x -c "\ apt update && \ apt install -y curl && \ curl -L 'https://download.docker.com/linux/static/stable/x86_64/docker-{{ .DOCKER_CLI_VERSION }}.tgz' | \ tar -vxzf - docker/docker --strip-component=1 && \ mv docker /usr/local/bin/ &&\ docker version && \ apt install ./snapshot/dive_*_linux_amd64.deb -y && \ dive --version && \ dive '{{ .TEST_IMAGE }}' --ci \ " ci-test-rpm-package-install: desc: Test RPM package installation cmds: - | docker run \ --platform linux/amd64 \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /${PWD}:/src \ -w /src \ --env CLICOLOR_FORCE=true \ fedora:latest \ /bin/bash -x -c "\ curl -L 'https://download.docker.com/linux/static/stable/x86_64/docker-{{ .DOCKER_CLI_VERSION }}.tgz' | \ tar -vxzf - docker/docker --strip-component=1 && \ mv docker /usr/local/bin/ &&\ docker version && \ dnf install ./snapshot/dive_*_linux_amd64.rpm -y && \ dive --version && \ dive '{{ .TEST_IMAGE }}' --ci \ " generate-compressed-test-images: desc: Generate compressed test images for testing cmds: - | for alg in uncompressed gzip estargz zstd; do \ for exporter in docker image; do \ docker buildx build \ -f .data/Dockerfile.minimal \ --tag test-dive-${exporter}:${alg} \ --output type=${exporter},force-compression=true,compression=${alg} . ; \ done ; \ done && \ echo 'Exported test data!' generate-compressed-test-data: desc: Generate compressed test data for testing cmds: - | for alg in uncompressed gzip estargz zstd; \ do \ docker buildx build \ -f .data/Dockerfile.minimal \ --output type=tar,dest=.data/test-${alg}-image.tar,force-compression=true,compression=${alg} . ; \ docker buildx build \ -f .data/Dockerfile.minimal \ --output type=oci,dest=.data/test-oci-${alg}-image.tar,force-compression=true,compression=${alg} . ; \ done && \ echo 'Exported test data!' ci-test-linux-run: desc: Test Linux binary execution (CI only) deps: [ci-check, generate-compressed-test-images] cmds: - | ls -la {{ .SNAPSHOT_DIR }} ls -la {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1 chmod 755 {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1/dive && \ {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1/dive '{{ .TEST_IMAGE }}' --ci && \ {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1/dive --source docker-archive .data/test-kaniko-image.tar --ci --ci-config .data/.dive-ci - | for alg in uncompressed gzip estargz zstd; do \ for exporter in docker image; do \ {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1/dive "test-dive-${exporter}:${alg}" --ci ; \ done && \ {{ .SNAPSHOT_DIR }}/dive_linux_amd64_v1/dive --source docker-archive .data/test-oci-${alg}-image.tar --ci --ci-config .data/.dive-ci; \ done ci-test-mac-run: desc: Test macOS binary execution (CI only) deps: [ci-check] cmds: - | chmod 755 {{ .SNAPSHOT_DIR }}/dive_darwin_amd64_v1/dive && \ {{ .SNAPSHOT_DIR }}/dive_darwin_amd64_v1/dive --source docker-archive .data/test-docker-image.tar --ci --ci-config .data/.dive-ci ## Build-related targets ################################# build: desc: Build the project deps: [tools, tmpdir] generates: - "{{ .PROJECT }}" cmds: - silent: true cmd: | echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml - "{{ .BUILD_CMD }}" snapshot: desc: Create a snapshot release aliases: - build deps: [tools, tmpdir] sources: - "**/*.go" - ".goreleaser.yaml" method: checksum cmds: - silent: true cmd: | echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml - "{{ .SNAPSHOT_CMD }}" changelog: desc: Generate a changelog deps: [tools] generates: - "{{ .CHANGELOG }}" - "{{ .NEXT_VERSION }}" cmds: - "{{ .CHRONICLE }} -vv -n --version-file {{ .NEXT_VERSION }} > {{ .CHANGELOG }}" - "{{ .GLOW }} -w 200 {{ .CHANGELOG }}" ## Release targets ################################# release: desc: Create a release interactive: true deps: [tools] cmds: - cmd: .github/scripts/trigger-release.sh silent: true ## CI-only targets ################################# ci-check: preconditions: - sh: test -n "$CI" msg: "This step should ONLY be run in CI. Exiting..." cmds: - echo "Running in CI environment" silent: true internal: true ci-release: # desc: "[CI only] Create a release" deps: [ci-check, tools] cmds: - "{{ .CHRONICLE }} -vvv > CHANGELOG.md" - cmd: "cat CHANGELOG.md" silent: true - "{{ .RELEASE_CMD }}" ## Cleanup targets ################################# clean-snapshot: desc: Remove any snapshot builds cmds: - "rm -rf {{ .SNAPSHOT_DIR }}" - "rm -rf {{ .TMP_DIR }}/goreleaser.yaml"