From f4f0eb30dcd23bbee683f40d5353fa6bdbde9925 Mon Sep 17 00:00:00 2001 From: Opliko Date: Fri, 10 May 2024 17:10:42 +0200 Subject: [PATCH] Small docker improvements and fixes (#12335) * feat: disable uneecessary npm features disables npm audit, fund and update-notifier for a few second startup speedup * fix: make default actually defaults not forced values * feat: upgrade on container changes * feat: support changing build verb * fix: use local volumes instead of bind mounts * fix: save just the hash without any unexpected whitespace * feat: use run with mount instead of copying for cross-platfomr builds * ci: try with minimal cache * ci: don't fetch all branches * feat: bsic support for other package managers via PACKAGE_MANAGER env var * refactor: better structured entrypoint Co-authored-by: NavyStack <137406386+NavyStack@users.noreply.github.com> * ci: properly cache the node_modules mount * fix: syntax error * refactor: fine tune docker-related files * ci: docker image taging (time, latest) * fix: remove the trailing slash for correct directory path * docker: todo- use environment variables to create files * docker: fix permissions * docker: fix permissions * docker: fix stage * feat: auto-upgrade on package.json changes * fix: don't profile-gate postgres --------- Co-authored-by: NavyStack <137406386+NavyStack@users.noreply.github.com> Co-authored-by: NavyStack --- .docker/.gitkeep | 0 .docker/build/.gitkeep | 0 .docker/config/.gitkeep | 0 .docker/database/mongo/data/.gitkeep | 0 .docker/database/postgresql/data/.gitkeep | 0 .docker/database/redis/data/.gitkeep | 0 .docker/public/uploads/.gitkeep | 0 .github/workflows/docker.yml | 17 +- .gitignore | 3 +- Dockerfile | 95 +++++---- dev.Dockerfile | 76 ++++++++ docker-compose-pgsql.yml | 68 +++++++ docker-compose-redis.yml | 51 +++++ docker-compose.yml | 84 ++++---- install/docker/entrypoint.sh | 226 ++++++++++++++++++---- install/docker/setup.json | 38 ++-- src/database/mongo.js | 12 +- src/database/postgres.js | 12 +- src/database/redis.js | 8 +- 19 files changed, 542 insertions(+), 148 deletions(-) create mode 100644 .docker/.gitkeep create mode 100644 .docker/build/.gitkeep create mode 100644 .docker/config/.gitkeep create mode 100644 .docker/database/mongo/data/.gitkeep create mode 100644 .docker/database/postgresql/data/.gitkeep create mode 100644 .docker/database/redis/data/.gitkeep create mode 100644 .docker/public/uploads/.gitkeep create mode 100644 dev.Dockerfile create mode 100644 docker-compose-pgsql.yml create mode 100644 docker-compose-redis.yml diff --git a/.docker/.gitkeep b/.docker/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/build/.gitkeep b/.docker/build/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/config/.gitkeep b/.docker/config/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/database/mongo/data/.gitkeep b/.docker/database/mongo/data/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/database/postgresql/data/.gitkeep b/.docker/database/postgresql/data/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/database/redis/data/.gitkeep b/.docker/database/redis/data/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.docker/public/uploads/.gitkeep b/.docker/public/uploads/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 16fc4a0de0..ee3150384f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,8 +21,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -37,6 +35,9 @@ jobs: username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Get current date in NST + run: echo "CURRENT_DATE_NST=$(date +'%Y%m%d-%H%M%S' -d '-3 hours -30 minutes')" >> $GITHUB_ENV + - name: Docker meta id: meta uses: docker/metadata-action@v5 @@ -48,12 +49,22 @@ jobs: type=semver,pattern={{major}}.x type=raw,value=latest,enable={{is_default_branch}} type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }} + type=raw,value=${{ env.CURRENT_DATE_NST }} + flavor: | + latest=true + + - name: Cache node_modules + id: cache-node-modules + uses: actions/cache@v3 + with: + path: var-cache-node-modules + key: var-cache-node-modules-${{ hashFiles('Dockerfile', 'install/package.json') }} - name: Build and push Docker images uses: docker/build-push-action@v5 with: cache-from: type=gha - cache-to: type=gha,mode=max + cache-to: type=gha,mode=min context: . file: ./Dockerfile platforms: linux/amd64,linux/arm64,linux/arm/v7 diff --git a/.gitignore b/.gitignore index 887ef337b0..42a1b3c705 100644 --- a/.gitignore +++ b/.gitignore @@ -71,4 +71,5 @@ package-lock.json link-plugins.sh test.sh -.docker/ \ No newline at end of file +.docker/** +!**/.gitkeep \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 14f61d3ada..9778513430 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,51 +1,74 @@ -FROM --platform=$BUILDPLATFORM node:lts as npm +FROM node:lts as build -RUN mkdir -p /usr/src/build && \ - chown -R node:node /usr/src/build -WORKDIR /usr/src/build +ENV NODE_ENV=production \ + DAEMON=false \ + SILENT=false \ + USER=nodebb \ + UID=1001 \ + GID=1001 -ARG NODE_ENV -ENV NODE_ENV $NODE_ENV +WORKDIR /usr/src/app/ -COPY --chown=node:node install/package.json /usr/src/build/package.json +COPY . /usr/src/app/ -USER node +# Install corepack to allow usage of other package managers +RUN corepack enable + +# Removing unnecessary files for us +RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \; + +# Prepage package.json +RUN cp /usr/src/app/install/package.json /usr/src/app/ + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive \ + apt-get -y --no-install-recommends install \ + tini + +RUN groupadd --gid ${GID} ${USER} \ + && useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \ + && chown -R ${USER}:${USER} /usr/src/app/ + +USER ${USER} RUN npm install --omit=dev + # TODO: generate lockfiles for each package manager + ## pnpm import \ -FROM node:lts as rebuild +FROM node:lts-slim AS final -ARG BUILDPLATFORM -ARG TARGETPLATFORM +ENV NODE_ENV=production \ + DAEMON=false \ + SILENT=false \ + USER=nodebb \ + UID=1001 \ + GID=1001 -RUN mkdir -p /usr/src/build && \ - chown -R node:node /usr/src/build +WORKDIR /usr/src/app/ -COPY --from=npm /usr/src/build /usr/src/build +COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/ +COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/ -RUN if [ $BUILDPLATFORM != $TARGETPLATFORM ]; then \ - npm rebuild && \ - npm cache clean --force; fi +RUN corepack enable \ + && groupadd --gid ${GID} ${USER} \ + && useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \ + && mkdir -p /usr/src/app/logs/ /opt/config/ \ + && chown -R ${USER}:${USER} /usr/src/app/ /opt/config/ \ + && chmod +x /usr/local/bin/entrypoint.sh \ + && chmod +x /usr/local/bin/tini -FROM node:lts-slim as run +# TODO: Have docker-compose use environment variables to create files like setup.json and config.json. +# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate -ARG NODE_ENV -ENV NODE_ENV=$NODE_ENV \ - daemon=false \ - silent=false - -RUN mkdir -p /usr/src/app && \ - chown -R node:node /usr/src/app - -COPY --chown=node:node --from=rebuild /usr/src/build /usr/src/app - - -WORKDIR /usr/src/app - -USER node - -COPY --chown=node:node . /usr/src/app +USER ${USER} EXPOSE 4567 -VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config"] -ENTRYPOINT ["./install/docker/entrypoint.sh"] \ No newline at end of file + +VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"] + +# Utilising tini as our init system within the Docker container for graceful start-up and termination. +# Tini serves as an uncomplicated init system, adept at managing the reaping of zombie processes and forwarding signals. +# This approach is crucial to circumvent issues with unmanaged subprocesses and signal handling in containerised environments. +# By integrating tini, we enhance the reliability and stability of our Docker containers. +# Ensures smooth start-up and shutdown processes, and reliable, safe handling of signal processing. +ENTRYPOINT ["tini", "--", "entrypoint.sh"] \ No newline at end of file diff --git a/dev.Dockerfile b/dev.Dockerfile new file mode 100644 index 0000000000..a2b3867ed5 --- /dev/null +++ b/dev.Dockerfile @@ -0,0 +1,76 @@ +FROM node:lts AS git + +ENV USER=nodebb \ + UID=1001 \ + GID=1001 + +WORKDIR /usr/src/app/ + +RUN groupadd --gid ${GID} ${USER} \ + && useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \ + && chown -R ${USER}:${USER} /usr/src/app/ + +RUN apt-get update \ + && apt-get -y --no-install-recommends install tini + +USER ${USER} + +# Change to the git branch you want to test +RUN git clone --recurse-submodules -j8 --depth 1 https://github.com/NodeBB/NodeBB.git . + +RUN find . -mindepth 1 -maxdepth 1 -name '.*' ! -name '.' ! -name '..' -exec bash -c 'echo "Deleting {}"; rm -rf {}' \; + +FROM node:lts AS node_modules_touch + +ENV NODE_ENV=development \ + USER=nodebb \ + UID=1001 \ + GID=1001 + +WORKDIR /usr/src/app/ + +RUN corepack enable \ + && groupadd --gid ${GID} ${USER} \ + && useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \ + && chown -R ${USER}:${USER} /usr/src/app/ + +COPY --from=git --chown=${USER}:${USER} /usr/src/app/install/package.json /usr/src/app/ + +USER ${USER} + +RUN npm install + +FROM node:lts-slim AS final + +ENV NODE_ENV=development \ + DAEMON=false \ + SILENT=false \ + USER=nodebb \ + UID=1001 \ + GID=1001 + +WORKDIR /usr/src/app/ + +COPY --from=build --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/install/docker/setup.json /usr/src/app/ +COPY --from=build --chown=${USER}:${USER} /usr/bin/tini /usr/src/app/install/docker/entrypoint.sh /usr/local/bin/ +COPY --from=node_modules_touch --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/ +COPY --from=git --chown=${USER}:${USER} /usr/src/app/ /usr/src/app/ + +RUN corepack enable \ + && groupadd --gid ${GID} ${USER} \ + && useradd --uid ${UID} --gid ${GID} --home-dir /usr/src/app/ --shell /bin/bash ${USER} \ + && mkdir -p /usr/src/app/logs/ /opt/config/ \ + && chown -R ${USER}:${USER} /usr/src/app/ /opt/config/ \ + && chmod +x /usr/local/bin/entrypoint.sh \ + && chmod +x /usr/local/bin/tini + +# TODO: Have docker-compose use environment variables to create files like setup.json and config.json. +# COPY --from=hairyhenderson/gomplate:stable /gomplate /usr/local/bin/gomplate + +USER ${USER} + +EXPOSE 4567 + +VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config/"] + +ENTRYPOINT ["tini", "--", "entrypoint.sh"] \ No newline at end of file diff --git a/docker-compose-pgsql.yml b/docker-compose-pgsql.yml new file mode 100644 index 0000000000..579bb70a44 --- /dev/null +++ b/docker-compose-pgsql.yml @@ -0,0 +1,68 @@ +version: '3.8' + +services: + nodebb: + build: . + # image: ghcr.io/nodebb/nodebb:latest + restart: unless-stopped + ports: + - '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want + volumes: + - nodebb-build:/usr/src/app/build + - nodebb-uploads:/usr/src/app/public/uploads + - nodebb-config:/opt/config + - ./install/docker/setup.json:/usr/src/app/setup.json + + postgres: + image: postgres:16.1-alpine + restart: unless-stopped + environment: + POSTGRES_USER: nodebb + POSTGRES_PASSWORD: nodebb + POSTGRES_DB: nodebb + volumes: + - postgres-data:/var/lib/postgresql/data + + redis: + image: redis:7.2.3-alpine + restart: unless-stopped + command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning'] + # command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF + volumes: + - redis-data:/data + +volumes: + postgres-data: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/database/postgresql/data + + redis-data: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/database/redis + + nodebb-build: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/build + + nodebb-uploads: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/public/uploads + + nodebb-config: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/config diff --git a/docker-compose-redis.yml b/docker-compose-redis.yml new file mode 100644 index 0000000000..c6d543d7c1 --- /dev/null +++ b/docker-compose-redis.yml @@ -0,0 +1,51 @@ +version: '3.8' + +services: + nodebb: + build: . + # image: ghcr.io/nodebb/nodebb:latest + restart: unless-stopped + ports: + - '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want + volumes: + - nodebb-build:/usr/src/app/build + - nodebb-uploads:/usr/src/app/public/uploads + - nodebb-config:/opt/config + - ./install/docker/setup.json:/usr/src/app/setup.json + + redis: + image: redis:7.2.3-alpine + restart: unless-stopped + command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning'] + # command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF + volumes: + - redis-data:/data + +volumes: + redis-data: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/database/redis + + nodebb-build: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/build + + nodebb-uploads: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/public/uploads + + nodebb-config: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/config diff --git a/docker-compose.yml b/docker-compose.yml index fe20dcdc18..d9b793b8c8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,53 +3,69 @@ version: '3.8' services: nodebb: build: . + # image: ghcr.io/nodebb/nodebb:latest restart: unless-stopped ports: - - "4567:4567/tcp" # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want - # uncomment if you want to use another container as a reverse proxy - # expose: - # - 4567 + - '4567:4567' # comment this out if you don't want to expose NodeBB to the host, or change the first number to any port you want volumes: - - ./.docker/build:/usr/src/app/build - - ./.docker/public/uploads:/usr/src/app/public/uploads - - ./.docker:/opt/config + - nodebb-build:/usr/src/app/build + - nodebb-uploads:/usr/src/app/public/uploads + - nodebb-config:/opt/config - ./install/docker/setup.json:/usr/src/app/setup.json + mongo: - image: "mongo:7-jammy" + image: 'mongo:7-jammy' restart: unless-stopped - expose: - - "27017" + ports: + - '27017:27017' environment: MONGO_INITDB_ROOT_USERNAME: nodebb MONGO_INITDB_ROOT_PASSWORD: nodebb MONGO_INITDB_DATABASE: nodebb volumes: - - ./.docker/database/mongo/config:/etc/mongo - - ./.docker/database/mongo/data:/data/db + - mongo-data:/data/db - ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js - profiles: - - mongo - postgres: - image: postgres:16.2-alpine - restart: unless-stopped - expose: - - "5432" - environment: - POSTGRES_USER: nodebb - POSTGRES_PASSWORD: nodebb - POSTGRES_DB: nodebb - volumes: - - ./.docker/database/postgresql/data:/var/lib/postgresql/data - profiles: - - postgres + redis: image: redis:7.2.3-alpine restart: unless-stopped - command: ["redis-server", "--appendonly", "yes", "--loglevel", "warning"] - # command: ["redis-server", "--save", "60", "1", "--loglevel", "warning"] # uncomment if you want to use snapshotting instead of AOF - expose: - - "6379" + command: ['redis-server', '--appendonly', 'yes', '--loglevel', 'warning'] + # command: ['redis-server', '--save', '60', '1', '--loglevel', 'warning'] # uncomment if you want to use snapshotting instead of AOF volumes: - - ./.docker/database/redis:/data - profiles: - - redis \ No newline at end of file + - redis-data:/data + +volumes: + mongo-data: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/database/mongo/data + + redis-data: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/database/redis + + nodebb-build: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/build + + nodebb-uploads: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/public/uploads + + nodebb-config: + driver: local + driver_opts: + o: bind + type: none + device: ./.docker/config diff --git a/install/docker/entrypoint.sh b/install/docker/entrypoint.sh index e5b2036321..fd0359192c 100755 --- a/install/docker/entrypoint.sh +++ b/install/docker/entrypoint.sh @@ -1,46 +1,192 @@ #!/bin/bash -export CONFIG_DIR="${CONFIG_DIR:-/opt/config}" -export CONFIG=$CONFIG_DIR/config.json -export FORCE_BUILD_BEFORE_START="${FORCE_BUILD_BEFORE_START:-false}" +set -e -# Supported verbs: install (web install), setup (interactive CLI session). Default: web install -# TODO: constraint it using a hash set (or hash table) -export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}" -# Setup variable for backward compatibility, default: -export SETUP="${SETUP:-}" +# Function to set default values for environment variables +set_defaults() { + export CONFIG_DIR="${CONFIG_DIR:-/opt/config}" + export CONFIG="$CONFIG_DIR/config.json" + export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}" + export START_BUILD="${START_BUILD:-false}" + export SETUP="${SETUP:-}" + export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}" + export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}" +} -mkdir -p $CONFIG_DIR - -# if the folder is mounted as a volume this can fail, the check below is to ensure there is still write access -chmod -fR 760 $CONFIG_DIR 2> /dev/null - -if [[ ! -w $CONFIG_DIR ]]; then - echo "panic: no write permission for $CONFIG_DIR" - exit 1 -fi - -[[ -f $CONFIG_DIR/package.json ]] || cp install/package.json $CONFIG_DIR/package.json -[[ -f $CONFIG_DIR/package-lock.json ]] || touch $CONFIG_DIR/package-lock.json - -ln -fs $CONFIG_DIR/package.json package.json -ln -fs $CONFIG_DIR/package-lock.json package-lock.json - -npm install --omit=dev - -if [[ -n $SETUP ]]; then - echo "Setup environmental variable detected" - echo "Starting setup session" - ./nodebb setup --config=$CONFIG -elif [ -f $CONFIG ]; then - echo "Config file exist at $CONFIG, assuming it is a valid config" - echo "Starting forum" - if [ "$FORCE_BUILD_BEFORE_START" = true ]; then - ./nodebb build --config=$CONFIG +# Function to check if a directory exists and is writable +check_directory() { + local dir="$1" + if [ ! -d "$dir" ]; then + echo "Error: Directory $dir does not exist. Creating..." + mkdir -p "$dir" || { + echo "Error: Failed to create directory $dir" + exit 1 + } fi - ./nodebb start --config=$CONFIG -else - echo "Config file not found at $CONFIG" + if [ ! -w "$dir" ]; then + echo "Error: No write permission for directory $dir" + exit 1 + fi +} + +# Function to copy or link package.json and lock files based on package manager +copy_or_link_files() { + local src_dir="$1" + local dest_dir="$2" + local package_manager="$3" + local lock_file + + case "$package_manager" in + yarn) lock_file="yarn.lock" ;; + npm) lock_file="package-lock.json" ;; + pnpm) lock_file="pnpm-lock.yaml" ;; + *) + echo "Unknown package manager: $package_manager" + exit 1 + ;; + esac + + # Check if source and destination files are the same + if [ "$(realpath "$src_dir/package.json")" != "$(realpath "$dest_dir/package.json")" ]; then + cp "$src_dir/package.json" "$dest_dir/package.json" + fi + + if [ "$(realpath "$src_dir/$lock_file")" != "$(realpath "$dest_dir/$lock_file")" ]; then + cp "$src_dir/$lock_file" "$dest_dir/$lock_file" + fi + + # Remove unnecessary lock files in src_dir + rm -f "$src_dir/"{yarn.lock,package-lock.json,pnpm-lock.yaml} + + # Symbolically link the copied files in src_dir to dest_dir + ln -fs "$dest_dir/package.json" "$src_dir/package.json" + ln -fs "$dest_dir/$lock_file" "$src_dir/$lock_file" +} + +# Function to install dependencies using pnpm +install_dependencies() { + case "$PACKAGE_MANAGER" in + yarn) yarn install || { + echo "Failed to install dependencies with yarn" + exit 1 + } ;; + npm) npm install || { + echo "Failed to install dependencies with npm" + exit 1 + } ;; + pnpm) pnpm install || { + echo "Failed to install dependencies with pnpm" + exit 1 + } ;; + *) + echo "Unknown package manager: $PACKAGE_MANAGER" + exit 1 + ;; + esac +} + +# Function to start setup session +start_setup_session() { + local config="$1" + echo "Starting setup session" + exec /usr/src/app/nodebb setup --config="$config" +} + +# Handle building and upgrading NodeBB +build_forum() { + local config="$1" + local start_build="$2" + local package_hash=$(md5sum install/package.json | head -c 32) + if [ package_hash = "$(cat $CONFIG_DIR/install_hash.md5)" ]; then + echo "package.json was updated. Upgrading..." + /usr/src/app/nodebb upgrade --config="$config" || { + echo "Failed to build NodeBB. Exiting..." + exit 1 + } + elif [ "$start_build" = true ]; then + echo "Build before start is enabled. Building..." + /usr/src/app/nodebb "${NODEBB_BUILD_VERB}" --config="$config" || { + echo "Failed to build NodeBB. Exiting..." + exit 1 + } + else + echo "No changes in package.json. Skipping build..." + return + fi + echo -n $package_hash > $CONFIG_DIR/install_hash.md5 +} + + +# Function to start forum +start_forum() { + local config="$1" + local start_build="$2" + + build_forum "$config" "$start_build" + + case "$PACKAGE_MANAGER" in + yarn) + yarn start --config="$config" --no-silent --no-daemon || { + echo "Failed to start forum with yarn" + exit 1 + } + ;; + npm) + npm start -- --config="$config" --no-silent --no-daemon || { + echo "Failed to start forum with npm" + exit 1 + } + ;; + pnpm) + pnpm start -- --config="$config" --no-silent --no-daemon || { + echo "Failed to start forum with pnpm" + exit 1 + } + ;; + *) + echo "Unknown package manager: $PACKAGE_MANAGER" + exit 1 + ;; + esac +} + +# Function to start installation session +start_installation_session() { + local nodebb_init_verb="$1" + local config="$2" + + echo "Config file not found at $config" echo "Starting installation session" - ./nodebb "${NODEBB_INIT_VERB}" --config=$CONFIG -fi \ No newline at end of file + exec /usr/src/app/nodebb "$nodebb_init_verb" --config="$config" +} + +# Function for debugging and logging +debug_log() { + local message="$1" + echo "DEBUG: $message" +} + +# Main function +main() { + set_defaults + check_directory "$CONFIG_DIR" + copy_or_link_files /usr/src/app "$CONFIG_DIR" "$PACKAGE_MANAGER" + install_dependencies + + debug_log "PACKAGE_MANAGER: $PACKAGE_MANAGER" + debug_log "CONFIG location: $CONFIG" + debug_log "START_BUILD: $START_BUILD" + + if [ -n "$SETUP" ]; then + start_setup_session "$CONFIG" + fi + + if [ -f "$CONFIG" ]; then + start_forum "$CONFIG" "$START_BUILD" + else + start_installation_session "$NODEBB_INIT_VERB" "$CONFIG" + fi +} + +# Execute main function +main "$@" diff --git a/install/docker/setup.json b/install/docker/setup.json index 3fad840593..a9fa9017bb 100644 --- a/install/docker/setup.json +++ b/install/docker/setup.json @@ -1,21 +1,23 @@ { - "mongo": { - "host": "mongo", - "port": 27017, - "database": "nodebb", - "username": "nodebb", - "password": "nodebb" - }, - "redis": { - "host": "redis", - "port": 6379, - "database": 0 - }, - "postgres": { - "host": "postgres", - "port": 5432, - "database": "nodebb", - "username": "nodebb", - "password": "nodebb" + "defaults": { + "mongo": { + "host": "mongo", + "port": 27017, + "database": "nodebb", + "username": "nodebb", + "password": "nodebb" + }, + "redis": { + "host": "redis", + "port": 6379, + "database": 0 + }, + "postgres": { + "host": "postgres", + "port": 5432, + "database": "nodebb", + "username": "nodebb", + "password": "nodebb" + } } } \ No newline at end of file diff --git a/src/database/mongo.js b/src/database/mongo.js index 753bd13427..131916cb6f 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -22,31 +22,31 @@ mongoModule.questions = [ { name: 'mongo:uri', description: 'MongoDB connection URI: (leave blank if you wish to specify host, port, username/password and database individually)\nFormat: mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', - default: nconf.get('mongo:uri') || '', + default: nconf.get('mongo:uri') || nconf.get('defaults:mongo:uri') || '', hideOnWebInstall: true, }, { name: 'mongo:host', description: 'Host IP or address of your MongoDB instance', - default: nconf.get('mongo:host') || '127.0.0.1', + default: nconf.get('mongo:host') || nconf.get('defaults:mongo:host') || '127.0.0.1', ask: isUriNotSpecified, }, { name: 'mongo:port', description: 'Host port of your MongoDB instance', - default: nconf.get('mongo:port') || 27017, + default: nconf.get('mongo:port') || nconf.get('defaults:mongo:port') || 27017, ask: isUriNotSpecified, }, { name: 'mongo:username', description: 'MongoDB username', - default: nconf.get('mongo:username') || '', + default: nconf.get('mongo:username') || nconf.get('defaults:mongo:username') || '', ask: isUriNotSpecified, }, { name: 'mongo:password', description: 'Password of your MongoDB database', - default: nconf.get('mongo:password') || '', + default: nconf.get('mongo:password') || nconf.get('defaults:mongo:password') || '', hidden: true, ask: isUriNotSpecified, before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; }, @@ -54,7 +54,7 @@ mongoModule.questions = [ { name: 'mongo:database', description: 'MongoDB database name', - default: nconf.get('mongo:database') || 'nodebb', + default: nconf.get('mongo:database') || nconf.get('defaults:mongo:database') || 'nodebb', ask: isUriNotSpecified, }, ]; diff --git a/src/database/postgres.js b/src/database/postgres.js index 6e034b7d0c..84a456ca86 100644 --- a/src/database/postgres.js +++ b/src/database/postgres.js @@ -13,34 +13,34 @@ postgresModule.questions = [ { name: 'postgres:host', description: 'Host IP or address of your PostgreSQL instance', - default: nconf.get('postgres:host') || '127.0.0.1', + default: nconf.get('postgres:host') || nconf.get('defaults:postgres:host') || '127.0.0.1', }, { name: 'postgres:port', description: 'Host port of your PostgreSQL instance', - default: nconf.get('postgres:port') || 5432, + default: nconf.get('postgres:port') || nconf.get('defaults:postgres:port') || 5432, }, { name: 'postgres:username', description: 'PostgreSQL username', - default: nconf.get('postgres:username') || '', + default: nconf.get('postgres:username') || nconf.get('defaults:postgres:username') || '', }, { name: 'postgres:password', description: 'Password of your PostgreSQL database', hidden: true, - default: nconf.get('postgres:password') || '', + default: nconf.get('postgres:password') || nconf.get('defaults:postgres:password') || '', before: function (value) { value = value || nconf.get('postgres:password') || ''; return value; }, }, { name: 'postgres:database', description: 'PostgreSQL database name', - default: nconf.get('postgres:database') || 'nodebb', + default: nconf.get('postgres:database') || nconf.get('defaults:postgres:database') || 'nodebb', }, { name: 'postgres:ssl', description: 'Enable SSL for PostgreSQL database access', - default: nconf.get('postgres:ssl') || false, + default: nconf.get('postgres:ssl') || nconf.get('defaults:postgres:ssl') || false, }, ]; diff --git a/src/database/redis.js b/src/database/redis.js index 22d13cc9e4..15a40c9fb4 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -11,24 +11,24 @@ redisModule.questions = [ { name: 'redis:host', description: 'Host IP or address of your Redis instance', - default: nconf.get('redis:host') || '127.0.0.1', + default: nconf.get('redis:host') || nconf.get('defaults:redis:host') || '127.0.0.1', }, { name: 'redis:port', description: 'Host port of your Redis instance', - default: nconf.get('redis:port') || 6379, + default: nconf.get('redis:port') || nconf.get('defaults:redis:port') || 6379, }, { name: 'redis:password', description: 'Password of your Redis database', hidden: true, - default: nconf.get('redis:password') || '', + default: nconf.get('redis:password') || nconf.get('defaults:redis:password') || '', before: function (value) { value = value || nconf.get('redis:password') || ''; return value; }, }, { name: 'redis:database', description: 'Which database to use (0..n)', - default: nconf.get('redis:database') || 0, + default: nconf.get('redis:database') || nconf.get('defaults:redis:database') || 0, }, ];