From 433bcad6d9c19d01e1d7d64538653cf17d552e8f Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 15 Dec 2025 22:33:52 +0100 Subject: [PATCH 01/37] versioned packages --- bin/spp | 2 +- composer.lock | 95 +++++++++++++++++---------------- config/static-php.repo | 9 ++++ config/templates/craft.yml.twig | 4 +- src/extension.php | 10 +++- src/package/cgi.php | 6 +++ src/package/cli.php | 10 ++-- src/package/devel.php | 14 +++-- src/package/embed.php | 10 +++- src/package/fpm.php | 8 ++- src/package/frankenphp.php | 62 ++++++++++++++++++--- src/package/pie.php | 21 +++++++- src/package/spx.php | 8 ++- src/step/CreatePackages.php | 70 +++++++++++++++--------- 14 files changed, 236 insertions(+), 93 deletions(-) diff --git a/bin/spp b/bin/spp index 3623415..841431d 100755 --- a/bin/spp +++ b/bin/spp @@ -74,5 +74,5 @@ function getLibdir(): string function getConfdir(): string { - return '/etc/' . \staticphp\step\CreatePackages::getPrefix(); + return '/etc/php-zts'; } diff --git a/composer.lock b/composer.lock index d56f0ed..75cd468 100644 --- a/composer.lock +++ b/composer.lock @@ -81,12 +81,12 @@ "source": { "type": "git", "url": "https://github.com/crazywhalecc/static-php-cli.git", - "reference": "b2182b4fe1d0278c1efcdbfce7a5b0268fb4aae0" + "reference": "d064e1353cf7dd8e97a769267fc9a804e38169f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/b2182b4fe1d0278c1efcdbfce7a5b0268fb4aae0", - "reference": "b2182b4fe1d0278c1efcdbfce7a5b0268fb4aae0", + "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/d064e1353cf7dd8e97a769267fc9a804e38169f1", + "reference": "d064e1353cf7dd8e97a769267fc9a804e38169f1", "shasum": "" }, "require": { @@ -141,7 +141,7 @@ "type": "other" } ], - "time": "2025-12-05T11:20:14+00:00" + "time": "2025-12-15T17:50:20+00:00" }, { "name": "doctrine/inflector", @@ -1007,16 +1007,16 @@ }, { "name": "symfony/console", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8" + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", - "reference": "0bc0f45254b99c58d45a8fbf9fb955d46cbd1bb8", + "url": "https://api.github.com/repos/symfony/console/zipball/6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", + "reference": "6d9f0fbf2ec2e9785880096e3abd0ca0c88b506e", "shasum": "" }, "require": { @@ -1081,7 +1081,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.0" + "source": "https://github.com/symfony/console/tree/v7.4.1" }, "funding": [ { @@ -1101,7 +1101,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T13:27:24+00:00" + "time": "2025-12-05T15:23:39+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1659,16 +1659,16 @@ }, { "name": "symfony/string", - "version": "v8.0.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f" + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f929eccf09531078c243df72398560e32fa4cf4f", - "reference": "f929eccf09531078c243df72398560e32fa4cf4f", + "url": "https://api.github.com/repos/symfony/string/zipball/ba65a969ac918ce0cc3edfac6cdde847eba231dc", + "reference": "ba65a969ac918ce0cc3edfac6cdde847eba231dc", "shasum": "" }, "require": { @@ -1725,7 +1725,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v8.0.0" + "source": "https://github.com/symfony/string/tree/v8.0.1" }, "funding": [ { @@ -1745,20 +1745,20 @@ "type": "tidelift" } ], - "time": "2025-09-11T14:37:55+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/translation", - "version": "v8.0.0", + "version": "v8.0.1", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6" + "reference": "770e3b8b0ba8360958abedcabacd4203467333ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/82ab368a6fca6358d995b6dd5c41590fb42c03e6", - "reference": "82ab368a6fca6358d995b6dd5c41590fb42c03e6", + "url": "https://api.github.com/repos/symfony/translation/zipball/770e3b8b0ba8360958abedcabacd4203467333ca", + "reference": "770e3b8b0ba8360958abedcabacd4203467333ca", "shasum": "" }, "require": { @@ -1818,7 +1818,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v8.0.0" + "source": "https://github.com/symfony/translation/tree/v8.0.1" }, "funding": [ { @@ -1838,7 +1838,7 @@ "type": "tidelift" } ], - "time": "2025-11-27T08:09:45+00:00" + "time": "2025-12-01T09:13:36+00:00" }, { "name": "symfony/translation-contracts", @@ -1924,16 +1924,16 @@ }, { "name": "symfony/yaml", - "version": "v7.4.0", + "version": "v7.4.1", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810" + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/6c84a4b55aee4cd02034d1c528e83f69ddf63810", - "reference": "6c84a4b55aee4cd02034d1c528e83f69ddf63810", + "url": "https://api.github.com/repos/symfony/yaml/zipball/24dd4de28d2e3988b311751ac49e684d783e2345", + "reference": "24dd4de28d2e3988b311751ac49e684d783e2345", "shasum": "" }, "require": { @@ -1976,7 +1976,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v7.4.0" + "source": "https://github.com/symfony/yaml/tree/v7.4.1" }, "funding": [ { @@ -1996,20 +1996,20 @@ "type": "tidelift" } ], - "time": "2025-11-16T10:14:42+00:00" + "time": "2025-12-04T18:11:45+00:00" }, { "name": "twig/twig", - "version": "v3.22.1", + "version": "v3.22.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3" + "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", - "reference": "1de2ec1fc43ab58a4b7e80b214b96bfc895750f3", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/946ddeafa3c9f4ce279d1f34051af041db0e16f2", + "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2", "shasum": "" }, "require": { @@ -2063,7 +2063,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.22.1" + "source": "https://github.com/twigphp/Twig/tree/v3.22.2" }, "funding": [ { @@ -2075,7 +2075,7 @@ "type": "tidelift" } ], - "time": "2025-11-16T16:01:12+00:00" + "time": "2025-12-14T11:28:47+00:00" }, { "name": "voku/portable-ascii", @@ -2228,12 +2228,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "114fdb88709703f1e43a022f08844798d11439f4" + "reference": "1553067758ae7f3df13df7c7e232c62d928e1d23" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/114fdb88709703f1e43a022f08844798d11439f4", - "reference": "114fdb88709703f1e43a022f08844798d11439f4", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/1553067758ae7f3df13df7c7e232c62d928e1d23", + "reference": "1553067758ae7f3df13df7c7e232c62d928e1d23", "shasum": "" }, "conflict": { @@ -2286,7 +2286,7 @@ "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", "aws/aws-sdk-php": "<3.288.1", - "azuracast/azuracast": "<0.18.3", + "azuracast/azuracast": "<=0.23.1", "b13/seo_basics": "<0.8.2", "backdrop/backdrop": "<=1.32", "backpack/crud": "<3.4.9", @@ -2485,6 +2485,7 @@ "feehi/feehicms": "<=2.1.1", "fenom/fenom": "<=2.12.1", "filament/actions": ">=3.2,<3.2.123", + "filament/filament": ">=4,<4.3.1", "filament/infolists": ">=3,<3.2.115", "filament/tables": ">=3,<3.2.115", "filegator/filegator": "<7.8", @@ -2503,6 +2504,7 @@ "floriangaerber/magnesium": "<0.3.1", "fluidtypo3/vhs": "<5.1.1", "fof/byobu": ">=0.3.0.0-beta2,<1.1.7", + "fof/pretty-mail": "<=1.1.2", "fof/upload": "<1.2.3", "foodcoopshop/foodcoopshop": ">=3.2,<3.6.1", "fooman/tcpdf": "<6.2.22", @@ -2566,7 +2568,7 @@ "ibexa/http-cache": ">=4.6,<4.6.14", "ibexa/post-install": "<1.0.16|>=4.6,<4.6.14", "ibexa/solr": ">=4.5,<4.5.4", - "ibexa/user": ">=4,<4.4.3|>=5,<5.0.3", + "ibexa/user": ">=4,<4.4.3|>=5,<5.0.4", "icecoder/icecoder": "<=8.1", "idno/known": "<=1.3.1", "ilicmiljan/secure-props": ">=1.2,<1.2.2", @@ -2649,7 +2651,7 @@ "leantime/leantime": "<3.3", "lexik/jwt-authentication-bundle": "<2.10.7|>=2.11,<2.11.3", "libreform/libreform": ">=2,<=2.0.8", - "librenms/librenms": "<2017.08.18", + "librenms/librenms": "<25.11", "liftkit/database": "<2.13.2", "lightsaml/lightsaml": "<1.3.5", "limesurvey/limesurvey": "<6.5.12", @@ -2703,6 +2705,7 @@ "microsoft/microsoft-graph-core": "<2.0.2", "microweber/microweber": "<=2.0.19", "mikehaertl/php-shellcommand": "<1.6.1", + "mineadmin/mineadmin": "<=3.0.9", "miniorange/miniorange-saml": "<1.4.3", "mittwald/typo3_forum": "<1.2.1", "mobiledetect/mobiledetectlib": "<2.8.32", @@ -2739,6 +2742,7 @@ "netgen/tagsbundle": ">=3.4,<3.4.11|>=4,<4.0.15", "nette/application": ">=2,<2.0.19|>=2.1,<2.1.13|>=2.2,<2.2.10|>=2.3,<2.3.14|>=2.4,<2.4.16|>=3,<3.0.6", "nette/nette": ">=2,<2.0.19|>=2.1,<2.1.13", + "neuron-core/neuron-ai": "<=2.8.11", "nilsteampassnet/teampass": "<3.1.3.1-dev", "nitsan/ns-backup": "<13.0.1", "nonfiction/nterchange": "<4.1.1", @@ -2758,7 +2762,7 @@ "october/system": "<3.7.5", "oliverklee/phpunit": "<3.5.15", "omeka/omeka-s": "<4.0.3", - "onelogin/php-saml": "<2.10.4", + "onelogin/php-saml": "<2.21.1|>=3,<3.8.1|>=4,<4.3.1", "oneup/uploader-bundle": ">=1,<1.9.3|>=2,<2.1.5", "open-web-analytics/open-web-analytics": "<1.8.1", "opencart/opencart": ">=0", @@ -2873,7 +2877,7 @@ "reportico-web/reportico": "<=8.1", "rhukster/dom-sanitizer": "<1.0.7", "rmccue/requests": ">=1.6,<1.8", - "robrichards/xmlseclibs": ">=1,<3.0.4", + "robrichards/xmlseclibs": "<=3.1.3", "roots/soil": "<4.1", "roundcube/roundcubemail": "<1.5.10|>=1.6,<1.6.11", "rudloff/alltube": "<3.0.3", @@ -2892,8 +2896,8 @@ "shopware/core": "<6.6.10.9-dev|>=6.7,<6.7.4.1-dev", "shopware/platform": "<6.6.10.7-dev|>=6.7,<6.7.3.1-dev", "shopware/production": "<=6.3.5.2", - "shopware/shopware": "<=5.7.17|>=6.7,<6.7.2.1-dev", - "shopware/storefront": "<=6.4.8.1|>=6.5.8,<6.5.8.7-dev", + "shopware/shopware": "<=5.7.17|>=6.4.6,<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", + "shopware/storefront": "<6.6.10.10-dev|>=6.7,<6.7.5.1-dev", "shopxo/shopxo": "<=6.4", "showdoc/showdoc": "<2.10.4", "shuchkin/simplexlsx": ">=1.0.12,<1.1.13", @@ -3144,6 +3148,7 @@ "yoast-seo-for-typo3/yoast_seo": "<7.2.3", "yourls/yourls": "<=1.8.2", "yuan1994/tpadmin": "<=1.3.12", + "yungifez/skuul": "<=2.6.5", "z-push/z-push-dev": "<2.7.6", "zencart/zencart": "<=1.5.7.0-beta", "zendesk/zendesk_api_client_php": "<2.2.11", @@ -3219,7 +3224,7 @@ "type": "tidelift" } ], - "time": "2025-12-05T02:37:28+00:00" + "time": "2025-12-12T23:06:01+00:00" } ], "aliases": [], diff --git a/config/static-php.repo b/config/static-php.repo index 43be095..cb2bc5e 100644 --- a/config/static-php.repo +++ b/config/static-php.repo @@ -4,3 +4,12 @@ baseurl=https://rpm.henderkes.com/\$basearch/el\$releasever enabled=1 gpgcheck=1 gpgkey=https://key.henderkes.com/static-php.gpg +excludepkgs=*-debuginfo + +[static-php-debuginfo] +name=Static PHP repository - Debuginfo +baseurl=https://rpm.henderkes.com/$basearch/el$releasever +enabled=0 +gpgcheck=1 +gpgkey=https://key.henderkes.com/static-php.asc +includepkgs=*-debuginfo diff --git a/config/templates/craft.yml.twig b/config/templates/craft.yml.twig index e9a9917..67d5253 100644 --- a/config/templates/craft.yml.twig +++ b/config/templates/craft.yml.twig @@ -34,8 +34,8 @@ build-options: with-suggested-libs: true with-suggested-exts: true no-strip: false - with-config-file-path: '/etc/{{ prefix }}' - with-config-file-scan-dir: '/etc/{{ prefix }}/conf.d' + with-config-file-path: '/etc/php-zts' + with-config-file-scan-dir: '/etc/php-zts/conf.d' # Hardcoded INI options for cli and micro SAPI (e.g. "memory_limit=4G", list accepted) with-hardcoded-ini: [ ] # UPX pack the binary (default: false) diff --git a/src/extension.php b/src/extension.php index f448603..3414fc0 100644 --- a/src/extension.php +++ b/src/extension.php @@ -146,18 +146,24 @@ public function getFpmConfig(): array $depends = array_merge($depends, $ordered); + $versionedConflicts = CreatePackages::getVersionedConflicts('-' . $this->name); return [ 'config-files' => [ getConfdir() . '/conf.d/' . $this->prefix . $this->name . '.ini', ], 'depends' => $depends, + 'provides' => [ + 'php-zts-' . $this->name, + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ ...($this->getIniPath() ? [$this->getIniPath() => getConfdir() . '/conf.d/' . $this->prefix . $this->name . '.ini'] : [] ), ...($this->isSharedExtension() ? - [BUILD_MODULES_PATH . '/' . $this->name . '.so' => getLibdir() . '/' . CreatePackages::getPrefix() . '/modules/' . $this->name . '.so'] + [BUILD_MODULES_PATH . '/' . $this->name . '.so' => getLibdir() . '/php-zts/modules/' . $this->name . '.so'] : [] ), ] @@ -209,7 +215,7 @@ public function getDebuginfoFpmConfig(): array if (!file_exists($src)) { return []; } - $targetSo = getLibdir() . '/' . CreatePackages::getPrefix() . '/modules/' . $this->name . '.so'; + $targetSo = getLibdir() . '/php-zts/modules/' . $this->name . '.so'; $target = '/usr/lib/debug' . $targetSo . '.debug'; return [ 'depends' => [CreatePackages::getPrefix() . '-' . $this->name], diff --git a/src/package/cgi.php b/src/package/cgi.php index 05cc07e..e518b21 100644 --- a/src/package/cgi.php +++ b/src/package/cgi.php @@ -14,10 +14,16 @@ public function getName(): string public function getFpmConfig(): array { + $versionedConflicts = CreatePackages::getVersionedConflicts('-cgi'); return [ 'depends' => [ CreatePackages::getPrefix() . '-cli', ], + 'provides' => [ + 'php-zts-cgi', + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ BUILD_BIN_PATH . '/php-cgi' => '/usr/bin/php-cgi-zts', ] diff --git a/src/package/cli.php b/src/package/cli.php index 5e11c7c..ebae08f 100644 --- a/src/package/cli.php +++ b/src/package/cli.php @@ -17,12 +17,15 @@ public function getFpmConfig(): array { $config = CraftConfig::getInstance(); $staticExtensions = $config->getStaticExtensions(); + $prefix = CreatePackages::getPrefix(); $contents = file_get_contents(INI_PATH . '/php.ini'); - $contents = str_replace('$libdir', getLibdir() . '/' . CreatePackages::getPrefix(), $contents); + $contents = str_replace('$libdir', getLibdir() . '/php-zts', $contents); file_put_contents(TEMP_DIR . '/php.ini', $contents); - $provides = ['php-zts']; - $replaces = []; + $provides = ['php-zts', $prefix]; + $versionedConflicts = CreatePackages::getVersionedConflicts('-cli'); + $replaces = $versionedConflicts; + $conflicts = $versionedConflicts; $configFiles = [ getConfdir(), getConfdir() . '/php.ini' @@ -65,6 +68,7 @@ public function getFpmConfig(): array ], 'provides' => $provides, 'replaces' => $replaces, + 'conflicts' => $conflicts, 'files' => $files ]; } diff --git a/src/package/devel.php b/src/package/devel.php index 18d7c83..e573d7b 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -38,7 +38,7 @@ public function getFpmConfig(): array $phpConfigContent ); $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $libName = 'lib' . CreatePackages::getPrefix() . "-$phpVersion.so"; + $libName = 'libphp-zts-' . $phpVersion . '.so'; $phpConfigContent = str_replace('libphp.so', $libName, $phpConfigContent); file_put_contents($modifiedPhpConfigPath, $phpConfigContent); @@ -65,8 +65,8 @@ public function getFpmConfig(): array '"`eval echo ${prefix}/include`/php"' ], [ - str_replace('/usr/', '', getLibdir()) . '/' . CreatePackages::getPrefix() . '`', - '"`eval echo ${prefix}/include`/' . CreatePackages::getPrefix() . '"' + str_replace('/usr/', '', getLibdir()) . '/php-zts`', + '"`eval echo ${prefix}/include`/php-zts"' ], $phpizeContent ); @@ -74,20 +74,24 @@ public function getFpmConfig(): array file_put_contents($modifiedPhpizePath, $phpizeContent); chmod($modifiedPhpizePath, 0755); + $versionedConflicts = CreatePackages::getVersionedConflicts('-devel'); return [ 'files' => [ $modifiedPhpConfigPath => '/usr/bin/php-config-zts', $modifiedPhpizePath => '/usr/bin/phpize-zts', BUILD_INCLUDE_PATH . '/php/' => '/usr/include/php-zts', - BUILD_LIB_PATH . '/php/build' => getLibdir() . '/' . CreatePackages::getPrefix(), + BUILD_LIB_PATH . '/php/build' => getLibdir() . '/php-zts', ], 'depends' => [ CreatePackages::getPrefix() . '-cli', ], 'provides' => [ + 'php-zts-devel', 'php-config-zts', 'phpize-zts', - ] + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, ]; } diff --git a/src/package/embed.php b/src/package/embed.php index dd1ab78..5f7c658 100644 --- a/src/package/embed.php +++ b/src/package/embed.php @@ -15,15 +15,21 @@ public function getName(): string public function getFpmConfig(): array { $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $name = 'lib' . CreatePackages::getPrefix() . "-$phpVersion.so"; + $name = 'libphp-zts-' . $phpVersion . '.so'; + $versionedConflicts = CreatePackages::getVersionedConflicts('-embed'); return [ 'depends' => [ CreatePackages::getPrefix() . '-cli', ], 'provides' => [ $name, + 'php-zts-embed', + CreatePackages::getPrefix() . '-embed', + 'php-zts-embedded', CreatePackages::getPrefix() . '-embedded' ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ BUILD_LIB_PATH . '/' . $name => getLibdir() . '/' . $name, ] @@ -38,7 +44,7 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { $phpVersionDigits = str_replace('.', '', SPP_PHP_VERSION); - $libName = 'lib' . CreatePackages::getPrefix() . "-{$phpVersionDigits}.so"; + $libName = 'libphp-zts-' . $phpVersionDigits . '.so'; $src = BUILD_ROOT_PATH . '/debug/' . $libName . '.debug'; if (!file_exists($src)) { return []; diff --git a/src/package/fpm.php b/src/package/fpm.php index 6092f31..b88eb54 100644 --- a/src/package/fpm.php +++ b/src/package/fpm.php @@ -11,16 +11,22 @@ public function getName(): string { return CreatePackages::getPrefix() . '-fpm'; } - + public function getFpmConfig(): array { $contents = file_get_contents(INI_PATH . '/php-fpm.conf'); $contents = str_replace('$confdir', getConfdir(), $contents); file_put_contents(TEMP_DIR . '/php-fpm.conf', $contents); + $versionedConflicts = CreatePackages::getVersionedConflicts('-fpm'); return [ 'depends' => [ CreatePackages::getPrefix() . '-cli', ], + 'provides' => [ + 'php-zts-fpm', + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ TEMP_DIR . '/php-fpm.conf' => getConfdir() . '/php-fpm.conf', INI_PATH . '/www.conf' => getConfdir() . '/fpm.d/www.conf', diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index a1f9b0e..8e68f52 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -10,6 +10,10 @@ class frankenphp implements package { public function getName(): string { + $phpVersion = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + return 'frankenphp' . $matches[1] . '.' . $matches[2]; + } return 'frankenphp'; } @@ -33,6 +37,33 @@ public function getLicense(): string return 'MIT'; } + /** + * Get list of versioned frankenphp packages to conflict/replace with + */ + private function getVersionedConflicts(): array + { + $conflicts = []; + $phpVersion = SPP_PHP_VERSION; + + if (!preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + return []; + } + + $currentMajor = (int)$matches[1]; + $currentMinor = (int)$matches[2]; + + // Generate conflicts for frankenphp8.0 through frankenphp8.9 + for ($minor = 0; $minor <= 9; $minor++) { + // Skip the current version + if ($currentMajor === 8 && $minor === $currentMinor) { + continue; + } + $conflicts[] = "frankenphp{$currentMajor}.{$minor}"; + } + + return $conflicts; + } + /** * Create FrankenPHP packages (both RPM and DEB) */ @@ -61,20 +92,21 @@ public function createRpmPackage(string $architecture, array $binaryDependencies $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $phpEmbedName = 'lib' . CreatePackages::getPrefix() . '-' . $phpVersion . '.so'; + $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); $output = implode("\n", $output); preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); - $latestTag = $matches[1]; - $version = $latestTag . '_' . $phpVersion; + $version = $matches[1]; - $name = "frankenphp"; + $name = $this->getName(); $computed = (string)$this->getNextIteration($name, $version, $architecture); $iteration = $iterationOverride ?? $computed; + $versionedConflicts = $this->getVersionedConflicts(); + $fpmArgs = [ 'fpm', '-s', 'dir', @@ -85,8 +117,16 @@ public function createRpmPackage(string $architecture, array $binaryDependencies '-v', $version, '--license', $this->getLicense(), '--config-files', '/etc/frankenphp/Caddyfile', + '--provides', 'frankenphp', ]; + foreach ($versionedConflicts as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + $fpmArgs[] = '--replaces'; + $fpmArgs[] = $conflict; + } + foreach ($binaryDependencies as $lib => $dependencyVersion) { $fpmArgs[] = '--depends'; $fpmArgs[] = "$lib({$dependencyVersion})(64bit)"; @@ -160,7 +200,7 @@ public function createDebPackage(string $architecture, array $binaryDependencies $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $phpEmbedName = 'lib' . CreatePackages::getPrefix() . '-' . $phpVersion . '.so'; + $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); @@ -168,12 +208,14 @@ public function createDebPackage(string $architecture, array $binaryDependencies preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); $version = $matches[1]; - $name = "frankenphp"; + $name = $this->getName(); $computed = (string)$this->getNextIteration($name, $version, $architecture); $iteration = $iterationOverride ?? $computed; $debIteration = $iteration; + $versionedConflicts = $this->getVersionedConflicts(); + $fpmArgs = [ 'fpm', '-s', 'dir', @@ -184,8 +226,16 @@ public function createDebPackage(string $architecture, array $binaryDependencies '-v', $version, '--license', $this->getLicense(), '--config-files', '/etc/frankenphp/Caddyfile', + '--provides', 'frankenphp', ]; + foreach ($versionedConflicts as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + $fpmArgs[] = '--replaces'; + $fpmArgs[] = $conflict; + } + $systemLibraryMap = [ 'ld-linux-x86-64.so.2' => 'libc6', 'ld-linux-aarch64.so.1' => 'libc6', diff --git a/src/package/pie.php b/src/package/pie.php index 48874ea..f426207 100644 --- a/src/package/pie.php +++ b/src/package/pie.php @@ -12,7 +12,7 @@ class pie implements package { public function getName(): string { - return 'pie-zts'; + return 'pie-' . CreatePackages::getPrefix(); } /** @@ -49,12 +49,31 @@ public function getFpmConfig(): array [$pharSource, $wrapperSource] = $this->prepareArtifacts(); $prefix = CreatePackages::getPrefix(); + $versionedConflicts = []; + + // Generate conflicts for pie-php-zts8.0, pie-php-zts8.1, etc. + $phpVersion = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + $currentMajor = (int)$matches[1]; + $currentMinor = (int)$matches[2]; + for ($minor = 0; $minor <= 9; $minor++) { + if ($currentMajor === 8 && $minor === $currentMinor) { + continue; + } + $versionedConflicts[] = "pie-php-zts{$currentMajor}.{$minor}"; + } + } return [ 'depends' => [ $prefix . '-cli', $prefix . '-devel', ], + 'provides' => [ + 'pie-zts', + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ $pharSource => '/usr/share/php-zts/pie.phar', $wrapperSource => '/usr/bin/pie-zts', diff --git a/src/package/spx.php b/src/package/spx.php index 1064691..b407b2c 100644 --- a/src/package/spx.php +++ b/src/package/spx.php @@ -9,6 +9,7 @@ class spx extends extension { public function getFpmConfig(): array { + $versionedConflicts = CreatePackages::getVersionedConflicts('-spx'); return [ 'config-files' => [ getConfdir() . '/conf.d/20-spx.ini', @@ -16,8 +17,13 @@ public function getFpmConfig(): array 'depends' => [ CreatePackages::getPrefix() . '-cli' ], + 'provides' => [ + 'php-zts-spx', + ], + 'replaces' => $versionedConflicts, + 'conflicts' => $versionedConflicts, 'files' => [ - BUILD_MODULES_PATH . '/spx.so' => getLibdir() . '/' . CreatePackages::getPrefix() . '/modules/spx.so', + BUILD_MODULES_PATH . '/spx.so' => getLibdir() . '/php-zts/modules/spx.so', $this->getIniPath() => getConfdir() . '/conf.d/20-spx.ini', BUILD_ROOT_PATH . '/share/misc/php-spx/assets/web-ui' => '/usr/share/php-zts/misc/php-spx/assets/web-ui', ] diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 537875d..e9ecc80 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -88,16 +88,6 @@ private static function createGenericPackage(string $name): void $pkg = new $packageClass(); if (method_exists($pkg, 'getVersion')) { $pkgVersion = $pkg->getVersion(); - if ($pkgVersion !== $phpVersion) { - // Extract major and minor version numbers from PHP version - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - $majorMinor = $matches[1] . $matches[2]; // Combine major and minor without dot - $pkgVersion .= '_' . $majorMinor; - } - else { - throw new \RuntimeException("Warning: Could not extract major.minor from PHP version: {$phpVersion}"); - } - } } $package = $pkg ?? new $packageClass(); @@ -270,18 +260,6 @@ private static function getExtensionVersion(string $extension, string $phpVersio echo "Detected version for extension {$extension}: {$extensionVersion}\n"; - // If extension version is different from PHP version, add postfix based on PHP major.minor version - if ($extensionVersion !== $phpVersion) { - // Extract major and minor version numbers from PHP version - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - $majorMinor = $matches[1] . $matches[2]; // Combine major and minor without dot - $extensionVersion .= '_' . $majorMinor; - } - else { - throw new \RuntimeException("Warning: Could not extract major.minor from PHP version: {$phpVersion}"); - } - } - return $extensionVersion; } @@ -363,6 +341,13 @@ private static function createRpmPackage(\staticphp\package $package, string $ph } } + if (isset($config['conflicts']) && is_array($config['conflicts'])) { + foreach ($config['conflicts'] as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + } + } + foreach (self::$binaryDependencies as $lib => $version) { $fpmArgs[] = '--depends'; $fpmArgs[] = "{$lib}({$version})(64bit)"; @@ -442,8 +427,6 @@ private static function createDebPackage( echo "Creating DEB package for {$name}...\n"; - $phpVersion = preg_replace('/_\d+$/', '', $phpVersion); - //$osRelease = parse_ini_file('/etc/os-release'); //$distroCodename = $osRelease['VERSION_CODENAME'] ?? null; //$debIteration = $distroCodename !== '' ? "{$iteration}~{$distroCodename}" : $iteration; @@ -506,6 +489,13 @@ private static function createDebPackage( } } + if (isset($config['conflicts']) && is_array($config['conflicts'])) { + foreach ($config['conflicts'] as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + } + } + $systemLibraryMap = [ 'ld-linux-x86-64.so.2' => 'libc6', 'ld-linux-aarch64.so.1' => 'libc6', @@ -704,6 +694,38 @@ private static function getNextIteration(string $name, string $phpVersion, strin public static function getPrefix(): string { + $phpVersion = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + return 'php-zts' . $matches[1] . '.' . $matches[2]; + } return 'php-zts'; } + + /** + * Get list of versioned package names to conflict/replace with + * For example, for php-zts8.5-cli, returns [php-zts8.0-cli, php-zts8.1-cli, ..., php-zts8.9-cli] excluding 8.5 + */ + public static function getVersionedConflicts(string $suffix): array + { + $conflicts = []; + $phpVersion = SPP_PHP_VERSION; + + if (!preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + return []; + } + + $currentMajor = (int)$matches[1]; + $currentMinor = (int)$matches[2]; + + // Generate conflicts for versions 8.0 through 8.9 + for ($minor = 0; $minor <= 9; $minor++) { + // Skip the current version + if ($currentMajor === 8 && $minor === $currentMinor) { + continue; + } + $conflicts[] = "php-zts{$currentMajor}.{$minor}{$suffix}"; + } + + return $conflicts; + } } From fb0d28390087ef952d3548690fc325f57f2aa8df Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 15 Dec 2025 22:51:55 +0100 Subject: [PATCH 02/37] readme --- README.md | 2 +- bin/spp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index b0178b6..1f6c059 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ A tool for building and packaging PHP and shared extensions with static-php-cli. 1. Clone the repository: ``` - git clone https://github.com/static-php/spc-packages.git + git clone https://github.com/static-php/packages.git cd spc-packages ``` diff --git a/bin/spp b/bin/spp index 841431d..aa25a6b 100755 --- a/bin/spp +++ b/bin/spp @@ -1,9 +1,6 @@ #!/usr/bin/env php Date: Mon, 15 Dec 2025 23:01:53 +0100 Subject: [PATCH 03/37] use common package stream in all streams --- bin/createrepo_static | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/bin/createrepo_static b/bin/createrepo_static index 510a397..871eb18 100755 --- a/bin/createrepo_static +++ b/bin/createrepo_static @@ -56,14 +56,36 @@ def parse_rpm_info(filename): stream = f"8.{php_patch[-1]}" return name, version, release, arch, stream + # Match static-php + match_static = re.match(r'(static-php)-(?P\d+)-(?P\d+)\.(?P[^.]+)\.rpm', basename) + if match_static: + name = match_static.group(1) + version = match_static.group("version") + release = match_static.group("release") + arch = match_static.group("arch") + stream = "common" + return name, version, release, arch, stream + return None def build_module_structure(rpm_map, platform): documents = [] timestamp = int(datetime.now(timezone.utc).strftime('%Y%m%d')) + # Collect common artifacts (static-php) + common_artifacts = [] + if "common" in rpm_map: + for pkg in rpm_map["common"]: + info = parse_rpm_info(pkg) + if info: + name, version, release, arch, _ = info + common_artifacts.append(f"{name}-0:{version}-{release}.{arch}") + for stream, pkg_list in sorted(rpm_map.items()): - artifacts = [] + if stream == "common": + continue + + artifacts = common_artifacts.copy() for pkg in sorted(pkg_list): info = parse_rpm_info(pkg) @@ -128,10 +150,12 @@ for rpm in rpm_files: # Build modules.yaml modules_yaml = build_module_structure(rpm_map, platform) -# Determine highest stream for default +# Determine highest stream for default (exclude "common") if rpm_map: - default_stream = sorted(rpm_map.keys())[-1] # Use highest PHP version - modules_yaml.append(build_defaults_document(default_stream)) + streams = [s for s in rpm_map.keys() if s != "common"] + if streams: + default_stream = sorted(streams)[-1] + modules_yaml.append(build_defaults_document(default_stream)) output_path = os.path.join(os.getcwd(), "modules.yaml") with open(output_path, "w") as f: From 9c631d15ee2eb0762e5e64f80b720f11092c7361 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Dec 2025 10:01:08 +0100 Subject: [PATCH 04/37] delete source dirs after building to save CI space --- composer.lock | 25 +++++++++++++------------ src/step/RunSPC.php | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/composer.lock b/composer.lock index 75cd468..6627f3a 100644 --- a/composer.lock +++ b/composer.lock @@ -81,12 +81,12 @@ "source": { "type": "git", "url": "https://github.com/crazywhalecc/static-php-cli.git", - "reference": "d064e1353cf7dd8e97a769267fc9a804e38169f1" + "reference": "37f604c821217fc56b2229ccdfd6821ec57d8bdb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/d064e1353cf7dd8e97a769267fc9a804e38169f1", - "reference": "d064e1353cf7dd8e97a769267fc9a804e38169f1", + "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/37f604c821217fc56b2229ccdfd6821ec57d8bdb", + "reference": "37f604c821217fc56b2229ccdfd6821ec57d8bdb", "shasum": "" }, "require": { @@ -141,7 +141,7 @@ "type": "other" } ], - "time": "2025-12-15T17:50:20+00:00" + "time": "2025-12-18T08:58:45+00:00" }, { "name": "doctrine/inflector", @@ -2228,12 +2228,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "1553067758ae7f3df13df7c7e232c62d928e1d23" + "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/1553067758ae7f3df13df7c7e232c62d928e1d23", - "reference": "1553067758ae7f3df13df7c7e232c62d928e1d23", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e5034c4df32edeafb119b2c1e2b58876d0286ea8", + "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8", "shasum": "" }, "conflict": { @@ -2255,6 +2255,7 @@ "alextselegidis/easyappointments": "<1.5.2.0-beta1", "alexusmai/laravel-file-manager": "<=3.3.1", "alt-design/alt-redirect": "<1.6.4", + "altcha-org/altcha": "<1.3.1", "alterphp/easyadmin-extension-bundle": ">=1.2,<1.2.11|>=1.3,<1.3.1", "amazing/media2click": ">=1,<1.3.3", "ameos/ameos_tarteaucitron": "<1.2.23", @@ -2278,10 +2279,10 @@ "athlon1600/php-proxy-app": "<=3", "athlon1600/youtube-downloader": "<=4", "austintoddj/canvas": "<=3.4.2", - "auth0/auth0-php": ">=3.3,<=8.16", - "auth0/login": "<=7.18", - "auth0/symfony": "<=5.4.1", - "auth0/wordpress": "<=5.3", + "auth0/auth0-php": ">=3.3,<8.18", + "auth0/login": "<7.20", + "auth0/symfony": "<=5.5", + "auth0/wordpress": "<=5.4", "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", @@ -3224,7 +3225,7 @@ "type": "tidelift" } ], - "time": "2025-12-12T23:06:01+00:00" + "time": "2025-12-17T21:06:23+00:00" } ], "aliases": [], diff --git a/src/step/RunSPC.php b/src/step/RunSPC.php index a72ac51..f2d452b 100644 --- a/src/step/RunSPC.php +++ b/src/step/RunSPC.php @@ -134,7 +134,7 @@ public static function run(bool $debug = false, string $phpVersion = '8.4', ?arr $args[] = '--debug'; } - $process = new Process($args, BASE_PATH . '/vendor/crazywhalecc/static-php-cli'); + $process = new Process($args, BASE_PATH . '/vendor/crazywhalecc/static-php-cli', env: ['CI' => true]); $process->setTimeout(null); if (Process::isTtySupported()) { $process->setTty(true); // Interactive mode From c57c693147a08a0d1683aa8bb8f37dddc63ce3d1 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Dec 2025 12:55:50 +0100 Subject: [PATCH 05/37] versioned fully? WIP --- bin/spp | 46 ++++++- config/templates/craft.yml.twig | 6 +- src/Command/BaseCommand.php | 2 +- src/extension.php | 23 +++- src/package/cgi.php | 4 +- src/package/cli.php | 61 +++++++--- src/package/devel.php | 18 +-- src/package/fpm.php | 38 +++++- src/package/frankenphp.php | 35 +++--- src/package/pie.php | 44 ++++--- src/package/spx.php | 4 +- src/step/CreatePackages.php | 209 +++++++++++++++++++++++++++++++- src/util/TwigRenderer.php | 7 +- 13 files changed, 416 insertions(+), 81 deletions(-) diff --git a/bin/spp b/bin/spp index aa25a6b..b71f299 100755 --- a/bin/spp +++ b/bin/spp @@ -7,6 +7,7 @@ const INI_PATH = BASE_PATH . '/src/ini'; const DIST_PATH = BASE_PATH . '/dist'; const DIST_RPM_PATH = DIST_PATH . '/rpm'; const DIST_DEB_PATH = DIST_PATH . '/deb'; +const DIST_APK_PATH = DIST_PATH . '/apk'; define('TEMP_DIR', sys_get_temp_dir() . '/spc-packages'); @@ -71,5 +72,48 @@ function getLibdir(): string function getConfdir(): string { - return '/etc/php-zts'; + // Get the prefix which is package-type aware (php-zts for RPM, php-zts8.3 for DEB, php-zts83 for APK) + $prefix = \staticphp\step\CreatePackages::getPrefix(); + return '/etc/' . $prefix; +} + +function getModuledir(): string +{ + // Get the prefix which is package-type aware + $prefix = \staticphp\step\CreatePackages::getPrefix(); + return getLibdir() . '/' . $prefix . '/modules'; +} + +function getPhpLibdir(): string +{ + // Get the prefix which is package-type aware + $prefix = \staticphp\step\CreatePackages::getPrefix(); + return getLibdir() . '/' . $prefix; +} + +function getBinarySuffix(): string +{ + // Get the suffix for binaries based on package type + // RPM: -zts, DEB: -zts8.3, APK: -zts83 + $prefix = \staticphp\step\CreatePackages::getPrefix(); + + // Extract the version suffix from the prefix + // php-zts -> -zts + // php-zts8.3 -> -zts8.3 + // php-zts83 -> -zts83 + return str_replace('php', '', $prefix); +} + +function getVarLibdir(): string +{ + // Get /var/lib/{prefix} path + $prefix = \staticphp\step\CreatePackages::getPrefix(); + return '/var/lib/' . $prefix; +} + +function getSharedir(): string +{ + // Get /usr/share/{prefix} path + $prefix = \staticphp\step\CreatePackages::getPrefix(); + return '/usr/share/' . $prefix; } diff --git a/config/templates/craft.yml.twig b/config/templates/craft.yml.twig index 67d5253..a7a9d9c 100644 --- a/config/templates/craft.yml.twig +++ b/config/templates/craft.yml.twig @@ -34,8 +34,8 @@ build-options: with-suggested-libs: true with-suggested-exts: true no-strip: false - with-config-file-path: '/etc/php-zts' - with-config-file-scan-dir: '/etc/php-zts/conf.d' + with-config-file-path: '{{ confdir }}' + with-config-file-scan-dir: '{{ confdir }}/conf.d' # Hardcoded INI options for cli and micro SAPI (e.g. "memory_limit=4G", list accepted) with-hardcoded-ini: [ ] # UPX pack the binary (default: false) @@ -86,7 +86,7 @@ extra-env: {% else -%} SPC_TARGET: '{{ target }}' {% endif -%} - EXTENSION_DIR: "{{ is_rhel ? '/usr/lib64/php-zts/modules' : '/usr/lib/php-zts/modules' }}" + EXTENSION_DIR: "{{ moduledir }}" SPC_CMD_VAR_PHP_EMBED_TYPE: 'shared' SPC_MICRO_PATCHES: SPC_DEFAULT_C_FLAGS: "{{ cflags }}" diff --git a/src/Command/BaseCommand.php b/src/Command/BaseCommand.php index 588c86e..ef10311 100644 --- a/src/Command/BaseCommand.php +++ b/src/Command/BaseCommand.php @@ -44,7 +44,7 @@ protected function initialize(InputInterface $input, OutputInterface $output) protected function createDirectories(): void { - $paths = [BUILD_ROOT_PATH, BUILD_BIN_PATH, BUILD_LIB_PATH, BUILD_MODULES_PATH, DIST_PATH, DIST_RPM_PATH, DIST_DEB_PATH]; + $paths = [BUILD_ROOT_PATH, BUILD_BIN_PATH, BUILD_LIB_PATH, BUILD_MODULES_PATH, DIST_PATH, DIST_RPM_PATH, DIST_DEB_PATH, DIST_APK_PATH]; foreach ($paths as $path) { if (!is_dir($path) && !mkdir($path, 0755, true) && !is_dir($path)) { throw new \RuntimeException("Failed to create directory: " . $path); diff --git a/src/extension.php b/src/extension.php index 3414fc0..8395cef 100644 --- a/src/extension.php +++ b/src/extension.php @@ -163,7 +163,7 @@ public function getFpmConfig(): array : [] ), ...($this->isSharedExtension() ? - [BUILD_MODULES_PATH . '/' . $this->name . '.so' => getLibdir() . '/php-zts/modules/' . $this->name . '.so'] + [BUILD_MODULES_PATH . '/' . $this->name . '.so' => getModuledir() . '/' . $this->name . '.so'] : [] ), ] @@ -185,9 +185,24 @@ protected function getIniPath(): ?string } $tempIniPath = TEMP_DIR . '/' . $this->prefix . $this->name . '.ini'; $iniContent = file_get_contents($iniPath); + + // Get the dynamic prefix for path replacements + $prefix = CreatePackages::getPrefix(); + + // Replace extension directives and versioned paths $iniContent = str_replace( - [';extension=' . $this->name, ';zend_extension=' . $this->name], - ['extension=' . $this->name, 'zend_extension=' . $this->name], + [ + ';extension=' . $this->name, + ';zend_extension=' . $this->name, + '/usr/share/php-zts/', + '/usr/local/share/php-zts/', + ], + [ + 'extension=' . $this->name, + 'zend_extension=' . $this->name, + '/usr/share/' . $prefix . '/', + '/usr/local/share/' . $prefix . '/', + ], $iniContent ); file_put_contents($tempIniPath, $iniContent); @@ -215,7 +230,7 @@ public function getDebuginfoFpmConfig(): array if (!file_exists($src)) { return []; } - $targetSo = getLibdir() . '/php-zts/modules/' . $this->name . '.so'; + $targetSo = getModuledir() . '/' . $this->name . '.so'; $target = '/usr/lib/debug' . $targetSo . '.debug'; return [ 'depends' => [CreatePackages::getPrefix() . '-' . $this->name], diff --git a/src/package/cgi.php b/src/package/cgi.php index e518b21..f91d8b6 100644 --- a/src/package/cgi.php +++ b/src/package/cgi.php @@ -25,7 +25,7 @@ public function getFpmConfig(): array 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ - BUILD_BIN_PATH . '/php-cgi' => '/usr/bin/php-cgi-zts', + BUILD_BIN_PATH . '/php-cgi' => '/usr/bin/php-cgi' . getBinarySuffix(), ] ]; } @@ -44,7 +44,7 @@ public function getDebuginfoFpmConfig(): array return [ 'depends' => [CreatePackages::getPrefix() . '-cgi'], 'files' => [ - $src => '/usr/lib/debug/usr/bin/php-cgi-zts.debug', + $src => '/usr/lib/debug/usr/bin/php-cgi' . getBinarySuffix() . '.debug', ], ]; } diff --git a/src/package/cli.php b/src/package/cli.php index ebae08f..09daf7b 100644 --- a/src/package/cli.php +++ b/src/package/cli.php @@ -20,7 +20,17 @@ public function getFpmConfig(): array $prefix = CreatePackages::getPrefix(); $contents = file_get_contents(INI_PATH . '/php.ini'); - $contents = str_replace('$libdir', getLibdir() . '/php-zts', $contents); + $contents = str_replace( + [ + '$libdir', + '/etc/php-zts', + ], + [ + getPhpLibdir(), + getConfdir(), + ], + $contents + ); file_put_contents(TEMP_DIR . '/php.ini', $contents); $provides = ['php-zts', $prefix]; $versionedConflicts = CreatePackages::getVersionedConflicts('-cli'); @@ -32,7 +42,7 @@ public function getFpmConfig(): array ]; $files = [ TEMP_DIR . '/php.ini' => getConfdir() . '/php.ini', - BUILD_BIN_PATH . '/php' => '/usr/bin/php-zts', + BUILD_BIN_PATH . '/php' => '/usr/bin/php' . getBinarySuffix(), ]; foreach ($staticExtensions as $ext) { @@ -42,7 +52,23 @@ public function getFpmConfig(): array // Add .ini files for statically compiled extensions $iniFile = INI_PATH . "/extension/{$ext}.ini"; if (file_exists($iniFile)) { - $files[$iniFile] = getConfdir() . "/conf.d/{$ext}.ini"; + // Process the .ini file to replace hardcoded paths + $iniContents = file_get_contents($iniFile); + $iniContents = str_replace( + [ + '/usr/share/php-zts/', + '/usr/local/share/php-zts/', + ], + [ + getSharedir() . '/', + '/usr/local/share/' . $prefix . '/', + ], + $iniContents + ); + $tempIniPath = TEMP_DIR . "/{$ext}.ini"; + file_put_contents($tempIniPath, $iniContents); + + $files[$tempIniPath] = getConfdir() . "/conf.d/{$ext}.ini"; $configFiles[] = getConfdir() . "/conf.d/{$ext}.ini"; } } @@ -50,21 +76,21 @@ public function getFpmConfig(): array if (!file_exists(BUILD_ROOT_PATH . '/license/LICENSE')) { copy(BASE_PATH . '/LICENSE', BUILD_ROOT_PATH . '/license/LICENSE'); } - $files[BUILD_ROOT_PATH . '/license'] = '/usr/share/licenses/php-zts/'; + $files[BUILD_ROOT_PATH . '/license'] = '/usr/share/licenses/' . CreatePackages::getPrefix() . '/'; return [ 'config-files' => $configFiles, 'empty_directories' => [ - '/usr/share/php-zts/preload', - '/var/lib/php-zts/session', - '/var/lib/php-zts/wsdlcache', - '/var/lib/php-zts/opcache', + getSharedir() . '/preload', + getVarLibdir() . '/session', + getVarLibdir() . '/wsdlcache', + getVarLibdir() . '/opcache', ], 'directories' => [ - '/usr/share/php-zts/preload', - '/var/lib/php-zts/session', - '/var/lib/php-zts/wsdlcache', - '/var/lib/php-zts/opcache', + getSharedir() . '/preload', + getVarLibdir() . '/session', + getVarLibdir() . '/wsdlcache', + getVarLibdir() . '/opcache', ], 'provides' => $provides, 'replaces' => $replaces, @@ -75,15 +101,16 @@ public function getFpmConfig(): array public function getFpmExtraArgs(): array { - $afterInstallScript = <<<'BASH' + $binarySuffix = getBinarySuffix(); + $afterInstallScript = << [CreatePackages::getPrefix() . '-cli'], 'files' => [ diff --git a/src/package/devel.php b/src/package/devel.php index e573d7b..9ab1ca8 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -55,7 +55,7 @@ public function getFpmConfig(): array ], [ 'prefix="/usr"', - 'datarootdir="/php-zts"', + 'datarootdir="/' . \staticphp\step\CreatePackages::getPrefix() . '"', ], $phpizeContent ); @@ -65,8 +65,8 @@ public function getFpmConfig(): array '"`eval echo ${prefix}/include`/php"' ], [ - str_replace('/usr/', '', getLibdir()) . '/php-zts`', - '"`eval echo ${prefix}/include`/php-zts"' + str_replace('/usr/', '', getPhpLibdir()) . '`', + '"`eval echo ${prefix}/include`/' . \staticphp\step\CreatePackages::getPrefix() . '"' ], $phpizeContent ); @@ -77,18 +77,18 @@ public function getFpmConfig(): array $versionedConflicts = CreatePackages::getVersionedConflicts('-devel'); return [ 'files' => [ - $modifiedPhpConfigPath => '/usr/bin/php-config-zts', - $modifiedPhpizePath => '/usr/bin/phpize-zts', - BUILD_INCLUDE_PATH . '/php/' => '/usr/include/php-zts', - BUILD_LIB_PATH . '/php/build' => getLibdir() . '/php-zts', + $modifiedPhpConfigPath => '/usr/bin/php-config' . getBinarySuffix(), + $modifiedPhpizePath => '/usr/bin/phpize' . getBinarySuffix(), + BUILD_INCLUDE_PATH . '/php/' => '/usr/include/' . \staticphp\step\CreatePackages::getPrefix(), + BUILD_LIB_PATH . '/php/build' => getPhpLibdir(), ], 'depends' => [ CreatePackages::getPrefix() . '-cli', ], 'provides' => [ 'php-zts-devel', - 'php-config-zts', - 'phpize-zts', + 'php-config' . getBinarySuffix(), + 'phpize' . getBinarySuffix(), ], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, diff --git a/src/package/fpm.php b/src/package/fpm.php index b88eb54..95ad371 100644 --- a/src/package/fpm.php +++ b/src/package/fpm.php @@ -17,6 +17,32 @@ public function getFpmConfig(): array $contents = file_get_contents(INI_PATH . '/php-fpm.conf'); $contents = str_replace('$confdir', getConfdir(), $contents); file_put_contents(TEMP_DIR . '/php-fpm.conf', $contents); + + // Process the systemd service file to replace hardcoded paths + $serviceContents = file_get_contents(INI_PATH . '/php-fpm.service'); + $binarySuffix = getBinarySuffix(); + $serviceContents = str_replace( + [ + '/usr/sbin/php-fpm-zts', + 'RuntimeDirectory=php-fpm-zts', + ], + [ + '/usr/sbin/php-fpm' . $binarySuffix, + 'RuntimeDirectory=php-fpm' . $binarySuffix, + ], + $serviceContents + ); + file_put_contents(TEMP_DIR . '/php-fpm.service', $serviceContents); + + // Process www.conf to replace hardcoded paths + $wwwContents = file_get_contents(INI_PATH . '/www.conf'); + $wwwContents = str_replace( + '/var/lib/php-zts/', + getVarLibdir() . '/', + $wwwContents + ); + file_put_contents(TEMP_DIR . '/www.conf', $wwwContents); + $versionedConflicts = CreatePackages::getVersionedConflicts('-fpm'); return [ 'depends' => [ @@ -29,17 +55,17 @@ public function getFpmConfig(): array 'conflicts' => $versionedConflicts, 'files' => [ TEMP_DIR . '/php-fpm.conf' => getConfdir() . '/php-fpm.conf', - INI_PATH . '/www.conf' => getConfdir() . '/fpm.d/www.conf', - INI_PATH . '/php-fpm.service' => '/usr/lib/systemd/system/php-fpm-zts.service', - BUILD_BIN_PATH . '/php-fpm' => '/usr/sbin/php-fpm-zts', + TEMP_DIR . '/www.conf' => getConfdir() . '/fpm.d/www.conf', + TEMP_DIR . '/php-fpm.service' => '/usr/lib/systemd/system/php-fpm' . getBinarySuffix() . '.service', + BUILD_BIN_PATH . '/php-fpm' => '/usr/sbin/php-fpm' . getBinarySuffix(), ], 'empty_directories' => [ getConfdir() . '/fpm.d/', - '/var/log/php-zts/php-fpm', + '/var/log/' . CreatePackages::getPrefix() . '/php-fpm', ], 'directories' => [ getConfdir() . '/fpm.d/', - '/var/log/php-zts/php-fpm', + '/var/log/' . CreatePackages::getPrefix() . '/php-fpm', ], ]; } @@ -55,7 +81,7 @@ public function getDebuginfoFpmConfig(): array if (!file_exists($src)) { return []; } - $target = '/usr/lib/debug/usr/sbin/php-fpm-zts.debug'; + $target = '/usr/lib/debug/usr/sbin/php-fpm' . getBinarySuffix() . '.debug'; return [ 'depends' => [CreatePackages::getPrefix() . '-fpm'], 'files' => [ diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index 8e68f52..c5f3b13 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -10,9 +10,13 @@ class frankenphp implements package { public function getName(): string { - $phpVersion = SPP_PHP_VERSION; - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - return 'frankenphp' . $matches[1] . '.' . $matches[2]; + // RPM packages use frankenphp (unversioned, for module system) + // DEB/APK packages use versioned frankenphp8.3 or frankenphp83 + $prefix = CreatePackages::getPrefix(); + + // Extract version from prefix (e.g., "php-zts8.3" -> "8.3", "php-zts" -> "") + if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + return 'frankenphp' . $matches[1]; } return 'frankenphp'; } @@ -39,26 +43,17 @@ public function getLicense(): string /** * Get list of versioned frankenphp packages to conflict/replace with + * For RPM packages, returns empty array (RPM uses module system instead) */ private function getVersionedConflicts(): array { - $conflicts = []; - $phpVersion = SPP_PHP_VERSION; - - if (!preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - return []; - } + // Get the conflicts list from CreatePackages using the franken prefix + $phpConflicts = CreatePackages::getVersionedConflicts(''); - $currentMajor = (int)$matches[1]; - $currentMinor = (int)$matches[2]; - - // Generate conflicts for frankenphp8.0 through frankenphp8.9 - for ($minor = 0; $minor <= 9; $minor++) { - // Skip the current version - if ($currentMajor === 8 && $minor === $currentMinor) { - continue; - } - $conflicts[] = "frankenphp{$currentMajor}.{$minor}"; + // Transform php-zts8.3 conflicts to frankenphp8.3 conflicts + $conflicts = []; + foreach ($phpConflicts as $conflict) { + $conflicts[] = str_replace('php-zts', 'frankenphp', $conflict); } return $conflicts; @@ -88,6 +83,7 @@ public function createPackages(array $packageTypes, array $binaryDependencies, ? */ public function createRpmPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { + CreatePackages::setCurrentPackageType('rpm'); echo "Creating RPM package for FrankenPHP...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; @@ -196,6 +192,7 @@ public function createRpmPackage(string $architecture, array $binaryDependencies */ public function createDebPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { + CreatePackages::setCurrentPackageType('deb'); echo "Creating DEB package for FrankenPHP...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; diff --git a/src/package/pie.php b/src/package/pie.php index f426207..057209f 100644 --- a/src/package/pie.php +++ b/src/package/pie.php @@ -49,19 +49,12 @@ public function getFpmConfig(): array [$pharSource, $wrapperSource] = $this->prepareArtifacts(); $prefix = CreatePackages::getPrefix(); - $versionedConflicts = []; - // Generate conflicts for pie-php-zts8.0, pie-php-zts8.1, etc. - $phpVersion = SPP_PHP_VERSION; - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - $currentMajor = (int)$matches[1]; - $currentMinor = (int)$matches[2]; - for ($minor = 0; $minor <= 9; $minor++) { - if ($currentMajor === 8 && $minor === $currentMinor) { - continue; - } - $versionedConflicts[] = "pie-php-zts{$currentMajor}.{$minor}"; - } + // Get versioned conflicts for pie packages (pie-php-zts8.0, pie-php-zts8.1, etc.) + $phpConflicts = CreatePackages::getVersionedConflicts(''); + $versionedConflicts = []; + foreach ($phpConflicts as $conflict) { + $versionedConflicts[] = 'pie-' . $conflict; } return [ @@ -75,8 +68,8 @@ public function getFpmConfig(): array 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ - $pharSource => '/usr/share/php-zts/pie.phar', - $wrapperSource => '/usr/bin/pie-zts', + $pharSource => getSharedir() . '/pie.phar', + $wrapperSource => '/usr/bin/pie' . getBinarySuffix(), ], ]; } @@ -103,7 +96,28 @@ private function prepareArtifacts(): array $this->downloadLatestPiePhar($pharPath); } - $wrapperPath = INI_PATH . '/pie-zts'; + // Process the pie wrapper script to replace hardcoded paths + $wrapperSource = INI_PATH . '/pie-zts'; + $wrapperPath = TEMP_DIR . '/pie' . getBinarySuffix(); + $wrapperContents = file_get_contents($wrapperSource); + $binarySuffix = getBinarySuffix(); + + $wrapperContents = str_replace( + [ + '/usr/bin/php-zts', + '/usr/share/php-zts/', + '/usr/bin/php-config-zts', + ], + [ + '/usr/bin/php' . $binarySuffix, + getSharedir() . '/', + '/usr/bin/php-config' . $binarySuffix, + ], + $wrapperContents + ); + file_put_contents($wrapperPath, $wrapperContents); + chmod($wrapperPath, 0755); + return [$pharPath, $wrapperPath]; } diff --git a/src/package/spx.php b/src/package/spx.php index b407b2c..cf48843 100644 --- a/src/package/spx.php +++ b/src/package/spx.php @@ -23,9 +23,9 @@ public function getFpmConfig(): array 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ - BUILD_MODULES_PATH . '/spx.so' => getLibdir() . '/php-zts/modules/spx.so', + BUILD_MODULES_PATH . '/spx.so' => getModuledir() . '/spx.so', $this->getIniPath() => getConfdir() . '/conf.d/20-spx.ini', - BUILD_ROOT_PATH . '/share/misc/php-spx/assets/web-ui' => '/usr/share/php-zts/misc/php-spx/assets/web-ui', + BUILD_ROOT_PATH . '/share/misc/php-spx/assets/web-ui' => '/usr/share/' . \staticphp\step\CreatePackages::getPrefix() . '/misc/php-spx/assets/web-ui', ] ]; } diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index e9ecc80..87eb3b0 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -16,7 +16,12 @@ class CreatePackages private static $binaryDependencies = []; private static $packageTypes = []; private static ?string $iterationOverride = null; + private static ?string $currentPackageType = null; + public static function setCurrentPackageType(?string $type): void + { + self::$currentPackageType = $type; + } public static function run($packageNames = null, string $packageTypes = 'rpm,deb', string $phpVersion = '8.4', ?string $iteration = null): true { @@ -272,10 +277,15 @@ private static function createPackageWithFpm(\staticphp\package $package, string if (in_array('deb', self::$packageTypes, true)) { self::createDebPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); } + + if (in_array('apk', self::$packageTypes, true)) { + self::createApkPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); + } } private static function createRpmPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void { + self::$currentPackageType = 'rpm'; $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); @@ -421,6 +431,7 @@ private static function createDebPackage( bool $isDebuginfo = false, ): void { + self::$currentPackageType = 'deb'; $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); @@ -581,6 +592,165 @@ private static function createDebPackage( echo "DEB package created: " . DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}_{$architecture}.deb\n"; } + private static function createApkPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void + { + self::$currentPackageType = 'apk'; + $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); + $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); + $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); + + echo "Creating APK package for {$name}...\n"; + + // APK uses r{iteration} format for revision number + $apkIteration = $iteration; + $fullVersion = "{$phpVersion}-r{$apkIteration}"; + + $fpmArgs = [...[ + 'fpm', + '-s', 'dir', + '-t', 'apk', + '-p', DIST_APK_PATH, + '--name', $name, + '--version', $phpVersion, + '--architecture', $architecture, + '--iteration', $apkIteration, + '--description', "Static PHP Package for {$name}", + '--license', $package->getLicense(), + '--maintainer', 'Marc Henderkes ', + '--vendor', 'Marc Henderkes ', + '--url', 'apks.henderkes.com', + ], ...$extraArgs]; + + // Ensure non-CLI packages depend on the same PHP major.minor as php-zts-cli (ignore iteration/patch) + if ($name !== self::getPrefix() . '-cli') { + [$fullPhpVersion] = self::getPhpVersionAndArchitecture(); + if (preg_match('/^(\d+)\.(\d+)/', $fullPhpVersion, $m)) { + $maj = (int)$m[1]; + $min = (int)$m[2]; + $nextMin = $min + 1; + $lowerBound = sprintf('%d.%d', $maj, $min); + $upperBound = sprintf('%d.%d', $maj, $nextMin); + // APK dependency format: package>=version and package={$lowerBound}"; + $fpmArgs[] = '--depends'; + $fpmArgs[] = self::getPrefix() . "-cli<{$upperBound}"; + } + } + + // If this is a debuginfo package, make it depend exactly on its base package version-iteration + if (str_ends_with($name, '-debuginfo')) { + $base = preg_replace('/-debuginfo$/', '', $name); + $fpmArgs[] = '--depends'; + $fpmArgs[] = sprintf('%s=%s', $base, $fullVersion); + } + + if (isset($config['provides']) && is_array($config['provides'])) { + foreach ($config['provides'] as $provide) { + $fpmArgs[] = '--provides'; + $fpmArgs[] = "{$provide}={$fullVersion}"; + } + } + + if (isset($config['replaces']) && is_array($config['replaces'])) { + foreach ($config['replaces'] as $replace) { + $fpmArgs[] = '--replaces'; + $fpmArgs[] = $replace; + } + } + + if (isset($config['conflicts']) && is_array($config['conflicts'])) { + foreach ($config['conflicts'] as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + } + } + + // Alpine library dependencies - simpler naming than Debian + foreach (self::$binaryDependencies as $lib => $version) { + // For Alpine, we can use a simpler approach - most .so files map to package names + // by removing the .so suffix and version numbers + $packageName = preg_replace('/\.so(\.\d+)*$/', '', $lib); + + // Common Alpine package mappings + $alpineLibMap = [ + 'ld-linux-x86-64' => 'musl', + 'ld-linux-aarch64' => 'musl', + 'libc' => 'musl', + 'libm' => 'musl', + 'libpthread' => 'musl', + 'libutil' => 'musl', + 'libdl' => 'musl', + 'librt' => 'musl', + 'libresolv' => 'musl', + ]; + + if (isset($alpineLibMap[$packageName])) { + $packageName = $alpineLibMap[$packageName]; + } + + $numericVersion = preg_replace('/[^0-9.]/', '', $version); + $fpmArgs[] = '--depends'; + $fpmArgs[] = "{$packageName}>={$numericVersion}"; + } + + if (isset($config['depends']) && is_array($config['depends'])) { + foreach ($config['depends'] as $depend) { + $fpmArgs[] = '--depends'; + $fpmArgs[] = $depend; + } + } + + if (isset($config['directories']) && is_array($config['directories'])) { + foreach ($config['directories'] as $dir) { + $fpmArgs[] = '--directories'; + $fpmArgs[] = $dir; + } + } + + if (isset($config['config-files']) && is_array($config['config-files'])) { + foreach ($config['config-files'] as $configFile) { + $fpmArgs[] = '--config-files'; + $fpmArgs[] = $configFile; + } + } + + if (isset($config['files']) && is_array($config['files'])) { + foreach ($config['files'] as $source => $dest) { + if (file_exists($source)) { + $fpmArgs[] = $source . '=' . $dest; + } + else { + echo "Warning: Source file not found: {$source}\n"; + } + } + } + + if (isset($config['empty_directories']) && is_array($config['empty_directories'])) { + $emptyDir = TEMP_DIR . '/spp_empty'; + if (!file_exists($emptyDir) && !mkdir($emptyDir, 0755, true) && !is_dir($emptyDir)) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', $emptyDir)); + } + if (is_dir($emptyDir)) { + $files = array_diff((array)scandir($emptyDir), ['.', '..']); + if (!empty($files)) { + exec('rm -rf ' . escapeshellarg($emptyDir . '/*')); + } + } + foreach ($config['empty_directories'] as $dir) { + $fpmArgs[] = $emptyDir . '=' . $dir; + } + } + + $apkProcess = new Process($fpmArgs); + $apkProcess->setTimeout(null); + $apkProcess->run(function ($type, $buffer) { + echo $buffer; + }); + + echo "APK package created: " . DIST_APK_PATH . "/{$name}-{$phpVersion}-r{$apkIteration}.{$architecture}.apk\n"; + } + private static function getPhpVersionAndArchitecture(): array { if (!empty(self::$versionArch)) { @@ -689,12 +859,37 @@ private static function getNextIteration(string $name, string $phpVersion, strin } } + $apkPattern = DIST_APK_PATH . "/{$name}-{$phpVersion}-r*.{$architecture}.apk"; + $apkFiles = glob($apkPattern); + + foreach ($apkFiles as $file) { + if (preg_match("/{$name}-{$phpVersion}-r(\d+)\.{$architecture}\.apk$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } + } + return $maxIteration + 1; } public static function getPrefix(): string { $phpVersion = SPP_PHP_VERSION; + + // RPM packages always use php-zts (for module system) + if (self::$currentPackageType === 'rpm') { + return 'php-zts'; + } + + // APK packages use php-zts83 (no dot) + if (self::$currentPackageType === 'apk') { + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + return 'php-zts' . $matches[1] . $matches[2]; + } + return 'php-zts'; + } + + // DEB packages use php-zts8.3 (with dot) if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { return 'php-zts' . $matches[1] . '.' . $matches[2]; } @@ -704,9 +899,15 @@ public static function getPrefix(): string /** * Get list of versioned package names to conflict/replace with * For example, for php-zts8.5-cli, returns [php-zts8.0-cli, php-zts8.1-cli, ..., php-zts8.9-cli] excluding 8.5 + * For RPM packages, returns empty array (RPM uses module system instead) */ public static function getVersionedConflicts(string $suffix): array { + // RPM packages use module system, no versioned conflicts needed + if (self::$currentPackageType === 'rpm') { + return []; + } + $conflicts = []; $phpVersion = SPP_PHP_VERSION; @@ -723,7 +924,13 @@ public static function getVersionedConflicts(string $suffix): array if ($currentMajor === 8 && $minor === $currentMinor) { continue; } - $conflicts[] = "php-zts{$currentMajor}.{$minor}{$suffix}"; + + // APK uses php-zts83 format (no dot), DEB uses php-zts8.3 (with dot) + if (self::$currentPackageType === 'apk') { + $conflicts[] = "php-zts{$currentMajor}{$minor}{$suffix}"; + } else { + $conflicts[] = "php-zts{$currentMajor}.{$minor}{$suffix}"; + } } return $conflicts; diff --git a/src/util/TwigRenderer.php b/src/util/TwigRenderer.php index c109d1d..50e235f 100644 --- a/src/util/TwigRenderer.php +++ b/src/util/TwigRenderer.php @@ -52,13 +52,18 @@ public static function renderCraftTemplate(string $phpVersion = '8.4', ?string $ } // Prepare template variables + $prefix = CreatePackages::getPrefix(); + $libdir = in_array($majorOsVersion, ['7', '8', '9', '10']) ? '/usr/lib64' : '/usr/lib'; + $templateVars = [ 'php_version' => $phpVersion, 'php_version_nodot' => str_replace('.', '', $phpVersion), 'target' => SPP_TARGET, 'arch' => $arch, 'os' => $majorOsVersion, - 'prefix' => CreatePackages::getPrefix(), + 'prefix' => $prefix, + 'confdir' => '/etc/' . $prefix, + 'moduledir' => $libdir . '/' . $prefix . '/modules', // Optional filter: when provided, craft.yml will include only selected packages // across extensions/shared-extensions/sapi, while always including cli SAPI. 'filter_packages' => $packages, From ea3404856ebfb9b1a309ec6abecc0eefdd3c6ce6 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Dec 2025 19:31:42 +0100 Subject: [PATCH 06/37] add package info for alpine --- src/package/frankenphp.php | 132 ++++++++++++++++++ .../frankenphp/alpine/frankenphp.openrc | 28 ++++ .../frankenphp/alpine/post-deinstall.sh | 13 ++ src/package/frankenphp/alpine/post-install.sh | 29 ++++ .../frankenphp/alpine/pre-deinstall.sh | 11 ++ 5 files changed, 213 insertions(+) create mode 100644 src/package/frankenphp/alpine/frankenphp.openrc create mode 100644 src/package/frankenphp/alpine/post-deinstall.sh create mode 100644 src/package/frankenphp/alpine/post-install.sh create mode 100644 src/package/frankenphp/alpine/pre-deinstall.sh diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index c5f3b13..c1aa635 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -76,6 +76,9 @@ public function createPackages(array $packageTypes, array $binaryDependencies, ? if (in_array('deb', $packageTypes, true)) { $this->createDebPackage($architecture, $binaryDependencies, $iterationOverride); } + if (in_array('apk', $packageTypes, true)) { + $this->createApkPackage($architecture, $binaryDependencies, $iterationOverride); + } } /** @@ -316,6 +319,135 @@ public function createDebPackage(string $architecture, array $binaryDependencies } } + /** + * Create APK package for FrankenPHP + */ + public function createApkPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void + { + CreatePackages::setCurrentPackageType('apk'); + echo "Creating APK package for FrankenPHP...\n"; + + $packageFolder = DIST_PATH . '/frankenphp/package'; + $phpVersion = str_replace('.', '', SPP_PHP_VERSION); + $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; + + $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; + [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); + $output = implode("\n", $output); + preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); + $version = $matches[1]; + + $name = $this->getName(); + + $computed = (string)$this->getNextIteration($name, $version, $architecture); + $iteration = $iterationOverride ?? $computed; + + $versionedConflicts = $this->getVersionedConflicts(); + + $fpmArgs = [ + 'fpm', + '-s', 'dir', + '-t', 'apk', + '-p', DIST_APK_PATH, + '-n', $name, + '-v', $version, + '--license', $this->getLicense(), + '--config-files', '/etc/frankenphp/Caddyfile', + '--provides', 'frankenphp', + ]; + + foreach ($versionedConflicts as $conflict) { + $fpmArgs[] = '--conflicts'; + $fpmArgs[] = $conflict; + $fpmArgs[] = '--replaces'; + $fpmArgs[] = $conflict; + } + + // Alpine library dependencies - simpler naming than Debian + $alpineLibMap = [ + 'ld-linux-x86-64.so.2' => 'musl', + 'ld-linux-aarch64.so.1' => 'musl', + 'libc.so.6' => 'musl', + 'libm.so.6' => 'musl', + 'libpthread.so.0' => 'musl', + 'libutil.so.1' => 'musl', + 'libdl.so.2' => 'musl', + 'librt.so.1' => 'musl', + 'libresolv.so.2' => 'musl', + 'libgcc_s.so.1' => 'libgcc', + 'libstdc++.so.6' => 'libstdc++', + ]; + + foreach ($binaryDependencies as $lib => $ver) { + if (isset($alpineLibMap[$lib])) { + // Use mapped name for system libraries + $packageName = $alpineLibMap[$lib]; + } + else { + // For other libraries, remove .so suffix + $packageName = preg_replace('/\.so(\.\d+)*$/', '', $lib); + } + + $numericVersion = preg_replace('/[^0-9.]/', '', $ver); + $fpmArgs[] = '--depends'; + $fpmArgs[] = "{$packageName}>={$numericVersion}"; + } + + if (!is_dir("{$packageFolder}/empty/") && !mkdir("{$packageFolder}/empty/", 0755, true) && !is_dir("{$packageFolder}/empty/")) { + throw new \RuntimeException(sprintf('Directory "%s" was not created', "{$packageFolder}/empty/")); + } + + $alpineFolder = BASE_PATH . '/src/package/frankenphp'; + + $fpmArgs = [...$fpmArgs, ...[ + '--depends', $phpEmbedName, + '--after-install', "{$alpineFolder}/alpine/post-install.sh", + '--before-remove', "{$alpineFolder}/alpine/pre-deinstall.sh", + '--after-remove', "{$alpineFolder}/alpine/post-deinstall.sh", + '--iteration', $iteration, + BUILD_BIN_PATH . '/frankenphp=/usr/bin/frankenphp', + "{$alpineFolder}/alpine/frankenphp.openrc=/etc/init.d/frankenphp", + "{$packageFolder}/Caddyfile=/etc/frankenphp/Caddyfile", + "{$packageFolder}/content/=/usr/share/frankenphp", + "{$packageFolder}/empty/=/var/lib/frankenphp", + "{$packageFolder}/empty/=/etc/frankenphp/Caddyfile.d", + ]]; + + $apkProcess = new Process($fpmArgs); + $apkProcess->setTimeout(null); + $apkProcess->run(function ($type, $buffer) { + echo $buffer; + }); + + echo "APK package created: " . DIST_APK_PATH . "/{$name}-{$version}-r{$iteration}.{$architecture}.apk\n"; + + // Create FrankenPHP debuginfo package if debug file exists + $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; + if (file_exists($frankenDbg)) { + $dbgArgs = [ + 'fpm', + '-s', 'dir', + '-t', 'apk', + '-p', DIST_APK_PATH, + '-n', $name . '-debuginfo', + '-v', $version, + '--iteration', $iteration, + '--architecture', $architecture, + '--license', $this->getLicense(), + '--depends', sprintf('%s=%s-r%s', $name, $version, $iteration), + $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', + ]; + $dbgProcess = new Process($dbgArgs); + $dbgProcess->setTimeout(null); + $dbgProcess->run(function ($type, $buffer) { + echo $buffer; + }); + if (!$dbgProcess->isSuccessful()) { + throw new \RuntimeException("APK debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); + } + } + } + /** * Prepare FrankenPHP repository by cloning or updating */ diff --git a/src/package/frankenphp/alpine/frankenphp.openrc b/src/package/frankenphp/alpine/frankenphp.openrc new file mode 100644 index 0000000..23e5e36 --- /dev/null +++ b/src/package/frankenphp/alpine/frankenphp.openrc @@ -0,0 +1,28 @@ +#!/sbin/openrc-run + +name="FrankenPHP" +description="Modern PHP app server" + +command="/usr/bin/frankenphp" +command_args="run --environ --config /etc/frankenphp/Caddyfile" +command_user="frankenphp:frankenphp" +command_background="yes" +pidfile="/run/frankenphp/frankenphp.pid" +start_stop_daemon_args="--chdir /var/lib/frankenphp" + +depend() { + need net + after firewall +} + +start_pre() { + checkpath --directory --owner frankenphp:frankenphp --mode 0755 /run/frankenphp + + $command validate --config /etc/frankenphp/Caddyfile +} + +reload() { + ebegin "Reloading $name configuration" + $command reload --config /etc/frankenphp/Caddyfile --force + eend $? +} diff --git a/src/package/frankenphp/alpine/post-deinstall.sh b/src/package/frankenphp/alpine/post-deinstall.sh new file mode 100644 index 0000000..fd102fc --- /dev/null +++ b/src/package/frankenphp/alpine/post-deinstall.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +if getent passwd frankenphp >/dev/null; then + deluser frankenphp +fi + +if getent group frankenphp >/dev/null; then + delgroup frankenphp +fi + +rmdir /var/lib/frankenphp 2>/dev/null || true + +exit 0 diff --git a/src/package/frankenphp/alpine/post-install.sh b/src/package/frankenphp/alpine/post-install.sh new file mode 100644 index 0000000..5afeea7 --- /dev/null +++ b/src/package/frankenphp/alpine/post-install.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +if ! getent group frankenphp >/dev/null; then + addgroup -S frankenphp +fi + +if ! getent passwd frankenphp >/dev/null; then + adduser -S -h /var/lib/frankenphp -s /sbin/nologin -G frankenphp -g "FrankenPHP web server" frankenphp +fi + +chown -R frankenphp:frankenphp /var/lib/frankenphp +chmod 755 /var/lib/frankenphp + +# allow binding to privileged ports +if command -v setcap >/dev/null 2>&1; then + setcap cap_net_bind_service=+ep /usr/bin/frankenphp || true +fi + +# trust FrankenPHP certificates +if [ -x /usr/bin/frankenphp ]; then + HOME=/var/lib/frankenphp /usr/bin/frankenphp trust || true +fi + +if command -v rc-update >/dev/null 2>&1; then + rc-update add frankenphp default + rc-service frankenphp start +fi + +exit 0 diff --git a/src/package/frankenphp/alpine/pre-deinstall.sh b/src/package/frankenphp/alpine/pre-deinstall.sh new file mode 100644 index 0000000..59713ea --- /dev/null +++ b/src/package/frankenphp/alpine/pre-deinstall.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +if command -v rc-service >/dev/null 2>&1; then + rc-service frankenphp stop || true +fi + +if command -v rc-update >/dev/null 2>&1; then + rc-update del frankenphp default || true +fi + +exit 0 From d2510d8efe8dd3c0d92d139cb7ffec4d57ae6d21 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Dec 2025 22:16:53 +0100 Subject: [PATCH 07/37] apk packages are not working yet, something about origin metadata missing maybe? --- config/templates/craft.yml.twig | 6 +- .../frankenphp/alpine/post-deinstall.sh | 0 src/package/frankenphp/alpine/post-install.sh | 0 .../frankenphp/alpine/pre-deinstall.sh | 0 src/package/pie.php | 21 ++- src/step/CreatePackages.php | 175 +++++++++++++++++- 6 files changed, 189 insertions(+), 13 deletions(-) mode change 100644 => 100755 src/package/frankenphp/alpine/post-deinstall.sh mode change 100644 => 100755 src/package/frankenphp/alpine/post-install.sh mode change 100644 => 100755 src/package/frankenphp/alpine/pre-deinstall.sh diff --git a/config/templates/craft.yml.twig b/config/templates/craft.yml.twig index 7bc59d8..4ce9f7d 100644 --- a/config/templates/craft.yml.twig +++ b/config/templates/craft.yml.twig @@ -47,11 +47,7 @@ download-options: no-alt: false shallow-clone: true {% if php_version == '8.5' %} - ignore-cache-sources: 'php-src,swoole,apcu,pdo_sqlsrv' - custom-url: - - 'pdo_sqlsrv:https://github.com/stancl/msphpsql/archive/refs/heads/push-posxmnwpksqv.tar.gz' - - 'swoole:https://github.com/swoole/swoole-src/archive/eb726b72b6caf4a86bf0af2d29fca40b56d352e9.tar.gz' - - 'amqp:https://github.com/php-amqp/php-amqp/archive/refs/pull/595/merge.tar.gz' + ignore-cache-sources: '' {% else -%} ignore-cache-sources: 'php-src' {% endif -%} diff --git a/src/package/frankenphp/alpine/post-deinstall.sh b/src/package/frankenphp/alpine/post-deinstall.sh old mode 100644 new mode 100755 diff --git a/src/package/frankenphp/alpine/post-install.sh b/src/package/frankenphp/alpine/post-install.sh old mode 100644 new mode 100755 diff --git a/src/package/frankenphp/alpine/pre-deinstall.sh b/src/package/frankenphp/alpine/pre-deinstall.sh old mode 100644 new mode 100755 diff --git a/src/package/pie.php b/src/package/pie.php index 057209f..496c80f 100644 --- a/src/package/pie.php +++ b/src/package/pie.php @@ -24,8 +24,8 @@ public function getVersion(): string // Ensure artifacts exist and get the staged phar path [$pharSource] = $this->prepareArtifacts(); - $proc = new Process(['php', $pharSource, '-V']); - $proc->setTimeout(2); + $proc = new Process(['php', $pharSource, '-V'], env: self::getCleanEnvironment()); + $proc->setTimeout(30); $proc->run(); if (!$proc->isSuccessful()) { // Include both stdout and stderr for parsing attempt/fallback @@ -89,6 +89,23 @@ public function getLicense(): string return 'BSD-3-Clause'; } + /** + * Get environment without Xdebug variables that would cause connection attempts + */ + private static function getCleanEnvironment(): array + { + $env = $_SERVER; + + // Explicitly disable Xdebug-related environment variables + // Must be set to empty/0, not unset, as they inherit from parent + $env['XDEBUG_SESSION'] = ''; + $env['XDEBUG_CONFIG'] = ''; + $env['XDEBUG_MODE'] = 'off'; + $env['PHP_IDE_CONFIG'] = ''; + + return $env; + } + private function prepareArtifacts(): array { $pharPath = DOWNLOAD_PATH . '/pie.phar'; diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 87eb3b0..55f3212 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -559,6 +559,14 @@ private static function createDebPackage( if (isset($config['files']) && is_array($config['files'])) { foreach ($config['files'] as $source => $dest) { if (file_exists($source)) { + // Check if this is a binary that needs its debug link fixed + // Only fix binaries in BUILD_BIN_PATH that are being renamed + if (str_starts_with($source, BUILD_BIN_PATH . '/') && + is_executable($source) && + basename($source) !== basename($dest)) { + // Fix the debug link and use the temporary binary instead + $source = self::fixBinaryDebugLink($source, $dest); + } $fpmArgs[] = $source . '=' . $dest; } else { @@ -683,6 +691,7 @@ private static function createApkPackage(\staticphp\package $package, string $ph 'libdl' => 'musl', 'librt' => 'musl', 'libresolv' => 'musl', + 'libgcc_s' => 'libgcc', ]; if (isset($alpineLibMap[$packageName])) { @@ -718,6 +727,14 @@ private static function createApkPackage(\staticphp\package $package, string $ph if (isset($config['files']) && is_array($config['files'])) { foreach ($config['files'] as $source => $dest) { if (file_exists($source)) { + // Check if this is a binary that needs its debug link fixed + // Only fix binaries in BUILD_BIN_PATH that are being renamed + if (str_starts_with($source, BUILD_BIN_PATH . '/') && + is_executable($source) && + basename($source) !== basename($dest)) { + // Fix the debug link and use the temporary binary instead + $source = self::fixBinaryDebugLink($source, $dest); + } $fpmArgs[] = $source . '=' . $dest; } else { @@ -795,15 +812,26 @@ private static function getPhpVersionAndArchitecture(): array private static function getBinaryDependencies(string $binaryPath): array { - $process = new Process(['ldd', '-v', $binaryPath]); - $process->run(); + // Detect if this is a musl binary + $fileProcess = new Process(['file', $binaryPath]); + $fileProcess->run(); + $fileOutput = $fileProcess->getOutput(); + $isMusl = str_contains($fileOutput, 'musl') || str_contains($fileOutput, 'statically linked'); + + // For musl binaries, we need to use the musl dynamic linker instead of ldd + if ($isMusl) { + $output = self::getMuslBinaryDependencies($binaryPath); + } else { + $process = new Process(['ldd', '-v', $binaryPath]); + $process->run(); + + if (!$process->isSuccessful()) { + throw new \RuntimeException("ldd failed: " . $process->getErrorOutput()); + } - if (!$process->isSuccessful()) { - throw new \RuntimeException("ldd failed: " . $process->getErrorOutput()); + $output = $process->getOutput(); } - $output = $process->getOutput(); - $output = preg_replace('/.*?' . preg_quote($binaryPath, '/') . ':\s*\n/s', '', $output, 1); $output = preg_replace('/\n\s*\/.*?:.*/s', '', $output, 1); @@ -835,6 +863,141 @@ private static function getBinaryDependencies(string $binaryPath): array return $dependencies; } + /** + * Get dependencies for musl-linked binaries using the musl dynamic linker + */ + private static function getMuslBinaryDependencies(string $binaryPath): string + { + // Detect architecture from the binary + $archProcess = new Process(['uname', '-m']); + $archProcess->run(); + $arch = trim($archProcess->getOutput()); + + // Map architecture to musl loader name + $archMap = [ + 'x86_64' => 'x86_64', + 'aarch64' => 'aarch64', + 'arm64' => 'aarch64', + 'armv7l' => 'armv7', + 'armhf' => 'armhf', + ]; + + $muslArch = $archMap[$arch] ?? 'x86_64'; + + // Try to find the musl dynamic linker in common locations + $basePaths = ['/lib', '/usr/lib', '/usr/lib64']; + $muslLoaders = []; + + foreach ($basePaths as $basePath) { + $muslLoaders[] = "{$basePath}/ld-musl-{$muslArch}.so.1"; + // Also try without .1 suffix (some systems) + $muslLoaders[] = "{$basePath}/ld-musl-{$muslArch}.so"; + } + + $muslLoader = null; + foreach ($muslLoaders as $loader) { + if (file_exists($loader)) { + $muslLoader = $loader; + break; + } + } + + if ($muslLoader === null) { + throw new \RuntimeException("Could not find musl dynamic linker for architecture {$arch} (tried: " . implode(', ', $muslLoaders) . ")"); + } + + echo "Using musl dynamic linker: {$muslLoader}\n"; + + // Use the musl loader to list dependencies + $process = new Process([$muslLoader, '--list', $binaryPath]); + $process->run(); + + if (!$process->isSuccessful()) { + // If the binary is statically linked, --list might fail + // Check if it's actually static + $readelfProcess = new Process(['readelf', '-d', $binaryPath]); + $readelfProcess->run(); + if (!str_contains($readelfProcess->getOutput(), 'NEEDED')) { + echo "Binary {$binaryPath} appears to be statically linked (no dynamic dependencies)\n"; + return ''; + } + throw new \RuntimeException("Musl ldd failed: " . $process->getErrorOutput()); + } + + return $process->getOutput(); + } + + /** + * Fix GNU debuglink in a binary to match its new filename + * This is needed when binaries are renamed during packaging (e.g., php -> php-zts8.3) + */ + private static function fixBinaryDebugLink(string $sourceBinary, string $targetBinaryName): string + { + // Extract just the filename from the target path + $targetFilename = basename($targetBinaryName); + $newDebugFileName = $targetFilename . '.debug'; + + // Create a temporary copy of the binary to modify + $tempBinary = TEMP_DIR . '/' . $targetFilename; + + // Copy the source binary to temp location + if (!copy($sourceBinary, $tempBinary)) { + echo "Warning: Failed to copy {$sourceBinary} to {$tempBinary}, debug link won't be fixed\n"; + return $sourceBinary; + } + + // Find the original debug file + // Map binary names to their debug files + $binaryName = basename($sourceBinary); + $debugMap = [ + 'php' => BUILD_ROOT_PATH . '/debug/php-zts.debug', + 'php-cgi' => BUILD_ROOT_PATH . '/debug/php-cgi-zts.debug', + 'php-fpm' => BUILD_ROOT_PATH . '/debug/php-fpm-zts.debug', + 'frankenphp' => BUILD_ROOT_PATH . '/debug/frankenphp.debug', + ]; + + $originalDebugFile = $debugMap[$binaryName] ?? null; + + // If no debug file exists, we can't fix the debug link + if ($originalDebugFile === null || !file_exists($originalDebugFile)) { + echo "No debug file found for {$binaryName}, skipping debug link fix\n"; + return $tempBinary; + } + + // Create a temporary copy of the debug file with the new name + // objcopy needs the actual file to exist to compute the checksum + $tempDebugFile = TEMP_DIR . '/' . $newDebugFileName; + if (!copy($originalDebugFile, $tempDebugFile)) { + echo "Warning: Failed to copy debug file, debug link won't be fixed\n"; + return $tempBinary; + } + + // Remove existing debug link + $removeProcess = new Process(['objcopy', '--remove-section=.gnu_debuglink', $tempBinary]); + $removeProcess->run(); + if (!$removeProcess->isSuccessful()) { + echo "Warning: Failed to remove debug link from {$tempBinary}: " . $removeProcess->getErrorOutput() . "\n"; + @unlink($tempDebugFile); + return $sourceBinary; + } + + // Add new debug link pointing to the renamed debug file + $addProcess = new Process(['objcopy', '--add-gnu-debuglink=' . $tempDebugFile, $tempBinary]); + $addProcess->run(); + if (!$addProcess->isSuccessful()) { + echo "Warning: Failed to add debug link to {$tempBinary}: " . $addProcess->getErrorOutput() . "\n"; + @unlink($tempDebugFile); + return $sourceBinary; + } + + echo "Fixed debug link in {$targetFilename}: {$newDebugFileName}\n"; + + // Clean up the temporary debug file (we don't need it anymore, just needed it for objcopy) + @unlink($tempDebugFile); + + return $tempBinary; + } + private static function getNextIteration(string $name, string $phpVersion, string $architecture): int { $maxIteration = 0; From ea702d8a794e6b800b95e50fd7540250fb106878 Mon Sep 17 00:00:00 2001 From: henderkes Date: Thu, 18 Dec 2025 22:19:20 +0100 Subject: [PATCH 08/37] use sh --- src/package/cli.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/package/cli.php b/src/package/cli.php index 09daf7b..8018439 100644 --- a/src/package/cli.php +++ b/src/package/cli.php @@ -103,13 +103,13 @@ public function getFpmExtraArgs(): array { $binarySuffix = getBinarySuffix(); $afterInstallScript = << Date: Thu, 18 Dec 2025 23:30:41 +0100 Subject: [PATCH 09/37] use nfpm for creation of apk packages (fpm creates invalid crap) --- composer.json | 3 +- composer.lock | 31 ++--- src/package/frankenphp.php | 228 +++++++++++++++++++++++++----------- src/step/CreatePackages.php | 214 +++++++++++++++++---------------- 4 files changed, 283 insertions(+), 193 deletions(-) diff --git a/composer.json b/composer.json index 5912514..6c547af 100644 --- a/composer.json +++ b/composer.json @@ -23,9 +23,10 @@ "prefer-stable": true, "require": { "php": ">=8.4", + "ext-yaml": "*", + "ext-zlib": "*", "crazywhalecc/static-php-cli": "dev-henderkes-patch-1", "laravel/helpers": "^1.7", - "ext-zlib": "*", "twig/twig": "^3.0" }, "require-dev": { diff --git a/composer.lock b/composer.lock index 174eae2..4f96ca8 100644 --- a/composer.lock +++ b/composer.lock @@ -81,21 +81,12 @@ "source": { "type": "git", "url": "https://github.com/crazywhalecc/static-php-cli.git", -<<<<<<< HEAD - "reference": "37f604c821217fc56b2229ccdfd6821ec57d8bdb" + "reference": "53f7cdefe0e791d621d86cc2ce6938d228ec3b19" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/37f604c821217fc56b2229ccdfd6821ec57d8bdb", - "reference": "37f604c821217fc56b2229ccdfd6821ec57d8bdb", -======= - "reference": "0eda08d9bc7eb04c5f8e83a3e16a14b1a1825458" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/0eda08d9bc7eb04c5f8e83a3e16a14b1a1825458", - "reference": "0eda08d9bc7eb04c5f8e83a3e16a14b1a1825458", ->>>>>>> master + "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/53f7cdefe0e791d621d86cc2ce6938d228ec3b19", + "reference": "53f7cdefe0e791d621d86cc2ce6938d228ec3b19", "shasum": "" }, "require": { @@ -150,11 +141,7 @@ "type": "other" } ], -<<<<<<< HEAD - "time": "2025-12-18T08:58:45+00:00" -======= - "time": "2025-12-18T13:52:02+00:00" ->>>>>>> master + "time": "2025-12-18T19:12:01+00:00" }, { "name": "doctrine/inflector", @@ -2241,12 +2228,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8" + "reference": "f960b7bc5c3bae7e348f7b65736072f74493bc3a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/e5034c4df32edeafb119b2c1e2b58876d0286ea8", - "reference": "e5034c4df32edeafb119b2c1e2b58876d0286ea8", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f960b7bc5c3bae7e348f7b65736072f74493bc3a", + "reference": "f960b7bc5c3bae7e348f7b65736072f74493bc3a", "shasum": "" }, "conflict": { @@ -2299,7 +2286,7 @@ "automad/automad": "<2.0.0.0-alpha5", "automattic/jetpack": "<9.8", "awesome-support/awesome-support": "<=6.0.7", - "aws/aws-sdk-php": "<3.288.1", + "aws/aws-sdk-php": "<3.368", "azuracast/azuracast": "<=0.23.1", "b13/seo_basics": "<0.8.2", "backdrop/backdrop": "<=1.32", @@ -3238,7 +3225,7 @@ "type": "tidelift" } ], - "time": "2025-12-17T21:06:23+00:00" + "time": "2025-12-18T19:06:20+00:00" } ], "aliases": [], diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index c1aa635..a58d28f 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -268,6 +268,14 @@ public function createDebPackage(string $architecture, array $binaryDependencies throw new \RuntimeException(sprintf('Directory "%s" was not created', "{$packageFolder}/empty/")); } + // Determine the FrankenPHP suffix (just version, not -zts prefix) + // Extract version from package name: frankenphp8.5 or frankenphp85 + $prefix = CreatePackages::getPrefix(); + $frankenphpSuffix = ''; + if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + $frankenphpSuffix = $matches[1]; + } + $fpmArgs = [...$fpmArgs, ...[ '--depends', $phpEmbedName, '--after-install', "{$packageFolder}/debian/postinst.sh", @@ -276,8 +284,8 @@ public function createDebPackage(string $architecture, array $binaryDependencies '--iteration', $debIteration, '--rpm-user', 'frankenphp', '--rpm-group', 'frankenphp', - BUILD_BIN_PATH . '/frankenphp=/usr/bin/frankenphp', - "{$packageFolder}/debian/frankenphp.service=/usr/lib/systemd/system/frankenphp.service", + BUILD_BIN_PATH . '/frankenphp=/usr/bin/frankenphp' . $frankenphpSuffix, + "{$packageFolder}/debian/frankenphp.service=/usr/lib/systemd/system/frankenphp{$frankenphpSuffix}.service", "{$packageFolder}/Caddyfile=/etc/frankenphp/Caddyfile", "{$packageFolder}/content/=/usr/share/frankenphp", "{$packageFolder}/empty/=/var/lib/frankenphp" @@ -325,7 +333,7 @@ public function createDebPackage(string $architecture, array $binaryDependencies public function createApkPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { CreatePackages::setCurrentPackageType('apk'); - echo "Creating APK package for FrankenPHP...\n"; + echo "Creating APK package for FrankenPHP using nfpm...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); @@ -344,26 +352,31 @@ public function createApkPackage(string $architecture, array $binaryDependencies $versionedConflicts = $this->getVersionedConflicts(); - $fpmArgs = [ - 'fpm', - '-s', 'dir', - '-t', 'apk', - '-p', DIST_APK_PATH, - '-n', $name, - '-v', $version, - '--license', $this->getLicense(), - '--config-files', '/etc/frankenphp/Caddyfile', - '--provides', 'frankenphp', + // Build nfpm config + $nfpmConfig = [ + 'name' => $name, + 'arch' => $architecture, + 'platform' => 'linux', + 'version' => $version, + 'release' => $iteration, + 'section' => 'default', + 'priority' => 'optional', + 'maintainer' => 'Marc Henderkes ', + 'description' => "FrankenPHP - Modern PHP application server", + 'vendor' => 'Marc Henderkes', + 'homepage' => 'https://apks.henderkes.com', + 'license' => $this->getLicense(), + 'apk' => [ + 'signature' => [ + 'key_name' => CreatePackages::getPrefix(), + ], + ], ]; - foreach ($versionedConflicts as $conflict) { - $fpmArgs[] = '--conflicts'; - $fpmArgs[] = $conflict; - $fpmArgs[] = '--replaces'; - $fpmArgs[] = $conflict; - } + // Build dependencies + $depends = [$phpEmbedName]; - // Alpine library dependencies - simpler naming than Debian + // Alpine library dependencies $alpineLibMap = [ 'ld-linux-x86-64.so.2' => 'musl', 'ld-linux-aarch64.so.1' => 'musl', @@ -380,72 +393,153 @@ public function createApkPackage(string $architecture, array $binaryDependencies foreach ($binaryDependencies as $lib => $ver) { if (isset($alpineLibMap[$lib])) { - // Use mapped name for system libraries $packageName = $alpineLibMap[$lib]; - } - else { - // For other libraries, remove .so suffix + } else { $packageName = preg_replace('/\.so(\.\d+)*$/', '', $lib); } - $numericVersion = preg_replace('/[^0-9.]/', '', $ver); - $fpmArgs[] = '--depends'; - $fpmArgs[] = "{$packageName}>={$numericVersion}"; + $depends[] = "{$packageName}>={$numericVersion}"; } - if (!is_dir("{$packageFolder}/empty/") && !mkdir("{$packageFolder}/empty/", 0755, true) && !is_dir("{$packageFolder}/empty/")) { - throw new \RuntimeException(sprintf('Directory "%s" was not created', "{$packageFolder}/empty/")); + $nfpmConfig['depends'] = $depends; + $nfpmConfig['provides'] = ['frankenphp']; + $nfpmConfig['replaces'] = $versionedConflicts; + $nfpmConfig['conflicts'] = $versionedConflicts; + + // Determine the FrankenPHP suffix + $prefix = CreatePackages::getPrefix(); + $frankenphpSuffix = ''; + if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + $frankenphpSuffix = $matches[1]; } $alpineFolder = BASE_PATH . '/src/package/frankenphp'; - $fpmArgs = [...$fpmArgs, ...[ - '--depends', $phpEmbedName, - '--after-install', "{$alpineFolder}/alpine/post-install.sh", - '--before-remove', "{$alpineFolder}/alpine/pre-deinstall.sh", - '--after-remove', "{$alpineFolder}/alpine/post-deinstall.sh", - '--iteration', $iteration, - BUILD_BIN_PATH . '/frankenphp=/usr/bin/frankenphp', - "{$alpineFolder}/alpine/frankenphp.openrc=/etc/init.d/frankenphp", - "{$packageFolder}/Caddyfile=/etc/frankenphp/Caddyfile", - "{$packageFolder}/content/=/usr/share/frankenphp", - "{$packageFolder}/empty/=/var/lib/frankenphp", - "{$packageFolder}/empty/=/etc/frankenphp/Caddyfile.d", - ]]; + // Build contents + $contents = [ + [ + 'src' => BUILD_BIN_PATH . '/frankenphp', + 'dst' => '/usr/bin/frankenphp' . $frankenphpSuffix, + ], + [ + 'src' => "{$alpineFolder}/alpine/frankenphp.openrc", + 'dst' => "/etc/init.d/frankenphp{$frankenphpSuffix}", + ], + [ + 'src' => "{$packageFolder}/Caddyfile", + 'dst' => '/etc/frankenphp/Caddyfile', + 'type' => 'config', + ], + [ + 'src' => "{$packageFolder}/content/", + 'dst' => '/usr/share/frankenphp/', + ], + [ + 'dst' => '/var/lib/frankenphp', + 'type' => 'dir', + ], + [ + 'dst' => '/etc/frankenphp/Caddyfile.d', + 'type' => 'dir', + ], + ]; - $apkProcess = new Process($fpmArgs); - $apkProcess->setTimeout(null); - $apkProcess->run(function ($type, $buffer) { + $nfpmConfig['contents'] = $contents; + + // Add scripts + $nfpmConfig['scripts'] = [ + 'postinstall' => "{$alpineFolder}/alpine/post-install.sh", + 'preremove' => "{$alpineFolder}/alpine/pre-deinstall.sh", + 'postremove' => "{$alpineFolder}/alpine/post-deinstall.sh", + ]; + + // Write nfpm config + $nfpmConfigFile = TEMP_DIR . "/nfpm-{$name}.yaml"; + if (!yaml_emit_file($nfpmConfigFile, $nfpmConfig, YAML_UTF8_ENCODING)) { + throw new \RuntimeException("Failed to write YAML file: {$nfpmConfigFile}"); + } + + echo "nfpm config written to: {$nfpmConfigFile}\n"; + + // Run nfpm + $outputFile = DIST_APK_PATH . "/{$name}-{$version}-r{$iteration}.{$architecture}.apk"; + $nfpmProcess = new Process([ + 'nfpm', 'package', + '--config', $nfpmConfigFile, + '--packager', 'apk', + '--target', $outputFile + ]); + $nfpmProcess->setTimeout(null); + $nfpmProcess->run(function ($type, $buffer) { echo $buffer; }); - echo "APK package created: " . DIST_APK_PATH . "/{$name}-{$version}-r{$iteration}.{$architecture}.apk\n"; + if (!$nfpmProcess->isSuccessful()) { + echo "nfpm config file contents:\n"; + echo file_get_contents($nfpmConfigFile); + throw new \RuntimeException("nfpm package creation failed: " . $nfpmProcess->getErrorOutput()); + } + + @unlink($nfpmConfigFile); + + echo "APK package created: {$outputFile}\n"; // Create FrankenPHP debuginfo package if debug file exists $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { - $dbgArgs = [ - 'fpm', - '-s', 'dir', - '-t', 'apk', - '-p', DIST_APK_PATH, - '-n', $name . '-debuginfo', - '-v', $version, - '--iteration', $iteration, - '--architecture', $architecture, - '--license', $this->getLicense(), - '--depends', sprintf('%s=%s-r%s', $name, $version, $iteration), - $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', - ]; - $dbgProcess = new Process($dbgArgs); - $dbgProcess->setTimeout(null); - $dbgProcess->run(function ($type, $buffer) { - echo $buffer; - }); - if (!$dbgProcess->isSuccessful()) { - throw new \RuntimeException("APK debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); - } + $this->createApkDebuginfo($name, $version, $iteration, $architecture, $frankenDbg, $frankenphpSuffix); + } + } + + private function createApkDebuginfo(string $name, string $version, string $iteration, string $architecture, string $frankenDbg, string $frankenphpSuffix): void + { + $dbgName = $name . '-debuginfo'; + + $nfpmConfig = [ + 'name' => $dbgName, + 'arch' => $architecture, + 'platform' => 'linux', + 'version' => $version, + 'release' => $iteration, + 'section' => 'default', + 'priority' => 'optional', + 'maintainer' => 'Marc Henderkes ', + 'description' => "Debug symbols for FrankenPHP", + 'vendor' => 'Marc Henderkes', + 'homepage' => 'https://apks.henderkes.com', + 'license' => $this->getLicense(), + 'depends' => [sprintf('%s=%s-r%s', $name, $version, $iteration)], + 'contents' => [ + [ + 'src' => $frankenDbg, + 'dst' => '/usr/lib/debug/usr/bin/frankenphp' . $frankenphpSuffix . '.debug', + ], + ], + ]; + + $nfpmConfigFile = TEMP_DIR . "/nfpm-{$dbgName}.yaml"; + if (!yaml_emit_file($nfpmConfigFile, $nfpmConfig, YAML_UTF8_ENCODING)) { + throw new \RuntimeException("Failed to write YAML file: {$nfpmConfigFile}"); + } + + $outputFile = DIST_APK_PATH . "/{$dbgName}-{$version}-r{$iteration}.{$architecture}.apk"; + $dbgProcess = new Process([ + 'nfpm', 'package', + '--config', $nfpmConfigFile, + '--packager', 'apk', + '--target', $outputFile + ]); + $dbgProcess->setTimeout(null); + $dbgProcess->run(function ($type, $buffer) { + echo $buffer; + }); + + if (!$dbgProcess->isSuccessful()) { + throw new \RuntimeException("nfpm debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); } + + @unlink($nfpmConfigFile); + echo "APK debuginfo package created: {$outputFile}\n"; } /** diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 55f3212..b3fb41b 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -607,29 +607,44 @@ private static function createApkPackage(\staticphp\package $package, string $ph $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); - echo "Creating APK package for {$name}...\n"; + echo "Creating APK package for {$name} using nfpm...\n"; // APK uses r{iteration} format for revision number $apkIteration = $iteration; $fullVersion = "{$phpVersion}-r{$apkIteration}"; - $fpmArgs = [...[ - 'fpm', - '-s', 'dir', - '-t', 'apk', - '-p', DIST_APK_PATH, - '--name', $name, - '--version', $phpVersion, - '--architecture', $architecture, - '--iteration', $apkIteration, - '--description', "Static PHP Package for {$name}", - '--license', $package->getLicense(), - '--maintainer', 'Marc Henderkes ', - '--vendor', 'Marc Henderkes ', - '--url', 'apks.henderkes.com', - ], ...$extraArgs]; + // Use nfpm instead of fpm for APK packages + self::createApkWithNfpm($package, $name, $phpVersion, $architecture, $apkIteration, $config, $isDebuginfo); + } + private static function createApkWithNfpm(\staticphp\package $package, string $name, string $phpVersion, string $architecture, string $iteration, array $config, bool $isDebuginfo): void + { + $fullVersion = "{$phpVersion}-r{$iteration}"; + + // Create nfpm YAML config + $nfpmConfig = [ + 'name' => $name, + 'arch' => $architecture, + 'platform' => 'linux', + 'version' => $phpVersion, + 'release' => $iteration, + 'section' => 'default', + 'priority' => 'optional', + 'maintainer' => 'Marc Henderkes ', + 'description' => "Static PHP Package for {$name}", + 'vendor' => 'Marc Henderkes', + 'homepage' => 'https://apks.henderkes.com', + 'license' => $package->getLicense(), + 'apk' => [ + 'signature' => [ + 'key_name' => self::getPrefix(), + ], + ], + ]; - // Ensure non-CLI packages depend on the same PHP major.minor as php-zts-cli (ignore iteration/patch) + // Build dependencies + $depends = []; + + // Ensure non-CLI packages depend on the same PHP major.minor if ($name !== self::getPrefix() . '-cli') { [$fullPhpVersion] = self::getPhpVersionAndArchitecture(); if (preg_match('/^(\d+)\.(\d+)/', $fullPhpVersion, $m)) { @@ -638,134 +653,124 @@ private static function createApkPackage(\staticphp\package $package, string $ph $nextMin = $min + 1; $lowerBound = sprintf('%d.%d', $maj, $min); $upperBound = sprintf('%d.%d', $maj, $nextMin); - // APK dependency format: package>=version and package={$lowerBound}"; - $fpmArgs[] = '--depends'; - $fpmArgs[] = self::getPrefix() . "-cli<{$upperBound}"; + $depends[] = self::getPrefix() . "-cli>={$lowerBound}"; + $depends[] = self::getPrefix() . "-cli<{$upperBound}"; } } - // If this is a debuginfo package, make it depend exactly on its base package version-iteration + // Debuginfo packages depend on their base package if (str_ends_with($name, '-debuginfo')) { $base = preg_replace('/-debuginfo$/', '', $name); - $fpmArgs[] = '--depends'; - $fpmArgs[] = sprintf('%s=%s', $base, $fullVersion); - } - - if (isset($config['provides']) && is_array($config['provides'])) { - foreach ($config['provides'] as $provide) { - $fpmArgs[] = '--provides'; - $fpmArgs[] = "{$provide}={$fullVersion}"; - } - } - - if (isset($config['replaces']) && is_array($config['replaces'])) { - foreach ($config['replaces'] as $replace) { - $fpmArgs[] = '--replaces'; - $fpmArgs[] = $replace; - } - } - - if (isset($config['conflicts']) && is_array($config['conflicts'])) { - foreach ($config['conflicts'] as $conflict) { - $fpmArgs[] = '--conflicts'; - $fpmArgs[] = $conflict; - } - } + $depends[] = sprintf('%s=%s', $base, $fullVersion); + } + + // Alpine library dependencies + $alpineLibMap = [ + 'ld-linux-x86-64' => 'musl', + 'ld-linux-aarch64' => 'musl', + 'libc' => 'musl', + 'libm' => 'musl', + 'libpthread' => 'musl', + 'libutil' => 'musl', + 'libdl' => 'musl', + 'librt' => 'musl', + 'libresolv' => 'musl', + 'libgcc_s' => 'libgcc', + ]; - // Alpine library dependencies - simpler naming than Debian foreach (self::$binaryDependencies as $lib => $version) { - // For Alpine, we can use a simpler approach - most .so files map to package names - // by removing the .so suffix and version numbers $packageName = preg_replace('/\.so(\.\d+)*$/', '', $lib); - - // Common Alpine package mappings - $alpineLibMap = [ - 'ld-linux-x86-64' => 'musl', - 'ld-linux-aarch64' => 'musl', - 'libc' => 'musl', - 'libm' => 'musl', - 'libpthread' => 'musl', - 'libutil' => 'musl', - 'libdl' => 'musl', - 'librt' => 'musl', - 'libresolv' => 'musl', - 'libgcc_s' => 'libgcc', - ]; - if (isset($alpineLibMap[$packageName])) { $packageName = $alpineLibMap[$packageName]; } - $numericVersion = preg_replace('/[^0-9.]/', '', $version); - $fpmArgs[] = '--depends'; - $fpmArgs[] = "{$packageName}>={$numericVersion}"; + $depends[] = "{$packageName}>={$numericVersion}"; } if (isset($config['depends']) && is_array($config['depends'])) { - foreach ($config['depends'] as $depend) { - $fpmArgs[] = '--depends'; - $fpmArgs[] = $depend; - } + $depends = array_merge($depends, $config['depends']); } - if (isset($config['directories']) && is_array($config['directories'])) { - foreach ($config['directories'] as $dir) { - $fpmArgs[] = '--directories'; - $fpmArgs[] = $dir; - } + if (!empty($depends)) { + $nfpmConfig['depends'] = $depends; } - if (isset($config['config-files']) && is_array($config['config-files'])) { - foreach ($config['config-files'] as $configFile) { - $fpmArgs[] = '--config-files'; - $fpmArgs[] = $configFile; - } + // Add provides, replaces, conflicts + if (isset($config['provides']) && is_array($config['provides'])) { + $nfpmConfig['provides'] = $config['provides']; + } + if (isset($config['replaces']) && is_array($config['replaces'])) { + $nfpmConfig['replaces'] = $config['replaces']; + } + if (isset($config['conflicts']) && is_array($config['conflicts'])) { + $nfpmConfig['conflicts'] = $config['conflicts']; } + // Build contents (files) + $contents = []; if (isset($config['files']) && is_array($config['files'])) { foreach ($config['files'] as $source => $dest) { if (file_exists($source)) { - // Check if this is a binary that needs its debug link fixed - // Only fix binaries in BUILD_BIN_PATH that are being renamed + // Fix debug link for renamed binaries if (str_starts_with($source, BUILD_BIN_PATH . '/') && is_executable($source) && basename($source) !== basename($dest)) { - // Fix the debug link and use the temporary binary instead $source = self::fixBinaryDebugLink($source, $dest); } - $fpmArgs[] = $source . '=' . $dest; - } - else { + $contentItem = ['src' => $source, 'dst' => $dest]; + // Mark config files + if (isset($config['config-files']) && in_array($dest, $config['config-files'])) { + $contentItem['type'] = 'config'; + } + $contents[] = $contentItem; + } else { echo "Warning: Source file not found: {$source}\n"; } } } + // Handle empty directories if (isset($config['empty_directories']) && is_array($config['empty_directories'])) { - $emptyDir = TEMP_DIR . '/spp_empty'; - if (!file_exists($emptyDir) && !mkdir($emptyDir, 0755, true) && !is_dir($emptyDir)) { - throw new \RuntimeException(sprintf('Directory "%s" was not created', $emptyDir)); - } - if (is_dir($emptyDir)) { - $files = array_diff((array)scandir($emptyDir), ['.', '..']); - if (!empty($files)) { - exec('rm -rf ' . escapeshellarg($emptyDir . '/*')); - } - } foreach ($config['empty_directories'] as $dir) { - $fpmArgs[] = $emptyDir . '=' . $dir; + $contents[] = ['dst' => $dir, 'type' => 'dir']; } } - $apkProcess = new Process($fpmArgs); - $apkProcess->setTimeout(null); - $apkProcess->run(function ($type, $buffer) { + if (!empty($contents)) { + $nfpmConfig['contents'] = $contents; + } + + // Write nfpm config to YAML file + $nfpmConfigFile = TEMP_DIR . "/nfpm-{$name}.yaml"; + if (!yaml_emit_file($nfpmConfigFile, $nfpmConfig, YAML_UTF8_ENCODING)) { + throw new \RuntimeException("Failed to write YAML file: {$nfpmConfigFile}"); + } + + echo "nfpm config written to: {$nfpmConfigFile}\n"; + + // Run nfpm to create the package + $outputFile = DIST_APK_PATH . "/{$name}-{$phpVersion}-r{$iteration}.{$architecture}.apk"; + $nfpmProcess = new Process([ + 'nfpm', 'package', + '--config', $nfpmConfigFile, + '--packager', 'apk', + '--target', $outputFile + ]); + $nfpmProcess->setTimeout(null); + $nfpmProcess->run(function ($type, $buffer) { echo $buffer; }); - echo "APK package created: " . DIST_APK_PATH . "/{$name}-{$phpVersion}-r{$apkIteration}.{$architecture}.apk\n"; + if (!$nfpmProcess->isSuccessful()) { + echo "nfpm config file contents:\n"; + echo file_get_contents($nfpmConfigFile); + throw new \RuntimeException("nfpm package creation failed: " . $nfpmProcess->getErrorOutput()); + } + + // Clean up config file + @unlink($nfpmConfigFile); + + echo "APK package created: {$outputFile}\n"; } private static function getPhpVersionAndArchitecture(): array @@ -946,6 +951,9 @@ private static function fixBinaryDebugLink(string $sourceBinary, string $targetB return $sourceBinary; } + // Ensure the temporary binary is executable + chmod($tempBinary, 0755); + // Find the original debug file // Map binary names to their debug files $binaryName = basename($sourceBinary); From 0ca68b6aa0643c874194ec978094c6494142b128 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 20 Dec 2025 20:37:46 +0100 Subject: [PATCH 10/37] rework more stuff for versioning (only pie, php or frankenphp harcoded now, rest is dynamic with --prefix) --- bin/spp | 25 ++++++------ composer.lock | 11 +++--- src/Command/AllCommand.php | 7 ++-- src/Command/BaseCommand.php | 19 ++++++++- src/Command/PackageCommand.php | 6 +-- src/extension.php | 17 +++++--- src/package/cgi.php | 9 ++--- src/package/cli.php | 29 ++++++-------- src/package/devel.php | 11 +++--- src/package/embed.php | 21 +++++----- src/package/fpm.php | 38 ++++++++++++------ src/package/frankenphp.php | 48 ++++++++++++----------- src/package/pie.php | 28 +++++++------ src/package/spx.php | 4 +- src/step/CreatePackages.php | 72 ++++++++++++++-------------------- src/step/RunSPC.php | 16 +++++--- src/util/TwigRenderer.php | 6 ++- 17 files changed, 195 insertions(+), 172 deletions(-) diff --git a/bin/spp b/bin/spp index b71f299..500e55a 100755 --- a/bin/spp +++ b/bin/spp @@ -66,42 +66,39 @@ function setupGithubToken(string $method, string $url, array &$headers): void function getLibdir(): string { - $libdir = in_array(SystemUtil::getOSRelease()['dist'], ['redhat', 'centos']) ? '/usr/lib64' : '/usr/lib'; - return $libdir; + // Use the SPP_TYPE constant set by BaseCommand + // rpm uses /usr/lib64, deb and apk use /usr/lib + $type = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; + return $type === 'rpm' ? '/usr/lib64' : '/usr/lib'; } function getConfdir(): string { - // Get the prefix which is package-type aware (php-zts for RPM, php-zts8.3 for DEB, php-zts83 for APK) + // Get the prefix from user-provided parameter (e.g., php-zts, php-zts8.5, php-zts85) $prefix = \staticphp\step\CreatePackages::getPrefix(); return '/etc/' . $prefix; } function getModuledir(): string { - // Get the prefix which is package-type aware + // Get the prefix from user-provided parameter $prefix = \staticphp\step\CreatePackages::getPrefix(); return getLibdir() . '/' . $prefix . '/modules'; } function getPhpLibdir(): string { - // Get the prefix which is package-type aware + // Get the prefix from user-provided parameter $prefix = \staticphp\step\CreatePackages::getPrefix(); return getLibdir() . '/' . $prefix; } function getBinarySuffix(): string { - // Get the suffix for binaries based on package type - // RPM: -zts, DEB: -zts8.3, APK: -zts83 - $prefix = \staticphp\step\CreatePackages::getPrefix(); - - // Extract the version suffix from the prefix - // php-zts -> -zts - // php-zts8.3 -> -zts8.3 - // php-zts83 -> -zts83 - return str_replace('php', '', $prefix); + // Get the suffix for binaries from the prefix + // The prefix is set by the user, e.g., "-zts", "-zts8.5", "-zts85" + // This returns exactly what was passed as --prefix + return defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; } function getVarLibdir(): string diff --git a/composer.lock b/composer.lock index 4f96ca8..6d88036 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3a028b36b942ff3da62d5e6cc6d84937", + "content-hash": "8887f9391893506dae15b45829a13386", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -2228,12 +2228,12 @@ "source": { "type": "git", "url": "https://github.com/Roave/SecurityAdvisories.git", - "reference": "f960b7bc5c3bae7e348f7b65736072f74493bc3a" + "reference": "a08c38341cd11dc1b1cb4fa87f65913c95908d73" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/f960b7bc5c3bae7e348f7b65736072f74493bc3a", - "reference": "f960b7bc5c3bae7e348f7b65736072f74493bc3a", + "url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a08c38341cd11dc1b1cb4fa87f65913c95908d73", + "reference": "a08c38341cd11dc1b1cb4fa87f65913c95908d73", "shasum": "" }, "conflict": { @@ -3225,7 +3225,7 @@ "type": "tidelift" } ], - "time": "2025-12-18T19:06:20+00:00" + "time": "2025-12-19T16:40:43+00:00" } ], "aliases": [], @@ -3238,6 +3238,7 @@ "prefer-lowest": false, "platform": { "php": ">=8.4", + "ext-yaml": "*", "ext-zlib": "*" }, "platform-dev": {}, diff --git a/src/Command/AllCommand.php b/src/Command/AllCommand.php index e6e9ca3..e1b7bab 100644 --- a/src/Command/AllCommand.php +++ b/src/Command/AllCommand.php @@ -19,7 +19,6 @@ protected function configure(): void { parent::configure(); $this - ->addOption('type', null, InputOption::VALUE_REQUIRED, 'Specify which package types to build (rpm,deb)', 'rpm') ->addOption('packages', null, InputOption::VALUE_REQUIRED, 'Specify which packages to build (comma-separated)') ->addOption('iteration', null, InputOption::VALUE_REQUIRED, 'Specify iteration number to use for packages (overrides auto-detected)'); } @@ -28,9 +27,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $debug = $input->getOption('debug'); $packageNames = $input->getOption('packages'); - $packageTypes = $input->getOption('type'); - $phpVersion = $input->getOption('phpv'); $iteration = $input->getOption('iteration'); + $phpVersion = SPP_PHP_VERSION; // Get from constant // Run build step $output->writeln("Building PHP with extensions using static-php-cli..."); @@ -53,7 +51,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln("Creating packages for all extensions..."); } - $packageResult = CreatePackages::run($packageNames, $packageTypes, $phpVersion, $iteration); + // All parameters now come from constants set by BaseCommand::initialize() + $packageResult = CreatePackages::run($packageNames, $iteration); if (!$packageResult) { $output->writeln("Package creation failed."); diff --git a/src/Command/BaseCommand.php b/src/Command/BaseCommand.php index ef10311..51751cd 100644 --- a/src/Command/BaseCommand.php +++ b/src/Command/BaseCommand.php @@ -14,7 +14,9 @@ protected function configure(): void $this ->addOption('debug', null, InputOption::VALUE_NONE, 'Print debug messages') ->addOption('phpv', null, InputOption::VALUE_REQUIRED, 'Specify PHP version to build', '8.4') - ->addOption('target', null, InputOption::VALUE_REQUIRED, 'Specify the target triple for Zig (e.g., x86_64-linux-gnu, aarch64-linux-gnu)', 'native-native'); + ->addOption('target', null, InputOption::VALUE_REQUIRED, 'Specify the target triple for Zig (e.g., x86_64-linux-gnu, aarch64-linux-gnu)', 'native-native') + ->addOption('prefix', null, InputOption::VALUE_REQUIRED, 'Specify the package prefix (e.g., -zts, -zts8.5, -zts85)', '-zts') + ->addOption('type', null, InputOption::VALUE_REQUIRED, 'Specify package type: rpm (uses /usr/lib64), deb (uses /usr/lib), or apk (uses /usr/lib). Required.', null); } protected function initialize(InputInterface $input, OutputInterface $output) @@ -22,6 +24,19 @@ protected function initialize(InputInterface $input, OutputInterface $output) // Define build paths with PHP version $phpVersion = $input->getOption('phpv') ?? '8.4'; $target = $input->getOption('target') ?? 'native-native'; + $prefix = $input->getOption('prefix') ?? '-zts'; + $type = $input->getOption('type'); + + // Validate that --type is provided + if ($type === null) { + throw new \InvalidArgumentException('The --type option is required. Specify: rpm, deb, or apk'); + } + + // Validate type value + $validTypes = ['rpm', 'deb', 'apk']; + if (!in_array($type, $validTypes, true)) { + throw new \InvalidArgumentException('Invalid --type value. Must be one of: ' . implode(', ', $validTypes)); + } // Check if constants are already defined if (defined('SPP_PHP_VERSION')) { @@ -32,6 +47,8 @@ protected function initialize(InputInterface $input, OutputInterface $output) // Define constants define('SPP_PHP_VERSION', $phpVersion); define('SPP_TARGET', $target); + define('SPP_PREFIX', $prefix); + define('SPP_TYPE', $type); define('BUILD_ROOT_PATH', BASE_PATH . '/build/' . $phpVersion); define('BUILD_BIN_PATH', BUILD_ROOT_PATH . '/bin'); define('BUILD_LIB_PATH', BUILD_ROOT_PATH . '/lib'); diff --git a/src/Command/PackageCommand.php b/src/Command/PackageCommand.php index 8d3cf17..2b7d8a6 100644 --- a/src/Command/PackageCommand.php +++ b/src/Command/PackageCommand.php @@ -18,7 +18,6 @@ protected function configure(): void { parent::configure(); $this - ->addOption('type', null, InputOption::VALUE_REQUIRED, 'Specify which package types to build (rpm,deb)', 'rpm') ->addOption('packages', null, InputOption::VALUE_REQUIRED, 'Specify which packages to build (comma-separated)') ->addOption('iteration', null, InputOption::VALUE_REQUIRED, 'Specify iteration number to use for packages (overrides auto-detected)'); } @@ -26,8 +25,6 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $packageNames = $input->getOption('packages'); - $packageTypes = $input->getOption('type'); - $phpVersion = $input->getOption('phpv'); $iteration = $input->getOption('iteration'); if ($packageNames) { @@ -37,7 +34,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int $output->writeln("Creating packages for all extensions..."); } - $result = CreatePackages::run($packageNames, $packageTypes, $phpVersion, $iteration); + // All parameters now come from constants set by BaseCommand::initialize() + $result = CreatePackages::run($packageNames, $iteration); if ($result) { $output->writeln("Package creation completed successfully."); diff --git a/src/extension.php b/src/extension.php index 8395cef..ae4d6c8 100644 --- a/src/extension.php +++ b/src/extension.php @@ -152,9 +152,7 @@ public function getFpmConfig(): array getConfdir() . '/conf.d/' . $this->prefix . $this->name . '.ini', ], 'depends' => $depends, - 'provides' => [ - 'php-zts-' . $this->name, - ], + 'provides' => [], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ @@ -189,17 +187,24 @@ protected function getIniPath(): ?string // Get the dynamic prefix for path replacements $prefix = CreatePackages::getPrefix(); - // Replace extension directives and versioned paths + // Replace extension directives and ALL hardcoded php paths with prefix-based paths $iniContent = str_replace( [ ';extension=' . $this->name, ';zend_extension=' . $this->name, - '/usr/share/php-zts/', - '/usr/local/share/php-zts/', ], [ 'extension=' . $this->name, 'zend_extension=' . $this->name, + ], + $iniContent + ); + $iniContent = preg_replace( + [ + '#/usr/share/php[^/]*/#', + '#/usr/local/share/php[^/]*/#', + ], + [ '/usr/share/' . $prefix . '/', '/usr/local/share/' . $prefix . '/', ], diff --git a/src/package/cgi.php b/src/package/cgi.php index f91d8b6..dd578f8 100644 --- a/src/package/cgi.php +++ b/src/package/cgi.php @@ -19,9 +19,7 @@ public function getFpmConfig(): array 'depends' => [ CreatePackages::getPrefix() . '-cli', ], - 'provides' => [ - 'php-zts-cgi', - ], + 'provides' => [], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ @@ -37,14 +35,15 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { - $src = BUILD_ROOT_PATH . '/debug/php-cgi-zts.debug'; + $binarySuffix = getBinarySuffix(); + $src = BUILD_ROOT_PATH . '/debug/php-cgi' . $binarySuffix . '.debug'; if (!file_exists($src)) { return []; } return [ 'depends' => [CreatePackages::getPrefix() . '-cgi'], 'files' => [ - $src => '/usr/lib/debug/usr/bin/php-cgi' . getBinarySuffix() . '.debug', + $src => '/usr/lib/debug/usr/bin/php-cgi' . $binarySuffix . '.debug', ], ]; } diff --git a/src/package/cli.php b/src/package/cli.php index 8018439..0811378 100644 --- a/src/package/cli.php +++ b/src/package/cli.php @@ -20,19 +20,11 @@ public function getFpmConfig(): array $prefix = CreatePackages::getPrefix(); $contents = file_get_contents(INI_PATH . '/php.ini'); - $contents = str_replace( - [ - '$libdir', - '/etc/php-zts', - ], - [ - getPhpLibdir(), - getConfdir(), - ], - $contents - ); + $contents = str_replace('$libdir', getPhpLibdir(), $contents); + // Replace ALL hardcoded /etc/php* paths with prefix-based conf dir + $contents = preg_replace('#/etc/php[^/]*#', getConfdir(), $contents); file_put_contents(TEMP_DIR . '/php.ini', $contents); - $provides = ['php-zts', $prefix]; + $provides = [$prefix]; $versionedConflicts = CreatePackages::getVersionedConflicts('-cli'); $replaces = $versionedConflicts; $conflicts = $versionedConflicts; @@ -52,12 +44,12 @@ public function getFpmConfig(): array // Add .ini files for statically compiled extensions $iniFile = INI_PATH . "/extension/{$ext}.ini"; if (file_exists($iniFile)) { - // Process the .ini file to replace hardcoded paths + // Process the .ini file to replace ALL hardcoded php paths with prefix-based paths $iniContents = file_get_contents($iniFile); - $iniContents = str_replace( + $iniContents = preg_replace( [ - '/usr/share/php-zts/', - '/usr/local/share/php-zts/', + '#/usr/share/php[^/]*/#', + '#/usr/local/share/php[^/]*/#', ], [ getSharedir() . '/', @@ -128,11 +120,12 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { - $src = BUILD_ROOT_PATH . '/debug/php-zts.debug'; + $binarySuffix = getBinarySuffix(); + $src = BUILD_ROOT_PATH . '/debug/php' . $binarySuffix . '.debug'; if (!file_exists($src)) { return []; } - $target = '/usr/lib/debug/usr/bin/php' . getBinarySuffix() . '.debug'; + $target = '/usr/lib/debug/usr/bin/php' . $binarySuffix . '.debug'; return [ 'depends' => [CreatePackages::getPrefix() . '-cli'], 'files' => [ diff --git a/src/package/devel.php b/src/package/devel.php index 9ab1ca8..1f92337 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -18,6 +18,7 @@ public function getFpmConfig(): array $phpConfigContent = file_get_contents($phpConfigPath); + $binarySuffix = getBinarySuffix(); $phpConfigContent = preg_replace( [ '/^prefix=.*$/m', @@ -25,20 +26,21 @@ public function getFpmConfig(): array '/^libs=.*$/m', '/^program_prefix=.*$/m', '/^program_suffix=.*$/m', - '#/php(?!-zts)#' ], [ 'prefix="/usr"', 'ldflags="-lpthread"', 'libs=""', 'program_prefix=""', - 'program_suffix="-zts"', - '/php-zts' + 'program_suffix="' . $binarySuffix . '"', ], $phpConfigContent ); + + // Replace all /php paths with versioned paths + $phpConfigContent = preg_replace('#/php(?!' . preg_quote($binarySuffix, '#') . ')#', '/' . CreatePackages::getPrefix(), $phpConfigContent); $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $libName = 'libphp-zts-' . $phpVersion . '.so'; + $libName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; $phpConfigContent = str_replace('libphp.so', $libName, $phpConfigContent); file_put_contents($modifiedPhpConfigPath, $phpConfigContent); @@ -86,7 +88,6 @@ public function getFpmConfig(): array CreatePackages::getPrefix() . '-cli', ], 'provides' => [ - 'php-zts-devel', 'php-config' . getBinarySuffix(), 'phpize' . getBinarySuffix(), ], diff --git a/src/package/embed.php b/src/package/embed.php index 5f7c658..ee4e966 100644 --- a/src/package/embed.php +++ b/src/package/embed.php @@ -15,19 +15,21 @@ public function getName(): string public function getFpmConfig(): array { $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $name = 'libphp-zts-' . $phpVersion . '.so'; + $prefix = getBinarySuffix(); // e.g., "-zts", "-nts", "-zts8.5" + $name = 'libphp' . $prefix . '-' . $phpVersion . '.so'; $versionedConflicts = CreatePackages::getVersionedConflicts('-embed'); + $provides = [ + $name, + CreatePackages::getPrefix() . '-embedded' + ]; + if ($this->getName() !== CreatePackages::getPrefix() . '-embed') { + $provides[] = CreatePackages::getPrefix() . '-embed'; + } return [ 'depends' => [ CreatePackages::getPrefix() . '-cli', ], - 'provides' => [ - $name, - 'php-zts-embed', - CreatePackages::getPrefix() . '-embed', - 'php-zts-embedded', - CreatePackages::getPrefix() . '-embedded' - ], + 'provides' => $provides, 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ @@ -44,7 +46,8 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { $phpVersionDigits = str_replace('.', '', SPP_PHP_VERSION); - $libName = 'libphp-zts-' . $phpVersionDigits . '.so'; + $prefix = getBinarySuffix(); + $libName = 'libphp' . $prefix . '-' . $phpVersionDigits . '.so'; $src = BUILD_ROOT_PATH . '/debug/' . $libName . '.debug'; if (!file_exists($src)) { return []; diff --git a/src/package/fpm.php b/src/package/fpm.php index 95ad371..16b7471 100644 --- a/src/package/fpm.php +++ b/src/package/fpm.php @@ -14,17 +14,22 @@ public function getName(): string public function getFpmConfig(): array { + $prefix = CreatePackages::getPrefix(); $contents = file_get_contents(INI_PATH . '/php-fpm.conf'); $contents = str_replace('$confdir', getConfdir(), $contents); + // Replace ALL hardcoded /var/log/php* paths with prefix-based paths + $contents = preg_replace('#/var/log/php[^/]*/#', '/var/log/' . $prefix . '/', $contents); + // Replace ALL hardcoded /run/php-fpm* paths with prefix-based paths + $contents = preg_replace('#/run/php-fpm[^/]*/#', '/run/php-fpm' . getBinarySuffix() . '/', $contents); file_put_contents(TEMP_DIR . '/php-fpm.conf', $contents); - // Process the systemd service file to replace hardcoded paths + // Process the systemd service file to replace ALL hardcoded paths $serviceContents = file_get_contents(INI_PATH . '/php-fpm.service'); $binarySuffix = getBinarySuffix(); - $serviceContents = str_replace( + $serviceContents = preg_replace( [ - '/usr/sbin/php-fpm-zts', - 'RuntimeDirectory=php-fpm-zts', + '#/usr/sbin/php-fpm[^ ]*#', + '#RuntimeDirectory=php-fpm[^ ]*#', ], [ '/usr/sbin/php-fpm' . $binarySuffix, @@ -34,11 +39,19 @@ public function getFpmConfig(): array ); file_put_contents(TEMP_DIR . '/php-fpm.service', $serviceContents); - // Process www.conf to replace hardcoded paths + // Process www.conf to replace ALL hardcoded paths $wwwContents = file_get_contents(INI_PATH . '/www.conf'); - $wwwContents = str_replace( - '/var/lib/php-zts/', - getVarLibdir() . '/', + $wwwContents = preg_replace( + [ + '#/var/lib/php[^/]*/#', + '#/var/log/php[^/]*/#', + '#/run/php-fpm[^/]*/#', + ], + [ + getVarLibdir() . '/', + '/var/log/' . $prefix . '/', + '/run/php-fpm' . $binarySuffix . '/', + ], $wwwContents ); file_put_contents(TEMP_DIR . '/www.conf', $wwwContents); @@ -48,9 +61,7 @@ public function getFpmConfig(): array 'depends' => [ CreatePackages::getPrefix() . '-cli', ], - 'provides' => [ - 'php-zts-fpm', - ], + 'provides' => [], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ @@ -77,11 +88,12 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { - $src = BUILD_ROOT_PATH . '/debug/php-fpm-zts.debug'; + $binarySuffix = getBinarySuffix(); + $src = BUILD_ROOT_PATH . '/debug/php-fpm' . $binarySuffix . '.debug'; if (!file_exists($src)) { return []; } - $target = '/usr/lib/debug/usr/sbin/php-fpm' . getBinarySuffix() . '.debug'; + $target = '/usr/lib/debug/usr/sbin/php-fpm' . $binarySuffix . '.debug'; return [ 'depends' => [CreatePackages::getPrefix() . '-fpm'], 'files' => [ diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index a58d28f..f1dad60 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -10,12 +10,15 @@ class frankenphp implements package { public function getName(): string { - // RPM packages use frankenphp (unversioned, for module system) - // DEB/APK packages use versioned frankenphp8.3 or frankenphp83 + // Extract version suffix from prefix for frankenphp naming + // e.g., "php-zts8.3" -> "frankenphp8.3", "php-nts85" -> "frankenphp85", "php-zts" -> "frankenphp" $prefix = CreatePackages::getPrefix(); - // Extract version from prefix (e.g., "php-zts8.3" -> "8.3", "php-zts" -> "") - if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + // Remove "php" and any non-digit prefix to get just the version part + // php-zts8.5 -> -zts8.5 -> 8.5 + // php-nts85 -> -nts85 -> 85 + $suffix = str_replace('php', '', $prefix); + if (preg_match('/(\d+\.?\d*)/', $suffix, $matches)) { return 'frankenphp' . $matches[1]; } return 'frankenphp'; @@ -50,10 +53,15 @@ private function getVersionedConflicts(): array // Get the conflicts list from CreatePackages using the franken prefix $phpConflicts = CreatePackages::getVersionedConflicts(''); - // Transform php-zts8.3 conflicts to frankenphp8.3 conflicts + // Transform php{prefix} conflicts to frankenphp conflicts + // e.g., php-zts8.3 -> frankenphp8.3, php-nts85 -> frankenphp85 $conflicts = []; foreach ($phpConflicts as $conflict) { - $conflicts[] = str_replace('php-zts', 'frankenphp', $conflict); + // Replace the full php prefix with just the version part for frankenphp + // Extract just the version numbers + if (preg_match('/(\d+\.?\d*)/', $conflict, $matches)) { + $conflicts[] = 'frankenphp' . $matches[1]; + } } return $conflicts; @@ -86,12 +94,12 @@ public function createPackages(array $packageTypes, array $binaryDependencies, ? */ public function createRpmPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { - CreatePackages::setCurrentPackageType('rpm'); echo "Creating RPM package for FrankenPHP...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; + $binarySuffix = getBinarySuffix(); + $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); @@ -195,12 +203,12 @@ public function createRpmPackage(string $architecture, array $binaryDependencies */ public function createDebPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { - CreatePackages::setCurrentPackageType('deb'); echo "Creating DEB package for FrankenPHP...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; + $binarySuffix = getBinarySuffix(); + $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); @@ -268,11 +276,12 @@ public function createDebPackage(string $architecture, array $binaryDependencies throw new \RuntimeException(sprintf('Directory "%s" was not created', "{$packageFolder}/empty/")); } - // Determine the FrankenPHP suffix (just version, not -zts prefix) + // Determine the FrankenPHP suffix (just version, not prefix) // Extract version from package name: frankenphp8.5 or frankenphp85 $prefix = CreatePackages::getPrefix(); $frankenphpSuffix = ''; - if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + // Extract version numbers from prefix (e.g., "php-zts8.5" -> "8.5", "php-nts85" -> "85") + if (preg_match('/(\d+\.?\d*)/', $prefix, $matches)) { $frankenphpSuffix = $matches[1]; } @@ -332,12 +341,12 @@ public function createDebPackage(string $architecture, array $binaryDependencies */ public function createApkPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void { - CreatePackages::setCurrentPackageType('apk'); echo "Creating APK package for FrankenPHP using nfpm...\n"; $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $phpEmbedName = 'libphp-zts-' . $phpVersion . '.so'; + $binarySuffix = getBinarySuffix(); + $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); @@ -366,11 +375,6 @@ public function createApkPackage(string $architecture, array $binaryDependencies 'vendor' => 'Marc Henderkes', 'homepage' => 'https://apks.henderkes.com', 'license' => $this->getLicense(), - 'apk' => [ - 'signature' => [ - 'key_name' => CreatePackages::getPrefix(), - ], - ], ]; // Build dependencies @@ -406,10 +410,10 @@ public function createApkPackage(string $architecture, array $binaryDependencies $nfpmConfig['replaces'] = $versionedConflicts; $nfpmConfig['conflicts'] = $versionedConflicts; - // Determine the FrankenPHP suffix + // Determine the FrankenPHP suffix (just version numbers) $prefix = CreatePackages::getPrefix(); $frankenphpSuffix = ''; - if (preg_match('/php-zts(\d+\.?\d*)/', $prefix, $matches)) { + if (preg_match('/(\d+\.?\d*)/', $prefix, $matches)) { $frankenphpSuffix = $matches[1]; } @@ -494,7 +498,7 @@ public function createApkPackage(string $architecture, array $binaryDependencies private function createApkDebuginfo(string $name, string $version, string $iteration, string $architecture, string $frankenDbg, string $frankenphpSuffix): void { $dbgName = $name . '-debuginfo'; - + $nfpmConfig = [ 'name' => $dbgName, 'arch' => $architecture, diff --git a/src/package/pie.php b/src/package/pie.php index 496c80f..0dcaeb0 100644 --- a/src/package/pie.php +++ b/src/package/pie.php @@ -12,7 +12,8 @@ class pie implements package { public function getName(): string { - return 'pie-' . CreatePackages::getPrefix(); + // Return pie with the suffix (e.g., "pie-zts", "pie-zts8.5", "pie-zts85") + return 'pie' . getBinarySuffix(); } /** @@ -50,11 +51,13 @@ public function getFpmConfig(): array $prefix = CreatePackages::getPrefix(); - // Get versioned conflicts for pie packages (pie-php-zts8.0, pie-php-zts8.1, etc.) + // Get versioned conflicts for pie packages (pie-zts8.0, pie-zts8.1, etc.) + // Replace the 'php' prefix from conflicts with 'pie' $phpConflicts = CreatePackages::getVersionedConflicts(''); $versionedConflicts = []; foreach ($phpConflicts as $conflict) { - $versionedConflicts[] = 'pie-' . $conflict; + // Replace 'php' with 'pie' (e.g., php-zts8.5 -> pie-zts8.5, php-nts85 -> pie-nts85) + $versionedConflicts[] = str_replace('php', 'pie', $conflict); } return [ @@ -62,9 +65,7 @@ public function getFpmConfig(): array $prefix . '-cli', $prefix . '-devel', ], - 'provides' => [ - 'pie-zts', - ], + 'provides' => [], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ @@ -115,19 +116,20 @@ private function prepareArtifacts(): array // Process the pie wrapper script to replace hardcoded paths $wrapperSource = INI_PATH . '/pie-zts'; - $wrapperPath = TEMP_DIR . '/pie' . getBinarySuffix(); - $wrapperContents = file_get_contents($wrapperSource); $binarySuffix = getBinarySuffix(); + $wrapperPath = TEMP_DIR . '/pie' . $binarySuffix; + $wrapperContents = file_get_contents($wrapperSource); - $wrapperContents = str_replace( + // Replace ALL hardcoded paths with prefix-based paths + $wrapperContents = preg_replace( [ - '/usr/bin/php-zts', - '/usr/share/php-zts/', - '/usr/bin/php-config-zts', + '#/usr/bin/php[^ ]*#', + '#/usr/share/php[^ /]*/pie\.phar#', + '#/usr/bin/php-config[^ ]*#', ], [ '/usr/bin/php' . $binarySuffix, - getSharedir() . '/', + getSharedir() . '/pie.phar', '/usr/bin/php-config' . $binarySuffix, ], $wrapperContents diff --git a/src/package/spx.php b/src/package/spx.php index cf48843..2a283cf 100644 --- a/src/package/spx.php +++ b/src/package/spx.php @@ -17,9 +17,7 @@ public function getFpmConfig(): array 'depends' => [ CreatePackages::getPrefix() . '-cli' ], - 'provides' => [ - 'php-zts-spx', - ], + 'provides' => [], 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index b3fb41b..eb0a7a2 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -16,14 +16,9 @@ class CreatePackages private static $binaryDependencies = []; private static $packageTypes = []; private static ?string $iterationOverride = null; - private static ?string $currentPackageType = null; + private static string $prefix = '-zts'; - public static function setCurrentPackageType(?string $type): void - { - self::$currentPackageType = $type; - } - - public static function run($packageNames = null, string $packageTypes = 'rpm,deb', string $phpVersion = '8.4', ?string $iteration = null): true + public static function run($packageNames = null, ?string $iteration = null): true { self::loadConfig(); @@ -33,8 +28,13 @@ public static function run($packageNames = null, string $packageTypes = 'rpm,deb $phpBinary = BUILD_BIN_PATH . '/php'; self::$binaryDependencies = self::getBinaryDependencies($phpBinary); - self::$packageTypes = explode(',', strtolower($packageTypes)); - self::$iterationOverride = $iteration !== null && $iteration !== '' ? (string)$iteration : null; + // Use values from constants set by BaseCommand + self::$prefix = defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; + $packageType = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; + + // Package type is now a single value, not a comma-separated list + self::$packageTypes = [$packageType]; + self::$iterationOverride = $iteration !== null && $iteration !== '' ? $iteration : null; if ($packageNames !== null) { if (is_string($packageNames)) { @@ -285,7 +285,6 @@ private static function createPackageWithFpm(\staticphp\package $package, string private static function createRpmPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void { - self::$currentPackageType = 'rpm'; $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); @@ -431,7 +430,6 @@ private static function createDebPackage( bool $isDebuginfo = false, ): void { - self::$currentPackageType = 'deb'; $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); @@ -602,7 +600,6 @@ private static function createDebPackage( private static function createApkPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void { - self::$currentPackageType = 'apk'; $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); $extraArgs = $isDebuginfo ? [] : $package->getFpmExtraArgs(); @@ -955,12 +952,13 @@ private static function fixBinaryDebugLink(string $sourceBinary, string $targetB chmod($tempBinary, 0755); // Find the original debug file - // Map binary names to their debug files + // Map binary names to their debug files using the prefix $binaryName = basename($sourceBinary); + $binarySuffix = getBinarySuffix(); $debugMap = [ - 'php' => BUILD_ROOT_PATH . '/debug/php-zts.debug', - 'php-cgi' => BUILD_ROOT_PATH . '/debug/php-cgi-zts.debug', - 'php-fpm' => BUILD_ROOT_PATH . '/debug/php-fpm-zts.debug', + 'php' => BUILD_ROOT_PATH . '/debug/php' . $binarySuffix . '.debug', + 'php-cgi' => BUILD_ROOT_PATH . '/debug/php-cgi' . $binarySuffix . '.debug', + 'php-fpm' => BUILD_ROOT_PATH . '/debug/php-fpm' . $binarySuffix . '.debug', 'frankenphp' => BUILD_ROOT_PATH . '/debug/frankenphp.debug', ]; @@ -1045,37 +1043,21 @@ private static function getNextIteration(string $name, string $phpVersion, strin public static function getPrefix(): string { - $phpVersion = SPP_PHP_VERSION; - - // RPM packages always use php-zts (for module system) - if (self::$currentPackageType === 'rpm') { - return 'php-zts'; - } - - // APK packages use php-zts83 (no dot) - if (self::$currentPackageType === 'apk') { - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - return 'php-zts' . $matches[1] . $matches[2]; - } - return 'php-zts'; - } - - // DEB packages use php-zts8.3 (with dot) - if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - return 'php-zts' . $matches[1] . '.' . $matches[2]; - } - return 'php-zts'; + // Return the prefix set by the user, prepended with "php" + // For example: "-zts" becomes "php-zts", "-zts8.5" becomes "php-zts8.5" + return 'php' . self::$prefix; } /** * Get list of versioned package names to conflict/replace with * For example, for php-zts8.5-cli, returns [php-zts8.0-cli, php-zts8.1-cli, ..., php-zts8.9-cli] excluding 8.5 - * For RPM packages, returns empty array (RPM uses module system instead) + * For RPM packages (using unversioned prefix like -zts), returns empty array (RPM uses module system instead) */ public static function getVersionedConflicts(string $suffix): array { // RPM packages use module system, no versioned conflicts needed - if (self::$currentPackageType === 'rpm') { + // Detect RPM by checking if prefix is unversioned (e.g., "-zts" or "-nts" without version number) + if (!preg_match('/\d/', self::$prefix)) { return []; } @@ -1089,6 +1071,12 @@ public static function getVersionedConflicts(string $suffix): array $currentMajor = (int)$matches[1]; $currentMinor = (int)$matches[2]; + // Extract the base prefix (without version) and whether it uses dots + // e.g., "-zts8.5" -> base: "-zts", usesDot: true + // e.g., "-nts85" -> base: "-nts", usesDot: false + $usesDot = str_contains(self::$prefix, '.'); + $basePrefix = preg_replace('/\d+\.?\d*/', '', self::$prefix); + // Generate conflicts for versions 8.0 through 8.9 for ($minor = 0; $minor <= 9; $minor++) { // Skip the current version @@ -1096,11 +1084,11 @@ public static function getVersionedConflicts(string $suffix): array continue; } - // APK uses php-zts83 format (no dot), DEB uses php-zts8.3 (with dot) - if (self::$currentPackageType === 'apk') { - $conflicts[] = "php-zts{$currentMajor}{$minor}{$suffix}"; + // Use the same format as the current prefix + if ($usesDot) { + $conflicts[] = "php{$basePrefix}{$currentMajor}.{$minor}{$suffix}"; } else { - $conflicts[] = "php-zts{$currentMajor}.{$minor}{$suffix}"; + $conflicts[] = "php{$basePrefix}{$currentMajor}{$minor}{$suffix}"; } } diff --git a/src/step/RunSPC.php b/src/step/RunSPC.php index f2d452b..0dc1a67 100644 --- a/src/step/RunSPC.php +++ b/src/step/RunSPC.php @@ -57,6 +57,9 @@ private static function fixGnuDebugLinks(): void return; } + // Get the binary suffix from the prefix (e.g., "-zts", "-nts", "-zts8.5") + $binarySuffix = defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; + $ensureRename = function (string $from, string $to) { if ($from === $to) { return; @@ -74,16 +77,17 @@ private static function fixGnuDebugLinks(): void } }; + // Rename debug files using the prefix $sapiMap = [ - $binDir . '/php' => $debugDir . '/php-zts.debug', - $binDir . '/php-fpm' => $debugDir . '/php-fpm-zts.debug', - $binDir . '/php-cgi' => $debugDir . '/php-cgi-zts.debug', + $binDir . '/php' => $debugDir . '/php' . $binarySuffix . '.debug', + $binDir . '/php-fpm' => $debugDir . '/php-fpm' . $binarySuffix . '.debug', + $binDir . '/php-cgi' => $debugDir . '/php-cgi' . $binarySuffix . '.debug', $binDir . '/frankenphp' => $debugDir . '/frankenphp.debug', ]; - $ensureRename($debugDir . '/php.debug', $debugDir . '/php-zts.debug'); - $ensureRename($debugDir . '/php-fpm.debug', $debugDir . '/php-fpm-zts.debug'); - $ensureRename($debugDir . '/php-cgi.debug', $debugDir . '/php-cgi-zts.debug'); + $ensureRename($debugDir . '/php.debug', $debugDir . '/php' . $binarySuffix . '.debug'); + $ensureRename($debugDir . '/php-fpm.debug', $debugDir . '/php-fpm' . $binarySuffix . '.debug'); + $ensureRename($debugDir . '/php-cgi.debug', $debugDir . '/php-cgi' . $binarySuffix . '.debug'); foreach ($sapiMap as $binary => $dbgFile) { if (!file_exists($binary)) { diff --git a/src/util/TwigRenderer.php b/src/util/TwigRenderer.php index c12e0ac..5b37020 100644 --- a/src/util/TwigRenderer.php +++ b/src/util/TwigRenderer.php @@ -52,8 +52,10 @@ public static function renderCraftTemplate(string $phpVersion = '8.4', ?string $ } // Prepare template variables - $prefix = CreatePackages::getPrefix(); - $libdir = in_array($majorOsVersion, ['7', '8', '9', '10']) ? '/usr/lib64' : '/usr/lib'; + // Use SPP_PREFIX and SPP_TYPE constants set by BaseCommand + $prefix = 'php' . (defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'); + $packageType = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; + $libdir = $packageType === 'rpm' ? '/usr/lib64' : '/usr/lib'; $templateVars = [ 'php_version' => $phpVersion, From 922426cb406cf6f26e9fe71006862e28bc70902c Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 20 Dec 2025 21:05:50 +0100 Subject: [PATCH 11/37] don't remove source and download dir after it's too late --- src/step/RunSPC.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/step/RunSPC.php b/src/step/RunSPC.php index 0dc1a67..2c6ed3f 100644 --- a/src/step/RunSPC.php +++ b/src/step/RunSPC.php @@ -152,12 +152,6 @@ public static function run(bool $debug = false, string $phpVersion = '8.4', ?arr echo "Static PHP CLI build completed successfully.\n"; - // Free up space for github runners - if (getenv('CI') || getenv('GITHUB_ACTION')) { - FileSystem::removeDir(BASE_PATH . '/vendor/crazywhalecc/static-php-cli/source'); - FileSystem::removeDir(BASE_PATH . '/vendor/crazywhalecc/static-php-cli/downloads'); - } - // Copy the built files to our build directory self::copyBuiltFiles($phpVersion); From 64b870369ab6aa2574e15b1ba2f342cd95686bfa Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 20 Dec 2025 21:14:30 +0100 Subject: [PATCH 12/37] rename variable for clarity --- src/package/embed.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/package/embed.php b/src/package/embed.php index ee4e966..6e3dc37 100644 --- a/src/package/embed.php +++ b/src/package/embed.php @@ -16,10 +16,10 @@ public function getFpmConfig(): array { $phpVersion = str_replace('.', '', SPP_PHP_VERSION); $prefix = getBinarySuffix(); // e.g., "-zts", "-nts", "-zts8.5" - $name = 'libphp' . $prefix . '-' . $phpVersion . '.so'; + $libphp = 'libphp' . $prefix . '-' . $phpVersion . '.so'; $versionedConflicts = CreatePackages::getVersionedConflicts('-embed'); $provides = [ - $name, + $libphp, CreatePackages::getPrefix() . '-embedded' ]; if ($this->getName() !== CreatePackages::getPrefix() . '-embed') { @@ -33,7 +33,7 @@ public function getFpmConfig(): array 'replaces' => $versionedConflicts, 'conflicts' => $versionedConflicts, 'files' => [ - BUILD_LIB_PATH . '/' . $name => getLibdir() . '/' . $name, + BUILD_LIB_PATH . '/' . $libphp => getLibdir() . '/' . $libphp, ] ]; } From abeff8f1758b1831d7c65974a8a9ab6efb3757f2 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 20 Dec 2025 22:07:13 +0100 Subject: [PATCH 13/37] all command has to pass param --- src/Command/AllCommand.php | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/src/Command/AllCommand.php b/src/Command/AllCommand.php index e1b7bab..e9ebea0 100644 --- a/src/Command/AllCommand.php +++ b/src/Command/AllCommand.php @@ -26,15 +26,24 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { $debug = $input->getOption('debug'); - $packageNames = $input->getOption('packages'); + $packagesOpt = $input->getOption('packages'); $iteration = $input->getOption('iteration'); $phpVersion = SPP_PHP_VERSION; // Get from constant + // Process packages option + $packages = null; + if (is_string($packagesOpt) && $packagesOpt !== '') { + $packages = array_values(array_filter(array_map('trim', explode(',', $packagesOpt)))); + } + // Run build step $output->writeln("Building PHP with extensions using static-php-cli..."); $output->writeln("Using PHP version: {$phpVersion}"); + if ($packages) { + $output->writeln("Building packages: " . implode(', ', $packages)); + } - $buildResult = RunSPC::run($debug, $phpVersion); + $buildResult = RunSPC::run($debug, $phpVersion, $packages); if (!$buildResult) { $output->writeln("Build failed."); @@ -43,16 +52,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int } // Run package step - if ($packageNames) { - // Split by comma to support multiple packages - $packageNames = explode(',', $packageNames); - $output->writeln("Creating packages for: " . implode(', ', $packageNames) . "..."); + if ($packages) { + $output->writeln("Creating packages for: " . implode(', ', $packages) . "..."); } else { $output->writeln("Creating packages for all extensions..."); } // All parameters now come from constants set by BaseCommand::initialize() - $packageResult = CreatePackages::run($packageNames, $iteration); + $packageResult = CreatePackages::run($packages, $iteration); if (!$packageResult) { $output->writeln("Package creation failed."); From d12cd60abf45b9a20aa68bd4944e26cbbcc7011b Mon Sep 17 00:00:00 2001 From: henderkes Date: Sat, 20 Dec 2025 23:47:33 +0100 Subject: [PATCH 14/37] versioned seems working! --- config/templates/craft.yml.twig | 4 +- src/package/devel.php | 16 ++++- src/package/embed.php | 15 +++- src/package/frankenphp.php | 30 ++++++-- src/package/meta.php | 47 +++++++++++++ src/step/CreatePackages.php | 4 ++ src/step/RunSPC.php | 117 -------------------------------- src/util/TwigRenderer.php | 7 ++ 8 files changed, 111 insertions(+), 129 deletions(-) create mode 100644 src/package/meta.php diff --git a/config/templates/craft.yml.twig b/config/templates/craft.yml.twig index 3b70767..39a96b5 100644 --- a/config/templates/craft.yml.twig +++ b/config/templates/craft.yml.twig @@ -87,14 +87,14 @@ extra-env: {% else -%} SPC_TARGET: '{{ target }}' {% endif -%} - EXTENSION_DIR: "{{ is_rhel ? '/usr/lib64/php-zts/modules' : '/usr/lib/php-zts/modules' }}" + EXTENSION_DIR: "{{ moduledir }}" SPC_CMD_VAR_PHP_EMBED_TYPE: 'shared' SPC_MICRO_PATCHES: SPC_DEFAULT_C_FLAGS: "{{ cflags }}" SPC_DEFAULT_CXX_FLAGS: "{{ cflags }}" SPC_DEFAULT_LD_FLAGS: "-Wl,-z,relro -Wl,--as-needed -Wl,-z,now -Wl,-z,noexecstack -Wl,--build-id=sha1 {{ using_gcc and is_rhel ? specs_ldflags : '' }}" SPC_CMD_VAR_PHP_MAKE_EXTRA_CFLAGS: "{{ cflags }} -g -fPIE{{ using_gcc ? '' : ' -flto'}}" - SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS: "-pie -release zts-{{ php_version_nodot }}{{ using_gcc ? '' : ' -flto'}}" + SPC_CMD_VAR_PHP_MAKE_EXTRA_LDFLAGS: "-pie -release {{ release_prefix }}-{{ php_version_nodot }}{{ using_gcc ? '' : ' -flto'}}" SPC_CMD_PREFIX_PHP_CONFIGURE: "./configure --prefix= --with-valgrind=no --disable-shared --enable-static --disable-all --disable-cgi --disable-phpdbg --disable-debug --with-pic --disable-dependency-tracking --enable-rtld-now --enable-re2c-cgoto --disable-rpath" SPC_CMD_VAR_FRANKENPHP_XCADDY_MODULES: "--with github.com/dunglas/frankenphp/caddy --with github.com/dunglas/mercure/caddy --with github.com/dunglas/vulcain/caddy --with github.com/dunglas/caddy-cbrotli --with github.com/baldinof/caddy-supervisor" PHP_BUILD_PROVIDER: "Static PHP #StandWithUkraine" diff --git a/src/package/devel.php b/src/package/devel.php index 1f92337..29925a4 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -18,6 +18,11 @@ public function getFpmConfig(): array $phpConfigContent = file_get_contents($phpConfigPath); + // Replace buildroot paths with BUILD_ROOT_PATH + $builtDir = BASE_PATH . '/vendor/crazywhalecc/static-php-cli/buildroot'; + $phpConfigContent = str_replace($builtDir, BUILD_ROOT_PATH, $phpConfigContent); + $phpConfigContent = str_replace('/app/buildroot', BUILD_ROOT_PATH, $phpConfigContent); + $binarySuffix = getBinarySuffix(); $phpConfigContent = preg_replace( [ @@ -40,7 +45,11 @@ public function getFpmConfig(): array // Replace all /php paths with versioned paths $phpConfigContent = preg_replace('#/php(?!' . preg_quote($binarySuffix, '#') . ')#', '/' . CreatePackages::getPrefix(), $phpConfigContent); $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $libName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; + // Use release prefix format for libphp (leading dash removed, dots kept) + $releasePrefix = ltrim($binarySuffix, '-'); + $libName = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersion . '.so' + : 'libphp-' . $phpVersion . '.so'; $phpConfigContent = str_replace('libphp.so', $libName, $phpConfigContent); file_put_contents($modifiedPhpConfigPath, $phpConfigContent); @@ -50,6 +59,11 @@ public function getFpmConfig(): array $modifiedPhpizePath = TEMP_DIR . '/phpize'; $phpizeContent = file_get_contents($phpizePath); + + // Replace buildroot paths with BUILD_ROOT_PATH + $phpizeContent = str_replace($builtDir, BUILD_ROOT_PATH, $phpizeContent); + $phpizeContent = str_replace('/app/buildroot', BUILD_ROOT_PATH, $phpizeContent); + $phpizeContent = preg_replace( [ '/^prefix=.*$/m', diff --git a/src/package/embed.php b/src/package/embed.php index 6e3dc37..9191631 100644 --- a/src/package/embed.php +++ b/src/package/embed.php @@ -15,8 +15,13 @@ public function getName(): string public function getFpmConfig(): array { $phpVersion = str_replace('.', '', SPP_PHP_VERSION); - $prefix = getBinarySuffix(); // e.g., "-zts", "-nts", "-zts8.5" - $libphp = 'libphp' . $prefix . '-' . $phpVersion . '.so'; + $prefix = getBinarySuffix(); // e.g., "-zts", "-nts", "-zts8.5", or "" + // SPC produces libphp-{prefix}-{version}.so with only leading dash removed from prefix + // e.g., "-zts" -> "libphp-zts-85.so", "-zts8.5" -> "libphp-zts8.5-85.so", "" -> "libphp-85.so" + $releasePrefix = ltrim($prefix, '-'); + $libphp = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersion . '.so' + : 'libphp-' . $phpVersion . '.so'; $versionedConflicts = CreatePackages::getVersionedConflicts('-embed'); $provides = [ $libphp, @@ -47,7 +52,11 @@ public function getDebuginfoFpmConfig(): array { $phpVersionDigits = str_replace('.', '', SPP_PHP_VERSION); $prefix = getBinarySuffix(); - $libName = 'libphp' . $prefix . '-' . $phpVersionDigits . '.so'; + // SPC produces libphp-{prefix}-{version}.so with only leading dash removed from prefix + $releasePrefix = ltrim($prefix, '-'); + $libName = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersionDigits . '.so' + : 'libphp-' . $phpVersionDigits . '.so'; $src = BUILD_ROOT_PATH . '/debug/' . $libName . '.debug'; if (!file_exists($src)) { return []; diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index f1dad60..ba3534d 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -99,12 +99,18 @@ public function createRpmPackage(string $architecture, array $binaryDependencies $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); $binarySuffix = getBinarySuffix(); - $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; + // SPC produces libphp-{prefix}-{version}.so with only leading dash removed from prefix + $releasePrefix = ltrim($binarySuffix, '-'); + $phpEmbedName = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersion . '.so' + : 'libphp-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); $output = implode("\n", $output); - preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); + if (!preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches)) { + throw new \RuntimeException("Unable to detect FrankenPHP version from output: " . $output); + } $version = $matches[1]; $name = $this->getName(); @@ -208,12 +214,18 @@ public function createDebPackage(string $architecture, array $binaryDependencies $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); $binarySuffix = getBinarySuffix(); - $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; + // SPC produces libphp-{prefix}-{version}.so with only leading dash removed from prefix + $releasePrefix = ltrim($binarySuffix, '-'); + $phpEmbedName = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersion . '.so' + : 'libphp-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); $output = implode("\n", $output); - preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); + if (!preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches)) { + throw new \RuntimeException("Unable to detect FrankenPHP version from output: " . $output); + } $version = $matches[1]; $name = $this->getName(); @@ -346,12 +358,18 @@ public function createApkPackage(string $architecture, array $binaryDependencies $packageFolder = DIST_PATH . '/frankenphp/package'; $phpVersion = str_replace('.', '', SPP_PHP_VERSION); $binarySuffix = getBinarySuffix(); - $phpEmbedName = 'libphp' . $binarySuffix . '-' . $phpVersion . '.so'; + // SPC produces libphp-{prefix}-{version}.so with only leading dash removed from prefix + $releasePrefix = ltrim($binarySuffix, '-'); + $phpEmbedName = $releasePrefix !== '' + ? 'libphp-' . $releasePrefix . '-' . $phpVersion . '.so' + : 'libphp-' . $phpVersion . '.so'; $ldLibraryPath = 'LD_LIBRARY_PATH=' . BUILD_LIB_PATH; [, $output] = shell()->execWithResult($ldLibraryPath . ' ' . BUILD_BIN_PATH . '/frankenphp --version'); $output = implode("\n", $output); - preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches); + if (!preg_match('/FrankenPHP v(\d+\.\d+\.\d+)/', $output, $matches)) { + throw new \RuntimeException("Unable to detect FrankenPHP version from output: " . $output); + } $version = $matches[1]; $name = $this->getName(); diff --git a/src/package/meta.php b/src/package/meta.php new file mode 100644 index 0000000..53f8a5b --- /dev/null +++ b/src/package/meta.php @@ -0,0 +1,47 @@ + [ + CreatePackages::getPrefix() . '-cli', + ], + 'provides' => [], + 'replaces' => [], + 'conflicts' => [], + 'files' => [], + ]; + } + + public function getFpmExtraArgs(): array + { + return []; + } + + public function getDebuginfoFpmConfig(): array + { + return []; + } + + public function getLicense(): string + { + return 'PHP-3.01'; + } +} diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index eb0a7a2..8b252dc 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -68,6 +68,10 @@ public static function run($packageNames = null, ?string $iteration = null): tru self::createSapiPackages(); self::createSapiPackage('devel'); self::createGenericPackage('pie'); + // Create metapackage for APK to allow "apk add php-zts85" + if (in_array('apk', self::$packageTypes, true)) { + self::createGenericPackage('meta'); + } self::createExtensionPackages(); } diff --git a/src/step/RunSPC.php b/src/step/RunSPC.php index 2c6ed3f..3c9ef5f 100644 --- a/src/step/RunSPC.php +++ b/src/step/RunSPC.php @@ -2,118 +2,12 @@ namespace staticphp\step; -use ArrayIterator; use Exception; -use FilesystemIterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -use SPC\store\FileSystem; -use SplFileInfo; use Symfony\Component\Process\Process; use staticphp\util\TwigRenderer; class RunSPC { - private static function replaceInFiles(string $dir, string $builtDir, string $movedDir): void { - if (is_dir($dir)) { - $files = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS) - ); - } - else { - $files = new ArrayIterator([new SplFileInfo($dir)]); - } - - foreach ($files as $file) { - if (!$file->isFile()) { - continue; - } - - $path = $file->getPathname(); - $contents = file_get_contents($path); - - if ($contents === false) { - continue; - } - if (!str_contains($contents, $builtDir)) { - continue; - } - - $newContents = str_replace($builtDir, $movedDir, $contents); - - if ($newContents !== $contents) { - file_put_contents($path, $newContents); - } - } - } - - private static function fixGnuDebugLinks(): void - { - $debugDir = BUILD_ROOT_PATH . '/debug'; - $binDir = BUILD_BIN_PATH; - - if (!is_dir($debugDir)) { - echo "No debug directory found at {$debugDir}, skipping GNU debuglink normalization.\n"; - return; - } - - // Get the binary suffix from the prefix (e.g., "-zts", "-nts", "-zts8.5") - $binarySuffix = defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; - - $ensureRename = function (string $from, string $to) { - if ($from === $to) { - return; - } - if (file_exists($from)) { - if (!file_exists($to)) { - if (!@rename($from, $to)) { - echo "Failed to rename {$from} -> {$to}\n"; - } else { - echo "Renamed {$from} -> {$to}\n"; - } - } else { - @unlink($from); - } - } - }; - - // Rename debug files using the prefix - $sapiMap = [ - $binDir . '/php' => $debugDir . '/php' . $binarySuffix . '.debug', - $binDir . '/php-fpm' => $debugDir . '/php-fpm' . $binarySuffix . '.debug', - $binDir . '/php-cgi' => $debugDir . '/php-cgi' . $binarySuffix . '.debug', - $binDir . '/frankenphp' => $debugDir . '/frankenphp.debug', - ]; - - $ensureRename($debugDir . '/php.debug', $debugDir . '/php' . $binarySuffix . '.debug'); - $ensureRename($debugDir . '/php-fpm.debug', $debugDir . '/php-fpm' . $binarySuffix . '.debug'); - $ensureRename($debugDir . '/php-cgi.debug', $debugDir . '/php-cgi' . $binarySuffix . '.debug'); - - foreach ($sapiMap as $binary => $dbgFile) { - if (!file_exists($binary)) { - continue; - } - self::runProcess(['objcopy', '--remove-section=.gnu_debuglink', $binary], "Removed existing gnu-debuglink from {$binary}"); - if (file_exists($dbgFile)) { - self::runProcess(['objcopy', '--add-gnu-debuglink=' . $dbgFile, $binary], "Added gnu-debuglink to {$binary} -> {$dbgFile}"); - } - } - } - - private static function runProcess(array $cmd, string $okMessage): void - { - $p = new Process($cmd); - $p->setTimeout(null); - $p->run(); - if ($p->isSuccessful()) { - echo $okMessage . "\n"; - } else { - // Log but do not fail the build - $bin = is_array($cmd) ? implode(' ', $cmd) : (string)$cmd; - echo "Warning: command failed: {$bin}\n" . $p->getErrorOutput() . "\n"; - } - } - public static function run(bool $debug = false, string $phpVersion = '8.4', ?array $packages = null): bool { $craftYmlDest = BASE_PATH . '/vendor/crazywhalecc/static-php-cli/craft.yml'; @@ -155,17 +49,6 @@ public static function run(bool $debug = false, string $phpVersion = '8.4', ?arr // Copy the built files to our build directory self::copyBuiltFiles($phpVersion); - // Fix the prefix - $builtDir = BASE_PATH . '/vendor/crazywhalecc/static-php-cli/buildroot'; - $movedDir = BUILD_ROOT_PATH; - self::replaceInFiles(BUILD_BIN_PATH . '/php-config', $builtDir, $movedDir); - self::replaceInFiles(BUILD_BIN_PATH . '/php-config', '/app/buildroot', $movedDir); - self::replaceInFiles(BUILD_LIB_PATH . '/pkgconfig', $builtDir, $movedDir); - self::replaceInFiles(BUILD_LIB_PATH . '/pkgconfig', '/app/buildroot', $movedDir); - - // After files are copied and paths fixed, normalize GNU debug links - self::fixGnuDebugLinks(); - return true; } catch (Exception $e) { echo "Error running static-php-cli with: " . $e->getMessage() . "\n"; diff --git a/src/util/TwigRenderer.php b/src/util/TwigRenderer.php index 5b37020..26685b2 100644 --- a/src/util/TwigRenderer.php +++ b/src/util/TwigRenderer.php @@ -57,6 +57,12 @@ public static function renderCraftTemplate(string $phpVersion = '8.4', ?string $ $packageType = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; $libdir = $packageType === 'rpm' ? '/usr/lib64' : '/usr/lib'; + // Get the binary suffix (e.g., "-zts", "-nts", "-zts8.5") + $binarySuffix = defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; + // For the -release flag: remove only the leading dash (keep dots) + // e.g., "-zts" -> "zts", "-zts8.5" -> "zts8.5" + $releasePrefix = ltrim($binarySuffix, '-'); + $templateVars = [ 'php_version' => $phpVersion, 'php_version_nodot' => str_replace('.', '', $phpVersion), @@ -64,6 +70,7 @@ public static function renderCraftTemplate(string $phpVersion = '8.4', ?string $ 'arch' => $arch, 'os' => $majorOsVersion, 'prefix' => $prefix, + 'release_prefix' => $releasePrefix, 'confdir' => '/etc/' . $prefix, 'moduledir' => $libdir . '/' . $prefix . '/modules', // Optional filter: when provided, craft.yml will include only selected packages From 306e2311eda54cdbb70a18be9398bfa3ed586180 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 21 Dec 2025 01:04:30 +0100 Subject: [PATCH 15/37] versioned packages do not conflict with other versions --- src/package/frankenphp.php | 30 ++++++-------- src/package/frankenphp/alpine/post-install.sh | 4 ++ src/step/CreatePackages.php | 40 +------------------ 3 files changed, 19 insertions(+), 55 deletions(-) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index ba3534d..31deb91 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -46,25 +46,11 @@ public function getLicense(): string /** * Get list of versioned frankenphp packages to conflict/replace with - * For RPM packages, returns empty array (RPM uses module system instead) + * Returns empty array - versioned FrankenPHP packages can coexist */ private function getVersionedConflicts(): array { - // Get the conflicts list from CreatePackages using the franken prefix - $phpConflicts = CreatePackages::getVersionedConflicts(''); - - // Transform php{prefix} conflicts to frankenphp conflicts - // e.g., php-zts8.3 -> frankenphp8.3, php-nts85 -> frankenphp85 - $conflicts = []; - foreach ($phpConflicts as $conflict) { - // Replace the full php prefix with just the version part for frankenphp - // Extract just the version numbers - if (preg_match('/(\d+\.?\d*)/', $conflict, $matches)) { - $conflicts[] = 'frankenphp' . $matches[1]; - } - } - - return $conflicts; + return []; } /** @@ -424,7 +410,7 @@ public function createApkPackage(string $architecture, array $binaryDependencies } $nfpmConfig['depends'] = $depends; - $nfpmConfig['provides'] = ['frankenphp']; + $nfpmConfig['provides'] = [$this->getName() !== 'frankenphp' ? 'frankenphp' : '']; $nfpmConfig['replaces'] = $versionedConflicts; $nfpmConfig['conflicts'] = $versionedConflicts; @@ -676,6 +662,16 @@ private function getNextIteration(string $name, string $version, string $archite } } + $apkPattern = DIST_APK_PATH . "/{$name}-{$version}-r*.{$architecture}.apk"; + $apkFiles = glob($apkPattern); + + foreach ($apkFiles as $file) { + if (preg_match("/{$name}-{$version}-r(\d+)\.{$architecture}\.apk$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } + } + return $maxIteration + 1; } } diff --git a/src/package/frankenphp/alpine/post-install.sh b/src/package/frankenphp/alpine/post-install.sh index 5afeea7..d78af85 100755 --- a/src/package/frankenphp/alpine/post-install.sh +++ b/src/package/frankenphp/alpine/post-install.sh @@ -18,7 +18,11 @@ fi # trust FrankenPHP certificates if [ -x /usr/bin/frankenphp ]; then + HOME=/var/lib/frankenphp /usr/bin/frankenphp run >/dev/null 2>&1 & + FRANKENPHP_PID=$! + sleep 2 HOME=/var/lib/frankenphp /usr/bin/frankenphp trust || true + kill -TERM $FRANKENPHP_PID 2>/dev/null || true fi if command -v rc-update >/dev/null 2>&1; then diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 8b252dc..b2364a0 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -1059,43 +1059,7 @@ public static function getPrefix(): string */ public static function getVersionedConflicts(string $suffix): array { - // RPM packages use module system, no versioned conflicts needed - // Detect RPM by checking if prefix is unversioned (e.g., "-zts" or "-nts" without version number) - if (!preg_match('/\d/', self::$prefix)) { - return []; - } - - $conflicts = []; - $phpVersion = SPP_PHP_VERSION; - - if (!preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { - return []; - } - - $currentMajor = (int)$matches[1]; - $currentMinor = (int)$matches[2]; - - // Extract the base prefix (without version) and whether it uses dots - // e.g., "-zts8.5" -> base: "-zts", usesDot: true - // e.g., "-nts85" -> base: "-nts", usesDot: false - $usesDot = str_contains(self::$prefix, '.'); - $basePrefix = preg_replace('/\d+\.?\d*/', '', self::$prefix); - - // Generate conflicts for versions 8.0 through 8.9 - for ($minor = 0; $minor <= 9; $minor++) { - // Skip the current version - if ($currentMajor === 8 && $minor === $currentMinor) { - continue; - } - - // Use the same format as the current prefix - if ($usesDot) { - $conflicts[] = "php{$basePrefix}{$currentMajor}.{$minor}{$suffix}"; - } else { - $conflicts[] = "php{$basePrefix}{$currentMajor}{$minor}{$suffix}"; - } - } - - return $conflicts; + // Versioned packages can coexist - no conflicts + return []; } } From 838fe1d928af3d81476551321365f4fc51e546e8 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 21 Dec 2025 01:58:23 +0100 Subject: [PATCH 16/37] pie wrapper cleanup (depend on sed on alpine) --- composer.lock | 8 ++++---- config/templates/pie-wrapper.twig | 18 ++++++++++++++++++ src/ini/pie-zts | 18 ------------------ src/package/devel.php | 23 +++++++++++++++++++---- src/package/frankenphp.php | 4 +++- src/package/pie.php | 27 ++++++++------------------- src/util/TwigRenderer.php | 20 ++++++++++++++++++++ 7 files changed, 72 insertions(+), 46 deletions(-) create mode 100644 config/templates/pie-wrapper.twig delete mode 100755 src/ini/pie-zts diff --git a/composer.lock b/composer.lock index 6d88036..26f3dba 100644 --- a/composer.lock +++ b/composer.lock @@ -81,12 +81,12 @@ "source": { "type": "git", "url": "https://github.com/crazywhalecc/static-php-cli.git", - "reference": "53f7cdefe0e791d621d86cc2ce6938d228ec3b19" + "reference": "6b5200002e6d744a450038e982c3c88d386ef3e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/53f7cdefe0e791d621d86cc2ce6938d228ec3b19", - "reference": "53f7cdefe0e791d621d86cc2ce6938d228ec3b19", + "url": "https://api.github.com/repos/crazywhalecc/static-php-cli/zipball/6b5200002e6d744a450038e982c3c88d386ef3e6", + "reference": "6b5200002e6d744a450038e982c3c88d386ef3e6", "shasum": "" }, "require": { @@ -141,7 +141,7 @@ "type": "other" } ], - "time": "2025-12-18T19:12:01+00:00" + "time": "2025-12-20T22:29:25+00:00" }, { "name": "doctrine/inflector", diff --git a/config/templates/pie-wrapper.twig b/config/templates/pie-wrapper.twig new file mode 100644 index 0000000..93075cc --- /dev/null +++ b/config/templates/pie-wrapper.twig @@ -0,0 +1,18 @@ +#!/bin/sh +# pie{{ binary_suffix }} wrapper +# Runs php/pie with the PHP binary. Adds --with-php-config only when not provided by the user. + +have_cfg=0 +for arg in "$@"; do + case "$arg" in + --with-php-config|--with-php-config=*) + have_cfg=1 + ;; + esac +done + +if [ "$have_cfg" -eq 1 ]; then + exec /usr/bin/php{{ binary_suffix }} {{ sharedir }}/pie.phar "$@" +else + exec /usr/bin/php{{ binary_suffix }} {{ sharedir }}/pie.phar --with-php-config=/usr/bin/php-config{{ binary_suffix }} "$@" +fi diff --git a/src/ini/pie-zts b/src/ini/pie-zts deleted file mode 100755 index 98dab58..0000000 --- a/src/ini/pie-zts +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# pie-zts wrapper -# Runs php/pie with the ZTS PHP binary. Adds --with-php-config only when not provided by the user. - -have_cfg=0 -for arg in "$@"; do - case "$arg" in - --with-php-config|--with-php-config=*) - have_cfg=1 - ;; - esac -done - -if [ "$have_cfg" -eq 1 ]; then - exec /usr/bin/php-zts /usr/share/php-zts/pie.phar "$@" -else - exec /usr/bin/php-zts /usr/share/php-zts/pie.phar --with-php-config=/usr/bin/php-config-zts "$@" -fi diff --git a/src/package/devel.php b/src/package/devel.php index 29925a4..b702913 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -52,6 +52,11 @@ public function getFpmConfig(): array : 'libphp-' . $phpVersion . '.so'; $phpConfigContent = str_replace('libphp.so', $libName, $phpConfigContent); + // For APK, sed is in /bin/sed instead of /usr/bin/sed + if (defined('SPP_TYPE') && SPP_TYPE === 'apk') { + $phpConfigContent = str_replace('/usr/bin/sed', '/bin/sed', $phpConfigContent); + } + file_put_contents($modifiedPhpConfigPath, $phpConfigContent); chmod($modifiedPhpConfigPath, 0755); @@ -87,20 +92,30 @@ public function getFpmConfig(): array $phpizeContent ); + // For APK, sed is in /bin/sed instead of /usr/bin/sed + if (defined('SPP_TYPE') && SPP_TYPE === 'apk') { + $phpizeContent = str_replace('/usr/bin/sed', '/bin/sed', $phpizeContent); + } + file_put_contents($modifiedPhpizePath, $phpizeContent); chmod($modifiedPhpizePath, 0755); $versionedConflicts = CreatePackages::getVersionedConflicts('-devel'); + + // APK needs sed dependency since php-config uses sed + $depends = [CreatePackages::getPrefix() . '-cli']; + if (defined('SPP_TYPE') && SPP_TYPE === 'apk') { + $depends[] = 'sed'; + } + return [ 'files' => [ $modifiedPhpConfigPath => '/usr/bin/php-config' . getBinarySuffix(), $modifiedPhpizePath => '/usr/bin/phpize' . getBinarySuffix(), BUILD_INCLUDE_PATH . '/php/' => '/usr/include/' . \staticphp\step\CreatePackages::getPrefix(), - BUILD_LIB_PATH . '/php/build' => getPhpLibdir(), - ], - 'depends' => [ - CreatePackages::getPrefix() . '-cli', + BUILD_LIB_PATH . '/php/build' => getPhpLibdir() . '/build', ], + 'depends' => $depends, 'provides' => [ 'php-config' . getBinarySuffix(), 'phpize' . getBinarySuffix(), diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index 31deb91..f9e1602 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -382,7 +382,9 @@ public function createApkPackage(string $architecture, array $binaryDependencies ]; // Build dependencies - $depends = [$phpEmbedName]; + // For APK, depend on the embed package, not the .so file + $embedPackageName = CreatePackages::getPrefix() . '-embed'; + $depends = [$embedPackageName]; // Alpine library dependencies $alpineLibMap = [ diff --git a/src/package/pie.php b/src/package/pie.php index 0dcaeb0..8bccbc7 100644 --- a/src/package/pie.php +++ b/src/package/pie.php @@ -26,7 +26,7 @@ public function getVersion(): string [$pharSource] = $this->prepareArtifacts(); $proc = new Process(['php', $pharSource, '-V'], env: self::getCleanEnvironment()); - $proc->setTimeout(30); + $proc->setTimeout(2); $proc->run(); if (!$proc->isSuccessful()) { // Include both stdout and stderr for parsing attempt/fallback @@ -114,26 +114,15 @@ private function prepareArtifacts(): array $this->downloadLatestPiePhar($pharPath); } - // Process the pie wrapper script to replace hardcoded paths - $wrapperSource = INI_PATH . '/pie-zts'; + // Render the pie wrapper script using Twig template $binarySuffix = getBinarySuffix(); $wrapperPath = TEMP_DIR . '/pie' . $binarySuffix; - $wrapperContents = file_get_contents($wrapperSource); - - // Replace ALL hardcoded paths with prefix-based paths - $wrapperContents = preg_replace( - [ - '#/usr/bin/php[^ ]*#', - '#/usr/share/php[^ /]*/pie\.phar#', - '#/usr/bin/php-config[^ ]*#', - ], - [ - '/usr/bin/php' . $binarySuffix, - getSharedir() . '/pie.phar', - '/usr/bin/php-config' . $binarySuffix, - ], - $wrapperContents - ); + + $wrapperContents = \staticphp\util\TwigRenderer::render('pie-wrapper.twig', [ + 'binary_suffix' => $binarySuffix, + 'sharedir' => getSharedir(), + ]); + file_put_contents($wrapperPath, $wrapperContents); chmod($wrapperPath, 0755); diff --git a/src/util/TwigRenderer.php b/src/util/TwigRenderer.php index 26685b2..be573a3 100644 --- a/src/util/TwigRenderer.php +++ b/src/util/TwigRenderer.php @@ -8,6 +8,26 @@ class TwigRenderer { + /** + * Renders any Twig template with the given variables + * + * @param string $templateName Template file name (e.g., 'pie-wrapper.twig') + * @param array $variables Variables to pass to the template + * @return string The rendered template content + * @throws \RuntimeException If there's an error rendering the template + */ + public static function render(string $templateName, array $variables = []): string + { + $loader = new FilesystemLoader(BASE_PATH . '/config/templates'); + $twig = new Environment($loader); + + try { + return $twig->render($templateName, $variables); + } catch (\Exception $e) { + throw new \RuntimeException("Error rendering template {$templateName}: " . $e->getMessage()); + } + } + /** * Renders a Twig template with the given variables * From bb31657faa2c13aec3a62d9775a34cd6062f4d51 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 21 Dec 2025 22:23:09 +0100 Subject: [PATCH 17/37] why is apk so shit --- config/templates/craft.yml.twig | 4 ++-- src/step/CreatePackages.php | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/config/templates/craft.yml.twig b/config/templates/craft.yml.twig index 39a96b5..f158b0e 100644 --- a/config/templates/craft.yml.twig +++ b/config/templates/craft.yml.twig @@ -63,13 +63,13 @@ craft-options: {% set arch_flags = arch == 'x86_64' ? ' -mtls-dialect=gnu2 -m64 -fcf-protection' : '' %} {% if arch == 'x86_64' and os == '10' %} {% set arch_flags = arch_flags ~ ' -march=x86-64-v3' %} {% endif %} {% if arch == 'x86_64' and os == '9' %} {% set arch_flags = arch_flags ~ ' -march=x86-64-v2' %} {% endif %} -{% set using_gcc = target == ('native-native-gnu' or target == 'native-native') and php_version < '8.5' %} +{% set using_gcc = (target == 'native-native-gnu' or target == 'native-native') and php_version < '8.5' %} {% set specs_cflags = '-specs=/usr/lib/rpm/redhat/redhat-hardened-cc1' %} {% set specs_ldflags = '-specs=/usr/lib/rpm/redhat/redhat-hardened-ld' ~ (os == '10' ? ' -Wl,-z,pack-relative-relocs' : '') %} {% set cflags = '-fPIC -O3 -pipe -fno-plt -fno-semantic-interposition -fasynchronous-unwind-tables -fstack-clash-protection -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -fno-strict-aliasing' ~ arch_flags %} -{% set is_rhel = os in ['7', '8', '9', '10'] %} +{% set is_rhel = os in ['7', '8', '9', '10'] and target == 'native-native-gnu' %} {% if using_gcc and is_rhel %} {% if os == '7' or os == '8' %} {% set cflags = cflags ~ ' -Wp,-D_FORTIFY_SOURCE=2' ~ ' ' ~ specs_cflags %} diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index b2364a0..9e79942 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -698,7 +698,14 @@ private static function createApkWithNfpm(\staticphp\package $package, string $n // Add provides, replaces, conflicts if (isset($config['provides']) && is_array($config['provides'])) { - $nfpmConfig['provides'] = $config['provides']; + // For APK cli packages: filter out the base prefix from provides since we have a separate meta package + // This prevents conflicts between php-zts-cli (which provides php-zts) and the php-zts meta package + $provides = $config['provides']; + if ($name === self::getPrefix() . '-cli') { + $provides = array_values(array_filter($provides, fn($p) => $p !== self::getPrefix())); + $provides = array_values($provides); + } + $nfpmConfig['provides'] = $provides; } if (isset($config['replaces']) && is_array($config['replaces'])) { $nfpmConfig['replaces'] = $config['replaces']; From 3a8c1c8d38d511d65ec5bdb6adfebfeaf13daf08 Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 21 Dec 2025 22:30:49 +0100 Subject: [PATCH 18/37] dont require sed --- src/package/devel.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/package/devel.php b/src/package/devel.php index b702913..8d5246b 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -102,12 +102,6 @@ public function getFpmConfig(): array $versionedConflicts = CreatePackages::getVersionedConflicts('-devel'); - // APK needs sed dependency since php-config uses sed - $depends = [CreatePackages::getPrefix() . '-cli']; - if (defined('SPP_TYPE') && SPP_TYPE === 'apk') { - $depends[] = 'sed'; - } - return [ 'files' => [ $modifiedPhpConfigPath => '/usr/bin/php-config' . getBinarySuffix(), @@ -115,7 +109,7 @@ public function getFpmConfig(): array BUILD_INCLUDE_PATH . '/php/' => '/usr/include/' . \staticphp\step\CreatePackages::getPrefix(), BUILD_LIB_PATH . '/php/build' => getPhpLibdir() . '/build', ], - 'depends' => $depends, + 'depends' => [CreatePackages::getPrefix() . '-cli'], 'provides' => [ 'php-config' . getBinarySuffix(), 'phpize' . getBinarySuffix(), From 5a7db97e42a29a3d0659059f4dd511229bf58a7f Mon Sep 17 00:00:00 2001 From: henderkes Date: Sun, 21 Dec 2025 22:31:13 +0100 Subject: [PATCH 19/37] or do? --- src/package/devel.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/package/devel.php b/src/package/devel.php index 8d5246b..b702913 100644 --- a/src/package/devel.php +++ b/src/package/devel.php @@ -102,6 +102,12 @@ public function getFpmConfig(): array $versionedConflicts = CreatePackages::getVersionedConflicts('-devel'); + // APK needs sed dependency since php-config uses sed + $depends = [CreatePackages::getPrefix() . '-cli']; + if (defined('SPP_TYPE') && SPP_TYPE === 'apk') { + $depends[] = 'sed'; + } + return [ 'files' => [ $modifiedPhpConfigPath => '/usr/bin/php-config' . getBinarySuffix(), @@ -109,7 +115,7 @@ public function getFpmConfig(): array BUILD_INCLUDE_PATH . '/php/' => '/usr/include/' . \staticphp\step\CreatePackages::getPrefix(), BUILD_LIB_PATH . '/php/build' => getPhpLibdir() . '/build', ], - 'depends' => [CreatePackages::getPrefix() . '-cli'], + 'depends' => $depends, 'provides' => [ 'php-config' . getBinarySuffix(), 'phpize' . getBinarySuffix(), From 281293cf4b3d24d9dcd5cb0c531305887ee57ae7 Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 00:13:58 +0100 Subject: [PATCH 20/37] dont create debuginfo by default on alpine/deb --- src/package/frankenphp.php | 38 +++++++++++++++++-------------- src/step/CreatePackages.php | 45 +++++++++++++++++++++++++------------ 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index f9e1602..2eb8de3 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -56,7 +56,7 @@ private function getVersionedConflicts(): array /** * Create FrankenPHP packages (both RPM and DEB) */ - public function createPackages(array $packageTypes, array $binaryDependencies, ?string $iterationOverride = null): void + public function createPackages(string $packageType, array $binaryDependencies, ?string $iterationOverride = null, bool $debuginfo = false): void { echo "Creating FrankenPHP package\n"; @@ -64,21 +64,21 @@ public function createPackages(array $packageTypes, array $binaryDependencies, ? $this->prepareFrankenPhpRepository(); - if (in_array('rpm', $packageTypes, true)) { - $this->createRpmPackage($architecture, $binaryDependencies, $iterationOverride); + if ($packageType === 'rpm') { + $this->createRpmPackage($architecture, $binaryDependencies, $iterationOverride, $debuginfo); } - if (in_array('deb', $packageTypes, true)) { - $this->createDebPackage($architecture, $binaryDependencies, $iterationOverride); + if ($packageType === 'deb') { + $this->createDebPackage($architecture, $binaryDependencies, $iterationOverride, $debuginfo); } - if (in_array('apk', $packageTypes, true)) { - $this->createApkPackage($architecture, $binaryDependencies, $iterationOverride); + if ($packageType === 'apk') { + $this->createApkPackage($architecture, $binaryDependencies, $iterationOverride, $debuginfo); } } /** * Create RPM package for FrankenPHP */ - public function createRpmPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void + public function createRpmPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null, bool $debuginfo = false): void { echo "Creating RPM package for FrankenPHP...\n"; @@ -193,7 +193,7 @@ public function createRpmPackage(string $architecture, array $binaryDependencies /** * Create DEB package for FrankenPHP */ - public function createDebPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void + public function createDebPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null, bool $debuginfo = false): void { echo "Creating DEB package for FrankenPHP...\n"; @@ -306,9 +306,10 @@ public function createDebPackage(string $architecture, array $binaryDependencies echo "DEB package created: " . DIST_DEB_PATH . "/{$name}-{$version}-{$debIteration}.{$architecture}.deb\n"; - // Create FrankenPHP debuginfo package if debug file exists - $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; - if (file_exists($frankenDbg)) { + // Create FrankenPHP debuginfo package if debug file exists (only if --debuginfo flag set for DEB) + if ($debuginfo) { + $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; + if (file_exists($frankenDbg)) { $dbgArgs = [ 'fpm', '-s', 'dir', @@ -331,13 +332,14 @@ public function createDebPackage(string $architecture, array $binaryDependencies if (!$dbgProcess->isSuccessful()) { throw new \RuntimeException("DEB debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); } + } } } /** * Create APK package for FrankenPHP */ - public function createApkPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null): void + public function createApkPackage(string $architecture, array $binaryDependencies, ?string $iterationOverride = null, bool $debuginfo = false): void { echo "Creating APK package for FrankenPHP using nfpm...\n"; @@ -494,10 +496,12 @@ public function createApkPackage(string $architecture, array $binaryDependencies echo "APK package created: {$outputFile}\n"; - // Create FrankenPHP debuginfo package if debug file exists - $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; - if (file_exists($frankenDbg)) { - $this->createApkDebuginfo($name, $version, $iteration, $architecture, $frankenDbg, $frankenphpSuffix); + // Create FrankenPHP debuginfo package if debug file exists (only if --debuginfo flag set for APK) + if ($debuginfo) { + $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; + if (file_exists($frankenDbg)) { + $this->createApkDebuginfo($name, $version, $iteration, $architecture, $frankenDbg, $frankenphpSuffix); + } } } diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 9e79942..0183b12 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -14,11 +14,12 @@ class CreatePackages private static $sharedExtensions = []; private static $sapis = []; private static $binaryDependencies = []; - private static $packageTypes = []; + private static string $packageType = 'rpm'; private static ?string $iterationOverride = null; private static string $prefix = '-zts'; + private static bool $debuginfo = false; - public static function run($packageNames = null, ?string $iteration = null): true + public static function run($packageNames = null, ?string $iteration = null, ?bool $debuginfo = null): true { self::loadConfig(); @@ -30,12 +31,14 @@ public static function run($packageNames = null, ?string $iteration = null): tru // Use values from constants set by BaseCommand self::$prefix = defined('SPP_PREFIX') ? SPP_PREFIX : '-zts'; - $packageType = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; - - // Package type is now a single value, not a comma-separated list - self::$packageTypes = [$packageType]; + self::$packageType = defined('SPP_TYPE') ? SPP_TYPE : 'rpm'; self::$iterationOverride = $iteration !== null && $iteration !== '' ? $iteration : null; + // Set debuginfo flag from parameter + if ($debuginfo !== null) { + self::$debuginfo = $debuginfo; + } + if ($packageNames !== null) { if (is_string($packageNames)) { $packageNames = [$packageNames]; @@ -69,7 +72,7 @@ public static function run($packageNames = null, ?string $iteration = null): tru self::createSapiPackage('devel'); self::createGenericPackage('pie'); // Create metapackage for APK to allow "apk add php-zts85" - if (in_array('apk', self::$packageTypes, true)) { + if (self::$packageType === 'apk') { self::createGenericPackage('meta'); } self::createExtensionPackages(); @@ -106,9 +109,12 @@ private static function createGenericPackage(string $name): void self::createPackageWithFpm($package, $pkgVersion, $architecture, $iteration); + // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { - self::createPackageWithFpm($package, $pkgVersion, $architecture, $iteration, true); + if (self::$packageType === 'rpm' || self::$debuginfo) { + self::createPackageWithFpm($package, $pkgVersion, $architecture, $iteration, true); + } } } @@ -149,7 +155,7 @@ private static function createSapiPackage(string $sapi): void // FrankenPHP has a special package creation flow if ($sapi === 'frankenphp') { $package = new $packageClass(); - $package->createPackages(self::$packageTypes, self::$binaryDependencies, self::$iterationOverride); + $package->createPackages(self::$packageType, self::$binaryDependencies, self::$iterationOverride, self::$debuginfo); return; } @@ -162,9 +168,12 @@ private static function createSapiPackage(string $sapi): void self::createPackageWithFpm($package, $phpVersion, $architecture, $iteration); + // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { - self::createPackageWithFpm($package, $phpVersion, $architecture, $iteration, true); + if (self::$packageType === 'rpm' || self::$debuginfo) { + self::createPackageWithFpm($package, $phpVersion, $architecture, $iteration, true); + } } } @@ -201,9 +210,12 @@ private static function createExtensionPackage(string $extension): void self::createPackageWithFpm($package, $extensionVersion, $architecture, $iteration); + // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { - self::createPackageWithFpm($package, $extensionVersion, $architecture, $iteration, true); + if (self::$packageType === 'rpm' || self::$debuginfo) { + self::createPackageWithFpm($package, $extensionVersion, $architecture, $iteration, true); + } } } @@ -274,15 +286,15 @@ private static function getExtensionVersion(string $extension, string $phpVersio private static function createPackageWithFpm(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void { - if (in_array('rpm', self::$packageTypes, true)) { + if (self::$packageType === 'rpm') { self::createRpmPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); } - if (in_array('deb', self::$packageTypes, true)) { + if (self::$packageType === 'deb') { self::createDebPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); } - if (in_array('apk', self::$packageTypes, true)) { + if (self::$packageType === 'apk') { self::createApkPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); } } @@ -1059,6 +1071,11 @@ public static function getPrefix(): string return 'php' . self::$prefix; } + public static function getDebuginfo(): bool + { + return self::$debuginfo; + } + /** * Get list of versioned package names to conflict/replace with * For example, for php-zts8.5-cli, returns [php-zts8.0-cli, php-zts8.1-cli, ..., php-zts8.9-cli] excluding 8.5 From 1156a787d0223107c76d154558b2112d41c95c5b Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 11:54:18 +0100 Subject: [PATCH 21/37] some more versioning detection stuff --- src/package/frankenphp.php | 206 ++++++++++++++++++++++++++---------- src/step/CreatePackages.php | 191 ++++++++++++++++++++++++--------- 2 files changed, 290 insertions(+), 107 deletions(-) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index 2eb8de3..2f54271 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -101,17 +101,24 @@ public function createRpmPackage(string $architecture, array $binaryDependencies $name = $this->getName(); - $computed = (string)$this->getNextIteration($name, $version, $architecture); + // Calculate iteration for RPM (with possible override) + $computed = (string)$this->getNextIteration($name, $version, $architecture, 'rpm'); $iteration = $iterationOverride ?? $computed; $versionedConflicts = $this->getVersionedConflicts(); + // Generate full package filename with PHP version suffix and distribution version + $phpSuffix = $this->getPhpVersionSuffix(); + $distVersion = $this->getDistVersion(); + $distSuffix = $distVersion !== '' ? ".{$distVersion}" : ''; + $packageFile = DIST_RPM_PATH . "/{$name}-{$version}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $fpmArgs = [ 'fpm', '-s', 'dir', '-t', 'rpm', '--rpm-compression', 'xz', - '-p', DIST_RPM_PATH, + '-p', $packageFile, // Full path with phpSuffix and distVersion in filename '-n', $name, '-v', $version, '--license', $this->getLicense(), @@ -160,17 +167,18 @@ public function createRpmPackage(string $architecture, array $binaryDependencies echo $buffer; }); - echo "RPM package created: " . DIST_RPM_PATH . "/{$name}-{$version}-{$iteration}.{$architecture}.rpm\n"; + echo "RPM package created: {$packageFile}\n"; // Create FrankenPHP debuginfo package if debug file exists $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { + $dbgPackageFile = DIST_RPM_PATH . "/{$name}-debuginfo-{$version}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; $dbgArgs = [ 'fpm', '-s', 'dir', '-t', 'rpm', '--rpm-compression', 'xz', - '-p', DIST_RPM_PATH, + '-p', $dbgPackageFile, '-n', $name . '-debuginfo', '-v', $version, '--iteration', $iteration, @@ -187,6 +195,8 @@ public function createRpmPackage(string $architecture, array $binaryDependencies if (!$dbgProcess->isSuccessful()) { throw new \RuntimeException("RPM debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); } + + echo "RPM debuginfo package created: {$dbgPackageFile}\n"; } } @@ -216,18 +226,23 @@ public function createDebPackage(string $architecture, array $binaryDependencies $name = $this->getName(); - $computed = (string)$this->getNextIteration($name, $version, $architecture); + // Calculate iteration for DEB (with possible override) + $computed = (string)$this->getNextIteration($name, $version, $architecture, 'deb'); $iteration = $iterationOverride ?? $computed; $debIteration = $iteration; $versionedConflicts = $this->getVersionedConflicts(); + // Generate full package filename with PHP version suffix + $phpSuffix = $this->getPhpVersionSuffix(); + $packageFile = DIST_DEB_PATH . "/{$name}_{$version}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + $fpmArgs = [ 'fpm', '-s', 'dir', '-t', 'deb', '--deb-compression', 'xz', - '-p', DIST_DEB_PATH, + '-p', $packageFile, // Full path with phpSuffix in filename '-n', $name, '-v', $version, '--license', $this->getLicense(), @@ -304,34 +319,37 @@ public function createDebPackage(string $architecture, array $binaryDependencies echo $buffer; }); - echo "DEB package created: " . DIST_DEB_PATH . "/{$name}-{$version}-{$debIteration}.{$architecture}.deb\n"; + echo "DEB package created: {$packageFile}\n"; // Create FrankenPHP debuginfo package if debug file exists (only if --debuginfo flag set for DEB) if ($debuginfo) { $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { - $dbgArgs = [ - 'fpm', - '-s', 'dir', - '-t', 'deb', - '--deb-compression', 'xz', - '-p', DIST_DEB_PATH, - '-n', $name . '-debuginfo', - '-v', $version, - '--iteration', $debIteration, - '--architecture', $architecture, - '--license', $this->getLicense(), - '--depends', sprintf('%s (= %s-%s)', $name, $version, $debIteration), - $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', - ]; - $dbgProcess = new Process($dbgArgs); - $dbgProcess->setTimeout(null); - $dbgProcess->run(function ($type, $buffer) { - echo $buffer; - }); - if (!$dbgProcess->isSuccessful()) { - throw new \RuntimeException("DEB debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); - } + $dbgPackageFile = DIST_DEB_PATH . "/{$name}-debuginfo_{$version}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + $dbgArgs = [ + 'fpm', + '-s', 'dir', + '-t', 'deb', + '--deb-compression', 'xz', + '-p', $dbgPackageFile, + '-n', $name . '-debuginfo', + '-v', $version, + '--iteration', $debIteration, + '--architecture', $architecture, + '--license', $this->getLicense(), + '--depends', sprintf('%s (= %s-%s)', $name, $version, $debIteration), + $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', + ]; + $dbgProcess = new Process($dbgArgs); + $dbgProcess->setTimeout(null); + $dbgProcess->run(function ($type, $buffer) { + echo $buffer; + }); + if (!$dbgProcess->isSuccessful()) { + throw new \RuntimeException("DEB debuginfo package creation failed: " . $dbgProcess->getErrorOutput()); + } + + echo "DEB debuginfo package created: {$dbgPackageFile}\n"; } } } @@ -362,7 +380,8 @@ public function createApkPackage(string $architecture, array $binaryDependencies $name = $this->getName(); - $computed = (string)$this->getNextIteration($name, $version, $architecture); + // Calculate iteration for APK (with possible override) + $computed = (string)$this->getNextIteration($name, $version, $architecture, 'apk'); $iteration = $iterationOverride ?? $computed; $versionedConflicts = $this->getVersionedConflicts(); @@ -473,8 +492,9 @@ public function createApkPackage(string $architecture, array $binaryDependencies echo "nfpm config written to: {$nfpmConfigFile}\n"; - // Run nfpm - $outputFile = DIST_APK_PATH . "/{$name}-{$version}-r{$iteration}.{$architecture}.apk"; + // Run nfpm with full filename including PHP version suffix + $phpSuffix = $this->getPhpVersionSuffix(); + $outputFile = DIST_APK_PATH . "/{$name}-{$version}-r{$iteration}.{$phpSuffix}.{$architecture}.apk"; $nfpmProcess = new Process([ 'nfpm', 'package', '--config', $nfpmConfigFile, @@ -536,7 +556,8 @@ private function createApkDebuginfo(string $name, string $version, string $itera throw new \RuntimeException("Failed to write YAML file: {$nfpmConfigFile}"); } - $outputFile = DIST_APK_PATH . "/{$dbgName}-{$version}-r{$iteration}.{$architecture}.apk"; + $phpSuffix = $this->getPhpVersionSuffix(); + $outputFile = DIST_APK_PATH . "/{$dbgName}-{$version}-r{$iteration}.{$phpSuffix}.{$architecture}.apk"; $dbgProcess = new Process([ 'nfpm', 'package', '--config', $nfpmConfigFile, @@ -553,6 +574,7 @@ private function createApkDebuginfo(string $name, string $version, string $itera } @unlink($nfpmConfigFile); + echo "APK debuginfo package created: {$outputFile}\n"; } @@ -644,40 +666,114 @@ private function getPhpVersionAndArchitecture(): array /** * Get next iteration number for package */ - private function getNextIteration(string $name, string $version, string $architecture): int + private function getNextIteration(string $name, string $version, string $architecture, string $packageType): int { $maxIteration = 0; - $rpmPattern = DIST_RPM_PATH . "/{$name}-{$version}-*.{$architecture}.rpm"; - $rpmFiles = glob($rpmPattern); - - foreach ($rpmFiles as $file) { - if (preg_match("/{$name}-{$version}-(\d+)\.{$architecture}\.rpm$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + if ($packageType === 'rpm') { + // RPM: {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm + // Also match old formats: + // - {name}-{version}-{iteration}.{arch}.rpm (no suffix) + // - {name}-{version}-{iteration}.{phpSuffix}.{arch}.rpm (no distVersion) + $rpmPattern = DIST_RPM_PATH . "/{$name}-{$version}-*.rpm"; + $rpmFiles = glob($rpmPattern); + + foreach ($rpmFiles as $file) { + // Match all formats: iteration followed by 0-2 parts, then arch.rpm + if (preg_match("/{$name}-" . preg_quote($version, '/') . "-(\d+)(?:\.[^.]+){0,2}\.{$architecture}\.rpm$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } - $debPattern = DIST_DEB_PATH . "/{$name}_{$version}-*_{$architecture}.deb"; - $debFiles = glob($debPattern); - - foreach ($debFiles as $file) { - if (preg_match("/{$name}_{$version}-(\d+)_{$architecture}\.deb$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + if ($packageType === 'deb') { + // DEB: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb + // Also match old format without phpSuffix: {name}_{version}-{iteration}_{arch}.deb + $debPattern = DIST_DEB_PATH . "/{$name}_{$version}-*.deb"; + $debFiles = glob($debPattern); + + foreach ($debFiles as $file) { + // Match both formats: + // - New: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb + // - Old: {name}_{version}-{iteration}_{arch}.deb (without phpSuffix) + if (preg_match("/{$name}_" . preg_quote($version, '/') . "-(\d+)(?:\.[^_]+)?_{$architecture}\.deb$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } - $apkPattern = DIST_APK_PATH . "/{$name}-{$version}-r*.{$architecture}.apk"; - $apkFiles = glob($apkPattern); - - foreach ($apkFiles as $file) { - if (preg_match("/{$name}-{$version}-r(\d+)\.{$architecture}\.apk$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + if ($packageType === 'apk') { + // APK: {name}-{version}-r{iteration}.{phpSuffix}.{arch}.apk + // Also match old format: {name}-{version}-r{iteration}.{arch}.apk (no phpSuffix) + $apkPattern = DIST_APK_PATH . "/{$name}-{$version}-r*.apk"; + $apkFiles = glob($apkPattern); + + foreach ($apkFiles as $file) { + // Match both formats: r{iteration} followed by 0-1 parts, then arch.apk + if (preg_match("/{$name}-" . preg_quote($version, '/') . "-r(\d+)(?:\.[^.]+)?\.{$architecture}\.apk$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } return $maxIteration + 1; } + + /** + * Get PHP version suffix for package filenames (e.g., "static-83" for PHP 8.3) + */ + private function getPhpVersionSuffix(): string + { + [$phpVersion,] = $this->getPhpVersionAndArchitecture(); + + // Extract major.minor version (e.g., "8.3.29" -> "8.3") + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + $majorMinorNoDot = $matches[1] . $matches[2]; // e.g., "83" + } else { + $majorMinorNoDot = str_replace('.', '', $phpVersion); + } + + // Construct suffix: static-{version} (e.g., "static-83") + return 'static-' . $majorMinorNoDot; + } + + /** + * Get distribution version for RPM filenames (e.g., "el9", "el8", "fc39") + */ + private function getDistVersion(): string + { + if (!file_exists('/etc/os-release')) { + return ''; + } + + $osRelease = parse_ini_file('/etc/os-release'); + if (!$osRelease || !isset($osRelease['ID'], $osRelease['VERSION_ID'])) { + return ''; + } + + $id = $osRelease['ID']; + $versionId = $osRelease['VERSION_ID']; + + // Extract major version number + if (preg_match('/^(\d+)/', $versionId, $matches)) { + $majorVersion = $matches[1]; + } else { + return ''; + } + + // Map distribution ID to prefix + $distMap = [ + 'rhel' => 'el', + 'centos' => 'el', + 'rocky' => 'el', + 'almalinux' => 'el', + 'fedora' => 'fc', + ]; + + $prefix = $distMap[$id] ?? ''; + return $prefix !== '' ? $prefix . $majorVersion : ''; + } } diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 0183b12..97ffebf 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -104,16 +104,13 @@ private static function createGenericPackage(string $name): void $package = $pkg ?? new $packageClass(); - $computed = (string)self::getNextIteration($package->getName(), $pkgVersion, $architecture); - $iteration = self::$iterationOverride ?? $computed; - - self::createPackageWithFpm($package, $pkgVersion, $architecture, $iteration); + self::createPackageWithFpm($package, $pkgVersion, $architecture); // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { if (self::$packageType === 'rpm' || self::$debuginfo) { - self::createPackageWithFpm($package, $pkgVersion, $architecture, $iteration, true); + self::createPackageWithFpm($package, $pkgVersion, $architecture, true); } } } @@ -163,16 +160,13 @@ private static function createSapiPackage(string $sapi): void $package = new $packageClass(); - $computed = (string)self::getNextIteration($package->getName(), $phpVersion, $architecture); - $iteration = self::$iterationOverride ?? $computed; - - self::createPackageWithFpm($package, $phpVersion, $architecture, $iteration); + self::createPackageWithFpm($package, $phpVersion, $architecture); // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { if (self::$packageType === 'rpm' || self::$debuginfo) { - self::createPackageWithFpm($package, $phpVersion, $architecture, $iteration, true); + self::createPackageWithFpm($package, $phpVersion, $architecture, true); } } } @@ -194,9 +188,6 @@ private static function createExtensionPackage(string $extension): void [$phpVersion, $architecture] = self::getPhpVersionAndArchitecture(); $extensionVersion = self::getExtensionVersion($extension, $phpVersion); - $computed = (string)self::getNextIteration(self::getPrefix() . "-{$extension}", $extensionVersion, $architecture); - $iteration = self::$iterationOverride ?? $computed; - $package = new extension($extension); $packageClass = "\\staticphp\\package\\{$extension}"; if (class_exists($packageClass)) { @@ -208,13 +199,13 @@ private static function createExtensionPackage(string $extension): void return; } - self::createPackageWithFpm($package, $extensionVersion, $architecture, $iteration); + self::createPackageWithFpm($package, $extensionVersion, $architecture); // Create debuginfo packages: always for RPM, only if --debuginfo flag set for others $dbgConfig = $package->getDebuginfoFpmConfig(); if (is_array($dbgConfig) && !empty($dbgConfig['files'])) { if (self::$packageType === 'rpm' || self::$debuginfo) { - self::createPackageWithFpm($package, $extensionVersion, $architecture, $iteration, true); + self::createPackageWithFpm($package, $extensionVersion, $architecture, true); } } } @@ -284,22 +275,22 @@ private static function getExtensionVersion(string $extension, string $phpVersio return $extensionVersion; } - private static function createPackageWithFpm(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void + private static function createPackageWithFpm(\staticphp\package $package, string $phpVersion, string $architecture, bool $isDebuginfo = false): void { if (self::$packageType === 'rpm') { - self::createRpmPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); + self::createRpmPackage($package, $phpVersion, $architecture, $isDebuginfo); } if (self::$packageType === 'deb') { - self::createDebPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); + self::createDebPackage($package, $phpVersion, $architecture, $isDebuginfo); } if (self::$packageType === 'apk') { - self::createApkPackage($package, $phpVersion, $architecture, $iteration, $isDebuginfo); + self::createApkPackage($package, $phpVersion, $architecture, $isDebuginfo); } } - private static function createRpmPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void + private static function createRpmPackage(\staticphp\package $package, string $phpVersion, string $architecture, bool $isDebuginfo = false): void { $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); @@ -307,12 +298,22 @@ private static function createRpmPackage(\staticphp\package $package, string $ph echo "Creating RPM package for {$name}...\n"; + // Calculate iteration for RPM (with possible override) + $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'rpm'); + $iteration = self::$iterationOverride ?? $computed; + + // Generate full package filename with PHP version suffix and distribution version + $phpSuffix = self::getPhpVersionSuffix(); + $distVersion = self::getDistVersion(); + $distSuffix = $distVersion !== '' ? ".{$distVersion}" : ''; + $packageFile = DIST_RPM_PATH . "/{$name}-{$phpVersion}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $fpmArgs = [...[ 'fpm', '-s', 'dir', '-t', 'rpm', '--rpm-compression', 'xz', - '-p', DIST_RPM_PATH, + '-p', $packageFile, // Full path with phpSuffix and distVersion in filename '--name', $name, '--version', $phpVersion, '--iteration', $iteration, @@ -436,13 +437,14 @@ private static function createRpmPackage(\staticphp\package $package, string $ph if (!$rpmProcess->isSuccessful()) { throw new \RuntimeException("RPM package creation failed: " . $rpmProcess->getErrorOutput()); } + + echo "RPM package created: {$packageFile}\n"; } private static function createDebPackage( \staticphp\package $package, string $phpVersion, string $architecture, - string $iteration, bool $isDebuginfo = false, ): void { @@ -452,18 +454,26 @@ private static function createDebPackage( echo "Creating DEB package for {$name}...\n"; + // Calculate iteration for DEB (with possible override) + $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'deb'); + $iteration = self::$iterationOverride ?? $computed; + //$osRelease = parse_ini_file('/etc/os-release'); //$distroCodename = $osRelease['VERSION_CODENAME'] ?? null; //$debIteration = $distroCodename !== '' ? "{$iteration}~{$distroCodename}" : $iteration; $debIteration = $iteration; $fullVersion = "{$phpVersion}-{$debIteration}"; + // Generate full package filename with PHP version suffix + $phpSuffix = self::getPhpVersionSuffix(); + $packageFile = DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + $fpmArgs = [...[ 'fpm', '-s', 'dir', '-t', 'deb', '--deb-compression', 'xz', - '-p', DIST_DEB_PATH, + '-p', $packageFile, // Full path with phpSuffix in filename '--name', $name, '--version', $phpVersion, '--architecture', $architecture, @@ -610,11 +620,14 @@ private static function createDebPackage( $debProcess->run(function ($type, $buffer) { echo $buffer; }); + if (!$debProcess->isSuccessful()) { + throw new \RuntimeException("DEB package creation failed: " . $debProcess->getErrorOutput()); + } - echo "DEB package created: " . DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}_{$architecture}.deb\n"; + echo "DEB package created: {$packageFile}\n"; } - private static function createApkPackage(\staticphp\package $package, string $phpVersion, string $architecture, string $iteration, bool $isDebuginfo = false): void + private static function createApkPackage(\staticphp\package $package, string $phpVersion, string $architecture, bool $isDebuginfo = false): void { $name = $isDebuginfo ? $package->getName() . '-debuginfo' : $package->getName(); $config = $isDebuginfo ? $package->getDebuginfoFpmConfig() : $package->getFpmConfig(); @@ -622,6 +635,10 @@ private static function createApkPackage(\staticphp\package $package, string $ph echo "Creating APK package for {$name} using nfpm...\n"; + // Calculate iteration for APK (with possible override) + $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'apk'); + $iteration = self::$iterationOverride ?? $computed; + // APK uses r{iteration} format for revision number $apkIteration = $iteration; $fullVersion = "{$phpVersion}-r{$apkIteration}"; @@ -768,8 +785,9 @@ private static function createApkWithNfpm(\staticphp\package $package, string $n echo "nfpm config written to: {$nfpmConfigFile}\n"; - // Run nfpm to create the package - $outputFile = DIST_APK_PATH . "/{$name}-{$phpVersion}-r{$iteration}.{$architecture}.apk"; + // Run nfpm to create the package with full filename including PHP version suffix + $phpSuffix = self::getPhpVersionSuffix(); + $outputFile = DIST_APK_PATH . "/{$name}-{$phpVersion}-r{$iteration}.{$phpSuffix}.{$architecture}.apk"; $nfpmProcess = new Process([ 'nfpm', 'package', '--config', $nfpmConfigFile, @@ -979,9 +997,9 @@ private static function fixBinaryDebugLink(string $sourceBinary, string $targetB $binaryName = basename($sourceBinary); $binarySuffix = getBinarySuffix(); $debugMap = [ - 'php' => BUILD_ROOT_PATH . '/debug/php' . $binarySuffix . '.debug', - 'php-cgi' => BUILD_ROOT_PATH . '/debug/php-cgi' . $binarySuffix . '.debug', - 'php-fpm' => BUILD_ROOT_PATH . '/debug/php-fpm' . $binarySuffix . '.debug', + 'php' => BUILD_ROOT_PATH . '/debug/php.debug', + 'php-cgi' => BUILD_ROOT_PATH . '/debug/php-cgi.debug', + 'php-fpm' => BUILD_ROOT_PATH . '/debug/php-fpm.debug', 'frankenphp' => BUILD_ROOT_PATH . '/debug/frankenphp.debug', ]; @@ -1027,37 +1045,56 @@ private static function fixBinaryDebugLink(string $sourceBinary, string $targetB return $tempBinary; } - private static function getNextIteration(string $name, string $phpVersion, string $architecture): int + private static function getNextIteration(string $name, string $phpVersion, string $architecture, string $packageType): int { $maxIteration = 0; - $rpmPattern = DIST_RPM_PATH . "/{$name}-{$phpVersion}-*.{$architecture}.rpm"; - $rpmFiles = glob($rpmPattern); - - foreach ($rpmFiles as $file) { - if (preg_match("/{$name}-{$phpVersion}-(\d+)\.{$architecture}\.rpm$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + if ($packageType === 'rpm') { + // RPM: {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm + // Also match old formats: + // - {name}-{version}-{iteration}.{arch}.rpm (no suffix) + // - {name}-{version}-{iteration}.{phpSuffix}.{arch}.rpm (no distVersion) + $rpmPattern = DIST_RPM_PATH . "/{$name}-{$phpVersion}-*.rpm"; + $rpmFiles = glob($rpmPattern); + + foreach ($rpmFiles as $file) { + // Match all formats: iteration followed by 0-2 parts, then arch.rpm + if (preg_match("/{$name}-" . preg_quote($phpVersion, '/') . "-(\d+)(?:\.[^.]+){0,2}\.{$architecture}\.rpm$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } - $debPattern = DIST_DEB_PATH . "/{$name}_{$phpVersion}-*_{$architecture}.deb"; - $debFiles = glob($debPattern); + if ($packageType === 'deb') { + // DEB: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb + // Also match old format without phpSuffix: {name}_{version}-{iteration}_{arch}.deb + $debPattern = DIST_DEB_PATH . "/{$name}_{$phpVersion}-*.deb"; + $debFiles = glob($debPattern); - foreach ($debFiles as $file) { - if (preg_match("/{$name}_{$phpVersion}-(\d+)_{$architecture}\.deb$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + foreach ($debFiles as $file) { + // Match both formats: + // - New: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb + // - Old: {name}_{version}-{iteration}_{arch}.deb (without phpSuffix) + if (preg_match("/{$name}_" . preg_quote($phpVersion, '/') . "-(\d+)(?:\.[^_]+)?_{$architecture}\.deb$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } - $apkPattern = DIST_APK_PATH . "/{$name}-{$phpVersion}-r*.{$architecture}.apk"; - $apkFiles = glob($apkPattern); + if ($packageType === 'apk') { + // APK: {name}-{version}-r{iteration}.{phpSuffix}.{arch}.apk + // Also match old format: {name}-{version}-r{iteration}.{arch}.apk (no phpSuffix) + $apkPattern = DIST_APK_PATH . "/{$name}-{$phpVersion}-r*.apk"; + $apkFiles = glob($apkPattern); - foreach ($apkFiles as $file) { - if (preg_match("/{$name}-{$phpVersion}-r(\d+)\.{$architecture}\.apk$/", $file, $matches)) { - $iteration = (int)$matches[1]; - $maxIteration = max($maxIteration, $iteration); + foreach ($apkFiles as $file) { + // Match both formats: r{iteration} followed by 0-1 parts, then arch.apk + if (preg_match("/{$name}-" . preg_quote($phpVersion, '/') . "-r(\d+)(?:\.[^.]+)?\.{$architecture}\.apk$/", $file, $matches)) { + $iteration = (int)$matches[1]; + $maxIteration = max($maxIteration, $iteration); + } } } @@ -1071,9 +1108,59 @@ public static function getPrefix(): string return 'php' . self::$prefix; } - public static function getDebuginfo(): bool + /** + * Get PHP version suffix for package filenames (e.g., "static-83" for PHP 8.3) + */ + private static function getPhpVersionSuffix(): string + { + [$phpVersion,] = self::getPhpVersionAndArchitecture(); + + // Extract major.minor version (e.g., "8.3.29" -> "8.3") + if (preg_match('/^(\d+)\.(\d+)/', $phpVersion, $matches)) { + $majorMinorNoDot = $matches[1] . $matches[2]; // e.g., "83" + } else { + $majorMinorNoDot = str_replace('.', '', $phpVersion); + } + + // Construct suffix: static-{version} (e.g., "static-83") + return 'static-' . $majorMinorNoDot; + } + + /** + * Get distribution version for RPM filenames (e.g., "el9", "el8", "fc39") + */ + private static function getDistVersion(): string { - return self::$debuginfo; + if (!file_exists('/etc/os-release')) { + return ''; + } + + $osRelease = parse_ini_file('/etc/os-release'); + if (!$osRelease || !isset($osRelease['ID'], $osRelease['VERSION_ID'])) { + return ''; + } + + $id = $osRelease['ID']; + $versionId = $osRelease['VERSION_ID']; + + // Extract major version number + if (preg_match('/^(\d+)/', $versionId, $matches)) { + $majorVersion = $matches[1]; + } else { + return ''; + } + + // Map distribution ID to prefix + $distMap = [ + 'rhel' => 'el', + 'centos' => 'el', + 'rocky' => 'el', + 'almalinux' => 'el', + 'fedora' => 'fc', + ]; + + $prefix = $distMap[$id] ?? ''; + return $prefix !== '' ? $prefix . $majorVersion : ''; } /** From f6f8ed026c1443a36030a261039debfcc68686f5 Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 14:27:15 +0100 Subject: [PATCH 22/37] bring back debuginfo creation on rpm --- src/package/cgi.php | 2 +- src/package/cli.php | 2 +- src/package/embed.php | 4 +++- src/package/fpm.php | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/package/cgi.php b/src/package/cgi.php index dd578f8..7543162 100644 --- a/src/package/cgi.php +++ b/src/package/cgi.php @@ -36,7 +36,7 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { $binarySuffix = getBinarySuffix(); - $src = BUILD_ROOT_PATH . '/debug/php-cgi' . $binarySuffix . '.debug'; + $src = BUILD_ROOT_PATH . '/debug/php-cgi.debug'; if (!file_exists($src)) { return []; } diff --git a/src/package/cli.php b/src/package/cli.php index 0811378..0b2a19e 100644 --- a/src/package/cli.php +++ b/src/package/cli.php @@ -121,7 +121,7 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { $binarySuffix = getBinarySuffix(); - $src = BUILD_ROOT_PATH . '/debug/php' . $binarySuffix . '.debug'; + $src = BUILD_ROOT_PATH . '/debug/php.debug'; if (!file_exists($src)) { return []; } diff --git a/src/package/embed.php b/src/package/embed.php index 9191631..e944ece 100644 --- a/src/package/embed.php +++ b/src/package/embed.php @@ -57,7 +57,9 @@ public function getDebuginfoFpmConfig(): array $libName = $releasePrefix !== '' ? 'libphp-' . $releasePrefix . '-' . $phpVersionDigits . '.so' : 'libphp-' . $phpVersionDigits . '.so'; - $src = BUILD_ROOT_PATH . '/debug/' . $libName . '.debug'; + + // Debug file is just libphp.so.debug (without version/prefix) + $src = BUILD_ROOT_PATH . '/debug/libphp.so.debug'; if (!file_exists($src)) { return []; } diff --git a/src/package/fpm.php b/src/package/fpm.php index 16b7471..4453e3d 100644 --- a/src/package/fpm.php +++ b/src/package/fpm.php @@ -89,7 +89,7 @@ public function getFpmExtraArgs(): array public function getDebuginfoFpmConfig(): array { $binarySuffix = getBinarySuffix(); - $src = BUILD_ROOT_PATH . '/debug/php-fpm' . $binarySuffix . '.debug'; + $src = BUILD_ROOT_PATH . '/debug/php-fpm.debug'; if (!file_exists($src)) { return []; } From 418194a940eb687f6f595b4203bd647538cebc7f Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 22:05:32 +0100 Subject: [PATCH 23/37] add forgejo-helper, debian workflow on versioned branch --- .github/workflows/build-deb-forgejo.yml | 116 +++++++++++++++++ .github/workflows/build-gcc-deb-packages.yml | 8 +- .github/workflows/spc-download.yml | 25 +++- bin/forgejo-helper | 127 +++++++++++++++++++ src/package/frankenphp.php | 37 +++--- src/step/CreatePackages.php | 27 ++-- 6 files changed, 301 insertions(+), 39 deletions(-) create mode 100644 .github/workflows/build-deb-forgejo.yml create mode 100755 bin/forgejo-helper diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml new file mode 100644 index 0000000..db7d932 --- /dev/null +++ b/.github/workflows/build-deb-forgejo.yml @@ -0,0 +1,116 @@ +name: Build and upload Debian packages to Forgejo + +on: + workflow_dispatch: + inputs: + debug_tmate: + description: "Open tmate session on failure" + type: boolean + required: false + default: false + repository_dispatch: + types: [spc-download] + +permissions: + contents: read + +jobs: + build: + name: Build Debian packages for PHP ${{ matrix.php_version }} on ${{ matrix.arch }} + runs-on: ubuntu-24.04 + container: + image: debian:11 + permissions: + contents: read + defaults: + run: + shell: bash + strategy: + matrix: + php_version: ['8.2', '8.3', '8.4', '8.5'] + arch: ['x86_64', 'aarch64'] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BASH_ENV: /tmp/gha-bashenv + PHP_VERSION: ${{ matrix.php_version }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: versioned + persist-credentials: false + + - name: Set PHP version short + run: | + # Convert "8.5" to "85" + PHP_VERSION_SHORT=$(echo "$PHP_VERSION" | tr -d '.') + echo "PHP_VERSION_SHORT=$PHP_VERSION_SHORT" >> $GITHUB_ENV + + - name: Bootstrap container + run: | + apt-get update + apt-get install -y ruby build-essential jq curl gzip sudo git gnupg tar zstd python3 python3-requests + apt-get upgrade -y + gem install --no-document fpm + + - name: Install cmake + run: | + curl -o cmake.tar.gz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-${{ matrix.arch }}.tar.gz && \ + sudo tar -xzf cmake.tar.gz -C /usr/local --strip-components=1 && \ + rm cmake.tar.gz + + - name: Install composer + run: | + sudo curl -L https://files.henderkes.com/${{ matrix.arch }}-linux/php -o /usr/local/bin/php + sudo chmod +x /usr/local/bin/php + sudo curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet + sudo mv composer.phar /usr/local/bin/composer + + - name: Prepare cache directories + run: | + composer config -g cache-dir + + - name: Cache Composer downloads + uses: actions/cache@v4 + with: + path: ~/.cache/composer + key: composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + composer- + + - name: Install vendor + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Download artifact from spc-download.yml + uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 + with: + workflow: spc-download.yml + name: downloads-tarball + branch: master + + - name: Extract with permissions + run: | + mkdir -p vendor/crazywhalecc/static-php-cli/downloads + tar -xzf downloads.tar.gz -C vendor/crazywhalecc/static-php-cli/downloads + rm downloads.tar.gz + + - name: Build PHP packages + run: bin/spp all --target=${{ matrix.arch }}-native-gnu --phpv=${{ matrix.php_version }} --prefix="-zts" --type=deb + + - name: Upload to Forgejo + working-directory: dist/deb + run: | + ../../bin/forgejo-helper upload deb "${{ env.PHP_VERSION_SHORT }}" "${{ secrets.FORGEJO_PASSWORD }}" "*.deb" + + - name: Upload logs on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: build-logs-deb-${{ matrix.arch }}-php${{ matrix.php_version }} + path: vendor/crazywhalecc/static-php-cli/log + + - name: Setup tmate session + if: ${{ failure() && inputs.debug_tmate == true }} + uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 + timeout-minutes: 10 diff --git a/.github/workflows/build-gcc-deb-packages.yml b/.github/workflows/build-gcc-deb-packages.yml index 57e4eb6..7118806 100644 --- a/.github/workflows/build-gcc-deb-packages.yml +++ b/.github/workflows/build-gcc-deb-packages.yml @@ -94,13 +94,7 @@ jobs: rm downloads.tar.gz - name: Build PHP - run: php bin/spp build --target=native-native-gnu --phpv=${{ matrix.php-version }} - - - name: Remove spc build dir to clear up space for gh runners - run: sudo rm -rf vendor/crazywhalecc/static-php-cli/source vendor/crazywhalecc/static-php-cli/buildroot - - - name: Build deb packages - run: php bin/spp package --target=native-native-gnu --type=deb --phpv=${{ matrix.php-version }} + run: php bin/spp all --target=native-native-gnu --phpv=${{ matrix.php-version }} --prefix="-zts" --type=deb - name: Stage deb artifacts run: | diff --git a/.github/workflows/spc-download.yml b/.github/workflows/spc-download.yml index 77a594e..9b571ea 100644 --- a/.github/workflows/spc-download.yml +++ b/.github/workflows/spc-download.yml @@ -18,11 +18,20 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - - name: Set up PHP - uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2 - with: - php-version: '8.4' - tools: composer:v2 + - name: Set architecture variables + run: | + if [[ "${{ matrix.arch }}" == "arm64" ]]; then + echo "RPM_ARCH=aarch64" >> $GITHUB_ENV + else + echo "RPM_ARCH=x86_64" >> $GITHUB_ENV + fi + + - name: Install composer + run: | + sudo curl -L https://files.henderkes.com/${RPM_ARCH}-linux/php -o /usr/local/bin/php + sudo chmod +x /usr/local/bin/php + sudo curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet + sudo mv composer.phar /usr/local/bin/composer - name: Checkout code uses: actions/checkout@v4 @@ -58,3 +67,9 @@ jobs: run: gh workflow run build-gcc-deb-packages.yml env: GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user + + - name: Trigger build-deb-forgejo workflow on versioned branch + if: success() + run: gh workflow run build-deb-forgejo.yml --ref versioned + env: + GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user diff --git a/bin/forgejo-helper b/bin/forgejo-helper new file mode 100755 index 0000000..c3fd059 --- /dev/null +++ b/bin/forgejo-helper @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +import sys +import requests +import re +from pathlib import Path +from fnmatch import fnmatch + +def get_remote_packages(owner, password, pkg_type): + url = f"https://git.henderkes.com/api/v1/packages/{owner}" + params = {"type": pkg_type, "limit": 100} + response = requests.get(url, params=params, auth=(owner, password)) + response.raise_for_status() + return response.json() + +def list_packages(owner, password, pkg_type, pattern=None): + packages = get_remote_packages(owner, password, pkg_type) + for pkg in packages: + full_name = f"{pkg['name']}-{pkg['version']}" + if pattern and not fnmatch(full_name, pattern): + continue + print(full_name) + +def delete_packages(owner, password, pkg_type, pattern): + packages = get_remote_packages(owner, password, pkg_type) + for pkg in packages: + full_name = f"{pkg['name']}-{pkg['version']}" + if fnmatch(full_name, pattern): + print(f"Deleting: {pkg['name']} {pkg['version']}") + if pkg_type == 'debian': + for arch in ['amd64', 'arm64']: + url = f"https://git.henderkes.com/api/packages/{owner}/debian/pool/php-zts/main/{pkg['name']}/{pkg['version']}/{arch}" + resp = requests.delete(url, auth=(owner, password)) + print(f"Delete {arch} response: {resp.status_code}") + if resp.status_code not in [200, 204, 404]: + print(f"Error: {resp.text}") + else: + url = f"https://git.henderkes.com/api/v1/packages/{owner}/{pkg_type}/{pkg['name']}/{pkg['version']}" + resp = requests.delete(url, auth=(owner, password)) + print(f"Delete response: {resp.status_code}") + if resp.status_code not in [200, 204]: + print(f"Error: {resp.text}") + +def upload_packages(owner, password, pkg_type, pattern, repo="php-zts", branch="main"): + for pkg_file in Path(".").glob(pattern): + if not pkg_file.is_file(): + continue + + if pkg_type == 'rpm': + match = re.match(r'(.+?)-(\d+[^-]+)-(\d+)\.[^.]+\.(el\d+)\.', pkg_file.name) + if not match: + continue + name, ver, rel, el_version = match.groups() + version = f"{ver}-{rel}" + elif pkg_type == 'debian': + match = re.match(r'(.+?)_(\d+[^_]+)_(amd64|arm64)\.deb', pkg_file.name) + if not match: + continue + name, version, arch = match.groups() + else: + match = re.match(r'(.+?)-(\d+\..+)\.(x86_64|aarch64)\.(apk|pkg\.tar\.zst)', pkg_file.name) + if not match: + continue + name, version, arch, ext = match.groups() + + print(f"Uploading: {pkg_file.name}") + if pkg_type == 'rpm': + url = f"https://git.henderkes.com/api/packages/{owner}/rpm/{el_version}/upload?sign=true" + elif pkg_type == 'debian': + url = f"https://git.henderkes.com/api/packages/{owner}/debian/pool/php-zts/main/upload" + else: + url = f"https://git.henderkes.com/api/packages/{owner}/{pkg_type}/{branch}/{repo}" + with open(pkg_file, 'rb') as f: + resp = requests.put(url, files={'file': f}, auth=(owner, password)) + if resp.status_code == 201: + print(f"Upload successful: {resp.status_code}") + elif resp.status_code == 409: + print(f"Skip: {pkg_file.name} (already exists)") + continue + else: + print(f"Upload response: {resp.status_code}") + print(f"Error: {resp.text}") + continue + + link_name = name.replace('_', '-') if pkg_type == 'debian' else name + print(f"Linking: {link_name} to {repo}") + url = f"https://git.henderkes.com/api/v1/packages/{owner}/{pkg_type}/{link_name}/-/link/{repo}" + resp = requests.post(url, auth=(owner, password)) + print(f"Link response: {resp.status_code}") + if resp.status_code != 201: + print(f"Error: {resp.text}") + +if __name__ == "__main__": + if len(sys.argv) < 5: + print("Usage: script.py [pattern]") + print("Examples:") + print(" script.py upload alpine 85 pass '*.apk'") + print(" script.py upload rpm 85 pass '*.rpm'") + print(" script.py upload debian 85 pass '*.deb'") + print(" script.py list alpine 85 pass") + print(" script.py list alpine 85 pass '*8.3.29*'") + print(" script.py delete alpine 85 pass '*cli*'") + sys.exit(1) + + action = sys.argv[1] + pkg_type = sys.argv[2] + owner = sys.argv[3] + password = sys.argv[4] + + if pkg_type not in ['alpine', 'arch', 'debian', 'rpm']: + print(f"Error: Invalid package type. Must be alpine, arch, debian, or rpm") + sys.exit(1) + + if action == "upload": + pattern = sys.argv[5] if len(sys.argv) > 5 else "*.apk" + upload_packages(owner, password, pkg_type, pattern) + elif action == "list": + pattern = sys.argv[5] if len(sys.argv) > 5 else None + list_packages(owner, password, pkg_type, pattern) + elif action == "delete": + if len(sys.argv) < 6: + print("Error: delete requires a pattern") + sys.exit(1) + delete_packages(owner, password, pkg_type, sys.argv[5]) + else: + print(f"Unknown action: {action}") + sys.exit(1) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index 2f54271..5e4bec3 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -226,25 +226,32 @@ public function createDebPackage(string $architecture, array $binaryDependencies $name = $this->getName(); + // Convert system architecture to Debian architecture naming + $debArch = match($architecture) { + 'x86_64' => 'amd64', + 'aarch64' => 'arm64', + default => $architecture, + }; + // Calculate iteration for DEB (with possible override) - $computed = (string)$this->getNextIteration($name, $version, $architecture, 'deb'); + $computed = (string)$this->getNextIteration($name, $version, $debArch, 'deb'); $iteration = $iterationOverride ?? $computed; $debIteration = $iteration; $versionedConflicts = $this->getVersionedConflicts(); - // Generate full package filename with PHP version suffix - $phpSuffix = $this->getPhpVersionSuffix(); - $packageFile = DIST_DEB_PATH . "/{$name}_{$version}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + // Debian filename format: {name}_{version}-{revision}_{arch}.deb + $packageFile = DIST_DEB_PATH . "/{$name}_{$version}-{$debIteration}_{$debArch}.deb"; $fpmArgs = [ 'fpm', '-s', 'dir', '-t', 'deb', '--deb-compression', 'xz', - '-p', $packageFile, // Full path with phpSuffix in filename + '-p', $packageFile, '-n', $name, '-v', $version, + '--architecture', $debArch, '--license', $this->getLicense(), '--config-files', '/etc/frankenphp/Caddyfile', '--provides', 'frankenphp', @@ -325,17 +332,18 @@ public function createDebPackage(string $architecture, array $binaryDependencies if ($debuginfo) { $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { - $dbgPackageFile = DIST_DEB_PATH . "/{$name}-debuginfo_{$version}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + $dbgDebName = "{$name}-debuginfo"; + $dbgPackageFile = DIST_DEB_PATH . "/{$dbgDebName}_{$version}-{$debIteration}_{$debArch}.deb"; $dbgArgs = [ 'fpm', '-s', 'dir', '-t', 'deb', '--deb-compression', 'xz', '-p', $dbgPackageFile, - '-n', $name . '-debuginfo', + '-n', $dbgDebName, '-v', $version, '--iteration', $debIteration, - '--architecture', $architecture, + '--architecture', $debArch, '--license', $this->getLicense(), '--depends', sprintf('%s (= %s-%s)', $name, $version, $debIteration), $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', @@ -688,16 +696,15 @@ private function getNextIteration(string $name, string $version, string $archite } if ($packageType === 'deb') { - // DEB: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb - // Also match old format without phpSuffix: {name}_{version}-{iteration}_{arch}.deb - $debPattern = DIST_DEB_PATH . "/{$name}_{$version}-*.deb"; + // DEB: {name}-{phpSuffix}_{version}-{iteration}_{arch}.deb + // Also match old formats for backwards compatibility + $debPattern = DIST_DEB_PATH . "/{$name}*.deb"; $debFiles = glob($debPattern); foreach ($debFiles as $file) { - // Match both formats: - // - New: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb - // - Old: {name}_{version}-{iteration}_{arch}.deb (without phpSuffix) - if (preg_match("/{$name}_" . preg_quote($version, '/') . "-(\d+)(?:\.[^_]+)?_{$architecture}\.deb$/", $file, $matches)) { + // Match new format: {name}-{phpSuffix}_{version}-{iteration}_{arch}.deb + // The name might have the phpSuffix included or not + if (preg_match("/" . preg_quote($name, '/') . "(?:-[^_]+)?_" . preg_quote($version, '/') . "-(\d+)_{$architecture}\.deb$/", $file, $matches)) { $iteration = (int)$matches[1]; $maxIteration = max($maxIteration, $iteration); } diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 97ffebf..35ba679 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -454,8 +454,15 @@ private static function createDebPackage( echo "Creating DEB package for {$name}...\n"; + // Convert system architecture to Debian architecture naming + $debArch = match($architecture) { + 'x86_64' => 'amd64', + 'aarch64' => 'arm64', + default => $architecture, + }; + // Calculate iteration for DEB (with possible override) - $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'deb'); + $computed = (string)self::getNextIteration($name, $phpVersion, $debArch, 'deb'); $iteration = self::$iterationOverride ?? $computed; //$osRelease = parse_ini_file('/etc/os-release'); @@ -464,19 +471,18 @@ private static function createDebPackage( $debIteration = $iteration; $fullVersion = "{$phpVersion}-{$debIteration}"; - // Generate full package filename with PHP version suffix - $phpSuffix = self::getPhpVersionSuffix(); - $packageFile = DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}.{$phpSuffix}_{$architecture}.deb"; + // Debian filename format: {name}_{version}-{revision}_{arch}.deb + $packageFile = DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}_{$debArch}.deb"; $fpmArgs = [...[ 'fpm', '-s', 'dir', '-t', 'deb', '--deb-compression', 'xz', - '-p', $packageFile, // Full path with phpSuffix in filename + '-p', $packageFile, '--name', $name, '--version', $phpVersion, - '--architecture', $architecture, + '--architecture', $debArch, '--iteration', $debIteration, // Debian revision (includes distro) '--description', "Static PHP Package for {$name}", '--license', $package->getLicense(), @@ -1067,16 +1073,13 @@ private static function getNextIteration(string $name, string $phpVersion, strin } if ($packageType === 'deb') { - // DEB: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb - // Also match old format without phpSuffix: {name}_{version}-{iteration}_{arch}.deb + // DEB: {name}_{version}-{iteration}_{arch}.deb $debPattern = DIST_DEB_PATH . "/{$name}_{$phpVersion}-*.deb"; $debFiles = glob($debPattern); foreach ($debFiles as $file) { - // Match both formats: - // - New: {name}_{version}-{iteration}.{phpSuffix}_{arch}.deb - // - Old: {name}_{version}-{iteration}_{arch}.deb (without phpSuffix) - if (preg_match("/{$name}_" . preg_quote($phpVersion, '/') . "-(\d+)(?:\.[^_]+)?_{$architecture}\.deb$/", $file, $matches)) { + // Match: {name}_{version}-{iteration}_{arch}.deb + if (preg_match("/" . preg_quote($name, '/') . "_" . preg_quote($phpVersion, '/') . "-(\d+)_{$architecture}\.deb$/", $file, $matches)) { $iteration = (int)$matches[1]; $maxIteration = max($maxIteration, $iteration); } From c2596a67dd0b67dc42b7138044e7d96912dd4dd0 Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 22:27:23 +0100 Subject: [PATCH 24/37] fix workflow! --- .github/workflows/build-deb-forgejo.yml | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml index 106d97e..ace675f 100644 --- a/.github/workflows/build-deb-forgejo.yml +++ b/.github/workflows/build-deb-forgejo.yml @@ -16,8 +16,8 @@ permissions: jobs: build: - name: Build Debian packages for PHP ${{ matrix.php_version }} on ${{ matrix.arch }} - runs-on: ${{ matrix.arch == 'aarch64' && 'ubuntu-24.04-arm64' || 'ubuntu-24.04' }} + name: Build for ${{ matrix.arch }} PHP ${{ matrix.php-version }} + runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }} container: image: debian:11 permissions: @@ -26,13 +26,13 @@ jobs: run: shell: bash strategy: + fail-fast: false matrix: - php_version: ['8.2', '8.3', '8.4', '8.5'] - arch: ['x86_64', 'aarch64'] + php-version: ['8.2', '8.3', '8.4', '8.5'] + arch: ['amd64', 'arm64'] env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BASH_ENV: /tmp/gha-bashenv - PHP_VERSION: ${{ matrix.php_version }} steps: - name: Checkout code @@ -41,10 +41,18 @@ jobs: ref: versioned persist-credentials: false + - name: Set architecture variables + run: | + if [[ "${{ matrix.arch }}" == "arm64" ]]; then + echo "RPM_ARCH=aarch64" >> $GITHUB_ENV + else + echo "RPM_ARCH=x86_64" >> $GITHUB_ENV + fi + - name: Set PHP version short run: | # Convert "8.5" to "85" - PHP_VERSION_SHORT=$(echo "$PHP_VERSION" | tr -d '.') + PHP_VERSION_SHORT=$(echo "${{ matrix.php-version }}" | tr -d '.') echo "PHP_VERSION_SHORT=$PHP_VERSION_SHORT" >> $GITHUB_ENV - name: Bootstrap container @@ -56,13 +64,13 @@ jobs: - name: Install cmake run: | - curl -o cmake.tar.gz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-${{ matrix.arch }}.tar.gz && \ + curl -o cmake.tar.gz -fsSL https://github.com/Kitware/CMake/releases/download/v3.31.4/cmake-3.31.4-linux-$(uname -m).tar.gz && \ sudo tar -xzf cmake.tar.gz -C /usr/local --strip-components=1 && \ rm cmake.tar.gz - name: Install composer run: | - sudo curl -L https://files.henderkes.com/${{ matrix.arch }}-linux/php -o /usr/local/bin/php + sudo curl -L https://files.henderkes.com/${RPM_ARCH}-linux/php -o /usr/local/bin/php sudo chmod +x /usr/local/bin/php sudo curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet sudo mv composer.phar /usr/local/bin/composer @@ -96,7 +104,7 @@ jobs: rm downloads.tar.gz - name: Build PHP packages - run: bin/spp all --target=${{ matrix.arch }}-native-gnu --phpv=${{ matrix.php_version }} --prefix="-zts" --type=deb + run: bin/spp all --target=native-native-gnu --phpv=${{ matrix.php-version }} --prefix="-zts" --type=deb - name: Upload to Forgejo working-directory: dist/deb @@ -107,7 +115,7 @@ jobs: if: ${{ failure() }} uses: actions/upload-artifact@v4 with: - name: build-logs-deb-${{ matrix.arch }}-php${{ matrix.php_version }} + name: build-logs-${{ matrix.arch }}-php${{ matrix.php-version }} path: vendor/crazywhalecc/static-php-cli/log - name: Setup tmate session From b6d0856c4ec30584626b96f6808b689df730e549 Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 23:17:45 +0100 Subject: [PATCH 25/37] fuck --- .github/workflows/build-deb-forgejo.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml index ace675f..1ad61ed 100644 --- a/.github/workflows/build-deb-forgejo.yml +++ b/.github/workflows/build-deb-forgejo.yml @@ -109,7 +109,7 @@ jobs: - name: Upload to Forgejo working-directory: dist/deb run: | - ../../bin/forgejo-helper upload deb "${{ env.PHP_VERSION_SHORT }}" "${{ secrets.FORGEJO_PASSWORD }}" "*.deb" + ../../bin/forgejo-helper upload debian "${{ env.PHP_VERSION_SHORT }}" "${{ secrets.FORGEJO_PASSWORD }}" "*.deb" - name: Upload logs on failure if: ${{ failure() }} From 370473f758eec178b63094458c3a1967d94af0a0 Mon Sep 17 00:00:00 2001 From: henderkes Date: Mon, 22 Dec 2025 23:22:17 +0100 Subject: [PATCH 26/37] why is tmate not working? --- .github/workflows/build-deb-forgejo.yml | 16 +++++++++++++++- .github/workflows/build-gcc-deb-packages.yml | 1 - 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml index 1ad61ed..c956f8c 100644 --- a/.github/workflows/build-deb-forgejo.yml +++ b/.github/workflows/build-deb-forgejo.yml @@ -118,7 +118,21 @@ jobs: name: build-logs-${{ matrix.arch }}-php${{ matrix.php-version }} path: vendor/crazywhalecc/static-php-cli/log + - name: Install tmate + if: ${{ failure() && inputs.debug_tmate == true }} + run: | + case "${RPM_ARCH}" in + x86_64) arch="amd64" ;; + arm64) arch="arm64v8" ;; + esac + dir="tmate-2.4.0-static-linux-$arch" + curl -L "https://github.com/tmate-io/tmate/releases/download/2.4.0/$dir.tar.xz" | tar -xJ -O "$dir/tmate" > /usr/bin/tmate + chmod +x /usr/bin/tmate + - name: Setup tmate session if: ${{ failure() && inputs.debug_tmate == true }} uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 - timeout-minutes: 10 + with: + install-dependencies: false + sudo: false + timeout-minutes: 30 diff --git a/.github/workflows/build-gcc-deb-packages.yml b/.github/workflows/build-gcc-deb-packages.yml index 7118806..b819628 100644 --- a/.github/workflows/build-gcc-deb-packages.yml +++ b/.github/workflows/build-gcc-deb-packages.yml @@ -220,4 +220,3 @@ jobs: # if: ${{ failure() && github.event_name == 'workflow_dispatch' }} # uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 # timeout-minutes: 10 -# token: From 512a1830c18e80e69ed4ca0566fa54eee5817c58 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 00:02:12 +0100 Subject: [PATCH 27/37] fix tmate --- .github/workflows/build-deb-forgejo.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml index c956f8c..f6dcc7b 100644 --- a/.github/workflows/build-deb-forgejo.yml +++ b/.github/workflows/build-deb-forgejo.yml @@ -122,8 +122,8 @@ jobs: if: ${{ failure() && inputs.debug_tmate == true }} run: | case "${RPM_ARCH}" in - x86_64) arch="amd64" ;; - arm64) arch="arm64v8" ;; + x86_64) arch="amd64" ;; + aarch64) arch="arm64v8" ;; esac dir="tmate-2.4.0-static-linux-$arch" curl -L "https://github.com/tmate-io/tmate/releases/download/2.4.0/$dir.tar.xz" | tar -xJ -O "$dir/tmate" > /usr/bin/tmate @@ -134,5 +134,5 @@ jobs: uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 with: install-dependencies: false - sudo: false + sudo: true timeout-minutes: 30 From 588cb0ee9f75e6d9ff95423ed9906e3b07e3650e Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 00:07:44 +0100 Subject: [PATCH 28/37] params for the job to run selectively --- .github/workflows/build-deb-forgejo.yml | 65 +++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-deb-forgejo.yml b/.github/workflows/build-deb-forgejo.yml index f6dcc7b..28e6616 100644 --- a/.github/workflows/build-deb-forgejo.yml +++ b/.github/workflows/build-deb-forgejo.yml @@ -3,6 +3,18 @@ name: Build and upload Debian packages to Forgejo on: workflow_dispatch: inputs: + iteration: + description: "Optional: override package iteration (integer). Leave empty for auto" + required: false + default: "" + php_versions: + description: "Optional: PHP versions (comma-separated, e.g., 8.2,8.5). Leave empty for all" + required: false + default: "" + architectures: + description: "Optional: Architectures (comma-separated, e.g., amd64,arm64). Leave empty for all" + required: false + default: "" debug_tmate: description: "Open tmate session on failure" type: boolean @@ -15,7 +27,46 @@ permissions: contents: read jobs: + setup-matrix: + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Set up matrix + id: set-matrix + run: | + # Default values + default_php='["8.2","8.3","8.4","8.5"]' + default_arch='["amd64","arm64"]' + + # Parse inputs or use defaults + if [[ -n "${INPUTS_PHP_VERSIONS}" ]]; then + php_versions=$(echo "${INPUTS_PHP_VERSIONS}" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') + else + php_versions=$default_php + fi + + if [[ -n "${INPUTS_ARCHITECTURES}" ]]; then + arch_versions=$(echo "${INPUTS_ARCHITECTURES}" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') + else + arch_versions=$default_arch + fi + + # Create matrix JSON (compact, single line) + matrix=$(jq -nc \ + --argjson php "$php_versions" \ + --argjson arch "$arch_versions" \ + '{"php-version":$php,"arch":$arch}') + + echo "matrix=$matrix" >> $GITHUB_OUTPUT + env: + INPUTS_PHP_VERSIONS: ${{ inputs.php_versions }} + INPUTS_ARCHITECTURES: ${{ inputs.architectures }} + build: + needs: setup-matrix name: Build for ${{ matrix.arch }} PHP ${{ matrix.php-version }} runs-on: ubuntu-24.04${{ matrix.arch == 'arm64' && '-arm' || '' }} container: @@ -27,12 +78,11 @@ jobs: shell: bash strategy: fail-fast: false - matrix: - php-version: ['8.2', '8.3', '8.4', '8.5'] - arch: ['amd64', 'arm64'] + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BASH_ENV: /tmp/gha-bashenv + ITERATION: ${{ inputs.iteration || '' }} steps: - name: Checkout code @@ -104,7 +154,12 @@ jobs: rm downloads.tar.gz - name: Build PHP packages - run: bin/spp all --target=native-native-gnu --phpv=${{ matrix.php-version }} --prefix="-zts" --type=deb + run: | + if [[ -n "${ITERATION}" ]]; then + bin/spp all --target=native-native-gnu --phpv=${{ matrix.php-version }} --prefix="-zts" --type=deb --iteration="${ITERATION}" + else + bin/spp all --target=native-native-gnu --phpv=${{ matrix.php-version }} --prefix="-zts" --type=deb + fi - name: Upload to Forgejo working-directory: dist/deb @@ -134,5 +189,5 @@ jobs: uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 with: install-dependencies: false - sudo: true + sudo: false timeout-minutes: 30 From 71f9e8c180304cf1f3d3eb386d4dc90bc4b24895 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:00:38 +0100 Subject: [PATCH 29/37] add php version to debian frankenphp package --- src/package/frankenphp.php | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index 5e4bec3..fc76f09 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -233,15 +233,25 @@ public function createDebPackage(string $architecture, array $binaryDependencies default => $architecture, }; + // For DEB packages, append PHP version to package version for proper sorting + // e.g., 1.11.0+php85 is higher than 1.11.0+php83 + $phpMajorMinor = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpMajorMinor, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $phpMajorMinor); + } + $debVersion = $version . '+php' . $phpVersionSuffix; + // Calculate iteration for DEB (with possible override) - $computed = (string)$this->getNextIteration($name, $version, $debArch, 'deb'); + $computed = (string)$this->getNextIteration($name, $debVersion, $debArch, 'deb'); $iteration = $iterationOverride ?? $computed; $debIteration = $iteration; $versionedConflicts = $this->getVersionedConflicts(); // Debian filename format: {name}_{version}-{revision}_{arch}.deb - $packageFile = DIST_DEB_PATH . "/{$name}_{$version}-{$debIteration}_{$debArch}.deb"; + $packageFile = DIST_DEB_PATH . "/{$name}_{$debVersion}-{$debIteration}_{$debArch}.deb"; $fpmArgs = [ 'fpm', @@ -250,7 +260,7 @@ public function createDebPackage(string $architecture, array $binaryDependencies '--deb-compression', 'xz', '-p', $packageFile, '-n', $name, - '-v', $version, + '-v', $debVersion, '--architecture', $debArch, '--license', $this->getLicense(), '--config-files', '/etc/frankenphp/Caddyfile', @@ -333,7 +343,7 @@ public function createDebPackage(string $architecture, array $binaryDependencies $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { $dbgDebName = "{$name}-debuginfo"; - $dbgPackageFile = DIST_DEB_PATH . "/{$dbgDebName}_{$version}-{$debIteration}_{$debArch}.deb"; + $dbgPackageFile = DIST_DEB_PATH . "/{$dbgDebName}_{$debVersion}-{$debIteration}_{$debArch}.deb"; $dbgArgs = [ 'fpm', '-s', 'dir', @@ -341,11 +351,11 @@ public function createDebPackage(string $architecture, array $binaryDependencies '--deb-compression', 'xz', '-p', $dbgPackageFile, '-n', $dbgDebName, - '-v', $version, + '-v', $debVersion, '--iteration', $debIteration, '--architecture', $debArch, '--license', $this->getLicense(), - '--depends', sprintf('%s (= %s-%s)', $name, $version, $debIteration), + '--depends', sprintf('%s (= %s-%s)', $name, $debVersion, $debIteration), $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', ]; $dbgProcess = new Process($dbgArgs); From af6b41656f3090ef526cb5678cc300d4b06c8f8f Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:09:44 +0100 Subject: [PATCH 30/37] add apk workflow --- .github/workflows/build-apk-forgejo.yml | 173 ++++++++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 .github/workflows/build-apk-forgejo.yml diff --git a/.github/workflows/build-apk-forgejo.yml b/.github/workflows/build-apk-forgejo.yml new file mode 100644 index 0000000..afd4354 --- /dev/null +++ b/.github/workflows/build-apk-forgejo.yml @@ -0,0 +1,173 @@ +name: Build and upload Alpine APK packages to Forgejo + +on: + workflow_dispatch: + inputs: + iteration: + description: "Optional: override package iteration (integer). Leave empty for auto" + required: false + default: "" + php_versions: + description: "Optional: PHP versions (comma-separated, e.g., 8.2,8.5). Leave empty for all" + required: false + default: "" + architectures: + description: "Optional: Architectures (comma-separated, e.g., x86_64,aarch64). Leave empty for all" + required: false + default: "" + packages: + description: "Optional: override packages list. Leave empty for default" + required: false + default: "" + debug_tmate: + description: "Open tmate session on failure" + type: boolean + required: false + default: false + repository_dispatch: + types: [spc-download] + +permissions: + contents: read + +jobs: + setup-matrix: + runs-on: ubuntu-24.04 + permissions: + contents: read + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + - name: Set up matrix + id: set-matrix + run: | + # Default values + default_php='["8.2","8.3","8.4","8.5"]' + default_arch='["x86_64","aarch64"]' + + # Parse inputs or use defaults + if [[ -n "${INPUTS_PHP_VERSIONS}" ]]; then + php_versions=$(echo "${INPUTS_PHP_VERSIONS}" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') + else + php_versions=$default_php + fi + + if [[ -n "${INPUTS_ARCHITECTURES}" ]]; then + arch_versions=$(echo "${INPUTS_ARCHITECTURES}" | jq -R 'split(",") | map(gsub("^\\s+|\\s+$";""))') + else + arch_versions=$default_arch + fi + + # Create matrix JSON (compact, single line) + matrix=$(jq -nc \ + --argjson php "$php_versions" \ + --argjson arch "$arch_versions" \ + '{"php-version":$php,"arch":$arch}') + + echo "matrix=$matrix" >> $GITHUB_OUTPUT + env: + INPUTS_PHP_VERSIONS: ${{ inputs.php_versions }} + INPUTS_ARCHITECTURES: ${{ inputs.architectures }} + + build: + needs: setup-matrix + name: Build for ${{ matrix.arch }} PHP ${{ matrix.php-version }} + runs-on: ${{ matrix.arch == 'x86_64' && 'ubuntu-24.04' || 'ubuntu-24.04-arm' }} + permissions: + contents: read + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.setup-matrix.outputs.matrix) }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + ITERATION: ${{ inputs.iteration || '' }} + PACKAGES: ${{ inputs.packages || '' }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: versioned + persist-credentials: false + + - name: Set PHP version short + run: | + # Convert "8.5" to "85" + PHP_VERSION_SHORT=$(echo "${{ matrix.php-version }}" | tr -d '.') + echo "PHP_VERSION_SHORT=$PHP_VERSION_SHORT" >> $GITHUB_ENV + + - name: Install APT dependencies + run: | + sudo apt-get update + sudo apt-get install -y curl ruby build-essential tar zstd python3 python3-requests + sudo apt-get upgrade -y + sudo gem install fpm + + - name: Install nfpm + run: | + curl -sfL https://goreleaser.com/static/run | bash -s -- --version + curl -sfL https://goreleaser.com/static/run | bash -s -- install github.com/goreleaser/nfpm/v2/cmd/nfpm@latest + + - name: Install composer + run: | + sudo curl -L https://files.henderkes.com/${{ matrix.arch }}-linux/php -o /usr/local/bin/php + sudo chmod +x /usr/local/bin/php + sudo curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet + sudo mv composer.phar /usr/local/bin/composer + + - name: Prepare cache directories + run: | + composer config -g cache-dir + + - name: Cache Composer downloads + uses: actions/cache@v4 + with: + path: ~/.cache/composer + key: composer-${{ hashFiles('**/composer.lock') }} + restore-keys: | + composer- + + - name: Install vendor + run: composer install --no-interaction --prefer-dist --no-progress + + - name: Download artifact from spc-download.yml + uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11 + with: + workflow: spc-download.yml + name: downloads-tarball + branch: master + + - name: Extract with permissions + run: | + mkdir -p vendor/crazywhalecc/static-php-cli/downloads + tar -xzf downloads.tar.gz -C vendor/crazywhalecc/static-php-cli/downloads + rm downloads.tar.gz + + - name: Build PHP packages + run: | + PACKAGES_FLAG="" + if [[ -n "${{ env.PACKAGES }}" ]]; then + PACKAGES_FLAG="--packages=${{ env.PACKAGES }}" + fi + if [[ -n "${ITERATION}" ]]; then + php bin/spp all --target=native-native-musl --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk --iteration="${ITERATION}" $PACKAGES_FLAG -dynamic + else + php bin/spp all --target=native-native-musl --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk $PACKAGES_FLAG -dynamic + fi + + - name: Upload to Forgejo + working-directory: dist/apk + run: | + ../../bin/forgejo-helper upload alpine "${{ env.PHP_VERSION_SHORT }}" "${{ secrets.FORGEJO_PASSWORD }}" "*.apk" + + - name: Upload logs on failure + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: build-logs-${{ matrix.arch }}-php${{ matrix.php-version }} + path: vendor/crazywhalecc/static-php-cli/log + + - name: Setup tmate session + if: ${{ failure() && inputs.debug_tmate == true }} + uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3 + timeout-minutes: 30 From 15cd0ced8919ca44198b255e65c288a7fcb819d5 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:17:52 +0100 Subject: [PATCH 31/37] use debs instead of goreleaser --- .github/workflows/build-apk-forgejo.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-apk-forgejo.yml b/.github/workflows/build-apk-forgejo.yml index afd4354..dc82a0a 100644 --- a/.github/workflows/build-apk-forgejo.yml +++ b/.github/workflows/build-apk-forgejo.yml @@ -105,8 +105,14 @@ jobs: - name: Install nfpm run: | - curl -sfL https://goreleaser.com/static/run | bash -s -- --version - curl -sfL https://goreleaser.com/static/run | bash -s -- install github.com/goreleaser/nfpm/v2/cmd/nfpm@latest + if [[ "${{ matrix.arch }}" == "aarch64" ]]; then + NFPM_ARCH="arm64" + else + NFPM_ARCH="amd64" + fi + curl -L -o nfpm.deb https://github.com/goreleaser/nfpm/releases/download/v2.44.0/nfpm_2.44.0_${NFPM_ARCH}.deb + sudo dpkg -i nfpm.deb + rm nfpm.deb - name: Install composer run: | From 978743194845e274d519ef7d88337be3e50aa7b3 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:19:56 +0100 Subject: [PATCH 32/37] we don't need fpm for apks --- .github/workflows/build-apk-forgejo.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build-apk-forgejo.yml b/.github/workflows/build-apk-forgejo.yml index dc82a0a..579426a 100644 --- a/.github/workflows/build-apk-forgejo.yml +++ b/.github/workflows/build-apk-forgejo.yml @@ -99,9 +99,8 @@ jobs: - name: Install APT dependencies run: | sudo apt-get update - sudo apt-get install -y curl ruby build-essential tar zstd python3 python3-requests + sudo apt-get install -y curl build-essential tar zstd python3 python3-requests sudo apt-get upgrade -y - sudo gem install fpm - name: Install nfpm run: | From 32ec78a9ad933981675c067311259476a4887157 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:41:05 +0100 Subject: [PATCH 33/37] +phpversion for debian packages that can have conflicting versions --- src/step/CreatePackages.php | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 35ba679..619da00 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -461,18 +461,34 @@ private static function createDebPackage( default => $architecture, }; + // For DEB packages, append PHP version to package version for extensions + // This ensures proper version ordering when the same extension version is built for different PHP versions + // e.g., redis 6.0.2+php85 is higher than redis 6.0.2+php83 + [$fullPhpVersion] = self::getPhpVersionAndArchitecture(); + $debVersion = $phpVersion; + + // If package version differs from PHP version, it's an extension - append PHP version + if ($phpVersion !== $fullPhpVersion) { + if (preg_match('/^(\d+)\.(\d+)/', $fullPhpVersion, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $fullPhpVersion); + } + $debVersion = $phpVersion . '+php' . $phpVersionSuffix; + } + // Calculate iteration for DEB (with possible override) - $computed = (string)self::getNextIteration($name, $phpVersion, $debArch, 'deb'); + $computed = (string)self::getNextIteration($name, $debVersion, $debArch, 'deb'); $iteration = self::$iterationOverride ?? $computed; //$osRelease = parse_ini_file('/etc/os-release'); //$distroCodename = $osRelease['VERSION_CODENAME'] ?? null; //$debIteration = $distroCodename !== '' ? "{$iteration}~{$distroCodename}" : $iteration; $debIteration = $iteration; - $fullVersion = "{$phpVersion}-{$debIteration}"; + $fullVersion = "{$debVersion}-{$debIteration}"; // Debian filename format: {name}_{version}-{revision}_{arch}.deb - $packageFile = DIST_DEB_PATH . "/{$name}_{$phpVersion}-{$debIteration}_{$debArch}.deb"; + $packageFile = DIST_DEB_PATH . "/{$name}_{$debVersion}-{$debIteration}_{$debArch}.deb"; $fpmArgs = [...[ 'fpm', @@ -481,7 +497,7 @@ private static function createDebPackage( '--deb-compression', 'xz', '-p', $packageFile, '--name', $name, - '--version', $phpVersion, + '--version', $debVersion, '--architecture', $debArch, '--iteration', $debIteration, // Debian revision (includes distro) '--description', "Static PHP Package for {$name}", From ccda5a82d619e1de0fb52829295340c33d8954aa Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:42:23 +0100 Subject: [PATCH 34/37] fml --- .github/workflows/build-apk-forgejo.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-apk-forgejo.yml b/.github/workflows/build-apk-forgejo.yml index 579426a..0f86e1f 100644 --- a/.github/workflows/build-apk-forgejo.yml +++ b/.github/workflows/build-apk-forgejo.yml @@ -155,9 +155,9 @@ jobs: PACKAGES_FLAG="--packages=${{ env.PACKAGES }}" fi if [[ -n "${ITERATION}" ]]; then - php bin/spp all --target=native-native-musl --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk --iteration="${ITERATION}" $PACKAGES_FLAG -dynamic + php bin/spp all --target="native-native-musl -dynamic" --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk --iteration="${ITERATION}" $PACKAGES_FLAG else - php bin/spp all --target=native-native-musl --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk $PACKAGES_FLAG -dynamic + php bin/spp all --target="native-native-musl -dynamic" --phpv=${{ matrix.php-version }} --prefix="-zts" --type=apk $PACKAGES_FLAG fi - name: Upload to Forgejo From a575f30ed91003c4207c99b3456b0b81025e2738 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 01:58:15 +0100 Subject: [PATCH 35/37] need to _85 for apks too --- src/package/frankenphp.php | 16 +++++++++--- src/step/CreatePackages.php | 50 +++++++++++++++++++++++++++++-------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index fc76f09..dd8d5e3 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -398,8 +398,18 @@ public function createApkPackage(string $architecture, array $binaryDependencies $name = $this->getName(); + // For APK packages, append PHP version to package version for proper sorting + // e.g., 1.11.0_85 is higher than 1.11.0_83 + $phpMajorMinor = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpMajorMinor, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $phpMajorMinor); + } + $apkVersion = $version . '_' . $phpVersionSuffix; + // Calculate iteration for APK (with possible override) - $computed = (string)$this->getNextIteration($name, $version, $architecture, 'apk'); + $computed = (string)$this->getNextIteration($name, $apkVersion, $architecture, 'apk'); $iteration = $iterationOverride ?? $computed; $versionedConflicts = $this->getVersionedConflicts(); @@ -409,7 +419,7 @@ public function createApkPackage(string $architecture, array $binaryDependencies 'name' => $name, 'arch' => $architecture, 'platform' => 'linux', - 'version' => $version, + 'version' => $apkVersion, 'release' => $iteration, 'section' => 'default', 'priority' => 'optional', @@ -538,7 +548,7 @@ public function createApkPackage(string $architecture, array $binaryDependencies if ($debuginfo) { $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { - $this->createApkDebuginfo($name, $version, $iteration, $architecture, $frankenDbg, $frankenphpSuffix); + $this->createApkDebuginfo($name, $apkVersion, $iteration, $architecture, $frankenDbg, $frankenphpSuffix); } } } diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 619da00..3c125a4 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -298,15 +298,30 @@ private static function createRpmPackage(\staticphp\package $package, string $ph echo "Creating RPM package for {$name}...\n"; + // For RPM packages, append PHP version to package version for extensions + // This ensures proper version ordering when the same extension version is built for different PHP versions + [$fullPhpVersion] = self::getPhpVersionAndArchitecture(); + $rpmVersion = $phpVersion; + + // If package version differs from PHP version, it's an extension - append PHP version + if ($phpVersion !== $fullPhpVersion) { + if (preg_match('/^(\d+)\.(\d+)/', $fullPhpVersion, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $fullPhpVersion); + } + $rpmVersion = $phpVersion . '+php' . $phpVersionSuffix; + } + // Calculate iteration for RPM (with possible override) - $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'rpm'); + $computed = (string)self::getNextIteration($name, $rpmVersion, $architecture, 'rpm'); $iteration = self::$iterationOverride ?? $computed; // Generate full package filename with PHP version suffix and distribution version $phpSuffix = self::getPhpVersionSuffix(); $distVersion = self::getDistVersion(); $distSuffix = $distVersion !== '' ? ".{$distVersion}" : ''; - $packageFile = DIST_RPM_PATH . "/{$name}-{$phpVersion}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $packageFile = DIST_RPM_PATH . "/{$name}-{$rpmVersion}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; $fpmArgs = [...[ 'fpm', @@ -315,7 +330,7 @@ private static function createRpmPackage(\staticphp\package $package, string $ph '--rpm-compression', 'xz', '-p', $packageFile, // Full path with phpSuffix and distVersion in filename '--name', $name, - '--version', $phpVersion, + '--version', $rpmVersion, '--iteration', $iteration, '--architecture', $architecture, '--description', "Static PHP Package for {$name}", @@ -345,17 +360,17 @@ private static function createRpmPackage(\staticphp\package $package, string $ph if (str_ends_with($name, '-debuginfo')) { $base = preg_replace('/-debuginfo$/', '', $name); $fpmArgs[] = '--depends'; - $fpmArgs[] = sprintf('%s = %s-%s', $base, $phpVersion, $iteration); + $fpmArgs[] = sprintf('%s = %s-%s', $base, $rpmVersion, $iteration); } if (isset($config['provides']) && is_array($config['provides'])) { foreach ($config['provides'] as $provide) { $fpmArgs[] = '--provides'; - $fpmArgs[] = "$provide = $phpVersion-$iteration"; + $fpmArgs[] = "$provide = $rpmVersion-$iteration"; if (str_ends_with($provide, '.so')) { $provide = str_replace('.so', '.so()(64bit)', $provide); $fpmArgs[] = '--provides'; - $fpmArgs[] = "$provide = $phpVersion-$iteration"; + $fpmArgs[] = "$provide = $rpmVersion-$iteration"; } } } @@ -363,7 +378,7 @@ private static function createRpmPackage(\staticphp\package $package, string $ph if (isset($config['replaces']) && is_array($config['replaces'])) { foreach ($config['replaces'] as $replace) { $fpmArgs[] = '--replaces'; - $fpmArgs[] = "$replace < {$phpVersion}-{$iteration}"; + $fpmArgs[] = "$replace < {$rpmVersion}-{$iteration}"; } } @@ -657,16 +672,31 @@ private static function createApkPackage(\staticphp\package $package, string $ph echo "Creating APK package for {$name} using nfpm...\n"; + // For APK packages, append PHP version to package version for extensions + // This ensures proper version ordering when the same extension version is built for different PHP versions + [$fullPhpVersion] = self::getPhpVersionAndArchitecture(); + $apkVersion = $phpVersion; + + // If package version differs from PHP version, it's an extension - append PHP version + if ($phpVersion !== $fullPhpVersion) { + if (preg_match('/^(\d+)\.(\d+)/', $fullPhpVersion, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $fullPhpVersion); + } + $apkVersion = $phpVersion . '_' . $phpVersionSuffix; + } + // Calculate iteration for APK (with possible override) - $computed = (string)self::getNextIteration($name, $phpVersion, $architecture, 'apk'); + $computed = (string)self::getNextIteration($name, $apkVersion, $architecture, 'apk'); $iteration = self::$iterationOverride ?? $computed; // APK uses r{iteration} format for revision number $apkIteration = $iteration; - $fullVersion = "{$phpVersion}-r{$apkIteration}"; + $fullVersion = "{$apkVersion}-r{$apkIteration}"; // Use nfpm instead of fpm for APK packages - self::createApkWithNfpm($package, $name, $phpVersion, $architecture, $apkIteration, $config, $isDebuginfo); + self::createApkWithNfpm($package, $name, $apkVersion, $architecture, $apkIteration, $config, $isDebuginfo); } private static function createApkWithNfpm(\staticphp\package $package, string $name, string $phpVersion, string $architecture, string $iteration, array $config, bool $isDebuginfo): void { From 6b1431d82b2c1378b4cefbb1d5f865556e4521d6 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 18:49:12 +0100 Subject: [PATCH 36/37] no more different email addresses, unify under pkg@ --- composer.json | 2 +- src/package/frankenphp.php | 4 ++-- src/step/CreatePackages.php | 14 +++++++------- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.json b/composer.json index 6c547af..445f0c5 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "authors": [ { "name": "Marc Henderkes", - "email": "rpms@henderkes.com" + "email": "pkg@henderkes.com" } ], "support": { diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index dd8d5e3..e2ed80f 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -423,7 +423,7 @@ public function createApkPackage(string $architecture, array $binaryDependencies 'release' => $iteration, 'section' => 'default', 'priority' => 'optional', - 'maintainer' => 'Marc Henderkes ', + 'maintainer' => 'Marc Henderkes ', 'description' => "FrankenPHP - Modern PHP application server", 'vendor' => 'Marc Henderkes', 'homepage' => 'https://apks.henderkes.com', @@ -565,7 +565,7 @@ private function createApkDebuginfo(string $name, string $version, string $itera 'release' => $iteration, 'section' => 'default', 'priority' => 'optional', - 'maintainer' => 'Marc Henderkes ', + 'maintainer' => 'Marc Henderkes ', 'description' => "Debug symbols for FrankenPHP", 'vendor' => 'Marc Henderkes', 'homepage' => 'https://apks.henderkes.com', diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 3c125a4..9bb9ae1 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -335,9 +335,9 @@ private static function createRpmPackage(\staticphp\package $package, string $ph '--architecture', $architecture, '--description', "Static PHP Package for {$name}", '--license', $package->getLicense(), - '--maintainer', 'Marc Henderkes ', - '--vendor', 'Marc Henderkes ', - '--url', 'rpms.henderkes.com', + '--maintainer', 'Marc Henderkes ', + '--vendor', 'Marc Henderkes ', + '--url', 'pkgs.henderkes.com', ], ...$extraArgs]; // Ensure non-CLI packages depend on the same PHP major.minor as php-zts-cli (ignore iteration/patch) @@ -517,9 +517,9 @@ private static function createDebPackage( '--iteration', $debIteration, // Debian revision (includes distro) '--description', "Static PHP Package for {$name}", '--license', $package->getLicense(), - '--maintainer', 'Marc Henderkes ', - '--vendor', 'Marc Henderkes ', - '--url', 'debs.henderkes.com', + '--maintainer', 'Marc Henderkes ', + '--vendor', 'Marc Henderkes ', + '--url', 'pkgs.henderkes.com', ], ...$extraArgs]; // Ensure non-CLI packages depend on the same PHP major.minor as php-zts-cli (ignore iteration/patch) @@ -711,7 +711,7 @@ private static function createApkWithNfpm(\staticphp\package $package, string $n 'release' => $iteration, 'section' => 'default', 'priority' => 'optional', - 'maintainer' => 'Marc Henderkes ', + 'maintainer' => 'Marc Henderkes ', 'description' => "Static PHP Package for {$name}", 'vendor' => 'Marc Henderkes', 'homepage' => 'https://apks.henderkes.com', From f36f97f364495e5321c0c729574719cf9ff89f19 Mon Sep 17 00:00:00 2001 From: henderkes Date: Tue, 23 Dec 2025 20:58:29 +0100 Subject: [PATCH 37/37] blub --- bin/createrepo_static | 9 ++++++--- src/package/frankenphp.php | 32 ++++++++++++++++++++------------ src/step/CreatePackages.php | 13 ++++++------- 3 files changed, 32 insertions(+), 22 deletions(-) diff --git a/bin/createrepo_static b/bin/createrepo_static index 871eb18..dde01c7 100755 --- a/bin/createrepo_static +++ b/bin/createrepo_static @@ -20,7 +20,8 @@ def parse_rpm_info(filename): basename = os.path.basename(filename) # Match standard php-zts RPMs - match_php = re.match(r'(?Pphp-zts-[^-]+)-(?P\d+\.\d+\.\d+)-(?P[^.]+)\.(?P[^.]+)\.rpm', basename) + # Format: php-zts-cli-8.5.1-1.el10.x86_64.rpm (with optional .el10/.el9/etc) + match_php = re.match(r'(?Pphp-zts-[^-]+)-(?P\d+\.\d+\.\d+)-(?P[^.]+)(?:\.[^.]+)?\.(?Px86_64|aarch64|noarch)\.rpm', basename) if match_php: name = match_php.group("name") version = match_php.group("version") @@ -30,7 +31,8 @@ def parse_rpm_info(filename): return name, version, release, arch, stream # Match extension packages - match_ext = re.match(r'(?Pphp-zts-[^-]+)-(?P\d+\.\d+\.\d+_\d+)-(?P[^.]+)\.(?P[^.]+)\.rpm', basename) + # Format: php-zts-apcu-5.1.28_85-1.el10.x86_64.rpm (with optional .el10/.el9/etc) + match_ext = re.match(r'(?Pphp-zts-[^-]+)-(?P\d+\.\d+\.\d+(?:~[a-z0-9]+)?_\d+)-(?P[^.]+)(?:\.[^.]+)?\.(?Px86_64|aarch64|noarch)\.rpm', basename) if match_ext: name = match_ext.group("name") version = match_ext.group("version") @@ -46,7 +48,8 @@ def parse_rpm_info(filename): return name, version, release, arch, stream # Match frankenphp - match_franken = re.match(r'(frankenphp)-(?P\d+\.\d+\.\d+_\d+)-(?P\d+)\.(?P[^.]+)\.rpm', basename) + # Format: frankenphp-1.11.0_85-1.el10.x86_64.rpm (with optional .el10/.el9/etc) + match_franken = re.match(r'(frankenphp)-(?P\d+\.\d+\.\d+_\d+)-(?P\d+)(?:\.[^.]+)?\.(?Px86_64|aarch64|noarch)\.rpm', basename) if match_franken: name = match_franken.group(1) version = match_franken.group("version") diff --git a/src/package/frankenphp.php b/src/package/frankenphp.php index e2ed80f..2406220 100644 --- a/src/package/frankenphp.php +++ b/src/package/frankenphp.php @@ -99,28 +99,36 @@ public function createRpmPackage(string $architecture, array $binaryDependencies } $version = $matches[1]; + // Append PHP version suffix to FrankenPHP version + $phpMajorMinor = SPP_PHP_VERSION; + if (preg_match('/^(\d+)\.(\d+)/', $phpMajorMinor, $phpMatches)) { + $phpVersionSuffix = $phpMatches[1] . $phpMatches[2]; // e.g., "85" from "8.5" + } else { + $phpVersionSuffix = str_replace('.', '', $phpMajorMinor); + } + $rpmVersion = $version . '_' . $phpVersionSuffix; + $name = $this->getName(); // Calculate iteration for RPM (with possible override) - $computed = (string)$this->getNextIteration($name, $version, $architecture, 'rpm'); + $computed = (string)$this->getNextIteration($name, $rpmVersion, $architecture, 'rpm'); $iteration = $iterationOverride ?? $computed; $versionedConflicts = $this->getVersionedConflicts(); - // Generate full package filename with PHP version suffix and distribution version - $phpSuffix = $this->getPhpVersionSuffix(); + // Generate full package filename with distribution version $distVersion = $this->getDistVersion(); $distSuffix = $distVersion !== '' ? ".{$distVersion}" : ''; - $packageFile = DIST_RPM_PATH . "/{$name}-{$version}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $packageFile = DIST_RPM_PATH . "/{$name}-{$rpmVersion}-{$iteration}{$distSuffix}.{$architecture}.rpm"; $fpmArgs = [ 'fpm', '-s', 'dir', '-t', 'rpm', '--rpm-compression', 'xz', - '-p', $packageFile, // Full path with phpSuffix and distVersion in filename + '-p', $packageFile, // Full path with distVersion in filename '-n', $name, - '-v', $version, + '-v', $rpmVersion, '--license', $this->getLicense(), '--config-files', '/etc/frankenphp/Caddyfile', '--provides', 'frankenphp', @@ -172,7 +180,7 @@ public function createRpmPackage(string $architecture, array $binaryDependencies // Create FrankenPHP debuginfo package if debug file exists $frankenDbg = BUILD_ROOT_PATH . '/debug/frankenphp.debug'; if (file_exists($frankenDbg)) { - $dbgPackageFile = DIST_RPM_PATH . "/{$name}-debuginfo-{$version}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $dbgPackageFile = DIST_RPM_PATH . "/{$name}-debuginfo-{$rpmVersion}-{$iteration}{$distSuffix}.{$architecture}.rpm"; $dbgArgs = [ 'fpm', '-s', 'dir', @@ -180,11 +188,11 @@ public function createRpmPackage(string $architecture, array $binaryDependencies '--rpm-compression', 'xz', '-p', $dbgPackageFile, '-n', $name . '-debuginfo', - '-v', $version, + '-v', $rpmVersion, '--iteration', $iteration, '--architecture', $architecture, '--license', $this->getLicense(), - '--depends', sprintf('%s = %s-%s', $name, $version, $iteration), + '--depends', sprintf('%s = %s-%s', $name, $rpmVersion, $iteration), $frankenDbg . '=/usr/lib/debug/usr/bin/frankenphp.debug', ]; $dbgProcess = new Process($dbgArgs); @@ -699,10 +707,10 @@ private function getNextIteration(string $name, string $version, string $archite $maxIteration = 0; if ($packageType === 'rpm') { - // RPM: {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm + // RPM: {name}-{version}-{iteration}.{distVersion}.{arch}.rpm // Also match old formats: - // - {name}-{version}-{iteration}.{arch}.rpm (no suffix) - // - {name}-{version}-{iteration}.{phpSuffix}.{arch}.rpm (no distVersion) + // - {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm (with phpSuffix) + // - {name}-{version}-{iteration}.{arch}.rpm (no distVersion) $rpmPattern = DIST_RPM_PATH . "/{$name}-{$version}-*.rpm"; $rpmFiles = glob($rpmPattern); diff --git a/src/step/CreatePackages.php b/src/step/CreatePackages.php index 9bb9ae1..2030643 100644 --- a/src/step/CreatePackages.php +++ b/src/step/CreatePackages.php @@ -310,18 +310,17 @@ private static function createRpmPackage(\staticphp\package $package, string $ph } else { $phpVersionSuffix = str_replace('.', '', $fullPhpVersion); } - $rpmVersion = $phpVersion . '+php' . $phpVersionSuffix; + $rpmVersion = $phpVersion . '_' . $phpVersionSuffix; } // Calculate iteration for RPM (with possible override) $computed = (string)self::getNextIteration($name, $rpmVersion, $architecture, 'rpm'); $iteration = self::$iterationOverride ?? $computed; - // Generate full package filename with PHP version suffix and distribution version - $phpSuffix = self::getPhpVersionSuffix(); + // Generate full package filename with distribution version $distVersion = self::getDistVersion(); $distSuffix = $distVersion !== '' ? ".{$distVersion}" : ''; - $packageFile = DIST_RPM_PATH . "/{$name}-{$rpmVersion}-{$iteration}.{$phpSuffix}{$distSuffix}.{$architecture}.rpm"; + $packageFile = DIST_RPM_PATH . "/{$name}-{$rpmVersion}-{$iteration}{$distSuffix}.{$architecture}.rpm"; $fpmArgs = [...[ 'fpm', @@ -1102,10 +1101,10 @@ private static function getNextIteration(string $name, string $phpVersion, strin $maxIteration = 0; if ($packageType === 'rpm') { - // RPM: {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm + // RPM: {name}-{version}-{iteration}.{distVersion}.{arch}.rpm // Also match old formats: - // - {name}-{version}-{iteration}.{arch}.rpm (no suffix) - // - {name}-{version}-{iteration}.{phpSuffix}.{arch}.rpm (no distVersion) + // - {name}-{version}-{iteration}.{phpSuffix}.{distVersion}.{arch}.rpm (with phpSuffix) + // - {name}-{version}-{iteration}.{arch}.rpm (no distVersion) $rpmPattern = DIST_RPM_PATH . "/{$name}-{$phpVersion}-*.rpm"; $rpmFiles = glob($rpmPattern);