mades/summer-craft-skeleton

Starting point for an application built on the summer-craft framework and hubs

Maintainers

Package info

github.com/mades/summer-craft-skeleton

Type:project

pkg:composer/mades/summer-craft-skeleton

Transparency log

Statistics

Installs: 4

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

0.1.1 2026-08-12 02:14 UTC

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