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
22 changes: 22 additions & 0 deletions .github/actions/build-docs/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Build documentation and books
description: Validate both manuscripts and build the site with PDF and EPUB downloads.
runs:
using: composite
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: shivammathur/setup-php@v2
with:
php-version: '8.4'
coverage: none
- name: Install rendering dependencies
shell: bash
run: |
sudo apt-get update
sudo apt-get install -y libcairo2 libpango-1.0-0 libpangoft2-1.0-0 fonts-dejavu-core fonts-liberation
python -m venv book/.venv
book/.venv/bin/pip install -r book/build/requirements.txt
- name: Validate and build documentation
shell: bash
run: bash scripts/build-docs.sh
47 changes: 42 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
pull_request:
branches: [ main ]

permissions:
contents: read

jobs:
quality:
name: Quality (PHP ${{ matrix.php }})
Expand Down Expand Up @@ -72,15 +75,49 @@ jobs:
if-no-files-found: warn

docs:
name: Docs (mkdocs --strict)
name: Docs (site + bilingual books)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: ./.github/actions/build-docs
- uses: actions/upload-artifact@v4
with:
name: books
path: site/downloads/
if-no-files-found: error
- uses: actions/upload-pages-artifact@v4
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
with:
path: site/

pages:
name: Publish documentation
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs: [quality, browser, docs, guard]
runs-on: ubuntu-latest
concurrency:
group: github-pages
cancel-in-progress: false
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Skip a superseded main build
id: current
uses: actions/github-script@v7
with:
python-version: '3.12'
- run: pip install mkdocs-material
- run: mkdocs build --strict
script: |
const head = await github.rest.repos.getCommit({...context.repo, ref: 'main'});
return head.data.sha === context.sha;
- uses: actions/configure-pages@v5
if: steps.current.outputs.result == 'true'
- id: deployment
uses: actions/deploy-pages@v4
if: steps.current.outputs.result == 'true'

guard:
name: Pre-push safety guard
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ jobs:
exit 1
- name: Install the public release and exercise its installer
run: php scripts/check-package-install.php --published
- uses: ./.github/actions/build-docs
- name: Publish the verified release notes
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -78,3 +79,4 @@ jobs:
PY
gh release view "$RELEASE_TAG" >/dev/null 2>&1 ||
gh release create "$RELEASE_TAG" --verify-tag --title "LaraFly ${RELEASE_TAG#v}" --notes-file "$RUNNER_TEMP/release-notes.md"
gh release upload "$RELEASE_TAG" site/downloads/* --clobber
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@
[*PyFly by Example*](https://github.com/fireflyframework/fireflyframework-pyfly). It builds **Lumen**, the
wallet-and-ledger service in [`samples/lumen/`](samples/lumen/), from an empty directory into a secured,
event-driven, actuator-observed microservice, chapter by chapter — every listing drawn from that real project
(it boots and its tests pass against this framework version, `26.09.3`).
(its boot and test suite are verified in CI against the same framework source).

**[Download the book in English or Spanish, as PDF or EPUB](https://fireflyframework.github.io/fireflyframework-php/book/).**
The published editions include the single-package installation and bundled installer.

The book is **structurally complete and bilingual (English + Spanish)**: a quick start, **fifteen chapters**
across four parts — Foundations (DI, config, HTTP), Modelling & Persisting the Domain (repositories, DDD),
Expand Down
13 changes: 12 additions & 1 deletion book/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and `book/dist/` (the generated PDF/EPUB) are gitignored and never committed.
Only the *sources* — `book.yaml`, `build/*.py`, `theme/*.css`, `art/`,
`src/` (EN), `src-es/` (ES), `tests/` — are tracked.

**[Published PDF and EPUB editions (English + Español)](https://fireflyframework.github.io/fireflyframework-php/book/)**
are rebuilt with the MkDocs site after every successful `main` CI run. GitHub releases also carry the
books built from their tag. Each download set includes `SHA256SUMS` and a `build-info.json` source commit.

## One-time setup

The first build needs **network access** (to install the Python deps) and a
Expand All @@ -29,7 +33,7 @@ brew install cairo pango

`book/build/run.sh` sets `DYLD_FALLBACK_LIBRARY_PATH` to Homebrew's `lib/` so
WeasyPrint finds `libcairo`/`libpango` without any manual `export`. On Linux,
install the equivalent packages (e.g. `apt install libcairo2 libpango-1.0-0`)
install the equivalent packages (e.g. `apt install libcairo2 libpango-1.0-0 libpangoft2-1.0-0`)
and `run.sh`'s `DYLD_FALLBACK_LIBRARY_PATH` export is a no-op (Linux uses the
system loader path instead).

Expand All @@ -43,6 +47,13 @@ bash book/build/run.sh --config book.es.yaml # Spanish -> book/dist/larafly-by
`book/dist/` is created on demand and is gitignored — nobody commits a
generated PDF/EPUB.

To run the same pipeline as CI, including the book tests, PHP listing checks, both editions,
the PDF text-boundary check, the strict MkDocs build and `site/downloads/` packaging:

```bash
bash scripts/build-docs.sh
```

## Verifying PHP code listings

A fenced ` ```php ` block is linted with the real PHP CLI (`php -l`, via a temp
Expand Down
3 changes: 3 additions & 0 deletions book/build/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ pygments==2.20.0
pyyaml==6.0.3
pytest==9.1.1
cairosvg==2.9.0
mkdocs-material==9.7.6
mkdocs==1.6.1
pdfplumber==0.11.9
31 changes: 31 additions & 0 deletions book/build/verify_pdf.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Reject generated books whose text extends beyond the PDF page boundaries."""
from __future__ import annotations

import sys

import pdfplumber


def main(paths: list[str]) -> int:
failures = 0
for path in paths:
with pdfplumber.open(path) as document:
if not document.pages:
raise ValueError(f'{path}: no PDF pages found')
for number, page in enumerate(document.pages, 1):
# Read the content stream, including text wholly outside the MediaBox.
# Poppler's bounding-box output drops that text before it can be checked.
outside = [char['text'] for char in page.chars if char['text'].strip() and (
char['x0'] < -1 or char['top'] < -1
or char['x1'] > page.width + 1 or char['bottom'] > page.height + 1
)]
if outside:
print(f'FAIL {path}:{number}: off-page text {"".join(outside)!r}')
failures += 1
page.close()
print(f'{path}: checked {len(document.pages)} pages')
return 1 if failures else 0


if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:]))
2 changes: 1 addition & 1 deletion book/src-es/00-front/00-preface.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Los desarrolladores que llegan desde Spring Boot, Micronaut o Quarkus se sentir

Cada capítulo hace avanzar **Lumen**, un servicio de monedero digital y libro mayor: un `Wallet` puede abrirse, recibir depósitos, sufrir retiradas y transferirse a otros monederos, protegiendo un único invariante por encima de todos los demás — **el saldo nunca es negativo** — y registrando cada cambio de estado como un evento de dominio que un escuchador proyecta en un libro mayor de solo anexado. Es un sistema pequeño, pero tiene exactamente la forma de uno real: una capa de dominio sin dependencia alguna del framework, un puerto hexagonal y su adaptador Eloquent, manejadores de comando y consulta de CQRS, eventos de dominio conectados a un bus de eventos, seguridad a nivel de método en una operación sensible, y un controlador REST delgado que no contiene lógica de negocio propia.

El recorrido empieza con suavidad. El **Inicio rápido** te lleva desde un `composer create-project` vacío hasta un endpoint en ejecución y consultable con curl, previendo en miniatura los estereotipos, el contenedor y la ruta de arranque compilada antes de que ningún capítulo te pida razonar sobre ellos. El **Capítulo 1** da un paso atrás y argumenta el enfoque completo — qué problema resuelve LaraFly y sobre qué pilares se sostiene. El **Capítulo 2** abre la sala de máquinas: el contenedor de inyección de dependencias, los atributos de estereotipo y el escaneo de componentes que compila tus clases anotadas en un manifiesto de arranque en caché, sin reflexión. Las partes posteriores de este libro — que llegarán en los capítulos siguientes — construyen hacia afuera desde esa base hacia la configuración, HTTP, la persistencia, el modelado de dominio, CQRS, la arquitectura orientada a eventos, la seguridad y la observabilidad, siempre a través de la misma base de código de `Lumen`, siempre con código que puedes ejecutar.
El recorrido empieza con suavidad. El **Inicio rápido** te lleva desde el instalador incluido `firefly new` hasta un endpoint en ejecución y consultable con curl, previendo en miniatura los estereotipos, el contenedor y la ruta de arranque compilada antes de que ningún capítulo te pida razonar sobre ellos. El **Capítulo 1** da un paso atrás y argumenta el enfoque completo — qué problema resuelve LaraFly y sobre qué pilares se sostiene. El **Capítulo 2** abre la sala de máquinas: el contenedor de inyección de dependencias, los atributos de estereotipo y el escaneo de componentes que compila tus clases anotadas en un manifiesto de arranque en caché, sin reflexión. Las partes posteriores de este libro — que llegarán en los capítulos siguientes — construyen hacia afuera desde esa base hacia la configuración, HTTP, la persistencia, el modelado de dominio, CQRS, la arquitectura orientada a eventos, la seguridad y la observabilidad, siempre a través de la misma base de código de `Lumen`, siempre con código que puedes ejecutar.

Cuando hayas terminado de trabajar todo el libro, tendrás un modelo mental de cada capa de un servicio LaraFly de producción, y una aplicación real y probada que lo demuestre.

Expand Down
Loading
Loading