Skip to content

Add Dockerfiles and GitHub Actions workflow for building and publishing PR images #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
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
81 changes: 81 additions & 0 deletions .github/workflows/build-pr-images.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
name: Build and Publish PR Images

on:
push:
branches:
- '*'
tags:
- '*'

jobs:
build_and_publish_pr_images:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
pull-requests: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to GitHub Container Registry
if: startsWith(github.ref, 'refs/tags/')
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Extract PR metadata
id: pr_metadata
run: |
echo "PR_NUMBER=${{ github.event.pull_request.number }}" >> $GITHUB_ENV
echo "SHORT_SHA=$(git rev-parse --short ${{ github.event.pull_request.head.sha }})" >> $GITHUB_ENV
echo "BRANCH_NAME=$(echo ${{ github.event.pull_request.head.ref }} | sed 's/[^a-zA-Z0-9]/-/g')" >> $GITHUB_ENV

- name: Build and push API Gateway image
if: startsWith(github.ref, 'refs/tags/')
uses: docker/build-push-action@v6
with:
context: .
file: apps/api-gateway/Dockerfile
target: patched
build-args: |
BASE_IMAGE=node:20-alpine
SN_GITHUB_NPM_TOKEN=${{ secrets.SN_GITHUB_NPM_TOKEN }}
push: true
tags: |
ghcr.io/${{ github.repository }}/mark-api-gateway:pr-${{ env.PR_NUMBER }}-${{ env.SHORT_SHA }}

- name: Build and push API image
uses: docker/build-push-action@v4
with:
context: .
file: apps/api/Dockerfile
target: patched
build-args: |
BASE_IMAGE=node:20-alpine
SN_GITHUB_NPM_TOKEN=${{ secrets.SN_GITHUB_NPM_TOKEN }}
push: true
tags: |
ghcr.io/${{ github.repository }}/mark-api:pr-${{ env.PR_NUMBER }}-${{ env.SHORT_SHA }}
- name: Build and push Web image
if: startsWith(github.ref, 'refs/tags/')
uses: docker/build-push-action@v4
with:
context: .
file: apps/web/Dockerfile
target: patched
build-args: |
BASE_IMAGE=node:20-alpine
SN_GITHUB_NPM_TOKEN=${{ secrets.SN_GITHUB_NPM_TOKEN }}
push: true
tags: |
ghcr.io/${{ github.repository }}/mark-web:pr-${{ env.PR_NUMBER }}-${{ env.SHORT_SHA }}

10 changes: 4 additions & 6 deletions apps/api-gateway/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# local-test/api-gateway.Dockerfile
# Using a consistent base for all stages
ARG BASE_IMAGE=icr.io/skills-network/node:20
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} AS builder

ARG SN_GITHUB_NPM_TOKEN
ARG SN_GITHUB_NPM_REGISTRY=https://npm.pkg.github.com
ARG DIR=/usr/src/app

# Pruning using turbo
WORKDIR $DIR
COPY . .
RUN yarn global add turbo@^2.0.3
RUN echo "@ibm-skills-network:registry=$SN_GITHUB_NPM_REGISTRY" >> .npmrc && echo "//npm.pkg.github.com/:_authToken=$SN_GITHUB_NPM_TOKEN" >> .npmrc
RUN turbo prune api-gateway --docker && rm -f .npmrc

# Installing the isolated workspace
Expand All @@ -37,7 +35,7 @@ FROM ${BASE_IMAGE} AS production
ARG DIR=/usr/src/app
ENV NODE_ENV production
WORKDIR $DIR
RUN apk add --no-cache dumb-init~=1
RUN apk add --no-cache dumb-init
COPY --chown=node:node --from=sourcer $DIR/apps/api-gateway/dist ./dist
COPY --chown=node:node --from=sourcer $DIR/node_modules $DIR/node_modules
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
Expand All @@ -48,4 +46,4 @@ EXPOSE 3000
FROM production AS patched
USER root
RUN apk -U upgrade
USER node
USER node
28 changes: 7 additions & 21 deletions apps/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,37 +1,27 @@
# local-test/api.Dockerfile
# Using a consistent base for all stages
ARG BASE_IMAGE=icr.io/skills-network/node:20
# hadolint ignore=DL3006
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} AS builder

ARG SN_GITHUB_NPM_TOKEN
ARG SN_GITHUB_NPM_REGISTRY=https://npm.pkg.github.com
ARG DIR=/usr/src/app

# Pruning using turbo
WORKDIR $DIR
COPY . .
RUN yarn global add turbo@^2.0.3
RUN echo "@ibm-skills-network:registry=$SN_GITHUB_NPM_REGISTRY" >> .npmrc && echo "//npm.pkg.github.com/:_authToken=$SN_GITHUB_NPM_TOKEN" >> .npmrc
RUN turbo prune api --docker && rm -f .npmrc

# Installing the isolated workspace
# hadolint ignore=DL3006
FROM ${BASE_IMAGE} AS installer
ARG DIR=/usr/src/app
WORKDIR $DIR
COPY --from=builder $DIR/out/json/ .
COPY --from=builder $DIR/out/yarn.lock ./yarn.lock
COPY --from=builder $DIR/turbo.json ./turbo.json
COPY --from=builder $DIR/packages ./packages
COPY --from=builder $DIR/apps/api/prisma ./prisma
# Install build dependencies (including pkgconf) and rebuild native modules
RUN apk add --no-cache python3~=3 make~=4 g++~=14 pkgconf~=2 \
&& yarn install --frozen-lockfile \
&& yarn prisma generate \
&& npm rebuild cld --build-from-source
RUN yarn install --ignore-scripts --frozen-lockfile

# Running build using turbo
# hadolint ignore=DL3006
FROM ${BASE_IMAGE} AS sourcer
ARG DIR=/usr/src/app
WORKDIR $DIR
Expand All @@ -41,23 +31,19 @@ COPY --from=builder /usr/local/share/.config/yarn/global /usr/local/share/.confi
RUN yarn build --filter=api && yarn install --production --ignore-scripts --frozen-lockfile

# Production stage
# hadolint ignore=DL3006
FROM ${BASE_IMAGE} AS production
ENV NODE_ENV production
ARG DIR=/usr/src/app
ENV NODE_ENV production
WORKDIR $DIR
RUN apk add --no-cache dumb-init~=1 postgresql15-client~=15
RUN apk add --no-cache dumb-init
COPY --chown=node:node --from=sourcer $DIR/apps/api/dist ./dist
COPY --chown=node:node --from=sourcer $DIR/node_modules $DIR/node_modules
COPY --chown=node:node --from=sourcer $DIR/prisma ./prisma
COPY --chown=node:node --from=sourcer $DIR/apps/api/migrate.sh ./migrate.sh
COPY --chown=node:node --from=sourcer $DIR/apps/api/ensureDb.js ./ensureDb.js
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "dist/src/main.js"]
CMD ["node", "dist/main.js"]
EXPOSE 3000

# Patched stage with updates
FROM production AS patched
USER root
RUN apk -U upgrade
USER node
USER node
10 changes: 4 additions & 6 deletions apps/web/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
# local-test/ui.Dockerfile
# Using a consistent base for all stages
ARG BASE_IMAGE=icr.io/skills-network/node:20
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} AS builder

ARG SN_GITHUB_NPM_TOKEN
ARG SN_GITHUB_NPM_REGISTRY=https://npm.pkg.github.com
ARG DIR=/usr/src/app

# Pruning using turbo for the Next.js app
WORKDIR $DIR
COPY . .
RUN yarn global add turbo@^2.0.3
# pragma: allowlist secret
RUN echo "@ibm-skills-network:registry=$SN_GITHUB_NPM_REGISTRY" >> .npmrc && echo "//npm.pkg.github.com/:_authToken=$SN_GITHUB_NPM_TOKEN" >> .npmrc
RUN turbo prune web --docker && rm -f .npmrc

# Installing the isolated workspace for the Next.js app
Expand All @@ -33,7 +31,7 @@ FROM ${BASE_IMAGE} AS production
ARG DIR=/usr/src/app
ENV NODE_ENV production
WORKDIR $DIR
RUN apk add --no-cache dumb-init~=1
RUN apk add --no-cache dumb-init
COPY --chown=node:node --from=sourcer $DIR/apps/web/.next ./.next
COPY --chown=node:node --from=sourcer $DIR/package.json $DIR/package.json
COPY --chown=node:node --from=sourcer $DIR/node_modules $DIR/node_modules
Expand All @@ -47,4 +45,4 @@ EXPOSE 3000
FROM production AS patched
USER root
RUN apk -U upgrade
USER node
USER node
55 changes: 55 additions & 0 deletions docker-compose.local-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
version: '3.8'

services:
# Service to build API Gateway image
api-gateway-build:
build:
context: .
dockerfile: ./local-test/api-gateway.Dockerfile
target: patched
image: local/mark-api-gateway:pr-test
container_name: mark-api-gateway-test
command: ["echo", "API Gateway image built successfully"]
profiles: ["build"]

# Service to build API image
api-build:
build:
context: .
dockerfile: ./local-test/api.Dockerfile
target: patched
image: local/mark-api:pr-test
container_name: mark-api-test
command: ["echo", "API image built successfully"]
profiles: ["build"]

# Service to build UI image
ui-build:
build:
context: .
dockerfile: ./local-test/ui.Dockerfile
target: patched
image: local/mark-ui:pr-test
container_name: mark-ui-test
command: ["echo", "UI image built successfully"]
profiles: ["build"]

# Services for actually running the applications once built
api-gateway:
image: local/mark-api-gateway:pr-test
ports:
- "8080:8080"
container_name: mark-api-gateway-running
profiles: ["run"]

api:
image: local/mark-api:pr-test
ports:
- "3000:3000"
container_name: mark-api-runn
ui:
image: local/mark-ui:pr-test
ports:
- "80:80"
container_name: mark-ui-running
profiles: ["run"]
48 changes: 48 additions & 0 deletions local-test/api-gateway.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Using a consistent base for all stages
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} AS builder

ARG DIR=/usr/src/app

# Pruning using turbo
WORKDIR $DIR
COPY . .
RUN yarn global add turbo@^2.0.3
RUN turbo prune api-gateway --docker && rm -f .npmrc

# Installing the isolated workspace
FROM ${BASE_IMAGE} AS installer
ARG DIR=/usr/src/app
WORKDIR $DIR
COPY --from=builder $DIR/out/json/ .
COPY --from=builder $DIR/out/yarn.lock ./yarn.lock
COPY --from=builder $DIR/turbo.json ./turbo.json
COPY --from=builder $DIR/packages ./packages
RUN yarn install --ignore-scripts --frozen-lockfile

# Running build using turbo
FROM ${BASE_IMAGE} AS sourcer
ARG DIR=/usr/src/app
WORKDIR $DIR
COPY --from=installer $DIR/ .
COPY --from=builder $DIR/out/full/ .
COPY --from=builder /usr/local/share/.config/yarn/global /usr/local/share/.config/yarn/global
RUN yarn build --filter=api-gateway && yarn install --production --ignore-scripts --frozen-lockfile

# Production stage
FROM ${BASE_IMAGE} AS production
ARG DIR=/usr/src/app
ENV NODE_ENV production
WORKDIR $DIR
RUN apk add --no-cache dumb-init
COPY --chown=node:node --from=sourcer $DIR/apps/api-gateway/dist ./dist
COPY --chown=node:node --from=sourcer $DIR/node_modules $DIR/node_modules
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "dist/main.js"]
EXPOSE 3000

# Patched stage with updates
FROM production AS patched
USER root
RUN apk -U upgrade
USER node
56 changes: 56 additions & 0 deletions local-test/api.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Using a consistent base for all stages
ARG BASE_IMAGE=node:20-alpine
FROM ${BASE_IMAGE} AS builder

ARG DIR=/usr/src/app

# Pruning using turbo
WORKDIR $DIR
COPY . .
RUN yarn global add turbo@^2.0.3
RUN turbo prune api --docker && rm -f .npmrc

# Installing the isolated workspace
FROM ${BASE_IMAGE} AS installer
ARG DIR=/usr/src/app
WORKDIR $DIR
COPY --from=builder $DIR/out/json/ .
COPY --from=builder $DIR/out/yarn.lock ./yarn.lock
COPY --from=builder $DIR/turbo.json ./turbo.json
COPY --from=builder $DIR/packages ./packages
COPY --from=builder $DIR/apps/api/prisma ./prisma
# Install build dependencies and rebuild native modules
RUN apk add --no-cache python3 make g++ pkgconf \
&& yarn install --frozen-lockfile \
&& yarn prisma generate \
&& npm rebuild cld --build-from-source

# Running build using turbo
FROM ${BASE_IMAGE} AS sourcer
ARG DIR=/usr/src/app
WORKDIR $DIR
COPY --from=installer $DIR/ .
COPY --from=builder $DIR/out/full/ .
COPY --from=builder /usr/local/share/.config/yarn/global /usr/local/share/.config/yarn/global
RUN yarn build --filter=api && yarn install --production --ignore-scripts --frozen-lockfile

# Production stage
FROM ${BASE_IMAGE} AS production
ENV NODE_ENV production
ARG DIR=/usr/src/app
WORKDIR $DIR
RUN apk add --no-cache dumb-init postgresql-client
COPY --chown=node:node --from=sourcer $DIR/apps/api/dist ./dist
COPY --chown=node:node --from=sourcer $DIR/node_modules $DIR/node_modules
COPY --chown=node:node --from=sourcer $DIR/prisma ./prisma
COPY --chown=node:node --from=sourcer $DIR/apps/api/migrate.sh ./migrate.sh
COPY --chown=node:node --from=sourcer $DIR/apps/api/ensureDb.js ./ensureDb.js
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
CMD ["node", "dist/src/main.js"]
EXPOSE 3000

# Patched stage with updates
FROM production AS patched
USER root
RUN apk -U upgrade
USER node
Loading
Loading