From d652118ba850a9bff817ee2b214787001d5241b6 Mon Sep 17 00:00:00 2001 From: LoneRifle Date: Mon, 19 Feb 2024 09:40:02 +0800 Subject: [PATCH] build(deploy): make initial CI flow Ensure that everything works up to building and pushing --- .github/workflows/ci.yml | 47 +++++++++++++++++++ Dockerfile.demos | 98 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 Dockerfile.demos diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..08a0646 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,47 @@ +name: CI + +on: + workflow_dispatch: + push: + pull_request: + types: [opened, reopened] + schedule: + # * is a special character in YAML so you have to quote this string + - cron: '30 15 * * *' +jobs: + deploy: + runs-on: ubuntu-latest + env: + IMAGE_TAG: github-actions-${{ github.sha }}-${{ github.run_id }}-${{github.run_attempt}} + steps: + - name: Checkout this repo + uses: actions/checkout@v4 + - name: Checkout opengovsg/FormSG + uses: actions/checkout@v4 + with: + name: opengovsg/FormSG + ref: refs/heads/release-al2 + - name: Set app version + run: | + echo "APP_VERSION=$(jq -r .version package.json)-$(echo ${GITHUB_REF##*/})-$(echo ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_ENV + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Setup Fly + uses: superfly/flyctl-actions/setup-flyctl@master + + - name: Auth to Fly Docker + env: + FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }} + run: flyctl auth docker + + - name: Build docker image + uses: docker/build-push-action@v3 + env: + DOCKER_REPO: registry.fly.io + APP_NAME: FormSG + with: + context: . + file: ./Dockerfile.demos + tags: registry.fly.io/${{env.APP_NAME}}:${{env.APP_VERSION}} diff --git a/Dockerfile.demos b/Dockerfile.demos new file mode 100644 index 0000000..e4b5621 --- /dev/null +++ b/Dockerfile.demos @@ -0,0 +1,98 @@ +# syntax=docker/dockerfile:1 + +FROM node:hydrogen-alpine3.18 as build + +# node-modules-builder stage installs/compiles the node_modules folder +# Python version must be specified starting in alpine3.12 +RUN apk update && apk upgrade && \ + apk --no-cache add --virtual native-deps \ + g++ gcc libgcc libstdc++ linux-headers autoconf automake make nasm python3 git curl && \ + npm install --quiet node-gyp -g +WORKDIR /build + +COPY package.json package-lock.json ./ +COPY shared/package.json shared/package-lock.json ./shared/ +COPY frontend/package.json frontend/package-lock.json ./frontend/ +COPY frontend/patches ./frontend/patches + +# Allow running of postinstall scripts +# RUN npm config set unsafe-perm true +# --legacy-peer-deps flag +# A breaking change in the peer dependency resolution strategy was introduced in +# npm 7. This resulted in npm throwing an error when installing packages: +# npm ERR! code ERESOLVE +# npm ERR! ERESOLVE unable to resolve dependency tree +# See also: +# * https://stackoverflow.com/questions/66239691/what-does-npm-install-legacy-peer-deps-do-exactly-when-is-it-recommended-wh +# NOTE: This flag is used again later in the build process when calling npm prune. +RUN npm ci --legacy-peer-deps + +COPY . ./ + +# --openssl-legacy-provider flag +# A breaking change in the SSL provider was introduced in node 17. This caused +# webpack 4 to break. This is an interim solution; we should investigate removing +# this flag once angular has been removed and we have upgraded to CRA5 (which uses +# webpack 5). +# See also: +# * https://stackoverflow.com/questions/69692842/error-message-error0308010cdigital-envelope-routinesunsupported +# * https://github.com/webpack/webpack/issues/14532#issuecomment-1304378535 +# These options are only used in the build stage, not the start stage. +ENV NODE_OPTIONS="--max-old-space-size=4096 --openssl-legacy-provider" + +RUN npm run build + +RUN npm prune --production --legacy-peer-deps + +# This stage builds the final container +FROM node:hydrogen-alpine3.18 +LABEL maintainer=FormSG +WORKDIR /opt/formsg + +# Install build from backend-build +COPY --from=build /build/node_modules /opt/formsg/node_modules +COPY --from=build /build/package.json /opt/formsg/package.json +COPY --from=build /build/dist /opt/formsg/dist + +# Built backend goes back to root working directory +RUN mv /opt/formsg/dist/backend/src /opt/formsg/ +RUN mv /opt/formsg/dist/backend/shared /opt/formsg/ + +# Install chromium from official docs +# https://github.com/puppeteer/puppeteer/blob/master/docs/troubleshooting.md#running-on-alpine +# Note that each alpine version supports a specific version of chromium +# Note that chromium and puppeteer-core are released together and it is the only version +# that is guaranteed to work. Upgrades must be done in lockstep. +# https://www.npmjs.com/package/puppeteer-core?activeTab=versions for corresponding versions + +RUN apk add --no-cache \ +# Compatible chromium versions can be found here https://pkgs.alpinelinux.org/packages?name=chromium&branch=v3.18&repo=&arch=&maintainer= + chromium=119.0.6045.159-r0 \ + nss \ + freetype \ + freetype-dev \ + harfbuzz \ + ca-certificates \ + ttf-freefont \ + tini + +# Tell Puppeteer to skip installing Chrome. We'll be using the installed package. +ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true +ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser + +# This package is needed to render Chinese characters in autoreply PDFs +RUN apk add font-wqy-zenhei --repository https://dl-cdn.alpinelinux.org/alpine/edge/community + +ENV CHROMIUM_BIN=/usr/bin/chromium-browser + +# Run as non-privileged user +RUN addgroup -S formsguser && adduser -S -g formsguser formsguser +USER formsguser + +ENV NODE_ENV=production +EXPOSE 4545 + +# tini is the init process that will adopt orphaned zombie processes +# e.g. chromium when launched to create a new PDF +ENTRYPOINT [ "tini", "--" ] +CMD [ "npm", "start" ]