chore(release): 2.0.15-unstable.20260903082039 #2837
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ## | |
| # api-test-coverage.yml — automated Newman runs against a live OR. | |
| # | |
| # Boots a minimal Nextcloud + Postgres stack via .github/docker-compose.ci.yml, | |
| # enables the openregister app, and runs the same orchestrator script | |
| # (tests/newman/run-all.sh) that developers use locally — single source | |
| # of truth for the API integration suite. | |
| # | |
| # The orchestrator excludes agent-cms (needs Ollama) and federation | |
| # (needs a 2nd NC) from the default domain list. To run extras | |
| # explicitly, override COLLECTIONS in workflow_dispatch. | |
| ## | |
| name: api-test-coverage | |
| # SECURITY: deny-by-default permissions for the GITHUB_TOKEN at the | |
| # workflow level. The job below runs PR-supplied scripts | |
| # (tests/newman/run-all.sh) inside a CI container, so a malicious PR | |
| # could otherwise exfiltrate the token or use it to comment/approve/ | |
| # push. `contents: read` is the only scope the suite needs (clones the | |
| # PR ref). Override per-job if a future step needs more. | |
| permissions: | |
| contents: read | |
| on: | |
| push: | |
| branches: [development, main] | |
| pull_request: | |
| branches: [development] | |
| workflow_dispatch: | |
| inputs: | |
| collections: | |
| description: "Space-separated domain subset (default: all 8). Examples: 'crud relations', 'auth-matrix'." | |
| required: false | |
| type: string | |
| jobs: | |
| newman-suite: | |
| name: Newman API Test Suite | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repo | |
| uses: actions/checkout@v4 | |
| - name: Set up PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.3' | |
| tools: composer:v2 | |
| coverage: none | |
| - name: Cache composer deps | |
| uses: actions/cache@v4 | |
| with: | |
| path: ~/.composer/cache | |
| key: composer-${{ hashFiles('**/composer.lock') }} | |
| restore-keys: composer- | |
| - name: Install composer deps (production) | |
| run: composer install --no-dev --no-interaction --no-progress --prefer-dist | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| - name: Install Newman | |
| run: npm install -g newman | |
| - name: Boot CI stack (db + Nextcloud) | |
| run: docker compose -f .github/docker-compose.ci.yml up -d | |
| - name: Wait for Nextcloud to be installed | |
| run: | | |
| for i in $(seq 1 60); do | |
| if docker exec nextcloud su -s /bin/bash www-data -c "php /var/www/html/occ status" 2>/dev/null | grep -q "installed: true"; then | |
| echo "✓ Nextcloud installed and ready" | |
| break | |
| fi | |
| echo "Waiting for Nextcloud installation... ($i/60)" | |
| sleep 5 | |
| done | |
| docker exec nextcloud su -s /bin/bash www-data -c "php /var/www/html/occ status" | |
| - name: Copy openregister into container | |
| run: | | |
| docker exec nextcloud mkdir -p /var/www/html/custom_apps/openregister | |
| # Stream the working tree (excluding heavy/local junk) into the container. | |
| tar --exclude='.git' --exclude='node_modules' --exclude='.claude' --exclude='tests/e2e/playwright-report' --exclude='tests/e2e/test-results' -cf - . | docker exec -i nextcloud tar -xf - -C /var/www/html/custom_apps/openregister | |
| docker exec nextcloud chown -R www-data:www-data /var/www/html/custom_apps/openregister | |
| - name: Enable openregister app | |
| run: | | |
| docker exec nextcloud su -s /bin/bash www-data -c "php /var/www/html/occ app:enable openregister" | |
| docker exec nextcloud su -s /bin/bash www-data -c "php /var/www/html/occ app:list" | grep openregister | |
| - name: Provide composer inside the container | |
| # The unit-suite step below runs INSIDE the nextcloud container, which | |
| # ships no composer — which is why this repo used to carry a committed | |
| # `composer.phar` at its root and invoke `php composer.phar install`. | |
| # | |
| # A checked-in package-manager binary is exactly the supply-chain shape | |
| # ADR-100 Decision 2 forbids: it is never reviewed, never updated with | |
| # the lockfile, and its provenance is a git history nobody reads. | |
| # | |
| # The runner already has composer (see the `composer install` step | |
| # above, which uses it), so hand that one to the container instead of | |
| # shipping a second copy in the repository. `command -v` fails loudly | |
| # if it is ever absent, rather than falling through to a missing binary. | |
| # | |
| # Copied to a plain path and invoked as `php /usr/local/bin/composer`, | |
| # exactly as the old committed phar was invoked. `docker cp` does not | |
| # carry the executable bit reliably, and the first attempt at this step | |
| # failed with "composer: executable file not found in $PATH" for that | |
| # reason. Running the phar through `php` sidesteps both the exec bit and | |
| # the shebang, and needs nothing on the container's PATH. | |
| run: | | |
| set -e | |
| # readlink -f, not `command -v` alone: setup-php puts a SYMLINK on | |
| # PATH, and `docker cp` copies the link itself rather than its target | |
| # — which lands a dangling symlink in the container and fails at use | |
| # time with "Could not open input file: /usr/local/bin/composer". | |
| COMPOSER_BIN="$(readlink -f "$(command -v composer)")" | |
| echo "Copying composer from ${COMPOSER_BIN} ($(stat -c%s "${COMPOSER_BIN}") bytes)" | |
| docker cp "${COMPOSER_BIN}" nextcloud:/usr/local/bin/composer | |
| # Prove it is usable HERE, where the message names the cause, rather | |
| # than 40 lines later where it reads as a dependency-install failure. | |
| docker exec nextcloud php /usr/local/bin/composer --version | |
| - name: PHPUnit unit suite (in-container, HARD GATE) | |
| # The unit suite requires the full Nextcloud runtime — the OCP stubs | |
| # reference Doctrine\DBAL\* and OC\* internals that only resolve once | |
| # lib/base.php is bootstrapped, so it CANNOT run in a bare PHP CI | |
| # container (see tests/bootstrap.php + TESTING-CI-ROLLOUT.md). Run it | |
| # here, inside the booted NC stack, after the app is deployed/enabled. | |
| # | |
| # Copy the app OUT of /var/www/html first: when phpunit runs from | |
| # inside custom_apps, NC's autoloader (already loaded by base.php) | |
| # hijacks the bootstrap. A unique COMPOSER_AUTOLOADER_SUFFIX avoids the | |
| # ComposerAutoloaderInit redeclaration between the live and copied vendor. | |
| run: | | |
| docker exec -u www-data nextcloud bash -lc ' | |
| set -e | |
| rm -rf /tmp/ortest | |
| cp -r /var/www/html/custom_apps/openregister /tmp/ortest | |
| cd /tmp/ortest | |
| # The deployed vendor is production-only (--no-dev) so it lacks | |
| # phpunit. Install dev deps + a unique autoloader suffix in the copy | |
| # (the suffix avoids the ComposerAutoloaderInit redeclaration against | |
| # the live app vendor already loaded by base.php). | |
| # | |
| # --ignore-platform-req=ext-xsl: the require-dev tool edgedesign/phpqa | |
| # declares ext-xsl, which the nextcloud:34 runtime image does not ship | |
| # (no docker-php-ext-xsl.ini). phpqa is a static-analysis bundle unused | |
| # by phpunit, so skip the platform gate rather than fail the install. | |
| # ext-gd is ignored too as a belt-and-suspenders no-op (phpspreadsheet | |
| # requires it; the NC image does ship gd, so this is harmless there). | |
| php -r "\$j=json_decode(file_get_contents(\"composer.json\"),true); \$j[\"config\"][\"autoloader-suffix\"]=\"OrTestCI\"; file_put_contents(\"composer.json\", json_encode(\$j, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));" | |
| php /usr/local/bin/composer install --no-interaction --no-progress --prefer-dist --ignore-platform-req=ext-xsl --ignore-platform-req=ext-gd | |
| OPENREGISTER_TEST_NC_ROOT=/var/www/html php vendor/bin/phpunit -c phpunit.xml --no-coverage --colors=never tests/Unit | |
| ' | |
| - name: Wait for openregister API to respond | |
| run: | | |
| # app:enable returns before the magic-mapping repair step + PHP-FPM | |
| # opcache warm-up finish. Newman starts firing requests within ~1s | |
| # of app:enable, so without a wait the first batch lose to ECONNRESET | |
| # while PHP-FPM is still spinning up the openregister code paths. | |
| # Poll until the registers endpoint returns a real HTTP status | |
| # (200/401/etc.) — anything except connection-level errors (000) | |
| # which curl emits when the server isn't reachable yet. | |
| # | |
| # We need: a single 3-digit code that's a valid HTTP response. | |
| # 000 = no response (ECONNRESET/refused/timeout) — must keep waiting. | |
| # 5xx = server up but app may be init-erroring — also keep waiting. | |
| # 2xx/3xx/4xx = app responding, safe to start tests. | |
| for i in $(seq 1 60); do | |
| # --max-time guards against curl itself hanging on slow boot. | |
| code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 \ | |
| -u admin:admin \ | |
| http://localhost:8080/index.php/apps/openregister/api/registers \ | |
| 2>/dev/null | tr -d '\n') | |
| # Only accept a strictly-3-digit code between 100 and 499. | |
| if [ -n "$code" ] \ | |
| && [ ${#code} -eq 3 ] \ | |
| && [ "$code" -ge 100 ] 2>/dev/null \ | |
| && [ "$code" -lt 500 ]; then | |
| echo "✓ openregister API responding (HTTP $code, attempt $i)" | |
| # Belt-and-braces: give magic-mapping repair step + opcache a | |
| # final 5s to settle before flooding Newman traffic. | |
| sleep 5 | |
| break | |
| fi | |
| echo "Waiting for openregister API... (attempt $i/60, last='$code')" | |
| sleep 2 | |
| done | |
| - name: Run Newman orchestrator | |
| env: | |
| BASE_URL: http://localhost:8080 | |
| ADMIN_USER: admin | |
| ADMIN_PASSWORD: admin | |
| CONTAINER_NAME: nextcloud | |
| NEWMAN_RUNNER: host | |
| FAIL_FAST: "0" | |
| # platform-annotations + files hardcode 'decidesk/meeting' / | |
| # register=1 / schema=1 IDs that don't exist in fresh CI state | |
| # (they assume dev-container state). Excluded by default until | |
| # the collections gain proper setUp steps that seed their own | |
| # test fixtures. Match agent-cms / federation pattern (extras). | |
| # Override via workflow_dispatch input to run them anyway. | |
| COLLECTIONS: ${{ inputs.collections || 'crud graphql relations auth-matrix error-matrix referential-integrity' }} | |
| run: bash tests/newman/run-all.sh | |
| - name: Upload Newman reports | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: newman-reports | |
| path: | | |
| tests/newman/reports/ | |
| newman-*.json | |
| retention-days: 14 | |
| if-no-files-found: ignore | |
| - name: Collect docker logs on failure | |
| if: failure() | |
| run: | | |
| mkdir -p ci-logs | |
| docker compose -f .github/docker-compose.ci.yml logs --no-color > ci-logs/docker-compose.log | |
| docker exec nextcloud cat /var/www/html/data/nextcloud.log > ci-logs/nextcloud.log 2>&1 || true | |
| - name: Upload docker logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ci-logs | |
| path: ci-logs/ | |
| retention-days: 14 | |
| - name: Tear down CI stack | |
| if: always() | |
| run: docker compose -f .github/docker-compose.ci.yml down -v |