Files
timebank-cc-public/references/RUNBOOK_LARAVEL_12_PREPARATION.md
T

10 KiB

Runbook: Laravel 12 preparation steps, for every installation

Laravel 12 needs a few package changes that already work on Laravel 11. They reach main one step at a time, and every installation (Timebank.cc, Uuro) takes them with its normal deploy. This runbook is the checklist per step: what changes, how to deploy it, what to check afterwards and how to go back. Background: Phase 1 of plans/LARAVEL_UPGRADE_PLAN.md.

From Laravel 11.57 on main (every installation, since the 10 to 11 upgrade)
To still Laravel 11.57, with Carbon 3, flatpickr in the app, unused packages removed, PHPUnit 11, Fortify 1.41
Tool ./deploy.sh -e server, once per step
Duration a normal deploy per step, no maintenance window
Database no migrations in any step
Rollback code only, to the tag of the previous step (see Rollback)

Laravel 12 itself comes after all five steps, with scripts/upgrade.sh and a runbook of its own.

The steps

Each step is a tag on main. Timebank.cc deploys a step first and watches its log for two or three days; then the next step goes to main. Take the steps in order, and preferably one at a time. When your installation is several steps behind, a deploy brings them all at once: that works, but do the checks of every step it brought.

Tag Step What changes Visible to members
l12-prep-0-start starting point main before the first step; the rollback target of step 1 -
l12-prep-1-carbon3 Carbon 3 date library 2 -> 3; durations and timestamps keep their old behaviour no (dates must look the same)
l12-prep-2-flatpickr flatpickr into the app the date picker component moves into the app, package asdh/laravel-flatpickr removed no
l12-prep-3-cleanup package clean-up unused robsontenorio/mary and laradumps/laradumps removed, config/mary.php deleted, psr/simple-cache 1 -> 3 no
l12-prep-4-phpunit PHPUnit 11 test tools only (PHPUnit 11.5, Collision 8.9) no
l12-prep-5-qrcode QR codes, Fortify 1.41 QR codes made by the app itself (bacon 3), simplesoftwareio/simple-qrcode removed, Fortify 1.21 -> 1.41 no (QR codes look the same)

Start every terminal session by setting the application root once (the server's own path):

export APP=/var/www/CHANGE-ME     # the application root on this server

Once: is the installation on main's history?

main was rewritten once, after the 10 to 11 upgrade. An installation that was deployed in between sits on a commit that is not on main any more, and a routine deploy would then merge two histories. Check it once, read-only:

( cd "$APP" && git fetch -q origin main \
  && echo "branch: $(git rev-parse --abbrev-ref HEAD)   HEAD: $(git log -1 --format='%h %s')" \
  && if git merge-base --is-ancestor HEAD origin/main; then echo "OK: on main's history, nothing to do"; \
     else echo "NOT on main's history. Code that differs from origin/main (expect nothing):"; \
       git diff --stat HEAD origin/main -- app bootstrap/app.php config database lang resources routes composer.json composer.lock package.json; fi )
  • OK: go on with the steps.

  • NOT on main's history and no code differs: move the installation onto main without changing any code (this changes the installation; the tag keeps the old position):

    ( set -e; cd "$APP"
      git diff --name-only HEAD origin/main | xargs -r git -c core.fileMode=false status --porcelain -- | grep . \
        && { echo "Local changes on files that differ: stop and ask"; exit 1; } || true
      git tag "pre-realign-$(date +%F)" HEAD
      git -c core.fileMode=false checkout -B main origin/main
      git branch -u origin/main main
      git status -sb | head -1 )
    

    The last line must show ## main...origin/main without "ahead" or "behind".

  • NOT on main's history and code differs: stop and ask; do not deploy.


Every step

1. Before the deploy (read-only)

( cd "$APP"; git fetch -q origin main --tags
  echo "now:  $(git describe --tags --always HEAD)"
  echo "main: $(git describe --tags --always origin/main)"
  git merge-base --is-ancestor HEAD origin/main && echo "fast-forward: yes" || echo "fast-forward: NO"
  echo "steps this deploy brings: $(git tag -l 'l12-prep-*' --merged origin/main --no-merged HEAD | tr '\n' ' ')"
  php -v | head -1; df -h "$APP" | tail -1
  echo "local changes:"; git -c core.fileMode=false status --porcelain | head )
  • fast-forward: yes must be there (otherwise see the section above).
  • PHP 8.2 or newer; a few hundred MB free.
  • Local changes (the last lines) are the ones deploy.sh asks about; on a server they are usually build files.

2. Deploy

cd "$APP" && ./deploy.sh -e server

As root, the way you always deploy. It installs the changed packages with composer install, clears the caches and restarts the queue workers.

3. Check (read-only)

( cd "$APP" && echo "now: $(git describe --tags --always HEAD)" \
  && sudo -u www-data php artisan upgrade:verify \
  && sudo -u www-data php artisan about --only=environment | head -12 )
  • upgrade:verify ends without failures.
  • Then the checks of the step(s) this deploy brought, below, in the browser.

4. The next days: watch the log (read-only)

( cd "$APP"; for d in $(seq 0 2); do f=storage/logs/laravel-$(date -d "-$d day" +%F).log; [ -f $f ] && echo "== $f: $(grep -c '\.ERROR' $f) errors" && grep '\.ERROR' $f | cut -c1-160 | sed -E 's/\[[^]]*\] //' | sort | uniq -c | sort -rn | head -5; done )

An error type you have not seen before the deploy deserves a look. Report it, with the step and the log lines.


Checks per step

Step 1: Carbon 3 (l12-prep-1-carbon3)

Carbon 3 counts differences between dates with a sign and with fractions, and reads timestamps as UTC. The code now asks for the old behaviour everywhere; these checks confirm it.

  • A post and a profile show the same date and time as before the deploy (not 1 or 2 hours off).
  • Chat: "online" / "last seen" and the typing indicator behave as before.
  • Log in, stay idle longer than the platform's inactivity logout: you are logged out as before.
  • The next morning: no errors from the scheduled commands (inactive profiles, bounce processing, expired chat messages) in the log.

Step 2: flatpickr into the app (l12-prep-2-flatpickr)

The <x-flatpickr> component and its views move into the app (app/View/Components/Flatpickr.php, resources/views/flatpickr/); the package is removed. No view that uses the picker changed.

  • The date pickers open and fill in on: the transactions page (period filter), a post's start and end date in Posts (admin), creating and editing a call, a mailing's send date (admin), the date of birth in the profile settings and the statistics period (admin).
  • A chosen date and time is saved as chosen (not shifted by the timezone).
  • Only if your platform folder overrides the picker's views (platform/views/vendor/flatpickr/): check that your override still applies, and tell us.

Step 3: package clean-up (l12-prep-3-cleanup)

Nothing visible changes. Removed: Mary (with config/mary.php; nothing used it), laradumps (development only); the psr/simple-cache requirement moves from 1 to 3.

  • Home page, search, a profile and the chat open without errors.
  • Icons look as before (they come from WireUI, not from Mary).

Step 4: PHPUnit 11 (l12-prep-4-phpunit)

Test tools only. Nothing changes for visitors.

  • upgrade:verify passes; nothing else to check.

Step 5: QR codes and Fortify 1.41 (l12-prep-5-qrcode)

The QR codes are made by the app itself now (the package used before does not work with Laravel 12's Fortify). They look the same. Fortify, the login package, goes from 1.21 to 1.41; it adds no routes or tables.

  • Pay page: the QR code appears.
  • A transaction and an account statement (PDF): the QR code appears and can be scanned.
  • The PDF of a post (and of a call, if your platform has calls): QR code present.
  • Log in and out, the password reset mail, and two-factor authentication: turning it on shows a QR code that an authenticator app accepts, and logging in with a code works.

Rollback

No step has migrations, so a rollback only puts the code back. Use it when a step breaks something for members and a fix is not available soon. It changes the installation:

( set -e; cd "$APP"
  PREV=l12-prep-CHANGE-ME          # the tag of the step BEFORE the one you leave, e.g. l12-prep-1-carbon3
  git fetch -q origin --tags
  git -c core.fileMode=false checkout -B main "$PREV"
  COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction --prefer-dist --optimize-autoloader
  php artisan optimize:clear
  git log -1 --format='now: %h %s' )

Then restart the queue workers and Reverb the way deploy.sh does on your server (the timebank-* services), and run the check of step 3 above.

After a rollback, do not deploy until the fix is on main: a deploy pulls main and brings the step back. Report what broke; the fix comes as a new commit on main, and the next deploy fast-forwards as usual.

Troubleshooting

Symptom Cause and fix
Class "...MaryServiceProvider" not found (or another removed package) right after the deploy A stale package cache. cd "$APP" && rm -f bootstrap/cache/packages.php bootstrap/cache/services.php && composer dump-autoload && php artisan optimize:clear
deploy.sh stops: "main moves Laravel from 11 to 12" Not one of these steps: Laravel 12 itself has reached main. Use its runbook and scripts/upgrade.sh.
git pull in deploy.sh wants to merge, or "divergent branches" The installation is not on main's history: see the first section.
Dates 1-2 hours off after step 1 Report it with the page and the expected time: a createFromTimestamp() call without the app timezone.
A date picker is missing or unstyled after step 2 php artisan view:clear, then reload; if it stays, report the page.

Record

Step Installation Deployed (date) upgrade:verify Checks Log quiet after 2-3 days
1 Carbon 3
2 flatpickr
3 clean-up
4 PHPUnit 11
5 QR codes