Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
* text=auto eol=lf
/.github export-ignore
/.gitignore export-ignore
/.gitattributes export-ignore
/tests export-ignore
/samples export-ignore
/docs export-ignore
/book export-ignore
/scripts export-ignore
/phpunit.xml.dist export-ignore
/phpstan.neon.dist export-ignore
/pint.json export-ignore
/deptrac.yaml export-ignore
/monorepo-builder.php export-ignore
/package.json export-ignore
/package-lock.json export-ignore
/testbench.yaml export-ignore
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ jobs:
tools: composer:v2
- name: Install dependencies
run: composer install --no-interaction --prefer-dist
- name: Package metadata
run: |
composer validate --strict
composer mono-validate
- name: Install exported library in a clean consumer
run: composer test:package
- name: Pint (code style)
run: composer pint-test
- name: PHPStan (level max)
Expand Down
112 changes: 19 additions & 93 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,107 +1,33 @@
name: Release (split mirrors)
name: Release (single package)

# Dormant until a v* tag is pushed by the controller (see docs/publishing.md). Pushes NOTHING on
# every other event — no branches:, no pull_request: trigger.
#
# The split faithfully mirrors the TAGGED commit's composer.json. Per docs/publishing.md, the
# release-time `vendor/bin/monorepo-builder bump-interdependency <version>` runs on the release
# commit BEFORE it is tagged, rewriting every `firefly/xyz: "*@dev"` sibling constraint to
# `^26.07` — so by the time this workflow runs (triggered by the tag push), the commit being split
# already carries `^26.07` interdependencies, not `*@dev`. Each mirror is therefore
# stable-installable on its own via Packagist.
# Packagist reads tags from this repository's root composer.json.
# This gate validates the tagged distribution; indexing is checked separately (docs/publishing.md).
on:
push:
tags:
- 'v*'

jobs:
preflight:
name: Preflight (release credentials)
runs-on: ubuntu-latest
steps:
# The split action below exits 0 even when its push fails, so without this gate a release with no
# credential produces a whole matrix of green jobs and zero published packages. Fail here instead, once,
# loudly.
- name: ACCESS_TOKEN must be present
env:
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
if [ -z "${ACCESS_TOKEN}" ]; then
echo "::error::ACCESS_TOKEN is not set, so nothing can be pushed to the mirrors."
echo "::error::Create the org PAT and the fireflyframework/firefly-* repositories first — see docs/publishing.md steps 6-7."
exit 1
fi
echo "ACCESS_TOKEN is present."
permissions:
contents: read

split:
needs: preflight
name: Split ${{ matrix.package.local }}
jobs:
package:
name: Validate package (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package:
- { local: packages/kernel, split: firefly-kernel }
- { local: packages/container, split: firefly-container }
- { local: packages/config, split: firefly-config }
- { local: packages/context, split: firefly-context }
- { local: packages/autoconfigure, split: firefly-autoconfigure }
- { local: packages/validation, split: firefly-validation }
- { local: packages/web, split: firefly-web }
- { local: packages/resilience, split: firefly-resilience }
- { local: packages/scheduling, split: firefly-scheduling }
- { local: packages/scheduling-postgres, split: firefly-scheduling-postgres }
- { local: packages/domain, split: firefly-domain }
- { local: packages/data, split: firefly-data }
- { local: packages/eda, split: firefly-eda }
- { local: packages/eda-kafka, split: firefly-eda-kafka }
- { local: packages/eda-postgres, split: firefly-eda-postgres }
- { local: packages/eda-rabbitmq, split: firefly-eda-rabbitmq }
- { local: packages/messaging, split: firefly-messaging }
- { local: packages/cqrs, split: firefly-cqrs }
- { local: packages/security, split: firefly-security }
- { local: packages/security-oauth2-client, split: firefly-security-oauth2-client }
- { local: packages/security-oauth2-server, split: firefly-security-oauth2-server }
- { local: packages/actuator, split: firefly-actuator }
- { local: packages/observability, split: firefly-observability }
- { local: packages/admin, split: firefly-admin }
- { local: packages/openapi, split: firefly-openapi }
- { local: packages/testing, split: firefly-testing }
- { local: packages/cli, split: firefly-cli }
- { local: packages/firefly, split: firefly-firefly }
- { local: packages/installer, split: firefly-installer }
- { local: skeleton, split: firefly-skeleton }
php: [ '8.3', '8.4', '8.5' ]
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
fetch-depth: 0
- name: Guard (never split a tree with sensitive paths)
run: bash scripts/check-no-sensitive-tracked.sh
- name: Subtree split to the read-only mirror
uses: symplify/monorepo-split-github-action@v2.3.0
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
with:
tag: ${{ github.ref_name }}
package-directory: ${{ matrix.package.local }}
split-repository-organization: fireflyframework
split-repository-name: ${{ matrix.package.split }}
user-name: fireflybot
user-email: bot@fireflyframework.dev
- name: Verify the mirror actually received the tag
# symplify/monorepo-split-github-action@v2.3.0 exits 0 whether or not the push succeeded: on
# v26.09.1 all 28 jobs went green while the mirrors did not exist and nothing was published.
# A release is only released once the tag is readable on the mirror, so assert exactly that.
env:
GH_TOKEN: ${{ secrets.ACCESS_TOKEN }}
run: |
set -euo pipefail
repo="fireflyframework/${{ matrix.package.split }}"
tag="${{ github.ref_name }}"
sha="$(gh api "repos/${repo}/git/ref/tags/${tag}" --jq '.object.sha' 2>/dev/null || true)"
if [ -z "${sha}" ]; then
echo "::error::${repo} has no tag ${tag}. The split step reported success but published nothing."
echo "::error::Usual cause: the mirror repository does not exist, or ACCESS_TOKEN cannot write to it."
exit 1
fi
echo "${repo} @ ${tag} = ${sha}"
php-version: ${{ matrix.php }}
coverage: none
tools: composer:v2
- run: bash scripts/check-no-sensitive-tracked.sh
- run: composer validate --strict
- run: composer install --no-interaction --prefer-dist
- run: composer check
- run: composer mono-validate
- run: composer test:package
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ All notable changes to LaraFly are documented here. This project uses CalVer (`Y

## [Unreleased]

### Changed

- Publish the repository root as the `firefly/firefly` library, replacing component names at the same
version. Runtime dependencies, autoloading and Laravel discovery now belong to that package; no split
mirrors or cross-repository release credential are needed.
- Keep Lumen and component test support in development autoloading, and validate exported and copied
consumer installations in CI. The installer uses the bundled skeleton and adds Testbench explicitly
when the testing capability is requested. PostgreSQL outbox migrations are registered only for that
configured transport.

## [26.09.8] - 2026-09-25

### Fixed
Expand Down
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ will have — that they are worth naming outright:

```bash
# 1 · Install the global installer once, then scaffold a new app
composer global require firefly/installer
composer global require firefly/firefly
firefly new my-app
cd my-app

Expand Down Expand Up @@ -986,26 +986,27 @@ LaraFly layers onto Laravel, it is not a standalone runtime. Full details in
**New project — the global installer:**

```bash
composer global require firefly/installer
composer global require firefly/firefly
firefly new my-app
```

**New project — without the installer** (exactly what `firefly new` wraps):
**New project — bundled installer:**

```bash
composer create-project firefly/skeleton my-app
composer global require firefly/firefly
firefly new my-app
```

**Adding LaraFly to an existing Laravel app** — `firefly/firefly` is a `type: metapackage` (the Maven BOM
analogue) that pulls in the whole runtime family, developer console included, with one line:
**Adding LaraFly to an existing Laravel app** — `firefly/firefly` is the complete framework library,
developer console included:

```bash
composer require firefly/firefly
```

The browser dashboard (`firefly/admin`) and the API-documentation package (`firefly/openapi`) come with it. The
broker adapters (`firefly/eda-rabbitmq`, `firefly/eda-postgres`, `firefly/eda-kafka`) and the test kit
(`firefly/testing`) stay separate — each binds you to an infrastructure choice or belongs in `require-dev`.
broker adapters and testing helpers are included too. Configure the transport you need; install its
required PHP extension separately. Applications using the test kit add Testbench with `--dev`.

```bash
composer require firefly/admin # /firefly — the dashboard over the actuator (see its access model first)
Expand Down Expand Up @@ -1055,9 +1056,9 @@ Full flag reference and generated-file contents: [CLI](docs/cli.md).

## Modules

29 packages under `packages/*`, plus `firefly/skeleton` at the top level — 30 shippable units in total. Each
is an independently installable Composer package with its own test suite; the one exception is
`firefly/firefly`, a `type: metapackage` that carries a dependency list and nothing else. The 32
One published library, `firefly/firefly`, contains the components under `packages/*` and the bundled
application skeleton. Its `replace` entries satisfy component requirements at the framework's version;
component names remain useful module boundaries, with their own test suites. The 32
[module guides](docs/modules/) below group them by concern:

| Group | Module | Package(s) |
Expand Down Expand Up @@ -1095,9 +1096,8 @@ is an independently installable Composer package with its own test suite; the on
| Testing | [Integration Testing](docs/modules/integration-testing.md) — `@group integration`, testcontainers | `firefly/testing` |
| Tooling | [Installer](docs/modules/installer.md) — the global `firefly new` scaffolding tool | `firefly/installer` |

`firefly/firefly` (the runtime metapackage) and `firefly/cli` (the dev-console — see
[CLI & Project Scaffolding](#cli--project-scaffolding) above) round out the 29 packages; `firefly/skeleton`
is the 30th unit, a `type: project` create-project template at the top level.
`firefly/cli` supplies the developer console — see [CLI & Project Scaffolding](#cli--project-scaffolding).
All component names resolve through the root library; the top-level `skeleton/` template is bundled with it.

---

Expand Down Expand Up @@ -1191,15 +1191,16 @@ model across every runtime:

## Contributing

`fireflyframework-php` is a single monorepo housing every LaraFly package as an independent Composer unit
under `packages/*`, plus `firefly/skeleton` at the top level. Before opening a PR, the full gate must pass:
`fireflyframework-php` publishes its root as one library. Component descriptors under `packages/*`
preserve module boundaries; `skeleton/` is the bundled application template. Before opening a PR, the full gate must pass:

```bash
composer install
git config core.hooksPath scripts/hooks # activates the committed pre-push safety guard

composer check # pint --test && phpstan analyse && pest && deptrac analyse
composer mono-validate # validates every packages/*/composer.json
composer mono-validate # component dependency-version consistency
composer test:package # exported and copied consumer installations
.venv-docs/bin/mkdocs build --strict # docs/: link integrity, no orphaned pages
bash scripts/check-no-sensitive-tracked.sh
```
Expand Down
7 changes: 6 additions & 1 deletion book/src-es/13-cli-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,12 @@ final class NewCommand extends Command
$io = new SymfonyStyle($input, $output);
$runner = $this->runner ?? new SymfonyProcessRunner($output);
// …
$create = ['composer', 'create-project', 'firefly/skeleton', $directory, '--no-interaction'];
$repository = json_encode([
'type' => 'path',
'url' => dirname(__DIR__, 3).'/skeleton',
'options' => ['symlink' => false, 'versions' => ['firefly/skeleton' => Version::VERSION]],
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
$create = ['composer', 'create-project', 'firefly/skeleton', $directory, '--no-interaction', '--repository='.$repository];
if ($input->getOption('dev')) {
$create[] = '--stability=dev';
}
Expand Down
7 changes: 6 additions & 1 deletion book/src/13-cli-cache.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,12 @@ final class NewCommand extends Command
$io = new SymfonyStyle($input, $output);
$runner = $this->runner ?? new SymfonyProcessRunner($output);
// …
$create = ['composer', 'create-project', 'firefly/skeleton', $directory, '--no-interaction'];
$repository = json_encode([
'type' => 'path',
'url' => dirname(__DIR__, 3).'/skeleton',
'options' => ['symlink' => false, 'versions' => ['firefly/skeleton' => Version::VERSION]],
], JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES);
$create = ['composer', 'create-project', 'firefly/skeleton', $directory, '--no-interaction', '--repository='.$repository];
if ($input->getOption('dev')) {
$create[] = '--stability=dev';
}
Expand Down
Loading
Loading