Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 181 additions & 14 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -336,20 +336,6 @@ jobs:
exercised_package: 'laravel/framework:12.69.0'
bootstrap: fresh_supported_install
script: laravel.php
- framework: Laravel
version: '12'
php: '8.3'
package: 'laravel/framework:^12.69.0'
exercised_package: 'laravel/framework:12.69.0'
bootstrap: fresh_supported_install
script: laravel.php
- framework: Laravel
version: '13'
php: '8.3'
package: 'laravel/framework:^13.30.0'
exercised_package: 'laravel/framework:13.30.0'
bootstrap: fresh_supported_install
script: laravel.php
- framework: Laravel
version: '12'
php: '8.4'
Expand Down Expand Up @@ -517,6 +503,184 @@ jobs:
DURABLE_WORKFLOW_WORKER_TOKEN=worker-secret \
php "tests/compat/${COMPAT_SCRIPT}" "$consumer/vendor/autoload.php"

framework-compat83:
name: ${{ matrix.framework }} ${{ matrix.version }}
runs-on: ubuntu-24.04
timeout-minutes: 10
needs: action-policy
strategy:
fail-fast: false
matrix:
include:
- framework: Laravel
version: '12'
php: '8.3'
package: 'laravel/framework:^12.69.0'
exercised_package: 'laravel/framework:12.69.0'
bootstrap: fresh_supported_install
script: laravel.php
- framework: Laravel
version: '13'
php: '8.3'
package: 'laravel/framework:^13.30.0'
exercised_package: 'laravel/framework:13.30.0'
bootstrap: fresh_supported_install
script: laravel.php
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- name: Select stock-profile system Python
run: |
/usr/bin/python3 -I -S -c 'import sys; assert sys.version_info[:2] == (3, 12)'
echo /usr/bin >> "$GITHUB_PATH"
- name: Create isolated framework Composer state
run: |
state="$(mktemp -d "$RUNNER_TEMP/framework-composer.XXXXXX")"
mkdir -m 700 "$state/home" "$state/cache" "$state/tmp"
{
echo "COMPOSER_HOME=$state/home"
echo "COMPOSER_CACHE_DIR=$state/cache"
echo "TMPDIR=$state/tmp"
} >> "$GITHUB_ENV"
- 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: Set up the qualified stock PHP 8.3 framework profile
uses: durable-workflow/.github/.github/actions/setup-php-linux@411a14ac07d32073eed6ab4977f2825eba4739a7 # bound-linux-php
with:
php-version: '8.3'
ini-file: production
tools: composer:2.10.3,phpunit:11.5.57
coverage: none
- name: Require the actual stock framework modules
run: |
php -r 'if (PHP_MAJOR_VERSION !== 8 || PHP_MINOR_VERSION !== 3) { throw new RuntimeException("Expected PHP 8.3"); }
foreach (["curl", "mbstring", "pdo_sqlite", "sqlite3", "xml"] as $extension) {
if (!extension_loaded($extension)) { throw new RuntimeException("Missing ".$extension); }
}'
- 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="$RUNNER_TEMP/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 "$RUNNER_TEMP/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"
"$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"

laravel-transition-compat:
name: Laravel ${{ matrix.source }} to service mode
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -1103,6 +1267,7 @@ jobs:
- test-php83
- carbon-2
- framework-compat
- framework-compat83
- laravel-transition-compat
- analyse
- docs
Expand All @@ -1124,6 +1289,7 @@ jobs:
PHP_83_RESULT: ${{ needs.test-php83.result }}
CARBON_2_RESULT: ${{ needs.carbon-2.result }}
FRAMEWORK_RESULT: ${{ needs.framework-compat.result }}
FRAMEWORK_83_RESULT: ${{ needs.framework-compat83.result }}
LARAVEL_TRANSITION_RESULT: ${{ needs.laravel-transition-compat.result }}
ANALYSIS_RESULT: ${{ needs.analyse.result }}
DOCS_RESULT: ${{ needs.docs.result }}
Expand All @@ -1135,6 +1301,7 @@ jobs:
test "$PHP_83_RESULT" = success
test "$CARBON_2_RESULT" = success
test "$FRAMEWORK_RESULT" = success
test "$FRAMEWORK_83_RESULT" = success
test "$LARAVEL_TRANSITION_RESULT" = success
test "$ANALYSIS_RESULT" = success
test "$DOCS_RESULT" = success
Expand Down
30 changes: 30 additions & 0 deletions tests/LaravelAdoptionContractTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,17 @@ static function (string $workflow) use ($search, $replace): string {
'composer require "$FRAMEWORK_EXERCISED_PACKAGE" \\',
'composer require "$FRAMEWORK_PACKAGE" \\',
), 'The Laravel consumer must install exactly $FRAMEWORK_EXERCISED_PACKAGE.'];
yield 'stock 8.3 job installs a caret instead of the exact minimum' => [static function (string $workflow): string {
$offset = strpos($workflow, ' framework-compat83:');
if ($offset === false) {
return $workflow;
}
return substr($workflow, 0, $offset).str_replace(
'composer require "$FRAMEWORK_EXERCISED_PACKAGE" \\',
'composer require "$FRAMEWORK_PACKAGE" \\',
substr($workflow, $offset),
);
}, 'The Laravel consumer must install exactly $FRAMEWORK_EXERCISED_PACKAGE.'];
yield 'exact minimum removed from a cell' => [$first(
" exercised_package: 'laravel/framework:12.69.0'\n",
'',
Expand Down Expand Up @@ -158,10 +169,29 @@ private static function ciMinimumQualificationErrors(string $workflow, array $ma
);
$cells = array_map(static fn (array $match): array => ['laravel' => $match[1], 'php' => $match[2],
'package' => $match[3], 'exercised_package' => $match[4], 'bootstrap' => $match[5]], $matches);
$order = static fn (array $left, array $right): int =>
[$left['laravel'], $left['php']] <=> [$right['laravel'], $right['php']];
usort($cells, $order);
usort($matrix, $order);
if ($cells !== $matrix || count($matches) !== substr_count($workflow, "- framework: Laravel\n")) {
$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.';
}
foreach ($jobs[0] as $job) {
$errors = array_merge($errors, self::ciFrameworkJobMinimumErrors($job));
}

return $errors;
}

/** @return list<string> */
private static function ciFrameworkJobMinimumErrors(string $workflow): array
{
$errors = [];
$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" \\');
Expand Down
Loading