From 6113b30a7d765431feb2eedf197e68e987e5de31 Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Thu, 25 Jan 2024 08:57:23 +0100 Subject: [PATCH] chore: prep for deployment (#199) * chore: setup docker + next config * chore: create build actin * fix: allow building when db is not available --- .dockerignore | 21 +++++++++++++++ .github/workflows/build.yaml | 50 ++++++++++++++++++++++++++++++++++++ docker/app/Dockerfile | 33 ++++++++++++++++++++++++ next.config.js | 1 + src/pages/index.tsx | 38 +++++++++++++++++---------- 5 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/build.yaml create mode 100644 docker/app/Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..74b5e38 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +# all node_modules +**/node_modules + +# build output +.next + +# development files +.DS_Store +.env.local +.env.development.local +.env.test.local +.env.production.local + +# npm debug log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# docker files +.dockerignore +docker/**/* diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..8c4dcc2 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,50 @@ +name: Build + +on: + workflow_dispatch: + +jobs: + build: + name: Build + runs-on: ubuntu-22.04 + environment: staging # Hardcoded for now. TODO: use a matrix? + strategy: + fail-fast: false + matrix: + node-version: [20.x] + site_tlds: ['dev'] + apps: ['app'] + + steps: + - name: Checkout source code + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Create a tagname + id: tagname + run: | + echo "tagname=$(git rev-parse --short HEAD)-$(date +%Y%m%d)-$(date +%H%M)" >> $GITHUB_ENV + + - name: Build & Tag Images + run: | + docker build . \ + --tag registry.digitalocean.com/${{ vars.DOCR_NAME }}/${{ matrix.site_tlds }}/tech-event-calendar-${{ matrix.apps }}:$tagname \ + --tag registry.digitalocean.com/${{ vars.DOCR_NAME }}/${{ matrix.site_tlds }}/tech-event-calendar-${{ matrix.apps }}:latest \ + --file docker/${{ matrix.apps }}/Dockerfile + + - name: Install doctl + uses: digitalocean/action-doctl@v2 + with: + token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} + + - name: Log in to DigitalOcean Container Registry with short-lived credentials + run: doctl registry login --expiry-seconds 1200 + + - name: Push image to DigitalOcean Container Registry + run: | + docker push registry.digitalocean.com/${{ vars.DOCR_NAME }}/${{ matrix.site_tlds }}/tech-event-calendar-${{ matrix.apps }}:$tagname + docker push registry.digitalocean.com/${{ vars.DOCR_NAME }}/${{ matrix.site_tlds }}/tech-event-calendar-${{ matrix.apps }}:latest \ No newline at end of file diff --git a/docker/app/Dockerfile b/docker/app/Dockerfile new file mode 100644 index 0000000..19082d3 --- /dev/null +++ b/docker/app/Dockerfile @@ -0,0 +1,33 @@ +FROM node:20-alpine as base +WORKDIR /app +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk update +RUN apk add --no-cache libc6-compat +# 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 + +FROM base as build +RUN npm i -g pnpm@8 +USER node +WORKDIR /app/build +# First copy the package.json and pnpm-lock.yaml to leverage Docker cache +COPY --chown=node:node package.json pnpm-lock.yaml ./ +# We don't need Cypress in production +RUN CYPRESS_INSTALL_BINARY=0 pnpm install +# Now we need the source to build the app +COPY --chown=node:node . . +RUN pnpm prisma generate +RUN pnpm run build + +FROM base as production +ENV NODE_ENV=production +COPY --from=build --chown=node:node /app/build/.next/standalone ./ +COPY --from=build --chown=node:node /app/build/.next/static ./.next/static +COPY --from=build /app/build/public ./public +USER node +EXPOSE 3000 +ENV PORT 3000 +ENV HOSTNAME "0.0.0.0" +CMD ["node", "server.js"] \ No newline at end of file diff --git a/next.config.js b/next.config.js index 25a13cc..a2e3a6e 100644 --- a/next.config.js +++ b/next.config.js @@ -25,6 +25,7 @@ const nextConfig = { }, ]; }, + output: "standalone", }; module.exports = nextConfig; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 3efb6ba..c1adc3b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -33,20 +33,30 @@ type EventProps = { }; export const getStaticProps: GetStaticProps = async () => { - const events = await prisma.event.findMany({ - select: { - id: true, - name: true, - date: true, - link: true, - latitude: true, - longitude: true, - }, - }); - const serializeableEvents = events.map((event) => ({ - ...event, - date: event.date.toISOString(), - })); + let serializeableEvents: EventInfo[] = []; + try { + const events = await prisma.event.findMany({ + select: { + id: true, + name: true, + date: true, + link: true, + latitude: true, + longitude: true, + }, + }); + serializeableEvents = events.map((event) => ({ + ...event, + date: event.date.toISOString(), + })); + } catch (e) { + console.log(); + console.log("Error fetching events from database"); + console.log( + "If this happens while building the image, it's fine, it just means the database isn't available" + ); + console.log(e); + } return { props: {