Search by

deburca / phorward

deburca

Offload PHP lint, phpcs, phpstan and phpunit runs to an SSH-reachable server, each in a throwaway sandboxed container.

0.2.0 2026-11-08 15:48 UTC

This package is auto-updated.

Last update: 2026-11-08 15:49:17 UTC


README

CI

Offload PHP lint, phpcs, phpstan and phpunit runs to an SSH-reachable Linux server. Each run happens in a throwaway, network-less, read-only, non-root container, so the machine you work on stays light and code you do not trust (for example model-generated code) never runs on it.

Status: early. The server-side image and test tools work, and the Composer client (bin/phorward) has config, run, doctor, server:install, verify and harness:install. The package is not published yet.

How it fits together

Part Where State
Image profiles profiles/<name>/ drupal-module works (PHP 8.3 and 8.5)
Server tools server/ phorward-run, build-image, smoke-test, hostile-test, access-test, authorized-key, parity-check, shard-tests.py
Client bin/phorward, src/ config, run, verify, doctor, server:install, harness:install
Harness templates templates/ opencode agents and commands and a retry loop on top of verify; see templates/README.md

A validation image has two stages: a base (PHP, Composer, lint tools, Drupal core with core-dev, a throwaway in-container MariaDB) that knows no module, and a thin module layer with that module's composer dependencies and patches. Everything is baked in, so runs work with --network none. The tree under test is mounted read-only at run time and never baked in.

Requirements (server)

  • Linux with Docker, or rootless Podman with its docker command available (the podman-docker package or a docker symlink to podman; the scripts call docker), plus rsync and python3
  • Network access while building images; none while running them
  • About 8 GB RAM for Kernel tests with MariaDB (--memory 6g)
  • x86_64 or arm64

The client

bin/phorward is being built. So far phorward config prints the resolved configuration and where each value came from; the settings, their scopes and the layering rules are in docs/configuration.md. phorward doctor checks the connection, the server and the image end to end and says how to fix what it finds. phorward server:install <admin-host> prints, and with --apply runs, the steps that set up the server side. phorward verify is the gate a change must pass before it is pushed (lint, standards, static analysis, the tests that match the change, and a security scan of the added lines); see docs/verify.md. phorward harness:install puts opencode agents, commands and a retry loop that use verify as their gate into the project (templates/README.md). It filters lint, coding-standard and CI failures; it does not show that the model's logic or tests are right, and a person still reviews both. phorward run sends the project to the server and reports; see docs/client.md for composer scripts aliases, exit statuses and report formats.

Documentation

Quick start server, client, first run
Using it in a project scripts aliases, exit statuses, reports, CI
phorward verify the gate a change must pass; the agent harness
Restricted SSH access the security guide: read it before exposing a server
Configuration, policy, profiles, protocol reference
Troubleshooting start with phorward doctor
Compatibility what stays stable; 0.1 is experimental

Try the server tools

server/build-image --module ~/path/to/module --php 8.3 --dev-package drupal/key
server/smoke-test --module ~/path/to/module
server/build-image --profile php-library --module ~/path/to/library   # a plain PHP project
server/parity-check --module ~/path/to/module --phases mariadb

phorward-run is the SSH forced command that runs the checks (protocol in docs/protocol.md). For a local trial, with a policy file or the defaults:

tar -C ~/path/to/module -cf - . | server/phorward-run run --protocol 1 --module mymodule --checks lint,phpcs
server/phorward-run info --protocol 1

Access is by one restricted SSH key per client (docs/ssh-access.md); server/access-test proves it can run phorward-run and nothing else. Limits are the server's, set in a policy file (docs/policy.md). After changing it, check it with server/hostile-test, which fills the run slots with hostile samples while timing the host's other services.

Builds are reproducible (base images pinned by digest, tools installed from lock files; see docs/image.md), and server/images lists and prunes the images they leave behind.

build-image builds an image from a profile (docs/profiles.md): drupal-module for a Drupal module, php-library for a plain PHP project. It reads the package name from composer.json and the project name from where the profile says (a Drupal module's *.info.yml, or the package name). The image records the mount path and name as labels (phorward.module-path, phorward.module-name), which the other tools read.

See docs/image.md for the design and measurements.

Tests

python3 -I -m unittest discover -s tests -v   # the server tools: shard-tests, phorward-run, profiles, authorized-key
composer install && vendor/bin/phpunit         # the PHP client: configuration loader and commands
shellcheck server/build-image server/smoke-test server/parity-check server/hostile-test server/access-test profiles/*/with-* profiles/*/phorward-check

GitHub Actions (.github/workflows/) runs these and composer validate on every push. A heavier workflow builds the images of both profiles from the fixtures in tests/fixtures/ and smoke-tests them, weekly and on demand, on x86_64 and arm64.

Licence

MIT. See LICENSE.