mades / summer-craft-skeleton
Starting point for an application built on the summer-craft framework and hubs
Package info
github.com/mades/summer-craft-skeleton
Type:project
pkg:composer/mades/summer-craft-skeleton
Requires
- php: >=8.4
- ext-yaml: *
- mades/summer-craft-core: ^0.1
- mades/summer-craft-service-hub: ^0.1
This package is auto-updated.
Last update: 2026-08-12 02:24:13 UTC
README
Starting point for an application built on the summer-craft framework and hubs. It carries the boilerplate every such application needs — entry points, boot, config loaders, the Docker stack — and nothing else.
Boundaries
What belongs here: what every new application starts with and then owns — entry
points, boot wiring, config loaders, the Docker stack. Nothing that would need
updating later, because a create-project template is copied once and never
updated again.
What does not: anything reusable. If a fix would be worth delivering to existing
applications too, it belongs in a hub, where composer update carries it. That is
why boot itself is not here but in the core's AppBootstrap — this repository only
calls it.
Creating an application
Docker is the only requirement — no PHP and no composer on the host. The hubs are ordinary composer dependencies, so the application can be created anywhere:
docker run --rm -u "$(id -u):$(id -g)" -e COMPOSER_HOME=/tmp -v "$PWD":/app -w /app \ composer:2 create-project mades/summer-craft-skeleton myapp --ignore-platform-req=ext-yaml cd myapp make init # .env and config/app.yaml from the examples, with your uid/gid make base # the shared runtime image, once per machine make start
With composer installed on the host the first command is just
composer create-project mades/summer-craft-skeleton myapp --ignore-platform-req=ext-yaml.
That flag is needed either way. ext-yaml is required by the application, not by
whatever creates it — the config is read from config/app.yaml at runtime, and the
runtime is the container — but create-project checks the machine it runs on, and
no setting inside this package can tell it otherwise. Drop the flag if that machine
has the extension.
From then on nothing needs it: make composer runs composer inside the base
image, which carries the same PHP and extensions the application runs on.
config.platform in composer.json is there only for the case of running composer
on the host anyway — it pins the base image's PHP and yaml versions so the resolve
still targets the container. Update those two values if you change the base image.
The application answers on APP_HTTP_PORT (8080 by default) by Host header. Ask
it for the demo module rather than / — nothing is routed at the root until you
route something there:
curl -H "Host: app.local" http://localhost:8080/demo
Change COMPOSE_PROJECT_NAME and the ports in .env when running
several applications at once — the ports collide, the names alone do not.
The demo module
src/app/Module/Demo/ is a working example of both entry points, wired the way
a real module is. Delete it once you have a module of your own — nothing
else refers to it.
Over HTTP, with the stack running (make start):
curl -H "Host: app.local" http://localhost:8080/demo curl -H "Host: app.local" http://localhost:8080/demo/hello/yourname
The first segment after /demo becomes the method name (helloAction), the
rest become its arguments — that is ControllerRoutingResolver::camelBased()
in Demo/Loader.php, and defaultAction() answers the bare /demo.
From the command line, inside the running container:
make shell php src/boot/cli.php handle/App/Module/Demo/Handler/GreetCliHandler yourname
or without opening a shell first:
docker compose exec php-apache-dev php src/boot/cli.php handle/App/Module/Demo/Handler/GreetCliHandler
The handler is addressed by its class name, spelled with slashes. Only classes
implementing SummerCraft\Core\Cli\CliHandler can be reached this way, however
the argument is spelled. The route is registered ->forMethod('CLI'), so the
same path over HTTP is a 404 — drop that filter and it would not be.
Layout
| Path | |
|---|---|
public_html/index.php |
HTTP entry point |
src/boot/{http,cli,loader}.php |
entry points and the one file that is yours: base path and config loader |
src/app/Config/*ConfigLoader.php |
what this application configures on top of the hubs |
src/app/Module/Root.php |
marks the directory the module loader scans |
src/app/Module/Demo/ |
example module: one controller, one CLI handler — delete it |
.env |
environment: the dev stack and the application read the same file |
config/app.yaml |
the application's own structured config |
compose.yaml |
the dev stack, at the root it mounts |
Dockerfile |
this application's image: dev and prod targets |
docker/ |
Apache, supervisord and xdebug configuration |
docker/base.Dockerfile |
the shared runtime image, built by make base |
Both config files live at the root rather than under docker/: they
belong to the application, not to one particular stack, and the application must
keep working if that stack is ever replaced. Neither is required — without
them the application falls back to the process environment, which is how the
production image runs, since neither file is copied into it.
compose.yaml is at the root for the same reason it reads .env from there:
compose resolves its relative paths against the file's own directory, and what
this stack mounts is the application root. Keeping the two together is why
docker compose needs no flags here. What stays under docker/ is the
stack's own configuration files.
The boot procedure itself is not here — it lives in the framework's
AppBootstrap, so fixes to it arrive with a composer update rather than
having to be copied into every application that was ever generated.
Adding this application's own dependencies
Composer packages go through the base image, so the host stays free of PHP:
make composer ARGS="require monolog/monolog"
make composer ARGS=update
System packages go in the app stage of Dockerfile — shared by both targets —
and never in docker/base.Dockerfile: that image is shared between applications,
and anything added there is paid for by every one of them. PHP extensions the
application needs beyond the hubs' go in its composer.json as well, so the
requirement is declared and not merely installed.
Production image
make image-prod
That builds the prod target of the same Dockerfile the dev stack builds —
one definition, so a change to the Apache configuration cannot reach one and
miss the other. vendor/ is deliberately not copied in: the image runs
composer install itself, so what it carries is resolved from the lock file
rather than from whatever a developer machine happened to have.
Working on a hub from an application
When a hub needs editing alongside the application, add a path repository for it and require the branch:
"repositories": [{ "type": "path", "url": "../summer-craft-core" }], "require": { "mades/summer-craft-core": "dev-master" }
Composer symlinks the checkout into vendor/, so the hub is edited in place. The
dev stack then has to mount it as a sibling — /var/www/summer-craft-core beside
/var/www/html — for ../summer-craft-core to mean the same thing inside the
container. Undo both once the change is released; the default here is the
registry.
Neighbouring packages
- summer-craft-core — DI, routing, PSR-7, events. Required.
- summer-craft-service-hub — database, logging, mail, HTTP client, files. Required.