feat: [OCISDEV-1437] show suggested expiry date of 30 days, tests #3462
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Acceptance Tests | |
| on: | |
| pull_request: | |
| workflow_dispatch: | |
| jobs: | |
| detect-changes: | |
| name: detect-changes | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| outputs: | |
| docs-only: ${{ steps.check.outputs.docs-only }} | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check if changes are docs-only | |
| id: check | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| run: | | |
| # Always run full CI on workflow_dispatch, tags, or when PR title contains [full-ci] | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "docs-only=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| if echo "$PR_TITLE" | grep -qi "\[full-ci\]"; then | |
| echo "docs-only=false" >> "$GITHUB_OUTPUT" | |
| exit 0 | |
| fi | |
| # Get changed files between the PR base and head | |
| CHANGED_FILES=$(git diff --name-only "${{ github.event.pull_request.base.sha }}" "${{ github.sha }}" 2>/dev/null || git diff --name-only HEAD~1) | |
| # Patterns that are considered documentation/non-code changes | |
| DOCS_ONLY=true | |
| while IFS= read -r file; do | |
| case "$file" in | |
| .github/workflows/*) DOCS_ONLY=false; break ;; | |
| .github/*|.vscode/*|changelog/*|docs/*|deployments/*) ;; | |
| */CHANGELOG.md|*/CONTRIBUTING.md|*/README.md|CHANGELOG.md|CONTRIBUTING.md|LICENSE|README.md) ;; | |
| *) DOCS_ONLY=false; break ;; | |
| esac | |
| done <<< "$CHANGED_FILES" | |
| echo "docs-only=$DOCS_ONLY" >> "$GITHUB_OUTPUT" | |
| echo "Changed files docs-only: $DOCS_ONLY" | |
| build-and-test: | |
| name: build-and-test | |
| needs: [detect-changes] | |
| if: needs.detect-changes.outputs.docs-only != 'true' | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 60 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| node-version: "24" | |
| - name: Enable pnpm | |
| run: corepack enable && corepack prepare pnpm@10.33.3 --activate | |
| - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 | |
| with: | |
| php-version: "8.4" | |
| extensions: curl, xml, mbstring, zip, ldap, gd | |
| tools: composer | |
| - name: Install libvips-dev for ENABLE_VIPS build | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips-dev | |
| - name: Pre-checks and generate | |
| run: | | |
| # Phase 1 — I/O + light CPU, no contention between tasks. | |
| # wa (I/O wait) will spike during npm/pnpm downloads. | |
| # Expected avg load: ~50-60 % CPU on 2 vCPU. | |
| vmstat 2 > /tmp/vmstat-phase1.log & MONITOR_PID=$! | |
| (make vendor-bin-codestyle && make vendor-bin-codesniffer && make test-php-style && make check-env-var-annotations) > /tmp/php-style.log 2>&1 & PIDS=($!) | |
| (npm install -g @gherlint/gherlint@1.1.0 && make test-gherkin-lint) > /tmp/gherkin.log 2>&1 & PIDS+=($!) | |
| bash tests/acceptance/check-deleted-suites-in-expected-failure.sh > /tmp/suites.log 2>&1 & PIDS+=($!) | |
| make govulncheck > /tmp/govulncheck.log 2>&1 & PIDS+=($!) | |
| make ci-node-generate > /tmp/node-gen.log 2>&1 & PIDS+=($!) | |
| make ci-go-generate > /tmp/go-gen.log 2>&1 & PIDS+=($!) | |
| make changelog-lint > /tmp/changelog-lint.log 2>&1 & PIDS+=($!) | |
| FAILED=0 | |
| for PID in "${PIDS[@]}"; do wait "$PID" || FAILED=1; done | |
| kill $MONITOR_PID 2>/dev/null; wait $MONITOR_PID 2>/dev/null || true | |
| # How to read the load line: | |
| # busy — % of time CPUs were doing work (100% = fully saturated) | |
| # runq — processes waiting for a CPU turn; runq >> nCPU means tasks | |
| # compete and each runs slower (runq 10 on 2 vCPU = ~5× slower) | |
| # wa — % waiting on disk/network; wa > 20% = I/O bound, not CPU bound | |
| # → high wa: add more parallel tasks; high runq: reduce them | |
| awk '/^[ ]*[0-9]/ { busy=100-$15; sum_b+=busy; if(busy>pk_b)pk_b=busy; | |
| sum_r+=$1; if($1>pk_r) pk_r=$1; sum_wa+=$16; n++ } | |
| END { printf "=== phase 1 load (2 vCPU): avg busy %d%% peak %d%% | avg runq %.0f peak %d | avg wa %d%%\n", | |
| sum_b/n, pk_b, sum_r/n, pk_r, sum_wa/n }' /tmp/vmstat-phase1.log | |
| echo "=== php-style ===" && cat /tmp/php-style.log | |
| echo "=== gherkin ===" && cat /tmp/gherkin.log | |
| echo "=== suites ===" && cat /tmp/suites.log | |
| echo "=== govulncheck ===" && cat /tmp/govulncheck.log | |
| echo "=== ci-node-generate ===" && cat /tmp/node-gen.log | |
| echo "=== ci-go-generate ===" && cat /tmp/go-gen.log | |
| echo "=== changelog-lint ===" && cat /tmp/changelog-lint.log | |
| exit $FAILED | |
| - name: Trivy scan | |
| uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0 | |
| with: | |
| scan-type: fs | |
| format: table | |
| exit-code: 1 | |
| severity: CRITICAL,HIGH | |
| trivy-config: .trivy.yaml | |
| - name: Build, lint and test | |
| run: | | |
| # Phase 2 — all three are CPU-bound Go compilation on 2 vCPU. | |
| # They compete for CPU (expect id < 10, load ~95 %) but share the | |
| # Go build cache within this runner, so make test reuses artifacts | |
| # from the ocis build. Critical path = ocis build (~300 s). | |
| vmstat 2 > /tmp/vmstat-phase2.log & MONITOR_PID=$! | |
| make ci-golangci-lint > /tmp/golangci-lint.log 2>&1 & PIDS=($!) | |
| ( ENABLE_VIPS=true make -C ocis build-debug \ | |
| && mv ocis/bin/ocis-debug ocis/bin/ocis ) > /tmp/ocis-build.log 2>&1 & PIDS+=($!) | |
| make test > /tmp/unit-tests.log 2>&1 & PIDS+=($!) | |
| # vips-tagged files live only under thumbnails; scoped run, no coverprofile. | |
| go test -v -tags 'enable_vips' ./services/thumbnails/... > /tmp/unit-tests-vips.log 2>&1 & PIDS+=($!) | |
| GOWORK=off make -C tests/ociswrapper build > /tmp/ociswrapper-build.log 2>&1 & PIDS+=($!) | |
| FAILED=0 | |
| for PID in "${PIDS[@]}"; do wait "$PID" || FAILED=1; done | |
| kill $MONITOR_PID 2>/dev/null; wait $MONITOR_PID 2>/dev/null || true | |
| # Signal: busy=saturated runq>>2=tasks competing(slows each) wa>20%=I/O bound | |
| awk '/^[ ]*[0-9]/ { busy=100-$15; sum_b+=busy; if(busy>pk_b)pk_b=busy; | |
| sum_r+=$1; if($1>pk_r) pk_r=$1; sum_wa+=$16; n++ } | |
| END { printf "=== phase 2 load (2 vCPU): avg busy %d%% peak %d%% | avg runq %.0f peak %d | avg wa %d%%\n", | |
| sum_b/n, pk_b, sum_r/n, pk_r, sum_wa/n }' /tmp/vmstat-phase2.log | |
| echo "=== golangci-lint ===" && cat /tmp/golangci-lint.log | |
| echo "=== ocis build ===" && cat /tmp/ocis-build.log | |
| echo "=== ociswrapper build ===" && cat /tmp/ociswrapper-build.log | |
| echo "=== unit tests ===" && cat /tmp/unit-tests.log | |
| echo "=== vips unit tests ===" && cat /tmp/unit-tests-vips.log | |
| exit $FAILED | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: ocis-build-artifacts | |
| path: | | |
| ocis/bin/ocis | |
| tests/ociswrapper/bin/ociswrapper | |
| retention-days: 1 | |
| if-no-files-found: error | |
| local-api-tests: | |
| name: ${{ matrix.suite }} | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| GOCOVERDIR: ${{ github.workspace }}/coverage-reports | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| suite: | |
| # contract & locks | |
| - apiContract | |
| - apiLocks | |
| # settings & notifications (needs email) | |
| - apiSettings | |
| - apiNotification | |
| - apiCors | |
| # graph | |
| - apiGraphUser | |
| - apiGraph | |
| - apiGraphGroup | |
| # spaces & dav | |
| - apiSpaces | |
| - apiSpacesShares | |
| - apiSpacesDavOperation | |
| - apiDownloads | |
| - apiAsyncUpload | |
| - apiDepthInfinity | |
| - apiArchiver | |
| - apiActivities | |
| # search | |
| - apiSearch1 | |
| - apiSearch2 | |
| - apiSearchContent # needs Tika | |
| # sharing | |
| - apiSharingNgShares | |
| - apiReshare | |
| - apiSharingNgPermissions | |
| - apiSharingNgAdditionalShareRole | |
| - apiSharingNgDriveInvitation | |
| - apiSharingNgItemInvitation | |
| - apiSharingNgDriveLinkShare | |
| - apiSharingNgItemLinkShare | |
| - apiSharingNgLinkShareManagement | |
| # auth | |
| - apiAuthApp | |
| # antivirus (needs ClamAV) | |
| - apiAntivirus | |
| # federation (needs email + federation ocis) | |
| - apiOcm | |
| # collaboration (needs WOPI) | |
| - apiCollaboration | |
| - apiVault | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 | |
| with: | |
| php-version: "8.4" | |
| extensions: curl, xml, mbstring, zip, ldap, gd | |
| tools: composer | |
| - name: Cache libcurl 8.12.0 | |
| id: cache-libcurl | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: /opt/libcurl | |
| key: libcurl-8.12.0-${{ runner.os }} | |
| - name: Install libcurl 8.12.0 build dependencies | |
| # Always run: libvips-dev is needed at runtime (govips thumbnails in the pre-built binary) | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libssl-dev libnghttp2-dev libpsl-dev libldap-dev libssh-dev zlib1g-dev libvips-dev | |
| - name: Compile libcurl 8.12.0 from source | |
| if: steps.cache-libcurl.outputs.cache-hit != 'true' | |
| run: | | |
| cd /tmp | |
| curl -sLO https://curl.se/download/curl-8.12.0.tar.gz | |
| tar xzf curl-8.12.0.tar.gz | |
| cd curl-8.12.0 | |
| ./configure --with-ssl --with-zlib --with-nghttp2 --prefix=/opt/libcurl --enable-versioned-symbols --silent | |
| make -j$(nproc) --silent | |
| sudo make install --silent | |
| - name: Restore libcurl ldconfig | |
| # Always run: on cache hit the .so files are restored to /opt/libcurl/lib but | |
| # /etc/ld.so.cache on the fresh runner doesn't know about them yet. | |
| # Without ldconfig, PHP's curl extension can't find libcurl even though it's present. | |
| run: | | |
| echo "/opt/libcurl/lib" | sudo tee /etc/ld.so.conf.d/libcurl-8.conf | |
| sudo ldconfig | |
| /opt/libcurl/bin/curl --version | head -1 | |
| php -r ' | |
| $v = curl_version()["version"]; | |
| echo "PHP curl: $v\n"; | |
| if (version_compare($v, "8.12.0", "<")) { | |
| fwrite(STDERR, "FATAL: PHP sees libcurl $v, need >= 8.12.0\n"); | |
| exit(1); | |
| } | |
| ' | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper | |
| - name: Run ${{ matrix.suite }} | |
| run: | | |
| mkdir -p "$GOCOVERDIR" | |
| BEHAT_SUITES=${{ matrix.suite }} python3 tests/acceptance/run-github.py | |
| - name: Upload raw coverage data | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: api-coverage-${{ matrix.suite }} | |
| path: ${{ env.GOCOVERDIR }} | |
| if-no-files-found: error | |
| cli-tests: | |
| needs: [build-and-test] | |
| name: ${{ matrix.suite }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| GOCOVERDIR: ${{ github.workspace }}/coverage-reports | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| suite: | |
| - cliCommands,apiServiceAvailability # grouped: both need ClamAV + email services | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 | |
| with: | |
| php-version: "8.4" | |
| extensions: curl, xml, mbstring, zip, ldap, gd | |
| tools: composer | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper | |
| - name: Run ${{ matrix.suite }} | |
| run: | | |
| mkdir -p "$GOCOVERDIR" | |
| BEHAT_SUITES="${{ matrix.suite }}" python3 tests/acceptance/run-github.py | |
| - name: Upload raw coverage data | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: api-coverage-cli-${{ matrix.suite }} | |
| path: ${{ env.GOCOVERDIR }} | |
| if-no-files-found: error | |
| ldap-pool-tests: | |
| needs: [build-and-test] | |
| name: ldapPool-${{ matrix.suite }} | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| GOCOVERDIR: ${{ github.workspace }}/coverage-reports | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| suite: | |
| - apiGraphUser,apiGraph,apiGraphGroup | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 | |
| with: | |
| php-version: "8.4" | |
| extensions: curl, xml, mbstring, zip, ldap, gd | |
| tools: composer | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper | |
| - name: Run ${{ matrix.suite }} with OCIS_LDAP_POOL_ENABLED=true | |
| run: | | |
| mkdir -p "$GOCOVERDIR" | |
| BEHAT_SUITES="${{ matrix.suite }}" OCIS_LDAP_POOL_ENABLED=true python3 tests/acceptance/run-github.py | |
| - name: Upload raw coverage data | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: api-coverage-ldap-pool-${{ matrix.suite }} | |
| path: ${{ env.GOCOVERDIR }} | |
| if-no-files-found: error | |
| core-api-tests: | |
| name: ${{ matrix.suite }} | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| env: | |
| GOCOVERDIR: ${{ github.workspace }}/coverage-reports | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| suite: | |
| - "coreApiAuth,coreApiCapabilities,coreApiFavorites,coreApiMain,coreApiVersions" | |
| - "coreApiShareManagementBasicToShares,coreApiShareManagementToShares" | |
| - "coreApiSharees" | |
| - "coreApiSharePublicLink2" | |
| - "coreApiShareOperationsToShares1,coreApiShareOperationsToShares2,coreApiSharePublicLink1,coreApiShareCreateSpecialToShares1,coreApiShareCreateSpecialToShares2,coreApiShareUpdateToShares" | |
| - "coreApiTrashbin,coreApiTrashbinRestore,coreApiWebdavEtagPropagation1,coreApiWebdavEtagPropagation2" | |
| - "coreApiWebdavDelete,coreApiWebdavOperations,coreApiWebdavMove2" | |
| - "coreApiWebdavProperties" | |
| - "coreApiWebdavMove1,coreApiWebdavPreviews,coreApiWebdavUpload,coreApiWebdavUploadTUS" | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2.37.2 | |
| with: | |
| php-version: "8.4" | |
| extensions: curl, xml, mbstring, zip, ldap, gd | |
| tools: composer | |
| - name: Cache libcurl 8.12.0 | |
| id: cache-libcurl | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: /opt/libcurl | |
| key: libcurl-8.12.0-${{ runner.os }} | |
| - name: Install libcurl 8.12.0 build dependencies | |
| # Always run: libvips-dev is needed at runtime (govips thumbnails in the pre-built binary) | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libssl-dev libnghttp2-dev libpsl-dev libldap-dev libssh-dev zlib1g-dev libvips-dev | |
| - name: Compile libcurl 8.12.0 from source | |
| if: steps.cache-libcurl.outputs.cache-hit != 'true' | |
| run: | | |
| cd /tmp | |
| curl -sLO https://curl.se/download/curl-8.12.0.tar.gz | |
| tar xzf curl-8.12.0.tar.gz | |
| cd curl-8.12.0 | |
| ./configure --with-ssl --with-zlib --with-nghttp2 --prefix=/opt/libcurl --enable-versioned-symbols --silent | |
| make -j$(nproc) --silent | |
| sudo make install --silent | |
| - name: Restore libcurl ldconfig | |
| # Always run: on cache hit the .so files are restored to /opt/libcurl/lib but | |
| # /etc/ld.so.cache on the fresh runner doesn't know about them yet. | |
| # Without ldconfig, PHP's curl extension can't find libcurl even though it's present. | |
| run: | | |
| echo "/opt/libcurl/lib" | sudo tee /etc/ld.so.conf.d/libcurl-8.conf | |
| sudo ldconfig | |
| /opt/libcurl/bin/curl --version | head -1 | |
| php -r ' | |
| $v = curl_version()["version"]; | |
| echo "PHP curl: $v\n"; | |
| if (version_compare($v, "8.12.0", "<")) { | |
| fwrite(STDERR, "FATAL: PHP sees libcurl $v, need >= 8.12.0\n"); | |
| exit(1); | |
| } | |
| ' | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper | |
| - name: Run ${{ matrix.suite }} | |
| run: | | |
| mkdir -p "$GOCOVERDIR" | |
| BEHAT_SUITES="${{ matrix.suite }}" \ | |
| ACCEPTANCE_TEST_TYPE=core-api \ | |
| WITH_REMOTE_PHP=true \ | |
| python3 tests/acceptance/run-github.py | |
| - name: Upload raw coverage data | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: api-coverage-core-${{ matrix.suite }} | |
| path: ${{ env.GOCOVERDIR }} | |
| if-no-files-found: error | |
| e2e-tests: | |
| name: e2e-${{ matrix.suite }} | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - suite: part-1 | |
| args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc,vault-storage --run-part 1" | |
| - suite: part-2 | |
| args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc,vault-storage --run-part 2" | |
| - suite: part-3 | |
| args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc,vault-storage --run-part 3" | |
| - suite: part-4 | |
| args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc,vault-storage --run-part 4" | |
| - suite: search | |
| args: "--type playwright --suites search" | |
| tika: true | |
| - suite: keycloak | |
| args: "--type playwright --suites journeys,keycloak" | |
| keycloak: true | |
| - suite: mfa | |
| args: "--type playwright --suites mfa" | |
| mfa: true | |
| - suite: oidc | |
| args: "--type playwright specs/oidc/refreshToken.spec.ts" | |
| oidc: true | |
| - suite: oidc-iframe | |
| args: "--type playwright specs/oidc/iframeTokenRenewal.spec.ts" | |
| oidc_iframe: true | |
| - suite: app-provider | |
| args: "--type playwright --suites app-provider" | |
| collaboration: true | |
| - suite: ocm | |
| args: "--type playwright --suites ocm" | |
| federated: true | |
| - suite: vault-storage | |
| args: "--type playwright --suites vault-storage" | |
| vault_storage: true | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 | |
| with: | |
| # 24.16.0 breaks playwright install (yauzl zip extraction hangs); pin to last known good. | |
| # https://github.com/microsoft/playwright/issues/40724 | |
| node-version: "24.15.0" | |
| - name: Enable pnpm | |
| run: | | |
| corepack enable && corepack prepare pnpm@10.33.3 --activate | |
| pnpm config set store-dir ./.pnpm-store | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis tests/ociswrapper/bin/ociswrapper | |
| - name: Cache Playwright Chromium | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: ~/.cache/ms-playwright | |
| key: playwright-chromium-${{ hashFiles('web/pnpm-lock.yaml') }} | |
| # --- Tika (search suite only) --- | |
| - name: Start Tika | |
| if: matrix.tika == true | |
| run: | | |
| docker run -d --name tika --network host apache/tika:3.2.2.0-full | |
| timeout 120 bash -c 'until curl -sf http://localhost:9998; do sleep 2; done' | |
| echo "tika ready." | |
| # --- Keycloak (keycloak and mfa suites) --- | |
| - name: Generate Keycloak certs | |
| if: matrix.keycloak == true || matrix.mfa == true || matrix.vault_storage == true | |
| run: | | |
| mkdir -p keycloak-certs | |
| openssl req -x509 -newkey rsa:2048 \ | |
| -keyout keycloak-certs/keycloakkey.pem \ | |
| -out keycloak-certs/keycloakcrt.pem \ | |
| -nodes -days 365 -subj '/CN=keycloak' \ | |
| -addext 'subjectAltName=DNS:localhost,IP:127.0.0.1' | |
| chmod -R 777 keycloak-certs | |
| - name: Start Postgres | |
| if: matrix.keycloak == true || matrix.mfa == true || matrix.vault_storage == true | |
| run: | | |
| docker run -d --name postgres --network host \ | |
| -e POSTGRES_DB=keycloak \ | |
| -e POSTGRES_USER=keycloak \ | |
| -e POSTGRES_PASSWORD=keycloak \ | |
| postgres:alpine3.18 | |
| timeout 30 bash -c 'until docker exec postgres pg_isready -U keycloak; do sleep 1; done' | |
| - name: Start Keycloak | |
| if: matrix.keycloak == true || matrix.mfa == true || matrix.vault_storage == true | |
| run: | | |
| # The mfa suite needs a different realm with TOTP MFA configured. | |
| # ocis-ci-realm uses ocis-server:9200, ocis-mfa-ci-realm uses localhost:9200; | |
| # ocis runs on 127.0.0.1:9200 so we rewrite both forms. | |
| if [[ "${{ matrix.mfa }}" == "true" || "${{ matrix.vault_storage }}" == "true" ]]; then | |
| REALM_FILE=tests/config/ci/ocis-mfa-ci-realm.dist.json | |
| else | |
| REALM_FILE=tests/config/ci/ocis-ci-realm.dist.json | |
| fi | |
| sed -e 's|https://ocis-server:9200|https://127.0.0.1:9200|g' \ | |
| -e 's|https://localhost:9200|https://127.0.0.1:9200|g' \ | |
| "$REALM_FILE" > /tmp/ocis-realm.json | |
| docker run -d --name keycloak --network host \ | |
| -e OCIS_DOMAIN=https://127.0.0.1:9200 \ | |
| -e KC_HOSTNAME=localhost \ | |
| -e KC_PORT=8443 \ | |
| -e KC_DB=postgres \ | |
| -e "KC_DB_URL=jdbc:postgresql://localhost:5432/keycloak" \ | |
| -e KC_DB_USERNAME=keycloak \ | |
| -e KC_DB_PASSWORD=keycloak \ | |
| -e KC_FEATURES=impersonation \ | |
| -e KC_BOOTSTRAP_ADMIN_USERNAME=admin \ | |
| -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \ | |
| -e KC_HTTPS_CERTIFICATE_FILE=/keycloak-certs/keycloakcrt.pem \ | |
| -e KC_HTTPS_CERTIFICATE_KEY_FILE=/keycloak-certs/keycloakkey.pem \ | |
| -v "$(pwd)/keycloak-certs:/keycloak-certs:ro" \ | |
| -v "/tmp/ocis-realm.json:/opt/keycloak/data/import/oCIS-realm.json:ro" \ | |
| quay.io/keycloak/keycloak:26.2.5 \ | |
| start-dev --proxy-headers xforwarded \ | |
| --spi-connections-http-client-default-disable-trust-manager=true \ | |
| --import-realm --health-enabled=true | |
| timeout 300 bash -c 'until curl -skf https://localhost:9000/health/ready; do sleep 3; done' \ | |
| || (echo "=== keycloak logs ===" && docker logs keycloak --tail 80 && exit 1) | |
| echo "keycloak ready." | |
| # --- Collabora + OnlyOffice (app-provider suite only) --- | |
| - name: Start Collabora | |
| if: matrix.collaboration == true | |
| run: | | |
| docker run -d \ | |
| --name collabora \ | |
| --network host \ | |
| --entrypoint bash \ | |
| -e DONT_GEN_SSL_CERT=set \ | |
| -e "extra_params=--o:ssl.enable=true --o:ssl.termination=true --o:welcome.enable=false --o:net.frame_ancestors=https://127.0.0.1:9200" \ | |
| collabora/code:25.04.7.3.1 \ | |
| -c "coolconfig generate-proof-key && bash /start-collabora-online.sh" | |
| timeout 150 bash -c 'until curl -kfsSL https://localhost:9980/hosting/discovery > /dev/null; do sleep 5; done' | |
| echo "collabora ready." | |
| - name: Start OnlyOffice | |
| if: matrix.collaboration == true | |
| run: | | |
| sudo systemctl stop postgresql || true | |
| docker run -d \ | |
| --name onlyoffice \ | |
| --network host \ | |
| -e WOPI_ENABLED=true \ | |
| -e USE_UNAUTHORIZED_STORAGE=true \ | |
| -v "${{ github.workspace }}/tests/config/ci/only-office.json:/tmp/local.json:ro" \ | |
| --entrypoint /bin/sh \ | |
| onlyoffice/documentserver:9.2.1 \ | |
| -c "set -e | |
| cp /tmp/local.json /etc/onlyoffice/documentserver/local.json | |
| openssl req -x509 -newkey rsa:4096 -keyout onlyoffice.key -out onlyoffice.crt -sha256 -days 365 -batch -nodes | |
| mkdir -p /var/www/onlyoffice/Data/certs | |
| cp onlyoffice.key /var/www/onlyoffice/Data/certs/ | |
| cp onlyoffice.crt /var/www/onlyoffice/Data/certs/ | |
| chmod 400 /var/www/onlyoffice/Data/certs/onlyoffice.key | |
| /app/ds/run-document-server.sh" | |
| timeout 150 bash -c 'until curl -kfsSL https://localhost:443/hosting/discovery > /dev/null; do sleep 5; done' | |
| echo "onlyoffice ready." | |
| - name: Run e2e-${{ matrix.suite }} | |
| run: E2E_ARGS="${{ matrix.args }}" python3 tests/acceptance/run-e2e.py | |
| env: | |
| TIKA_NEEDED: ${{ matrix.tika == true && 'true' || 'false' }} | |
| KEYCLOAK_NEEDED: ${{ matrix.keycloak == true && 'true' || 'false' }} | |
| MFA_NEEDED: ${{ matrix.mfa == true && 'true' || 'false' }} | |
| OIDC_NEEDED: ${{ matrix.oidc == true && 'true' || 'false' }} | |
| OIDC_IFRAME_NEEDED: ${{ matrix.oidc_iframe == true && 'true' || 'false' }} | |
| COLLABORATION_NEEDED: ${{ matrix.collaboration == true && 'true' || 'false' }} | |
| FEDERATED_NEEDED: ${{ matrix.federated == true && 'true' || 'false' }} | |
| VAULT_STORAGE_NEEDED: ${{ matrix.vault_storage == true && 'true' || 'false' }} | |
| - name: Upload tracing result | |
| if: failure() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: playwright-traces-${{ matrix.suite }}-${{ github.run_attempt }} | |
| path: web/reports/e2e | |
| retention-days: 7 | |
| - name: Upload a11y result | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: playwright-a11y-result-${{ matrix.suite }}-${{ github.run_attempt }} | |
| path: web/reports/e2e/a11y-report.json | |
| retention-days: 7 | |
| litmus: | |
| name: litmus | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis | |
| - name: Run litmus | |
| run: python3 tests/acceptance/run-litmus.py | |
| cs3api: | |
| name: cs3api | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis | |
| - name: Run cs3api validator | |
| run: python3 tests/acceptance/run-cs3api.py | |
| wopi-builtin: | |
| name: wopi-builtin | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis | |
| - name: Run WOPI validator (builtin) | |
| run: python3 tests/acceptance/run-wopi.py --type builtin | |
| wopi-cs3: | |
| name: wopi-cs3 | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - name: Install libvips runtime | |
| run: | | |
| sudo apt-get update -qq | |
| # NEEDRESTART_MODE=a: auto-restart services silently; without this, needrestart can fail with exit code 6 | |
| sudo NEEDRESTART_MODE=a apt-get install -y libvips42t64 | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: ocis-build-artifacts | |
| - name: Restore binary permissions | |
| run: chmod +x ocis/bin/ocis | |
| - name: Run WOPI validator (cs3) | |
| run: python3 tests/acceptance/run-wopi.py --type cs3 | |
| external-ldap-tests: | |
| name: external-ldap-smoke | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: "24" | |
| - name: Enable pnpm | |
| run: corepack enable && corepack prepare pnpm@10.28.1 --activate | |
| # web/idp assets must be embedded for `ocis server` to come up (same as run-github.py) | |
| - name: Generate assets | |
| run: make ci-node-generate | |
| - name: Build oCIS | |
| run: make -C ocis build | |
| # Boots an external osixia LDAP + oCIS (embedded idm excluded) and runs the | |
| # smoke scope against the graph API — exercises the external-directory | |
| # read-after-write / retry path (OCISDEV-1030/1032/1033) that the embedded-idm | |
| # acceptance jobs never hit. | |
| - name: Run external-LDAP smoke | |
| run: tests/ldap-smoke/run.sh | |
| external-ldap-distributed-tests: | |
| name: external-ldap-distributed-smoke | |
| needs: [build-and-test] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0 | |
| with: | |
| node-version: "24" | |
| - name: Enable pnpm | |
| run: corepack enable && corepack prepare pnpm@10.28.1 --activate | |
| # web/idp assets must be embedded for `ocis server` to come up (same as run-github.py) | |
| - name: Generate assets | |
| run: make ci-node-generate | |
| - name: Build oCIS | |
| run: make -C ocis build | |
| # Distributed LDAP topology (proxy over an async syncrepl replica + write master); | |
| # the replication lag exercises the retry / read-after-write path (OCISDEV-1030/1032/1033). | |
| - name: Run external-LDAP distributed smoke | |
| run: tests/ldap-smoke-distributed/run.sh | |
| acceptance-coverage: | |
| name: acceptance-coverage | |
| needs: [detect-changes, local-api-tests, cli-tests, ldap-pool-tests, core-api-tests] | |
| if: always() && github.event_name == 'pull_request' && needs.detect-changes.outputs.docs-only != 'true' | |
| runs-on: ubuntu-latest | |
| env: | |
| GOCOVERDIR: ${{ github.workspace }}/coverage-reports | |
| steps: | |
| - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 | |
| - uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 # v6.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Download raw coverage data | |
| uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| pattern: api-coverage-* | |
| path: ${{ env.GOCOVERDIR }} | |
| merge-multiple: true | |
| - name: Convert coverage counters to profile | |
| run: go tool covdata textfmt -i=${{ env.GOCOVERDIR }} -o=coverage.out | |
| - name: Publish coverage summary | |
| run: | | |
| { | |
| echo "## Acceptance test coverage" | |
| echo '```' | |
| go tool covdata percent -i=${{ env.GOCOVERDIR }} | |
| echo '```' | |
| echo | |
| echo "<details>" | |
| echo "<summary>Per-function coverage</summary>" | |
| echo | |
| echo '```' | |
| go tool cover -func=coverage.out | |
| echo '```' | |
| echo "</details>" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| all-acceptance-tests: | |
| needs: [detect-changes, local-api-tests, cli-tests, ldap-pool-tests, core-api-tests, litmus, cs3api, wopi-builtin, wopi-cs3, e2e-tests, external-ldap-tests, external-ldap-distributed-tests] | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: always() | |
| steps: | |
| - name: Check all jobs passed | |
| run: | | |
| # If docs-only, all test jobs are expected to be skipped — that's a pass | |
| if [[ "${{ needs.detect-changes.outputs.docs-only }}" == "true" ]]; then | |
| echo "Docs-only change — skipping tests is expected." | |
| exit 0 | |
| fi | |
| if [[ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]]; then | |
| exit 1 | |
| fi |