diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index faf51b7..af95e4a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -36,6 +36,14 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + with: + # PR builds don't log in or push, so the base image built in this job + # must stay in the local Docker image store for the web/sandbox stages + # to consume via FROM without pulling from (or pushing to) ghcr.io. + # The classic "docker" driver shares images with the host daemon + # automatically; the "docker-container" driver (needed for registry + # cache export/import) does not. + driver: ${{ github.event_name == 'pull_request' && 'docker' || 'docker-container' }} - name: Log in to GitHub Container Registry if: github.event_name != 'pull_request' @@ -55,6 +63,24 @@ jobs: type=sha,prefix=,format=short type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} + - name: Set build cache options + id: cache + run: | + # Registry cache import/export requires authentication. PR builds + # don't log in to ghcr.io (see step above) and packages published + # via GITHUB_TOKEN are private by default (see + # https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry), + # so an anonymous cache pull/push would fail with 403. Leaving these + # empty simply disables cache import/export for PR builds; it does + # not require any manual package visibility change on GitHub. + if [ "${{ github.event_name }}" = "pull_request" ]; then + echo "from=" >> "$GITHUB_OUTPUT" + echo "to=" >> "$GITHUB_OUTPUT" + else + echo "from=type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base" >> "$GITHUB_OUTPUT" + echo "to=type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base,mode=max" >> "$GITHUB_OUTPUT" + fi + - name: Build and push base image uses: docker/build-push-action@v6 with: @@ -62,8 +88,8 @@ jobs: file: Dockerfile.base push: ${{ github.event_name != 'pull_request' }} tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/base:${{ steps.meta.outputs.version }} - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base,mode=max + cache-from: ${{ steps.cache.outputs.from }} + cache-to: ${{ steps.cache.outputs.to }} - name: Build and push web image uses: docker/build-push-action@v6 @@ -75,8 +101,8 @@ jobs: push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base,mode=max + cache-from: ${{ steps.cache.outputs.from }} + cache-to: ${{ steps.cache.outputs.to }} - name: Build and push sandbox image uses: docker/build-push-action@v6 @@ -90,5 +116,5 @@ jobs: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/sandbox:${{ steps.meta.outputs.version }} ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/sandbox:latest labels: ${{ steps.meta.outputs.labels }} - cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base - cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/buildcache:base,mode=max + cache-from: ${{ steps.cache.outputs.from }} + cache-to: ${{ steps.cache.outputs.to }} diff --git a/Dockerfile.base b/Dockerfile.base index 3ef95bd..9861e8b 100644 --- a/Dockerfile.base +++ b/Dockerfile.base @@ -9,16 +9,24 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ ARG CS_TAG=ubuntu-schedule-release ARG CS_ARCH=x86_64 -# Download and extract CovScript SDK + cspkg repo +# Download and extract CovScript SDK + cspkg repo. +# The upstream release archives are not guaranteed to have a stable internal +# layout across csbuild versions: some releases place the SDK files directly +# at the archive root, while others nest them under a top-level "build/" (or +# "cspkg-repo/") directory. The conditional moves below normalize either +# layout into the expected /opt/covscript and /opt/covscript/cspkg-repo paths. RUN curl -fsSL -o /tmp/covscript.7z \ "https://github.com/covscript/csbuild/releases/download/${CS_TAG}/covscript-ubuntu-${CS_ARCH}.7z" \ && curl -fsSL -o /tmp/cspkg.7z \ "https://github.com/covscript/csbuild/releases/download/${CS_TAG}/cspkg-ubuntu-${CS_ARCH}.7z" \ - && 7z x /tmp/covscript.7z -o/opt/covscript -y \ + && mkdir -p /opt/covscript-tmp \ + && 7z x /tmp/covscript.7z -o/opt/covscript-tmp -y \ + && mkdir -p /opt/covscript \ + && sh -c 'if [ -d /opt/covscript-tmp/build ]; then mv /opt/covscript-tmp/build/* /opt/covscript/; else mv /opt/covscript-tmp/* /opt/covscript/; fi' \ && mkdir -p /opt/covscript/cspkg-repo \ && 7z x /tmp/cspkg.7z -o/opt/pkg-tmp -y \ && sh -c 'if [ -d /opt/pkg-tmp/cspkg-repo ]; then mv /opt/pkg-tmp/cspkg-repo/* /opt/covscript/cspkg-repo/; else mv /opt/pkg-tmp/* /opt/covscript/cspkg-repo/; fi' \ - && rm -rf /tmp/covscript.7z /tmp/cspkg.7z /opt/pkg-tmp + && rm -rf /tmp/covscript.7z /tmp/cspkg.7z /opt/covscript-tmp /opt/pkg-tmp ENV COVSCRIPT_HOME=/opt/covscript ENV PATH=/opt/covscript/bin:$PATH diff --git a/Dockerfile.sandbox b/Dockerfile.sandbox index 4f71026..658b5c4 100644 --- a/Dockerfile.sandbox +++ b/Dockerfile.sandbox @@ -1,6 +1,6 @@ ARG BASE_IMAGE=covscript-base:latest FROM ${BASE_IMAGE} -RUN useradd -m -s /bin/bash sandbox && mkdir -p /app/data && chown sandbox:sandbox /app/data +RUN useradd -m -s /bin/bash sandbox && mkdir -p /app/data && chown -R sandbox:sandbox /app USER sandbox WORKDIR /app COPY --chown=sandbox:sandbox configs/sandbox.json config.json diff --git a/Dockerfile.web b/Dockerfile.web index dc8b4f1..d5bb36a 100644 --- a/Dockerfile.web +++ b/Dockerfile.web @@ -1,4 +1,5 @@ # Stage 1: Frontend build +ARG BASE_IMAGE=covscript-base:latest FROM node:20-alpine AS frontend WORKDIR /app COPY package.json package-lock.json ./ @@ -9,7 +10,6 @@ COPY index.html vite.config.js ./ RUN npm run build # Stage 2: Web server -ARG BASE_IMAGE=covscript-base:latest FROM ${BASE_IMAGE} WORKDIR /app COPY configs/web.json config.json