mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-10-29 10:06:13 +01:00
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 <navystack@askfront.com>
This commit is contained in:
0
.docker/.gitkeep
Normal file
0
.docker/.gitkeep
Normal file
0
.docker/build/.gitkeep
Normal file
0
.docker/build/.gitkeep
Normal file
0
.docker/config/.gitkeep
Normal file
0
.docker/config/.gitkeep
Normal file
0
.docker/database/mongo/data/.gitkeep
Normal file
0
.docker/database/mongo/data/.gitkeep
Normal file
0
.docker/database/postgresql/data/.gitkeep
Normal file
0
.docker/database/postgresql/data/.gitkeep
Normal file
0
.docker/database/redis/data/.gitkeep
Normal file
0
.docker/database/redis/data/.gitkeep
Normal file
0
.docker/public/uploads/.gitkeep
Normal file
0
.docker/public/uploads/.gitkeep
Normal file
17
.github/workflows/docker.yml
vendored
17
.github/workflows/docker.yml
vendored
@@ -21,8 +21,6 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up QEMU
|
- name: Set up QEMU
|
||||||
uses: docker/setup-qemu-action@v3
|
uses: docker/setup-qemu-action@v3
|
||||||
@@ -37,6 +35,9 @@ jobs:
|
|||||||
username: ${{ github.repository_owner }}
|
username: ${{ github.repository_owner }}
|
||||||
password: ${{ secrets.GITHUB_TOKEN }}
|
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
|
- name: Docker meta
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
@@ -48,12 +49,22 @@ jobs:
|
|||||||
type=semver,pattern={{major}}.x
|
type=semver,pattern={{major}}.x
|
||||||
type=raw,value=latest,enable={{is_default_branch}}
|
type=raw,value=latest,enable={{is_default_branch}}
|
||||||
type=ref,event=branch,enable=${{ github.event.repository.default_branch != github.ref }}
|
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
|
- name: Build and push Docker images
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v5
|
||||||
with:
|
with:
|
||||||
cache-from: type=gha
|
cache-from: type=gha
|
||||||
cache-to: type=gha,mode=max
|
cache-to: type=gha,mode=min
|
||||||
context: .
|
context: .
|
||||||
file: ./Dockerfile
|
file: ./Dockerfile
|
||||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -71,4 +71,5 @@ package-lock.json
|
|||||||
link-plugins.sh
|
link-plugins.sh
|
||||||
test.sh
|
test.sh
|
||||||
|
|
||||||
.docker/
|
.docker/**
|
||||||
|
!**/.gitkeep
|
||||||
95
Dockerfile
95
Dockerfile
@@ -1,51 +1,74 @@
|
|||||||
FROM --platform=$BUILDPLATFORM node:lts as npm
|
FROM node:lts as build
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/build && \
|
ENV NODE_ENV=production \
|
||||||
chown -R node:node /usr/src/build
|
DAEMON=false \
|
||||||
WORKDIR /usr/src/build
|
SILENT=false \
|
||||||
|
USER=nodebb \
|
||||||
|
UID=1001 \
|
||||||
|
GID=1001
|
||||||
|
|
||||||
ARG NODE_ENV
|
WORKDIR /usr/src/app/
|
||||||
ENV NODE_ENV $NODE_ENV
|
|
||||||
|
|
||||||
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
|
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
|
ENV NODE_ENV=production \
|
||||||
ARG TARGETPLATFORM
|
DAEMON=false \
|
||||||
|
SILENT=false \
|
||||||
|
USER=nodebb \
|
||||||
|
UID=1001 \
|
||||||
|
GID=1001
|
||||||
|
|
||||||
RUN mkdir -p /usr/src/build && \
|
WORKDIR /usr/src/app/
|
||||||
chown -R node:node /usr/src/build
|
|
||||||
|
|
||||||
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 \
|
RUN corepack enable \
|
||||||
npm rebuild && \
|
&& groupadd --gid ${GID} ${USER} \
|
||||||
npm cache clean --force; fi
|
&& 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
|
USER ${USER}
|
||||||
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
|
|
||||||
|
|
||||||
EXPOSE 4567
|
EXPOSE 4567
|
||||||
VOLUME ["/usr/src/app/node_modules", "/usr/src/app/build", "/usr/src/app/public/uploads", "/opt/config"]
|
|
||||||
ENTRYPOINT ["./install/docker/entrypoint.sh"]
|
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"]
|
||||||
76
dev.Dockerfile
Normal file
76
dev.Dockerfile
Normal file
@@ -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"]
|
||||||
68
docker-compose-pgsql.yml
Normal file
68
docker-compose-pgsql.yml
Normal file
@@ -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
|
||||||
51
docker-compose-redis.yml
Normal file
51
docker-compose-redis.yml
Normal file
@@ -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
|
||||||
@@ -3,53 +3,69 @@ version: '3.8'
|
|||||||
services:
|
services:
|
||||||
nodebb:
|
nodebb:
|
||||||
build: .
|
build: .
|
||||||
|
# image: ghcr.io/nodebb/nodebb:latest
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
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
|
- '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
|
||||||
# uncomment if you want to use another container as a reverse proxy
|
|
||||||
# expose:
|
|
||||||
# - 4567
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./.docker/build:/usr/src/app/build
|
- nodebb-build:/usr/src/app/build
|
||||||
- ./.docker/public/uploads:/usr/src/app/public/uploads
|
- nodebb-uploads:/usr/src/app/public/uploads
|
||||||
- ./.docker:/opt/config
|
- nodebb-config:/opt/config
|
||||||
- ./install/docker/setup.json:/usr/src/app/setup.json
|
- ./install/docker/setup.json:/usr/src/app/setup.json
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
image: "mongo:7-jammy"
|
image: 'mongo:7-jammy'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
expose:
|
ports:
|
||||||
- "27017"
|
- '27017:27017'
|
||||||
environment:
|
environment:
|
||||||
MONGO_INITDB_ROOT_USERNAME: nodebb
|
MONGO_INITDB_ROOT_USERNAME: nodebb
|
||||||
MONGO_INITDB_ROOT_PASSWORD: nodebb
|
MONGO_INITDB_ROOT_PASSWORD: nodebb
|
||||||
MONGO_INITDB_DATABASE: nodebb
|
MONGO_INITDB_DATABASE: nodebb
|
||||||
volumes:
|
volumes:
|
||||||
- ./.docker/database/mongo/config:/etc/mongo
|
- mongo-data:/data/db
|
||||||
- ./.docker/database/mongo/data:/data/db
|
|
||||||
- ./install/docker/mongodb-user-init.js:/docker-entrypoint-initdb.d/user-init.js
|
- ./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:
|
redis:
|
||||||
image: redis:7.2.3-alpine
|
image: redis:7.2.3-alpine
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: ["redis-server", "--appendonly", "yes", "--loglevel", "warning"]
|
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
|
# command: ['redis-server', '--save', '60', '1', '--loglevel', 'warning'] # uncomment if you want to use snapshotting instead of AOF
|
||||||
expose:
|
|
||||||
- "6379"
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./.docker/database/redis:/data
|
- redis-data:/data
|
||||||
profiles:
|
|
||||||
- redis
|
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
|
||||||
|
|||||||
@@ -1,46 +1,192 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Function to set default values for environment variables
|
||||||
|
set_defaults() {
|
||||||
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
|
export CONFIG_DIR="${CONFIG_DIR:-/opt/config}"
|
||||||
export CONFIG=$CONFIG_DIR/config.json
|
export CONFIG="$CONFIG_DIR/config.json"
|
||||||
export FORCE_BUILD_BEFORE_START="${FORCE_BUILD_BEFORE_START:-false}"
|
|
||||||
|
|
||||||
# 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}"
|
export NODEBB_INIT_VERB="${NODEBB_INIT_VERB:-install}"
|
||||||
# Setup variable for backward compatibility, default: <empty>
|
export START_BUILD="${START_BUILD:-false}"
|
||||||
export SETUP="${SETUP:-}"
|
export SETUP="${SETUP:-}"
|
||||||
|
export PACKAGE_MANAGER="${PACKAGE_MANAGER:-npm}"
|
||||||
|
export OVERRIDE_UPDATE_LOCK="${OVERRIDE_UPDATE_LOCK:-false}"
|
||||||
|
}
|
||||||
|
|
||||||
mkdir -p $CONFIG_DIR
|
# Function to check if a directory exists and is writable
|
||||||
|
check_directory() {
|
||||||
# if the folder is mounted as a volume this can fail, the check below is to ensure there is still write access
|
local dir="$1"
|
||||||
chmod -fR 760 $CONFIG_DIR 2> /dev/null
|
if [ ! -d "$dir" ]; then
|
||||||
|
echo "Error: Directory $dir does not exist. Creating..."
|
||||||
if [[ ! -w $CONFIG_DIR ]]; then
|
mkdir -p "$dir" || {
|
||||||
echo "panic: no write permission for $CONFIG_DIR"
|
echo "Error: Failed to create directory $dir"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
if [ ! -w "$dir" ]; then
|
||||||
|
echo "Error: No write permission for directory $dir"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
[[ -f $CONFIG_DIR/package.json ]] || cp install/package.json $CONFIG_DIR/package.json
|
# Function to copy or link package.json and lock files based on package manager
|
||||||
[[ -f $CONFIG_DIR/package-lock.json ]] || touch $CONFIG_DIR/package-lock.json
|
copy_or_link_files() {
|
||||||
|
local src_dir="$1"
|
||||||
|
local dest_dir="$2"
|
||||||
|
local package_manager="$3"
|
||||||
|
local lock_file
|
||||||
|
|
||||||
ln -fs $CONFIG_DIR/package.json package.json
|
case "$package_manager" in
|
||||||
ln -fs $CONFIG_DIR/package-lock.json package-lock.json
|
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
|
||||||
|
|
||||||
npm install --omit=dev
|
# 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 [[ -n $SETUP ]]; then
|
if [ "$(realpath "$src_dir/$lock_file")" != "$(realpath "$dest_dir/$lock_file")" ]; then
|
||||||
echo "Setup environmental variable detected"
|
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"
|
echo "Starting setup session"
|
||||||
./nodebb setup --config=$CONFIG
|
exec /usr/src/app/nodebb setup --config="$config"
|
||||||
elif [ -f $CONFIG ]; then
|
}
|
||||||
echo "Config file exist at $CONFIG, assuming it is a valid config"
|
|
||||||
echo "Starting forum"
|
# Handle building and upgrading NodeBB
|
||||||
if [ "$FORCE_BUILD_BEFORE_START" = true ]; then
|
build_forum() {
|
||||||
./nodebb build --config=$CONFIG
|
local config="$1"
|
||||||
fi
|
local start_build="$2"
|
||||||
./nodebb start --config=$CONFIG
|
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
|
else
|
||||||
echo "Config file not found at $CONFIG"
|
echo "No changes in package.json. Skipping build..."
|
||||||
echo "Starting installation session"
|
return
|
||||||
./nodebb "${NODEBB_INIT_VERB}" --config=$CONFIG
|
|
||||||
fi
|
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"
|
||||||
|
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 "$@"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"defaults": {
|
||||||
"mongo": {
|
"mongo": {
|
||||||
"host": "mongo",
|
"host": "mongo",
|
||||||
"port": 27017,
|
"port": 27017,
|
||||||
@@ -19,3 +20,4 @@
|
|||||||
"password": "nodebb"
|
"password": "nodebb"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -22,31 +22,31 @@ mongoModule.questions = [
|
|||||||
{
|
{
|
||||||
name: 'mongo:uri',
|
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]]',
|
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,
|
hideOnWebInstall: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mongo:host',
|
name: 'mongo:host',
|
||||||
description: 'Host IP or address of your MongoDB instance',
|
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,
|
ask: isUriNotSpecified,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mongo:port',
|
name: 'mongo:port',
|
||||||
description: 'Host port of your MongoDB instance',
|
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,
|
ask: isUriNotSpecified,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mongo:username',
|
name: 'mongo:username',
|
||||||
description: 'MongoDB username',
|
description: 'MongoDB username',
|
||||||
default: nconf.get('mongo:username') || '',
|
default: nconf.get('mongo:username') || nconf.get('defaults:mongo:username') || '',
|
||||||
ask: isUriNotSpecified,
|
ask: isUriNotSpecified,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'mongo:password',
|
name: 'mongo:password',
|
||||||
description: 'Password of your MongoDB database',
|
description: 'Password of your MongoDB database',
|
||||||
default: nconf.get('mongo:password') || '',
|
default: nconf.get('mongo:password') || nconf.get('defaults:mongo:password') || '',
|
||||||
hidden: true,
|
hidden: true,
|
||||||
ask: isUriNotSpecified,
|
ask: isUriNotSpecified,
|
||||||
before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; },
|
before: function (value) { value = value || nconf.get('mongo:password') || ''; return value; },
|
||||||
@@ -54,7 +54,7 @@ mongoModule.questions = [
|
|||||||
{
|
{
|
||||||
name: 'mongo:database',
|
name: 'mongo:database',
|
||||||
description: 'MongoDB database name',
|
description: 'MongoDB database name',
|
||||||
default: nconf.get('mongo:database') || 'nodebb',
|
default: nconf.get('mongo:database') || nconf.get('defaults:mongo:database') || 'nodebb',
|
||||||
ask: isUriNotSpecified,
|
ask: isUriNotSpecified,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,34 +13,34 @@ postgresModule.questions = [
|
|||||||
{
|
{
|
||||||
name: 'postgres:host',
|
name: 'postgres:host',
|
||||||
description: 'Host IP or address of your PostgreSQL instance',
|
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',
|
name: 'postgres:port',
|
||||||
description: 'Host port of your PostgreSQL instance',
|
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',
|
name: 'postgres:username',
|
||||||
description: 'PostgreSQL username',
|
description: 'PostgreSQL username',
|
||||||
default: nconf.get('postgres:username') || '',
|
default: nconf.get('postgres:username') || nconf.get('defaults:postgres:username') || '',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'postgres:password',
|
name: 'postgres:password',
|
||||||
description: 'Password of your PostgreSQL database',
|
description: 'Password of your PostgreSQL database',
|
||||||
hidden: true,
|
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; },
|
before: function (value) { value = value || nconf.get('postgres:password') || ''; return value; },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'postgres:database',
|
name: 'postgres:database',
|
||||||
description: 'PostgreSQL database name',
|
description: 'PostgreSQL database name',
|
||||||
default: nconf.get('postgres:database') || 'nodebb',
|
default: nconf.get('postgres:database') || nconf.get('defaults:postgres:database') || 'nodebb',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'postgres:ssl',
|
name: 'postgres:ssl',
|
||||||
description: 'Enable SSL for PostgreSQL database access',
|
description: 'Enable SSL for PostgreSQL database access',
|
||||||
default: nconf.get('postgres:ssl') || false,
|
default: nconf.get('postgres:ssl') || nconf.get('defaults:postgres:ssl') || false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -11,24 +11,24 @@ redisModule.questions = [
|
|||||||
{
|
{
|
||||||
name: 'redis:host',
|
name: 'redis:host',
|
||||||
description: 'Host IP or address of your Redis instance',
|
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',
|
name: 'redis:port',
|
||||||
description: 'Host port of your Redis instance',
|
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',
|
name: 'redis:password',
|
||||||
description: 'Password of your Redis database',
|
description: 'Password of your Redis database',
|
||||||
hidden: true,
|
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; },
|
before: function (value) { value = value || nconf.get('redis:password') || ''; return value; },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'redis:database',
|
name: 'redis:database',
|
||||||
description: 'Which database to use (0..n)',
|
description: 'Which database to use (0..n)',
|
||||||
default: nconf.get('redis:database') || 0,
|
default: nconf.get('redis:database') || nconf.get('defaults:redis:database') || 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user