From 9a0c1f0fad0a9775dd371aa17ebfc6e838c44e28 Mon Sep 17 00:00:00 2001 From: Maxime Jacob Date: Fri, 26 Jan 2024 15:07:51 -0500 Subject: [PATCH 1/3] * Builds proper production package --- README.md | 9 +++++---- package-lock.json | 1 - package.json | 2 +- scripts/build-app.sh | 22 ++++++++++++++++++++++ 4 files changed, 28 insertions(+), 6 deletions(-) create mode 100755 scripts/build-app.sh diff --git a/README.md b/README.md index 5f707383..20da3ade 100644 --- a/README.md +++ b/README.md @@ -45,10 +45,11 @@ If you want to contribute financially and help us keep the application free and ## Run locally 1. Clone the repository (or fork it if you intend to contribute) -2. Start a PostgreSQL server. You can run `./scripts/start-local-db.sh` if you don’t have a server already. -3. Copy the file `.env.example` as `.env` -4. Run `npm install` to install dependencies. This will also apply database migrations and update Prisma Client. -5. Run `npm run dev` to start the development server +2. `npm install` +3. Start a PostgreSQL server. You can run `./scripts/start-local-db.sh` if you don’t have a server already. +4. Copy the file `.env.example` as `.env` +5. `npm run prisma-init` +6. `npm run dev` ## Run in a container diff --git a/package-lock.json b/package-lock.json index 52b0e543..4f0adb57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,6 @@ "": { "name": "spliit2", "version": "0.1.0", - "hasInstallScript": true, "dependencies": { "@hookform/resolvers": "^3.3.2", "@prisma/client": "^5.6.0", diff --git a/package.json b/package.json index 5706b1ef..ed729dae 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "lint": "next lint", "check-types": "tsc --noEmit", "check-formatting": "prettier -c src", - "postinstall": "prisma migrate deploy && prisma generate", + "prisma-init": "prisma migrate deploy && prisma generate", "build-image": "./scripts/build-image.sh", "start-container": "docker compose --env-file container.env up" }, diff --git a/scripts/build-app.sh b/scripts/build-app.sh new file mode 100755 index 00000000..8b03bf7c --- /dev/null +++ b/scripts/build-app.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +ENV_FILE=".env" +EXAMPLE_ENV_FILE=".env.example" + +if [ ! -f "${ENV_FILE}" ]; then + echo "Missing ${ENV_FILE} file, creating it from ${EXAMPLE_ENV_FILE}" + cat "${EXAMPLE_ENV_FILE}" > ${ENV_FILE} +fi + +npm i +npx prisma generate +npx next build + +mkdir -p spliit +mv ./.next ./spliit +cp -r ./node_modules ./spliit/.next +cp LICENSE ./spliit +cp README.md ./spliit + +tar -zcvf spliit.tar.gz ./spliit +rm -rf ./spliit From a0d2c313e13391c631c0ac488f35fed23de50dd1 Mon Sep 17 00:00:00 2001 From: Maxime Jacob Date: Fri, 16 Feb 2024 07:32:56 -0500 Subject: [PATCH 2/3] * Docker image has prod build * Docker image prints version on startup * Build command now creates a self-contained archive * Builds nightly (version with git hash) or prod * Fixes prisma generate being run too late --- .env.example | 22 ++++++++++++-- .gitignore | 2 ++ Dockerfile | 51 +++++++++------------------------ compose.yaml | 4 +-- container.env.example | 6 ---- package.json | 16 +++++++++-- prisma/schema.prisma | 1 + scripts/build-app.sh | 22 -------------- scripts/build-image.sh | 26 +++++++++++------ scripts/build.env | 22 -------------- scripts/container-entrypoint.sh | 6 ++-- scripts/package.sh | 12 ++++++++ scripts/post-build.sh | 5 ++++ scripts/pre-build.sh | 42 +++++++++++++++++++++++++++ scripts/set-env.sh | 29 +++++++++++++++++++ 15 files changed, 161 insertions(+), 105 deletions(-) delete mode 100644 container.env.example delete mode 100755 scripts/build-app.sh delete mode 100644 scripts/build.env create mode 100755 scripts/package.sh create mode 100755 scripts/post-build.sh create mode 100755 scripts/pre-build.sh create mode 100755 scripts/set-env.sh diff --git a/.env.example b/.env.example index bc38b5f2..a360a701 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,20 @@ -POSTGRES_PRISMA_URL=postgresql://postgres:1234@localhost -POSTGRES_URL_NON_POOLING=postgresql://postgres:1234@localhost \ No newline at end of file +# db +POSTGRES_PASSWORD=1234 + +# app +POSTGRES_PRISMA_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db +POSTGRES_URL_NON_POOLING=postgresql://postgres:${POSTGRES_PASSWORD}@db +NEXT_PUBLIC_BASE_URL=http://localhost:3000 + +# app-minio +NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS=false +S3_UPLOAD_KEY="" +S3_UPLOAD_SECRET="" +S3_UPLOAD_BUCKET=spliit +S3_UPLOAD_REGION=eu-north-1 +S3_UPLOAD_ENDPOINT=s3://minio.example.com + +# app-openai +NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT=false +OPENAI_API_KEY="" +NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT=false diff --git a/.gitignore b/.gitignore index 9d718ff6..e6ebe57e 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,8 @@ # production /build +tmp/ +dist/ # misc .DS_Store diff --git a/Dockerfile b/Dockerfile index 3fefd3c8..6f92f43d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,47 +1,22 @@ -FROM node:21-alpine as base +ARG SPLIIT_NODE_VERSION="21-alpine" -WORKDIR /usr/app -COPY ./package.json \ - ./package-lock.json \ - ./next.config.js \ - ./tsconfig.json \ - ./reset.d.ts \ - ./tailwind.config.js \ - ./postcss.config.js ./ -COPY ./scripts ./scripts -COPY ./prisma ./prisma -COPY ./src ./src - -RUN apk add --no-cache openssl && \ - npm ci --ignore-scripts && \ - npx prisma generate - -ENV NEXT_TELEMETRY_DISABLED=1 - -COPY scripts/build.env .env -RUN npm run build - -RUN rm -r .next/cache +FROM node:${SPLIIT_NODE_VERSION} -FROM node:21-alpine as runtime-deps +ARG SPLIIT_APP_NAME="spliit2" +ARG SPLIIT_VERSION="" +ARG SPLIIT_DEST_DIR="./" WORKDIR /usr/app -COPY --from=base /usr/app/package.json /usr/app/package-lock.json ./ -COPY --from=base /usr/app/prisma ./prisma -RUN npm ci --omit=dev --omit=optional --ignore-scripts && \ - npx prisma generate +COPY ${SPLIIT_DEST_DIR} ./ +COPY ./scripts/container-entrypoint.sh ./ -FROM node:21-alpine as runner +RUN apk add --no-cache openssl -EXPOSE 3000/tcp -WORKDIR /usr/app +ENV NEXT_TELEMETRY_DISABLED=1 +ENV SPLIIT_APP_NAME=${SPLIIT_APP_NAME} +ENV SPLIIT_VERSION=${SPLIIT_VERSION} -COPY --from=base /usr/app/package.json /usr/app/package-lock.json ./ -COPY --from=runtime-deps /usr/app/node_modules ./node_modules -COPY ./public ./public -COPY ./scripts ./scripts -COPY --from=base /usr/app/prisma ./prisma -COPY --from=base /usr/app/.next ./.next +EXPOSE 3000/tcp -ENTRYPOINT ["/bin/sh", "/usr/app/scripts/container-entrypoint.sh"] +ENTRYPOINT ["/bin/sh", "/usr/app/container-entrypoint.sh"] diff --git a/compose.yaml b/compose.yaml index e0ef1119..18e36454 100644 --- a/compose.yaml +++ b/compose.yaml @@ -4,7 +4,7 @@ services: ports: - 3000:3000 env_file: - - container.env + - .env depends_on: db: condition: service_healthy @@ -14,7 +14,7 @@ services: ports: - 5432:5432 env_file: - - container.env + - .env volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: diff --git a/container.env.example b/container.env.example deleted file mode 100644 index 7451dcbf..00000000 --- a/container.env.example +++ /dev/null @@ -1,6 +0,0 @@ -# db -POSTGRES_PASSWORD=1234 - -# app -POSTGRES_PRISMA_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db -POSTGRES_URL_NON_POOLING=postgresql://postgres:${POSTGRES_PASSWORD}@db diff --git a/package.json b/package.json index ed729dae..b28e07e8 100644 --- a/package.json +++ b/package.json @@ -4,14 +4,24 @@ "private": true, "scripts": { "dev": "next dev", - "build": "next build", + "prebuild": "./scripts/pre-build.sh nightly", + "build": "./scripts/package.sh", + "postbuild": "./scripts/post-build.sh", + "prebuild-prod": "./scripts/pre-build.sh prod", + "build-prod": "./scripts/package.sh", + "postbuild-prod": "./scripts/post-build.sh", + "prebuild-image": "./scripts/pre-build.sh nightly", + "build-image": "./scripts/build-image.sh", + "postbuild-image": "./scripts/post-build.sh", + "prebuild-image-prod": "./scripts/pre-build.sh prod", + "build-image-prod": "./scripts/build-image.sh", + "postbuild-image-prod": "./scripts/post-build.sh", "start": "next start", "lint": "next lint", "check-types": "tsc --noEmit", "check-formatting": "prettier -c src", "prisma-init": "prisma migrate deploy && prisma generate", - "build-image": "./scripts/build-image.sh", - "start-container": "docker compose --env-file container.env up" + "start-container": "docker compose --env-file .env up" }, "dependencies": { "@hookform/resolvers": "^3.3.2", diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 394342de..cfbb2fe1 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -3,6 +3,7 @@ generator client { provider = "prisma-client-js" + binaryTargets = ["native", "linux-musl-openssl-3.0.x"] } datasource db { diff --git a/scripts/build-app.sh b/scripts/build-app.sh deleted file mode 100755 index 8b03bf7c..00000000 --- a/scripts/build-app.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -ENV_FILE=".env" -EXAMPLE_ENV_FILE=".env.example" - -if [ ! -f "${ENV_FILE}" ]; then - echo "Missing ${ENV_FILE} file, creating it from ${EXAMPLE_ENV_FILE}" - cat "${EXAMPLE_ENV_FILE}" > ${ENV_FILE} -fi - -npm i -npx prisma generate -npx next build - -mkdir -p spliit -mv ./.next ./spliit -cp -r ./node_modules ./spliit/.next -cp LICENSE ./spliit -cp README.md ./spliit - -tar -zcvf spliit.tar.gz ./spliit -rm -rf ./spliit diff --git a/scripts/build-image.sh b/scripts/build-image.sh index c900089e..ca2a87b6 100755 --- a/scripts/build-image.sh +++ b/scripts/build-image.sh @@ -1,12 +1,22 @@ #!/bin/bash -SPLIIT_APP_NAME=$(node -p -e "require('./package.json').name") -SPLIIT_VERSION=$(node -p -e "require('./package.json').version") - -# we need to set dummy data for POSTGRES env vars in order for build not to fail -docker buildx build \ - -t ${SPLIIT_APP_NAME}:${SPLIIT_VERSION} \ - -t ${SPLIIT_APP_NAME}:latest \ - . +source scripts/set-env.sh + +if [ ! -d "${SPLIIT_DEST_DIR}" ]; then + echo "Build folder ${SPLIIT_DEST_DIR} not found" + exit 1 +fi + +mkdir -p ${SPLIIT_DIST_FOLDER} + +docker buildx build \ + --no-cache \ + --build-arg "SPLIIT_APP_NAME=${SPLIIT_APP_NAME}" \ + --build-arg "SPLIIT_VERSION=${SPLIIT_VERSION}" \ + --build-arg "SPLIIT_DEST_DIR=${SPLIIT_DEST_DIR}" \ + --build-arg "SPLIIT_NODE_VERSION=${SPLIIT_NODE_VERSION}" \ + -t ${SPLIIT_APP_NAME}:${SPLIIT_VERSION} \ + -t ${SPLIIT_APP_NAME}:latest . +docker save ${SPLIIT_APP_NAME}:${SPLIIT_VERSION} > ./${SPLIIT_DIST_FOLDER}/${SPLIIT_APP_NAME}-${SPLIIT_VERSION}-docker.tar.gz docker image prune -f diff --git a/scripts/build.env b/scripts/build.env deleted file mode 100644 index 98b3fac0..00000000 --- a/scripts/build.env +++ /dev/null @@ -1,22 +0,0 @@ -# build file that contains all possible env vars with mocked values -# as most of them are used at build time in order to have the production build to work properly - -# db -POSTGRES_PASSWORD=1234 - -# app -POSTGRES_PRISMA_URL=postgresql://postgres:${POSTGRES_PASSWORD}@db -POSTGRES_URL_NON_POOLING=postgresql://postgres:${POSTGRES_PASSWORD}@db - -# app-minio -NEXT_PUBLIC_ENABLE_EXPENSE_DOCUMENTS=false -S3_UPLOAD_KEY=AAAAAAAAAAAAAAAAAAAA -S3_UPLOAD_SECRET=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA -S3_UPLOAD_BUCKET=spliit -S3_UPLOAD_REGION=eu-north-1 -S3_UPLOAD_ENDPOINT=s3://minio.example.com - -# app-openai -NEXT_PUBLIC_ENABLE_RECEIPT_EXTRACT=false -OPENAI_API_KEY=XXXXXXXXXXXXXXXXXXXXXXXXXXXX -NEXT_PUBLIC_ENABLE_CATEGORY_EXTRACT=false diff --git a/scripts/container-entrypoint.sh b/scripts/container-entrypoint.sh index 58713147..25625882 100755 --- a/scripts/container-entrypoint.sh +++ b/scripts/container-entrypoint.sh @@ -1,6 +1,8 @@ -#!/bin/bash +#!/bin/sh set -euxo pipefail +echo ${SPLIIT_APP_NAME} v${SPLIIT_VERSION} + npx prisma migrate deploy -npm run start +npx next start diff --git a/scripts/package.sh b/scripts/package.sh new file mode 100755 index 00000000..7afe1884 --- /dev/null +++ b/scripts/package.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +source scripts/set-env.sh + +if [ ! -d "${SPLIIT_DEST_DIR}" ]; then + echo "Build folder ${SPLIIT_DEST_DIR} not found" + exit 1 +fi + +mkdir -p ${SPLIIT_DIST_FOLDER} + +tar -zcvf ./${SPLIIT_DIST_FOLDER}/${SPLIIT_RELEASE_NAME}.tar.gz -C ./${SPLIIT_TMP_DIR} ${SPLIIT_APP_NAME} diff --git a/scripts/post-build.sh b/scripts/post-build.sh new file mode 100755 index 00000000..d670a9fb --- /dev/null +++ b/scripts/post-build.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +source scripts/set-env.sh + +rm -rf ./${SPLIIT_TMP_DIR} diff --git a/scripts/pre-build.sh b/scripts/pre-build.sh new file mode 100755 index 00000000..40e22e76 --- /dev/null +++ b/scripts/pre-build.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +ENV_FILE=".env" +EXAMPLE_ENV_FILE=".env.example" +NEXT_BUILD_DIR=.next + +export SPLIIT_RELEASE_TYPE=$1 +source scripts/set-env.sh + +# Create env file if not exists +if [ ! -f "${ENV_FILE}" ]; then + echo "Missing ${ENV_FILE} file, creating it from ${EXAMPLE_ENV_FILE}" + cat "${EXAMPLE_ENV_FILE}" > ${ENV_FILE} +fi + +# Get dependencies and build +npm i +npx prisma generate +npx next build + +# Package +if [ -d ${SPLIIT_DEST_DIR} ]; then + rm -rf ${SPLIIT_DEST_DIR} +else + mkdir -p ${SPLIIT_DEST_DIR} +fi + +mv ./${NEXT_BUILD_DIR} ${SPLIIT_DEST_DIR} +rm -rf ${NEXT_BUILD_DIR} +rm -rf ${SPLIIT_DEST_DIR}/.next/cache +cp package.json package-lock.json ${SPLIIT_DEST_DIR} +if [ "${SPLIIT_RELEASE_TYPE}" = "nightly" ]; then + sed -i "s/\s*\"version\":.*/ \"version\":\"${SPLIIT_VERSION}\",/" ${SPLIIT_DEST_DIR}/package.json +fi +PROJECT_DIR=$(pwd) +cp -r ./prisma ${SPLIIT_DEST_DIR} +cp -r ./public ${SPLIIT_DEST_DIR} +cp LICENSE README.md ${SPLIIT_DEST_DIR} +cd ${SPLIIT_DEST_DIR} +npm ci --omit=dev --omit=optional --ignore-scripts +npx prisma generate +cd ${PROJECT_DIR} diff --git a/scripts/set-env.sh b/scripts/set-env.sh new file mode 100755 index 00000000..3b244821 --- /dev/null +++ b/scripts/set-env.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +export SPLIIT_NODE_VERSION="21-alpine" + +export SPLIIT_TMP_DIR=tmp +export SPLIIT_DIST_FOLDER=dist + +export SPLIIT_APP_NAME=$(node -p -e "require('./package.json').name") +export SPLIIT_COMMIT_HASH=$(git rev-parse --short HEAD) + +VERSION=$(node -p -e "require('./package.json').version") + +export SPLIIT_DEST_DIR=./${SPLIIT_TMP_DIR}/${SPLIIT_APP_NAME} + +# Get version based on release type +if [ ! -z ${SPLIIT_RELEASE_TYPE+x} ]; then + if [ "${SPLIIT_RELEASE_TYPE}" = "prod" ]; then + export SPLIIT_VERSION=${VERSION} + elif [ "${SPLIIT_RELEASE_TYPE}" = "nightly" ]; then + export SPLIIT_VERSION=${VERSION}-${SPLIIT_COMMIT_HASH} + else + echo "First argument must be either \"prod\" or \"nightly\"" + exit 1 + fi +elif [ -d ${SPLIIT_DEST_DIR} ]; then + export SPLIIT_VERSION=$(node -p -e "require('${SPLIIT_DEST_DIR}/package.json').version") +fi + +export SPLIIT_RELEASE_NAME=${SPLIIT_APP_NAME}-${SPLIIT_VERSION} From 093bd40d5290074633e17ae4ef3ee0779d9e7c01 Mon Sep 17 00:00:00 2001 From: Maxime Jacob Date: Sat, 24 Feb 2024 19:24:55 -0500 Subject: [PATCH 3/3] * Updates README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 20da3ade..3d708c09 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ If you want to contribute financially and help us keep the application free and ## Run in a container 1. Run `npm run build-image` to build the docker image from the Dockerfile -2. Copy the file `container.env.example` as `container.env` +2. Copy the file `.env.example` as `.env` 3. Run `npm run start-container` to start the postgres and the spliit2 containers 4. You can access the app by browsing to http://localhost:3000