From 79da177aa0200c8748fee6b260e83716b916acf7 Mon Sep 17 00:00:00 2001 From: Yifeng Wang Date: Wed, 6 Dec 2023 17:38:27 +0800 Subject: [PATCH] chore: merge canary --- .github/actions/build-rust/action.yml | 48 +- .github/actions/download-core/action.yml | 22 + .github/actions/setup-node/action.yml | 41 +- .github/workflows/build-desktop.yml | 190 ------ .github/workflows/build-server.yml | 311 ---------- .github/workflows/build-test.yml | 599 +++++++++++++++++++ .github/workflows/build.yml | 199 ------ .github/workflows/cancel.yml | 18 - .github/workflows/codeql.yml | 70 --- packages/backend/storage/index.d.ts | 35 +- packages/backend/storage/package.json | 9 +- packages/frontend/native/index.d.ts | 59 +- packages/frontend/native/index.js | 68 ++- packages/frontend/native/package.json | 6 +- tests/affine-cloud/e2e/basic.spec.ts | 142 ----- tests/affine-cloud/e2e/collaboration.spec.ts | 440 +++++--------- tests/affine-cloud/e2e/login.spec.ts | 110 +++- tests/affine-cloud/e2e/migration.spec.ts | 111 ++++ tests/affine-cloud/e2e/workspace.spec.ts | 96 +++ yarn.lock | 596 +++++++++++++++++- 20 files changed, 1822 insertions(+), 1348 deletions(-) create mode 100644 .github/actions/download-core/action.yml delete mode 100644 .github/workflows/build-desktop.yml delete mode 100644 .github/workflows/build-server.yml create mode 100644 .github/workflows/build-test.yml delete mode 100644 .github/workflows/build.yml delete mode 100644 .github/workflows/cancel.yml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 tests/affine-cloud/e2e/basic.spec.ts create mode 100644 tests/affine-cloud/e2e/migration.spec.ts create mode 100644 tests/affine-cloud/e2e/workspace.spec.ts diff --git a/.github/actions/build-rust/action.yml b/.github/actions/build-rust/action.yml index f187641f85104..08f230cef90ac 100644 --- a/.github/actions/build-rust/action.yml +++ b/.github/actions/build-rust/action.yml @@ -29,6 +29,11 @@ runs: env: CARGO_INCREMENTAL: '1' + - name: Set CC + if: ${{ contains(inputs.target, 'linux') && inputs.package != '@affine/native' }} + shell: bash + run: echo "CC=clang" >> "$GITHUB_ENV" + - name: Cache cargo uses: actions/cache@v3 with: @@ -36,51 +41,12 @@ runs: ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ - .cargo-cache + ~/.napi-rs target/${{ inputs.target }} key: stable-${{ inputs.target }}-cargo-cache - name: Build - if: ${{ inputs.target != 'x86_64-unknown-linux-gnu' && inputs.target != 'aarch64-unknown-linux-gnu' }} shell: bash run: | - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} + yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} --use-napi-cross env: NX_CLOUD_ACCESS_TOKEN: ${{ inputs.nx_token }} - - - name: Build - if: ${{ inputs.target == 'x86_64-unknown-linux-gnu' }} - uses: addnab/docker-run-action@v3 - with: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian - options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }} - run: | - export CC=x86_64-unknown-linux-gnu-gcc - export CC_x86_64_unknown_linux_gnu=x86_64-unknown-linux-gnu-gcc - rm -rf /usr/local/rustup/downloads/* - rustup target add x86_64-unknown-linux-gnu - export RUSTFLAGS="-C debuginfo=1" - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} - if [ -d "node_modules/.cache" ]; then - chmod -R 777 node_modules/.cache - fi - if [ -d "target" ]; then - chmod -R 777 target; - fi - - - name: Build - if: ${{ inputs.target == 'aarch64-unknown-linux-gnu' }} - uses: addnab/docker-run-action@v3 - with: - image: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian-aarch64 - options: --user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build -e NX_CLOUD_ACCESS_TOKEN=${{ inputs.nx_token }} - run: | - export RUSTFLAGS="-C debuginfo=1" - rm -rf /usr/local/rustup/downloads/* - rustup target add aarch64-unknown-linux-gnu - yarn workspace ${{ inputs.package }} nx build ${{ inputs.package }} --target ${{ inputs.target }} - if [ -d "node_modules/.cache" ]; then - chmod -R 777 node_modules/.cache - fi - if [ -d "target" ]; then - chmod -R 777 target; - fi diff --git a/.github/actions/download-core/action.yml b/.github/actions/download-core/action.yml new file mode 100644 index 0000000000000..c70fcfbe9b80c --- /dev/null +++ b/.github/actions/download-core/action.yml @@ -0,0 +1,22 @@ +name: 'Download core artifacts' +description: 'Download core artifacts and extract to dist' +inputs: + path: + description: 'Path to extract' + required: true + +runs: + using: 'composite' + steps: + - name: Download tar.gz + uses: actions/download-artifact@v3 + with: + name: core + path: . + + - name: Extract core artifacts + shell: bash + run: | + mkdir -p ${{ inputs.path }} + tar -xvf dist.tar.gz --directory ${{ inputs.path }} + rm dist.tar.gz diff --git a/.github/actions/setup-node/action.yml b/.github/actions/setup-node/action.yml index c9dbcdba327fe..f18caccede500 100644 --- a/.github/actions/setup-node/action.yml +++ b/.github/actions/setup-node/action.yml @@ -36,6 +36,9 @@ inputs: description: 'Set enableScripts in .yarnrc.yml' required: false default: 'true' + full-cache: + description: 'Full installation cache' + required: false runs: using: 'composite' @@ -46,7 +49,6 @@ runs: node-version-file: '.nvmrc' registry-url: https://npm.pkg.github.com scope: '@toeverything' - cache: 'yarn' - name: Set nmMode if: ${{ inputs.hard-link-nm == 'false' }} @@ -63,6 +65,29 @@ runs: shell: bash run: yarn config set enableScripts false + - name: Set yarn global cache path + shell: bash + id: yarn-cache + run: node -e "const p = $(yarn config cacheFolder --json).effective; console.log('yarn_global_cache=' + p)" >> $GITHUB_OUTPUT + + - name: Cache non-full yarn cache + uses: actions/cache@v3 + if: ${{ inputs.full-cache != 'true' }} + with: + path: | + node_modules + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-${{ github.job }}-${{ runner.os }} + + - name: Cache full yarn cache + uses: actions/cache@v3 + if: ${{ inputs.full-cache == 'true' }} + with: + path: | + node_modules + ${{ steps.yarn-cache.outputs.yarn_global_cache }} + key: node_modules-cache-full-${{ runner.os }} + - name: yarn install if: ${{ inputs.package-install == 'true' }} continue-on-error: true @@ -102,8 +127,8 @@ runs: id: playwright-cache if: ${{ inputs.playwright-install == 'true' }} with: - path: '~/.cache/ms-playwright' - key: '${{ runner.os }}-${{ runner.arch }}-playwright-${{ steps.playwright-version.outputs.version }}' + path: ${{ github.workspace }}/node_modules/.cache/ms-playwright + key: '${{ runner.os }}-playwright-${{ steps.playwright-version.outputs.version }}' # As a fallback, if the Playwright version has changed, try use the # most recently cached version. There's a good chance that at least one # of the browser binary versions haven't been updated, so Playwright can @@ -113,7 +138,7 @@ runs: # date cache, but still let Playwright decide if it needs to download # new binaries or not. restore-keys: | - ${{ runner.os }}-${{ runner.arch }}-playwright- + ${{ runner.os }}-playwright- # If the Playwright browser binaries weren't able to be restored, we tell # playwright to install everything for us. @@ -121,6 +146,8 @@ runs: shell: bash if: inputs.playwright-install == 'true' run: yarn playwright install --with-deps chromium + env: + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright - name: Get installed Electron version id: electron-version @@ -134,16 +161,16 @@ runs: if: ${{ inputs.electron-install == 'true' }} with: path: 'node_modules/.cache/electron' - key: '${{ runner.os }}-{{ runner.arch }}-electron-${{ steps.electron-version.outputs.version }}' + key: '${{ runner.os }}-electron-${{ steps.electron-version.outputs.version }}' restore-keys: | - ${{ runner.os }}-{{ runner.arch }}-electron- + ${{ runner.os }}-electron- - name: Install Electron binary shell: bash if: inputs.electron-install == 'true' run: node ./node_modules/electron/install.js env: - ELECTRON_OVERRIDE_DIST_PATH: ./node_modules/.cache/electron + electron_config_cache: ./node_modules/.cache/electron - name: Build Infra shell: bash diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml deleted file mode 100644 index 8017e0edf715b..0000000000000 --- a/.github/workflows/build-desktop.yml +++ /dev/null @@ -1,190 +0,0 @@ -name: Build(Desktop) & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-desktop.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-desktop.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - COVERAGE: true - DISTRIBUTION: desktop - MACOSX_DEPLOYMENT_TARGET: '10.13' - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - build-core: - name: Build @affine/core - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - name: Build Core - run: yarn nx build @affine/core - - name: Upload core artifact - uses: actions/upload-artifact@v3 - with: - name: core - path: ./packages/frontend/core/dist - if-no-files-found: error - - build-native: - name: Build Native - runs-on: ubuntu-latest - needs: build-core - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - name: Run tests - run: yarn test - working-directory: ./packages/frontend/native - - desktop-test: - name: Desktop Test - runs-on: ${{ matrix.spec.os }} - strategy: - fail-fast: false - # all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64 - matrix: - spec: - - { - os: macos-latest, - platform: macos, - arch: x64, - target: x86_64-apple-darwin, - test: true, - } - - { - os: macos-latest, - platform: macos, - arch: arm64, - target: aarch64-apple-darwin, - test: false, - } - - { - os: ubuntu-latest, - platform: linux, - arch: x64, - target: x86_64-unknown-linux-gnu, - test: true, - } - - { - os: windows-latest, - platform: windows, - arch: x64, - target: x86_64-pc-windows-msvc, - test: true, - } - needs: build-core - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - timeout-minutes: 10 - with: - extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop - playwright-install: true - hard-link-nm: false - enableScripts: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: ${{ matrix.spec.target }} - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Run unit tests - if: ${{ matrix.spec.test }} - shell: bash - run: yarn vitest - working-directory: packages/frontend/electron - - - name: Download core artifact - uses: actions/download-artifact@v3 - with: - name: core - path: packages/frontend/electron/resources/web-static - - - name: Build Desktop Layers - run: yarn workspace @affine/electron build - - - name: Run desktop tests - if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }} - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e - env: - COVERAGE: true - - - name: Run desktop tests - if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }} - run: yarn workspace @affine-test/affine-desktop e2e - env: - COVERAGE: true - - - name: Make bundle - if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} - env: - SKIP_BUNDLE: true - SKIP_WEB_BUILD: true - HOIST_NODE_MODULES: 1 - run: yarn workspace @affine/electron package --platform=darwin --arch=arm64 - - - name: Output check - if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} - run: | - yarn workspace @affine/electron ts-node ./scripts/macos-arm64-output-check.ts - - - name: Collect code coverage report - if: ${{ matrix.spec.test }} - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - if: ${{ matrix.spec.test }} - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2etest-${{ matrix.spec.os }}-${{ matrix.spec.arch }} - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }} - path: ./test-results - if-no-files-found: ignore diff --git a/.github/workflows/build-server.yml b/.github/workflows/build-server.yml deleted file mode 100644 index 9fb88605c49b0..0000000000000 --- a/.github/workflows/build-server.yml +++ /dev/null @@ -1,311 +0,0 @@ -name: Build(Server) & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-server.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build-server.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - COVERAGE: true - DISTRIBUTION: browser - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - build-storage: - name: Build Storage - runs-on: ubuntu-latest - env: - RUSTFLAGS: '-C debuginfo=1' - - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - extra-flags: workspaces focus @affine/storage - electron-install: false - build-infra: false - build-plugins: false - - name: Build Rust - uses: ./.github/actions/build-rust - with: - target: 'x86_64-unknown-linux-gnu' - package: '@affine/storage' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - name: Upload storage.node - uses: actions/upload-artifact@v3 - with: - name: storage.node - path: ./packages/backend/storage/storage.node - if-no-files-found: error - - server-test: - name: Server Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server exec prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Run server tests - run: yarn workspace @affine/server test:coverage - env: - CARGO_TARGET_DIR: '${{ github.workspace }}/target' - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Upload server test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./packages/backend/server/.coverage/lcov.info - flags: server-test - name: affine - fail_ci_if_error: false - - server-e2e-test: - name: Server E2E Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server exec prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Run playwright tests - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-cloud e2e --forbid-only - env: - COVERAGE: true - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: server-e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-server - path: ./tests/affine-cloud/test-results - if-no-files-found: ignore - - server-desktop-e2e-test: - name: Server Desktop E2E Test - runs-on: ubuntu-latest - needs: build-storage - services: - postgres: - image: postgres - env: - POSTGRES_PASSWORD: affine - options: >- - --health-cmd pg_isready - --health-interval 10s - --health-timeout 5s - --health-retries 5 - ports: - - 5432:5432 - mailer: - image: mailhog/mailhog - ports: - - 1025:1025 - - 8025:8025 - steps: - - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - hard-link-nm: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Initialize database - run: | - psql -h localhost -U postgres -c "CREATE DATABASE affine;" - psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" - psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" - env: - PGPASSWORD: affine - - - name: Generate prisma client - run: | - yarn workspace @affine/server exec prisma generate - yarn workspace @affine/server prisma db push - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Run init-db script - run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts - env: - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - - - name: Download storage.node - uses: actions/download-artifact@v3 - with: - name: storage.node - path: ./packages/backend/server - - - name: Build Plugins - run: yarn run build:plugins - - - name: Build Desktop Layers - run: yarn workspace @affine/electron build:dev - - - name: Run playwright tests - run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" yarn workspace @affine-test/affine-desktop-cloud e2e - env: - COVERAGE: true - DEV_SERVER_URL: http://localhost:8080 - DATABASE_URL: postgresql://affine:affine@localhost:5432/affine - ENABLE_LOCAL_EMAIL: true - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: server-e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-server - path: ./tests/affine-cloud/test-results - if-no-files-found: ignore diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000000000..7b76cb92cb434 --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,599 @@ +name: Build & Test + +on: + push: + branches: + - canary + - v[0-9]+.[0-9]+.x-staging + - v[0-9]+.[0-9]+.x + paths-ignore: + - README.md + pull_request: + +env: + DEBUG: napi:* + BUILD_TYPE: canary + APP_NAME: affine + AFFINE_ENV: dev + COVERAGE: true + MACOSX_DEPLOYMENT_TARGET: '10.13' + NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/node_modules/.cache/ms-playwright + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ['javascript', 'typescript'] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs + # queries: security-extended,security-and-quality + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v2 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 + lint: + name: Lint + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Run oxlint + # oxlint is fast, so wrong code will fail quickly + run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'])") + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + - name: Run i18n codegen + run: yarn i18n-codegen gen + - name: Run ESLint + run: yarn lint:eslint --max-warnings=0 + - name: Run Prettier + # Set nmMode in `actions/setup-node` will modify the .yarnrc.yml + run: | + git checkout .yarnrc.yml + yarn lint:prettier + - name: Run Type Check + run: yarn typecheck + + check-yarn-binary: + name: Check yarn binary + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run check + run: | + yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])") + git diff --exit-code + + e2e-plugin-test: + name: E2E Plugin Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + - name: Run playwright tests + run: yarn e2e --forbid-only + working-directory: tests/affine-plugin + env: + COVERAGE: true + - name: Collect code coverage report + run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov + + - name: Upload e2e test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./.coverage/lcov.info + flags: e2e-plugin-test + name: affine + fail_ci_if_error: false + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-plugin + path: ./test-results + if-no-files-found: ignore + + e2e-test: + name: E2E Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5] + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + + - name: Run playwright tests + run: yarn workspace @affine-test/affine-local e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }} + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-${{ matrix.shard }} + path: ./test-results + if-no-files-found: ignore + + e2e-migration-test: + name: E2E Migration Test + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + electron-install: false + full-cache: true + + - name: Run playwright tests + run: yarn workspace @affine-test/affine-migration e2e --forbid-only + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-migration + path: ./tests/affine-migration/test-results + if-no-files-found: ignore + + unit-test: + name: Unit Test + runs-on: ubuntu-latest + needs: + - build-native + env: + DISTRIBUTION: browser + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + + - name: Download affine.linux-x64-gnu.node + uses: actions/download-artifact@v3 + with: + name: affine.linux-x64-gnu.node + path: ./packages/frontend/native + + - name: Unit Test + run: yarn nx test:coverage @affine/monorepo + + - name: Upload unit test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./.coverage/store/lcov.info + flags: unittest + name: affine + fail_ci_if_error: false + + build-native: + name: Build AFFiNE native (${{ matrix.spec.target }}) + runs-on: ${{ matrix.spec.os }} + env: + CARGO_PROFILE_RELEASE_DEBUG: '1' + strategy: + fail-fast: false + matrix: + spec: + - { os: ubuntu-latest, target: x86_64-unknown-linux-gnu } + - { os: windows-latest, target: x86_64-pc-windows-msvc } + - { os: macos-latest, target: x86_64-apple-darwin } + - { os: macos-latest, target: aarch64-apple-darwin } + + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + extra-flags: workspaces focus @affine/native + electron-install: false + build-infra: false + build-plugins: false + - name: Setup filename + id: filename + shell: bash + run: | + export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)") + echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT" + - name: Build AFFiNE native + uses: ./.github/actions/build-rust + with: + target: ${{ matrix.spec.target }} + package: '@affine/native' + nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + - name: Upload ${{ steps.filename.outputs.filename }} + uses: actions/upload-artifact@v3 + with: + name: ${{ steps.filename.outputs.filename }} + path: ./packages/frontend/native/${{ steps.filename.outputs.filename }} + if-no-files-found: error + + build-storage: + name: Build Storage + runs-on: ubuntu-latest + env: + CARGO_PROFILE_RELEASE_DEBUG: '1' + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + extra-flags: workspaces focus @affine/storage + electron-install: false + build-infra: false + build-plugins: false + - name: Build Rust + uses: ./.github/actions/build-rust + with: + target: 'x86_64-unknown-linux-gnu' + package: '@affine/storage' + nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} + - name: Upload storage.node + uses: actions/upload-artifact@v3 + with: + name: storage.node + path: ./packages/backend/storage/storage.node + if-no-files-found: error + + build-core: + name: Build @affine/core + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + build-plugins: false + full-cache: true + - name: Build Core + # always skip cache because its fast, and cache configuration is always changing + run: yarn nx build @affine/core --skip-nx-cache + - name: zip core + run: tar -czf dist.tar.gz --directory=packages/frontend/core/dist . + - name: Upload core artifact + uses: actions/upload-artifact@v3 + with: + name: core + path: dist.tar.gz + if-no-files-found: error + + server-test: + name: Server Test + runs-on: ubuntu-latest + needs: build-storage + env: + DISTRIBUTION: browser + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: affine + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + mailer: + image: mailhog/mailhog + ports: + - 1025:1025 + - 8025:8025 + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + electron-install: false + full-cache: true + + - name: Initialize database + run: | + psql -h localhost -U postgres -c "CREATE DATABASE affine;" + psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" + psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" + env: + PGPASSWORD: affine + + - name: Generate prisma client + run: | + yarn workspace @affine/server exec prisma generate + yarn workspace @affine/server exec prisma db push + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Run init-db script + run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Download storage.node + uses: actions/download-artifact@v3 + with: + name: storage.node + path: ./packages/backend/server + + - name: Run server tests + run: yarn workspace @affine/server test:coverage + env: + CARGO_TARGET_DIR: '${{ github.workspace }}/target' + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Upload server test coverage results + uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ./packages/backend/server/.coverage/lcov.info + flags: server-test + name: affine + fail_ci_if_error: false + + server-e2e-test: + name: ${{ matrix.tests.name }} + runs-on: ubuntu-latest + env: + DISTRIBUTION: browser + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + strategy: + fail-fast: false + matrix: + tests: + - name: 'Server E2E Test 1/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=1/3 + - name: 'Server E2E Test 2/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=2/3 + - name: 'Server E2E Test 3/3' + script: yarn workspace @affine-test/affine-cloud e2e --forbid-only --shard=3/3 + - name: 'Server Desktop E2E Test' + script: | + yarn workspace @affine/electron build:dev + xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop-cloud e2e + needs: + - build-storage + - build-native + services: + postgres: + image: postgres + env: + POSTGRES_PASSWORD: affine + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + mailer: + image: mailhog/mailhog + ports: + - 1025:1025 + - 8025:8025 + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: ./.github/actions/setup-node + with: + playwright-install: true + hard-link-nm: false + + - name: Initialize database + run: | + psql -h localhost -U postgres -c "CREATE DATABASE affine;" + psql -h localhost -U postgres -c "CREATE USER affine WITH PASSWORD 'affine';" + psql -h localhost -U postgres -c "ALTER USER affine WITH SUPERUSER;" + env: + PGPASSWORD: affine + + - name: Generate prisma client + run: | + yarn workspace @affine/server exec prisma generate + yarn workspace @affine/server exec prisma db push + env: + DATABASE_URL: postgresql://affine:affine@localhost:5432/affine + + - name: Run init-db script + run: yarn workspace @affine/server exec ts-node ./scripts/init-db.ts + - name: Download storage.node + uses: actions/download-artifact@v3 + with: + name: storage.node + path: ./packages/backend/server + + - name: Download affine.linux-x64-gnu.node + uses: actions/download-artifact@v3 + with: + name: affine.linux-x64-gnu.node + path: ./packages/frontend/native + + - name: ${{ matrix.tests.name }} + run: | + ${{ matrix.tests.script }} + env: + DEV_SERVER_URL: http://localhost:8080 + ENABLE_LOCAL_EMAIL: true + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-server + path: ./tests/affine-cloud/test-results + if-no-files-found: ignore + + desktop-test: + name: Desktop Test (${{ matrix.spec.os }}, ${{ matrix.spec.platform }}, ${{ matrix.spec.arch }}, ${{ matrix.spec.target }}, ${{ matrix.spec.test }}) + runs-on: ${{ matrix.spec.os }} + strategy: + fail-fast: false + # all combinations: macos-latest x64, macos-latest arm64, windows-latest x64, ubuntu-latest x64 + matrix: + spec: + - { + os: macos-latest, + platform: macos, + arch: x64, + target: x86_64-apple-darwin, + test: true, + } + - { + os: macos-latest, + platform: macos, + arch: arm64, + target: aarch64-apple-darwin, + test: false, + } + - { + os: ubuntu-latest, + platform: linux, + arch: x64, + target: x86_64-unknown-linux-gnu, + test: true, + } + - { + os: windows-latest, + platform: windows, + arch: x64, + target: x86_64-pc-windows-msvc, + test: true, + } + needs: + - build-core + - build-native + steps: + - uses: actions/checkout@v4 + - name: Setup Node.js + uses: ./.github/actions/setup-node + timeout-minutes: 10 + with: + extra-flags: workspaces focus @affine/electron @affine/monorepo @affine-test/affine-desktop + playwright-install: true + hard-link-nm: false + enableScripts: false + + - name: Setup filename + id: filename + shell: bash + run: | + export PLATFORM_ARCH_ABI=$(node -e "console.log(require('@napi-rs/cli').parseTriple('${{ matrix.spec.target }}').platformArchABI)") + echo "filename=affine.$PLATFORM_ARCH_ABI.node" >> "$GITHUB_OUTPUT" + + - name: Download ${{ steps.filename.outputs.filename }} + uses: actions/download-artifact@v3 + with: + name: ${{ steps.filename.outputs.filename }} + path: ./packages/frontend/native + + - name: Run unit tests + if: ${{ matrix.spec.test }} + shell: bash + run: yarn vitest + working-directory: packages/frontend/electron + + - name: Download core artifact + uses: ./.github/actions/download-core + with: + path: packages/frontend/electron/resources/web-static + + - name: Build Desktop Layers + run: yarn workspace @affine/electron build + + - name: Run desktop tests + if: ${{ matrix.spec.test && matrix.spec.os == 'ubuntu-latest' }} + run: xvfb-run --auto-servernum --server-args="-screen 0 1280x960x24" -- yarn workspace @affine-test/affine-desktop e2e + + - name: Run desktop tests + if: ${{ matrix.spec.test && matrix.spec.os != 'ubuntu-latest' }} + run: yarn workspace @affine-test/affine-desktop e2e + + - name: Make bundle + if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} + env: + SKIP_BUNDLE: true + SKIP_WEB_BUILD: true + HOIST_NODE_MODULES: 1 + run: yarn workspace @affine/electron package --platform=darwin --arch=arm64 + + - name: Output check + if: ${{ matrix.spec.os == 'macos-latest' && matrix.spec.arch == 'arm64' }} + run: | + yarn workspace @affine/electron ts-node ./scripts/macos-arm64-output-check.ts + + - name: Upload test results + if: ${{ failure() }} + uses: actions/upload-artifact@v3 + with: + name: test-results-e2e-${{ matrix.spec.os }}-${{ matrix.spec.arch }} + path: ./test-results + if-no-files-found: ignore diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index ee01a2e891cd1..0000000000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,199 +0,0 @@ -name: Build & Test - -on: - push: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - pull_request: - merge_group: - branches: - - canary - - v[0-9]+.[0-9]+.x-staging - - v[0-9]+.[0-9]+.x - paths-ignore: - - README.md - - .github/** - - '!.github/workflows/build.yml' - - '!.github/actions/build-rust/action.yml' - - '!.github/actions/setup-node/action.yml' - -env: - DEBUG: napi:* - BUILD_TYPE: canary - APP_NAME: affine - AFFINE_ENV: dev - COVERAGE: true - DISTRIBUTION: browser - MACOSX_DEPLOYMENT_TARGET: '10.13' - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - -jobs: - lint: - name: Lint - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - name: Run oxlint - # oxlint is fast, so wrong code will fail quickly - run: yarn dlx $(node -e "console.log(require('./package.json').scripts['lint:ox'])") - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - name: Run i18n codegen - run: yarn i18n-codegen gen - - name: Run ESLint - run: yarn lint:eslint --max-warnings=0 - - name: Run Prettier - # Set nmMode in `actions/setup-node` will modify the .yarnrc.yml - run: | - git checkout .yarnrc.yml - yarn lint:prettier - - name: Run Type Check - run: yarn typecheck - - check-yarn-binary: - name: Check yarn binary - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run check - run: | - yarn set version $(node -e "console.log(require('./package.json').packageManager.split('@')[1])") - git diff --exit-code - - e2e-plugin-test: - name: E2E Plugin Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - name: Run playwright tests - run: yarn e2e --forbid-only - working-directory: tests/affine-plugin - env: - COVERAGE: true - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2e-plugin-test - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-plugin - path: ./test-results - if-no-files-found: ignore - - e2e-test: - name: E2E Test - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4, 5] - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - - name: Run playwright tests - run: yarn e2e --forbid-only --shard=${{ matrix.shard }}/${{ strategy.job-total }} - working-directory: tests/affine-local - env: - COVERAGE: true - - - name: Collect code coverage report - run: yarn exec nyc report -t .nyc_output --report-dir .coverage --reporter=lcov - - - name: Upload e2e test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/lcov.info - flags: e2etest - name: affine - fail_ci_if_error: false - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-${{ matrix.shard }} - path: ./test-results - if-no-files-found: ignore - - e2e-migration-test: - name: E2E Migration Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - playwright-install: true - electron-install: false - - - name: Run playwright tests - run: yarn workspace @affine-test/affine-migration e2e --forbid-only - - - name: Upload test results - if: ${{ failure() }} - uses: actions/upload-artifact@v3 - with: - name: test-results-e2e-migration - path: ./tests/affine-migration/test-results - if-no-files-found: ignore - - unit-test: - name: Unit Test - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Setup Node.js - uses: ./.github/actions/setup-node - with: - electron-install: false - - - name: Build AFFiNE native - uses: ./.github/actions/build-rust - with: - target: x86_64-unknown-linux-gnu - package: '@affine/native' - nx_token: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} - - - name: Unit Test - run: yarn nx test:coverage @affine/monorepo - - - name: Upload unit test coverage results - uses: codecov/codecov-action@v3 - with: - token: ${{ secrets.CODECOV_TOKEN }} - files: ./.coverage/store/lcov.info - flags: unittest - name: affine - fail_ci_if_error: false diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml deleted file mode 100644 index 4924afafe0c76..0000000000000 --- a/.github/workflows/cancel.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: Cancel -on: - pull_request_target: - types: - - edited - - synchronize - -jobs: - cancel: - name: 'Cancel Previous Runs' - runs-on: ubuntu-latest - timeout-minutes: 2 - steps: - - uses: styfle/cancel-workflow-action@0.12.0 - with: - # See https://api.github.com/repos/toeverything/AFFiNE/actions/workflows - workflow_id: 44038251, 61883931, 65188160, 66789140 - access_token: ${{ github.token }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 3e219c0dceef1..0000000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,70 +0,0 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# -name: 'CodeQL' - -on: - push: - branches: [canary] - pull_request: - merge_group: - # The branches below must be a subset of the branches above - branches: [canary] - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-latest - permissions: - actions: read - contents: read - security-events: write - - strategy: - fail-fast: false - matrix: - language: ['javascript'] - # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] - # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # If the Autobuild fails above, remove it and uncomment the following three lines. - # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - - # - run: | - # echo "Run, Build Application using script" - # ./location_of_script_within_repo/buildscript.sh - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/packages/backend/storage/index.d.ts b/packages/backend/storage/index.d.ts index bd6060f9fb2c0..28e5de90c0620 100644 --- a/packages/backend/storage/index.d.ts +++ b/packages/backend/storage/index.d.ts @@ -1,21 +1,6 @@ -/* tslint:disable */ -/* eslint-disable */ - /* auto-generated by NAPI-RS */ +/* eslint-disable */ -export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise -export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise -export interface Blob { - contentType: string - lastModified: string - size: number - data: Buffer -} -/** - * Merge updates in form like `Y.applyUpdate(doc, update)` way and return the - * result binary. - */ -export function mergeUpdatesInApplyWay(updates: Array): Buffer export class Storage { /** Create a storage instance and establish connection to persist store. */ static connect(database: string, debugOnlyAutoMigrate?: boolean | undefined | null): Promise @@ -30,3 +15,21 @@ export class Storage { /** Workspace size taken by blobs. */ blobsSize(workspaces: Array): Promise } + +export interface Blob { + contentType: string + lastModified: string + size: number + data: Buffer +} + +/** + * Merge updates in form like `Y.applyUpdate(doc, update)` way and return the + * result binary. + */ +export function mergeUpdatesInApplyWay(updates: Array): Buffer + +export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise + +export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise + diff --git a/packages/backend/storage/package.json b/packages/backend/storage/package.json index 874f5fc0f4a69..87f4002f67bbc 100644 --- a/packages/backend/storage/package.json +++ b/packages/backend/storage/package.json @@ -16,27 +16,26 @@ } }, "napi": { - "name": "storage", + "binaryName": "storage", "targets": [ "aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "aarch64-pc-windows-msvc", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", - "x86_64-unknown-linux-gnu", - "universal-apple-darwin" + "x86_64-unknown-linux-gnu" ] }, "scripts": { "test": "node --test ./__tests__/**/*.spec.js", - "build": "napi build --release --strip", + "build": "napi build --release --strip --no-const-enum", "build:debug": "napi build", "prepublishOnly": "napi prepublish -t npm", "artifacts": "napi artifacts", "version": "napi version" }, "devDependencies": { - "@napi-rs/cli": "^2.16.5", + "@napi-rs/cli": "3.0.0-alpha.12", "lib0": "^0.2.87", "nx": "^17.1.3", "nx-cloud": "^16.5.2", diff --git a/packages/frontend/native/index.d.ts b/packages/frontend/native/index.d.ts index 47c9a16d5a44a..3446100ebc996 100644 --- a/packages/frontend/native/index.d.ts +++ b/packages/frontend/native/index.d.ts @@ -1,32 +1,6 @@ -/* tslint:disable */ -/* eslint-disable */ - /* auto-generated by NAPI-RS */ +/* eslint-disable */ -export interface BlobRow { - key: string - data: Buffer - timestamp: Date -} -export interface UpdateRow { - id: number - timestamp: Date - data: Buffer - docId?: string -} -export interface InsertRow { - docId?: string - data: Uint8Array -} -export enum ValidationResult { - MissingTables = 0, - MissingDocIdColumn = 1, - MissingVersionColumn = 2, - GeneralError = 3, - Valid = 4 -} -export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise -export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise export class SqliteConnection { constructor(path: string) connect(): Promise @@ -47,3 +21,34 @@ export class SqliteConnection { static validate(path: string): Promise migrateAddDocId(): Promise } + +export interface BlobRow { + key: string + data: Buffer + timestamp: Date +} + +export interface InsertRow { + docId?: string + data: Uint8Array +} + +export function mintChallengeResponse(resource: string, bits?: number | undefined | null): Promise + +export interface UpdateRow { + id: number + timestamp: Date + data: Buffer + docId?: string +} + +export enum ValidationResult { + MissingTables = 0, + MissingDocIdColumn = 1, + MissingVersionColumn = 2, + GeneralError = 3, + Valid = 4 +} + +export function verifyChallengeResponse(response: string, bits: number, resource: string): Promise + diff --git a/packages/frontend/native/index.js b/packages/frontend/native/index.js index 5773485893ed3..0f5779e9f234e 100644 --- a/packages/frontend/native/index.js +++ b/packages/frontend/native/index.js @@ -1,7 +1,5 @@ -/* tslint:disable */ +// prettier-ignore /* eslint-disable */ -/* prettier-ignore */ - /* auto-generated by NAPI-RS */ const { existsSync, readFileSync } = require('fs') @@ -13,18 +11,52 @@ let nativeBinding = null let localFileExisted = false let loadError = null -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - const lddPath = require('child_process').execSync('which ldd').toString().trim() - return readFileSync(lddPath, 'utf8').includes('musl') - } catch (e) { +const isMusl = () => { + let musl = false + if (process.platform === 'linux') { + musl = isMuslFromFilesystem() + if (musl === null) { + musl = isMuslFromReport() + } + if (musl === null) { + musl = isMuslFromChildProcess() + } + } + return musl +} + +const isFileMusl = (f) => f.includes('libc.musl-') || f.includes('ld-musl-') + +const isMuslFromFilesystem = () => { + try { + return readFileSync('/usr/bin/ldd', 'utf-8').includes('musl') + } catch { + return null + } +} + +const isMuslFromReport = () => { + const report = typeof process.report.getReport === 'function' ? process.report.getReport() : null + if (!report) { + return null + } + if (report.header && report.header.glibcVersionRuntime) { + return false + } + if (Array.isArray(report.sharedObjects)) { + if (report.sharedObjects.some(isFileMusl)) { return true } - } else { - const { glibcVersionRuntime } = process.report.getReport().header - return !glibcVersionRuntime + } + return false +} + +const isMuslFromChildProcess = () => { + try { + return require('child_process').execSync('ldd --version', { encoding: 'utf8' }).includes('musl') + } catch (e) { + // If we reach this case, we don't know if the system is musl or not, so is better to just fallback to false + return false } } @@ -252,9 +284,7 @@ if (!nativeBinding) { throw new Error(`Failed to load native binding`) } -const { SqliteConnection, ValidationResult, verifyChallengeResponse, mintChallengeResponse } = nativeBinding - -module.exports.SqliteConnection = SqliteConnection -module.exports.ValidationResult = ValidationResult -module.exports.verifyChallengeResponse = verifyChallengeResponse -module.exports.mintChallengeResponse = mintChallengeResponse +module.exports.SqliteConnection = nativeBinding.SqliteConnection +module.exports.mintChallengeResponse = nativeBinding.mintChallengeResponse +module.exports.ValidationResult = nativeBinding.ValidationResult +module.exports.verifyChallengeResponse = nativeBinding.verifyChallengeResponse diff --git a/packages/frontend/native/package.json b/packages/frontend/native/package.json index 0805113456aff..4764ebe36a12a 100644 --- a/packages/frontend/native/package.json +++ b/packages/frontend/native/package.json @@ -4,7 +4,7 @@ "main": "index.js", "types": "index.d.ts", "napi": { - "name": "affine", + "binaryName": "affine", "triples": { "additional": [ "aarch64-apple-darwin", @@ -35,7 +35,7 @@ } }, "devDependencies": { - "@napi-rs/cli": "^2.16.5", + "@napi-rs/cli": "3.0.0-alpha.12", "@types/node": "^20.9.3", "@types/uuid": "^9.0.7", "ava": "^5.3.1", @@ -53,7 +53,7 @@ "scripts": { "artifacts": "napi artifacts", "build": "napi build --platform --release --no-const-enum", - "build:debug": "napi build --platform --no-const-enum", + "build:debug": "napi build --platform", "universal": "napi universal", "test": "ava", "version": "napi version" diff --git a/tests/affine-cloud/e2e/basic.spec.ts b/tests/affine-cloud/e2e/basic.spec.ts deleted file mode 100644 index ca4696e5d2644..0000000000000 --- a/tests/affine-cloud/e2e/basic.spec.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { readFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; - -import { test } from '@affine-test/kit/playwright'; -import { - createRandomUser, - deleteUser, - enableCloudWorkspace, - getLoginCookie, - loginUser, - runPrisma, -} from '@affine-test/kit/utils/cloud'; -import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor'; -import { coreUrl } from '@affine-test/kit/utils/load-page'; -import { - clickNewPageButton, - waitForEditorLoad, -} from '@affine-test/kit/utils/page-logic'; -import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar'; -import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; -import { expect } from '@playwright/test'; - -let user: { - id: string; - name: string; - email: string; - password: string; -}; - -test.beforeEach(async () => { - user = await createRandomUser(); -}); - -test.beforeEach(async ({ page, context }) => { - await loginUser(page, user.email, { - beforeLogin: async () => { - expect(await getLoginCookie(context)).toBeUndefined(); - }, - afterLogin: async () => { - expect(await getLoginCookie(context)).toBeTruthy(); - await page.reload(); - await waitForEditorLoad(page); - expect(await getLoginCookie(context)).toBeTruthy(); - }, - }); -}); - -test.afterEach(async () => { - // if you want to keep the user in the database for debugging, - // comment this line - await deleteUser(user.email); -}); - -test.describe('basic', () => { - test('migration', async ({ page, browser }) => { - let workspaceId: string; - { - // create the old cloud workspace in another browser - const context = await browser.newContext(); - const page = await context.newPage(); - await loginUser(page, user.email); - await page.reload(); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - await waitForEditorLoad(page); - // http://localhost:8080/workspace/2bc0b6c8-f68d-4dd3-98a8-be746754f9e1/xxx - workspaceId = page.url().split('/')[4]; - await runPrisma(async client => { - const sqls = ( - await readFile( - resolve(__dirname, 'fixtures', '0.9.0-canary.9-snapshots.sql'), - 'utf-8' - ) - ) - .replaceAll('2bc0b6c8-f68d-4dd3-98a8-be746754f9e1', workspaceId) - .split('\n'); - await client.snapshot.deleteMany({ - where: { - workspaceId, - }, - }); - - for (const sql of sqls) { - await client.$executeRawUnsafe(sql); - } - }); - await page.close(); - } - await page.reload(); - await page.waitForTimeout(1000); - await page.goto(`${coreUrl}/workspace/${workspaceId}/all`); - await page.getByTestId('upgrade-workspace-button').click(); - await expect(page.getByText('Refresh Current Page')).toBeVisible({ - timeout: 60000, - }); - await page.goto( - // page 'F1SX6cgNxy' has edgeless mode - `${coreUrl}/workspace/${workspaceId}/F1SX6cgNxy` - ); - await waitForEditorLoad(page); - await clickEdgelessModeButton(page); - await expect(page.locator('affine-edgeless-page')).toBeVisible({ - timeout: 1000, - }); - }); - - test('can see and change email and password in setting panel', async ({ - page, - }) => { - const newName = 'test name'; - { - await clickSideBarSettingButton(page); - const locator = page.getByTestId('user-info-card'); - expect(locator.getByText(user.email)).toBeTruthy(); - expect(locator.getByText(user.name)).toBeTruthy(); - await locator.click({ - delay: 50, - }); - const nameInput = page.getByPlaceholder('Input account name'); - await nameInput.clear(); - await nameInput.pressSequentially(newName, { - delay: 50, - }); - await page.getByTestId('save-user-name').click({ - delay: 50, - }); - } - await page.reload(); - { - await clickSideBarSettingButton(page); - const locator = page.getByTestId('user-info-card'); - expect(locator.getByText(user.email)).toBeTruthy(); - expect(locator.getByText(newName)).toBeTruthy(); - } - }); -}); diff --git a/tests/affine-cloud/e2e/collaboration.spec.ts b/tests/affine-cloud/e2e/collaboration.spec.ts index d4af767f3a789..349efeec2f2fa 100644 --- a/tests/affine-cloud/e2e/collaboration.spec.ts +++ b/tests/affine-cloud/e2e/collaboration.spec.ts @@ -12,16 +12,8 @@ import { getBlockSuiteEditorTitle, waitForEditorLoad, } from '@affine-test/kit/utils/page-logic'; -import { - clickUserInfoCard, - openSettingModal, - openWorkspaceSettingPanel, -} from '@affine-test/kit/utils/setting'; -import { - clickSideBarAllPageButton, - clickSideBarCurrentWorkspaceBanner, - clickSideBarSettingButton, -} from '@affine-test/kit/utils/sidebar'; +import { clickUserInfoCard } from '@affine-test/kit/utils/setting'; +import { clickSideBarSettingButton } from '@affine-test/kit/utils/sidebar'; import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; import { expect } from '@playwright/test'; import { resolve } from 'path'; @@ -41,298 +33,184 @@ test.beforeEach(async ({ page }) => { await loginUser(page, user.email); }); -test.describe('collaboration', () => { - test('can enable share page', async ({ page, browser }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspaceFromShareButton(page); - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - await page.keyboard.press('Enter', { delay: 50 }); - await page.keyboard.type('TEST CONTENT', { delay: 50 }); - await page.getByTestId('cloud-share-menu-button').click(); - await page.getByTestId('share-menu-create-link-button').click(); - await page.getByTestId('share-menu-copy-link-button').click(); - - // check share page is accessible +test('can enable share page', async ({ page, browser }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( { - const context = await browser.newContext(); - const url: string = await page.evaluate(() => - navigator.clipboard.readText() - ); - const page2 = await context.newPage(); - await page2.goto(url); - await waitForEditorLoad(page2); - const title = getBlockSuiteEditorTitle(page2); - expect(await title.innerText()).toBe('TEST TITLE'); - expect(await page2.textContent('affine-paragraph')).toContain( - 'TEST CONTENT' - ); - } + name: 'test', + }, + page + ); + await enableCloudWorkspaceFromShareButton(page); + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, }); + await page.keyboard.press('Enter', { delay: 50 }); + await page.keyboard.type('TEST CONTENT', { delay: 50 }); + await page.getByTestId('cloud-share-menu-button').click(); + await page.getByTestId('share-menu-create-link-button').click(); + await page.getByTestId('share-menu-copy-link-button').click(); - test('share page with default edgeless', async ({ page, browser }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page + // check share page is accessible + { + const context = await browser.newContext(); + const url: string = await page.evaluate(() => + navigator.clipboard.readText() ); - await enableCloudWorkspaceFromShareButton(page); - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - await page.keyboard.press('Enter', { delay: 50 }); - await page.keyboard.type('TEST CONTENT', { delay: 50 }); - await clickEdgelessModeButton(page); - await expect(page.locator('affine-edgeless-page')).toBeVisible({ - timeout: 1000, - }); - await page.getByTestId('cloud-share-menu-button').click(); - await page.getByTestId('share-menu-create-link-button').click(); - await page.getByTestId('share-menu-copy-link-button').click(); + const page2 = await context.newPage(); + await page2.goto(url); + await waitForEditorLoad(page2); + const title = getBlockSuiteEditorTitle(page2); + expect(await title.innerText()).toBe('TEST TITLE'); + expect(await page2.textContent('affine-paragraph')).toContain( + 'TEST CONTENT' + ); + } +}); - // check share page is accessible +test('share page with default edgeless', async ({ page, browser }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( { - const context = await browser.newContext(); - const url: string = await page.evaluate(() => - navigator.clipboard.readText() - ); - const page2 = await context.newPage(); - await page2.goto(url); - await waitForEditorLoad(page2); - await expect(page.locator('affine-edgeless-page')).toBeVisible({ - timeout: 1000, - }); - expect(await page2.textContent('affine-paragraph')).toContain( - 'TEST CONTENT' - ); - const logo = page2.getByTestId('share-page-logo'); - const editButton = page2.getByTestId('share-page-edit-button'); - await expect(editButton).not.toBeVisible(); - await expect(logo).toBeVisible(); - } + name: 'test', + }, + page + ); + await enableCloudWorkspaceFromShareButton(page); + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { + delay: 50, }); + await page.keyboard.press('Enter', { delay: 50 }); + await page.keyboard.type('TEST CONTENT', { delay: 50 }); + await clickEdgelessModeButton(page); + await expect(page.locator('affine-edgeless-page')).toBeVisible({ + timeout: 1000, + }); + await page.getByTestId('cloud-share-menu-button').click(); + await page.getByTestId('share-menu-create-link-button').click(); + await page.getByTestId('share-menu-copy-link-button').click(); - test('can collaborate with other user and name should display when editing', async ({ - page, - browser, - }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - const currentUrl = page.url(); - // format: http://localhost:8080/workspace/${workspaceId}/xxx - const workspaceId = currentUrl.split('/')[4]; - const userB = await createRandomUser(); + // check share page is accessible + { const context = await browser.newContext(); + const url: string = await page.evaluate(() => + navigator.clipboard.readText() + ); const page2 = await context.newPage(); - await loginUser(page2, userB.email); - await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); - await page2.reload(); + await page2.goto(url); await waitForEditorLoad(page2); - await page2.goto(currentUrl); - { - const title = getBlockSuiteEditorTitle(page); - await title.pressSequentially('TEST TITLE', { - delay: 50, - }); - } - await page2.waitForTimeout(200); - { - const title = getBlockSuiteEditorTitle(page2); - expect(await title.innerText()).toBe('TEST TITLE'); - const typingPromise = Promise.all([ - page.keyboard.press('Enter', { delay: 50 }), - page.keyboard.type('TEST CONTENT', { delay: 50 }), - ]); - // username should be visible when editing - await expect(page2.getByText(user.name)).toBeVisible(); - await typingPromise; - } - - // change username - await clickSideBarSettingButton(page); - await clickUserInfoCard(page); - const input = page.getByTestId('user-name-input'); - await input.clear(); - await input.pressSequentially('TEST USER', { - delay: 50, - }); - await page.getByTestId('save-user-name').click({ - delay: 50, + await expect(page.locator('affine-edgeless-page')).toBeVisible({ + timeout: 1000, }); - await page.keyboard.press('Escape', { + expect(await page2.textContent('affine-paragraph')).toContain( + 'TEST CONTENT' + ); + const logo = page2.getByTestId('share-page-logo'); + const editButton = page2.getByTestId('share-page-edit-button'); + await expect(editButton).not.toBeVisible(); + await expect(logo).toBeVisible(); + } +}); + +test('can collaborate with other user and name should display when editing', async ({ + page, + browser, +}) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + const currentUrl = page.url(); + // format: http://localhost:8080/workspace/${workspaceId}/xxx + const workspaceId = currentUrl.split('/')[4]; + const userB = await createRandomUser(); + const context = await browser.newContext(); + const page2 = await context.newPage(); + await loginUser(page2, userB.email); + await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); + await page2.reload(); + await waitForEditorLoad(page2); + await page2.goto(currentUrl); + { + const title = getBlockSuiteEditorTitle(page); + await title.pressSequentially('TEST TITLE', { delay: 50, }); - const title = getBlockSuiteEditorTitle(page); - await title.focus(); + } + await page2.waitForTimeout(200); + { + const title = getBlockSuiteEditorTitle(page2); + expect(await title.innerText()).toBe('TEST TITLE'); + const typingPromise = Promise.all([ + page.keyboard.press('Enter', { delay: 50 }), + page.keyboard.type('TEST CONTENT', { delay: 50 }), + ]); + // username should be visible when editing + await expect(page2.getByText(user.name)).toBeVisible(); + await typingPromise; + } - { - await expect(page2.getByText('TEST USER')).toBeVisible({ - timeout: 2000, - }); - } + // change username + await clickSideBarSettingButton(page); + await clickUserInfoCard(page); + const input = page.getByTestId('user-name-input'); + await input.clear(); + await input.pressSequentially('TEST USER', { + delay: 50, }); - - test('can sync collections between different browser', async ({ - page, - browser, - }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await page.getByTestId('slider-bar-add-collection-button').click(); - const title = page.getByTestId('input-collection-title'); - await title.isVisible(); - await title.fill('test collection'); - await page.getByTestId('save-collection').click(); - - { - const context = await browser.newContext(); - const page2 = await context.newPage(); - await loginUser(page2, user.email); - await page2.goto(page.url()); - const collections = page2.getByTestId('collections'); - await expect(collections.getByText('test collection')).toBeVisible(); - } + await page.getByTestId('save-user-name').click({ + delay: 50, }); - - test('exit successfully and re-login', async ({ page }) => { - await page.reload(); - await clickSideBarAllPageButton(page); - await page.waitForTimeout(200); - const url = page.url(); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickSideBarSettingButton(page); - await clickUserInfoCard(page); - await page.getByTestId('sign-out-button').click(); - await page.getByTestId('confirm-sign-out-button').click(); - await page.waitForTimeout(5000); - expect(page.url()).toBe(url); + await page.keyboard.press('Escape', { + delay: 50, }); -}); - -test.describe('collaboration members', () => { - test('should have pagination in member list', async ({ page }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await enableCloudWorkspace(page); - await clickNewPageButton(page); - const currentUrl = page.url(); - // format: http://localhost:8080/workspace/${workspaceId}/xxx - const workspaceId = currentUrl.split('/')[4]; - - // create 10 user and add to workspace - const createUserAndAddToWorkspace = async () => { - const userB = await createRandomUser(); - await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); - }; - await Promise.all( - Array.from({ length: 10 }) - .fill(1) - .map(() => createUserAndAddToWorkspace()) - ); - - await openSettingModal(page); - await openWorkspaceSettingPanel(page, 'test'); - - await page.waitForTimeout(1000); - - const firstPageMemberItemCount = await page - .locator('[data-testid="member-item"]') - .count(); - - expect(firstPageMemberItemCount).toBe(8); - - const navigationItems = await page - .getByRole('navigation') - .getByRole('button') - .all(); - - // make sure the first member is the owner - await expect(page.getByTestId('member-item').first()).toContainText( - 'Workspace Owner' - ); + const title = getBlockSuiteEditorTitle(page); + await title.focus(); - // There have four pagination items: < 1 2 > - expect(navigationItems.length).toBe(4); - // Click second page - await navigationItems[2].click(); - await page.waitForTimeout(500); - // There should have other three members in second page - const secondPageMemberItemCount = await page - .locator('[data-testid="member-item"]') - .count(); - expect(secondPageMemberItemCount).toBe(3); - // Click left arrow to back to first page - await navigationItems[0].click(); - await page.waitForTimeout(500); - expect(await page.locator('[data-testid="member-item"]').count()).toBe(8); - // Click right arrow to second page - await navigationItems[3].click(); - await page.waitForTimeout(500); - expect(await page.locator('[data-testid="member-item"]').count()).toBe(3); - }); + { + await expect(page2.getByText('TEST USER')).toBeVisible({ + timeout: 2000, + }); + } }); -test.describe('sign out', () => { - test('can sign out', async ({ page }) => { - await page.reload(); - await waitForEditorLoad(page); - await createLocalWorkspace( - { - name: 'test', - }, - page - ); - await clickSideBarAllPageButton(page); - const currentUrl = page.url(); - await clickSideBarCurrentWorkspaceBanner(page); - await page.getByTestId('workspace-modal-account-option').click(); - await page.getByTestId('workspace-modal-sign-out-option').click(); - await page.getByTestId('confirm-sign-out-button').click(); - await clickSideBarCurrentWorkspaceBanner(page); - const signInButton = page.getByTestId('cloud-signin-button'); - await expect(signInButton).toBeVisible(); - expect(page.url()).toBe(currentUrl); - }); +test('can sync collections between different browser', async ({ + page, + browser, +}) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await page.getByTestId('slider-bar-add-collection-button').click(); + const title = page.getByTestId('input-collection-title'); + await title.isVisible(); + await title.fill('test collection'); + await page.getByTestId('save-collection').click(); + + { + const context = await browser.newContext(); + const page2 = await context.newPage(); + await loginUser(page2, user.email); + await page2.goto(page.url()); + const collections = page2.getByTestId('collections'); + await expect(collections.getByText('test collection')).toBeVisible(); + } }); test('can sync svg between different browsers', async ({ page, browser }) => { diff --git a/tests/affine-cloud/e2e/login.spec.ts b/tests/affine-cloud/e2e/login.spec.ts index 0cc007a4778e1..6bfca654600e5 100644 --- a/tests/affine-cloud/e2e/login.spec.ts +++ b/tests/affine-cloud/e2e/login.spec.ts @@ -1,17 +1,111 @@ import { test } from '@affine-test/kit/playwright'; +import { + createRandomUser, + enableCloudWorkspace, + loginUser, +} from '@affine-test/kit/utils/cloud'; import { openHomePage } from '@affine-test/kit/utils/load-page'; import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic'; -import { clickSideBarCurrentWorkspaceBanner } from '@affine-test/kit/utils/sidebar'; +import { clickUserInfoCard } from '@affine-test/kit/utils/setting'; +import { + clickSideBarAllPageButton, + clickSideBarCurrentWorkspaceBanner, + clickSideBarSettingButton, +} from '@affine-test/kit/utils/sidebar'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; import { expect } from '@playwright/test'; -test.describe('login', () => { - test('can open login modal in workspace list', async ({ page }) => { - await openHomePage(page); +test('can open login modal in workspace list', async ({ page }) => { + await openHomePage(page); + await waitForEditorLoad(page); + await clickSideBarCurrentWorkspaceBanner(page); + await page.getByTestId('cloud-signin-button').click({ + delay: 200, + }); + await expect(page.getByTestId('auth-modal')).toBeVisible(); +}); + +test.describe('login first', () => { + let user: { + id: string; + name: string; + email: string; + password: string; + }; + + test.beforeEach(async ({ page }) => { + user = await createRandomUser(); + await loginUser(page, user.email); + }); + + test('exit successfully and re-login', async ({ page }) => { + await page.reload(); + await clickSideBarAllPageButton(page); + await page.waitForTimeout(200); + const url = page.url(); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickSideBarSettingButton(page); + await clickUserInfoCard(page); + await page.getByTestId('sign-out-button').click(); + await page.getByTestId('confirm-sign-out-button').click(); + await page.waitForTimeout(5000); + expect(page.url()).toBe(url); + }); + + test('can sign out', async ({ page }) => { + await page.reload(); await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await clickSideBarAllPageButton(page); + const currentUrl = page.url(); + await clickSideBarCurrentWorkspaceBanner(page); + await page.getByTestId('workspace-modal-account-option').click(); + await page.getByTestId('workspace-modal-sign-out-option').click(); + await page.getByTestId('confirm-sign-out-button').click(); await clickSideBarCurrentWorkspaceBanner(page); - await page.getByTestId('cloud-signin-button').click({ - delay: 200, - }); - await expect(page.getByTestId('auth-modal')).toBeVisible(); + const signInButton = page.getByTestId('cloud-signin-button'); + await expect(signInButton).toBeVisible(); + expect(page.url()).toBe(currentUrl); + }); + + test('can see and change email and password in setting panel', async ({ + page, + }) => { + const newName = 'test name'; + { + await clickSideBarSettingButton(page); + const locator = page.getByTestId('user-info-card'); + expect(locator.getByText(user.email)).toBeTruthy(); + expect(locator.getByText(user.name)).toBeTruthy(); + await locator.click({ + delay: 50, + }); + const nameInput = page.getByPlaceholder('Input account name'); + await nameInput.clear(); + await nameInput.pressSequentially(newName, { + delay: 50, + }); + await page.getByTestId('save-user-name').click({ + delay: 50, + }); + } + await page.reload(); + { + await clickSideBarSettingButton(page); + const locator = page.getByTestId('user-info-card'); + expect(locator.getByText(user.email)).toBeTruthy(); + expect(locator.getByText(newName)).toBeTruthy(); + } }); }); diff --git a/tests/affine-cloud/e2e/migration.spec.ts b/tests/affine-cloud/e2e/migration.spec.ts new file mode 100644 index 0000000000000..f5db86fb57ead --- /dev/null +++ b/tests/affine-cloud/e2e/migration.spec.ts @@ -0,0 +1,111 @@ +import { readFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; + +import { test } from '@affine-test/kit/playwright'; +import { + createRandomUser, + deleteUser, + enableCloudWorkspace, + getLoginCookie, + loginUser, + runPrisma, +} from '@affine-test/kit/utils/cloud'; +import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor'; +import { coreUrl } from '@affine-test/kit/utils/load-page'; +import { + clickNewPageButton, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; +import { expect } from '@playwright/test'; + +let user: { + id: string; + name: string; + email: string; + password: string; +}; + +test.beforeEach(async () => { + user = await createRandomUser(); +}); + +test.beforeEach(async ({ page, context }) => { + await loginUser(page, user.email, { + beforeLogin: async () => { + expect(await getLoginCookie(context)).toBeUndefined(); + }, + afterLogin: async () => { + expect(await getLoginCookie(context)).toBeTruthy(); + await page.reload(); + await waitForEditorLoad(page); + expect(await getLoginCookie(context)).toBeTruthy(); + }, + }); +}); + +test.afterEach(async () => { + // if you want to keep the user in the database for debugging, + // comment this line + await deleteUser(user.email); +}); + +test('migration', async ({ page, browser }) => { + let workspaceId: string; + { + // create the old cloud workspace in another browser + const context = await browser.newContext(); + const page = await context.newPage(); + await loginUser(page, user.email); + await page.reload(); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + await waitForEditorLoad(page); + // http://localhost:8080/workspace/2bc0b6c8-f68d-4dd3-98a8-be746754f9e1/xxx + workspaceId = page.url().split('/')[4]; + await runPrisma(async client => { + const sqls = ( + await readFile( + resolve(__dirname, 'fixtures', '0.9.0-canary.9-snapshots.sql'), + 'utf-8' + ) + ) + .replaceAll('2bc0b6c8-f68d-4dd3-98a8-be746754f9e1', workspaceId) + .split('\n'); + await client.snapshot.deleteMany({ + where: { + workspaceId, + }, + }); + + for (const sql of sqls) { + await client.$executeRawUnsafe(sql); + } + }); + await page.close(); + } + await page.reload(); + await page.waitForTimeout(1000); + await page.goto(`${coreUrl}/workspace/${workspaceId}/all`); + await page.getByTestId('upgrade-workspace-button').click(); + await expect(page.getByText('Refresh Current Page')).toBeVisible({ + timeout: 60000, + }); + await page.goto( + // page 'F1SX6cgNxy' has edgeless mode + `${coreUrl}/workspace/${workspaceId}/F1SX6cgNxy` + ); + await page.waitForTimeout(5000); + await page.reload(); + await waitForEditorLoad(page); + await clickEdgelessModeButton(page); + await expect(page.locator('affine-edgeless-page')).toBeVisible({ + timeout: 1000, + }); +}); diff --git a/tests/affine-cloud/e2e/workspace.spec.ts b/tests/affine-cloud/e2e/workspace.spec.ts new file mode 100644 index 0000000000000..31d00f1769ac1 --- /dev/null +++ b/tests/affine-cloud/e2e/workspace.spec.ts @@ -0,0 +1,96 @@ +import { test } from '@affine-test/kit/playwright'; +import { + addUserToWorkspace, + createRandomUser, + enableCloudWorkspace, + loginUser, +} from '@affine-test/kit/utils/cloud'; +import { + clickNewPageButton, + waitForEditorLoad, +} from '@affine-test/kit/utils/page-logic'; +import { + openSettingModal, + openWorkspaceSettingPanel, +} from '@affine-test/kit/utils/setting'; +import { createLocalWorkspace } from '@affine-test/kit/utils/workspace'; +import { expect } from '@playwright/test'; + +let user: { + id: string; + name: string; + email: string; + password: string; +}; + +test.beforeEach(async ({ page }) => { + user = await createRandomUser(); + await loginUser(page, user.email); +}); + +test('should have pagination in member list', async ({ page }) => { + await page.reload(); + await waitForEditorLoad(page); + await createLocalWorkspace( + { + name: 'test', + }, + page + ); + await enableCloudWorkspace(page); + await clickNewPageButton(page); + const currentUrl = page.url(); + // format: http://localhost:8080/workspace/${workspaceId}/xxx + const workspaceId = currentUrl.split('/')[4]; + + // create 10 user and add to workspace + const createUserAndAddToWorkspace = async () => { + const userB = await createRandomUser(); + await addUserToWorkspace(workspaceId, userB.id, 1 /* READ */); + }; + await Promise.all( + Array.from({ length: 10 }) + .fill(1) + .map(() => createUserAndAddToWorkspace()) + ); + + await openSettingModal(page); + await openWorkspaceSettingPanel(page, 'test'); + + await page.waitForTimeout(1000); + + const firstPageMemberItemCount = await page + .locator('[data-testid="member-item"]') + .count(); + + expect(firstPageMemberItemCount).toBe(8); + + const navigationItems = await page + .getByRole('navigation') + .getByRole('button') + .all(); + + // make sure the first member is the owner + await expect(page.getByTestId('member-item').first()).toContainText( + 'Workspace Owner' + ); + + // There have four pagination items: < 1 2 > + expect(navigationItems.length).toBe(4); + // Click second page + await navigationItems[2].click(); + await page.waitForTimeout(500); + // There should have other three members in second page + const secondPageMemberItemCount = await page + .locator('[data-testid="member-item"]') + .count(); + expect(secondPageMemberItemCount).toBe(3); + // Click left arrow to back to first page + await navigationItems[0].click(); + await page.waitForTimeout(500); + expect(await page.locator('[data-testid="member-item"]').count()).toBe(8); + // Click right arrow to second page + await navigationItems[3].click(); + await page.waitForTimeout(500); + expect(await page.locator('[data-testid="member-item"]').count()).toBe(3); +}); diff --git a/yarn.lock b/yarn.lock index baeb00bd9dd72..61f00e0fda36c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -656,7 +656,7 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/native@workspace:packages/frontend/native" dependencies: - "@napi-rs/cli": "npm:^2.16.5" + "@napi-rs/cli": "npm:3.0.0-alpha.12" "@types/node": "npm:^20.9.3" "@types/uuid": "npm:^9.0.7" ava: "npm:^5.3.1" @@ -821,7 +821,7 @@ __metadata: version: 0.0.0-use.local resolution: "@affine/storage@workspace:packages/backend/storage" dependencies: - "@napi-rs/cli": "npm:^2.16.5" + "@napi-rs/cli": "npm:3.0.0-alpha.12" lib0: "npm:^0.2.87" nx: "npm:^17.1.3" nx-cloud: "npm:^16.5.2" @@ -7183,6 +7183,15 @@ __metadata: languageName: node linkType: hard +"@ljharb/through@npm:^2.3.11": + version: 2.3.11 + resolution: "@ljharb/through@npm:2.3.11" + dependencies: + call-bind: "npm:^1.0.2" + checksum: 45bcc0681b89bbaf4c814473f3dcb89ba8c6d259becce36d21aafde8959c2fb0e3e640bd735fa00ac68b13e2947b165225bf56a70609f697b20c91a6982bfbd1 + languageName: node + linkType: hard + "@lukeed/csprng@npm:^1.0.0": version: 1.1.0 resolution: "@lukeed/csprng@npm:1.1.0" @@ -7383,12 +7392,68 @@ __metadata: languageName: node linkType: hard -"@napi-rs/cli@npm:^2.16.5": - version: 2.16.5 - resolution: "@napi-rs/cli@npm:2.16.5" +"@napi-rs/cli@npm:3.0.0-alpha.12": + version: 3.0.0-alpha.12 + resolution: "@napi-rs/cli@npm:3.0.0-alpha.12" + dependencies: + "@napi-rs/cross-toolchain": "npm:^0.0.12" + "@octokit/rest": "npm:^20.0.2" + "@tybys/wasm-util": "npm:0.8.0" + clipanion: "npm:^3.2.1" + colorette: "npm:^2.0.20" + debug: "npm:^4.3.4" + emnapi: "npm:0.44.0" + inquirer: "npm:^9.2.12" + js-yaml: "npm:^4.1.0" + lodash-es: "npm:^4.17.21" + toml: "npm:^3.0.0" + typanion: "npm:^3.14.0" + peerDependencies: + "@emnapi/runtime": 0.44.0 + "@tybys/wasm-util": 0.8.0 + emnapi: 0.44.0 + peerDependenciesMeta: + "@emnapi/runtime": + optional: true + "@tybys/wasm-util": + optional: true + emnapi: + optional: true bin: - napi: scripts/index.js - checksum: 37c16d900887970d080e5a3dd5656463d27e5d4c78f3c50d8382af0b4c51752c705e4713c18b46a83ff54a3c58d5d146aabc82ac7b7bbda8134adb7325bb9fa1 + napi: dist/cli.js + napi-raw: cli.mjs + checksum: 50911df427f970f6926dc62e083c740b8b342e2181c65a56f4f0758d96995d110b5762fffafc1124628b6192987986b3bba93adf25921eda39c81034cd1dff54 + languageName: node + linkType: hard + +"@napi-rs/cross-toolchain@npm:^0.0.12": + version: 0.0.12 + resolution: "@napi-rs/cross-toolchain@npm:0.0.12" + dependencies: + "@napi-rs/lzma": "npm:^1.2.1" + "@napi-rs/tar": "npm:^0.0.1" + debug: "npm:^4.3.4" + peerDependencies: + "@napi-rs/cross-toolchain-arm64-target-aarch64": ^0.0.12 + "@napi-rs/cross-toolchain-arm64-target-armv7": ^0.0.12 + "@napi-rs/cross-toolchain-arm64-target-x86_64": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-aarch64": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-armv7": ^0.0.12 + "@napi-rs/cross-toolchain-x64-target-x86_64": ^0.0.12 + peerDependenciesMeta: + "@napi-rs/cross-toolchain-arm64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-arm64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-arm64-target-x86_64": + optional: true + "@napi-rs/cross-toolchain-x64-target-aarch64": + optional: true + "@napi-rs/cross-toolchain-x64-target-armv7": + optional: true + "@napi-rs/cross-toolchain-x64-target-x86_64": + optional: true + checksum: 7631a0d72f4264ab46b8792519284aa9f8dd6bf922a27dd598c4eec398c83730950454a5f36ceb502fe7c018e89d99f2186ed16416d21e35f6d120c2a6ea48fc languageName: node linkType: hard @@ -7521,6 +7586,145 @@ __metadata: languageName: node linkType: hard +"@napi-rs/lzma-android-arm-eabi@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-android-arm-eabi@npm:1.2.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-android-arm64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-android-arm64@npm:1.2.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-arm64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-darwin-arm64@npm:1.2.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-darwin-x64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-darwin-x64@npm:1.2.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-freebsd-x64@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-freebsd-x64@npm:1.2.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm-gnueabihf@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm-gnueabihf@npm:1.2.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-gnu@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm64-gnu@npm:1.2.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-arm64-musl@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-arm64-musl@npm:1.2.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-gnu@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-x64-gnu@npm:1.2.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/lzma-linux-x64-musl@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-linux-x64-musl@npm:1.2.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-arm64-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-arm64-msvc@npm:1.2.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-ia32-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-ia32-msvc@npm:1.2.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/lzma-win32-x64-msvc@npm:1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma-win32-x64-msvc@npm:1.2.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/lzma@npm:^1.2.1": + version: 1.2.1 + resolution: "@napi-rs/lzma@npm:1.2.1" + dependencies: + "@napi-rs/lzma-android-arm-eabi": "npm:1.2.1" + "@napi-rs/lzma-android-arm64": "npm:1.2.1" + "@napi-rs/lzma-darwin-arm64": "npm:1.2.1" + "@napi-rs/lzma-darwin-x64": "npm:1.2.1" + "@napi-rs/lzma-freebsd-x64": "npm:1.2.1" + "@napi-rs/lzma-linux-arm-gnueabihf": "npm:1.2.1" + "@napi-rs/lzma-linux-arm64-gnu": "npm:1.2.1" + "@napi-rs/lzma-linux-arm64-musl": "npm:1.2.1" + "@napi-rs/lzma-linux-x64-gnu": "npm:1.2.1" + "@napi-rs/lzma-linux-x64-musl": "npm:1.2.1" + "@napi-rs/lzma-win32-arm64-msvc": "npm:1.2.1" + "@napi-rs/lzma-win32-ia32-msvc": "npm:1.2.1" + "@napi-rs/lzma-win32-x64-msvc": "npm:1.2.1" + dependenciesMeta: + "@napi-rs/lzma-android-arm-eabi": + optional: true + "@napi-rs/lzma-android-arm64": + optional: true + "@napi-rs/lzma-darwin-arm64": + optional: true + "@napi-rs/lzma-darwin-x64": + optional: true + "@napi-rs/lzma-freebsd-x64": + optional: true + "@napi-rs/lzma-linux-arm-gnueabihf": + optional: true + "@napi-rs/lzma-linux-arm64-gnu": + optional: true + "@napi-rs/lzma-linux-arm64-musl": + optional: true + "@napi-rs/lzma-linux-x64-gnu": + optional: true + "@napi-rs/lzma-linux-x64-musl": + optional: true + "@napi-rs/lzma-win32-arm64-msvc": + optional: true + "@napi-rs/lzma-win32-ia32-msvc": + optional: true + "@napi-rs/lzma-win32-x64-msvc": + optional: true + checksum: 5e6d9bee5e359227f7bb3616b3b2a8b064bde0f5aebf4b885df23954cc2f35b1ae44595405733e54826ab9896c02309eac5ff2b340592b698ef2aa135a44288c + languageName: node + linkType: hard + "@napi-rs/magic-string-android-arm-eabi@npm:0.3.4": version: 0.3.4 resolution: "@napi-rs/magic-string-android-arm-eabi@npm:0.3.4" @@ -7660,6 +7864,145 @@ __metadata: languageName: node linkType: hard +"@napi-rs/tar-android-arm-eabi@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-android-arm-eabi@npm:0.0.1" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-android-arm64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-android-arm64@npm:0.0.1" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-arm64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-darwin-arm64@npm:0.0.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-darwin-x64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-darwin-x64@npm:0.0.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-freebsd-x64@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-freebsd-x64@npm:0.0.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm-gnueabihf@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm-gnueabihf@npm:0.0.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-gnu@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm64-gnu@npm:0.0.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-arm64-musl@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-arm64-musl@npm:0.0.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-gnu@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-x64-gnu@npm:0.0.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@napi-rs/tar-linux-x64-musl@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-linux-x64-musl@npm:0.0.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@napi-rs/tar-win32-arm64-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-arm64-msvc@npm:0.0.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-ia32-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-ia32-msvc@npm:0.0.1" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@napi-rs/tar-win32-x64-msvc@npm:0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar-win32-x64-msvc@npm:0.0.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@napi-rs/tar@npm:^0.0.1": + version: 0.0.1 + resolution: "@napi-rs/tar@npm:0.0.1" + dependencies: + "@napi-rs/tar-android-arm-eabi": "npm:0.0.1" + "@napi-rs/tar-android-arm64": "npm:0.0.1" + "@napi-rs/tar-darwin-arm64": "npm:0.0.1" + "@napi-rs/tar-darwin-x64": "npm:0.0.1" + "@napi-rs/tar-freebsd-x64": "npm:0.0.1" + "@napi-rs/tar-linux-arm-gnueabihf": "npm:0.0.1" + "@napi-rs/tar-linux-arm64-gnu": "npm:0.0.1" + "@napi-rs/tar-linux-arm64-musl": "npm:0.0.1" + "@napi-rs/tar-linux-x64-gnu": "npm:0.0.1" + "@napi-rs/tar-linux-x64-musl": "npm:0.0.1" + "@napi-rs/tar-win32-arm64-msvc": "npm:0.0.1" + "@napi-rs/tar-win32-ia32-msvc": "npm:0.0.1" + "@napi-rs/tar-win32-x64-msvc": "npm:0.0.1" + dependenciesMeta: + "@napi-rs/tar-android-arm-eabi": + optional: true + "@napi-rs/tar-android-arm64": + optional: true + "@napi-rs/tar-darwin-arm64": + optional: true + "@napi-rs/tar-darwin-x64": + optional: true + "@napi-rs/tar-freebsd-x64": + optional: true + "@napi-rs/tar-linux-arm-gnueabihf": + optional: true + "@napi-rs/tar-linux-arm64-gnu": + optional: true + "@napi-rs/tar-linux-arm64-musl": + optional: true + "@napi-rs/tar-linux-x64-gnu": + optional: true + "@napi-rs/tar-linux-x64-musl": + optional: true + "@napi-rs/tar-win32-arm64-msvc": + optional: true + "@napi-rs/tar-win32-ia32-msvc": + optional: true + "@napi-rs/tar-win32-x64-msvc": + optional: true + checksum: b0a80a08641c05a2fa035cc8e8e642fad0c25e781f292fc541daecedde3921aa96c6ae7785aea733ab6a08983e740dff1b7eefc36546ad939fdaa23e0fab5d70 + languageName: node + linkType: hard + "@napi-rs/xattr-android-arm-eabi@npm:1.0.1": version: 1.0.1 resolution: "@napi-rs/xattr-android-arm-eabi@npm:1.0.1" @@ -8707,6 +9050,131 @@ __metadata: languageName: node linkType: hard +"@octokit/auth-token@npm:^4.0.0": + version: 4.0.0 + resolution: "@octokit/auth-token@npm:4.0.0" + checksum: 60e42701e341d700f73c518c7a35675d36d79fa9d5e838cc3ade96d147e49f5ba74db2e07b2337c2b95aaa540aa42088116df2122daa25633f9e70a2c8785c44 + languageName: node + linkType: hard + +"@octokit/core@npm:^5.0.0": + version: 5.0.2 + resolution: "@octokit/core@npm:5.0.2" + dependencies: + "@octokit/auth-token": "npm:^4.0.0" + "@octokit/graphql": "npm:^7.0.0" + "@octokit/request": "npm:^8.0.2" + "@octokit/request-error": "npm:^5.0.0" + "@octokit/types": "npm:^12.0.0" + before-after-hook: "npm:^2.2.0" + universal-user-agent: "npm:^6.0.0" + checksum: bb991f88793fab043c4c09f9441432596fe0e6448caf42cd2209f52c1f26807418be488ad2cea7a8293e58e79e5c0019f38dda46e8cf96af5e89e43cca37ec3e + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^9.0.0": + version: 9.0.4 + resolution: "@octokit/endpoint@npm:9.0.4" + dependencies: + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: 7df35c96f2b5628fe5b3f44a72614be9b439779c06b4dd1bb72283b3cb2ea53e59e1f9a108798efe5404b6856f4380a4c5be12d93255d854f0683cd6e22f3a27 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^7.0.0": + version: 7.0.2 + resolution: "@octokit/graphql@npm:7.0.2" + dependencies: + "@octokit/request": "npm:^8.0.1" + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: f5dcc51fed5304f65dab83fcea4c2a569107d3b71e8d084199dc44f0d0cfc852c9e1f341b06ae66601f9da4af3aad416b0c62dcd0567ac7568f072d8d90d502e + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^19.0.2": + version: 19.1.0 + resolution: "@octokit/openapi-types@npm:19.1.0" + checksum: 3abedc09baa91bb4de00a2b82bf519401c2b6388913b7caa98541002c9e9612eba8256926323b1e40782ac700309a3373bb8c13520af325cef1accc40cb4566b + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^9.0.0": + version: 9.1.4 + resolution: "@octokit/plugin-paginate-rest@npm:9.1.4" + dependencies: + "@octokit/types": "npm:^12.3.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 1573934e0c2a99e3512cd21a0dbb17f6ca1d5faffdffb499cb80519b1219da4d56f814a30c68c0961fcccf152895bdced478709195f53a6e4c32e71a3235f888 + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^4.0.0": + version: 4.0.0 + resolution: "@octokit/plugin-request-log@npm:4.0.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 2a8a6619640942092009a9248ceeb163ce01c978e2d7b2a7eb8686bd09a04b783c4cd9071eebb16652d233587abcde449a02ce4feabc652f0a171615fb3e9946 + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^10.0.0": + version: 10.2.0 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:10.2.0" + dependencies: + "@octokit/types": "npm:^12.3.0" + peerDependencies: + "@octokit/core": ">=5" + checksum: 0f8ca73b3e582b366b400278f19df6309f263efa3809a9d6ba613063e7a26f16d6f8d69c413bf9b23c2431ad4c795e4e06a43717b6acc1367186fb55347cfb69 + languageName: node + linkType: hard + +"@octokit/request-error@npm:^5.0.0": + version: 5.0.1 + resolution: "@octokit/request-error@npm:5.0.1" + dependencies: + "@octokit/types": "npm:^12.0.0" + deprecation: "npm:^2.0.0" + once: "npm:^1.4.0" + checksum: a21a4614c46cb173e4ba73fa048576204f1ddc541dee3e7c938ef36088566e3b25e04ca1f96f375ec2e3cc29b7ba970b3b078a89a20bc50cdcdbed879db94573 + languageName: node + linkType: hard + +"@octokit/request@npm:^8.0.1, @octokit/request@npm:^8.0.2": + version: 8.1.6 + resolution: "@octokit/request@npm:8.1.6" + dependencies: + "@octokit/endpoint": "npm:^9.0.0" + "@octokit/request-error": "npm:^5.0.0" + "@octokit/types": "npm:^12.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: aebea1c33d607d23c70f663cd5f8279a8bd932ab77b4ca5cca3b33968a347b4adb47476c886086f3a9aa1acefab3b79adac78ee7aa2dacd67eb1f2a05e272618 + languageName: node + linkType: hard + +"@octokit/rest@npm:^20.0.2": + version: 20.0.2 + resolution: "@octokit/rest@npm:20.0.2" + dependencies: + "@octokit/core": "npm:^5.0.0" + "@octokit/plugin-paginate-rest": "npm:^9.0.0" + "@octokit/plugin-request-log": "npm:^4.0.0" + "@octokit/plugin-rest-endpoint-methods": "npm:^10.0.0" + checksum: 527e1806ca274209a2a7daa485010dafb2ebb6c9b0b44c1d33a8f1f16f10e54a96386a4f642dc416160842a4b367d3953d27f8b827b9a94600709d2ac5e95d21 + languageName: node + linkType: hard + +"@octokit/types@npm:^12.0.0, @octokit/types@npm:^12.3.0": + version: 12.3.0 + resolution: "@octokit/types@npm:12.3.0" + dependencies: + "@octokit/openapi-types": "npm:^19.0.2" + checksum: ab78fd25490f995f79e341b00c375bade64eedb44d4c76fa3da85961003ba1efcac3cf168ea221bf4f9f5761afe91738412737acf30f1f41f3f2dbad14b872e4 + languageName: node + linkType: hard + "@open-draft/deferred-promise@npm:^2.2.0": version: 2.2.0 resolution: "@open-draft/deferred-promise@npm:2.2.0" @@ -13335,6 +13803,15 @@ __metadata: languageName: node linkType: hard +"@tybys/wasm-util@npm:0.8.0": + version: 0.8.0 + resolution: "@tybys/wasm-util@npm:0.8.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 7fa650acfc3fdcaf103f0f7acdf6f4cf67125632eb0f91c92d826375e262547d69de28838cf7a4b69fb665d2fb6f153558528fa1fddd5615a5aa838a87ed7d16 + languageName: node + linkType: hard + "@types/accepts@npm:*": version: 1.3.7 resolution: "@types/accepts@npm:1.3.7" @@ -15594,7 +16071,7 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": +"ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0, ansi-escapes@npm:^4.3.2": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" dependencies: @@ -16456,6 +16933,13 @@ __metadata: languageName: node linkType: hard +"before-after-hook@npm:^2.2.0": + version: 2.2.3 + resolution: "before-after-hook@npm:2.2.3" + checksum: e676f769dbc4abcf4b3317db2fd2badb4a92c0710e0a7da12cf14b59c3482d4febf835ad7de7874499060fd4e13adf0191628e504728b3c5bb4ec7a878c09940 + languageName: node + linkType: hard + "better-opn@npm:^3.0.2": version: 3.0.2 resolution: "better-opn@npm:3.0.2" @@ -17494,6 +17978,13 @@ __metadata: languageName: node linkType: hard +"cli-width@npm:^4.1.0": + version: 4.1.0 + resolution: "cli-width@npm:4.1.0" + checksum: b58876fbf0310a8a35c79b72ecfcf579b354e18ad04e6b20588724ea2b522799a758507a37dfe132fafaf93a9922cafd9514d9e1598e6b2cd46694853aed099f + languageName: node + linkType: hard + "client-only@npm:^0.0.1": version: 0.0.1 resolution: "client-only@npm:0.0.1" @@ -17501,7 +17992,7 @@ __metadata: languageName: node linkType: hard -"clipanion@npm:^3.1.0": +"clipanion@npm:^3.1.0, clipanion@npm:^3.2.1": version: 3.2.1 resolution: "clipanion@npm:3.2.1" dependencies: @@ -18919,6 +19410,13 @@ __metadata: languageName: node linkType: hard +"deprecation@npm:^2.0.0": + version: 2.3.1 + resolution: "deprecation@npm:2.3.1" + checksum: f56a05e182c2c195071385455956b0c4106fe14e36245b00c689ceef8e8ab639235176a96977ba7c74afb173317fac2e0ec6ec7a1c6d1e6eaa401c586c714132 + languageName: node + linkType: hard + "dequal@npm:2.0.3, dequal@npm:^2.0.0, dequal@npm:^2.0.2, dequal@npm:^2.0.3": version: 2.0.3 resolution: "dequal@npm:2.0.3" @@ -19552,6 +20050,18 @@ __metadata: languageName: node linkType: hard +"emnapi@npm:0.44.0": + version: 0.44.0 + resolution: "emnapi@npm:0.44.0" + peerDependencies: + node-addon-api: ">= 6.1.0" + peerDependenciesMeta: + node-addon-api: + optional: true + checksum: c0177b08bbca8c815e5b15c544d431cd270975bc5831bad03691eba271de5ad7cff78be431ae1b5e3816a622951a9ed525b83a1a3707b9f9c70ecc8aece7344d + languageName: node + linkType: hard + "emoji-regex@npm:^10.3.0": version: 10.3.0 resolution: "emoji-regex@npm:10.3.0" @@ -20696,7 +21206,7 @@ __metadata: languageName: node linkType: hard -"external-editor@npm:^3.0.3": +"external-editor@npm:^3.0.3, external-editor@npm:^3.1.0": version: 3.1.0 resolution: "external-editor@npm:3.1.0" dependencies: @@ -20977,6 +21487,19 @@ __metadata: "figures@npm:^5.0.0": version: 5.0.0 resolution: "figures@npm:5.0.0" +<<<<<<< HEAD +======= + dependencies: + escape-string-regexp: "npm:^5.0.0" + is-unicode-supported: "npm:^1.2.0" + checksum: 951d18be2f450c90462c484eff9bda705293319bc2f17b250194a0cf1a291600db4cb283a6ce199d49380c95b08d85d822ce4b18d2f9242663fd5895476d667c + languageName: node + linkType: hard + +"figures@npm:^6.0.1": + version: 6.0.1 + resolution: "figures@npm:6.0.1" +>>>>>>> e5f8a5833 (ci: refactor workflow (#5139)) dependencies: escape-string-regexp: "npm:^5.0.0" is-unicode-supported: "npm:^1.2.0" @@ -23268,6 +23791,29 @@ __metadata: languageName: node linkType: hard +"inquirer@npm:^9.2.12": + version: 9.2.12 + resolution: "inquirer@npm:9.2.12" + dependencies: + "@ljharb/through": "npm:^2.3.11" + ansi-escapes: "npm:^4.3.2" + chalk: "npm:^5.3.0" + cli-cursor: "npm:^3.1.0" + cli-width: "npm:^4.1.0" + external-editor: "npm:^3.1.0" + figures: "npm:^5.0.0" + lodash: "npm:^4.17.21" + mute-stream: "npm:1.0.0" + ora: "npm:^5.4.1" + run-async: "npm:^3.0.0" + rxjs: "npm:^7.8.1" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^6.2.0" + checksum: 02b259c641fd6c6b88c0c530aced23389d586bd5360799bab0ae11d2a965ac5ce9c587402faefad70f08b8b56ccae56fb973da3a8deb6e17ba4577f803d427c5 + languageName: node + linkType: hard + "interpret@npm:^3.1.1": version: 3.1.1 resolution: "interpret@npm:3.1.1" @@ -27976,6 +28522,13 @@ __metadata: languageName: node linkType: hard +"mute-stream@npm:1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 36fc968b0e9c9c63029d4f9dc63911950a3bdf55c9a87f58d3a266289b67180201cade911e7699f8b2fa596b34c9db43dad37649e3f7fdd13c3bb9edb0017ee7 + languageName: node + linkType: hard + "nanoid@npm:^3.3.3, nanoid@npm:^3.3.6": version: 3.3.7 resolution: "nanoid@npm:3.3.7" @@ -31930,6 +32483,13 @@ __metadata: languageName: node linkType: hard +"run-async@npm:^3.0.0": + version: 3.0.0 + resolution: "run-async@npm:3.0.0" + checksum: 97fb8747f7765b77ebcd311d3a33548099336f04c6434e0763039b98c1de0f1b4421000695aff8751f309c0b995d8dfd620c1f1e4c35572da38c101488165305 + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -33949,6 +34509,13 @@ __metadata: languageName: node linkType: hard +"toml@npm:^3.0.0": + version: 3.0.0 + resolution: "toml@npm:3.0.0" + checksum: cfef0966868d552bd02e741f30945a611f70841b7cddb07ea2b17441fe32543985bc0a7c0dcf7971af26fcaf8a17712a485d911f46bfe28644536e9a71a2bd09 + languageName: node + linkType: hard + "totalist@npm:^3.0.0": version: 3.0.1 resolution: "totalist@npm:3.0.1" @@ -34132,7 +34699,7 @@ __metadata: languageName: node linkType: hard -"typanion@npm:^3.8.0": +"typanion@npm:^3.14.0, typanion@npm:^3.8.0": version: 3.14.0 resolution: "typanion@npm:3.14.0" checksum: 5e88d9e6121ff0ec543f572152fdd1b70e9cca35406d79013ec8e08defa8ef96de5fec9e98da3afbd1eb4426b9e8e8fe423163d0b482e34a40103cab1ef29abd @@ -34581,6 +35148,13 @@ __metadata: languageName: node linkType: hard +"universal-user-agent@npm:^6.0.0": + version: 6.0.1 + resolution: "universal-user-agent@npm:6.0.1" + checksum: fdc8e1ae48a05decfc7ded09b62071f571c7fe0bd793d700704c80cea316101d4eac15cc27ed2bb64f4ce166d2684777c3198b9ab16034f547abea0d3aa1c93c + languageName: node + linkType: hard + "universalify@npm:^0.1.0": version: 0.1.2 resolution: "universalify@npm:0.1.2"