Skip to main content
fapost/foundation and fapost/support — and any Solution or Plugin you build — live in their own git repositories and are consumed by Core as versioned Composer dependencies. This page explains the two-lane setup and how to add a new package to it. The Core-owned shared packages use the fapost/* vendor and live under the fapost-lab organization. A Solution or Plugin you contribute is your own package: it uses your vendor (for example acme/hr-solution) and any git host you like. Everything below works the same regardless of vendor — nothing is hard-coded to fapost.

How it works

Each package is a standalone repository. During development its source is checked out into packages/<dir>, which the Core repository ignores (/packages is in .gitignore). The checkout directory name is free; the package’s Composer name is what matters. Two lanes coexist without ever editing composer.json per environment: composer.json and composer.lock always describe the production lane, so they are safe to commit. The local symlinks are created outside Composer’s dependency resolution, so they never leak into the lock file.

The linker

tools/dev-link-packages.php, exposed as the dev:link Composer script, replaces the Composer-installed copy of each package under vendor/ with a relative symlink to its local source. It is wired into post-install-cmd and post-update-cmd, so it runs automatically after every composer install and composer update. It auto-discovers packages by reading each packages/*/composer.json and linking vendor/<name> from that file’s name — so any vendor works with no changes to the linker. It is a no-op when packages/ is absent, which is why the same hook is safe in production and CI.

Set up an existing package for development

Clone the package into packages/ next to the others, then let Composer link it:
That creates vendor/fapost/foundation as a symlink to packages/fapost-foundation. A plain composer install re-creates the symlink on its own. From here, edits inside packages/fapost-foundation are picked up instantly — no composer update needed. The checkout is a normal git repository: branch, commit, and push it independently of Core.

Add a new package

Assume a new package <vendor>/<name>. For a Solution or Plugin this is your own vendor, not fapost. The steps are vendor- and host-agnostic.
1

Create the package repository

Scaffold packages/<dir> — any directory name — with its own composer.json ("name": "<vendor>/<name>", PSR-4 autoload, a license), initialise a git repository, push it, and tag the first release:
If the package builds on the platform contracts, require them by constraint in its own composer.json, for example "fapost/foundation": "^0.1".
A Solution or Plugin may depend on fapost/foundation. It must never depend on Core (App\…) directly.
2

Declare it in the root composer.json

Add a VCS repository and a version constraint. This is the only manual edit in Core, and it is what production resolves against:
Private repositories on a non-GitHub host may need Composer authentication configured through composer config or auth.json.
3

Resolve and link

Composer pins the tagged version into composer.lock from the VCS, and post-update-cmd runs dev:link for you, replacing vendor/<vendor>/<name> with a symlink to the local checkout. The linker needs no changes — it discovers the new package automatically from its composer.json name, whatever the vendor.

Publish a new version

Package changes reach production through tags, not the local checkout:
With 0.x versions, ^0.1 accepts 0.1.* only; bump the constraint to ^0.2 to adopt a new minor. Until you re-run composer update, Core keeps the version pinned in composer.lock — your local symlinked edits are visible to you but do not change what production installs.
Composer needs git or SSH access to each package repository to resolve it — the fapost-lab organization for the Core packages, and your own host for your Solution or Plugin. Make sure your key is authorised, or auth.json is set for private hosts, before running composer update.

Reference