diff --git a/.github/workflows/acceptance-tests-vault.yml b/.github/workflows/acceptance-tests-vault.yml new file mode 100644 index 00000000000..cedaf7ab07b --- /dev/null +++ b/.github/workflows/acceptance-tests-vault.yml @@ -0,0 +1,103 @@ +name: apiVault iteration + +on: + workflow_dispatch: + push: + branches: + - feat/ocisdev-821-5 + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 + with: + go-version-file: go.mod + cache: true + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: "10.28.1" + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "24" + - name: Enable pnpm + run: corepack enable && corepack prepare pnpm@10.33.3 --activate + - name: Install libvips-dev + run: sudo apt-get update -qq && sudo NEEDRESTART_MODE=a apt-get install -y libvips-dev + - name: Generate frontend assets + run: make ci-node-generate + - name: Build ocis and ociswrapper + run: | + ENABLE_VIPS=true make -C ocis build & + GOWORK=off make -C tests/ociswrapper build & + wait + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ocis-binary + path: ocis/bin/ocis + retention-days: 1 + if-no-files-found: error + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ociswrapper-binary + path: tests/ociswrapper/bin/ociswrapper + retention-days: 1 + if-no-files-found: error + + api-tests-vault: + name: apiVault + needs: [build] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + keycloak: "true" + keycloak-realm-file: tests/config/ci/ocis-mfa-ci-realm.dist.json + web-ui-config: tests/config/ci/ocis-config-vault.json + extra-server-env: | + { + "OCIS_ENABLE_VAULT_MODE": "true", + "FRONTEND_ENABLE_VAULT_MODE": "true", + "OCIS_MFA_ENABLED": "true", + "WEB_OIDC_SCOPE": "openid profile email acr" + } + - name: Cache Playwright browsers + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ hashFiles('web/pnpm-lock.yaml') }} + - name: Install Playwright and browsers + run: | + vendor-php/bin/playwright-install + vendor-php/bin/playwright-install --browsers + - name: Start vault storage-users + run: | + OCIS_JWT_SECRET=some-ocis-jwt-secret \ + STORAGE_USERS_ENABLE_VAULT_MODE=true \ + STORAGE_USERS_SERVICE_NAME=storage-users-vault \ + STORAGE_USERS_GRPC_ADDR=localhost:19285 \ + STORAGE_USERS_HTTP_ADDR=localhost:19286 \ + STORAGE_USERS_DATA_SERVER_URL=http://localhost:19286/data \ + STORAGE_USERS_DEBUG_ADDR=localhost:19287 \ + STORAGE_USERS_OCIS_ROOT="${HOME}/ocis/storage/users-vault" \ + STORAGE_USERS_EVENTS_CONSUMER_GROUP=vault-dcfs \ + ocis storage-users server &>/tmp/vault-storage-users.log & + timeout 30 bash -c 'until curl -sf http://localhost:19287/healthz; do sleep 1; done' \ + || (cat /tmp/vault-storage-users.log && exit 1) + - name: Test apiVault + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiVault + env: + KEYCLOAK: "true" + KC_URL: "https://localhost:8443" diff --git a/.github/workflows/acceptance-tests.yml b/.github/workflows/acceptance-tests.yml index b1ed134f50e..b75730718d1 100644 --- a/.github/workflows/acceptance-tests.yml +++ b/.github/workflows/acceptance-tests.yml @@ -59,6 +59,9 @@ jobs: go-version-file: go.mod cache: true + - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4 + with: + version: "10.28.1" - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: "24" @@ -157,17 +160,40 @@ jobs: exit $FAILED - - name: Upload build artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + - name: Upload oCIS binary + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 with: - name: ocis-build-artifacts - path: | - ocis/bin/ocis - tests/ociswrapper/bin/ociswrapper + name: ocis-binary + path: ocis/bin/ocis retention-days: 1 if-no-files-found: error - local-api-tests: + - name: Upload ociswrapper binary + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: ociswrapper-binary + path: tests/ociswrapper/bin/ociswrapper + retention-days: 1 + if-no-files-found: error + + unit-tests: + name: unit-tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 + with: + go-version-file: go.mod + cache: true + - name: Run unit tests + run: make test + + # --------------------------------------------------------------------------- + # API acceptance tests — split by required services so each group gets a + # single fixed ocis-setup call with no per-suite conditionals. + # --------------------------------------------------------------------------- + + api-tests-plain: name: ${{ matrix.suite }} needs: [build-and-test] runs-on: ubuntu-latest @@ -178,10 +204,6 @@ jobs: # contract & locks - apiContract - apiLocks - # settings & notifications (needs email) - - apiSettings - - apiNotification - - apiCors # graph - apiGraphUser - apiGraph @@ -195,10 +217,8 @@ jobs: - apiDepthInfinity - apiArchiver - apiActivities - # search + # search (no tika) - apiSearch1 - - apiSearch2 - - apiSearchContent # needs Tika # sharing - apiSharingNgShares - apiReshare @@ -209,76 +229,290 @@ jobs: - apiSharingNgDriveLinkShare - apiSharingNgItemLinkShare - apiSharingNgLinkShareManagement - # auth - - apiAuthApp - # antivirus (needs ClamAV) - - apiAntivirus - # federation (needs email + federation ocis) - - apiOcm - # collaboration (needs WOPI) - - apiCollaboration - - apiVault steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + - name: Test ${{ matrix.suite }} + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: ${{ matrix.suite }} + api-tests-email: + name: ${{ matrix.suite }} + needs: [build-and-test] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + suite: + # settings & notifications (needs Mailpit) + - apiSettings + - apiNotification + - apiCors + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 with: php-version: "8.4" extensions: curl, xml, mbstring, zip, ldap, gd tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + email: "true" + - name: Test ${{ matrix.suite }} + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: ${{ matrix.suite }} - - name: Cache libcurl 8.12.0 - id: cache-libcurl - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + api-tests-tika: + name: ${{ matrix.suite }} + needs: [build-and-test] + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + suite: + # full-text search (needs Tika) + - apiSearch2 + - apiSearchContent + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - path: /opt/libcurl - key: libcurl-8.12.0-${{ runner.os }} + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + tika: "true" + - name: Test ${{ matrix.suite }} + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: ${{ matrix.suite }} - - name: Install libcurl 8.12.0 build dependencies - # Always run: libvips-dev is needed at runtime (govips thumbnails in the pre-built binary) - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libssl-dev libnghttp2-dev libpsl-dev libldap-dev libssh-dev zlib1g-dev libvips-dev + api-tests-antivirus: + name: apiAntivirus + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + antivirus: "true" + extra-server-env: '{"POSTPROCESSING_STEPS":"virusscan"}' + - name: Test apiAntivirus + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiAntivirus - - name: Compile libcurl 8.12.0 from source - if: steps.cache-libcurl.outputs.cache-hit != 'true' - run: | - cd /tmp - curl -sLO https://curl.se/download/curl-8.12.0.tar.gz - tar xzf curl-8.12.0.tar.gz - cd curl-8.12.0 - ./configure --with-ssl --with-zlib --with-nghttp2 --prefix=/opt/libcurl --enable-versioned-symbols --silent - make -j$(nproc) --silent - sudo make install --silent - - - name: Restore libcurl ldconfig - # Always run: on cache hit the .so files are restored to /opt/libcurl/lib but - # /etc/ld.so.cache on the fresh runner doesn't know about them yet. - # Without ldconfig, PHP's curl extension can't find libcurl even though it's present. - run: | - echo "/opt/libcurl/lib" | sudo tee /etc/ld.so.conf.d/libcurl-8.conf - sudo ldconfig - /opt/libcurl/bin/curl --version | head -1 - php -r ' - $v = curl_version()["version"]; - echo "PHP curl: $v\n"; - if (version_compare($v, "8.12.0", "<")) { - fwrite(STDERR, "FATAL: PHP sees libcurl $v, need >= 8.12.0\n"); - exit(1); - } - ' + api-tests-auth-app: + name: apiAuthApp + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + extra-server-env: '{"OCIS_ADD_RUN_SERVICES":"auth-app","PROXY_ENABLE_APP_AUTH":"true"}' + - name: Test apiAuthApp + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiAuthApp - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + api-tests-ocm: + name: apiOcm + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Patch OCM providers.json — replace Docker hostnames with localhost for CI + run: | + # providers.json uses Docker Compose hostnames (ocis-server, federation-ocis-server). + # In CI both instances run on localhost at ports 9200/10200. + sed \ + -e 's|ocis-server:9200|localhost:9200|g' \ + -e 's|federation-ocis-server:10200|localhost:10200|g' \ + tests/config/ci/providers.json > /tmp/ocm-providers.json + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + email: "true" + extra-server-env: >- + {"OCIS_ADD_RUN_SERVICES":"ocm,notifications","OCIS_ENABLE_OCM":"true", + "OCM_OCM_INVITE_MANAGER_INSECURE":"true","OCM_OCM_SHARE_PROVIDER_INSECURE":"true", + "OCM_OCM_STORAGE_PROVIDER_INSECURE":"true", + "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE":"/tmp/ocm-providers.json", + "NOTIFICATIONS_SMTP_HOST":"localhost","NOTIFICATIONS_SMTP_PORT":"1025", + "NOTIFICATIONS_SMTP_INSECURE":"true", + "NOTIFICATIONS_SMTP_SENDER":"ownCloud ", + "NOTIFICATIONS_DEBUG_ADDR":"0.0.0.0:9174"} + - name: Setup federation oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary: ocis/bin/ocis + ocis-url: "https://localhost:10200" + ocis-config-dir: /home/runner/.ocis-federation/config + wrapper: "false" + demo-users: "true" + skip-test-runner-setup: "true" + pid-file: /tmp/ocis-federation.pid + log-file: /tmp/ocis-federation.log + debug-port-offset: "1000" + extra-server-env: >- + {"OCIS_BASE_DATA_PATH":"/home/runner/.ocis-federation", + "OCIS_RUNTIME_PORT":"10250", + "NATS_NATS_PORT":"10233", + "OCIS_EVENTS_ENDPOINT":"127.0.0.1:10233", + "MICRO_REGISTRY":"memory", + "OCIS_INSECURE":"true", + "OCIS_LDAP_URI":"ldaps://localhost:10235", + "STORAGE_SYSTEM_DATA_SERVER_URL":"http://localhost:10216/data", + "STORAGE_USERS_DATA_SERVER_URL":"http://localhost:10158/data", + "OCM_OCM_STORAGE_DATA_SERVER_URL":"http://localhost:10280/data", + "THUMBNAILS_DATA_ENDPOINT":"http://127.0.0.1:10190/thumbnails/data", + "OCIS_EXCLUDE_RUN_SERVICES":"idp", + "OCIS_ADD_RUN_SERVICES":"ocm,notifications", + "OCIS_ENABLE_OCM":"true", + "OCM_OCM_INVITE_MANAGER_INSECURE":"true", + "OCM_OCM_SHARE_PROVIDER_INSECURE":"true", + "OCM_OCM_STORAGE_PROVIDER_INSECURE":"true", + "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE":"/tmp/ocm-providers.json", + "NOTIFICATIONS_SMTP_HOST":"localhost","NOTIFICATIONS_SMTP_PORT":"1025", + "NOTIFICATIONS_SMTP_INSECURE":"true", + "NOTIFICATIONS_SMTP_SENDER":"ownCloud "} + - name: Test apiOcm + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiOcm + env: + TEST_SERVER_FED_URL: https://localhost:10200 - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper + api-tests-wopi: + name: apiCollaboration + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + extra-server-env: '{"GATEWAY_GRPC_ADDR":"0.0.0.0:9142"}' + collaboration-apps: "collabora,onlyoffice,fakeoffice" + - name: Test apiCollaboration + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiCollaboration + env: + COLLABORATION_SERVICE_URL: http://localhost:9320 - - name: Run ${{ matrix.suite }} - run: BEHAT_SUITES=${{ matrix.suite }} python3 tests/acceptance/run-github.py + api-tests-vault: + name: apiVault + needs: [build-and-test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 + with: + php-version: "8.4" + extensions: curl, xml, mbstring, zip, ldap, gd + tools: composer + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + keycloak: "true" + keycloak-realm-file: tests/config/ci/ocis-mfa-ci-realm.dist.json + web-ui-config: tests/config/ci/ocis-config-vault.json + extra-server-env: | + { + "OCIS_ENABLE_VAULT_MODE": "true", + "FRONTEND_ENABLE_VAULT_MODE": "true", + "OCIS_MFA_ENABLED": "true", + "WEB_OIDC_SCOPE": "openid profile email acr" + } + - name: Cache Playwright browsers + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ~/.cache/ms-playwright + key: playwright-chromium-${{ hashFiles('web/pnpm-lock.yaml') }} + - name: Install Playwright and browsers + run: | + vendor-php/bin/playwright-install + vendor-php/bin/playwright-install --browsers + - name: Start vault storage-users + run: | + OCIS_JWT_SECRET=some-ocis-jwt-secret \ + STORAGE_USERS_ENABLE_VAULT_MODE=true \ + STORAGE_USERS_SERVICE_NAME=storage-users-vault \ + STORAGE_USERS_GRPC_ADDR=localhost:19285 \ + STORAGE_USERS_HTTP_ADDR=localhost:19286 \ + STORAGE_USERS_DATA_SERVER_URL=http://localhost:19286/data \ + STORAGE_USERS_DEBUG_ADDR=localhost:19287 \ + STORAGE_USERS_OCIS_ROOT="${HOME}/ocis/storage/users-vault" \ + STORAGE_USERS_EVENTS_CONSUMER_GROUP=vault-dcfs \ + ocis storage-users server &>/tmp/vault-storage-users.log & + timeout 30 bash -c 'until curl -sf http://localhost:19287/healthz; do sleep 1; done' \ + || (cat /tmp/vault-storage-users.log && exit 1) + - name: Test apiVault + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: apiVault + env: + KEYCLOAK: "true" + KC_URL: "https://localhost:8443" cli-tests: needs: [build-and-test] @@ -291,29 +525,23 @@ jobs: - cliCommands,apiServiceAvailability # grouped: both need ClamAV + email services steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 with: php-version: "8.4" extensions: curl, xml, mbstring, zip, ldap, gd tools: composer - - - name: Install libvips runtime - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper - - - name: Run ${{ matrix.suite }} - run: BEHAT_SUITES="${{ matrix.suite }}" python3 tests/acceptance/run-github.py + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + email: "true" + antivirus: "true" + - name: Test ${{ matrix.suite }} + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: ${{ matrix.suite }} core-api-tests: name: ${{ matrix.suite }} @@ -334,69 +562,23 @@ jobs: - "coreApiWebdavMove1,coreApiWebdavPreviews,coreApiWebdavUpload,coreApiWebdavUploadTUS" steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 with: php-version: "8.4" extensions: curl, xml, mbstring, zip, ldap, gd tools: composer - - - name: Cache libcurl 8.12.0 - id: cache-libcurl - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - path: /opt/libcurl - key: libcurl-8.12.0-${{ runner.os }} - - - name: Install libcurl 8.12.0 build dependencies - # Always run: libvips-dev is needed at runtime (govips thumbnails in the pre-built binary) - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libssl-dev libnghttp2-dev libpsl-dev libldap-dev libssh-dev zlib1g-dev libvips-dev - - - name: Compile libcurl 8.12.0 from source - if: steps.cache-libcurl.outputs.cache-hit != 'true' - run: | - cd /tmp - curl -sLO https://curl.se/download/curl-8.12.0.tar.gz - tar xzf curl-8.12.0.tar.gz - cd curl-8.12.0 - ./configure --with-ssl --with-zlib --with-nghttp2 --prefix=/opt/libcurl --enable-versioned-symbols --silent - make -j$(nproc) --silent - sudo make install --silent - - - name: Restore libcurl ldconfig - # Always run: on cache hit the .so files are restored to /opt/libcurl/lib but - # /etc/ld.so.cache on the fresh runner doesn't know about them yet. - # Without ldconfig, PHP's curl extension can't find libcurl even though it's present. - run: | - echo "/opt/libcurl/lib" | sudo tee /etc/ld.so.conf.d/libcurl-8.conf - sudo ldconfig - /opt/libcurl/bin/curl --version | head -1 - php -r ' - $v = curl_version()["version"]; - echo "PHP curl: $v\n"; - if (version_compare($v, "8.12.0", "<")) { - fwrite(STDERR, "FATAL: PHP sees libcurl $v, need >= 8.12.0\n"); - exit(1); - } - ' - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + demo-users: "true" + - name: Test ${{ matrix.suite }} + uses: mklos-kw/ocis-github-actions/ocis-test@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper - - - name: Run ${{ matrix.suite }} - run: > - BEHAT_SUITES="${{ matrix.suite }}" - ACCEPTANCE_TEST_TYPE=core-api - WITH_REMOTE_PHP=true - python3 tests/acceptance/run-github.py + suite: ${{ matrix.suite }} + acceptance-test-type: core-api + with-remote-php: "true" e2e-tests: name: e2e-${{ matrix.suite }} @@ -420,9 +602,11 @@ jobs: - suite: keycloak args: "--type playwright --suites journeys,keycloak" keycloak: true + demo_users: "false" - suite: mfa args: "--type playwright --suites mfa" mfa: true + keycloak_realm_file: tests/config/ci/ocis-mfa-ci-realm.dist.json - suite: oidc args: "--type playwright specs/oidc/refreshToken.spec.ts" oidc: true @@ -436,6 +620,7 @@ jobs: args: "--type playwright --suites ocm" federated: true steps: + # --- tools --- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: @@ -453,135 +638,145 @@ jobs: # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper - + # --- install --- - name: Cache Playwright Chromium uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: ~/.cache/ms-playwright key: playwright-chromium-${{ hashFiles('web/pnpm-lock.yaml') }} - - # --- Tika (search suite only) --- - - name: Start Tika - if: matrix.tika == true + - name: Install web dependencies and Playwright run: | - docker run -d --name tika --network host apache/tika:3.2.2.0-full - timeout 120 bash -c 'until curl -sf http://localhost:9998; do sleep 2; done' - echo "tika ready." + pnpm install + pnpm exec playwright install --with-deps chromium + working-directory: web - # --- Keycloak (keycloak and mfa suites) --- - - name: Generate Keycloak certs - if: matrix.keycloak == true || matrix.mfa == true + # --- build extra-server-env (merge base + suite-specific vars) --- + - name: Build extra-server-env + shell: bash run: | - mkdir -p keycloak-certs - openssl req -x509 -newkey rsa:2048 \ - -keyout keycloak-certs/keycloakkey.pem \ - -out keycloak-certs/keycloakcrt.pem \ - -nodes -days 365 -subj '/CN=keycloak' \ - -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' - chmod -R 777 keycloak-certs - - - name: Start Postgres - if: matrix.keycloak == true || matrix.mfa == true - run: | - docker run -d --name postgres --network host \ - -e POSTGRES_DB=keycloak \ - -e POSTGRES_USER=keycloak \ - -e POSTGRES_PASSWORD=keycloak \ - postgres:alpine3.18 - timeout 30 bash -c 'until docker exec postgres pg_isready -U keycloak; do sleep 1; done' - - - name: Start Keycloak - if: matrix.keycloak == true || matrix.mfa == true - run: | - # The mfa suite needs a different realm with TOTP MFA configured. - # ocis-ci-realm uses ocis-server:9200, ocis-mfa-ci-realm uses localhost:9200; - # ocis runs on 127.0.0.1:9200 so we rewrite both forms. - if [[ "${{ matrix.mfa }}" == "true" ]]; then - REALM_FILE=tests/config/ci/ocis-mfa-ci-realm.dist.json - else - REALM_FILE=tests/config/ci/ocis-ci-realm.dist.json + BASE='{"THUMBNAILS_TXT_FONTMAP_FILE":"${{ github.workspace }}/tests/config/ci/fontsMap.json","OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST":"${{ github.workspace }}/tests/config/ci/banned-password-list.txt","OCIS_PASSWORD_POLICY_MIN_CHARACTERS":"3","OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS":"0","OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS":"0","OCIS_PASSWORD_POLICY_MIN_DIGITS":"0","OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS":"0","GRAPH_AVAILABLE_ROLES":"b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5,a8d5fe5e-96e3-418d-825b-534dbdf22b99,fb6c3e19-e378-47e5-b277-9732f9de6e21,58c63c02-1d89-4572-916a-870abc5a1b7d,2d00ce52-1fc2-4dbc-8b95-a73b73395f5a,1c996275-f1c9-4e71-abdf-a42f6495e960,312c0871-5ef7-4b3a-85b6-0e4074c64049,aa97fe03-7980-45ac-9e50-b325749fd7e6,63e64e19-8d43-42ec-a738-2b6af2610efa","FRONTEND_CONFIGURABLE_NOTIFICATIONS":"true"}' + SUITE_EXTRA='{}' + if [[ "${{ matrix.oidc }}" == "true" ]]; then + SUITE_EXTRA='{"IDP_ACCESS_TOKEN_EXPIRATION":"30","WEB_OIDC_SCOPE":"openid profile email offline_access"}' + elif [[ "${{ matrix.oidc_iframe }}" == "true" ]]; then + SUITE_EXTRA='{"IDP_ACCESS_TOKEN_EXPIRATION":"30"}' + elif [[ "${{ matrix.mfa }}" == "true" ]]; then + SUITE_EXTRA='{"OCIS_MFA_ENABLED":"true"}' + elif [[ "${{ matrix.collaboration }}" == "true" ]]; then + SUITE_EXTRA='{"PROXY_CSP_CONFIG_FILE_LOCATION":"${{ github.workspace }}/tests/config/ci/csp.yaml","COLLABORA_DOMAIN":"localhost:9980","ONLYOFFICE_DOMAIN":"localhost:443","FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR":"com.owncloud.api.collaboration.Collabora"}' + elif [[ "${{ matrix.federated }}" == "true" ]]; then + SUITE_EXTRA='{"OCIS_ADD_RUN_SERVICES":"ocm","OCIS_ENABLE_OCM":"true","OCM_OCM_INVITE_MANAGER_INSECURE":"true","OCM_OCM_SHARE_PROVIDER_INSECURE":"true","OCM_OCM_STORAGE_PROVIDER_INSECURE":"true","OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE":"${{ github.workspace }}/tests/config/local/providers.json"}' fi - sed -e 's|https://ocis-server:9200|https://127.0.0.1:9200|g' \ - -e 's|https://localhost:9200|https://127.0.0.1:9200|g' \ - "$REALM_FILE" > /tmp/ocis-realm.json - docker run -d --name keycloak --network host \ - -e OCIS_DOMAIN=https://127.0.0.1:9200 \ - -e KC_HOSTNAME=localhost \ - -e KC_PORT=8443 \ - -e KC_DB=postgres \ - -e "KC_DB_URL=jdbc:postgresql://localhost:5432/keycloak" \ - -e KC_DB_USERNAME=keycloak \ - -e KC_DB_PASSWORD=keycloak \ - -e KC_FEATURES=impersonation \ - -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ - -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ - -e KC_HTTPS_CERTIFICATE_FILE=/keycloak-certs/keycloakcrt.pem \ - -e KC_HTTPS_CERTIFICATE_KEY_FILE=/keycloak-certs/keycloakkey.pem \ - -v "$(pwd)/keycloak-certs:/keycloak-certs:ro" \ - -v "/tmp/ocis-realm.json:/opt/keycloak/data/import/oCIS-realm.json:ro" \ - quay.io/keycloak/keycloak:26.2.5 \ - start-dev --proxy-headers xforwarded \ - --spi-connections-http-client-default-disable-trust-manager=true \ - --import-realm --health-enabled=true - timeout 300 bash -c 'until curl -skf https://localhost:9000/health/ready; do sleep 3; done' \ - || (echo "=== keycloak logs ===" && docker logs keycloak --tail 80 && exit 1) - echo "keycloak ready." - - # --- Collabora + OnlyOffice (app-provider suite only) --- - - name: Start Collabora - if: matrix.collaboration == true - run: | - docker run -d \ - --name collabora \ - --network host \ - --entrypoint bash \ - -e DONT_GEN_SSL_CERT=set \ - -e "extra_params=--o:ssl.enable=true --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=https://127.0.0.1:9200" \ - collabora/code:25.04.7.3.1 \ - -c "coolconfig generate-proof-key && bash /start-collabora-online.sh" - timeout 150 bash -c 'until curl -kfsSL https://localhost:9980/hosting/discovery > /dev/null; do sleep 5; done' - echo "collabora ready." - - - name: Start OnlyOffice - if: matrix.collaboration == true + MERGED=$(jq -sc '.[0] * .[1]' <(echo "$BASE") <(echo "$SUITE_EXTRA")) + echo "OCIS_EXTRA_SERVER_ENV=$MERGED" >> "$GITHUB_ENV" + + # --- patch web-ui-config for suites that need custom OIDC scope or extra apps --- + - name: Prepare web-ui-config + shell: bash run: | - sudo systemctl stop postgresql || true - docker run -d \ - --name onlyoffice \ - --network host \ - -e WOPI_ENABLED=true \ - -e USE_UNAUTHORIZED_STORAGE=true \ - -v "${{ github.workspace }}/tests/config/ci/only-office.json:/tmp/local.json:ro" \ - --entrypoint /bin/sh \ - onlyoffice/documentserver:9.2.1 \ - -c "set -e - cp /tmp/local.json /etc/onlyoffice/documentserver/local.json - openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes - mkdir -p /var/www/onlyoffice/Data/certs - cp onlyoffice.key /var/www/onlyoffice/Data/certs/ - cp onlyoffice.crt /var/www/onlyoffice/Data/certs/ - chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key - /app/ds/run-document-server.sh" - timeout 150 bash -c 'until curl -kfsSL https://localhost:443/hosting/discovery > /dev/null; do sleep 5; done' - echo "onlyoffice ready." + SRC=tests/config/ci/ocis-config.json + DEST=tests/config/ci/ocis-config-suite.json + cp "$SRC" "$DEST" + if [[ "${{ matrix.oidc }}" == "true" ]]; then + jq '.openIdConnect.scope = "openid profile email offline_access"' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST" + elif [[ "${{ matrix.mfa }}" == "true" ]]; then + jq '.openIdConnect.scope = "openid profile email acr"' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST" + elif [[ "${{ matrix.federated }}" == "true" ]]; then + jq '.apps += ["ocm"]' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST" + elif [[ "${{ matrix.collaboration }}" == "true" ]]; then + jq '.apps += ["external"]' "$DEST" > "$DEST.tmp" && mv "$DEST.tmp" "$DEST" + fi + # --- services --- + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary-artifact: ocis-binary + ociswrapper-artifact: ociswrapper-binary + ocis-url: ${{ matrix.federated == true && 'https://localhost:9200' || 'https://127.0.0.1:9200' }} + demo-users: ${{ matrix.demo_users || 'true' }} + tika: ${{ matrix.tika || 'false' }} + keycloak: ${{ (matrix.keycloak == true || matrix.mfa == true) && 'true' || 'false' }} + keycloak-realm-file: ${{ matrix.keycloak_realm_file || 'tests/config/ci/ocis-ci-realm.dist.json' }} + web-ui-config: tests/config/ci/ocis-config-suite.json + collaboration-apps: ${{ matrix.collaboration == true && 'collabora,onlyoffice' || '' }} + collabora-image: collabora/code:25.04.7.3.1 + onlyoffice-image: onlyoffice/documentserver:9.2.1 + app-registry-file: ${{ matrix.collaboration == true && 'web/dev/docker/ocis/app-registry.yaml' || '' }} + # GRAPH_AVAILABLE_ROLES: viewer,spaceViewer,editor,spaceEditor,fileEditor, + # editorLite,manager,secureViewer,denied — full set required by e2e role tests. + extra-server-env: ${{ env.OCIS_EXTRA_SERVER_ENV }} + + # Save primary oCIS cert before federation setup overwrites OCIS_CERT + - name: Save primary oCIS cert + if: matrix.federated == true + shell: bash + run: echo "PRIMARY_OCIS_CERT=${{ env.OCIS_CERT }}" >> "$GITHUB_ENV" + + # --- OCM federation instance (ocm suite only, starts AFTER primary so binary is available) --- + - name: Setup federation oCIS + if: matrix.federated == true + uses: mklos-kw/ocis-github-actions/ocis-setup@main + with: + ocis-binary: ocis/bin/ocis + ocis-url: "https://localhost:10200" + ocis-config-dir: /home/runner/.ocis-federation/config + wrapper: "false" + demo-users: "true" + skip-test-runner-setup: "true" + pid-file: /tmp/ocis-federation.pid + log-file: /tmp/ocis-federation.log + debug-port-offset: "1000" + web-ui-config: tests/config/ci/ocis-config-suite.json + extra-server-env: >- + {"OCIS_BASE_DATA_PATH":"/home/runner/.ocis-federation", + "OCIS_RUNTIME_PORT":"10250", + "NATS_NATS_PORT":"10233", + "OCIS_EVENTS_ENDPOINT":"127.0.0.1:10233", + "MICRO_REGISTRY":"memory", + "OCIS_INSECURE":"true", + "OCIS_LDAP_URI":"ldaps://localhost:10235", + "STORAGE_SYSTEM_DATA_SERVER_URL":"http://localhost:10216/data", + "STORAGE_USERS_DATA_SERVER_URL":"http://localhost:10158/data", + "OCM_OCM_STORAGE_DATA_SERVER_URL":"http://localhost:10280/data", + "THUMBNAILS_DATA_ENDPOINT":"http://127.0.0.1:10190/thumbnails/data", + "OCIS_ADD_RUN_SERVICES":"ocm", + "OCIS_ENABLE_OCM":"true", + "OCM_OCM_INVITE_MANAGER_INSECURE":"true", + "OCM_OCM_SHARE_PROVIDER_INSECURE":"true", + "OCM_OCM_STORAGE_PROVIDER_INSECURE":"true", + "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE":"${{ github.workspace }}/tests/config/local/providers.json"} + + # --- run --- + - name: Combine CA certs for Node.js + shell: bash + run: | + cat "$OCIS_CERT" \ + ${KEYCLOAK_CERT:+"$KEYCLOAK_CERT"} \ + ${FEDERATED_CERT:+"$FEDERATED_CERT"} \ + > /tmp/combined-ca.pem + echo "CA_BUNDLE=/tmp/combined-ca.pem" >> "$GITHUB_ENV" + env: + OCIS_CERT: ${{ env.PRIMARY_OCIS_CERT || env.OCIS_CERT }} + KEYCLOAK_CERT: ${{ env.KEYCLOAK_CERT }} + FEDERATED_CERT: ${{ matrix.federated == true && '/home/runner/.ocis-federation/proxy/server.crt' || '' }} - name: Run e2e-${{ matrix.suite }} - run: E2E_ARGS="${{ matrix.args }}" python3 tests/acceptance/run-e2e.py + run: bash run-e2e.sh ${{ matrix.args }} + working-directory: web/tests/e2e env: - TIKA_NEEDED: ${{ matrix.tika == true && 'true' || 'false' }} - KEYCLOAK_NEEDED: ${{ matrix.keycloak == true && 'true' || 'false' }} - MFA_NEEDED: ${{ matrix.mfa == true && 'true' || 'false' }} - OIDC_NEEDED: ${{ matrix.oidc == true && 'true' || 'false' }} - OIDC_IFRAME_NEEDED: ${{ matrix.oidc_iframe == true && 'true' || 'false' }} - COLLABORATION_NEEDED: ${{ matrix.collaboration == true && 'true' || 'false' }} - FEDERATED_NEEDED: ${{ matrix.federated == true && 'true' || 'false' }} + BASE_URL_OCIS: ${{ matrix.federated == true && 'https://localhost:9200' || 'https://127.0.0.1:9200' }} + HEADLESS: "true" + RETRY: "3" + SKIP_A11Y_TESTS: "true" + NODE_EXTRA_CA_CERTS: ${{ env.CA_BUNDLE }} + BROWSER: chromium + KEYCLOAK: ${{ (matrix.keycloak == true || matrix.mfa == true) && 'true' || 'false' }} + KEYCLOAK_HOST: localhost:8443 + MFA: ${{ matrix.mfa == true && 'true' || 'false' }} + OIDC: ${{ matrix.oidc == true && 'true' || 'false' }} + COLLABORATION: ${{ matrix.collaboration == true && 'true' || 'false' }} + FEDERATED: ${{ matrix.federated == true && 'true' || 'false' }} + FEDERATED_BASE_URL_OCIS: localhost:10200 - name: Upload tracing result if: failure() @@ -605,23 +800,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Install libvips runtime - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis - + ocis-binary-artifact: ocis-binary + demo-users: "true" + wrapper: "false" + skip-test-runner-setup: "true" + docker-accessible: "true" + extra-server-env: '{"OCIS_ASYNC_UPLOADS":"false"}' - name: Run litmus - run: python3 tests/acceptance/run-litmus.py + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: litmus cs3api: name: cs3api @@ -629,23 +820,20 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Install libvips runtime - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis - + ocis-binary-artifact: ocis-binary + demo-users: "true" + wrapper: "false" + skip-test-runner-setup: "true" + extra-server-env: >- + {"GATEWAY_GRPC_ADDR":"0.0.0.0:9142", + "OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD":"false"} - name: Run cs3api validator - run: python3 tests/acceptance/run-cs3api.py + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: cs3api wopi-builtin: name: wopi-builtin @@ -653,23 +841,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Install libvips runtime - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis - - - name: Run WOPI validator (builtin) - run: python3 tests/acceptance/run-wopi.py --type builtin + ocis-binary-artifact: ocis-binary + wrapper: "false" + skip-test-runner-setup: "true" + collaboration-apps: "fakeoffice" + - name: Run WOPI validator + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: wopi-builtin wopi-cs3: name: wopi-cs3 @@ -677,26 +859,31 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - - name: Install libvips runtime - run: | - sudo apt-get update -qq - # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 - sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 - - - name: Download build artifacts - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + - name: Setup oCIS + uses: mklos-kw/ocis-github-actions/ocis-setup@main with: - name: ocis-build-artifacts - - - name: Restore binary permissions - run: chmod +x ocis/bin/ocis - - - name: Run WOPI validator (cs3) - run: python3 tests/acceptance/run-wopi.py --type cs3 + ocis-binary-artifact: ocis-binary + wrapper: "false" + skip-test-runner-setup: "true" + collaboration-apps: "cs3-wopi" + extra-server-env: | + { + "APP_PROVIDER_DRIVER": "wopi", + "APP_PROVIDER_WOPI_APP_NAME": "FakeOffice", + "APP_PROVIDER_WOPI_APP_URL": "http://localhost:8080", + "APP_PROVIDER_WOPI_INSECURE": "true", + "APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL": "http://localhost:9300", + "APP_PROVIDER_WOPI_FOLDER_URL_BASE_URL": "https://localhost:9200" + } + - name: Run WOPI validator + uses: mklos-kw/ocis-github-actions/ocis-test@main + with: + suite: wopi-cs3 all-acceptance-tests: - needs: [detect-changes, local-api-tests, cli-tests, core-api-tests, litmus, cs3api, wopi-builtin, wopi-cs3, e2e-tests] + # Not yet gating (run but don't block merge): api-tests-email, api-tests-tika, + # api-tests-auth-app, core-api-tests, e2e-tests + needs: [detect-changes, unit-tests, api-tests-plain, api-tests-antivirus, api-tests-ocm, api-tests-wopi, api-tests-vault, cli-tests, litmus, cs3api, wopi-builtin, wopi-cs3] runs-on: ubuntu-latest if: always() steps: diff --git a/.github/workflows/k6-load-test.yml b/.github/workflows/k6-load-test.yml index 883876aae8e..9c4fa94f77f 100644 --- a/.github/workflows/k6-load-test.yml +++ b/.github/workflows/k6-load-test.yml @@ -42,11 +42,11 @@ jobs: - name: Run k6 load tests if: ${{ !cancelled() }} - run: sh tests/config/drone/run_k6_tests.sh + run: sh tests/config/ci/run_k6_tests.sh - name: Retrieve OCIS logs if: ${{ !cancelled() }} - run: sh tests/config/drone/run_k6_tests.sh --ocis-log + run: sh tests/config/ci/run_k6_tests.sh --ocis-log - name: Show Grafana dashboard link if: ${{ !cancelled() }} diff --git a/go.mod b/go.mod index fbc5df91922..480fd3bfd81 100644 --- a/go.mod +++ b/go.mod @@ -103,7 +103,7 @@ require ( golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.21.0 golang.org/x/term v0.44.0 - golang.org/x/text v0.38.0 + golang.org/x/text v0.39.0 google.golang.org/genproto/googleapis/api v0.0.0-20260526163538-3dc84a4a5aaa google.golang.org/grpc v1.82.0 google.golang.org/protobuf v1.36.11 @@ -341,10 +341,10 @@ require ( go.uber.org/zap v1.27.0 // indirect go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/mod v0.36.0 // indirect + golang.org/x/mod v0.37.0 // indirect golang.org/x/sys v0.46.0 // indirect golang.org/x/time v0.15.0 // indirect - golang.org/x/tools v0.45.0 // indirect + golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260526163538-3dc84a4a5aaa // indirect gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect diff --git a/go.sum b/go.sum index 8d4629a93ec..2c2be9b768e 100644 --- a/go.sum +++ b/go.sum @@ -1054,8 +1054,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= -golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1210,8 +1210,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= -golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1264,8 +1264,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210112230658-8b4aab62c064/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= -golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= +golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q= +golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/scripts/govulncheck-wrapper.sh b/scripts/govulncheck-wrapper.sh index 7225efe70ec..aa15aeb25ed 100755 --- a/scripts/govulncheck-wrapper.sh +++ b/scripts/govulncheck-wrapper.sh @@ -21,103 +21,78 @@ echo "Running govulncheck..." "$GOVULNCHECK" -format json ./... > "$TMPFILE" 2>"$STDERRFILE" || true cat "$STDERRFILE" >&2 -python3 - "$TMPFILE" <<'PYEOF' -import json -import sys - -def parse_json_stream(path): - with open(path) as f: - content = f.read() - decoder = json.JSONDecoder() - idx = 0 - objects = [] - while idx < len(content): - while idx < len(content) and content[idx] in ' \t\n\r': - idx += 1 - if idx >= len(content): - break - obj, end_idx = decoder.raw_decode(content, idx) - idx = end_idx - objects.append(obj) - return objects - -objects = parse_json_stream(sys.argv[1]) - -# Collect OSV details -osvs = {} -for obj in objects: - if 'osv' in obj: - osv = obj['osv'] - osvs[osv['id']] = osv - -# Collect findings -findings = [obj['finding'] for obj in objects if 'finding' in obj] - -# Group by vuln ID -from collections import defaultdict -by_vuln = defaultdict(list) -for f in findings: - by_vuln[f['osv']].append(f) - -fail_vulns = [] -warn_vulns = [] - -for vid, entries in sorted(by_vuln.items()): - # Check if any trace reaches symbol level (has 'function' in trace frames) - is_called = any( - any('function' in frame for frame in entry.get('trace', [])) - for entry in entries +# govulncheck -format json outputs one JSON object per line (NDJSON). +# Classify each finding and display results. +jq -rn ' + [inputs] | + + # OSV summary lookup: { id: summary } + (map(select(has("osv"))) + | map({ (.osv.id): (.osv.summary // .osv.id) }) + | add // {}) as $osvs | + + # Classify every vuln group + (map(select(has("finding"))) | map(.finding) + | group_by(.osv) + | map( + . as $e | + ($e[0].osv) as $vid | + ($e | any(.[].trace[]?; has("function"))) as $called | + ($e[0].fixed_version // "") as $fixed | + ($e[0].trace[0]?.module // "") as $mod | + { + id: $vid, + summary: ($osvs[$vid] // $vid), + module: $mod, + fixed_version: $fixed, + category: ( + if ($called | not) then "IMPORTED" + elif ($fixed == "") then "NO_FIX" + elif ($mod == "stdlib") then "STDLIB" + else "FIXABLE" + end + ) + } + ) + ) as $vulns | + + ($vulns | map(select(.category != "FIXABLE"))) as $warns | + ($vulns | map(select(.category == "FIXABLE"))) as $fails | + + # Warnings + if ($warns | length) > 0 then "\n⚠ Vulnerabilities acknowledged (not blocking):" else empty end, + ($warns[] | + " \(.id): \(.summary)", + (if .category == "NO_FIX" then " module=\(.module) (no upstream fix available)" + elif .category == "STDLIB" then " module=\(.module) (needs Go toolchain upgrade to \(.fixed_version))" + else " module=\(.module) (code does not call vulnerable function)" + end) + ), + + # Failures or success + if ($fails | length) > 0 then + "\n✗ \($fails | length) fixable vulnerability(ies) found:", + ($fails[] | + " \(.id): \(.summary)", + " module=\(.module), fix: bump to \(.fixed_version)" ) - fixed_version = entries[0].get('fixed_version', '') - trace = entries[0].get('trace', []) - module = trace[0].get('module', '') if trace else '' - - # Determine category - if not is_called: - category = "IMPORTED" - elif not fixed_version: - category = "NO_FIX" - elif module == "stdlib": - category = "STDLIB" - else: - category = "FIXABLE" - - osv = osvs.get(vid, {}) - summary = osv.get('summary', vid) - - info = { - 'id': vid, - 'category': category, - 'module': module, - 'fixed_version': fixed_version, - 'summary': summary, - } - - if category == "FIXABLE": - fail_vulns.append(info) - else: - warn_vulns.append(info) - -# Print warnings -if warn_vulns: - print("\n⚠ Vulnerabilities acknowledged (not blocking):") - for v in warn_vulns: - reason = { - 'NO_FIX': 'no upstream fix available', - 'STDLIB': f'needs Go toolchain upgrade to {v["fixed_version"]}', - 'IMPORTED': 'code does not call vulnerable function', - }.get(v['category'], v['category']) - print(f" {v['id']}: {v['summary']}") - print(f" module={v['module']} ({reason})") - -# Print failures -if fail_vulns: - print(f"\n✗ {len(fail_vulns)} fixable vulnerability(ies) found:") - for v in fail_vulns: - print(f" {v['id']}: {v['summary']}") - print(f" module={v['module']}, fix: bump to {v['fixed_version']}") - sys.exit(1) -else: - print(f"\n✓ No fixable vulnerabilities found ({len(warn_vulns)} acknowledged warnings)") - sys.exit(0) -PYEOF + else + "\n✓ No fixable vulnerabilities found (\($warns | length) acknowledged warnings)" + end +' "$TMPFILE" + +# Exit 1 if any fixable vulnerabilities +FAIL_COUNT=$(jq -n ' + [inputs] + | map(select(has("finding"))) | map(.finding) + | group_by(.osv) + | map( + . as $e | + ($e | any(.[].trace[]?; has("function"))) as $called | + ($e[0].fixed_version // "") as $fixed | + ($e[0].trace[0]?.module // "") as $mod | + select($called and ($fixed != "") and ($mod != "stdlib")) + ) + | length +' "$TMPFILE") +[ "$FAIL_COUNT" -eq 0 ] diff --git a/tests/acceptance/bootstrap/Provisioning.php b/tests/acceptance/bootstrap/Provisioning.php index b2b94b34923..e7c29785100 100644 --- a/tests/acceptance/bootstrap/Provisioning.php +++ b/tests/acceptance/bootstrap/Provisioning.php @@ -645,7 +645,18 @@ public function userHasLoggedInViaWebUI(string $user): void { $user["actualUsername"], $user["password"], ); - $stateData = \json_decode($state['origins'][0]['localStorage'][2]['value']); + $localStorageItems = $state['origins'][0]['localStorage']; + $tokenItem = current( + array_filter( + $localStorageItems, + fn ($item) => str_starts_with($item['name'], 'oc_oAuth.user:'), + ), + ); + if ($tokenItem === false) { + $itemNames = implode(', ', array_column($localStorageItems, 'name')); + throw new \Exception("OIDC token not found in localStorage; items: " . $itemNames); + } + $stateData = \json_decode($tokenItem['value']); $this->setOcisUserToken($user, $stateData); } diff --git a/tests/acceptance/run-cs3api.py b/tests/acceptance/run-cs3api.py deleted file mode 100644 index f637e82520f..00000000000 --- a/tests/acceptance/run-cs3api.py +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env python3 -""" -Run CS3 API validator tests locally and in GitHub Actions CI. - -Usage: python3 tests/acceptance/run-cs3api.py -""" - -import os -import shutil -import signal -import subprocess -import sys -import time -from pathlib import Path - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -# HTTPS — matching drone: ocis init generates a self-signed cert; proxy uses TLS by default. -# Host-side curl calls use -k (insecure) to skip cert verification. -OCIS_URL = "https://127.0.0.1:9200" -CS3API_IMAGE = "owncloud/cs3api-validator:0.2.1" - - -def get_docker_bridge_ip() -> str: - """Return the Docker bridge gateway IP, reachable from host and Docker containers.""" - r = subprocess.run( - ["docker", "network", "inspect", "bridge", - "--format", "{{range .IPAM.Config}}{{.Gateway}}{{end}}"], - capture_output=True, text=True, check=True, - ) - return r.stdout.strip() - - -def base_server_env(repo_root: Path, ocis_config_dir: str, ocis_public_url: str) -> dict: - """OCIS server environment matching drone ocisServer(deploy_type='cs3api_validator').""" - return { - "OCIS_URL": ocis_public_url, - "OCIS_CONFIG_DIR": ocis_config_dir, - "STORAGE_USERS_DRIVER": "ocis", - "PROXY_ENABLE_BASIC_AUTH": "true", - # No PROXY_TLS override — drone lets ocis use its default TLS (self-signed cert from init) - # IDP excluded: its static assets are absent when running as a host process - "OCIS_EXCLUDE_RUN_SERVICES": "idp", - "OCIS_LOG_LEVEL": "error", - "IDM_CREATE_DEMO_USERS": "true", - "IDM_ADMIN_PASSWORD": "admin", - "FRONTEND_SEARCH_MIN_LENGTH": "2", - "OCIS_ASYNC_UPLOADS": "true", - "OCIS_EVENTS_ENABLE_TLS": "false", - "NATS_NATS_HOST": "0.0.0.0", - "NATS_NATS_PORT": "9233", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "EVENTHISTORY_STORE": "memory", - "WEB_UI_CONFIG_FILE": str(repo_root / "tests/config/ci/ocis-config.json"), - # cs3api_validator extras (drone ocisServer deploy_type="cs3api_validator") - "GATEWAY_GRPC_ADDR": "0.0.0.0:9142", - "OCIS_SHARING_PUBLIC_SHARE_MUST_HAVE_PASSWORD": "false", - } - - -def wait_for(condition_fn, timeout: int, label: str) -> None: - deadline = time.time() + timeout - while not condition_fn(): - if time.time() > deadline: - print(f"Timeout waiting for {label}", file=sys.stderr) - sys.exit(1) - time.sleep(1) - - -def ocis_healthy(ocis_url: str) -> bool: - r = subprocess.run( - ["curl", "-sk", "-uadmin:admin", - f"{ocis_url}/graph/v1.0/users/admin", - "-w", "%{http_code}", "-o", "/dev/null"], - capture_output=True, text=True, - ) - return r.stdout.strip() == "200" - - -def main() -> int: - repo_root = Path(__file__).resolve().parents[2] - ocis_bin = repo_root / "ocis/bin/ocis" - ocis_config_dir = Path.home() / ".ocis/config" - - if not ocis_bin.exists(): - subprocess.run(["make", "-C", str(repo_root / "ocis"), "build"], check=True) - - # Docker bridge gateway IP: reachable from both the host and Docker containers. - # cs3api-validator connects to the GRPC gateway at {bridge_ip}:9142. - bridge_ip = get_docker_bridge_ip() - print(f"Docker bridge IP: {bridge_ip}", flush=True) - - server_env = {**os.environ} - server_env.update(base_server_env(repo_root, str(ocis_config_dir), - f"https://{bridge_ip}:9200")) - - subprocess.run( - [str(ocis_bin), "init", "--insecure", "true"], - env=server_env, - check=True, - ) - shutil.copy( - repo_root / "tests/config/ci/app-registry.yaml", - ocis_config_dir / "app-registry.yaml", - ) - - print("Starting ocis...", flush=True) - ocis_proc = subprocess.Popen( - [str(ocis_bin), "server"], - env=server_env, - ) - - def cleanup(*_): - try: - ocis_proc.terminate() - except Exception: - pass - - signal.signal(signal.SIGTERM, cleanup) - signal.signal(signal.SIGINT, cleanup) - - try: - wait_for(lambda: ocis_healthy(OCIS_URL), 300, "ocis") - print("ocis ready.", flush=True) - - print(f"\nRunning cs3api-validator against {bridge_ip}:9142", flush=True) - result = subprocess.run( - ["docker", "run", "--rm", - "--entrypoint", "/usr/bin/cs3api-validator", - CS3API_IMAGE, - "/var/lib/cs3api-validator", - f"--endpoint={bridge_ip}:9142"], - ) - return result.returncode - - finally: - cleanup() - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/acceptance/run-e2e.py b/tests/acceptance/run-e2e.py deleted file mode 100755 index cd8d72d941f..00000000000 --- a/tests/acceptance/run-e2e.py +++ /dev/null @@ -1,562 +0,0 @@ -#!/usr/bin/env python3 -""" -Run Playwright e2e tests against a local OCIS instance. - -Usage: E2E_ARGS='--run-part 1' python3 tests/acceptance/run-e2e.py -Optional: TIKA_NEEDED=true, KEYCLOAK_NEEDED=true -""" - -import json -import os -import sys -import shlex -import subprocess -import signal -import tempfile -import time -import shutil -from pathlib import Path - -def wait_for(condition_fn, timeout: int, label: str) -> None: - deadline = time.time() + timeout - while not condition_fn(): - if time.time() > deadline: - print(f"Timeout waiting for {label}", file=sys.stderr) - sys.exit(1) - time.sleep(1) - - -def ocis_healthy(ocis_url: str, use_basic_auth: bool = True) -> bool: - if use_basic_auth: - cmd = ["curl", "-sk", "-uadmin:admin", - f"{ocis_url}/graph/v1.0/users/admin", - "-w", "%{http_code}", "-o", "/dev/null"] - else: - # Keycloak mode: no local admin user, check unauthenticated endpoint - cmd = ["curl", "-sk", - f"{ocis_url}/.well-known/openid-configuration", - "-w", "%{http_code}", "-o", "/dev/null"] - r = subprocess.run(cmd, capture_output=True, text=True) - return r.stdout.strip() == "200" - - -def search_ready(ocis_url: str) -> bool: - # WebDAV REPORT returning 207 proves auth-basic is registered in the gateway, - # the search service is alive, and the NATS startup backlog has drained. - body = ( - '' - '' - 'warmup-probe1' - '' - ) - cmd = [ - "curl", "-sk", "-uadmin:admin", - "-X", "REPORT", - "-H", "Content-Type: application/xml", - "-d", body, - f"{ocis_url}/dav/spaces", - "-w", "%{http_code}", "-o", "/dev/null", - ] - r = subprocess.run(cmd, capture_output=True, text=True) - return r.stdout.strip() == "207" - - -def tika_warm(tika_url: str) -> bool: - # Force a JVM extraction before tests start so cold-start latency doesn't - # delay the async upload→index pipeline past the test's poll window. - cmd = [ - "curl", "-sf", - "-X", "PUT", - "-H", "Content-Type: text/plain", - "-d", "warmup", - f"{tika_url}/tika", - "-w", "%{http_code}", "-o", "/dev/null", - ] - r = subprocess.run(cmd, capture_output=True, text=True) - return r.stdout.strip() == "200" - - -def app_providers_ready(ocis_url: str) -> bool: - # Collabora and OnlyOffice register asynchronously with the app registry - # over their WOPI bridge connection; don't start tests until both are up. - cmd = ["curl", "-sk", "-uadmin:admin", f"{ocis_url}/app/list"] - r = subprocess.run(cmd, capture_output=True, text=True) - if r.returncode != 0 or not r.stdout.strip(): - return False - try: - data = json.loads(r.stdout) - except json.JSONDecodeError: - return False - names = set() - for mime in data.get("mime-types", []): - for provider in mime.get("app_providers", []): - names.add(provider.get("name")) - return "Collabora" in names and "OnlyOffice" in names - - -def load_env_file(path: Path) -> dict: - """Parse a bash-style env file (export KEY=value) into a dict.""" - env = {} - for line in path.read_text().splitlines(): - line = line.strip() - if not line or line.startswith("#") or line.startswith("!"): - continue - line = line.removeprefix("export ").strip() - if "=" in line: - k, v = line.split("=", 1) - env[k.strip()] = v.strip() - return env - - -def run(cmd: list, env: dict = None, check: bool = True, cwd=None): - e = {**os.environ, **(env or {})} - return subprocess.run(cmd, env=e, check=check, cwd=cwd) - - -def main() -> int: - e2e_args = os.environ.get("E2E_ARGS", "").strip() - if not e2e_args: - print("E2E_ARGS is required, e.g. E2E_ARGS='--run-part 1' python3 run-e2e.py", - file=sys.stderr) - return 1 - - tika_needed = os.environ.get("TIKA_NEEDED", "").lower() == "true" - keycloak_needed = os.environ.get("KEYCLOAK_NEEDED", "").lower() == "true" - mfa_needed = os.environ.get("MFA_NEEDED", "").lower() == "true" - oidc_needed = os.environ.get("OIDC_NEEDED", "").lower() == "true" - oidc_iframe_needed = os.environ.get("OIDC_IFRAME_NEEDED", "").lower() == "true" - collaboration_needed = os.environ.get("COLLABORATION_NEEDED", "").lower() == "true" - federated_needed = os.environ.get("FEDERATED_NEEDED", "").lower() == "true" - # MFA mode runs ocis behind keycloak with TOTP enforced - keycloak_needed = keycloak_needed or mfa_needed - - repo_root = Path(__file__).resolve().parents[2] - ocis_bin = repo_root / "ocis/bin/ocis" - wrapper_bin = repo_root / "tests/ociswrapper/bin/ociswrapper" - # The federated suite must address the primary as localhost instead of - # 127.0.0.1: the ocm invite flow bakes this host into invite codes - # (token@host), and the federated instance authorizes it against - # tests/config/local/providers.json (shared with run-github.py's apiOcm - # suite), which only lists localhost:9200. - ocis_url = "https://localhost:9200" if federated_needed else "https://127.0.0.1:9200" - ocis_config_dir = Path.home() / ".ocis/config" - web_dir = repo_root / "web" - - # build ocis + ociswrapper only if not already provided (e.g. via artifact) - if not ocis_bin.exists(): - run(["make", "-C", str(repo_root / "ocis"), "build"]) - if not wrapper_bin.exists(): - run(["make", "-C", str(repo_root / "tests/ociswrapper"), "build"], - env={"GOWORK": "off"}) - - if not (web_dir / "node_modules").exists(): - pkg_manager = json.loads((web_dir / "package.json").read_text()).get("packageManager", "pnpm") - run(["npm", "install", "--silent", "--global", "--force", pkg_manager]) - subprocess.run(["pnpm", "config", "set", "store-dir", "./.pnpm-store"], - cwd=web_dir, check=True) - subprocess.run(["pnpm", "install"], cwd=web_dir, check=True) - subprocess.run(["pnpm", "exec", "playwright", "install", "--with-deps", "chromium"], - cwd=web_dir, check=True) - - # init ocis - run([str(ocis_bin), "init", "--insecure", "true"]) - # tests/config/ci/app-registry.yaml maps its one mimetype to "FakeOffice", the - # stub used by run-github.py's (Behat) apiCollaboration suite. The app-provider - # e2e suite drives real Collabora/OnlyOffice containers instead, so it needs its - # own mapping to those app names, for both the odt and docx mimetypes it exercises. - app_registry_src = ( - "tests/config/local/app-registry-office-suites.yaml" - if collaboration_needed - else "tests/config/ci/app-registry.yaml" - ) - shutil.copy( - repo_root / app_registry_src, - ocis_config_dir / "app-registry.yaml", - ) - - # Trust the self-signed cert system-wide so Chromium and node-fetch - # don't produce TLS handshake errors that load the server - ocis_cert = Path.home() / ".ocis/proxy/server.crt" - if ocis_cert.exists(): - subprocess.run( - ["sudo", "cp", str(ocis_cert), "/usr/local/share/ca-certificates/ocis.crt"], - check=True, - ) - subprocess.run(["sudo", "update-ca-certificates"], check=True) - - # Patch web UI config: replace Drone Docker service name with our URL - drone_web_cfg = json.loads( - (repo_root / "tests/config/ci/ocis-config.json").read_text() - ) - - def _patch_urls(obj, old, new): - if isinstance(obj, dict): - return {k: _patch_urls(v, old, new) for k, v in obj.items()} - if isinstance(obj, list): - return [_patch_urls(v, old, new) for v in obj] - if isinstance(obj, str): - return obj.replace(old, new) - return obj - - gha_web_cfg = _patch_urls(drone_web_cfg, "https://ocis-server:9200", ocis_url) - - # The web UI only loads the apps listed in its config. ocis-config.json predates - # the playwright suites, so add what specific suites depend on: - # - "external" is the blueprint app for the office editors: web-runtime builds - # the per-provider Collabora/OnlyOffice apps (including their "New file" menu - # entries) only when web-app-external itself is loaded. - # - "ocm" provides the "open-cloud-mesh" federation UI the ocm suite drives. - # (The federated instance's own config, tests/config/local/fed-ocis-web.json, - # already lists it.) - if collaboration_needed: - gha_web_cfg["apps"].append("external") - if federated_needed: - gha_web_cfg["apps"].append("ocm") - - # The web client reads its OIDC scope from openIdConnect.scope in this UI - # config, NOT from the WEB_OIDC_SCOPE server env var, so override it here. - # - oidc (refresh-token) spec asserts the scope contains `offline_access`. - # - mfa flow needs `acr` so Keycloak emits the LoA claim used for step-up. - if oidc_needed: - gha_web_cfg["openIdConnect"]["scope"] = "openid profile email offline_access" - if mfa_needed: - gha_web_cfg["openIdConnect"]["scope"] = "openid profile email acr" - - gha_web_cfg_path = ocis_config_dir / "web-ui-config.json" - gha_web_cfg_path.write_text(json.dumps(gha_web_cfg, indent=2)) - - server_env = { - **os.environ, - # core — matches drone ocisServer() - "OCIS_URL": ocis_url, - "OCIS_CONFIG_DIR": str(ocis_config_dir), - "STORAGE_USERS_DRIVER": "ocis", - "PROXY_ENABLE_BASIC_AUTH": "true", - - "OCIS_LOG_LEVEL": "error", - "IDM_CREATE_DEMO_USERS": "true", - "IDM_ADMIN_PASSWORD": "admin", - "FRONTEND_SEARCH_MIN_LENGTH": "2", - "OCIS_ASYNC_UPLOADS": "true", - "OCIS_EVENTS_ENABLE_TLS": "false", - "NATS_NATS_HOST": "0.0.0.0", - "NATS_NATS_PORT": "9233", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "EVENTHISTORY_STORE": "memory", - "OCIS_TRANSLATION_PATH": str(repo_root / "tests/config/translations"), - "WEB_UI_CONFIG_FILE": str(gha_web_cfg_path), - "THUMBNAILS_TXT_FONTMAP_FILE": str(repo_root / "tests/config/ci/fontsMap.json"), - # extra_server_environment — matches drone e2eTestPipeline() - "OCIS_PASSWORD_POLICY_BANNED_PASSWORDS_LIST": str(repo_root / "tests/config/ci/banned-password-list.txt"), - # Web's e2e tests use simple passwords (e.g. %public%, 123); relax the policy - # to match web's own CI (tests/actions/.env.ocis) so the client-side check - # doesn't block them. - "OCIS_PASSWORD_POLICY_MIN_CHARACTERS": "3", - "OCIS_PASSWORD_POLICY_MIN_LOWERCASE_CHARACTERS": "0", - "OCIS_PASSWORD_POLICY_MIN_UPPERCASE_CHARACTERS": "0", - "OCIS_PASSWORD_POLICY_MIN_DIGITS": "0", - "OCIS_PASSWORD_POLICY_MIN_SPECIAL_CHARACTERS": "0", - "GRAPH_AVAILABLE_ROLES": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5,a8d5fe5e-96e3-418d-825b-534dbdf22b99,fb6c3e19-e378-47e5-b277-9732f9de6e21,58c63c02-1d89-4572-916a-870abc5a1b7d,2d00ce52-1fc2-4dbc-8b95-a73b73395f5a,1c996275-f1c9-4e71-abdf-a42f6495e960,312c0871-5ef7-4b3a-85b6-0e4074c64049,aa97fe03-7980-45ac-9e50-b325749fd7e6,63e64e19-8d43-42ec-a738-2b6af2610efa", - "FRONTEND_CONFIGURABLE_NOTIFICATIONS": "true", - # debug addresses - "ACTIVITYLOG_DEBUG_ADDR": "0.0.0.0:9197", - "APP_PROVIDER_DEBUG_ADDR": "0.0.0.0:9165", - "APP_REGISTRY_DEBUG_ADDR": "0.0.0.0:9243", - "AUTH_BASIC_DEBUG_ADDR": "0.0.0.0:9147", - "AUTH_MACHINE_DEBUG_ADDR": "0.0.0.0:9167", - "AUTH_SERVICE_DEBUG_ADDR": "0.0.0.0:9198", - "CLIENTLOG_DEBUG_ADDR": "0.0.0.0:9260", - "EVENTHISTORY_DEBUG_ADDR": "0.0.0.0:9270", - "FRONTEND_DEBUG_ADDR": "0.0.0.0:9141", - "GATEWAY_DEBUG_ADDR": "0.0.0.0:9143", - "GRAPH_DEBUG_ADDR": "0.0.0.0:9124", - "GROUPS_DEBUG_ADDR": "0.0.0.0:9161", - "IDM_DEBUG_ADDR": "0.0.0.0:9239", - "IDP_DEBUG_ADDR": "0.0.0.0:9134", - "INVITATIONS_DEBUG_ADDR": "0.0.0.0:9269", - "NATS_DEBUG_ADDR": "0.0.0.0:9234", - "OCDAV_DEBUG_ADDR": "0.0.0.0:9163", - "OCM_DEBUG_ADDR": "0.0.0.0:9281", - "OCS_DEBUG_ADDR": "0.0.0.0:9114", - "POSTPROCESSING_DEBUG_ADDR": "0.0.0.0:9255", - "PROXY_DEBUG_ADDR": "0.0.0.0:9205", - "SEARCH_DEBUG_ADDR": "0.0.0.0:9224", - "SETTINGS_DEBUG_ADDR": "0.0.0.0:9194", - "SHARING_DEBUG_ADDR": "0.0.0.0:9151", - "SSE_DEBUG_ADDR": "0.0.0.0:9139", - "STORAGE_PUBLICLINK_DEBUG_ADDR": "0.0.0.0:9179", - "STORAGE_SHARES_DEBUG_ADDR": "0.0.0.0:9156", - "STORAGE_SYSTEM_DEBUG_ADDR": "0.0.0.0:9217", - "STORAGE_USERS_DEBUG_ADDR": "0.0.0.0:9159", - "THUMBNAILS_DEBUG_ADDR": "0.0.0.0:9189", - "USERLOG_DEBUG_ADDR": "0.0.0.0:9214", - "USERS_DEBUG_ADDR": "0.0.0.0:9145", - "WEB_DEBUG_ADDR": "0.0.0.0:9104", - "WEBDAV_DEBUG_ADDR": "0.0.0.0:9119", - "WEBFINGER_DEBUG_ADDR": "0.0.0.0:9279", - } - - if tika_needed: - server_env.update({ - "FRONTEND_FULL_TEXT_SEARCH_ENABLED": "true", - "SEARCH_EXTRACTOR_TYPE": "tika", - "SEARCH_EXTRACTOR_TIKA_TIKA_URL": "http://localhost:9998", - "SEARCH_EXTRACTOR_CS3SOURCE_INSECURE": "true", - }) - - if keycloak_needed: - server_env.update({ - "OCIS_EXCLUDE_RUN_SERVICES": "idp", - "PROXY_AUTOPROVISION_ACCOUNTS": "true", - # Group sync is opt-in and does not create groups by default; the group - # sync e2e test relies on both being enabled so Keycloak groups appear. - "PROXY_AUTOPROVISION_CLAIM_GROUPS": "groups", - "PROXY_AUTOPROVISION_GROUP_CREATE": "true", - "PROXY_ROLE_ASSIGNMENT_DRIVER": "oidc", - "OCIS_OIDC_ISSUER": "https://localhost:8443/realms/oCIS", - "PROXY_OIDC_REWRITE_WELLKNOWN": "true", - "WEB_OIDC_CLIENT_ID": "web", - "PROXY_USER_OIDC_CLAIM": "preferred_username", - "PROXY_USER_CS3_CLAIM": "username", - "OCIS_ADMIN_USER_ID": "", - "GRAPH_ASSIGN_DEFAULT_USER_ROLE": "false", - "GRAPH_USERNAME_MATCH": "none", - "PROXY_CSP_CONFIG_FILE_LOCATION": str(repo_root / "tests/config/ci/csp.yaml"), - "KEYCLOAK_DOMAIN": "localhost:8443", - "IDM_CREATE_DEMO_USERS": "false", - }) - - if mfa_needed: - server_env.update({ - "OCIS_MFA_ENABLED": "true", - "WEB_OIDC_SCOPE": "openid profile email acr", - }) - - # The two oidc tests need a short access-token lifetime so token renewal - # actually triggers within the test window. refreshToken.spec.ts uses a - # refresh token (offline_access scope); iframeTokenRenewal.spec.ts uses - # silent iframe renewal (no offline_access). - if oidc_needed: - server_env.update({ - "IDP_ACCESS_TOKEN_EXPIRATION": "30", - "WEB_OIDC_SCOPE": "openid profile email offline_access", - }) - if oidc_iframe_needed: - server_env["IDP_ACCESS_TOKEN_EXPIRATION"] = "30" - - if collaboration_needed: - server_env.update({ - "MICRO_REGISTRY": "nats-js-kv", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:9233", - "FRONTEND_APP_HANDLER_SECURE_VIEW_APP_ADDR": "com.owncloud.api.collaboration.Collabora", - # These two domains, the page origin, and Collabora's frame_ancestors - # (workflow) must all use the same host literal: CSP source - # expressions match by exact host. - "COLLABORA_DOMAIN": "127.0.0.1:9980", - "ONLYOFFICE_DOMAIN": "127.0.0.1:443", - # The default proxy CSP only allows frame-src 'self', so the browser - # refuses to embed the office editor iframes (different port). The CSP - # file whitelists ${COLLABORA_DOMAIN}/${ONLYOFFICE_DOMAIN} in frame-src - # -- without it the editors never load and every office test times out. - "PROXY_CSP_CONFIG_FILE_LOCATION": str(repo_root / "tests/config/ci/csp.yaml"), - }) - - federated_url = "https://localhost:10200" - federated_env = None - if federated_needed: - # run-github.py's (Behat apiOcm) federation setup already solved this exact - # problem, so reuse its canonical port-mapping file instead of re-deriving one: - # only offsetting *_DEBUG_ADDR left the actual service ports (e.g. gateway's - # GATEWAY_GRPC_ADDR 127.0.0.1:9142) on their defaults, so the federated - # "ocis server" process crashed trying to bind the same ports the primary - # instance already held. - providers_file = str(repo_root / "tests/config/local/providers.json") - server_env.update({ - "OCIS_ADD_RUN_SERVICES": "ocm", - "OCIS_ENABLE_OCM": "true", - "OCM_OCM_INVITE_MANAGER_INSECURE": "true", - "OCM_OCM_SHARE_PROVIDER_INSECURE": "true", - "OCM_OCM_STORAGE_PROVIDER_INSECURE": "true", - "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE": providers_file, - }) - - federated_config_dir = Path.home() / ".ocis-federated/config" - federated_data_dir = Path.home() / ".ocis-federated" - federated_env = {**server_env} - federated_env.update(load_env_file(repo_root / "tests/config/local/.env-federation")) - # override the file's ${HOME}-prefixed paths (load_env_file doesn't expand - # shell variables) and anything specific to this script's own layout - federated_env.update({ - "OCIS_URL": federated_url, - "OCIS_BASE_DATA_PATH": str(federated_data_dir), - "OCIS_CONFIG_DIR": str(federated_config_dir), - "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE": providers_file, - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:10233", - }) - - procs = [] - - print("Starting ocis...") - if keycloak_needed: - # external IdP: run ocis server directly (no ociswrapper) - ocis_proc = subprocess.Popen( - [str(ocis_bin), "server"], - env=server_env, - ) - else: - ocis_proc = subprocess.Popen( - [str(wrapper_bin), "serve", - "--bin", str(ocis_bin), - "--url", ocis_url, - "--admin-username", "admin", - "--admin-password", "admin"], - env=server_env, - ) - procs.append(ocis_proc) - - def cleanup(*_): - for p in procs: - try: - p.terminate() - except Exception: - pass - - signal.signal(signal.SIGTERM, cleanup) - signal.signal(signal.SIGINT, cleanup) - - tika_url = "http://localhost:9998" - - try: - wait_for(lambda: ocis_healthy(ocis_url, use_basic_auth=not keycloak_needed), 300, "ocis") - print("ocis ready.") - - if tika_needed: - wait_for(lambda: tika_warm(tika_url), 60, "tika warm-up") - print("tika warm.") - - if not keycloak_needed: - wait_for(lambda: search_ready(ocis_url), 60, "search service") - print("search ready.") - - if collaboration_needed: - # WOPI bridges: one per office app, started only after ocis itself is up - # so they can register with its gateway. Ported from setup_wopi_collabora - # / setup_wopi_onlyoffice in tests/actions/setup-services.sh. - wopi_common = { - "MICRO_REGISTRY": "nats-js-kv", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:9233", - "COLLABORATION_APP_INSECURE": "true", - "COLLABORATION_CS3API_DATAGATEWAY_INSECURE": "true", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "COLLABORATION_WOPI_SECRET": "some-wopi-secret", - } - collabora_env = { - **os.environ, **wopi_common, - "COLLABORATION_GRPC_ADDR": "0.0.0.0:9301", - "COLLABORATION_HTTP_ADDR": "0.0.0.0:9300", - "COLLABORATION_DEBUG_ADDR": "0.0.0.0:9304", - "COLLABORATION_APP_NAME": "Collabora", - "COLLABORATION_APP_ADDR": "https://127.0.0.1:9980", - "COLLABORATION_APP_ICON": "https://127.0.0.1:9980/favicon.ico", - "COLLABORATION_WOPI_SRC": "http://127.0.0.1:9300", - } - onlyoffice_env = { - **os.environ, **wopi_common, - "COLLABORATION_GRPC_ADDR": "0.0.0.0:9303", - "COLLABORATION_HTTP_ADDR": "0.0.0.0:9302", - "COLLABORATION_DEBUG_ADDR": "0.0.0.0:9305", - "COLLABORATION_APP_NAME": "OnlyOffice", - "COLLABORATION_APP_PRODUCT": "OnlyOffice", - "COLLABORATION_APP_ADDR": "https://127.0.0.1:443", - "COLLABORATION_APP_ICON": "https://127.0.0.1/web-apps/apps/documenteditor/main/resources/img/favicon.ico", - "COLLABORATION_WOPI_SRC": "http://127.0.0.1:9302", - } - procs.append(subprocess.Popen([str(ocis_bin), "collaboration", "server"], env=collabora_env)) - procs.append(subprocess.Popen([str(ocis_bin), "collaboration", "server"], env=onlyoffice_env)) - wait_for(lambda: app_providers_ready(ocis_url), 150, "Collabora/OnlyOffice app providers") - print("app providers ready.") - - if federated_needed: - # Second, independent ocis instance for OCM federation tests. See the - # federated_env comment above main() for its port-mapping source. - # OCIS_BASE_DATA_PATH must match federated_env's, or init writes the - # proxy cert under the default ~/.ocis (colliding with the primary - # instance's) instead of federated_data_dir, and the server then - # generates its own untrusted one lazily at startup. - run([str(ocis_bin), "init", "--insecure", "true"], - env={ - "OCIS_CONFIG_DIR": str(federated_config_dir), - "OCIS_BASE_DATA_PATH": str(federated_data_dir), - }) - federated_cert = federated_data_dir / "proxy/server.crt" - if federated_cert.exists(): - subprocess.run( - ["sudo", "cp", str(federated_cert), - "/usr/local/share/ca-certificates/ocis-federated.crt"], - check=True, - ) - subprocess.run(["sudo", "update-ca-certificates"], check=True) - federated_proc = subprocess.Popen( - [str(ocis_bin), "server"], - env=federated_env, - ) - procs.append(federated_proc) - wait_for(lambda: ocis_healthy(federated_url, use_basic_auth=True), 300, "ocis-federated") - print("ocis-federated ready.") - - playwright_env = { - **os.environ, - "BASE_URL_OCIS": ocis_url, - "HEADLESS": "true", - "RETRY": "3", - "SKIP_A11Y_TESTS": "true", - # web 12.4: enabling this both sets `use.trace=on` in playwright.config.ts - # AND triggers actor.ts to call tracing.start(), which double-starts and fails. - "REPORT_TRACING": "false", - "NODE_EXTRA_CA_CERTS": str(ocis_cert), - "BROWSER": "chromium", - } - - trusted_certs = [ocis_cert] - - if keycloak_needed: - playwright_env.update({ - "KEYCLOAK": "true", - "KEYCLOAK_HOST": "localhost:8443", - }) - keycloak_cert = repo_root / "keycloak-certs/keycloakcrt.pem" - if keycloak_cert.exists(): - trusted_certs.append(keycloak_cert) - - if mfa_needed: - playwright_env["MFA"] = "true" - - if federated_needed: - playwright_env["FEDERATED_BASE_URL_OCIS"] = "localhost:10200" - trusted_certs.append(federated_cert) - - if len(trusted_certs) > 1: - # NODE_EXTRA_CA_CERTS takes a single file; concatenate every self-signed - # cert in play (oCIS, Keycloak, federated instance) so node-fetch/axios - # calls made directly against any of them are trusted too. - bundle = tempfile.NamedTemporaryFile( - mode="w", suffix=".pem", prefix="ocis-ca-bundle-", delete=False) - for cert in trusted_certs: - bundle.write(cert.read_text()) - bundle.close() - playwright_env["NODE_EXTRA_CA_CERTS"] = bundle.name - - print(f"Running e2e: {e2e_args}") - result = subprocess.run( - ["bash", "run-e2e.sh"] + shlex.split(e2e_args), - cwd=web_dir / "tests/e2e", - env=playwright_env, - ) - return result.returncode - - finally: - cleanup() - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/acceptance/run-github.py b/tests/acceptance/run-github.py deleted file mode 100755 index f4eaa875042..00000000000 --- a/tests/acceptance/run-github.py +++ /dev/null @@ -1,899 +0,0 @@ -#!/usr/bin/env python3 -""" -Run ocis acceptance tests locally and in GitHub Actions CI. - -Usage: BEHAT_SUITES=apiGraph python3 tests/acceptance/run-github.py -""" - -import json -import os -import sys -import subprocess -import signal -import time -import tempfile -import shutil -from pathlib import Path - -# --------------------------------------------------------------------------- - -# NOTE: EMAIL_SMTP_HOST is "email" (container name) in drone, "localhost" here -# --------------------------------------------------------------------------- - -EMAIL_SMTP_HOST = "localhost" -EMAIL_SMTP_PORT = "1025" -EMAIL_PORT = "8025" -EMAIL_SMTP_SENDER = "ownCloud " - -KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:26.5.6" -POSTGRES_ALPINE_IMAGE = "postgres:alpine3.18" - -LOCAL_API_TESTS = { - "contractAndLock": { - "suites": ["apiContract", "apiLocks"], - }, - "settingsAndNotification": { - "suites": ["apiSettings", "apiNotification", "apiCors"], - "emailNeeded": True, - "extraEnvironment": { - "EMAIL_HOST": EMAIL_SMTP_HOST, - "EMAIL_PORT": EMAIL_PORT, - }, - "extraServerEnvironment": { - "OCIS_ADD_RUN_SERVICES": "notifications", - "NOTIFICATIONS_SMTP_HOST": EMAIL_SMTP_HOST, - "NOTIFICATIONS_SMTP_PORT": EMAIL_SMTP_PORT, - "NOTIFICATIONS_SMTP_INSECURE": "true", - "NOTIFICATIONS_SMTP_SENDER": EMAIL_SMTP_SENDER, - "NOTIFICATIONS_DEBUG_ADDR": "0.0.0.0:9174", - }, - }, - "graphUser": { - "suites": ["apiGraphUser"], - }, - "spaces": { - "suites": ["apiSpaces"], - }, - "spacesShares": { - "suites": ["apiSpacesShares"], - }, - "davOperations": { - "suites": [ - "apiSpacesDavOperation", "apiDownloads", "apiAsyncUpload", - "apiDepthInfinity", "apiArchiver", "apiActivities", - ], - }, - "groupAndSearch1": { - "suites": ["apiSearch1", "apiGraph", "apiGraphGroup"], - }, - "search2": { - "suites": ["apiSearch2", "apiSearchContent"], - "tikaNeeded": True, - "extraServerEnvironment": { - "FRONTEND_FULL_TEXT_SEARCH_ENABLED": "true", - "SEARCH_EXTRACTOR_TYPE": "tika", - "SEARCH_EXTRACTOR_TIKA_TIKA_URL": "http://localhost:9998", - "SEARCH_EXTRACTOR_CS3SOURCE_INSECURE": "true", - }, - }, - "sharingNg1": { - "suites": ["apiSharingNgShares", "apiReshare", "apiSharingNgPermissions"], - }, - "sharingNgAdditionalShareRole": { - "suites": ["apiSharingNgAdditionalShareRole"], - }, - "sharingNgShareInvitation": { - "suites": ["apiSharingNgDriveInvitation", "apiSharingNgItemInvitation"], - }, - "sharingNgLinkShare": { - "suites": [ - "apiSharingNgDriveLinkShare", "apiSharingNgItemLinkShare", - "apiSharingNgLinkShareManagement", - ], - }, - "antivirus": { - "suites": ["apiAntivirus"], - "antivirusNeeded": True, - "extraServerEnvironment": { - "ANTIVIRUS_SCANNER_TYPE": "clamav", - "ANTIVIRUS_CLAMAV_SOCKET": "tcp://clamav:3310", - "POSTPROCESSING_STEPS": "virusscan", - "OCIS_ADD_RUN_SERVICES": "antivirus", - "ANTIVIRUS_DEBUG_ADDR": "0.0.0.0:9277", - }, - }, - "ocm": { - "suites": ["apiOcm"], - "emailNeeded": True, - "federationServer": True, - "extraEnvironment": { - "EMAIL_HOST": EMAIL_SMTP_HOST, - "EMAIL_PORT": EMAIL_PORT, - }, - "extraServerEnvironment": { - "OCIS_ADD_RUN_SERVICES": "ocm,notifications", - "OCIS_ENABLE_OCM": "true", - "OCM_OCM_INVITE_MANAGER_INSECURE": "true", - "OCM_OCM_SHARE_PROVIDER_INSECURE": "true", - "OCM_OCM_STORAGE_PROVIDER_INSECURE": "true", - "OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE": "", # set at runtime - "NOTIFICATIONS_SMTP_HOST": EMAIL_SMTP_HOST, - "NOTIFICATIONS_SMTP_PORT": EMAIL_SMTP_PORT, - "NOTIFICATIONS_SMTP_INSECURE": "true", - "NOTIFICATIONS_SMTP_SENDER": EMAIL_SMTP_SENDER, - "NOTIFICATIONS_DEBUG_ADDR": "0.0.0.0:9174", - }, - }, - "authApp": { - "suites": ["apiAuthApp"], - "extraServerEnvironment": { - "OCIS_ADD_RUN_SERVICES": "auth-app", - "PROXY_ENABLE_APP_AUTH": "true", - }, - }, - "wopi": { - "suites": ["apiCollaboration"], - "collaborationServiceNeeded": True, - "extraServerEnvironment": { - "GATEWAY_GRPC_ADDR": "0.0.0.0:9142", - }, - }, - "vault": { - "suites": ["apiVault"], - "keycloakNeeded": True, - "vaultStorage": True, - "extraEnvironment": { - "KEYCLOAK": "true", - "KC_URL": "https://localhost:8443" - }, - "extraServerEnvironment": { - "OCIS_ENABLE_VAULT_MODE": "true", - "FRONTEND_ENABLE_VAULT_MODE": "true", - "OCIS_MFA_ENABLED": "true", - "SETTINGS_GRPC_ADDR": "localhost:9191", - # Keycloak as external OIDC provider — disable built-in IDP - "OCIS_EXCLUDE_RUN_SERVICES": "idp", - "OCIS_OIDC_ISSUER": "https://localhost:8443/realms/oCIS", - "PROXY_OIDC_REWRITE_WELLKNOWN": "true", - "PROXY_AUTOPROVISION_ACCOUNTS": "true", - "PROXY_ROLE_ASSIGNMENT_DRIVER": "oidc", - "PROXY_USER_OIDC_CLAIM": "preferred_username", - "PROXY_USER_CS3_CLAIM": "username", - "WEB_OIDC_CLIENT_ID": "web", - "KEYCLOAK_DOMAIN": "localhost:8443", - "OCIS_ADMIN_USER_ID": "", - "GRAPH_ASSIGN_DEFAULT_USER_ROLE": "false", - "GRAPH_USERNAME_MATCH": "none", - "IDM_CREATE_DEMO_USERS": "false", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:9233", - "WEB_OIDC_SCOPE": "openid profile email acr" - } - }, - "cliCommands": { - "suites": ["cliCommands", "apiServiceAvailability"], - "antivirusNeeded": True, - "emailNeeded": True, - "extraEnvironment": { - "EMAIL_HOST": EMAIL_SMTP_HOST, - "EMAIL_PORT": EMAIL_PORT, - }, - "extraServerEnvironment": { - "NOTIFICATIONS_SMTP_HOST": EMAIL_SMTP_HOST, - "NOTIFICATIONS_SMTP_PORT": EMAIL_SMTP_PORT, - "NOTIFICATIONS_SMTP_INSECURE": "true", - "NOTIFICATIONS_SMTP_SENDER": EMAIL_SMTP_SENDER, - "NOTIFICATIONS_DEBUG_ADDR": "0.0.0.0:9174", - "ANTIVIRUS_SCANNER_TYPE": "clamav", - "ANTIVIRUS_CLAMAV_SOCKET": "tcp://clamav:3310", - "ANTIVIRUS_DEBUG_ADDR": "0.0.0.0:9277", - "OCIS_ADD_RUN_SERVICES": "antivirus,notifications", - }, - }, -} - -# reverse lookup: suite → group config -_SUITE_TO_CONFIG: dict = {} -for _cfg in LOCAL_API_TESTS.values(): - for _s in _cfg.get("suites", []): - _SUITE_TO_CONFIG[_s] = _cfg - - -# GitHub Actions uses --network host: all wopi services share one network namespace. -# Drone gives each service its own container → all can use 9300/9301/9304. -# Assign distinct ports here to avoid collisions. -_WOPI_PORTS = { - "collabora": {"grpc": 9301, "http": 9300, "debug": 9304}, - "onlyoffice": {"grpc": 9311, "http": 9310, "debug": 9314}, - "fakeoffice": {"grpc": 9321, "http": 9320, "debug": 9324}, -} - - -def merged_config(suites: list) -> dict: - """Union config for all requested suites.""" - merged = { - "emailNeeded": False, - "antivirusNeeded": False, - "tikaNeeded": False, - "federationServer": False, - "collaborationServiceNeeded": False, - "keycloakNeeded": False, - "vaultStorage": False, - "extraServerEnvironment": {}, - "extraEnvironment": {}, - } - for suite in suites: - cfg = _SUITE_TO_CONFIG.get(suite, {}) - for flag in ("emailNeeded", "antivirusNeeded", "tikaNeeded", - "federationServer", "collaborationServiceNeeded", - "keycloakNeeded", "vaultStorage"): - if cfg.get(flag): - merged[flag] = True - merged["extraServerEnvironment"].update(cfg.get("extraServerEnvironment", {})) - merged["extraEnvironment"].update(cfg.get("extraEnvironment", {})) - return merged - - -def base_server_env(repo_root: Path, ocis_url: str, ocis_config_dir: str) -> dict: - """Base ocis server environment matching drone ocisServer() function.""" - return { - "OCIS_URL": ocis_url, - "OCIS_CONFIG_DIR": ocis_config_dir, - "STORAGE_USERS_DRIVER": "ocis", - "PROXY_ENABLE_BASIC_AUTH": "true", - "OCIS_LOG_LEVEL": "error", - "IDM_CREATE_DEMO_USERS": "true", - "IDM_ADMIN_PASSWORD": "admin", - "FRONTEND_SEARCH_MIN_LENGTH": "2", - "OCIS_ASYNC_UPLOADS": "true", - "OCIS_EVENTS_ENABLE_TLS": "false", - "NATS_NATS_HOST": "0.0.0.0", - "NATS_NATS_PORT": "9233", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:9233", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "EVENTHISTORY_STORE": "memory", - "OCIS_TRANSLATION_PATH": str(repo_root / "tests/config/translations"), - "WEB_UI_CONFIG_FILE": str(repo_root / "tests/config/ci/ocis-config.json"), - "THUMBNAILS_TXT_FONTMAP_FILE": str(repo_root / "tests/config/ci/fontsMap.json"), - # default tika off (overridden by search2 extraServerEnvironment) - "SEARCH_EXTRACTOR_TYPE": "basic", - "FRONTEND_FULL_TEXT_SEARCH_ENABLED": "false", - # debug addresses - "ACTIVITYLOG_DEBUG_ADDR": "0.0.0.0:9197", - "APP_PROVIDER_DEBUG_ADDR": "0.0.0.0:9165", - "APP_REGISTRY_DEBUG_ADDR": "0.0.0.0:9243", - "AUTH_BASIC_DEBUG_ADDR": "0.0.0.0:9147", - "AUTH_MACHINE_DEBUG_ADDR": "0.0.0.0:9167", - "AUTH_SERVICE_DEBUG_ADDR": "0.0.0.0:9198", - "CLIENTLOG_DEBUG_ADDR": "0.0.0.0:9260", - "EVENTHISTORY_DEBUG_ADDR": "0.0.0.0:9270", - "FRONTEND_DEBUG_ADDR": "0.0.0.0:9141", - "GATEWAY_DEBUG_ADDR": "0.0.0.0:9143", - "GRAPH_DEBUG_ADDR": "0.0.0.0:9124", - "GROUPS_DEBUG_ADDR": "0.0.0.0:9161", - "IDM_DEBUG_ADDR": "0.0.0.0:9239", - "IDP_DEBUG_ADDR": "0.0.0.0:9134", - "INVITATIONS_DEBUG_ADDR": "0.0.0.0:9269", - "NATS_DEBUG_ADDR": "0.0.0.0:9234", - "OCDAV_DEBUG_ADDR": "0.0.0.0:9163", - "OCM_DEBUG_ADDR": "0.0.0.0:9281", - "OCS_DEBUG_ADDR": "0.0.0.0:9114", - "POSTPROCESSING_DEBUG_ADDR": "0.0.0.0:9255", - "PROXY_DEBUG_ADDR": "0.0.0.0:9205", - "SEARCH_DEBUG_ADDR": "0.0.0.0:9224", - "SETTINGS_DEBUG_ADDR": "0.0.0.0:9194", - "SHARING_DEBUG_ADDR": "0.0.0.0:9151", - "SSE_DEBUG_ADDR": "0.0.0.0:9139", - "STORAGE_PUBLICLINK_DEBUG_ADDR": "0.0.0.0:9179", - "STORAGE_SHARES_DEBUG_ADDR": "0.0.0.0:9156", - "STORAGE_SYSTEM_DEBUG_ADDR": "0.0.0.0:9217", - "STORAGE_USERS_DEBUG_ADDR": "0.0.0.0:9159", - "THUMBNAILS_DEBUG_ADDR": "0.0.0.0:9189", - "USERLOG_DEBUG_ADDR": "0.0.0.0:9214", - "USERS_DEBUG_ADDR": "0.0.0.0:9145", - "WEB_DEBUG_ADDR": "0.0.0.0:9104", - "WEBDAV_DEBUG_ADDR": "0.0.0.0:9119", - "WEBFINGER_DEBUG_ADDR": "0.0.0.0:9279", - } - - -def wait_for(condition_fn, timeout: int, label: str, container: str = None) -> None: - start = time.time() - deadline = start + timeout - last_log = start - while not condition_fn(): - now = time.time() - if now > deadline: - elapsed = int(now - start) - print(f"Timeout waiting for {label} after {elapsed}s", file=sys.stderr) - # dump docker diagnostics — use explicit container name if provided - cname = container or label - for cmd in ( - ["docker", "ps", "-a", "--filter", f"name={cname}", "--no-trunc"], - ["docker", "logs", "--tail", "50", cname], - ): - r = subprocess.run(cmd, capture_output=True, text=True) - if r.stdout.strip(): - print(f"--- {' '.join(cmd)} ---", file=sys.stderr) - print(r.stdout, file=sys.stderr) - if r.stderr.strip(): - print(r.stderr, file=sys.stderr) - sys.exit(1) - if now - last_log >= 30: - print(f" Waiting for {label}... {int(now - start)}s") - last_log = now - time.sleep(1) - - -def ocis_healthy(ocis_url: str) -> bool: - r = subprocess.run( - ["curl", "-sk", "-uadmin:admin", - f"{ocis_url}/graph/v1.0/users/admin", - "-w", "%{http_code}", "-o", "/dev/null"], - capture_output=True, text=True, - ) - return r.stdout.strip() == "200" - - -def ocis_proxy_ready() -> bool: - """Check ocis readiness via the proxy debug port (no auth required). - - In vault mode PROXY_ROLE_ASSIGNMENT_DRIVER=oidc means basic-auth users - never receive the admin role, so /graph/v1.0/users/admin returns 403. - The proxy debug readyz endpoint (PROXY_DEBUG_ADDR 0.0.0.0:9205) is - unauthenticated and becomes available once the proxy is fully started. - """ - return subprocess.run( - ["curl", "-sf", "http://localhost:9205/readyz"], - capture_output=True, - ).returncode == 0 - - -def mailpit_healthy() -> bool: - return subprocess.run( - ["curl", "-sf", "http://localhost:8025/api/v1/messages"], - capture_output=True, - ).returncode == 0 - - -def tika_healthy() -> bool: - return subprocess.run( - ["curl", "-sf", "http://localhost:9998"], - capture_output=True, - ).returncode == 0 - - -def _tcp_ready(host: str, port: int) -> bool: - """Check if a TCP port is accepting connections.""" - import socket - try: - with socket.create_connection((host, port), timeout=2): - return True - except (ConnectionRefusedError, OSError): - return False - - -def clamav_healthy() -> bool: - return _tcp_ready("localhost", 3310) - - -def keycloak_healthy() -> bool: - return subprocess.run( - ["curl", "-skf", "https://localhost:9000/health/ready"], - capture_output=True, - ).returncode == 0 - - -def postgres_healthy() -> bool: - return subprocess.run( - ["docker", "exec", "postgres", "pg_isready", "-U", "keycloak"], - capture_output=True, - ).returncode == 0 - - - -def load_env_file(path: Path) -> dict: - """Parse a bash-style env file (export KEY=value) into a dict.""" - env = {} - for line in path.read_text().splitlines(): - line = line.strip() - if not line or line.startswith("#") or line.startswith("!"): - continue - line = line.removeprefix("export ").strip() - if "=" in line: - k, v = line.split("=", 1) - env[k.strip()] = v.strip() - return env - - -def run(cmd: list, env: dict = None, check: bool = True): - e = {**os.environ, **(env or {})} - return subprocess.run(cmd, env=e, check=check) - - -def main() -> int: - behat_suites_raw = os.environ.get("BEHAT_SUITES", "").strip() - if not behat_suites_raw: - print("BEHAT_SUITES is required", file=sys.stderr) - return 1 - - suites = [s.strip() for s in behat_suites_raw.split(",") if s.strip()] - acceptance_test_type = os.environ.get("ACCEPTANCE_TEST_TYPE", "api") - - repo_root = Path(__file__).resolve().parents[2] - ocis_bin = repo_root / "ocis/bin/ocis" - wrapper_bin = repo_root / "tests/ociswrapper/bin/ociswrapper" - ocis_url = "https://localhost:9200" - ocis_config_dir = Path.home() / ".ocis/config" - - ocis_fed_url = "https://localhost:10200" - - cfg = merged_config(suites) - print(f"Suites: {suites}") - print(f"Services: email={cfg['emailNeeded']} tika={cfg['tikaNeeded']} " - f"antivirus={cfg['antivirusNeeded']} federation={cfg['federationServer']} " - f"wopi={cfg['collaborationServiceNeeded']}") - - if not ocis_bin.exists(): - # generate IDP/web assets and build (skipped when pre-built binary is provided) - run(["make", "-C", str(repo_root / "services/idp"), "ci-node-generate"]) - run(["make", "-C", str(repo_root / "services/web"), "ci-node-generate"]) - - build_env = {} - if subprocess.run(["pkg-config", "--exists", "vips"], - capture_output=True).returncode == 0: - build_env["ENABLE_VIPS"] = "true" - run(["make", "-C", str(repo_root / "ocis"), "build"], env=build_env) - - if not wrapper_bin.exists(): - run(["make", "-C", str(repo_root / "tests/ociswrapper"), "build"], - env={"GOWORK": "off"}) - - # php deps - run(["composer", "install", "--no-progress"], - env={"COMPOSER_NO_INTERACTION": "1", "COMPOSER_NO_AUDIT": "1"}) - run(["composer", "bin", "behat", "install", "--no-progress"], - env={"COMPOSER_NO_INTERACTION": "1", "COMPOSER_NO_AUDIT": "1"}) - - # optional services - procs = [] - - if cfg["emailNeeded"]: - print("Starting mailpit...") - run(["docker", "run", "-d", "--name", "mailpit", "--network", "host", - "axllent/mailpit:v1.22.3"]) - wait_for(mailpit_healthy, 60, "mailpit") - print("mailpit ready.") - - if cfg["antivirusNeeded"]: - print("Starting clamav...") - run(["docker", "run", "-d", "--name", "clamav", "--network", "host", - "owncloudci/clamavd"]) - wait_for(clamav_healthy, 300, "clamav") - print("clamav ready.") - # override socket: drone uses container DNS "clamav", we use localhost - cfg["extraServerEnvironment"]["ANTIVIRUS_CLAMAV_SOCKET"] = "tcp://localhost:3310" - - if cfg["tikaNeeded"]: - print("Starting tika...") - run(["docker", "run", "-d", "--name", "tika", "--network", "host", - "apache/tika:3.2.2.0-full"]) - wait_for(tika_healthy, 120, "tika") - print("tika ready.") - - # OCM federation: rewrite providers.json with localhost URLs - if cfg["federationServer"]: - providers_src = repo_root / "tests/config/ci/providers.json" - providers = json.loads(providers_src.read_text()) - for p in providers: - # replace container DNS names with localhost - p["domain"] = p["domain"].replace("ocis-server:9200", "localhost:9200") - p["domain"] = p["domain"].replace("federation-ocis-server:10200", "localhost:10200") - for svc in p.get("services", []): - ep = svc.get("endpoint", {}) - ep["path"] = ep.get("path", "").replace("ocis-server:9200", "localhost:9200") - ep["path"] = ep.get("path", "").replace("federation-ocis-server:10200", "localhost:10200") - svc["host"] = svc.get("host", "").replace("ocis-server:9200", "localhost:9200") - svc["host"] = svc.get("host", "").replace("federation-ocis-server:10200", "localhost:10200") - providers_tmp = tempfile.NamedTemporaryFile( - mode="w", suffix=".json", prefix="ocm-providers-", delete=False) - json.dump(providers, providers_tmp) - providers_tmp.close() - cfg["extraServerEnvironment"]["OCM_OCM_PROVIDER_AUTHORIZER_PROVIDERS_FILE"] = providers_tmp.name - - if cfg["keycloakNeeded"]: - # Install Playwright runtime and browsers (required for web UI tests) - print("Installing Playwright...") - run([str(repo_root / "vendor-php/bin/playwright-install")]) - run([str(repo_root / "vendor-php/bin/playwright-install"), "--browsers"]) - print("Playwright ready.") - - # Generate TLS certs for keycloak - print("Generating keycloak certs...") - keycloak_certs_dir = repo_root / "keycloak-certs" - keycloak_certs_dir.mkdir(parents=True, exist_ok=True) - run(["openssl", "req", "-x509", "-newkey", "rsa:2048", - "-keyout", str(keycloak_certs_dir / "keycloakkey.pem"), - "-out", str(keycloak_certs_dir / "keycloakcrt.pem"), - "-nodes", "-days", "365", "-subj", "/CN=keycloak"]) - for f in keycloak_certs_dir.iterdir(): - f.chmod(0o777) - - # Set up postgres (keycloak backend) - print("Setting up postgres...") - # GitHub runners ship PostgreSQL pre-started on 5432; stop it to avoid conflicts. - subprocess.run(["sudo", "systemctl", "stop", "postgresql"], capture_output=True) - run(["docker", "run", "-d", "--name", "postgres", "--network", "host", - "-e", "POSTGRES_DB=keycloak", - "-e", "POSTGRES_USER=keycloak", - "-e", "POSTGRES_PASSWORD=keycloak", - POSTGRES_ALPINE_IMAGE]) - wait_for(postgres_healthy, 30, "postgres", container="postgres") - print("postgres ready.") - - # Patch realm and start keycloak - realm_src = repo_root / "tests/config/ci/ocis-mfa-ci-realm.dist.json" - realm_patched = Path("/tmp/ocis-realm.json") - realm_patched.write_text( - realm_src.read_text() - .replace("https://ocis:9200", "https://localhost:9200") - ) - print("Starting keycloak...") - run(["docker", "run", "-d", "--name", "keycloak", "--network", "host", - "-e", "OCIS_DOMAIN=https://localhost:9200", - "-e", "KC_HOSTNAME=localhost", - "-e", "KC_PORT=8443", - "-e", "KC_DB=postgres", - "-e", "KC_DB_URL=jdbc:postgresql://localhost:5432/keycloak", - "-e", "KC_DB_USERNAME=keycloak", - "-e", "KC_DB_PASSWORD=keycloak", - "-e", "KC_FEATURES=impersonation", - "-e", "KC_BOOTSTRAP_ADMIN_USERNAME=admin", - "-e", "KC_BOOTSTRAP_ADMIN_PASSWORD=admin", - "-e", "KC_HTTPS_CERTIFICATE_FILE=/keycloak-certs/keycloakcrt.pem", - "-e", "KC_HTTPS_CERTIFICATE_KEY_FILE=/keycloak-certs/keycloakkey.pem", - "-v", f"{repo_root}/keycloak-certs:/keycloak-certs:ro", - "-v", "/tmp/ocis-realm.json:/opt/keycloak/data/import/ocis-mfa-ci-realm.dist.json:ro", - KEYCLOAK_IMAGE, - "start-dev", "--proxy-headers", "xforwarded", - "--spi-connections-http-client-default-disable-trust-manager=true", - "--import-realm", "--health-enabled=true"]) - wait_for(keycloak_healthy, 300, "keycloak", container="keycloak") - print("keycloak ready.") - - # init ocis - run([str(ocis_bin), "init", "--insecure", "true"]) - shutil.copy( - repo_root / "tests/config/ci/app-registry.yaml", - ocis_config_dir / "app-registry.yaml", - ) - - # generate fontsMap.json with the correct absolute font path for this runner - font_path = str(repo_root / "tests/config/ci/NotoSans.ttf") - fontmap_tmp = tempfile.NamedTemporaryFile( - mode="w", suffix=".json", prefix="fontsMap-", delete=False) - json.dump({"defaultFont": font_path}, fontmap_tmp) - fontmap_tmp.close() - - # assemble ocis server env - server_env = {**os.environ} - server_env.update(base_server_env(repo_root, ocis_url, str(ocis_config_dir))) - server_env["THUMBNAILS_TXT_FONTMAP_FILE"] = fontmap_tmp.name - server_env.update(cfg["extraServerEnvironment"]) - - # Patch web UI config: replace container DNS name with localhost so the - # browser's 'self' CSP origin (localhost:9200) is not violated. - web_cfg_src = Path(server_env["WEB_UI_CONFIG_FILE"]) - web_cfg_data = json.loads( - web_cfg_src.read_text().replace("ocis-server:9200", "localhost:9200") - ) - if cfg["vaultStorage"]: - web_cfg_data.setdefault("openIdConnect", {})["scope"] = "openid profile email acr" - server_env["PROXY_CSP_CONFIG_FILE_LOCATION"] = str(repo_root / "tests/config/ci/csp.yaml") - web_cfg_tmp = tempfile.NamedTemporaryFile( - mode="w", suffix=".json", prefix="ocis-web-config-", delete=False) - json.dump(web_cfg_data, web_cfg_tmp, indent=2) - web_cfg_tmp.close() - server_env["WEB_UI_CONFIG_FILE"] = web_cfg_tmp.name - - # start ociswrapper (primary ocis) - print("Starting ocis...") - wrapper_proc = subprocess.Popen( - [str(wrapper_bin), "serve", - "--bin", str(ocis_bin), - "--url", ocis_url, - "--admin-username", "admin", - "--admin-password", "admin"], - env=server_env, - ) - procs.append(wrapper_proc) - - # start federation ocis server (second instance on port 10200) - if cfg["federationServer"]: - fed_config_dir = Path.home() / ".ocis-federation/config" - fed_config_dir.mkdir(parents=True, exist_ok=True) - fed_data_dir = Path.home() / ".ocis-federation" - - fed_env = {**os.environ} - fed_env.update(base_server_env(repo_root, ocis_fed_url, str(fed_config_dir))) - fed_env.update(cfg["extraServerEnvironment"]) - # load federation port mappings from canonical env file (single source of truth) - fed_env.update(load_env_file(repo_root / "tests/config/local/.env-federation")) - # CI-specific overrides - fed_env.update({ - "OCIS_URL": ocis_fed_url, - "OCIS_BASE_DATA_PATH": str(fed_data_dir), - "OCIS_CONFIG_DIR": str(fed_config_dir), - "OCIS_RUNTIME_PORT": "10250", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:10233", - }) - - # init federation ocis with separate config - run([str(ocis_bin), "init", "--insecure", "true", - "--config-path", str(fed_config_dir)]) - shutil.copy( - repo_root / "tests/config/ci/app-registry.yaml", - fed_config_dir / "app-registry.yaml", - ) - - print("Starting federation ocis...") - fed_proc = subprocess.Popen( - [str(ocis_bin), "server"], - env=fed_env, - ) - procs.append(fed_proc) - - # --------------------------------------------------------------------------- - # Collaboration service helpers — same names/call pattern as drone.star - # Only deviation: container hostnames → localhost - # --------------------------------------------------------------------------- - def fakeOffice(): - # BusyBox nc -k has a race between connections: after each response the - # while-loop restarts nc, leaving a window where connections are refused. - # The collaboration service hits that window at startup → readLoopPeekFailLocked - # → healthz never binds → 300s timeout. Use Python's built-in HTTP server - # instead; it handles concurrent connections without gaps. - run(["docker", "run", "-d", "--name", "fakeoffice", "--network", "host", - "-v", f"{repo_root}:/ocis:ro", - "python:3-alpine", - "python3", "-c", - "import http.server, pathlib\n" - "body = pathlib.Path('/ocis/tests/config/ci/hosting-discovery.xml').read_bytes()\n" - "class H(http.server.BaseHTTPRequestHandler):\n" - " def do_GET(self):\n" - " self.send_response(200)\n" - " self.send_header('Content-Type', 'text/xml')\n" - " self.end_headers()\n" - " self.wfile.write(body)\n" - " def log_message(self, *a): pass\n" - "http.server.HTTPServer(('', 8080), H).serve_forever()\n" - ]) - return [] - - def collaboraService(): - # drone commands copy-pasted verbatim - run(["docker", "run", "-d", "--name", "collabora", "--network", "host", - "-e", "DONT_GEN_SSL_CERT=set", - "-e", f"extra_params=--o:ssl.enable=true --o:ssl.termination=true " - f"--o:welcome.enable=false --o:net.frame_ancestors=https://localhost:9200", - "--entrypoint", "/bin/sh", - "collabora/code:24.04.5.1.1", - "-c", "\n".join([ - "set -e", - "coolconfig generate-proof-key", - "bash /start-collabora-online.sh", - ])]) - return [] - - def onlyofficeService(): - # GitHub runner ships PostgreSQL pre-started on 5432. - # OnlyOffice supervisord starts its own PostgreSQL on 5432 internally. - # With --network host both compete for the same port → OnlyOffice DB never - # starts → docservice stays down → nginx returns 502 forever. - # Drone avoids this because each service has its own network namespace. - subprocess.run(["sudo", "systemctl", "stop", "postgresql"], - capture_output=True) - only_office_json = repo_root / "tests/config/ci/only-office.json" - run(["docker", "run", "-d", "--name", "onlyoffice", "--network", "host", - "-e", "WOPI_ENABLED=true", - "-e", "USE_UNAUTHORIZED_STORAGE=true", - "-v", f"{only_office_json}:/tmp/only-office.json:ro", - "--entrypoint", "/bin/sh", - "onlyoffice/documentserver:9.0.0", - "-c", "\n".join([ - "set -e", - "cp /tmp/only-office.json /etc/onlyoffice/documentserver/local.json", - "openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes", - "mkdir -p /var/www/onlyoffice/Data/certs", - "cp onlyoffice.key /var/www/onlyoffice/Data/certs/", - "cp onlyoffice.crt /var/www/onlyoffice/Data/certs/", - "chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key", - "/app/ds/run-document-server.sh", - ])]) - return [] - - def wopiCollaborationService(name, ocis_url=ocis_url): - # drone: startOcisService("collaboration", "wopi-{name}", environment) - # runs: ocis/bin/ocis-debug collaboration server - service_name = "wopi-%s" % name - ports = _WOPI_PORTS[name] - environment = { - **os.environ, - "OCIS_URL": ocis_url, - "MICRO_REGISTRY": "nats-js-kv", - "MICRO_REGISTRY_ADDRESS": "localhost:9233", - "COLLABORATION_LOG_LEVEL": "debug", - "COLLABORATION_GRPC_ADDR": f"0.0.0.0:{ports['grpc']}", - "COLLABORATION_HTTP_ADDR": f"0.0.0.0:{ports['http']}", - "COLLABORATION_DEBUG_ADDR": f"0.0.0.0:{ports['debug']}", - "COLLABORATION_APP_PROOF_DISABLE": "true", - "COLLABORATION_APP_INSECURE": "true", - "COLLABORATION_CS3API_DATAGATEWAY_INSECURE": "true", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "COLLABORATION_WOPI_SECRET": "some-wopi-secret", - } - if name == "collabora": - environment["COLLABORATION_APP_NAME"] = "Collabora" - environment["COLLABORATION_APP_PRODUCT"] = "Collabora" - environment["COLLABORATION_APP_ADDR"] = "https://localhost:9980" - environment["COLLABORATION_APP_ICON"] = "https://localhost:9980/favicon.ico" - elif name == "onlyoffice": - environment["COLLABORATION_APP_NAME"] = "OnlyOffice" - environment["COLLABORATION_APP_PRODUCT"] = "OnlyOffice" - environment["COLLABORATION_APP_ADDR"] = "https://localhost:443" - environment["COLLABORATION_APP_ICON"] = "https://localhost:443/web-apps/apps/documenteditor/main/resources/img/favicon.ico" - elif name == "fakeoffice": - environment["COLLABORATION_APP_NAME"] = "FakeOffice" - environment["COLLABORATION_APP_PRODUCT"] = "Microsoft" - environment["COLLABORATION_APP_ADDR"] = "http://localhost:8080" - environment["COLLABORATION_WOPI_SRC"] = f"http://localhost:{ports['http']}" - print(f"Starting {service_name}...") - return [subprocess.Popen([str(ocis_bin), "collaboration", "server"], env=environment)] - - def ocisHealthCheck(name, services=[]): - # drone: curl healthz + readyz on each service (timeout 300s) - for service in services: - host, port = service.rsplit(":", 1) - for endpoint in ("healthz", "readyz"): - wait_for( - lambda h="localhost", p=int(port), ep=endpoint: subprocess.run( - ["curl", "-sf", f"http://{h}:{p}/{ep}"], capture_output=True - ).returncode == 0, - 300, f"{service}/{endpoint}", - ) - print(f"health-check-{name}: all services healthy.") - - def wopi_discovery_ready(app_url: str) -> bool: - """Return True once the WOPI app's /hosting/discovery returns HTTP 200.""" - url = app_url.rstrip("/") + "/hosting/discovery" - r = subprocess.run( - ["curl", "-sfk", url], capture_output=True - ) - return r.returncode == 0 - - # drone.star non-k8s collaborationServiceNeeded path (lines 1195-1196, 1140-1141, 1179-1183) - if cfg["collaborationServiceNeeded"]: - procs += fakeOffice() + collaboraService() + onlyofficeService() - # Wait for each app's /hosting/discovery to return 200 before starting its - # collaboration service. GetAppURLs in server.go calls discovery synchronously - # at startup — non-200 exits the process immediately, healthz never binds. - wait_for(lambda: wopi_discovery_ready("http://localhost:8080"), 300, "fakeoffice discovery", container="fakeoffice") - wait_for(lambda: wopi_discovery_ready("https://localhost:9980"), 300, "collabora discovery", container="collabora") - wait_for(lambda: wopi_discovery_ready("https://localhost:443"), 300, "onlyoffice discovery", container="onlyoffice") - procs += wopiCollaborationService("fakeoffice") + \ - wopiCollaborationService("collabora") + \ - wopiCollaborationService("onlyoffice") - ocisHealthCheck("wopi", [ - f"localhost:{_WOPI_PORTS['collabora']['debug']}", - f"localhost:{_WOPI_PORTS['onlyoffice']['debug']}", - f"localhost:{_WOPI_PORTS['fakeoffice']['debug']}", - ]) - - def cleanup(*_): - for p in procs: - try: - p.terminate() - except Exception: - pass - - signal.signal(signal.SIGTERM, cleanup) - signal.signal(signal.SIGINT, cleanup) - - try: - if cfg["vaultStorage"]: - # Basic auth is blocked in vault mode (OIDC role assignment), - # use the unauthenticated proxy debug readyz endpoint instead. - wait_for(ocis_proxy_ready, 300, "ocis") - else: - wait_for(lambda: ocis_healthy(ocis_url), 300, "ocis") - print("ocis ready.") - - if cfg["federationServer"]: - wait_for(lambda: ocis_healthy(ocis_fed_url), 300, "federation ocis") - print("federation ocis ready.") - - if cfg["vaultStorage"]: - vault_data_dir = Path.home() / "ocis/storage/users-vault" - vault_data_dir.mkdir(parents=True, exist_ok=True) - # Inherit the full server env so the process has PATH, OCIS_JWT_SECRET, - # OCIS_CONFIG_DIR, SSL settings, etc. — then override with vault-specific values. - vault_env = { - **server_env, - "STORAGE_USERS_ENABLE_VAULT_MODE": "true", - "STORAGE_USERS_SERVICE_NAME": "storage-users-vault", - "STORAGE_USERS_GRPC_ADDR": "localhost:19285", - "STORAGE_USERS_HTTP_ADDR": "localhost:19286", - "STORAGE_USERS_DATA_SERVER_URL": "http://localhost:19286/data", - "STORAGE_USERS_DEBUG_ADDR": "localhost:19287", - "STORAGE_USERS_OCIS_ROOT": str(vault_data_dir), - "STORAGE_USERS_EVENTS_CONSUMER_GROUP": "vault-dcfs", - } - print("Starting storage-users-vault...") - vault_proc = subprocess.Popen( - [str(ocis_bin), "storage-users", "server"], - env=vault_env, - ) - procs.append(vault_proc) - wait_for( - lambda: subprocess.run( - ["curl", "-sf", "http://localhost:19287/healthz"], - capture_output=True, - ).returncode == 0, - 120, "storage-users-vault", - ) - print("vault ready.") - - # expected failures file - if acceptance_test_type == "core-api": - filter_tags = "~@skipOnGraph&&~@skipOnOcis-OCIS-Storage" - base_failures = repo_root / "tests/acceptance/expected-failures-API-on-OCIS-storage.md" - else: - filter_tags = "~@skip&&~@skipOnGraph&&~@skipOnOcis-OCIS-Storage" - base_failures = repo_root / "tests/acceptance/expected-failures-localAPI-on-OCIS-storage.md" - - ef_override = os.environ.get("EXPECTED_FAILURES_FILE") - if ef_override: - p = Path(ef_override) - base_failures = p if p.is_absolute() else repo_root / p - - # merge expected-failures-without-remotephp.md only when not using remote.php - # (mirrors drone.star: "" if run_with_remote_php else "cat ...without-remotephp.md >> ...") - tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".md", delete=False) - tmp.write(base_failures.read_text()) - if os.environ.get("WITH_REMOTE_PHP", "false").lower() != "true": - without_rphp = repo_root / "tests/acceptance/expected-failures-without-remotephp.md" - if without_rphp.exists(): - tmp.write("\n") - tmp.write(without_rphp.read_text()) - tmp.close() - - # run tests - behat_env = { - **os.environ, - "TEST_SERVER_URL": ocis_url, - "TEST_SERVER_FED_URL": ocis_fed_url, - "OCIS_WRAPPER_URL": "http://localhost:5200", - "BEHAT_SUITES": behat_suites_raw, - "ACCEPTANCE_TEST_TYPE": acceptance_test_type, - "BEHAT_FILTER_TAGS": filter_tags, - "EXPECTED_FAILURES_FILE": tmp.name, - "STORAGE_DRIVER": "ocis", - "UPLOAD_DELETE_WAIT_TIME": "0", - "EMAIL_HOST": "localhost", - "EMAIL_PORT": EMAIL_PORT, - "COLLABORATION_SERVICE_URL": f"http://localhost:{_WOPI_PORTS['fakeoffice']['http']}", - } - behat_env.update(cfg["extraEnvironment"]) - - print(f"Running suites: {behat_suites_raw} (type: {acceptance_test_type})") - result = subprocess.run( - ["make", "-C", str(repo_root), "test-acceptance-api"], - env=behat_env, - ) - return result.returncode - - finally: - cleanup() - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/acceptance/run-litmus.py b/tests/acceptance/run-litmus.py deleted file mode 100644 index 380a70afa0f..00000000000 --- a/tests/acceptance/run-litmus.py +++ /dev/null @@ -1,242 +0,0 @@ -#!/usr/bin/env python3 -""" -Run litmus WebDAV compliance tests locally and in GitHub Actions CI. - -Usage: python3 tests/acceptance/run-litmus.py -""" - -import json -import os -import re -import shutil -import signal -import subprocess -import sys -import time -from pathlib import Path - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -# HTTPS — matching drone: ocis init generates a self-signed cert; proxy uses TLS by default. -# Host-side curl calls use -k (insecure) to skip cert verification. -OCIS_URL = "https://127.0.0.1:9200" -LITMUS_IMAGE = "owncloudci/litmus:latest" -LITMUS_TESTS = "basic copymove props http" -SHARE_ENDPOINT = "ocs/v2.php/apps/files_sharing/api/v1/shares" - - -def get_docker_bridge_ip() -> str: - """Return the Docker bridge gateway IP, reachable from host and Docker containers.""" - r = subprocess.run( - ["docker", "network", "inspect", "bridge", - "--format", "{{range .IPAM.Config}}{{.Gateway}}{{end}}"], - capture_output=True, text=True, check=True, - ) - return r.stdout.strip() - - -def base_server_env(repo_root: Path, ocis_config_dir: str, ocis_public_url: str) -> dict: - """OCIS server environment matching drone ocisServer() for litmus.""" - return { - "OCIS_URL": ocis_public_url, - "OCIS_CONFIG_DIR": ocis_config_dir, - "STORAGE_USERS_DRIVER": "ocis", - "PROXY_ENABLE_BASIC_AUTH": "true", - # No PROXY_TLS override — drone lets ocis use its default TLS (self-signed cert from init) - # IDP excluded: its static assets are absent when running as a host process - "OCIS_EXCLUDE_RUN_SERVICES": "idp", - "OCIS_LOG_LEVEL": "error", - "IDM_CREATE_DEMO_USERS": "true", - "IDM_ADMIN_PASSWORD": "admin", - "FRONTEND_SEARCH_MIN_LENGTH": "2", - "OCIS_EVENTS_ENABLE_TLS": "false", - "NATS_NATS_HOST": "0.0.0.0", - "NATS_NATS_PORT": "9233", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "EVENTHISTORY_STORE": "memory", - "WEB_UI_CONFIG_FILE": str(repo_root / "tests/config/ci/ocis-config.json"), - } - - -def wait_for(condition_fn, timeout: int, label: str) -> None: - deadline = time.time() + timeout - while not condition_fn(): - if time.time() > deadline: - print(f"Timeout waiting for {label}", file=sys.stderr) - sys.exit(1) - time.sleep(1) - - -def ocis_healthy(ocis_url: str) -> bool: - r = subprocess.run( - ["curl", "-sk", "-uadmin:admin", - f"{ocis_url}/graph/v1.0/users/admin", - "-w", "%{http_code}", "-o", "/dev/null"], - capture_output=True, text=True, - ) - return r.stdout.strip() == "200" - - -def setup_for_litmus(ocis_url: str) -> tuple: - """ - Translate tests/config/ci/setup-for-litmus.sh to Python. - Returns (space_id, public_token). - """ - # get personal space ID - r = subprocess.run( - ["curl", "-sk", "-uadmin:admin", f"{ocis_url}/graph/v1.0/me/drives"], - capture_output=True, text=True, check=True, - ) - drives = json.loads(r.stdout) - space_id = "" - for drive in drives.get("value", []): - if drive.get("driveType") == "personal": - web_dav_url = drive.get("root", {}).get("webDavUrl", "") - # last non-empty path segment (same as cut -d"/" -f6 in bash) - space_id = [p for p in web_dav_url.split("/") if p][-1] - break - if not space_id: - print("ERROR: could not determine personal space ID", file=sys.stderr) - sys.exit(1) - print(f"SPACE_ID={space_id}") - - # create test folder as einstein - subprocess.run( - ["curl", "-sk", "-ueinstein:relativity", "-X", "MKCOL", - f"{ocis_url}/remote.php/webdav/new_folder"], - capture_output=True, check=True, - ) - - # create share from einstein to admin - r = subprocess.run( - ["curl", "-sk", "-ueinstein:relativity", - f"{ocis_url}/{SHARE_ENDPOINT}", - "-d", "path=/new_folder&shareType=0&permissions=15&name=new_folder&shareWith=admin"], - capture_output=True, text=True, check=True, - ) - share_id_match = re.search(r"(.+?)", r.stdout) - if share_id_match: - share_id = share_id_match.group(1) - # accept the share as admin - subprocess.run( - ["curl", "-X", "POST", "-sk", "-uadmin:admin", - f"{ocis_url}/{SHARE_ENDPOINT}/pending/{share_id}"], - capture_output=True, check=True, - ) - - # create public share as einstein - r = subprocess.run( - ["curl", "-sk", "-ueinstein:relativity", - f"{ocis_url}/{SHARE_ENDPOINT}", - "-d", "path=/new_folder&shareType=3&permissions=15&name=new_folder"], - capture_output=True, text=True, check=True, - ) - public_token = "" - token_match = re.search(r"(.+?)", r.stdout) - if token_match: - public_token = token_match.group(1) - print(f"PUBLIC_TOKEN={public_token}") - - return space_id, public_token - - -def run_litmus(name: str, endpoint: str) -> int: - print(f"\nTesting endpoint [{name}]: {endpoint}", flush=True) - result = subprocess.run( - ["docker", "run", "--rm", - "-e", f"LITMUS_URL={endpoint}", - "-e", "LITMUS_USERNAME=admin", - "-e", "LITMUS_PASSWORD=admin", - "-e", f"TESTS={LITMUS_TESTS}", - LITMUS_IMAGE, - # No extra CMD — ENTRYPOINT is already litmus-wrapper; passing it again - # would make the wrapper use the path as LITMUS_URL, overriding the env var. - ], - ) - return result.returncode - - -def main() -> int: - repo_root = Path(__file__).resolve().parents[2] - ocis_bin = repo_root / "ocis/bin/ocis" - ocis_config_dir = Path.home() / ".ocis/config" - - if not ocis_bin.exists(): - subprocess.run(["make", "-C", str(repo_root / "ocis"), "build"], check=True) - - # Docker bridge gateway IP: reachable from both the host (via docker0 interface) - # and Docker containers (via bridge network default gateway). Use this as OCIS_URL - # so that any redirects OCIS generates stay on a hostname the litmus container - # can follow — matching how drone uses "ocis-server:9200" consistently. - # HTTPS: owncloudci/litmus accepts insecure (self-signed) certs, just like drone does. - bridge_ip = get_docker_bridge_ip() - litmus_base = f"https://{bridge_ip}:9200" - print(f"Docker bridge IP: {bridge_ip}", flush=True) - - # assemble server env first — same env vars drone sets on the container before - # running `ocis init`, so IDM_ADMIN_PASSWORD=admin is present during init and - # the config is written with the correct password (not a random one) - server_env = {**os.environ} - server_env.update(base_server_env(repo_root, str(ocis_config_dir), litmus_base)) - - # init ocis with full server env (mirrors drone: env is set before ocis init runs) - subprocess.run( - [str(ocis_bin), "init", "--insecure", "true"], - env=server_env, - check=True, - ) - shutil.copy( - repo_root / "tests/config/ci/app-registry.yaml", - ocis_config_dir / "app-registry.yaml", - ) - - # start ocis server directly (matching drone: no ociswrapper for litmus) - print("Starting ocis...", flush=True) - ocis_proc = subprocess.Popen( - [str(ocis_bin), "server"], - env=server_env, - ) - - def cleanup(*_): - try: - ocis_proc.terminate() - except Exception: - pass - - signal.signal(signal.SIGTERM, cleanup) - signal.signal(signal.SIGINT, cleanup) - - try: - wait_for(lambda: ocis_healthy(OCIS_URL), 300, "ocis") - print("ocis ready.", flush=True) - - space_id, _ = setup_for_litmus(OCIS_URL) - - endpoints = [ - ("old-endpoint", f"{litmus_base}/remote.php/webdav"), - ("new-endpoint", f"{litmus_base}/remote.php/dav/files/admin"), - ("new-shared", f"{litmus_base}/remote.php/dav/files/admin/Shares/new_folder/"), - ("old-shared", f"{litmus_base}/remote.php/webdav/Shares/new_folder/"), - ("spaces-endpoint", f"{litmus_base}/remote.php/dav/spaces/{space_id}"), - ] - - failed = [] - for name, endpoint in endpoints: - rc = run_litmus(name, endpoint) - if rc != 0: - failed.append(name) - - if failed: - print(f"\nFailed endpoints: {', '.join(failed)}", file=sys.stderr) - return 1 - print("\nAll litmus tests passed.") - return 0 - - finally: - cleanup() - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/acceptance/run-wopi.py b/tests/acceptance/run-wopi.py deleted file mode 100644 index a9a6d636885..00000000000 --- a/tests/acceptance/run-wopi.py +++ /dev/null @@ -1,374 +0,0 @@ -#!/usr/bin/env python3 -""" -Run WOPI validator tests locally and in GitHub Actions CI. - -Usage: python3 tests/acceptance/run-wopi.py --type builtin - python3 tests/acceptance/run-wopi.py --type cs3 -""" - -import argparse -import json -import os -import re -import shutil -import signal -import socket -import subprocess -import sys -import time -import urllib.parse -from pathlib import Path - -# --------------------------------------------------------------------------- -# Constants -# --------------------------------------------------------------------------- - -# HTTPS — matching drone; host-side curl calls use -k. -OCIS_URL = "https://127.0.0.1:9200" -VALIDATOR_IMAGE = "owncloudci/wopi-validator" -CS3_WOPI_IMAGE = "cs3org/wopiserver:v10.4.0" -FAKEOFFICE_IMAGE = "owncloudci/alpine:latest" - -# Testgroups shared between both variants (drone: testgroups list) -SHARED_TESTGROUPS = [ - "BaseWopiViewing", - "CheckFileInfoSchema", - "EditFlows", - "Locks", - "AccessTokens", - "GetLock", - "ExtendedLockLength", - "FileVersion", - "Features", -] - -# Testgroups only run for builtin (drone: builtinOnlyTestGroups, with -s flag) -BUILTIN_ONLY_TESTGROUPS = [ - "PutRelativeFile", - "RenameFileIfCreateChildFileIsNotSupported", -] - - -def get_docker_bridge_ip() -> str: - r = subprocess.run( - ["docker", "network", "inspect", "bridge", - "--format", "{{range .IPAM.Config}}{{.Gateway}}{{end}}"], - capture_output=True, text=True, check=True, - ) - return r.stdout.strip() - - -def wait_for(condition_fn, timeout: int, label: str) -> None: - deadline = time.time() + timeout - while not condition_fn(): - if time.time() > deadline: - print(f"Timeout waiting for {label}", file=sys.stderr) - sys.exit(1) - time.sleep(1) - - -def ocis_healthy(ocis_url: str) -> bool: - r = subprocess.run( - ["curl", "-sk", "-uadmin:admin", - f"{ocis_url}/graph/v1.0/users/admin", - "-w", "%{http_code}", "-o", "/dev/null"], - capture_output=True, text=True, - ) - return r.stdout.strip() == "200" - - -def tcp_reachable(host: str, port: int) -> bool: - try: - with socket.create_connection((host, port), timeout=1): - return True - except Exception: - return False - - -def wopi_discovery_ready(url: str) -> bool: - r = subprocess.run( - ["curl", "-sk", "-o", "/dev/null", "-w", "%{http_code}", url], - capture_output=True, text=True, - ) - return r.stdout.strip() == "200" - - -def base_server_env(repo_root: Path, ocis_config_dir: str, ocis_public_url: str, - bridge_ip: str, wopi_type: str) -> dict: - """ - OCIS server environment matching drone ocisServer(deploy_type='wopi_validator'). - builtin: also excludes app-provider (collaboration service takes that role). - """ - exclude = "idp,app-provider" if wopi_type == "builtin" else "idp" - return { - "OCIS_URL": ocis_public_url, - "OCIS_CONFIG_DIR": ocis_config_dir, - "STORAGE_USERS_DRIVER": "ocis", - "PROXY_ENABLE_BASIC_AUTH": "true", - # No PROXY_TLS override — drone uses default TLS (self-signed cert from init) - # IDP excluded: static assets absent when running as host process - "OCIS_EXCLUDE_RUN_SERVICES": exclude, - "OCIS_LOG_LEVEL": "error", - "IDM_CREATE_DEMO_USERS": "true", - "IDM_ADMIN_PASSWORD": "admin", - "FRONTEND_SEARCH_MIN_LENGTH": "2", - "OCIS_ASYNC_UPLOADS": "true", - "OCIS_EVENTS_ENABLE_TLS": "false", - "NATS_NATS_HOST": "0.0.0.0", - "NATS_NATS_PORT": "9233", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "EVENTHISTORY_STORE": "memory", - "WEB_UI_CONFIG_FILE": str(repo_root / "tests/config/ci/ocis-config.json"), - # wopi_validator extras (drone ocisServer deploy_type="wopi_validator") - "GATEWAY_GRPC_ADDR": "0.0.0.0:9142", - "APP_PROVIDER_EXTERNAL_ADDR": "com.owncloud.api.app-provider", - "APP_PROVIDER_DRIVER": "wopi", - "APP_PROVIDER_WOPI_APP_NAME": "FakeOffice", - "APP_PROVIDER_WOPI_APP_URL": f"http://{bridge_ip}:8080", - "APP_PROVIDER_WOPI_INSECURE": "true", - "APP_PROVIDER_WOPI_WOPI_SERVER_EXTERNAL_URL": f"http://{bridge_ip}:9300", - "APP_PROVIDER_WOPI_FOLDER_URL_BASE_URL": ocis_public_url, - } - - -def collab_service_env(bridge_ip: str, ocis_config_dir: str) -> dict: - """ - Environment for 'ocis collaboration server' (builtin wopi-fakeoffice). - Mirrors drone wopiCollaborationService("fakeoffice"). - """ - return { - "OCIS_URL": f"https://{bridge_ip}:9200", - "OCIS_CONFIG_DIR": ocis_config_dir, - "MICRO_REGISTRY": "nats-js-kv", - "MICRO_REGISTRY_ADDRESS": "127.0.0.1:9233", - "COLLABORATION_LOG_LEVEL": "debug", - "COLLABORATION_GRPC_ADDR": "0.0.0.0:9301", - "COLLABORATION_HTTP_ADDR": "0.0.0.0:9300", - "COLLABORATION_DEBUG_ADDR": "0.0.0.0:9304", - "COLLABORATION_APP_PROOF_DISABLE": "true", - "COLLABORATION_APP_INSECURE": "true", - "COLLABORATION_CS3API_DATAGATEWAY_INSECURE": "true", - "OCIS_JWT_SECRET": "some-ocis-jwt-secret", - "COLLABORATION_WOPI_SECRET": "some-wopi-secret", - "COLLABORATION_APP_NAME": "FakeOffice", - "COLLABORATION_APP_PRODUCT": "Microsoft", - "COLLABORATION_APP_ADDR": f"http://{bridge_ip}:8080", - # COLLABORATION_WOPI_SRC is what OCIS tells clients to use — must be reachable - # from Docker validator containers (collaboration service runs as host process) - "COLLABORATION_WOPI_SRC": f"http://{bridge_ip}:9300", - } - - -def prepare_test_file(bridge_ip: str) -> tuple: - """ - Upload test.wopitest via WebDAV, open the WOPI app, extract credentials. - Mirrors the prepare-test-file step from drone.star. - Returns (access_token, access_token_ttl, wopi_src). - """ - headers_file = "/tmp/wopi-headers.txt" - - # PUT empty test file (--retry-connrefused/--retry-all-errors matching drone) - subprocess.run( - ["curl", "-sk", "-u", "admin:admin", "-X", "PUT", - "--fail", "--retry-connrefused", "--retry", "7", "--retry-all-errors", - f"{OCIS_URL}/remote.php/webdav/test.wopitest", - "-D", headers_file], - check=True, - ) - - # Extract Oc-Fileid from response headers - headers_text = Path(headers_file).read_text() - print("--- PUT headers ---", flush=True) - print(headers_text[:500], flush=True) - m = re.search(r"Oc-Fileid:\s*(\S+)", headers_text, re.IGNORECASE) - if not m: - print("ERROR: Oc-Fileid not found in PUT response headers", file=sys.stderr) - sys.exit(1) - file_id = m.group(1).strip() - print(f"FILE_ID={file_id}", flush=True) - - # POST to app/open to get WOPI access token and wopi src - url = f"{OCIS_URL}/app/open?app_name=FakeOffice&file_id={urllib.parse.quote(file_id, safe='')}" - r = subprocess.run( - ["curl", "-sk", "-u", "admin:admin", "-X", "POST", - "--fail", "--retry-connrefused", "--retry", "7", "--retry-all-errors", url], - capture_output=True, text=True, check=True, - ) - open_json = json.loads(r.stdout) - print(f"open.json: {r.stdout[:800]}", flush=True) - - access_token = open_json["form_parameters"]["access_token"] - access_token_ttl = str(open_json["form_parameters"]["access_token_ttl"]) - app_url = open_json.get("app_url", "") - - # Construct wopi_src: drone extracts file ID from app_url after 'files%2F', - # then prepends http://wopi-fakeoffice:9300/wopi/files/ — we use bridge_ip instead. - wopi_base = f"http://{bridge_ip}:9300/wopi/files/" - if "files%2F" in app_url: - file_id_encoded = app_url.split("files%2F")[-1].strip().strip('"') - elif "files/" in app_url: - file_id_encoded = app_url.split("files/")[-1].strip().strip('"') - else: - file_id_encoded = urllib.parse.quote(file_id, safe="") - wopi_src = wopi_base + file_id_encoded - print(f"WOPI_SRC={wopi_src}", flush=True) - - return access_token, access_token_ttl, wopi_src - - -def run_validator(group: str, token: str, wopi_src: str, ttl: str, - secure: bool = False) -> int: - print(f"\nRunning testgroup [{group}] secure={secure}", flush=True) - cmd = [ - "docker", "run", "--rm", - "--workdir", "/app", - "--entrypoint", "/app/Microsoft.Office.WopiValidator", - VALIDATOR_IMAGE, - ] - if secure: - cmd.append("-s") - cmd += ["-t", token, "-w", wopi_src, "-l", ttl, "--testgroup", group] - return subprocess.run(cmd).returncode - - -def main() -> int: - parser = argparse.ArgumentParser() - parser.add_argument("--type", choices=["builtin", "cs3"], required=True, - help="WOPI server type: builtin (collaboration service) or cs3 (cs3org/wopiserver)") - args = parser.parse_args() - wopi_type = args.type - - repo_root = Path(__file__).resolve().parents[2] - ocis_bin = repo_root / "ocis/bin/ocis" - ocis_config_dir = Path.home() / ".ocis/config" - - if not ocis_bin.exists(): - subprocess.run(["make", "-C", str(repo_root / "ocis"), "build"], check=True) - - bridge_ip = get_docker_bridge_ip() - print(f"Docker bridge IP: {bridge_ip}", flush=True) - - procs = [] - containers = [] - - def cleanup(*_): - for p in procs: - try: - p.terminate() - except Exception: - pass - for name in containers: - subprocess.run(["docker", "rm", "-f", name], capture_output=True) - - signal.signal(signal.SIGTERM, cleanup) - signal.signal(signal.SIGINT, cleanup) - - try: - # --- fakeoffice: serves hosting-discovery.xml on :8080 --- - # Mirrors the old fakeOffice() step — owncloudci/alpine running serve-hosting-discovery.sh. - containers.append("wopi-fakeoffice-fake") - subprocess.run(["docker", "rm", "-f", "wopi-fakeoffice-fake"], capture_output=True) - subprocess.run([ - "docker", "run", "-d", "--name", "wopi-fakeoffice-fake", - "-p", "8080:8080", - "-v", f"{repo_root}:/ocis", - FAKEOFFICE_IMAGE, - "sh", "/ocis/tests/config/ci/serve-hosting-discovery.sh", - ], check=True) - - wait_for(lambda: tcp_reachable(bridge_ip, 8080), 60, "fakeoffice:8080") - print("fakeoffice ready.", flush=True) - - # --- Init and start OCIS --- - ocis_public_url = f"https://{bridge_ip}:9200" - server_env = {**os.environ} - server_env.update(base_server_env( - repo_root, str(ocis_config_dir), ocis_public_url, bridge_ip, wopi_type)) - - subprocess.run( - [str(ocis_bin), "init", "--insecure", "true"], - env=server_env, check=True, - ) - shutil.copy( - repo_root / "tests/config/ci/app-registry.yaml", - ocis_config_dir / "app-registry.yaml", - ) - - print("Starting ocis...", flush=True) - ocis_proc = subprocess.Popen([str(ocis_bin), "server"], env=server_env) - procs.append(ocis_proc) - - wait_for(lambda: ocis_healthy(OCIS_URL), 300, "ocis") - print("ocis ready.", flush=True) - - # --- Wait for fakeoffice discovery endpoint before starting WOPI service --- - # ocis collaboration server calls GetAppURLs synchronously at startup; - # if /hosting/discovery returns non-200, the process exits immediately. - wait_for(lambda: wopi_discovery_ready("http://127.0.0.1:8080/hosting/discovery"), - 300, "fakeoffice /hosting/discovery") - print("fakeoffice discovery ready.", flush=True) - - # --- Start wopi server (after OCIS is healthy so NATS/gRPC are up) --- - if wopi_type == "builtin": - # Run 'ocis collaboration server' as a host process. - # Mirrors drone wopiCollaborationService("fakeoffice") → startOcisService("collaboration"). - collab_env = {**os.environ} - collab_env.update(collab_service_env(bridge_ip, str(ocis_config_dir))) - print("Starting collaboration service...", flush=True) - collab_proc = subprocess.Popen( - [str(ocis_bin), "collaboration", "server"], - env=collab_env, - ) - procs.append(collab_proc) - else: - # cs3: patch wopiserver.conf (replace container hostname with bridge_ip), - # then run cs3org/wopiserver as a Docker container. - conf_text = (repo_root / "tests/config/ci/wopiserver.conf").read_text() - conf_text = conf_text.replace("ocis-server", bridge_ip) - conf_tmp = Path("/tmp/wopiserver-patched.conf") - conf_tmp.write_text(conf_text) - secret_tmp = Path("/tmp/wopisecret") - secret_tmp.write_text("123\n") - - containers.append("wopi-cs3server") - subprocess.run(["docker", "rm", "-f", "wopi-cs3server"], capture_output=True) - subprocess.run([ - "docker", "run", "-d", "--name", "wopi-cs3server", - "-p", "9300:9300", - "-v", f"{conf_tmp}:/etc/wopi/wopiserver.conf", - "-v", f"{secret_tmp}:/etc/wopi/wopisecret", - "--entrypoint", "/app/wopiserver.py", - CS3_WOPI_IMAGE, - ], check=True) - - wait_for(lambda: tcp_reachable(bridge_ip, 9300), 120, "wopi-fakeoffice:9300") - print("wopi server ready.", flush=True) - - # --- prepare-test-file: upload file, get WOPI credentials --- - access_token, ttl, wopi_src = prepare_test_file(bridge_ip) - - # --- Run validator for each testgroup --- - failed = [] - for group in SHARED_TESTGROUPS: - rc = run_validator(group, access_token, wopi_src, ttl, secure=False) - if rc != 0: - failed.append(group) - - if wopi_type == "builtin": - for group in BUILTIN_ONLY_TESTGROUPS: - rc = run_validator(group, access_token, wopi_src, ttl, secure=True) - if rc != 0: - failed.append(group) - - if failed: - print(f"\nFailed testgroups: {', '.join(failed)}", file=sys.stderr) - return 1 - print("\nAll WOPI validator tests passed.") - return 0 - - finally: - cleanup() - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/tests/config/ci/fakeoffice-server.py b/tests/config/ci/fakeoffice-server.py new file mode 100644 index 00000000000..db7a9c88091 --- /dev/null +++ b/tests/config/ci/fakeoffice-server.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +"""Minimal HTTP server that serves hosting-discovery.xml on :8080.""" +import http.server +import pathlib + +body = pathlib.Path('/ocis/tests/config/ci/hosting-discovery.xml').read_bytes() + + +class Handler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + self.send_response(200) + self.send_header('Content-Type', 'text/xml') + self.end_headers() + self.wfile.write(body) + + def log_message(self, *a): + pass + + +http.server.HTTPServer(('', 8080), Handler).serve_forever() diff --git a/tests/config/ci/ocis-config-vault.json b/tests/config/ci/ocis-config-vault.json new file mode 100644 index 00000000000..be934f2713c --- /dev/null +++ b/tests/config/ci/ocis-config-vault.json @@ -0,0 +1,31 @@ +{ + "server": "https://localhost:9200", + "theme": "https://localhost:9200/themes/owncloud/theme.json", + "version": "0.1.0", + "openIdConnect": { + "metadata_url": "https://ocis-server:9200/.well-known/openid-configuration", + "authority": "https://ocis-server:9200", + "client_id": "web", + "response_type": "code", + "scope": "openid profile email acr" + }, + "options": { + "topCenterNotifications": true, + "displayResourcesLazy": false, + "sidebar": { + "shares": { + "showAllOnLoad": true + } + } + }, + "apps": [ + "files", + "text-editor", + "preview", + "pdf-viewer", + "search", + "admin-settings", + "app-store", + "password-protected-folders" + ] +} diff --git a/tests/config/ci/ocis-mfa-ci-realm.dist.json b/tests/config/ci/ocis-mfa-ci-realm.dist.json index 0b960c794d0..776672885da 100644 --- a/tests/config/ci/ocis-mfa-ci-realm.dist.json +++ b/tests/config/ci/ocis-mfa-ci-realm.dist.json @@ -1233,18 +1233,18 @@ "clientId": "web", "name": "", "description": "", - "rootUrl": "https://localhost:9200", - "adminUrl": "https://localhost:9200", + "rootUrl": "https://ocis-server:9200", + "adminUrl": "https://ocis-server:9200", "baseUrl": "", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "redirectUris": [ - "https://localhost:9200/*" + "https://ocis-server:9200/*" ], "webOrigins": [ - "https://localhost:9200" + "https://ocis-server:9200" ], "notBefore": 0, "bearerOnly": false, @@ -1268,7 +1268,7 @@ "saml.server.signature.keyinfo.ext": "false", "exclude.session.state.from.auth.response": "false", "oidc.ciba.grant.enabled": "false", - "backchannel.logout.url": "https://localhost:9200/backchannel_logout", + "backchannel.logout.url": "https://ocis-server:9200/backchannel_logout", "backchannel.logout.session.required": "true", "client_credentials.use_refresh_token": "false", "saml_force_name_id_format": "false", diff --git a/tests/config/ci/onlyoffice-entrypoint.sh b/tests/config/ci/onlyoffice-entrypoint.sh new file mode 100644 index 00000000000..bd85fa0752e --- /dev/null +++ b/tests/config/ci/onlyoffice-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/sh +set -e +cp /tmp/only-office.json /etc/onlyoffice/documentserver/local.json +openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes +mkdir -p /var/www/onlyoffice/Data/certs +cp onlyoffice.key /var/www/onlyoffice/Data/certs/ +cp onlyoffice.crt /var/www/onlyoffice/Data/certs/ +chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key +/app/ds/run-document-server.sh diff --git a/vendor/golang.org/x/text/cases/context.go b/vendor/golang.org/x/text/cases/context.go index e9aa9e1936f..a28f45d7bf1 100644 --- a/vendor/golang.org/x/text/cases/context.go +++ b/vendor/golang.org/x/text/cases/context.go @@ -249,7 +249,7 @@ func upper(c *context) bool { return c.copy() } -// isUpper writes the isUppercase version of the current rune to dst. +// isUpper reports whether the current rune is in upper case. func isUpper(c *context) bool { ct := c.caseType() if c.info&hasMappingMask == 0 || ct == cUpper { diff --git a/vendor/golang.org/x/text/cases/map.go b/vendor/golang.org/x/text/cases/map.go index 0f7c6a14bb7..51a683092f2 100644 --- a/vendor/golang.org/x/text/cases/map.go +++ b/vendor/golang.org/x/text/cases/map.go @@ -774,7 +774,7 @@ func nlTitle(c *context) bool { // From CLDR: // # Special titlecasing for Dutch initial "ij". // ::Any-Title(); - // # Fix up Ij at the beginning of a "word" (per Any-Title, notUAX #29) + // # Fix up Ij at the beginning of a "word" (per Any-Title, not UAX #29) // [:^WB=ALetter:] [:WB=Extend:]* [[:WB=MidLetter:][:WB=MidNumLet:]]? { Ij } → IJ ; if c.src[c.pSrc] != 'I' && c.src[c.pSrc] != 'i' { return title(c) @@ -794,7 +794,7 @@ func nlTitleSpan(c *context) bool { // From CLDR: // # Special titlecasing for Dutch initial "ij". // ::Any-Title(); - // # Fix up Ij at the beginning of a "word" (per Any-Title, notUAX #29) + // # Fix up Ij at the beginning of a "word" (per Any-Title, not UAX #29) // [:^WB=ALetter:] [:WB=Extend:]* [[:WB=MidLetter:][:WB=MidNumLet:]]? { Ij } → IJ ; if c.src[c.pSrc] != 'I' { return isTitle(c) diff --git a/vendor/golang.org/x/text/unicode/norm/forminfo.go b/vendor/golang.org/x/text/unicode/norm/forminfo.go index f3a234e5f53..b3cf5d9bdee 100644 --- a/vendor/golang.org/x/text/unicode/norm/forminfo.go +++ b/vendor/golang.org/x/text/unicode/norm/forminfo.go @@ -121,8 +121,12 @@ func (p Properties) BoundaryAfter() bool { // // When all 6 bits are zero, the character is inert, meaning it is never // influenced by normalization. +// +// We set flags to 0x80 (high bit 7 unused in quick check data) to indicate an invalid rune. type qcInfo uint8 +func (p Properties) isInvalid() bool { return p.flags == 0x80 } + func (p Properties) isYesC() bool { return p.flags&0x10 == 0 } func (p Properties) isYesD() bool { return p.flags&0x4 == 0 } @@ -247,6 +251,9 @@ func (f Form) PropertiesString(s string) Properties { // to a Properties. See the comment at the top of the file // for more information on the format. func compInfo(v uint16, sz int) Properties { + if sz == 0 { + return Properties{flags: 0x80, size: 1} + } if v == 0 { return Properties{size: uint8(sz)} } else if v >= 0x8000 { @@ -254,7 +261,7 @@ func compInfo(v uint16, sz int) Properties { size: uint8(sz), ccc: uint8(v), tccc: uint8(v), - flags: qcInfo(v >> 8), + flags: qcInfo(v>>8) & 0x3f, } if p.ccc > 0 || p.combinesBackward() { p.nLead = uint8(p.flags & 0x3) diff --git a/vendor/golang.org/x/text/unicode/norm/iter.go b/vendor/golang.org/x/text/unicode/norm/iter.go index 417c6b26894..3cc059224d9 100644 --- a/vendor/golang.org/x/text/unicode/norm/iter.go +++ b/vendor/golang.org/x/text/unicode/norm/iter.go @@ -376,16 +376,12 @@ func nextComposed(i *Iter) []byte { goto doNorm } prevCC = i.info.tccc - sz := int(i.info.size) - if sz == 0 { - sz = 1 // illegal rune: copy byte-by-byte - } - p := outp + sz + p := outp + int(i.info.size) if p > len(i.buf) { break } outp = p - i.p += sz + i.p += int(i.info.size) if i.p >= i.rb.nsrc { i.setDone() break diff --git a/vendor/golang.org/x/text/unicode/norm/normalize.go b/vendor/golang.org/x/text/unicode/norm/normalize.go index 4747ad07a83..60b1511caa2 100644 --- a/vendor/golang.org/x/text/unicode/norm/normalize.go +++ b/vendor/golang.org/x/text/unicode/norm/normalize.go @@ -148,7 +148,7 @@ func (f Form) IsNormalString(s string) bool { // patched buffer and whether the decomposition is still in progress. func patchTail(rb *reorderBuffer) bool { info, p := lastRuneStart(&rb.f, rb.out) - if p == -1 || info.size == 0 { + if p == -1 || info.isInvalid() { return true } end := p + int(info.size) @@ -225,7 +225,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte { } fd := &rb.f if doMerge { - var info Properties + info := Properties{flags: 0x80, size: 1} // invalid rune if p < n { info = fd.info(src, p) if !info.BoundaryBefore() || info.nLeadingNonStarters() > 0 { @@ -235,7 +235,7 @@ func doAppend(rb *reorderBuffer, out []byte, p int) []byte { p = decomposeSegment(rb, p, true) } } - if info.size == 0 { + if info.isInvalid() { rb.doFlush() // Append incomplete UTF-8 encoding. return src.appendSlice(rb.out, p, n) @@ -314,7 +314,7 @@ func (f *formInfo) quickSpan(src input, i, end int, atEOF bool) (n int, ok bool) continue } info := f.info(src, i) - if info.size == 0 { + if info.isInvalid() { if atEOF { // include incomplete runes return n, true @@ -379,7 +379,7 @@ func (f Form) firstBoundary(src input, nsrc int) int { // CGJ insertion points correctly. Luckily it doesn't have to. for { info := fd.info(src, i) - if info.size == 0 { + if info.isInvalid() { return -1 } if s := ss.next(info); s != ssSuccess { @@ -424,7 +424,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int { } fd := formTable[f] info := fd.info(src, 0) - if info.size == 0 { + if info.isInvalid() { if atEOF { return 1 } @@ -435,7 +435,7 @@ func (f Form) nextBoundary(src input, nsrc int, atEOF bool) int { for i := int(info.size); i < nsrc; i += int(info.size) { info = fd.info(src, i) - if info.size == 0 { + if info.isInvalid() { if atEOF { return i } @@ -465,7 +465,7 @@ func lastBoundary(fd *formInfo, b []byte) int { if p == -1 { return -1 } - if info.size == 0 { // ends with incomplete rune + if info.isInvalid() { // ends with incomplete rune if p == 0 { // starts with incomplete rune return -1 } @@ -504,7 +504,7 @@ func lastBoundary(fd *formInfo, b []byte) int { func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int { // Force one character to be consumed. info := rb.f.info(rb.src, sp) - if info.size == 0 { + if info.isInvalid() { return 0 } if s := rb.ss.next(info); s == ssStarter { @@ -528,7 +528,7 @@ func decomposeSegment(rb *reorderBuffer, sp int, atEOF bool) int { break } info = rb.f.info(rb.src, sp) - if info.size == 0 { + if info.isInvalid() { if !atEOF { return int(iShortSrc) } diff --git a/vendor/golang.org/x/tools/go/packages/packages.go b/vendor/golang.org/x/tools/go/packages/packages.go index de683684ab1..1e5549abe4b 100644 --- a/vendor/golang.org/x/tools/go/packages/packages.go +++ b/vendor/golang.org/x/tools/go/packages/packages.go @@ -815,6 +815,12 @@ func (ld *loader) refine(response *DriverResponse) ([]*Package, error) { needsrc: needsrc, goVersion: response.GoVersion, } + // Don't trust the driver to respond with duplicate-free + // package names (go.dev/issue/63822). + if _, ok := ld.pkgs[lpkg.ID]; ok { + return nil, fmt.Errorf("%s response contained duplicate packages for ID %q", + cond(ld.externalDriver, "go/packages driver", "go list"), lpkg.ID) + } ld.pkgs[lpkg.ID] = lpkg if rootIndex >= 0 { initial[rootIndex] = lpkg @@ -1589,3 +1595,11 @@ func usesExportData(cfg *Config) bool { } type unit struct{} + +func cond[T any](cond bool, t, f T) T { + if cond { + return t + } else { + return f + } +} diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go index 4c9450f4eed..686f171d3bd 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iexport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iexport.go @@ -823,6 +823,9 @@ func (p *iexporter) doDecl(obj types.Object) { w.pos(m.Pos()) w.string(m.Name()) sig, _ := m.Type().(*types.Signature) + if w.p.version >= iexportVersionGenericMethods && w.bool(sig.TypeParams().Len() > 0) { + w.tparamList(obj.Name()+"."+m.Name(), sig.TypeParams(), obj.Pkg()) + } // Receiver type parameters are type arguments of the receiver type, so // their name must be qualified before exporting recv. diff --git a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go index 1ee4e935491..7b1723e0fc7 100644 --- a/vendor/golang.org/x/tools/internal/gcimporter/iimport.go +++ b/vendor/golang.org/x/tools/internal/gcimporter/iimport.go @@ -48,13 +48,14 @@ func (r *intReader) uint64() uint64 { // Keep this in sync with constants in iexport.go. const ( - iexportVersionGo1_11 = 0 - iexportVersionPosCol = 1 - iexportVersionGo1_18 = 2 - iexportVersionGenerics = 2 - iexportVersion = iexportVersionGenerics - - iexportVersionCurrent = 2 + iexportVersionGo1_11 = 0 + iexportVersionPosCol = 1 + iexportVersionGo1_18 = 2 + iexportVersionGenerics = 2 + iexportVersionGenericMethods = 3 + iexportVersion = iexportVersionGenericMethods + + iexportVersionCurrent = 3 ) type ident struct { @@ -179,9 +180,9 @@ func iimportCommon(fset *token.FileSet, getPackages GetPackagesFunc, data []byte version = int64(r.uint64()) switch version { - case iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: + case iexportVersionGenericMethods, iexportVersionGo1_18, iexportVersionPosCol, iexportVersionGo1_11: default: - if version > iexportVersionGo1_18 { + if version > iexportVersionGenericMethods { errorf("unstable iexport format version %d, just rebuild compiler and std library", version) } else { errorf("unknown iexport format version %d", version) @@ -614,6 +615,10 @@ func (r *importReader) obj(pkg *types.Package, name string) { for n := r.uint64(); n > 0; n-- { mpos := r.pos() mname := r.ident() + var tpars []*types.TypeParam + if r.p.version >= iexportVersionGenericMethods && r.bool() { + tpars = r.tparamList() + } recv := r.param(pkg) // If the receiver has any targs, set those as the @@ -628,8 +633,7 @@ func (r *importReader) obj(pkg *types.Package, name string) { rparams[i] = types.Unalias(targs.At(i)).(*types.TypeParam) } } - msig := r.signature(pkg, recv, rparams, nil) - + msig := r.signature(pkg, recv, rparams, tpars) named.AddMethod(types.NewFunc(mpos, pkg, mname, msig)) } } diff --git a/vendor/golang.org/x/tools/internal/imports/fix.go b/vendor/golang.org/x/tools/internal/imports/fix.go index d29ecfb0178..b99ea6c9146 100644 --- a/vendor/golang.org/x/tools/internal/imports/fix.go +++ b/vendor/golang.org/x/tools/internal/imports/fix.go @@ -273,7 +273,6 @@ func (p *pass) loadPackageNames(ctx context.Context, imports []*ImportInfo) erro } unknown = append(unknown, imp.ImportPath) } - names, err := p.source.LoadPackageNames(ctx, p.srcDir, unknown) if err != nil { return err diff --git a/vendor/golang.org/x/tools/internal/imports/imports.go b/vendor/golang.org/x/tools/internal/imports/imports.go index b5f5218b5cc..072293835d9 100644 --- a/vendor/golang.org/x/tools/internal/imports/imports.go +++ b/vendor/golang.org/x/tools/internal/imports/imports.go @@ -74,6 +74,10 @@ func Process(filename string, src []byte, opt *Options) (formatted []byte, err e // Note that filename's directory influences which imports can be chosen, // so it is important that filename be accurate. func FixImports(ctx context.Context, filename string, src []byte, goroot string, logf func(string, ...any), source Source) (fixes []*ImportFix, err error) { + if source == nil { + // In case someone adds a defective call from a new place + panic("source is nil") + } ctx, done := event.Start(ctx, "imports.FixImports") defer done() diff --git a/vendor/golang.org/x/tools/internal/stdlib/deps.go b/vendor/golang.org/x/tools/internal/stdlib/deps.go index dacfc1dfffc..0f73c719cd1 100644 --- a/vendor/golang.org/x/tools/internal/stdlib/deps.go +++ b/vendor/golang.org/x/tools/internal/stdlib/deps.go @@ -12,366 +12,386 @@ type pkginfo struct { } var deps = [...]pkginfo{ - {"archive/tar", "\x03q\x03F=\x01\n\x01$\x01\x01\x02\x05\b\x02\x01\x02\x02\r"}, - {"archive/zip", "\x02\x04g\a\x03\x13\x021=\x01+\x05\x01\x0f\x03\x02\x0f\x04"}, - {"bufio", "\x03q\x86\x01D\x15"}, - {"bytes", "t+[\x03\fH\x02\x02"}, + {"archive/tar", "\x03{\x03F>\x01\n\x01&\x01\x01\x02\x05\b\x02\x01\x02\x02\r"}, + {"archive/zip", "\x02\x04j\x0e\x03\x12\x022>\x01-\x05\x01\x0f\x03\x02\x0f\x04"}, + {"bufio", "\x03{\x87\x01F\x15"}, + {"bytes", "~*]\x03\fJ\x02\x02"}, {"cmp", ""}, - {"compress/bzip2", "\x02\x02\xf6\x01A"}, - {"compress/flate", "\x02r\x03\x83\x01\f\x033\x01\x03"}, - {"compress/gzip", "\x02\x04g\a\x03\x15nU"}, - {"compress/lzw", "\x02r\x03\x83\x01"}, - {"compress/zlib", "\x02\x04g\a\x03\x13\x01o"}, - {"container/heap", "\xbc\x02"}, + {"compress/bzip2", "\x02\x02\x81\x02C"}, + {"compress/flate", "\x02|\x03\x84\x01\f\x034\x02\x03"}, + {"compress/gzip", "\x02\x04j\x0e\x03\x14pW"}, + {"compress/lzw", "\x02|\x03\x84\x01"}, + {"compress/zlib", "\x02\x04j\x0e\x03\x12\x01q"}, + {"container/heap", "\xc9\x02"}, {"container/list", ""}, {"container/ring", ""}, - {"context", "t\\p\x01\x0e"}, - {"crypto", "\x8a\x01pC"}, - {"crypto/aes", "\x10\v\t\x99\x02"}, - {"crypto/cipher", "\x03!\x01\x01 \x12\x1c,Z"}, - {"crypto/des", "\x10\x16 .,\x9d\x01\x03"}, - {"crypto/dsa", "F\x03+\x86\x01\r"}, - {"crypto/ecdh", "\x03\v\r\x10\x04\x17\x03\x0f\x1c\x86\x01"}, - {"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x10\b\v\x06\x01\x03\x0e\x01\x1c\x86\x01\r\x05L\x01"}, - {"crypto/ed25519", "\x0e\x1f\x12\a\x03\b\a\x1cI=C"}, - {"crypto/elliptic", "4@\x86\x01\r9"}, - {"crypto/fips140", "#\x05\x95\x01\x98\x01"}, - {"crypto/hkdf", "0\x15\x01.\x16"}, - {"crypto/hmac", "\x1b\x16\x14\x01\x122"}, - {"crypto/hpke", "\x03\v\x02\x03\x04\x01\f\x01\x05\x1f\x05\a\x01\x01\x1d\x03\x13\x16\x9b\x01\x1c"}, - {"crypto/internal/boring", "\x0e\x02\x0el"}, - {"crypto/internal/boring/bbig", "\x1b\xec\x01N"}, - {"crypto/internal/boring/bcache", "\xc1\x02\x14"}, + {"context", "~]r\x01\x0e"}, + {"crypto", "\x93\x01rE"}, + {"crypto/aes", "\x10\v\n\xa5\x02"}, + {"crypto/cipher", "\x03\"\x01\x01 \x13$+\\"}, + {"crypto/des", "\x10\x17 7+\xa1\x01\x03"}, + {"crypto/dsa", "G\x034\x87\x01\r"}, + {"crypto/ecdh", "\x03\v\r\x11\x04\x17\x03\x10$\x87\x01"}, + {"crypto/ecdsa", "\x0e\x05\x03\x05\x01\x11\b\v\x06\x01\x03\x0f\x01$\x87\x01\r\x05O\x01"}, + {"crypto/ed25519", "\x0e \x12\a\x03\t\a$I>E"}, + {"crypto/elliptic", "5I\x87\x01\r;"}, + {"crypto/fips140", "$\x05\x9e\x01\x9b\x01"}, + {"crypto/hkdf", "1\x15\x017\x15"}, + {"crypto/hmac", "\x1b\x17\x14\x01\x139"}, + {"crypto/hpke", "\x03\v\x02\x03\x04\x01\r\x01\x05\x1f\x06\a\x01\x01%\x03\x12\x16\x9f\x01\x1d"}, + {"crypto/internal/boring", "\x0e\x02\x0eu"}, + {"crypto/internal/boring/bbig", "\x1b\xf7\x01P"}, + {"crypto/internal/boring/bcache", "\xce\x02\x14"}, {"crypto/internal/boring/sig", ""}, {"crypto/internal/constanttime", ""}, - {"crypto/internal/cryptotest", "\x03\r\v\b%\x10\x19\x06\x13\x12 \x04\x06\t\x19\x01\x11\x11\x1b\x01\a\x05\b\x03\x05\f"}, - {"crypto/internal/entropy", "K"}, - {"crypto/internal/entropy/v1.0.0", "D0\x95\x018\x14"}, - {"crypto/internal/fips140", "C1\xbf\x01\v\x17"}, - {"crypto/internal/fips140/aes", "\x03 \x03\x02\x14\x05\x01\x01\x05,\x95\x014"}, - {"crypto/internal/fips140/aes/gcm", "#\x01\x02\x02\x02\x12\x05\x01\x06,\x92\x01"}, - {"crypto/internal/fips140/alias", "\xd5\x02"}, - {"crypto/internal/fips140/bigmod", "(\x19\x01\x06,\x95\x01"}, - {"crypto/internal/fips140/check", "#\x0e\a\t\x02\xb7\x01["}, - {"crypto/internal/fips140/check/checktest", "(\x8b\x02\""}, - {"crypto/internal/fips140/drbg", "\x03\x1f\x01\x01\x04\x14\x05\n)\x86\x01\x0f7\x01"}, - {"crypto/internal/fips140/ecdh", "\x03 \x05\x02\n\r3\x86\x01\x0f7"}, - {"crypto/internal/fips140/ecdsa", "\x03 \x04\x01\x02\a\x03\x06:\x16pF"}, - {"crypto/internal/fips140/ed25519", "\x03 \x05\x02\x04\f:\xc9\x01\x03"}, - {"crypto/internal/fips140/edwards25519", "\x1f\t\a\x123\x95\x017"}, - {"crypto/internal/fips140/edwards25519/field", "(\x14\x053\x95\x01"}, - {"crypto/internal/fips140/hkdf", "\x03 \x05\t\a<\x16"}, - {"crypto/internal/fips140/hmac", "\x03 \x15\x01\x01:\x16"}, - {"crypto/internal/fips140/mldsa", "\x03\x1c\x04\x05\x02\x0e\x01\x03\x053\x95\x017"}, - {"crypto/internal/fips140/mlkem", "\x03 \x05\x02\x0f\x03\x053\xcc\x01"}, - {"crypto/internal/fips140/nistec", "\x1f\t\r\f3\x95\x01*\r\x15"}, - {"crypto/internal/fips140/nistec/fiat", "(\x148\x95\x01"}, - {"crypto/internal/fips140/pbkdf2", "\x03 \x05\t\a<\x16"}, - {"crypto/internal/fips140/rsa", "\x03\x1c\x04\x04\x01\x02\x0e\x01\x01\x028\x16pF"}, - {"crypto/internal/fips140/sha256", "\x03 \x1e\x01\x06,\x16\x7f"}, - {"crypto/internal/fips140/sha3", "\x03 \x19\x05\x012\x95\x01L"}, - {"crypto/internal/fips140/sha512", "\x03 \x1e\x01\x06,\x16\x7f"}, - {"crypto/internal/fips140/ssh", "(b"}, - {"crypto/internal/fips140/subtle", "\x1f\a\x1b\xc8\x01"}, - {"crypto/internal/fips140/tls12", "\x03 \x05\t\a\x02:\x16"}, - {"crypto/internal/fips140/tls13", "\x03 \x05\b\b\t3\x16"}, - {"crypto/internal/fips140cache", "\xb3\x02\r'"}, + {"crypto/internal/cryptotest", "\x03\r\v\t%\x11\x1a\r\x12\x12!\x04\x06\n\x19\x01\x11\x11\x1d\x01\a\x03\x02\b\x02\x01\x05\f"}, + {"crypto/internal/cryptotest/wycheproof", "\x0e\x12S\x01\f\x01r@\x05\x03\x15\x10"}, + {"crypto/internal/entropy", "L"}, + {"crypto/internal/entropy/v1.0.0", "E9\x96\x01:\x14"}, + {"crypto/internal/fips140", "D:\xc2\x01\v\x17"}, + {"crypto/internal/fips140/aes", "\x03!\x03\x02\x14\x05\x01\x01\x055\x96\x016"}, + {"crypto/internal/fips140/aes/gcm", "$\x01\x02\x02\x02\x12\x05\x01\x065\x93\x01"}, + {"crypto/internal/fips140/alias", "\xe2\x02"}, + {"crypto/internal/fips140/bigmod", ")\x19\x01\x065\x96\x01"}, + {"crypto/internal/fips140/check", "$\x0e\a\t\x02\xc1\x01]"}, + {"crypto/internal/fips140/check/checktest", ")\x97\x02\""}, + {"crypto/internal/fips140/drbg", "\x03 \x01\x01\x04\x14\x05\n2\x87\x01\x0f9\x01"}, + {"crypto/internal/fips140/ecdh", "\x03!\x05\x02\n\r<\x87\x01\x0f9"}, + {"crypto/internal/fips140/ecdsa", "\x03!\x04\x01\x02\a\x03\x06C\x15r\x0f9"}, + {"crypto/internal/fips140/ed25519", "\x03!\x05\x02\x04\fC\xcc\x01\x03"}, + {"crypto/internal/fips140/edwards25519", "\x1f\n\a\x12<\x96\x019"}, + {"crypto/internal/fips140/edwards25519/field", ")\x14\x05<\x96\x01"}, + {"crypto/internal/fips140/hkdf", "\x03!\x05\t\aE\x15"}, + {"crypto/internal/fips140/hmac", "\x03!\x15\x01\x01C\x15"}, + {"crypto/internal/fips140/mldsa", "\x03\x1c\x05\x05\x02\x0e\x01\x03\x05<\x96\x019"}, + {"crypto/internal/fips140/mlkem", "\x03!\x05\x02\x0f\x03\x05<\xcf\x01"}, + {"crypto/internal/fips140/nistec", "\x1f\n\r\f<\x96\x01,\r\x15"}, + {"crypto/internal/fips140/nistec/fiat", ")\x14A\x96\x01"}, + {"crypto/internal/fips140/pbkdf2", "\x03!\x05\t\aE\x15"}, + {"crypto/internal/fips140/rsa", "\x03\x1c\x05\x04\x01\x02\x0e\x01\x01\x02A\x15rH"}, + {"crypto/internal/fips140/sha256", "\x03!\x1e\x01\x065\x15\x81\x01"}, + {"crypto/internal/fips140/sha3", "\x03!\x19\x05\x01;\x96\x01N"}, + {"crypto/internal/fips140/sha512", "\x03!\x1e\x01\x065\x15\x81\x01"}, + {"crypto/internal/fips140/ssh", ")j"}, + {"crypto/internal/fips140/subtle", "\x1f\b\x1b\xd2\x01"}, + {"crypto/internal/fips140/tls12", "\x03!\x05\t\a\x02C\x15"}, + {"crypto/internal/fips140/tls13", "\x03!\x05\b\b\t<\x15"}, + {"crypto/internal/fips140cache", "\xc0\x02\r."}, {"crypto/internal/fips140deps", ""}, - {"crypto/internal/fips140deps/byteorder", "\xa0\x01"}, - {"crypto/internal/fips140deps/cpu", "\xb5\x01\a"}, - {"crypto/internal/fips140deps/godebug", "\xbd\x01"}, - {"crypto/internal/fips140deps/time", "\xcf\x02"}, - {"crypto/internal/fips140hash", "9\x1d4\xcb\x01"}, - {"crypto/internal/fips140only", "\x17\x13\x0e\x01\x01Pp"}, + {"crypto/internal/fips140deps/byteorder", "\xa9\x01"}, + {"crypto/internal/fips140deps/cpu", "\xbe\x01\b"}, + {"crypto/internal/fips140deps/godebug", "\xc7\x01"}, + {"crypto/internal/fips140deps/time", "\xe2\x02"}, + {"crypto/internal/fips140hash", ":\x1e;\xcf\x01"}, + {"crypto/internal/fips140only", "\x17\x14\x0e\x01\x01Xr"}, {"crypto/internal/fips140test", ""}, - {"crypto/internal/impl", "\xbe\x02"}, - {"crypto/internal/rand", "\x1b\x0f s=["}, - {"crypto/internal/randutil", "\xfa\x01\x12"}, - {"crypto/internal/sysrand", "tq! \r\r\x01\x01\r\x06"}, - {"crypto/internal/sysrand/internal/seccomp", "t"}, - {"crypto/md5", "\x0e8.\x16\x16i"}, - {"crypto/mlkem", "\x0e%"}, - {"crypto/mlkem/mlkemtest", "3\x13\b&"}, - {"crypto/pbkdf2", "6\x0f\x01.\x16"}, - {"crypto/rand", "\x1b\x0f\x1c\x03+\x86\x01\rN"}, - {"crypto/rc4", "& .\xc9\x01"}, - {"crypto/rsa", "\x0e\r\x01\v\x10\x0e\x01\x03\b\a\x1c\x03\x133=\f\x01"}, - {"crypto/sha1", "\x0e\r+\x02,\x16\x16\x15T"}, - {"crypto/sha256", "\x0e\r\x1dR"}, - {"crypto/sha3", "\x0e+Q\xcb\x01"}, - {"crypto/sha512", "\x0e\r\x1fP"}, - {"crypto/subtle", "\x1f\x1d\x9f\x01z"}, - {"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x02\x01\x01\x01\t\x01\x18\x01\x0f\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x17\x02\x03\x13\x16\x15\b=\x16\x16\r\b\x01\x01\x01\x02\x01\x0e\x06\x02\x01\x0f"}, - {"crypto/tls/internal/fips140tls", "\x17\xaa\x02"}, - {"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x017\x06\x01\x01\x02\x05\x0e\x06\x02\x02\x03F\x03:\x01\x02\b\x01\x01\x02\a\x10\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x03\x01"}, - {"crypto/x509/pkix", "j\x06\a\x90\x01H"}, - {"database/sql", "\x03\nQ\x16\x03\x83\x01\v\a\"\x05\b\x02\x03\x01\x0e\x02\x02\x02"}, - {"database/sql/driver", "\rg\x03\xb7\x01\x0f\x12"}, - {"debug/buildinfo", "\x03^\x02\x01\x01\b\a\x03g\x1a\x02\x01+\x0f "}, - {"debug/dwarf", "\x03j\a\x03\x83\x011\x11\x01\x01"}, - {"debug/elf", "\x03\x06W\r\a\x03g\x1b\x01\f \x17\x01\x17"}, - {"debug/gosym", "\x03j\n$\xa1\x01\x01\x01\x02"}, - {"debug/macho", "\x03\x06W\r\ng\x1c,\x17\x01"}, - {"debug/pe", "\x03\x06W\r\a\x03g\x1c,\x17\x01\x17"}, - {"debug/plan9obj", "m\a\x03g\x1c,"}, - {"embed", "t+B\x19\x01T"}, + {"crypto/internal/impl", "\xcb\x02"}, + {"crypto/internal/rand", "\x1b\x10 |>]"}, + {"crypto/internal/randutil", "\x85\x02\x12"}, + {"crypto/internal/sysrand", "~r!\"\r\r\x01\x01\r\x06"}, + {"crypto/internal/sysrand/internal/seccomp", "~"}, + {"crypto/md5", "\x0e97\x15\x16k"}, + {"crypto/mldsa", "\x0e%K\x87\x01"}, + {"crypto/mlkem", "\x0e&"}, + {"crypto/mlkem/mlkemtest", "4\x13\t."}, + {"crypto/pbkdf2", "7\x0f\x017\x15"}, + {"crypto/rand", "\x1b\x10\x1c\x034\x87\x01\rP"}, + {"crypto/rc4", "' 7\xcc\x01"}, + {"crypto/rsa", "\x0e\r\x01\f\x10\x0e\x01\x03\t\a$\x03\x124>\f\x01"}, + {"crypto/sha1", "\x0e\r,\x025\x15\x16\x15V"}, + {"crypto/sha256", "\x0e\r\x1eZ"}, + {"crypto/sha3", "\x0e,Y\xcf\x01"}, + {"crypto/sha512", "\x0e\r X"}, + {"crypto/subtle", "\x1f\x1e\xa9\x01|"}, + {"crypto/tls", "\x03\b\x02\x01\x01\x01\x01\x02\x01\x01\x01\x01\x01\x01\x01\x01\n\x01\x18\x01\x0f\x01\x01\x03\x01\x01\x01\x01\x02\x01\x02\x01\x1f\x02\x03\x12\x16\x15\t>\x16\x18\r\b\x01\x01\x01\x02\x01\x0e\x06\x03\x01\x15"}, + {"crypto/tls/internal/fips140tls", "\x17\xb7\x02"}, + {"crypto/x509", "\x03\v\x01\x01\x01\x01\x01\x01\x01\x01\x017\x01\x06\x01\x01\x02\x05\x0f\x06\t\x02\x03F\x03;\x01\x02\b\x01\x01\x02\a\x12\x05\x01\x06\a\b\x02\x01\x02\x0f\x02\x01\x01\x02\x04\x01"}, + {"crypto/x509/pkix", "m\x06\x0e\x91\x019\x11"}, + {"database/sql", "\x03\nS\x01\x1d\x03\x84\x01\v\a$\x05\b\x02\x03\x01\x0e\x02\x02\x02\x01"}, + {"database/sql/driver", "\rT\x1d\x03\xba\x01\x0f\x12\a"}, + {"database/sql/internal", ""}, + {"debug/buildinfo", "\x03a\x02\x01\x01\b\x0e\x03h\x1a\x02\x01-\x0f "}, + {"debug/dwarf", "\x03m\x0e\x03\x84\x013\x11\x01\x01"}, + {"debug/elf", "\x03\x06Z\r\x0e\x03h\x1b\x01\f\"\x17\x01\x17"}, + {"debug/gosym", "\x03m\x11#\xa5\x01\x01\x01\x02"}, + {"debug/macho", "\x03\x06Z\r\x11h\x1c.\x17\x01"}, + {"debug/pe", "\x03\x06Z\r\x0e\x03h\x1c.\x17\x01\x17"}, + {"debug/plan9obj", "p\x0e\x03h\x1c."}, + {"embed", "~*D\x19\x01V"}, {"embed/internal/embedtest", ""}, {"encoding", ""}, - {"encoding/ascii85", "\xfa\x01C"}, - {"encoding/asn1", "\x03q\x03g(\x01'\r\x02\x01\x11\x03\x01"}, - {"encoding/base32", "\xfa\x01A\x02"}, - {"encoding/base64", "\xa0\x01ZA\x02"}, - {"encoding/binary", "t\x86\x01\f(\r\x05"}, - {"encoding/csv", "\x02\x01q\x03\x83\x01D\x13\x02"}, - {"encoding/gob", "\x02f\x05\a\x03g\x1c\v\x01\x03\x1d\b\x12\x01\x10\x02"}, - {"encoding/hex", "t\x03\x83\x01A\x03"}, - {"encoding/json", "\x03\x01d\x04\b\x03\x83\x01\f(\r\x02\x01\x02\x11\x01\x01\x02"}, - {"encoding/pem", "\x03i\b\x86\x01A\x03"}, - {"encoding/xml", "\x02\x01e\f\x03\x83\x014\x05\n\x01\x02\x11\x02"}, - {"errors", "\xd0\x01\x85\x01"}, - {"expvar", "qLA\b\v\x15\r\b\x02\x03\x01\x12"}, - {"flag", "h\f\x03\x83\x01,\b\x05\b\x02\x01\x11"}, - {"fmt", "tF'\x19\f \b\r\x02\x03\x13"}, - {"go/ast", "\x03\x01s\x0f\x01s\x03)\b\r\x02\x01\x13\x02"}, - {"go/build", "\x02\x01q\x03\x01\x02\x02\b\x02\x01\x17\x1f\x04\x02\b\x1c\x13\x01+\x01\x04\x01\a\b\x02\x01\x13\x02\x02"}, - {"go/build/constraint", "t\xc9\x01\x01\x13\x02"}, - {"go/constant", "w\x10\x7f\x01\x024\x01\x02\x13"}, - {"go/doc", "\x04s\x01\x05\n=61\x10\x02\x01\x13\x02"}, - {"go/doc/comment", "\x03t\xc4\x01\x01\x01\x01\x13\x02"}, - {"go/format", "\x03t\x01\f\x01\x02sD"}, - {"go/importer", "y\a\x01\x02\x04\x01r9"}, - {"go/internal/gccgoimporter", "\x02\x01^\x13\x03\x04\f\x01p\x02,\x01\x05\x11\x01\r\b"}, - {"go/internal/gcimporter", "\x02u\x10\x010\x05\r0,\x15\x03\x02"}, - {"go/internal/scannerhooks", "\x87\x01"}, - {"go/internal/srcimporter", "w\x01\x01\v\x03\x01r,\x01\x05\x12\x02\x15"}, - {"go/parser", "\x03q\x03\x01\x02\b\x04\x01s\x01+\x06\x12"}, - {"go/printer", "w\x01\x02\x03\ns\f \x15\x02\x01\x02\f\x05\x02"}, - {"go/scanner", "\x03t\v\x05s2\x10\x01\x14\x02"}, - {"go/token", "\x04s\x86\x01>\x02\x03\x01\x10\x02"}, - {"go/types", "\x03\x01\x06j\x03\x01\x03\t\x03\x024\x063\x04\x03\t \x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"}, - {"go/version", "\xc2\x01|"}, - {"hash", "\xfa\x01"}, - {"hash/adler32", "t\x16\x16"}, - {"hash/crc32", "t\x16\x16\x15\x8b\x01\x01\x14"}, - {"hash/crc64", "t\x16\x16\xa0\x01"}, - {"hash/fnv", "t\x16\x16i"}, - {"hash/maphash", "\x8a\x01\x11<~"}, - {"html", "\xbe\x02\x02\x13"}, - {"html/template", "\x03n\x06\x19-=\x01\n!\x05\x01\x02\x03\f\x01\x02\r\x01\x03\x02"}, - {"image", "\x02r\x1fg\x0f4\x03\x01"}, + {"encoding/ascii85", "\x85\x02E"}, + {"encoding/asn1", "\x03{\x03h(\x01)\r\x02\x01\x11\x03\x01"}, + {"encoding/base32", "\x85\x02C\x02"}, + {"encoding/base64", "\xa9\x01\\C\x02"}, + {"encoding/binary", "~\x87\x01\f*\r\x05"}, + {"encoding/csv", "\x02\x01{\x03\x84\x01F\x13\x02"}, + {"encoding/gob", "\x02i\x05\x0e\x03h\x1c\v\x01\x03\x1f\b\x12\x01\x10\x02"}, + {"encoding/hex", "~\x03\x84\x01C\x03"}, + {"encoding/json", "\x03\x01g\n\x01\x01\x02\x01\x01\x03\x03\x84\x016\x0f\x01"}, + {"encoding/json/internal", "~"}, + {"encoding/json/internal/jsonflags", "u"}, + {"encoding/json/internal/jsonopts", "u\x01"}, + {"encoding/json/internal/jsontest", "\x03f\x15\x03\x83\x01\x01\x012\b\b\x03\x02\x0f"}, + {"encoding/json/internal/jsonwire", "\x04r\b\x87\x01\f7\x02\x01\x13\x01\x01"}, + {"encoding/json/jsontext", "\x03r\x01\x01\x02\x05\x87\x01\x03\t\x034\x02\x01\x02\x13"}, + {"encoding/json/v2", "\x03\x01g\x03\x01\x01\x03\x02\x01\x01\x02\x01\x04\x03\x84\x01\f\x03'\r\x02\x01\x02\x0f\x02\x02"}, + {"encoding/pem", "\x03l\x0f\x87\x01C\x03"}, + {"encoding/xml", "\x02\x01h\x13\x03\x84\x016\x05\n\x01\x02\x11\x02"}, + {"errors", "\xdb\x01\x87\x01"}, + {"expvar", "tSB\b\v\x17\r\b\x02\x03\x01\x12"}, + {"flag", "k\x13\x03\x84\x01.\b\x05\b\x02\x01\x11"}, + {"fmt", "~E)\x19\f\"\b\r\x02\x03\x13"}, + {"go/ast", "\x03\x01}\x0e\x01u\x03+\b\r\x02\x01\x13\x02"}, + {"go/build", "\x02\x01{\x03\x01\x02\x02\a\x02\x01\x17 \x04\x02\t\x1c\x13\x01-\x01\x04\x01\a\b\x02\x01\x13\x02\x02"}, + {"go/build/constraint", "~\xcc\x01\x01\x13\x02"}, + {"go/constant", "\x81\x01\x0f\x81\x01\x01\x026\x01\x02\x13"}, + {"go/doc", "\x04}\x01\x05\t>73\x10\x02\x01\x13\x02"}, + {"go/doc/comment", "\x03~\xc7\x01\x01\x01\x01\x13\x02"}, + {"go/format", "\x03~\x01\v\x01\x02uF"}, + {"go/importer", "\x83\x01\a\x01\x01\x04\x01t;"}, + {"go/internal/gccgoimporter", "\x02\x01a\x1a\x03\x04\v\x01r\x02.\x01\x05\x11\x01\r\b"}, + {"go/internal/gcimporter", "\x02\x7f\x0f\x010\x140.\x15\x03\x02"}, + {"go/internal/srcimporter", "\x81\x01\x01\x01\n\x03\x01t.\x01\x05\x12\x02\x15"}, + {"go/parser", "\x03{\x03\x01\x02\v\x01u\x01-\x06\x12"}, + {"go/printer", "\x81\x01\x01\x02\x03\tu\f\"\x15\x02\x01\x02\f\x05\x02"}, + {"go/scanner", "\x03~\x0fu4\x10\x01\x14\x02"}, + {"go/token", "\x04}\x87\x01@\x02\x03\x01\x10\x02"}, + {"go/types", "\x03\x01\x06t\x03\x01\x03\b\x03\x02\x0654\x04\x03\t\"\x06\a\b\x01\x01\x01\x02\x01\x10\x02\x02"}, + {"go/version", "\xcc\x01\x7f"}, + {"hash", "\x85\x02"}, + {"hash/adler32", "~\x15\x16"}, + {"hash/crc32", "~\x15\x16\x15\x8f\x01\x01\x14"}, + {"hash/crc64", "~\x15\x16\xa4\x01"}, + {"hash/fnv", "~\x15\x16k"}, + {"hash/maphash", "\x93\x01\x11>\x80\x01"}, + {"html", "\xcb\x02\x02\x13"}, + {"html/template", "\x03q\r\x18.>\x01\n#\x05\x01\x02\x03\n\x02\x01\x02\r\x01\x03\x02"}, + {"image", "\x02|\x1ei\x0f6\x03\x01"}, {"image/color", ""}, - {"image/color/palette", "\x93\x01"}, - {"image/draw", "\x92\x01\x01\x04"}, - {"image/gif", "\x02\x01\x05l\x03\x1b\x01\x01\x01\vZ\x0f"}, - {"image/internal/imageutil", "\x92\x01"}, - {"image/jpeg", "\x02r\x1e\x01\x04c"}, - {"image/png", "\x02\ad\n\x13\x02\x06\x01gC"}, - {"index/suffixarray", "\x03j\a\x86\x01\f+\n\x01"}, - {"internal/abi", "\xbc\x01\x99\x01"}, - {"internal/asan", "\xd5\x02"}, - {"internal/bisect", "\xb3\x02\r\x01"}, - {"internal/buildcfg", "wHg\x06\x02\x05\n\x01"}, - {"internal/bytealg", "\xb5\x01\xa0\x01"}, + {"image/color/palette", "\x9c\x01"}, + {"image/draw", "\x9b\x01\x01\x04"}, + {"image/gif", "\x02\x01\x05v\x03\x1a\x01\x01\x01\v\\\x0f"}, + {"image/internal/imageutil", "\x9b\x01"}, + {"image/jpeg", "\x02|\x1d\x01\x04e"}, + {"image/png", "\x02\ag\x11\x12\x02\x06\x01iE"}, + {"index/suffixarray", "\x03m\x0e\x87\x01\f-\n\x01"}, + {"internal/abi", "\xc6\x01\x9c\x01"}, + {"internal/asan", "\xe2\x02"}, + {"internal/bisect", "\xc0\x02\r\x01"}, + {"internal/buildcfg", "\x81\x01Hj\x06\x02\x05\n\x01"}, + {"internal/bytealg", "\xbe\x01\xa4\x01"}, {"internal/byteorder", ""}, {"internal/cfg", ""}, - {"internal/cgrouptest", "w[T\x06\x0f\x02\x01\x04\x01"}, - {"internal/chacha8rand", "\xa0\x01\x15\a\x99\x01"}, + {"internal/cgrouptest", "\x81\x01\\V\x06\x0f\x02\x01\x04\x01"}, + {"internal/chacha8rand", "\xa9\x01\x15\b\x9c\x01"}, {"internal/copyright", ""}, {"internal/coverage", ""}, {"internal/coverage/calloc", ""}, - {"internal/coverage/cfile", "q\x06\x17\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01\"\x02',\x06\a\n\x01\x03\x0e\x06"}, - {"internal/coverage/cformat", "\x04s.\x04Q\v6\x01\x02\x0e"}, - {"internal/coverage/cmerge", "w.a"}, - {"internal/coverage/decodecounter", "m\n.\v\x02H,\x17\x18"}, - {"internal/coverage/decodemeta", "\x02k\n\x17\x17\v\x02H,"}, - {"internal/coverage/encodecounter", "\x02k\n.\f\x01\x02F\v!\x15"}, - {"internal/coverage/encodemeta", "\x02\x01j\n\x13\x04\x17\r\x02F,/"}, - {"internal/coverage/pods", "\x04s.\x81\x01\x06\x05\n\x02\x01"}, - {"internal/coverage/rtcov", "\xd5\x02"}, - {"internal/coverage/slicereader", "m\n\x83\x01["}, - {"internal/coverage/slicewriter", "w\x83\x01"}, - {"internal/coverage/stringtab", "w9\x04F"}, + {"internal/coverage/cfile", "t\r\x16\x17\x01\x02\x01\x01\x01\x01\x01\x01\x01$\x02'.\x06\a\n\x01\x03\x0e\x06"}, + {"internal/coverage/cformat", "\x04}-\x04S\v8\x01\x02\x0e"}, + {"internal/coverage/cmerge", "\x81\x01-c"}, + {"internal/coverage/decodecounter", "p\x11-\v\x02J.\x17\x18"}, + {"internal/coverage/decodemeta", "\x02n\x11\x16\x17\v\x02J."}, + {"internal/coverage/encodecounter", "\x02n\x11-\f\x01\x02H\v#\x15"}, + {"internal/coverage/encodemeta", "\x02\x01m\x11\x12\x04\x17\r\x02H./"}, + {"internal/coverage/pods", "\x04}-\x85\x01\x06\x05\n\x02\x01"}, + {"internal/coverage/rtcov", "\xe2\x02"}, + {"internal/coverage/slicereader", "p\x11\x84\x01]"}, + {"internal/coverage/slicewriter", "\x81\x01\x84\x01"}, + {"internal/coverage/stringtab", "\x81\x018\x04H"}, {"internal/coverage/test", ""}, {"internal/coverage/uleb128", ""}, - {"internal/cpu", "\xd5\x02"}, - {"internal/dag", "\x04s\xc4\x01\x03"}, - {"internal/diff", "\x03t\xc5\x01\x02"}, - {"internal/exportdata", "\x02\x01q\x03\x02e\x1c,\x01\x05\x11\x01\x02"}, - {"internal/filepathlite", "t+B\x1a@"}, - {"internal/fmtsort", "\x04\xaa\x02\r"}, - {"internal/fuzz", "\x03\nH\x18\x04\x03\x03\x01\f\x036=\f\x03\x1d\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"}, + {"internal/cpu", "\xe2\x02"}, + {"internal/dag", "\x04}\xc7\x01\x03"}, + {"internal/diff", "\x03~\xc8\x01\x02"}, + {"internal/exportdata", "\x02\x01{\x03\x02f\x1c.\x01\x05\x11\x01\x02"}, + {"internal/filepathlite", "~*D\x1aB"}, + {"internal/fmtsort", "\x04\xb7\x02\r"}, + {"internal/fuzz", "\x03\nJ\x19\x04\n\x03\x01\v\x037>\f\x03\x1f\x01\x05\x02\x05\n\x01\x02\x01\x01\r\x04\x02"}, + {"internal/gate", "\r"}, {"internal/goarch", ""}, - {"internal/godebug", "\x9d\x01!\x82\x01\x01\x14"}, + {"internal/godebug", "\xa6\x01\"\x85\x01\x01\x14"}, {"internal/godebugs", ""}, {"internal/goexperiment", ""}, {"internal/goos", ""}, - {"internal/goroot", "\xa6\x02\x01\x05\x12\x02"}, + {"internal/goroot", "\xb3\x02\x01\x05\x12\x02"}, {"internal/gover", "\x04"}, {"internal/goversion", ""}, - {"internal/lazyregexp", "\xa6\x02\v\r\x02"}, - {"internal/lazytemplate", "\xfa\x01,\x18\x02\r"}, - {"internal/msan", "\xd5\x02"}, + {"internal/lazyregexp", "\xb3\x02\v\r\x02"}, + {"internal/lazytemplate", "\x85\x02.\x18\x02\r"}, + {"internal/msan", "\xe2\x02"}, + {"internal/nettest", "\x03\nqG@\f\n\x12\x06\x15\x05\x0f"}, {"internal/nettrace", ""}, - {"internal/obscuretestdata", "l\x8e\x01,"}, - {"internal/oserror", "t"}, - {"internal/pkgbits", "\x03R\x18\a\x03\x04\fs\r\x1f\r\n\x01"}, + {"internal/obscuretestdata", "o\x96\x01."}, + {"internal/oserror", "~"}, + {"internal/pkgbits", "\x03T\x19\x0e\x03\x04\vu\r!\r\n\x01"}, {"internal/platform", ""}, - {"internal/poll", "tl\x05\x159\r\x01\x01\r\x06"}, - {"internal/profile", "\x03\x04m\x03\x83\x017\n\x01\x01\x01\x11"}, + {"internal/poll", "~m\x05\x15;\r\x01\x01\r\x06"}, + {"internal/profile", "\x03\x04w\x03\x84\x019\n\x01\x01\x01\x11"}, {"internal/profilerecord", ""}, - {"internal/race", "\x9b\x01\xba\x01"}, - {"internal/reflectlite", "\x9b\x01!;<\""}, - {"internal/runtime/atomic", "\xbc\x01\x99\x01"}, - {"internal/runtime/cgroup", "\x9f\x01=\x04u"}, - {"internal/runtime/exithook", "\xd1\x01\x84\x01"}, - {"internal/runtime/gc", "\xbc\x01"}, - {"internal/runtime/gc/internal/gen", "\nc\n\x18k\x04\v\x1d\b\x10\x02"}, - {"internal/runtime/gc/scan", "\xb5\x01\a\x18\az"}, - {"internal/runtime/maps", "\x9b\x01\x01 \n\t\t\x03z"}, - {"internal/runtime/math", "\xbc\x01"}, + {"internal/race", "\xa4\x01\xbe\x01"}, + {"internal/reflectlite", "\xa4\x01\"<>\""}, + {"internal/runtime/atomic", "\xc6\x01\x9c\x01"}, + {"internal/runtime/cgroup", "\xa8\x01?\x04w"}, + {"internal/runtime/exithook", "\xdc\x01\x86\x01"}, + {"internal/runtime/gc", "\xc6\x01"}, + {"internal/runtime/gc/internal/gen", "\nf\x11\x17m\x04\v\x1f\b\x10\x02"}, + {"internal/runtime/gc/scan", "\xbe\x01\b\x19\a|"}, + {"internal/runtime/maps", "\xa4\x01\x01\x04\x15\b\x03\a\n\t\x03.N"}, + {"internal/runtime/math", "\xc6\x01"}, {"internal/runtime/pprof/label", ""}, {"internal/runtime/startlinetest", ""}, - {"internal/runtime/sys", "\xbc\x01\x04"}, - {"internal/runtime/syscall/linux", "\xbc\x01\x99\x01"}, + {"internal/runtime/sys", "\xc6\x01\x04"}, + {"internal/runtime/syscall/linux", "\xc6\x01\x9c\x01"}, {"internal/runtime/wasitest", ""}, - {"internal/saferio", "\xfa\x01["}, - {"internal/singleflight", "\xc0\x02"}, - {"internal/strconv", "\x89\x02L"}, - {"internal/stringslite", "\x9f\x01\xb6\x01"}, - {"internal/sync", "\x9b\x01!\x13r\x14"}, - {"internal/synctest", "\x9b\x01\xba\x01"}, - {"internal/syscall/execenv", "\xc2\x02"}, - {"internal/syscall/unix", "\xb3\x02\x0e\x01\x13"}, - {"internal/sysinfo", "\x02\x01\xb2\x01E,\x18\x02"}, + {"internal/saferio", "\x85\x02]"}, + {"internal/singleflight", "\xcd\x02"}, + {"internal/strconv", "\x94\x02N"}, + {"internal/stringslite", "\xa8\x01\xba\x01"}, + {"internal/sync", "\xa4\x01\"\x14t\x14"}, + {"internal/synctest", "\xa4\x01\xbe\x01"}, + {"internal/syscall/execenv", "\xcf\x02"}, + {"internal/syscall/unix", "\xeb\x01U\x0e\x01\x13"}, + {"internal/sysinfo", "\x02\x01\xbb\x01G.\x18\x02"}, {"internal/syslist", ""}, - {"internal/testenv", "\x03\ng\x02\x01*\x1b\x0f0+\x01\x05\a\n\x01\x02\x02\x01\f"}, - {"internal/testhash", "\x03\x87\x01p\x118\f"}, - {"internal/testlog", "\xc0\x02\x01\x14"}, - {"internal/testpty", "t\x03\xaf\x01"}, - {"internal/trace", "\x02\x01\x01\x06c\a\x03w\x03\x03\x06\x03\t+\n\x01\x01\x01\x11\x06"}, - {"internal/trace/internal/testgen", "\x03j\nu\x03\x02\x03\x011\v\r\x11"}, - {"internal/trace/internal/tracev1", "\x03\x01i\a\x03}\x06\f5\x01"}, - {"internal/trace/raw", "\x02k\nz\x03\x06C\x01\x13"}, - {"internal/trace/testtrace", "\x02\x01q\x03q\x04\x03\x05\x01\x05,\v\x02\b\x02\x01\x05"}, + {"internal/testenv", "\x03\nq\x02\x01)\x1c\x100-\x01\x05\a\n\x01\x02\x02\x01\f"}, + {"internal/testhash", "\x03\x90\x01r\x11:\f"}, + {"internal/testlog", "\xcd\x02\x01\x14"}, + {"internal/testpty", "~\x03\xb2\x01"}, + {"internal/trace", "\x02\x01\x01\x06f\x0e\x03x\x03\x03\x06\x03\t-\n\x01\x01\x01\x11\x06"}, + {"internal/trace/internal/testgen", "\x03m\x11v\x03\x02\x03\x013\v\r\x11"}, + {"internal/trace/internal/tracev1", "\x03\x01l\x0e\x03~\x06\f7\x01"}, + {"internal/trace/raw", "\x02n\x11{\x03\x06E\x01\x13"}, + {"internal/trace/testtrace", "\x02\x01{\x03r\x04\x03\x05\x01\x05.\v\x02\b\x02\x01\x05"}, {"internal/trace/tracev2", ""}, - {"internal/trace/traceviewer", "\x02d\v\x06\x1a<\x1f\a\a\x04\b\v\x15\x01\x05\a\n\x01\x02\x0f"}, + {"internal/trace/traceviewer", "\x02g\v\r\x19>\x1f\a\a\x04\b\v\x17\x01\x05\a\n\x01\x02\x0f"}, {"internal/trace/traceviewer/format", ""}, - {"internal/trace/version", "wz\t"}, - {"internal/txtar", "\x03t\xaf\x01\x18"}, - {"internal/types/errors", "\xbd\x02"}, - {"internal/unsafeheader", "\xd5\x02"}, - {"internal/xcoff", "`\r\a\x03g\x1c,\x17\x01"}, - {"internal/zstd", "m\a\x03\x83\x01\x0f"}, - {"io", "t\xcc\x01"}, - {"io/fs", "t+*11\x10\x14\x04"}, - {"io/ioutil", "\xfa\x01\x01+\x15\x03"}, - {"iter", "\xcf\x01d\""}, - {"log", "w\x83\x01\x05'\r\r\x01\x0e"}, + {"internal/trace/version", "\x81\x01{\t"}, + {"internal/txtar", "\x03~\xb2\x01\x18"}, + {"internal/types/errors", "\xca\x02"}, + {"internal/unsafeheader", "\xe2\x02"}, + {"internal/xcoff", "c\r\x0e\x03h\x1c.\x17\x01"}, + {"internal/zstd", "p\x0e\x03\x84\x01\x0f"}, + {"io", "~\xcf\x01"}, + {"io/fs", "~*,13\x10\x14\x04"}, + {"io/ioutil", "\x85\x02\x01-\x15\x03"}, + {"iter", "\xda\x01f\""}, + {"log", "\x81\x01\x84\x01\x05)\r\r\x01\x0e"}, {"log/internal", ""}, - {"log/slog", "\x03\n[\t\x03\x03\x83\x01\x04\x01\x02\x02\x03(\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"}, + {"log/slog", "\x03\n^\t\n\x03H<\x04\x01\x02\x02\x03*\x05\b\x02\x01\x02\x01\x0e\x02\x02\x02"}, {"log/slog/internal", ""}, - {"log/slog/internal/benchmarks", "\rg\x03\x83\x01\x06\x03:\x12"}, - {"log/slog/internal/buffer", "\xc0\x02"}, - {"log/syslog", "t\x03\x87\x01\x12\x16\x18\x02\x0f"}, - {"maps", "\xfd\x01X"}, - {"math", "\xb5\x01TL"}, - {"math/big", "\x03q\x03)\x15E\f\x03\x020\x02\x01\x02\x15"}, - {"math/big/internal/asmgen", "\x03\x01s\x92\x012\x03"}, - {"math/bits", "\xd5\x02"}, - {"math/cmplx", "\x86\x02\x03"}, - {"math/rand", "\xbd\x01I:\x01\x14"}, - {"math/rand/v2", "t,\x03c\x03L"}, - {"mime", "\x02\x01i\b\x03\x83\x01\v!\x15\x03\x02\x11\x02"}, - {"mime/multipart", "\x02\x01N#\x03F=\v\x01\a\x02\x15\x02\x06\x0f\x02\x01\x17"}, - {"mime/quotedprintable", "\x02\x01t\x83\x01"}, - {"net", "\x04\tg+\x1e\n\x05\x13\x01\x01\x04\x15\x01%\x06\r\b\x05\x01\x01\r\x06\a"}, - {"net/http", "\x02\x01\x03\x01\x04\x02D\b\x13\x01\a\x03F=\x01\x03\a\x01\x03\x02\x02\x01\x02\x06\x02\x01\x01\n\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\b\x01\x01\x01"}, - {"net/http/cgi", "\x02W\x1b\x03\x83\x01\x04\a\v\x01\x13\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x0e"}, - {"net/http/cookiejar", "\x04p\x03\x99\x01\x01\b\a\x05\x16\x03\x02\x0f\x04"}, - {"net/http/fcgi", "\x02\x01\n`\a\x03\x83\x01\x16\x01\x01\x14\x18\x02\x0f"}, - {"net/http/httptest", "\x02\x01\nL\x02\x1b\x01\x83\x01\x04\x12\x01\n\t\x02\x17\x01\x02\x0f\x0e"}, - {"net/http/httptrace", "\rLnI\x14\n!"}, - {"net/http/httputil", "\x02\x01\ng\x03\x83\x01\x04\x0f\x03\x01\x05\x02\x01\v\x01\x19\x02\x01\x0e\x0e"}, - {"net/http/internal", "\x02\x01q\x03\x83\x01"}, - {"net/http/internal/ascii", "\xbe\x02\x13"}, - {"net/http/internal/httpcommon", "\rg\x03\x9f\x01\x0e\x01\x17\x01\x01\x02\x1d\x02"}, - {"net/http/internal/testcert", "\xbe\x02"}, - {"net/http/pprof", "\x02\x01\nj\x19-\x02\x0e-\x04\x13\x14\x01\r\x04\x03\x01\x02\x01\x11"}, + {"log/slog/internal/benchmarks", "\rq\x03\x84\x01\x06\x03<\x12"}, + {"log/slog/internal/buffer", "\xcd\x02"}, + {"log/syslog", "~\x03\x88\x01\x12\x18\x18\x02\x0f"}, + {"maps", "\x88\x02Z"}, + {"math", "\xbe\x01VN"}, + {"math/big", "\x03{\x03(\x15G\f\x03\x022\x02\x01\x02\x15"}, + {"math/big/internal/asmgen", "\x03\x01}\x93\x014\x03"}, + {"math/bits", "\xe2\x02"}, + {"math/cmplx", "\x91\x02\x03"}, + {"math/rand", "\xc7\x01J<\x01\x14"}, + {"math/rand/v2", "~+\x03e\x03N"}, + {"mime", "\x02\x01l\x0f\x03\x84\x01\v#\x15\x03\x02\x11\x02"}, + {"mime/multipart", "\x02\x01P+\x03F>\v\x01\a\x02\x17\x02\x06\x0f\x02\x01\x17"}, + {"mime/quotedprintable", "\x02\x01~\x84\x01"}, + {"net", "\x04\tq*\x1f\v\x05\x13\x01\x01\x04\x15\x01'\x06\r\b\x05\x01\x01\r\x06\t"}, + {"net/http", "\x02\x01\x03\x01\x04\x02N\x14\x0f\x03F>\x01\x03\a\x01\x06\x01\x01\x02\x06\x02\x01\x01\f\x01\x01\x05\x01\x02\x05\b\x01\x01\x01\x02\x01\x0e\x02\x02\x02\n\x01\x03"}, + {"net/http/cgi", "\x02Y#\x03\x84\x01\x04\a\v\x01\x15\x01\x01\x01\x04\x01\x05\x02\b\x02\x01\x11\x10"}, + {"net/http/cookiejar", "\x04z\x03\x9a\x01\x01\b\t\x05\x16\x03\x02\x0f\x04"}, + {"net/http/fcgi", "\x02\x01\nc\x0e\x03\x84\x01\x16\x01\x01\x16\x18\x02\x0f"}, + {"net/http/httptest", "\x02\x01\nN\x02#\x01P4\x04\x12\x01\f\t\x02\r\n\x01\x02\x03\f\x06\n"}, + {"net/http/httptrace", "\rNwI\x16+"}, + {"net/http/httputil", "\x02\x01\nq\x03F>\x04\x0f\x03\x01\x05\x02\x01\r\x01\x19\x02\x01\x0e\x10"}, + {"net/http/internal", "\x02\x01m\x0e\x03\x84\x01"}, + {"net/http/internal/ascii", "\xcb\x02\x13"}, + {"net/http/internal/http2", "\x02\x01\x03\x01\x06F\b\x15\x0e\x03\x84\x01\x01\x03\b\x03\x02\x03\x02\x06\x02\x03\x01\n\x01\x01\b\x05\b\x02\x01\x02\x01\x0e\x10\x02\x02"}, + {"net/http/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"}, + {"net/http/internal/httpsfv", "\xc8\x02\x02\x01\x11\x04"}, + {"net/http/internal/testcert", "\xcb\x02"}, + {"net/http/pprof", "\x02\x01\nt\x18.\x11-\x04\x13\x16\x01\r\x04\x03\x01\x02\x01\x11"}, {"net/internal/cgotest", ""}, - {"net/internal/socktest", "w\xc9\x01\x02"}, - {"net/mail", "\x02r\x03\x83\x01\x04\x0f\x03\x14\x1a\x02\x0f\x04"}, - {"net/netip", "\x04p+\x01f\x034\x17"}, - {"net/rpc", "\x02m\x05\x03\x10\ni\x04\x12\x01\x1d\r\x03\x02"}, - {"net/rpc/jsonrpc", "q\x03\x03\x83\x01\x16\x11\x1f"}, - {"net/smtp", "\x194\f\x13\b\x03\x83\x01\x16\x14\x1a"}, - {"net/textproto", "\x02\x01q\x03\x83\x01\f\n-\x01\x02\x15"}, - {"net/url", "t\x03Fc\v\x10\x02\x01\x17"}, - {"os", "t+\x01\x19\x03\x10\x14\x01\x03\x01\x05\x10\x018\b\x05\x01\x01\r\x06"}, - {"os/exec", "\x03\ngI'\x01\x15\x01+\x06\a\n\x01\x03\x01\r"}, - {"os/exec/internal/fdtest", "\xc2\x02"}, - {"os/signal", "\r\x99\x02\x15\x05\x02"}, - {"os/user", "\x02\x01q\x03\x83\x01,\r\n\x01\x02"}, - {"path", "t+\xb4\x01"}, - {"path/filepath", "t+\x1aB+\r\b\x03\x04\x11"}, - {"plugin", "t"}, - {"reflect", "t'\x04\x1d\x13\b\x04\x05\x17\x06\t-\n\x03\x11\x02\x02"}, + {"net/internal/socktest", "\x81\x01\xcc\x01\x02"}, + {"net/mail", "\x02|\x03\x84\x01\x04\x0f\x03\x16\x1a\x02\x0f\x04"}, + {"net/netip", "\x04z*\x01h\x036\x17"}, + {"net/rpc", "\x02p\f\x03\x0f\nk\x04\x12\x01\x1f\r\x03\x02"}, + {"net/rpc/jsonrpc", "t\n\x03\x84\x01\x16\x13\x1f"}, + {"net/smtp", "\x195\r\x14\x0f\x03\x84\x01\x16\x16\x1a"}, + {"net/textproto", "\x02\x01{\x03\x84\x01\f\n/\x01\x02\x15"}, + {"net/url", "~\x03Ff\v\x10\x02\x01\x17"}, + {"os", "~*\x01\x19\x04\x11\x14\x01\x03\x01\x05\x10\x01:\b\x05\x01\x01\r\x06"}, + {"os/exec", "\x03\nqI(\x01\x15\x01-\x06\a\n\x01\x03\x01\r"}, + {"os/exec/internal/fdtest", "\xcf\x02"}, + {"os/signal", "\r\xa6\x02\x15\x05\x02"}, + {"os/user", "\x02\x01{\x03\x84\x01.\r\n\x01\x02"}, + {"path", "~*\xb8\x01"}, + {"path/filepath", "~*\x1aD-\r\b\x03\x04\x11"}, + {"plugin", "~"}, + {"reflect", "~&\x04\x1e\x03\x11\b\x04\x05\x17\x06\t/\n\x03\x11\x02\x02"}, {"reflect/internal/example1", ""}, {"reflect/internal/example2", ""}, - {"regexp", "\x03\xf7\x018\t\x02\x01\x02\x11\x02"}, - {"regexp/syntax", "\xbb\x02\x01\x01\x01\x02\x11\x02"}, - {"runtime", "\x9b\x01\x04\x01\x03\f\x06\a\x02\x01\x01\x0e\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18L"}, - {"runtime/coverage", "\xa7\x01S"}, - {"runtime/debug", "wUZ\r\b\x02\x01\x11\x06"}, - {"runtime/metrics", "\xbe\x01H-\""}, - {"runtime/pprof", "\x02\x01\x01\x03\x06`\a\x03$$\x0f\v!\f \r\b\x01\x01\x01\x02\x02\n\x03\x06"}, - {"runtime/race", "\xb9\x02"}, + {"regexp", "\x03\x82\x02\x037\t\x02\x01\x02\x11\x02"}, + {"regexp/syntax", "\xc8\x02\x01\x01\x01\x02\x11\x02"}, + {"runtime", "\xa4\x01\x04\x01\x03\f\x06\b\x02\x01\x01\x0f\x03\x01\x01\x01\x02\x01\x01\x01\x02\x01\x04\x01\x10\x18N"}, + {"runtime/coverage", "\xb0\x01U"}, + {"runtime/debug", "\x81\x01V\\\r\b\x02\x01\x11\x06"}, + {"runtime/metrics", "\xc8\x01I/\""}, + {"runtime/pprof", "\x02\x01\x01\x03\x06c\x0e\x03#5\v!\f\"\r\b\x01\x01\x01\x02\x02\n\x03\x06"}, + {"runtime/race", "\xc6\x02"}, {"runtime/race/internal/amd64v1", ""}, - {"runtime/trace", "\rg\x03z\t9\b\x05\x01\x0e\x06"}, - {"slices", "\x04\xf9\x01\fL"}, - {"sort", "\xd0\x0192"}, - {"strconv", "t+A\x01r"}, - {"strings", "t'\x04B\x19\x03\f7\x11\x02\x02"}, + {"runtime/trace", "\rq\x03{\t;\b\x05\x01\x0e\x06"}, + {"slices", "\x04\x84\x02\fN"}, + {"sort", "\xdb\x0194"}, + {"strconv", "~*C\x01t"}, + {"strings", "~&\x04D\x19\x03\f9\x11\x02\x02"}, {"structs", ""}, - {"sync", "\xcf\x01\x13\x01P\x0e\x14"}, - {"sync/atomic", "\xd5\x02"}, - {"syscall", "t(\x03\x01\x1c\n\x03\x06\r\x04S\b\x05\x01\x14"}, - {"testing", "\x03\ng\x02\x01X\x17\x14\f\x05\x1b\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"}, - {"testing/cryptotest", "QOZ\x124\x03\x12"}, - {"testing/fstest", "t\x03\x83\x01\x01\n&\x10\x03\t\b"}, - {"testing/internal/testdeps", "\x02\v\xae\x01/\x10,\x03\x05\x03\x06\a\x02\x0f"}, - {"testing/iotest", "\x03q\x03\x83\x01\x04"}, - {"testing/quick", "v\x01\x8f\x01\x05#\x10\x11"}, - {"testing/slogtest", "\rg\x03\x89\x01.\x05\x10\f"}, - {"testing/synctest", "\xe3\x01`\x12"}, - {"text/scanner", "\x03t\x83\x01,+\x02"}, - {"text/tabwriter", "w\x83\x01Y"}, - {"text/template", "t\x03C@\x01\n \x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"}, - {"text/template/parse", "\x03t\xbc\x01\n\x01\x13\x02"}, - {"time", "t+\x1e$(*\r\x02\x13"}, - {"time/tzdata", "t\xce\x01\x13"}, + {"sync", "\xda\x01\x02\x11\x01R\x0e\x14"}, + {"sync/atomic", "\xe2\x02"}, + {"syscall", "~'\x03\x01\x1d\n\x04\x06\r\x04U\b\x05\x01\x14"}, + {"testing", "\x03\nq\x02\x01Y\x17\x14\f\x05\x1d\x06\x02\x05\x02\x05\x01\x02\x01\x02\x01\x0e\x02\x04"}, + {"testing/cryptotest", "SV\\\x126\x03\x12"}, + {"testing/fstest", "~\x03\x84\x01\x01\n(\x10\x03\t\b"}, + {"testing/internal/testdeps", "\x02\v\xb7\x011\x10.\x03\x05\x03\x06\a\x02\x0f"}, + {"testing/iotest", "\x03{\x03\x84\x01\x04"}, + {"testing/quick", "\x80\x01\x01\x90\x01\x05%\x10\x11"}, + {"testing/slogtest", "\rq\x03\x8a\x010\x05\x10\f"}, + {"testing/synctest", "\xee\x01b\f\x06"}, + {"text/scanner", "\x03~\x84\x01.+\x02"}, + {"text/tabwriter", "\x81\x01\x84\x01["}, + {"text/template", "~\x03BB\x01\n\"\x01\x05\x01\x02\x05\v\x02\x0e\x03\x02"}, + {"text/template/parse", "\x03~\xbf\x01\n\x01\x13\x02"}, + {"time", "~*D(,\r\x02\x13"}, + {"time/tzdata", "~\xd1\x01\x13"}, {"unicode", ""}, {"unicode/utf16", ""}, {"unicode/utf8", ""}, - {"unique", "\x9b\x01!%\x01Q\r\x01\x14\x12"}, + {"unique", "\xa4\x01\"&\x01S\r\x01\x14\x19"}, {"unsafe", ""}, - {"vendor/golang.org/x/crypto/chacha20", "\x10]\a\x95\x01*'"}, - {"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aV\a\xe2\x01\x04\x01\a"}, - {"vendor/golang.org/x/crypto/cryptobyte", "j\n\x03\x90\x01'!\n"}, + {"uuid", "\x03\x01O\x1d\x03\v\xcf\x01\x0f"}, + {"vendor/golang.org/x/crypto/chacha20", "\x10`\x0e\x96\x01,)"}, + {"vendor/golang.org/x/crypto/chacha20poly1305", "\x10\aY\x0e\xe6\x01\x05\x01\f"}, + {"vendor/golang.org/x/crypto/cryptobyte", "m\x11\x03\x91\x01)!\v"}, {"vendor/golang.org/x/crypto/cryptobyte/asn1", ""}, - {"vendor/golang.org/x/crypto/internal/alias", "\xd5\x02"}, - {"vendor/golang.org/x/crypto/internal/poly1305", "X\x15\x9c\x01"}, - {"vendor/golang.org/x/net/dns/dnsmessage", "t\xc7\x01"}, - {"vendor/golang.org/x/net/http/httpguts", "\x90\x02\x14\x1a\x15\r"}, - {"vendor/golang.org/x/net/http/httpproxy", "t\x03\x99\x01\x10\x05\x01\x18\x15\r"}, - {"vendor/golang.org/x/net/http2/hpack", "\x03q\x03\x83\x01F"}, - {"vendor/golang.org/x/net/idna", "w\x8f\x018\x15\x10\x02\x01"}, - {"vendor/golang.org/x/net/nettest", "\x03j\a\x03\x83\x01\x11\x05\x16\x01\f\n\x01\x02\x02\x01\f"}, - {"vendor/golang.org/x/sys/cpu", "\xa6\x02\r\n\x01\x17"}, - {"vendor/golang.org/x/text/secure/bidirule", "t\xdf\x01\x11\x01"}, - {"vendor/golang.org/x/text/transform", "\x03q\x86\x01Y"}, - {"vendor/golang.org/x/text/unicode/bidi", "\x03\bl\x87\x01>\x17"}, - {"vendor/golang.org/x/text/unicode/norm", "m\n\x83\x01F\x13\x11"}, - {"weak", "\x9b\x01\x98\x01\""}, + {"vendor/golang.org/x/crypto/hkdf", "\x18\x01e\x15r"}, + {"vendor/golang.org/x/crypto/internal/alias", "\xe2\x02"}, + {"vendor/golang.org/x/crypto/internal/poly1305", "Z\x16\xa4\x01"}, + {"vendor/golang.org/x/net/dns/dnsmessage", "~\xca\x01"}, + {"vendor/golang.org/x/net/http/httpguts", "\x9b\x02\x16\x1a\x15\x10"}, + {"vendor/golang.org/x/net/http/httpproxy", "~\x03\x9a\x01\x12\x05\x01\x18\x15\x10"}, + {"vendor/golang.org/x/net/http2/hpack", "\x03{\x03\x84\x01H"}, + {"vendor/golang.org/x/net/http3", "\x9c\x02@\x06\x0f\x04"}, + {"vendor/golang.org/x/net/idna", "\x81\x01\x90\x01:\x13\x02\x17\x02\x01"}, + {"vendor/golang.org/x/net/internal/http3", "\rN#\x03\x84\x01\v\x04\a\x01\x05\x10\x01\x16\x02\x01\x02\x0f\x10\x02\x04\x03"}, + {"vendor/golang.org/x/net/internal/httpcommon", "\rq\x03\xa0\x01\x10\x01\x17\x01\x01\x02\x1f\x02"}, + {"vendor/golang.org/x/net/internal/quic/quicwire", "p"}, + {"vendor/golang.org/x/net/nettest", "\x03m\x0e\x03\x84\x01\x11\x05\x18\x01\f\n\x01\x02\x02\x01\f"}, + {"vendor/golang.org/x/net/quic", "\x03\n\x01\x01\x01\t:\x04\x04\x15\x03\v\x03\x12r\x06\x06\x06\x04\x12\x06\x15\x02\x01\x02\x01\x01\r\x06\x02\x01\x01\x02\v"}, + {"vendor/golang.org/x/sys/cpu", "\xb3\x02\r\n\x01\x17"}, + {"vendor/golang.org/x/text/secure/bidirule", "~\xe2\x01\x18\x01"}, + {"vendor/golang.org/x/text/transform", "\x03{\x87\x01["}, + {"vendor/golang.org/x/text/unicode/bidi", "\x03\bv\x88\x01@\x17"}, + {"vendor/golang.org/x/text/unicode/norm", "p\x11\x84\x01H\x13\x18"}, + {"weak", "\xa4\x01\x9c\x01\""}, } // bootstrap is the list of bootstrap packages extracted from cmd/dist. @@ -408,6 +428,7 @@ var bootstrap = map[string]bool{ "cmd/compile/internal/logopt": true, "cmd/compile/internal/loong64": true, "cmd/compile/internal/loopvar": true, + "cmd/compile/internal/midway": true, "cmd/compile/internal/mips": true, "cmd/compile/internal/mips64": true, "cmd/compile/internal/noder": true, @@ -512,6 +533,7 @@ var bootstrap = map[string]bool{ "internal/race": true, "internal/runtime/gc": true, "internal/saferio": true, + "internal/strconv": true, "internal/syscall/unix": true, "internal/types/errors": true, "internal/unsafeheader": true, diff --git a/vendor/golang.org/x/tools/internal/stdlib/manifest.go b/vendor/golang.org/x/tools/internal/stdlib/manifest.go index 33e4f505f3e..2180c296ec0 100644 --- a/vendor/golang.org/x/tools/internal/stdlib/manifest.go +++ b/vendor/golang.org/x/tools/internal/stdlib/manifest.go @@ -270,6 +270,7 @@ var PackageSymbols = map[string][]Symbol{ {"ContainsRune", Func, 7, "func(b []byte, r rune) bool"}, {"Count", Func, 0, "func(s []byte, sep []byte) int"}, {"Cut", Func, 18, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"}, + {"CutLast", Func, 27, "func(s []byte, sep []byte) (before []byte, after []byte, found bool)"}, {"CutPrefix", Func, 20, "func(s []byte, prefix []byte) (after []byte, found bool)"}, {"CutSuffix", Func, 20, "func(s []byte, suffix []byte) (before []byte, found bool)"}, {"Equal", Func, 0, "func(a []byte, b []byte) bool"}, @@ -538,6 +539,7 @@ var PackageSymbols = map[string][]Symbol{ {"MD4", Const, 0, ""}, {"MD5", Const, 0, ""}, {"MD5SHA1", Const, 0, ""}, + {"MLDSAMu", Const, 27, ""}, {"MessageSigner", Type, 25, ""}, {"PrivateKey", Type, 0, ""}, {"PublicKey", Type, 2, ""}, @@ -812,6 +814,40 @@ var PackageSymbols = map[string][]Symbol{ {"Size", Const, 0, ""}, {"Sum", Func, 2, "func(data []byte) [16]byte"}, }, + "crypto/mldsa": { + {"(*Options).HashFunc", Method, 27, ""}, + {"(*PrivateKey).Bytes", Method, 27, ""}, + {"(*PrivateKey).Equal", Method, 27, ""}, + {"(*PrivateKey).Public", Method, 27, ""}, + {"(*PrivateKey).PublicKey", Method, 27, ""}, + {"(*PrivateKey).Sign", Method, 27, ""}, + {"(*PrivateKey).SignDeterministic", Method, 27, ""}, + {"(*PublicKey).Bytes", Method, 27, ""}, + {"(*PublicKey).Equal", Method, 27, ""}, + {"(*PublicKey).Parameters", Method, 27, ""}, + {"(Parameters).PublicKeySize", Method, 27, ""}, + {"(Parameters).SignatureSize", Method, 27, ""}, + {"(Parameters).String", Method, 27, ""}, + {"GenerateKey", Func, 27, "func(params Parameters) (*PrivateKey, error)"}, + {"MLDSA44", Func, 27, "func() Parameters"}, + {"MLDSA44PublicKeySize", Const, 27, ""}, + {"MLDSA44SignatureSize", Const, 27, ""}, + {"MLDSA65", Func, 27, "func() Parameters"}, + {"MLDSA65PublicKeySize", Const, 27, ""}, + {"MLDSA65SignatureSize", Const, 27, ""}, + {"MLDSA87", Func, 27, "func() Parameters"}, + {"MLDSA87PublicKeySize", Const, 27, ""}, + {"MLDSA87SignatureSize", Const, 27, ""}, + {"NewPrivateKey", Func, 27, "func(params Parameters, seed []byte) (*PrivateKey, error)"}, + {"NewPublicKey", Func, 27, "func(params Parameters, encoding []byte) (*PublicKey, error)"}, + {"Options", Type, 27, ""}, + {"Options.Context", Field, 27, ""}, + {"Parameters", Type, 27, ""}, + {"PrivateKey", Type, 27, ""}, + {"PrivateKeySize", Const, 27, ""}, + {"PublicKey", Type, 27, ""}, + {"Verify", Func, 27, "func(pk *PublicKey, message []byte, signature []byte, opts *Options) error"}, + }, "crypto/mlkem": { {"(*DecapsulationKey1024).Bytes", Method, 24, ""}, {"(*DecapsulationKey1024).Decapsulate", Method, 24, ""}, @@ -1120,6 +1156,7 @@ var PackageSymbols = map[string][]Symbol{ {"ConnectionState.ECHAccepted", Field, 23, ""}, {"ConnectionState.HandshakeComplete", Field, 0, ""}, {"ConnectionState.HelloRetryRequest", Field, 26, ""}, + {"ConnectionState.LocalCertificate", Field, 27, ""}, {"ConnectionState.NegotiatedProtocol", Field, 0, ""}, {"ConnectionState.NegotiatedProtocolIsMutual", Field, 0, ""}, {"ConnectionState.OCSPResponse", Field, 5, ""}, @@ -1152,6 +1189,10 @@ var PackageSymbols = map[string][]Symbol{ {"InsecureCipherSuites", Func, 14, "func() []*CipherSuite"}, {"Listen", Func, 0, "func(network string, laddr string, config *Config) (net.Listener, error)"}, {"LoadX509KeyPair", Func, 0, "func(certFile string, keyFile string) (Certificate, error)"}, + {"MLDSA44", Const, 27, ""}, + {"MLDSA65", Const, 27, ""}, + {"MLDSA87", Const, 27, ""}, + {"MLKEM1024", Const, 27, ""}, {"NewLRUClientSessionCache", Func, 3, "func(capacity int) ClientSessionCache"}, {"NewListener", Func, 0, "func(inner net.Listener, config *Config) net.Listener"}, {"NewResumptionState", Func, 21, "func(ticket []byte, state *SessionState) (*ClientSessionState, error)"}, @@ -1166,6 +1207,7 @@ var PackageSymbols = map[string][]Symbol{ {"ParseSessionState", Func, 21, "func(data []byte) (*SessionState, error)"}, {"QUICClient", Func, 21, "func(config *QUICConfig) *QUICConn"}, {"QUICConfig", Type, 21, ""}, + {"QUICConfig.ClientHelloInfoConn", Field, 27, ""}, {"QUICConfig.EnableSessionEvents", Field, 23, ""}, {"QUICConfig.TLSConfig", Field, 21, ""}, {"QUICConn", Type, 21, ""}, @@ -1334,6 +1376,7 @@ var PackageSymbols = map[string][]Symbol{ {"Certificate.PublicKeyAlgorithm", Field, 0, ""}, {"Certificate.Raw", Field, 0, ""}, {"Certificate.RawIssuer", Field, 0, ""}, + {"Certificate.RawSignatureAlgorithm", Field, 27, ""}, {"Certificate.RawSubject", Field, 0, ""}, {"Certificate.RawSubjectPublicKeyInfo", Field, 0, ""}, {"Certificate.RawTBSCertificate", Field, 0, ""}, @@ -1362,6 +1405,7 @@ var PackageSymbols = map[string][]Symbol{ {"CertificateRequest.PublicKey", Field, 3, ""}, {"CertificateRequest.PublicKeyAlgorithm", Field, 3, ""}, {"CertificateRequest.Raw", Field, 3, ""}, + {"CertificateRequest.RawSignatureAlgorithm", Field, 27, ""}, {"CertificateRequest.RawSubject", Field, 3, ""}, {"CertificateRequest.RawSubjectPublicKeyInfo", Field, 3, ""}, {"CertificateRequest.RawTBSCertificateRequest", Field, 3, ""}, @@ -1422,6 +1466,10 @@ var PackageSymbols = map[string][]Symbol{ {"KeyUsageKeyEncipherment", Const, 0, ""}, {"MD2WithRSA", Const, 0, ""}, {"MD5WithRSA", Const, 0, ""}, + {"MLDSA", Const, 27, ""}, + {"MLDSA44", Const, 27, ""}, + {"MLDSA65", Const, 27, ""}, + {"MLDSA87", Const, 27, ""}, {"MarshalECPrivateKey", Func, 2, "func(key *ecdsa.PrivateKey) ([]byte, error)"}, {"MarshalPKCS1PrivateKey", Func, 0, "func(key *rsa.PrivateKey) []byte"}, {"MarshalPKCS1PublicKey", Func, 10, "func(key *rsa.PublicKey) []byte"}, @@ -1468,6 +1516,7 @@ var PackageSymbols = map[string][]Symbol{ {"RevocationList.Number", Field, 15, ""}, {"RevocationList.Raw", Field, 19, ""}, {"RevocationList.RawIssuer", Field, 19, ""}, + {"RevocationList.RawSignatureAlgorithm", Field, 27, ""}, {"RevocationList.RawTBSRevocationList", Field, 19, ""}, {"RevocationList.RevokedCertificateEntries", Field, 21, ""}, {"RevocationList.RevokedCertificates", Field, 15, ""}, @@ -1648,6 +1697,7 @@ var PackageSymbols = map[string][]Symbol{ {"(Scanner).Scan", Method, 0, ""}, {"ColumnType", Type, 8, ""}, {"Conn", Type, 9, ""}, + {"ConvertAssign", Func, 27, "func(scanCtx driver.ScanContext, dest any, src driver.Value) error"}, {"DB", Type, 0, ""}, {"DBStats", Type, 5, ""}, {"DBStats.Idle", Field, 11, ""}, @@ -1744,6 +1794,11 @@ var PackageSymbols = map[string][]Symbol{ {"(Rows).Next", Method, 0, ""}, {"(RowsAffected).LastInsertId", Method, 0, ""}, {"(RowsAffected).RowsAffected", Method, 0, ""}, + {"(RowsColumnScanner).Close", Method, 27, ""}, + {"(RowsColumnScanner).Columns", Method, 27, ""}, + {"(RowsColumnScanner).Next", Method, 27, ""}, + {"(RowsColumnScanner).NextRow", Method, 27, ""}, + {"(RowsColumnScanner).ScanColumn", Method, 27, ""}, {"(RowsColumnTypeDatabaseTypeName).Close", Method, 8, ""}, {"(RowsColumnTypeDatabaseTypeName).ColumnTypeDatabaseTypeName", Method, 8, ""}, {"(RowsColumnTypeDatabaseTypeName).Columns", Method, 8, ""}, @@ -1815,12 +1870,14 @@ var PackageSymbols = map[string][]Symbol{ {"ResultNoRows", Var, 0, ""}, {"Rows", Type, 0, ""}, {"RowsAffected", Type, 0, ""}, + {"RowsColumnScanner", Type, 27, ""}, {"RowsColumnTypeDatabaseTypeName", Type, 8, ""}, {"RowsColumnTypeLength", Type, 8, ""}, {"RowsColumnTypeNullable", Type, 8, ""}, {"RowsColumnTypePrecisionScale", Type, 8, ""}, {"RowsColumnTypeScanType", Type, 8, ""}, {"RowsNextResultSet", Type, 8, ""}, + {"ScanContext", Type, 27, ""}, {"SessionResetter", Type, 10, ""}, {"Stmt", Type, 0, ""}, {"StmtExecContext", Type, 8, ""}, @@ -5038,24 +5095,32 @@ var PackageSymbols = map[string][]Symbol{ {"(*InvalidUnmarshalError).Error", Method, 0, ""}, {"(*MarshalerError).Error", Method, 0, ""}, {"(*MarshalerError).Unwrap", Method, 13, ""}, + {"(*Number).UnmarshalJSONFrom", Method, 27, ""}, {"(*RawMessage).MarshalJSON", Method, 0, ""}, {"(*RawMessage).UnmarshalJSON", Method, 0, ""}, {"(*SyntaxError).Error", Method, 0, ""}, {"(*UnmarshalFieldError).Error", Method, 0, ""}, {"(*UnmarshalTypeError).Error", Method, 0, ""}, + {"(*UnmarshalTypeError).Unwrap", Method, 27, ""}, {"(*UnsupportedTypeError).Error", Method, 0, ""}, {"(*UnsupportedValueError).Error", Method, 0, ""}, {"(Delim).String", Method, 5, ""}, {"(Marshaler).MarshalJSON", Method, 0, ""}, {"(Number).Float64", Method, 1, ""}, {"(Number).Int64", Method, 1, ""}, + {"(Number).MarshalJSONTo", Method, 27, ""}, {"(Number).String", Method, 1, ""}, {"(RawMessage).MarshalJSON", Method, 8, ""}, {"(Unmarshaler).UnmarshalJSON", Method, 0, ""}, + {"CallMethodsWithLegacySemantics", Func, 27, "func(v bool) Options"}, {"Compact", Func, 0, "func(dst *bytes.Buffer, src []byte) error"}, {"Decoder", Type, 0, ""}, + {"DefaultOptionsV1", Func, 27, "func() Options"}, {"Delim", Type, 5, ""}, {"Encoder", Type, 0, ""}, + {"FormatByteArrayAsArray", Func, 27, "func(v bool) Options"}, + {"FormatBytesWithLegacySemantics", Func, 27, "func(v bool) Options"}, + {"FormatDurationAsNano", Func, 27, "func(v bool) Options"}, {"HTMLEscape", Func, 0, "func(dst *bytes.Buffer, src []byte)"}, {"Indent", Func, 0, "func(dst *bytes.Buffer, src []byte, prefix string, indent string) error"}, {"InvalidUTF8Error", Type, 0, ""}, @@ -5068,19 +5133,29 @@ var PackageSymbols = map[string][]Symbol{ {"MarshalerError", Type, 0, ""}, {"MarshalerError.Err", Field, 0, ""}, {"MarshalerError.Type", Field, 0, ""}, + {"MatchCaseSensitiveDelimiter", Func, 27, "func(v bool) Options"}, + {"MergeWithLegacySemantics", Func, 27, "func(v bool) Options"}, {"NewDecoder", Func, 0, "func(r io.Reader) *Decoder"}, {"NewEncoder", Func, 0, "func(w io.Writer) *Encoder"}, {"Number", Type, 1, ""}, + {"OmitEmptyWithLegacySemantics", Func, 27, "func(v bool) Options"}, + {"Options", Type, 27, ""}, + {"ParseBytesWithLooseRFC4648", Func, 27, "func(v bool) Options"}, + {"ParseTimeWithLooseRFC3339", Func, 27, "func(v bool) Options"}, {"RawMessage", Type, 0, ""}, + {"ReportErrorsWithLegacySemantics", Func, 27, "func(v bool) Options"}, + {"StringifyWithLegacySemantics", Func, 27, "func(v bool) Options"}, {"SyntaxError", Type, 0, ""}, {"SyntaxError.Offset", Field, 0, ""}, {"Token", Type, 5, ""}, {"Unmarshal", Func, 0, "func(data []byte, v any) error"}, + {"UnmarshalArrayFromAnyLength", Func, 27, "func(v bool) Options"}, {"UnmarshalFieldError", Type, 0, ""}, {"UnmarshalFieldError.Field", Field, 0, ""}, {"UnmarshalFieldError.Key", Field, 0, ""}, {"UnmarshalFieldError.Type", Field, 0, ""}, {"UnmarshalTypeError", Type, 0, ""}, + {"UnmarshalTypeError.Err", Field, 27, ""}, {"UnmarshalTypeError.Field", Field, 8, ""}, {"UnmarshalTypeError.Offset", Field, 5, ""}, {"UnmarshalTypeError.Struct", Field, 8, ""}, @@ -5094,6 +5169,158 @@ var PackageSymbols = map[string][]Symbol{ {"UnsupportedValueError.Value", Field, 0, ""}, {"Valid", Func, 9, "func(data []byte) bool"}, }, + "encoding/json/jsontext": { + {"(*Decoder).InputOffset", Method, 27, ""}, + {"(*Decoder).Options", Method, 27, ""}, + {"(*Decoder).PeekKind", Method, 27, ""}, + {"(*Decoder).ReadToken", Method, 27, ""}, + {"(*Decoder).ReadValue", Method, 27, ""}, + {"(*Decoder).Reset", Method, 27, ""}, + {"(*Decoder).SkipValue", Method, 27, ""}, + {"(*Decoder).StackDepth", Method, 27, ""}, + {"(*Decoder).StackIndex", Method, 27, ""}, + {"(*Decoder).StackPointer", Method, 27, ""}, + {"(*Decoder).UnreadBuffer", Method, 27, ""}, + {"(*Encoder).AvailableBuffer", Method, 27, ""}, + {"(*Encoder).Options", Method, 27, ""}, + {"(*Encoder).OutputOffset", Method, 27, ""}, + {"(*Encoder).Reset", Method, 27, ""}, + {"(*Encoder).StackDepth", Method, 27, ""}, + {"(*Encoder).StackIndex", Method, 27, ""}, + {"(*Encoder).StackPointer", Method, 27, ""}, + {"(*Encoder).WriteToken", Method, 27, ""}, + {"(*Encoder).WriteValue", Method, 27, ""}, + {"(*SyntacticError).Error", Method, 27, ""}, + {"(*SyntacticError).Unwrap", Method, 27, ""}, + {"(*Value).Canonicalize", Method, 27, ""}, + {"(*Value).Compact", Method, 27, ""}, + {"(*Value).Format", Method, 27, ""}, + {"(*Value).Indent", Method, 27, ""}, + {"(*Value).UnmarshalJSON", Method, 27, ""}, + {"(Kind).String", Method, 27, ""}, + {"(Pointer).AppendToken", Method, 27, ""}, + {"(Pointer).Contains", Method, 27, ""}, + {"(Pointer).IsValid", Method, 27, ""}, + {"(Pointer).LastToken", Method, 27, ""}, + {"(Pointer).Parent", Method, 27, ""}, + {"(Pointer).Tokens", Method, 27, ""}, + {"(Token).Bool", Method, 27, ""}, + {"(Token).Clone", Method, 27, ""}, + {"(Token).Float", Method, 27, ""}, + {"(Token).Float32", Method, 27, ""}, + {"(Token).Int", Method, 27, ""}, + {"(Token).Kind", Method, 27, ""}, + {"(Token).String", Method, 27, ""}, + {"(Token).Uint", Method, 27, ""}, + {"(Value).Clone", Method, 27, ""}, + {"(Value).IsValid", Method, 27, ""}, + {"(Value).Kind", Method, 27, ""}, + {"(Value).MarshalJSON", Method, 27, ""}, + {"(Value).String", Method, 27, ""}, + {"AllowDuplicateNames", Func, 27, "func(v bool) Options"}, + {"AllowInvalidUTF8", Func, 27, "func(v bool) Options"}, + {"AppendFloat", Func, 27, "func(dst []byte, src float64, bits int) []byte"}, + {"AppendFormat", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes, opts ...Options) ([]byte, error)"}, + {"AppendQuote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"}, + {"AppendUnquote", Func, 27, "func[Bytes ~[]byte | ~string](dst []byte, src Bytes) ([]byte, error)"}, + {"BeginArray", Var, 27, ""}, + {"BeginObject", Var, 27, ""}, + {"Bool", Func, 27, "func(b bool) Token"}, + {"CanonicalizeRawFloats", Func, 27, "func(v bool) Options"}, + {"CanonicalizeRawInts", Func, 27, "func(v bool) Options"}, + {"Decoder", Type, 27, ""}, + {"Encoder", Type, 27, ""}, + {"EndArray", Var, 27, ""}, + {"EndObject", Var, 27, ""}, + {"ErrDuplicateName", Var, 27, ""}, + {"ErrNonStringName", Var, 27, ""}, + {"EscapeForHTML", Func, 27, "func(v bool) Options"}, + {"EscapeForJS", Func, 27, "func(v bool) Options"}, + {"False", Var, 27, ""}, + {"Float", Func, 27, "func(n float64) Token"}, + {"Float32", Func, 27, "func(n float32) Token"}, + {"Int", Func, 27, "func(n int64) Token"}, + {"Internal", Var, 27, ""}, + {"Kind", Type, 27, ""}, + {"KindBeginArray", Const, 27, ""}, + {"KindBeginObject", Const, 27, ""}, + {"KindEndArray", Const, 27, ""}, + {"KindEndObject", Const, 27, ""}, + {"KindFalse", Const, 27, ""}, + {"KindInvalid", Const, 27, ""}, + {"KindNull", Const, 27, ""}, + {"KindNumber", Const, 27, ""}, + {"KindString", Const, 27, ""}, + {"KindTrue", Const, 27, ""}, + {"Multiline", Func, 27, "func(v bool) Options"}, + {"NewDecoder", Func, 27, "func(r io.Reader, opts ...Options) *Decoder"}, + {"NewEncoder", Func, 27, "func(w io.Writer, opts ...Options) *Encoder"}, + {"Null", Var, 27, ""}, + {"Options", Type, 27, ""}, + {"Pointer", Type, 27, ""}, + {"PreserveRawStrings", Func, 27, "func(v bool) Options"}, + {"ReorderRawObjects", Func, 27, "func(v bool) Options"}, + {"SpaceAfterColon", Func, 27, "func(v bool) Options"}, + {"SpaceAfterComma", Func, 27, "func(v bool) Options"}, + {"String", Func, 27, "func(s string) Token"}, + {"SyntacticError", Type, 27, ""}, + {"SyntacticError.ByteOffset", Field, 27, ""}, + {"SyntacticError.Err", Field, 27, ""}, + {"SyntacticError.JSONPointer", Field, 27, ""}, + {"Token", Type, 27, ""}, + {"True", Var, 27, ""}, + {"Uint", Func, 27, "func(n uint64) Token"}, + {"Value", Type, 27, ""}, + {"WithIndent", Func, 27, "func(indent string) Options"}, + {"WithIndentPrefix", Func, 27, "func(prefix string) Options"}, + }, + "encoding/json/v2": { + {"(*SemanticError).Error", Method, 27, ""}, + {"(*SemanticError).Unwrap", Method, 27, ""}, + {"(Marshaler).MarshalJSON", Method, 27, ""}, + {"(MarshalerTo).MarshalJSONTo", Method, 27, ""}, + {"(Unmarshaler).UnmarshalJSON", Method, 27, ""}, + {"(UnmarshalerFrom).UnmarshalJSONFrom", Method, 27, ""}, + {"DefaultOptionsV2", Func, 27, "func() Options"}, + {"Deterministic", Func, 27, "func(v bool) Options"}, + {"ErrUnknownName", Var, 27, ""}, + {"FormatNilMapAsNull", Func, 27, "func(v bool) Options"}, + {"FormatNilSliceAsNull", Func, 27, "func(v bool) Options"}, + {"GetOption", Func, 27, "func[T any](opts Options, setter func(T) Options) (T, bool)"}, + {"JoinMarshalers", Func, 27, "func(ms ...*Marshalers) *Marshalers"}, + {"JoinOptions", Func, 27, "func(srcs ...Options) Options"}, + {"JoinUnmarshalers", Func, 27, "func(us ...*Unmarshalers) *Unmarshalers"}, + {"Marshal", Func, 27, "func(in any, opts ...Options) (out []byte, err error)"}, + {"MarshalEncode", Func, 27, "func(out *jsontext.Encoder, in any, opts ...Options) (err error)"}, + {"MarshalFunc", Func, 27, "func[T any](fn func(T) ([]byte, error)) *Marshalers"}, + {"MarshalToFunc", Func, 27, "func[T any](fn func(*jsontext.Encoder, T) error) *Marshalers"}, + {"MarshalWrite", Func, 27, "func(out io.Writer, in any, opts ...Options) (err error)"}, + {"Marshaler", Type, 27, ""}, + {"MarshalerTo", Type, 27, ""}, + {"Marshalers", Type, 27, ""}, + {"MatchCaseInsensitiveNames", Func, 27, "func(v bool) Options"}, + {"OmitZeroStructFields", Func, 27, "func(v bool) Options"}, + {"Options", Type, 27, ""}, + {"RejectUnknownMembers", Func, 27, "func(v bool) Options"}, + {"SemanticError", Type, 27, ""}, + {"SemanticError.ByteOffset", Field, 27, ""}, + {"SemanticError.Err", Field, 27, ""}, + {"SemanticError.GoType", Field, 27, ""}, + {"SemanticError.JSONKind", Field, 27, ""}, + {"SemanticError.JSONPointer", Field, 27, ""}, + {"SemanticError.JSONValue", Field, 27, ""}, + {"StringifyNumbers", Func, 27, "func(v bool) Options"}, + {"Unmarshal", Func, 27, "func(in []byte, out any, opts ...Options) (err error)"}, + {"UnmarshalDecode", Func, 27, "func(in *jsontext.Decoder, out any, opts ...Options) (err error)"}, + {"UnmarshalFromFunc", Func, 27, "func[T any](fn func(*jsontext.Decoder, T) error) *Unmarshalers"}, + {"UnmarshalFunc", Func, 27, "func[T any](fn func([]byte, T) error) *Unmarshalers"}, + {"UnmarshalRead", Func, 27, "func(in io.Reader, out any, opts ...Options) (err error)"}, + {"Unmarshaler", Type, 27, ""}, + {"UnmarshalerFrom", Type, 27, ""}, + {"Unmarshalers", Type, 27, ""}, + {"WithMarshalers", Func, 27, "func(v *Marshalers) Options"}, + {"WithUnmarshalers", Func, 27, "func(v *Unmarshalers) Options"}, + }, "encoding/pem": { {"Block", Type, 0, ""}, {"Block.Bytes", Field, 0, ""}, @@ -6002,6 +6229,7 @@ var PackageSymbols = map[string][]Symbol{ {"Shift", Func, 5, "func(x Value, op token.Token, s uint) Value"}, {"Sign", Func, 5, "func(x Value) int"}, {"String", Const, 5, ""}, + {"StringLen", Func, 27, "func(x Value) int64"}, {"StringVal", Func, 5, "func(x Value) string"}, {"ToComplex", Func, 6, "func(x Value) Value"}, {"ToFloat", Func, 6, "func(x Value) Value"}, @@ -6183,6 +6411,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*ErrorList).Add", Method, 0, ""}, {"(*ErrorList).RemoveMultiples", Method, 0, ""}, {"(*ErrorList).Reset", Method, 0, ""}, + {"(*Scanner).End", Method, 27, ""}, {"(*Scanner).Init", Method, 0, ""}, {"(*Scanner).Scan", Method, 0, ""}, {"(Error).Error", Method, 0, ""}, @@ -6222,6 +6451,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*File).SetLines", Method, 0, ""}, {"(*File).SetLinesForContent", Method, 0, ""}, {"(*File).Size", Method, 0, ""}, + {"(*File).String", Method, 27, ""}, {"(*FileSet).AddExistingFiles", Method, 25, ""}, {"(*FileSet).AddFile", Method, 0, ""}, {"(*FileSet).Base", Method, 0, ""}, @@ -6529,6 +6759,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Tuple).Variables", Method, 24, ""}, {"(*TypeList).At", Method, 18, ""}, {"(*TypeList).Len", Method, 18, ""}, + {"(*TypeList).String", Method, 27, ""}, {"(*TypeList).Types", Method, 24, ""}, {"(*TypeName).Exported", Method, 5, ""}, {"(*TypeName).Id", Method, 5, ""}, @@ -6547,6 +6778,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*TypeParam).Underlying", Method, 18, ""}, {"(*TypeParamList).At", Method, 18, ""}, {"(*TypeParamList).Len", Method, 18, ""}, + {"(*TypeParamList).String", Method, 27, ""}, {"(*TypeParamList).TypeParams", Method, 24, ""}, {"(*Union).Len", Method, 18, ""}, {"(*Union).String", Method, 18, ""}, @@ -6571,9 +6803,14 @@ var PackageSymbols = map[string][]Symbol{ {"(Checker).PkgNameOf", Method, 22, ""}, {"(Checker).TypeOf", Method, 5, ""}, {"(Error).Error", Method, 5, ""}, + {"(Hasher).Equal", Method, 27, ""}, + {"(Hasher).Hash", Method, 27, ""}, + {"(HasherIgnoreTags).Equal", Method, 27, ""}, + {"(HasherIgnoreTags).Hash", Method, 27, ""}, {"(Importer).Import", Method, 5, ""}, {"(ImporterFrom).Import", Method, 6, ""}, {"(ImporterFrom).ImportFrom", Method, 6, ""}, + {"(Instance).String", Method, 27, ""}, {"(Object).Exported", Method, 5, ""}, {"(Object).Id", Method, 5, ""}, {"(Object).Name", Method, 5, ""}, @@ -6643,6 +6880,8 @@ var PackageSymbols = map[string][]Symbol{ {"Float32", Const, 5, ""}, {"Float64", Const, 5, ""}, {"Func", Type, 5, ""}, + {"Hasher", Type, 27, ""}, + {"HasherIgnoreTags", Type, 27, ""}, {"Id", Func, 5, "func(pkg *Package, name string) string"}, {"Identical", Func, 5, "func(x Type, y Type) bool"}, {"IdenticalIgnoreTags", Func, 8, "func(x Type, y Type) bool"}, @@ -6877,9 +7116,13 @@ var PackageSymbols = map[string][]Symbol{ {"(*Hash).Write", Method, 14, ""}, {"(*Hash).WriteByte", Method, 14, ""}, {"(*Hash).WriteString", Method, 14, ""}, + {"(ComparableHasher).Equal", Method, 27, ""}, + {"(ComparableHasher).Hash", Method, 27, ""}, {"Bytes", Func, 19, "func(seed Seed, b []byte) uint64"}, {"Comparable", Func, 24, "func[T comparable](seed Seed, v T) uint64"}, + {"ComparableHasher", Type, 27, ""}, {"Hash", Type, 14, ""}, + {"Hasher", Type, 27, ""}, {"MakeSeed", Func, 14, "func() Seed"}, {"Seed", Type, 14, ""}, {"String", Func, 19, "func(seed Seed, s string) uint64"}, @@ -8035,6 +8278,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Int).CmpAbs", Method, 10, ""}, {"(*Int).Div", Method, 0, ""}, {"(*Int).DivMod", Method, 0, ""}, + {"(*Int).Divide", Method, 27, ""}, {"(*Int).Exp", Method, 0, ""}, {"(*Int).FillBytes", Method, 15, ""}, {"(*Int).Float64", Method, 21, ""}, @@ -8119,9 +8363,11 @@ var PackageSymbols = map[string][]Symbol{ {"Accuracy", Type, 5, ""}, {"AwayFromZero", Const, 5, ""}, {"Below", Const, 5, ""}, + {"Ceil", Const, 27, ""}, {"ErrNaN", Type, 5, ""}, {"Exact", Const, 5, ""}, {"Float", Type, 5, ""}, + {"Floor", Const, 27, ""}, {"Int", Type, 0, ""}, {"Jacobi", Func, 5, "func(x *Int, y *Int) int"}, {"MaxBase", Const, 0, ""}, @@ -8133,12 +8379,14 @@ var PackageSymbols = map[string][]Symbol{ {"NewRat", Func, 0, "func(a int64, b int64) *Rat"}, {"ParseFloat", Func, 5, "func(s string, base int, prec uint, mode RoundingMode) (f *Float, b int, err error)"}, {"Rat", Type, 0, ""}, + {"Round", Const, 27, ""}, {"RoundingMode", Type, 5, ""}, {"ToNearestAway", Const, 5, ""}, {"ToNearestEven", Const, 5, ""}, {"ToNegativeInf", Const, 5, ""}, {"ToPositiveInf", Const, 5, ""}, {"ToZero", Const, 5, ""}, + {"Trunc", Const, 27, ""}, {"Word", Type, 0, ""}, }, "math/bits": { @@ -8290,6 +8538,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Rand).Int64", Method, 22, ""}, {"(*Rand).Int64N", Method, 22, ""}, {"(*Rand).IntN", Method, 22, ""}, + {"(*Rand).N", Method, 27, ""}, {"(*Rand).NormFloat64", Method, 22, ""}, {"(*Rand).Perm", Method, 22, ""}, {"(*Rand).Shuffle", Method, 22, ""}, @@ -8985,7 +9234,7 @@ var PackageSymbols = map[string][]Symbol{ {"NoBody", Var, 8, ""}, {"NotFound", Func, 0, "func(w ResponseWriter, r *Request)"}, {"NotFoundHandler", Func, 0, "func() Handler"}, - {"ParseCookie", Func, 23, "func(line string) ([]*Cookie, error)"}, + {"ParseCookie", Func, 23, "func(line string) (#rv1 []*Cookie, #rv2 error)"}, {"ParseHTTPVersion", Func, 0, "func(vers string) (major int, minor int, ok bool)"}, {"ParseSetCookie", Func, 23, "func(line string) (*Cookie, error)"}, {"ParseTime", Func, 1, "func(text string) (t time.Time, err error)"}, @@ -9061,6 +9310,7 @@ var PackageSymbols = map[string][]Symbol{ {"Server.BaseContext", Field, 13, ""}, {"Server.ConnContext", Field, 13, ""}, {"Server.ConnState", Field, 3, ""}, + {"Server.DisableClientPriority", Field, 27, ""}, {"Server.DisableGeneralOptionsHandler", Field, 20, ""}, {"Server.ErrorLog", Field, 3, ""}, {"Server.HTTP2", Field, 24, ""}, @@ -9226,6 +9476,7 @@ var PackageSymbols = map[string][]Symbol{ {"NewRequestWithContext", Func, 23, "func(ctx context.Context, method string, target string, body io.Reader) *http.Request"}, {"NewServer", Func, 0, "func(handler http.Handler) *Server"}, {"NewTLSServer", Func, 0, "func(handler http.Handler) *Server"}, + {"NewTestServer", Func, 27, "func(t testing.TB, handler http.Handler) *Server"}, {"NewUnstartedServer", Func, 0, "func(handler http.Handler) *Server"}, {"ResponseRecorder", Type, 0, ""}, {"ResponseRecorder.Body", Field, 0, ""}, @@ -9596,6 +9847,7 @@ var PackageSymbols = map[string][]Symbol{ {"(*Error).Timeout", Method, 6, ""}, {"(*Error).Unwrap", Method, 13, ""}, {"(*URL).AppendBinary", Method, 24, ""}, + {"(*URL).Clone", Method, 27, ""}, {"(*URL).EscapedFragment", Method, 15, ""}, {"(*URL).EscapedPath", Method, 5, ""}, {"(*URL).Hostname", Method, 8, ""}, @@ -9616,6 +9868,7 @@ var PackageSymbols = map[string][]Symbol{ {"(EscapeError).Error", Method, 0, ""}, {"(InvalidHostError).Error", Method, 6, ""}, {"(Values).Add", Method, 0, ""}, + {"(Values).Clone", Method, 27, ""}, {"(Values).Del", Method, 0, ""}, {"(Values).Encode", Method, 0, ""}, {"(Values).Get", Method, 0, ""}, @@ -10793,6 +11046,7 @@ var PackageSymbols = map[string][]Symbol{ {"ContainsRune", Func, 0, "func(s string, r rune) bool"}, {"Count", Func, 0, "func(s string, substr string) int"}, {"Cut", Func, 18, "func(s string, sep string) (before string, after string, found bool)"}, + {"CutLast", Func, 27, "func(s string, sep string) (before string, after string, found bool)"}, {"CutPrefix", Func, 20, "func(s string, prefix string) (after string, found bool)"}, {"CutSuffix", Func, 20, "func(s string, suffix string) (before string, found bool)"}, {"EqualFold", Func, 0, "func(s string, t string) bool"}, @@ -17476,6 +17730,7 @@ var PackageSymbols = map[string][]Symbol{ {"TestHandler", Func, 21, "func(h slog.Handler, results func() []map[string]any) error"}, }, "testing/synctest": { + {"Sleep", Func, 27, "func(d time.Duration)"}, {"Test", Func, 25, "func(t *testing.T, f func(*testing.T))"}, {"Wait", Func, 25, "func()"}, }, @@ -17980,6 +18235,7 @@ var PackageSymbols = map[string][]Symbol{ {"Bassa_Vah", Var, 4, ""}, {"Batak", Var, 0, ""}, {"Bengali", Var, 0, ""}, + {"Beria_Erfe", Var, 27, ""}, {"Bhaiksuki", Var, 7, ""}, {"Bidi_Control", Var, 0, ""}, {"Bopomofo", Var, 0, ""}, @@ -18029,6 +18285,7 @@ var PackageSymbols = map[string][]Symbol{ {"Extender", Var, 0, ""}, {"FoldCategory", Var, 0, ""}, {"FoldScript", Var, 0, ""}, + {"Garay", Var, 27, ""}, {"Georgian", Var, 0, ""}, {"Glagolitic", Var, 0, ""}, {"Gothic", Var, 0, ""}, @@ -18038,6 +18295,7 @@ var PackageSymbols = map[string][]Symbol{ {"Gujarati", Var, 0, ""}, {"Gunjala_Gondi", Var, 13, ""}, {"Gurmukhi", Var, 0, ""}, + {"Gurung_Khema", Var, 27, ""}, {"Han", Var, 0, ""}, {"Hangul", Var, 0, ""}, {"Hanifi_Rohingya", Var, 13, ""}, @@ -18049,6 +18307,9 @@ var PackageSymbols = map[string][]Symbol{ {"Hyphen", Var, 0, ""}, {"IDS_Binary_Operator", Var, 0, ""}, {"IDS_Trinary_Operator", Var, 0, ""}, + {"IDS_Unary_Operator", Var, 27, ""}, + {"ID_Compat_Math_Continue", Var, 27, ""}, + {"ID_Compat_Math_Start", Var, 27, ""}, {"Ideographic", Var, 0, ""}, {"Imperial_Aramaic", Var, 0, ""}, {"In", Func, 2, "func(r rune, ranges ...*RangeTable) bool"}, @@ -18082,6 +18343,7 @@ var PackageSymbols = map[string][]Symbol{ {"Khmer", Var, 0, ""}, {"Khojki", Var, 4, ""}, {"Khudawadi", Var, 4, ""}, + {"Kirat_Rai", Var, 27, ""}, {"L", Var, 0, ""}, {"LC", Var, 25, ""}, {"Lao", Var, 0, ""}, @@ -18125,6 +18387,7 @@ var PackageSymbols = map[string][]Symbol{ {"Miao", Var, 1, ""}, {"Mn", Var, 0, ""}, {"Modi", Var, 4, ""}, + {"Modifier_Combining_Mark", Var, 27, ""}, {"Mongolian", Var, 0, ""}, {"Mro", Var, 4, ""}, {"Multani", Var, 5, ""}, @@ -18145,6 +18408,7 @@ var PackageSymbols = map[string][]Symbol{ {"Nyiakeng_Puachue_Hmong", Var, 14, ""}, {"Ogham", Var, 0, ""}, {"Ol_Chiki", Var, 0, ""}, + {"Ol_Onal", Var, 27, ""}, {"Old_Hungarian", Var, 5, ""}, {"Old_Italic", Var, 0, ""}, {"Old_North_Arabian", Var, 4, ""}, @@ -18214,6 +18478,7 @@ var PackageSymbols = map[string][]Symbol{ {"Sharada", Var, 1, ""}, {"Shavian", Var, 0, ""}, {"Siddham", Var, 4, ""}, + {"Sidetic", Var, 27, ""}, {"SignWriting", Var, 5, ""}, {"SimpleFold", Func, 0, "func(r rune) rune"}, {"Sinhala", Var, 0, ""}, @@ -18227,6 +18492,7 @@ var PackageSymbols = map[string][]Symbol{ {"Space", Var, 0, ""}, {"SpecialCase", Type, 0, ""}, {"Sundanese", Var, 0, ""}, + {"Sunuwar", Var, 27, ""}, {"Syloti_Nagri", Var, 0, ""}, {"Symbol", Var, 0, ""}, {"Syriac", Var, 0, ""}, @@ -18235,6 +18501,7 @@ var PackageSymbols = map[string][]Symbol{ {"Tai_Le", Var, 0, ""}, {"Tai_Tham", Var, 0, ""}, {"Tai_Viet", Var, 0, ""}, + {"Tai_Yo", Var, 27, ""}, {"Takri", Var, 1, ""}, {"Tamil", Var, 0, ""}, {"Tangsa", Var, 21, ""}, @@ -18252,7 +18519,10 @@ var PackageSymbols = map[string][]Symbol{ {"ToLower", Func, 0, "func(r rune) rune"}, {"ToTitle", Func, 0, "func(r rune) rune"}, {"ToUpper", Func, 0, "func(r rune) rune"}, + {"Todhri", Var, 27, ""}, + {"Tolong_Siki", Var, 27, ""}, {"Toto", Var, 21, ""}, + {"Tulu_Tigalari", Var, 27, ""}, {"TurkishCase", Var, 0, ""}, {"Ugaritic", Var, 0, ""}, {"Unified_Ideograph", Var, 0, ""}, @@ -18320,6 +18590,21 @@ var PackageSymbols = map[string][]Symbol{ {"String", Func, 0, ""}, {"StringData", Func, 0, ""}, }, + "uuid": { + {"(*UUID).UnmarshalText", Method, 27, ""}, + {"(UUID).AppendText", Method, 27, ""}, + {"(UUID).Compare", Method, 27, ""}, + {"(UUID).MarshalText", Method, 27, ""}, + {"(UUID).String", Method, 27, ""}, + {"Max", Func, 27, "func() UUID"}, + {"MustParse", Func, 27, "func(s string) UUID"}, + {"New", Func, 27, "func() UUID"}, + {"NewV4", Func, 27, "func() UUID"}, + {"NewV7", Func, 27, "func() UUID"}, + {"Nil", Func, 27, "func() UUID"}, + {"Parse", Func, 27, "func(s string) (UUID, error)"}, + {"UUID", Type, 27, ""}, + }, "weak": { {"(Pointer).Value", Method, 24, ""}, {"Make", Func, 24, "func[T any](ptr *T) Pointer[T]"}, diff --git a/vendor/golang.org/x/tools/internal/typesinternal/element.go b/vendor/golang.org/x/tools/internal/typesinternal/element.go index 5fe4d8abcb5..89eeea16533 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/element.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/element.go @@ -37,6 +37,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type tmset := msets.MethodSet(T) for method := range tmset.Methods() { sig := method.Type().(*types.Signature) + if sig.TypeParams() != nil { + continue // skip type-parameterized methods + } + // It is tempting to call visit(sig, false) // but, as noted in golang.org/cl/65450043, // the Signature.Recv field is ignored by @@ -123,10 +127,10 @@ func ForEachElement(rtypes *typeutil.Map, msets *typeutil.MethodSetCache, T type case *types.TypeParam, *types.Union: // forEachReachable must not be called on parameterized types. - panic(T) + panic(fmt.Sprintf("ForEachElement called on type containing %T", T)) default: - panic(T) + panic(fmt.Sprintf("ForEachElement called on unexpected type %T", T)) } } visit(T, false) diff --git a/vendor/golang.org/x/tools/internal/typesinternal/types.go b/vendor/golang.org/x/tools/internal/typesinternal/types.go index 6582cc81f5a..d2c0b4c5ff2 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/types.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/types.go @@ -22,6 +22,7 @@ import ( "go/ast" "go/token" "go/types" + "iter" "reflect" "golang.org/x/tools/go/ast/inspector" @@ -242,3 +243,30 @@ func ObjectKind(obj types.Object) string { } return "unknown symbol" } + +// ImplicitFieldSelections returns the sequence of implicit embedded fields +// traversed by the given selection. It skips the final leaf field or method. +// The boolean component indicates whether the traversal traversed a pointer. +func ImplicitFieldSelections(seln types.Selection) iter.Seq2[*types.Var, bool] { + return func(yield func(*types.Var, bool) bool) { + var ( + t = seln.Recv() + indices = seln.Index() + ) + for _, idx := range indices[:len(indices)-1] { + ptr, isPtr := t.Underlying().(*types.Pointer) + if isPtr { + t = ptr.Elem() + } + structType, ok := t.Underlying().(*types.Struct) + if !ok { + break + } + field := structType.Field(idx) + if !yield(field, isPtr) { + break + } + t = field.Type() + } + } +} diff --git a/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go index d612a710297..706ad33ef89 100644 --- a/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go +++ b/vendor/golang.org/x/tools/internal/typesinternal/zerovalue.go @@ -259,13 +259,13 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr { case *types.Signature: var params []*ast.Field for v := range t.Params().Variables() { + var names []*ast.Ident + if v.Name() != "" { + names = []*ast.Ident{ast.NewIdent(v.Name())} + } params = append(params, &ast.Field{ - Type: TypeExpr(v.Type(), qual), - Names: []*ast.Ident{ - { - Name: v.Name(), - }, - }, + Type: TypeExpr(v.Type(), qual), + Names: names, }) } if t.Variadic() { @@ -328,10 +328,10 @@ func TypeExpr(t types.Type, qual types.Qualifier) ast.Expr { return expr case *types.Struct: - return ast.NewIdent(t.String()) + return ast.NewIdent(types.TypeString(t, qual)) case *types.Interface: - return ast.NewIdent(t.String()) + return ast.NewIdent(types.TypeString(t, qual)) case *types.Union: if t.Len() == 0 { diff --git a/vendor/modules.txt b/vendor/modules.txt index 7dbb79f8944..50e5202327e 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -2296,7 +2296,7 @@ golang.org/x/image/vector golang.org/x/image/vp8 golang.org/x/image/vp8l golang.org/x/image/webp -# golang.org/x/mod v0.36.0 +# golang.org/x/mod v0.37.0 ## explicit; go 1.25.0 golang.org/x/mod/internal/lazyregexp golang.org/x/mod/module @@ -2349,7 +2349,7 @@ golang.org/x/sys/windows/svc/mgr # golang.org/x/term v0.44.0 ## explicit; go 1.25.0 golang.org/x/term -# golang.org/x/text v0.38.0 +# golang.org/x/text v0.39.0 ## explicit; go 1.25.0 golang.org/x/text/cases golang.org/x/text/encoding @@ -2377,7 +2377,7 @@ golang.org/x/text/width # golang.org/x/time v0.15.0 ## explicit; go 1.25.0 golang.org/x/time/rate -# golang.org/x/tools v0.45.0 +# golang.org/x/tools v0.47.0 ## explicit; go 1.25.0 golang.org/x/tools/cover golang.org/x/tools/go/ast/astutil diff --git a/web/package.json b/web/package.json index f88bcaa5262..3283ecfaa03 100644 --- a/web/package.json +++ b/web/package.json @@ -113,7 +113,10 @@ "js-cookie": "3.0.7", "linkify-it": "5.0.1", "minimatch@>=9.0.0 <9.0.7": "9.0.7", - "ws": "8.21.0" + "ws": "8.21.0", + "brace-expansion@>=1.0.0 <1.1.16": "1.1.16", + "brace-expansion@>=2.0.0 <2.1.2": "2.1.2", + "js-yaml@>=4.0.0 <4.3.0": "4.3.0" }, "patchedDependencies": { "caf@15.0.1": "patches/caf@15.0.1.patch", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 6830ba452bf..925503b5019 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -17,6 +17,9 @@ overrides: linkify-it: 5.0.1 minimatch@>=9.0.0 <9.0.7: 9.0.7 ws: 8.21.0 + brace-expansion@>=1.0.0 <1.1.16: 1.1.16 + brace-expansion@>=2.0.0 <2.1.2: 2.1.2 + js-yaml@>=4.0.0 <4.3.0: 4.3.0 patchedDependencies: caf@15.0.1: @@ -96,13 +99,13 @@ importers: version: 11.0.0 '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) '@vitest/coverage-v8': specifier: 4.1.5 version: 4.1.5(vitest@4.1.6) '@vitest/web-worker': specifier: 4.1.5 - version: 4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))) + version: 4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))) '@vue/compiler-dom': specifier: 3.5.29 version: 3.5.29 @@ -117,10 +120,10 @@ importers: version: 4.11.0 browserslist-to-esbuild: specifier: ^2.1.1 - version: 2.1.1(browserslist@4.28.5) + version: 2.1.1(browserslist@4.28.6) browserslist-useragent-regexp: specifier: ^4.1.3 - version: 4.1.3(browserslist@4.28.5) + version: 4.1.3(browserslist@4.28.6) commander: specifier: 14.0.2 version: 14.0.2 @@ -195,25 +198,25 @@ importers: version: 5.9.3 vite: specifier: 7.3.5 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vite-plugin-environment: specifier: ^1.1.3 - version: 1.1.3(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 1.1.3(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-node-polyfills: specifier: 0.24.0 - version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-static-copy: specifier: ^3.1.4 - version: 3.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 3.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-treat-umd-as-commonjs: specifier: 0.1.4 - version: 0.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 0.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vitest: specifier: 4.1.6 - version: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vitest-mock-extended: specifier: 3.1.0 - version: 3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))) + version: 3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))) vue: specifier: 3.5.29 version: 3.5.29(typescript@5.9.3) @@ -301,7 +304,7 @@ importers: version: link:../web-test-helpers '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) autoprefixer: specifier: 10.4.22 version: 10.4.22(postcss@8.5.16) @@ -379,13 +382,13 @@ importers: version: 4.1.1(webpack@5.99.9(postcss@8.5.16)) vite: specifier: ^7.3.5 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-node-polyfills: specifier: ^0.24.0 - version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) yaml: specifier: ^2.8.1 version: 2.8.2 @@ -434,13 +437,13 @@ importers: dependencies: '@vitejs/plugin-vue': specifier: ^6.0.6 - version: 6.0.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 6.0.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) rollup-plugin-serve: specifier: ^3.0.0 version: 3.0.0 vite: specifier: ^5 || ^6.0.0 || ^7.0.0 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) packages/prettier-config: dependencies: @@ -779,7 +782,7 @@ importers: version: 14.0.0 vitest-mock-extended: specifier: 3.1.0 - version: 3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))) + version: 3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))) vue: specifier: ^3.5.29 version: 3.5.29(typescript@5.9.3) @@ -932,19 +935,19 @@ importers: version: 3.7.1 '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) clean-publish: specifier: 6.0.1 version: 6.0.1 vite: specifier: 7.3.5 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-node-polyfills: specifier: 0.24.0 - version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) publishDirectory: package packages/web-pkg: @@ -1096,19 +1099,19 @@ importers: version: 4.17.12 '@vitest/web-worker': specifier: 4.1.5 - version: 4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))) + version: 4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))) clean-publish: specifier: 6.0.1 version: 6.0.1 vite: specifier: 7.3.5 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vite-plugin-dts: specifier: 4.5.4 - version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) vite-plugin-node-polyfills: specifier: 0.24.0 - version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) publishDirectory: package packages/web-runtime: @@ -1266,7 +1269,7 @@ importers: version: 1.18.1(debug@4.4.3) vitest-mock-extended: specifier: 3.1.1 - version: 3.1.1(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))) + version: 3.1.1(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))) vue: specifier: ^3.5.29 version: 3.5.29(typescript@5.9.3) @@ -1279,16 +1282,16 @@ importers: devDependencies: '@vitejs/plugin-vue': specifier: 6.0.1 - version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) + version: 6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3)) clean-publish: specifier: ^6.0.1 version: 6.0.1 vite: specifier: ^7.3.5 - version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + version: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vite-plugin-dts: specifier: ^4.5.4 - version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + version: 4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) publishDirectory: package tests/e2e: @@ -3759,8 +3762,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - baseline-browser-mapping@2.10.42: - resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} + baseline-browser-mapping@2.11.0: + resolution: {integrity: sha512-oCu2wfipvX3AePSgmOuKkIywOu+8n9psz7hXYmk56ghpu3+7KzNIBopaOs4c9BrtdnTtW30unG9GTfHo7EwERQ==} engines: {node: '>=6.0.0'} hasBin: true @@ -3789,11 +3792,11 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + brace-expansion@1.1.16: + resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@2.1.2: + resolution: {integrity: sha512-w5JZcKgdhDOgOwm8H+KgbosopHMuGcl6qbulwjtz3SM7I7P3yW1eAjzMPLrIE+NQ9vjgANKHWeMHnrT0OXW1oA==} brace-expansion@5.0.7: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} @@ -3848,8 +3851,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true - browserslist@4.28.5: - resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3896,8 +3899,8 @@ packages: caniuse-lite@1.0.30001791: resolution: {integrity: sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==} - caniuse-lite@1.0.30001803: - resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} + caniuse-lite@1.0.30001806: + resolution: {integrity: sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -4397,8 +4400,8 @@ packages: electron-to-chromium@1.5.349: resolution: {integrity: sha512-QsWVGyRuY07Aqb234QytTfwd5d9AJlfNIQ5wIOl1L+PZDzI9d9+Fn0FRale/QYlFxt/bUnB0/nLd1jFPGxGK1A==} - electron-to-chromium@1.5.388: - resolution: {integrity: sha512-Pl/aJaqOOxYxda3vcx1IKSJimwYXHDkEnGn0F+kG2EE68dDtx2uCinaS+Vih8Z91B9t8CSAbiF/HKyWcnXjhzw==} + electron-to-chromium@1.5.394: + resolution: {integrity: sha512-Wmt2Gm0o8JWBuGgmc4XZ0u9s1RaCRqhxP47phplmfg04+qypTUurpeJGP45A7Fhv7jdrrVH44PLlR9qXo37cVQ==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -4430,8 +4433,8 @@ packages: resolution: {integrity: sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==} engines: {node: '>=10.13.0'} - enhanced-resolve@5.24.2: - resolution: {integrity: sha512-rpsZEGT1jFuve6QlpyRp9ckQ+kN61hvF9BzCPyMdaKTm8UJce96KBn3sorXOFXlzjPrs3Vc4T1NsSroZ3PxlFw==} + enhanced-resolve@5.24.3: + resolution: {integrity: sha512-PwKooW9JUzh5chmYfHM3IQl5OkK2u2Nm011MgeZrss3JmFraUx/fqrf78kk8GUMYoibx/14MdwTl/1WKkG7TpQ==} engines: {node: '>=10.13.0'} entities@4.5.0: @@ -5295,8 +5298,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.3.0: + resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true jsdom@27.2.0: @@ -5770,8 +5773,8 @@ packages: node-releases@2.0.38: resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} - node-releases@2.0.50: - resolution: {integrity: sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==} + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} engines: {node: '>=18'} node-stdlib-browser@1.3.1: @@ -6795,8 +6798,8 @@ packages: uglify-js: optional: true - terser@5.48.0: - resolution: {integrity: sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==} + terser@5.49.0: + resolution: {integrity: sha512-SNiDnXyHSrxVcIOtVbULzcTmniUiwcV7Nwdyj1twVubeTmbjoa8p69KKDpfkdoOavuM4/GRm1+ykI8qqnavHoA==} engines: {node: '>=10'} hasBin: true @@ -8686,7 +8689,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.3.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -9858,16 +9861,16 @@ snapshots: '@vavt/util@2.1.2': {} - '@vitejs/plugin-vue@6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.1(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-beta.29 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) - '@vitejs/plugin-vue@6.0.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': + '@vitejs/plugin-vue@6.0.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))(vue@3.5.29(typescript@5.9.3))': dependencies: '@rolldown/pluginutils': 1.0.0-rc.13 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) vue: 3.5.29(typescript@5.9.3) '@vitest/coverage-v8@4.1.5(vitest@4.1.6)': @@ -9882,7 +9885,7 @@ snapshots: obug: 2.1.1 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) '@vitest/expect@4.1.6': dependencies: @@ -9893,13 +9896,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))': + '@vitest/mocker@4.1.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))': dependencies: '@vitest/spy': 4.1.6 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) '@vitest/pretty-format@4.1.5': dependencies: @@ -9935,10 +9938,10 @@ snapshots: convert-source-map: 2.0.0 tinyrainbow: 3.1.0 - '@vitest/web-worker@4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)))': + '@vitest/web-worker@4.1.5(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)))': dependencies: obug: 2.1.1 - vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) '@volar/language-core@2.4.23': dependencies: @@ -10423,7 +10426,7 @@ snapshots: baseline-browser-mapping@2.10.27: {} - baseline-browser-mapping@2.10.42: {} + baseline-browser-mapping@2.11.0: {} bidi-js@1.0.3: dependencies: @@ -10443,12 +10446,12 @@ snapshots: boolbase@1.0.0: {} - brace-expansion@1.1.11: + brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: + brace-expansion@2.1.2: dependencies: balanced-match: 1.0.2 @@ -10511,15 +10514,15 @@ snapshots: dependencies: pako: 1.0.11 - browserslist-to-esbuild@2.1.1(browserslist@4.28.5): + browserslist-to-esbuild@2.1.1(browserslist@4.28.6): dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 meow: 13.2.0 - browserslist-useragent-regexp@4.1.3(browserslist@4.28.5): + browserslist-useragent-regexp@4.1.3(browserslist@4.28.6): dependencies: argue-cli: 2.1.0 - browserslist: 4.28.5 + browserslist: 4.28.6 easy-table: 1.2.0 picocolors: 1.1.1 regexp-tree: 0.1.27 @@ -10533,13 +10536,13 @@ snapshots: node-releases: 2.0.38 update-browserslist-db: 1.2.3(browserslist@4.28.2) - browserslist@4.28.5: + browserslist@4.28.6: dependencies: - baseline-browser-mapping: 2.10.42 - caniuse-lite: 1.0.30001803 - electron-to-chromium: 1.5.388 - node-releases: 2.0.50 - update-browserslist-db: 1.2.3(browserslist@4.28.5) + baseline-browser-mapping: 2.11.0 + caniuse-lite: 1.0.30001806 + electron-to-chromium: 1.5.394 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) buffer-from@1.1.2: {} @@ -10590,7 +10593,7 @@ snapshots: caniuse-lite@1.0.30001791: {} - caniuse-lite@1.0.30001803: {} + caniuse-lite@1.0.30001806: {} chai@6.2.2: {} @@ -10757,7 +10760,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.3.0 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 @@ -11136,7 +11139,7 @@ snapshots: electron-to-chromium@1.5.349: {} - electron-to-chromium@1.5.388: {} + electron-to-chromium@1.5.394: {} elliptic@6.6.1: dependencies: @@ -11169,7 +11172,7 @@ snapshots: graceful-fs: 4.2.11 tapable: 2.3.3 - enhanced-resolve@5.24.2: + enhanced-resolve@5.24.3: dependencies: graceful-fs: 4.2.11 tapable: 2.3.3 @@ -12133,7 +12136,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@4.3.0: dependencies: argparse: 2.0.1 @@ -12537,15 +12540,15 @@ snapshots: minimatch@3.0.8: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.16 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.11 + brace-expansion: 1.1.16 minimatch@5.1.6: dependencies: - brace-expansion: 2.0.1 + brace-expansion: 2.1.2 minimatch@9.0.7: dependencies: @@ -12612,7 +12615,7 @@ snapshots: node-releases@2.0.38: {} - node-releases@2.0.50: {} + node-releases@2.0.51: {} node-stdlib-browser@1.3.1: dependencies: @@ -13694,12 +13697,12 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 - terser: 5.48.0 + terser: 5.49.0 webpack: 5.99.9(postcss@8.5.16) optionalDependencies: postcss: 8.5.16 - terser@5.48.0: + terser@5.49.0: dependencies: '@jridgewell/source-map': 0.3.11 acorn: 8.17.0 @@ -13910,9 +13913,9 @@ snapshots: escalade: 3.2.0 picocolors: 1.1.1 - update-browserslist-db@1.2.3(browserslist@4.28.5): + update-browserslist-db@1.2.3(browserslist@4.28.6): dependencies: - browserslist: 4.28.5 + browserslist: 4.28.6 escalade: 3.2.0 picocolors: 1.1.1 @@ -13978,7 +13981,7 @@ snapshots: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - vite-plugin-dts@4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vite-plugin-dts@4.5.4(@types/node@24.13.2)(rollup@4.62.2)(typescript@5.9.3)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: '@microsoft/api-extractor': 7.52.8(@types/node@24.13.2) '@rollup/pluginutils': 5.1.4(rollup@4.62.2) @@ -13991,38 +13994,38 @@ snapshots: magic-string: 0.30.21 typescript: 5.9.3 optionalDependencies: - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) transitivePeerDependencies: - '@types/node' - rollup - supports-color - vite-plugin-environment@1.1.3(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vite-plugin-environment@1.1.3(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) - vite-plugin-node-polyfills@0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vite-plugin-node-polyfills@0.24.0(rollup@4.62.2)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.62.2) node-stdlib-browser: 1.3.1 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) transitivePeerDependencies: - rollup - vite-plugin-static-copy@3.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vite-plugin-static-copy@3.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: chokidar: 3.6.0 p-map: 7.0.3 picocolors: 1.1.1 tinyglobby: 0.2.16 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) - vite-plugin-treat-umd-as-commonjs@0.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vite-plugin-treat-umd-as-commonjs@0.1.4(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: '@rollup/pluginutils': 4.2.1 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) - vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2): + vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2): dependencies: esbuild: 0.27.7 fdir: 6.5.0(picomatch@4.0.4) @@ -14034,25 +14037,25 @@ snapshots: '@types/node': 24.13.2 fsevents: 2.3.3 sass: 1.94.1 - terser: 5.48.0 + terser: 5.49.0 yaml: 2.8.2 - vitest-mock-extended@3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))): + vitest-mock-extended@3.1.0(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))): dependencies: ts-essentials: 10.0.4(typescript@5.9.3) typescript: 5.9.3 - vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) - vitest-mock-extended@3.1.1(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2))): + vitest-mock-extended@3.1.1(typescript@5.9.3)(vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2))): dependencies: ts-essentials: 10.0.4(typescript@5.9.3) typescript: 5.9.3 - vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + vitest: 4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) - vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)): + vitest@4.1.6(@types/node@24.13.2)(@vitest/coverage-v8@4.1.5(vitest@4.1.6))(happy-dom@20.8.9)(jsdom@27.2.0)(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)): dependencies: '@vitest/expect': 4.1.6 - '@vitest/mocker': 4.1.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2)) + '@vitest/mocker': 4.1.6(vite@7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2)) '@vitest/pretty-format': 4.1.6 '@vitest/runner': 4.1.6 '@vitest/snapshot': 4.1.6 @@ -14069,7 +14072,7 @@ snapshots: tinyexec: 1.1.2 tinyglobby: 0.2.16 tinyrainbow: 3.1.0 - vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.48.0)(yaml@2.8.2) + vite: 7.3.5(@types/node@24.13.2)(sass@1.94.1)(terser@5.49.0)(yaml@2.8.2) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.13.2 @@ -14226,9 +14229,9 @@ snapshots: '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.17.0 - browserslist: 4.28.5 + browserslist: 4.28.6 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.24.2 + enhanced-resolve: 5.24.3 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0