diff --git a/.github/workflows/base-image.yml b/.github/workflows/base-image.yml new file mode 100644 index 00000000..06a0d253 --- /dev/null +++ b/.github/workflows/base-image.yml @@ -0,0 +1,58 @@ +name: Publish base image to GHCR + +# Build the heavy esplora-base image (Bitcoin Core, Elements, electrs, libwally +# wasm, etc.) and publish it to GitHub Packages (GHCR). This is expensive, so it +# only runs when the base Dockerfile changes, or when triggered manually. +on: + push: + branches: [master] + paths: + - contrib/Dockerfile.base + - .github/workflows/base-image.yml + workflow_dispatch: + +env: + REGISTRY: ghcr.io + # Publishes to ghcr.io//esplora-base. metadata-action lowercases this. + IMAGE_NAME: ${{ github.repository_owner }}/esplora-base + +jobs: + build-and-push-base: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + 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: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract image metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=latest + type=sha,format=long + + - name: Build and push base image + uses: docker/build-push-action@v6 + with: + context: . + file: contrib/Dockerfile.base + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml new file mode 100644 index 00000000..517844b3 --- /dev/null +++ b/.github/workflows/release-docker.yml @@ -0,0 +1,74 @@ +name: Publish Docker image to GHCR + +# Build the esplora Docker image and publish it to GitHub Packages (GHCR) +# whenever a new GitHub Release is published. Can also be run manually +# (workflow_dispatch) for testing, with an optional override tag. +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Extra image tag to push (e.g. test-build). Leave blank to only push :latest on manual runs." + required: false + default: "" + +env: + REGISTRY: ghcr.io + # Publishes to ghcr.io//. metadata-action lowercases this for us. + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Compute lowercase owner + id: owner + run: echo "lc=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT" + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract image metadata (tags, labels) + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # Release runs derive semver tags from the release tag; manual runs + # tag :latest plus an optional caller-supplied tag. + tags: | + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=raw,value=latest + type=raw,value=${{ inputs.tag }},enable=${{ github.event_name == 'workflow_dispatch' && inputs.tag != '' }} + + - name: Build and push image + uses: docker/build-push-action@v6 + with: + context: . + file: contrib/Dockerfile + platforms: linux/amd64 + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + # Build on top of the GHCR base image, and stamp the built commit into + # the footer HTML (mirroring the GitLab build). + build-args: | + BASE_IMAGE=${{ env.REGISTRY }}/${{ steps.owner.outputs.lc }}/esplora-base:latest + FOOT_HTML= + # Speed up repeat builds via the GitHub Actions cache backend. + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/README.md b/README.md index 8cb0aef3..b86e952b 100644 --- a/README.md +++ b/README.md @@ -152,6 +152,31 @@ docker build -t esplora -f contrib/Dockerfile . Alternatively, you may use the pre-built [`blockstream/esplora` image](https://hub.docker.com/r/blockstream/esplora) from Docker Hub. +## Docker images on GitHub Packages (GHCR) + +In addition to Docker Hub, images are published to the GitHub Container Registry +via GitHub Actions: + +- `ghcr.io//esplora` — the explorer image, published on every published + GitHub Release (`.github/workflows/release-docker.yml`). Tagged with the + release version (e.g. `1.2.3`, `1.2`) and `latest`. +- `ghcr.io//esplora-base` — the base image, rebuilt when + `contrib/Dockerfile.base` changes or on manual dispatch + (`.github/workflows/base-image.yml`). Tagged `latest`. + +The release image builds `FROM` the GHCR base image, so on a fresh setup run the +**Publish base image to GHCR** workflow once (Actions → Run workflow) before +cutting your first release. Both workflows authenticate with the built-in +`GITHUB_TOKEN`; ensure Actions has package write access (Settings → Actions → +Workflow permissions) and set the package visibility to public if you want +anonymous pulls. + +Pull the latest release image with: + +```bash +docker pull ghcr.io//esplora:latest +``` + ## How to run the explorer for Bitcoin mainnet ```bash diff --git a/contrib/Dockerfile b/contrib/Dockerfile index 87eb3de3..0826b236 100644 --- a/contrib/Dockerfile +++ b/contrib/Dockerfile @@ -1,4 +1,7 @@ -FROM blockstream/esplora-base:latest AS build +# Base image is overridable so CI can point at a registry-specific build +# (e.g. ghcr.io//esplora-base) while keeping the Docker Hub default. +ARG BASE_IMAGE=blockstream/esplora-base:latest +FROM ${BASE_IMAGE} AS build FROM debian:bookworm-slim