Source: .gitlab/generate-appsec.php — generates the appsec-trigger
child pipeline; all job script: sections are defined inline in this
file.
| CI Job | Image | What it does |
|---|---|---|
appsec integration tests: [{target}] |
docker:29.4.0-noble |
Gradle integration tests with C++ helper (release/zts variants) |
appsec integration tests (helper-rust): [{target}] |
same | Same tests with Rust helper (-PuseHelperRust); includes debug variant |
appsec integration tests (ssi): [{target}] |
same | SSI mode (PHP 8.3 only) |
helper-rust build and test |
same | cargo fmt --check + build + unit tests |
helper-rust code coverage |
same | Unit test coverage via cargo-llvm-cov |
helper-rust integration coverage |
same | Integration coverage collection (not needed locally) |
Runner: docker-in-docker:amd64
Matrix: PHP 7.0+ × release/debug/zts/musl/ssi (varies by job group)
Important: testX.Y-debug are not gradle targets that are run on CI. They
may, however, be useful for debugging.
CI passes TERM=dumb and --scan -Pbuildscan to Gradle. TERM=dumb
suppresses progress animations in CI logs; both flags are optional
locally.
- JDK 17+. If not available via your system package manager, use SDKMAN
(
sdk install java 17) or download from https://download.oracle.com/java/17/archive/. - Docker daemon running
All ./gradlew commands run from:
appsec/tests/integration/
./gradlew test8.3-debug --info./gradlew test8.3-debug --info \
--tests "com.datadog.appsec.php.integration.Apache2FpmTests.Pool environment"The --tests filter accepts:
- Full method:
"com.datadog.appsec.php.integration.Apache2FpmTests.Pool environment" - Class only:
"*Apache2FpmTests*"or"com.datadog.appsec.php.integration.Apache2FpmTests" - Wildcard:
"*FpmTests*"
./gradlew test8.3-debug -PuseHelperRust --info \
--tests "com.datadog.appsec.php.integration.Apache2FpmTests.Pool environment"This builds the Rust helper via the buildHelperRust task (musl build, works on both glibc and musl targets) and stores the binary in the php-helper-rust Docker volume.
Omit -PuseHelperRust and -PhelperBinary. The C++ helper is built via the buildAppsec-* task.
By default, Gradle resolves Docker images via pinned SHA256 digests in gradle/tag_mappings.gradle. To use floating tags (locally-built images or latest from Docker Hub):
./gradlew test8.3-debug -PfloatingImageTags --infoPattern: test{version}-{variant}
| Variant | Notes |
|---|---|
release |
Standard build |
debug |
Debug build (assertions enabled) |
release-zts |
Thread-safe build |
release-musl |
Alpine/musl (only 8.5-release-musl) |
release-ssi / debug-ssi |
SSI mode (only PHP 8.3) |
Full list: ./gradlew tasks --group=Verification
| Task | Description |
|---|---|
buildHelperRust |
Build helper-rust with musl (universal binary). Output in php-helper-rust volume. |
testHelperRust |
cargo fmt --check + cargo build --release + cargo test --release (runs inside php-deps image) |
coverageHelperRust |
Unit test coverage via cargo-llvm-cov. Output: php-helper-rust-coverage volume. |
buildHelperRustWithCoverage |
Build with -C instrument-coverage for integration coverage collection. |
generateHelperRustIntegrationCoverage |
Merge .profraw files into lcov after integration run. |
| Task | Description |
|---|---|
buildTracer-{v}-{var} |
Build ddtrace.so for given PHP version/variant |
buildAppsec-{v}-{var} |
Build ddappsec.so (C++ extension + helper) |
buildHelperRust |
Build Rust helper (musl, universal) |
buildLibddwaf |
Build libddwaf shared library |
| Task | Description |
|---|---|
loadCaches |
Restore Docker volume caches from tarball |
saveCaches |
Save Docker volume caches to tarball |
clean |
Delete build directory and clean Docker volumes |
check |
Run all test tasks |
All tasks: ./gradlew tasks --all
Start a test container without running tests (for manual debugging):
./gradlew runMain8.3-release -PtestClass=com.datadog.appsec.php.integration.Apache2FpmTestsThe -PtestClass property is required (the task is not created without it). Add -PuseHelperRust or -PhelperBinary=... as needed.
SSI variant:
./gradlew runMain8.3-release-ssi -PtestClass=com.datadog.appsec.php.integration.Apache2FpmTestsAfter a test run, logs are in:
build/test-logs/<TestClassName>-<version>-<variant>/
For example:
build/test-logs/com.datadog.appsec.php.integration.Apache2FpmTests-8.3-debug/
├── access.log
├── appsec.log # PHP extension appsec log
├── error.log # Apache error log
├── helper.log # Helper process log (C++ or Rust)
├── php_error.log
├── php_fpm_error.log
└── sidecar.log
To distinguish which helper ran, check helper.log:
- Rust: starts with
[INFO] AppSec helper starting - C++: starts with
[info]lines likeStarted listening on abstract socket
The test8.5-release-musl target uses an Alpine-based nginx+fpm image. Tests tagged with @Tag("musl") are included; untagged tests are excluded.
./gradlew test8.5-release-musl -PuseHelperRust --infoThe buildHelperRust task already produces a musl-linked binary (built on Alpine with cargo +nightly, using LLVM libunwind). The patchelf --remove-needed libc.musl-* step makes it load on both musl and glibc systems.
| CI Job | Gradle Command |
|---|---|
appsec integration tests: [test8.3-release] |
./gradlew test8.3-release |
appsec integration tests (helper-rust): [test8.3-debug] |
./gradlew test8.3-debug -PuseHelperRust |
appsec integration tests (ssi): [test8.3-release-ssi] |
./gradlew test8.3-release-ssi |
helper-rust build and test |
./gradlew testHelperRust |
helper-rust code coverage |
./gradlew coverageHelperRust |
helper-rust integration coverage |
./gradlew buildHelperRustWithCoverage then integration test with -PuseHelperRustCoverage |
CI also passes --scan -Pbuildscan for Gradle build scans, which is optional locally.
Gradle uses named Docker volumes for build artifacts and caches. Key volumes:
| Volume | Contents |
|---|---|
php-helper-rust |
libddappsec-helper.so (Rust helper binary) |
php-tracer-{v}-{var} |
Built ddtrace.so |
php-appsec-{v}-{var} |
Built ddappsec.so + C++ helper |
php-tracer-cargo-cache |
Cargo registry cache |
php-tracer-cargo-cache-git |
Cargo git cache |
php-appsec-boost-cache |
Boost build cache |
php-helper-rust-coverage |
Coverage-instrumented binary + profraw files |
To force a rebuild, remove the relevant volume:
docker volume rm php-helper-rustTo clean everything:
./gradlew cleanAttach a Java debugger to the test runner:
./gradlew test8.3-debug --tests "*Apache2FpmTests*" --debug-jvmEnable PHP Xdebug in the test container:
./gradlew test8.3-debug --tests "*Apache2FpmTests*" -PXDEBUG=1A significant number of tests are skipped on any given target — this is
normal. Skip conditions are @EnabledIf guards in the test classes:
| Test class | Skips on | Reason |
|---|---|---|
FrankenphpClassicTests, FrankenphpWorkerTests |
anything except 8.4-zts |
Requires ZTS + PHP 8.4 |
Laravel8xTests |
anything except 7.4 (NTS) |
Requires PHP 7.4 non-ZTS |
Symfony62Tests |
anything except 8.1 (NTS) |
Requires PHP 8.1 non-ZTS |
RaspSqliTests |
no MySQL service | Requires a running MySQL |
SsiStableConfigTests |
non-SSI variants | Requires -DSSI=true |
On test8.3-debug expect ~67 skips out of ~300 tests; all are expected.
After a run, the HTML report is at:
appsec/tests/integration/build/reports/tests/test8.3-debug/index.html
(Replace test8.3-debug with your target.) Open in a browser for a
structured pass/fail/skip breakdown.
Run gradle with --debug-jvm. This will stop for the debugger, indicating so in the output.
When you see the message, start jdb in a tmux session.
If you need to inspect sidecar/helper or PHP issues:
- Put a breakpoint in jdb that stops the test in the appropriate place (usually just before a request is executed).
- Inside the test container (determine first its id), attach gdb to sidecar
(
pref -f dd-ipc-helper) or to PHP (usually an apache or an FPM worker -- if you're investigating code run during processes it will not be the master process). sidecar requires as a first commandfile /proc/<pid>/exe). - See gdb.md for more information on how to run gdb. Always read this file before attempting to use gdb.
- The
testtask itself is disabled (tasks['test'].enabled = false). Use versioned tasks liketest8.3-debug. - Docker images are pulled from
docker.io/datadog/dd-appsec-php-ci. Without-PfloatingImageTags, images are resolved by SHA256 digest fromgradle/tag_mappings.gradle. If a digest is not locally available, Docker will pull it. - The
buildHelperRusttask uses thenginx-fpm-php-8.5-release-muslimage (Alpine with Rust nightly). This image must be available locally or pullable. - On first run, Gradle downloads its wrapper, dependencies, and Docker images. Expect 5-10 minutes. Subsequent runs with warm caches take ~20-50 seconds for a single test.
- c-ares DNS failure in Alpine containers. Alpine's
curlandgituse c-ares for DNS, which fails to resolve hosts when the DNS server includes EDNS COOKIE options in responses (common with home routers).wgetandgetentare unaffected (they use musl's native resolver). This breaks thebuildAppsec-*-musltask which needs togit clonecmake dependencies. Fix: pass--dns 8.8.8.8to the Docker command. --infois recommended for seeing test output in the console. Without it, output goes only to the HTML report inbuild/reports/tests/.