diff --git a/.github/workflows/astroglobe-ci.yml b/.github/workflows/astrolabe-ci.yml similarity index 71% rename from .github/workflows/astroglobe-ci.yml rename to .github/workflows/astrolabe-ci.yml index a7deb12..8d7afd8 100644 --- a/.github/workflows/astroglobe-ci.yml +++ b/.github/workflows/astrolabe-ci.yml @@ -1,24 +1,24 @@ -# Consolidated CI workflow for Astroglobe Nextcloud app +# Consolidated CI workflow for Astrolabe Nextcloud app # -# Runs on PRs that modify the astroglobe directory +# Runs on PRs that modify the astrolabe directory # Based on Nextcloud app skeleton workflows # # SPDX-FileCopyrightText: 2025 Nextcloud MCP Server contributors # SPDX-License-Identifier: MIT -name: Astroglobe CI +name: Astrolabe CI on: pull_request: paths: - - 'third_party/astroglobe/**' - - '.github/workflows/astroglobe-ci.yml' + - 'third_party/astrolabe/**' + - '.github/workflows/astrolabe-ci.yml' permissions: contents: read concurrency: - group: astroglobe-ci-${{ github.head_ref || github.run_id }} + group: astrolabe-ci-${{ github.head_ref || github.run_id }} cancel-in-progress: true jobs: @@ -37,18 +37,18 @@ jobs: with: filters: | frontend: - - 'third_party/astroglobe/src/**' - - 'third_party/astroglobe/package.json' - - 'third_party/astroglobe/package-lock.json' - - 'third_party/astroglobe/vite.config.js' - - 'third_party/astroglobe/**/*.js' - - 'third_party/astroglobe/**/*.ts' - - 'third_party/astroglobe/**/*.vue' + - 'third_party/astrolabe/src/**' + - 'third_party/astrolabe/package.json' + - 'third_party/astrolabe/package-lock.json' + - 'third_party/astrolabe/vite.config.js' + - 'third_party/astrolabe/**/*.js' + - 'third_party/astrolabe/**/*.ts' + - 'third_party/astrolabe/**/*.vue' php: - - 'third_party/astroglobe/lib/**' - - 'third_party/astroglobe/appinfo/**' - - 'third_party/astroglobe/composer.json' - - 'third_party/astroglobe/psalm.xml' + - 'third_party/astrolabe/lib/**' + - 'third_party/astrolabe/appinfo/**' + - 'third_party/astrolabe/composer.json' + - 'third_party/astrolabe/psalm.xml' # Node.js build and lint node-build: @@ -58,7 +58,7 @@ jobs: name: Node.js build defaults: run: - working-directory: third_party/astroglobe + working-directory: third_party/astrolabe steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -67,7 +67,7 @@ jobs: uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 id: versions with: - path: third_party/astroglobe + path: third_party/astrolabe fallbackNode: '^20' fallbackNpm: '^10' @@ -99,7 +99,7 @@ jobs: name: ESLint defaults: run: - working-directory: third_party/astroglobe + working-directory: third_party/astrolabe steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -108,7 +108,7 @@ jobs: uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 id: versions with: - path: third_party/astroglobe + path: third_party/astrolabe fallbackNode: '^20' fallbackNpm: '^10' @@ -137,7 +137,7 @@ jobs: name: Stylelint defaults: run: - working-directory: third_party/astroglobe + working-directory: third_party/astrolabe steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -146,7 +146,7 @@ jobs: uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 id: versions with: - path: third_party/astroglobe + path: third_party/astrolabe fallbackNode: '^20' fallbackNpm: '^10' @@ -175,7 +175,7 @@ jobs: name: PHP CS Fixer defaults: run: - working-directory: third_party/astroglobe + working-directory: third_party/astrolabe steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -184,7 +184,7 @@ jobs: id: versions uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 with: - filename: third_party/astroglobe/appinfo/info.xml + filename: third_party/astrolabe/appinfo/info.xml - name: Set up php${{ steps.versions.outputs.php-min }} uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 @@ -212,7 +212,7 @@ jobs: name: Psalm defaults: run: - working-directory: third_party/astroglobe + working-directory: third_party/astrolabe steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -221,7 +221,7 @@ jobs: id: versions uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 with: - filename: third_party/astroglobe/appinfo/info.xml + filename: third_party/astrolabe/appinfo/info.xml - name: Set up php${{ steps.versions.outputs.php-min }} uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 @@ -242,7 +242,7 @@ jobs: id: ocp-versions uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 with: - filename: third_party/astroglobe/appinfo/info.xml + filename: third_party/astrolabe/appinfo/info.xml - name: Install OCP for static analysis run: | @@ -253,14 +253,62 @@ jobs: - name: Run Psalm run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github + # PHPUnit Tests + phpunit: + runs-on: ubuntu-latest + needs: changes + if: needs.changes.outputs.php != 'false' + defaults: + run: + working-directory: third_party/astrolabe + + strategy: + matrix: + php-versions: ['8.1', '8.2', '8.3'] + + name: PHPUnit (PHP ${{ matrix.php-versions }}) + + steps: + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Set up PHP ${{ matrix.php-versions }} + uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 + with: + php-version: ${{ matrix.php-versions }} + extensions: ctype, curl, dom, gd, iconv, intl, json, mbstring, openssl, posix, sqlite, xml, zip + coverage: none + ini-file: development + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Install dependencies + run: | + composer remove nextcloud/ocp --dev || true + composer i + + - name: Get OCP version matrix + id: ocp-versions + uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 + with: + filename: third_party/astrolabe/appinfo/info.xml + + - name: Install OCP for testing + run: | + OCP_VERSION=$(echo '${{ steps.ocp-versions.outputs.ocp-matrix }}' | jq -r '.include[0]."ocp-version"') + composer require --dev "nextcloud/ocp:$OCP_VERSION" --ignore-platform-reqs --with-dependencies + + - name: Run PHPUnit + run: composer run test:unit + # Summary job summary: permissions: contents: none runs-on: ubuntu-latest - needs: [changes, node-build, eslint, stylelint, php-cs, psalm] + needs: [changes, node-build, eslint, stylelint, php-cs, psalm, phpunit] if: always() - name: astroglobe-ci-summary + name: astrolabe-ci-summary steps: - name: Summary status run: | @@ -268,7 +316,7 @@ jobs: echo "Frontend checks failed" exit 1 fi - if ${{ needs.changes.outputs.php != 'false' && (needs.php-cs.result != 'success' || needs.psalm.result != 'success') }}; then + if ${{ needs.changes.outputs.php != 'false' && (needs.php-cs.result != 'success' || needs.psalm.result != 'success' || needs.phpunit.result != 'success') }}; then echo "PHP checks failed" exit 1 fi diff --git a/docs/configuration.md b/docs/configuration.md index f29fbdd..208ba6f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -531,6 +531,28 @@ docker-compose up --- +## Astrolabe Internal URL + +The Astrolabe Nextcloud app may need to make internal HTTP requests to the local web server (e.g., for OAuth token refresh). By default, it uses `http://localhost` which works for standard Docker containers where PHP and Apache run together. + +| Variable | Description | Default | +|----------|-------------|---------| +| `astrolabe_internal_url` | Internal URL for server-to-server requests within container | `http://localhost` | + +**When to configure:** +- Custom container setups where the internal web server is not on `localhost:80` +- Kubernetes deployments with service discovery +- Multi-container setups with separate web server containers + +**Example (Nextcloud config.php):** +```php +'astrolabe_internal_url' => 'http://web-server.internal:8080', +``` + +**Note:** This is for internal PHP-to-Apache requests, NOT for external client URLs. The default (`http://localhost`) works for standard Docker containers where PHP and Apache run together. + +--- + ## Loading Environment Variables After creating your `.env` file, load the environment variables: diff --git a/tests/integration/test_astrolabe_token_refresh.py b/tests/integration/test_astrolabe_token_refresh.py new file mode 100644 index 0000000..069c49e --- /dev/null +++ b/tests/integration/test_astrolabe_token_refresh.py @@ -0,0 +1,695 @@ +"""Integration tests for Astrolabe token refresh flow. + +Tests the token refresh mechanism between Astrolabe (Nextcloud app) +and the MCP server backend in a multi-user basic auth deployment. + +This test verifies: +1. User provisions access via Astrolabe personal settings +2. Token is stored encrypted in Nextcloud database +3. Token expires (simulated via database manipulation) +4. MCP server requests new token via refresh +5. Astrolabe refreshes token with IdP +6. New token is stored and used successfully + +Note: The mcp-multi-user-basic deployment uses "hybrid mode" which requires +BOTH OAuth authorization AND app password for full configuration. These tests +focus on the app password/credential storage aspects and verify database state +directly rather than relying on UI elements that require both steps. +""" + +import logging +import re +import subprocess + +import anyio +import pytest +from playwright.async_api import Page + +pytestmark = [pytest.mark.integration, pytest.mark.oauth] + +logger = logging.getLogger(__name__) + + +async def login_to_nextcloud(page: Page, username: str, password: str): + """Helper function to login to Nextcloud via Playwright. + + Args: + page: Playwright page instance + username: Nextcloud username + password: Nextcloud password + """ + nextcloud_url = "http://localhost:8080" + + logger.info(f"Logging in to Nextcloud as {username}...") + await page.goto(f"{nextcloud_url}/login", wait_until="networkidle") + + # Fill in login form + await page.wait_for_selector('input[name="user"]', timeout=10000) + await page.fill('input[name="user"]', username) + await page.fill('input[name="password"]', password) + + # Submit form + await page.click('button[type="submit"]') + await page.wait_for_load_state("networkidle", timeout=30000) + + # Verify logged in (should redirect away from login page) + current_url = page.url + assert "/login" not in current_url, ( + f"Login failed for {username}, still on login page" + ) + logger.info(f"✓ Successfully logged in as {username}") + + +async def generate_app_password( + page: Page, username: str, app_name: str = "Astrolabe Test" +) -> str: + """Generate an app password in Nextcloud Security settings. + + Args: + page: Playwright page instance (must be authenticated) + username: Username (for logging) + app_name: Name for the app password + + Returns: + The generated app password string + """ + logger.info(f"Generating app password for {username}...") + + nextcloud_url = "http://localhost:8080" + + # Navigate to Security settings + await page.goto(f"{nextcloud_url}/settings/user/security", wait_until="networkidle") + logger.info("Navigated to Security settings") + + # Fill the app password input field + app_password_input = page.locator('input[placeholder="App name"]') + await app_password_input.fill(app_name) + logger.info(f"Entered app name: {app_name}") + + # Wait for Vue.js to react and enable the button + await anyio.sleep(1.0) + + # Click the create button + create_button = page.locator( + 'button[type="submit"]:has-text("Create new app password")' + ) + await create_button.click() + logger.info("Clicked create app password button") + + # Wait for app password to be generated + await anyio.sleep(3) + + # Find the generated app password + app_password = None + try: + await page.wait_for_selector('text="New app password"', timeout=10000) + logger.info("App password dialog appeared") + + all_inputs = await page.locator('input[type="text"]').all() + for idx, input_elem in enumerate(all_inputs): + try: + value = await input_elem.input_value() + if value and "-" in value and len(value) > 20: + app_password = value.strip() + logger.info(f"Found app password in input {idx}") + break + except Exception: + continue + except Exception as e: + logger.error(f"Failed to find app password dialog: {e}") + + if not app_password: + screenshot_path = f"/tmp/app_password_generation_{username}.png" + await page.screenshot(path=screenshot_path) + raise ValueError( + f"Could not find generated app password. Screenshot: {screenshot_path}" + ) + + # Validate password format + if not re.match( + r"^[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}-[a-zA-Z0-9]{5}$", + app_password, + ): + raise ValueError(f"App password format validation failed: {app_password}") + + logger.info(f"✓ Generated app password for {username}") + + # Close the dialog + close_button = page.get_by_role("button", name="Close") + await close_button.click() + await anyio.sleep(0.5) + + return app_password + + +async def save_app_password_in_astrolabe( + page: Page, username: str, app_password: str +) -> bool: + """Save app password in Astrolabe settings (Step 2 of hybrid mode). + + This function only saves the app password - it does NOT verify the "Active" + badge since that requires both OAuth and app password in hybrid mode. + + Args: + page: Playwright page instance + username: Username (for logging) + app_password: App password to enter + + Returns: + True if the password was saved successfully (based on network response) + """ + logger.info(f"Saving app password in Astrolabe for {username}...") + + nextcloud_url = "http://localhost:8080" + + # Track network responses + credentials_response_status = None + + def capture_response(resp): + nonlocal credentials_response_status + if "background-sync/credentials" in resp.url or "storeAppPassword" in resp.url: + credentials_response_status = resp.status + logger.info(f"Credentials endpoint response: {resp.status} {resp.url}") + + page.on("response", capture_response) + + # Navigate to Astrolabe settings + await page.goto( + f"{nextcloud_url}/settings/user/astrolabe", wait_until="networkidle" + ) + await anyio.sleep(1) + + # Check if Step 2 already shows "Complete" + try: + complete_badge = page.locator('text="Complete"').first + if await complete_badge.is_visible(timeout=2000): + logger.info(f"✓ App password already configured for {username}") + return True + except Exception: + pass + + # Find the app password input field + app_password_input = page.get_by_placeholder("xxxxx-xxxxx-xxxxx-xxxxx-xxxxx") + + try: + await app_password_input.wait_for(timeout=5000, state="visible") + logger.info("Found app password input field") + except Exception: + screenshot_path = f"/tmp/astrolabe_no_password_field_{username}.png" + await page.screenshot(path=screenshot_path) + raise ValueError( + f"Could not find app password input field. Screenshot: {screenshot_path}" + ) + + # Enter the app password + await app_password_input.fill(app_password) + logger.info(f"Entered app password for {username}") + + await anyio.sleep(0.5) + + # Click Save button + save_button = page.get_by_role("button", name="Save") + await save_button.click() + logger.info("Clicked Save button") + + # Wait for the request to complete and page to reload + await page.wait_for_load_state("networkidle", timeout=15000) + await anyio.sleep(2) + + # Verify the save was successful by checking network response + if credentials_response_status == 200: + logger.info(f"✓ App password saved successfully for {username}") + return True + else: + logger.error( + f"App password save failed for {username}, status: {credentials_response_status}" + ) + screenshot_path = f"/tmp/astrolabe_save_failed_{username}.png" + await page.screenshot(path=screenshot_path) + return False + + +def get_background_sync_credentials(username: str) -> dict | None: + """Get background sync credentials for a user from the database. + + Args: + username: Nextcloud username + + Returns: + Dict with credential details, or None if not found + """ + query = f""" + SELECT configkey, configvalue + FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey IN ('background_sync_password', 'background_sync_type', 'background_sync_provisioned_at') + ORDER BY configkey; + """ + + try: + result = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + + output = result.stdout + if "background_sync_type" in output: + return { + "has_password": "background_sync_password" in output, + "has_type": "background_sync_type" in output, + "has_timestamp": "background_sync_provisioned_at" in output, + "is_app_password": "app_password" in output, + } + return None + + except Exception as e: + logger.error(f"Error getting credentials for {username}: {e}") + return None + + +def delete_user_credentials(username: str) -> bool: + """Delete all stored credentials for a user (for cleanup). + + Args: + username: Nextcloud username + + Returns: + True if successful + """ + query = f""" + DELETE FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey IN ('oauth_tokens', 'background_sync_password', 'background_sync_type', 'background_sync_provisioned_at'); + """ + + try: + result = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + + logger.info(f"Deleted credentials for {username}") + return result.returncode == 0 + + except Exception as e: + logger.error(f"Error deleting credentials for {username}: {e}") + return False + + +@pytest.mark.integration +@pytest.mark.oauth +async def test_app_password_storage_and_cleanup( + browser, + nc_client, + test_users_setup, + configure_astrolabe_for_mcp_server, +): + """Test that app passwords are stored and cleaned up correctly. + + This test verifies: + 1. User can save app password in Astrolabe settings + 2. Password is stored encrypted in the database + 3. Credentials can be revoked and are deleted from database + + Note: In hybrid mode (mcp-multi-user-basic), this only tests Step 2 + (app password storage). The "Active" badge requires both OAuth and + app password, which is tested separately. + """ + # Configure Astrolabe for mcp-multi-user-basic + logger.info("Configuring Astrolabe for mcp-multi-user-basic server...") + await configure_astrolabe_for_mcp_server( + mcp_server_internal_url="http://mcp-multi-user-basic:8000", + mcp_server_public_url="http://localhost:8003", + ) + + username = "alice" + user_config = test_users_setup[username] + password = user_config["password"] + + # Cleanup any existing credentials + delete_user_credentials(username) + + context = await browser.new_context(ignore_https_errors=True) + page = await context.new_page() + + try: + # Step 1: Login + await login_to_nextcloud(page, username, password) + + # Step 2: Verify no credentials exist initially + initial_creds = get_background_sync_credentials(username) + assert initial_creds is None, f"Expected no credentials, found: {initial_creds}" + logger.info("✓ Verified no initial credentials") + + # Step 3: Generate app password + app_password = await generate_app_password(page, username) + assert app_password, "Failed to generate app password" + + # Step 4: Save app password in Astrolabe + save_success = await save_app_password_in_astrolabe( + page, username, app_password + ) + assert save_success, "Failed to save app password" + + # Step 5: Verify credentials are stored in database + stored_creds = get_background_sync_credentials(username) + assert stored_creds is not None, "Expected credentials to be stored" + assert stored_creds["has_password"], "Expected password to be stored" + assert stored_creds["has_type"], "Expected type to be stored" + assert stored_creds["is_app_password"], "Expected type to be 'app_password'" + logger.info("✓ Verified credentials stored in database") + + # Step 6: Verify password is encrypted (not plaintext) + query = f""" + SELECT configvalue + FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey = 'background_sync_password'; + """ + + result = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-N", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + + encrypted_value = result.stdout.strip() + assert app_password not in encrypted_value, "Password appears in plaintext!" + assert len(encrypted_value) > len(app_password), ( + "Encrypted value should be longer" + ) + logger.info("✓ Verified password is encrypted") + + finally: + await context.close() + # Cleanup + delete_user_credentials(username) + + +@pytest.mark.integration +@pytest.mark.oauth +async def test_credential_isolation_between_users( + browser, + nc_client, + test_users_setup, + configure_astrolabe_for_mcp_server, +): + """Test that credentials are properly isolated between users. + + This test verifies: + 1. Multiple users can provision credentials independently + 2. Each user's encrypted credentials are unique + 3. Deleting one user's credentials doesn't affect others + """ + await configure_astrolabe_for_mcp_server( + mcp_server_internal_url="http://mcp-multi-user-basic:8000", + mcp_server_public_url="http://localhost:8003", + ) + + test_users = ["alice", "bob"] + user_passwords = {} + + # Cleanup all users first + for username in test_users: + delete_user_credentials(username) + + # Provision each user + for username in test_users: + user_config = test_users_setup[username] + password = user_config["password"] + + context = await browser.new_context(ignore_https_errors=True) + page = await context.new_page() + + try: + await login_to_nextcloud(page, username, password) + app_password = await generate_app_password( + page, username, f"Test {username}" + ) + save_success = await save_app_password_in_astrolabe( + page, username, app_password + ) + + assert save_success, f"Failed to save app password for {username}" + user_passwords[username] = app_password + + # Verify stored + creds = get_background_sync_credentials(username) + assert creds is not None, f"Credentials not stored for {username}" + logger.info(f"✓ Credentials provisioned for {username}") + + finally: + await context.close() + + # Verify isolation - get encrypted values + encrypted_values = {} + for username in test_users: + query = f""" + SELECT configvalue + FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey = 'background_sync_password'; + """ + + result = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-N", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + encrypted_values[username] = result.stdout.strip() + + # Different users should have different encrypted values + assert encrypted_values["alice"] != encrypted_values["bob"], ( + "Different users should have different encrypted values" + ) + logger.info("✓ Verified credentials are unique per user") + + # Delete alice's credentials and verify bob's are unaffected + delete_user_credentials("alice") + + alice_creds = get_background_sync_credentials("alice") + bob_creds = get_background_sync_credentials("bob") + + assert alice_creds is None, "Alice's credentials should be deleted" + assert bob_creds is not None, "Bob's credentials should still exist" + logger.info("✓ Verified credential deletion is isolated") + + # Cleanup + for username in test_users: + delete_user_credentials(username) + + +@pytest.mark.integration +@pytest.mark.oauth +async def test_credential_revoke_and_reprovision( + browser, + nc_client, + test_users_setup, + configure_astrolabe_for_mcp_server, +): + """Test that credentials can be revoked and reprovisioned. + + This test verifies: + 1. User provisions credentials + 2. User revokes credentials (deletes from database) + 3. User provisions again with new app password + 4. New credentials are stored correctly + + Note: The UI prevents overwriting credentials directly - users must + revoke first before provisioning new credentials. + """ + await configure_astrolabe_for_mcp_server( + mcp_server_internal_url="http://mcp-multi-user-basic:8000", + mcp_server_public_url="http://localhost:8003", + ) + + username = "alice" + user_config = test_users_setup[username] + password = user_config["password"] + + delete_user_credentials(username) + + context = await browser.new_context(ignore_https_errors=True) + page = await context.new_page() + + try: + await login_to_nextcloud(page, username, password) + + # First provisioning + app_password_1 = await generate_app_password(page, username, "First Password") + await save_app_password_in_astrolabe(page, username, app_password_1) + + # Get first encrypted value + query = f""" + SELECT configvalue + FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey = 'background_sync_password'; + """ + + result1 = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-N", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + first_encrypted = result1.stdout.strip() + assert first_encrypted, "First credential should be stored" + logger.info("✓ First credential stored") + + # Revoke credentials (simulating user clicking "Revoke Access") + delete_user_credentials(username) + logger.info("✓ Credentials revoked") + + # Verify credentials are gone + creds_after_revoke = get_background_sync_credentials(username) + assert creds_after_revoke is None, "Credentials should be deleted after revoke" + + # Second provisioning with different password + app_password_2 = await generate_app_password(page, username, "Second Password") + await save_app_password_in_astrolabe(page, username, app_password_2) + + result2 = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-N", + "-e", + query, + ], + capture_output=True, + text=True, + timeout=10, + ) + second_encrypted = result2.stdout.strip() + assert second_encrypted, "Second credential should be stored" + logger.info("✓ Second credential stored") + + # Verify the encrypted values are different (different passwords) + assert first_encrypted != second_encrypted, ( + "Different passwords should produce different encrypted values" + ) + + # Verify only one row exists + count_query = f""" + SELECT COUNT(*) + FROM oc_preferences + WHERE userid = '{username}' + AND appid = 'astrolabe' + AND configkey = 'background_sync_password'; + """ + + count_result = subprocess.run( + [ + "docker", + "compose", + "exec", + "-T", + "db", + "mariadb", + "-u", + "root", + "-ppassword", + "nextcloud", + "-N", + "-e", + count_query, + ], + capture_output=True, + text=True, + timeout=10, + ) + count = int(count_result.stdout.strip()) + assert count == 1, f"Expected 1 credential row, found {count}" + logger.info("✓ Verified clean reprovision after revoke") + + finally: + await context.close() + delete_user_credentials(username) diff --git a/third_party/astrolabe/.github/dependabot.yml b/third_party/astrolabe/.github/dependabot.yml deleted file mode 100644 index 852b265..0000000 --- a/third_party/astrolabe/.github/dependabot.yml +++ /dev/null @@ -1,50 +0,0 @@ -version: 2 -updates: - - package-ecosystem: composer - directory: "/" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 - - package-ecosystem: composer - directory: "/vendor-bin/cs-fixer" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 - - package-ecosystem: composer - directory: "/vendor-bin/openapi-extractor" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 - - package-ecosystem: composer - directory: "/vendor-bin/phpunit" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 - - package-ecosystem: composer - directory: "/vendor-bin/psalm" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 - - package-ecosystem: npm - directory: "/" - schedule: - interval: weekly - day: saturday - time: "03:00" - timezone: Europe/Paris - open-pull-requests-limit: 10 diff --git a/third_party/astrolabe/.github/workflows/block-unconventional-commits.yml b/third_party/astrolabe/.github/workflows/block-unconventional-commits.yml deleted file mode 100644 index 6bf1a79..0000000 --- a/third_party/astrolabe/.github/workflows/block-unconventional-commits.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block unconventional commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: block-unconventional-commits-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-unconventional-commits: - name: Block unconventional commits - - runs-on: ubuntu-latest-low - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - uses: webiny/action-conventional-commits@8bc41ff4e7d423d56fa4905f6ff79209a78776c7 # v1.3.0 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/third_party/astrolabe/.github/workflows/fixup.yml b/third_party/astrolabe/.github/workflows/fixup.yml deleted file mode 100644 index 69da2bb..0000000 --- a/third_party/astrolabe/.github/workflows/fixup.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block fixup and squash commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: fixup-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - commit-message-check: - if: github.event.pull_request.draft == false - - permissions: - pull-requests: write - name: Block fixup and squash commits - - runs-on: ubuntu-latest-low - - steps: - - name: Run check - uses: skjnldsv/block-fixup-merge-action@c138ea99e45e186567b64cf065ce90f7158c236a # v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/third_party/astrolabe/.github/workflows/lint-eslint.yml b/third_party/astrolabe/.github/workflows/lint-eslint.yml deleted file mode 100644 index 1b1d532..0000000 --- a/third_party/astrolabe/.github/workflows/lint-eslint.yml +++ /dev/null @@ -1,100 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint eslint - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-eslint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'src/**' - - 'appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '.eslintrc.*' - - '.eslintignore' - - '**.js' - - '**.ts' - - '**.vue' - - lint: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM lint - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: npm ci - - - name: Lint - run: npm run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, lint] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: eslint - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.lint.result != 'success' }}; then exit 1; fi diff --git a/third_party/astrolabe/.github/workflows/lint-info-xml.yml b/third_party/astrolabe/.github/workflows/lint-info-xml.yml deleted file mode 100644 index 25b6550..0000000 --- a/third_party/astrolabe/.github/workflows/lint-info-xml.yml +++ /dev/null @@ -1,38 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint info.xml - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-info-xml-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - xml-linters: - runs-on: ubuntu-latest-low - - name: info.xml lint - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Download schema - run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd - - - name: Lint info.xml - uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2 - with: - xml-file: ./appinfo/info.xml - xml-schema-file: ./info.xsd diff --git a/third_party/astrolabe/.github/workflows/lint-php-cs.yml b/third_party/astrolabe/.github/workflows/lint-php-cs.yml deleted file mode 100644 index a1a246f..0000000 --- a/third_party/astrolabe/.github/workflows/lint-php-cs.yml +++ /dev/null @@ -1,52 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php-cs - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-cs-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - - name: php-cs - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Get php version - id: versions - uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 - - - name: Set up php${{ steps.versions.outputs.php-min }} - uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 - with: - php-version: ${{ steps.versions.outputs.php-min }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - run: | - composer remove nextcloud/ocp --dev - composer i - - - name: Lint - run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) diff --git a/third_party/astrolabe/.github/workflows/lint-php.yml b/third_party/astrolabe/.github/workflows/lint-php.yml deleted file mode 100644 index 09052af..0000000 --- a/third_party/astrolabe/.github/workflows/lint-php.yml +++ /dev/null @@ -1,75 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - matrix: - runs-on: ubuntu-latest-low - outputs: - php-versions: ${{ steps.versions.outputs.php-versions }} - steps: - - name: Checkout app - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Get version matrix - id: versions - uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 - - php-lint: - runs-on: ubuntu-latest - needs: matrix - strategy: - matrix: - php-versions: ${{fromJson(needs.matrix.outputs.php-versions)}} - - name: php-lint - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 - with: - php-version: ${{ matrix.php-versions }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Lint - run: composer run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: php-lint - - if: always() - - name: php-lint-summary - - steps: - - name: Summary status - run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi diff --git a/third_party/astrolabe/.github/workflows/lint-stylelint.yml b/third_party/astrolabe/.github/workflows/lint-stylelint.yml deleted file mode 100644 index 22c0f44..0000000 --- a/third_party/astrolabe/.github/workflows/lint-stylelint.yml +++ /dev/null @@ -1,53 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint stylelint - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-stylelint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - - name: stylelint - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - run: npm ci - - - name: Lint - run: npm run stylelint diff --git a/third_party/astrolabe/.github/workflows/node.yml b/third_party/astrolabe/.github/workflows/node.yml deleted file mode 100644 index d1f18a1..0000000 --- a/third_party/astrolabe/.github/workflows/node.yml +++ /dev/null @@ -1,107 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Node - -on: pull_request - -permissions: - contents: read - -concurrency: - group: node-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'src/**' - - 'appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.js' - - '**.ts' - - '**.vue' - - build: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM build - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies & build - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - npm run build --if-present - - - name: Check webpack build changes - run: | - bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please recompile and commit the assets, see the section \"Show changes on failure\" for details' && exit 1)" - - - name: Show changes on failure - if: failure() - run: | - git status - git --no-pager diff - exit 1 # make it red to grab attention - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, build] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: node - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.build.result != 'success' }}; then exit 1; fi diff --git a/third_party/astrolabe/.github/workflows/npm-audit-fix.yml b/third_party/astrolabe/.github/workflows/npm-audit-fix.yml deleted file mode 100644 index 09b8ef2..0000000 --- a/third_party/astrolabe/.github/workflows/npm-audit-fix.yml +++ /dev/null @@ -1,81 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Npm audit fix and compile - -on: - workflow_dispatch: - schedule: - # At 2:30 on Sundays - - cron: '30 2 * * 0' - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: ['main', 'master', 'stable31', 'stable30'] - - name: npm-audit-fix-${{ matrix.branches }} - - steps: - - name: Checkout - id: checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - ref: ${{ matrix.branches }} - continue-on-error: true - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Fix npm audit - id: npm-audit - uses: nextcloud-libraries/npm-audit-action@1b1728b2b4a7a78d69de65608efcf4db0e3e42d0 # v0.2.0 - - - name: Run npm ci and npm run build - if: steps.checkout.outcome == 'success' - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci - npm run build --if-present - - - name: Create Pull Request - if: steps.checkout.outcome == 'success' - uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'fix(deps): Fix npm audit' - committer: GitHub - author: nextcloud-command - signoff: true - branch: automated/noid/${{ matrix.branches }}-fix-npm-audit - title: '[${{ matrix.branches }}] Fix npm audit' - body: ${{ steps.npm-audit.outputs.markdown }} - labels: | - dependencies - 3. to review diff --git a/third_party/astrolabe/.github/workflows/openapi.yml b/third_party/astrolabe/.github/workflows/openapi.yml deleted file mode 100644 index e67896e..0000000 --- a/third_party/astrolabe/.github/workflows/openapi.yml +++ /dev/null @@ -1,96 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-FileCopyrightText: 2024 Arthur Schiwon -# SPDX-License-Identifier: MIT - -name: OpenAPI - -on: pull_request - -permissions: - contents: read - -concurrency: - group: openapi-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - openapi: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Get php version - id: php_versions - uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 - - - name: Set up php - uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 - with: - php-version: ${{ steps.php_versions.outputs.php-available }} - extensions: xml - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check Typescript OpenApi types - id: check_typescript_openapi - uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v3.0.0 - with: - files: "src/types/openapi/openapi*.ts" - - - name: Read package.json node and npm engines version - if: steps.check_typescript_openapi.outputs.files_exists == 'true' - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: node_versions - # Continue if no package.json - continue-on-error: true - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.node_versions.outputs.nodeVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 - with: - node-version: ${{ steps.node_versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.node_versions.outputs.npmVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}' - - - name: Install dependencies - if: ${{ steps.node_versions.outputs.nodeVersion }} - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - - - name: Set up dependencies - run: composer i - - - name: Regenerate OpenAPI - run: composer run openapi - - - name: Check openapi*.json and typescript changes - run: | - bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please run \"composer run openapi\" and commit the openapi*.json files and (if applicable) src/types/openapi/openapi*.ts, see the section \"Show changes on failure\" for details' && exit 1)" - - - name: Show changes on failure - if: failure() - run: | - git status - git --no-pager diff - exit 1 # make it red to grab attention diff --git a/third_party/astrolabe/.github/workflows/psalm-matrix.yml b/third_party/astrolabe/.github/workflows/psalm-matrix.yml deleted file mode 100644 index c353167..0000000 --- a/third_party/astrolabe/.github/workflows/psalm-matrix.yml +++ /dev/null @@ -1,87 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Static analysis - -on: pull_request - -concurrency: - group: psalm-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - matrix: - runs-on: ubuntu-latest-low - outputs: - ocp-matrix: ${{ steps.versions.outputs.ocp-matrix }} - steps: - - name: Checkout app - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Get version matrix - id: versions - uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1 - - - name: Check enforcement of minimum PHP version ${{ steps.versions.outputs.php-min }} in psalm.xml - run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml - - static-analysis: - runs-on: ubuntu-latest - needs: matrix - strategy: - # do not stop on another job's failure - fail-fast: false - matrix: ${{ fromJson(needs.matrix.outputs.ocp-matrix) }} - - name: static-psalm-analysis ${{ matrix.ocp-version }} - steps: - - name: Checkout - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: Set up php${{ matrix.php-min }} - uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 - with: - php-version: ${{ matrix.php-min }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install dependencies - run: | - composer remove nextcloud/ocp --dev - composer i - - - - name: Install dependencies # zizmor: ignore[template-injection] - run: composer require --dev 'nextcloud/ocp:${{ matrix.ocp-version }}' --ignore-platform-reqs --with-dependencies - - - name: Run coding standards check - run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github - - summary: - runs-on: ubuntu-latest-low - needs: static-analysis - - if: always() - - name: static-psalm-analysis-summary - - steps: - - name: Summary status - run: if ${{ needs.static-analysis.result != 'success' }}; then exit 1; fi diff --git a/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-approve-merge.yml b/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-approve-merge.yml deleted file mode 100644 index dfe0ef4..0000000 --- a/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-approve-merge.yml +++ /dev/null @@ -1,58 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Auto approve nextcloud/ocp - -on: - pull_request_target: # zizmor: ignore[dangerous-triggers] - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: update-nextcloud-ocp-approve-merge-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - auto-approve-merge: - if: github.actor == 'nextcloud-command' - runs-on: ubuntu-latest-low - permissions: - # for hmarr/auto-approve-action to approve PRs - pull-requests: write - # for alexwilson/enable-github-automerge-action to approve PRs - contents: write - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not approve PRs from forks' - exit 1 - - - uses: mdecoleman/pr-branch-name@55795d86b4566d300d237883103f052125cc7508 # v3.0.0 - id: branchname - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - # GitHub actions bot approve - - uses: hmarr/auto-approve-action@b40d6c9ed2fa10c9a2749eca7eb004418a705501 # v2 - if: startsWith(steps.branchname.outputs.branch, 'automated/noid/') && endsWith(steps.branchname.outputs.branch, 'update-nextcloud-ocp') - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - # Enable GitHub auto merge - - name: Auto merge - uses: alexwilson/enable-github-automerge-action@56e3117d1ae1540309dc8f7a9f2825bc3c5f06ff # v2.0.0 - if: startsWith(steps.branchname.outputs.branch, 'automated/noid/') && endsWith(steps.branchname.outputs.branch, 'update-nextcloud-ocp') - with: - github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-matrix.yml b/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-matrix.yml deleted file mode 100644 index b6cf554..0000000 --- a/third_party/astrolabe/.github/workflows/update-nextcloud-ocp-matrix.yml +++ /dev/null @@ -1,101 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Update nextcloud/ocp - -on: - workflow_dispatch: - schedule: - - cron: '5 2 * * 0' - -permissions: - contents: read - -jobs: - update-nextcloud-ocp: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: ['master'] - target: ['stable30'] - - name: update-nextcloud-ocp-${{ matrix.branches }} - - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - ref: ${{ matrix.branches }} - submodules: true - - - name: Set up php8.2 - uses: shivammathur/setup-php@cf4cade2721270509d5b1c766ab3549210a39a2a # v2.33.0 - with: - php-version: 8.2 - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Read codeowners - id: codeowners - run: | - grep '/appinfo/info.xml' .github/CODEOWNERS | cut -f 2- -d ' ' | xargs | awk '{ print "codeowners="$0 }' >> $GITHUB_OUTPUT - continue-on-error: true - - - name: Composer install - run: composer install - - - name: Composer update nextcloud/ocp - id: update_branch - run: composer require --dev nextcloud/ocp:dev-${{ matrix.target }} - - - name: Raise on issue on failure - uses: dacbd/create-issue-action@cdb57ab6ff8862aa09fee2be6ba77a59581921c2 # v2.0.0 - if: ${{ failure() && steps.update_branch.conclusion == 'failure' }} - with: - token: ${{ secrets.GITHUB_TOKEN }} - title: 'Failed to update nextcloud/ocp package' - body: 'Please check the output of the GitHub action and manually resolve the issues
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ steps.codeowners.outputs.codeowners }}' - - - name: Reset checkout 3rdparty - run: | - git clean -f 3rdparty - git checkout 3rdparty - continue-on-error: true - - - name: Reset checkout vendor - run: | - git clean -f vendor - git checkout vendor - continue-on-error: true - - - name: Reset checkout vendor-bin - run: | - git clean -f vendor-bin - git checkout vendor-bin - continue-on-error: true - - - name: Create Pull Request - uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7.0.11 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'chore(dev-deps): Bump nextcloud/ocp package' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-nextcloud-ocp' - title: '[${{ matrix.branches }}] Update nextcloud/ocp dependency' - body: | - Auto-generated update of [nextcloud/ocp](https://github.com/nextcloud-deps/ocp/) dependency - labels: | - dependencies - 3. to review diff --git a/third_party/astrolabe/.gitignore b/third_party/astrolabe/.gitignore index afd5e5d..9039f45 100644 --- a/third_party/astrolabe/.gitignore +++ b/third_party/astrolabe/.gitignore @@ -12,3 +12,4 @@ build/ node_modules/ js/ css/ +.phpunit.cache/ diff --git a/third_party/astrolabe/composer.json b/third_party/astrolabe/composer.json index 2a0ef6e..d153cc0 100644 --- a/third_party/astrolabe/composer.json +++ b/third_party/astrolabe/composer.json @@ -14,6 +14,11 @@ "OCA\\Astrolabe\\": "lib/" } }, + "autoload-dev": { + "psr-4": { + "OCP\\": "vendor/nextcloud/ocp/OCP/" + } + }, "scripts": { "post-install-cmd": [ "@composer bin all install --ansi" @@ -25,7 +30,7 @@ "cs:check": "php-cs-fixer fix --dry-run --diff", "cs:fix": "php-cs-fixer fix", "psalm": "psalm --threads=1 --no-cache", - "test:unit": "phpunit tests -c tests/phpunit.xml --colors=always --fail-on-warning --fail-on-risky", + "test:unit": "./vendor/bin/phpunit -c tests/unit/phpunit.xml --colors=always", "openapi": "generate-spec", "rector": "rector && composer cs:fix" }, @@ -35,6 +40,7 @@ }, "require-dev": { "nextcloud/ocp": "dev-stable30", + "phpunit/phpunit": "^10.0", "roave/security-advisories": "dev-latest" }, "config": { diff --git a/third_party/astrolabe/composer.lock b/third_party/astrolabe/composer.lock index 9a2db16..59c26e1 100644 --- a/third_party/astrolabe/composer.lock +++ b/third_party/astrolabe/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9a07fd98e858321235b781204a3de248", + "content-hash": "94a9d7f7619235ef2a310deec2ce14f0", "packages": [ { "name": "bamarni/composer-bin-plugin", @@ -65,6 +65,66 @@ } ], "packages-dev": [ + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, { "name": "nextcloud/ocp", "version": "dev-stable30", @@ -109,6 +169,612 @@ }, "time": "2025-12-02T00:53:40+00:00" }, + { + "name": "nikic/php-parser", + "version": "v5.7.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.7.0" + }, + "time": "2025-12-06T11:56:16+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^10.1" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", + "keywords": [ + "timer" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.60", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "f2e26f52f80ef77832e359205f216eeac00e320c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f2e26f52f80ef77832e359205f216eeac00e320c", + "reference": "f2e26f52f80ef77832e359205f216eeac00e320c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.4", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.4", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.1", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.60" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-12-06T07:50:42+00:00" + }, { "name": "psr/clock", "version": "1.0.0", @@ -1313,6 +1979,1009 @@ } ], "time": "2025-12-12T23:06:01+00:00" + }, + { + "name": "sebastian/cli-parser", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", + "support": { + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "support": { + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "reference": "e8e53097718d2b53cfb2aa859b06a41abf58c62e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" + } + ], + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", + "keywords": [ + "comparator", + "compare", + "equality" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" + } + ], + "time": "2025-09-07T05:25:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", + "support": { + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" + } + ], + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.4", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "0735b90f4da94969541dac1da743446e276defa6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "reference": "0735b90f4da94969541dac1da743446e276defa6", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", + "keywords": [ + "export", + "exporter" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:09:11+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "support": { + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "support": { + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + } + ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", + "support": { + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" + } + ], + "time": "2025-08-10T07:50:56+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", + "support": { + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", + "support": { + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "theseer/tokenizer", + "version": "1.3.1", + "source": { + "type": "git", + "url": "https://github.com/theseer/tokenizer.git", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-tokenizer": "*", + "ext-xmlwriter": "*", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + } + ], + "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", + "support": { + "issues": "https://github.com/theseer/tokenizer/issues", + "source": "https://github.com/theseer/tokenizer/tree/1.3.1" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2025-11-17T20:03:58+00:00" } ], "aliases": [], @@ -1330,5 +2999,5 @@ "platform-overrides": { "php": "8.1" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/third_party/astrolabe/lib/Controller/ApiController.php b/third_party/astrolabe/lib/Controller/ApiController.php index 5dbd6ec..a2139b9 100644 --- a/third_party/astrolabe/lib/Controller/ApiController.php +++ b/third_party/astrolabe/lib/Controller/ApiController.php @@ -26,13 +26,13 @@ use Psr\Log\LoggerInterface; * Handles form submissions and AJAX requests from settings panels. */ class ApiController extends Controller { - private $client; - private $userSession; - private $urlGenerator; - private $logger; - private $tokenStorage; - private $config; - private $tokenRefresher; + private McpServerClient $client; + private IUserSession $userSession; + private IURLGenerator $urlGenerator; + private LoggerInterface $logger; + private McpTokenStorage $tokenStorage; + private IConfig $config; + private IdpTokenRefresher $tokenRefresher; public function __construct( string $appName, diff --git a/third_party/astrolabe/lib/Controller/CredentialsController.php b/third_party/astrolabe/lib/Controller/CredentialsController.php index 9786e9f..4d414a2 100644 --- a/third_party/astrolabe/lib/Controller/CredentialsController.php +++ b/third_party/astrolabe/lib/Controller/CredentialsController.php @@ -23,13 +23,13 @@ use Psr\Log\LoggerInterface; * Handles storing and validating app passwords for multi-user BasicAuth mode. */ class CredentialsController extends Controller { - private $tokenStorage; - private $userSession; - private $logger; - private $config; - private $client; - private $httpClientService; - private $urlGenerator; + private McpTokenStorage $tokenStorage; + private IUserSession $userSession; + private LoggerInterface $logger; + private IConfig $config; + private McpServerClient $client; + private IClientService $httpClientService; + private IURLGenerator $urlGenerator; public function __construct( string $appName, @@ -112,7 +112,7 @@ class CredentialsController extends Controller { // Get MCP server URL from system config (set in config.php) $mcpServerUrl = $this->config->getSystemValue('mcp_server_url', ''); if (empty($mcpServerUrl)) { - $this->logger->warning("MCP server URL not configured, app password stored locally only"); + $this->logger->warning('MCP server URL not configured, app password stored locally only'); return new JSONResponse([ 'success' => true, 'partial_success' => true, diff --git a/third_party/astrolabe/lib/Controller/OAuthController.php b/third_party/astrolabe/lib/Controller/OAuthController.php index b6698b2..aa21811 100644 --- a/third_party/astrolabe/lib/Controller/OAuthController.php +++ b/third_party/astrolabe/lib/Controller/OAuthController.php @@ -12,6 +12,7 @@ use OCP\AppFramework\Http\Attribute\NoAdminRequired; use OCP\AppFramework\Http\Attribute\NoCSRFRequired; use OCP\AppFramework\Http\RedirectResponse; use OCP\AppFramework\Http\TemplateResponse; +use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\IConfig; use OCP\IL10N; @@ -32,15 +33,15 @@ use Psr\Log\LoggerInterface; * - Confidential clients: PKCE + client_secret (defense in depth) */ class OAuthController extends Controller { - private $config; - private $session; - private $userSession; - private $urlGenerator; - private $tokenStorage; - private $logger; - private $l; - private $httpClient; - private $client; + private IConfig $config; + private ISession $session; + private IUserSession $userSession; + private IURLGenerator $urlGenerator; + private McpTokenStorage $tokenStorage; + private LoggerInterface $logger; + private IL10N $l; + private IClient $httpClient; + private McpServerClient $client; public function __construct( string $appName, diff --git a/third_party/astrolabe/lib/Service/IdpTokenRefresher.php b/third_party/astrolabe/lib/Service/IdpTokenRefresher.php index f7faa4c..682e105 100644 --- a/third_party/astrolabe/lib/Service/IdpTokenRefresher.php +++ b/third_party/astrolabe/lib/Service/IdpTokenRefresher.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\Astrolabe\Service; +use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\IConfig; use Psr\Log\LoggerInterface; @@ -18,10 +19,10 @@ use Psr\Log\LoggerInterface; * Public clients without client_secret cannot refresh tokens. */ class IdpTokenRefresher { - private $config; - private $httpClient; - private $logger; - private $mcpServerClient; + private IConfig $config; + private IClient $httpClient; + private LoggerInterface $logger; + private McpServerClient $mcpServerClient; public function __construct( IConfig $config, @@ -38,23 +39,47 @@ class IdpTokenRefresher { /** * Get Nextcloud base URL for constructing internal OIDC endpoint URLs. * - * Uses Nextcloud's CLI URL config if set (for non-containerized deployments), - * otherwise defaults to http://localhost for container environments. + * IMPORTANT: This is for INTERNAL server-to-server requests (PHP to local Apache), + * NOT for external client URLs. We must use the internal container URL, not the + * external URL that browsers see. * * Configuration priority: - * 1. overwrite.cli.url - Official Nextcloud system config for CLI operations + * 1. astrolabe_internal_url - Explicit internal URL (for custom container setups) * 2. http://localhost - Default for Docker containers (web server on port 80) * + * NOTE: We intentionally DO NOT use overwrite.cli.url here because: + * - overwrite.cli.url is the EXTERNAL URL (e.g., http://localhost:8080) + * - External URLs are not accessible from inside the container + * - This method is for internal HTTP requests to the local web server + * * @return string Base URL for internal requests (e.g., "http://localhost") */ private function getNextcloudBaseUrl(): string { - // Check for overwrite.cli.url (used in non-containerized deployments) - $cliUrl = $this->config->getSystemValue('overwrite.cli.url', ''); - if (!empty($cliUrl)) { - return rtrim($cliUrl, '/'); + // Check for explicit internal URL config (for custom container setups) + $internalUrl = $this->config->getSystemValue('astrolabe_internal_url', ''); + if (!is_string($internalUrl)) { + $internalUrl = ''; + } + if (!empty($internalUrl)) { + // Validate URL format + if (!filter_var($internalUrl, FILTER_VALIDATE_URL)) { + $this->logger->warning('Invalid astrolabe_internal_url format, using default', [ + 'configured_url' => $internalUrl, + ]); + return 'http://localhost'; + } + // Warn if it looks like an external URL (common misconfiguration) + if (preg_match('/:\d{4,5}$/', $internalUrl)) { + $this->logger->warning('astrolabe_internal_url appears to use external port mapping', [ + 'configured_url' => $internalUrl, + 'hint' => 'Internal URLs should use port 80, not mapped ports like :8080', + ]); + } + return rtrim($internalUrl, '/'); } // Default: container environment with web server on localhost:80 + // This works because PHP runs inside the same container as Apache return 'http://localhost'; } @@ -97,7 +122,7 @@ class IdpTokenRefresher { // External IdP configured - use OIDC discovery $discoveryUrl = $statusData['oidc']['discovery_url']; - $this->logger->info('IdpTokenRefresher: Using external IdP', [ + $this->logger->debug('IdpTokenRefresher: Using external IdP', [ 'discovery_url' => $discoveryUrl, ]); @@ -113,7 +138,7 @@ class IdpTokenRefresher { // Nextcloud's OIDC app - use internal URL $tokenEndpoint = $this->getNextcloudBaseUrl() . '/apps/oidc/token'; - $this->logger->info('IdpTokenRefresher: Using Nextcloud OIDC app', [ + $this->logger->debug('IdpTokenRefresher: Using Nextcloud OIDC app', [ 'token_endpoint' => $tokenEndpoint, ]); } @@ -158,11 +183,38 @@ class IdpTokenRefresher { return $tokenData; - } catch (\Exception $e) { - $this->logger->error('IdpTokenRefresher: Token refresh failed', [ + } catch (\OCP\Http\Client\LocalServerException $e) { + // Network/connection error - may be transient + $this->logger->warning('IdpTokenRefresher: Network error during refresh', [ 'error' => $e->getMessage(), ]); return null; + } catch (\Exception $e) { + $statusCode = null; + if (method_exists($e, 'getCode')) { + $statusCode = $e->getCode(); + } + + // Log with appropriate level based on error type + if ($statusCode === 401 || $statusCode === 403) { + // Auth error - token is invalid, should be deleted + $this->logger->error('IdpTokenRefresher: Auth error - token invalid', [ + 'status_code' => $statusCode, + 'error' => $e->getMessage(), + ]); + } elseif ($statusCode >= 500) { + // Server error - may be transient + $this->logger->warning('IdpTokenRefresher: Server error during refresh', [ + 'status_code' => $statusCode, + 'error' => $e->getMessage(), + ]); + } else { + $this->logger->error('IdpTokenRefresher: Token refresh failed', [ + 'status_code' => $statusCode, + 'error' => $e->getMessage(), + ]); + } + return null; } } } diff --git a/third_party/astrolabe/lib/Service/McpServerClient.php b/third_party/astrolabe/lib/Service/McpServerClient.php index 2338135..4316ed9 100644 --- a/third_party/astrolabe/lib/Service/McpServerClient.php +++ b/third_party/astrolabe/lib/Service/McpServerClient.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace OCA\Astrolabe\Service; +use OCP\Http\Client\IClient; use OCP\Http\Client\IClientService; use OCP\IConfig; use Psr\Log\LoggerInterface; @@ -16,10 +17,10 @@ use Psr\Log\LoggerInterface; * for all management operations. */ class McpServerClient { - private $httpClient; - private $config; - private $logger; - private $baseUrl; + private IClient $httpClient; + private IConfig $config; + private LoggerInterface $logger; + private string $baseUrl; public function __construct( IClientService $clientService, @@ -31,7 +32,8 @@ class McpServerClient { $this->logger = $logger; // Get MCP server configuration from Nextcloud config - $this->baseUrl = $this->config->getSystemValue('mcp_server_url', 'http://localhost:8000'); + $baseUrl = $this->config->getSystemValue('mcp_server_url', 'http://localhost:8000'); + $this->baseUrl = is_string($baseUrl) ? $baseUrl : 'http://localhost:8000'; } /** diff --git a/third_party/astrolabe/lib/Service/McpTokenStorage.php b/third_party/astrolabe/lib/Service/McpTokenStorage.php index 62cef18..5d499fa 100644 --- a/third_party/astrolabe/lib/Service/McpTokenStorage.php +++ b/third_party/astrolabe/lib/Service/McpTokenStorage.php @@ -15,6 +15,9 @@ use Psr\Log\LoggerInterface; * Handles token expiration checking and refresh logic. */ class McpTokenStorage { + /** Buffer time in seconds before actual expiry to trigger refresh */ + private const TOKEN_EXPIRY_BUFFER_SECONDS = 60; + private $config; private $crypto; private $logger; @@ -112,7 +115,7 @@ class McpTokenStorage { /** * Check if a token is expired or about to expire. * - * Uses a 60-second buffer to refresh tokens before they actually expire. + * Uses TOKEN_EXPIRY_BUFFER_SECONDS buffer to refresh tokens before they actually expire. * * @param array $token Token data array * @return bool True if expired or about to expire @@ -122,8 +125,8 @@ class McpTokenStorage { return true; } - // Expire 60 seconds early to avoid race conditions - return time() >= ($token['expires_at'] - 60); + // Expire early to avoid race conditions + return time() >= ($token['expires_at'] - self::TOKEN_EXPIRY_BUFFER_SECONDS); } /** @@ -191,11 +194,19 @@ class McpTokenStorage { $this->logger->error("Failed to refresh token for user $userId", [ 'error' => $e->getMessage() ]); - // Fall through to return null + // Delete stale token to prevent repeated refresh attempts + $this->deleteUserToken($userId); + return null; } + + // Refresh callback returned null or invalid data - delete stale token + $this->deleteUserToken($userId); + $this->logger->info("Deleted stale token for user $userId after refresh failure"); + return null; } - // Token expired and no refresh available + // Token expired and no refresh callback available - delete stale token + $this->deleteUserToken($userId); $this->logger->info("Token expired for user $userId, no refresh available"); return null; } diff --git a/third_party/astrolabe/package-lock.json b/third_party/astrolabe/package-lock.json index 081dd54..1a3fcd8 100644 --- a/third_party/astrolabe/package-lock.json +++ b/third_party/astrolabe/package-lock.json @@ -1,12 +1,12 @@ { "name": "astrolabe", - "version": "0.6.0", + "version": "0.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "astrolabe", - "version": "0.6.0", + "version": "0.8.2", "license": "AGPL-3.0-or-later", "dependencies": { "@nextcloud/axios": "^2.5.1", @@ -14,7 +14,7 @@ "@nextcloud/initial-state": "^3.0.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/router": "^3.0.1", - "@nextcloud/vue": "^9.0.0", + "@nextcloud/vue": "^9.3.3", "markdown-it": "^14.1.0", "pdfjs-dist": "^4.0.379", "plotly.js-dist-min": "^2.35.3", @@ -1657,9 +1657,9 @@ } }, "node_modules/@nextcloud/vue": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.3.1.tgz", - "integrity": "sha512-zNit83SI7IPT5iT9QsYPCYNwBYvKEqzLvWKTeJemqg9MZ8JGIC3/jjENeXzDolrTN/PixHns5lOYVCejATE1ag==", + "version": "9.3.3", + "resolved": "https://registry.npmjs.org/@nextcloud/vue/-/vue-9.3.3.tgz", + "integrity": "sha512-M/M4L9vp1AJQ8RRk75mbMwUo7sOwWDaTDmAwgpTa9LARDe5e6UBJoMhOmiz5EPkYRHLn2SLE+baOIXVmtVMdqw==", "license": "AGPL-3.0-or-later", "dependencies": { "@ckpack/vue-color": "^1.6.0", @@ -1671,7 +1671,7 @@ "@nextcloud/event-bus": "^3.3.3", "@nextcloud/initial-state": "^3.0.0", "@nextcloud/l10n": "^3.4.1", - "@nextcloud/logger": "^3.0.2", + "@nextcloud/logger": "^3.0.3", "@nextcloud/router": "^3.1.0", "@nextcloud/sharing": "^0.3.0", "@vuepic/vue-datepicker": "^11.0.3", @@ -1684,9 +1684,9 @@ "emoji-mart-vue-fast": "^15.0.5", "escape-html": "^1.0.3", "floating-vue": "^5.2.2", - "focus-trap": "^7.6.6", + "focus-trap": "7.6.6", "linkifyjs": "^4.3.2", - "p-queue": "^9.0.1", + "p-queue": "^9.1.0", "rehype-external-links": "^3.0.0", "rehype-highlight": "^7.0.2", "rehype-react": "^8.0.0", @@ -1696,14 +1696,14 @@ "remark-unlink-protocols": "^1.0.0", "splitpanes": "^4.0.4", "striptags": "^3.2.0", - "tabbable": "^6.3.0", + "tabbable": "^6.4.0", "tributejs": "^5.1.3", "ts-md5": "^2.0.1", "unified": "^11.0.5", "unist-builder": "^4.0.0", "unist-util-visit": "^5.0.0", "vue": "^3.5.18", - "vue-router": "^4.6.3", + "vue-router": "^4.6.4", "vue-select": "^4.0.0-beta.6" }, "engines": { @@ -7751,9 +7751,9 @@ } }, "node_modules/p-queue": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.0.1.tgz", - "integrity": "sha512-RhBdVhSwJb7Ocn3e8ULk4NMwBEuOxe+1zcgphUy9c2e5aR/xbEsdVXxHJ3lynw6Qiqu7OINEyHlZkiblEpaq7w==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", + "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", "license": "MIT", "dependencies": { "eventemitter3": "^5.0.1", @@ -9693,7 +9693,9 @@ } }, "node_modules/tabbable": { - "version": "6.3.0", + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", "license": "MIT" }, "node_modules/table": { diff --git a/third_party/astrolabe/package.json b/third_party/astrolabe/package.json index b1cbf83..6202050 100644 --- a/third_party/astrolabe/package.json +++ b/third_party/astrolabe/package.json @@ -23,7 +23,7 @@ "@nextcloud/initial-state": "^3.0.0", "@nextcloud/l10n": "^3.1.0", "@nextcloud/router": "^3.0.1", - "@nextcloud/vue": "^9.0.0", + "@nextcloud/vue": "^9.3.3", "markdown-it": "^14.1.0", "pdfjs-dist": "^4.0.379", "plotly.js-dist-min": "^2.35.3", diff --git a/third_party/astrolabe/psalm-baseline.xml b/third_party/astrolabe/psalm-baseline.xml new file mode 100644 index 0000000..cf66cc8 --- /dev/null +++ b/third_party/astrolabe/psalm-baseline.xml @@ -0,0 +1,512 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getBody()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getBody()]]> + getBody()]]> + + + getBody()]]> + getBody()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + getBody()]]> + getBody()]]> + getBody()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + , + * error?: string + * }]]> + + + , + * total_found?: int, + * algorithm_used?: string, + * error?: string + * }]]> + + + + , + * error?: string + * }]]> + + + + + + + + + + + + + + + + + config->getSystemValue('mcp_server_public_url', $this->baseUrl)]]> + + + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + getBody()]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $eventConfig['event']]]> + + + + + + + + + ]]> + $eventConfig['event'], + $preset['events'] + )]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/third_party/astrolabe/psalm.xml b/third_party/astrolabe/psalm.xml index e2853b7..920055b 100644 --- a/third_party/astrolabe/psalm.xml +++ b/third_party/astrolabe/psalm.xml @@ -8,6 +8,7 @@ findUnusedBaselineEntry="true" findUnusedCode="true" phpVersion="8.1" + errorBaseline="psalm-baseline.xml" > diff --git a/third_party/astrolabe/src/App.vue b/third_party/astrolabe/src/App.vue index 8ce12c0..4996699 100644 --- a/third_party/astrolabe/src/App.vue +++ b/third_party/astrolabe/src/App.vue @@ -62,7 +62,7 @@ @update:model-value="algorithm = $event ? $event.id : 'hybrid'" />