diff --git a/.env.example b/.env.example index b84d06a..a0994c6 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,6 @@ -NEXTCLOUD_VERSION=35-fpm +# GHCR app image tag shared by app and cron. stable-fpm follows the latest approved major. +# For production, pin a supported major such as -fpm before deployment. +NEXTCLOUD_VERSION=stable-fpm VIRTUAL_HOST= LETSENCRYPT_HOST= diff --git a/Makefile b/Makefile index 875aab6..410f5ed 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ COMPOSE ?= docker compose GARAGES3_COMPOSE_FILE ?= docker-compose-garages3.yml APP_TEST_IMAGE ?= nextcloud-app:acceptance +# Maintainer builds use the latest reviewed upstream base, not the deployment image channel. +APP_BASE_IMAGE ?= $(shell jq -er '.versions[-1].image' .github/nextcloud-supported.json) .PHONY: up-garages3 down-garages3 bootstrap-garages3 garage-status-garages3 start-garages3 wait-nextcloud-garages3 setup-garages3 test-scan-images test-ci-scripts test-development-image-scripts test-ncdd test-app-image test-current-app-image scan-images @@ -32,6 +34,7 @@ test-scan-images: test-ci-scripts: bats tests/check-action-pins.bats \ + tests/compose-images.bats \ tests/nextcloud-versions.bats \ tests/publish-images.bats @@ -47,20 +50,20 @@ test-app-image: test-current-app-image: @set -e; \ - 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; }; \ + base_image='$(APP_BASE_IMAGE)'; \ + test -n "$$base_image" || { echo 'No approved Nextcloud image in .github/nextcloud-supported.json' >&2; exit 1; }; \ docker buildx build --platform linux/amd64 --load --tag "$(APP_TEST_IMAGE)" \ - --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=$$base_image" --file .docker/app/Dockerfile .docker/app; \ $(MAKE) test-app-image APP_IMAGE="$(APP_TEST_IMAGE)" scan-images: @set -e; \ - 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; }; \ + base_image='$(APP_BASE_IMAGE)'; \ + test -n "$$base_image" || { echo 'No approved Nextcloud image in .github/nextcloud-supported.json' >&2; exit 1; }; \ docker buildx build --platform linux/amd64 --load --tag nextcloud-app:scan-amd64 \ - --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=$$base_image" --file .docker/app/Dockerfile .docker/app; \ docker buildx build --platform linux/arm64 --load --tag nextcloud-app:scan-arm64 \ - --build-arg "NEXTCLOUD_BASE_IMAGE=nextcloud:$version" --file .docker/app/Dockerfile .docker/app; \ + --build-arg "NEXTCLOUD_BASE_IMAGE=$$base_image" --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 d853d3d..69e275c 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ cp .env.example .env # Edit .env: domain, credentials, administrator account, and your Nextcloud version. docker network create reverse-proxy docker network create postgres -docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d --build +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` The external `reverse-proxy` network must have a configured proxy attached. Set up the proxy and DNS before expecting HTTPS to work. For production, review the PostgreSQL version in the Compose example, your backup strategy, and volume migrations. @@ -31,7 +32,9 @@ For environment variables, existing deployments, storage options, and troublesho ## Nextcloud versions and image updates -Set `NEXTCLOUD_VERSION` in `.env` to select the **official Nextcloud base image** for this repository's local Docker Compose build. This is separate from publishing the LibreCode images to GHCR. +Set `NEXTCLOUD_VERSION` in `.env` to select the **published LibreCode application image tag** for both `app` and `cron`. The `web` service uses the independent `nextcloud-docker-web:main` image. The default Compose deployment does not build images locally. + +The sample `.env.example` uses the moving `stable-fpm` channel. Set `NEXTCLOUD_VERSION` to a maintained major-specific tag (for example, `35-fpm`) before production deployment so a future image pull does not switch Nextcloud majors unexpectedly. Prebuilt LibreCode application images use versioned tags, such as: diff --git a/docker-compose-garages3.yml b/docker-compose-garages3.yml index 8a26f92..15a55e8 100644 --- a/docker-compose-garages3.yml +++ b/docker-compose-garages3.yml @@ -34,10 +34,7 @@ services: - ./garage/data:/var/lib/garage/data app: - build: - context: .docker/app - args: - NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} + image: ghcr.io/librecodecoop/nextcloud-docker-app:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped volumes: - ./volumes/nextcloud:/var/www/html @@ -83,7 +80,7 @@ services: - internal web: - build: .docker/web + image: ghcr.io/librecodecoop/nextcloud-docker-web:main restart: unless-stopped volumes: - ./volumes/nextcloud:/var/www/html:ro @@ -100,10 +97,7 @@ services: - reverse-proxy cron: - build: - context: .docker/app - args: - NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} + image: ghcr.io/librecodecoop/nextcloud-docker-app:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped environment: - TZ diff --git a/docker-compose.yml b/docker-compose.yml index 3e4c68b..76eb393 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,10 +13,7 @@ services: - internal app: - build: - context: .docker/app - args: - NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} + image: ghcr.io/librecodecoop/nextcloud-docker-app:${NEXTCLOUD_VERSION:-stable-fpm} volumes: - ./volumes/nextcloud:/var/www/html - ./app-hooks/pre-installation:/docker-entrypoint-hooks.d/pre-installation @@ -47,7 +44,7 @@ services: - internal web: - build: .docker/web + image: ghcr.io/librecodecoop/nextcloud-docker-web:main restart: unless-stopped volumes: - ./volumes/nextcloud:/var/www/html:ro @@ -64,10 +61,7 @@ services: - reverse-proxy cron: - build: - context: .docker/app - args: - NEXTCLOUD_BASE_IMAGE: nextcloud:${NEXTCLOUD_VERSION:-stable-fpm} + image: ghcr.io/librecodecoop/nextcloud-docker-app:${NEXTCLOUD_VERSION:-stable-fpm} restart: unless-stopped environment: - TZ diff --git a/docs/README_ptBR.md b/docs/README_ptBR.md index a3e3fce..b63cc26 100644 --- a/docs/README_ptBR.md +++ b/docs/README_ptBR.md @@ -22,7 +22,8 @@ cp .env.example .env # Edite .env: domínio, credenciais, administrador e versão do Nextcloud. docker network create reverse-proxy docker network create postgres -docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d --build +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` O proxy reverso deve estar configurado e conectado à rede externa `reverse-proxy`. Configure também o DNS antes de esperar que o HTTPS funcione. Em produção, revise a versão do PostgreSQL usada no exemplo, os backups e as migrações dos volumes. @@ -31,7 +32,9 @@ Para variáveis de ambiente, instalações existentes, armazenamento e solução ## Versões e atualizações do Nextcloud -Defina `NEXTCLOUD_VERSION` no arquivo `.env` para selecionar a **imagem-base oficial do Nextcloud** utilizada no build local do Docker Compose. Isso é diferente da publicação das imagens da LibreCode no GHCR. +Defina `NEXTCLOUD_VERSION` no arquivo `.env` para selecionar a **tag da imagem de aplicação publicada pela LibreCode** para os serviços `app` e `cron`. O serviço `web` utiliza a imagem independente `nextcloud-docker-web:main`. O Compose padrão não constrói imagens localmente. + +O arquivo `.env.example` utiliza o canal móvel `stable-fpm`. Antes de implantar em produção, configure `NEXTCLOUD_VERSION` com uma tag de versão principal ainda suportada (por exemplo, `35-fpm`), para evitar a troca inesperada de versão principal em futuras atualizações das imagens. As imagens de aplicação da LibreCode disponibilizam tags por versão, por exemplo: diff --git a/docs/deployment.md b/docs/deployment.md index 489d582..a752417 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -1,6 +1,6 @@ # Deployment and customization -This guide covers the example Docker Compose stack shipped with this repository. The Compose configuration **builds application and web images locally**. It does not pull the prebuilt LibreCode GHCR images by default. +This guide covers the example Docker Compose stack shipped with this repository. The default stack **uses the prebuilt LibreCode GHCR images**: `app` and `cron` share a versioned application image, while `web` uses the published Nginx image. Image maintainers can still use the repository's explicit build commands. Return to the [project overview](../README.md) or see the [published image tags](images.md). @@ -23,11 +23,11 @@ Create these networks **once** if they do not exist. Configure the proxy service cp .env.example .env ``` -Edit `.env` before running the services. At a minimum, provide administrator credentials, the database password, and your public domain. +Edit `.env` before running the services. At a minimum, provide administrator credentials, the database password, and your public domain. The sample selects `stable-fpm`, which moves to the newest approved major; pin a maintained major-specific tag before production deployment to avoid unintended major upgrades. | Variable | Purpose | | --- | --- | -| `NEXTCLOUD_VERSION` | Nextcloud version/tag used for the **official Docker base image** during a local build, e.g. `35-fpm` | +| `NEXTCLOUD_VERSION` | Tag of the **published LibreCode GHCR application image** used by `app` and `cron`; default `stable-fpm` (moving); use a supported major tag for production | | `VIRTUAL_HOST`, `LETSENCRYPT_HOST` | Public domain passed to the external proxy | | `LETSENCRYPT_EMAIL` | Contact email for certificate management | | `NEXTCLOUD_TRUSTED_DOMAINS` | Trusted hostnames, including the public domain and the internal `web` host where applicable | @@ -46,7 +46,8 @@ See `docker-compose.yml` and `.env.example` for the values consumed by the stack The standard setup uses the base Compose file plus the PostgreSQL extension: ```bash -docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d --build +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` The main Compose file provides Nextcloud FPM, an Nginx web container, Redis, and cron. The additional Compose file supplies PostgreSQL and connects the app and cron services to its network. @@ -64,16 +65,16 @@ docker compose -f docker-compose.yml -f docker-compose-postgres.yml \ ## Choosing and upgrading Nextcloud -Change `NEXTCLOUD_VERSION` in `.env` to select the official upstream image used by local Compose builds. It **does not control** which GHCR versions are published by LibreCode. +Change `NEXTCLOUD_VERSION` in `.env` to select a published LibreCode application image tag for both `app` and `cron`. This setting **does not control** which GHCR images LibreCode builds and publishes. ```bash -docker compose -f docker-compose.yml -f docker-compose-postgres.yml build --pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull app cron web docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` Back up the database, application configuration, and persistent files before an upgrade. Check Nextcloud's upgrade requirements and compatibility first: downgrades and skipping unsupported upgrade paths can leave stored data unusable. Use a controlled major-version upgrade process instead of unattended `:latest` changes. -For users deploying the prebuilt GHCR images, see the [image selection and upgrade policy](images.md). +See the [image selection and upgrade policy](images.md) before changing major versions or using immutable OCI digests. ## Optional Garage S3 primary storage diff --git a/docs/deployment_ptBR.md b/docs/deployment_ptBR.md index bf50174..bc244b1 100644 --- a/docs/deployment_ptBR.md +++ b/docs/deployment_ptBR.md @@ -1,6 +1,6 @@ # Implantação e personalização -Este guia explica o ambiente Docker Compose fornecido pelo repositório. O Compose **constrói as imagens da aplicação e do servidor web localmente**. Por padrão, ele não utiliza as imagens pré-compiladas da LibreCode no GHCR. +Este guia explica o ambiente Docker Compose fornecido pelo repositório. A configuração padrão **utiliza as imagens publicadas pela LibreCode no GHCR**: `app` e `cron` compartilham uma imagem de aplicação versionada, enquanto `web` utiliza a imagem publicada do Nginx. Os mantenedores ainda podem utilizar os comandos específicos para builds locais. Volte para a [apresentação do projeto](README_ptBR.md) ou consulte as [tags das imagens publicadas](images.md). @@ -23,11 +23,11 @@ Crie essas redes **uma única vez**, caso ainda não existam. Configure separada cp .env.example .env ``` -Edite o `.env` antes de iniciar os serviços. Informe, no mínimo, as credenciais do administrador, a senha do banco e o domínio público. +Edite o `.env` antes de iniciar os serviços. Informe, no mínimo, as credenciais do administrador, a senha do banco e o domínio público. O exemplo seleciona `stable-fpm`, que acompanha a versão principal aprovada mais recente; fixe uma tag de versão principal suportada antes da implantação em produção para evitar atualizações inesperadas. | Variável | Finalidade | | --- | --- | -| `NEXTCLOUD_VERSION` | Versão/tag da **imagem-base oficial do Nextcloud** utilizada no build local, por exemplo `35-fpm` | +| `NEXTCLOUD_VERSION` | Tag da **imagem de aplicação publicada pela LibreCode no GHCR** para `app` e `cron`; padrão `stable-fpm` (móvel); utilize uma tag de versão principal em produção | | `VIRTUAL_HOST`, `LETSENCRYPT_HOST` | Domínio público utilizado pelo proxy | | `LETSENCRYPT_EMAIL` | E-mail de contato para os certificados | | `NEXTCLOUD_TRUSTED_DOMAINS` | Domínios confiáveis, incluindo o domínio público e o host interno `web`, quando aplicável | @@ -46,7 +46,8 @@ Consulte `docker-compose.yml` e `.env.example` para verificar as variáveis usad A configuração padrão combina o Compose principal com a extensão do PostgreSQL: ```bash -docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d --build +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` O Compose principal contém Nextcloud FPM, Nginx, Redis e cron. A extensão adiciona o PostgreSQL e conecta os serviços da aplicação e do cron à rede do banco. @@ -64,16 +65,16 @@ docker compose -f docker-compose.yml -f docker-compose-postgres.yml \ ## Escolher e atualizar a versão do Nextcloud -Altere `NEXTCLOUD_VERSION` no arquivo `.env` para escolher a imagem oficial utilizada nos builds locais. Essa variável **não define** quais versões são publicadas pela LibreCode no GHCR. +Altere `NEXTCLOUD_VERSION` no arquivo `.env` para escolher uma tag publicada da imagem de aplicação da LibreCode, utilizada por `app` e `cron`. Essa variável **não define** quais versões são construídas e publicadas pela LibreCode no GHCR. ```bash -docker compose -f docker-compose.yml -f docker-compose-postgres.yml build --pull +docker compose -f docker-compose.yml -f docker-compose-postgres.yml pull app cron web docker compose -f docker-compose.yml -f docker-compose-postgres.yml up -d ``` Faça backup do banco de dados, da configuração e dos arquivos persistidos antes de atualizar. Confira as exigências de compatibilidade do Nextcloud: downgrade ou saltos não suportados entre versões podem impedir o uso dos dados. Prefira atualizações de versões principais planejadas. -Para quem utiliza as imagens do GHCR, consulte a [política de tags e atualizações](images.md). +Consulte a [política de tags e atualizações](images.md) antes de mudar de versão principal ou utilizar digests OCI imutáveis. ## Armazenamento primário opcional com Garage S3 diff --git a/docs/images.md b/docs/images.md index 77d12f6..df43695 100644 --- a/docs/images.md +++ b/docs/images.md @@ -9,7 +9,7 @@ The LibreCode Nextcloud images let you choose a maintained Nextcloud release for | `ghcr.io/librecodecoop/nextcloud-docker-app` | Nextcloud application runtime with PHP-FPM | | `ghcr.io/librecodecoop/nextcloud-docker-web` | Nginx web runtime; independent of the Nextcloud major | -The [repository's Docker Compose example](deployment.md) builds its images locally from the official `nextcloud:${NEXTCLOUD_VERSION}` base. It does not automatically pull these GHCR images. +The [repository's Docker Compose example](deployment.md) uses these published GHCR images by default. `NEXTCLOUD_VERSION` selects the application tag shared by `app` and `cron`; `web` uses the independent `:main` tag. The example environment defaults to the moving `:stable-fpm` alias; set a major-specific tag for production before pulling images. ## Application tags diff --git a/docs/maintenance.md b/docs/maintenance.md index b472bec..79624a1 100644 --- a/docs/maintenance.md +++ b/docs/maintenance.md @@ -15,7 +15,7 @@ For daily builds, `NEXTCLOUD_DAILY_URL` identifies the upstream archive. The Doc The generic image retains required runtime dependencies such as Poppler, UTF-8 locale support, and the PHP bz2 extension. Avoid reinstalling extensions already provided by the official Nextcloud base. Keep LibreSign-specific behavior outside this generic runtime. -The repository Compose example retains `NEXTCLOUD_VERSION` as its user-facing setting for the **locally built official base image**. It does not control the GHCR publication matrix. +The repository Compose example uses `NEXTCLOUD_VERSION` to select the **published LibreCode application image tag** for `app` and `cron`. Builds stay in CI and explicit maintainer commands; this setting does not control the GHCR publication matrix. The `make test-current-app-image` and `make scan-images` maintenance targets use the newest reviewed upstream image reference from `.github/nextcloud-supported.json` (overridable with `APP_BASE_IMAGE`), independently of the deployment image selector. ## Reviewed stable-version inventory diff --git a/tests/compose-images.bats b/tests/compose-images.bats new file mode 100644 index 0000000..409711b --- /dev/null +++ b/tests/compose-images.bats @@ -0,0 +1,57 @@ +#!/usr/bin/env bats + +setup() { + REPO_ROOT=$(cd "$BATS_TEST_DIRNAME/.." && pwd) +} + +assert_published_images() { + local file="$1" selected_tag="$2" expected_tag="$3" config + + # Compose config validates interpolation without pulling any registry image. + # Empty selected_tag exercises the fallback even if a local .env exists. + config=$(NEXTCLOUD_VERSION="$selected_tag" docker compose \ + --env-file /dev/null \ + --project-directory "$REPO_ROOT" \ + -f "$REPO_ROOT/$file" config --format json) || return 1 + + jq -e --arg app_image "ghcr.io/librecodecoop/nextcloud-docker-app:$expected_tag" ' + .services.app.image == $app_image and + .services.cron.image == $app_image and + .services.web.image == "ghcr.io/librecodecoop/nextcloud-docker-web:main" and + (.services.cron.entrypoint == ["/cron.sh"]) and + ([.services.app, .services.cron, .services.web] | all(has("build") | not)) + ' <<< "$config" >/dev/null +} + +@test "standard Compose defaults to the stable-fpm channel" { + assert_published_images docker-compose.yml "" stable-fpm +} + +@test "Garage S3 Compose defaults to the stable-fpm channel" { + assert_published_images docker-compose-garages3.yml "" stable-fpm +} + +@test "standard Compose applies an arbitrary image tag to both app and cron" { + assert_published_images docker-compose.yml "test-version-fpm" "test-version-fpm" +} + +@test "Garage S3 Compose applies an arbitrary image tag to both app and cron" { + assert_published_images docker-compose-garages3.yml "test-version-fpm" "test-version-fpm" +} + +@test "sample environment selects the moving stable-fpm channel" { + run grep -Fx "NEXTCLOUD_VERSION=stable-fpm" "$REPO_ROOT/.env.example" + [ "$status" -eq 0 ] +} + +@test "maintainer builds use the reviewed upstream base independent of the Compose channel" { + local approved_base target + approved_base=$(jq -er '.versions[-1].image' "$REPO_ROOT/.github/nextcloud-supported.json") + + for target in test-current-app-image scan-images; do + run make --no-print-directory -n -C "$REPO_ROOT" "$target" + [ "$status" -eq 0 ] + [[ "$output" == *"base_image='$approved_base'"* ]] + [[ "$output" == *'NEXTCLOUD_BASE_IMAGE=$base_image'* ]] + done +}