diff --git a/.github/workflows/ai-policy.yml b/.github/workflows/ai-policy.yml deleted file mode 100644 index 4b4653ee67a..00000000000 --- a/.github/workflows/ai-policy.yml +++ /dev/null @@ -1,178 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: AI Policy - -on: - pull_request: - types: [opened, synchronize, reopened] - branches: [master, main] - -permissions: - contents: read - # Required to add the "AI assisted" label via `gh pr edit --add-label` - pull-requests: write - # Required to create the "AI assisted" label via the REST labels endpoint - # (labels are an issues-scoped resource in the GitHub API) - issues: write - -concurrency: - group: ai-policy-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - check-ai-trailers: - runs-on: ubuntu-latest-low - steps: - - name: Collect PR commit messages - id: collect - env: - # Fall back to the default token when the PAT is unavailable - # (e.g. Dependabot- or fork-triggered runs don't receive Actions secrets) - GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT || github.token }} - COMMITS_URL: ${{ github.event.pull_request.commits_url }} - run: | - set -euo pipefail - gh api ${COMMITS_URL} | jq -r '.[] | .commit.message' > /tmp/pr_commits.txt - echo "--- PR commit messages ---" - cat /tmp/pr_commits.txt - echo "--------------------------" - - - name: Define shared agent detection patterns - run: | - set -euo pipefail - - # Email addresses known to be used by coding agents. - # These should never appear in Signed-off-by because the DCO can only be attested by a human. - EMAIL_PATTERN="copilot@github\.com\ - |noreply@anthropic\.com\ - |devin@cognition\.ai\ - |devin@cognition-labs\.com\ - |aider@aider\.chat\ - |noreply@aider\.chat\ - |codex@openai\.com\ - |cursor@anysphere\.com\ - |windsurf@codeium\.com\ - |codeium@codeium\.com\ - |amazon-q@amazon\.com\ - |codewhisperer@amazon\.com\ - |gemini-code-assist@google\.com\ - |openhands@all-hands\.dev\ - |swe-agent@princeton\.edu" - - # Strip embedded whitespace (used above only for readability) - EMAIL_PATTERN=$(echo "$EMAIL_PATTERN" | tr -d ' \n') - echo "AGENT_EMAIL_PATTERN=${EMAIL_PATTERN}" >> "$GITHUB_ENV" - - # Display-name prefixes used by known coding agents (shared by Signed-off-by and Co-Authored-By checks) - # shellcheck disable=SC2016 - echo 'AGENT_NAMES=GitHub Copilot|Claude( [A-Za-z0-9. -]+)?|Devin( AI)?|aider( \(.*\))?|OpenAI Codex|Cursor( AI)?|Windsurf|Amazon Q|CodeWhisperer|Gemini Code Assist|OpenHands|SWE-agent|AutoCodeRover|Tabnine' >> "$GITHUB_ENV" - - - name: Check for AI-assistant / Assisted-by trailers - id: ai_trailers - run: | - set -euo pipefail - AI_ASSISTED=false - if grep -qiE '^(AI-assistant|Assisted-by|AI-Assisted-By):' /tmp/pr_commits.txt; then - AI_ASSISTED=true - echo "Found AI-assistant/Assisted-by/AI-Assisted-By trailer(s):" - grep -iE '^(AI-assistant|Assisted-by|AI-Assisted-By):' /tmp/pr_commits.txt - fi - echo "ai_assisted=${AI_ASSISTED}" >> "$GITHUB_OUTPUT" - - - name: Check for coding-agent Signed-off-by trailers - id: agent_signoff - run: | - set -euo pipefail - - EMAIL_HITS=$(grep -iE "^Signed-off-by:.*<(${AGENT_EMAIL_PATTERN})>" /tmp/pr_commits.txt 2>/dev/null || true) - NAME_HITS=$(grep -iE "^Signed-off-by: *(${AGENT_NAMES}) *[<(]" /tmp/pr_commits.txt 2>/dev/null || true) - - AGENT_LINES=$(printf '%s\n%s' "$EMAIL_HITS" "$NAME_HITS" | sort -u | sed '/^[[:space:]]*$/d') - - AGENT_SIGNOFF=false - if [ -n "$AGENT_LINES" ]; then - AGENT_SIGNOFF=true - fi - - echo "agent_signoff=${AGENT_SIGNOFF}" >> "$GITHUB_OUTPUT" - { - echo "agent_lines<> "$GITHUB_OUTPUT" - - - name: Check for coding-agent Co-Authored-By trailers - id: co_authored - run: | - set -euo pipefail - - EMAIL_HITS=$(grep -iE "^Co-Authored-By:.*<(${AGENT_EMAIL_PATTERN})>" /tmp/pr_commits.txt 2>/dev/null || true) - NAME_HITS=$(grep -iE "^Co-Authored-By: *(${AGENT_NAMES}) *[<(]" /tmp/pr_commits.txt 2>/dev/null || true) - - CO_AUTHORED=false - if [ -n "$EMAIL_HITS" ] || [ -n "$NAME_HITS" ]; then - CO_AUTHORED=true - echo "Found coding-agent Co-Authored-By trailer(s):" - printf '%s\n%s' "$EMAIL_HITS" "$NAME_HITS" | sort -u | sed '/^[[:space:]]*$/d' - fi - - echo "co_authored=${CO_AUTHORED}" >> "$GITHUB_OUTPUT" - - - name: Create 'AI assisted' label if absent - if: steps.ai_trailers.outputs.ai_assisted == 'true' || steps.agent_signoff.outputs.agent_signoff == 'true' || steps.co_authored.outputs.co_authored == 'true' - env: - # Fall back to the default token when the PAT is unavailable - # (e.g. Dependabot- or fork-triggered runs don't receive Actions secrets) - GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT || github.token }} - run: | - gh api "repos/${{ github.repository }}/labels" \ - --method POST \ - -f name="AI assisted" \ - -f color="d93f0b" \ - -f description="This PR contains AI-assisted commits" \ - 2>/dev/null || true - - - name: Label PR as AI assisted - if: steps.ai_trailers.outputs.ai_assisted == 'true' || steps.agent_signoff.outputs.agent_signoff == 'true' || steps.co_authored.outputs.co_authored == 'true' - env: - # Fall back to the default token when the PAT is unavailable - # (e.g. Dependabot- or fork-triggered runs don't receive Actions secrets) - GH_TOKEN: ${{ secrets.COMMAND_BOT_PAT || github.token }} - run: | - gh api "repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/labels" \ - --method POST \ - -f "labels[]=AI assisted" - echo "Added 'AI assisted' label to PR #${{ github.event.pull_request.number }}" - - - name: Fail on coding-agent Signed-off-by - if: steps.agent_signoff.outputs.agent_signoff == 'true' - env: - AGENT_LINES: ${{ steps.agent_signoff.outputs.agent_lines }} - AGENTS_MD_URL: https://github.com/${{ github.repository }}/blob/${{ github.base_ref }}/AGENTS.md - run: | - echo "::error title=Coding-agent sign-off detected::A Signed-off-by trailer from a known coding agent was found in one or more commits." - echo "" - echo "Offending trailer(s):" - echo "${AGENT_LINES}" - echo "" - echo "The 'Signed-off-by' trailer represents the Developer Certificate of Origin (DCO)" - echo "and must only be attested by a human contributor." - echo "Please amend the affected commit(s) to remove the coding-agent sign-off" - echo "and replace it with an 'Assisted-by' trailer, for example:" - echo "" - echo " Assisted-by: Claude Code:claude-sonnet-4-6" - echo "" - echo "References:" - echo " • AGENTS.md (this repository)" - echo " ${AGENTS_MD_URL}" - echo " • AI Contribution Policy" - echo " https://github.com/nextcloud/.github/blob/master/AI_POLICY.md" - echo " • Contribution Guidelines" - echo " https://github.com/nextcloud/.github/blob/master/CONTRIBUTING.md" - exit 1 diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml deleted file mode 100644 index 06b080a060c..00000000000 --- a/.github/workflows/appstore-build-publish.yml +++ /dev/null @@ -1,202 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Build and publish app release - -on: - release: - types: [published] - -permissions: - contents: write - -jobs: - build_and_publish: - runs-on: ubuntu-latest - - # Only allowed to be run on nextcloud-releases repositories - if: ${{ github.repository_owner == 'nextcloud-releases' }} - - steps: - - name: Check actor permission - uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0 - with: - require: write - - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - echo "APP_VERSION=${GITHUB_REF##*/}" >> $GITHUB_ENV - - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: ${{ env.APP_NAME }} - - - name: Get app version number - id: app-version - uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 - with: - filename: ${{ env.APP_NAME }}/appinfo/info.xml - expression: "//info//version/text()" - - - name: Validate app version against tag - run: | - [ "${{ env.APP_VERSION }}" = "v${{ fromJSON(steps.app-version.outputs.result).version }}" ] - - - name: Get appinfo data - id: appinfo - uses: skjnldsv/xpath-action@f5b036e9d973f42c86324833fd00be90665fbf77 # v1.0.0 - with: - filename: ${{ env.APP_NAME }}/appinfo/info.xml - expression: "//info//dependencies//nextcloud/@min-version" - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - # Continue if no package.json - continue-on-error: true - with: - path: ${{ env.APP_NAME }} - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - # Skip if no package.json - if: ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - package-manager-cache: false - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - # Skip if no package.json - if: ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Get php version - id: php-versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - with: - filename: ${{ env.APP_NAME }}/appinfo/info.xml - - - name: Set up php ${{ steps.php-versions.outputs.php-min }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ steps.php-versions.outputs.php-min }} - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check composer.json - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "${{ env.APP_NAME }}/composer.json" - - - name: Install composer dependencies - if: steps.check_composer.outputs.files_exists == 'true' - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - composer-options: '--no-dev' - working-directory: ${{ env.APP_NAME }} - ignore-cache: 'yes' - - - name: Build ${{ env.APP_NAME }} - # Skip if no package.json - if: ${{ steps.versions.outputs.nodeVersion }} - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - cd ${{ env.APP_NAME }} - npm ci - npm run build --if-present - - - name: Check Krankerl config - id: krankerl - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: ${{ env.APP_NAME }}/krankerl.toml - - - name: Install Krankerl - if: steps.krankerl.outputs.files_exists == 'true' - run: | - wget https://github.com/ChristophWurst/krankerl/releases/download/v0.14.0/krankerl_0.14.0_amd64.deb - sudo dpkg -i krankerl_0.14.0_amd64.deb - - - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with krankerl - if: steps.krankerl.outputs.files_exists == 'true' - run: | - cd ${{ env.APP_NAME }} - krankerl package - - - name: Package ${{ env.APP_NAME }} ${{ env.APP_VERSION }} with makefile - if: steps.krankerl.outputs.files_exists != 'true' - run: | - cd ${{ env.APP_NAME }} - make appstore version=${{ fromJSON(steps.app-version.outputs.result).version }} - - - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} - run: | - NCVERSION='${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }}' - DOWNLOAD_URL=$(curl -s "https://updates.nextcloud.com/updater_server/latest?channel=beta&version=$NCVERSION" | jq -r '.downloads.zip[0]') - echo "DOWNLOAD_URL=$DOWNLOAD_URL" >> $GITHUB_ENV - - - name: Download server ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} - continue-on-error: true - id: server-download - if: ${{ env.DOWNLOAD_URL != 'null' }} - run: | - echo "Downloading release tarball from $DOWNLOAD_URL" - wget $DOWNLOAD_URL -O nextcloud.zip - unzip nextcloud.zip - - - name: Checkout server master fallback - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - if: ${{ steps.server-download.outcome != 'success' }} - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - path: nextcloud - - - - name: Sign app - run: | - # Extracting release - cd ${{ env.APP_NAME }}/build/artifacts - tar -xvf ${{ env.APP_NAME }}.tar.gz - cd ../../../ - # Setting up keys - echo '${{ secrets.APP_PRIVATE_KEY }}' > ${{ env.APP_NAME }}.key - wget --quiet "https://github.com/nextcloud/app-certificate-requests/raw/master/${{ env.APP_NAME }}/${{ env.APP_NAME }}.crt" - # Signing - php nextcloud/occ integrity:sign-app --privateKey=../${{ env.APP_NAME }}.key --certificate=../${{ env.APP_NAME }}.crt --path=../${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }} - # Rebuilding archive - cd ${{ env.APP_NAME }}/build/artifacts - tar -zcvf ${{ env.APP_NAME }}.tar.gz ${{ env.APP_NAME }} - - - name: Attach tarball to github release - uses: svenstaro/upload-release-action@29e53e917877a24fad85510ded594ab3c9ca12de # 2.11.5 - id: attach_to_release - with: - repo_token: ${{ secrets.GITHUB_TOKEN }} - file: ${{ env.APP_NAME }}/build/artifacts/${{ env.APP_NAME }}.tar.gz - asset_name: ${{ env.APP_NAME }}-${{ env.APP_VERSION }}.tar.gz - tag: ${{ github.ref }} - overwrite: true - - - name: Upload app to Nextcloud appstore - uses: nextcloud-libraries/nextcloud-appstore-push-action@a011fe619bcf6e77ddebc96f9908e1af4071b9c1 # v1.0.3 - with: - app_name: ${{ env.APP_NAME }} - appstore_token: ${{ secrets.APPSTORE_TOKEN }} - download_url: ${{ steps.attach_to_release.outputs.browser_download_url }} - app_private_key: ${{ secrets.APP_PRIVATE_KEY }} diff --git a/.github/workflows/appstore-build-publish.yml.patch b/.github/workflows/appstore-build-publish.yml.patch deleted file mode 100644 index 211cfc822f7..00000000000 --- a/.github/workflows/appstore-build-publish.yml.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/.github/workflows/appstore-build-publish.yml b/.github/workflows/appstore-build-publish.yml -index 31086eaf2f..16834c4893 100644 ---- a/.github/workflows/appstore-build-publish.yml -+++ b/.github/workflows/appstore-build-publish.yml -@@ -138,7 +138,7 @@ jobs: - if: steps.krankerl.outputs.files_exists != 'true' - run: | - cd ${{ env.APP_NAME }} -- make appstore -+ make appstore version=${{ fromJSON(steps.app-version.outputs.result).version }} - - - name: Check server download link for ${{ fromJSON(steps.appinfo.outputs.result).nextcloud.min-version }} - run: | diff --git a/.github/workflows/block-unconventional-commits.yml b/.github/workflows/block-unconventional-commits.yml deleted file mode 100644 index 4972f8bb33d..00000000000 --- a/.github/workflows/block-unconventional-commits.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block unconventional commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: block-unconventional-commits-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - block-unconventional-commits: - name: Block unconventional commits - - runs-on: ubuntu-latest-low - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: webiny/action-conventional-commits@7f91b1595ca1951cdb671ddc9f07a49081ec5b69 # v1.4.2 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/command-openapi.yml b/.github/workflows/command-openapi.yml deleted file mode 100644 index fb7ca980a74..00000000000 --- a/.github/workflows/command-openapi.yml +++ /dev/null @@ -1,199 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: OpenAPI Command -on: - issue_comment: - types: [created] - -permissions: - contents: read - -jobs: - init: - runs-on: ubuntu-latest - - # On pull requests and if the comment starts with `/openapi` - if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/openapi') - - outputs: - git_path: ${{ steps.git-path.outputs.path }} - arg1: ${{ steps.command.outputs.arg1 }} - arg2: ${{ steps.command.outputs.arg2 }} - head_ref: ${{ steps.comment-branch.outputs.head_ref }} - base_ref: ${{ steps.comment-branch.outputs.base_ref }} - - steps: - - name: Get repository from pull request comment - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - id: get-repository - with: - github-token: ${{secrets.GITHUB_TOKEN}} - script: | - const pull = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number - }); - - const repositoryName = pull.data.head?.repo?.full_name - console.log(repositoryName) - return repositoryName - - - name: Disabled on forks - if: ${{ fromJSON(steps.get-repository.outputs.result) != github.repository }} - run: | - echo 'Can not execute /openapi on forks' - exit 1 - - - name: Check actor permission - uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0 - with: - require: write - - - name: Add reaction on start - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '+1' - - - name: Parse command - uses: skjnldsv/parse-command-comment@5c955203c52424151e6d0e58fb9de8a9f6a605a1 # v3.1 - id: command - - # Init path depending on which command is run - - name: Init path - id: git-path - run: | - if ${{ startsWith(steps.command.outputs.arg1, '/') }}; then - echo "path=${{steps.command.outputs.arg1}}" >> $GITHUB_OUTPUT - else - echo "path=${{steps.command.outputs.arg2}}" >> $GITHUB_OUTPUT - fi - - - name: Init branch - uses: xt0rted/pull-request-comment-branch@e8b8daa837e8ea7331c0003c9c316a64c6d8b0b1 # v3.0.0 - id: comment-branch - - - name: Add reaction on failure - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 - if: failure() - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '-1' - - process: - runs-on: ubuntu-latest - needs: init - - steps: - - name: Restore cached git repository - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .git - key: git-repo - - - name: Checkout ${{ needs.init.outputs.head_ref }} - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - # Needed to allow force push later - persist-credentials: true - token: ${{ secrets.COMMAND_BOT_PAT }} - fetch-depth: 0 - ref: ${{ needs.init.outputs.head_ref }} - - - name: Setup git - run: | - git config --local user.email 'nextcloud-command@users.noreply.github.com' - git config --local user.name 'nextcloud-command' - - - name: Check Typescript OpenApi types - id: check_typescript_openapi - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "src/types/openapi/openapi*.ts" - - - name: Read package.json node and npm engines version - if: steps.check_typescript_openapi.outputs.files_exists == 'true' - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: node_versions - # Continue if no package.json - continue-on-error: true - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.node_versions.outputs.nodeVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.node_versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.node_versions.outputs.npmVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}' - - - name: Rebase to ${{ needs.init.outputs.base_ref }} - if: ${{ contains(needs.init.outputs.arg1, 'rebase') }} - run: | - git fetch origin '${{ needs.init.outputs.base_ref }}:${{ needs.init.outputs.base_ref }}' - git rebase 'origin/${{ needs.init.outputs.base_ref }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Regenerate OpenAPI - run: composer run openapi - - - name: Commit default - if: ${{ !contains(needs.init.outputs.arg1, 'fixup') && !contains(needs.init.outputs.arg1, 'amend') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --signoff -m 'fix(openapi): Regenerate OpenAPI assets' - - - name: Commit fixup - if: ${{ contains(needs.init.outputs.arg1, 'fixup') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --fixup=HEAD --signoff - - - name: Commit amend - if: ${{ contains(needs.init.outputs.arg1, 'amend') }} - run: | - git add '${{ github.workspace }}${{ needs.init.outputs.git_path }}' - git commit --amend --no-edit --signoff - # Remove any [skip ci] from the amended commit - git commit --amend -m "$(git log -1 --format='%B' | sed '/\[skip ci\]/d')" - - - name: Push normally - if: ${{ !contains(needs.init.outputs.arg1, 'rebase') && !contains(needs.init.outputs.arg1, 'amend') }} - run: git push origin '${{ needs.init.outputs.head_ref }}' - - - name: Force push - if: ${{ contains(needs.init.outputs.arg1, 'rebase') || contains(needs.init.outputs.arg1, 'amend') }} - run: git push --force-with-lease origin '${{ needs.init.outputs.head_ref }}' - - - name: Add reaction on failure - uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5.0.0 - if: failure() - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - repository: ${{ github.event.repository.full_name }} - comment-id: ${{ github.event.comment.id }} - reactions: '-1' diff --git a/.github/workflows/dependabot-approve-merge.yml b/.github/workflows/dependabot-approve-merge.yml deleted file mode 100644 index 68f8beb6f77..00000000000 --- a/.github/workflows/dependabot-approve-merge.yml +++ /dev/null @@ -1,101 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Auto approve Dependabot PRs - -on: - pull_request: - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: dependabot-approve-merge-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - auto-approve-merge: - if: github.event.pull_request.user.login == 'dependabot[bot]' - runs-on: ubuntu-latest-low - env: - # env variable for maintainers: 'true' allows to auto-merge 1.0.2 -> 2.0.0 - ALLOW_MAJOR: false - # env variable for maintainers: 'true' allows to auto-merge 1.0.2 -> 1.1.0 - ALLOW_MINOR: false - # env variable for maintainers: RegExp string to ignore some dependencies from auto-approve and auto-merge - IGNORE_PATTERN: '(webrtc-adapter|\@nextcloud\/vue)' - permissions: - # for auto-approve step to work - pull-requests: write - # for alexwilson/enable-github-automerge-action to approve PRs - contents: write - - steps: - - name: Disabled on forks - if: ${{ github.event.pull_request.head.repo.full_name != github.repository }} - run: | - echo 'Can not approve PRs from forks' - exit 1 - - - uses: mdecoleman/pr-branch-name@55795d86b4566d300d237883103f052125cc7508 # v3.0.0 - id: branchname - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Dependabot metadata - id: metadata - if: startsWith(steps.branchname.outputs.branch, 'dependabot/') - uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Check for ignored dependencies in the PR - id: validate - if: startsWith(steps.branchname.outputs.branch, 'dependabot/') - env: - IGNORE_PATTERN: ${{ env.IGNORE_PATTERN }} - DEPENDENCY_NAMES: ${{ steps.metadata.outputs.dependency-names }} - run: | - if [[ -z ${IGNORE_PATTERN} ]]; then - echo "ignore=false" >> "$GITHUB_OUTPUT" - elif [[ -z ${DEPENDENCY_NAMES} ]]; then - echo "ignore=false" >> "$GITHUB_OUTPUT" - elif [[ ${DEPENDENCY_NAMES} =~ ${IGNORE_PATTERN} ]]; then - echo "ignore=true" >> "$GITHUB_OUTPUT" - fi - - - name: GitHub actions bot approve - id: auto_approve - if: ${{ - startsWith(steps.branchname.outputs.branch, 'dependabot/') - && steps.validate.outputs.ignore != 'true' - }} - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{ github.event.pull_request.html_url }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - # Enable GitHub auto merge - - name: Auto merge - uses: alexwilson/enable-github-automerge-action@2c32e18a76e0726ffe7a573bfff2d42a20885126 # 3.0.0 - if: ${{ - startsWith(steps.branchname.outputs.branch, 'dependabot/') - && steps.auto_approve.conclusion == 'success' - && (github.event.action == 'opened' || github.event.action == 'reopened') - && ( - steps.metadata.outputs.update-type == 'version-update:semver-patch' - || (fromJSON(env.ALLOW_MINOR) && steps.metadata.outputs.update-type == 'version-update:semver-minor') - || (fromJSON(env.ALLOW_MAJOR) && steps.metadata.outputs.update-type == 'version-update:semver-major') - ) - }} - with: - github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/dependabot-approve-merge.yml.patch b/.github/workflows/dependabot-approve-merge.yml.patch deleted file mode 100644 index c47734a24f9..00000000000 --- a/.github/workflows/dependabot-approve-merge.yml.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/.github/workflows/dependabot-approve-merge.yml b/.github/workflows/dependabot-approve-merge.yml -index 68f8ca09c2..3d34b9735a 100644 ---- a/.github/workflows/dependabot-approve-merge.yml -+++ b/.github/workflows/dependabot-approve-merge.yml -@@ -30,9 +30,9 @@ jobs: - # env variable for maintainers: 'true' allows to auto-merge 1.0.2 -> 2.0.0 - ALLOW_MAJOR: false - # env variable for maintainers: 'true' allows to auto-merge 1.0.2 -> 1.1.0 -- ALLOW_MINOR: true -+ ALLOW_MINOR: false - # env variable for maintainers: RegExp string to ignore some dependencies from auto-approve and auto-merge -- IGNORE_PATTERN: '' -+ IGNORE_PATTERN: '(webrtc-adapter|\@nextcloud\/vue)' - permissions: - # for auto-approve step to work - pull-requests: write diff --git a/.github/workflows/fixup.yml b/.github/workflows/fixup.yml deleted file mode 100644 index 69da2bbb039..00000000000 --- a/.github/workflows/fixup.yml +++ /dev/null @@ -1,36 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Block fixup and squash commits - -on: - pull_request: - types: [opened, ready_for_review, reopened, synchronize] - -permissions: - contents: read - -concurrency: - group: fixup-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - commit-message-check: - if: github.event.pull_request.draft == false - - permissions: - pull-requests: write - name: Block fixup and squash commits - - runs-on: ubuntu-latest-low - - steps: - - name: Run check - uses: skjnldsv/block-fixup-merge-action@c138ea99e45e186567b64cf065ce90f7158c236a # v2 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/integration-federation.yml b/.github/workflows/integration-federation.yml deleted file mode 100644 index d7bc4473ed4..00000000000 --- a/.github/workflows/integration-federation.yml +++ /dev/null @@ -1,360 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration Federation - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-federation-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-federation: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - name: Federation ${{ matrix.host-server-versions }} (${{ matrix.host-db }}) -> ${{ matrix.remote-server-versions }} (${{ matrix.remote-db }}) - - strategy: - fail-fast: false - matrix: - include: - - test-suite: 'federation' - php-versions: '8.3' - host-db: 'oci' - host-server-versions: 'master' - host-spreed-versions: ${{ github.event.pull_request.head.sha }} - host-guests-versions: 'main' - host-circles-versions: 'master' - host-notifications-versions: 'master' - remote-db: 'mysql' - remote-server-versions: 'stable35' - remote-spreed-versions: 'stable35' - remote-guests-versions: 'main' - remote-circles-versions: 'stable35' - remote-notifications-versions: 'stable35' - - test-suite: 'federation' - php-versions: '8.3' - host-db: 'pgsql' - host-server-versions: 'master' - host-spreed-versions: ${{ github.event.pull_request.head.sha }} - host-guests-versions: 'main' - host-circles-versions: 'master' - host-notifications-versions: 'master' - remote-db: 'mysql' - remote-server-versions: 'stable35' - remote-spreed-versions: 'stable35' - remote-guests-versions: 'main' - remote-circles-versions: 'stable35' - remote-notifications-versions: 'stable35' - - test-suite: 'federation' - php-versions: '8.3' - host-db: 'oci' - host-server-versions: 'stable35' - host-spreed-versions: 'stable35' - host-guests-versions: 'main' - host-circles-versions: 'stable35' - host-notifications-versions: 'stable35' - remote-db: 'mysql' - remote-server-versions: 'master' - remote-spreed-versions: ${{ github.event.pull_request.head.sha }} - remote-guests-versions: 'main' - remote-circles-versions: 'master' - remote-notifications-versions: 'master' - - services: - oracle: - image: ghcr.io/gvenzl/oracle-free:23 - - # Provide passwords and other environment variables to container - env: - ORACLE_PASSWORD: oracle - - # Forward Oracle port - ports: - - 1521:1521 - - # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 20s - --health-timeout 10s - --health-retries 10 - - mysql: - image: ghcr.io/nextcloud/continuous-integration-mysql-8.4:latest # zizmor: ignore[unpinned-images] - ports: - - 4444:3306/tcp - env: - MYSQL_ROOT_PASSWORD: rootpassword - options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10 - - postgres: - image: ghcr.io/nextcloud/continuous-integration-postgres-18:latest # zizmor: ignore[unpinned-images] - ports: - - 4445:5432/tcp - env: - POSTGRES_USER: root - POSTGRES_PASSWORD: rootpassword - POSTGRES_DB: nextcloud - options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - Host - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: host - submodules: true - repository: nextcloud/server - ref: ${{ matrix.host-server-versions }} - - - name: Checkout spreed app - Host - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: host/apps/spreed - ref: ${{ matrix.host-spreed-versions }} - - - name: Checkout circles app - Host - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: host/apps/circles - ref: ${{ matrix.host-circles-versions }} - - - name: Checkout guests app - Host - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: host/apps/guests - ref: ${{ matrix.host-guests-versions }} - - - name: Checkout notifications app - Host - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: host/apps/notifications - ref: ${{ matrix.host-notifications-versions }} - - - name: Checkout server - Remote - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: remote - submodules: true - repository: nextcloud/server - ref: ${{ matrix.remote-server-versions }} - - - name: Checkout spreed app - Remote - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: remote/apps/spreed - ref: ${{ matrix.remote-spreed-versions }} - - - name: Checkout circles app - Remote - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: remote/apps/circles - ref: ${{ matrix.remote-circles-versions }} - - - name: Checkout guests app - Remote - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: remote/apps/guests - ref: ${{ matrix.remote-guests-versions }} - - - name: Checkout notifications app - Remote - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: remote/apps/notifications - ref: ${{ matrix.remote-notifications-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql, oci8, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up production dependencies - Host - working-directory: host/apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up production dependencies - Remote - working-directory: remote/apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - Host - working-directory: host/apps/notifications - run: composer i --no-dev - - - name: Set up notifications production dependencies - Remote - working-directory: remote/apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: host/apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - Host (Postgres) - if: ${{ matrix.host-db == 'pgsql' }} - env: - DB_PORT: 4445 - working-directory: host/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Host (Oracle) - if: ${{ matrix.host-db == 'oci' }} - env: - DB_PORT: 1521 - working-directory: host/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=oci --database-name=FREE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=system --database-pass=oracle --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Host (MySQL) - if: ${{ matrix.host-db == 'mysql' }} - env: - DB_PORT: 4444 - working-directory: host/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Remote (Postgres) - if: ${{ matrix.remote-db == 'pgsql' }} - env: - DB_PORT: 4445 - working-directory: remote/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Remote (Oracle) - if: ${{ matrix.remote-db == 'oracle' }} - env: - DB_PORT: 1521 - working-directory: remote/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=oci --database-name=FREE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=system --database-pass=oracle --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Remote (MySQL) - if: ${{ matrix.remote-db == 'mysql' }} - env: - DB_PORT: 4444 - working-directory: remote/ - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - - - name: Set up Nextcloud - Host (Shared) - working-directory: host/ - run: | - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - - - name: Set up Nextcloud - Remote (Shared) - working-directory: remote/ - run: | - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - echo 'REMOTE_ROOT_DIR='$PWD >> $GITHUB_ENV - - - name: Run integration - working-directory: host/apps/${{ env.APP_NAME }}/tests/integration - run: REMOTE_ROOT_DIR=${{ env.REMOTE_ROOT_DIR }} bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - Host - if: always() - working-directory: host/ - run: | - cat data/nextcloud.log - - - name: Print logs - Remote - if: always() - working-directory: remote/ - run: | - cat data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-federation] - - if: always() - - name: integration-federation-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-federation.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-mariadb.yml b/.github/workflows/integration-mariadb.yml deleted file mode 100644 index 2932b7bee49..00000000000 --- a/.github/workflows/integration-mariadb.yml +++ /dev/null @@ -1,203 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration mariadb - -on: - pull_request: - paths: - # Only running on PR for this file to save CI time (otherwise pgsql only) - - '.github/workflows/integration-mariadb.yml' - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-mariadb-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-mariadb: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - test-suite: ['callapi', 'chat-1', 'chat-2', 'chat-3', 'chat-4', 'command', 'conversation-1', 'conversation-2', 'conversation-3', 'conversation-4', 'conversation-5', 'federation', 'integration', 'sharing-1', 'sharing-2', 'sharing-3', 'sharing-4'] - php-versions: ['8.3'] - server-versions: ['master'] - guests-versions: ['main'] - circles-versions: ['master'] - notifications-versions: ['master'] - password-policy-versions: ['master'] - - services: - mariadb: - image: mariadb:12.3 # zizmor: ignore[unpinned-images] - ports: - - 4444:3306/tcp - env: - MARIADB_ROOT_PASSWORD: rootpassword - options: --health-cmd="mariadb-admin ping" --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Checkout circles app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: apps/circles - ref: ${{ matrix.circles-versions }} - - - name: Checkout guests app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: apps/guests - ref: ${{ matrix.guests-versions }} - - - name: Checkout notifications app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: apps/notifications - ref: ${{ matrix.notifications-versions }} - - - name: Checkout password_policy app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/password_policy - path: apps/password_policy - ref: ${{ matrix.password-policy-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable ONLY_FULL_GROUP_BY MariaDB option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo 'SELECT @@sql_mode;' | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - - name: Set up production dependencies - working-directory: apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - working-directory: apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - ./occ app:disable password_policy - - - name: Run integration - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Check Federation log - if: always() - id: check_federation_log - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "data/tests-talk-real-federated-server/data/nextcloud.log" - - - name: Print federation logs - if: always() && steps.check_federation_log.outputs.files_exists == 'true' - run: | - cat data/tests-talk-real-federated-server/data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-mariadb] - - if: always() - - name: integration-mariadb-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-mariadb.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-mysql.yml b/.github/workflows/integration-mysql.yml deleted file mode 100644 index 7a866c674d0..00000000000 --- a/.github/workflows/integration-mysql.yml +++ /dev/null @@ -1,203 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration mysql - -on: - pull_request: - paths: - # Only running on PR for this file to save CI time (otherwise pgsql only) - - '.github/workflows/integration-mysql.yml' - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-mysql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-mysql: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - test-suite: ['callapi', 'chat-1', 'chat-2', 'chat-3', 'chat-4', 'command', 'conversation-1', 'conversation-2', 'conversation-3', 'conversation-4', 'conversation-5', 'federation', 'integration', 'sharing-1', 'sharing-2', 'sharing-3', 'sharing-4'] - php-versions: ['8.3'] - server-versions: ['master'] - guests-versions: ['main'] - circles-versions: ['master'] - notifications-versions: ['master'] - password-policy-versions: ['master'] - - services: - mysql: - image: mysql:9.7 # zizmor: ignore[unpinned-images] - ports: - - 4444:3306/tcp - env: - MYSQL_ROOT_PASSWORD: rootpassword - options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10 - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Checkout circles app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: apps/circles - ref: ${{ matrix.circles-versions }} - - - name: Checkout guests app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: apps/guests - ref: ${{ matrix.guests-versions }} - - - name: Checkout notifications app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: apps/notifications - ref: ${{ matrix.notifications-versions }} - - - name: Checkout password_policy app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/password_policy - path: apps/password_policy - ref: ${{ matrix.password-policy-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable ONLY_FULL_GROUP_BY MySQL option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo 'SELECT @@sql_mode;' | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - - name: Set up production dependencies - working-directory: apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - working-directory: apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - ./occ app:disable password_policy - - - name: Run integration - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Check Federation log - if: always() - id: check_federation_log - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "data/tests-talk-real-federated-server/data/nextcloud.log" - - - name: Print federation logs - if: always() && steps.check_federation_log.outputs.files_exists == 'true' - run: | - cat data/tests-talk-real-federated-server/data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-mysql] - - if: always() - - name: integration-mysql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-mysql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-oci.yml b/.github/workflows/integration-oci.yml deleted file mode 100644 index eeec3a71ae0..00000000000 --- a/.github/workflows/integration-oci.yml +++ /dev/null @@ -1,208 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration oci - -on: - pull_request: - paths: - # Only running on PR for this file to save CI time (otherwise pgsql only) - - '.github/workflows/integration-oci.yml' - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-oci-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-oci: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - test-suite: ['callapi', 'chat-1', 'chat-2', 'chat-3', 'chat-4', 'command', 'conversation-1', 'conversation-2', 'conversation-3', 'conversation-4', 'conversation-5', 'federation', 'integration', 'sharing-1', 'sharing-2', 'sharing-3', 'sharing-4'] - php-versions: ['8.3'] - server-versions: ['master'] - guests-versions: ['main'] - circles-versions: ['master'] - notifications-versions: ['master'] - password-policy-versions: ['master'] - - services: - oracle: - image: ghcr.io/gvenzl/oracle-free:23 - - # Provide passwords and other environment variables to container - env: - ORACLE_PASSWORD: oracle - - # Forward Oracle port - ports: - - 1521:1521 - - # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 20s - --health-timeout 10s - --health-retries 10 - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Checkout circles app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: apps/circles - ref: ${{ matrix.circles-versions }} - - - name: Checkout guests app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: apps/guests - ref: ${{ matrix.guests-versions }} - - - name: Checkout notifications app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: apps/notifications - ref: ${{ matrix.notifications-versions }} - - - name: Checkout password_policy app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/password_policy - path: apps/password_policy - ref: ${{ matrix.password-policy-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, oci8, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up production dependencies - working-directory: apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - working-directory: apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 1521 - run: | - mkdir data - ./occ maintenance:install --verbose --database=oci --database-name=FREE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=system --database-pass=oracle --admin-user admin --admin-pass admin - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - ./occ app:disable password_policy - - - name: Run integration - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Check Federation log - if: always() - id: check_federation_log - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "data/tests-talk-real-federated-server/data/nextcloud.log" - - - name: Print federation logs - if: always() && steps.check_federation_log.outputs.files_exists == 'true' - run: | - cat data/tests-talk-real-federated-server/data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-oci] - - if: always() - - name: integration-oci-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-oci.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-pgsql.yml b/.github/workflows/integration-pgsql.yml deleted file mode 100644 index 194a8710b1e..00000000000 --- a/.github/workflows/integration-pgsql.yml +++ /dev/null @@ -1,231 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration pgsql - -on: - pull_request: - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-pgsql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-pgsql: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - test-suite: - - 'callapi' - - 'chat-1' - - 'chat-2' - - 'chat-3' - - 'chat-4' - - 'command' - - 'conversation-1' - - 'conversation-2' - - 'conversation-3' - - 'conversation-4' - - 'conversation-5' - - 'federation' - - 'integration' - - 'scaling' - - 'sharing-1' - - 'sharing-2' - - 'sharing-3' - - 'sharing-4' - php-versions: ['8.5'] - server-versions: ['master'] - guests-versions: ['main'] - circles-versions: ['master'] - notifications-versions: ['master'] - password-policy-versions: ['master'] - - services: - postgres: - image: ghcr.io/nextcloud/continuous-integration-postgres-18:latest # zizmor: ignore[unpinned-images] - ports: - - 4444:5432/tcp - env: - POSTGRES_USER: root - POSTGRES_PASSWORD: rootpassword - POSTGRES_DB: nextcloud - options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Patch version check for nightly PHP - if: ${{ matrix.php-versions == '8.4' }} - run: echo " lib/versioncheck.php - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Checkout circles app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: apps/circles - ref: ${{ matrix.circles-versions }} - - - name: Checkout guests app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: apps/guests - ref: ${{ matrix.guests-versions }} - - - name: Checkout notifications app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: apps/notifications - ref: ${{ matrix.notifications-versions }} - - - name: Checkout password_policy app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/password_policy - path: apps/password_policy - ref: ${{ matrix.password-policy-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up production dependencies - working-directory: apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - working-directory: apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - ./occ app:disable password_policy - - - name: Run integration - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Print query.log - if: matrix.test-suite == 'scaling' - run: | - cat data/query.log - - - name: Upload query.log - if: matrix.test-suite == 'scaling' - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: query-list.txt - path: data/query.log - - - name: Check Federation log - if: always() - id: check_federation_log - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "data/tests-talk-real-federated-server/data/nextcloud.log" - - - name: Print federation logs - if: always() && steps.check_federation_log.outputs.files_exists == 'true' - run: | - cat data/tests-talk-real-federated-server/data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-pgsql] - - if: always() - - name: integration-pgsql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-pgsql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/integration-sqlite.yml b/.github/workflows/integration-sqlite.yml deleted file mode 100644 index 4ca7442478a..00000000000 --- a/.github/workflows/integration-sqlite.yml +++ /dev/null @@ -1,189 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Integration sqlite - -on: - pull_request: - paths: - # Only running on PR for this file to save CI time (otherwise pgsql only) - - '.github/workflows/integration-sqlite.yml' - schedule: - - cron: "5 2 * * *" - -permissions: - contents: read - -concurrency: - group: integration-sqlite-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/integration/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - integration-sqlite: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - test-suite: ['callapi', 'chat-1', 'chat-2', 'chat-3', 'chat-4', 'command', 'conversation-1', 'conversation-2', 'conversation-3', 'conversation-4', 'conversation-5', 'federation', 'integration', 'sharing-1', 'sharing-2', 'sharing-3', 'sharing-4'] - php-versions: ['8.3'] - server-versions: ['master'] - guests-versions: ['main'] - circles-versions: ['master'] - notifications-versions: ['master'] - password-policy-versions: ['master'] - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Checkout circles app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/circles - path: apps/circles - ref: ${{ matrix.circles-versions }} - - - name: Checkout guests app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/guests - path: apps/guests - ref: ${{ matrix.guests-versions }} - - - name: Checkout notifications app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/notifications - path: apps/notifications - ref: ${{ matrix.notifications-versions }} - - - name: Checkout password_policy app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - repository: nextcloud/password_policy - path: apps/password_policy - ref: ${{ matrix.password-policy-versions }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: apcu, bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3: ini-values: apc.enable_cli=on - ini-values: apc.enable_cli=on, disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up production dependencies - working-directory: apps/${{ env.APP_NAME }} - run: composer i --no-dev - - - name: Set up notifications production dependencies - working-directory: apps/notifications - run: composer i --no-dev - - - name: Set up behat dependencies - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ config:system:set debug --value=true --type=boolean - ./occ config:system:set hashing_default_password --value=true --type=boolean - ./occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu" - ./occ config:system:set memcache.distributed --value="\\OC\\Memcache\\APCu" - ./occ app:enable --force ${{ env.APP_NAME }} - ./occ app:enable --force circles - ./occ app:enable --force guests - ./occ app:enable --force notifications - ./occ app:disable password_policy - - - name: Run integration - working-directory: apps/${{ env.APP_NAME }}/tests/integration - run: bash run.sh features/${{ matrix.test-suite }} - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Check Federation log - if: always() - id: check_federation_log - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "data/tests-talk-real-federated-server/data/nextcloud.log" - - - name: Print federation logs - if: always() && steps.check_federation_log.outputs.files_exists == 'true' - run: | - cat data/tests-talk-real-federated-server/data/nextcloud.log - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, integration-sqlite] - - if: always() - - name: integration-sqlite-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.integration-sqlite.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/lint-action.yml b/.github/workflows/lint-action.yml deleted file mode 100644 index 66475cf7c2e..00000000000 --- a/.github/workflows/lint-action.yml +++ /dev/null @@ -1,42 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint GitHub Actions - -on: - pull_request: - paths: - - '.github/workflows/**' - -permissions: - contents: read - -jobs: - action-lint: - runs-on: ubuntu-latest - - name: GitHub Actions lint - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: GitHub action templates lint - uses: ibiqlik/action-yamllint@2576378a8e339169678f9939646ee3ee325e845c # v3.1.1 - with: - file_or_dir: .github/workflows - config_data: | - line-length: warning - - - name: Install the latest version of uv - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0 - - - name: Check GitHub actions - run: uvx zizmor --min-severity medium .github/workflows/*.yml diff --git a/.github/workflows/lint-eslint.yml b/.github/workflows/lint-eslint.yml deleted file mode 100644 index 317e3c67546..00000000000 --- a/.github/workflows/lint-eslint.yml +++ /dev/null @@ -1,100 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint eslint - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-eslint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'src/**' - - 'appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '.eslintrc.*' - - '.eslintignore' - - '**.js' - - '**.ts' - - '**.vue' - - lint: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM lint - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: npm ci - - - name: Lint - run: npm run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, lint] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: eslint - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.lint.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/lint-info-xml.yml b/.github/workflows/lint-info-xml.yml deleted file mode 100644 index 8e6968c9160..00000000000 --- a/.github/workflows/lint-info-xml.yml +++ /dev/null @@ -1,40 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint info.xml - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-info-xml-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - xml-linters: - runs-on: ubuntu-latest-low - - name: info.xml lint - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - sparse-checkout: | - appinfo/ - - - name: Download schema - run: wget https://raw.githubusercontent.com/nextcloud/appstore/master/nextcloudappstore/api/v1/release/info.xsd - - - name: Lint info.xml - uses: ChristophWurst/xmllint-action@36f2a302f84f8c83fceea0b9c59e1eb4a616d3c1 # v1.2 - with: - xml-file: ./appinfo/info.xml - xml-schema-file: ./info.xsd diff --git a/.github/workflows/lint-php-cs.yml b/.github/workflows/lint-php-cs.yml deleted file mode 100644 index 06d0842fda7..00000000000 --- a/.github/workflows/lint-php-cs.yml +++ /dev/null @@ -1,54 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php-cs - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-cs-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - - name: php-cs - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get php version - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - - name: Set up php${{ steps.versions.outputs.php-min }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ steps.versions.outputs.php-min }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Remove nextcloud/ocp - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Lint - run: composer run cs:check || ( echo 'Please run `composer run cs:fix` to format your code' && exit 1 ) diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml deleted file mode 100644 index e4c6e9ba632..00000000000 --- a/.github/workflows/lint-php.yml +++ /dev/null @@ -1,76 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint php - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-php-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - matrix: - runs-on: ubuntu-latest-low - outputs: - php-min: ${{ steps.versions.outputs.php-min }} - php-max: ${{ steps.versions.outputs.php-max }} - steps: - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - php-lint: - runs-on: ubuntu-latest-low - needs: matrix - strategy: - matrix: - php-versions: ['${{ needs.matrix.outputs.php-min }}', '${{ needs.matrix.outputs.php-max }}'] - - name: php-lint - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Lint - run: composer run lint - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: php-lint - - if: always() - - name: php-lint-summary - - steps: - - name: Summary status - run: if ${{ needs.php-lint.result != 'success' && needs.php-lint.result != 'skipped' }}; then exit 1; fi diff --git a/.github/workflows/lint-stylelint.yml b/.github/workflows/lint-stylelint.yml deleted file mode 100644 index 89149dd4e1b..00000000000 --- a/.github/workflows/lint-stylelint.yml +++ /dev/null @@ -1,53 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Lint stylelint - -on: pull_request - -permissions: - contents: read - -concurrency: - group: lint-stylelint-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - lint: - runs-on: ubuntu-latest - - name: stylelint - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - run: npm ci - - - name: Lint - run: npm run stylelint diff --git a/.github/workflows/lint-typescript.yml b/.github/workflows/lint-typescript.yml deleted file mode 100644 index 51037d404d8..00000000000 --- a/.github/workflows/lint-typescript.yml +++ /dev/null @@ -1,100 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Type checking - -on: - pull_request: - push: - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: lint-typescript-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/lint-typescript.yml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.ts' - - '**.vue' - - test: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci - - - name: Check types - run: | - npm run --if-present check-types - npm run --if-present ts:check - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, test] - - if: always() - - name: typescript-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.test.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/node-test.yml b/.github/workflows/node-test.yml deleted file mode 100644 index 18fc60a0729..00000000000 --- a/.github/workflows/node-test.yml +++ /dev/null @@ -1,110 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Node tests - -on: - pull_request: - push: - branches: - - main - - master - - stable* - -permissions: - contents: read - -concurrency: - group: node-tests-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - '__tests__/**' - - '__mocks__/**' - - 'src/**' - - 'appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.js' - - '**.ts' - - '**.vue' - - test: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - # In contrast to the workflow template, there is no build step here - - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci - - # In contrast to the workflow template, there is no coverage step here - - name: Test - run: npm run test --if-present - - - name: Collect coverage - uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1 - with: - files: ./coverage/lcov.info - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, test] - - if: always() - - name: test-summary - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.test.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/node-test.yml.patch b/.github/workflows/node-test.yml.patch deleted file mode 100644 index d55e1de3bf2..00000000000 --- a/.github/workflows/node-test.yml.patch +++ /dev/null @@ -1,27 +0,0 @@ -diff --git b/.github/workflows/node-test.yml a/.github/workflows/node-test.yml -index a61b9cb06..9081c98b6 100644 ---- b/.github/workflows/node-test.yml -+++ a/.github/workflows/node-test.yml -@@ -79,19 +79,17 @@ jobs: - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - -- - name: Install dependencies & build -+ # In contrast to the workflow template, there is no build step here -+ - name: Install dependencies - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci -- npm run build --if-present - -+ # In contrast to the workflow template, there is no coverage step here - - name: Test - run: npm run test --if-present - -- - name: Test and process coverage -- run: npm run test:coverage --if-present -- - - name: Collect coverage - uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 - with: diff --git a/.github/workflows/npm-audit-fix.yml b/.github/workflows/npm-audit-fix.yml deleted file mode 100644 index 3a3ecc24823..00000000000 --- a/.github/workflows/npm-audit-fix.yml +++ /dev/null @@ -1,107 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Npm audit fix and compile - -on: - workflow_dispatch: - schedule: - # At 2:30 on Sundays - - cron: '30 2 * * 0' - -permissions: - contents: write - pull-requests: write - -jobs: - build: - runs-on: ubuntu-latest - - env: - # env variable for maintainers: 'false' disables auto-merge for these pull requests - AUTOMERGE: true - - strategy: - fail-fast: false - matrix: - branches: - - ${{ github.event.repository.default_branch }} - - 'stable35' - - 'stable34' - - 'stable33' - - name: npm-audit-fix-${{ matrix.branches }} - - steps: - - name: Checkout - id: checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - ref: ${{ matrix.branches }} - continue-on-error: true - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Fix npm audit - id: npm-audit - uses: nextcloud-libraries/npm-audit-action@1b1728b2b4a7a78d69de65608efcf4db0e3e42d0 # v0.2.0 - - - name: Run npm ci and npm run build - if: steps.checkout.outcome == 'success' - env: - CYPRESS_INSTALL_BINARY: 0 - run: | - npm ci - npm run build --if-present - - - name: Create Pull Request - id: create-pull-request - if: steps.checkout.outcome == 'success' - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'fix(deps): Fix npm audit' - committer: GitHub - author: nextcloud-command - signoff: true - branch: automated/noid/${{ matrix.branches }}-fix-npm-audit - title: '[${{ matrix.branches }}] Fix npm audit' - body: ${{ steps.npm-audit.outputs.markdown }} - labels: | - dependencies - 3. to review - - # Approve using the default GITHUB_TOKEN, as the PR itself was created - # using COMMAND_BOT_PAT and GitHub does not allow an account to approve its own PR - - name: GitHub actions bot approve - if: steps.create-pull-request.outputs.pull-request-operation != 'none' - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{ steps.create-pull-request.outputs.pull-request-url }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable auto merge - if: steps.create-pull-request.outputs.pull-request-operation != 'none' && fromJSON(env.AUTOMERGE) - uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - pull-request-number: ${{ steps.create-pull-request.outputs.pull-request-number }} diff --git a/.github/workflows/npm-build.yml b/.github/workflows/npm-build.yml deleted file mode 100644 index a7f1e42674f..00000000000 --- a/.github/workflows/npm-build.yml +++ /dev/null @@ -1,113 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Build Javascript - -on: pull_request - -permissions: - contents: read - -concurrency: - group: node-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src}} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'src/**' - - 'appinfo/info.xml' - - 'package.json' - - 'package-lock.json' - - 'tsconfig.json' - - '**.js' - - '**.ts' - - '**.vue' - - build: - runs-on: ubuntu-latest - - needs: changes - if: needs.changes.outputs.src != 'false' - - name: NPM build - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Read package.json node and npm engines version - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: versions - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Validate package-lock.json # See https://github.com/npm/cli/issues/4460 - run: | - npm i -g npm-package-lock-add-resolved@1.1.4 - npm-package-lock-add-resolved - git --no-pager diff --exit-code - - - name: Install dependencies & build - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - npm run build --if-present - - - name: Check build changes - run: | - bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please recompile and commit the assets, see the section \"Show changes on failure\" for details' && exit 1)" - - - name: Show changes on failure - if: failure() - run: | - git status - git --no-pager diff - exit 1 # make it red to grab attention - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes, build] - - if: always() - - # This is the summary, we just avoid to rename it so that branch protection rules still match - name: node - - steps: - - name: Summary status - run: if ${{ needs.changes.outputs.src != 'false' && needs.build.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/occ-command-documentation.yml b/.github/workflows/occ-command-documentation.yml deleted file mode 100644 index db722ba832b..00000000000 --- a/.github/workflows/occ-command-documentation.yml +++ /dev/null @@ -1,93 +0,0 @@ -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Documentation - -on: - pull_request: - paths: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - -permissions: - contents: read - -concurrency: - group: occ-commands-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - occ-commands: - runs-on: ubuntu-latest - - strategy: - matrix: - php-versions: ['8.4'] - server-versions: ['master'] - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Set up dependencies - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: composer i - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ app:enable ${{ env.APP_NAME }} - ./occ config:system:set debug --value true --type boolean - - - name: Check if documentation is up to date - id: check_documentation - run: | - ./occ talk:developer:update-docs - cd apps/${{ env.APP_NAME }} - bash -c "[[ ! \"`git status --porcelain `\" ]] || (cat docs/occ.md && echo 'Documentation outdated. Run the command talk:developer:update-docs locally, commit the occ.md file and push the changes.' && exit 1)" diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml deleted file mode 100644 index f8a14609704..00000000000 --- a/.github/workflows/openapi.yml +++ /dev/null @@ -1,96 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-FileCopyrightText: 2024 Arthur Schiwon -# SPDX-License-Identifier: MIT - -name: OpenAPI - -on: pull_request - -permissions: - contents: read - -concurrency: - group: openapi-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - openapi: - runs-on: ubuntu-latest - - if: ${{ github.repository_owner != 'nextcloud-gmbh' }} - - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get php version - id: php_versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - - name: Set up php - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ steps.php_versions.outputs.php-available }} - extensions: xml - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check Typescript OpenApi types - id: check_typescript_openapi - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: "src/types/openapi/openapi*.ts" - - - name: Read package.json node and npm engines version - if: steps.check_typescript_openapi.outputs.files_exists == 'true' - uses: skjnldsv/read-package-engines-version-actions@06d6db44aa62f4609ba070f556ec24bc99fb85cb # v4 - id: node_versions - # Continue if no package.json - continue-on-error: true - with: - fallbackNode: '^24' - fallbackNpm: '^11.3' - - - name: Set up node ${{ steps.node_versions.outputs.nodeVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.node_versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.node_versions.outputs.npmVersion }} - if: ${{ steps.node_versions.outputs.nodeVersion }} - run: npm i -g 'npm@${{ steps.node_versions.outputs.npmVersion }}' - - - name: Install dependencies - if: ${{ steps.node_versions.outputs.nodeVersion }} - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: | - npm ci - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Regenerate OpenAPI - run: composer run openapi - - - name: Check openapi*.json and typescript changes - run: | - bash -c "[[ ! \"`git status --porcelain `\" ]] || (echo 'Please run \"composer run openapi\" and commit the openapi*.json files and (if applicable) src/types/openapi/openapi*.ts, see the section \"Show changes on failure\" for details' && exit 1)" - - - name: Show changes on failure - if: failure() - run: | - git status - git --no-pager diff - exit 1 # make it red to grab attention diff --git a/.github/workflows/phpunit-mariadb.yml b/.github/workflows/phpunit-mariadb.yml deleted file mode 100644 index 8387c872745..00000000000 --- a/.github/workflows/phpunit-mariadb.yml +++ /dev/null @@ -1,205 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit MariaDB - -on: pull_request - -permissions: - contents: read - -concurrency: - group: phpunit-mariadb-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes-and-matrix: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src }} - php-version: ${{ steps.versions.outputs.php-available-list }} - server-max: ${{ steps.versions.outputs.branches-max-list }} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - - name: Checkout app - if: steps.changes.outputs.src != 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - if: steps.changes.outputs.src != 'false' - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - phpunit-mariadb: - runs-on: ubuntu-latest - - needs: changes-and-matrix - if: needs.changes-and-matrix.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-version) }} - server-versions: ${{ fromJson(needs.changes-and-matrix.outputs.server-max) }} - mariadb-versions: ['10.6', '11.8'] - - name: MariaDB ${{ matrix.mariadb-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - - services: - mariadb: - image: ghcr.io/nextcloud/continuous-integration-mariadb-${{ matrix.mariadb-versions }}:latest # zizmor: ignore[unpinned-images] - ports: - - 4444:3306/tcp - env: - MARIADB_ROOT_PASSWORD: rootpassword - options: --health-cmd="mariadb-admin ping" --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Set app env - if: ${{ env.APP_NAME == '' }} - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable ONLY_FULL_GROUP_BY MariaDB option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo 'SELECT @@sql_mode;' | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Remove nextcloud/ocp - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - working-directory: apps/${{ env.APP_NAME }} - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ app:enable --force ${{ env.APP_NAME }} - - - name: Check PHPUnit script is defined - id: check_phpunit - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:unit ' | wc -l | grep 1 - - - name: PHPUnit - # Only run if phpunit config file exists - if: steps.check_phpunit.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:unit - - - name: Check PHPUnit integration script is defined - id: check_integration - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:integration ' | wc -l | grep 1 - - - name: Run Nextcloud - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - run: php -S localhost:8080 & - - - name: PHPUnit integration - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:integration - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Skipped - # Fail the action when neither unit nor integration tests ran - if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' - run: | - echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' - exit 1 - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes-and-matrix, phpunit-mariadb] - - if: always() - - name: phpunit-mariadb-summary - - steps: - - name: Summary status - run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && needs.phpunit-mariadb.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-mariadb.yml.patch b/.github/workflows/phpunit-mariadb.yml.patch deleted file mode 100644 index 363a30869e4..00000000000 --- a/.github/workflows/phpunit-mariadb.yml.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/.github/workflows/phpunit-mariadb.yml b/.github/workflows/phpunit-mariadb.yml -index 55139b44f3..8387c87274 100644 ---- a/.github/workflows/phpunit-mariadb.yml -+++ b/.github/workflows/phpunit-mariadb.yml -@@ -69,7 +69,7 @@ jobs: - matrix: - php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-version) }} - server-versions: ${{ fromJson(needs.changes-and-matrix.outputs.server-max) }} -- mariadb-versions: ['10.6', '11.4'] -+ mariadb-versions: ['10.6', '11.8'] - - name: MariaDB ${{ matrix.mariadb-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - diff --git a/.github/workflows/phpunit-mysql.yml b/.github/workflows/phpunit-mysql.yml deleted file mode 100644 index a7d98c9cfe2..00000000000 --- a/.github/workflows/phpunit-mysql.yml +++ /dev/null @@ -1,203 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit MySQL - -on: pull_request - -permissions: - contents: read - -concurrency: - group: phpunit-mysql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes-and-matrix: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src }} - matrix: ${{ steps.versions.outputs.sparse-matrix }} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - - name: Checkout app - if: steps.changes.outputs.src != 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - if: steps.changes.outputs.src != 'false' - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - with: - matrix: '{"mysql-versions": ["8.4"]}' - - phpunit-mysql: - runs-on: ubuntu-latest - - needs: changes-and-matrix - if: needs.changes-and-matrix.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.changes-and-matrix.outputs.matrix) }} - - name: MySQL ${{ matrix.mysql-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - - services: - mysql: - image: ghcr.io/nextcloud/continuous-integration-mysql-${{ matrix.mysql-versions }}:latest # zizmor: ignore[unpinned-images] - ports: - - 4444:3306/tcp - env: - MYSQL_ROOT_PASSWORD: rootpassword - options: --health-cmd="mysqladmin ping" --health-interval 5s --health-timeout 2s --health-retries 10 - - steps: - - name: Set app env - if: ${{ env.APP_NAME == '' }} - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, mysql, pdo_mysql - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable ONLY_FULL_GROUP_BY MySQL option - run: | - echo "SET GLOBAL sql_mode=(SELECT CONCAT(@@sql_mode,',ONLY_FULL_GROUP_BY'));" | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - echo 'SELECT @@sql_mode;' | mysql -h 127.0.0.1 -P 4444 -u root -prootpassword - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Remove nextcloud/ocp - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - working-directory: apps/${{ env.APP_NAME }} - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=mysql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ app:enable --force ${{ env.APP_NAME }} - - - name: Check PHPUnit script is defined - id: check_phpunit - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:unit ' | wc -l | grep 1 - - - name: PHPUnit - # Only run if phpunit config file exists - if: steps.check_phpunit.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:unit - - - name: Check PHPUnit integration script is defined - id: check_integration - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:integration ' | wc -l | grep 1 - - - name: Run Nextcloud - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - run: php -S localhost:8080 & - - - name: PHPUnit integration - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:integration - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Skipped - # Fail the action when neither unit nor integration tests ran - if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' - run: | - echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' - exit 1 - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes-and-matrix, phpunit-mysql] - - if: always() - - name: phpunit-mysql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && needs.phpunit-mysql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-oci.yml b/.github/workflows/phpunit-oci.yml deleted file mode 100644 index f3b17ff8fb5..00000000000 --- a/.github/workflows/phpunit-oci.yml +++ /dev/null @@ -1,210 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit OCI - -on: pull_request - -permissions: - contents: read - -concurrency: - group: phpunit-oci-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes-and-matrix: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src }} - php-version: ${{ steps.versions.outputs.php-available-list }} - server-max: ${{ steps.versions.outputs.branches-max-list }} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - - name: Checkout app - if: steps.changes.outputs.src != 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - if: steps.changes.outputs.src != 'false' - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - phpunit-oci: - runs-on: ubuntu-latest - - needs: changes-and-matrix - if: needs.changes-and-matrix.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-version) }} - server-versions: ${{ fromJson(needs.changes-and-matrix.outputs.server-max) }} - oci-versions: ['18', '21', '23'] - - name: OCI ${{ matrix.oci-versions }} PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - - services: - oracle: - image: ghcr.io/gvenzl/oracle-${{ matrix.oci-versions < 23 && 'xe' || 'free' }}:${{ matrix.oci-versions }} - - # Provide passwords and other environment variables to container - env: - ORACLE_PASSWORD: oracle - - # Forward Oracle port - ports: - - 1521:1521 - - # Provide healthcheck script options for startup - options: >- - --health-cmd healthcheck.sh - --health-interval 20s - --health-timeout 10s - --health-retries 10 - - steps: - - name: Set app env - if: ${{ env.APP_NAME == '' }} - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, oci8 - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Remove nextcloud/ocp - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - working-directory: apps/${{ env.APP_NAME }} - - - name: Set up Nextcloud - env: - DB_PORT: 1521 - run: | - mkdir data - ./occ maintenance:install --verbose --database=oci --database-name=${{ matrix.oci-versions < 23 && 'XE' || 'FREE' }} --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=system --database-pass=oracle --admin-user admin --admin-pass admin - ./occ app:enable --force ${{ env.APP_NAME }} - - - name: Check PHPUnit script is defined - id: check_phpunit - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:unit ' | wc -l | grep 1 - - - name: PHPUnit - # Only run if phpunit config file exists - if: steps.check_phpunit.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:unit - - - name: Check PHPUnit integration script is defined - id: check_integration - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:integration ' | wc -l | grep 1 - - - name: Run Nextcloud - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - run: php -S localhost:8080 & - - - name: PHPUnit integration - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:integration - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Skipped - # Fail the action when neither unit nor integration tests ran - if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' - run: | - echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' - exit 1 - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes-and-matrix, phpunit-oci] - - if: always() - - name: phpunit-oci-summary - - steps: - - name: Summary status - run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && needs.phpunit-oci.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-pgsql.yml b/.github/workflows/phpunit-pgsql.yml deleted file mode 100644 index 0d63f7dad1b..00000000000 --- a/.github/workflows/phpunit-pgsql.yml +++ /dev/null @@ -1,201 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit PostgreSQL - -on: pull_request - -permissions: - contents: read - -concurrency: - group: phpunit-pgsql-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes-and-matrix: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src }} - php-version: ${{ steps.versions.outputs.php-available-list }} - server-max: ${{ steps.versions.outputs.branches-max-list }} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - - name: Checkout app - if: steps.changes.outputs.src != 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - if: steps.changes.outputs.src != 'false' - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - phpunit-pgsql: - runs-on: ubuntu-latest - - needs: changes-and-matrix - if: needs.changes-and-matrix.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-version) }} - server-versions: ${{ fromJson(needs.changes-and-matrix.outputs.server-max) }} - - name: PostgreSQL PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - - services: - postgres: - image: ghcr.io/nextcloud/continuous-integration-postgres-16:latest # zizmor: ignore[unpinned-images] - ports: - - 4444:5432/tcp - env: - POSTGRES_USER: root - POSTGRES_PASSWORD: rootpassword - POSTGRES_DB: nextcloud - options: --health-cmd pg_isready --health-interval 5s --health-timeout 2s --health-retries 5 - - steps: - - name: Set app env - if: ${{ env.APP_NAME == '' }} - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, pgsql, pdo_pgsql - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Remove nextcloud/ocp - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - working-directory: apps/${{ env.APP_NAME }} - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=pgsql --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ app:enable --force ${{ env.APP_NAME }} - - - name: Check PHPUnit script is defined - id: check_phpunit - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:unit ' | wc -l | grep 1 - - - name: PHPUnit - # Only run if phpunit config file exists - if: steps.check_phpunit.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:unit - - - name: Check PHPUnit integration script is defined - id: check_integration - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:integration ' | wc -l | grep 1 - - - name: Run Nextcloud - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - run: php -S localhost:8080 & - - - name: PHPUnit integration - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:integration - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Skipped - # Fail the action when neither unit nor integration tests ran - if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' - run: | - echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' - exit 1 - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes-and-matrix, phpunit-pgsql] - - if: always() - - name: phpunit-pgsql-summary - - steps: - - name: Summary status - run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && needs.phpunit-pgsql.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/phpunit-sqlite.yml b/.github/workflows/phpunit-sqlite.yml deleted file mode 100644 index d6bcf8e7bb1..00000000000 --- a/.github/workflows/phpunit-sqlite.yml +++ /dev/null @@ -1,190 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: PHPUnit SQLite - -on: pull_request - -permissions: - contents: read - -concurrency: - group: phpunit-sqlite-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - changes-and-matrix: - runs-on: ubuntu-latest-low - permissions: - contents: read - pull-requests: read - - outputs: - src: ${{ steps.changes.outputs.src }} - php-version: ${{ steps.versions.outputs.php-available-list }} - server-max: ${{ steps.versions.outputs.branches-max-list }} - - steps: - - uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 - id: changes - continue-on-error: true - with: - filters: | - src: - - '.github/workflows/**' - - 'appinfo/**' - - 'lib/**' - - 'templates/**' - - 'tests/**' - - 'vendor/**' - - 'vendor-bin/**' - - '.php-cs-fixer.dist.php' - - 'composer.json' - - 'composer.lock' - - - name: Checkout app - if: steps.changes.outputs.src != 'false' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get version matrix - if: steps.changes.outputs.src != 'false' - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - phpunit-sqlite: - runs-on: ubuntu-latest - - needs: changes-and-matrix - if: needs.changes-and-matrix.outputs.src != 'false' - - strategy: - fail-fast: false - matrix: - php-versions: ${{ fromJson(needs.changes-and-matrix.outputs.php-version) }} - server-versions: ${{ fromJson(needs.changes-and-matrix.outputs.server-max) }} - - name: SQLite PHP ${{ matrix.php-versions }} Nextcloud ${{ matrix.server-versions }} - - steps: - - name: Set app env - if: ${{ env.APP_NAME == '' }} - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.server-versions }} - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - - - name: Set up php ${{ matrix.php-versions }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ matrix.php-versions }} - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Check composer file existence - id: check_composer - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: apps/${{ env.APP_NAME }}/composer.json - - - name: Remove nextcloud/ocp - # Only run if phpunit config file exists - if: steps.check_composer.outputs.files_exists == 'true' - working-directory: apps/${{ env.APP_NAME }} - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - with: - working-directory: apps/${{ env.APP_NAME }} - - - name: Set up Nextcloud - env: - DB_PORT: 4444 - run: | - mkdir data - ./occ maintenance:install --verbose --database=sqlite --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin - ./occ app:enable --force ${{ env.APP_NAME }} - - - name: Check PHPUnit script is defined - id: check_phpunit - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:unit ' | wc -l | grep 1 - - - name: PHPUnit - # Only run if phpunit config file exists - if: steps.check_phpunit.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:unit - - - name: Check PHPUnit integration script is defined - id: check_integration - continue-on-error: true - working-directory: apps/${{ env.APP_NAME }} - run: | - composer run --list | grep '^ test:integration ' | wc -l | grep 1 - - - name: Run Nextcloud - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - run: php -S localhost:8080 & - - - name: PHPUnit integration - # Only run if phpunit integration config file exists - if: steps.check_integration.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: composer run test:integration - - - name: Print logs - if: always() - run: | - cat data/nextcloud.log - - - name: Skipped - # Fail the action when neither unit nor integration tests ran - if: steps.check_phpunit.outcome == 'failure' && steps.check_integration.outcome == 'failure' - run: | - echo 'Neither PHPUnit nor PHPUnit integration tests are specified in composer.json scripts' - exit 1 - - summary: - permissions: - contents: none - runs-on: ubuntu-latest-low - needs: [changes-and-matrix, phpunit-sqlite] - - if: always() - - name: phpunit-sqlite-summary - - steps: - - name: Summary status - run: if ${{ needs.changes-and-matrix.outputs.src != 'false' && needs.phpunit-sqlite.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 00000000000..c7fad607805 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,208 @@ +# This workflow is provided via the organization template repository +# +# https://github.com/nextcloud/.github +# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization +# +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: MIT + +# This workflow runs Playwright tests on pull requests to the master and stable branches. +# It expects the following npm scripts to be defined in package.json: +# - "playwright": runs the Playwright tests +# - "playwright:install-ci": installs the Playwright browsers for CI (optional) +# +# If you only use the "Desktop Chrome" browser with "channel: chrome" in your Playwright config, +# you can skip the "playwright:install-ci" script as the GitHub Actions runner already has Chrome Browser installed. +# +# For merging the reports, it expects a Playwright merge config file at tests/playwright/merge.config.ts. +# You can adjust the path in the "Merge into HTML Report" step if you have a different location for the merge config file. +# The default configuration would look like this: +# ```js +# export default { +# testDir: 'tests/playwright/e2e', // path to your Playwright tests +# reporter: [['html', { open: 'never' }]], +# } +# ``` + +name: Playwright Tests + +on: + pull_request: + branches: + - main + - master + - stable35 + - stable34 + - stable33 + +permissions: + contents: read +concurrency: + group: playwright-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + playwright-setup: + timeout-minutes: 10 + name: Playwright setup + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - name: Read package.json + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + - name: Check composer.json + id: check_composer + uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 + with: + files: 'composer.json' + - name: Install composer dependencies + if: steps.check_composer.outputs.files_exists == 'true' + uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 + with: + composer-options: '--no-dev' + - name: Set up node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + - name: Set up npm + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + - name: Install dependencies and build + run: | + npm ci + npm run build --if-present + - name: Save context + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + playwright-tests: + needs: [playwright-setup] + timeout-minutes: 30 + name: Playwright tests ${{ matrix.shardIndex }} / ${{ matrix.shardTotal }} + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shardIndex: [1, 2] # adjust to your needs + shardTotal: [2] + outputs: + node-version: ${{ steps.versions.outputs.node-version }} + package-manager-version: ${{ steps.versions.outputs.package-manager-version }} + + steps: + - name: Restore context + id: cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + + - name: Read package.json + uses: nextcloud-libraries/parse-package-engines-action@122ae05d4257008180a514e1ddeb0c1b9d094bdd # v0.1.0 + id: versions + + - name: Set up node + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + with: + node-version: ${{ steps.versions.outputs.node-version }} + + - name: Set up npm + run: npm i -g 'npm@${{ steps.versions.outputs.package-manager-version }}' + + - name: Install dependencies and build + if: steps.cache.outputs.cache-hit != 'true' + run: | + npm ci + npm run build --if-present + + - name: Install Playwright browsers + run: npm run playwright:install-ci --if-present + + - name: Run Playwright tests + run: npm run playwright -- --shard='${{ matrix.shardIndex }}/${{ matrix.shardTotal }}' + + - name: Upload blob report to GitHub Actions Artifacts + if: ${{ !cancelled() }} + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: blob-report-${{ matrix.shardIndex }} + path: blob-report + retention-days: 1 + + merge-reports: + # Merge reports after playwright-tests, even if some shards have failed + if: ${{ !cancelled() }} + needs: [playwright-tests] + + runs-on: ubuntu-latest-low + steps: + - name: Restore context + id: cache + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + key: playwright-context-${{ github.run_id }} + path: ./ + + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + if: steps.cache.outputs.cache-hit != 'true' + with: + persist-credentials: false + + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 + if: steps.cache.outputs.cache-hit != 'true' + with: + node-version: ${{ needs.playwright-tests.outputs.node-version }} + + - name: Set up npm + if: steps.cache.outputs.cache-hit != 'true' + run: npm i -g 'npm@${{ needs.playwright-tests.outputs.package-manager-version }}' + + - name: Install dependencies + if: steps.cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Download blob reports from GitHub Actions Artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + path: all-blob-reports + pattern: blob-report-* + merge-multiple: true + + - name: Merge into HTML Report + run: npx playwright merge-reports --config tests/playwright/merge.config.ts --reporter html,github ./all-blob-reports + + - name: Upload HTML report + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: html-report--attempt-${{ github.run_attempt }} + path: playwright-report + retention-days: 7 + + - name: How to show the logs + run: | + echo 'To view the report:' + echo ' 1. Extract the folder from the zip file' + echo ' 2. run "npx playwright show-report name-of-my-extracted-playwright-report"' + + summary: + permissions: + contents: none + runs-on: ubuntu-latest-low + needs: [playwright-tests] + + if: always() + + name: playwright-test-summary + + steps: + - name: Summary status + run: if ${{ needs.playwright-tests.result != 'success' }}; then exit 1; fi diff --git a/.github/workflows/pr-feedback.yml b/.github/workflows/pr-feedback.yml deleted file mode 100644 index e93d2be9b01..00000000000 --- a/.github/workflows/pr-feedback.yml +++ /dev/null @@ -1,55 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization - -# SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-FileCopyrightText: 2023 Marcel Klehr -# SPDX-FileCopyrightText: 2023 Joas Schilling <213943+nickvergessen@users.noreply.github.com> -# SPDX-FileCopyrightText: 2023 Daniel Kesselberg -# SPDX-FileCopyrightText: 2023 Florian Steffens -# SPDX-License-Identifier: MIT - -name: 'Ask for feedback on PRs' -on: - schedule: - - cron: '30 1 * * *' - -permissions: - contents: read - pull-requests: write - -jobs: - pr-feedback: - if: ${{ github.repository_owner == 'nextcloud' }} - runs-on: ubuntu-latest - steps: - - name: The get-github-handles-from-website action - uses: marcelklehr/get-github-handles-from-website-action@06b2239db0a48fe1484ba0bfd966a3ab81a08308 # v1.0.1 - id: scrape - with: - website: 'https://nextcloud.com/team/' - - - name: Get blocklist - id: blocklist - run: | - blocklist=$(curl https://raw.githubusercontent.com/nextcloud/.github/master/non-community-usernames.txt | paste -s -d, -) - echo "blocklist=$blocklist" >> "$GITHUB_OUTPUT" - - - uses: nextcloud-libraries/pr-feedback-action@4738cf661995d96df3512409ec17c900a4081d33 # v2.0.0 - with: - feedback-message: | - Hello there, - Thank you so much for taking the time and effort to create a pull request to our Nextcloud project. - - We hope that the review process is going smooth and is helpful for you. We want to ensure your pull request is reviewed to your satisfaction. If you have a moment, our community management team would very much appreciate your feedback on your experience with this PR review process. - - Your feedback is valuable to us as we continuously strive to improve our community developer experience. Please take a moment to complete our short survey by clicking on the following link: https://cloud.nextcloud.com/apps/forms/s/i9Ago4EQRZ7TWxjfmeEpPkf6 - - Thank you for contributing to Nextcloud and we hope to hear from you soon! - - (If you believe you should not receive this message, you can add yourself to the [blocklist](https://github.com/nextcloud/.github/blob/master/non-community-usernames.txt).) - days-before-feedback: 14 - max-days-overdue: 7 - exempt-authors: '${{ steps.blocklist.outputs.blocklist }},${{ steps.scrape.outputs.users }}' - exempt-bots: true diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml deleted file mode 100644 index d6e2d52f357..00000000000 --- a/.github/workflows/psalm.yml +++ /dev/null @@ -1,61 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Static analysis - -on: pull_request - -concurrency: - group: psalm-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - static-analysis: - runs-on: ubuntu-latest - - name: static-psalm-analysis - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: Get php version - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - - name: Check enforcement of minimum PHP version ${{ steps.versions.outputs.php-min }} in psalm.xml - run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml - - - name: Set up php${{ steps.versions.outputs.php-available }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ steps.versions.outputs.php-available }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - # Temporary workaround for missing pcntl_* in PHP 8.3 - ini-values: disable_functions= - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Remove nextcloud/ocp - run: | - composer remove nextcloud/ocp --dev --no-scripts - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Install nextcloud/ocp:dev-${{ steps.versions.outputs.branches-max }} - run: composer require --dev nextcloud/ocp:dev-${{ steps.versions.outputs.branches-max }} --ignore-platform-reqs --with-dependencies - - - name: Run coding standards check - run: composer run psalm -- --threads=1 --monochrome --no-progress --output-format=github diff --git a/.github/workflows/rector-apply.yml b/.github/workflows/rector-apply.yml deleted file mode 100644 index 71b25d2f00f..00000000000 --- a/.github/workflows/rector-apply.yml +++ /dev/null @@ -1,65 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Apply rector changes - -on: - workflow_dispatch: - schedule: - # At 14:30 on Sundays - - cron: '30 14 * * 0' - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - - name: rector-apply - - steps: - - name: Checkout - id: checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - ref: ${{ github.event.repository.default_branch }} - - - name: Get php version - id: versions - uses: nextcloud-libraries/nextcloud-version-matrix@cd0211ffcef1065e2020cd579e4843b8746e7a58 # v1.3.3 - - - name: Set up php${{ steps.versions.outputs.php-min }} - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: ${{ steps.versions.outputs.php-min }} - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - ini-file: development - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Install composer dependencies - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Rector - run: composer run rector - - - name: Create Pull Request - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'refactor: Apply rector changes' - committer: GitHub - author: nextcloud-command - signoff: true - branch: automated/noid/rector-changes - title: 'Apply rector changes' - labels: | - technical debt diff --git a/.github/workflows/rector-apply.yml.patch b/.github/workflows/rector-apply.yml.patch deleted file mode 100644 index 282a5f340a6..00000000000 --- a/.github/workflows/rector-apply.yml.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/.github/workflows/rector-apply.yml b/.github/workflows/rector-apply.yml -index c61c5b1062..c98cbe3721 100644 ---- a/.github/workflows/rector-apply.yml -+++ b/.github/workflows/rector-apply.yml -@@ -67,4 +67,3 @@ jobs: - title: 'Apply rector changes' - labels: | - technical debt -- 3. to review diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml deleted file mode 100644 index 67fdafc440e..00000000000 --- a/.github/workflows/reuse.yml +++ /dev/null @@ -1,27 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization - -# SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. -# -# SPDX-License-Identifier: CC0-1.0 - -name: REUSE Compliance Check - -on: [pull_request] - -permissions: - contents: read - -jobs: - reuse-compliance-check: - runs-on: ubuntu-latest-low - steps: - - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - name: REUSE Compliance Check - uses: fsfe/reuse-action@676e2d560c9a403aa252096d99fcab3e1132b0f5 # v6.0.0 diff --git a/.github/workflows/sync-workflow-templates.yml b/.github/workflows/sync-workflow-templates.yml deleted file mode 100644 index 2642cebd775..00000000000 --- a/.github/workflows/sync-workflow-templates.yml +++ /dev/null @@ -1,140 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -# This workflow will update all workflow templates -# Additionally it will reapply `workflow.yml.patch` files after syncing and only then commit the result -name: Update workflows -on: - workflow_dispatch: - schedule: - - cron: "5 2 * * 0" - -permissions: - contents: read - -jobs: - dispatch: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: - - ${{ github.event.repository.default_branch }} - - 'stable35' - - 'stable34' - - 'stable33' - - name: Update workflows in ${{ matrix.branches }} - - permissions: - contents: write - pull-requests: write - - steps: - - name: Check actor permission - uses: skjnldsv/check-actor-permission@69e92a3c4711150929bca9fcf34448c5bf5526e7 # v3.0 - with: - require: admin - - - name: Checkout workflow repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: source - repository: nextcloud/.github - - - name: Checkout app - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: target - ref: ${{ matrix.branches }} - - - name: Copy all workflow templates - run: | - echo 'SUMMARY<> $GITHUB_ENV - draft_only=0 - for workflow in ./source/workflow-templates/*.yml; do - echo "❓ Looking for $workflow" - if [ -f "$workflow" ]; then - filename=$(basename "$workflow") - target_file="./target/.github/workflows/$filename" - - # Only copy if the file exists in the target repository - if [ -f "$target_file" ]; then - if [ -f "./target/.github/actions-lock.txt" ]; then - locked_version=$(grep " $filename" ./target/.github/actions-lock.txt | cat) - else - echo "# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors" >> ./target/.github/actions-lock.txt - echo "# SPDX-License""-Identifier: MIT" >> ./target/.github/actions-lock.txt - locked_version="" - fi - locked_version=$(echo $locked_version | cut -f 1 -d " ") - new_version=$(md5sum $workflow | cut -f 1 -d " ") - - # Only update if the action changes - if [[ "$locked_version" != "$new_version" ]]; then - echo "ℹ️ Locked version: $locked_version" - echo "ℹ️ Current version: $new_version" - echo "🆙 Updating existing workflow: $filename" - echo "- 🆙 Updated [$filename](https://github.com/nextcloud/.github/commits/master/workflow-templates/$filename)" >> $GITHUB_ENV - - cp "$workflow" "$target_file" - - # Apply patch if one exists - if [ -f "$target_file.patch" ]; then - echo "🩹 Applying patch" - cd ./target - set +e - patch -p1 < ".github/workflows/$filename.patch" - patch_worked=$? - set -e - cd - - if [[ "$patch_worked" == "0" ]]; then - echo " - Patch applied" >> $GITHUB_ENV - else - echo " - [ ] ❌ Patch failed" >> $GITHUB_ENV - draft_only=1 - fi - fi - - if [[ "$locked_version" != "" ]]; then - sed -i "s/$locked_version $filename/$new_version $filename/" ./target/.github/actions-lock.txt - else - echo "$new_version $filename" >> ./target/.github/actions-lock.txt - fi - else - echo "✅ Skipping $filename: already up to date" - fi - else - echo "⏭️ Skipping $filename: does not exist in target repository" - fi - fi - done - echo 'EOF' >> $GITHUB_ENV - echo "DRAFT_ONLY=${draft_only}" >> $GITHUB_ENV - - - name: Create Pull Request - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.COMMAND_BOT_WORKFLOWS }} - commit-message: 'ci(actions): Update workflow templates from organization template repository' - committer: GitHub - author: nextcloud-command - path: target - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-workflows' - title: '[${{ matrix.branches }}] ci(actions): Update workflow templates from organization template repository' - draft: ${{ env.DRAFT_ONLY == 1 }} - add-paths: .github/workflows/*.yml,.github/actions-lock.txt - body: | - Automated update of all workflow templates from [nextcloud/.github](https://github.com/nextcloud/.github) - ${{ env.SUMMARY }} - labels: | - dependencies diff --git a/.github/workflows/sync-workflow-templates.yml.patch b/.github/workflows/sync-workflow-templates.yml.patch deleted file mode 100644 index ce1e7b513ba..00000000000 --- a/.github/workflows/sync-workflow-templates.yml.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/.github/workflows/sync-workflow-templates.yml b/.github/workflows/sync-workflow-templates.yml -index 2dbd84b71f..304796d10c 100644 ---- a/.github/workflows/sync-workflow-templates.yml -+++ b/.github/workflows/sync-workflow-templates.yml -@@ -132,4 +132,3 @@ jobs: - ${{ env.SUMMARY }} - labels: | - dependencies -- 3. to review diff --git a/.github/workflows/update-nextcloud-ocp.yml b/.github/workflows/update-nextcloud-ocp.yml deleted file mode 100644 index 98fe45aa10e..00000000000 --- a/.github/workflows/update-nextcloud-ocp.yml +++ /dev/null @@ -1,139 +0,0 @@ -# This workflow is provided via the organization template repository -# -# https://github.com/nextcloud/.github -# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization -# -# SPDX-FileCopyrightText: 2022-2024 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Update nextcloud/ocp - -on: - workflow_dispatch: - schedule: - - cron: "5 2 * * 0" - -permissions: - contents: write - issues: write - pull-requests: write - -jobs: - update-nextcloud-ocp: - runs-on: ubuntu-latest - - # Only allowed to be run on nextcloud repositories - if: ${{ github.repository_owner == 'nextcloud' }} - - env: - # env variable for maintainers: 'false' disables auto-merge for these pull requests - AUTOMERGE: true - - strategy: - fail-fast: false - matrix: - branches: - - ${{ github.event.repository.default_branch }} - - 'stable35' - - 'stable34' - - 'stable33' - - name: update-nextcloud-ocp-${{ matrix.branches }} - - steps: - - id: checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - ref: ${{ matrix.branches }} - submodules: true - continue-on-error: true - - - name: Set up php8.3 - if: steps.checkout.outcome == 'success' - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 - with: - php-version: 8.3 - # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation - extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite - coverage: none - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Read codeowners - if: steps.checkout.outcome == 'success' - id: codeowners - run: | - grep '/appinfo/info.xml' .github/CODEOWNERS | cut -f 2- -d ' ' | xargs | awk '{ print "codeowners="$0 }' >> $GITHUB_OUTPUT - continue-on-error: true - - - name: Install composer dependencies - if: steps.checkout.outcome == 'success' - uses: ramsey/composer-install@65e4f84970763564f46a70b8a54b90d033b3bdda # 4.0.0 - - - name: Check composer bin for nextcloud/ocp exists - id: check_composer_bin - uses: andstor/file-existence-action@558493d6c74bf472d87c84eab196434afc2fa029 # v3.1.0 - with: - files: vendor-bin/nextcloud-ocp/composer.json - - - name: Composer update nextcloud/ocp - id: update_branch - env: - USE_COMPOSER_BIN: ${{ steps.check_composer_bin.outputs.files_exists }} - BRANCH_NAME: ${{ matrix.branches }} - run: | - COMPOSER_CMD='composer' - if [[ "$USE_COMPOSER_BIN" == 'true' ]]; then - COMPOSER_CMD='composer bin nextcloud-ocp' - fi - - PACKAGE_VERSION="nextcloud/ocp:dev-$BRANCH_NAME" - if [[ "$BRANCH_NAME" == 'main' ]]; then - PACKAGE_VERSION='nextcloud/ocp:dev-master' - fi - - echo $COMPOSER_CMD require --dev $PACKAGE_VERSION - $COMPOSER_CMD require --dev $PACKAGE_VERSION - - - name: Raise on issue on failure - uses: dacbd/create-issue-action@cdb57ab6ff8862aa09fee2be6ba77a59581921c2 # v2.0.0 - if: ${{ steps.checkout.outcome == 'success' && failure() && steps.update_branch.conclusion == 'failure' }} - with: - token: ${{ secrets.GITHUB_TOKEN }} - title: 'Failed to update nextcloud/ocp package on branch ${{ matrix.branches }}' - body: 'Please check the output of the GitHub action and manually resolve the issues
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
${{ steps.codeowners.outputs.codeowners }}' - - - name: Create Pull Request - id: create-pull-request - if: steps.checkout.outcome == 'success' - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ secrets.COMMAND_BOT_PAT }} - commit-message: 'chore(dev-deps): Bump nextcloud/ocp package' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-nextcloud-ocp' - title: '[${{ matrix.branches }}] Update nextcloud/ocp dependency' - add-paths: ${{ steps.check_composer_bin.outputs.files_exists == 'true' && 'vendor-bin/nextcloud-ocp/composer.json,vendor-bin/nextcloud-ocp/composer.lock' || 'composer.json,composer.lock' }} - body: | - Auto-generated update of [nextcloud/ocp](https://github.com/nextcloud-deps/ocp/) dependency - labels: | - dependencies - - # Approve using the default GITHUB_TOKEN, as the PR itself was created - # using COMMAND_BOT_PAT and GitHub does not allow an account to approve its own PR - - name: GitHub actions bot approve - if: steps.create-pull-request.outputs.pull-request-operation != 'none' - run: gh pr review --approve "$PR_URL" - env: - PR_URL: ${{ steps.create-pull-request.outputs.pull-request-url }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - - name: Enable auto merge - if: steps.create-pull-request.outputs.pull-request-operation != 'none' && fromJSON(env.AUTOMERGE) - uses: peter-evans/enable-pull-request-automerge@a660677d5469627102a1c1e11409dd063606628d # v3.0.0 - with: - token: ${{ secrets.GITHUB_TOKEN }} - pull-request-number: ${{ steps.create-pull-request.outputs.pull-request-number }} diff --git a/.github/workflows/update-nextcloud-ocp.yml.patch b/.github/workflows/update-nextcloud-ocp.yml.patch deleted file mode 100644 index bf1c807f469..00000000000 --- a/.github/workflows/update-nextcloud-ocp.yml.patch +++ /dev/null @@ -1,12 +0,0 @@ -diff --git a/.github/workflows/update-nextcloud-ocp.yml b/.github/workflows/update-nextcloud-ocp.yml -index bc3b4241e8..ba34942f59 100644 ---- a/.github/workflows/update-nextcloud-ocp.yml -+++ b/.github/workflows/update-nextcloud-ocp.yml -@@ -125,7 +125,6 @@ jobs: - Auto-generated update of [nextcloud/ocp](https://github.com/nextcloud-deps/ocp/) dependency - labels: | - dependencies -- 3. to review - - # Approve using the default GITHUB_TOKEN, as the PR itself was created - # using COMMAND_BOT_PAT and GitHub does not allow an account to approve its own PR diff --git a/.github/workflows/update-nextcloud-openapi.yml b/.github/workflows/update-nextcloud-openapi.yml deleted file mode 100644 index 70cf848a726..00000000000 --- a/.github/workflows/update-nextcloud-openapi.yml +++ /dev/null @@ -1,98 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT - -name: Update nextcloud/openapi - -on: - workflow_dispatch: - schedule: - - cron: "5 4 * * 0" - -permissions: - contents: read - -jobs: - update-nextcloud-openapi: - runs-on: ubuntu-latest - - strategy: - fail-fast: false - matrix: - branches: - - ${{ github.event.repository.default_branch }} - - 'stable35' - - 'stable34' - - 'stable33' - - name: Update Nextcloud OpenAPI types from core - - steps: - - name: Set app env - run: | - # Split and keep last - echo "APP_NAME=${GITHUB_REPOSITORY##*/}" >> $GITHUB_ENV - - - name: Checkout server - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - submodules: true - repository: nextcloud/server - ref: ${{ matrix.branches == 'main' && 'master' || matrix.branches }} - - - name: Checkout app - id: checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - path: apps/${{ env.APP_NAME }} - ref: ${{ matrix.branches }} - continue-on-error: true - - - name: Read package.json node and npm engines version - if: steps.checkout.outcome == 'success' - uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3 - id: versions - with: - fallbackNode: '^20' - fallbackNpm: '^10' - - - name: Set up node ${{ steps.versions.outputs.nodeVersion }} - if: steps.checkout.outcome == 'success' - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - with: - node-version: ${{ steps.versions.outputs.nodeVersion }} - - - name: Set up npm ${{ steps.versions.outputs.npmVersion }} - if: steps.checkout.outcome == 'success' - run: npm i -g 'npm@${{ steps.versions.outputs.npmVersion }}' - - - name: Install dependencies - if: steps.checkout.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - env: - CYPRESS_INSTALL_BINARY: 0 - PUPPETEER_SKIP_DOWNLOAD: true - run: npm ci - - - name: Generate types - if: steps.checkout.outcome == 'success' - working-directory: apps/${{ env.APP_NAME }} - run: npm run ts:generate-core-types - - - name: Create Pull Request - if: steps.checkout.outcome == 'success' - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - path: apps/${{ env.APP_NAME }} - token: ${{ secrets.COMMAND_BOT_PAT }} # zizmor: ignore[secrets-outside-env] - commit-message: 'chore(ts): update OpenAPI types from core' - committer: GitHub - author: nextcloud-command - signoff: true - branch: 'automated/noid/${{ matrix.branches }}-update-nextcloud-openapi' - title: '[${{ matrix.branches }}] Update Nextcloud OpenAPI types' - body: | - Auto-generated update of Nextcloud OpenAPI types - labels: | - dependencies diff --git a/.github/workflows/update-stable-titles.yml b/.github/workflows/update-stable-titles.yml deleted file mode 100644 index ca8be8bff6c..00000000000 --- a/.github/workflows/update-stable-titles.yml +++ /dev/null @@ -1,71 +0,0 @@ -# SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors -# SPDX-License-Identifier: MIT -name: Update PRs titles on stable branches - -on: - pull_request: - types: [opened, edited] - branches: - - "stable*" - -concurrency: - group: stable-pr-title-${{ github.event.pull_request.number }} - cancel-in-progress: true - -jobs: - update-pr-title: - runs-on: ubuntu-latest-low - permissions: - pull-requests: write - contents: read - - steps: - - name: Wait for potential title edits - run: sleep 15 - - - name: Get PR details and update title - # Renovate already have target branch in the title - if: github.event.pull_request.user.login != 'renovate[bot]' - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - script: | - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - - const baseBranch = pr.base.ref; - const currentTitle = pr.title; - - // Check if this is a stable branch - // Should not happen as we only trigger on stable* branches 🤷‍♀️ - if (!baseBranch.startsWith('stable')) { - console.log(`Not a stable branch: ${baseBranch}`); - return; - } - - const prefix = `[${baseBranch}]`; - - // Check if title already has the correct prefix and no other stable tags - const correctTagRegex = new RegExp(`^\\[${baseBranch}\\]\\s*`); - const hasOtherStableTags = /\[stable[\d.]*\]/.test(currentTitle.replace(correctTagRegex, '')); - - if (correctTagRegex.test(currentTitle) && !hasOtherStableTags) { - console.log(`Title already has correct prefix only: ${currentTitle}`); - return; - } - - // Remove all stable tags and add the correct one - const cleanTitle = currentTitle.replace(/\[stable[\d.]*\]\s*/g, '').trim(); - const newTitle = `${prefix} ${cleanTitle}`; - - console.log(`Updating title from: "${currentTitle}" to: "${newTitle}"`); - - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - title: newTitle, - }); diff --git a/.gitignore b/.gitignore index 60011888296..34cb731a340 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,8 @@ # JetBrains Fleet settings /.fleet + +# Playwright +/playwright-report +/test-results +/blob-report diff --git a/package-lock.json b/package-lock.json index bcd329417ef..6923c15edc4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,8 +65,10 @@ }, "devDependencies": { "@nextcloud/browserslist-config": "^3.1.2", + "@nextcloud/e2e-test-server": "^0.6.0", "@nextcloud/eslint-config": "^9.0.1", "@nextcloud/stylelint-config": "^3.2.2", + "@playwright/test": "^1.63.0", "@rsdoctor/rspack-plugin": "^1.6.4", "@rspack/cli": "^2.2.7", "@rspack/core": "^2.2.7", @@ -197,6 +199,13 @@ "node": ">=6.9.0" } }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", @@ -447,7 +456,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -460,7 +468,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true, "peer": true @@ -469,7 +476,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -481,7 +487,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true, "peer": true @@ -490,7 +495,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -502,7 +506,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true, "peer": true @@ -796,6 +799,112 @@ "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", "license": "MIT" }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.5", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", + "integrity": "sha512-7VZM+SVdEcUUqSQeNI3zM8Qs/BhQKZndPo2h5VkYkAM8Iz0wJIa8mKV5ekQGqG8UUsnkQ0NMxIxwkIHYvj0qOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.1.tgz", + "integrity": "sha512-wtF6h+DY6M3YaDBPAmvuuA6jV8Sif9MjtOI5euKFWRgCDl5PeDpPsHR9u2l6St5ceY8AZgoNDww5+HvEsXFsGg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@hapi/address": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-5.1.1.tgz", + "integrity": "sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/formula": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-3.0.2.tgz", + "integrity": "sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/hoek": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.7.tgz", + "integrity": "sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/tlds": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@hapi/tlds/-/tlds-1.1.7.tgz", + "integrity": "sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^11.0.2" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -932,6 +1041,17 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, "node_modules/@keyv/serialize": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", @@ -960,7 +1080,6 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1143,6 +1262,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@nextcloud/e2e-test-server": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@nextcloud/e2e-test-server/-/e2e-test-server-0.6.1.tgz", + "integrity": "sha512-qUnOJBubZMF3eJ1PE8nqGKZ/qt1WF/erAGolHPBxZpP0X8pCFIjBaSxI63ct7N9GGruoBiqbONgHJZI9yRwuhw==", + "dev": true, + "license": "AGPL-3.0-or-later", + "dependencies": { + "@nextcloud/paths": "^3.1.0", + "dockerode": "^5.0.0", + "fast-xml-parser": "^5.2.2", + "tar-stream": "^3.2.1", + "wait-on": "^9.0.1" + }, + "engines": { + "node": "^22.0.0 || ^24.0.0 || >=26.0.0" + } + }, "node_modules/@nextcloud/eslint-config": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/@nextcloud/eslint-config/-/eslint-config-9.0.1.tgz", @@ -1739,7 +1875,6 @@ "version": "2.6.0", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1778,7 +1913,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1799,7 +1933,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1820,7 +1953,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1841,7 +1973,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1862,7 +1993,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1883,7 +2013,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1904,7 +2033,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1925,7 +2053,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1946,7 +2073,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1967,7 +2093,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1988,7 +2113,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2009,7 +2133,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2027,7 +2150,6 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -2037,6 +2159,88 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", + "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", + "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", + "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", + "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@redocly/ajv": { "version": "8.11.2", "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", @@ -2117,7 +2321,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2135,7 +2338,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2153,7 +2355,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2171,7 +2372,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2189,7 +2389,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2207,7 +2406,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2225,7 +2423,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2243,7 +2440,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2261,7 +2457,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2279,7 +2474,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2297,7 +2491,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2315,7 +2508,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2333,7 +2525,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -2353,7 +2544,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2371,7 +2561,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2712,7 +2901,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2726,7 +2914,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2740,7 +2927,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2754,7 +2940,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2768,7 +2953,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2782,7 +2966,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2796,7 +2979,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2810,7 +2992,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2824,7 +3005,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2838,7 +3018,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2852,7 +3031,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2865,7 +3043,6 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2877,7 +3054,6 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2888,7 +3064,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -2899,7 +3074,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -2910,7 +3084,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2924,7 +3097,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -2938,7 +3110,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3299,6 +3470,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, "node_modules/@stylistic/eslint-plugin": { "version": "5.10.0", "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-5.10.0.tgz", @@ -3359,7 +3537,6 @@ "version": "0.10.3", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -3370,7 +3547,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -4772,7 +4948,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "peer": true, "engines": { "node": ">=8" } @@ -4821,6 +4996,16 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, "node_modules/asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", @@ -5022,6 +5207,21 @@ "node": ">= 6" } }, + "node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/bail": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", @@ -5036,6 +5236,91 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.8.2.tgz", + "integrity": "sha512-+ZI68KHMUvosXfKbg/UOHK0tbCdRnegbvPEdEcZ3Nd6TetieQsJPRXBRXPdLyy8+3VSEbPXtsumTpEtt78xv9w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.28.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-path": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.1.2.tgz", + "integrity": "sha512-ZyKbsuuqK6Ag0K8pX6V5Txq6XeJRvY+wXucnFGRjiyVYP9YWDpIQugk/b+enRYrEYBJaqLzghRQpXPMR7341Nw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bare-stream": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.4.tgz", + "integrity": "sha512-PcrQ8lVLbiJscNm1Kez+Yp4Gy4AHGcN1lzwjvf5NybWen7VvEgUfyfnXYJ2zNqWnzOfCb1Abq6lH8ti0syQszA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.5.4.tgz", + "integrity": "sha512-Gxa7UVWBr0/edU1b+TJhn/AZvMQUj9OGspvYsaTYQrAbZA4BOTZGL3LiZxvD+CeMlDH4juwD84+eTAp/bLYW5g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base-64": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", @@ -5083,6 +5368,16 @@ "node": ">=6.0.0" } }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, "node_modules/bezier-easing": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", @@ -5458,6 +5753,16 @@ "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/builtin-status-codes": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", @@ -5685,6 +5990,13 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, "node_modules/chrome-trace-event": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", @@ -5731,6 +6043,21 @@ ], "license": "MIT" }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", @@ -5948,6 +6275,21 @@ } } }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/create-ecdh": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", @@ -6418,12 +6760,46 @@ "randombytes": "^2.0.0" } }, - "node_modules/diffie-hellman/node_modules/bn.js": { - "version": "4.12.3", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", - "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", - "license": "MIT" - }, + "node_modules/diffie-hellman/node_modules/bn.js": { + "version": "4.12.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.3.tgz", + "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", + "license": "MIT" + }, + "node_modules/docker-modem": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz", + "integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-5.0.1.tgz", + "integrity": "sha512-avsq/xk4YPIrn0CgleX5bjT9Y8IT1p9PxrNQ++RBQ2WEyFfHCTDsT9kmyxz+H/axnjAwg8wJWEIuPGOUuNupiA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.7", + "protobufjs": "^7.3.2", + "tar-fs": "^2.1.4" + }, + "engines": { + "node": ">= 14.17" + } + }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -6634,6 +7010,16 @@ "integrity": "sha512-2qQtc7syPcJmvF7BfKxBvwgvhzDth6u3q23+Jr4JOQUcmMGgmj7B4/ePZ2DmOhrrb77S8kMNHq2kilVASysfqA==", "license": "MIT" }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { "version": "6.6.10", "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.10.tgz", @@ -7312,6 +7698,16 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/evp_bytestokey": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", @@ -7419,6 +7815,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "devOptional": true }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -7753,6 +8156,13 @@ "node": ">=12.20.0" } }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, "node_modules/fs-extra": { "version": "11.4.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", @@ -7772,7 +8182,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -7837,6 +8246,16 @@ "node": ">= 0.4" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-east-asian-width": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", @@ -8518,7 +8937,7 @@ "version": "5.1.9", "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.9.tgz", "integrity": "sha512-m8nVez3rwrgmWxtLMt1ZYXB2Lv7OKYn/disyxAlSDYAlKSlFoPPfIAmAM/M5xqL4m4C/wAPw7S2/CNaUii1Hxg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -8688,7 +9107,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, + "devOptional": true, "engines": { "node": ">=0.10.0" } @@ -8698,7 +9117,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "peer": true, "engines": { "node": ">=8" } @@ -8726,7 +9144,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, + "devOptional": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -8962,6 +9380,25 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/joi": { + "version": "18.2.9", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.9.tgz", + "integrity": "sha512-2mD929bUVKUhOLQQEVhlf6EZ0Mlo0DeRb5MO7cViR9AXLtBauuccEtB1py9Ocxpo/P7ucnh442iY/iOwrh3IQw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/address": "^5.1.1", + "@hapi/formula": "^3.0.2", + "@hapi/hoek": "^11.0.7", + "@hapi/pinpoint": "^2.0.1", + "@hapi/tlds": "^1.1.1", + "@hapi/topo": "^6.0.2", + "@standard-schema/spec": "^1.1.0" + }, + "engines": { + "node": ">= 20" + } + }, "node_modules/js-beautify": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-2.0.3.tgz", @@ -9524,7 +9961,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9546,7 +9982,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9568,7 +10003,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9590,7 +10024,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9612,7 +10045,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9634,7 +10066,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9656,7 +10087,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9678,7 +10108,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9700,7 +10129,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9722,7 +10150,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9744,7 +10171,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -9822,6 +10248,20 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -9830,6 +10270,13 @@ "license": "MIT", "peer": true }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -10784,6 +11231,13 @@ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, "node_modules/mlly": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.2.tgz", @@ -10848,6 +11302,14 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/nan": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.29.0.tgz", + "integrity": "sha512-GlGk3HIvitbvs+LT3g6XUP1kpirKNvmDFwF/bmo6XNWSb/eYEs/O4bfgIEIXCZ+lIOTS5xNwDvSGMw6FJdAhtA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/nanoid": { "version": "3.3.18", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", @@ -10915,7 +11377,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "dev": true, "license": "MIT", "optional": true }, @@ -11586,6 +12047,35 @@ "pathe": "^2.0.3" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/pluralize": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", @@ -11825,6 +12315,30 @@ "dev": true, "license": "ISC" }, + "node_modules/protobufjs": { + "version": "7.6.6", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", + "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.5", + "@protobufjs/eventemitter": "^1.1.1", + "@protobufjs/fetch": "^1.1.1", + "@protobufjs/float": "^1.0.2", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.1", + "@types/node": ">=13.7.0", + "long": "^5.3.2" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/proxy-from-env": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", @@ -11860,6 +12374,17 @@ "integrity": "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g==", "license": "MIT" }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -12155,6 +12680,16 @@ "unist-util-visit": "^5.0.0" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -12288,12 +12823,29 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/rxjs-interop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/rxjs-interop/-/rxjs-interop-2.0.0.tgz", "integrity": "sha512-ASEq9atUw7lualXB+knvgtvwkCEvGWV2gDD/8qnASzBkzEARZck9JAyxmY8OS6Nc1pCPEgDTKNcx+YqqYfzArw==", "license": "MIT" }, + "node_modules/rxjs/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -12327,7 +12879,7 @@ "version": "1.105.0", "resolved": "https://registry.npmjs.org/sass/-/sass-1.105.0.tgz", "integrity": "sha512-4EjSuRTyG8M4yRXXY1PT9OUV5zz23xBxJkbIAprohKHqf6HOU4MCpE3U3vAC+iO1gxobBjESNbcj8rYAQZWnyA==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "chokidar": "^5.0.0", @@ -12863,6 +13415,31 @@ "dev": true, "license": "CC0-1.0" }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -12916,6 +13493,18 @@ "xtend": "^4.0.2" } }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -12943,7 +13532,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12957,8 +13545,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "peer": true + "dev": true }, "node_modules/stringify-entities": { "version": "4.0.4", @@ -12979,7 +13566,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -13593,6 +14179,96 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/tar-fs/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/tar-fs/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.2.tgz", + "integrity": "sha512-+8NeqHRjQWH9nYlwo2gamAMImZCVzI4UoEgDpWorBt9OEfppiZn+uSkskzQKPWIZyji/C8fpWO7u69G0DX0tbg==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.9.0", + "bare-fs": "^4.8.2", + "fast-fifo": "^1.3.2", + "streamx": "^2.28.1" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, "node_modules/terser": { "version": "5.31.6", "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", @@ -13647,6 +14323,16 @@ } } }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, "node_modules/timers-browserify": { "version": "2.0.12", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.12.tgz", @@ -13907,6 +14593,13 @@ "integrity": "sha512-C3TaO7K81YvjCgQH9Q1S3R3P3BtN3RIM8n+OvX4il1K1zgE8ZhI0op7kClgkxtutIE8hQrcrHBXvIheqKUUCxw==", "dev": true }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14920,6 +15613,25 @@ "node": ">=18" } }, + "node_modules/wait-on": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-9.5.1.tgz", + "integrity": "sha512-bOtj1wRY6u+GPnNskZLcgZNHOaL5NC4+5hRCbEpVoVX6MbMrCLCqIBM2bjsAfrgst3ZC0siIo7vLb4LuYFfIow==", + "dev": true, + "license": "MIT", + "dependencies": { + "axios": "^1.20.0", + "joi": "^18.2.9", + "lodash": "^4.18.1", + "rxjs": "^7.8.2" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/wasm-check": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/wasm-check/-/wasm-check-3.0.2.tgz", @@ -15193,6 +15905,24 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -15277,6 +16007,16 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/yaml-ast-parser": { "version": "0.0.43", "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", @@ -15284,6 +16024,25 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/yargs": { + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", diff --git a/package.json b/package.json index 4ba7a65facc..6cd52e135e3 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,9 @@ "dev": "rspack build --env development", "lint": "eslint", "lint:fix": "eslint --fix", + "playwright": "playwright test --project=chromium", + "playwright:docs": "playwright test --project=docs --reporter=list", + "playwright:ui": "playwright test --ui", "serve": "rspack dev --env development", "stylelint": "stylelint \"src/**/*.scss\" \"src/**/*.vue\"", "stylelint:fix": "stylelint \"src/**/*.scss\" \"src/**/*.vue\" --fix", @@ -90,8 +93,10 @@ }, "devDependencies": { "@nextcloud/browserslist-config": "^3.1.2", + "@nextcloud/e2e-test-server": "^0.6.0", "@nextcloud/eslint-config": "^9.0.1", "@nextcloud/stylelint-config": "^3.2.2", + "@playwright/test": "^1.63.0", "@rsdoctor/rspack-plugin": "^1.6.4", "@rspack/cli": "^2.2.7", "@rspack/core": "^2.2.7", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 00000000000..3f90bce2de5 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,79 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { DocsOptions } from './tests/playwright/support/fixtures/users.ts' + +import { defineConfig, devices } from '@playwright/test' + +export default defineConfig({ + timeout: 90_000, + forbidOnly: !!process.env.CI, + retries: process.env.CI ? 1 : 0, + workers: process.env.CI ? 1 : undefined, + reporter: process.env.CI ? [['blob'], ['dot'], ['github']] : 'html', + use: { + // Trailing slash matters: fixtures resolve relative paths (e.g. './login') + // against this, and without it the last segment ("index.php") gets + // replaced instead of appended. + baseURL: 'http://localhost:8089/index.php/', + trace: 'on-first-retry', + screenshot: 'only-on-failure', + }, + projects: [ + { + name: 'chromium', + testDir: './tests/playwright/e2e', + use: { + ...devices['Desktop Chrome'], + // Uses the runner's pre-installed system Chrome instead of + // Playwright's own bundled Chromium — playwright.yml's CI + // workflow relies on this to skip a browser install step. + channel: 'chrome', + }, + }, + // Docs screenshots: run only via `npm run playwright:docs`, reviewed by a human, not asserted in CI + { + name: 'docs-setup', + testDir: './tests/playwright/docs-screenshots', + testMatch: 'global.setup.ts', + }, + { + name: 'docs', + // Same specs as e2e, in docs mode: demo users, and docsScreenshot() writes files + testDir: './tests/playwright/e2e', + grep: /@docs/, + dependencies: ['docs-setup'], + // Pixel-consistent images and shared demo accounts: no retries, no parallel workers + retries: 0, + workers: 1, + fullyParallel: false, + use: { + ...devices['Desktop Chrome'], + viewport: { width: 1280, height: 800 }, + deviceScaleFactor: 2, + docsMode: true, + screenshot: 'off', + trace: 'off', + }, + }, + ], + webServer: { + command: 'node tests/playwright/start-nextcloud-server.mjs', + env: { + NEXTCLOUD_PORT: '8089', + }, + stderr: 'pipe', + stdout: 'pipe', + gracefulShutdown: { + signal: 'SIGTERM', + timeout: 10000, + }, + reuseExistingServer: !process.env.CI, + timeout: 5 * 60 * 1000, + wait: { + stdout: /Nextcloud is now ready to use/, + }, + }, +}) diff --git a/tests/playwright/docs-screenshots/demo-avatars/christine.png b/tests/playwright/docs-screenshots/demo-avatars/christine.png new file mode 100644 index 00000000000..35f81d31a44 Binary files /dev/null and b/tests/playwright/docs-screenshots/demo-avatars/christine.png differ diff --git a/tests/playwright/docs-screenshots/demo-avatars/leon.png b/tests/playwright/docs-screenshots/demo-avatars/leon.png new file mode 100644 index 00000000000..63957893d66 Binary files /dev/null and b/tests/playwright/docs-screenshots/demo-avatars/leon.png differ diff --git a/tests/playwright/docs-screenshots/demo-avatars/louis.png b/tests/playwright/docs-screenshots/demo-avatars/louis.png new file mode 100644 index 00000000000..d5f22e12516 Binary files /dev/null and b/tests/playwright/docs-screenshots/demo-avatars/louis.png differ diff --git a/tests/playwright/docs-screenshots/demo-users-list.ts b/tests/playwright/docs-screenshots/demo-users-list.ts new file mode 100644 index 00000000000..fffee2c62ca --- /dev/null +++ b/tests/playwright/docs-screenshots/demo-users-list.ts @@ -0,0 +1,19 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +/** + * The fixed cast every docs screenshot draws from — "test-a1b2c3" and a + * lorem-ipsum reply read as obviously fake in a screenshot meant for users. + * + * Seeded once by global.setup.ts before any docs spec starts; add new + * names here as more screenshot specs need them. + */ +export const DEMO_PASSWORD = 'demo-password-123' + +export const DEMO_USERS = [ + { userId: 'christine', displayName: 'Christine' }, + { userId: 'leon', displayName: 'Leon' }, + { userId: 'louis', displayName: 'Louis' }, +] as const diff --git a/tests/playwright/docs-screenshots/docs-screenshot.ts b/tests/playwright/docs-screenshots/docs-screenshot.ts new file mode 100644 index 00000000000..07105474d36 --- /dev/null +++ b/tests/playwright/docs-screenshots/docs-screenshot.ts @@ -0,0 +1,44 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { Locator, Page } from '@playwright/test' + +import { readFile } from 'node:fs/promises' +import { join } from 'node:path' + +// playwright test always runs with the repo root as cwd. Under /test-results, +// already gitignored: the docs site lives in a separate repo, so these are +// picked up and moved over manually rather than committed here. +const OUTPUT_DIR = join(process.cwd(), 'test-results/docs-screenshots-output') +const AVATARS_DIR = join(process.cwd(), 'tests/playwright/docs-screenshots/demo-avatars') + +/** + * Uploads `demo-avatars/.png` as the account's personal avatar. + * + * `OCS-APIRequest: true` skips the CSRF token core's avatar endpoint would + * otherwise require for a session-cookie request — same trick Talk's own + * OCS-based APIs get for free by extending OCSController. + */ +export async function setDemoAvatar(page: Page, userId: string): Promise { + const buffer = await readFile(join(AVATARS_DIR, `${userId}.png`)) + await page.request.post('./avatar/', { + headers: { 'OCS-APIRequest': 'true' }, + multipart: { 'files[]': { name: `${userId}.png`, mimeType: 'image/png', buffer } }, + }) +} + +/** + * Captures `target` (a page or a scoped locator) into the gitignored output + * folder. Viewport/scale are fixed by the `docs` project, the only + * project this runs under, so every shot comes out the same size. + * + * @param target - Page for a full-viewport shot, or a Locator to crop to one element + * @param name - File name, e.g. "chat.png" + */ +export async function saveDocsScreenshot(target: Page | Locator, name: string): Promise { + const path = join(OUTPUT_DIR, name) + await target.screenshot({ path }) + console.log(`[docs-screenshot] wrote ${path}`) +} diff --git a/tests/playwright/docs-screenshots/global.setup.ts b/tests/playwright/docs-screenshots/global.setup.ts new file mode 100644 index 00000000000..fa5d544e9fd --- /dev/null +++ b/tests/playwright/docs-screenshots/global.setup.ts @@ -0,0 +1,27 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { test as setup } from '@playwright/test' +import { runOcc } from '../support/database.ts' +import { DEMO_PASSWORD, DEMO_USERS } from './demo-users-list.ts' + +/** + * Runs once as the `docs-setup` project, which the `docs` project depends on. + * + * Seeds every {@link DEMO_USERS} account up front so individual docs + * screenshot specs don't each pay for a `user:info` + `user:add` round trip. + */ +setup('seed demo users', async () => { + for (const { userId, displayName } of DEMO_USERS) { + const { exitCode } = await runOcc(['user:info', userId], { failOnError: false }) + if (exitCode === 0) { + continue + } + + await runOcc(['user:add', '--password-from-env', '--display-name', displayName, userId], { + env: [`NC_PASS=${DEMO_PASSWORD}`], + }) + } +}) diff --git a/tests/playwright/e2e/chat/two-users-chat.spec.ts b/tests/playwright/e2e/chat/two-users-chat.spec.ts new file mode 100644 index 00000000000..7aef7a5f883 --- /dev/null +++ b/tests/playwright/e2e/chat/two-users-chat.spec.ts @@ -0,0 +1,36 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { expect } from '@playwright/test' +import { test } from '../../support/fixtures/conversations.ts' +import { setRoomEmojiAvatar } from '../../support/room-updates.ts' +import { ConversationPage } from '../../support/sections/ConversationPage.ts' + +test('a reply from one user shows up for the other', { tag: '@docs' }, async ({ createSession, createConversation, docsScreenshot }) => { + const { user: userA, page: pageA } = await createSession() + const { user: userB, page: pageB } = await createSession() + + // Both accounts are already invited, so neither page has to + // drive the "new conversation" / "add participant" UI. + const token = await createConversation('Project kickoff', { + user: [userA.userId, userB.userId], + moderator: [userA.userId], + }) + await setRoomEmojiAvatar(pageA, token, '🚀', 'ffc107') + + const convA = new ConversationPage(pageA) + const convB = new ConversationPage(pageB) + await convA.open(token) + await convB.open(token) + + const reply = 'Sounds good, see you all at 10am!' + await convB.messageForm.send(reply) + + // Scoped to the message list: the same text also appears in the left + // sidebar's conversation preview, which would otherwise match too. + await expect(convA.messagesList.getByText(reply)).toBeVisible() + + await docsScreenshot(pageA, 'chat.png') +}) diff --git a/tests/playwright/e2e/conversations/main-app.spec.ts b/tests/playwright/e2e/conversations/main-app.spec.ts new file mode 100644 index 00000000000..48df562a7eb --- /dev/null +++ b/tests/playwright/e2e/conversations/main-app.spec.ts @@ -0,0 +1,17 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { expect } from '@playwright/test' +import { test } from '../../support/fixtures/users.ts' + +test('Can open the Talk main app', async ({ createSession }) => { + const { page } = await createSession() + + await page.goto('/apps/spreed') + // The left sidebar (conversation list) only renders once the app has + // mounted and the logged-in user has been resolved. + await expect(page.locator('#content')).toBeVisible() + await expect(page.getByRole('navigation', { name: /conversation list/i })).toBeVisible() +}) diff --git a/tests/playwright/e2e/conversations/room-details.spec.ts b/tests/playwright/e2e/conversations/room-details.spec.ts new file mode 100644 index 00000000000..e0550a6e847 --- /dev/null +++ b/tests/playwright/e2e/conversations/room-details.spec.ts @@ -0,0 +1,41 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { expect } from '@playwright/test' +import { test } from '../../support/fixtures/users.ts' +import { createRoomViaApi, setRoomDescription, setRoomEmojiAvatar, setRoomName } from '../../support/room-updates.ts' +import { ConversationPage } from '../../support/sections/ConversationPage.ts' + +test('a moderator can create a room and set its avatar, name and description', async ({ createSession }) => { + const { page } = await createSession() + + // The creator is the room's owner, i.e. already a moderator — no need to + // promote anyone before the calls below. + const room = await createRoomViaApi(page, 'New room') + const token = room.token + + // Visual confirmation: the header reflects the new name and avatar, and + // the chat itself narrates every change via system messages. + const conv = new ConversationPage(page) + await conv.open(token) + + await expect(conv.messagesList.getByText('You created the conversation')).toBeVisible() + + const after = await setRoomEmojiAvatar(page, token, '🎉', '00ff00') + expect(after.isCustomAvatar).toBe(true) + + const nameResult = await setRoomName(page, token, 'Emoji avatar room') + expect(nameResult.name).toBe('Emoji avatar room') + + const descriptionResult = await setRoomDescription(page, token, 'A short description') + expect(descriptionResult.description).toBe('A short description') + + await expect(page.locator('.conversation-header .title')).toHaveText('Emoji avatar room') + await expect(page.locator('.top-bar__icon-wrapper img.avatar')).toHaveAttribute('src', new RegExp(`[?&]v=${after.avatarVersion}(&|$)`)) + + await expect(conv.messagesList.getByText('You set the conversation picture').first()).toBeVisible() + await expect(conv.messagesList.getByText('You renamed the conversation from "New room" to "Emoji avatar room"')).toBeVisible() + await expect(conv.messagesList.getByText('You set the description')).toBeVisible() +}) diff --git a/tests/playwright/e2e/integrations/admin-settings.spec.ts b/tests/playwright/e2e/integrations/admin-settings.spec.ts new file mode 100644 index 00000000000..0f02c4d320b --- /dev/null +++ b/tests/playwright/e2e/integrations/admin-settings.spec.ts @@ -0,0 +1,14 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { expect } from '@playwright/test' +import { ADMIN_USER, test } from '../../support/fixtures/users.ts' + +test('Can open the Talk admin settings', async ({ createSession }) => { + const { page } = await createSession(ADMIN_USER) + + await page.goto('/settings/admin/talk') + await expect(page.locator('#admin_settings')).toBeVisible() +}) diff --git a/tests/playwright/merge.config.ts b/tests/playwright/merge.config.ts new file mode 100644 index 00000000000..6236a93a5b1 --- /dev/null +++ b/tests/playwright/merge.config.ts @@ -0,0 +1,11 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +// Needed to merge multiple Playwright reports +// when they are ran on self-hosted and github runners (different test directories are used) +export default { + testDir: 'tests/playwright/e2e', + reporter: [['html', { open: 'never' }]], +} diff --git a/tests/playwright/start-nextcloud-server.mjs b/tests/playwright/start-nextcloud-server.mjs new file mode 100644 index 00000000000..9116f85cec8 --- /dev/null +++ b/tests/playwright/start-nextcloud-server.mjs @@ -0,0 +1,41 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { configureNextcloud, startNextcloud, stopNextcloud, waitOnNextcloud } from '@nextcloud/e2e-test-server/docker' +import { execSync } from 'node:child_process' +import { readFileSync } from 'node:fs' + +async function start() { + const appinfo = readFileSync('appinfo/info.xml', 'utf-8') + const maxVersion = appinfo.match(//)?.[1] + + let branch = 'master' + if (maxVersion) { + const refs = execSync('git ls-remote --refs', { encoding: 'utf-8' }) + const stableBranch = `stable${maxVersion}` + branch = refs.includes(`refs/heads/${stableBranch}`) ? stableBranch : branch + } + + return startNextcloud(branch, true, { + exposePort: Number.parseInt(process.env.NEXTCLOUD_PORT ?? '8089', 10), + }) +} + +async function stop() { + process.stderr.write('Stopping Nextcloud server...\n') + await stopNextcloud() + process.exit(0) +} + +process.on('SIGTERM', stop) +process.on('SIGINT', stop) + +const ip = await start() +await waitOnNextcloud(ip) +await configureNextcloud(['spreed']) + +while (true) { + await new Promise((resolve) => setTimeout(resolve, 5000)) +} diff --git a/tests/playwright/support/database.ts b/tests/playwright/support/database.ts new file mode 100644 index 00000000000..80d860016f3 --- /dev/null +++ b/tests/playwright/support/database.ts @@ -0,0 +1,78 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { runOcc as rawRunOcc } from '@nextcloud/e2e-test-server' +import { createRandomUser as rawCreateRandomUser, login as rawLogin } from '@nextcloud/e2e-test-server/playwright' +import { mkdirSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' + +const LOCK_DIR = join(tmpdir(), 'spreed-playwright-db.lock') +const MAX_WAIT_MS = 30_000 + +/** + * Serializes `fn` across every worker process in this test run. + * + * Parallel Playwright workers are separate OS processes that can all hit the + * same throwaway SQLite database at once (user creation, login, room + * creation). SQLite has no built-in queueing for concurrent writers, and + * these short-lived connections set no busy timeout, so a collision fails + * immediately with "database is locked" instead of waiting its turn. + * + * Rather than guessing at every error shape that contention can surface as + * and retrying each one, this makes sure only one such write ever runs at a + * time — via an atomic `mkdir`, which fails with EEXIST while another + * worker holds it — without limiting how many workers run in parallel + * otherwise (page rendering, navigation, etc. are unaffected). + * + * @param fn the write operation to serialize + */ +async function withDatabaseLock(fn: () => Promise): Promise { + const deadline = Date.now() + MAX_WAIT_MS + for (;;) { + try { + mkdirSync(LOCK_DIR) + break + } catch { + if (Date.now() > deadline) { + throw new Error(`Timed out waiting ${MAX_WAIT_MS}ms for the database lock at ${LOCK_DIR}`) + } + await new Promise((resolve) => setTimeout(resolve, 50)) + } + } + try { + return await fn() + } finally { + rmSync(LOCK_DIR, { recursive: true, force: true }) + } +} + +/** + * Locked drop-ins for `@nextcloud/e2e-test-server`'s `createRandomUser`, + * `login`, and `runOcc`. Import these instead of the raw package whenever a + * fixture or spec writes to the throwaway database — the call is + * automatically serialized across worker processes, so nobody has to + * remember to wrap it in a lock themselves. + * + * `occ` runs through Docker's exec API and `login()` through an HTTP POST — + * two unrelated transports, so there is no single network layer to + * intercept both at. Wrapping the functions here is the equivalent: every + * call site gets locking for free just by importing from this module. + * + * This lock only covers writes made by test-harness code (this module). + * It was verified — with a throwaway stress spec that has since been + * removed, sending ~15 rounds of truly concurrent messages between two + * logged-in pages under 4-worker parallelism — that ordinary in-app chat + * traffic (message sends, polling, read receipts) does not need the same + * protection: those writes never collided across several runs, even with + * SQLite's WAL mode left disabled in start-nextcloud-server.mjs. If that + * ever changes, re-enable the WAL line there first before reaching for + * anything more here. + */ +export const createRandomUser: typeof rawCreateRandomUser = () => withDatabaseLock(rawCreateRandomUser) + +export const login: typeof rawLogin = (request, user) => withDatabaseLock(() => rawLogin(request, user)) + +export const runOcc: typeof rawRunOcc = (command, options) => withDatabaseLock(() => rawRunOcc(command, options)) diff --git a/tests/playwright/support/fixtures/conversations.ts b/tests/playwright/support/fixtures/conversations.ts new file mode 100644 index 00000000000..abe1b12d9ae --- /dev/null +++ b/tests/playwright/support/fixtures/conversations.ts @@ -0,0 +1,92 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { runOcc } from '../database.ts' +import { test as base } from './users.ts' + +/** + * Mirrors every option `occ talk:room:create` accepts, besides the required + * `name`. See `lib/Command/Room/Create.php`. + */ +export interface CreateConversationOptions { + description?: string + user?: string[] + group?: string[] + public?: boolean + readonly?: boolean + listable?: number + password?: string + owner?: string + moderator?: string[] + messageExpiration?: number + preserve?: boolean +} + +function buildCreateArgs(name: string, options: CreateConversationOptions): string[] { + const args = ['talk:room:create'] + + if (options.description !== undefined) { + args.push('--description', options.description) + } + for (const user of options.user ?? []) { + args.push('--user', user) + } + for (const group of options.group ?? []) { + args.push('--group', group) + } + if (options.public) { + args.push('--public') + } + if (options.readonly) { + args.push('--readonly') + } + if (options.listable !== undefined) { + args.push('--listable', String(options.listable)) + } + if (options.password !== undefined) { + args.push('--password', options.password) + } + if (options.owner !== undefined) { + args.push('--owner', options.owner) + } + for (const moderator of options.moderator ?? []) { + args.push('--moderator', moderator) + } + if (options.messageExpiration !== undefined) { + args.push('--message-expiration', String(options.messageExpiration)) + } + if (options.preserve) { + args.push('--preserve') + } + + // Everything after "--" is positional, so a name starting with "-" is + // never misread as another option. + args.push('--', name) + return args +} + +/** + * Test fixture exposing a `createConversation()` factory: creates a Talk + * conversation via `occ talk:room:create` and returns its room token. + */ +export const test = base.extend<{ + createConversation: (name: string, options?: CreateConversationOptions) => Promise +}>({ + // Playwright reads this literal object pattern to know this fixture needs no + // other fixtures; anything else (e.g. a plain `_`) breaks its dependency + // detection at runtime. + // eslint-disable-next-line no-empty-pattern + createConversation: async ({}, use) => { + await use(async (name, options = {}) => { + const { stdout } = await runOcc(buildCreateArgs(name, options), { verbose: true }) + + const token = stdout.match(/Room token: (\S+)/)?.[1] + if (!token) { + throw new Error(`Could not read the room token from occ output:\n${stdout}`) + } + return token + }) + }, +}) diff --git a/tests/playwright/support/fixtures/users.ts b/tests/playwright/support/fixtures/users.ts new file mode 100644 index 00000000000..78afef58465 --- /dev/null +++ b/tests/playwright/support/fixtures/users.ts @@ -0,0 +1,85 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { User } from '@nextcloud/e2e-test-server' +import type { Locator, Page } from '@playwright/test' + +import { test as base } from '@playwright/test' +import { DEMO_PASSWORD, DEMO_USERS } from '../../docs-screenshots/demo-users-list.ts' +import { saveDocsScreenshot, setDemoAvatar } from '../../docs-screenshots/docs-screenshot.ts' +import { createRandomUser, login } from '../database.ts' + +/** + * The fixed admin account every throwaway Nextcloud instance ships with. + */ +export const ADMIN_USER: User = { userId: 'admin', password: 'admin', language: 'en' } + +export interface UserSession { + user: User + page: Page +} + +export interface DocsOptions { + /** Set by the `docs` project: sessions use demo users, `docsScreenshot()` writes files */ + docsMode: boolean +} + +/** + * Test fixture exposing a `createSession()` factory instead of a fixed + * number of named users — call it once per user a test needs. With no + * argument it creates a fresh throwaway account (in docs mode: the next + * of DEMO_USERS, with their avatar); pass e.g. `ADMIN_USER` to log in as + * a specific one. Every page opened this way is closed automatically once + * the test ends. + * + * `docsScreenshot()` saves a docs image in docs mode and does nothing otherwise. + */ +export const test = base.extend Promise + docsScreenshot: (target: Page | Locator, name: string) => Promise +}>({ + docsMode: [false, { option: true }], + + createSession: async ({ browser, baseURL, docsMode }, use) => { + const opened: Page[] = [] + let demoIndex = 0 + + await use(async (user) => { + let resolvedUser = user + let isDemoUser = false + if (!resolvedUser && docsMode) { + const demoUser = DEMO_USERS[demoIndex++] + if (!demoUser) { + throw new Error(`Only ${DEMO_USERS.length} demo users exist, add more to demo-users-list.ts`) + } + resolvedUser = { userId: demoUser.userId, password: DEMO_PASSWORD, language: 'en' } + isDemoUser = true + } + resolvedUser ??= await createRandomUser() + + // Important: make sure we authenticate in a clean environment by unsetting storage state. + const page = await browser.newPage({ + storageState: undefined, + baseURL, + }) + await login(page.request, resolvedUser) + if (isDemoUser) { + await setDemoAvatar(page, resolvedUser.userId) + } + opened.push(page) + return { user: resolvedUser, page } + }) + + await Promise.all(opened.map((page) => page.close())) + }, + + docsScreenshot: async ({ docsMode }, use) => { + await use(async (target, name) => { + if (docsMode) { + await saveDocsScreenshot(target, name) + } + }) + }, +}) diff --git a/tests/playwright/support/room-updates.ts b/tests/playwright/support/room-updates.ts new file mode 100644 index 00000000000..03d5ac8ad9d --- /dev/null +++ b/tests/playwright/support/room-updates.ts @@ -0,0 +1,95 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { Page } from '@playwright/test' + +/** + * Hits Talk's room OCS endpoints directly via `page.request`, instead of + * through src/services/conversationsService.ts / avatarService.ts: those run + * in-browser and depend on @nextcloud/axios (CSRF token) and + * @nextcloud/router's generateOcsUrl() (OC.* globals) — neither exists in + * this Node-side test harness. Each helper below mirrors one of those + * service functions' endpoint/params so the two stay in lockstep; see each + * helper's docstring for which one. + */ + +/** + * Shape of the `TalkRoom` fields these helpers' callers care about — see + * lib/ResponseDefinitions.php. + */ +export interface RoomAvatarResult { + avatarVersion: string + isCustomAvatar: boolean +} + +export interface RoomNameResult { + name: string +} + +export interface RoomDescriptionResult { + description: string +} + +export interface CreatedRoom { + token: string + name: string +} + +async function requestRoom(page: Page, method: 'post' | 'put', path: string, data: Record): Promise { + // baseURL ends in "/index.php/"; OCS lives one level up from there. + const response = await page.request[method](`../ocs/v2.php/apps/spreed/api/${path}?format=json`, { + // Lets a session-cookie request skip the CSRF token these endpoints + // would otherwise need. + headers: { 'OCS-APIRequest': 'true' }, + data, + }) + const { ocs } = await response.json() + return ocs.data +} + +/** + * Creates a group conversation via Talk's room-creation OCS endpoint, for + * tests that exercise the API surface itself rather than going through + * `occ talk:room:create` (see conversations.ts's `createConversation`). + * `page` must belong to a logged-in user, who becomes the room's owner. + * + * Mirrors src/services/conversationsService.ts's `createLegacyConversation` + * (same endpoint/params) — that one runs in-browser via axios, which is why + * it can't be imported directly here; see this file's module comment. + */ +export async function createRoomViaApi(page: Page, name: string): Promise { + return requestRoom(page, 'post', 'v4/room', { roomType: 2 /* Room::TYPE_GROUP */, roomName: name }) +} + +/** + * Sets a conversation's avatar to an emoji on a color, via Talk's + * `avatar/emoji` OCS endpoint — no file upload needed. `page` must belong to + * a moderator of `token`'s room. + * + * Mirrors src/services/avatarService.ts's `setConversationEmojiAvatar`. + */ +export async function setRoomEmojiAvatar(page: Page, token: string, emoji: string, color?: string): Promise { + return requestRoom(page, 'post', `v1/room/${token}/avatar/emoji`, { emoji, color }) +} + +/** + * Renames a conversation, via Talk's room-rename OCS endpoint. `page` must + * belong to a moderator of `token`'s room. + * + * Mirrors src/services/conversationsService.ts's `setConversationName`. + */ +export async function setRoomName(page: Page, token: string, name: string): Promise { + return requestRoom(page, 'put', `v4/room/${token}`, { roomName: name }) +} + +/** + * Sets a conversation's description, via Talk's set-description OCS + * endpoint. `page` must belong to a moderator of `token`'s room. + * + * Mirrors src/services/conversationsService.ts's `setConversationDescription`. + */ +export async function setRoomDescription(page: Page, token: string, description: string): Promise { + return requestRoom(page, 'put', `v4/room/${token}/description`, { description }) +} diff --git a/tests/playwright/support/sections/ConversationPage.ts b/tests/playwright/support/sections/ConversationPage.ts new file mode 100644 index 00000000000..5d167638af5 --- /dev/null +++ b/tests/playwright/support/sections/ConversationPage.ts @@ -0,0 +1,27 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { Page } from '@playwright/test' + +import { NewMessage } from './NewMessage.ts' + +/** + * A single conversation's chat view, opened by room token. + */ +export class ConversationPage { + readonly messageForm: NewMessage + + constructor(private readonly page: Page) { + this.messageForm = new NewMessage(page) + } + + get messagesList() { + return this.page.getByLabel('Conversation messages') + } + + async open(token: string) { + await this.page.goto(`/index.php/call/${token}`) + } +} diff --git a/tests/playwright/support/sections/NewMessage.ts b/tests/playwright/support/sections/NewMessage.ts new file mode 100644 index 00000000000..664358aab16 --- /dev/null +++ b/tests/playwright/support/sections/NewMessage.ts @@ -0,0 +1,27 @@ +/*! + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import type { Page } from '@playwright/test' + +/** + * The message composer at the bottom of a conversation. + */ +export class NewMessage { + constructor(private readonly page: Page) { + } + + private get input() { + return this.page.locator('form.new-message-form .rich-contenteditable__input') + } + + private get sendButton() { + return this.page.getByRole('button', { name: 'Send message', exact: true }) + } + + async send(text: string) { + await this.input.fill(text) + await this.sendButton.click() + } +} diff --git a/tsconfig.json b/tsconfig.json index e30426d459b..6871e1d090c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "extends": "@vue/tsconfig/tsconfig.json", - "include": ["src/**/*.ts", "src/**/*.vue", "src/env.d.ts"], + "include": ["src/**/*.ts", "src/**/*.vue", "src/env.d.ts", "tests/playwright/**/*.ts", "playwright.config.ts"], "exclude": ["node_modules", "vendor"], "compilerOptions": { "outDir": "./js",