Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: create a static build of FrankenPHP #198

Merged
merged 24 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
platforms: ${{ steps.matrix.outputs.platforms }}
metadata: ${{ steps.matrix.outputs.metadata }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
Expand Down Expand Up @@ -57,7 +57,7 @@ jobs:
- platform: linux/386
qemu: false
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Set up QEMU
if: matrix.qemu
Expand Down
97 changes: 97 additions & 0 deletions .github/workflows/static.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
name: Build binary releases
on:
pull_request:
branches:
- main
push:
branches:
- main
tags:
- v*
workflow_dispatch:
inputs: {}
jobs:
build-linux:
name: Build Linux x86_64 binary
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
version: latest

- name: Build
id: build
uses: docker/bake-action@v3
with:
pull: true
load: true
targets: static-builder
set: |
*.cache-from=type=gha,scope=${{github.ref}}-static-builder
*.cache-from=type=gha,scope=refs/heads/main-static-builder
*.cache-to=type=gha,scope=${{github.ref}}-static-builder
env:
VERSION: ${{github.ref_name}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Copy binary
run: docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/caddy/frankenphp/frankenphp frankenphp ; docker rm static-builder

- name: Upload binary
uses: actions/upload-artifact@v3
with:
name: frankenphp-linux-x86_64-dev
path: frankenphp

build-mac:
name: Build macOS binaries
runs-on: macos-latest

steps:
- uses: actions/checkout@v4

- uses: actions/checkout@v4
with:
repository: crazywhalecc/static-php-cli
path: static-php-cli

- uses: actions/setup-go@v4
with:
go-version: '1.21'

- name: Install static-php-cli dependencies
working-directory: static-php-cli/
run: composer install --no-dev -a

- name: Install missing system dependencies
run: ./bin/spc doctor --auto-fix
working-directory: static-php-cli/

- name: Fetch libraries sources
working-directory: static-php-cli/
run: ./bin/spc fetch --with-php=8.2 -A
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build static libphp
working-directory: static-php-cli/
run: ./bin/spc build --enable-zts --build-embed --debug "bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu"

- name: Set CGO flags
working-directory: static-php-cli/
run: |
echo "CGO_CFLAGS=$(./buildroot/bin/php-config --includes | sed s#-I/#-I$PWD/buildroot/#g)" >> "$GITHUB_ENV"
echo "CGO_LDFLAGS=-framework CoreFoundation -framework SystemConfiguration $(./buildroot/bin/php-config --ldflags) $(./buildroot/bin/php-config --libs)" >> "$GITHUB_ENV"

- name: Build FrankenPHP
working-directory: caddy/frankenphp/
run: go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie -w -s"

- name: Upload binary
uses: actions/upload-artifact@v3
with:
name: frankenphp-mac-x86_64-dev
path: caddy/frankenphp/frankenphp
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
matrix:
php-versions: ['8.2', '8.3']
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- uses: actions/setup-go@v4
with:
Expand Down
19 changes: 18 additions & 1 deletion docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ variable "VERSION" {
default = "dev"
}

variable "GO_VERSION" {
default = "1.21"
}

variable "SHA" {}

variable "LATEST" {
Expand Down Expand Up @@ -59,7 +63,7 @@ target "default" {
}
contexts = {
php-base = "docker-image://php:${php-version}-zts-${os}"
golang-base = "docker-image://golang:1.21-${os}"
golang-base = "docker-image://golang:${GO_VERSION}-${os}"
}
dockerfile = os == "alpine" ? "alpine.Dockerfile" : "Dockerfile"
context = "./"
Expand Down Expand Up @@ -91,3 +95,16 @@ target "default" {
FRANKENPHP_VERSION = VERSION
}
}

target "static-builder" {
contexts = {
golang-base = "docker-image://golang:${GO_VERSION}-alpine"
}
dockerfile = "static-builder.Dockerfile"
context = "./"
tags = ["${IMAGE_NAME}:static-builder"]
args = {
FRANKENPHP_VERSION = VERSION
}
secret = ["id=github-token,env=GITHUB_TOKEN"]
}
5 changes: 5 additions & 0 deletions docs/compile.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Compile From Sources

This document explain how to create a FrankenPHP build that will load PHP as a dymanic library.
This is the recommended method.

Alternatively, [creating static builds](static.md) is also possible.

## Install PHP

FrankenPHP is compatible with the PHP 8.2 and superior.
Expand Down
64 changes: 64 additions & 0 deletions docs/static.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Create a Static Build

Instead of using a local installation of the PHP library,
it's possible to create a static build of FrankenPHP thanks to the great [static-php-cli project](https://github.com/crazywhalecc/static-php-cli) (despite its name, this project support all SAPIs, not only CLI).

With this method, a single, portable, binary will contain the PHP interpreter, the Caddy web server and FrankenPHP!

## Linux

We provide a Docker image to build a Linux static binary:

```console
docker buildx bake --load static-builder
docker cp $(docker create --name static-builder dunglas/frankenphp:static-builder):/go/src/app/caddy/frankenphp/frankenphp frankenphp ; docker rm static-builder
```

The resulting static binary is named `frankenphp` and is available in the current directory.

If you want to build the static binary without Docker, take a look to the `static-builder.Dockerfile` file.

### Custom Extensions

By default, most popular PHP extensions are compiled.

To reduce the size of the binary and to reduce the attack surface, you can choose the list of extensions to build using the `PHP_EXTENSIONS` Docker ARG.

For instance, run the following command to only build the `opcache` extension:

```console
docker buildx bake --load --set static-builder.args.PHP_EXTENSIONS=opcache,pdo_sqlite static-builder
# ...
```

See [the list of supported extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html).

### GitHub Token

If you hit the GitHub API rate limit, set a GitHub Personal Access Token in an environment variable named `GITHUB_TOKEN`:

```console
GITHUB_TOKEN="xxx" docker --load buildx bake static-builder
# ...
```

## macOS

Run the following command to create a static binary for macOS:

```console
git clone --depth=1 https://github.com/crazywhalecc/static-php-cli.git
cd static-php-cli
composer install --no-dev -a
./bin/spc doctor --auto-fix
./bin/spc fetch --with-php=8.2 -A
./bin/spc build --enable-zts --build-embed --debug "bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu"
export CGO_CFLAGS="$(./buildroot/bin/php-config --includes | sed s#-I/#-I$PWD/buildroot/#g)"
export CGO_LDFLAGS="-framework CoreFoundation -framework SystemConfiguration $(./buildroot/bin/php-config --ldflags) $(./buildroot/bin/php-config --libs)"

git clone --depth=1 https://github.com/dunglas/frankenphp.git
cd frankenphp/caddy/frankenphp
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie"
```

See [the list of supported extensions](https://static-php-cli.zhamao.me/en/guide/extensions.html).
11 changes: 8 additions & 3 deletions frankenphp.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ package frankenphp
// Use PHP includes corresponding to your PHP installation by running:
//
// export CGO_CFLAGS=$(php-config --includes)
// export CGO_LDFLAGS=$(php-config --ldflags --libs)
//
// We also set these flags for hardening: https://github.com/docker-library/php/blob/master/8.2/bookworm/zts/Dockerfile#L57-L59

// #cgo pkg-config: libxml-2.0 sqlite3
// #cgo CFLAGS: -Wall -Werror
// #cgo darwin pkg-config: libxml-2.0 sqlite3
// #cgo CFLAGS: -Wall -Werror -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
// #cgo CFLAGS: -I/usr/local/include/php -I/usr/local/include/php/main -I/usr/local/include/php/TSRM -I/usr/local/include/php/Zend -I/usr/local/include/php/ext -I/usr/local/include/php/ext/date/lib
// #cgo linux CFLAGS: -D_GNU_SOURCE
// #cgo CPPFLAGS: -fstack-protector-strong -fpic -fpie -O2 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64
// #cgo darwin LDFLAGS: -L/opt/homebrew/opt/libiconv/lib -liconv
// #cgo LDFLAGS: -L/usr/local/lib -L/usr/lib -lphp -lresolv -ldl -lm -lutil
// #cgo linux LDFLAGS: -Wl,-O1
// #cgo LDFLAGS: -pie -L/usr/local/lib -L/usr/lib -lphp -lresolv -ldl -lm -lutil
// #include <stdlib.h>
// #include <stdint.h>
// #include <php_variables.h>
Expand Down
79 changes: 79 additions & 0 deletions static-builder.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# syntax=docker/dockerfile:1
FROM golang-base

ARG FRANKENPHP_VERSION='dev'
ARG PHP_VERSION='8.2'
ARG PHP_EXTENSIONS='bcmath,calendar,ctype,curl,dba,dom,exif,filter,fileinfo,gd,iconv,intl,mbstring,mbregex,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,readline,redis,session,simplexml,sockets,sqlite3,tokenizer,xml,xmlreader,xmlwriter,zip,zlib,apcu'

RUN apk update; \
apk add --no-cache \
autoconf \
automake \
bash \
binutils \
bison \
build-base \
cmake \
curl \
file \
flex \
g++ \
gcc \
git \
jq \
libgcc \
libstdc++ \
linux-headers \
m4 \
make \
php82 \
php82-common \
php82-dom \
php82-mbstring \
php82-openssl \
php82-pcntl \
php82-phar \
php82-posix \
php82-tokenizer \
php82-xml \
php82-xmlwriter \
pkgconfig \
wget \
xz ; \
ln -sf /usr/bin/php82 /usr/bin/php

# https://getcomposer.org/doc/03-cli.md#composer-allow-superuser
ENV COMPOSER_ALLOW_SUPERUSER=1
ENV PATH="${PATH}:/root/.composer/vendor/bin"

COPY --from=composer/composer:2-bin --link /composer /usr/bin/composer

WORKDIR /static-php-cli

RUN git clone --depth=1 https://github.com/crazywhalecc/static-php-cli . && \
composer install --no-cache --no-dev --classmap-authoritative

RUN --mount=type=secret,id=github-token GITHUB_TOKEN=$(cat /run/secrets/github-token) ./bin/spc download --with-php=$PHP_VERSION --all

RUN ./bin/spc build --build-embed --enable-zts --debug "$PHP_EXTENSIONS"

ENV PATH="/static-php-cli/buildroot/bin:/static-php-cli/buildroot/usr/bin:$PATH"

WORKDIR /go/src/app

COPY go.mod go.sum ./
RUN go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get

RUN mkdir caddy && cd caddy
COPY caddy/go.mod caddy/go.sum ./caddy/

RUN cd caddy && go mod graph | awk '{if ($1 !~ "@") print $2}' | xargs go get

COPY *.* ./
COPY caddy caddy
COPY C-Thread-Pool C-Thread-Pool

RUN cd caddy/frankenphp && \
CGO_CFLAGS="$(/static-php-cli/buildroot/bin/php-config --includes | sed s#-I/#-I/static-php-cli/buildroot/#g)" \
CGO_LDFLAGS="$(/static-php-cli/buildroot/bin/php-config --ldflags) $(/static-php-cli/buildroot/bin/php-config --libs | sed -e 's/-lgcc_s//g')" \
go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags -static-pie -s -w -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP $FRANKENPHP_VERSION Caddy'"