diff --git a/.docker/app/Dockerfile b/.docker/app/Dockerfile index 6ca75aa..3a94750 100644 --- a/.docker/app/Dockerfile +++ b/.docker/app/Dockerfile @@ -1,12 +1,32 @@ -ARG NEXTCLOUD_VERSION=stable-fpm +ARG NEXTCLOUD_BASE_IMAGE=nextcloud:stable-fpm +ARG NEXTCLOUD_SOURCE=release -FROM nextcloud:${NEXTCLOUD_VERSION} +FROM mlocati/php-extension-installer:2.12.0 AS php-extension-installer + +FROM ${NEXTCLOUD_BASE_IMAGE} AS release + +FROM ${NEXTCLOUD_BASE_IMAGE} AS daily +ARG NEXTCLOUD_DAILY_URL +RUN set -eux; \ + test -n "${NEXTCLOUD_DAILY_URL}"; \ + archive_name="$(basename "${NEXTCLOUD_DAILY_URL}")"; \ + curl -fsSL "${NEXTCLOUD_DAILY_URL}" -o "/tmp/${archive_name}"; \ + curl -fsSL "${NEXTCLOUD_DAILY_URL}.sha512" -o "/tmp/${archive_name}.sha512"; \ + expected_sha512="$(awk -v archive="${archive_name}" '$2 == archive { print $1; exit }' "/tmp/${archive_name}.sha512")"; \ + test -n "${expected_sha512}"; \ + echo "${expected_sha512} /tmp/${archive_name}" | sha512sum -c -; \ + rm -rf /usr/src/nextcloud; \ + tar -xjf "/tmp/${archive_name}" -C /usr/src/; \ + rm -f "/tmp/${archive_name}" "/tmp/${archive_name}.sha512"; \ + rm -rf /usr/src/nextcloud/updater; \ + mkdir -p /usr/src/nextcloud/data /usr/src/nextcloud/custom_apps; \ + chmod +x /usr/src/nextcloud/occ + +FROM ${NEXTCLOUD_SOURCE} AS runtime RUN apt-get update \ - && apt-get install -y \ - gzip \ + && apt-get install -y --no-install-recommends \ locales \ - postgresql-client \ poppler-utils \ && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ && locale-gen \ @@ -16,10 +36,9 @@ ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en ENV LC_ALL=en_US.UTF-8 -ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ -RUN chmod uga+x /usr/local/bin/install-php-extensions && sync \ - && install-php-extensions \ - bz2 \ - imagick +COPY --from=php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/install-php-extensions +RUN install-php-extensions \ + bz2 \ + && rm /usr/local/bin/install-php-extensions COPY config/php.ini /usr/local/etc/php/conf.d/ diff --git a/.docker/app/Dockerfile.35 b/.docker/app/Dockerfile.35 deleted file mode 100644 index b3b078b..0000000 --- a/.docker/app/Dockerfile.35 +++ /dev/null @@ -1,40 +0,0 @@ -ARG NEXTCLOUD_BASE_IMAGE=nextcloud:34-fpm - -FROM ${NEXTCLOUD_BASE_IMAGE} - -ARG NEXTCLOUD_DAILY_URL=https://download.nextcloud.com/server/daily/latest-master.tar.bz2 - -RUN set -eux; \ - curl -fsSL "${NEXTCLOUD_DAILY_URL}" -o /tmp/nextcloud.tar.bz2; \ - curl -fsSL "${NEXTCLOUD_DAILY_URL}.sha512" -o /tmp/nextcloud.tar.bz2.sha512; \ - cd /tmp; \ - expected_sha512="$(awk '$2 == "latest-master.tar.bz2" { print $1 }' nextcloud.tar.bz2.sha512)"; \ - test -n "${expected_sha512}"; \ - echo "${expected_sha512} nextcloud.tar.bz2" | sha512sum -c -; \ - rm -rf /usr/src/nextcloud; \ - tar -xjf nextcloud.tar.bz2 -C /usr/src/; \ - nextcloud_major="$(php -r 'require "/usr/src/nextcloud/version.php"; echo $OC_Version[0];')"; \ - test "${nextcloud_major}" = 35; \ - rm -f /tmp/nextcloud.tar.bz2 /tmp/nextcloud.tar.bz2.sha512; \ - rm -rf /usr/src/nextcloud/updater; \ - mkdir -p /usr/src/nextcloud/data /usr/src/nextcloud/custom_apps; \ - chmod +x /usr/src/nextcloud/occ - -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - locales \ - poppler-utils \ - postgresql-client \ - && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen \ - && locale-gen \ - && rm -rf /var/lib/apt/lists/* - -ENV LANG=en_US.UTF-8 -ENV LANGUAGE=en_US:en -ENV LC_ALL=en_US.UTF-8 - -ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ -RUN chmod uga+x /usr/local/bin/install-php-extensions && sync \ - && install-php-extensions bz2 imagick - -COPY config/php.ini /usr/local/etc/php/conf.d/ diff --git a/.env.example b/.env.example index 8a588cb..fc4375d 100644 --- a/.env.example +++ b/.env.example @@ -8,9 +8,6 @@ LETSENCRYPT_EMAIL= TZ= POSTGRES_PASSWORD= -NEXTCLOUD_BACKUP_DIR=/backups -NEXTCLOUD_UPGRADE_MIN_FREE_MB=2048 - NEXTCLOUD_ADMIN_USER= NEXTCLOUD_ADMIN_PASSWORD= diff --git a/.github/actions/build-and-scan/action.yml b/.github/actions/build-and-scan/action.yml index f633326..02d9955 100644 --- a/.github/actions/build-and-scan/action.yml +++ b/.github/actions/build-and-scan/action.yml @@ -30,7 +30,7 @@ runs: platforms: linux/amd64 load: true build-args: | - NEXTCLOUD_VERSION=${{ inputs.nextcloud_version }} + NEXTCLOUD_BASE_IMAGE=nextcloud:${{ inputs.nextcloud_version }} tags: scan/app:amd64 cache-from: type=gha cache-to: type=gha,mode=max @@ -42,7 +42,7 @@ runs: platforms: linux/arm64 load: true build-args: | - NEXTCLOUD_VERSION=${{ inputs.nextcloud_version }} + NEXTCLOUD_BASE_IMAGE=nextcloud:${{ inputs.nextcloud_version }} tags: scan/app:arm64 cache-from: type=gha cache-to: type=gha,mode=max @@ -70,7 +70,8 @@ runs: - name: Install Trivy uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1 with: - version: v0.74.0 + # renovate: datasource=github-releases depName=aquasecurity/trivy + version: v0.75.0 cache: true - name: Scan runtime images diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..8c8a80f --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,23 @@ +version: 2 + +updates: + - package-ecosystem: docker + directory: /.docker/app + schedule: + interval: weekly + cooldown: + default-days: 7 + + - package-ecosystem: docker + directory: /.docker/web + schedule: + interval: weekly + cooldown: + default-days: 7 + + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + cooldown: + default-days: 7 diff --git a/.github/workflows/nextcloud-35-development.yml b/.github/workflows/nextcloud-35-development.yml deleted file mode 100644 index 388676e..0000000 --- a/.github/workflows/nextcloud-35-development.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: Build Nextcloud 35 Development Image - -on: - workflow_dispatch: - push: - branches: - - main - paths: - - .docker/app/Dockerfile.35 - - .docker/app/config/** - - .github/workflows/nextcloud-35-development.yml - -concurrency: - group: nextcloud-35-development - cancel-in-progress: true - -env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}-app - -jobs: - build: - name: Build and push app:35 - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Log in to GitHub Container Registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Build and push Nextcloud 35 development image - uses: docker/build-push-action@v6 - with: - context: .docker/app - file: .docker/app/Dockerfile.35 - platforms: linux/amd64 - push: true - tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:35 - cache-from: type=gha,scope=nextcloud-35-development - cache-to: type=gha,mode=max,scope=nextcloud-35-development diff --git a/.github/workflows/nextcloud-development.yml b/.github/workflows/nextcloud-development.yml new file mode 100644 index 0000000..137628f --- /dev/null +++ b/.github/workflows/nextcloud-development.yml @@ -0,0 +1,97 @@ +name: Validate Nextcloud master image + +on: + pull_request: + branches: + - main + paths: + - '.docker/app/**' + - 'tests/app-image.bats' + - 'scripts/scan-images.sh' + - 'trivy.yaml' + - '.github/workflows/nextcloud-development.yml' + push: + branches: + - main + paths: + - '.docker/app/**' + - 'tests/app-image.bats' + - 'scripts/scan-images.sh' + - 'trivy.yaml' + - '.github/workflows/nextcloud-development.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: nextcloud-master-validation-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + name: master-fpm / linux/${{ matrix.arch }} + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + arch: + - amd64 + - arm64 + + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up QEMU + uses: docker/setup-qemu-action@c7c53464625b32c7a7e944ae62b3e17d2b600130 # v3 + with: + platforms: arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3 + + - name: Setup Bats + uses: bats-core/bats-action@77d6fb60505b4d0d1d73e48bd035b55074bbfb43 # 4.0.0 + with: + support-install: false + assert-install: false + detik-install: false + file-install: false + + - name: Build Nextcloud master app image + uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: .docker/app + platforms: linux/${{ matrix.arch }} + load: true + build-args: | + NEXTCLOUD_BASE_IMAGE=nextcloud:stable-fpm + NEXTCLOUD_SOURCE=daily + NEXTCLOUD_DAILY_URL=https://download.nextcloud.com/server/daily/latest-master.tar.bz2 + tags: scan/master:${{ matrix.arch }} + cache-from: type=gha,scope=master-${{ matrix.arch }} + cache-to: type=gha,mode=max,scope=master-${{ matrix.arch }} + + - name: Install Trivy + uses: aquasecurity/setup-trivy@81e514348e19b6112ce2a7e3ecbafe19c1e1f567 # v0.3.1 + with: + # renovate: datasource=github-releases depName=aquasecurity/trivy + version: v0.75.0 + cache: true + + - name: Scan Nextcloud master app image + shell: bash + env: + ARCH: ${{ matrix.arch }} + run: | + bash scripts/scan-images.sh "app@linux/${ARCH}=scan/master:${ARCH}" + + - name: Runtime acceptance + shell: bash + env: + APP_IMAGE: scan/master:${{ matrix.arch }} + run: bats tests/app-image.bats diff --git a/Makefile b/Makefile index 16e5fcc..62dae15 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ COMPOSE ?= docker compose GARAGES3_COMPOSE_FILE ?= docker-compose-garages3.yml APP_TEST_IMAGE ?= nextcloud-app:acceptance -.PHONY: up-garages3 down-garages3 bootstrap-garages3 garage-status-garages3 start-garages3 wait-nextcloud-garages3 setup-garages3 test-hooks test-scan-images test-ncdd test-app-image test-current-app-image scan-images +.PHONY: up-garages3 down-garages3 bootstrap-garages3 garage-status-garages3 start-garages3 wait-nextcloud-garages3 setup-garages3 test-scan-images test-ncdd test-app-image test-current-app-image scan-images up-garages3: $(COMPOSE) -f $(GARAGES3_COMPOSE_FILE) up -d garage @@ -27,9 +27,6 @@ setup-garages3: $(MAKE) start-garages3 $(MAKE) wait-nextcloud-garages3 -test-hooks: - bash tests/test-hooks.sh - test-scan-images: bash tests/test-scan-images.sh @@ -45,7 +42,7 @@ test-current-app-image: version="$$(sed -n 's/^NEXTCLOUD_VERSION=//p' .env.example | head -n 1)"; \ test -n "$$version" || { echo 'NEXTCLOUD_VERSION is missing from .env.example' >&2; exit 1; }; \ docker buildx build --platform linux/amd64 --load --tag "$(APP_TEST_IMAGE)" \ - --build-arg "NEXTCLOUD_VERSION=$$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ $(MAKE) test-app-image APP_IMAGE="$(APP_TEST_IMAGE)" scan-images: @@ -53,9 +50,9 @@ scan-images: version="$$(sed -n 's/^NEXTCLOUD_VERSION=//p' .env.example | head -n 1)"; \ test -n "$$version" || { echo 'NEXTCLOUD_VERSION is missing from .env.example' >&2; exit 1; }; \ docker buildx build --platform linux/amd64 --load --tag nextcloud-app:scan-amd64 \ - --build-arg "NEXTCLOUD_VERSION=$$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ docker buildx build --platform linux/arm64 --load --tag nextcloud-app:scan-arm64 \ - --build-arg "NEXTCLOUD_VERSION=$$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ docker buildx build --platform linux/amd64 --load --tag nextcloud-web:scan-amd64 \ --file .docker/web/Dockerfile .docker/web; \ docker buildx build --platform linux/arm64 --load --tag nextcloud-web:scan-arm64 \ diff --git a/README.md b/README.md index d32956b..aeebc5e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ Languages avaliable: [pt-BR](docs/README_ptBR.md) - [After setup](#after-setup) - [Custom setup](#custom-setup) - [Customize docker-compose content](#customize-docker-compose-content) - - [Nextcloud upgrade hooks](#nextcloud-upgrade-hooks) - [PHP](#php) - [Run Nextcloud](#run-nextcloud) - [Use a specific version of Nextcloud](#use-a-specific-version-of-nextcloud) @@ -128,44 +127,6 @@ You can do this using environments and creating a file called `docker-compose.ov The main compose files now include a `redis` service by default. This keeps the stack self-contained for Nextcloud installations that already use Redis in `config.php` and avoids depending on a host-specific external network. -### Nextcloud upgrade hooks - -This repository mounts the official Nextcloud Docker hook directories so you can extend install and upgrade flows without touching the image entrypoint. - -The `app` service uses these mounts: - -```yaml -services: - app: - volumes: - - ./volumes/nextcloud:/var/www/html - - ./backups:/backups - - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation - - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade - - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade - - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting -``` - -The upgrade hooks behave like this: - -- `pre-upgrade`: turns maintenance mode on -- `pre-upgrade`: saves the active app list to `/backups/app_list.old` -- `pre-upgrade`: checks free disk space on the Nextcloud volume and the backup volume -- `pre-upgrade`: creates a compressed PostgreSQL dump at `/backups/nextcloud-db.sql.gz`, replacing the previous dump -- `post-upgrade`: saves the new app list to `/backups/app_list.new` and prints a diff when possible -- `post-upgrade`: runs the extra `occ` commands needed after a major upgrade -- `post-upgrade`: turns maintenance mode off at the end - -The following variables control the safety check and backup location: - -- `NEXTCLOUD_BACKUP_DIR`, defaulting to `/backups` -- `NEXTCLOUD_UPGRADE_MIN_FREE_MB`, defaulting to `2048` - -Both upgrade hooks use `NEXTCLOUD_BACKUP_DIR` for the app list files and the database dump. - -This repository includes the `./backups` directory so Docker does not create it as a root-owned host path on a fresh checkout. It must still be writable by `www-data` inside the container. The recommended host-side ownership is `www-data:www-data` with mode `0755`. - ### Garage S3 primary storage Use `docker-compose-garages3.yml` when you want Nextcloud to store files in a Garage S3 bucket instead of the local `data/` directory. diff --git a/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh b/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh deleted file mode 100755 index 0437868..0000000 --- a/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -backup_dir=${NEXTCLOUD_BACKUP_DIR:-/backups} - -cleanup() { - php occ maintenance:mode --off >/dev/null 2>&1 || true -} - -trap cleanup EXIT - -run_occ() { - echo "Running: php occ $*" - php occ "$@" -} - -echo "Running post-upgrade Nextcloud commands" -php occ app:list > "$backup_dir/app_list.new" -if [ -f "$backup_dir/app_list.old" ]; then - echo "Comparing app lists" - diff -u "$backup_dir/app_list.old" "$backup_dir/app_list.new" || true -fi -run_occ db:add-missing-columns -run_occ db:add-missing-indices -run_occ db:add-missing-primary-keys -run_occ maintenance:repair --include-expensive -run_occ config:system:set maintenance_window_start --type=integer --value=1 -run_occ app:update --all -echo "Post-upgrade commands completed successfully" diff --git a/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh b/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh deleted file mode 100755 index cc4d47a..0000000 --- a/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -nextcloud_dir=${NEXTCLOUD_DIR:-/var/www/html} -backup_dir=${NEXTCLOUD_BACKUP_DIR:-/backups} -min_free_mb=${NEXTCLOUD_UPGRADE_MIN_FREE_MB:-2048} -db_host=${POSTGRES_HOST:-postgres} -db_name=${POSTGRES_DB:-nextcloud} -db_user=${POSTGRES_USER:-nextcloud} -db_password=${POSTGRES_PASSWORD:-} -backup_file="${backup_dir}/nextcloud-db.sql.gz" -backup_tmp_file="${backup_file}.tmp" -app_list_file="${backup_dir}/app_list.old" -maintenance_enabled=0 - -cleanup() { - if [ "$maintenance_enabled" -eq 1 ]; then - php occ maintenance:mode --off >/dev/null 2>&1 || true - fi - - rm -f "$backup_tmp_file" -} - -trap cleanup EXIT - -run_occ() { - echo "Running: php occ $*" - php occ "$@" -} - -check_free_space() { - local path=$1 - local label=$2 - local available_mb - - available_mb=$(df -Pm "$path" | awk 'NR==2 { print $4 }') - - if [ "$available_mb" -lt "$min_free_mb" ]; then - echo "Not enough disk space on ${label}: ${available_mb} MB available, ${min_free_mb} MB required" - exit 1 - fi - - echo "${label} has ${available_mb} MB free" -} - -echo "Running pre-upgrade safety checks" -mkdir -p "$backup_dir" - -run_occ maintenance:mode --on -maintenance_enabled=1 -php occ app:list > "$app_list_file" -echo "Saved active apps list to ${app_list_file}" - -check_free_space "$nextcloud_dir" "Nextcloud volume" -check_free_space "$backup_dir" "Backup volume" - -if ! command -v pg_dump >/dev/null 2>&1; then - echo "pg_dump is not available in the container image" - exit 1 -fi - -if [ -z "$db_password" ]; then - echo "POSTGRES_PASSWORD is empty, refusing to create a database dump" - exit 1 -fi - -echo "Creating PostgreSQL dump at ${backup_file}" -PGPASSWORD="$db_password" pg_dump -h "$db_host" -U "$db_user" "$db_name" | gzip -9 > "$backup_tmp_file" -mv "$backup_tmp_file" "$backup_file" -rm -f "$backup_tmp_file" -echo "Database dump completed successfully" diff --git a/docker-compose-garages3.yml b/docker-compose-garages3.yml index 6b73c9b..8a26f92 100644 --- a/docker-compose-garages3.yml +++ b/docker-compose-garages3.yml @@ -37,15 +37,12 @@ services: build: context: .docker/app args: - NEXTCLOUD_VERSION: ${NEXTCLOUD_VERSION:-stable-fpm} + NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped volumes: - ./volumes/nextcloud:/var/www/html - - ./backups:/backups - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation - - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade - - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting - ./.docker/garages3.config.php:/var/www/html/config/garages3.config.php:ro environment: @@ -106,7 +103,7 @@ services: build: context: .docker/app args: - NEXTCLOUD_VERSION: ${NEXTCLOUD_VERSION:-stable-fpm} + NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped environment: - TZ diff --git a/docker-compose.yml b/docker-compose.yml index 736eb46..3e4c68b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,14 +16,11 @@ services: build: context: .docker/app args: - NEXTCLOUD_VERSION: ${NEXTCLOUD_VERSION:-stable-fpm} + NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} volumes: - ./volumes/nextcloud:/var/www/html - - ./volumes/backups:/backups - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation - - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade - - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting restart: unless-stopped environment: @@ -70,7 +67,7 @@ services: build: context: .docker/app args: - NEXTCLOUD_VERSION: ${NEXTCLOUD_VERSION:-stable-fpm} + NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped environment: - TZ diff --git a/docs/README_ptBR.md b/docs/README_ptBR.md index 5ade48a..1e013f8 100644 --- a/docs/README_ptBR.md +++ b/docs/README_ptBR.md @@ -6,7 +6,6 @@ - [Após a configuração](#após-a-configuração) - [Configuração personalizada](#configuração-personalizada) - [Personalize o conteúdo do docker-compose](#personalize-o-conteúdo-do-docker-compose) - - [Hooks de upgrade do Nextcloud](#hooks-de-upgrade-do-nextcloud) - [PHP](#php) - [Execute o Nextcloud](#execute-o-nextcloud) - [Use uma versão específica do Nextcloud](#use-uma-versão-específica-do-nextcloud) @@ -83,44 +82,6 @@ Você pode fazer isso usando variáveis de ambiente e criando um arquivo chamado Os arquivos principais de compose agora incluem o serviço `redis` por padrão. Isso deixa o stack autocontido para instalações do Nextcloud que já usam Redis no `config.php` e evita depender de uma rede externa específica do host. -### Hooks de upgrade do Nextcloud - -Este repositório monta os diretórios oficiais de hooks do Nextcloud Docker para permitir extensões do fluxo de instalação e upgrade sem alterar o entrypoint da imagem. - -O serviço `app` usa estes mounts: - -```yaml -services: - app: - volumes: - - ./volumes/nextcloud:/var/www/html - - ./backups:/backups - - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation - - ./app-hooks/post-installation:/docker-entrypoint-hooks.d/post-installation - - ./app-hooks/pre-upgrade:/docker-entrypoint-hooks.d/pre-upgrade - - ./app-hooks/post-upgrade:/docker-entrypoint-hooks.d/post-upgrade - - ./app-hooks/before-starting:/docker-entrypoint-hooks.d/before-starting -``` - -Os hooks de upgrade funcionam assim: - -- `pre-upgrade`: ativa o modo de manutenção -- `pre-upgrade`: salva a lista de apps ativos em `/backups/app_list.old` -- `pre-upgrade`: verifica o espaço livre no volume do Nextcloud e no volume de backup -- `pre-upgrade`: cria um dump compactado do PostgreSQL em `/backups/nextcloud-db.sql.gz`, substituindo o dump anterior -- `post-upgrade`: salva a nova lista de apps em `/backups/app_list.new` e mostra o diff quando possível -- `post-upgrade`: executa os comandos `occ` extras necessários após um upgrade maior -- `post-upgrade`: desativa o modo de manutenção ao final - -As variáveis abaixo controlam a checagem e o diretório de backup: - -- `NEXTCLOUD_BACKUP_DIR`, com padrão `/backups` -- `NEXTCLOUD_UPGRADE_MIN_FREE_MB`, com padrão `2048` - -Os dois hooks de upgrade usam `NEXTCLOUD_BACKUP_DIR` para os arquivos da lista de apps e para o dump do banco. - -Este repositório já inclui o diretório `./backups`, então o Docker não cria um caminho root-owned em um checkout novo. Ele ainda precisa ser gravável pelo `www-data` dentro do container. O recomendado é usar `www-data:www-data` com permissão `0755`. - ### Storage primário Garage S3 Use `docker-compose-garages3.yml` quando quiser que o Nextcloud grave os arquivos em um bucket Garage S3 em vez do diretório local `data/`. diff --git a/docs/images.md b/docs/images.md index a04aa50..8dfb323 100644 --- a/docs/images.md +++ b/docs/images.md @@ -149,6 +149,23 @@ Immutable web image: ghcr.io/librecodecoop/nextcloud-docker-web@sha256: ``` +## App image build sources + +The app image uses a single `.docker/app/Dockerfile` for both release and development builds. + +The Dockerfile models its two source modes as Docker build stages instead of runtime shell branching: + +- `NEXTCLOUD_SOURCE=release` selects the stage that keeps the Nextcloud payload provided by the selected official Nextcloud base image; +- `NEXTCLOUD_SOURCE=daily` selects the stage that replaces that payload with a verified upstream daily archive. + +The base runtime and the Nextcloud server payload are separate inputs. A development build can therefore follow Nextcloud Server `master` without introducing a Dockerfile or image identity tied to a future major. + +For daily builds, `NEXTCLOUD_DAILY_URL` is required. The Dockerfile downloads the matching `.sha512` file, verifies the archive, and prepares `/usr/src/nextcloud` in the layout expected by the official Nextcloud entrypoint. + +The repository Compose environment keeps `NEXTCLOUD_VERSION` as its user-facing version setting and maps it to the generic official base image. There is no second Compose stack for development images. + +The app image remains LibreSign-ready while being generic across Nextcloud versions. It keeps repository-level runtime requirements such as Poppler, UTF-8 locale support, and the PHP bz2 extension, but does not reinstall extensions already supplied by the official Nextcloud base image such as Imagick. Dependabot owns Docker and GitHub Actions updates in this repository. Renovate is deliberately restricted to custom regex-managed values that Dependabot cannot see, currently the explicit Trivy binary version used by CI. The two bots must not manage the same dependency. + ## Runtime acceptance The app image has a runtime acceptance test based on the same behavioral checks used by the official Nextcloud container projects. @@ -171,12 +188,12 @@ CI must run this test against the exact locally loaded app images produced by th ## Implementation boundary -This document defines the target contract. It does not by itself migrate the current Dockerfiles, workflows, or historical tags. +This document defines the target contract and the generic app-image foundation now implements the release/daily source split. -The image-foundation work in #47 must implement this convention incrementally. In particular: +Remaining work in #47 must continue incrementally. In particular: -- use one generic app image build path instead of a Dockerfile per Nextcloud major; -- keep the development channel tied to Nextcloud Server `master`; +- publish development images from the generic foundation using the documented `:master-fpm` contract; +- add the remaining OCI traceability metadata to published images; - keep LibreSign-specific behavior out of the generic runtime; -- test and scan images before publication; -- preserve the existing Compose environment until its replacement path is validated. +- preserve scan and runtime-acceptance gates before publication; +- preserve the existing Compose environment until a replacement is explicitly validated. diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..d705793 --- /dev/null +++ b/renovate.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "enabledManagers": [ + "custom.regex" + ], + "dependencyDashboard": false, + "customManagers": [ + { + "customType": "regex", + "managerFilePatterns": [ + "/^\\.github/actions/build-and-scan/action\\.yml$/", + "/^\\.github/workflows/nextcloud-development\\.yml$/" + ], + "matchStrings": [ + "# renovate: datasource=(?\\S+) depName=(?\\S+)\\s+version: (?v?\\d+\\.\\d+\\.\\d+)" + ], + "versioningTemplate": "semver-coerced" + } + ] +} diff --git a/tests/test-hooks.sh b/tests/test-hooks.sh deleted file mode 100644 index 7c543f4..0000000 --- a/tests/test-hooks.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -repo_root=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd) -tmp_root=$(mktemp -d) -trap 'rm -rf "$tmp_root"' EXIT - -bin_dir="$tmp_root/bin" -work_dir="$tmp_root/work" -backup_dir="$tmp_root/backups" -nextcloud_dir="$tmp_root/nextcloud" -log_file="$tmp_root/php.log" -dump_output="$tmp_root/pg_dump.out" - -mkdir -p "$bin_dir" "$work_dir" "$backup_dir" "$nextcloud_dir" - -cat > "$bin_dir/php" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail - -log_file=${HOOK_TEST_LOG_FILE:?} - -printf 'php %s\n' "$*" >> "$log_file" - -case "${1:-}" in - occ) - shift - case "${1:-}" in - maintenance:mode) - exit 0 - ;; - app:list) - printf 'app1\napp2\n' - exit 0 - ;; - db:add-missing-columns|db:add-missing-indices|db:add-missing-primary-keys|maintenance:repair|config:system:set|app:update) - exit 0 - ;; - *) - printf 'unexpected occ command: %s\n' "$*" >&2 - exit 1 - ;; - esac - ;; - *) - printf 'unexpected php invocation: %s\n' "$*" >&2 - exit 1 - ;; -esac -EOF - -cat > "$bin_dir/pg_dump" <<'EOF' -#!/usr/bin/env bash -set -euo pipefail - -printf '%s\n' "CREATE TABLE test();" -EOF - -chmod +x "$bin_dir/php" "$bin_dir/pg_dump" - -export PATH="$bin_dir:$PATH" -export HOOK_TEST_LOG_FILE="$log_file" -export NEXTCLOUD_DIR="$nextcloud_dir" -export NEXTCLOUD_BACKUP_DIR="$backup_dir" -export NEXTCLOUD_UPGRADE_MIN_FREE_MB=1 -export POSTGRES_PASSWORD="secret" - -cp "$repo_root/app-hooks/pre-upgrade/01-check-disk-and-dump-db.sh" "$work_dir/" -cp "$repo_root/app-hooks/post-upgrade/01-run-post-upgrade-commands.sh" "$work_dir/" - -pushd "$work_dir" >/dev/null -bash ./01-check-disk-and-dump-db.sh -popd >/dev/null - -test -f "$backup_dir/nextcloud-db.sql.gz" -test ! -f "$backup_dir/nextcloud-db.sql.gz.tmp" -gzip -dc "$backup_dir/nextcloud-db.sql.gz" > "$dump_output" -grep -q "CREATE TABLE test();" "$dump_output" - -grep -q "php occ maintenance:mode --on" "$log_file" -grep -q "php occ maintenance:mode --off" "$log_file" -grep -q "php occ app:list" "$log_file" - -: > "$log_file" - -pushd "$work_dir" >/dev/null -HOOK_TEST_LOG_FILE="$log_file" bash ./01-run-post-upgrade-commands.sh -popd >/dev/null - -test -f "$backup_dir/app_list.new" -grep -q "php occ db:add-missing-columns" "$log_file" -grep -q "php occ maintenance:mode --off" "$log_file" - -echo "hook tests passed"