# Build the Docker image and push to GitHub Container Registry (GHCR) and Docker Hub. # # GHCR: uses GITHUB_TOKEN — requires `packages: write` (set below at workflow level). # If push still fails with write_package: repo Settings → Actions → General → Workflow # permissions → "Read and write permissions"; and on the GHCR package → Package settings # → "Manage Actions access" → add this repo with Role: Write (or link the package to the repo). # # Docker Hub: repository secrets DOCKERHUB_TOKEN (optional) and DOCKERHUB_USERNAME if needed. name: Publish Docker image on: push: branches: - main - master tags: - 'v*' workflow_dispatch: # Workflow-level permissions so GITHUB_TOKEN can push to GHCR (not only job-level). permissions: contents: read packages: write jobs: build-and-push: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # secrets.* cannot be used in `if:` — detect token via env in a run step instead. - name: Check Docker Hub credentials id: dockerhub env: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | if [ -n "$DOCKERHUB_TOKEN" ]; then echo "login=true" >> "$GITHUB_OUTPUT" else echo "login=false" >> "$GITHUB_OUTPUT" fi - name: Log in to Docker Hub if: steps.dockerhub.outputs.login == 'true' uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME || github.repository_owner }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Compute image tags id: taglist env: DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }} run: | set -euo pipefail # GHCR image = ghcr.io// (must match GitHub’s expected path). OWNER="$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" REPO="$(echo '${{ github.event.repository.name }}' | tr '[:upper:]' '[:lower:]')" GHCR="ghcr.io/${OWNER}/${REPO}" DH="${OWNER}/${REPO}" SHA_SHORT="$(echo '${{ github.sha }}' | cut -c1-7)" SHA_TAG="sha-${SHA_SHORT}" TAGS="${GHCR}:${SHA_TAG}" DEFAULT_BRANCH="${{ github.event.repository.default_branch }}" if [ "${{ github.ref }}" = "refs/heads/${DEFAULT_BRANCH}" ]; then TAGS="${TAGS}"$'\n'"${GHCR}:latest" fi if [[ "${{ github.ref }}" == refs/tags/v* ]]; then TAGS="${TAGS}"$'\n'"${GHCR}:${{ github.ref_name }}" fi if [ -n "${DOCKERHUB_TOKEN}" ]; then TAGS="${TAGS}"$'\n'"${DH}:${SHA_TAG}" if [ "${{ github.ref }}" = "refs/heads/${DEFAULT_BRANCH}" ]; then TAGS="${TAGS}"$'\n'"${DH}:latest" fi if [[ "${{ github.ref }}" == refs/tags/v* ]]; then TAGS="${TAGS}"$'\n'"${DH}:${{ github.ref_name }}" fi fi { echo 'tags<> "${GITHUB_OUTPUT}" - name: Build and push uses: docker/build-push-action@v6 with: context: . push: true tags: ${{ steps.taglist.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max