diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..76fc16d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,57 @@ +# Dependencies +node_modules +npm-debug.log +yarn-error.log + +# Build outputs +.next +out +build +dist + +# Environment files +.env*.local +.env + +# Git +.git +.github +.gitignore + +# IDE and development files +.vscode +.idea +*.swp +*.swo +*~ +.DS_Store + +# Tests and coverage +tests +coverage +.coverage +*.test.ts +*.test.tsx +*.spec.ts + +# Documentation +docs +*.md +README.md + +# Other configuration files that aren't needed in production +.eslint* +tsconfig.json +postcss.config.mjs +tailwind.config.js +components.json +playwright.config.ts +vitest.config.ts +vitest.setup.ts +stryker.config.json +lighthouserc.json +sentry*.config.ts +sentry.properties + +# Scripts +scripts \ No newline at end of file diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index a00c880..ec95118 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -6,23 +6,23 @@ # branches: # - main -# jobs: -# e2e: -# runs-on: ubuntu-latest -# steps: -# - uses: actions/checkout@v4 -# - uses: actions/setup-node@v4 -# with: -# node-version: 20 -# cache: npm -# - run: npm ci -# - name: Install Sentry CLI -# run: npm install @sentry/cli -# - name: Upload source maps to Sentry -# run: npx sentry-cli sourcemaps inject --org $SENTRY_ORG --project $SENTRY_PROJECT --release $GITHUB_SHA --url-prefix /_next/static/chunks -# env: -# SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} -# SENTRY_ORG: ${{ secrets.SENTRY_ORG }} -# SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} -# - run: npx playwright install --with-deps chromium -# - run: npm run test:e2e \ No newline at end of file +jobs: + e2e: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + - run: npm ci + - name: Install Sentry CLI + run: npm install @sentry/cli + - name: Upload source maps to Sentry + run: npx sentry-cli sourcemaps inject --org $SENTRY_ORG --project $SENTRY_PROJECT --release $GITHUB_SHA --url-prefix /_next/static/chunks + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} + - run: npx playwright install --with-deps chromium + - run: npm run test:e2e diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..54b5a7b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +name: Release Docker Image + +on: + push: + tags: + - 'v*' # Trigger on version tags like v1.0.0 + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write # Required to push to GitHub Container Registry + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - 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: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: + - type=semver,pattern={{version}} # Use the tag version + - type=semver,pattern={{major}}.{{minor}} # Also tag major.minor + - type=semver,pattern={{major}} # Also tag major + - type=sha,prefix= # Also tag with commit SHA + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1613aad --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Stage 1: Dependencies - Install all dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +# Copy package files +COPY package*.json ./ +RUN npm ci + +# Stage 2: Builder - Build the application +FROM node:20-alpine AS builder +WORKDIR /app + +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Stage 3: Runner - Production server +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production + +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy necessary files from builder +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Switch to non-root user +USER nextjs + +# Expose the port Next.js will run on +EXPOSE 3000 + +ENV PORT 3000 + +# Start the Next.js server +CMD ["node", "server.js"] \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index fd5bd2e..229ad9c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,6 +7,7 @@ import { withSentryConfig } from "@sentry/nextjs"; * @type {import('next').NextConfig} **/ const nextConfig = { + output: 'standalone', async headers() { return [ {