Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

+299
View File
@@ -0,0 +1,299 @@
# Upgrade from Laravel 10 to Laravel 11, for any server and platform
This guide upgrades an installation of this application from **Laravel 10** (any older version of the
`main` branch) to **Laravel 11** (the `laravel-11` branch). It is the same for every platform that runs this
code (Timebank.cc, Uuro or any other) and for every server: production, development or test.
Check where you start:
```bash
php artisan --version # Laravel Framework 10.x: this guide applies
git branch --show-current # main
```
The upgrade is done by `scripts/upgrade.sh`. The script checks the server, makes a verified database backup,
runs the new `deploy.sh` while the site is in maintenance mode, and lets you test before visitors see the new
version. If something is wrong, it rolls back.
Plan about 30 minutes. The site itself is offline (maintenance page) for about 5 to 15 minutes.
Upgrade one server at a time. Start with a test or development server if you have one.
**Status (2026-10-19).** Timebank.cc runs Laravel 11 since 2026-10-15. The last installation on Laravel 10 is the Uuro
staging server, which runs a branch of its own (`uuro`) instead of `main`. It moves to `main` during the upgrade
with `upgrade.sh --leave-branch`, after its config, logos and favicons are copied into its platform folder `platform/`.
Its day-by-day checklist is [RUNBOOK_LARAVEL_10_TO_11.md](RUNBOOK_LARAVEL_10_TO_11.md); this guide explains each step.
---
## 1. Before you start
### Requirements
| What | Needed | How to check |
|---|---|---|
| PHP for the website and the command line | 8.2 or newer (Laravel 11 needs it) | `php -v`; the script also asks the website itself |
| Git branch | `main` | `git branch --show-current` |
| tmux or screen | so a dropped SSH connection does not stop the upgrade halfway | `tmux -V` (capital V); install with `apt-get install -y tmux` |
| Free disk space | about twice the size of `vendor/` plus 2 GB | `df -h .` and `du -sh vendor` |
| MySQL user that may drop and create the database (root or a deployment user) | for migrations, and for a database restore during a rollback | see `.env` below |
| Time and a quiet moment | not during a busy period or a mailing | |
Local changes to tracked files stop the upgrade. Two kinds of change are expected and ignored:
- `public/build/`, which `deploy.sh` rebuilds on every server
- file modes (the executable bit), which `deploy.sh`'s `chmod -R 775` changes
Check with:
```bash
git -c core.fileMode=false status --short --untracked-files=no -- . ':(exclude)public/build'
```
This should print nothing. If it lists files, find out why they were changed on the server before you continue.
### Check `.env`
These keys are read by `deploy.sh` and `scripts/upgrade.sh`. Show them without printing passwords:
```bash
grep -E '^(APP_URL|DEPLOY_ENVIRONMENT|DEPLOY_APP_DIR|DEPLOY_SERVER_TYPE|DEPLOY_WS_URL|TIMEBANK_CONFIG|TIMEBANK_THEME|DB_DEPLOY_USERNAME|SCOUT_QUEUE_TIMEOUT)=' .env
grep -c '^DB_DEPLOY_PASSWORD=.' .env # 1 = set
```
| Key | Purpose |
|---|---|
| `APP_URL` | The site's address. The script uses it to ask the website which PHP version serves it. |
| `DEPLOY_ENVIRONMENT=server` | `deploy.sh` runs in server mode. Server mode also needs `DEPLOY_APP_DIR`, `DEPLOY_SERVER_TYPE` (`dev` or `prod`) and `DEPLOY_WS_URL`. |
| `TIMEBANK_CONFIG` | The platform config, `config/<name>.php`. It can be any name, e.g. `uuro`. |
| `TIMEBANK_THEME` | `timebank_cc`, `uuro`, `vegetable` or `yellow`. |
| `DB_DEPLOY_USERNAME`, `DB_DEPLOY_PASSWORD` | The MySQL user for migrations and database restores. Without them you are asked for one: have the MySQL root password ready. |
| `SCOUT_QUEUE_TIMEOUT=1800` | Recommended: lets the nightly search re-index finish without the queue worker being killed. |
### Know your restart commands
After the deploy you restart long-running processes, because they still hold the old code. The script
suggests commands for the services it finds. Typically:
```bash
sudo systemctl restart <your Reverb service>
sudo systemctl reload apache2 # or nginx
sudo systemctl reload php8.3-fpm # if the site runs on PHP-FPM
```
`deploy.sh` restarts the queue workers itself.
---
## 2. Dry run (changes nothing)
Log in to the server, start tmux and go to the application root:
```bash
tmux new -s upgrade # reconnect later with: tmux attach -t upgrade
cd /var/www/<your app>
git fetch origin
git show origin/laravel-11:scripts/upgrade.sh > /tmp/timebank-upgrade.sh
bash /tmp/timebank-upgrade.sh --dry-run --target origin/laravel-11
```
Always run the script from `/tmp`, taken from the target branch. The copy in your installation is older or
absent.
A good dry run ends with `Dry run: nothing was changed` and shows:
- `1. Checks`: the PHP version is accepted for both the command line and the website, there is enough disk
space, and `Made on this server, differ from git: ...` (if shown) is fine: deploy.sh makes those files
again. A changed `public/storage` is never reported: it is this server's own storage link.
- `2. Plan`: current and target commit, `Laravel major upgrade (10 -> 11)`, and the number of new migrations.
If any migration changes existing tables, a rollback after going live needs the database restore.
If it stops instead, see [Troubleshooting](#7-troubleshooting).
---
## 3. The upgrade
```bash
bash /tmp/timebank-upgrade.sh --target origin/laravel-11
```
The script goes through eight steps and waits for you at each question. What to answer:
| Step | Question or action | Answer |
|---|---|---|
| 1. Checks | `Not running inside tmux or screen... Continue anyway?` (only outside tmux) | Stop (`N`) and start tmux. |
| 2. Plan | `Upgrade this installation to <commit>?` | `y` |
| 3. Backup | `Make a database backup now with scripts/backup-database.sh?` | `y`. The backup is verified and kept with the upgrade state, together with a snapshot of `vendor/` and `public/build`. |
| 4. Maintenance | The script prints a command with a secret. | In a second tmux window (`Ctrl-b c`), in the application root, run that command: `php artisan down --retry=60 --secret=<secret>`. Then press Enter in the first window. |
| 5. Deploy | The new `deploy.sh` runs and asks its own questions. | See below. |
| 6. Version check | Shows `Laravel Framework 11.x`. | Nothing to do. |
| 7. Restart | Shows restart commands for this server. | Run them in the second window, then press Enter. |
| 8. Go / no-go | `Go live (g) or roll back now (r)?` | First test (see below), then `g` or `r`. |
### Questions from `deploy.sh` (step 5)
- **`New configuration keys available ... Would you like to merge new keys now? (y/N)`**: press `y`
(a single key press). For each config file it then asks `Merge these keys into <file>? (yes/no)`: type `yes`.
New settings are added and your own values stay as they are. A backup of each file goes to
`storage/config-backups/`.
- **`MySQL username [root]` / `MySQL password`**: only when `DB_DEPLOY_*` is not in `.env`.
- **`You have uncommitted changes. Continue anyway? (y/n)`**: this should not appear any more. If it does,
check what `git status` lists before you answer.
If `deploy.sh` fails, the script stops and the site stays in maintenance mode. See
[Troubleshooting](#7-troubleshooting).
### Test before going live (step 8)
The site is still in maintenance mode. Open `<APP_URL>/<secret>` once in your browser: that browser can then
use the site while visitors still see the maintenance page. Check:
- log in, and switch to an organisation or bank profile
- a payment
- a chat message with an image
- a poll, if your platform uses polls
- search
- the error log: `tail -n 50 storage/logs/laravel-$(date +%F).log` (or `laravel.log`)
Then:
- **All fine**: answer `g`. Run `php artisan up` in the second window, then press Enter.
- **Something wrong**: answer `r`. The rollback starts right away and may also restore the database, which
is safe at this point because nothing went live.
---
## 4. After the upgrade
0. **Health check** (once this version has it): `php artisan upgrade:verify` checks profiles, permissions,
white-label settings, payment protection, search and background services, read-only. Run it before and
after an upgrade; exit code 1 means a check failed.
1. **Platform settings** (all platforms). See which new settings arrived, and check that nothing is waiting:
```bash
php artisan config:merge --all --dry-run # should report nothing new
```
On a platform other than Timebank.cc, review these in its platform config (`platform/config.php`, or
`config/<TIMEBANK_CONFIG>.php` without a platform folder). They came from the default template:
| Setting | Default for a new platform | Meaning |
|---|---|---|
| `platform_translations.*` | your existing names | Platform name, currency and terms per language. |
| `mail.*` | your existing addresses | Sender and recipient addresses. |
| `local_pages` | all `false` | Timebank.cc's city pages (`the-hague`, `lekkernassuh`, `amst-brus-lisb`). A page that is off has no URL, and its footer link disappears. |
| `android_app.enabled` | `false` | The Android app page and the app's update check (`/app/version`). |
| `account_info.*_self`, `*_privileged` | `true` | Which balances and totals owners and privileged viewers (admins, banks) see. |
A setting a config file does not have yet keeps its old behaviour until it is merged.
2. **Email logo, favicons and preview image** (platforms other than Timebank.cc; all branding is described in
[MULTI_PLATFORM_INSTANCES.md](MULTI_PLATFORM_INSTANCES.md), sections 3 and 6). Without its own logo file, a
platform gets a text logo with its name in emails and PDFs. Put the files in the platform folder (Uuro's
runbook copies them there from its branch):
```
platform/public/mail-logo.png dark logo, for white backgrounds (PNG, about 900×700, transparent)
platform/public/mail-logo-inverted.png light logo, for the newsletter header
platform/public/favicon.ico, favicon-32x32.png, favicon-16x16.png, apple-touch-icon.png
platform/public/og-image.png preview for shared links (about 1200×630)
```
`deploy.sh` links them at `/platform` (`php artisan platform:link`); `php artisan platform:check` confirms it.
3. **Workers and scheduler**. Check that the queue workers run, with all queues:
```bash
sudo systemctl status 'timebank-*' --no-pager | grep -E 'Active|●'
```
4. **The next days**. Watch the error log. When all is well, free the disk space of the snapshot. The
script prints the exact command:
```bash
rm storage/app/upgrades/<stamp>/vendor.tar.gz
```
Keep the database backup in that folder a little longer.
### Routine deploys afterwards
`./deploy.sh -e server` keeps working as before. It pulls `main`. Until `main` itself has been
fast-forwarded to the Laravel 11 version, that pull finds nothing new on an upgraded server, which is
harmless.
`main` is fast-forwarded to Laravel 11 only after every server has been upgraded, so after Uuro: a Laravel 10
server's own `deploy.sh` would otherwise pull Laravel 11 without the protection of this script. From Laravel 11
on, `deploy.sh` stops a routine server deploy that would raise the Laravel major version.
In its "pull conflict" question, never choose option 2 (reset and `git clean`): it deletes untracked files,
such as uploads in `storage/`.
---
## 5. Rollback
Roll back with the copy of the script in the upgrade's state folder. The script printed the exact command;
without a stamp it takes the latest upgrade:
```bash
bash storage/app/upgrades/<stamp>/upgrade.sh --rollback
```
It asks for confirmation, switches on maintenance mode and resets the code to the previous commit. It then
restores `vendor/`, `bootstrap/cache` and the server's own `public/build` from the snapshot (no download
needed), clears the caches and tells you what to restart.
**The database:**
- **Before going live**, the rollback offers to restore the backup. That is safe.
- **After going live**, it rolls back the code only, unless you type `RESTORE`. A restore deletes everything
written since the backup: payments, messages, votes and new profiles. Usually the code-only rollback is
right, because the old code ignores new tables.
The restore connects as `DB_DEPLOY_USERNAME`, or asks for a MySQL user that may drop and create the
database.
After a rollback, do not run `./deploy.sh` until the problem is fixed: it would pull the version you just
left.
---
## 6. What the upgrade changes
- **Laravel 10 to 11.** Laravel 11.57 and the matching package versions (Livewire 3.8 among them); the
application code was adapted. No database changes are needed for it. PHP 8.2 or newer is required.
- **Platform config.** A platform may use its own config name: `TIMEBANK_CONFIG=myorg` with `config/myorg.php`.
`deploy.sh` creates the file from `config/timebank-default.php.example` when it is missing, and
`config:merge` keeps it up to date. A config file that is not in git is excluded from git on that server
(`.git/info/exclude`), so deploys no longer report it as an uncommitted change.
- **Settings for platform-specific pages.** `local_pages` and `android_app`, see [After the upgrade](#4-after-the-upgrade).
- **Branding.** Texts that showed "Timebank" or "Timebank.cc" now use the platform name. Emails use the
theme's logo or the generated text logo.
- **Deploys.** `deploy.sh` no longer stops at a server-built `public/build` or at mode-only changes, and stops a
routine server deploy that would raise the Laravel major version.
- **Platform folder.** Everything platform-specific may live in `platform/` (outside git): config, theme, text and
template overrides, logos, favicons and the preview image. See
[MULTI_PLATFORM_INSTANCES.md](MULTI_PLATFORM_INSTANCES.md), section 3. A platform branch of its own is no longer
needed; `upgrade.sh --leave-branch` moves an installation off one.
- **Open Graph tags** (preview of shared links) and optional favicon formats for every platform.
- **Admin roles.** The legacy roles `Admin\{id}\admin` are replaced by the roles from `admin_roles`
(`database:update` runs `admin-roles:migrate`).
---
## 7. Troubleshooting
| Message | Meaning and what to do |
|---|---|
| `There are local changes to tracked files` | Files were edited on the server. Look at them with `git diff`. Commit the change upstream, or save a copy and `git checkout -- <file>`. |
| `This installation has commits that are not on <target>` | The server has local commits. A fast-forward is not possible: ask the developers. |
| `This server's PHP does not meet the requirements` / `This site is served by PHP x` | Install PHP 8.2 or newer (with the extensions listed) and switch the site to it first. |
| `Could not ask the site which PHP serves it` | `APP_URL` is unreachable from the server itself. The script lists the PHP versions it finds and asks you to confirm. |
| `Only N MB free` | Free disk space (old backups in `backups/`, old snapshots in `storage/app/upgrades/`). |
| `deploy.sh stopped with an error` | The site stays in maintenance mode. Read the error above it. Either fix the cause, run `bash deploy.sh -e server` again and continue with steps 6 to 8 by hand (restart, test through the bypass URL, `php artisan up`), or roll back with the command the script printed (database restore allowed: nothing went live). |
| `⊘ <file>: N new keys available (not applied)` or `not merged - ...` | `config:merge` did not change that file; it keeps working with the old settings. Run `php artisan config:merge <name> --dry-run` to see the keys, and add a reported key by hand from the `.example` file if needed. |
| `✗ <file>: Merged config is invalid` | The merge checked its own result and restored the file. Nothing changed. Report the message to the developers. |
| Restore fails with `Access denied ... DROP` | Add `DB_DEPLOY_USERNAME` and `DB_DEPLOY_PASSWORD` to `.env`, or restore by hand: `gzip -dc <backup>.sql.gz \| mysql -u root -p` |
| The site still shows the maintenance page | Run `php artisan up` in the application root. |
| `apt-get update`: `GPG error: https://packages.sury.org/php ... EXPKEYSIG` | The signing key of the Sury PHP repository expired: no PHP updates arrive and `apt-get install` stops. Install Sury's key package and point the source at it (backup first):<br>`cp -a /etc/apt/sources.list.d/php.list /root/php.list.bak-$(date +%F)`<br>`curl -fsSLo /tmp/sury-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb && dpkg -i /tmp/sury-keyring.deb`<br>`echo "deb [signed-by=/usr/share/keyrings/debsuryorg-archive-keyring.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list`<br>`apt-get update` |
All output of an upgrade and a rollback is logged in `storage/app/upgrades/<stamp>/` (`upgrade.log`,
`rollback.log`), together with `state.env`. Send these files along when you ask for help.