Skip to content

fix: [stable-8.2] harden nats communication #3118

fix: [stable-8.2] harden nats communication

fix: [stable-8.2] harden nats communication #3118

name: Acceptance Tests
on:
pull_request:
workflow_dispatch:
jobs:
detect-changes:
name: detect-changes
runs-on: ubuntu-latest
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
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
PR_TITLE="${{ github.event.pull_request.title }}"
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
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@57a97c7e7821a5776cebc9bb87c984fa69cba8f1 # v0.35.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 > /tmp/ocis-build.log 2>&1 & PIDS+=($!)
make test > /tmp/unit-tests.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
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
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: BEHAT_SUITES=${{ matrix.suite }} python3 tests/acceptance/run-github.py
cli-tests:
needs: [build-and-test]
name: ${{ matrix.suite }}
runs-on: ubuntu-latest
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: BEHAT_SUITES="${{ matrix.suite }}" python3 tests/acceptance/run-github.py
ldap-pool-tests:
needs: [build-and-test]
name: ldapPool-${{ matrix.suite }}
runs-on: ubuntu-latest
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: BEHAT_SUITES="${{ matrix.suite }}" OCIS_LDAP_POOL_ENABLED=true python3 tests/acceptance/run-github.py
core-api-tests:
name: ${{ matrix.suite }}
needs: [build-and-test]
runs-on: ubuntu-latest
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: >
BEHAT_SUITES="${{ matrix.suite }}"
ACCEPTANCE_TEST_TYPE=core-api
WITH_REMOTE_PHP=true
python3 tests/acceptance/run-github.py
e2e-tests:
name: e2e-${{ matrix.suite }}
needs: [build-and-test]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- suite: part-1
args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc --run-part 1"
- suite: part-2
args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc --run-part 2"
- suite: part-3
args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc --run-part 3"
- suite: part-4
args: "--type playwright --total-parts 4 --xsuites search,app-provider,ocm,keycloak,mfa,oidc --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
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
run: |
mkdir -p keycloak-certs
openssl req -x509 -newkey rsa:2048 \
-keyout keycloak-certs/keycloakkey.pem \
-out keycloak-certs/keycloakcrt.pem \
-nodes -days 365 -subj '/CN=keycloak' \
-addext 'subjectAltName=DNS:localhost,IP:127.0.0.1'
chmod -R 777 keycloak-certs
- name: Start Postgres
if: matrix.keycloak == true || matrix.mfa == true
run: |
docker run -d --name postgres --network host \
-e POSTGRES_DB=keycloak \
-e POSTGRES_USER=keycloak \
-e POSTGRES_PASSWORD=keycloak \
postgres:alpine3.18
timeout 30 bash -c 'until docker exec postgres pg_isready -U keycloak; do sleep 1; done'
- name: Start Keycloak
if: matrix.keycloak == true || matrix.mfa == true
run: |
# The mfa suite needs a different realm with TOTP MFA configured.
# ocis-ci-realm uses ocis-server:9200, ocis-mfa-ci-realm uses localhost:9200;
# ocis runs on 127.0.0.1:9200 so we rewrite both forms.
if [[ "${{ matrix.mfa }}" == "true" ]]; then
REALM_FILE=tests/config/ci/ocis-mfa-ci-realm.dist.json
else
REALM_FILE=tests/config/ci/ocis-ci-realm.dist.json
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' }}
- 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
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
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
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
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
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
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
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
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