From 07a405bb1109139a9f7f365c1d50e542e0ef9bf5 Mon Sep 17 00:00:00 2001 From: Matthieu Moquet Date: Thu, 7 Nov 2024 19:57:20 +0100 Subject: [PATCH] chore: build docker image --- .github/release-drafter.yml | 51 +++++++++++++ .github/workflows/docker.yaml | 101 +++++++++++++++++++++++++ .github/workflows/pr-title.yaml | 29 +++++++ .github/workflows/release-drafter.yaml | 22 ++++++ .github/workflows/release.yaml | 40 ++++++++++ .github/workflows/test.yaml | 46 +++++++++++ Dockerfile | 70 +++++++++++++++++ next.config.ts | 2 +- 8 files changed, 360 insertions(+), 1 deletion(-) create mode 100644 .github/release-drafter.yml create mode 100644 .github/workflows/docker.yaml create mode 100644 .github/workflows/pr-title.yaml create mode 100644 .github/workflows/release-drafter.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/test.yaml create mode 100644 Dockerfile 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/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..53d87e7 --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,40 @@ +name: Release + +on: + release: + types: [published] + +permissions: + contents: write + packages: write + +jobs: + releases-matrix: + name: Release Go Binary + runs-on: ubuntu-latest + strategy: + matrix: + goos: [linux, windows, darwin] + goarch: ["386", amd64, arm64] + exclude: + - goarch: "386" + goos: darwin + - goarch: arm64 + goos: windows + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + fetch-tags: true + + - uses: wangyoucao577/go-release-action@v1 + env: + BUILD_FOLDER: . + with: + build_command: make build + extra_files: LICENSE README.md + github_token: ${{ secrets.GITHUB_TOKEN }} + goos: ${{ matrix.goos }} + goarch: ${{ matrix.goarch }} + goversion: "1.22" + md5sum: false diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..1930691 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,46 @@ +name: Test + +on: + push: + branches: + - main + paths: + - 'pkg/**' + - '*.go' + - 'go.*' + - '.github/workflows/test.yaml' + pull_request: + paths: + - 'pkg/**' + - '*.go' + - 'go.*' + - '.github/workflows/test.yaml' + +env: + GO_VERSION: "1.22" + +jobs: + unit-tests: + name: 'Unit tests' + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + + - id: go-cache-paths + run: | + echo "go-mod=$(go env GOMODCACHE)" >> $GITHUB_OUTPUT + + # Cache go mod cache to speedup deps downloads + - uses: actions/cache@v3 + with: + path: ${{ steps.go-cache-paths.outputs.go-mod }} + key: ${{ runner.os }}-go-mod-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-go-mod- + + - name: 'Run unit tests' + run: | + make test 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;