From 684620d173ccdbefe0b36daaaf10b9323e98af13 Mon Sep 17 00:00:00 2001 From: Yostin Vargas Date: Sun, 23 Aug 2026 21:44:03 -0400 Subject: [PATCH 1/2] docs: make forge the documented interface The installer signed off with 'docker compose up -d' -- the command forge exists to replace -- so forge help was never discovered. That line was in install_cert.sh, which runs last, not install.sh. Every docker compose instruction in both READMEs is now a forge command. The two mentions left are prose: the sentence saying forge wraps compose, and the one describing what CI validates. forge logs grew to make that possible. It followed one PHP container, so troubleshooting still pointed at docker compose logs -- naming a container is not something you can do before you know what broke. No argument now means every service; an argument can be a service or a version. Also realigns the two READMEs, which had drifted further than the heading list showed: English had no xdebug commands, Spanish was missing three troubleshooting entries. Both now carry the union in the same order. A lint step greps for the instructions coming back. --- .github/workflows/ci.yml | 27 +++++++++++ README.es.md | 81 +++++++++++++++++++++++---------- README.md | 97 ++++++++++++++++++++++++++++------------ TASKS.md | 28 +++++++++++- bin/forge | 22 +++++++-- install_cert.sh | 2 +- 6 files changed, 198 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3670fea..e309f70 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,6 +25,22 @@ jobs: docker-library/php/config_files/xdebug \ docker-library/httpd/config_files/docker-entrypoint.sh + # `forge` exists so the common commands are one word; a README that teaches + # `docker compose up` instead is how it stayed undiscovered until someone + # installed the project and never found `forge help`. Prose about what CI + # validates may still say `docker compose config` -- that is not an instruction. + - name: the READMEs do not teach docker compose + run: | + fail=0 + for pattern in 'docker compose up' 'docker compose logs' \ + 'docker compose stop' 'docker compose --profile' \ + 'docker compose build'; do + if grep -n "$pattern" README.md README.es.md; then + echo "^ use the forge command instead"; fail=1 + fi + done + exit $fail + - name: the helper scripts compile run: | python3 -m py_compile .github/scripts/drive-install.py @@ -141,6 +157,17 @@ jobs: # unknown versions must be refused, not passed to docker if forge use 9.9 2>/dev/null; then echo "9.9 should have been rejected"; exit 1; fi + # No argument means every service. Naming one narrows it -- a service, not + # just a PHP version, which is what sent the README to + # `docker compose logs` for troubleshooting. + timeout 5 forge logs > /tmp/all.log 2>&1 || true + grep -q apachedev /tmp/all.log || { echo "forge logs showed no apache"; exit 1; } + timeout 5 forge logs apachedev > /tmp/one.log 2>&1 || true + if grep -qE '^php8[0-9]dev' /tmp/one.log; then + echo "forge logs apachedev leaked other services"; exit 1 + fi + if forge logs nope 2>/dev/null; then echo "unknown service accepted"; exit 1; fi + - name: build images # Every PHP version is built. 8.5 broke silently once: opcache became # built-in and docker-php-ext-install started failing on it. diff --git a/README.es.md b/README.es.md index c757516..6880d5a 100644 --- a/README.es.md +++ b/README.es.md @@ -133,12 +133,14 @@ Y abre **https://welcome--sites.phpforge.dev**. 4. **Levanta el entorno:** ```bash - docker compose up -d + forge start ``` Comprueba que funciona: `https://welcome--sites.phpforge.dev` ## 💻 Uso +### ▶️ Arrancar y parar + ```bash forge start # levantar todo forge stop @@ -150,6 +152,11 @@ forge use 8.5 # cambiar la versión por defecto forge shell 8.4 # entrar a un contenedor forge logs 8.4 # seguir sus logs +forge profile list # servicios opcionales, y cuáles están encendidos +forge profile on|off # encender o apagar uno +forge db list # las bases de datos entre ellos +forge mail on|off # un buzón de pruebas en mail. + forge images build|pull # construir en local, o usar las publicadas forge certs # regenerar los certificados forge dns status # ver el DNS local @@ -162,6 +169,12 @@ enlaza en `~/.local/bin`. Los argumentos de versión aceptan `8.5` u `85`, y las versiones disponibles salen de `docker-compose.yml` — añade un servicio y `forge use 8.6` funciona solo. +Es un envoltorio de `docker compose`, no un reemplazo: no esconde nada y los +comandos crudos siguen funcionando desde la carpeta del proyecto. Existe para que +lo que haces a diario sea una palabra, y para que lo que tiene truco — un enlace +relativo que los contenedores puedan seguir, nginx y Apache sin pelearse por el +puerto 80 — salga bien sin que tengas que acordarte de por qué. + ### 🌐 Tus proyectos Viven en la carpeta que elegiste al instalar (`PROJECTS_DIR` en `.env`, @@ -216,6 +229,13 @@ ln -s ../projects/mi-app/public ~/php-devforge/sites/mi-app El enlace debe apuntar a la carpeta que contiene el `index.php` (el `public/` del framework). El `.htaccess` funciona: `AllowOverride All` está activado. +### 🔄 Día a día + +- Edita el código en tu editor — los archivos están montados, los cambios son inmediatos +- No hay que reiniciar nada por un cambio de código +- Entra a un contenedor con `forge shell 8.4` +- Mira qué está pasando con `forge logs` + ### 🐞 Xdebug Dentro del contenedor: @@ -235,12 +255,15 @@ del stack; el resto de tu DNS no se toca, así que apagar los contenedores nunca sin internet. ```bash -./setup-local-dns.sh # aplicar -./setup-local-dns.sh --status # ver la configuración -./setup-local-dns.sh --test # comprobar la resolución -./setup-local-dns.sh --remove # deshacer: borra un archivo +forge dns setup # aplicar +forge dns status # ver la configuración +forge dns test # comprobar la resolución +forge dns remove # deshacer: borra un archivo ``` +Llaman a `./setup-local-dns.sh`, que también puedes ejecutar directamente con las +mismas opciones como `--banderas`. + dnsmasq escucha en `127.0.0.1:${DNS_PORT}` en vez del puerto 53, que suele estar ocupado por systemd-resolved, Pi-hole o similar. El instalador busca uno libre y lo escribe en `.env`. @@ -264,7 +287,7 @@ upload_max_filesize = 100M ``` ```bash -docker compose up -d --force-recreate php84dev +forge restart php84dev ``` Se lee *además* de la configuración de la imagen, así que no pisa nada y el interruptor @@ -312,7 +335,8 @@ IMAGE_MODE=build # construir siempre en local IMAGE_MODE=always # volver a descargar, para forzar la última publicada ``` -Después, `docker compose up -d`. +Después, `forge start`. O sáltate la edición: `forge images build` y +`forge images pull` ponen esa línea y reinician por ti. Elige `build` si editas algo dentro de `docker-library/`: tus cambios se recogen solos, y la caché de Docker hace que no cueste nada cuando no hay cambios. @@ -360,11 +384,13 @@ Algunos no se levantan por defecto. Llevan un `profile` de compose, así que los cuando los quieras: ```bash -docker compose --profile search up -d # Elasticsearch + Kibana +forge profile list # qué existe, y qué está encendido +forge profile on search # Elasticsearch + Kibana +forge profile off search ``` -Los perfiles son cosa de compose, así que este sigue siendo un comando de -`docker compose`; `forge start` cubre el conjunto por defecto. +El listado lee los archivos de compose, así que se mantiene solo: cada fila muestra +las imágenes que arranca ese perfil. | Perfil | Servicios | Notas | |---|---|---| @@ -393,8 +419,8 @@ prefieres nginx, está disponible — como alternativa, no como añadido, porque quieren los puertos 80 y 443: ```bash -docker compose stop apachedev -docker compose --profile nginx up -d nginxdev +forge profile on nginx # para apachedev; no pueden compartir los puertos +forge profile off nginx # y lo devuelve ``` Sirve las mismas URLs, incluidas las rutas anidadas y el sufijo `--pNN`. @@ -420,21 +446,28 @@ dentro de un contenedor; tu editor debe escuchar en el puerto **9003**. ## 🐛 Problemas comunes -- **El DNS no resuelve**: ejecuta `./setup-local-dns.sh --status` y `--test`. Puede - hacer falta reiniciar el navegador. -- **El certificado no es de confianza**: vuelve a ejecutar `./install_cert.sh` y - reinicia el navegador. Firefox tiene su propio almacén en Linux: instala `nss` (Arch) - o `libnss3-tools` (Debian/Ubuntu) y repite. +- **El DNS no resuelve**: ejecuta `forge dns status` y `forge dns test`. Puede hacer + falta reiniciar el navegador. +- **El certificado no es de confianza**: vuelve a ejecutar `forge certs` y reinicia el + navegador. Firefox tiene su propio almacén en Linux: instala `nss` (Arch) o + `libnss3-tools` (Debian/Ubuntu) y repite. - **Los contenedores no arrancan**: comprueba que Docker esté corriendo y que los puertos 80 y 443 estén libres. -- **Problemas de permisos**: revisa que `PUID`/`PGID` en `.env` coincidan con los tuyos - (`id -u`, `id -g`) y recrea los contenedores. -- **No cambia la versión de PHP**: revisa `PHP_VERSION` en `.env` (83, 84 u 85), o añade - `--p83`/`--p84`/`--p85` al host. -- **`npm` muestra un aviso sobre pnpm**: es a propósito. npm sigue funcionando. +- **Problemas de permisos bajo `public_html`**: revisa que `PUID`/`PGID` en `.env` + coincidan con los tuyos (`id -u`, `id -g`) y haz `forge restart`. Los contenedores + adoptan esos ids al arrancar, así que lo que escriben es tuyo. +- **No cambia la versión de PHP**: revisa `PHP_VERSION` en `.env`, o añade + `--p83`/`--p84`/`--p85` al nombre del host. `forge status` muestra la de por defecto. +- **`npm` muestra un aviso sobre pnpm**: es a propósito. npm sigue funcionando; aquí se + prefiere pnpm. - **Un `.ini` de `custom/php.d/` parece ignorado**: recrea el contenedor con - `docker compose up -d --force-recreate php84dev`. Reiniciar no basta. -- **Los alias no funcionan**: asegúrate de haber hecho `source aliases.bash`. + `forge restart php84dev`. Reiniciar no basta. +- **`forge: command not found`**: el instalador lo enlaza en `~/.local/bin`, que + algunos shells no tienen en el `PATH`. Añádelo, o haz `source aliases.bash` desde la + carpeta del proyecto. + +Para más ayuda, mira los logs con `forge logs` — o `forge logs apachedev` para un +servicio — o abre un issue en GitHub. ## 🤝 Contribuir diff --git a/README.md b/README.md index feabb66..c6990e8 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ Then open **https://welcome--sites.phpforge.dev**. 4. **Start the containers:** ```bash - docker compose up -d + forge start ``` Or `forge start`, which works from anywhere. @@ -151,6 +151,11 @@ forge use 8.5 # set the default PHP version forge shell 8.4 # open a shell in a container forge logs 8.4 # follow its logs +forge profile list # optional services, and which are on +forge profile on|off # turn one on or off +forge db list # the databases among them +forge mail on|off # a mail catcher at mail. + forge images build|pull # build locally, or use the published images forge certs # regenerate the certificates forge dns status # inspect the local DNS @@ -162,6 +167,12 @@ forge help `~/.local/bin`. Version arguments accept `8.5` or `85`, and the available versions come from `docker-compose.yml` — add a service and `forge use 8.6` just works. +It is a wrapper around `docker compose`, not a replacement for it: nothing is +hidden and the raw commands still work from the project directory. It exists so +the things you do daily are one word, and so the ones with a trick to them — a +relative symlink the containers can follow, nginx and Apache not fighting over +port 80 — are right without you remembering why. + ### 🌐 Accessing Your Projects 1. **Create project structure:** @@ -224,10 +235,22 @@ come from `docker-compose.yml` — add a service and `forge use 8.6` just works. ### 🔄 Development Workflow -- Edit code in your IDE (files are volume-mounted for live updates) -- Changes appear immediately without restarting containers -- Use Xdebug for debugging (configure your IDE to listen on port **9003**, the Xdebug 3 default) +- Edit code in your editor — the files are volume-mounted, so changes are live +- Nothing to restart for a code change - Open a shell in a container with `forge shell 8.4` +- Watch what is happening with `forge logs` + +### 🐞 Xdebug + +Inside a container: + +```bash +xdebug # toggle it on or off +xdebug --force-activate +xdebug /path/script.php # run one script with it on, then turn it back off +``` + +Point your editor at port **9003**, the Xdebug 3 default. ## 🌐 Local DNS @@ -236,12 +259,15 @@ the stack's dnsmasq; the rest of your DNS is untouched, so stopping the containe never costs you internet access. ```bash -./setup-local-dns.sh # apply -./setup-local-dns.sh --status # show what is configured -./setup-local-dns.sh --test # check resolution -./setup-local-dns.sh --remove # undo: deletes one file +forge dns setup # apply +forge dns status # show what is configured +forge dns test # check resolution +forge dns remove # undo: deletes one file ``` +These call `./setup-local-dns.sh`, which you can also run directly with the same +options as `--flags`. + dnsmasq listens on `127.0.0.1:${DNS_PORT}` rather than port 53, which is usually taken by systemd-resolved, Pi-hole or similar. The installer picks a free port and writes it to `.env`; change it there if you need to. @@ -266,7 +292,7 @@ upload_max_filesize = 100M ``` ```bash -docker compose up -d --force-recreate php84dev +forge restart php84dev ``` It is scanned *in addition to* the image's own config, so nothing is shadowed and @@ -315,12 +341,13 @@ IMAGE_MODE=build # always build locally IMAGE_MODE=always # re-pull every time, to force the newest published image ``` -Then `docker compose up -d`. +Then `forge start`. Or skip the editing: `forge images build` and `forge images pull` +set that line and restart for you. Pick `build` if you edit anything under `docker-library/`: your changes are picked up automatically, and Docker's layer cache makes it cheap when nothing changed. -Either way `docker compose build` still builds on demand, and compose falls back -to building if an image cannot be pulled. +Either way an image still gets built on demand, and compose falls back to building +if one cannot be pulled. **Note:** settings baked in at build time — `NODE_VERSION`, for instance — only apply when you build. A pulled image already has them fixed. @@ -365,11 +392,13 @@ Some services are not started by default. They carry a compose `profile`, so you ask for them when you want them: ```bash -docker compose --profile search up -d # Elasticsearch + Kibana +forge profile list # what exists, and what is on +forge profile on search # Elasticsearch + Kibana +forge profile off search ``` -Profiles are a compose feature, so this one stays a `docker compose` command; -`forge start` covers the default set. +The listing reads the compose files, so it stays right on its own: each row shows +the images that profile starts. | Profile | Services | Notes | |---|---|---| @@ -398,8 +427,8 @@ would rather run nginx, it is there — as an alternative, not an addition, sinc want ports 80 and 443: ```bash -docker compose stop apachedev -docker compose --profile nginx up -d nginxdev +forge profile on nginx # stops apachedev; they cannot share the ports +forge profile off nginx # and brings it back ``` It serves the same URLs, including nested paths and the `--pNN` version suffix. @@ -425,18 +454,28 @@ container; your IDE should listen on port **9003**. ## 🐛 Troubleshooting -### ❓ Common Issues - -- **DNS resolution not working**: Ensure you ran `./setup-local-dns.sh` and restarted your browser or system. You may need to flush DNS cache. -- **PHP version not switching**: check `PHP_VERSION` in `.env` (83, 84 or 85), or append `--p83`/`--p84`/`--p85` to the host. `forge status` prints the default. -- **`npm` prints a message about pnpm**: intentional. npm still runs; pnpm is preferred here. -- **An `.ini` in `custom/php.d/` seems ignored**: recreate the container, `docker compose up -d --force-recreate php84dev`. A restart alone does not re-read it. -- **SSL certificate not trusted**: Make sure you ran `./install_cert.sh` and restart your browser afterwards. Check the CA is present with `openssl verify -CAfile .caroot/rootCA.pem certificates/php-devforge.pem`. Firefox keeps its own trust store on Linux, so install `nss` (Arch) or `libnss3-tools` (Debian/Ubuntu) and re-run the script if Firefox still complains. -- **Containers not starting**: Check that Docker and Docker Compose are installed and running. Ensure ports 80 and 443 are not in use by other services. -- **Permission issues with public_html**: Check that `PUID`/`PGID` in `.env` match your own ids (`id -u`, `id -g`), then recreate the containers with `docker compose up -d --force-recreate`. The containers adopt those ids on startup, so files they write are owned by you. -- **Aliases not working**: Ensure you sourced `aliases.bash` or added it to your `~/.bashrc`. - -For more help, check the logs with `docker compose logs` or create an issue on GitHub. +- **DNS not resolving**: run `forge dns status` and `forge dns test`. You may need to + restart your browser. +- **SSL certificate not trusted**: re-run `forge certs` and restart your browser. + Firefox keeps its own trust store on Linux: install `nss` (Arch) or `libnss3-tools` + (Debian/Ubuntu) and try again. +- **Containers not starting**: check Docker is running and that ports 80 and 443 are + free. +- **Permission issues under `public_html`**: check `PUID`/`PGID` in `.env` match your + own (`id -u`, `id -g`), then `forge restart`. The containers adopt those ids on + startup, so files they write belong to you. +- **PHP version not switching**: check `PHP_VERSION` in `.env`, or append + `--p83`/`--p84`/`--p85` to the host name. `forge status` prints the default. +- **`npm` prints a notice about pnpm**: deliberate. npm still works; pnpm is preferred + here. +- **An `.ini` in `custom/php.d/` seems ignored**: recreate the container with + `forge restart php84dev`. A plain restart does not re-read it. +- **`forge: command not found`**: the installer links it into `~/.local/bin`, which + some shells do not have on `PATH`. Add it, or `source aliases.bash` from the project + directory. + +For more help, check the logs with `forge logs` — or `forge logs apachedev` for one +service — or open an issue on GitHub. ## 🤝 Contributing diff --git a/TASKS.md b/TASKS.md index 0cb29a0..1c7ec2d 100644 --- a/TASKS.md +++ b/TASKS.md @@ -1106,7 +1106,7 @@ what the README explains first. keypresses, so adding a database cannot silently break it — the first version did count, and would have. -- [ ] **34. The installer's last words send you to `docker compose`, not `forge`** +- [x] **34. The installer's last words send you to `docker compose`, not `forge`** — FIXED It ends with: ``` @@ -1121,7 +1121,7 @@ what the README explains first. that one already ends with `forge start`. The installer calls the certificate script last, so its closing line is what you actually read. -- [ ] **35. The README teaches `docker compose` in 11 places** +- [x] **35. The README teaches `docker compose` in 11 places** — FIXED Same problem, wider. `README.md` lines 133, 269, 318, 322, 368, 401, 433, 436, 439 and the Spanish equivalents reach for compose where a `forge` subcommand exists. @@ -1146,6 +1146,30 @@ what the README explains first. whoever edits one cannot tell what the other has. Counting `##` and calling them aligned is exactly what hid this. + Done together. Every `docker compose` instruction is now a `forge` command in + both languages; the two that remain are prose, not instructions — the sentence + saying `forge` wraps compose, and the one describing what CI validates. + + The pass needed one more command. `forge logs` followed a single PHP container, + so troubleshooting still sent you to `docker compose logs`; naming a container + is not something you can do when you do not yet know what broke. No argument now + means every service, and an argument can be a service as well as a version. + + The subsections were worse than the heading list suggested. English was missing + the `xdebug` commands entirely; Spanish was missing three troubleshooting entries + and had no subsection for its command block. Both now carry the union, in the + same order, so the two files are translations rather than variants. + + Also switched to `forge` where the READMEs called the scripts directly: + `forge dns setup|status|test|remove` and `forge certs`, each noting the script + still works. And the stale "Aliases not working" entry became + `forge: command not found`, which is the thing people actually hit — the aliases + it referred to were removed when `forge` replaced them. + + CI guards it: a lint step greps both READMEs for `docker compose up|logs|stop| + --profile|build` and fails. Verified locally that it fails when a violation is + added, not just that it passes today. + - [ ] **36. There is no uninstall** `install.sh` writes `.env`, generates certificates, installs a CA into the system trust store and the NSS stores, symlinks `forge` into `~/.local/bin`, diff --git a/bin/forge b/bin/forge index 41865b1..fabb904 100755 --- a/bin/forge +++ b/bin/forge @@ -86,10 +86,26 @@ cmd_shell() { docker exec -it -w "$wd" -u php-devforge "php${v}dev" bash } +# No argument means every service: when something breaks you rarely know which +# container to look at, and picking one for you is how `docker compose logs` +# ended up in the troubleshooting section instead of this command. cmd_logs() { need_env - local v; v="$(normalise "${1:-$(current_version)}")" - docker compose logs -f "php${v}dev" + local arg="${1:-}" + if [ -z "$arg" ]; then + docker compose logs -f + elif printf '%s' "$arg" | grep -qE '^[0-9]+\.?[0-9]+$'; then + docker compose logs -f "php$(normalise "$arg")dev" + else + # Plain compose here, not compose_with_env: that one clears + # COMPOSE_PROFILES, which would hide mailpit and the databases. + docker compose config --services | grep -qx "$arg" || { + err "Unknown service: $arg" + echo " available: $(docker compose config --services | sort | paste -sd' ' -)" >&2 + exit 1 + } + docker compose logs -f "$arg" + fi } cmd_mail() { @@ -291,7 +307,7 @@ PHP DevForge forge use set the default PHP version forge shell [version] open a shell in a PHP container - forge logs [version] follow a container's logs + forge logs [version|service] follow the logs; everything if you name nothing forge link [name] publish a project: symlinks it under sites/ diff --git a/install_cert.sh b/install_cert.sh index 885fcae..ab3828e 100755 --- a/install_cert.sh +++ b/install_cert.sh @@ -141,4 +141,4 @@ fi echo "" echo "🎉 Done. Restart your browser so it picks up the new CA." -echo " Start the environment with: docker compose up -d" +echo " Start the environment with: forge start" From 33a3e8d0007f03f3609cf5889946f295e4e7c55e Mon Sep 17 00:00:00 2001 From: Yostin Vargas Date: Sun, 23 Aug 2026 21:48:32 -0400 Subject: [PATCH 2/2] ci: check forge logs where containers actually exist The assertions sat in 'the forge command works', which runs before the stack is built, so 'forge logs' had nothing to show. They now live after the welcome page is confirmed served, in a step whose comment says why it has to. They also asserted the wrong thing. 'apachedev appears in the output' passes even with a stopped container -- compose keeps logs for those -- and would have passed if the command regressed to following one container. It now counts distinct services: at least three with no argument, exactly one when a service is named. Verified by putting the old single-container behaviour back and watching the step fail with 'named 1 services'. --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e309f70..4af6197 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -157,16 +157,6 @@ jobs: # unknown versions must be refused, not passed to docker if forge use 9.9 2>/dev/null; then echo "9.9 should have been rejected"; exit 1; fi - # No argument means every service. Naming one narrows it -- a service, not - # just a PHP version, which is what sent the README to - # `docker compose logs` for troubleshooting. - timeout 5 forge logs > /tmp/all.log 2>&1 || true - grep -q apachedev /tmp/all.log || { echo "forge logs showed no apache"; exit 1; } - timeout 5 forge logs apachedev > /tmp/one.log 2>&1 || true - if grep -qE '^php8[0-9]dev' /tmp/one.log; then - echo "forge logs apachedev leaked other services"; exit 1 - fi - if forge logs nope 2>/dev/null; then echo "unknown service accepted"; exit 1; fi - name: build images # Every PHP version is built. 8.5 broke silently once: opcache became @@ -225,6 +215,29 @@ jobs: # Hyphens in project names were broken for a long time: mi-app--sites # resolved to sites/app/mi. + # Needs running containers, so it sits after the page is confirmed served + # rather than with the other forge checks, which run before the stack is up. + - name: forge logs reaches every container + run: | + export PATH="$HOME/.local/bin:$PATH" + # Counting distinct services, not looking for one name: compose prints + # logs for stopped containers too, so "apachedev appears" would pass even + # if the command had regressed to following a single container. + named() { grep -oE '^[a-z0-9]+ *\|' "$1" | tr -d ' |' | sort -u; } + + timeout 5 forge logs > /tmp/all.log 2>&1 || true + all="$(named /tmp/all.log | wc -l)" + test "$all" -ge 3 || { echo "forge logs named $all services, expected the stack"; exit 1; } + + # naming one narrows it to exactly that -- a service, not just a PHP + # version, which is what sent the README to `docker compose logs` + timeout 5 forge logs apachedev > /tmp/one.log 2>&1 || true + test "$(named /tmp/one.log)" = apachedev || { + echo "forge logs apachedev named: $(named /tmp/one.log | tr '\n' ' ')"; exit 1; } + + if forge logs nope 2>/dev/null; then echo "unknown service accepted"; exit 1; fi + echo "forge logs: $all services with no argument, one when named" + - name: a hyphenated project name resolves run: | curl -fsS --resolve my-app--sites.phpforge.dev:443:127.0.0.1 \