name: Release multi-arch container image on: workflow_dispatch: push: branches: - main - "test/**" schedule: - cron: "0 0 * * *" # Run every day at 00:00 UTC. env: REGISTRY: ghcr.io/${{ github.repository_owner }} REGISTRY_USER: ${{ github.actor }} REGISTRY_PASSWORD: ${{ github.token }} IMAGE_NAME: dangerzone/dangerzone BUILDKIT_IMAGE: "docker.io/moby/buildkit:v19.0@sha256:14aa1b4dd92ea0a4cd03a54d0c6079046ea98cd0c0ae6176bdd7036ba370cbbe" jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install dev. dependencies run: |- sudo apt-get update sudo apt-get install -y git python3-poetry --no-install-recommends poetry install --only package - name: Verify that the Dockerfile matches the commited template and params run: |- cp Dockerfile Dockerfile.orig make Dockerfile diff Dockerfile.orig Dockerfile prepare: runs-on: ubuntu-latest outputs: debian_archive_date: ${{ steps.date.outputs.debian_archive_date }} source_date_epoch: ${{ steps.date.outputs.source_date_epoch }} steps: - name: Get current date id: date run: | DEBIAN_ARCHIVE_DATE=$(date -u +'%Y%m%d') SOURCE_DATE_EPOCH=$(date -u -d ${DEBIAN_ARCHIVE_DATE} +"%s") echo "debian_archive_date=${DEBIAN_ARCHIVE_DATE}" >> $GITHUB_OUTPUT echo "source_date_epoch=${SOURCE_DATE_EPOCH}" >> $GITHUB_OUTPUT build: runs-on: ubuntu-24.04${{ matrix.platform.suffix }} needs: - prepare strategy: fail-fast: false matrix: platform: - suffix: "" name: "linux/amd64" - suffix: "-arm" name: "linux/arm64" steps: - uses: actions/checkout@v4 - name: Prepare run: | platform=${{ matrix.platform.name }} echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: driver-opts: image=${{ env.BUILDKIT_IMAGE }} - name: Build and push by digest id: build uses: docker/build-push-action@v6 with: context: ./dangerzone/ file: Dockerfile build-args: | DEBIAN_ARCHIVE_DATE=${{ needs.prepare.outputs.debian_archive_date }} SOURCE_DATE_EPOCH=${{ needs.prepare.outputs.source_date_epoch }} # Remove potentially incorrect Docker provenance that cannot be # reproduced. provenance: false platforms: ${{ matrix.platform.name }} outputs: type=image,"name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}",rewrite-timestamp=true,push-by-digest=true,name-canonical=true,push=true cache-from: type=gha cache-to: type=gha,mode=max - name: Export digest run: | mkdir -p ${{ runner.temp }}/digests digest="${{ steps.build.outputs.digest }}" touch "${{ runner.temp }}/digests/${digest#sha256:}" echo "Image digest is: ${digest}" - name: Upload digest uses: actions/upload-artifact@v4 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* if-no-files-found: error retention-days: 1 merge: runs-on: ubuntu-latest needs: - prepare - build outputs: digest: ${{ steps.image.outputs.digest }} image: ${{ steps.image.outputs.image }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Compute image tag id: tag run: | DEBIAN_ARCHIVE_DATE=${{ needs.prepare.outputs.debian_archive_date }} TAG=$(git describe --long --first-parent | tail -c +2) echo "tag=${DEBIAN_ARCHIVE_DATE}-${TAG}" >> $GITHUB_OUTPUT - name: Download digests uses: actions/download-artifact@v4 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to GHCR uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 with: driver-opts: image=${{ env.BUILDKIT_IMAGE }} - name: Create manifest list and push working-directory: ${{ runner.temp }}/digests run: | IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }} DIGESTS=$(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *) docker buildx imagetools create -t ${IMAGE} ${DIGESTS} - name: Inspect image id: image run: | # NOTE: Set the image as an output because the `env` context is not # available to the inputs of a reusable workflow call. image_name="${REGISTRY}/${IMAGE_NAME}" echo "image=$image_name" >> "$GITHUB_OUTPUT" docker buildx imagetools inspect ${image_name}:${{ steps.tag.outputs.tag }} digest=$(docker buildx imagetools inspect ${image_name}:${{ steps.tag.outputs.tag }} --format "{{json .Manifest}}" | jq -r '.digest') echo "digest=$digest" >> "$GITHUB_OUTPUT" # This step calls the container workflow to generate provenance and push it to # the container registry. provenance: needs: - merge permissions: actions: read # for detecting the Github Actions environment. id-token: write # for creating OIDC tokens for signing. packages: write # for uploading attestations. uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0 with: digest: ${{ needs.merge.outputs.digest }} image: ${{ needs.merge.outputs.image }} registry-username: ${{ github.actor }} secrets: registry-password: ${{ secrets.GITHUB_TOKEN }} # This step ensures that the image is reproducible check-reproducibility: needs: - prepare - merge runs-on: ubuntu-24.04${{ matrix.platform.suffix }} strategy: fail-fast: false matrix: platform: - suffix: "" name: "linux/amd64" - suffix: "-arm" name: "linux/arm64" steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Reproduce the same container image run: | ./dev_scripts/reproduce-image.py \ --debian-archive-date ${{ needs.build.prepare.debian_archive_date }} \ --source ${{ needs.merge.outputs.image }}@${{ needs.merge.outputs.digest }} \ --platform ${{ matrix.platform.name }}