Testing docker build on a fork without pushing to registry #72
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Docker Build | |
on: | |
# Allow manual triggering | |
workflow_dispatch: | |
pull_request: | |
types: [labeled, opened, synchronize, reopened] | |
push: | |
# Ref: GHA Filter pattern syntax: https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#filter-pattern-cheat-sheet | |
# Run on pushes to main, release branches, and previous/future major version branches | |
branches: | |
- main | |
- 'v[0-9]+.*' # Matches any release branch, e.g. v6.0.3, v12.1.0 | |
- '[0-9]+.x' # Matches any major version branch, e.g. 5.x, 23.x | |
jobs: | |
build: | |
runs-on: ubuntu-latest-16-cores | |
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: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Calculate cache image name | |
id: cache | |
run: | | |
OWNER_LOWER=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]') | |
echo "image=ghcr.io/${OWNER_LOWER}/ghost-test" >> $GITHUB_OUTPUT | |
- name: Docker meta | |
id: meta | |
uses: docker/metadata-action@v5 | |
with: | |
# List of Docker images to use as base name for tags | |
images: | | |
ghcr.io/${{ github.repository_owner }}/ghost-test | |
# Generate tags based on the following events/attributes | |
tags: | | |
type=ref,event=branch | |
type=ref,event=pr | |
type=sha | |
type=raw,value=latest,enable={{is_default_branch}} | |
# Set labels | |
labels: | | |
org.opencontainers.image.title=Ghost Development | |
org.opencontainers.image.description=Ghost development build | |
org.opencontainers.image.vendor=TryGhost | |
maintainer=TryGhost | |
- name: Determine build strategy | |
id: strategy | |
run: | | |
IS_FORK_PR="false" | |
if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then | |
IS_FORK_PR="true" | |
fi | |
echo "is-fork-pr=$IS_FORK_PR" >> $GITHUB_OUTPUT | |
echo "should-push=${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}" >> $GITHUB_OUTPUT | |
echo "should-load=$IS_FORK_PR" >> $GITHUB_OUTPUT | |
echo "Build strategy determined:" | |
echo "- Is fork PR: $IS_FORK_PR" | |
echo "- Should push: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}" | |
echo "- Should load: $IS_FORK_PR" | |
- name: Display Docker metadata | |
run: | | |
echo "Generated tags:" | |
echo "${{ steps.meta.outputs.tags }}" | |
echo "" | |
echo "Generated labels:" | |
echo "${{ steps.meta.outputs.labels }}" | |
- name: Build and push Docker image | |
uses: docker/build-push-action@v6 | |
id: build | |
with: | |
context: . | |
file: .docker/Dockerfile | |
# Only push on main branch or PRs from the main repo (not forks) | |
push: ${{ steps.strategy.outputs.should-push }} | |
# Only load image on PRs from forks (to allow testing without pushing) | |
load: ${{ steps.strategy.outputs.should-load }} | |
tags: ${{ steps.meta.outputs.tags }} | |
labels: ${{ steps.meta.outputs.labels }} | |
# On PRs: use both main cache and PR-specific cache | |
# On main: only use main cache | |
cache-from: | | |
type=registry,ref=${{ steps.cache.outputs.image }}:cache-main | |
${{ github.event_name == 'pull_request' && format('type=registry,ref={0}:cache-pr-{1}', steps.cache.outputs.image, github.event.pull_request.number) || '' }} | |
# Only export cache if we can push (not on fork PRs) | |
cache-to: ${{ steps.strategy.outputs.should-push == 'true' && format('type=registry,ref={0}:cache-{1},mode=max', steps.cache.outputs.image, github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || 'main') || '' }} | |
- name: Save image as artifact (fork PR) | |
if: steps.strategy.outputs.is-fork-pr == 'true' | |
run: | | |
IMAGE_TAG=$(echo "${{ steps.meta.outputs.tags }}" | head -n1) | |
echo "Saving image: $IMAGE_TAG" | |
docker save "$IMAGE_TAG" | gzip > docker-image.tar.gz | |
echo "Image saved as docker-image.tar.gz" | |
ls -lh docker-image.tar.gz | |
- name: Upload image artifact (fork PR) | |
if: steps.strategy.outputs.is-fork-pr == 'true' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: docker-image | |
path: docker-image.tar.gz | |
retention-days: 1 | |
outputs: | |
image-tags: ${{ steps.meta.outputs.tags }} | |
image-digest: ${{ steps.build.outputs.digest }} | |
is-fork: ${{ steps.strategy.outputs.is-fork-pr }} | |
test: | |
needs: build | |
runs-on: ubuntu-latest-16-cores | |
steps: | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Download image artifact (fork PR) | |
if: needs.build.outputs.is-fork == 'true' | |
uses: actions/download-artifact@v4 | |
with: | |
name: docker-image | |
- name: Load image from artifact (fork PR) | |
if: needs.build.outputs.is-fork == 'true' | |
run: | | |
echo "Loading Docker image from artifact..." | |
gunzip -c docker-image.tar.gz | docker load | |
echo "Available images after load:" | |
docker images | |
- name: Log in to GitHub Container Registry | |
if: needs.build.outputs.is-fork == 'false' | |
uses: docker/login-action@v3 | |
with: | |
registry: ghcr.io | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Extract first image tag | |
id: image | |
run: | | |
TAGS="${{ needs.build.outputs.image-tags }}" | |
FIRST_TAG=$(echo "$TAGS" | head -n1) | |
echo "tag=$FIRST_TAG" >> $GITHUB_OUTPUT | |
echo "Using image tag: $FIRST_TAG" | |
- name: Pull image from registry (main repo/branch) | |
if: needs.build.outputs.is-fork == 'false' | |
run: | | |
echo "Pulling image from registry..." | |
docker pull ${{ steps.image.outputs.tag }} | |
- name: Verify image is available | |
run: | | |
IMAGE_TAG="${{ steps.image.outputs.tag }}" | |
echo "Checking for image: $IMAGE_TAG" | |
if docker inspect "$IMAGE_TAG" > /dev/null 2>&1; then | |
echo "✅ Image $IMAGE_TAG is available" | |
echo "Image details:" | |
docker inspect "$IMAGE_TAG" --format='{{.Id}} {{.Created}}' | |
else | |
echo "❌ Image $IMAGE_TAG is not available" | |
echo "Available images:" | |
docker images | |
# For fork PRs, the image might have been loaded with a different tag | |
if [ "${{ needs.build.outputs.is-fork }}" = "true" ]; then | |
echo "" | |
echo "Searching for any loaded Ghost images..." | |
docker images --format "table {{.Repository}}:{{.Tag}}\t{{.ID}}\t{{.CreatedAt}}" | grep -E "(ghost|Ghost)" || echo "No Ghost images found" | |
fi | |
exit 1 | |
fi | |
- name: Test container | |
run: | | |
echo "Testing container..." | |
# Run a basic test - replace with your actual tests | |
docker run --rm ${{ steps.image.outputs.tag }} node --version | |
# Example: Run your application's health check or tests | |
# docker run --rm -p 2368:2368 ${{ steps.image.outputs.tag }} & | |
# sleep 5 | |
# curl -f http://localhost:2368 || exit 1 | |
- name: Show container logs (on failure) | |
if: failure() | |
run: | | |
echo "Showing recent container logs..." | |
docker ps -a | |
# Show logs from the most recent container if needed | |
CONTAINER_ID=$(docker ps -lq) | |
if [ ! -z "$CONTAINER_ID" ]; then | |
docker logs $CONTAINER_ID | |
fi |