# Publish the container image to GHCR. # # FIRST RUN: a package GHCR creates for the first time is private, even in a # public repository. Set it to public by hand under the package's settings # and check with a logged-out `docker pull`. # # Two architectures, each built on its own native runner rather than under # QEMU (`ubuntu-24.04-arm` is free for public repositories). Each runner # pushes an untagged image by digest and a final job joins the two into one # multi-arch tag. name: Publish image on: release: types: [published] workflow_dispatch: inputs: ref: description: "Tag, branch or SHA to build" required: true default: main tag_latest: description: "Also move :latest to this build" type: boolean default: false env: IMAGE: ghcr.io/coffey-labs/ihasvpn jobs: version: runs-on: ubuntu-latest outputs: version: ${{ steps.v.outputs.version }} steps: - uses: actions/checkout@v7 with: ref: ${{ inputs.ref || github.ref }} fetch-depth: 0 - id: v run: | V="$(git describe --tags --always --dirty)" V="${V#v}" echo "version=$V" >> "$GITHUB_OUTPUT" echo "version $V" build: needs: version runs-on: ${{ matrix.runner }} permissions: contents: read packages: write strategy: fail-fast: false matrix: include: - platform: linux/amd64 runner: ubuntu-latest - platform: linux/arm64 runner: ubuntu-24.04-arm steps: - uses: actions/checkout@v7 with: ref: ${{ inputs.ref || github.ref }} - uses: docker/setup-buildx-action@v4 - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push by digest id: push uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.platform }} build-args: IHASVPN_VERSION=${{ needs.version.outputs.version }} provenance: false sbom: false cache-from: type=gha,scope=${{ matrix.platform }} cache-to: type=gha,mode=max,scope=${{ matrix.platform }} outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true - name: Save the digest run: | mkdir -p /tmp/digests digest="${{ steps.push.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - uses: actions/upload-artifact@v7 with: name: digest-${{ strategy.job-index }} path: /tmp/digests/* retention-days: 1 if-no-files-found: error publish: needs: [version, build] runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/download-artifact@v8 with: path: /tmp/digests pattern: digest-* merge-multiple: true - uses: docker/setup-buildx-action@v4 - uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Create the manifest run: | tags=(-t "${IMAGE}:${{ needs.version.outputs.version }}") if [ "${{ github.event_name }}" = "release" ] && [ "${{ github.event.release.prerelease }}" = "false" ]; then tags+=(-t "${IMAGE}:latest") elif [ "${{ inputs.tag_latest }}" = "true" ]; then tags+=(-t "${IMAGE}:latest") fi refs=() for f in /tmp/digests/*; do refs+=("${IMAGE}@sha256:$(basename "$f")") done docker buildx imagetools create "${tags[@]}" "${refs[@]}" - name: Show what landed run: docker buildx imagetools inspect "${IMAGE}:${{ needs.version.outputs.version }}"