From d0c0c2610c46f7412945ddbd30ea2b070a1db89b Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Fri, 13 Nov 2026 16:34:27 +0000 Subject: [PATCH 1/3] Prepare fixed-minor read-only framework callers and closed coverage guards --- .github/workflows/ci.yml | 370 ++++++++++++++++++++++-- scripts/ci/run-php-container.sh | 1 + tests/LaravelAdoptionContractTest.php | 32 +- tests/ci/test_php_container_launcher.py | 2 + 4 files changed, 375 insertions(+), 30 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9a1f884..f8c184c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -324,7 +324,8 @@ jobs: framework-compat: name: ${{ matrix.framework }} ${{ matrix.version }} runs-on: ubuntu-24.04 - timeout-minutes: 10 + timeout-minutes: 15 + needs: action-policy strategy: fail-fast: false matrix: @@ -336,6 +337,320 @@ jobs: exercised_package: 'laravel/framework:12.69.0' bootstrap: fresh_supported_install script: laravel.php + - framework: Symfony + version: '7' + php: '8.2' + package: 'symfony/framework-bundle:^7.0' + console_package: 'symfony/console:^7.0' + script: symfony.php + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - name: Check out the fixed APT prerequisite + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + repository: durable-workflow/.github + ref: 18783be6bef737cc778e4c7282a8797e39daa61a + path: .central-apt-source + persist-credentials: false + - name: Select Ubuntu's Python 3.12 + run: | + /usr/bin/python3 -I -S -c 'import sys; assert sys.version_info[:2] == (3, 12)' + echo /usr/bin >> "$GITHUB_PATH" + - name: Select authenticated dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + with: + clients: pip,python,composer,curl,git + - name: Disable APT download retries (CVE-2026-109518) + run: echo 'Acquire::Retries "0";' | sudo tee /etc/apt/apt.conf.d/99-no-retries >/dev/null + - name: Prepare the reviewed PHP container runtime + run: bash "$GITHUB_WORKSPACE/scripts/ci/setup-php-container.sh" "8.2" + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Install SDK into a clean framework consumer + env: + FRAMEWORK_BOOTSTRAP: ${{ matrix.bootstrap }} + FRAMEWORK_PACKAGE: ${{ matrix.package }} + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + FRAMEWORK_CONSOLE_PACKAGE: ${{ matrix.console_package }} + COMPAT_SCRIPT: ${{ matrix.script }} + run: | + consumer="$(mktemp -d)" + printf '%s\n' "$consumer" > "$RUNNER_TEMP/framework-consumer" + cd "$consumer" + composer init --name=durable-workflow/framework-compat --no-interaction + composer config repositories.sdk path "$GITHUB_WORKSPACE" + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + bootstrap_arguments=() + if [ "$FRAMEWORK_BOOTSTRAP" = existing_supported_lock ]; then + # Model the established application named by the adoption contract. + # Composer may refuse a new installation of an end-of-life Laravel + # line, but SDK installation still runs with normal policy against + # the resulting existing lock file. + bootstrap_arguments+=(--no-blocking) + fi + # matrix.package is the published caret support range. Install + # exactly its minimum release, and prove the SDK does not move it. + exercised_version="${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + composer require "$FRAMEWORK_EXERCISED_PACKAGE" \ + --no-interaction --prefer-dist --no-progress "${bootstrap_arguments[@]}" + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + composer require durable-workflow/sdk:@dev \ + --no-interaction --prefer-dist --no-progress + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + else + framework_packages=("$FRAMEWORK_PACKAGE") + if [ -n "$FRAMEWORK_CONSOLE_PACKAGE" ]; then + framework_packages+=("$FRAMEWORK_CONSOLE_PACKAGE") + fi + composer require durable-workflow/sdk:@dev "${framework_packages[@]}" \ + --no-interaction --prefer-dist --with-all-dependencies + fi + composer require --dev phpstan/phpstan:^2.0 'monolog/monolog:^2.9|^3.0' --no-interaction --prefer-dist + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + fi + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Install SDK into a fresh Laravel application for cached role isolation + if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' + env: + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + run: | + application="$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" + exercised_version="${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + composer create-project laravel/laravel:^13.0 "$application" \ + --no-interaction --prefer-dist --no-progress + composer --working-dir="$application" require "$FRAMEWORK_EXERCISED_PACKAGE" \ + --with-all-dependencies --no-interaction --prefer-dist --no-progress + php scripts/ci/assert-installed-version.php "$application" laravel/framework "$exercised_version" + composer --working-dir="$application" config repositories.sdk path "$GITHUB_WORKSPACE" + composer --working-dir="$application" require durable-workflow/sdk:@dev \ + --no-interaction --prefer-dist --no-progress + php scripts/ci/assert-installed-version.php "$application" laravel/framework "$exercised_version" + - name: Exercise fresh Laravel cached role isolation + if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' + run: | + bash tests/compat/laravel-fresh-config-cache.sh "$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" + - name: Exercise native container, diagnostics, and testing integration + env: + COMPAT_SCRIPT: ${{ matrix.script }} + run: | + consumer="$(cat "$RUNNER_TEMP/framework-consumer")" + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + DURABLE_WORKFLOW_CLIENT_TOKEN=client-secret \ + DURABLE_WORKFLOW_WORKER_TOKEN=worker-secret \ + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + php tests/compat/laravel-config-cache.php "$consumer/vendor/autoload.php" + bridge_path=src/Bridge/Laravel + compatibility_scripts=( + "tests/compat/${COMPAT_SCRIPT}" + tests/compat/laravel-config-cache.php + tests/compat/laravel-fresh-config-cache.php + tests/compat/laravel-fresh-role-launch.php + ) + else + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + bridge_path=src/Bridge/Symfony + compatibility_scripts=("tests/compat/${COMPAT_SCRIPT}") + fi + compatibility_scripts+=(tests/compat/history-cache.php tests/compat/execution-telemetry.php) + php tests/compat/static-credentials.php "$consumer/vendor/autoload.php" "$COMPAT_SCRIPT" + php "$consumer/vendor/bin/phpstan" analyse -c phpstan-framework.neon \ + --autoload-file="$consumer/vendor/autoload.php" \ + src/Bridge/FrameworkRuntimeException.php \ + src/Bridge/ServiceConfiguration.php src/Bridge/Event \ + "$bridge_path" "${compatibility_scripts[@]}" + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Exercise optional tracing in framework workers + if: >- + (matrix.framework == 'Laravel' && matrix.version == '12' && matrix.php == '8.2') || + (matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4') || + (matrix.framework == 'Symfony' && matrix.version == '6.4') + env: + COMPAT_SCRIPT: ${{ matrix.script }} + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + DURABLE_WORKFLOW_TEST_TRACING: '1' + run: | + consumer="$(cat "$RUNNER_TEMP/framework-consumer")" + composer --working-dir="$consumer" require --dev open-telemetry/api:1.10.1 'monolog/monolog:^3.0' \ + --no-interaction --prefer-dist --no-progress + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + php scripts/ci/assert-installed-version.php "$consumer" laravel/framework \ + "${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + fi + DURABLE_WORKFLOW_CLIENT_TOKEN=client-secret \ + DURABLE_WORKFLOW_WORKER_TOKEN=worker-secret \ + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + + framework-compat81: + name: ${{ matrix.framework }} ${{ matrix.version }} + runs-on: ubuntu-24.04 + timeout-minutes: 15 + needs: action-policy + strategy: + fail-fast: false + matrix: + include: + - framework: Symfony + version: '6.4' + php: '8.1' + package: 'symfony/framework-bundle:^6.4' + console_package: 'symfony/console:^6.4' + script: symfony.php + steps: + - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - name: Check out the fixed APT prerequisite + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + repository: durable-workflow/.github + ref: 18783be6bef737cc778e4c7282a8797e39daa61a + path: .central-apt-source + persist-credentials: false + - name: Select Ubuntu's Python 3.12 + run: | + /usr/bin/python3 -I -S -c 'import sys; assert sys.version_info[:2] == (3, 12)' + echo /usr/bin >> "$GITHUB_PATH" + - name: Select authenticated dependency trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + with: + clients: pip,python,composer,curl,git + - name: Disable APT download retries (CVE-2026-109518) + run: echo 'Acquire::Retries "0";' | sudo tee /etc/apt/apt.conf.d/99-no-retries >/dev/null + - name: Prepare the reviewed PHP container runtime + run: bash "$GITHUB_WORKSPACE/scripts/ci/setup-php-container.sh" "8.1" + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Install SDK into a clean framework consumer + env: + FRAMEWORK_BOOTSTRAP: ${{ matrix.bootstrap }} + FRAMEWORK_PACKAGE: ${{ matrix.package }} + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + FRAMEWORK_CONSOLE_PACKAGE: ${{ matrix.console_package }} + COMPAT_SCRIPT: ${{ matrix.script }} + run: | + consumer="$(mktemp -d)" + printf '%s\n' "$consumer" > "$RUNNER_TEMP/framework-consumer" + cd "$consumer" + composer init --name=durable-workflow/framework-compat --no-interaction + composer config repositories.sdk path "$GITHUB_WORKSPACE" + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + bootstrap_arguments=() + if [ "$FRAMEWORK_BOOTSTRAP" = existing_supported_lock ]; then + # Model the established application named by the adoption contract. + # Composer may refuse a new installation of an end-of-life Laravel + # line, but SDK installation still runs with normal policy against + # the resulting existing lock file. + bootstrap_arguments+=(--no-blocking) + fi + # matrix.package is the published caret support range. Install + # exactly its minimum release, and prove the SDK does not move it. + exercised_version="${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + composer require "$FRAMEWORK_EXERCISED_PACKAGE" \ + --no-interaction --prefer-dist --no-progress "${bootstrap_arguments[@]}" + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + composer require durable-workflow/sdk:@dev \ + --no-interaction --prefer-dist --no-progress + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + else + framework_packages=("$FRAMEWORK_PACKAGE") + if [ -n "$FRAMEWORK_CONSOLE_PACKAGE" ]; then + framework_packages+=("$FRAMEWORK_CONSOLE_PACKAGE") + fi + composer require durable-workflow/sdk:@dev "${framework_packages[@]}" \ + --no-interaction --prefer-dist --with-all-dependencies + fi + composer require --dev phpstan/phpstan:^2.0 'monolog/monolog:^2.9|^3.0' --no-interaction --prefer-dist + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" + fi + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Install SDK into a fresh Laravel application for cached role isolation + if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' + env: + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + run: | + application="$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" + exercised_version="${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + composer create-project laravel/laravel:^13.0 "$application" \ + --no-interaction --prefer-dist --no-progress + composer --working-dir="$application" require "$FRAMEWORK_EXERCISED_PACKAGE" \ + --with-all-dependencies --no-interaction --prefer-dist --no-progress + php scripts/ci/assert-installed-version.php "$application" laravel/framework "$exercised_version" + composer --working-dir="$application" config repositories.sdk path "$GITHUB_WORKSPACE" + composer --working-dir="$application" require durable-workflow/sdk:@dev \ + --no-interaction --prefer-dist --no-progress + php scripts/ci/assert-installed-version.php "$application" laravel/framework "$exercised_version" + - name: Exercise fresh Laravel cached role isolation + if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' + run: | + bash tests/compat/laravel-fresh-config-cache.sh "$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" + - name: Exercise native container, diagnostics, and testing integration + env: + COMPAT_SCRIPT: ${{ matrix.script }} + run: | + consumer="$(cat "$RUNNER_TEMP/framework-consumer")" + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + DURABLE_WORKFLOW_CLIENT_TOKEN=client-secret \ + DURABLE_WORKFLOW_WORKER_TOKEN=worker-secret \ + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + php tests/compat/laravel-config-cache.php "$consumer/vendor/autoload.php" + bridge_path=src/Bridge/Laravel + compatibility_scripts=( + "tests/compat/${COMPAT_SCRIPT}" + tests/compat/laravel-config-cache.php + tests/compat/laravel-fresh-config-cache.php + tests/compat/laravel-fresh-role-launch.php + ) + else + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + bridge_path=src/Bridge/Symfony + compatibility_scripts=("tests/compat/${COMPAT_SCRIPT}") + fi + compatibility_scripts+=(tests/compat/history-cache.php tests/compat/execution-telemetry.php) + php tests/compat/static-credentials.php "$consumer/vendor/autoload.php" "$COMPAT_SCRIPT" + php "$consumer/vendor/bin/phpstan" analyse -c phpstan-framework.neon \ + --autoload-file="$consumer/vendor/autoload.php" \ + src/Bridge/FrameworkRuntimeException.php \ + src/Bridge/ServiceConfiguration.php src/Bridge/Event \ + "$bridge_path" "${compatibility_scripts[@]}" + - name: Verify Composer trust before requests + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust + - name: Exercise optional tracing in framework workers + if: >- + (matrix.framework == 'Laravel' && matrix.version == '12' && matrix.php == '8.2') || + (matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4') || + (matrix.framework == 'Symfony' && matrix.version == '6.4') + env: + COMPAT_SCRIPT: ${{ matrix.script }} + FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} + DURABLE_WORKFLOW_TEST_TRACING: '1' + run: | + consumer="$(cat "$RUNNER_TEMP/framework-consumer")" + composer --working-dir="$consumer" require --dev open-telemetry/api:1.10.1 'monolog/monolog:^3.0' \ + --no-interaction --prefer-dist --no-progress + if [ "$COMPAT_SCRIPT" = laravel.php ]; then + php scripts/ci/assert-installed-version.php "$consumer" laravel/framework \ + "${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" + fi + DURABLE_WORKFLOW_CLIENT_TOKEN=client-secret \ + DURABLE_WORKFLOW_WORKER_TOKEN=worker-secret \ + php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php" + + framework-compat84: + name: ${{ matrix.framework }} ${{ matrix.version }} + runs-on: ubuntu-24.04 + timeout-minutes: 15 + needs: action-policy + strategy: + fail-fast: false + matrix: + include: - framework: Laravel version: '12' php: '8.4' @@ -350,18 +665,6 @@ jobs: exercised_package: 'laravel/framework:13.30.0' bootstrap: fresh_supported_install script: laravel.php - - framework: Symfony - version: '6.4' - php: '8.1' - package: 'symfony/framework-bundle:^6.4' - console_package: 'symfony/console:^6.4' - script: symfony.php - - framework: Symfony - version: '7' - php: '8.2' - package: 'symfony/framework-bundle:^7.0' - console_package: 'symfony/console:^7.0' - script: symfony.php - framework: Symfony version: '8' php: '8.4' @@ -370,22 +673,29 @@ jobs: script: symfony.php steps: - uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 - - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6 with: - python-version: '3.12' + persist-credentials: false + - name: Check out the fixed APT prerequisite + uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + repository: durable-workflow/.github + ref: 18783be6bef737cc778e4c7282a8797e39daa61a + path: .central-apt-source + persist-credentials: false + - name: Select Ubuntu's Python 3.12 + run: | + /usr/bin/python3 -I -S -c 'import sys; assert sys.version_info[:2] == (3, 12)' + echo /usr/bin >> "$GITHUB_PATH" - name: Select authenticated dependency trust - uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + uses: durable-workflow/.github/.github/actions/bootstrap-patched-pip@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust with: clients: pip,python,composer,curl,git - name: Disable APT download retries (CVE-2026-109518) run: echo 'Acquire::Retries "0";' | sudo tee /etc/apt/apt.conf.d/99-no-retries >/dev/null - - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2 - with: - php-version: ${{ matrix.php }} - tools: composer:2.10.3 - coverage: none + - name: Prepare the reviewed PHP container runtime + run: bash "$GITHUB_WORKSPACE/scripts/ci/setup-php-container.sh" "8.4" - name: Verify Composer trust before requests - uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust - name: Install SDK into a clean framework consumer env: FRAMEWORK_BOOTSTRAP: ${{ matrix.bootstrap }} @@ -430,13 +740,13 @@ jobs: php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version" fi - name: Verify Composer trust before requests - uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust - name: Install SDK into a fresh Laravel application for cached role isolation if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' env: FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} run: | - application="$RUNNER_TEMP/laravel-fresh-config-cache" + application="$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" exercised_version="${FRAMEWORK_EXERCISED_PACKAGE#laravel/framework:}" composer create-project laravel/laravel:^13.0 "$application" \ --no-interaction --prefer-dist --no-progress @@ -450,7 +760,7 @@ jobs: - name: Exercise fresh Laravel cached role isolation if: matrix.framework == 'Laravel' && matrix.version == '13' && matrix.php == '8.4' run: | - bash tests/compat/laravel-fresh-config-cache.sh "$RUNNER_TEMP/laravel-fresh-config-cache" + bash tests/compat/laravel-fresh-config-cache.sh "$SDK_PHP_CI_STATE/data/laravel-fresh-config-cache" - name: Exercise native container, diagnostics, and testing integration env: COMPAT_SCRIPT: ${{ matrix.script }} @@ -475,13 +785,13 @@ jobs: fi compatibility_scripts+=(tests/compat/history-cache.php tests/compat/execution-telemetry.php) php tests/compat/static-credentials.php "$consumer/vendor/autoload.php" "$COMPAT_SCRIPT" - "$consumer/vendor/bin/phpstan" analyse -c phpstan-framework.neon \ + php "$consumer/vendor/bin/phpstan" analyse -c phpstan-framework.neon \ --autoload-file="$consumer/vendor/autoload.php" \ src/Bridge/FrameworkRuntimeException.php \ src/Bridge/ServiceConfiguration.php src/Bridge/Event \ "$bridge_path" "${compatibility_scripts[@]}" - name: Verify Composer trust before requests - uses: durable-workflow/.github/.github/actions/verify-composer-trust@75587fc33ff40c34f32ef777293dad56bb6f48c7 # clean-trust + uses: durable-workflow/.github/.github/actions/verify-composer-trust@242c2a7fb23459db2e1ada5f84dbee6d61d9b1e5 # clean-trust - name: Exercise optional tracing in framework workers if: >- (matrix.framework == 'Laravel' && matrix.version == '12' && matrix.php == '8.2') || @@ -1267,6 +1577,8 @@ jobs: - test-php83 - carbon-2 - framework-compat + - framework-compat81 + - framework-compat84 - framework-compat83 - laravel-transition-compat - analyse @@ -1289,6 +1601,8 @@ jobs: PHP_83_RESULT: ${{ needs.test-php83.result }} CARBON_2_RESULT: ${{ needs.carbon-2.result }} FRAMEWORK_RESULT: ${{ needs.framework-compat.result }} + FRAMEWORK_81_RESULT: ${{ needs.framework-compat81.result }} + FRAMEWORK_84_RESULT: ${{ needs.framework-compat84.result }} FRAMEWORK_83_RESULT: ${{ needs.framework-compat83.result }} LARAVEL_TRANSITION_RESULT: ${{ needs.laravel-transition-compat.result }} ANALYSIS_RESULT: ${{ needs.analyse.result }} @@ -1301,6 +1615,8 @@ jobs: test "$PHP_83_RESULT" = success test "$CARBON_2_RESULT" = success test "$FRAMEWORK_RESULT" = success + test "$FRAMEWORK_81_RESULT" = success + test "$FRAMEWORK_84_RESULT" = success test "$FRAMEWORK_83_RESULT" = success test "$LARAVEL_TRANSITION_RESULT" = success test "$ANALYSIS_RESULT" = success diff --git a/scripts/ci/run-php-container.sh b/scripts/ci/run-php-container.sh index 4d4013e6..4bff0f74 100755 --- a/scripts/ci/run-php-container.sh +++ b/scripts/ci/run-php-container.sh @@ -65,6 +65,7 @@ while IFS= read -r variable; do FRAMEWORK_BOOTSTRAP|FRAMEWORK_PACKAGE|FRAMEWORK_EXERCISED_PACKAGE|FRAMEWORK_CONSOLE_PACKAGE|COMPAT_SCRIPT|\ DURABLE_WORKFLOW_RUNTIME_URL|DURABLE_WORKFLOW_SERVER_URL|DURABLE_WORKFLOW_NAMESPACE|\ DURABLE_WORKFLOW_TASK_QUEUE|DURABLE_WORKFLOW_WORKER_ID|\ + DURABLE_WORKFLOW_TEST_TRACING|\ DURABLE_WORKFLOW_WORKER_TOKEN|DURABLE_WORKFLOW_CLIENT_TOKEN|\ DW_CI_CA_SHA256|GITHUB_ACTIONS|CI|TMPDIR|TZ) options+=(--env "$variable") ;; diff --git a/tests/LaravelAdoptionContractTest.php b/tests/LaravelAdoptionContractTest.php index ccd1da52..74bbc733 100644 --- a/tests/LaravelAdoptionContractTest.php +++ b/tests/LaravelAdoptionContractTest.php @@ -124,6 +124,14 @@ static function (string $workflow) use ($search, $replace): string { substr($workflow, $offset), ); }, 'The Laravel consumer must install exactly $FRAMEWORK_EXERCISED_PACKAGE.']; + yield 'container tool executes outside the PHP launcher' => [$first( + 'php "$consumer/vendor/bin/phpstan" analyse', + '"$consumer/vendor/bin/phpstan" analyse', + ), 'Container framework tools must use the PHP launcher.']; + yield 'Symfony cell contradicts its fixed minor' => [$first( + " php: '8.1'\n", + " php: '8.4'\n", + ), 'A framework cell must use its job\'s fixed PHP minor.']; yield 'exact minimum removed from a cell' => [$first( " exercised_package: 'laravel/framework:12.69.0'\n", '', @@ -177,9 +185,23 @@ private static function ciMinimumQualificationErrors(string $workflow, array $ma $errors[] = 'The CI Laravel cells differ from the adoption contract.'; } - preg_match_all('/^ framework-compat(?:83)?:\n.*?(?=^ [a-zA-Z0-9_-]+:\n|\z)/ms', $workflow, $jobs); - if (count($jobs[0]) !== 2) { - $errors[] = 'Both framework jobs must qualify their Laravel cells.'; + preg_match_all('/^ framework-compat(?:81|83|84)?:\n.*?(?=^ [a-zA-Z0-9_-]+:\n|\z)/ms', $workflow, $jobs); + $minors = ['framework-compat' => '8.2', 'framework-compat81' => '8.1', + 'framework-compat83' => '8.3', 'framework-compat84' => '8.4']; + $names = []; + foreach ($jobs[0] as $job) { + preg_match('/^ (framework-compat[0-9]*):/', $job, $name); + $names[] = $name[1]; + preg_match_all("/^ {12}php: '([^']+)'$/m", $job, $actual); + if ($actual[1] === [] || array_filter($actual[1], static fn (string $minor): bool => $minor !== $minors[$name[1]]) !== []) { + $errors[] = 'A framework cell must use its job\'s fixed PHP minor.'; + } + } + sort($names); + $expectedNames = array_keys($minors); + sort($expectedNames); + if ($names !== $expectedNames) { + $errors[] = 'All four framework jobs must qualify their cells.'; } foreach ($jobs[0] as $job) { $errors = array_merge($errors, self::ciFrameworkJobMinimumErrors($job)); @@ -192,6 +214,10 @@ private static function ciMinimumQualificationErrors(string $workflow, array $ma private static function ciFrameworkJobMinimumErrors(string $workflow): array { $errors = []; + if (str_contains($workflow, 'setup-php-container.sh') + && !str_contains($workflow, 'php "$consumer/vendor/bin/phpstan" analyse')) { + $errors[] = 'Container framework tools must use the PHP launcher.'; + } $install = self::between($workflow, ' - name: Install SDK into a clean framework consumer', ' else'); $assert = 'php "$GITHUB_WORKSPACE/scripts/ci/assert-installed-version.php" "$consumer" laravel/framework "$exercised_version"'; $require = strpos($install, 'composer require "$FRAMEWORK_EXERCISED_PACKAGE" \\'); diff --git a/tests/ci/test_php_container_launcher.py b/tests/ci/test_php_container_launcher.py index a0595fc2..d524057e 100644 --- a/tests/ci/test_php_container_launcher.py +++ b/tests/ci/test_php_container_launcher.py @@ -43,11 +43,13 @@ def arguments(self): def test_config_id_and_caps_are_bound_without_passing_host_credentials(self): for name in ("GH_TOKEN", "GITHUB_TOKEN", "COMPOSER_AUTH"): self.environment[name] = "synthetic-unforwarded-canary" + self.environment["DURABLE_WORKFLOW_TEST_TRACING"] = "1" result = self.invoke("php", "-r", "echo 1;") self.assertEqual(result.returncode, 0, result.stderr) arguments = self.arguments() self.assertIn("sha256:" + "a" * 64, arguments) self.assertIn("--init", arguments) + self.assertIn("DURABLE_WORKFLOW_TEST_TRACING", arguments) for option, value in (("--cpus", "2"), ("--memory", "2g"), ("--pids-limit", "512"), ("--cap-drop", "ALL"), ("--security-opt", "no-new-privileges")): From fc843c2069bf23801975d973e4642692aeddbbdb Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Fri, 13 Nov 2026 16:41:15 +0000 Subject: [PATCH 2/3] Require the tracing control flag inside each framework runtime --- .github/workflows/ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c08f1250..aee86a1f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -474,6 +474,7 @@ jobs: FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} DURABLE_WORKFLOW_TEST_TRACING: '1' run: | + php -r 'if (getenv("DURABLE_WORKFLOW_TEST_TRACING") !== "1") { throw new RuntimeException("Tracing control flag was not forwarded"); }' consumer="$(cat "$RUNNER_TEMP/framework-consumer")" composer --working-dir="$consumer" require --dev open-telemetry/api:1.10.1 'monolog/monolog:^3.0' \ --no-interaction --prefer-dist --no-progress @@ -631,6 +632,7 @@ jobs: FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} DURABLE_WORKFLOW_TEST_TRACING: '1' run: | + php -r 'if (getenv("DURABLE_WORKFLOW_TEST_TRACING") !== "1") { throw new RuntimeException("Tracing control flag was not forwarded"); }' consumer="$(cat "$RUNNER_TEMP/framework-consumer")" composer --working-dir="$consumer" require --dev open-telemetry/api:1.10.1 'monolog/monolog:^3.0' \ --no-interaction --prefer-dist --no-progress @@ -802,6 +804,7 @@ jobs: FRAMEWORK_EXERCISED_PACKAGE: ${{ matrix.exercised_package }} DURABLE_WORKFLOW_TEST_TRACING: '1' run: | + php -r 'if (getenv("DURABLE_WORKFLOW_TEST_TRACING") !== "1") { throw new RuntimeException("Tracing control flag was not forwarded"); }' consumer="$(cat "$RUNNER_TEMP/framework-consumer")" composer --working-dir="$consumer" require --dev open-telemetry/api:1.10.1 'monolog/monolog:^3.0' \ --no-interaction --prefer-dist --no-progress From 2713469e40ba16766e462cf4f80d1dcdaf704fda Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Fri, 13 Nov 2026 16:55:13 +0000 Subject: [PATCH 3/3] Preserve explicit synthetic process-role handoffs in framework tests --- scripts/ci/run-php-container.sh | 1 + tests/ci/test_php_container_launcher.py | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/scripts/ci/run-php-container.sh b/scripts/ci/run-php-container.sh index 4bff0f74..28c69607 100755 --- a/scripts/ci/run-php-container.sh +++ b/scripts/ci/run-php-container.sh @@ -66,6 +66,7 @@ while IFS= read -r variable; do DURABLE_WORKFLOW_RUNTIME_URL|DURABLE_WORKFLOW_SERVER_URL|DURABLE_WORKFLOW_NAMESPACE|\ DURABLE_WORKFLOW_TASK_QUEUE|DURABLE_WORKFLOW_WORKER_ID|\ DURABLE_WORKFLOW_TEST_TRACING|\ + DURABLE_WORKFLOW_PROCESS_ROLE|DURABLE_WORKFLOW_PROCESS_TOKEN|DURABLE_WORKFLOW_ROLE_PROBE_LOG|\ DURABLE_WORKFLOW_WORKER_TOKEN|DURABLE_WORKFLOW_CLIENT_TOKEN|\ DW_CI_CA_SHA256|GITHUB_ACTIONS|CI|TMPDIR|TZ) options+=(--env "$variable") ;; diff --git a/tests/ci/test_php_container_launcher.py b/tests/ci/test_php_container_launcher.py index d524057e..82109aab 100644 --- a/tests/ci/test_php_container_launcher.py +++ b/tests/ci/test_php_container_launcher.py @@ -44,12 +44,17 @@ def test_config_id_and_caps_are_bound_without_passing_host_credentials(self): for name in ("GH_TOKEN", "GITHUB_TOKEN", "COMPOSER_AUTH"): self.environment[name] = "synthetic-unforwarded-canary" self.environment["DURABLE_WORKFLOW_TEST_TRACING"] = "1" + self.environment.update({"DURABLE_WORKFLOW_PROCESS_ROLE": "worker", + "DURABLE_WORKFLOW_PROCESS_TOKEN": "synthetic-runtime-role", + "DURABLE_WORKFLOW_ROLE_PROBE_LOG": str(self.state / "data/role.jsonl")}) result = self.invoke("php", "-r", "echo 1;") self.assertEqual(result.returncode, 0, result.stderr) arguments = self.arguments() self.assertIn("sha256:" + "a" * 64, arguments) self.assertIn("--init", arguments) self.assertIn("DURABLE_WORKFLOW_TEST_TRACING", arguments) + for name in ("DURABLE_WORKFLOW_PROCESS_ROLE", "DURABLE_WORKFLOW_PROCESS_TOKEN", "DURABLE_WORKFLOW_ROLE_PROBE_LOG"): + self.assertIn(name, arguments) for option, value in (("--cpus", "2"), ("--memory", "2g"), ("--pids-limit", "512"), ("--cap-drop", "ALL"), ("--security-opt", "no-new-privileges")):