BookNest's repository from GitHub gets the Dockerfile and .dockerignore of Multi-Stage and BuildKit and a workflow that runs on demand, on version tags, and, without pushing, on pull requests that touch the image:
# Builds BookNest's API image and pushes it to ghcr.io with the workflow's own token.
name: Publish image
on:
workflow_dispatch:
push:
tags: ["v*"]
pull_request:
paths: [Dockerfile, .dockerignore, .github/workflows/publish-image.yml]
permissions:
contents: read
packages: write
jobs:
image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: docker/setup-buildx-action@v4
- uses: docker/login-action@v4
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v6
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=semver,pattern={{version}}
type=raw,value=latest,enable={{is_default_branch}}
- uses: docker/build-push-action@v7
with:
target: runtime
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}metadata-action derives the tags (sha-<commit>, 1.4.0 from a v1.4.0 tag, latest on the default branch) and OCI labels; build-push-action builds the runtime stage. A ruleset requires pull requests for main, so the files arrive on a branch:
git switch -q -c ch4-ghcr
cp ~/v5-ch4/booknest/Dockerfile ~/v5-ch4/booknest/.dockerignore .
git add Dockerfile .dockerignore .github/workflows/publish-image.yml
git commit -q -m "Publish the API image to ghcr.io from GitHub Actions"
git push -q -u origin ch4-ghcr 2>&1 | grep -v '^remote: *$'
gh pr create --title "Publish the API image to ghcr.io" \
--body "Adds the Dockerfile from Chapter 4 and a workflow that pushes to ghcr.io."
sleep 15; gh pr checks --watch --interval 10 | tail -1
gh pr merge --squash --delete-branchremote: Create a pull request for 'ch4-ghcr' on GitHub by visiting: remote: https://github.com/binarybehemoth/booknest/pull/new/ch4-ghcr https://github.com/binarybehemoth/booknest/pull/14 image pass 24s https://github.com/binarybehemoth/booknest/actions/runs/36128715748/... ... Updating 4943462..3bbdf49 Fast-forward .dockerignore | 21 +++++++++++++++++++ .github/workflows/publish-image.yml | 40 +++++++++++++++++++++++++++++++++++++ Dockerfile | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+)
The pull request's image check built without pushing, and the squash merge landed as 3bbdf49. Now publish from main and pull the result anonymously:
gh workflow run publish-image.yml --ref main && sleep 8
RUN=$(gh run list -w publish-image.yml -e workflow_dispatch -L1 \
--json databaseId -q '.[0].databaseId')
gh run watch "$RUN" --exit-status >/dev/null
gh run view "$RUN" --json conclusion -q .conclusion
gh run view "$RUN" --log | grep -oE 'pushing manifest for [^@]*' | sort -u
docker pull -q ghcr.io/binarybehemoth/booknest:latesthttps://github.com/binarybehemoth/booknest/actions/runs/36130127272 success pushing manifest for ghcr.io/binarybehemoth/booknest:latest pushing manifest for ghcr.io/binarybehemoth/booknest:sha-ef86458 ghcr.io/binarybehemoth/booknest:latest
One image went out under two tags, and the pull needed no credentials because the package inherited the repository's public visibility:
