diff --git a/.dockerignore b/.dockerignore index 475a3451..da7e032f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,34 +1,34 @@ -*/README.md -*/.gitignore -*/node_modules - **/.DS_Store -server/**/.gitkeep +*/node_modules server/swagger.json server/.env -server/.editorconfig -server/.eslintignore -server/.npmrc -server/test + +server/dist server/logs -server/.venv +server/test server/.tmp +server/.venv server/views/* +!server/views/.gitkeep server/public/* !server/public/preloaded-favicons !server/public/favicons server/public/favicons/* +!server/public/favicons/.gitkeep !server/public/user-avatars server/public/user-avatars/* +!server/public/user-avatars/.gitkeep !server/public/background-images server/public/background-images/* +!server/public/background-images/.gitkeep server/private/* !server/private/attachments server/private/attachments/* +!server/private/attachments/.gitkeep client/dist diff --git a/.github/workflows/build-and-publish-release-package.yml b/.github/workflows/build-and-publish-release-package.yml index 8065586c..3b9d8970 100644 --- a/.github/workflows/build-and-publish-release-package.yml +++ b/.github/workflows/build-and-publish-release-package.yml @@ -21,6 +21,14 @@ jobs: - name: Update npm run: npm install npm --global + - name: Install server dependencies + run: npm install --omit=prod --ignore-scripts + working-directory: ./server + + - name: Build server + run: npm run build + working-directory: ./server + - name: Install client dependencies run: npm install --omit=dev working-directory: ./client @@ -29,24 +37,25 @@ jobs: run: DISABLE_ESLINT_PLUGIN=true npm run build working-directory: ./client - - name: Include server into dist - run: mv server dist + - name: Include licenses into dist + run: | + mv LICENSE.md server/dist + mv "LICENSES/PLANKA Community License DE.md" server/dist/LICENSE_DE.md - name: Include built client into dist run: | - mv dist/* ../dist/public - cp ../dist/public/index.html ../dist/views - working-directory: ./client - - - name: Include LICENSE.md, README.md, SECURITY.md into dist - run: mv LICENSE.md README.md SECURITY.md dist + mv ../../client/dist/* public + cp public/index.html views + working-directory: ./server/dist - name: Create release package run: | mv dist planka zip -r planka-prebuild.zip planka + working-directory: ./server - name: Publish release package - run: gh release upload ${{ github.event.release.tag_name }} planka-prebuild.zip env: GH_TOKEN: ${{ github.token }} + run: gh release upload ${{ github.event.release.tag_name }} planka-prebuild.zip + working-directory: ./server diff --git a/.gitignore b/.gitignore index 9d3a0d82..aadc4576 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -node_modules docker-compose.override.yml - .idea .DS_Store +node_modules + # Prevent another lockfile than package-lock.json (npm) from being created # If some case you are using pnpm or yarn, don't forget to generate npm lockfile # before commiting your code by running: diff --git a/Dockerfile b/Dockerfile index da623800..05a99269 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,19 @@ -FROM node:22-alpine AS server-dependencies +# Stage 1: Server build +FROM node:22-alpine AS server RUN apk -U upgrade \ && apk add build-base python3 --no-cache WORKDIR /app -COPY server/package.json server/package-lock.json server/requirements.txt ./ -COPY server/patches ./patches +COPY server . RUN npm install npm --global \ - && npm install --omit=dev + && npm install \ + && npm run build \ + && npm prune --production +# Stage 2: Client build FROM node:22 AS client WORKDIR /app @@ -18,10 +21,10 @@ WORKDIR /app COPY client . RUN npm install npm --global \ - && npm install --omit=dev - -RUN DISABLE_ESLINT_PLUGIN=true npm run build + && npm install --omit=dev \ + && DISABLE_ESLINT_PLUGIN=true npm run build +# Stage 3: Final image FROM node:22-alpine RUN apk -U upgrade \ @@ -31,18 +34,20 @@ RUN apk -U upgrade \ USER node WORKDIR /app -COPY --chown=node:node server . +COPY --chown=node:node LICENSE.md . +COPY --chown=node:node ["LICENSES/PLANKA Community License DE.md", "LICENSE_DE.md"] + +COPY --from=server --chown=node:node /app/node_modules node_modules +COPY --from=server --chown=node:node /app/dist . + +COPY --from=client --chown=node:node /app/dist public +COPY --from=client --chown=node:node /app/dist/index.html views RUN python3 -m venv .venv \ && .venv/bin/pip3 install -r requirements.txt --no-cache-dir \ && mv .env.sample .env \ && npm config set update-notifier false -COPY --from=server-dependencies --chown=node:node /app/node_modules node_modules - -COPY --from=client --chown=node:node /app/dist public -COPY --from=client --chown=node:node /app/dist/index.html views - VOLUME /app/public/favicons VOLUME /app/public/user-avatars VOLUME /app/public/background-images diff --git a/package.json b/package.json index bdb3302b..9bc79a34 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "postinstall": "npm i --prefix server && npm i --prefix client", "lint": "npm run server:lint && npm run client:lint", "prepare": "husky", + "server:build": "npm run build --prefix server", "server:console": "npm run console --prefix server", "server:db:create-admin-user": "npm run db:create-admin-user --prefix server", "server:db:init": "npm run db:init --prefix server", diff --git a/server/.buildignore b/server/.buildignore new file mode 100644 index 00000000..a17d2595 --- /dev/null +++ b/server/.buildignore @@ -0,0 +1,36 @@ +**/.gitkeep +**/.DS_Store + +build.js +generate-swagger.js +nodemon.json +swagger.json +version-template.ejs +.buildignore +.editorconfig +.env +.eslintignore +.gitignore +.npmrc + +.tmp +.venv +dist +logs +node_modules +test + +views/* + +public/* +!public/preloaded-favicons +!public/favicons +public/favicons/* +!public/user-avatars +public/user-avatars/* +!public/background-images +public/background-images/* + +private/* +!private/attachments +private/attachments/* diff --git a/server/.gitignore b/server/.gitignore index 86b93dc8..d15cc679 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -131,6 +131,12 @@ lib-cov swagger.json +dist +logs + +views/* +!views/.gitkeep + public/* !public/preloaded-favicons !public/favicons @@ -147,8 +153,3 @@ private/* !private/attachments private/attachments/* !private/attachments/.gitkeep - -views/* -!views/.gitkeep - -logs diff --git a/server/build.js b/server/build.js new file mode 100644 index 00000000..eb8cf27b --- /dev/null +++ b/server/build.js @@ -0,0 +1,42 @@ +const fs = require('fs'); +const path = require('path'); +// eslint-disable-next-line import/no-extraneous-dependencies +const ignore = require('ignore'); + +const OUT_DIR = 'dist'; + +const ig = ignore(); +if (fs.existsSync('.buildignore')) { + const patterns = fs.readFileSync('.buildignore', 'utf8'); + ig.add(patterns); +} + +if (fs.existsSync(OUT_DIR)) { + fs.rmSync(OUT_DIR, { recursive: true, force: true }); +} + +fs.mkdirSync(OUT_DIR, { recursive: true }); + +const build = (src, dest) => { + const dirents = fs.readdirSync(src, { withFileTypes: true }); + + // eslint-disable-next-line no-restricted-syntax + for (const dirent of dirents) { + const srcPath = path.join(src, dirent.name); + + if (ig.ignores(srcPath)) { + continue; // eslint-disable-line no-continue + } + + const destPath = path.join(dest, dirent.name); + + if (dirent.isDirectory()) { + fs.mkdirSync(destPath, { recursive: true }); + build(srcPath, destPath); + } else { + fs.copyFileSync(srcPath, destPath); + } + } +}; + +build('./', OUT_DIR); diff --git a/server/package-lock.json b/server/package-lock.json index 43f062dc..e5b47ec0 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -48,6 +48,7 @@ "eslint-config-prettier": "^9.1.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.4", + "ignore": "^7.0.5", "mocha": "^10.8.2", "nodemon": "^3.1.11", "prettier": "3.3.3", @@ -1108,6 +1109,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", @@ -4784,6 +4795,16 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/esm": { "version": "3.2.25", "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", @@ -6015,9 +6036,9 @@ } }, "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { diff --git a/server/package.json b/server/package.json index fe0bf672..dd38ded8 100644 --- a/server/package.json +++ b/server/package.json @@ -3,6 +3,7 @@ "private": true, "main": "app.js", "scripts": { + "build": "node build.js", "console": "dotenv sails console", "db:create-admin-user": "node db/create-admin-user.js", "db:init": "node db/init.js", @@ -84,6 +85,7 @@ "eslint-config-prettier": "^9.1.2", "eslint-plugin-import": "^2.32.0", "eslint-plugin-prettier": "^5.5.4", + "ignore": "^7.0.5", "mocha": "^10.8.2", "nodemon": "^3.1.11", "prettier": "3.3.3",