name: Tests Workflow on: workflow_dispatch: push: branches: - "**" pull_request: branches: - "**" jobs: unit-tests: name: Unit Tests runs-on: ubuntu-latest timeout-minutes: 30 steps: - name: Clean Up Actions Tools Cache run: rm -rf /opt/hostedtoolcache - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Set Up Go uses: actions/setup-go@v6 with: go-version-file: go.mod check-latest: true - name: Install Go Releaser uses: goreleaser/goreleaser-action@v6 with: install-only: true - name: Install Dependencies run: | sudo apt-get update sudo apt-get install -y make sudo apt-get install -y build-essential - name: Run Makefile Targets run: | make build-all - name: Upload Hauler Binaries uses: actions/upload-artifact@v6 with: name: hauler-binaries path: dist/* - name: Upload Coverage Report uses: actions/upload-artifact@v6 with: name: coverage-report path: coverage.out integration-tests: name: Integration Tests runs-on: ubuntu-latest needs: [unit-tests] timeout-minutes: 30 steps: - name: Clean Up Actions Tools Cache run: rm -rf /opt/hostedtoolcache - name: Checkout uses: actions/checkout@v6 with: fetch-depth: 0 - name: Configure Git run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Install Dependencies run: | sudo apt-get update sudo apt-get install -y unzip sudo apt-get install -y tree sudo apt-get install -y jq - name: Download Artifacts uses: actions/download-artifact@v6 with: name: hauler-binaries path: dist - name: Prepare Hauler for Tests run: | pwd ls -la ls -la dist/ chmod -R 755 dist/ testdata/certificate-script.sh sudo mv dist/hauler_linux_amd64_v1/hauler /usr/local/bin/hauler ./testdata/certificate-script.sh && sudo chown -R $(whoami) testdata/certs/ - name: Verify - hauler version run: | hauler version - name: Verify - hauler completion run: | hauler completion hauler completion bash hauler completion fish hauler completion powershell hauler completion zsh - name: Verify - hauler help run: | hauler help - name: Verify - hauler login run: | hauler login --help hauler login docker.io --username ${{ secrets.DOCKERHUB_USERNAME }} --password ${{ secrets.DOCKERHUB_TOKEN }} echo ${{ secrets.GITHUB_TOKEN }} | hauler login ghcr.io --username ${{ github.repository_owner }} --password-stdin - name: Verify - hauler store run: | hauler store --help - name: Verify - hauler store add run: | hauler store add --help - name: Verify - hauler store add chart run: | hauler store add chart --help # verify via helm repository hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 # verify via oci helm repository hauler store add chart hauler-helm --repo oci://ghcr.io/hauler-dev hauler store add chart hauler-helm --repo oci://ghcr.io/hauler-dev --version 1.0.6 # verify via local helm repository curl -sfOL https://github.com/rancherfederal/rancher-cluster-templates/releases/download/rancher-cluster-templates-0.5.2/rancher-cluster-templates-0.5.2.tgz hauler store add chart rancher-cluster-templates-0.5.2.tgz --repo . curl -sfOL https://github.com/rancherfederal/rancher-cluster-templates/releases/download/rancher-cluster-templates-0.5.1/rancher-cluster-templates-0.5.1.tgz hauler store add chart rancher-cluster-templates-0.5.1.tgz --repo . --version 0.5.1 curl -sfLO https://raw.githubusercontent.com/helm/helm/v3.18.6/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz curl -sfLO https://raw.githubusercontent.com/helm/helm/v3.18.6/cmd/helm/testdata/testcharts/signtest-0.1.0.tgz.prov curl -sfLO https://raw.githubusercontent.com/helm/helm/v3.18.6/cmd/helm/testdata/helm-test-key.pub hauler store add chart signtest-0.1.0.tgz --repo . --version 0.1.0 --verify --keyring helm-test-key.pub # verify via the hauler store contents hauler store info - name: Verify - hauler store add chart --rewrite run: | # add chart with rewrite flag hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 --rewrite custom-path/rancher:2.8.4 # verify new ref in store hauler store info | grep 'custom-path/rancher:2.8.4' # confrim leading slash trimmed from rewrite hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 --rewrite /custom-path/rancher:2.8.4 # verify no leading slash ! hauler store info | grep '/custom-path/rancher:2.8.4' # confirm old tag used if not specified hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 --rewrite /custom-path/rancher # confirm tag hauler store info | grep '2.8.4' - name: Verify - hauler store add file run: | hauler store add file --help # verify via remote file hauler store add file https://get.rke2.io/install.sh hauler store add file https://get.rke2.io/install.sh --name rke2-install.sh # verify via local file hauler store add file testdata/hauler-manifest.yaml hauler store add file testdata/hauler-manifest.yaml --name hauler-manifest-local.yaml # verify via the hauler store contents hauler store info - name: Verify - hauler store add image run: | hauler store add image --help # verify via image reference hauler store add image ghcr.io/hauler-dev/library/busybox # verify via image reference with version and platform hauler store add image ghcr.io/hauler-dev/library/busybox:stable --platform linux/amd64 # verify via image reference with full reference hauler store add image ghcr.io/hauler-dev/distroless/base@sha256:7fa7445dfbebae4f4b7ab0e6ef99276e96075ae42584af6286ba080750d6dfe5 # verify via the hauler store contents hauler store info - name: Verify - hauler store add image --rewrite run: | # add image with rewrite flag hauler store add image ghcr.io/hauler-dev/library/busybox --rewrite custom-registry.io/custom-path/busybox:latest # verify new ref in store hauler store info | grep 'custom-registry.io/custom-path/busybox:latest' # confrim leading slash trimmed from rewrite hauler store add image ghcr.io/hauler-dev/library/busybox --rewrite /custom-path/busybox:latest # verify no leading slash ! hauler store info | grep '/custom-path/busybox:latest' # confirm old tag used if not specified hauler store add image ghcr.io/hauler-dev/library/busybox:stable --rewrite /custom-path/busybox # confirm tag hauler store info | grep ':stable' # confirm old registry used if not specified hauler store add image ghcr.io/hauler-dev/library/nginx:1.25-alpine --rewrite custom-path/nginx # verify existing registry associated with rewritten image in store hauler store info | grep 'ghcr.io/custom-path/nginx' - name: Verify - hauler store copy run: | hauler store copy --help # need more tests here - name: Verify - hauler store extract run: | hauler store extract --help # verify via extracting hauler store content hauler store extract hauler/hauler-manifest-local.yaml:latest # view extracted content from store cat hauler-manifest-local.yaml - name: Verify - hauler store info run: | hauler store info --help # verify via table output hauler store info --output table # verify via json output hauler store info --output json # verify via filtered output (chart) hauler store info --type chart # verify via filtered output (file) hauler store info --type file # verify via filtered output (image) hauler store info --type image # verify store directory structure tree -hC store - name: Verify - hauler store save run: | hauler store save --help # verify via save hauler store save # verify via save with filename hauler store save --filename store.tar.zst # verify via save with filename and platform (amd64) hauler store save --filename store-amd64.tar.zst --platform linux/amd64 # verify via save with chunk-size (splits into haul-chunked.tar.zst.001, haul-chunked.tar.zst.002, ...) hauler store save --filename haul-chunked.tar.zst --chunk-size 50M # verify chunk files exist and original is removed ls haul-chunked.tar.zst.* ! test -f haul-chunked.tar.zst # verify at least two chunks were produced [ $(ls haul-chunked.tar.zst.* | wc -l) -ge 2 ] - name: Remove Hauler Store Contents run: | rm -rf store hauler store info - name: Verify - hauler store load run: | hauler store load --help # verify via load hauler store load # verify via load with multiple files hauler store load --filename haul.tar.zst --filename store.tar.zst # confirm store contents tar -xOf store.tar.zst index.json # verify via load with filename and temp directory hauler store load --filename store.tar.zst --tempdir /opt # verify via load with filename and platform (amd64) hauler store load --filename store-amd64.tar.zst # verify via load from chunks using explicit first chunk rm -rf store hauler store load --filename haul-chunked.tar.zst.001 hauler store info # verify via load from chunks using base filename (auto-detect) rm -rf store hauler store load --filename haul-chunked.tar.zst hauler store info - name: Verify Hauler Store Contents run: | # verify store hauler store info # verify store directory structure tree -hC store - name: Verify - docker load run: | docker load --help # verify via load docker load --input store-amd64.tar.zst - name: Verify Docker Images Contents run: | docker images --help # verify images docker images --all - name: Remove Hauler Store Contents run: | rm -rf store haul.tar.zst store.tar.zst store-amd64.tar.zst haul-chunked.tar.zst.* hauler store info - name: Verify - hauler store sync run: | hauler store sync --help # verify via sync hauler store sync --filename testdata/hauler-manifest-pipeline.yaml # verify images are present hauler store info | grep 'musl' # verify that only one kube-vip image with the correct platform is present hauler store info --output json | jq -e '.artifacts | map(select(.type=="image" and .reference=="ghcr.io/kube-vip/kube-vip:v1.0.4")) | length == 1' >/dev/null # verify via sync with multiple files hauler store sync --filename testdata/hauler-manifest-pipeline.yaml --filename testdata/hauler-manifest.yaml # need more tests here - name: Verify - hauler store sync (image list) run: | # verify via local image list file hauler store sync --image-txt testdata/images.txt # verify via multiple image list files hauler store sync --image-txt testdata/images.txt --image-txt testdata/images.txt # verify via remote image list file hauler store sync --image-txt https://raw.githubusercontent.com/hauler-dev/hauler/main/testdata/images.txt # confirm images are present in the store hauler store info | grep 'busybox' - name: Verify - hauler store serve run: | hauler store serve --help - name: Verify - hauler store serve registry run: | hauler store serve registry --help # verify via registry hauler store serve registry & until curl -sf http://localhost:5000/v2/_catalog; do : ; done pkill -f "hauler store serve registry" # verify via registry with different port hauler store serve registry --port 5001 & until curl -sf http://localhost:5001/v2/_catalog; do : ; done pkill -f "hauler store serve registry --port 5001" # verify via registry with different port and readonly hauler store serve registry --port 5001 --readonly & until curl -sf http://localhost:5001/v2/_catalog; do : ; done pkill -f "hauler store serve registry --port 5001 --readonly" # verify via registry with different port with readonly with tls # hauler store serve registry --port 5001 --readonly --tls-cert testdata/certs/server-cert.crt --tls-key testdata/certs/server-cert.key & # until curl -sf --cacert testdata/certs/cacerts.pem https://localhost:5001/v2/_catalog; do : ; done # pkill -f "hauler store serve registry --port 5001 --readonly --tls-cert testdata/certs/server-cert.crt --tls-key testdata/certs/server-cert.key" - name: Verify - hauler store serve fileserver run: | hauler store serve fileserver --help # verify via fileserver hauler store serve fileserver & until curl -sf http://localhost:8080; do : ; done pkill -f "hauler store serve fileserver" # verify via fileserver with different port hauler store serve fileserver --port 8000 & until curl -sf http://localhost:8000; do : ; done pkill -f "hauler store serve fileserver --port 8000" # verify via fileserver with different port and timeout hauler store serve fileserver --port 8000 --timeout 120 & until curl -sf http://localhost:8000; do : ; done pkill -f "hauler store serve fileserver --port 8000 --timeout 120" # verify via fileserver with different port with timeout and tls # hauler store serve fileserver --port 8000 --timeout 120 --tls-cert testdata/certs/server-cert.crt --tls-key testdata/certs/server-cert.key & # until curl -sf --cacert testdata/certs/cacerts.pem https://localhost:8000; do : ; done # pkill -f "hauler store serve fileserver --port 8000 --timeout 120 --tls-cert testdata/certs/server-cert.crt --tls-key testdata/certs/server-cert.key" - name: Verify Hauler Store Contents run: | # verify store hauler store info # verify store directory structure tree -hC store # verify registry directory structure tree -hC registry # verify fileserver directory structure tree -hC fileserver - name: Verify - hauler store remove (image) run: | hauler store remove --help # add test images hauler store add image ghcr.io/hauler-dev/library/nginx:1.25-alpine hauler store add image ghcr.io/hauler-dev/library/nginx:1.26-alpine # confirm artifacts hauler store info | grep 'nginx:1.25' hauler store info | grep 'nginx:1.26' # count blobs before delete BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact hauler store remove nginx:1.25 --force # verify artifact removed ! hauler store info | grep -q "nginx:1.25" # non-deleted artifact exists hauler store info | grep -q "nginx:1.26" # count blobs after delete BLOBS_AFTER=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs after deletion: $BLOBS_AFTER" # verify only unreferenced blobs removed if [ "$BLOBS_AFTER" -ge "$BLOBS_BEFORE" ]; then echo "ERROR: No blobs were cleaned up" exit 1 fi if [ "$BLOBS_AFTER" -eq 0 ]; then echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi # verify remaining image not missing layers hauler store extract ghcr.io/hauler-dev/library/nginx:1.26-alpine - name: Verify - hauler store remove (chart) run: | hauler store remove --help # add test images hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.4 hauler store add chart rancher --repo https://releases.rancher.com/server-charts/stable --version 2.8.5 # confirm artifacts hauler store info | grep '2.8.4' hauler store info | grep '2.8.5' # count blobs before delete BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact hauler store remove 2.8.4 --force # verify artifact removed ! hauler store info | grep -q "2.8.4" # non-deleted artifact exists hauler store info | grep -q "2.8.5" # count blobs after delete BLOBS_AFTER=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs after deletion: $BLOBS_AFTER" # verify only unreferenced blobs removed if [ "$BLOBS_AFTER" -ge "$BLOBS_BEFORE" ]; then echo "ERROR: No blobs were cleaned up" exit 1 fi if [ "$BLOBS_AFTER" -eq 0 ]; then echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi # verify remaining image not missing layers hauler store extract hauler/rancher:2.8.5 - name: Verify - hauler store remove (file) run: | hauler store remove --help # add test images hauler store add file https://get.hauler.dev hauler store add file https://get.rke2.io/install.sh # confirm artifacts hauler store info | grep 'get.hauler.dev' hauler store info | grep 'install.sh' # count blobs before delete BLOBS_BEFORE=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs before deletion: $BLOBS_BEFORE" # delete one artifact hauler store remove get.hauler.dev --force # verify artifact removed ! hauler store info | grep -q "get.hauler.dev" # non-deleted artifact exists hauler store info | grep -q "install.sh" # count blobs after delete BLOBS_AFTER=$(find store/blobs/sha256 -type f | wc -l | xargs) echo "blobs after deletion: $BLOBS_AFTER" # verify only unreferenced blobs removed if [ "$BLOBS_AFTER" -ge "$BLOBS_BEFORE" ]; then echo "ERROR: No blobs were cleaned up" exit 1 fi if [ "$BLOBS_AFTER" -eq 0 ]; then echo "ERROR: All blobs deleted (shared layers removed)" exit 1 fi # verify remaining image not missing layers hauler store extract hauler/install.sh:latest - name: Create Hauler Report run: | hauler version >> hauler-report.txt hauler store info --output table >> hauler-report.txt - name: Remove Hauler Store Contents run: | rm -rf store registry fileserver hauler store info - name: Upload Hauler Report uses: actions/upload-artifact@v6 with: name: hauler-report path: hauler-report.txt - name: Verify - hauler logout run: | hauler logout --help hauler logout docker.io hauler logout ghcr.io - name: Remove Hauler Store Credentials run: | rm -rf ~/.docker/config.json