From a6d3fb1efd838a6a68e37dd64ace2ffa8ee49149 Mon Sep 17 00:00:00 2001 From: Evan Date: Thu, 25 Dec 2025 16:00:28 -0800 Subject: [PATCH] switch from dockerhub to ghcr, improve docker caching (#2695) ## Description: Switch to GHCR for faster pulls/pushes and increased rate limits Use cache-builder driver for better caching, so npm ci is cached if dependencies don't change. ## Please complete the following: - [x] I have added screenshots for all UI updates - [x] I process any text displayed to the user through translateText() and I've added it to the en.json file - [x] I have added relevant tests to the test directory - [x] I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced ## Please put your Discord username so you can be contacted if a bug or regression is found: evan --- .github/workflows/deploy.yml | 11 +++++---- .github/workflows/release.yml | 24 +++++++++--------- Dockerfile | 6 +++-- build.sh | 46 ++++++++++++++++++++++++----------- deploy.sh | 14 +++++------ example.env | 6 ++--- update.sh | 6 ++--- 7 files changed, 67 insertions(+), 46 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ddd5fdc78..f18326265 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -84,11 +84,12 @@ jobs: token: ${{ steps.generate-token.outputs.token }} environment-url: https://${{ env.FQDN }} environment: ${{ inputs.target_domain == 'openfront.io' && 'prod' || 'staging' }} - - name: 🔗 Log in to Docker Hub + - name: 🔗 Log in to GHCR uses: docker/login-action@v3 with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + registry: ghcr.io + username: ${{ vars.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} - name: 🔑 Create SSH private key env: SERVER_HOST_MASTERS: ${{ secrets.SERVER_HOST_MASTERS }} @@ -108,8 +109,8 @@ jobs: ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }} CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - DOCKER_REPO: ${{ vars.DOCKERHUB_REPO }} - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: ${{ vars.GHCR_REPO }} + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} ENV: ${{ inputs.target_domain == 'openfront.io' && 'prod' || 'staging' }} HOST: ${{ github.event_name == 'workflow_dispatch' && inputs.target_host || 'staging' }} OTEL_ENDPOINT: ${{ secrets.OTEL_ENDPOINT }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a65e7b8f..22bbb6ae7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,12 +19,12 @@ jobs: - name: 🔗 Log in to Docker Hub uses: docker/login-action@v3 with: - username: ${{ vars.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} + username: ${{ vars.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} - id: build env: - DOCKER_REPO: openfront-prod - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: openfront-prod + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} RELEASE_BODY: ${{ github.event.release.body }} RELEASE_NAME: ${{ github.event.release.name }} RELEASE_TAG_NAME: ${{ github.event.release.tag_name }} @@ -66,8 +66,8 @@ jobs: ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }} CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - DOCKER_REPO: openfront-prod - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: openfront-prod + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }} OTEL_ENDPOINT: ${{ secrets.OTEL_ENDPOINT }} @@ -124,8 +124,8 @@ jobs: ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }} CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - DOCKER_REPO: ${{ vars.DOCKERHUB_REPO }} - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: ${{ vars.GHCR_REPO }} + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }} OTEL_ENDPOINT: ${{ secrets.OTEL_ENDPOINT }} @@ -182,8 +182,8 @@ jobs: ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }} CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - DOCKER_REPO: ${{ vars.DOCKERHUB_REPO }} - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: ${{ vars.GHCR_REPO }} + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }} OTEL_ENDPOINT: ${{ secrets.OTEL_ENDPOINT }} @@ -240,8 +240,8 @@ jobs: ADMIN_TOKEN: ${{ secrets.ADMIN_TOKEN }} CF_ACCOUNT_ID: ${{ secrets.CF_ACCOUNT_ID }} CF_API_TOKEN: ${{ secrets.CF_API_TOKEN }} - DOCKER_REPO: ${{ vars.DOCKERHUB_REPO }} - DOCKER_USERNAME: ${{ vars.DOCKERHUB_USERNAME }} + GHCR_REPO: ${{ vars.GHCR_REPO }} + GHCR_USERNAME: ${{ vars.GHCR_USERNAME }} DOMAIN: ${{ vars.DOMAIN }} IMAGE_ID: ${{ needs.build.outputs.IMAGE_ID }} OTEL_ENDPOINT: ${{ secrets.OTEL_ENDPOINT }} diff --git a/Dockerfile b/Dockerfile index ac1d7b17f..8e8f475d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,7 +7,8 @@ FROM base AS build ENV HUSKY=0 # Copy package files first for better caching COPY package*.json ./ -RUN npm ci +RUN --mount=type=cache,target=/root/.npm \ + npm ci # Copy only what's needed for build COPY tsconfig.json ./ @@ -29,7 +30,8 @@ FROM base AS prod-deps ENV HUSKY=0 ENV NPM_CONFIG_IGNORE_SCRIPTS=1 COPY package*.json ./ -RUN npm ci --omit=dev +RUN --mount=type=cache,target=/root/.npm \ + npm ci --omit=dev # Final production image FROM base diff --git a/build.sh b/build.sh index 5fbdf9ac7..d3c3dce78 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/bash -# build.sh - Build and upload Docker image to Docker Hub +# build.sh - Build and upload image to GitHub Container Registry # This script: -# 1. Builds and uploads the Docker image to Docker Hub with appropriate tag +# 1. Builds and uploads the image to GitHub Container Registry with appropriate tag # 2. Optionally saves container metadata to a file (if METADATA_FILE is provided as 3rd argument) set -e # Exit immediately if a command exits with a non-zero status @@ -57,23 +57,22 @@ if [ -f .env.$DEPLOY_ENV ]; then fi # Check required environment variables for build -if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_REPO" ]; then - echo "Error: DOCKER_USERNAME or DOCKER_REPO not defined in .env file or environment" +if [ -z "$GHCR_USERNAME" ] || [ -z "$GHCR_REPO" ]; then + echo "Error: GHCR_USERNAME or GHCR_REPO not defined in .env file or environment" exit 1 fi -DOCKER_IMAGE="${DOCKER_USERNAME}/${DOCKER_REPO}:${VERSION_TAG}" +GHCR_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:${VERSION_TAG}" -# If ADDITIONAL_VERSION_TAG is provided ADDITIONAL_DOCKER_IMAGE will be set +# If ADDITIONAL_VERSION_TAG is provided ADDITIONAL_GHCR_IMAGE will be set # example usage: adding latest tag if [ -n "$ADDITIONAL_VERSION_TAG" ]; then - ADDITIONAL_DOCKER_IMAGE="${DOCKER_USERNAME}/${DOCKER_REPO}:${ADDITIONAL_VERSION_TAG}" + ADDITIONAL_GHCR_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:${ADDITIONAL_VERSION_TAG}" fi -# Build and upload Docker image to Docker Hub echo "Environment: ${DEPLOY_ENV}" echo "Using version tag: $VERSION_TAG" -echo "Docker repository: $DOCKER_REPO" +echo "Docker repository: $GHCR_REPO" echo "Metadata file: $METADATA_FILE" # Get Git commit for build info @@ -87,12 +86,31 @@ if [ -n "$VERSION_TXT" ]; then echo "$VERSION_TXT" > resources/version.txt fi +# Set up cache image reference +CACHE_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:latest" +BUILDCACHE_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:buildcache" + +echo "Building with buildx and registry cache..." + +# Create buildx builder with docker-container driver if it doesn't exist +if ! docker buildx inspect cache-builder > /dev/null 2>&1; then + echo "Creating buildx builder..." + docker buildx create --name cache-builder --driver docker-container --use +else + echo "Using existing buildx builder..." + docker buildx use cache-builder +fi + +# Use buildx with registry cache for best performance +# --push will push all tags automatically docker buildx build \ --platform linux/amd64 \ --build-arg GIT_COMMIT=$GIT_COMMIT \ - --metadata-file $METADATA_FILE \ - -t $DOCKER_IMAGE \ - ${ADDITIONAL_DOCKER_IMAGE:+-t "$ADDITIONAL_DOCKER_IMAGE"} \ + --cache-from type=registry,ref=$BUILDCACHE_IMAGE \ + --cache-to type=registry,ref=$BUILDCACHE_IMAGE,mode=max \ + --tag $GHCR_IMAGE \ + --tag $CACHE_IMAGE \ + ${ADDITIONAL_GHCR_IMAGE:+--tag "$ADDITIONAL_GHCR_IMAGE"} \ --push \ . @@ -102,6 +120,6 @@ if [ $? -ne 0 ]; then fi echo "✅ Docker image built and pushed successfully." -echo "Image: $DOCKER_IMAGE" +echo "Image: $GHCR_IMAGE" -print_header "BUILD COMPLETED SUCCESSFULLY ${DOCKER_IMAGE}" +print_header "BUILD COMPLETED SUCCESSFULLY ${GHCR_IMAGE}" diff --git a/deploy.sh b/deploy.sh index cc5b0ac35..1a8aa21a8 100755 --- a/deploy.sh +++ b/deploy.sh @@ -76,15 +76,15 @@ if [ -f .env.$ENV ]; then fi # Check required environment variables for deployment -if [ -z "$DOCKER_USERNAME" ] || [ -z "$DOCKER_REPO" ]; then - echo "Error: DOCKER_USERNAME or DOCKER_REPO not defined in .env file or environment" +if [ -z "$GHCR_USERNAME" ] || [ -z "$GHCR_REPO" ]; then + echo "Error: GHCR_USERNAME or GHCR_REPO not defined in .env file or environment" exit 1 fi if [[ "$VERSION_TAG" == sha256:* ]]; then - DOCKER_IMAGE="${DOCKER_USERNAME}/${DOCKER_REPO}@${VERSION_TAG}" + GHCR_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}@${VERSION_TAG}" else - DOCKER_IMAGE="${DOCKER_USERNAME}/${DOCKER_REPO}:${VERSION_TAG}" + GHCR_IMAGE="${GHCR_USERNAME}/${GHCR_REPO}:${VERSION_TAG}" fi if [ "$HOST" == "staging" ]; then @@ -139,7 +139,7 @@ print_header "DEPLOYMENT INFORMATION" echo "Environment: ${ENV}" echo "Host: ${HOST}" echo "Subdomain: ${SUBDOMAIN}" -echo "Docker Image: $DOCKER_IMAGE" +echo "Image: $GHCR_IMAGE" echo "Target Server: $SERVER_HOST" # Copy update script to Hetzner server @@ -168,8 +168,8 @@ cat > $ENV_FILE << 'EOL' GAME_ENV=$ENV ENV=$ENV HOST=$HOST -DOCKER_IMAGE=$DOCKER_IMAGE -DOCKER_TOKEN=$DOCKER_TOKEN +GHCR_IMAGE=$GHCR_IMAGE +GHCR_TOKEN=$GHCR_TOKEN ADMIN_TOKEN=$ADMIN_TOKEN CF_ACCOUNT_ID=$CF_ACCOUNT_ID R2_ACCESS_KEY=$R2_ACCESS_KEY diff --git a/example.env b/example.env index 4135026cf..be9ca624f 100644 --- a/example.env +++ b/example.env @@ -2,9 +2,9 @@ SSH_KEY=~/.ssh/your-ssh-key # Docker Configuration -DOCKER_USERNAME=username -DOCKER_REPO=your-repo-name -DOCKER_TOKEN=your_docker_token_here +GHCR_USERNAME=username +GHCR_REPO=your-repo-name +GHCR_TOKEN=your_docker_token_here # Admin credentials ADMIN_TOKEN=your_admin_token_here diff --git a/update.sh b/update.sh index 5bfa32dc3..cfd6cfe9e 100755 --- a/update.sh +++ b/update.sh @@ -28,8 +28,8 @@ echo "======================================================" # Container and image configuration CONTAINER_NAME="openfront-${ENV}-${SUBDOMAIN}" -echo "Pulling ${DOCKER_IMAGE} from Docker Hub..." -docker pull "${DOCKER_IMAGE}" +echo "Pulling ${GHCR_IMAGE} from GitHub Container Registry..." +docker pull "${GHCR_IMAGE}" echo "Checking for existing container..." # Check for running container @@ -67,7 +67,7 @@ docker run -d \ --env-file "$ENV_FILE" \ --name "${CONTAINER_NAME}" \ -v "cloudflared-${CONTAINER_NAME}:/etc/cloudflared" \ - "${DOCKER_IMAGE}" + "${GHCR_IMAGE}" if [ $? -eq 0 ]; then echo "Update complete! New ${CONTAINER_NAME} container is running."