diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c7d36f33b..f8a3e67db 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -1,10 +1,5 @@ name: Docker -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - on: schedule: - cron: '20 0 * * *' @@ -13,7 +8,6 @@ on: - main - feature/** - dev - # Publish semver tags as releases. tags: [ 'v*.*.*' ] paths-ignore: - 'docs/**' @@ -26,64 +20,78 @@ on: merge_group: env: - # github.repository as / IMAGE_NAME: ${{ github.repository }} - jobs: pre-commit: name: Linting Checks runs-on: ubuntu-22.04 steps: - - - name: Checkout repository + - name: Checkout repository uses: actions/checkout@v4 - - - name: Install python + - name: Install python uses: actions/setup-python@v5 with: python-version: 3.x - - - name: Check files + - name: Check files uses: pre-commit/action@v3.0.1 - - - name: Install pnpm + - name: Install pnpm uses: pnpm/action-setup@v4 with: version: 10 run_install: false - - - name: Install Node.js + - name: Install Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: 'pnpm' - - - name: Install dependencies + - name: Install dependencies run: pnpm install - - - name: Lint frontend + - name: Lint frontend run: pnpm run lint build: name: Docker Build & Push - if: github.repository == 'gethomepage/homepage' + if: github.repository == 'gethomepage/homepage' runs-on: self-hosted - needs: - - pre-commit + needs: [ pre-commit ] permissions: contents: read packages: write - # This is used to complete the identity challenge - # with sigstore/fulcio when running outside of PRs. id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 - # Login to Docker Registry - # https://github.com/docker/login-action + - name: Next.js build cache + uses: actions/cache@v4 + with: + path: .next/cache + key: nextjs-${{ runner.os }}-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + nextjs-${{ runner.os }}- + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + run_install: false + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install + + # Build Next.js app + - name: Build Next.js app + run: pnpm run build + + # Docker logins - name: Log into registry ${{ env.REGISTRY }} if: github.event_name != 'pull_request' uses: docker/login-action@v3 @@ -91,6 +99,7 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Login to Docker Hub if: github.event_name != 'pull_request' uses: docker/login-action@v3 @@ -98,17 +107,12 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - # Setup QEMU - # https://github.com/marketplace/actions/docker-setup-buildx#with-qemu - name: Setup QEMU uses: docker/setup-qemu-action@v3.6.0 - # Workaround: https://github.com/docker/build-push-action/issues/461 - name: Setup Docker buildx uses: docker/setup-buildx-action@v3 - # Extract metadata (tags, labels) for Docker - # https://github.com/docker/metadata-action - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 @@ -119,8 +123,6 @@ jobs: flavor: | latest=auto - # Build and push Docker image with Buildx (don't push on PR) - # https://github.com/docker/build-push-action - name: Build and push Docker image id: build-and-push uses: docker/build-push-action@v6 @@ -133,15 +135,10 @@ jobs: BUILDTIME=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }} VERSION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }} REVISION=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.revision'] }} - # https://github.com/docker/setup-qemu-action#about - # platforms: linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6 platforms: linux/amd64,linux/arm64 cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max - # Temp fix - # https://github.com/docker/build-push-action/issues/252 - # https://github.com/moby/buildkit/issues/1896 - name: Move cache run: | rm -rf /tmp/.buildx-cache diff --git a/Dockerfile b/Dockerfile index 7963407c3..a4111ff95 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,25 @@ -# Install dependencies only when needed -FROM docker.io/node:22-alpine AS deps +# Base build stage (for building Next.js if needed) +FROM node:22-alpine AS builder WORKDIR /app -COPY --link package.json pnpm-lock.yaml* ./ - -SHELL ["/bin/ash", "-xeo", "pipefail", "-c"] -RUN apk add --no-cache libc6-compat \ - && apk add --no-cache --virtual .gyp python3 make g++ \ - && npm install -g pnpm - -RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm fetch | grep -v "cross-device link not permitted\|Falling back to copying packages from store" +RUN mkdir config -RUN --mount=type=cache,id=pnpm-store,target=/root/.local/share/pnpm/store pnpm install -r --offline +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && corepack prepare pnpm@latest --activate +RUN pnpm install --frozen-lockfile -# Rebuild the source code only when needed -FROM docker.io/node:22-alpine AS builder -WORKDIR /app - -RUN mkdir config +COPY . . ARG BUILDTIME ARG VERSION ARG REVISION -COPY --link --from=deps /app/node_modules ./node_modules/ -COPY . . - -SHELL ["/bin/ash", "-xeo", "pipefail", "-c"] -RUN npm install -g pnpm \ - && pnpm run telemetry \ - && NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION pnpm run build +# Build only if needed (local use) +RUN pnpm run telemetry \ +&& NEXT_PUBLIC_BUILDTIME=$BUILDTIME NEXT_PUBLIC_VERSION=$VERSION NEXT_PUBLIC_REVISION=$REVISION pnpm run build -# Production image, copy all the files and run next +# Final runtime image FROM docker.io/node:22-alpine AS runner LABEL org.opencontainers.image.title "Homepage" LABEL org.opencontainers.image.description "A self-hosted services landing page, with docker and service integrations." @@ -41,20 +28,16 @@ LABEL org.opencontainers.image.documentation='https://github.com/gethomepage/hom LABEL org.opencontainers.image.source='https://github.com/gethomepage/homepage' LABEL org.opencontainers.image.licenses='Apache-2.0' -ENV NODE_ENV=production - WORKDIR /app +ENV NODE_ENV=production -# Copy files from context (this allows the files to copy before the builder stage is done). -COPY --link --chown=1000:1000 package.json next.config.js ./ -COPY --link --chown=1000:1000 /public ./public/ - -# Copy files from builder -COPY --link --from=builder --chown=1000:1000 /app/.next/standalone ./ -COPY --link --from=builder --chown=1000:1000 /app/.next/static/ ./.next/static/ -COPY --link --chmod=755 docker-entrypoint.sh /usr/local/bin/ +COPY package.json pnpm-lock.yaml ./ +RUN corepack enable && corepack prepare pnpm@latest --activate +RUN pnpm install --frozen-lockfile --prod -RUN apk add --no-cache su-exec +# Copy pre-built assets from builder stage +COPY --from=builder /app/.next .next +COPY --from=builder /app/public public ENV HOSTNAME=0.0.0.0 ENV PORT=3000 @@ -63,5 +46,4 @@ EXPOSE $PORT HEALTHCHECK --interval=10s --timeout=3s --start-period=20s \ CMD wget --no-verbose --tries=1 --spider --no-check-certificate http://127.0.0.1:$PORT/api/healthcheck || exit 1 -ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["node", "server.js"] +CMD ["pnpm", "start"]