mirror of
https://github.com/ether/etherpad-lite.git
synced 2026-07-18 00:57:55 +00:00
* fix(docker): share corepack cache so etherpad user can resolve pnpm (#7687) PR #7674 switched the Dockerfile from `npm install -g pnpm` to corepack and `corepack prepare pnpm@${PnpmVersion} --activate`. The activate step runs as root and writes its lastKnownGood pin into `$COREPACK_HOME`, which defaults to `~/.cache/node/corepack` — i.e. a per-user path. The Dockerfile then drops to `USER etherpad` and later runs `bin/installLocalPlugins.sh`, which invokes `pnpm` as etherpad. With an empty per-user corepack cache and no shared activation file, corepack re-resolves pnpm and (for forks/configs without a `packageManager` pin matching the activated version) can fall back to "latest" from the registry — pulling `pnpm@10.33.4` instead of the requested 11.x and failing the workspace's `engines.pnpm` check. Pin `COREPACK_HOME=/opt/corepack` and chown it to etherpad after the prepare step. Both root and etherpad now share the same lastKnownGood file and tarball cache, so etherpad inherits the activated pnpm without hitting the registry again. Verified end-to-end: - `docker build --target development --build-arg ETHERPAD_LOCAL_PLUGINS=ep_test` with a stub local plugin runs `installLocalPlugins.sh` cleanly: `Done in 16.6s using pnpm v11.0.6`. - `docker run ... pnpm --version` as etherpad reports 11.0.6 from the shared cache — no "Unsupported environment" error. Note: corepack still emits a one-time "about to download" line at runtime because `corepack prepare pnpm@11.0.6` resolves to the highest matching patch (11.0.8) at build time while the project's `packageManager` field pins exactly 11.0.6. That's a follow-up — the download succeeds non-interactively and the engine check passes. Fixes #7687. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(docker): action Qodo PR review (#7687 follow-up) - Replace hard-coded /opt/corepack with ${COREPACK_HOME} in mkdir/chown so the env var stays the single source of truth (Qodo: "COREPACK_HOME path duplication"). - Add a build-test-local-plugin job to .github/workflows/docker.yml that builds the development target with a stub ETHERPAD_LOCAL_PLUGINS so the original failure mode (corepack/pnpm cache invisible across the USER switch) cannot silently regress (Qodo: "COREPACK_HOME fix lacks test"). The job is small — `docker build` only, no run — and uses the shared GHA buildx cache. Verified: same docker build + `docker run pnpm --version` flow on the variable form gives identical output (pnpm 11.0.6 from the etherpad-owned cache). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
341 lines
11 KiB
YAML
341 lines
11 KiB
YAML
name: "Docker"
|
|
on:
|
|
pull_request:
|
|
paths-ignore:
|
|
- 'doc/**'
|
|
push:
|
|
branches:
|
|
- 'develop'
|
|
paths-ignore:
|
|
- 'doc/**'
|
|
tags:
|
|
- 'v?[0-9]+.[0-9]+.[0-9]+'
|
|
env:
|
|
TEST_TAG: etherpad/etherpad:test
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build-test:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
env:
|
|
PNPM_HOME: ~/.pnpm-store
|
|
steps:
|
|
-
|
|
name: Check out
|
|
uses: actions/checkout@v6
|
|
with:
|
|
path: etherpad
|
|
-
|
|
name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
-
|
|
name: Build and export to Docker
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: ./etherpad
|
|
target: production
|
|
load: true
|
|
tags: ${{ env.TEST_TAG }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
- uses: actions/cache@v5
|
|
name: Cache pnpm store
|
|
with:
|
|
path: ${{ env.PNPM_HOME }}
|
|
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
restore-keys: |
|
|
${{ runner.os }}-pnpm-store-
|
|
- uses: pnpm/action-setup@v6
|
|
name: Install pnpm
|
|
with:
|
|
run_install: false
|
|
# Repo is checked out into ./etherpad, so the action can't find
|
|
# packageManager in a root package.json.
|
|
package_json_file: etherpad/package.json
|
|
- name: Use Node.js
|
|
uses: actions/setup-node@v6
|
|
with:
|
|
node-version: 22
|
|
cache: pnpm
|
|
cache-dependency-path: etherpad/pnpm-lock.yaml
|
|
-
|
|
name: Test
|
|
working-directory: etherpad
|
|
run: |
|
|
docker run --rm -d -p 9001:9001 --name test ${{ env.TEST_TAG }}
|
|
./bin/installDeps.sh
|
|
docker logs -f test &
|
|
while true; do
|
|
echo "Waiting for Docker container to start..."
|
|
status=$(docker container inspect -f '{{.State.Health.Status}}' test) || exit 1
|
|
case ${status} in
|
|
healthy) break;;
|
|
starting) sleep 2;;
|
|
*) printf %s\\n "unexpected status: ${status}" >&2; exit 1;;
|
|
esac
|
|
done
|
|
(cd src && pnpm run test-container)
|
|
git clean -dxf .
|
|
|
|
build-test-local-plugin:
|
|
# Regression coverage for #7687: the Docker image's
|
|
# `bin/installLocalPlugins.sh` step runs as the `etherpad` user and
|
|
# invokes pnpm via the corepack shim. A previous corepack/cache bug
|
|
# made that path fail when ETHERPAD_LOCAL_PLUGINS was set. This job
|
|
# builds the development target with a stub local plugin so the
|
|
# regression cannot silently come back.
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
steps:
|
|
-
|
|
name: Check out
|
|
uses: actions/checkout@v6
|
|
with:
|
|
path: etherpad
|
|
-
|
|
name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
-
|
|
name: Stub a local plugin
|
|
run: |
|
|
mkdir -p etherpad/local_plugins/ep_test_corepack
|
|
cat > etherpad/local_plugins/ep_test_corepack/package.json <<'EOF'
|
|
{
|
|
"name": "ep_test_corepack",
|
|
"version": "0.0.1",
|
|
"description": "regression-test stub for ether/etherpad#7687",
|
|
"main": "index.js"
|
|
}
|
|
EOF
|
|
cat > etherpad/local_plugins/ep_test_corepack/index.js <<'EOF'
|
|
exports.placeholder = true;
|
|
EOF
|
|
-
|
|
name: Build with ETHERPAD_LOCAL_PLUGINS (must succeed)
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: ./etherpad
|
|
target: development
|
|
load: false
|
|
build-args: |
|
|
ETHERPAD_LOCAL_PLUGINS=ep_test_corepack
|
|
cache-from: type=gha
|
|
|
|
build-test-db-drivers:
|
|
# Spinning up MySQL + Postgres + cross-driver smoke is expensive; only
|
|
# run it on pushes to develop (and tagged release pushes), not on every
|
|
# feature-branch PR.
|
|
if: github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
services:
|
|
mysql:
|
|
image: mysql:8
|
|
env:
|
|
MYSQL_ROOT_PASSWORD: password
|
|
MYSQL_DATABASE: etherpad
|
|
MYSQL_USER: etherpad
|
|
MYSQL_PASSWORD: password
|
|
ports:
|
|
- 3306:3306
|
|
options: >-
|
|
--health-cmd="mysqladmin ping -h 127.0.0.1 -uroot -ppassword"
|
|
--health-interval=5s
|
|
--health-timeout=5s
|
|
--health-retries=20
|
|
postgres:
|
|
image: postgres:16
|
|
env:
|
|
POSTGRES_DB: etherpad
|
|
POSTGRES_USER: etherpad
|
|
POSTGRES_PASSWORD: password
|
|
ports:
|
|
- 5432:5432
|
|
options: >-
|
|
--health-cmd="pg_isready -U etherpad -d etherpad"
|
|
--health-interval=5s
|
|
--health-timeout=5s
|
|
--health-retries=20
|
|
steps:
|
|
-
|
|
name: Check out
|
|
uses: actions/checkout@v6
|
|
with:
|
|
path: etherpad
|
|
-
|
|
name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
-
|
|
name: Build production image
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: ./etherpad
|
|
target: production
|
|
load: true
|
|
tags: ${{ env.TEST_TAG }}
|
|
cache-from: type=gha
|
|
cache-to: type=gha,mode=max
|
|
-
|
|
name: Driver presence test (all 10 ueberdb2 drivers must resolve)
|
|
run: |
|
|
docker run --rm -w /opt/etherpad-lite/src "$TEST_TAG" node -e "
|
|
const mods = [
|
|
'@elastic/elasticsearch','cassandra-driver','mongodb','mssql',
|
|
'mysql2','nano','pg','redis','rethinkdb','surrealdb'
|
|
];
|
|
let fail = false;
|
|
for (const m of mods) {
|
|
try { require(m); console.log('ok', m); }
|
|
catch (e) { console.error('MISSING', m, e.message); fail = true; }
|
|
}
|
|
if (fail) process.exit(1);
|
|
"
|
|
-
|
|
name: MySQL smoke — start Etherpad against mysql service
|
|
run: |
|
|
docker run --rm -d \
|
|
--network host \
|
|
-e NODE_ENV=production \
|
|
-e ADMIN_PASSWORD=admin \
|
|
-e DB_TYPE=mysql \
|
|
-e DB_HOST=127.0.0.1 \
|
|
-e DB_PORT=3306 \
|
|
-e DB_NAME=etherpad \
|
|
-e DB_USER=etherpad \
|
|
-e DB_PASS=password \
|
|
-e DB_CHARSET=utf8mb4 \
|
|
-e DEFAULT_PAD_TEXT="Test " \
|
|
--name et-mysql "$TEST_TAG"
|
|
for i in $(seq 1 60); do
|
|
if curl -sf http://127.0.0.1:9001/ >/dev/null; then
|
|
echo "mysql smoke: Etherpad is serving /"
|
|
docker rm -f et-mysql
|
|
exit 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
echo "mysql smoke: timed out waiting for Etherpad"
|
|
docker logs et-mysql || true
|
|
docker rm -f et-mysql || true
|
|
exit 1
|
|
-
|
|
name: Postgres smoke — start Etherpad against postgres service
|
|
run: |
|
|
docker run --rm -d \
|
|
--network host \
|
|
-e NODE_ENV=production \
|
|
-e ADMIN_PASSWORD=admin \
|
|
-e DB_TYPE=postgres \
|
|
-e DB_HOST=127.0.0.1 \
|
|
-e DB_PORT=5432 \
|
|
-e DB_NAME=etherpad \
|
|
-e DB_USER=etherpad \
|
|
-e DB_PASS=password \
|
|
-e DEFAULT_PAD_TEXT="Test " \
|
|
--name et-postgres "$TEST_TAG"
|
|
for i in $(seq 1 60); do
|
|
if curl -sf http://127.0.0.1:9001/ >/dev/null; then
|
|
echo "postgres smoke: Etherpad is serving /"
|
|
docker rm -f et-postgres
|
|
exit 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
echo "postgres smoke: timed out waiting for Etherpad"
|
|
docker logs et-postgres || true
|
|
docker rm -f et-postgres || true
|
|
exit 1
|
|
|
|
publish:
|
|
needs: [build-test, build-test-db-drivers]
|
|
if: github.event_name == 'push'
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
steps:
|
|
-
|
|
name: Check out
|
|
uses: actions/checkout@v6
|
|
with:
|
|
path: etherpad
|
|
-
|
|
name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v4
|
|
-
|
|
name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v4
|
|
-
|
|
name: Docker meta
|
|
id: meta
|
|
uses: docker/metadata-action@v6
|
|
with:
|
|
images: |
|
|
etherpad/etherpad
|
|
ghcr.io/ether/etherpad
|
|
tags: |
|
|
type=ref,event=branch
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=semver,pattern={{major}}
|
|
-
|
|
name: Log in to Docker Hub
|
|
uses: docker/login-action@v4
|
|
with:
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
-
|
|
name: Log in to GHCR
|
|
uses: docker/login-action@v4
|
|
with:
|
|
registry: ghcr.io
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
-
|
|
name: Build and push
|
|
id: build-docker
|
|
uses: docker/build-push-action@v7
|
|
with:
|
|
context: ./etherpad
|
|
target: production
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
cache-from: type=gha
|
|
- name: Update repo description
|
|
uses: peter-evans/dockerhub-description@v5
|
|
if: github.ref == 'refs/heads/master'
|
|
with:
|
|
readme-filepath: ./etherpad/README.md
|
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
repository: etherpad/etherpad
|
|
enable-url-completion: true
|
|
- name: Check out ether-charts
|
|
if: github.ref == 'refs/heads/develop'
|
|
uses: actions/checkout@v6
|
|
with:
|
|
path: ether-charts
|
|
repository: ether/ether-charts
|
|
token: ${{ secrets.ETHER_CHART_TOKEN }}
|
|
- name: Update tag in values-dev.yaml
|
|
if: success() && github.ref == 'refs/heads/develop'
|
|
working-directory: ether-charts
|
|
run: |
|
|
sed -i 's/tag: ".*"/tag: "${{ steps.build-docker.outputs.digest }}"/' values-dev.yaml
|
|
- name: Commit and push changes
|
|
working-directory: ether-charts
|
|
if: success() && github.ref == 'refs/heads/develop'
|
|
run: |
|
|
git config --global user.name 'github-actions[bot]'
|
|
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
|
|
git add values-dev.yaml
|
|
git commit -m 'Update develop image tag'
|
|
git push
|