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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions .github/workflows/build-php-core.yml
Original file line numberDiff line numberDiff line change
Expand Up@@ -49,12 +49,45 @@ jobs:
curl -sSfLO "https://github.com/oras-project/oras/releases/download/v1.3.1/oras_1.3.1_linux_${ORAS_ARCH}.tar.gz"
tar -xzf "oras_1.3.1_linux_${ORAS_ARCH}.tar.gz" -C /usr/local/bin oras

- name: Build PHP
- uses: actions/setup-go@v6
with:
go-version: '1.26'

- name: Build phpup
run: make bin/phpup

# Run the PHP-core build via phpup. phpup docker-wraps
# builders/linux/build-php.sh unchanged and writes the resulting
# bundle.tar.zst + meta.json + bundle.tar.zst.sha256 into a
# project-relative output dir. The subsequent "Stage bundle for
# publish" step copies them to /tmp/ so "Push to GHCR",
# "Sign bundle", "Smoke test", and "Upload bundle artifact" keep
# reading from the same paths they did before this rewiring —
# preserving the digest job-output contract byte-for-byte.
- name: Build PHP core via phpup
env:
PHP_VERSION: ${{ inputs.version }}
OS: ${{ inputs.os }}
ARCH: ${{ inputs.arch }}
WORKSPACE: ${{ github.workspace }}
run: ./builders/linux/build-php.sh
TS: ${{ inputs.ts }}
PHPUP_OUT_DIR: ${{ github.workspace }}/build/php/${{ inputs.version }}-${{ inputs.os }}-${{ inputs.arch }}-${{ inputs.ts }}
run: |
./bin/phpup build php \
--php "$PHP_VERSION" \
--os "$OS" \
--arch "$ARCH" \
--ts "$TS" \
--registry oci-layout:./out/oci-layout \
--repo . \
--out-dir "$PHPUP_OUT_DIR"

- name: Stage bundle for publish
env:
PHPUP_OUT_DIR: ${{ github.workspace }}/build/php/${{ inputs.version }}-${{ inputs.os }}-${{ inputs.arch }}-${{ inputs.ts }}
run: |
cp "$PHPUP_OUT_DIR/bundle.tar.zst" /tmp/bundle.tar.zst
cp "$PHPUP_OUT_DIR/bundle.tar.zst.sha256" /tmp/bundle.tar.zst.sha256
cp "$PHPUP_OUT_DIR/meta.json" /tmp/meta.json

- name: Push to GHCR
id: push
Expand Down
8 changes: 8 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -11,6 +11,14 @@ bin/
dist/
cmd/phpup/bundles.lock

# Docker-wrapped build output (phpup build php|ext).
# Leading slash anchors to repo root so we don't accidentally shadow
# internal/build/ (the Go package) when adding new files to it.
/build/

# Default OCI-layout registry target (phpup build --registry flag).
/out/

# Dependencies
vendor/
node_modules/
Expand Down
64 changes: 46 additions & 18 deletions Makefile
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,10 +2,23 @@
build-linux-amd64 build-linux-arm64 bundle-php bundle-ext gc-bundles-dry-run \
local-ci

# Path to the native phpup binary used by bundle-php / bundle-ext. Overridable
# so CI / power users can point at a pre-built binary.
PHPUP_BIN ?= bin/phpup

# Ensure the embedded lockfile is available for go vet/test/build
cmd/phpup/bundles.lock: bundles.lock
@cp bundles.lock cmd/phpup/bundles.lock

# Local native build of phpup used by bundle-php / bundle-ext. Kept as a
# file target so Make only rebuilds on demand. The embedded lockfile is the
# sole declared dependency because cmd/phpup's own source files change
# rarely enough that a `make clean` + `make bin/phpup` is acceptable; if
# this becomes a friction point, widen the deps to cmd/phpup/*.go.
$(PHPUP_BIN): cmd/phpup/bundles.lock
@mkdir -p $(dir $(PHPUP_BIN))
go build -o $(PHPUP_BIN) ./cmd/phpup

# Full pre-push check: static analysis + tests + builds + a docker smoke that
# exercises the published bundles on both jammy and noble runners. Takes ~5
# minutes when the bundle caches are cold. Use check-fast for rapid iteration
Expand DownExpand Up@@ -83,24 +96,39 @@ build-linux-arm64:
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/phpup-linux-arm64 ./cmd/phpup
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build -o bin/planner-linux-arm64 ./cmd/planner

# Build a PHP core bundle locally via Docker
bundle-php:
docker run --rm \
-v $$(pwd):/workspace -w /workspace \
-e PHP_VERSION=$(PHP_VERSION) \
-e ARCH=$(or $(ARCH),x86_64) \
ubuntu:22.04 \
bash -c "apt-get update && apt-get install -y curl xz-utils && ./builders/linux/build-php.sh"

# Build an extension bundle locally via Docker
bundle-ext:
docker run --rm \
-v $$(pwd):/workspace -w /workspace \
-e EXT_NAME=$(EXT_NAME) \
-e EXT_VERSION=$(EXT_VERSION) \
-e PHP_ABI=$(PHP_ABI) \
ubuntu:22.04 \
bash -c "apt-get update && apt-get install -y curl && ./builders/linux/build-ext.sh"
# Build a PHP core bundle locally via phpup (docker-wrapped under the hood).
# Invocation:
# make bundle-php PHP_VERSION=8.4 [OS=jammy] [ARCH=x86_64] [TS=nts] \
# [REGISTRY=oci-layout:./out/oci-layout]
# phpup docker-wraps builders/linux/build-php.sh unchanged and writes the
# resulting OCI bundle into the target registry.
bundle-php: $(PHPUP_BIN)
$(PHPUP_BIN) build php \
--php $(PHP_VERSION) \
--os $(or $(OS),jammy) \
--arch $(or $(ARCH),x86_64) \
--ts $(or $(TS),nts) \
--registry $(or $(REGISTRY),oci-layout:./out/oci-layout) \
--repo .

# Build an extension bundle locally via phpup (docker-wrapped under the hood).
# Invocation:
# make bundle-ext EXT_NAME=redis EXT_VERSION=6.2.0 PHP_ABI=8.4-nts \
# PHP_CORE_DIGEST=sha256:… \
# [OS=jammy] [ARCH=x86_64] \
# [REGISTRY=oci-layout:./out/oci-layout]
# Requires the prerequisite php-core already in REGISTRY (run `make bundle-php`
# first, or point REGISTRY at a remote where the core is published).
bundle-ext: $(PHPUP_BIN)
$(PHPUP_BIN) build ext \
--ext $(EXT_NAME) \
--ext-version $(EXT_VERSION) \
--php-abi $(PHP_ABI) \
--arch $(or $(ARCH),x86_64) \
--os $(or $(OS),jammy) \
--php-core-digest $(PHP_CORE_DIGEST) \
--registry $(or $(REGISTRY),oci-layout:./out/oci-layout) \
--repo .

# Clean build artifacts
clean:
Expand Down
10 changes: 10 additions & 0 deletions cmd/phpup/main.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,7 @@ import (
"sort"
"strings"

"github.com/buildrush/setup-php/internal/build"
"github.com/buildrush/setup-php/internal/cache"
"github.com/buildrush/setup-php/internal/catalog"
"github.com/buildrush/setup-php/internal/compat"
Expand DownExpand Up@@ -54,6 +55,15 @@ func main() {
return
}

// `phpup build …` is dispatched before the setup-flow flag.Parse so the
// two argv universes never collide. build.Main uses its own FlagSet.
if len(os.Args) > 1 && os.Args[1] == "build" {
if err := build.Main(os.Args[2:]); err != nil {
log.Fatalf("%v", err)
}
return
}

registryFlag := flag.String("registry", "",
"OCI artifact store (e.g., ghcr.io/buildrush or oci-layout:./out/oci-layout). Overrides INPUT_REGISTRY / PHPUP_REGISTRY; defaults to ghcr.io/buildrush.")
flag.Parse()
Expand Down
44 changes: 20 additions & 24 deletions cmd/planner/main.go
Original file line numberDiff line numberDiff line change
Expand Up@@ -53,30 +53,28 @@ func main() {

// Hash builder scripts and shared support files the builders source. Changes
// to any of these change the bundle contents; fold them into builderHash so
// spec_hash invalidates and bundles rebuild.
builderHashPHP, err := planner.HashFile(filepath.Join("builders", "linux", "build-php.sh"))
// spec_hash invalidates and bundles rebuild. Ordering mirrors the historical
// inline concatenation (build-<kind>.sh + schema env + capture + pack) so
// spec_hash values stay byte-identical across this refactor.
common := []string{
filepath.Join("builders", "common", "bundle-schema-version.env"),
filepath.Join("builders", "common", "capture-hermetic-libs.sh"),
filepath.Join("builders", "common", "pack-bundle.sh"),
}
builderHashPHP, err := planner.HashFiles(append(
[]string{filepath.Join("builders", "linux", "build-php.sh")},
common...,
))
if err != nil {
log.Fatalf("hash php builder: %v", err)
}
builderHashExt, err := planner.HashFile(filepath.Join("builders", "linux", "build-ext.sh"))
builderHashExt, err := planner.HashFiles(append(
[]string{filepath.Join("builders", "linux", "build-ext.sh")},
common...,
))
if err != nil {
log.Fatalf("hash ext builder: %v", err)
}
schemaEnvHash, err := planner.HashFile(filepath.Join("builders", "common", "bundle-schema-version.env"))
if err != nil {
log.Fatalf("hash schema env: %v", err)
}
captureHash, err := planner.HashFile(filepath.Join("builders", "common", "capture-hermetic-libs.sh"))
if err != nil {
log.Fatalf("hash capture script: %v", err)
}
packHash, err := planner.HashFile(filepath.Join("builders", "common", "pack-bundle.sh"))
if err != nil {
log.Fatalf("hash pack-bundle script: %v", err)
}
commonHash := schemaEnvHash + ":" + captureHash + ":" + packHash
builderHashPHP = builderHashPHP + ":" + commonHash
builderHashExt = builderHashExt + ":" + commonHash

// Expand PHP matrix
phpCells := planner.ExpandPHPMatrix(cat.PHP)
Expand DownExpand Up@@ -134,13 +132,11 @@ func readBuilderOS(path string) (string, error) {
if err != nil {
return "", fmt.Errorf("read %s: %w", path, err)
}
for _, line := range strings.Split(string(data), "\n") {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "BUILDER_OS=") {
return strings.TrimPrefix(line, "BUILDER_OS="), nil
}
v := planner.ParseEnvValue(data, "BUILDER_OS")
if v == "" {
return "", fmt.Errorf("%s: BUILDER_OS not found", path)
}
return "", fmt.Errorf("%s: BUILDER_OS not found", path)
return v, nil
}

func filterExisting(ctx context.Context, cells []planner.MatrixCell, lf *lockfile.Lockfile, client *oci.Client) []planner.MatrixCell {
Expand Down
Loading
Loading