Skip to content

Latest commit

 

History

History
63 lines (53 loc) · 7.79 KB

AGENTS.md

File metadata and controls

63 lines (53 loc) · 7.79 KB

AGENTS.md

Scope and source of truth

  • Honor ignore/control files first: respect .gitignore, .aiignore, and other *ignore rules (for example .dockerignore) when selecting files to read, modify, or generate unless the user explicitly overrides this.
  • This repository builds and publishes custom PHP container images to GHCR (ghcr.io/toshy/php) and publishes docs to GitHub Pages.
  • Existing AI guidance discovered from the requested glob search: README.md only.
  • Treat build/workflow/doc config files below as canonical behavior.

Architecture map (read in this order)

  • docker-bake.hcl is the release matrix brain: PHP versions, flavor/OS compatibility, targets, tags, and OCI labels are generated here.
  • Dockerfile defines 5 stages: common -> base, ffmpeg, otel, otel-ffmpeg.
  • common installs shared binaries/extensions; each target stage adds only its delta.
  • Build context php-base is injected from upstream php:<version>-<flavor>-<os> via bake contexts (not a local FROM alias).
  • Target names may contain - (for example otel-ffmpeg); workflow parsing in release.yml derives TARGETS directly from bake metadata (.target[].target | unique) and strips suffixes longest-first — docker-bake.hcl remains the single source of truth for the target list.

Image and tag model you must preserve

  • Tag pattern is <version>-<flavor>-<os>(-<target>); base is default and has no -base suffix.
  • latest is emitted only for default tuple (8.5 + fpm + trixie + base) in docker-bake.hcl.
  • Short tags (for example fpm-trixie-ffmpeg) are emitted only for the default PHP version.
  • Allowed flavor/OS pairs are constrained by FLAVOR_OS_MAP; avoid introducing unsupported combinations.

CI/CD data flow

  • release.yml pipeline is prepare -> build -> merge.
  • prepare resolves concrete upstream patch versions with skopeo inspect docker://docker.io/library/php:<minor> and computes the bake matrix.
  • build runs per-platform builds and pushes by digest.
  • merge assembles multi-arch manifest lists and assigns final tags from bake metadata.
  • security.yml performs scheduled Trivy scans of published GHCR tags (CRITICAL, ignore unfixed).
  • docs.yml builds docs in a container and deploys /site to GitHub Pages on main pushes.

Critical local workflows

  • List tasks: task --list.
  • Build docs: task docs; live docs: task docs:live p=8001.
  • Inspect generated bake matrix/tags before CI edits: task bake:print pv="8.2.30,8.3.30,8.4.17,8.5.2".
  • Local amd64 bake run: task bake pv="8.2.30,8.3.30,8.4.17,8.5.2".
  • Local target vulnerability scan: task trivy pv="8.5.2-fpm-trixie" t="ffmpeg".
  • Workflow simulation uses act tasks in Taskfile.yml (act:push, act:job:prepare, act:job:build, act:job:merge).

Project-specific conventions

  • Keep docs and build config synchronized: if extensions/packages/stages change in Dockerfile, update docs/images.md and docs/usage.md examples.
  • Default OS is trixie; docs/usage.md still references bookworm as deprecated context.
  • Docs tooling is containerized (docker.io/zensical/zensical:0.0); avoid host-only assumptions.
  • Dependency/security automation is GitHub-native (.github/dependabot.yml and scheduled workflows).

Change checklist for agents

  • If changing defaults (DEFAULT_*, PHP_VERSIONS, targets), update docker-bake.hcl, .github/workflows/release.yml, and docs together.
  • If changing target contents, verify tag examples in docs still match real output from task bake:print.
  • Prefer validating matrix/tag behavior locally before modifying workflow logic.

Release pipeline gotchas (must preserve)

  • docker-bake.hcl is the single source of truth for targets. The prepare job in release.yml derives TARGETS from bake metadata via [.target[].target] | unique — do NOT reintroduce a hardcoded TARGETS env list.
  • The build job is fully metadata-driven: a Derive bake args step builds targets: and per-target cache-from/cache-to overrides from needs.prepare.outputs.targets, and digests are uploaded as a single bundled artifact digest--<variant>--<platform> containing the full <target>/<sha> tree. The merge job downloads the bundle and cds into /tmp/digest/${{ matrix.target }}. Adding a target requires no edits to release.yml.
  • Bake target naming convention is php-<ver-with-dashes>-<flavor>-<os>-<target> and <target> may itself contain - (for example otel-ffmpeg). Always strip the target suffix using the metadata-derived list with a longest-first regex; never use positional split("-") indexes on the full target name.
  • Per-platform digest artifacts use a -- (double-hyphen) delimiter: digest--<variant>--<platform>. The merge job's actions/download-artifact pattern (digest--<variant>--*) relies on this so variant boundaries are unambiguous. Do not introduce a variant or platform name containing --.
  • When adding a new target: update the Dockerfile stage and docker-bake.hcl TARGETS. The release.yml workflow picks it up automatically. Then update security.yml scan matrix and docs (docs/usage.md, docs/images.md).
  • docker-bake.hcl declares the desired platforms (linux/amd64, linux/386, linux/arm/v7, linux/arm64), but upstream php:<version>-<flavor>-<os> images do not always publish all of them: an arch disappears from the manifest list when its Debian base image is missing for that build (see docker-library/php#1683 — e.g. php:8.5.10-zts-trixie has no linux/386 while php:8.5.10-cli-trixie does). The prepare job therefore inspects each variant's php-base manifest with skopeo inspect --raw, intersects it with the bake platforms and emits build_matrix (an explicit {variant, platform} include-matrix). Missing platforms are skipped with a ::warning:: instead of failing the run with no match for platform in manifest. Keep the build job on matrix.include — do NOT go back to a full variant x platform cross product.
  • merge stays a variant x target matrix (a manifest list is platform-agnostic and is assembled from whatever digests build uploaded), but its variant axis comes from build_variants (unique variants of build_matrix), not variants. A variant with zero available platforms is skipped everywhere instead of producing a merge job that fails on missing digest artifacts.

Trivy CRITICALs on published images (stale base packages)

  • Symptom: task trivy:remote (or security.yml) reports fixed CRITICALs (e.g. ffmpeg/libav*, libgnutls30t64) on some PHP lines (commonly older ones like 8.2/8.3) but not others (8.4/8.5).
  • Root cause is NOT the Dockerfile. The prepare job in release.yml (Check if PHP images already exists) skips rebuilding any version whose resolved patch tag already exists, unless force=true. So an image is only rebuilt when the PHP patch version bumps — Debian base/security package updates (gnutls from the base image, ffmpeg from the ffmpeg/otel-ffmpeg apt install) do NOT trigger a rebuild while PHP stays the same. Lines parked on an old patch ship stale packages.
  • Distinguish real findings from scanner false positives by comparing versions: if installed < fixed (e.g. gnutls u2 < u4, ffmpeg 7.1.3 < 7.1.4) the finding is real and a rebuild fixes it. Debian backports fixes without bumping the upstream version, so a scanner flagging an already-patched build (installed == fixed) is the false positive — do NOT add those to a .trivyignore.
  • Fix: rebuild and republish the affected lines (Release workflow via workflow_dispatch with the PHP version and force: true). A fresh rebuild pulls the updated base (gnutls) and re-runs apt install (ffmpeg). Do NOT add apt-get upgrade to the Dockerfile — it fights the curated base image and the fix belongs in the base rebuild.