diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml new file mode 100644 index 0000000..7591c77 --- /dev/null +++ b/.github/release-drafter.yml @@ -0,0 +1,51 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +change-template: '* $TITLE (#$NUMBER) by @$AUTHOR' +template: | + $CHANGES + + **Full Changelog**: https://github.com/$OWNER/$REPOSITORY/compare/$PREVIOUS_TAG...v$RESOLVED_VERSION + +sort-direction: ascending + +categories: + - title: '⚠ī¸ BREAKING CHANGES' + label: 'breaking' + - title: 'đŸ’Ģ Features' + label: 'feature' + - title: '🛠ī¸ Bug fixes' + label: 'fix' + - title: '🕹ī¸ Others' + label: 'chore' + +version-resolver: + # Major is not meant to be used at the moment. + # Should be used with label breaking in the future. + major: + labels: + - 'major' + minor: + labels: + - 'breaking' + - 'feature' + - 'chore' + patch: + labels: + - 'fix' + +exclude-labels: + - 'skip-changelog' + +autolabeler: + - label: 'breaking' + title: + - '/!:/i' + - label: 'chore' + title: + - '/^chore/i' + - label: 'fix' + title: + - '/^fix/i' + - label: 'feature' + title: + - '/^feat/i' diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml new file mode 100644 index 0000000..3979902 --- /dev/null +++ b/.github/workflows/docker.yaml @@ -0,0 +1,101 @@ +name: Docker + +on: + push: + branches: + - main + paths: + - 'src/**' + - '*.ts' + - '*.json' + - '*.yaml' + - Dockerfile + - .github/workflows/docker.yaml + pull_request: + branches: + - main + paths: + - 'src/**' + - '*.ts' + - '*.json' + - '*.yaml' + - Dockerfile + - .github/workflows/docker.yaml + release: + types: + - published + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + PLATFORMS: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: "Generate Build ID (main)" + if: github.ref == 'refs/heads/main' && github.event_name == 'push' + run: | + branch=${GITHUB_REF##*/} + sha=${GITHUB_SHA::8} + ts=$(date +%s) + echo "BUILD_ID=${branch}-${ts}-${sha}" >> $GITHUB_ENV + + - name: "Generate Build ID (PR)" + if: github.event_name == 'pull_request' + run: | + echo "BUILD_ID=pr-${{ github.event.number }}-$GITHUB_RUN_ID" >> $GITHUB_ENV + + - name: "Generate Build ID (Release)" + if: github.event_name == 'release' + run: | + echo "BUILD_ID=${GITHUB_REF##*/}" >> $GITHUB_ENV + + - name: 'Generate App Version' + run: echo "VERSION=$(make version)" >> $GITHUB_ENV + + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v4 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=pr + type=ref,event=branch + type=raw,value=${{ env.BUILD_ID }} + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} + + - name: Build and push Docker image + uses: docker/build-push-action@v4 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + platforms: ${{ env.PLATFORMS }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/pr-title.yaml b/.github/workflows/pr-title.yaml new file mode 100644 index 0000000..f6064af --- /dev/null +++ b/.github/workflows/pr-title.yaml @@ -0,0 +1,29 @@ +name: 'Validate PR title' + +on: + pull_request: + branches: [ main ] + types: [ opened, reopened, synchronize ] + +permissions: + pull-requests: read + statuses: write + +jobs: + main: + name: Validate PR title + runs-on: ubuntu-latest + steps: + # Please look up the latest version from + # https://github.com/amannn/action-semantic-pull-request/releases + - uses: amannn/action-semantic-pull-request@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + fix + feat + chore + requireScope: false + wip: true + validateSingleCommit: false diff --git a/.github/workflows/release-drafter.yaml b/.github/workflows/release-drafter.yaml new file mode 100644 index 0000000..49a0a21 --- /dev/null +++ b/.github/workflows/release-drafter.yaml @@ -0,0 +1,22 @@ +name: Release Drafter + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + types: [ opened, reopened, synchronize ] + +permissions: + contents: read + +jobs: + update_release_draft: + permissions: + contents: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: release-drafter/release-drafter@v5 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..d1961bd --- /dev/null +++ b/Dockerfile @@ -0,0 +1,70 @@ +# syntax=docker.io/docker/dockerfile:1 + +FROM node:22-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED=1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +# Uncomment the following line in case you want to disable telemetry during runtime. +# ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Set the correct permission for prerender cache +RUN mkdir .next +RUN chown nextjs:nodejs .next + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/next-config-js/output +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] diff --git a/next.config.ts b/next.config.ts index e9ffa30..68a6c64 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: "standalone", }; export default nextConfig;