diff --git a/.github/workflows/publishImage.yml b/.github/workflows/publishImage.yml new file mode 100644 index 0000000..bf1e731 --- /dev/null +++ b/.github/workflows/publishImage.yml @@ -0,0 +1,61 @@ +name: Publish Docker image + +on: + pull_request: + branches: [ main ] + release: + types: [ published ] + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }}/distcode + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=raw,value=latest,enable={{is_default_branch}} + type=sha + + - uses: docker/build-push-action@v5 + with: + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: linux/amd64,linux/arm64 + cache-from: type=gha + cache-to: type=gha,mode=max + build-args: | + DATABASE_URL=${{ secrets.DATABASE_URL }} + NEXTAUTH_URL=${{ secrets.NEXTAUTH_URL }} + NEXTAUTH_SECRET=${{ secrets.NEXTAUTH_SECRET }} + GLITHUB_CLIENT_ID=${{ secrets.GLITHUB_CLIENT_ID }} + GLITHUB_CLIENT_SECRET=${{ secrets.GLITHUB_CLIENT_SECRET }} + GOOGLE_CLIENT_ID=${{ secrets.GOOGLE_CLIENT_ID }} + GOOGLE_CLIENT_SECRET=${{ secrets.GOOGLE_CLIENT_SECRET }} + POSTGRES_DB=${{ secrets.POSTGRES_DB }} + POSTGRES_PASSWORD=${{ secrets.POSTGRES_PASSWORD }} + POSTGRES_USER=${{ secrets.POSTGRES_USER }} + RABBITMQ_URL=${{ secrets.RABBITMQ_URL }} + RABBITMQ_EXCHANGE=${{ secrets.RABBITMQ_EXCHANGE }} + RABBITMQ_EXCHANGETYPE=${{ secrets.RABBITMQ_EXCHANGETYPE }} + RABBITMQ_QUEUE=${{ secrets.RABBITMQ_QUEUE }} + RABBITMQ_ROUTING_KEY=${{ secrets.RABBITMQ_ROUTING_KEY }} + diff --git a/Dockerfile b/Dockerfile index 8038f89..02b319a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,28 +1,120 @@ -# 1. Install dependencies only when needed -FROM node:22-alpine AS deps +# Use the official Node.js 22 Slim image as a base for better compatibility +FROM node:22-slim AS base + +# Stage 1: Install dependencies +FROM base AS deps WORKDIR /app -RUN npm install -g pnpm -COPY package.json pnpm-lock.yaml* ./ -RUN pnpm install --frozen-lockfile +# Copy package manifests and install all dependencies +COPY package.json pnpm-lock.yaml* .npmrc* ./ +RUN corepack enable pnpm && pnpm i --frozen-lockfile -# 2. Rebuild the source code only when needed -FROM node:22-alpine AS builder +# Stage 2: Build the application +FROM base AS builder WORKDIR /app -RUN npm install -g pnpm + +# Define build-time arguments for environment variables +ARG DATABASE_URL +ARG NEXTAUTH_URL +ARG NEXTAUTH_SECRET +ARG GLITHUB_CLIENT_ID +ARG GLITHUB_CLIENT_SECRET +ARG GOOGLE_CLIENT_ID +ARG GOOGLE_CLIENT_SECRET +ARG POSTGRES_DB +ARG POSTGRES_PASSWORD +ARG POSTGRES_USER +ARG RABBITMQ_URL +ARG RABBITMQ_EXCHANGE +ARG RABBITMQ_EXCHANGETYPE +ARG RABBITMQ_QUEUE +ARG RABBITMQ_ROUTING_KEY + +# Set environment variables for the build process +ENV DATABASE_URL=$DATABASE_URL +ENV NEXTAUTH_URL=$NEXTAUTH_URL +ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET +ENV GLITHUB_CLIENT_ID=$GLITHUB_CLIENT_ID +ENV GLITHUB_CLIENT_SECRET=$GLITHUB_CLIENT_SECRET +ENV GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID +ENV GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET +ENV POSTGRES_DB=$POSTGRES_DB +ENV POSTGRES_PASSWORD=$POSTGRES_PASSWORD +ENV POSTGRES_USER=$POSTGRES_USER +ENV RABBITMQ_URL=$RABBITMQ_URL +ENV RABBITMQ_EXCHANGE=$RABBITMQ_EXCHANGE +ENV RABBITMQ_EXCHANGETYPE=$RABBITMQ_EXCHANGETYPE +ENV RABBITMQ_QUEUE=$RABBITMQ_QUEUE +ENV RABBITMQ_ROUTING_KEY=$RABBITMQ_ROUTING_KEY + +# Copy dependencies and source code COPY --from=deps /app/node_modules ./node_modules COPY . . -RUN pnpm build -# 3. Production image, copy all the files and run nextjs -FROM node:22-alpine AS runner +# Build the Next.js application using pnpm +RUN corepack enable pnpm && pnpm run build + +# Stage 3: Production image +FROM base AS runner WORKDIR /app -ENV NODE_ENV production -RUN npm install -g pnpm -COPY --from=builder /app/public ./public -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/node_modules ./node_modules -COPY --from=builder /app/package.json ./package.json + +ENV NODE_ENV=production + +# Create a non-root user for security (Debian-style) +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# We only need production dependencies to run the app +COPY package.json pnpm-lock.yaml* .npmrc* ./ +RUN corepack enable pnpm && pnpm i --prod --frozen-lockfile + + +# Define build-time arguments for environment variables +ARG DATABASE_URL +ARG NEXTAUTH_URL +ARG NEXTAUTH_SECRET +ARG GLITHUB_CLIENT_ID +ARG GLITHUB_CLIENT_SECRET +ARG GOOGLE_CLIENT_ID +ARG GOOGLE_CLIENT_SECRET +ARG POSTGRES_DB +ARG POSTGRES_PASSWORD +ARG POSTGRES_USER +ARG RABBITMQ_URL +ARG RABBITMQ_EXCHANGE +ARG RABBITMQ_EXCHANGETYPE +ARG RABBITMQ_QUEUE +ARG RABBITMQ_ROUTING_KEY + +# Set environment variables for the build process +ENV DATABASE_URL=$DATABASE_URL +ENV NEXTAUTH_URL=$NEXTAUTH_URL +ENV NEXTAUTH_SECRET=$NEXTAUTH_SECRET +ENV GLITHUB_CLIENT_ID=$GLITHUB_CLIENT_ID +ENV GLITHUB_CLIENT_SECRET=$GLITHUB_CLIENT_SECRET +ENV GOOGLE_CLIENT_ID=$GOOGLE_CLIENT_ID +ENV GOOGLE_CLIENT_SECRET=$GOOGLE_CLIENT_SECRET +ENV POSTGRES_DB=$POSTGRES_DB +ENV POSTGRES_PASSWORD=$POSTGRES_PASSWORD +ENV POSTGRES_USER=$POSTGRES_USER +ENV RABBITMQ_URL=$RABBITMQ_URL +ENV RABBITMQ_EXCHANGE=$RABBITMQ_EXCHANGE +ENV RABBITMQ_EXCHANGETYPE=$RABBITMQ_EXCHANGETYPE +ENV RABBITMQ_QUEUE=$RABBITMQ_QUEUE +ENV RABBITMQ_ROUTING_KEY=$RABBITMQ_ROUTING_KEY + +# Copy the built application from the builder stage, setting ownership +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next +COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/package.json . + +# Switch to the non-root user +USER nextjs EXPOSE 3000 + +ENV PORT=3000 + +# Start the Next.js server using pnpm start (which runs `next start`) CMD ["pnpm", "start"] \ No newline at end of file diff --git a/next.config.ts b/next.config.ts index 01e9ffc..03ce852 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + output: "standalone", turbopack: { rules: { "*.svg": {