Skip to content

LinkAclTest: invalidate resources_acl_bo's static cache after every s… #833

LinkAclTest: invalidate resources_acl_bo's static cache after every s…

LinkAclTest: invalidate resources_acl_bo's static cache after every s… #833

Workflow file for this run

name: EGroupware Testing
on:
pull_request:
push:
branches:
- master
workflow_dispatch:
jobs:
phpunit:
runs-on: ubuntu-24.04
timeout-minutes: 45
env:
EGW_DB_ROOT_PW: ciroot
COMPOSE_PROJECT_NAME: egwci
EGW_CI_ROOT: ${{ github.workspace }}/sources
EGW_CI_DOCKER_DIR: ${{ github.workspace }}/sources/doc/docker
EGW_SRC_DIR: /var/www
EGW_CONFIG_USER: ""
EGW_CONFIG_PASSWD: ""
SYSOP_USER: ""
SYSOP_PASS: ""
steps:
- uses: actions/checkout@v4
with:
submodules: true
path: sources
- name: Cache Composer downloads
uses: actions/cache@v4
with:
path: ~/.composer/cache
key: composer-cache-${{ runner.os }}-${{ hashFiles('sources/composer.lock') }}
restore-keys: |
composer-cache-${{ runner.os }}-
- name: Install PHP dependencies in sources
shell: bash
run: |
set -euo pipefail
COMPOSER_CACHE_DIR="${HOME}/.composer/cache"
docker run --rm --user "$(id -u):$(id -g)" \
-v "${COMPOSER_CACHE_DIR}":/tmp/composer-cache \
-v "${GITHUB_WORKSPACE}/sources":/app -w /app \
-e COMPOSER_CACHE_DIR=/tmp/composer-cache \
composer install --no-interaction --ignore-platform-reqs
php "${GITHUB_WORKSPACE}/sources/install-cli.php" --git-apps pull
- name: Prepare compose project layout
run: |
set -euo pipefail
mkdir -p "$GITHUB_WORKSPACE/sources/doc/docker/data" "$GITHUB_WORKSPACE/sources/doc/docker/sessions"
rm -f "$GITHUB_WORKSPACE/sources/doc/docker/data/header.inc.php"
- name: Start CI containers
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
$COMPOSE up -d --force-recreate db egroupware nginx
- name: Wait for install
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
MAX_WAIT=60
WAIT_INTERVAL=5
ELAPSED=0
SUCCESS_MSG="EGroupware successful installed"
echo "Waiting for database to be ready..."
until $COMPOSE exec -T db mariadb-admin ping -h "db" --silent --user=root --password="$EGW_DB_ROOT_PW"; do
if [ "$ELAPSED" -ge "$MAX_WAIT" ]; then
echo "::error::Timed out waiting for EGroupware installation"
echo "==== egroupware logs ===="
$COMPOSE logs egroupware || true
echo "==== database logs ===="
$COMPOSE logs db || true
exit 1
fi
echo " ... waiting for DB"
sleep "$WAIT_INTERVAL"
ELAPSED=$((ELAPSED + WAIT_INTERVAL))
done
echo "Waiting for EGroupware installation log message..."
INSTALL_LOG="$(mktemp)"
until $COMPOSE logs egroupware > "$INSTALL_LOG" && grep -q "$SUCCESS_MSG" "$INSTALL_LOG"; do
if [ "$ELAPSED" -ge "$MAX_WAIT" ]; then
echo "::error::Timed out waiting for EGroupware installation"
echo "==== egroupware logs ===="
$COMPOSE logs egroupware || true
echo "==== database logs ===="
$COMPOSE logs db || true
exit 1
fi
sleep "$WAIT_INTERVAL"
ELAPSED=$((ELAPSED + WAIT_INTERVAL))
echo " ... still waiting (${ELAPSED}s)"
done
$COMPOSE exec -T egroupware ln -sf /var/lib/egroupware/header.inc.php "$EGW_SRC_DIR/header.inc.php"
- name: Extract install credentials
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
INSTALL_LOG="$(mktemp)"
$COMPOSE logs egroupware > "$INSTALL_LOG"
# Get sysop admin user from install log
SYSOP_USER="sysop"
SYSOP_PASS="$(
awk '
/EGroupware username: sysop/ {found=1; next}
found && /password:/ {
sub(/^[^:]*:[[:space:]]*/, "", $0)
print
exit
}
' "$INSTALL_LOG"
)"
# Get setup user/password from install log.
CONFIG_USER="$(
awk '
/Setup username:/ {sub(/.*Setup username:[[:space:]]*/, "", $0); print; exit}
' "$INSTALL_LOG"
)"
CONFIG_PASS="$(
awk '
/Setup username:/ {found=1; next}
found && /password:/ {
sub(/^[^:]*:[[:space:]]*/, "", $0)
print
exit
}
' "$INSTALL_LOG"
)"
for v in SYSOP_USER SYSOP_PASS CONFIG_USER CONFIG_PASS; do
if [ -z "${!v}" ]; then
echo "::error::Missing $v"
exit 1
fi
done
{
echo "SYSOP_USER=$SYSOP_USER"
echo "SYSOP_PASS=$SYSOP_PASS"
echo "EGW_CONFIG_USER=$CONFIG_USER"
echo "EGW_CONFIG_PASSWD=$CONFIG_PASS"
} >> "$GITHUB_ENV"
- name: Debug admin-cli path mapping
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
echo "== Host checks =="
pwd
ls -la "$GITHUB_WORKSPACE/sources/admin/admin-cli.php" || true
ls -la "$GITHUB_WORKSPACE/sources/admin" | sed -n '1,80p' || true
echo "== Compose resolved mounts (egroupware service) =="
docker compose --project-directory "$GITHUB_WORKSPACE/sources" \
-f "$GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml" \
-f "$GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml" \
config | sed -n '/egroupware:/,/db:/p'
echo "== Container checks =="
$COMPOSE exec -T egroupware sh -lc 'pwd; ls -la /var/www | sed -n "1,80p"; ls -la /var/www/admin | sed -n "1,80p" || true; ls -la /var/www/egroupware/admin | sed -n "1,80p" || true'
$COMPOSE exec -T egroupware sh -lc 'test -f /var/www/admin/admin-cli.php && echo "admin-cli in /var/www" || true; test -f /var/www/egroupware/admin/admin-cli.php && echo "admin-cli in /var/www/egroupware" || true'
- name: Detect app root in container
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
APP_DIR="$(
$COMPOSE exec -T egroupware sh -lc '
if [ -f /var/www/admin/admin-cli.php ]; then
echo /var/www
elif [ -f /var/www/egroupware/admin/admin-cli.php ]; then
echo /var/www/egroupware
else
exit 1
fi
'
)"
echo "EGW_SRC_DIR=$APP_DIR" >> "$GITHUB_ENV"
echo "Detected EGW_SRC_DIR=$APP_DIR"
- name: Configure for phpunit test (apps, user)
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
# Get test user from phpunit.xml
EGW_TEST_USER="$(
sed -n 's/.*<var name="EGW_USER" value="\([^"]*\)".*/\1/p' "$GITHUB_WORKSPACE/sources/doc/phpunit.xml"
)"
EGW_TEST_PASS="$(
sed -n 's/.*<var name="EGW_PASSWORD" value="\([^"]*\)".*/\1/p' "$GITHUB_WORKSPACE/sources/doc/phpunit.xml"
)"
for v in SYSOP_USER SYSOP_PASS EGW_TEST_USER EGW_TEST_PASS; do
if [ -z "${!v}" ]; then
echo "::error::Missing $v"
exit 1
fi
done
{
echo "SYSOP_USER=$SYSOP_USER"
echo "SYSOP_PASS=$SYSOP_PASS"
} >> "$GITHUB_ENV"
$COMPOSE exec -T -w "$EGW_SRC_DIR" egroupware php admin/admin-cli.php \
--edit-user "$SYSOP_USER,$SYSOP_PASS,$EGW_TEST_USER,Test,User,$EGW_TEST_PASS,$EGW_TEST_USER@example.invalid"
$COMPOSE exec -T -w "$EGW_SRC_DIR" egroupware php admin/admin-cli.php \
--allow-app "$SYSOP_USER,$SYSOP_PASS,$EGW_TEST_USER,projectmanager"
# Ensure share links resolve locally in CI
$COMPOSE exec -T -w "$EGW_SRC_DIR" egroupware php setup/setup-cli.php \
--config default,"$EGW_CONFIG_USER","$EGW_CONFIG_PASSWD",hostname=localhost,webserver_url=/egroupware
- name: Debug web access
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
docker compose exec nginx nginx -T || true
docker compose exec nginx ss -lntp || true
$COMPOSE exec -T egroupware sh -lc '
which curl || apt-get update && apt-get install -y curl
echo "Testing 'nginx' URL:"
curl -I -v http://nginx/ || true
echo
echo "Listening ports:"
ss -lntp || netstat -lntp || true
'
- name: Install fixture test app
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
# Ensure fixture test app is linked and installed before tests
$COMPOSE exec -T \
-e EGW_CONFIG_USER="$EGW_CONFIG_USER" \
-e EGW_CONFIG_PASSWD="$EGW_CONFIG_PASSWD" \
-w "$EGW_SRC_DIR" egroupware sh -lc '
if [ -d api/tests/fixtures/apps/test ]; then
ln -sfn api/tests/fixtures/apps/test test
set +e
php setup/setup-cli.php --update default,$EGW_CONFIG_USER,$EGW_CONFIG_PASSWD,no,test
rc=$?
set -e
echo "setup-cli exit code: $rc"
fi
'
if ! $COMPOSE exec -T db mariadb -u root -p"$EGW_DB_ROOT_PW" -D "egroupware" -e "SHOW TABLES LIKE 'egw_test%';" | grep -q "egw_test"; then
echo "egw_test table not found after test app install, some tests will be skipped"
fi
- name: Install webauthn app
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
# webauthn is a newer first-party app (composer.json require egroupware/webauthn); the CI
# image's baked-in install predates it, so its schema (egw_webauthn_pubkeys) was never
# created - install it explicitly, same as the "test" fixture app above.
#
# header.inc.php still points EGW_SERVER_ROOT at the image's baked-in /usr/share/egroupware
# at this point in the pipeline (that only gets patched to $EGW_SRC_DIR later, in "Run
# PHPUnit") - without this, setup-cli.php scans the old baked-in app list, never sees
# webauthn as new, and silently no-ops with "No update necessary". Patch it here too
# (idempotent, harmless if "Run PHPUnit" repeats it) so the scan sees the real checkout.
$COMPOSE exec -T egroupware sed -i "s#/usr/share/egroupware#$EGW_SRC_DIR#g" /var/lib/egroupware/header.inc.php
$COMPOSE exec -T \
-e EGW_CONFIG_USER="$EGW_CONFIG_USER" \
-e EGW_CONFIG_PASSWD="$EGW_CONFIG_PASSWD" \
-w "$EGW_SRC_DIR" egroupware sh -lc '
set +e
php setup/setup-cli.php --update default,$EGW_CONFIG_USER,$EGW_CONFIG_PASSWD,no,webauthn
rc=$?
set -e
echo "setup-cli exit code: $rc"
'
if ! $COMPOSE exec -T db mariadb -u root -p"$EGW_DB_ROOT_PW" -D "egroupware" -e "SHOW TABLES LIKE 'egw_webauthn_pubkeys';" | grep -q "egw_webauthn_pubkeys"; then
echo "::error::egw_webauthn_pubkeys table not found after webauthn app install"
exit 1
fi
- name: Disable swoolepush for CI tests
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
$COMPOSE exec -T db mariadb -u root -p"$EGW_DB_ROOT_PW" -D "egroupware" -e \
"UPDATE egw_applications SET app_enabled=-1 WHERE app_name='swoolepush';"
$COMPOSE exec -T -w "$EGW_SRC_DIR" egroupware php -r '
require "api/src/loader/common.php";
\EGroupware\Api\Cache::unsetInstance(\EGroupware\Api\Egw\Applications::class, "apps");
\EGroupware\Api\Cache::unsetInstance(\EGroupware\Api\Hooks::class, "locations");
'
- name: Run PHPUnit
timeout-minutes: 30
run: |
set -euo pipefail
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
EGW_CONFIG_USER="${EGW_CONFIG_USER:-}"
EGW_CONFIG_PASSWD="${EGW_CONFIG_PASSWD:-}"
for v in EGW_CONFIG_USER EGW_CONFIG_PASSWD; do
if [ -z "${!v}" ]; then
echo "::error::Missing $v"
exit 1
fi
done
# header.inc.php is generated against the image's own baked-in /usr/share/egroupware
# install (rsynced from the image at container start), but the "Run PHPUnit" step below
# serves the actual checked-out branch from $EGW_SRC_DIR instead - patch the mismatched
# EGW_SERVER_ROOT so the app-specific autoloader (api/src/autoload.php, which resolves
# classes via EGW_INCLUDE_ROOT rather than composer's own correctly-resolved base dir)
# looks in the right place. Done inside the container (not via the host bind-mount path)
# since the file is owned by the container's own user, not the Actions runner.
$COMPOSE exec -T egroupware \
sed -i "s#/usr/share/egroupware#$EGW_SRC_DIR#g" /var/lib/egroupware/header.inc.php
# Run tests with local HTTP endpoint inside egroupware container
$COMPOSE exec -T \
-e HTTP_HOST=127.0.0.1:8081 \
-e HTTP_X_FORWARDED_PROTO=http \
-e EGW_URL=http://127.0.0.1:8081 \
-e EGW_CONFIG_USER="$EGW_CONFIG_USER" \
-e EGW_CONFIG_PASSWD="$EGW_CONFIG_PASSWD" \
-w "$EGW_SRC_DIR" egroupware \
bash -lc '
set -euo pipefail
php -S 127.0.0.1:8081 -t "$PWD" "$PWD/.github/ci-router.php" > /tmp/egw-phpunit-http.log 2>&1 &
server_pid=$!
trap "kill $server_pid 2>/dev/null || true" EXIT
for i in $(seq 1 20); do
php -r '\''$fp=@fsockopen("127.0.0.1",8081,$errno,$errstr,0.2); if($fp){fclose($fp); exit(0);} exit(1);'\'' && break
sleep 1
done
set +e
php vendor/bin/phpunit --configuration doc/phpunit.xml
rc=$?
set -e
echo "=== php built-in server log (last 300 lines) ==="
tail -n 300 /tmp/egw-phpunit-http.log || true
exit $rc
'
- name: Tear down
if: always()
run: |
COMPOSE="docker compose --project-directory $GITHUB_WORKSPACE/sources \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.yml \
-f $GITHUB_WORKSPACE/sources/doc/docker/docker-compose.ci.yml"
$COMPOSE down -v --remove-orphans
jstest:
runs-on: ubuntu-24.04
timeout-minutes: 10
env:
EGW_DB_ROOT_PW: ciroot
COMPOSE_PROJECT_NAME: egwci
EGW_CI_ROOT: ${{ github.workspace }}/sources
EGW_CI_DOCKER_DIR: ${{ github.workspace }}/sources/doc/docker
EGW_SRC_DIR: /var/www
EGW_CONFIG_USER: ""
EGW_CONFIG_PASSWD: ""
SYSOP_USER: ""
SYSOP_PASS: ""
defaults:
run:
shell: bash
working-directory: sources
steps:
- uses: actions/checkout@v4
with:
submodules: true
path: sources
- name: Cache Composer downloads
uses: actions/cache@v4
with:
path: ~/.composer/cache
key: composer-cache-${{ runner.os }}-${{ hashFiles('sources/composer.lock') }}
restore-keys: |
composer-cache-${{ runner.os }}-
- name: Install PHP dependencies in sources
shell: bash
run: |
set -euo pipefail
COMPOSER_CACHE_DIR="${HOME}/.composer/cache"
docker run --rm --user "$(id -u):$(id -g)" \
-v "${COMPOSER_CACHE_DIR}":/tmp/composer-cache \
-v "${GITHUB_WORKSPACE}/sources":/app -w /app \
-e COMPOSER_CACHE_DIR=/tmp/composer-cache \
composer install --no-interaction --ignore-platform-reqs
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: sources/package-lock.json
- name: Install npm dependencies
run: npm ci
- name: Install Playwright system dependencies
run: npx playwright install-deps chromium firefox
- name: Install Playwright Chromium
timeout-minutes: 10
env:
DEBUG: pw:install
run: npx playwright install chromium
- name: Install Playwright Firefox
timeout-minutes: 10
env:
DEBUG: pw:install
run: npx playwright install firefox
- name: Run jstest
run: npm run jstest