name: Tests on: pull_request: branches: - master jobs: linting: runs-on: ubuntu-latest steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install the latest version of uv uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 - name: Check format run: uv run --frozen ruff format --diff - name: Linting run: uv run --frozen ruff check - name: Type check run: uv run --frozen ty check -- nextcloud_mcp_server unit-test: runs-on: ubuntu-latest needs: [linting] steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install the latest version of uv uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 - name: Run unit tests run: uv run pytest -v -m unit -o "addopts=-p no:asyncio" integration-test: runs-on: ubuntu-latest needs: [linting] strategy: fail-fast: false matrix: nextcloud_version: - "31" - "32" # - "33" # Disabled until all upstream apps support NC 33 mode: - "single-user" - "multi-user-basic" - "oauth" - "login-flow" include: # Version-specific image pins — Renovate updates these via customManagers # renovate: datasource=docker depName=docker.io/library/nextcloud - nextcloud_version: "31" nextcloud_image: "docker.io/library/nextcloud:31.0.14@sha256:9bf3fae91aad4dca3eff02c1f71df8d5c6705a349065fb537aa5c5ef578f1013" # renovate: datasource=docker depName=docker.io/library/nextcloud - nextcloud_version: "32" nextcloud_image: "docker.io/library/nextcloud:32.0.6@sha256:5c4e09f72f096cd68379a8ae69f71e61d13da5a07430fc4a17c702a14e6a4267" # renovate: datasource=docker depName=docker.io/library/nextcloud # Disabled until all upstream apps support NC 33 # - nextcloud_version: "33" # nextcloud_image: "docker.io/library/nextcloud:33.0.0@sha256:d53f6cb35b0712aa890a5e4a8ca21043d6fcd390f38c55b710816dd7cbc2edc0" # Mode-specific properties - mode: single-user profile: single-user markers: "(smoke and not oauth and not keycloak and not login_flow and not multi_user_basic) or (integration and not oauth and not keycloak and not login_flow and not multi_user_basic)" wait-port: 8000 mcp-internal-url: "http://mcp:8000" needs-playwright: false extra-args: >- --ignore=tests/integration/test_qdrant_collection_creation.py --ignore=tests/rag_evaluation/ - mode: multi-user-basic profile: multi-user-basic markers: "multi_user_basic" wait-port: 8003 mcp-internal-url: "http://mcp-multi-user-basic:8000" needs-playwright: true extra-args: "" - mode: oauth profile: oauth markers: "oauth and not keycloak" wait-port: 8001 mcp-internal-url: "http://mcp-oauth:8001" needs-playwright: true extra-args: "" - mode: login-flow profile: login-flow markers: "login_flow" wait-port: 8004 mcp-internal-url: "http://mcp-login-flow:8004" needs-playwright: true extra-args: "" name: integration (${{ matrix.mode }} / nc${{ matrix.nextcloud_version }}) steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: submodules: 'true' - name: Set up PHP 8.4 if: matrix.mode != 'single-user' uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # 2.36.0 with: php-version: 8.4 coverage: none # OIDC app installed from app store (dev mount removed from docker-compose.yml) - name: Set up Node.js if: matrix.mode != 'single-user' uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 with: node-version: 24 - name: Build Astrolabe app if: matrix.mode != 'single-user' run: | cd third_party/astrolabe composer install --no-dev --optimize-autoloader npm ci npm run build # Start services with the appropriate profile - name: Run docker compose uses: hoverkraft-tech/compose-action@4894d2492015c1774ee5a13a95b1072093087ec3 # v2.5.0 with: compose-file: "./docker-compose.yml" compose-flags: "--profile ${{ matrix.profile }}" up-flags: "--build" env: MCP_SERVER_URL: ${{ matrix.mcp-internal-url }} NEXTCLOUD_IMAGE: ${{ matrix.nextcloud_image }} - name: Install the latest version of uv uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 - name: Install Playwright if: matrix.needs-playwright run: uv run playwright install chromium --with-deps # Wait for Nextcloud to be healthy - name: Wait for Nextcloud run: | echo "Waiting for Nextcloud at http://localhost:8080..." max_attempts=60 attempt=0 until curl -sSf http://localhost:8080/status.php 2>/dev/null | grep -q '"installed":true'; do attempt=$((attempt + 1)) if [ $attempt -ge $max_attempts ]; then echo "Nextcloud did not become ready in time." docker compose logs app exit 1 fi echo "Attempt $attempt/$max_attempts: Not ready, sleeping 5s..." sleep 5 done echo "Nextcloud is ready." # Wait for the MCP service to be healthy - name: Wait for MCP service (${{ matrix.mode }}) run: | echo "Waiting for MCP service on port ${{ matrix.wait-port }}..." max_attempts=30 attempt=0 until curl -o /dev/null -s -w "%{http_code}\n" http://localhost:${{ matrix.wait-port }}/health 2>/dev/null | grep -qE "200|404|405"; do attempt=$((attempt + 1)) if [ $attempt -ge $max_attempts ]; then echo "MCP service did not become ready in time." docker compose --profile ${{ matrix.profile }} logs exit 1 fi echo "Attempt $attempt/$max_attempts: Not ready, sleeping 5s..." sleep 5 done echo "MCP service is ready on port ${{ matrix.wait-port }}." - name: Verify OIDC configuration if: matrix.mode == 'oauth' || matrix.mode == 'login-flow' run: | echo "=== OIDC Discovery ===" curl -s http://localhost:8080/.well-known/openid-configuration | jq . echo "=== OIDC App Status ===" docker compose exec -T app php occ app:list --output=json 2>/dev/null | jq '.enabled.oidc // "NOT INSTALLED"' - name: Run tests (${{ matrix.mode }}) env: NEXTCLOUD_HOST: "http://localhost:8080" NEXTCLOUD_USERNAME: "admin" NEXTCLOUD_PASSWORD: "admin" run: | uv run pytest -v \ --log-cli-level=WARN \ -m '${{ matrix.markers }}' \ -o "addopts=-p no:asyncio" \ --timeout=300 \ ${{ matrix.extra-args }} - name: Collect service logs on failure if: failure() run: docker compose --profile ${{ matrix.profile }} logs --tail=500 > /tmp/docker-compose-logs.txt 2>&1 - name: Upload debug artifacts if: failure() uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: debug-${{ matrix.mode }}-nc${{ matrix.nextcloud_version }} path: | /tmp/*.png /tmp/docker-compose-logs.txt retention-days: 7 if-no-files-found: ignore