Build FrankenPHP #14
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build FrankenPHP | |
| # Builds custom static FrankenPHP binaries with the full extension set Burd | |
| # needs (redis, mongodb, imagick, memcached, intl, …) that the official builds | |
| # omit. Each build is a self-contained static binary published to a per-PHP-line | |
| # GitHub Release with a checksum. | |
| # | |
| # These builds take ~45-60 min each, so this does NOT run on push — trigger it | |
| # manually or let the monthly schedule refresh them. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| php: | |
| description: "PHP line to build ('all', or 8.3 / 8.4 / 8.5)" | |
| type: string | |
| default: "all" | |
| arch: | |
| description: "Architecture ('both', arm64, x86_64)" | |
| type: string | |
| default: "both" | |
| schedule: | |
| # Monthly: pick up new FrankenPHP / PHP patch releases. | |
| - cron: "0 4 1 * *" | |
| permissions: | |
| contents: write | |
| jobs: | |
| matrix: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| combos: ${{ steps.gen.outputs.combos }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - id: gen | |
| env: | |
| PHP_IN: ${{ inputs.php || 'all' }} | |
| ARCH_IN: ${{ inputs.arch || 'both' }} | |
| run: | | |
| phps='["8.3","8.4","8.5"]' | |
| [ "$PHP_IN" != "all" ] && [ -n "$PHP_IN" ] && phps="[\"$PHP_IN\"]" | |
| arches='[{"arch":"arm64","runner":"macos-14"},{"arch":"x86_64","runner":"macos-15-intel"}]' | |
| [ "$ARCH_IN" = "arm64" ] && arches='[{"arch":"arm64","runner":"macos-14"}]' | |
| [ "$ARCH_IN" = "x86_64" ] && arches='[{"arch":"x86_64","runner":"macos-15-intel"}]' | |
| combos=$(jq -cn --argjson p "$phps" --argjson a "$arches" \ | |
| '[$p[] as $php | $a[] | {php:$php, arch:.arch, runner:.runner}]') | |
| echo "combos=$combos" >> "$GITHUB_OUTPUT" | |
| build: | |
| needs: matrix | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: ${{ fromJson(needs.matrix.outputs.combos) }} | |
| runs-on: ${{ matrix.runner }} | |
| timeout-minutes: 120 | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Install Go | |
| uses: actions/setup-go@v5 | |
| with: | |
| go-version: "1.26" | |
| # static-php-cli / FrankenPHP's build tooling needs a host PHP + composer. | |
| # Use 8.4 — its composer.lock isn't satisfiable on older host PHP. | |
| - name: Set up host PHP + composer | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: "8.4" | |
| tools: composer | |
| - name: Install build dependencies | |
| run: | | |
| brew update | |
| brew install pkg-config autoconf automake libtool bison re2c | |
| - name: Build FrankenPHP (${{ matrix.php }}, ${{ matrix.arch }}) | |
| working-directory: frankenphp | |
| # GITHUB_TOKEN authenticates static-php-cli's GitHub API source | |
| # downloads — without it they hit the unauthenticated rate limit (403). | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: ./scripts/build-frankenphp.sh "${{ matrix.php }}" "${{ matrix.arch }}" darwin | |
| # On a compile failure, surface static-php-cli's detailed log so the | |
| # offending extension/error is visible without a --debug rebuild. | |
| - name: Dump build log on failure | |
| if: failure() | |
| run: | | |
| base="frankenphp/frankenphp/dist/static-php-cli/log" | |
| # spc.shell.log holds the raw compiler/make output — grep it for the | |
| # actual "error:" lines that identify the offending extension. | |
| if [ -f "$base/spc.shell.log" ]; then | |
| echo "===== compiler errors (spc.shell.log) =====" | |
| grep -iE "error:|Error [0-9]|undefined|no member|has no member|conflicting|incompatible|In file included" "$base/spc.shell.log" | tail -60 || true | |
| echo "===== spc.shell.log tail =====" | |
| tail -80 "$base/spc.shell.log" | |
| fi | |
| if [ -f "$base/spc.output.log" ]; then | |
| echo "===== spc.output.log tail =====" | |
| tail -40 "$base/spc.output.log" | |
| fi | |
| - name: Package + checksum | |
| id: pkg | |
| working-directory: frankenphp | |
| run: | | |
| set -euo pipefail | |
| bin="php-web-${{ matrix.php }}-darwin-${{ matrix.arch }}" | |
| # Extract the FrankenPHP version from the binary and combine it with | |
| # the PHP line, so the published version reads e.g. "8.4-1.12.4" | |
| # instead of just the FrankenPHP "1.12.4" — the PHP line is what users | |
| # care about when picking a version. | |
| fp_ver="$(./"$bin" version 2>/dev/null | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1 | tr -d v)" | |
| if [ -z "$fp_ver" ]; then | |
| echo "Could not determine FrankenPHP version from binary" >&2 | |
| exit 1 | |
| fi | |
| combined="${{ matrix.php }}-${fp_ver}" | |
| asset="frankenphp-${combined}-${{ matrix.arch }}" | |
| cp "$bin" "$asset" | |
| shasum -a 256 "$asset" | cut -d' ' -f1 > "$asset.sha256" | |
| echo "asset=$asset" >> "$GITHUB_OUTPUT" | |
| echo "combined=$combined" >> "$GITHUB_OUTPUT" | |
| # Tag per combined version so the release name matches the version | |
| # string, giving services.json a clean {version} URL substitution: | |
| # .../releases/download/frankenphp-<version>/frankenphp-<version>-<arch> | |
| echo "tag=frankenphp-${combined}" >> "$GITHUB_OUTPUT" | |
| - name: Publish to release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| working-directory: frankenphp | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.pkg.outputs.tag }}" | |
| if ! gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release create "$TAG" \ | |
| --title "FrankenPHP (PHP ${{ matrix.php }})" \ | |
| --notes "Custom static FrankenPHP for PHP ${{ matrix.php }} with Burd's full extension set (redis, mongodb, imagick, memcached, intl, and more). Versions are published as <php>-<frankenphp>, e.g. ${{ steps.pkg.outputs.combined }}." \ | |
| || true | |
| fi | |
| gh release upload "$TAG" \ | |
| "${{ steps.pkg.outputs.asset }}" \ | |
| "${{ steps.pkg.outputs.asset }}.sha256" \ | |
| --clobber |