diff --git a/apps/webapp/Dockerfile b/apps/webapp/Dockerfile deleted file mode 100644 index 63365c998..000000000 --- a/apps/webapp/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -FROM node:18-bullseye-slim AS pruner -RUN apt-get update && apt-get install -y openssl -WORKDIR /app -RUN yarn global add turbo -COPY . . -RUN turbo prune --scope=webapp --docker -RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' + - -# Base strategy to have layer caching -FROM node:18-bullseye-slim AS base -RUN apt-get update && apt-get install -y openssl -WORKDIR /app -COPY .gitignore .gitignore -COPY --from=pruner /app/out/json/ . -COPY --from=pruner /app/out/pnpm-lock.yaml ./pnpm-lock.yaml -COPY --from=pruner /app/out/pnpm-workspace.yaml ./pnpm-workspace.yaml - -FROM base AS dev-deps -WORKDIR /app -RUN corepack enable -RUN pnpm install --ignore-scripts --no-frozen-lockfile - -FROM base AS production-deps -WORKDIR /app -RUN corepack enable -ENV NODE_ENV production -RUN pnpm install --prod --no-frozen-lockfile -COPY --from=pruner /app/out/full/packages/database/prisma/schema.prisma /app/packages/database/prisma/schema.prisma -RUN pnpx prisma generate --schema /app/packages/database/prisma/schema.prisma - -FROM base AS builder -WORKDIR /app -RUN corepack enable -# ENV NODE_ENV production -COPY --from=pruner /app/out/full/ . -COPY --from=dev-deps /app/ . -COPY turbo.json turbo.json -RUN pnpm run generate -RUN pnpm run build --filter=webapp... - -# Runner -FROM node:18-bullseye-slim AS runner -RUN apt-get update && apt-get install -y openssl -WORKDIR /app -RUN corepack enable -ENV NODE_ENV production -# RUN addgroup --system --gid 1001 nodejs -# RUN adduser --system --uid 1001 remixjs -# RUN chown -R remixjs:nodejs /app -# USER remixjs - -# COPY --from=pruner --chown=remixjs:nodejs /app/out/full/ . -# COPY --from=production-deps --chown=remixjs:nodejs /app . -# COPY --from=builder --chown=remixjs:nodejs /app/apps/remix-app/app/styles/tailwind.css ./apps/remix-app/app/styles/tailwind.css -# COPY --from=builder --chown=remixjs:nodejs /app/apps/remix-app/build/server.js ./apps/remix-app/build/server.js -# COPY --from=builder --chown=remixjs:nodejs /app/apps/remix-app/build ./apps/remix-app/build -# COPY --from=builder --chown=remixjs:nodejs /app/apps/remix-app/public ./apps/remix-app/public - -COPY --from=pruner /app/out/full/ . -COPY --from=production-deps /app . -COPY --from=builder /app/apps/remix-app/app/styles/tailwind.css ./apps/remix-app/app/styles/tailwind.css -COPY --from=builder /app/apps/remix-app/build/server.js ./apps/remix-app/build/server.js -COPY --from=builder /app/apps/remix-app/build ./apps/remix-app/build -COPY --from=builder /app/apps/remix-app/public ./apps/remix-app/public - - -ENTRYPOINT ["pnpm", "--filter", "webapp", "run", "start"] \ No newline at end of file diff --git a/apps/webapp/package.json b/apps/webapp/package.json index f176e4fc9..51a4e8f64 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -11,7 +11,6 @@ "format": "prettier --write .", "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", "start": "cross-env NODE_ENV=production node --max-old-space-size=8192 ./build/server.js", - "docker:build": "cd ../.. && docker build -t trigger-webapp -f ./apps/webapp/Dockerfile .", "test": "vitest run", "test:cov": "vitest run --coverage", "test:e2e:dev": "start-server-and-test dev http://localhost:3000 \"npx cypress open\"", @@ -238,4 +237,4 @@ "engines": { "node": ">=16.0.0" } -} +} \ No newline at end of file diff --git a/apps/webapp/prisma/seed.ts b/apps/webapp/prisma/seed.ts index 9b0135cac..716a42fe2 100644 --- a/apps/webapp/prisma/seed.ts +++ b/apps/webapp/prisma/seed.ts @@ -66,6 +66,8 @@ async function seedIntegrationAuthMethods() { async function seed() { await seedIntegrationAuthMethods(); + + // TODO: extract this into a separate file and put it behind some kind of env var // Create a user, organization, and project const user = await prisma.user.upsert({ where: { diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 930bec268..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,26 +0,0 @@ -version: "3.7" - -volumes: - database: - driver: local - pulsardatav: - driver: local - -services: - db: - image: postgres:latest - restart: always - environment: - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=postgres - - POSTGRES_DB=postgres - ports: - - "5432:5432" - volumes: - - database:/var/lib/postgresql/data - networks: - - app_network - -networks: - app_network: - external: true diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 000000000..601c24eba --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,24 @@ +LOGIN_ORIGIN=http://localhost:3000 +APP_ORIGIN=http://localhost:3000 + +# Encryption key that will be used to encrypt magic link tokens +MAGIC_LINK_SECRET=secret +# Encryption key that will be used to encrypt session cookies +SESSION_SECRET=secret + +POSTGRES_USER=postgres +POSTGRES_PASSWORD=postgres +POSTGRES_DB=postgres +DATABASE_HOST=database:5432 +DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DATABASE_HOST}/${POSTGRES_DB} + +# Github sign in OAUTH client id and secret +GITHUB_CLIENT_ID= +GITHUB_SECRET= + +# E-mail settings +FROM_EMAIL= +REPLY_TO_EMAIL= +RESEND_API_KEY= + +NODE_ENV=production \ No newline at end of file diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..e73ed78a9 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,63 @@ +FROM node:18 AS pruner + +WORKDIR /triggerdotdev + +COPY . . +RUN npx turbo prune --scope=webapp --docker +RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' + + +# Base strategy to have layer caching +FROM node:18 AS base +RUN apt-get update && apt-get install -y openssl +WORKDIR /triggerdotdev +COPY .gitignore .gitignore +COPY --from=pruner /triggerdotdev/out/json/ . +COPY --from=pruner /triggerdotdev/out/pnpm-lock.yaml ./pnpm-lock.yaml +COPY --from=pruner /triggerdotdev/out/pnpm-workspace.yaml ./pnpm-workspace.yaml + +## Dev deps +FROM base AS dev-deps +WORKDIR /triggerdotdev +# Corepack is used to install pnpm +RUN corepack enable +ENV NODE_ENV development +RUN pnpm install --ignore-scripts --no-frozen-lockfile + +## Production deps +FROM base AS production-deps +WORKDIR /triggerdotdev +# Corepack is used to install pnpm +RUN corepack enable +ENV NODE_ENV production +RUN pnpm install --prod --no-frozen-lockfile +COPY --from=pruner /triggerdotdev/packages/database/prisma/schema.prisma /triggerdotdev/packages/database/prisma/schema.prisma +RUN pnpx prisma generate --schema /triggerdotdev/packages/database/prisma/schema.prisma + +## Builder (builds the webapp) +FROM base AS builder +WORKDIR /triggerdotdev +# Corepack is used to install pnpm +RUN corepack enable +COPY --from=pruner /triggerdotdev/out/full/ . +COPY --from=dev-deps /triggerdotdev/ . +COPY turbo.json turbo.json +COPY docker/scripts ./scripts +RUN chmod +x ./scripts/entrypoint.sh +RUN pnpm run generate +RUN pnpm run build --filter=webapp... + +# Runner +FROM node:18 AS runner +RUN apt-get update && apt-get install -y openssl +WORKDIR /triggerdotdev +RUN corepack enable +ENV NODE_ENV production + +COPY --from=pruner /triggerdotdev/out/full/ . +COPY --from=production-deps /triggerdotdev . +COPY --from=builder /triggerdotdev/apps/webapp/build/server.js ./apps/webapp/build/server.js +COPY --from=builder /triggerdotdev/apps/webapp/build ./apps/webapp/build +COPY --from=builder /triggerdotdev/apps/webapp/public ./apps/webapp/public +COPY --from=builder /triggerdotdev/scripts ./scripts + +CMD ["./scripts/entrypoint.sh"] \ No newline at end of file diff --git a/docker/compose-without-app.yml b/docker/compose-without-app.yml new file mode 100644 index 000000000..c6e9c9f5c --- /dev/null +++ b/docker/compose-without-app.yml @@ -0,0 +1,21 @@ +version: "3" + +volumes: + database-data: + +networks: + app_network: + external: false + +services: + database: + container_name: database + image: postgres:latest + restart: always + volumes: + - database-data:/var/lib/postgresql/data/ + env_file: .env + networks: + - app_network + ports: + - 5432:5432 diff --git a/docker/compose.yml b/docker/compose.yml new file mode 100644 index 000000000..9e94e325d --- /dev/null +++ b/docker/compose.yml @@ -0,0 +1,35 @@ +version: "3" + +volumes: + database-data: + +networks: + app_network: + external: false + +services: + webapp: + build: + context: ../ + dockerfile: docker/Dockerfile + ports: + - 3000:3000 + depends_on: + - database + env_file: .env + environment: + - DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${DATABASE_HOST}/${POSTGRES_DB} + networks: + - app_network + + database: + container_name: database + image: postgres:latest + restart: always + volumes: + - database-data:/var/lib/postgresql/data/ + env_file: .env + networks: + - app_network + ports: + - 5432:5432 diff --git a/docker/scripts/entrypoint.sh b/docker/scripts/entrypoint.sh new file mode 100755 index 000000000..a7e6cf49d --- /dev/null +++ b/docker/scripts/entrypoint.sh @@ -0,0 +1,7 @@ +#!/bin/sh +set -x + +scripts/wait-for-it.sh ${DATABASE_HOST}:5432 -- echo "database is up" +npx prisma migrate deploy --schema /triggerdotdev/packages/database/prisma/schema.prisma +npx ts-node@latest --transpile-only /triggerdotdev/apps/webapp/prisma/seed.ts +npx turbo run start diff --git a/docker/scripts/wait-for-it.sh b/docker/scripts/wait-for-it.sh new file mode 100755 index 000000000..1ebf3b0eb --- /dev/null +++ b/docker/scripts/wait-for-it.sh @@ -0,0 +1,184 @@ +#!/bin/sh + +# The MIT License (MIT) +# +# Copyright (c) 2017 Eficode Oy +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +set -- "$@" -- "$TIMEOUT" "$QUIET" "$PROTOCOL" "$HOST" "$PORT" "$result" +TIMEOUT=15 +QUIET=0 +# The protocol to make the request with, either "tcp" or "http" +PROTOCOL="tcp" + +echoerr() { + if [ "$QUIET" -ne 1 ]; then printf "%s\n" "$*" 1>&2; fi +} + +usage() { + exitcode="$1" + cat << USAGE >&2 +Usage: + $0 host:port|url [-t timeout] [-- command args] + -q | --quiet Do not output any status messages + -t TIMEOUT | --timeout=timeout Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit "$exitcode" +} + +wait_for() { + case "$PROTOCOL" in + tcp) + if ! command -v nc >/dev/null; then + echoerr 'nc command is missing!' + exit 1 + fi + ;; + wget) + if ! command -v wget >/dev/null; then + echoerr 'wget command is missing!' + exit 1 + fi + ;; + esac + + while :; do + case "$PROTOCOL" in + tcp) + nc -w 1 -z "$HOST" "$PORT" > /dev/null 2>&1 + ;; + http) + wget --timeout=1 -q "$HOST" -O /dev/null > /dev/null 2>&1 + ;; + *) + echoerr "Unknown protocol '$PROTOCOL'" + exit 1 + ;; + esac + + result=$? + + if [ $result -eq 0 ] ; then + if [ $# -gt 7 ] ; then + for result in $(seq $(($# - 7))); do + result=$1 + shift + set -- "$@" "$result" + done + + TIMEOUT=$2 QUIET=$3 PROTOCOL=$4 HOST=$5 PORT=$6 result=$7 + shift 7 + exec "$@" + fi + exit 0 + fi + + if [ "$TIMEOUT" -le 0 ]; then + break + fi + TIMEOUT=$((TIMEOUT - 1)) + + sleep 1 + done + echo "Operation timed out" >&2 + exit 1 +} + +while :; do + case "$1" in + http://*|https://*) + HOST="$1" + PROTOCOL="http" + shift 1 + ;; + *:* ) + HOST=$(printf "%s\n" "$1"| cut -d : -f 1) + PORT=$(printf "%s\n" "$1"| cut -d : -f 2) + shift 1 + ;; + -q | --quiet) + QUIET=1 + shift 1 + ;; + -q-*) + QUIET=0 + echoerr "Unknown option: $1" + usage 1 + ;; + -q*) + QUIET=1 + result=$1 + shift 1 + set -- -"${result#-q}" "$@" + ;; + -t | --timeout) + TIMEOUT="$2" + shift 2 + ;; + -t*) + TIMEOUT="${1#-t}" + shift 1 + ;; + --timeout=*) + TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + break + ;; + --help) + usage 0 + ;; + -*) + QUIET=0 + echoerr "Unknown option: $1" + usage 1 + ;; + *) + QUIET=0 + echoerr "Unknown argument: $1" + usage 1 + ;; + esac +done + +if ! [ "$TIMEOUT" -ge 0 ] 2>/dev/null; then + echoerr "Error: invalid timeout '$TIMEOUT'" + usage 3 +fi + +case "$PROTOCOL" in + tcp) + if [ "$HOST" = "" ] || [ "$PORT" = "" ]; then + echoerr "Error: you need to provide a host and port to test." + usage 2 + fi + ;; + http) + if [ "$HOST" = "" ]; then + echoerr "Error: you need to provide a host to test." + usage 2 + fi + ;; +esac + +wait_for "$@" \ No newline at end of file diff --git a/package.json b/package.json index aa6d8ac47..8519365c8 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,12 @@ "format": "prettier --write \"**/*.{ts,tsx,md}\"", "generate": "turbo run generate", "lint": "turbo run lint", - "docker": "docker-compose -f docker-compose.yml up -d", - "docker:stop": "docker-compose -f docker-compose.yml stop", - "docker:build": "turbo run docker:build", + "docker:services": "docker-compose -f docker/compose-without-app.yml up -d", + "docker:services:stop": "docker-compose -f docker/compose-without-app.yml stop", + "docker:app": "docker-compose -p triggerv2 -f docker/compose.yml up -d", + "docker:app:build": "docker-compose -p triggerv2 -f docker/compose.yml build webapp", + "docker:app:stop": "docker-compose -p triggerv2 -f docker/compose.yml stop", + "docker:app:down": "docker-compose -p triggerv2 -f docker/compose.yml down", "test": "turbo run test", "test:dev": "turbo run test:dev", "start": "turbo run start", @@ -63,4 +66,4 @@ "@changesets/cli": "^2.26.0", "node-fetch": "2.6.x" } -} +} \ No newline at end of file