From dca24585d4f5f22f2b24350395a303e3c4f2d6a3 Mon Sep 17 00:00:00 2001 From: crazywhalecc Date: Thu, 9 Jul 2026 11:56:29 +0900 Subject: [PATCH] Add frankenphp test label --- .github/workflows/tests.yml | 4 +- .../Command/Dev/GenExtTestMatrixCommand.php | 115 ++++++++++++++---- src/StaticPHP/Command/Dev/TestBotCommand.php | 24 +++- 3 files changed, 113 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34dbf959..690de888 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -227,7 +227,7 @@ jobs: echo "matrix=$(echo "$FINAL" | jq -c '{"combo": .}')" >> "$GITHUB_OUTPUT" ext-test: - name: "Ext test: ${{ matrix.combo.extension }} (PHP ${{ matrix.combo.php-version }} · ${{ matrix.combo.os }}-${{ matrix.combo.arch }})" + name: "Ext test: ${{ matrix.combo.extension }} [${{ matrix.combo.sapi }}] (PHP ${{ matrix.combo.php-version }} - ${{ matrix.combo.os }}-${{ matrix.combo.arch }})" needs: gen-matrix runs-on: ${{ matrix.combo.runner }} timeout-minutes: 120 @@ -266,5 +266,5 @@ jobs: if: always() && hashFiles('log/**') != '' uses: actions/upload-artifact@v4 with: - name: logs-${{ matrix.combo.os }}-${{ matrix.combo.arch }}-${{ matrix.combo.extension }}-php${{ matrix.combo.php-version }} + name: logs-${{ matrix.combo.os }}-${{ matrix.combo.arch }}-${{ matrix.combo.sapi }}-${{ matrix.combo.extension }}-php${{ matrix.combo.php-version }} path: log diff --git a/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php b/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php index 6b5c9e6d..23bc66be 100644 --- a/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php +++ b/src/StaticPHP/Command/Dev/GenExtTestMatrixCommand.php @@ -14,6 +14,12 @@ class GenExtTestMatrixCommand extends BaseCommand { private const string BUILD_TARGETS = '--build-cli --build-cgi --build-micro --with-suggests -vvv'; + private const string FRANKENPHP_BUILD_TARGETS = '--build-cli --build-frankenphp --enable-zts --with-suggests -vvv'; + + private const array SUPPORTED_SAPIS = [ + 'frankenphp', + ]; + private const array OS_RUNNERS = [ 'linux' => ['arch' => 'x86_64', 'runner' => 'ubuntu-latest', 'os_key' => 'Linux'], 'windows' => ['arch' => 'x86_64', 'runner' => 'windows-2025', 'os_key' => 'Windows'], @@ -84,6 +90,7 @@ class GenExtTestMatrixCommand extends BaseCommand $this->addOption('for-extensions', null, InputOption::VALUE_OPTIONAL, 'Filter by extension display names, comma-separated', '') ->addOption('for-libs', null, InputOption::VALUE_OPTIONAL, 'Filter by lib names (depends+suggests), comma-separated', '') ->addOption('os', null, InputOption::VALUE_OPTIONAL, 'Filter by OS (Linux/Darwin/Windows), comma-separated', '') + ->addOption('sapi', null, InputOption::VALUE_OPTIONAL, 'Add extra SAPI build tests, comma-separated (supported: frankenphp)', '') ->addOption('tier2', null, InputOption::VALUE_NONE, 'Use Tier 2 runners (Linux aarch64 + macOS x86_64, no Windows)'); } @@ -98,8 +105,15 @@ class GenExtTestMatrixCommand extends BaseCommand $filter_extensions = $parse_option('for-extensions'); $filter_libs = $parse_option('for-libs'); $filter_os_keys = $parse_option('os'); + $filter_sapis = array_unique($parse_option('sapi')); $tier2 = (bool) $this->input->getOption('tier2'); + $unknown_sapis = array_values(array_diff($filter_sapis, self::SUPPORTED_SAPIS)); + if (!empty($unknown_sapis)) { + $this->output->writeln('Unsupported SAPI(s): ' . implode(', ', $unknown_sapis) . ''); + return static::USER_ERROR; + } + $base_runners = $tier2 ? self::OS_RUNNERS_TIER2 : self::OS_RUNNERS; $all = PackageConfig::getAll(); @@ -126,6 +140,67 @@ class GenExtTestMatrixCommand extends BaseCommand } } + [$entries, $all_ext_lib_deps] = $this->buildEntriesForRunners( + $base_runners, + $filter_os_keys, + $all_regular, + $all_virtual, + $all_libraries, + self::BUILD_TARGETS, + 'default', + ); + + if (in_array('frankenphp', $filter_sapis, true)) { + [$frankenphp_entries, $frankenphp_ext_lib_deps] = $this->buildEntriesForRunners( + self::OS_RUNNERS, + [], + $all_regular, + $all_virtual, + $all_libraries, + self::FRANKENPHP_BUILD_TARGETS, + 'frankenphp', + ); + $entries = array_merge($entries, $frankenphp_entries); + $all_ext_lib_deps = array_replace_recursive($all_ext_lib_deps, $frankenphp_ext_lib_deps); + } + + if (!empty($filter_extensions) || !empty($filter_libs)) { + $entries = array_values(array_filter($entries, function (array $entry) use ($filter_extensions, $filter_libs, $all_ext_lib_deps): bool { + $names = explode(',', $entry['extension']); + + if (!empty($filter_extensions) && count(array_intersect($names, $filter_extensions)) > 0) { + return true; + } + + if (!empty($filter_libs)) { + $lib_deps = $all_ext_lib_deps[$entry['os']] ?? []; + foreach ($names as $name) { + if (count(array_intersect($lib_deps[$name] ?? [], $filter_libs)) > 0) { + return true; + } + } + } + + return false; + })); + } + + $this->output->write(json_encode($entries, JSON_UNESCAPED_SLASHES)); + return static::SUCCESS; + } + + /** + * @return array{array>, array>} + */ + private function buildEntriesForRunners( + array $base_runners, + array $filter_os_keys, + array $all_regular, + array $all_virtual, + array $all_libraries, + string $build_targets, + string $sapi, + ): array { $os_runners = empty($filter_os_keys) ? $base_runners : array_filter($base_runners, fn ($info) => in_array($info['os_key'], $filter_os_keys, true)); @@ -237,40 +312,18 @@ class GenExtTestMatrixCommand extends BaseCommand sort($groups); foreach ($groups as $group) { - $extra = $this->extraBuildFlags($group); $entries[] = [ 'runner' => $os_info['runner'], 'os' => $os, 'arch' => $os_info['arch'], + 'sapi' => $sapi, 'extension' => $group, - 'build-args' => './bin/spc build "' . $group . '" ' . self::BUILD_TARGETS . ($extra !== '' ? ' ' . $extra : ''), + 'build-args' => './bin/spc build "' . $group . '" ' . $this->buildTargetsWithExtraFlags($build_targets, $group), ]; } } - if (!empty($filter_extensions) || !empty($filter_libs)) { - $entries = array_values(array_filter($entries, function (array $entry) use ($filter_extensions, $filter_libs, $all_ext_lib_deps): bool { - $names = explode(',', $entry['extension']); - - if (!empty($filter_extensions) && count(array_intersect($names, $filter_extensions)) > 0) { - return true; - } - - if (!empty($filter_libs)) { - $lib_deps = $all_ext_lib_deps[$entry['os']] ?? []; - foreach ($names as $name) { - if (count(array_intersect($lib_deps[$name] ?? [], $filter_libs)) > 0) { - return true; - } - } - } - - return false; - })); - } - - $this->output->write(json_encode($entries, JSON_UNESCAPED_SLASHES)); - return static::SUCCESS; + return [$entries, $all_ext_lib_deps]; } /** @@ -397,6 +450,18 @@ class GenExtTestMatrixCommand extends BaseCommand return implode(' ', $flags); } + private function buildTargetsWithExtraFlags(string $build_targets, string $group): string + { + $flags = explode(' ', $build_targets); + foreach (explode(' ', $this->extraBuildFlags($group)) as $extra) { + if ($extra === '' || in_array($extra, $flags, true)) { + continue; + } + $flags[] = $extra; + } + return implode(' ', $flags); + } + /** * Resolve the value of a platform-specific array field, applying the suffix fallback chain. * diff --git a/src/StaticPHP/Command/Dev/TestBotCommand.php b/src/StaticPHP/Command/Dev/TestBotCommand.php index 6ee1e1e3..72f882ec 100644 --- a/src/StaticPHP/Command/Dev/TestBotCommand.php +++ b/src/StaticPHP/Command/Dev/TestBotCommand.php @@ -25,6 +25,10 @@ class TestBotCommand extends BaseCommand private const string TIER2_LABEL = 'test/tier2'; + private const array SAPI_LABELS = [ + 'sapi/frankenphp' => 'frankenphp', + ]; + /** PHP version labels → version string (8.5 is always included as default) */ private const array PHP_VERSION_LABELS = [ 'test/php-83' => '8.3', @@ -93,6 +97,12 @@ class TestBotCommand extends BaseCommand } $tier2 = in_array(self::TIER2_LABEL, $label_names, true); $need_test = in_array('need-test', $label_names, true); + $sapis = []; + foreach (self::SAPI_LABELS as $label => $sapi) { + if (in_array($label, $label_names, true)) { + $sapis[] = $sapi; + } + } // Resolve PHP versions (default always included) $php_versions = [self::DEFAULT_PHP_VERSION]; @@ -119,6 +129,9 @@ class TestBotCommand extends BaseCommand if (!empty($os_keys)) { $flag_parts[] = '--os=' . implode(',', $os_keys); } + if (!empty($sapis)) { + $flag_parts[] = '--sapi=' . implode(',', $sapis); + } $gen_matrix_args = implode(' ', $flag_parts); if ($tier2) { @@ -128,7 +141,7 @@ class TestBotCommand extends BaseCommand fn ($k) => $k !== 'Windows' )); if (!empty($tier2_os)) { - $tier2_parts = array_values(array_filter($flag_parts, fn ($f) => !str_starts_with($f, '--os='))); + $tier2_parts = array_values(array_filter($flag_parts, fn ($f) => !str_starts_with($f, '--os=') && !str_starts_with($f, '--sapi='))); $tier2_parts[] = '--os=' . implode(',', $tier2_os); $tier2_parts[] = '--tier2'; $gen_matrix_args_tier2 = implode(' ', $tier2_parts); @@ -143,6 +156,7 @@ class TestBotCommand extends BaseCommand $label_names, $os_keys, $tier2, + $sapis, $php_versions, $need_test, ); @@ -156,6 +170,7 @@ class TestBotCommand extends BaseCommand 'gen_matrix_args_tier2' => $gen_matrix_args_tier2, 'php_versions' => $php_versions, 'tier2' => $tier2, + 'sapis' => $sapis, 'comment_body' => $comment_body, ]; @@ -239,6 +254,7 @@ class TestBotCommand extends BaseCommand array $label_names, array $os_keys, bool $tier2, + array $sapis, array $php_versions, bool $need_test, ): string { @@ -258,6 +274,7 @@ class TestBotCommand extends BaseCommand '`test/linux` `test/windows` `test/macos` (platform)', '`test/tier2` (extra arch)', '`test/php-83` `test/php-84` (PHP version)', + '`sapi/frankenphp` (extra FrankenPHP SAPI build)', ]); // Case 1: need-test absent → invite the author to add it @@ -303,7 +320,8 @@ class TestBotCommand extends BaseCommand } $php_str = implode(', ', array_map(fn ($v) => "PHP {$v}", $php_versions)) . ' NTS'; - $active_test_labels = array_values(array_filter($label_names, fn ($l) => str_starts_with($l, 'test/'))); + $sapi_str = !empty($sapis) ? ' + SAPI: ' . implode(', ', $sapis) . ' (Tier1 Linux/macOS/Windows)' : ''; + $active_test_labels = array_values(array_filter($label_names, fn ($l) => str_starts_with($l, 'test/') || str_starts_with($l, 'sapi/'))); $labels_str = !empty($active_test_labels) ? '`' . implode('`, `', $active_test_labels) . '`' : '_none_'; return implode("\n", [ @@ -313,7 +331,7 @@ class TestBotCommand extends BaseCommand $detected, '**Active labels**: ' . $labels_str, '**Available labels**: ' . $available_labels, - '**Config**: ' . implode(' + ', $platform_parts) . ' | ' . $php_str, + '**Config**: ' . implode(' + ', $platform_parts) . ' | ' . $php_str . $sapi_str, ]); } }