diff --git a/.github/workflows/build_bench.yml b/.github/workflows/build_bench.yml index 257a31c1..1bc47018 100644 --- a/.github/workflows/build_bench.yml +++ b/.github/workflows/build_bench.yml @@ -38,7 +38,7 @@ jobs: run: echo "LATEST_BENCH_RELEASE=$(curl -s 'https://api.github.com/repos/frappe/bench/releases/latest' | jq -r '.tag_name')" >> "$GITHUB_ENV" - name: Build and test - uses: docker/bake-action@v7.0.0 + uses: docker/bake-action@v7.1.0 with: source: . targets: bench-test @@ -52,7 +52,7 @@ jobs: - name: Push if: ${{ github.repository == 'frappe/frappe_docker' && github.event_name != 'pull_request' }} - uses: docker/bake-action@v7.0.0 + uses: docker/bake-action@v7.1.0 with: targets: bench push: true diff --git a/.github/workflows/docker-build-push.yml b/.github/workflows/docker-build-push.yml index 376118cd..9fee1782 100644 --- a/.github/workflows/docker-build-push.yml +++ b/.github/workflows/docker-build-push.yml @@ -66,7 +66,7 @@ jobs: echo "NODE_VERSION=${{ inputs.node_version }}" >> "$GITHUB_ENV" - name: Build - uses: docker/bake-action@v7.0.0 + uses: docker/bake-action@v7.1.0 with: source: . push: true @@ -95,7 +95,7 @@ jobs: - name: Push if: ${{ inputs.push }} - uses: docker/bake-action@v7.0.0 + uses: docker/bake-action@v7.1.0 with: push: true set: "*.platform=linux/amd64,linux/arm64" diff --git a/.github/workflows/publish_docs.yml b/.github/workflows/publish_docs.yml index ab9de0ef..ffecb82c 100644 --- a/.github/workflows/publish_docs.yml +++ b/.github/workflows/publish_docs.yml @@ -32,17 +32,23 @@ jobs: - name: Checkout uses: actions/checkout@v6 - - name: Install pnpm - uses: pnpm/action-setup@v5 - with: - version: 10 - - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - cache: "pnpm" - cache-dependency-path: ./docs + + - name: Enable Corepack + run: corepack enable + + - name: Activate pnpm + run: corepack prepare pnpm@10.28.2 --activate + + - name: Show tool versions + run: | + node --version + corepack --version + pnpm --version + which pnpm - name: Install dependencies run: pnpm i --frozen-lockfile @@ -51,7 +57,7 @@ jobs: run: pnpm docs:build - name: Upload artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 with: path: docs/.vitepress/dist diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 00000000..b4afca59 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,25 @@ +# Maintainers + +This project is actively maintained by the following people. + +Maintainers are responsible for: + +- Reviewing and merging pull requests +- Managing releases +- Triaging and responding to issues +- Ensuring the overall health and direction of the project + +## Current Maintainers + +- [@revant](https://github.com/revant) +- [@DanielRadlAMR](https://github.com/DanielRadlAMR) +- [@Rocket-Quack](https://github.com/Rocket-Quack) + +## Emeritus Maintainers + +_(none)_ + +## Becoming a Maintainer + +Contributors who consistently help review pull requests, participate in issue triage, +and contribute to releases may be invited to become maintainers. diff --git a/devcontainer-example/docker-compose.yml b/devcontainer-example/docker-compose.yml index 3c23b4f3..bf3bbf72 100644 --- a/devcontainer-example/docker-compose.yml +++ b/devcontainer-example/docker-compose.yml @@ -5,7 +5,6 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --skip-character-set-client-handshake - - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 environment: MYSQL_ROOT_PASSWORD: 123 MARIADB_AUTO_UPGRADE: 1 diff --git a/development/vscode-example/settings.json b/development/vscode-example/settings.json index 1490b727..cfc5b0f7 100644 --- a/development/vscode-example/settings.json +++ b/development/vscode-example/settings.json @@ -1,3 +1,20 @@ { - "python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python" + "python.defaultInterpreterPath": "${workspaceFolder}/frappe-bench/env/bin/python", + "files.watcherExclude": { + // --- Node modules --- + "**/node_modules/**": true, + + // --- Frappe bench core dirs --- + "**/env/**": true, + "**/config/**": true, + + // --- Build artifacts --- + "**/__pycache__/**": true, + "**/*.pyc": true + }, + "files.exclude": { + "**/__pycache__": true, + "**/*.pyc": true, + "**/.git": false + } } diff --git a/docs/01-getting-started/03-arm64.md b/docs/01-getting-started/03-arm64.md index c76beb06..b9defd89 100644 --- a/docs/01-getting-started/03-arm64.md +++ b/docs/01-getting-started/03-arm64.md @@ -100,7 +100,7 @@ services: bench new-site --mariadb-user-host-login-scope=% --admin-password=admin --db-root-password=admin --install-app erpnext --set-default frontend; db: - image: mariadb:10.6 + image: mariadb:11.8 platform: linux/amd64 healthcheck: test: mysqladmin ping -h localhost --password=admin @@ -113,7 +113,6 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --skip-character-set-client-handshake - - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 environment: MYSQL_ROOT_PASSWORD: admin volumes: diff --git a/docs/02-setup/02-build-setup.md b/docs/02-setup/02-build-setup.md index 93331280..d0e6907b 100644 --- a/docs/02-setup/02-build-setup.md +++ b/docs/02-setup/02-build-setup.md @@ -7,11 +7,13 @@ This guide walks you through building Frappe images from the repository resource # Prerequisites - git -- docker or podman +- docker (Engine **v23.0+**) or podman - docker compose v2 or podman compose > Install containerization software according to the official maintainer documentation. Avoid package managers when not recommended, as they frequently cause compatibility issues. +> **Why Docker Engine v23+?** The build uses [BuildKit secrets](https://docs.docker.com/build/building/secrets/) (`--secret`) to keep `apps.json` tokens out of image layers. BuildKit is the default builder starting with Docker Engine 23.0 — older releases will fail or silently fall back to the legacy builder, which does not support secret mounts. + # Clone this repo ```bash @@ -42,23 +44,19 @@ To include custom apps in your image, create an `apps.json` file in the reposito ] ``` -Then generate a base64-encoded string from this file: - -```bash -export APPS_JSON_BASE64=$(base64 -w 0 apps.json) -``` - # Build the image Choose the appropriate build command based on your container runtime and desired image type. This example builds the `layered` image with the custom `apps.json` you created. +> **Security note:** The `apps.json` file is passed as a [BuildKit secret](https://docs.docker.com/build/building/secrets/) so that private repository tokens are **never** stored in image layer metadata. Do not use `--build-arg` for `apps.json` — build arguments are permanently visible via `docker image history`. This requires **Docker Engine v23.0+** (where BuildKit is the default builder). + `Docker`: ```bash docker build \ --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ --build-arg=FRAPPE_BRANCH=version-15 \ - --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --secret=id=apps_json,src=apps.json \ --tag=custom:15 \ --file=images/layered/Containerfile . ``` @@ -69,27 +67,27 @@ docker build \ podman build \ --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ --build-arg=FRAPPE_BRANCH=version-15 \ - --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --secret=id=apps_json,src=apps.json \ --tag=custom:15 \ --file=images/layered/Containerfile . ``` ## Build args -| Arg | Purpose | -| -------------------- | --------------------------------------------------------------------------------------------- | -| **Frappe Framework** | | -| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to https://github.com/frappe/frappe | -| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-15 | -| **Custom Apps** | | -| APPS_JSON_BASE64 | Base64-encoded JSON string from apps.json defining apps to install | -| **Dependencies** | | -| PYTHON_VERSION | Python version for the base image | -| NODE_VERSION | Node.js version | -| WKHTMLTOPDF_VERSION | wkhtmltopdf version | -| **bench only** | | -| DEBIAN_BASE | Debian base version for the bench image, defaults to `bookworm` | -| WKHTMLTOPDF_DISTRO | use the specified distro for debian package. Default is `bookworm` | +| Arg | Purpose | +| -------------------- | ----------------------------------------------------------------------------------------------- | +| **Frappe Framework** | | +| FRAPPE_PATH | Repository URL for Frappe framework source code. Defaults to | +| FRAPPE_BRANCH | Branch to use for Frappe framework. Defaults to version-15 | +| **Custom Apps** | | +| (secret) apps_json | Passed via `--secret=id=apps_json,src=apps.json`. Never use `--build-arg` for this file. | +| **Dependencies** | | +| PYTHON_VERSION | Python version for the base image | +| NODE_VERSION | Node.js version | +| WKHTMLTOPDF_VERSION | wkhtmltopdf version | +| **bench only** | | +| DEBIAN_BASE | Debian base version for the bench image, defaults to `bookworm` | +| WKHTMLTOPDF_DISTRO | use the specified distro for debian package. Default is `bookworm` | # env file diff --git a/docs/02-setup/08-single-server-nginxproxy-example.md b/docs/02-setup/08-single-server-nginxproxy-example.md index 0049e39f..4f1c212f 100644 --- a/docs/02-setup/08-single-server-nginxproxy-example.md +++ b/docs/02-setup/08-single-server-nginxproxy-example.md @@ -15,7 +15,7 @@ We will setup the following: ## Requirements -- A server that can run Docker (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD). +- A server that can run Docker Engine **v23.0+** (recommended: 2 vCPU, 4 GB RAM, 50 GB SSD). The custom-image build below uses [BuildKit secrets](https://docs.docker.com/build/building/secrets/), which require BuildKit as the default builder (Docker Engine 23.0+). - A public domain with DNS control. - Two subdomains pointing to your server IP (A/AAAA records): - `erp.your-domain.com` @@ -84,25 +84,17 @@ cat > ~/gitops/apps.json <<'EOF' EOF ``` -Generate the BASE64 value and build: +Build the image, passing `apps.json` as a [BuildKit secret](https://docs.docker.com/build/building/secrets/) so that private repo tokens are never stored in image layers. This requires **Docker Engine v23.0+**, where BuildKit is the default builder: ```shell -export APPS_JSON_BASE64=$(base64 -w 0 ~/gitops/apps.json) - docker build \ --build-arg=FRAPPE_PATH=https://github.com/frappe/frappe \ --build-arg=FRAPPE_BRANCH=version-16 \ - --build-arg=APPS_JSON_BASE64=$APPS_JSON_BASE64 \ + --secret=id=apps_json,src=$HOME/gitops/apps.json \ --tag=my-erpnext-prod-image:16.0.0 \ --file=images/layered/Containerfile . ``` -If `base64 -w 0` is not available on your system, use: - -```shell -export APPS_JSON_BASE64=$(base64 ~/gitops/apps.json | tr -d '\n') -``` - ### Configure environment Create an environment file for the bench: diff --git a/docs/06-migration/01-migrate-from-multi-image-setup.md b/docs/06-migration/01-migrate-from-multi-image-setup.md index 0fd2e344..04d8a2c1 100644 --- a/docs/06-migration/01-migrate-from-multi-image-setup.md +++ b/docs/06-migration/01-migrate-from-multi-image-setup.md @@ -114,3 +114,14 @@ create-site: # ... removed for brevity ``` + +## Upgrading from images with a nested sites/assets volume + +Previous images declared `VOLUME /home/frappe/frappe-bench/sites/assets` separately. This created an implicit nested mountpoint inside the `sites` volume, which could cause Docker to attach different anonymous volumes per container in multi-container setups. +That declaration has been removed. `sites` is now the single shared mount, consistent with the compose setup and docs. + +**After pulling the updated image:** + +- Recreate all containers (`docker compose up --force-recreate`). Without this, Docker may keep the old anonymous `sites/assets` volume + attached from before the change. +- No `bench build` is needed — this only fixes mount consistency, not the asset workflow. diff --git a/docs/package.json b/docs/package.json index 5a0481aa..153ee463 100644 --- a/docs/package.json +++ b/docs/package.json @@ -10,6 +10,7 @@ }, "pnpm": { "overrides": { + "vite": "7.3.2", "minimatch": "10.2.5", "picomatch": "4.0.4" } diff --git a/docs/pnpm-lock.yaml b/docs/pnpm-lock.yaml index 500375b9..2e0b4ee7 100644 --- a/docs/pnpm-lock.yaml +++ b/docs/pnpm-lock.yaml @@ -5,6 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: + vite: 7.3.2 minimatch: 10.2.5 picomatch: 4.0.4 @@ -412,7 +413,7 @@ packages: resolution: {integrity: sha512-bL3AxKuQySfk1iGcBsQnoRVexTPJq0Z/ixFVM8OhVJAP6ZXXXLtM7NFKWhLl30Kg7uTBqIaPXbh+nuQCuBDedg==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + vite: 7.3.2 vue: ^3.2.25 '@vue/compiler-core@3.5.30': @@ -846,8 +847,8 @@ packages: vfile@6.0.3: resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} + vite@7.3.2: + resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -1185,10 +1186,10 @@ snapshots: '@ungap/structured-clone@1.3.0': {} - '@vitejs/plugin-vue@6.0.5(vite@7.3.1)(vue@3.5.30)': + '@vitejs/plugin-vue@6.0.5(vite@7.3.2)(vue@3.5.30)': dependencies: '@rolldown/pluginutils': 1.0.0-rc.2 - vite: 7.3.1 + vite: 7.3.2 vue: 3.5.30 '@vue/compiler-core@3.5.30': @@ -1665,7 +1666,7 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite@7.3.1: + vite@7.3.2: dependencies: esbuild: 0.27.4 fdir: 6.5.0(picomatch@4.0.4) @@ -1692,7 +1693,7 @@ snapshots: '@shikijs/transformers': 3.23.0 '@shikijs/types': 3.23.0 '@types/markdown-it': 14.1.2 - '@vitejs/plugin-vue': 6.0.5(vite@7.3.1)(vue@3.5.30) + '@vitejs/plugin-vue': 6.0.5(vite@7.3.2)(vue@3.5.30) '@vue/devtools-api': 8.1.0 '@vue/shared': 3.5.30 '@vueuse/core': 14.2.1(vue@3.5.30) @@ -1701,7 +1702,7 @@ snapshots: mark.js: 8.11.1 minisearch: 7.2.0 shiki: 3.23.0 - vite: 7.3.1 + vite: 7.3.2 vue: 3.5.30 optionalDependencies: postcss: 8.5.8 diff --git a/example.env b/example.env index 2e6864aa..5471b7dc 100644 --- a/example.env +++ b/example.env @@ -1,6 +1,6 @@ # Reference: https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/04-env-variables.md -ERPNEXT_VERSION=v16.13.2 +ERPNEXT_VERSION=v16.14.0 DB_PASSWORD=123 diff --git a/images/custom/Containerfile b/images/custom/Containerfile index a8298b52..e0c5b335 100644 --- a/images/custom/Containerfile +++ b/images/custom/Containerfile @@ -113,18 +113,13 @@ RUN apt-get update \ libbz2-dev \ && rm -rf /var/lib/apt/lists/* -# apps.json includes -ARG APPS_JSON_BASE64 -RUN if [ -n "${APPS_JSON_BASE64}" ]; then \ - mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \ - fi - USER frappe ARG FRAPPE_BRANCH=version-16 ARG FRAPPE_PATH=https://github.com/frappe/frappe -RUN export APP_INSTALL_ARGS="" && \ - if [ -n "${APPS_JSON_BASE64}" ]; then \ +RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \ + export APP_INSTALL_ARGS="" && \ + if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \ export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \ fi && \ bench init ${APP_INSTALL_ARGS}\ @@ -149,7 +144,6 @@ WORKDIR /home/frappe/frappe-bench VOLUME [ \ "/home/frappe/frappe-bench/sites", \ - "/home/frappe/frappe-bench/sites/assets", \ "/home/frappe/frappe-bench/logs" \ ] diff --git a/images/layered/Containerfile b/images/layered/Containerfile index 142c487a..02bf9a20 100644 --- a/images/layered/Containerfile +++ b/images/layered/Containerfile @@ -4,18 +4,12 @@ FROM frappe/build:${FRAPPE_BRANCH} AS builder ARG FRAPPE_BRANCH=version-16 ARG FRAPPE_PATH=https://github.com/frappe/frappe -ARG APPS_JSON_BASE64 - -USER root - -RUN if [ -n "${APPS_JSON_BASE64}" ]; then \ - mkdir /opt/frappe && echo "${APPS_JSON_BASE64}" | base64 -d > /opt/frappe/apps.json; \ - fi USER frappe -RUN export APP_INSTALL_ARGS="" && \ - if [ -n "${APPS_JSON_BASE64}" ]; then \ +RUN --mount=type=secret,id=apps_json,target=/opt/frappe/apps.json,uid=1000,gid=1000 \ + export APP_INSTALL_ARGS="" && \ + if [ -f /opt/frappe/apps.json ] && [ -s /opt/frappe/apps.json ]; then \ export APP_INSTALL_ARGS="--apps_path=/opt/frappe/apps.json"; \ fi && \ bench init ${APP_INSTALL_ARGS}\ @@ -40,7 +34,6 @@ WORKDIR /home/frappe/frappe-bench VOLUME [ \ "/home/frappe/frappe-bench/sites", \ - "/home/frappe/frappe-bench/sites/assets", \ "/home/frappe/frappe-bench/logs" \ ] diff --git a/images/production/Containerfile b/images/production/Containerfile index 17f1573d..4dfb8685 100644 --- a/images/production/Containerfile +++ b/images/production/Containerfile @@ -137,7 +137,6 @@ WORKDIR /home/frappe/frappe-bench VOLUME [ \ "/home/frappe/frappe-bench/sites", \ - "/home/frappe/frappe-bench/sites/assets", \ "/home/frappe/frappe-bench/logs" \ ] diff --git a/overrides/compose.mariadb-shared.yaml b/overrides/compose.mariadb-shared.yaml index f28031c0..0c815007 100644 --- a/overrides/compose.mariadb-shared.yaml +++ b/overrides/compose.mariadb-shared.yaml @@ -13,7 +13,6 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --skip-character-set-client-handshake - - --skip-innodb-read-only-compressed environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-changeit} MARIADB_AUTO_UPGRADE: 1 diff --git a/overrides/compose.mariadb.yaml b/overrides/compose.mariadb.yaml index e79288c0..94e177c3 100644 --- a/overrides/compose.mariadb.yaml +++ b/overrides/compose.mariadb.yaml @@ -20,7 +20,6 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --skip-character-set-client-handshake - - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD:-123} MARIADB_AUTO_UPGRADE: 1 diff --git a/pwd.yml b/pwd.yml index a0259f04..4cb9b474 100644 --- a/pwd.yml +++ b/pwd.yml @@ -1,6 +1,6 @@ services: backend: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -16,7 +16,7 @@ services: MARIADB_ROOT_PASSWORD: admin configurator: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -45,7 +45,7 @@ services: - logs:/home/frappe/frappe-bench/logs create-site: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -78,7 +78,7 @@ services: bench new-site --mariadb-user-host-login-scope='%' --admin-password=admin --db-root-username=root --db-root-password=admin --install-app erpnext --set-default frontend; db: - image: mariadb:10.6 + image: mariadb:11.8 networks: - frappe_network healthcheck: @@ -92,7 +92,6 @@ services: - --character-set-server=utf8mb4 - --collation-server=utf8mb4_unicode_ci - --skip-character-set-client-handshake - - --skip-innodb-read-only-compressed # Temporary fix for MariaDB 10.6 environment: MYSQL_ROOT_PASSWORD: admin MARIADB_ROOT_PASSWORD: admin @@ -100,7 +99,7 @@ services: - db-data:/var/lib/mysql frontend: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network depends_on: @@ -126,7 +125,7 @@ services: - "8080:8080" queue-long: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -145,7 +144,7 @@ services: FRAPPE_REDIS_QUEUE: redis://redis-queue:6379 queue-short: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -182,7 +181,7 @@ services: condition: on-failure scheduler: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: @@ -196,7 +195,7 @@ services: - logs:/home/frappe/frappe-bench/logs websocket: - image: frappe/erpnext:v16.13.2 + image: frappe/erpnext:v16.14.0 networks: - frappe_network deploy: