From 72d95a3438e9fc898e4ee722a8d9f5a99afa6ddd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:20:32 +0000 Subject: [PATCH 01/10] Initial plan From 3353b1fcf7a1f13244e591147d1aaade268ea37a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:23:59 +0000 Subject: [PATCH 02/10] feat: add pgseed image workflow and ci image selection Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/b7b4fd6b-0316-4721-93ca-0239a76241bc Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/actions/parse-version/script.php | 57 +++++++++- .github/workflows/build-pgseed.yml | 132 +++++++++++++++++++++++ .github/workflows/ci.yml | 4 +- README.md | 10 ++ docker/pgseed/Dockerfile | 22 ++++ docker/pgseed/seed.sh | 25 +++++ 6 files changed, 247 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/build-pgseed.yml create mode 100644 docker/pgseed/Dockerfile create mode 100644 docker/pgseed/seed.sh diff --git a/.github/actions/parse-version/script.php b/.github/actions/parse-version/script.php index e4875104..782e23e0 100644 --- a/.github/actions/parse-version/script.php +++ b/.github/actions/parse-version/script.php @@ -105,6 +105,59 @@ function container_image_name($moodle_branch, $php) { return "ghcr.io/catalyst/$repo:latest"; } +/** + * Resolve a MOODLE_XXX_STABLE branch to a Moodle minor (10 digit) version. + * + * @param string $moodleBranch + * @param array $updates + * @return string + */ +function moodle_minor_version($moodleBranch, array $updates): string { + if (!preg_match('/^MOODLE_(\d+)_STABLE$/', $moodleBranch, $matches)) { + return ''; + } + + $series = $matches[1]; + $major = substr($series, 0, 1); + $minor = ltrim(substr($series, 1), '0'); + if ($minor === '') { + $minor = '0'; + } + + $branch = $major . '.' . $minor; + foreach ($updates as $update) { + if (($update['branch'] ?? '') === $branch) { + return (string)($update['version'] ?? ''); + } + } + + return ''; +} + +/** + * Build a deterministic pgseed image reference for a matrix entry. + * + * @param array $entry + * @return string + */ +function pgseed_image_name(array $entry): string { + if (($entry['database'] ?? '') !== 'pgsql') { + return ''; + } + + $minor = $entry['moodle-minor'] ?? ''; + $pgsql = $entry['pgsql-ver'] ?? ''; + $php = $entry['php'] ?? ''; + $branch = $entry['moodle-branch'] ?? ''; + if ($minor === '' || $pgsql === '' || $php === '' || $branch === '') { + return ''; + } + + $branchSlug = str_replace('_', '-', strtolower($branch)); + $tag = "{$branchSlug}-php{$php}-pg{$pgsql}-m{$minor}"; + return "ghcr.io/catalyst/catalyst-moodle-workflows-pgseed:{$tag}"; +} + $preparedMatrix = array_filter($matrix['include'], function($entry) use($plugin, $updates, $matrix) { if (!isset($entry)) { @@ -205,9 +258,11 @@ function container_image_name($moodle_branch, $php) { // Add container image name and short branch name to each entry -$finalMatrix = array_map(function($entry) { +$finalMatrix = array_map(function($entry) use ($updates) { $entry['container'] = container_image_name($entry['moodle-branch'], $entry['php']); $entry['moodle-branch-short'] = preg_replace('/MOODLE_(.*)_STABLE/', '$1', $entry['moodle-branch']); + $entry['moodle-minor'] = moodle_minor_version($entry['moodle-branch'], $updates); + $entry['pgseed-image'] = pgseed_image_name($entry); return $entry; }, array_values($preparedMatrix)); diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml new file mode 100644 index 00000000..81322b33 --- /dev/null +++ b/.github/workflows/build-pgseed.yml @@ -0,0 +1,132 @@ +name: Build pgseed images + +on: + workflow_dispatch: + schedule: + - cron: '15 6 * * 1' + push: + branches: + - main + paths: + - '.github/actions/matrix/matrix_includes.yml' + - 'docker/pgseed/**' + - '.github/workflows/build-pgseed.yml' + +jobs: + build-matrix: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install PyYAML + run: pip install pyyaml + + - name: Build pgseed matrix + id: set-matrix + run: | + import json + import urllib.request + import yaml + + with open('.github/actions/matrix/matrix_includes.yml', encoding='utf-8') as f: + data = yaml.safe_load(f) + + with urllib.request.urlopen('https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8', timeout=30) as response: + updates_data = json.loads(response.read().decode('utf-8')) + + branch_to_minor = { + row['branch']: str(row['version']) + for row in updates_data.get('updates', {}).get('core', []) + if isinstance(row, dict) and row.get('branch') and row.get('version') + } + + def to_minor_branch(moodle_branch): + if not moodle_branch.startswith('MOODLE_') or not moodle_branch.endswith('_STABLE'): + return '' + digits = moodle_branch[len('MOODLE_'):-len('_STABLE')] + if not digits.isdigit() or len(digits) < 2: + return '' + major = digits[0] + minor = digits[1:].lstrip('0') or '0' + return f"{major}.{minor}" + + tuples = {} + for row in data.get('include', []): + if row.get('database') != 'pgsql': + continue + moodle_branch = row.get('moodle-branch', '') + minor_branch = to_minor_branch(moodle_branch) + moodle_minor = branch_to_minor.get(minor_branch, '') + if not moodle_minor: + # main does not have a stable 10-digit Moodle minor. + continue + + php = str(row.get('php', '')) + pgsql_ver = str(row.get('pgsql-ver', '')) + if not php or not pgsql_ver: + continue + + branch_slug = moodle_branch.replace('_', '-').lower() + image = 'ghcr.io/catalyst/catalyst-moodle-workflows-pgseed' + immutable_tag = f"{branch_slug}-php{php}-pg{pgsql_ver}-m{moodle_minor}" + mutable_tag = f"{branch_slug}-php{php}-pg{pgsql_ver}-latest" + + key = (moodle_branch, php, pgsql_ver, moodle_minor) + tuples[key] = { + 'moodle-branch': moodle_branch, + 'php': php, + 'pgsql-ver': pgsql_ver, + 'moodle-minor': moodle_minor, + 'image': image, + 'immutable-tag': immutable_tag, + 'mutable-tag': mutable_tag, + } + + matrix = sorted(tuples.values(), key=lambda x: (x['moodle-branch'], x['php'], x['pgsql-ver'])) + with open(__import__('os').environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out: + out.write(f"matrix={json.dumps({'include': matrix})}\n") + shell: python + + build: + needs: build-matrix + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.build-matrix.outputs.matrix) }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build pgseed image + run: | + docker build \ + --build-arg MOODLE_BRANCH=${{ matrix['moodle-branch'] }} \ + --build-arg MOODLE_MINOR=${{ matrix['moodle-minor'] }} \ + --build-arg PHP_VERSION=${{ matrix.php }} \ + --build-arg POSTGRES_VERSION=${{ matrix['pgsql-ver'] }} \ + -t ${{ matrix.image }}:${{ matrix['immutable-tag'] }} \ + -t ${{ matrix.image }}:${{ matrix['mutable-tag'] }} \ + -f docker/pgseed/Dockerfile docker/pgseed + + - name: Push pgseed image tags + run: | + docker push ${{ matrix.image }}:${{ matrix['immutable-tag'] }} + docker push ${{ matrix.image }}:${{ matrix['mutable-tag'] }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a5a4afd5..556d9058 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -446,7 +446,7 @@ jobs: runs-on: 'ubuntu-latest' services: postgres: - image: "postgres:${{ matrix.pgsql-ver }}" + image: "${{ matrix.database == 'pgsql' && matrix['pgseed-image'] != '' && matrix['pgseed-image'] || format('postgres:{0}', matrix['pgsql-ver']) }}" env: POSTGRES_USER: 'postgres' POSTGRES_HOST_AUTH_METHOD: 'trust' @@ -510,7 +510,7 @@ jobs: runs-on: 'ubuntu-latest' services: postgres: - image: "postgres:${{ matrix.pgsql-ver }}" + image: "${{ matrix.database == 'pgsql' && matrix['pgseed-image'] != '' && matrix['pgseed-image'] || format('postgres:{0}', matrix['pgsql-ver']) }}" env: POSTGRES_USER: 'postgres' POSTGRES_HOST_AUTH_METHOD: 'trust' diff --git a/README.md b/README.md index a2743676..23d041d4 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,16 @@ When you call the reusable ci, it will: To view or modify the full matrix, please see it here: [.github/actions/matrix/matrix_includes.yml](.github/actions/matrix/matrix_includes.yml) +### Seeded PostgreSQL images + +PostgreSQL-backed CI jobs can use pre-built pgseed images published to GHCR: + +`ghcr.io/catalyst/catalyst-moodle-workflows-pgseed:-php-pg-m` + +- `` is the Moodle 10-digit minor version from the Moodle updates API (same source used by matrix parsing). +- Images are built by `.github/workflows/build-pgseed.yml` from `docker/pgseed/`. +- Rebuild these images by running the `Build pgseed images` workflow manually, or by changing `docker/pgseed/**`, `.github/actions/matrix/matrix_includes.yml`, or `.github/workflows/build-pgseed.yml`. + ## How does this automate releases? Whenever a change is made to version.php, the workflow is triggered on a release branch (e.g. __main__ / __MOODLE_XX_STABLE__), and tests pass, will it attempt to run the plugin/release action `.github/plugin/release/action.yml`. Doing so will automatically publish a release to the Moodle plugin directory at https://moodle.org/plugins. diff --git a/docker/pgseed/Dockerfile b/docker/pgseed/Dockerfile new file mode 100644 index 00000000..f8b43707 --- /dev/null +++ b/docker/pgseed/Dockerfile @@ -0,0 +1,22 @@ +ARG POSTGRES_VERSION=15 +FROM postgres:${POSTGRES_VERSION} + +ARG MOODLE_BRANCH +ARG MOODLE_MINOR +ARG PHP_VERSION +ARG POSTGRES_VERSION + +ENV MOODLE_BRANCH=${MOODLE_BRANCH} +ENV MOODLE_MINOR=${MOODLE_MINOR} +ENV PHP_VERSION=${PHP_VERSION} +ENV POSTGRES_VERSION=${POSTGRES_VERSION} + +LABEL org.opencontainers.image.source="https://github.com/catalyst/catalyst-moodle-workflows" +LABEL org.opencontainers.image.description="Seeded PostgreSQL image for catalyst-moodle-workflows" +LABEL org.catalyst.moodle.branch="${MOODLE_BRANCH}" +LABEL org.catalyst.moodle.minor="${MOODLE_MINOR}" +LABEL org.catalyst.php.version="${PHP_VERSION}" +LABEL org.catalyst.pgsql.version="${POSTGRES_VERSION}" + +COPY seed.sh /docker-entrypoint-initdb.d/20-seed.sh +RUN chmod +x /docker-entrypoint-initdb.d/20-seed.sh diff --git a/docker/pgseed/seed.sh b/docker/pgseed/seed.sh new file mode 100644 index 00000000..74057ae4 --- /dev/null +++ b/docker/pgseed/seed.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +MOODLE_BRANCH="${MOODLE_BRANCH:-unknown}" +MOODLE_MINOR="${MOODLE_MINOR:-unknown}" +PHP_VERSION="${PHP_VERSION:-unknown}" +POSTGRES_VERSION="${POSTGRES_VERSION:-unknown}" + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-SQL + CREATE DATABASE moodle; +SQL + +psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname moodle <<-SQL + CREATE SCHEMA IF NOT EXISTS ci_seed; + CREATE TABLE IF NOT EXISTS ci_seed.metadata ( + id BIGSERIAL PRIMARY KEY, + moodle_branch TEXT NOT NULL, + moodle_minor TEXT NOT NULL, + php_version TEXT NOT NULL, + pgsql_version TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() + ); + INSERT INTO ci_seed.metadata (moodle_branch, moodle_minor, php_version, pgsql_version) + VALUES ('${MOODLE_BRANCH}', '${MOODLE_MINOR}', '${PHP_VERSION}', '${POSTGRES_VERSION}'); +SQL From 70ec8ec78b10cc595ed87f3d3bfeb291f7f83b00 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:25:21 +0000 Subject: [PATCH 03/10] fix: harden pgseed workflow and seed metadata insertion Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/b7b4fd6b-0316-4721-93ca-0239a76241bc Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/actions/parse-version/script.php | 5 +---- .github/workflows/build-pgseed.yml | 17 ++++++++++++++--- docker/pgseed/seed.sh | 11 +++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/.github/actions/parse-version/script.php b/.github/actions/parse-version/script.php index 782e23e0..0d5ca505 100644 --- a/.github/actions/parse-version/script.php +++ b/.github/actions/parse-version/script.php @@ -119,10 +119,7 @@ function moodle_minor_version($moodleBranch, array $updates): string { $series = $matches[1]; $major = substr($series, 0, 1); - $minor = ltrim(substr($series, 1), '0'); - if ($minor === '') { - $minor = '0'; - } + $minor = (string)((int)substr($series, 1)); $branch = $major . '.' . $minor; foreach ($updates as $update) { diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index 81322b33..cab1a391 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -12,6 +12,9 @@ on: - 'docker/pgseed/**' - '.github/workflows/build-pgseed.yml' +permissions: + contents: read + jobs: build-matrix: runs-on: ubuntu-latest @@ -33,14 +36,22 @@ jobs: id: set-matrix run: | import json + import os + import sys import urllib.request import yaml with open('.github/actions/matrix/matrix_includes.yml', encoding='utf-8') as f: data = yaml.safe_load(f) - with urllib.request.urlopen('https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8', timeout=30) as response: - updates_data = json.loads(response.read().decode('utf-8')) + updates_url = 'https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8' + try: + with urllib.request.urlopen(updates_url, timeout=30) as response: + updates_data = json.loads(response.read().decode('utf-8')) + except Exception as exc: + print(f'Failed to fetch Moodle updates from {updates_url}: {exc}', file=sys.stderr) + print('Please retry the workflow or run it manually once the API is reachable.', file=sys.stderr) + raise branch_to_minor = { row['branch']: str(row['version']) @@ -91,7 +102,7 @@ jobs: } matrix = sorted(tuples.values(), key=lambda x: (x['moodle-branch'], x['php'], x['pgsql-ver'])) - with open(__import__('os').environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out: + with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as out: out.write(f"matrix={json.dumps({'include': matrix})}\n") shell: python diff --git a/docker/pgseed/seed.sh b/docker/pgseed/seed.sh index 74057ae4..4534ae7c 100644 --- a/docker/pgseed/seed.sh +++ b/docker/pgseed/seed.sh @@ -10,7 +10,14 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-SQL CREATE DATABASE moodle; SQL -psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname moodle <<-SQL +psql \ + -v ON_ERROR_STOP=1 \ + -v moodle_branch="$MOODLE_BRANCH" \ + -v moodle_minor="$MOODLE_MINOR" \ + -v php_version="$PHP_VERSION" \ + -v pgsql_version="$POSTGRES_VERSION" \ + --username "$POSTGRES_USER" \ + --dbname moodle <<-'SQL' CREATE SCHEMA IF NOT EXISTS ci_seed; CREATE TABLE IF NOT EXISTS ci_seed.metadata ( id BIGSERIAL PRIMARY KEY, @@ -21,5 +28,5 @@ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname moodle <<-SQL created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); INSERT INTO ci_seed.metadata (moodle_branch, moodle_minor, php_version, pgsql_version) - VALUES ('${MOODLE_BRANCH}', '${MOODLE_MINOR}', '${PHP_VERSION}', '${POSTGRES_VERSION}'); + VALUES (:'moodle_branch', :'moodle_minor', :'php_version', :'pgsql_version'); SQL From f55e16a92f838d6e7160844de848a789fd2c44bc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:26:04 +0000 Subject: [PATCH 04/10] chore: clarify pgseed scheduling and minor-version parsing intent Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/b7b4fd6b-0316-4721-93ca-0239a76241bc Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/actions/parse-version/script.php | 1 + .github/workflows/build-pgseed.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.github/actions/parse-version/script.php b/.github/actions/parse-version/script.php index 0d5ca505..d7c60968 100644 --- a/.github/actions/parse-version/script.php +++ b/.github/actions/parse-version/script.php @@ -119,6 +119,7 @@ function moodle_minor_version($moodleBranch, array $updates): string { $series = $matches[1]; $major = substr($series, 0, 1); + // Collapse branch suffixes like 01/001 to 1 for API branch format (e.g. 4.1, 3.11, 5.0). $minor = (string)((int)substr($series, 1)); $branch = $major . '.' . $minor; diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index cab1a391..07ec07c3 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -3,6 +3,7 @@ name: Build pgseed images on: workflow_dispatch: schedule: + # Runs weekly to refresh tags when Moodle minor versions roll forward. - cron: '15 6 * * 1' push: branches: @@ -44,6 +45,7 @@ jobs: with open('.github/actions/matrix/matrix_includes.yml', encoding='utf-8') as f: data = yaml.safe_load(f) + # branch=3.8 is accepted by this endpoint and still returns the full core updates list. updates_url = 'https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8' try: with urllib.request.urlopen(updates_url, timeout=30) as response: From b1e23fdfef4e45cc56e1ee2e0a92fb2b45d39617 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:34:56 +0000 Subject: [PATCH 05/10] feat: apply MDL-88495 patch and use phpunit util upgrade/restore Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/plugin/setup/action.yml | 20 +++++++++++++++++++- .github/workflows/ci.yml | 10 ++++++++++ README.md | 1 + 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.github/plugin/setup/action.yml b/.github/plugin/setup/action.yml index 7195b2b6..9c64a72b 100644 --- a/.github/plugin/setup/action.yml +++ b/.github/plugin/setup/action.yml @@ -107,6 +107,24 @@ runs: ((test -f plugin/patch/${{ inputs.moodle_branch }}.diff && cd $GITHUB_WORKSPACE/moodletemp && git am --whitespace=nowarn < ../plugin/patch/${{ inputs.moodle_branch }}.diff) || echo No patch found;) shell: bash + - name: Apply MDL-88495 phpunit snapshot patch + if: ${{ always() }} + run: | + set -euo pipefail + cd "$GITHUB_WORKSPACE/moodletemp" + if grep -q -- "--upgrade" public/admin/tool/phpunit/cli/util.php; then + echo "MDL-88495 patch already present in this Moodle checkout." + exit 0 + fi + curl -fsSL "https://github.com/moodle/moodle/compare/main...abhinavgandham:moodle:mdl88495-phpunitci.patch" -o /tmp/mdl88495-phpunitci.patch + if ! git am --3way --whitespace=nowarn /tmp/mdl88495-phpunitci.patch; then + git am --abort || true + echo "MDL-88495 patch did not apply cleanly on ${MOODLE_BRANCH}; continuing without patch." + fi + shell: bash + env: + MOODLE_BRANCH: ${{ inputs.moodle_branch }} + - name: Install Moodle and Plugin if: ${{ inputs.database != '' }} # Install moodle, but use our temporary directory to include any potential core patches that have been applied. @@ -123,4 +141,4 @@ runs: shell: bash env: DB: ${{ inputs.database }} - MOODLE_BRANCH: ${{ inputs.moodle_branch }} \ No newline at end of file + MOODLE_BRANCH: ${{ inputs.moodle_branch }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 556d9058..6c5701eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -490,6 +490,16 @@ jobs: database: ${{ matrix.database }} - name: Run phpunit run: | + if php moodle/public/admin/tool/phpunit/cli/util.php --help | grep -q -- '--upgrade'; then + dataroot="$(php -r 'require "moodle/config.php"; echo $CFG->dataroot;' 2>/dev/null || true)" + if [ -n "$dataroot" ] && compgen -G "$dataroot/snapshot-*" > /dev/null; then + php moodle/public/admin/tool/phpunit/cli/util.php --drop + php moodle/public/admin/tool/phpunit/cli/util.php --restore + fi + php moodle/public/admin/tool/phpunit/cli/util.php --upgrade + else + echo "phpunit util upgrade mode unavailable on this Moodle branch; continuing with standard phpunit flow." + fi moodle-plugin-ci phpunit cd moodle vendor/bin/phpunit --fail-on-risky --disallow-test-output --filter tool_dataprivacy_metadata_registry_testcase diff --git a/README.md b/README.md index 23d041d4..34fe00c2 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ PostgreSQL-backed CI jobs can use pre-built pgseed images published to GHCR: - `` is the Moodle 10-digit minor version from the Moodle updates API (same source used by matrix parsing). - Images are built by `.github/workflows/build-pgseed.yml` from `docker/pgseed/`. - Rebuild these images by running the `Build pgseed images` workflow manually, or by changing `docker/pgseed/**`, `.github/actions/matrix/matrix_includes.yml`, or `.github/workflows/build-pgseed.yml`. +- CI applies the temporary MDL-88495 patch to the Moodle checkout (until merged upstream), then runs `php public/admin/tool/phpunit/cli/util.php --upgrade` before phpunit. If a phpunit snapshot exists in dataroot, CI restores it (database and sitedata) with `util.php --restore`. ## How does this automate releases? From 934ba7596aaa1c8282516c6327eac06b08e1e86c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:36:10 +0000 Subject: [PATCH 06/10] fix: pin and verify mdl88495 patch and harden phpunit restore checks Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/plugin/setup/action.yml | 12 +++++++++++- .github/workflows/build-pgseed.yml | 1 + .github/workflows/ci.yml | 13 ++++++++----- 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/.github/plugin/setup/action.yml b/.github/plugin/setup/action.yml index 9c64a72b..ce4b2e9d 100644 --- a/.github/plugin/setup/action.yml +++ b/.github/plugin/setup/action.yml @@ -116,7 +116,17 @@ runs: echo "MDL-88495 patch already present in this Moodle checkout." exit 0 fi - curl -fsSL "https://github.com/moodle/moodle/compare/main...abhinavgandham:moodle:mdl88495-phpunitci.patch" -o /tmp/mdl88495-phpunitci.patch + patch_url="https://github.com/moodle/moodle/commit/e812521f260b571f05ced5efd3bb443a455bbf78.patch" + patch_file="/tmp/mdl88495-phpunitci.patch" + expected_sha256="4aee5364e688fb22170bc902887c623439f00cf45baad54e8dedfd6e66594290" + curl -fsSL "$patch_url" -o "$patch_file" + actual_sha256="$(sha256sum "$patch_file" | awk '{print $1}')" + if [ "$actual_sha256" != "$expected_sha256" ]; then + echo "Patch checksum mismatch for MDL-88495 patch." + echo "Expected: $expected_sha256" + echo "Actual: $actual_sha256" + exit 1 + fi if ! git am --3way --whitespace=nowarn /tmp/mdl88495-phpunitci.patch; then git am --abort || true echo "MDL-88495 patch did not apply cleanly on ${MOODLE_BRANCH}; continuing without patch." diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index 07ec07c3..3ff18ede 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -46,6 +46,7 @@ jobs: data = yaml.safe_load(f) # branch=3.8 is accepted by this endpoint and still returns the full core updates list. + # version=0.0 requests the full available set rather than filtering to a real installed version. updates_url = 'https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8' try: with urllib.request.urlopen(updates_url, timeout=30) as response: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c5701eb..a3f6ace9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -491,14 +491,17 @@ jobs: - name: Run phpunit run: | if php moodle/public/admin/tool/phpunit/cli/util.php --help | grep -q -- '--upgrade'; then - dataroot="$(php -r 'require "moodle/config.php"; echo $CFG->dataroot;' 2>/dev/null || true)" - if [ -n "$dataroot" ] && compgen -G "$dataroot/snapshot-*" > /dev/null; then - php moodle/public/admin/tool/phpunit/cli/util.php --drop - php moodle/public/admin/tool/phpunit/cli/util.php --restore + if dataroot="$(php -r 'require "moodle/config.php"; if (empty($CFG->dataroot)) { exit(1); } echo $CFG->dataroot;' 2>/dev/null)"; then + if compgen -G "$dataroot/snapshot-*" > /dev/null; then + php moodle/public/admin/tool/phpunit/cli/util.php --drop + php moodle/public/admin/tool/phpunit/cli/util.php --restore + fi + else + echo "Unable to determine Moodle dataroot; skipping snapshot restore." fi php moodle/public/admin/tool/phpunit/cli/util.php --upgrade else - echo "phpunit util upgrade mode unavailable on this Moodle branch; continuing with standard phpunit flow." + echo "PHPUnit util upgrade mode not available on this Moodle branch; continuing with standard PHPUnit flow." fi moodle-plugin-ci phpunit cd moodle From a6c24cfaf3ca215be693b5fbbbe8f16a0ad03755 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:36:59 +0000 Subject: [PATCH 07/10] chore: clarify mdl88495 and snapshot restore comments Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/plugin/setup/action.yml | 1 + .github/workflows/build-pgseed.yml | 2 +- .github/workflows/ci.yml | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/plugin/setup/action.yml b/.github/plugin/setup/action.yml index ce4b2e9d..af903de7 100644 --- a/.github/plugin/setup/action.yml +++ b/.github/plugin/setup/action.yml @@ -116,6 +116,7 @@ runs: echo "MDL-88495 patch already present in this Moodle checkout." exit 0 fi + # Temporary upstream patch for MDL-88495 phpunit snapshot/restore/upgrade support. patch_url="https://github.com/moodle/moodle/commit/e812521f260b571f05ced5efd3bb443a455bbf78.patch" patch_file="/tmp/mdl88495-phpunitci.patch" expected_sha256="4aee5364e688fb22170bc902887c623439f00cf45baad54e8dedfd6e66594290" diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index 3ff18ede..5e76f7ed 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -3,7 +3,7 @@ name: Build pgseed images on: workflow_dispatch: schedule: - # Runs weekly to refresh tags when Moodle minor versions roll forward. + # Runs weekly (Mondays at 06:15 UTC) to refresh tags when Moodle minor versions roll forward. - cron: '15 6 * * 1' push: branches: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3f6ace9..9d83bf98 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -491,7 +491,9 @@ jobs: - name: Run phpunit run: | if php moodle/public/admin/tool/phpunit/cli/util.php --help | grep -q -- '--upgrade'; then + # Read dataroot from Moodle config to detect phpunit snapshot directories. if dataroot="$(php -r 'require "moodle/config.php"; if (empty($CFG->dataroot)) { exit(1); } echo $CFG->dataroot;' 2>/dev/null)"; then + # snapshot-* directories are created by util.php --snapshot and include DB + sitedata state. if compgen -G "$dataroot/snapshot-*" > /dev/null; then php moodle/public/admin/tool/phpunit/cli/util.php --drop php moodle/public/admin/tool/phpunit/cli/util.php --restore From 8414d065a334ca9098993cbd46dcd22771fb96f8 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:37:55 +0000 Subject: [PATCH 08/10] chore: tighten wording around schedule and patch fallback behavior Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/actions/parse-version/script.php | 3 ++- .github/plugin/setup/action.yml | 2 +- .github/workflows/build-pgseed.yml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/actions/parse-version/script.php b/.github/actions/parse-version/script.php index d7c60968..9a33d9e8 100644 --- a/.github/actions/parse-version/script.php +++ b/.github/actions/parse-version/script.php @@ -119,7 +119,8 @@ function moodle_minor_version($moodleBranch, array $updates): string { $series = $matches[1]; $major = substr($series, 0, 1); - // Collapse branch suffixes like 01/001 to 1 for API branch format (e.g. 4.1, 3.11, 5.0). + // Cast via int to strip leading zeros, then back to string for consistent branch key comparisons. + // Example: MOODLE_401_STABLE => 4.1, MOODLE_3011_STABLE => 3.11. $minor = (string)((int)substr($series, 1)); $branch = $major . '.' . $minor; diff --git a/.github/plugin/setup/action.yml b/.github/plugin/setup/action.yml index af903de7..8003251e 100644 --- a/.github/plugin/setup/action.yml +++ b/.github/plugin/setup/action.yml @@ -130,7 +130,7 @@ runs: fi if ! git am --3way --whitespace=nowarn /tmp/mdl88495-phpunitci.patch; then git am --abort || true - echo "MDL-88495 patch did not apply cleanly on ${MOODLE_BRANCH}; continuing without patch." + echo "MDL-88495 patch did not apply cleanly on ${MOODLE_BRANCH}; continuing without patch (PHPUnit snapshot/restore features unavailable)." fi shell: bash env: diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index 5e76f7ed..9a7f2d8d 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -3,7 +3,7 @@ name: Build pgseed images on: workflow_dispatch: schedule: - # Runs weekly (Mondays at 06:15 UTC) to refresh tags when Moodle minor versions roll forward. + # Runs weekly (every Monday at 06:15 UTC) to refresh tags when Moodle minor versions roll forward. - cron: '15 6 * * 1' push: branches: From 75c14b138d5819fc81cb0f880b0f38a44dbdeae5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:39:56 +0000 Subject: [PATCH 09/10] feat: switch phpunit flow to docker-level pgseed sitedata restore Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/workflows/ci.yml | 21 ++++++++++++--------- README.md | 2 +- docker/pgseed/Dockerfile | 4 ++++ docker/pgseed/moodledata/.gitkeep | 0 4 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 docker/pgseed/moodledata/.gitkeep diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d83bf98..6ead699e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -490,17 +490,17 @@ jobs: database: ${{ matrix.database }} - name: Run phpunit run: | - if php moodle/public/admin/tool/phpunit/cli/util.php --help | grep -q -- '--upgrade'; then - # Read dataroot from Moodle config to detect phpunit snapshot directories. - if dataroot="$(php -r 'require "moodle/config.php"; if (empty($CFG->dataroot)) { exit(1); } echo $CFG->dataroot;' 2>/dev/null)"; then - # snapshot-* directories are created by util.php --snapshot and include DB + sitedata state. - if compgen -G "$dataroot/snapshot-*" > /dev/null; then - php moodle/public/admin/tool/phpunit/cli/util.php --drop - php moodle/public/admin/tool/phpunit/cli/util.php --restore + if [ "${MATRIX_DATABASE}" = "pgsql" ] && [ -n "${PGSEED_IMAGE}" ]; then + # Restore moodle sitedata from the pgseed postgres service container. + pgseed_container="$(docker ps --format '{{.ID}} {{.Image}}' | awk '$2 ~ /catalyst-moodle-workflows-pgseed/ {print $1; exit}')" + if [ -n "$pgseed_container" ] && dataroot="$(php -r 'require "moodle/config.php"; if (empty($CFG->dataroot)) { exit(1); } echo $CFG->dataroot;' 2>/dev/null)"; then + if docker cp "$pgseed_container:/pgseed/moodledata.tar.gz" /tmp/moodledata.tar.gz 2>/dev/null; then + mkdir -p "$dataroot" + tar -xzf /tmp/moodledata.tar.gz -C "$dataroot" --strip-components=1 fi - else - echo "Unable to determine Moodle dataroot; skipping snapshot restore." fi + fi + if php moodle/public/admin/tool/phpunit/cli/util.php --help | grep -q -- '--upgrade'; then php moodle/public/admin/tool/phpunit/cli/util.php --upgrade else echo "PHPUnit util upgrade mode not available on this Moodle branch; continuing with standard PHPUnit flow." @@ -510,6 +510,9 @@ jobs: vendor/bin/phpunit --fail-on-risky --disallow-test-output --filter tool_dataprivacy_metadata_registry_testcase vendor/bin/phpunit --fail-on-risky --disallow-test-output --testsuite core_privacy_testsuite --filter provider_testcase shell: bash + env: + MATRIX_DATABASE: ${{ matrix.database }} + PGSEED_IMAGE: ${{ matrix['pgseed-image'] }} behat: name: ${{ matrix.moodle-branch-short }} - behat - php${{ matrix.php }} - ${{ matrix.database }} needs: prepare_matrix diff --git a/README.md b/README.md index 34fe00c2..6b5bf0c7 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ PostgreSQL-backed CI jobs can use pre-built pgseed images published to GHCR: - `` is the Moodle 10-digit minor version from the Moodle updates API (same source used by matrix parsing). - Images are built by `.github/workflows/build-pgseed.yml` from `docker/pgseed/`. - Rebuild these images by running the `Build pgseed images` workflow manually, or by changing `docker/pgseed/**`, `.github/actions/matrix/matrix_includes.yml`, or `.github/workflows/build-pgseed.yml`. -- CI applies the temporary MDL-88495 patch to the Moodle checkout (until merged upstream), then runs `php public/admin/tool/phpunit/cli/util.php --upgrade` before phpunit. If a phpunit snapshot exists in dataroot, CI restores it (database and sitedata) with `util.php --restore`. +- CI applies the temporary MDL-88495 patch to the Moodle checkout (until merged upstream), runs `php public/admin/tool/phpunit/cli/util.php --upgrade` before phpunit, and restores sitedata from the pgseed postgres service container archive at `/pgseed/moodledata.tar.gz` (docker-level snapshot, no `util.php --restore` usage). ## How does this automate releases? diff --git a/docker/pgseed/Dockerfile b/docker/pgseed/Dockerfile index f8b43707..020682ad 100644 --- a/docker/pgseed/Dockerfile +++ b/docker/pgseed/Dockerfile @@ -19,4 +19,8 @@ LABEL org.catalyst.php.version="${PHP_VERSION}" LABEL org.catalyst.pgsql.version="${POSTGRES_VERSION}" COPY seed.sh /docker-entrypoint-initdb.d/20-seed.sh +COPY moodledata/ /opt/pgseed/moodledata/ RUN chmod +x /docker-entrypoint-initdb.d/20-seed.sh +RUN mkdir -p /pgseed \ + && tar -C /opt/pgseed -czf /pgseed/moodledata.tar.gz moodledata \ + && rm -rf /opt/pgseed diff --git a/docker/pgseed/moodledata/.gitkeep b/docker/pgseed/moodledata/.gitkeep new file mode 100644 index 00000000..e69de29b From d243704c27fbb74fecbf02ff808c5c587d88a279 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 17:41:02 +0000 Subject: [PATCH 10/10] chore: harden pgseed comments and service container lookup Agent-Logs-Url: https://github.com/catalyst/catalyst-moodle-workflows/sessions/cd506e69-d033-44d0-86c8-7e3c931bba7a Co-authored-by: brendanheywood <187449+brendanheywood@users.noreply.github.com> --- .github/actions/parse-version/script.php | 1 + .github/plugin/setup/action.yml | 1 + .github/workflows/build-pgseed.yml | 3 ++- .github/workflows/ci.yml | 2 +- 4 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/actions/parse-version/script.php b/.github/actions/parse-version/script.php index 9a33d9e8..a4c0d5fd 100644 --- a/.github/actions/parse-version/script.php +++ b/.github/actions/parse-version/script.php @@ -120,6 +120,7 @@ function moodle_minor_version($moodleBranch, array $updates): string { $series = $matches[1]; $major = substr($series, 0, 1); // Cast via int to strip leading zeros, then back to string for consistent branch key comparisons. + // This normalizes values used in the Moodle updates API map keys. // Example: MOODLE_401_STABLE => 4.1, MOODLE_3011_STABLE => 3.11. $minor = (string)((int)substr($series, 1)); diff --git a/.github/plugin/setup/action.yml b/.github/plugin/setup/action.yml index 8003251e..6772cc84 100644 --- a/.github/plugin/setup/action.yml +++ b/.github/plugin/setup/action.yml @@ -119,6 +119,7 @@ runs: # Temporary upstream patch for MDL-88495 phpunit snapshot/restore/upgrade support. patch_url="https://github.com/moodle/moodle/commit/e812521f260b571f05ced5efd3bb443a455bbf78.patch" patch_file="/tmp/mdl88495-phpunitci.patch" + # SHA256 for commit patch e812521f260b571f05ced5efd3bb443a455bbf78. expected_sha256="4aee5364e688fb22170bc902887c623439f00cf45baad54e8dedfd6e66594290" curl -fsSL "$patch_url" -o "$patch_file" actual_sha256="$(sha256sum "$patch_file" | awk '{print $1}')" diff --git a/.github/workflows/build-pgseed.yml b/.github/workflows/build-pgseed.yml index 9a7f2d8d..ddd3cad3 100644 --- a/.github/workflows/build-pgseed.yml +++ b/.github/workflows/build-pgseed.yml @@ -45,9 +45,10 @@ jobs: with open('.github/actions/matrix/matrix_includes.yml', encoding='utf-8') as f: data = yaml.safe_load(f) + MOODLE_UPDATES_QUERY_BRANCH = '3.8' # branch=3.8 is accepted by this endpoint and still returns the full core updates list. # version=0.0 requests the full available set rather than filtering to a real installed version. - updates_url = 'https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch=3.8' + updates_url = f'https://download.moodle.org/api/1.3/updates.php?format=json&version=0.0&branch={MOODLE_UPDATES_QUERY_BRANCH}' try: with urllib.request.urlopen(updates_url, timeout=30) as response: updates_data = json.loads(response.read().decode('utf-8')) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6ead699e..1ec54294 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -492,7 +492,7 @@ jobs: run: | if [ "${MATRIX_DATABASE}" = "pgsql" ] && [ -n "${PGSEED_IMAGE}" ]; then # Restore moodle sitedata from the pgseed postgres service container. - pgseed_container="$(docker ps --format '{{.ID}} {{.Image}}' | awk '$2 ~ /catalyst-moodle-workflows-pgseed/ {print $1; exit}')" + pgseed_container="$(docker ps --filter "name=postgres" --format '{{.ID}}' | head -n 1)" if [ -n "$pgseed_container" ] && dataroot="$(php -r 'require "moodle/config.php"; if (empty($CFG->dataroot)) { exit(1); } echo $CFG->dataroot;' 2>/dev/null)"; then if docker cp "$pgseed_container:/pgseed/moodledata.tar.gz" /tmp/moodledata.tar.gz 2>/dev/null; then mkdir -p "$dataroot"