From 84341f4c710ce1e2f8b2278ca3f05ccb4938722a Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 3 Jun 2026 11:05:31 +0200 Subject: [PATCH 01/12] Use test path that exists on macos, too Signed-off-by: Pablo Zmdl --- php/tests/tests/initial-setup.spec.js | 2 +- php/tests/tests/restore-instance.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/php/tests/tests/initial-setup.spec.js b/php/tests/tests/initial-setup.spec.js index 1f21f011..0a133943 100755 --- a/php/tests/tests/initial-setup.spec.js +++ b/php/tests/tests/initial-setup.spec.js @@ -69,7 +69,7 @@ test('Initial setup', async ({ page: setupPage }) => { const initialNextcloudPassword = await containersPage.locator('#initial-nextcloud-password').innerText(); // Set backup location and create backup - const borgBackupLocation = `/mnt/test/aio-${Math.floor(Math.random() * 2147483647)}` + const borgBackupLocation = `/tmp/test/aio-${Math.floor(Math.random() * 2147483647)}` await containersPage.locator('#borg_backup_host_location').click(); await containersPage.locator('#borg_backup_host_location').fill(borgBackupLocation); await containersPage.getByRole('button', { name: 'Submit backup location' }).click(); diff --git a/php/tests/tests/restore-instance.spec.js b/php/tests/tests/restore-instance.spec.js index 696a4376..439c21b1 100755 --- a/php/tests/tests/restore-instance.spec.js +++ b/php/tests/tests/restore-instance.spec.js @@ -32,7 +32,7 @@ test('Restore instance', async ({ page: setupPage }) => { // Reject invalid backup location await containersPage.locator('#borg_restore_host_location').click(); - await containersPage.locator('#borg_restore_host_location').fill('/mnt/test/aio-incorrect-path'); + await containersPage.locator('#borg_restore_host_location').fill('/tmp/test/aio-incorrect-path'); await containersPage.locator('#borg_restore_password').click(); await containersPage.locator('#borg_restore_password').fill(borgBackupPassword); await containersPage.getByRole('button', { name: 'Submit location and encryption password' }).click() From 39b5b14ead5996b73bbc8bd4f5f32e0dbf26d524 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 3 Jun 2026 11:04:08 +0200 Subject: [PATCH 02/12] Enable running playwright tests locally They now are running via a docker compose setup, which can be executed via ./php/tests/run.sh locally, and also gets called from the github workflow. Signed-off-by: Pablo Zmdl --- php/tests/Containers/composer/Dockerfile | 4 + php/tests/compose.yaml | 102 +++++++++++++++++++++++ php/tests/run.sh | 71 ++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 php/tests/Containers/composer/Dockerfile create mode 100644 php/tests/compose.yaml create mode 100755 php/tests/run.sh diff --git a/php/tests/Containers/composer/Dockerfile b/php/tests/Containers/composer/Dockerfile new file mode 100644 index 00000000..a483458b --- /dev/null +++ b/php/tests/Containers/composer/Dockerfile @@ -0,0 +1,4 @@ +FROM docker.io/library/composer:latest + +RUN pecl bundle -d /usr/src/php/ext apcu \ + && docker-php-ext-install apcu \ No newline at end of file diff --git a/php/tests/compose.yaml b/php/tests/compose.yaml new file mode 100644 index 00000000..9406d66c --- /dev/null +++ b/php/tests/compose.yaml @@ -0,0 +1,102 @@ +# This setup expects that you run the services via profiles! +# Usage: docker compose --profile local-code up +# or: docker compose --profile code-from-image up + +name: nextcloud-aio-test +services: + composer: + image: localhost/composer:latest + build: Containers/composer + pull_policy: never + volumes: + - ..:/app + working_dir: /app + command: |- + bash -c ' + test -d ./data && rm -r ./data + test -d ./session && rm -r ./session + composer install --no-dev + composer clear-cache + ' + + app-base: + image: ghcr.io/nextcloud-releases/all-in-one:develop${ARM64_SUFFIX-} + pull_policy: always # Always pull so we don't risk to run into the "Update for mastercontainer" page. + init: true + restart: always + ports: + - "8080:8080" + volumes: + - nextcloud_aio_mastercontainer:/mnt/docker-aio-config + - /var/run/docker.sock:/var/run/docker.sock:ro + profiles: + - none + environment: + SKIP_DOMAIN_VALIDATION: ${SKIP_DOMAIN_VALIDATION-true} + APACHE_PORT: 11000 + entrypoint: bash /start.sh + + app-code-from-image: + extends: app-base + container_name: nextcloud-aio-mastercontainer + profiles: + - code-from-image + + app-local-code: + extends: app-base + container_name: nextcloud-aio-mastercontainer + depends_on: + composer: + condition: service_completed_successfully + volumes: + - ..:/var/www/docker-aio/php + - ../../Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile + - ../../Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile + - ../../Containers/mastercontainer/start.sh:/start.sh + profiles: + - local-code + + test-runner-base: + image: mcr.microsoft.com/playwright:v1.56.1 + volumes: + - ..:/app + working_dir: /app + ports: + - '9323:9323' # to view test reports + profiles: + - none + environment: + BASE_URL: "https://nextcloud-aio-mastercontainer:8080" + DEBUG: "pw:api" + command: |- + bash -c " + cd tests + # Install dependencies + npm ci --omit=optional + # Run the initial setup tests + npx playwright test "${TESTS_FILE-}" + exit $? + " + + test-runner-code-from-image: + extends: test-runner-base + container_name: test-runner + profiles: + - code-from-image + depends_on: + app: + condition: service_healthy + + test-runner-local-code: + extends: test-runner-base + container_name: test-runner + profiles: + - local-code + depends_on: + app-local-code: + condition: service_healthy + +volumes: + nextcloud_aio_mastercontainer: + name: nextcloud_aio_mastercontainer + diff --git a/php/tests/run.sh b/php/tests/run.sh new file mode 100755 index 00000000..152e7d46 --- /dev/null +++ b/php/tests/run.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +if [[ "$1" = -* ]]; then + echo "Usage $(basename "$0") [PLAYWRIGHT_TESTS_FILE]" + exit 1 +fi + +cd "$(dirname "$0")/../.." || { + echo 'Cannot change to base directory, something is seriously wrong!' + exit 1 +} + +DOCO="docker compose -f ./php/tests/compose.yaml" + +if [[ $(uname -m) = 'arm64' ]]; then + export ARM64_SUFFIX='-arm64' +fi + +run_tests() { + export TESTS_FILE="$1" + export SKIP_DOMAIN_VALIDATION + + if [[ -n "$TEST_CODE_FROM_IMAGE" ]]; then + profile="code-from-image" + else + profile="local-code" + fi + + # Clean up old containers and volumes + docker container rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} > /dev/null 2>&1 + docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} > /dev/null 2>&1 + $DOCO --profile $profile down -v + sleep 1 + + echo -e "\n 📣 Running playwright tests for ${TESTS_FILE} with SKIP_DOMAIN_VALIDATION=$SKIP_DOMAIN_VALIDATION and profile '$profile'\n" + $DOCO --profile $profile run --remove-orphans test-runner-$profile + exitcode=$? + if test $exitcode -gt 0; then + for container in nextcloud-aio-{mastercontainer,borgbackup,desec-mock}; do + if docker container list --format="{{ .Names }}" | grep -q "$container"; then + echo -e "\n 📣 Log output from container ${container}:\n" + docker logs "$container" + fi + done + # Exit on failure so further test files don't even run. + exit $exitcode + fi +} + + +if [[ -n "$1" ]]; then + if [[ ! -f "$1" ]]; then + echo "Error: file '$1' does not exist." + exit 1 + fi + # Not using coreutils' `realpath --relative-to` here since that is not available on BSD/mac systems. + fullpath="$(realpath "$1")" + prefix="$(realpath ./php/tests)" + relpath="${fullpath#"$prefix"/}" + + if test -z "$SKIP_DOMAIN_VALIDATION"; then + SKIP_DOMAIN_VALIDATION=false + fi + run_tests "$relpath" +else + SKIP_DOMAIN_VALIDATION=true + run_tests tests/initial-setup.spec.js + sleep 1 + SKIP_DOMAIN_VALIDATION=false + run_tests tests/restore-instance.spec.js +fi From 5729e4f7cf26804aae5e7e95e57c472a0f6da3f7 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 2 Jul 2026 13:19:07 +0200 Subject: [PATCH 03/12] Make start.sh executable The error about not being allowed to execute /start.sh were due to the file permissions mounted from the local disk Signed-off-by: Pablo Zmdl --- Containers/mastercontainer/start.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 Containers/mastercontainer/start.sh diff --git a/Containers/mastercontainer/start.sh b/Containers/mastercontainer/start.sh old mode 100644 new mode 100755 From 4e897aca902c7b6c2f41a60d3d89b788ab8d4a90 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Tue, 16 Jun 2026 17:16:32 +0200 Subject: [PATCH 04/12] Make playwright use 'dot' reporter and always exit at the end Signed-off-by: Pablo Zmdl --- php/tests/compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/php/tests/compose.yaml b/php/tests/compose.yaml index 9406d66c..4844bb0c 100644 --- a/php/tests/compose.yaml +++ b/php/tests/compose.yaml @@ -68,6 +68,7 @@ services: environment: BASE_URL: "https://nextcloud-aio-mastercontainer:8080" DEBUG: "pw:api" + CI: "true" command: |- bash -c " cd tests From 62ba204537c377bbce4a6508a472aa72ba4f2563 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 3 Jun 2026 11:06:59 +0200 Subject: [PATCH 05/12] Run playwright tests in CI via compose setup Signed-off-by: Pablo Zmdl --- .github/workflows/playwright-on-push.yml | 94 +------------------ .../playwright-on-workflow-dispatch.yml | 71 ++------------ 2 files changed, 11 insertions(+), 154 deletions(-) diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml index 4391dc4c..b43a4f19 100644 --- a/.github/workflows/playwright-on-push.yml +++ b/.github/workflows/playwright-on-push.yml @@ -34,99 +34,13 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - with: - node-version: 24.15.0 - - - name: Install dependencies - run: cd php/tests && npm ci --omit=optional - - - name: Install Playwright Browsers - run: cd php/tests && npx playwright install --with-deps chromium - - - name: Set up php 8.5 - uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.37.0 - with: - extensions: apcu - php-version: 8.5 - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Adjust some things and fix permissions - run: | - cd php - rm -r ./data - rm -r ./session - composer install --no-dev - composer clear-cache - sudo chmod 777 -R ../ - - - name: Start fresh development server - run: | - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - docker pull ghcr.io/nextcloud-releases/all-in-one:develop - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume ./php:/var/www/docker-aio/php \ - --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \ - --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \ - --volume ./Containers/mastercontainer/start.sh:/start.sh \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env SKIP_DOMAIN_VALIDATION=true \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 - - name: Run Playwright tests for initial setup - run: | - cd php/tests - export DEBUG=pw:api - if ! npx playwright test tests/initial-setup.spec.js; then - docker logs nextcloud-aio-mastercontainer - docker logs nextcloud-aio-borgbackup - exit 1 - fi - - - name: Start fresh development server - run: | - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume ./php:/var/www/docker-aio/php \ - --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \ - --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \ - --volume ./Containers/mastercontainer/start.sh:/start.sh \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env SKIP_DOMAIN_VALIDATION=false \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 + run: ./php/tests/run.sh ./php/tests/tests/initial-setup.spec.js + env: + SKIP_DOMAIN_VALIDATION: 'true' - name: Run Playwright tests for backup restore - run: | - cd php/tests - export DEBUG=pw:api - if ! npx playwright test tests/restore-instance.spec.js; then - docker logs nextcloud-aio-mastercontainer - docker logs nextcloud-aio-borgbackup - exit 1 - fi + run: ./php/tests/run.sh ./php/tests/tests/restore-instance.spec.js - name: Start deSEC mock and fresh development server run: | diff --git a/.github/workflows/playwright-on-workflow-dispatch.yml b/.github/workflows/playwright-on-workflow-dispatch.yml index a605c511..a9419889 100644 --- a/.github/workflows/playwright-on-workflow-dispatch.yml +++ b/.github/workflows/playwright-on-workflow-dispatch.yml @@ -19,72 +19,15 @@ jobs: steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - with: - node-version: 24.15.0 - - - name: Install dependencies - run: cd php/tests && npm ci --omit=optional - - - name: Install Playwright Browsers - run: cd php/tests && npx playwright install --with-deps chromium - - - name: Start fresh development server - run: | - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - docker pull ghcr.io/nextcloud-releases/all-in-one:develop - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env SKIP_DOMAIN_VALIDATION=true \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 - - name: Run Playwright tests for initial setup - run: | - cd php/tests - export DEBUG=pw:api - if ! npx playwright test tests/initial-setup.spec.js; then - docker logs nextcloud-aio-mastercontainer - docker logs nextcloud-aio-borgbackup - exit 1 - fi - - - name: Start fresh development server - run: | - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env SKIP_DOMAIN_VALIDATION=false \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 + env: + TEST_CODE_FROM_IMAGE: yes + run: ./php/tests/run.sh ./php/tests/tests/initial-setup.spec.js - name: Run Playwright tests for backup restore - run: | - cd php/tests - export DEBUG=pw:api - if ! npx playwright test tests/restore-instance.spec.js; then - docker logs nextcloud-aio-mastercontainer - docker logs nextcloud-aio-borgbackup - exit 1 - fi + env: + TEST_CODE_FROM_IMAGE: yes + run: ./php/tests/run.sh ./php/tests/tests/restore-instance.spec.js - name: Start deSEC mock and fresh development server run: | @@ -151,4 +94,4 @@ jobs: name: playwright-report path: php/tests/playwright-report/ retention-days: 14 - overwrite: true + overwrite: true \ No newline at end of file From 22a5b652916adfd709c1dd1fc1773f96cd408f8f Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 25 Jun 2026 17:11:03 +0200 Subject: [PATCH 06/12] Refactor playwright login helper It now works for all test specs and does not require a password from the environment. Signed-off-by: Pablo Zmdl --- php/tests/tests/desec-existing-slug.spec.js | 2 +- php/tests/tests/desec-existing.spec.js | 2 +- php/tests/tests/desec-helpers.js | 22 ------------------- php/tests/tests/desec-register.spec.js | 2 +- php/tests/tests/helpers.js | 24 +++++++++++++++++++++ php/tests/tests/initial-setup.spec.js | 14 ++---------- php/tests/tests/restore-instance.spec.js | 14 ++---------- 7 files changed, 31 insertions(+), 49 deletions(-) delete mode 100644 php/tests/tests/desec-helpers.js create mode 100644 php/tests/tests/helpers.js diff --git a/php/tests/tests/desec-existing-slug.spec.js b/php/tests/tests/desec-existing-slug.spec.js index 01f1bfbe..179116cc 100644 --- a/php/tests/tests/desec-existing-slug.spec.js +++ b/php/tests/tests/desec-existing-slug.spec.js @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { DESEC_MOCK_URL, logInToContainersPage } from './desec-helpers.js'; +import { DESEC_MOCK_URL, logInToContainersPage } from './helpers.js'; // Exercises reusing a slug the user already owns on an existing deSEC account. This is the // case that previously failed: deSEC answers POST /domains/ with 403 "Domain limit exceeded" diff --git a/php/tests/tests/desec-existing.spec.js b/php/tests/tests/desec-existing.spec.js index 7c5af394..0c8efacb 100644 --- a/php/tests/tests/desec-existing.spec.js +++ b/php/tests/tests/desec-existing.spec.js @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { DESEC_MOCK_URL, logInToContainersPage } from './desec-helpers.js'; +import { DESEC_MOCK_URL, logInToContainersPage } from './helpers.js'; // Exercises the deSEC "I already have a verified account" login path: supplying a valid // password logs straight in and registers the domain in one step (no email-verification diff --git a/php/tests/tests/desec-helpers.js b/php/tests/tests/desec-helpers.js deleted file mode 100644 index 9332b308..00000000 --- a/php/tests/tests/desec-helpers.js +++ /dev/null @@ -1,22 +0,0 @@ -// Shared helpers for the deSEC Playwright scenarios. -// -// The deSEC mock is wired up by seeding configuration.json (see seed-desec-mock-config.php), -// which makes AIO consider itself already installed: /setup no longer renders the -// initial-password page. The seed step therefore writes a known master password (AIO_TEST_PASSWORD) -// that we log in with directly here instead of scraping it from /setup. - -export const DESEC_MOCK_URL = process.env.DESEC_MOCK_URL ?? 'http://localhost:8090'; - -const AIO_PASSWORD = process.env.AIO_TEST_PASSWORD; - -export async function logInToContainersPage(page) { - if (!AIO_PASSWORD) { - throw new Error('AIO_TEST_PASSWORD must be set to the master password seeded into configuration.json'); - } - await page.goto('./'); - await page.locator('#master-password').click(); - await page.locator('#master-password').fill(AIO_PASSWORD); - await page.getByRole('button', { name: 'Log in' }).click(); - await page.waitForURL('./containers'); - return page; -} diff --git a/php/tests/tests/desec-register.spec.js b/php/tests/tests/desec-register.spec.js index 7f2589ab..762012d2 100644 --- a/php/tests/tests/desec-register.spec.js +++ b/php/tests/tests/desec-register.spec.js @@ -1,5 +1,5 @@ import { test, expect } from '@playwright/test'; -import { DESEC_MOCK_URL, logInToContainersPage } from './desec-helpers.js'; +import { DESEC_MOCK_URL, logInToContainersPage } from './helpers.js'; // Drives the real AIO interface through the full deSEC "register a free domain" flow // against the local mock (php/tests/desec-mock.mjs). The mastercontainer's diff --git a/php/tests/tests/helpers.js b/php/tests/tests/helpers.js new file mode 100644 index 00000000..1246dad7 --- /dev/null +++ b/php/tests/tests/helpers.js @@ -0,0 +1,24 @@ +// Shared helpers for the deSEC Playwright scenarios. +// +// The deSEC mock is wired up by seeding configuration.json (see seed-desec-mock-config.php), +// which makes AIO consider itself already installed: /setup no longer renders the +// initial-password page. The seed step therefore writes a known master password (AIO_TEST_PASSWORD) +// that we log in with directly here instead of scraping it from /setup. + +export const DESEC_MOCK_URL = process.env.DESEC_MOCK_URL ?? 'http://localhost:8090'; + +export async function logInToContainersPage(setupPage) { + // Extract initial password + await setupPage.goto('./setup'); + const password = await setupPage.locator('#initial-password').innerText() + const containersPagePromise = setupPage.waitForEvent('popup'); + await setupPage.getByRole('link', { name: 'Open Nextcloud AIO login ↗' }).click(); + const containersPage = await containersPagePromise; + + // Log in and wait for redirect + await containersPage.locator('#master-password').click(); + await containersPage.locator('#master-password').fill(password); + await containersPage.getByRole('button', { name: 'Log in' }).click(); + await containersPage.waitForURL('./containers'); + return containersPage; +} diff --git a/php/tests/tests/initial-setup.spec.js b/php/tests/tests/initial-setup.spec.js index 0a133943..14636d8c 100755 --- a/php/tests/tests/initial-setup.spec.js +++ b/php/tests/tests/initial-setup.spec.js @@ -1,21 +1,11 @@ import { test, expect } from '@playwright/test'; import { writeFileSync } from 'node:fs' +import { logInToContainersPage } from './helpers.js'; test('Initial setup', async ({ page: setupPage }) => { test.setTimeout(10 * 60 * 1000) - // Extract initial password - await setupPage.goto('./setup'); - const password = await setupPage.locator('#initial-password').innerText() - const containersPagePromise = setupPage.waitForEvent('popup'); - await setupPage.getByRole('link', { name: 'Open Nextcloud AIO login ↗' }).click(); - const containersPage = await containersPagePromise; - - // Log in and wait for redirect - await containersPage.locator('#master-password').click(); - await containersPage.locator('#master-password').fill(password); - await containersPage.getByRole('button', { name: 'Log in' }).click(); - await containersPage.waitForURL('./containers'); + const containersPage = await logInToContainersPage(setupPage); // Reject IP addresses await containersPage.locator('#domain').click(); diff --git a/php/tests/tests/restore-instance.spec.js b/php/tests/tests/restore-instance.spec.js index 439c21b1..9f045a72 100755 --- a/php/tests/tests/restore-instance.spec.js +++ b/php/tests/tests/restore-instance.spec.js @@ -1,5 +1,6 @@ import { test, expect } from '@playwright/test'; import { readFileSync } from 'node:fs'; +import { logInToContainersPage } from './helpers.js'; test('Restore instance', async ({ page: setupPage }) => { test.setTimeout(10 * 60 * 1000) @@ -11,18 +12,7 @@ test('Restore instance', async ({ page: setupPage }) => { borgBackupPassword, } = JSON.parse(readFileSync('test_data.json')) - // Extract initial password - await setupPage.goto('./setup'); - const password = await setupPage.locator('#initial-password').innerText() - const containersPagePromise = setupPage.waitForEvent('popup'); - await setupPage.getByRole('link', { name: 'Open Nextcloud AIO login ↗' }).click(); - const containersPage = await containersPagePromise; - - // Log in and wait for redirect - await containersPage.locator('#master-password').click(); - await containersPage.locator('#master-password').fill(password); - await containersPage.getByRole('button', { name: 'Log in' }).click(); - await containersPage.waitForURL('./containers'); + const containersPage = await logInToContainersPage(setupPage); // Reject example.com (requires enabled domain validation) await containersPage.locator('#domain').click(); From 54c79c404ce887d20af5b26c46fc4903b8da0b8e Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 25 Jun 2026 17:12:23 +0200 Subject: [PATCH 07/12] Remove console output in playwright test Signed-off-by: Pablo Zmdl --- php/tests/tests/initial-setup.spec.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/php/tests/tests/initial-setup.spec.js b/php/tests/tests/initial-setup.spec.js index 14636d8c..b766878c 100755 --- a/php/tests/tests/initial-setup.spec.js +++ b/php/tests/tests/initial-setup.spec.js @@ -33,7 +33,6 @@ test('Initial setup', async ({ page: setupPage }) => { await containersPage.locator('#timezone').click(); await containersPage.locator('#timezone').fill('Invalid time zone'); containersPage.once('dialog', dialog => { - console.log(`Dialog message: ${dialog.message()}`) dialog.accept() }); await containersPage.getByRole('button', { name: 'Submit timezone' }).click(); @@ -43,7 +42,6 @@ test('Initial setup', async ({ page: setupPage }) => { await containersPage.locator('#timezone').click(); await containersPage.locator('#timezone').fill('Europe/Berlin'); containersPage.once('dialog', dialog => { - console.log(`Dialog message: ${dialog.message()}`) dialog.accept() }); await containersPage.getByRole('button', { name: 'Submit timezone' }).click(); @@ -64,7 +62,6 @@ test('Initial setup', async ({ page: setupPage }) => { await containersPage.locator('#borg_backup_host_location').fill(borgBackupLocation); await containersPage.getByRole('button', { name: 'Submit backup location' }).click(); containersPage.once('dialog', dialog => { - console.log(`Dialog message: ${dialog.message()}`) dialog.accept() }); await containersPage.getByRole('button', { name: 'Create backup' }).click(); From ce8a7f6f46258c73e9f90a4f07d72e88729892f5 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 25 Jun 2026 17:13:20 +0200 Subject: [PATCH 08/12] Allow to set desec URLs from environment Signed-off-by: Pablo Zmdl --- php/src/Data/ConfigurationManager.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index bb33515b..31c5d7f3 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -231,7 +231,7 @@ class ConfigurationManager * stray env var redirecting it in production. */ public string $desecApiBase { - get => $this->get('desec_api_base', 'https://desec.io/api/v1'); + get => $this->getEnvironmentalVariableOrConfig('TESTING___DESEC_API_BASE', 'desec_api_base', 'https://desec.io/api/v1'); } /** @@ -239,7 +239,7 @@ class ConfigurationManager * 'desec_update_url' config key (configuration.json) only — see desecApiBase. */ public string $desecUpdateUrl { - get => $this->get('desec_update_url', 'https://update.dedyn.io/'); + get => $this->getEnvironmentalVariableOrConfig('TESTING___DESEC_UPDATE_URL', 'desec_update_url', 'https://update.dedyn.io/'); } public string $desecToken { From bf5296b070c7637fa70f95cc8836aa067c21b46c Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 25 Jun 2026 17:17:16 +0200 Subject: [PATCH 09/12] Run desec playwright tests via docker compose, too Signed-off-by: Pablo Zmdl --- .github/workflows/playwright-on-push.yml | 69 ++----------------- .../playwright-on-workflow-dispatch.yml | 68 ++++-------------- php/tests/compose.yaml | 13 ++++ php/tests/run.sh | 6 ++ 4 files changed, 37 insertions(+), 119 deletions(-) diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml index b43a4f19..2f6de864 100644 --- a/.github/workflows/playwright-on-push.yml +++ b/.github/workflows/playwright-on-push.yml @@ -20,10 +20,6 @@ concurrency: env: BASE_URL: https://localhost:8080 - # Master password seeded into configuration.json for the deSEC test. Seeding the config - # makes AIO consider itself already installed, so /setup no longer generates/shows a - # password; the seed helper writes this one and the test logs in with it directly. - AIO_TEST_PASSWORD: correct horse battery staple aircraft jobs: test: @@ -42,67 +38,14 @@ jobs: - name: Run Playwright tests for backup restore run: ./php/tests/run.sh ./php/tests/tests/restore-instance.spec.js - - name: Start deSEC mock and fresh development server - run: | - # The deSEC flow must not touch the real deSEC API, so point it at a local mock. - node php/tests/desec-mock.mjs 8090 > desec-mock.log 2>&1 & - echo $! > desec-mock.pid - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup,caddy,dnsmasq} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - # The deSEC registration entry point renders regardless of SKIP_DOMAIN_VALIDATION, - # and the deSEC flow sets the domain with validation skipped internally, so no - # SKIP_DOMAIN_VALIDATION override is needed here. - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --add-host host.docker.internal:host-gateway \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume ./php:/var/www/docker-aio/php \ - --volume ./community-containers:/var/www/docker-aio/community-containers \ - --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \ - --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \ - --volume ./Containers/mastercontainer/start.sh:/start.sh \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 - # Point the running interface at the deSEC mock via configuration.json (config-only, - # no env override). The container runs PHP and has ./php mounted, so use the helper. - # AIO_TEST_PASSWORD is forwarded so the helper can seed the master password too. - docker exec --env AIO_TEST_PASSWORD="$AIO_TEST_PASSWORD" nextcloud-aio-mastercontainer \ - php /var/www/docker-aio/php/tests/seed-desec-mock-config.php \ - http://host.docker.internal:8090/api/v1 http://host.docker.internal:8090/update + - name: Run Playwright tests for desec registration + run: ./php/tests/run.sh ./php/tests/tests/desec-register.spec.js - - name: Run Playwright tests for deSEC domain registration - run: | - export DEBUG=pw:api - export DESEC_MOCK_URL=http://localhost:8090 + - name: Run Playwright tests for desec with existing account + run: ./php/tests/run.sh ./php/tests/tests/desec-existing.spec.js - # Each deSEC scenario ends by registering a domain, which persists in - # configuration.json and would hide the registration UI for the next scenario. - # Run each spec as its own step and re-seed (reset the deSEC state) in between. - reseed() { - docker exec --env AIO_TEST_PASSWORD="$AIO_TEST_PASSWORD" nextcloud-aio-mastercontainer \ - php /var/www/docker-aio/php/tests/seed-desec-mock-config.php \ - http://host.docker.internal:8090/api/v1 http://host.docker.internal:8090/update - } - dump_logs() { - docker logs nextcloud-aio-mastercontainer - echo "--- deSEC mock log ---" - cat "$GITHUB_WORKSPACE/desec-mock.log" || true - } - - cd php/tests - if ! npx playwright test tests/desec-register.spec.js; then dump_logs; exit 1; fi - reseed - if ! npx playwright test tests/desec-existing.spec.js; then dump_logs; exit 1; fi - reseed - if ! npx playwright test tests/desec-existing-slug.spec.js; then dump_logs; exit 1; fi - kill "$(cat "$GITHUB_WORKSPACE/desec-mock.pid")" || true + - name: Run Playwright tests for desec with existing slug + run: ./php/tests/run.sh ./php/tests/tests/desec-existing-slug.spec.js - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ !cancelled() }} diff --git a/.github/workflows/playwright-on-workflow-dispatch.yml b/.github/workflows/playwright-on-workflow-dispatch.yml index a9419889..e6e48c7f 100644 --- a/.github/workflows/playwright-on-workflow-dispatch.yml +++ b/.github/workflows/playwright-on-workflow-dispatch.yml @@ -29,64 +29,20 @@ jobs: TEST_CODE_FROM_IMAGE: yes run: ./php/tests/run.sh ./php/tests/tests/restore-instance.spec.js - - name: Start deSEC mock and fresh development server - run: | - # The deSEC flow must not touch the real deSEC API, so point it at a local mock. - node php/tests/desec-mock.mjs 8090 > desec-mock.log 2>&1 & - echo $! > desec-mock.pid - docker rm --force nextcloud-aio-{mastercontainer,apache,notify-push,nextcloud,redis,database,domaincheck,whiteboard,imaginary,talk,collabora,borgbackup,caddy,dnsmasq} || true - docker volume rm nextcloud_aio_{mastercontainer,apache,database,database_dump,nextcloud,nextcloud_data,redis,backup_cache,elasticsearch} || true - # The deSEC registration entry point renders regardless of SKIP_DOMAIN_VALIDATION, - # and the deSEC flow sets the domain with validation skipped internally, so no - # SKIP_DOMAIN_VALIDATION override is needed here. - # Mount ./php/tests so the locally-checked-out DesecManager is exercised. - docker run \ - -d \ - --init \ - --name nextcloud-aio-mastercontainer \ - --restart always \ - --publish 8080:8080 \ - --add-host host.docker.internal:host-gateway \ - --volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \ - --volume ./php/tests:/var/www/docker-aio/php/tests \ - --volume /var/run/docker.sock:/var/run/docker.sock:ro \ - --env APACHE_PORT=11000 \ - ghcr.io/nextcloud-releases/all-in-one:develop - echo Waiting for 10 seconds for the development container to start ... - sleep 10 - # Point the running interface at the deSEC mock via configuration.json (config-only, - # no env override). The container runs PHP and has ./php mounted, so use the helper. - # AIO_TEST_PASSWORD is forwarded so the helper can seed the master password too. - docker exec --env AIO_TEST_PASSWORD="$AIO_TEST_PASSWORD" nextcloud-aio-mastercontainer \ - php /var/www/docker-aio/php/tests/seed-desec-mock-config.php \ - http://host.docker.internal:8090/api/v1 http://host.docker.internal:8090/update + - name: Run Playwright tests for desec registration + env: + TEST_CODE_FROM_IMAGE: yes + run: ./php/tests/run.sh ./php/tests/tests/desec-register.spec.js - - name: Run Playwright tests for deSEC domain registration - run: | - export DEBUG=pw:api - export DESEC_MOCK_URL=http://localhost:8090 + - name: Run Playwright tests for desec with existing account + env: + TEST_CODE_FROM_IMAGE: yes + run: ./php/tests/run.sh ./php/tests/tests/desec-existing.spec.js - # Each deSEC scenario ends by registering a domain, which persists in - # configuration.json and would hide the registration UI for the next scenario. - # Run each spec as its own step and re-seed (reset the deSEC state) in between. - reseed() { - docker exec --env AIO_TEST_PASSWORD="$AIO_TEST_PASSWORD" nextcloud-aio-mastercontainer \ - php /var/www/docker-aio/php/tests/seed-desec-mock-config.php \ - http://host.docker.internal:8090/api/v1 http://host.docker.internal:8090/update - } - dump_logs() { - docker logs nextcloud-aio-mastercontainer - echo "--- deSEC mock log ---" - cat "$GITHUB_WORKSPACE/desec-mock.log" || true - } - - cd php/tests - if ! npx playwright test tests/desec-register.spec.js; then dump_logs; exit 1; fi - reseed - if ! npx playwright test tests/desec-existing.spec.js; then dump_logs; exit 1; fi - reseed - if ! npx playwright test tests/desec-existing-slug.spec.js; then dump_logs; exit 1; fi - kill "$(cat "$GITHUB_WORKSPACE/desec-mock.pid")" || true + - name: Run Playwright tests for desec with existing slug + env: + TEST_CODE_FROM_IMAGE: yes + run: ./php/tests/run.sh ./php/tests/tests/desec-existing-slug.spec.js - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ !cancelled() }} diff --git a/php/tests/compose.yaml b/php/tests/compose.yaml index 4844bb0c..73de8e0f 100644 --- a/php/tests/compose.yaml +++ b/php/tests/compose.yaml @@ -34,6 +34,8 @@ services: environment: SKIP_DOMAIN_VALIDATION: ${SKIP_DOMAIN_VALIDATION-true} APACHE_PORT: 11000 + TESTING___DESEC_API_BASE: http://desec-mock:8090/api/v1 + TESTING___DESEC_UPDATE_URL: http://desec-mock:8090/update entrypoint: bash /start.sh app-code-from-image: @@ -56,6 +58,13 @@ services: profiles: - local-code + desec-mock: + image: docker.io/library/node:latest + volumes: + - ..:/app/php + working_dir: /app + command: node php/tests/desec-mock.mjs 8090 2>&1 + test-runner-base: image: mcr.microsoft.com/playwright:v1.56.1 volumes: @@ -65,10 +74,14 @@ services: - '9323:9323' # to view test reports profiles: - none + depends_on: + desec-mock: + condition: service_started environment: BASE_URL: "https://nextcloud-aio-mastercontainer:8080" DEBUG: "pw:api" CI: "true" + DESEC_MOCK_URL: "http://desec-mock:8090" command: |- bash -c " cd tests diff --git a/php/tests/run.sh b/php/tests/run.sh index 152e7d46..2481ce1f 100755 --- a/php/tests/run.sh +++ b/php/tests/run.sh @@ -68,4 +68,10 @@ else sleep 1 SKIP_DOMAIN_VALIDATION=false run_tests tests/restore-instance.spec.js + sleep 1 + run_tests tests/desec-register.spec.js + sleep 1 + run_tests tests/desec-existing.spec.js + sleep 1 + run_tests tests/desec-existing-slug.spec.js fi From 2f8df05f323fbb449c3a21d0e22edd67e2a7eec3 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 25 Jun 2026 17:17:40 +0200 Subject: [PATCH 10/12] Remove useless seeding of desec mock The AIO password can be read from the setup page, if the tests are executed on a properly cleaned plate (as they are now). Resetting the desec tokens is also not necessary anymore with a properly cleaned plate. The desec URLs can now be provided through environment variables. This makes the whole setup simpler and less brittle. Signed-off-by: Pablo Zmdl --- php/tests/seed-desec-mock-config.php | 54 ---------------------------- 1 file changed, 54 deletions(-) delete mode 100644 php/tests/seed-desec-mock-config.php diff --git a/php/tests/seed-desec-mock-config.php b/php/tests/seed-desec-mock-config.php deleted file mode 100644 index 78d27de0..00000000 --- a/php/tests/seed-desec-mock-config.php +++ /dev/null @@ -1,54 +0,0 @@ - - -declare(strict_types=1); - -$apiBase = $argv[1] ?? ''; -$updateUrl = $argv[2] ?? ''; -if ($apiBase === '' || $updateUrl === '') { - fwrite(STDERR, "usage: php seed-desec-mock-config.php \n"); - exit(1); -} - -$password = getenv('AIO_TEST_PASSWORD'); -if ($password === false || $password === '') { - fwrite(STDERR, "AIO_TEST_PASSWORD env var must be set to the master password to seed\n"); - exit(1); -} - -$file = '/mnt/docker-aio-config/data/configuration.json'; -$config = is_file($file) ? json_decode((string)file_get_contents($file), true) : []; -if (!is_array($config)) { - $config = []; -} -$config['desec_api_base'] = $apiBase; -$config['desec_update_url'] = $updateUrl; -$config['password'] = $password; - -// Reset any deSEC state from a previous test run so the registration UI renders again. -unset($config['domain'], $config['desec_email']); -if (isset($config['secrets']) && is_array($config['secrets'])) { - unset($config['secrets']['DESEC_TOKEN'], $config['secrets']['DESEC_PASSWORD']); -} - -file_put_contents($file, json_encode($config, JSON_PRETTY_PRINT)); -echo "Seeded deSEC mock config into $file\n"; From 6589878dc624861d6a4decbac3494da46e9f363c Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Wed, 1 Jul 2026 15:07:48 +0200 Subject: [PATCH 11/12] Run NPM in standalone container and use working_dir properly Signed-off-by: Pablo Zmdl --- php/tests/compose.yaml | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/php/tests/compose.yaml b/php/tests/compose.yaml index 73de8e0f..937b8bb4 100644 --- a/php/tests/compose.yaml +++ b/php/tests/compose.yaml @@ -65,11 +65,18 @@ services: working_dir: /app command: node php/tests/desec-mock.mjs 8090 2>&1 + npm-installer: + image: docker.io/library/node:latest + volumes: + - ..:/app + working_dir: /app/tests + command: npm ci --omit=optional + test-runner-base: image: mcr.microsoft.com/playwright:v1.56.1 volumes: - ..:/app - working_dir: /app + working_dir: /app/tests ports: - '9323:9323' # to view test reports profiles: @@ -77,20 +84,14 @@ services: depends_on: desec-mock: condition: service_started + npm-installer: + condition: service_completed_successfully environment: BASE_URL: "https://nextcloud-aio-mastercontainer:8080" DEBUG: "pw:api" CI: "true" DESEC_MOCK_URL: "http://desec-mock:8090" - command: |- - bash -c " - cd tests - # Install dependencies - npm ci --omit=optional - # Run the initial setup tests - npx playwright test "${TESTS_FILE-}" - exit $? - " + command: npx playwright test "${TESTS_FILE-}" test-runner-code-from-image: extends: test-runner-base From bf473324ba10d08f5f0f19247035884189d71353 Mon Sep 17 00:00:00 2001 From: Pablo Zmdl Date: Thu, 2 Jul 2026 13:06:16 +0200 Subject: [PATCH 12/12] Pin and update test container image references regularly Assisted-by: Copilot v1.0.59 (Claude Sonnet 4.6) Signed-off-by: Pablo Zmdl --- .ci/update-test-container-image-reference.py | 87 +++++++++++++++++++ ...update-test-container-image-references.yml | 28 ++++++ php/tests/Containers/composer/Dockerfile | 2 +- php/tests/compose.yaml | 6 +- 4 files changed, 119 insertions(+), 4 deletions(-) create mode 100755 .ci/update-test-container-image-reference.py create mode 100644 .github/workflows/update-test-container-image-references.yml diff --git a/.ci/update-test-container-image-reference.py b/.ci/update-test-container-image-reference.py new file mode 100755 index 00000000..46ca9a61 --- /dev/null +++ b/.ci/update-test-container-image-reference.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later +"""Update container image digests in configured docker compose files and Dockerfiles. +The script will look for matches for `:@sha256:` in those files and update the digest, +if a different one is found for `:` at the registry. + +Requires: skopeo +""" +import re +import subprocess +import sys + +# Change this to add more files to check. +FILES = { + "compose": [ + "php/tests/compose.yaml", + ], + "dockerfile": [ + "php/tests/Containers/composer/Dockerfile", + ], +} + +PREFIX = { + "compose": "image: ", + "dockerfile": "FROM ", +} + +# Matches `:@sha256:` +IMAGE_RE = r"{prefix}([^\s@]+:[^\s@]+)@(sha256:[a-f0-9]+)" + +if subprocess.run(["which", "skopeo"], capture_output=True).returncode != 0: + sys.exit("Error: skopeo is not installed. Install it from https://github.com/containers/skopeo#installation") + + +def update_image_digest(content, full_ref, current_digest, file_path): + result = subprocess.run( + [ + "skopeo", "inspect", + "--override-os", "linux", + "--no-tags", + "--format", "{{.Digest}}", + f"docker://{full_ref}", + ], + capture_output=True, text=True, check=True, + ) + latest_digest = result.stdout.strip() + + msg_prefix = f"{file_path}: {full_ref}" + if latest_digest == current_digest: + print(f"{msg_prefix}: already up to date.") + return content + + old = f"{full_ref}@{current_digest}" + count = content.count(old) + content = content.replace(old, f"{full_ref}@{latest_digest}") + print(f"{msg_prefix}: updated {count} occurrence(s) to {latest_digest}.") + return content + + +def update_file(file_path, prefix): + with open(file_path) as f: + content = f.read() + + images = re.findall(IMAGE_RE.format(prefix=re.escape(prefix)), content) + if not images: + print(f"{file_path}: no pinned image references found.") + return + + # Run only once for every pair of file_path and image. + unique_images = list(dict.fromkeys(images)) + + for full_ref, current_digest in unique_images: + content = update_image_digest(content, full_ref, current_digest, file_path) + + with open(file_path, "w") as f: + f.write(content) + + +def main(): + for file_type, paths in FILES.items(): + for path in paths: + update_file(path, PREFIX[file_type]) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/update-test-container-image-references.yml b/.github/workflows/update-test-container-image-references.yml new file mode 100644 index 00000000..1354559c --- /dev/null +++ b/.github/workflows/update-test-container-image-references.yml @@ -0,0 +1,28 @@ +name: update-test-container-image-references + +on: + workflow_dispatch: + schedule: + - cron: '00 5 * * *' + +jobs: + update: + name: Update test container image references + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Install skopeo + run: sudo apt-get install -y skopeo + - name: Run update script + run: python3 .ci/update-image-reference.py + - name: Create Pull Request + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: "chore(tests): update test container image references" + signoff: true + title: Update test container image references + body: Automated update of node and playwright image digests in php/tests/compose.yaml + labels: dependencies, 3. to review + milestone: next + branch: update-test-container-image-references diff --git a/php/tests/Containers/composer/Dockerfile b/php/tests/Containers/composer/Dockerfile index a483458b..004e69bc 100644 --- a/php/tests/Containers/composer/Dockerfile +++ b/php/tests/Containers/composer/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/composer:latest +FROM docker.io/library/composer:2@sha256:7725eb4545c438629ae8bde3ef0bb9a5038ef566126ad878442a69007242d267 RUN pecl bundle -d /usr/src/php/ext apcu \ && docker-php-ext-install apcu \ No newline at end of file diff --git a/php/tests/compose.yaml b/php/tests/compose.yaml index 937b8bb4..73510efb 100644 --- a/php/tests/compose.yaml +++ b/php/tests/compose.yaml @@ -59,21 +59,21 @@ services: - local-code desec-mock: - image: docker.io/library/node:latest + image: docker.io/library/node:26@sha256:b46a10d964ad15136ebdf9012142131481caa0697d7a4d4eafe4bbabd818f876 volumes: - ..:/app/php working_dir: /app command: node php/tests/desec-mock.mjs 8090 2>&1 npm-installer: - image: docker.io/library/node:latest + image: docker.io/library/node:26@sha256:b46a10d964ad15136ebdf9012142131481caa0697d7a4d4eafe4bbabd818f876 volumes: - ..:/app working_dir: /app/tests command: npm ci --omit=optional test-runner-base: - image: mcr.microsoft.com/playwright:v1.56.1 + image: mcr.microsoft.com/playwright:v1.56.1@sha256:f1e7e01021efd65dd1a2c56064be399f3e4de00fd021ac561325f2bfbb2b837a volumes: - ..:/app working_dir: /app/tests