From baf2a11d57508695dd616126378d0eb5c86a2691 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:00:21 +0000 Subject: [PATCH 1/4] Initial plan From ee589dca069155c78c7ed0efba2f3f0edad71c19 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:03:41 +0000 Subject: [PATCH 2/4] Add Dockerfile, entrypoint scripts, CI workflow, README, LICENSE Co-authored-by: charabaruk <130681+charabaruk@users.noreply.github.com> --- .dockerignore | 2 + .github/workflows/docker-publish.yml | 73 ++++++++++++++++++++++++++++ Dockerfile | 62 +++++++++++++++++++++++ LICENSE | 23 +++++++++ README.md | 54 +++++++++++++++++++- cmd.sh | 4 ++ setup.sh | 38 +++++++++++++++ smoke/SmokeTest.php | 7 +++ 8 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-publish.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100755 cmd.sh create mode 100755 setup.sh create mode 100644 smoke/SmokeTest.php diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..f4b1198 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,2 @@ +.git +.github diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..f083cc1 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,73 @@ +name: Build and publish Docker images + +on: + push: + branches: [main] + schedule: + - cron: "0 3 * * 1" + workflow_dispatch: + pull_request: + +permissions: + contents: read + packages: write + +env: + IMAGE: ghcr.io/charabaruk/nextcloud-docker-testing + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + version: [24, 25, 26, 27, 28, 29, 30, 31] + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Compute tags + id: tags + run: | + echo "image=${IMAGE,,}" >> "$GITHUB_OUTPUT" + echo "sha=$(echo "${GITHUB_SHA}" | cut -c1-7)" >> "$GITHUB_OUTPUT" + + - name: Build image (local) + uses: docker/build-push-action@v6 + with: + context: . + build-args: NEXTCLOUD_VERSION=${{ matrix.version }} + load: true + push: false + tags: ${{ steps.tags.outputs.image }}:stable${{ matrix.version }} + cache-from: type=gha,scope=stable${{ matrix.version }} + cache-to: type=gha,mode=max,scope=stable${{ matrix.version }} + + - name: Smoke test + run: | + IMG=${{ steps.tags.outputs.image }}:stable${{ matrix.version }} + docker run --rm "$IMG" phpunit --version + docker run --rm "$IMG" php occ status | tee /dev/stderr | grep -E 'installed: *true' + docker run --rm -v "$PWD/smoke:/smoke:ro" "$IMG" \ + phpunit --bootstrap tests/bootstrap.php /smoke/SmokeTest.php + + - name: Push image + if: github.event_name != 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + build-args: NEXTCLOUD_VERSION=${{ matrix.version }} + push: true + tags: | + ${{ steps.tags.outputs.image }}:stable${{ matrix.version }} + ${{ steps.tags.outputs.image }}:stable${{ matrix.version }}-${{ steps.tags.outputs.sha }} + cache-from: type=gha,scope=stable${{ matrix.version }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..cb4cbd3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,62 @@ +ARG NEXTCLOUD_VERSION=30 +FROM nextcloud:${NEXTCLOUD_VERSION} + +# Re-declare after FROM so it is available in this build stage +ARG NEXTCLOUD_VERSION + +RUN apt-get update \ + && apt-get install -y --no-install-recommends git unzip sqlite3 libzip-dev \ + && rm -rf /var/lib/apt/lists/* + +# The official image already ships the PHP extensions Nextcloud needs +# (gd, zip, pdo_sqlite, intl, apcu, ...) for the matching PHP version. +# Only add zip if a base image happens to lack it. +RUN php -m | grep -qi '^zip$' || docker-php-ext-install zip + +# Composer (only if the base image does not already provide it) +COPY --from=composer:2 /usr/bin/composer /tmp/composer +RUN command -v composer >/dev/null 2>&1 || install -m 0755 /tmp/composer /usr/local/bin/composer; \ + rm -f /tmp/composer + +# PHP memory limit (zz- prefix so it is loaded after the base image's own setting) +ENV PHP_MEMORY_LIMIT=2048M +RUN echo 'memory_limit=2048M' > /usr/local/etc/php/conf.d/zz-memory-limit.ini + +# Replace the packaged Nextcloud with the matching server source tree +RUN find /var/www/html -mindepth 1 -delete \ + && git clone --depth=1 --branch "stable${NEXTCLOUD_VERSION}" \ + --recurse-submodules --shallow-submodules \ + https://github.com/nextcloud/server.git /var/www/html \ + && git config --system --add safe.directory /var/www/html + +WORKDIR /var/www/html + +# Server dependencies including dev dependencies. Newer branches keep PHPUnit +# in vendor-bin/phpunit, older ones in the root composer.json. +ENV COMPOSER_ALLOW_SUPERUSER=1 +RUN composer install --no-interaction --no-progress \ + && if [ -f vendor-bin/phpunit/composer.json ]; then \ + composer install --no-interaction --no-progress --working-dir=vendor-bin/phpunit; \ + fi \ + && for p in vendor/bin/phpunit vendor-bin/phpunit/vendor/bin/phpunit; do \ + if [ -e "$p" ]; then ln -sf "/var/www/html/$p" /usr/local/bin/phpunit; break; fi; \ + done \ + && phpunit --version \ + && test -f tests/bootstrap.php + +# Record the apps that ship with the server so setup.sh only enables mounted ones +RUN ls -1 apps > /opt/bundled-apps.txt + +# Run Apache as www-data on an unprivileged port +RUN sed -i 's/Listen 80$/Listen 8080/; s/Listen 443$/Listen 8443/' /etc/apache2/ports.conf \ + && sed -i 's///' /etc/apache2/sites-available/*.conf \ + && mkdir -p /var/run/apache2 /var/lock/apache2 /var/log/apache2 \ + && chown -R www-data:www-data /var/www/html /var/run/apache2 /var/lock/apache2 /var/log/apache2 + +COPY setup.sh cmd.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/setup.sh /usr/local/bin/cmd.sh + +USER www-data +EXPOSE 8080 +ENTRYPOINT ["/usr/local/bin/setup.sh"] +CMD ["/usr/local/bin/cmd.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c7b6953 --- /dev/null +++ b/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2026 charabaruk + +This project was written from scratch; it is not derived from any code in SteKoe/nextcloud-docker-testing. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 04f2dc4..b74374a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,54 @@ # nextcloud-docker-testing -Docker images for testing Nextcloud apps + +Docker images for developing and testing Nextcloud apps with PHPUnit. Each image is based on the +official `nextcloud:N` image and contains the matching Nextcloud server source (branch `stableN` of +[nextcloud/server](https://github.com/nextcloud/server), with submodules) in `/var/www/html`, +including `tests/bootstrap.php` and the composer dev dependencies (PHPUnit etc.). + +This is a from-scratch replacement for the unmaintained SteKoe/nextcloud-docker-testing. + +## Tags + +`ghcr.io/charabaruk/nextcloud-docker-testing:stable24` … `:stable31`. Every build is also pushed as +`stableN-`. Images are rebuilt weekly. No version is currently excluded. + +## Usage + +The entrypoint (`setup.sh`) installs Nextcloud with SQLite (admin/admin, override with +`NEXTCLOUD_ADMIN_USER` / `NEXTCLOUD_ADMIN_PASSWORD`), enables every app in `/var/www/html/apps` +that is not bundled with the server, and then executes the given command. It is idempotent. The +container runs as `www-data`; the default command starts Apache on port 8080. + +```sh +docker run --rm ghcr.io/charabaruk/nextcloud-docker-testing:stable30 phpunit --version + +docker run --rm -v "$PWD:/var/www/html/apps/myapp" \ + ghcr.io/charabaruk/nextcloud-docker-testing:stable30 \ + phpunit --bootstrap tests/bootstrap.php apps/myapp/tests/ +``` + +docker-compose (working directory is `/var/www/html`): + +```yaml +services: + app: + image: ghcr.io/charabaruk/nextcloud-docker-testing:stable30 + volumes: + - .:/var/www/html/apps/customproperties + - ~/.nextcloud:/.nextcloud +``` + +```sh +docker compose run app phpunit --bootstrap tests/bootstrap.php apps/customproperties/tests/. +``` + +## Adding a Nextcloud version + +Check that `nextcloud:N` exists on Docker Hub and that branch `stableN` exists in nextcloud/server, +then add `N` to `matrix.version` in `.github/workflows/docker-publish.yml`. If a version cannot be +built, remove it from the matrix and document the reason here. + +## Making the package public + +On GitHub: your profile/org → Packages → `nextcloud-docker-testing` → Package settings → Danger +Zone → Change visibility → Public. Other repositories' CI can then pull without authentication. diff --git a/cmd.sh b/cmd.sh new file mode 100755 index 0000000..79d3e23 --- /dev/null +++ b/cmd.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# Default command: start Apache in the foreground. +set -eu +exec apache2-foreground diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..03121e4 --- /dev/null +++ b/setup.sh @@ -0,0 +1,38 @@ +#!/bin/sh +# Installs Nextcloud (SQLite) if needed, enables mounted apps, then runs the command. +set -eu + +cd /var/www/html + +# Make sure we run as www-data (re-exec if started as root) +if [ "$(id -u)" = "0" ]; then + chown -R www-data:www-data /var/www/html/config /var/www/html/data 2>/dev/null || true + exec su -s /bin/sh www-data -c 'exec /usr/local/bin/setup.sh "$@"' -- sh "$@" +fi + +mkdir -p /var/www/html/data /var/www/html/config 2>/dev/null || true + +ADMIN_USER="${NEXTCLOUD_ADMIN_USER:-admin}" +ADMIN_PASSWORD="${NEXTCLOUD_ADMIN_PASSWORD:-admin}" + +if php occ status 2>/dev/null | grep -Eq 'installed: *true'; then + echo "Nextcloud already installed" +else + php occ maintenance:install \ + --database sqlite \ + --admin-user "$ADMIN_USER" \ + --admin-pass "$ADMIN_PASSWORD" \ + --no-interaction +fi + +# Enable apps that are not part of the server (e.g. mounted volumes) +for dir in apps/*/; do + app="$(basename "$dir")" + [ -f "$dir/appinfo/info.xml" ] || continue + if grep -qx "$app" /opt/bundled-apps.txt; then + continue + fi + php occ app:enable "$app" || echo "Warning: could not enable app $app" >&2 +done + +exec "$@" diff --git a/smoke/SmokeTest.php b/smoke/SmokeTest.php new file mode 100644 index 0000000..6ed0fbe --- /dev/null +++ b/smoke/SmokeTest.php @@ -0,0 +1,7 @@ +assertTrue(class_exists('OC')); + } +} From eabfdc9ea7cc4cd96b135c6bb1423155532cc61b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:21:35 +0000 Subject: [PATCH 3/4] Fix builds for 24-27 (apt archive fallback, standalone PHPUnit); use full name in LICENSE Co-authored-by: charabaruk <130681+charabaruk@users.noreply.github.com> --- Dockerfile | 33 +++++++++++++++++++++++++-------- LICENSE | 2 +- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index cb4cbd3..1bf1e6e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,9 +4,19 @@ FROM nextcloud:${NEXTCLOUD_VERSION} # Re-declare after FROM so it is available in this build stage ARG NEXTCLOUD_VERSION -RUN apt-get update \ - && apt-get install -y --no-install-recommends git unzip sqlite3 libzip-dev \ - && rm -rf /var/lib/apt/lists/* +# Debian bullseye (nextcloud:24/25 bases) is EOL: its security repository is gone from +# deb.debian.org, so fall back to archive.debian.org when the normal install fails. +RUN PKGS="git unzip sqlite3 libzip-dev"; \ + (apt-get update && apt-get install -y --no-install-recommends $PKGS) \ + || ( . /etc/os-release \ + && printf '%s\n' \ + "deb http://archive.debian.org/debian ${VERSION_CODENAME} main" \ + "deb http://archive.debian.org/debian-security ${VERSION_CODENAME}-security main" \ + > /etc/apt/sources.list \ + && rm -f /etc/apt/sources.list.d/debian.sources \ + && echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99archive \ + && apt-get update && apt-get install -y --no-install-recommends $PKGS ); \ + rc=$?; rm -rf /var/lib/apt/lists/*; exit $rc # The official image already ships the PHP extensions Nextcloud needs # (gd, zip, pdo_sqlite, intl, apcu, ...) for the matching PHP version. @@ -31,16 +41,23 @@ RUN find /var/www/html -mindepth 1 -delete \ WORKDIR /var/www/html -# Server dependencies including dev dependencies. Newer branches keep PHPUnit -# in vendor-bin/phpunit, older ones in the root composer.json. +# Server dependencies including dev dependencies. Branches 28+ keep PHPUnit in +# vendor-bin/phpunit; 24-27 do not depend on PHPUnit at all, so it is installed +# separately in /opt/phpunit. ENV COMPOSER_ALLOW_SUPERUSER=1 RUN composer install --no-interaction --no-progress \ && if [ -f vendor-bin/phpunit/composer.json ]; then \ composer install --no-interaction --no-progress --working-dir=vendor-bin/phpunit; \ fi \ - && for p in vendor/bin/phpunit vendor-bin/phpunit/vendor/bin/phpunit; do \ - if [ -e "$p" ]; then ln -sf "/var/www/html/$p" /usr/local/bin/phpunit; break; fi; \ - done \ + && if [ -e vendor-bin/phpunit/vendor/bin/phpunit ]; then \ + ln -sf /var/www/html/vendor-bin/phpunit/vendor/bin/phpunit /usr/local/bin/phpunit; \ + elif [ -e vendor/bin/phpunit ]; then \ + ln -sf /var/www/html/vendor/bin/phpunit /usr/local/bin/phpunit; \ + else \ + mkdir /opt/phpunit \ + && composer require --working-dir=/opt/phpunit --no-interaction --no-progress "phpunit/phpunit:^9.6" \ + && ln -sf /opt/phpunit/vendor/bin/phpunit /usr/local/bin/phpunit; \ + fi \ && phpunit --version \ && test -f tests/bootstrap.php diff --git a/LICENSE b/LICENSE index c7b6953..ebac466 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 charabaruk +Copyright (c) 2026 Chris Charabaruk This project was written from scratch; it is not derived from any code in SteKoe/nextcloud-docker-testing. From 54df78e0d4abddbd93705e292cdf8d72af26c252 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 2 Oct 2026 17:33:22 +0000 Subject: [PATCH 4/4] Fix nextcloud:25 build: archive.debian.org has no bullseye-security suite Co-authored-by: charabaruk <130681+charabaruk@users.noreply.github.com> --- Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile b/Dockerfile index 1bf1e6e..56225e7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,17 +5,17 @@ FROM nextcloud:${NEXTCLOUD_VERSION} ARG NEXTCLOUD_VERSION # Debian bullseye (nextcloud:24/25 bases) is EOL: its security repository is gone from -# deb.debian.org, so fall back to archive.debian.org when the normal install fails. -RUN PKGS="git unzip sqlite3 libzip-dev"; \ +# deb.debian.org and archive.debian.org has no bullseye-security suite, so fall back to the +# archived main suite only (allowing downgrades of packages the base image took from security). +RUN PKGS="git unzip"; \ (apt-get update && apt-get install -y --no-install-recommends $PKGS) \ || ( . /etc/os-release \ - && printf '%s\n' \ - "deb http://archive.debian.org/debian ${VERSION_CODENAME} main" \ - "deb http://archive.debian.org/debian-security ${VERSION_CODENAME}-security main" \ + && printf '%s\n' "deb http://archive.debian.org/debian ${VERSION_CODENAME} main" \ > /etc/apt/sources.list \ && rm -f /etc/apt/sources.list.d/debian.sources \ && echo 'Acquire::Check-Valid-Until "false";' > /etc/apt/apt.conf.d/99archive \ - && apt-get update && apt-get install -y --no-install-recommends $PKGS ); \ + && apt-get update \ + && apt-get install -y --no-install-recommends --allow-downgrades $PKGS ); \ rc=$?; rm -rf /var/lib/apt/lists/*; exit $rc # The official image already ships the PHP extensions Nextcloud needs