Files
amare/.github/workflows/ci.yml
Manoel Freitas 3dc1f449ee feat: ship public site with SEO and visuals (#3)
* feat: ship public site with SEO and visuals

Publish CMS content on public routes with responsive media,
accessibility checks, and deterministic visual baselines.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: make browser visual CI deterministic for media

Mount host public storage into FrankenPHP so seeded fixtures are served,
and replace PNG-as-JPG fixtures with real JPEGs so Chromium can render them.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: serve public media on same-origin storage paths

Pest Browser hosts on 127.0.0.1:port while Storage::url used
http://localhost, so screenshots captured broken images. Use relative
/storage URLs for media and absolutize only OG tags via url().

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: refresh visual baselines from CI Ubuntu screenshots

Media now loads on same-origin /storage paths, so baselines must
capture the rendered fixtures. Use full-page snapshots from the CI
runner to keep Pest's exact snapshot match stable across environments.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 10:12:24 -03:00

258 lines
7.5 KiB
YAML

name: CI
on:
push:
branches: [main]
pull_request:
concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
APP_ENV: testing
APP_KEY: base64:NXm/6jIyFcDGHoMKGc5QZuSaq0dRZFYPg1Isuy1fNvE=
APP_LOCALE: pt_BR
APP_FALLBACK_LOCALE: pt_BR
APP_TIMEZONE: America/Fortaleza
BCRYPT_ROUNDS: 4
CACHE_STORE: database
DB_CONNECTION: pgsql
DB_HOST: 127.0.0.1
DB_PORT: 5432
DB_DATABASE: amare_test
DB_USERNAME: amare
DB_PASSWORD: secret
QUEUE_CONNECTION: database
SESSION_DRIVER: database
jobs:
static:
name: static
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
coverage: none
- uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
restore-keys: composer-${{ runner.os }}-
- run: composer validate --strict
- run: composer install --no-interaction --prefer-dist
- run: composer pint:check
- run: composer phpstan
- run: composer audit
unit:
name: unit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
coverage: none
- uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
restore-keys: composer-${{ runner.os }}-
- run: composer install --no-interaction --prefer-dist
- run: npm ci
- run: npm run build
- run: composer test:unit
feature:
name: feature
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_DB: amare_test
POSTGRES_USER: amare
POSTGRES_PASSWORD: secret
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U amare -d amare_test"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
coverage: none
- uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
restore-keys: composer-${{ runner.os }}-
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-${{ runner.os }}-
- run: composer install --no-interaction --prefer-dist
- run: npm ci
- run: npm run build
- run: php artisan migrate --force
- run: composer test:feature
browser:
name: browser
runs-on: ubuntu-latest
services:
postgres:
image: postgres:17
env:
POSTGRES_DB: amare_test
POSTGRES_USER: amare
POSTGRES_PASSWORD: secret
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U amare -d amare_test"
--health-interval 5s
--health-timeout 5s
--health-retries 10
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.4"
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, pdo_pgsql, bcmath, intl, sodium, gd
coverage: none
- uses: actions/cache@v4
with:
path: ~/.composer/cache/files
key: composer-${{ runner.os }}-${{ hashFiles('**/composer.lock') }}
restore-keys: composer-${{ runner.os }}-
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: npm-${{ runner.os }}-
- run: composer install --no-interaction --prefer-dist
- run: npm ci
- run: npm run build
- run: npx playwright install chromium --with-deps
- run: php artisan migrate --force
- run: php artisan db:seed --class=VisualContentSeeder --force
- run: php artisan storage:link
- name: Build application image
run: docker build -t amare-app:ci .
- name: Run browser tests against FrankenPHP container
env:
APP_FROZEN_NOW: "2026-03-15T12:00:00-03:00"
run: |
docker run -d --name amare-web \
-e APP_ENV=testing \
-e APP_KEY="${APP_KEY}" \
-e APP_URL=http://127.0.0.1:8000 \
-e APP_LOCALE=pt_BR \
-e APP_FALLBACK_LOCALE=pt_BR \
-e APP_TIMEZONE=America/Fortaleza \
-e APP_FROZEN_NOW="${APP_FROZEN_NOW}" \
-e DB_CONNECTION=pgsql \
-e DB_HOST=host.docker.internal \
-e DB_PORT=5432 \
-e DB_DATABASE=amare_test \
-e DB_USERNAME=amare \
-e DB_PASSWORD=secret \
-e SESSION_DRIVER=database \
-e CACHE_STORE=database \
-e QUEUE_CONNECTION=database \
--add-host=host.docker.internal:host-gateway \
-v "${GITHUB_WORKSPACE}/storage/app/public:/app/storage/app/public" \
-p 8000:8000 \
amare-app:ci
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8000/up; then
break
fi
sleep 2
done
curl -fsS http://127.0.0.1:8000/up
./vendor/bin/pest --testsuite=Browser
- name: Collect failure diagnostics
if: failure()
run: |
mkdir -p artifacts/browser
docker logs amare-web > artifacts/browser/container.log 2>&1 || true
cp -R storage/logs artifacts/browser/app-logs 2>/dev/null || true
cp -R tests/Browser/Screenshots artifacts/browser/screenshots 2>/dev/null || true
cp -R tests/.pest artifacts/browser/pest 2>/dev/null || true
- name: Upload browser failure artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: browser-failure-artifacts
path: artifacts/browser
if-no-files-found: ignore
container:
name: container
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build production image
run: docker build -t amare-app:ci .
- name: Verify container healthcheck and storage link
run: |
docker run -d --name amare-health \
-e APP_ENV=production \
-e APP_KEY="${{ env.APP_KEY }}" \
-e APP_URL=http://127.0.0.1:8000 \
-e APP_DEBUG=false \
-e DB_CONNECTION=pgsql \
-e DB_HOST=127.0.0.1 \
-e DB_PORT=5432 \
-e DB_DATABASE=amare \
-e DB_USERNAME=amare \
-e DB_PASSWORD=secret \
-p 8000:8000 \
amare-app:ci
for i in $(seq 1 30); do
if curl -fsS http://127.0.0.1:8000/up; then
docker exec amare-health test -L /app/public/storage
exit 0
fi
sleep 2
done
docker logs amare-health
exit 1