Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
dist
.git
coverage
npm-debug.log
175 changes: 175 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Release

on:
workflow_dispatch:
inputs:
version_bump:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major

permissions:
contents: write
packages: write
id-token: write

jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: npm

- name: Install dependencies
run: npm ci

- name: Lint
run: npm run lint

- name: Type Check
run: npm run type-check

- name: Run tests
run: npm test

release:
needs: test
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.new_version }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Calculate next version
id: version
run: |
LATEST_TAG=$(git tag -l 'v*' | sort -V | tail -n1)
if [ -z "$LATEST_TAG" ]; then
LATEST_TAG="v0.0.0"
fi

echo "Latest tag: $LATEST_TAG"

VERSION=${LATEST_TAG#v}
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"

case "${{ inputs.version_bump }}" in
major)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
;;
minor)
MINOR=$((MINOR + 1))
PATCH=0
;;
patch)
PATCH=$((PATCH + 1))
;;
esac

NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
echo "New version: $NEW_VERSION"
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT

- name: Create and push tag
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a ${{ steps.version.outputs.new_version }} -m "Release ${{ steps.version.outputs.new_version }}"
git push origin ${{ steps.version.outputs.new_version }}

build-docker:
needs: release
runs-on: ubuntu-latest
steps:
- name: Checkout
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
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=raw,value=${{ needs.release.outputs.version }}
type=raw,value=latest

- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
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

github-release:
needs: [release, build-docker]
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Generate changelog
run: |
PREVIOUS_TAG=$(git tag -l 'v*' | sort -V | tail -n2 | head -n1)
if [ -z "$PREVIOUS_TAG" ]; then
PREVIOUS_TAG=$(git rev-list --max-parents=0 HEAD)
fi

echo "## Changes since $PREVIOUS_TAG" > changelog.md
echo "" >> changelog.md
git log --pretty=format:"- %s (%h)" $PREVIOUS_TAG..${{ needs.release.outputs.version }} >> changelog.md || true
echo "" >> changelog.md
echo "## Docker Image" >> changelog.md
echo "" >> changelog.md
echo "Docker image is available at:" >> changelog.md
echo "- \`ghcr.io/${{ github.repository }}:${{ needs.release.outputs.version }}\`" >> changelog.md
echo "- \`ghcr.io/${{ github.repository }}:latest\`" >> changelog.md
echo "" >> changelog.md
echo "Pull with: \`docker pull ghcr.io/${{ github.repository }}:${{ needs.release.outputs.version }}\`" >> changelog.md

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
tag_name: ${{ needs.release.outputs.version }}
name: Release ${{ needs.release.outputs.version }}
body_path: changelog.md
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM node:20-bookworm-slim AS base

FROM base AS build-tools
RUN apt-get update && apt-get install -y --no-install-recommends python3 make g++ && rm -rf /var/lib/apt/lists/*

FROM build-tools AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

FROM build-tools AS prod-deps
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev

FROM build-tools AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build

FROM base AS runner
WORKDIR /app

ENV NODE_ENV=production

COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist

RUN groupadd --system --gid 1001 nodejs && useradd --system --uid 1001 --gid 1001 copilot
RUN chown -R copilot:nodejs /app
USER copilot

EXPOSE 6060

ENV PORT=6060

CMD ["node", "dist/server.js"]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Point Docker CMD to compiled server path

The image launches node dist/server.js, but the build step is plain tsc with outDir: dist and sources under src/, so the emitted entrypoint is dist/src/server.js rather than dist/server.js. In practice, containers built from this Dockerfile will exit on startup with a module-not-found error for /app/dist/server.js, making the released image unusable until the CMD points at the actual compiled file.

Useful? React with 👍 / 👎.

Loading