# Upgrading Capell

Capell ships with a single upgrade entry point: `php artisan capell:upgrade`. Every operation is idempotent — re-running it is always safe.

## Admin Upgrades

Open **Administration → Upgrades** in the Capell admin when you want the most visible upgrade path. The page shows the installed Capell version, links to these docs, explains the upgrade safety rails, and exposes two actions:

- **Check for updates** sends the installed Capell package snapshot to the Capell Marketplace heartbeat API and stores the returned update/security advisory snapshot locally.
- **Preview Upgrade** runs `capell:upgrade --dry-run --force --no-clear-cache` so you can see pending migrations, upgrade steps, package commands, and version changes before anything writes.
- **Upgrade Capell** runs the production upgrade pipeline with confirmation.

The dashboard Capell info widget also links to the Upgrades so the upgrade path is visible immediately after signing in.

## Update And Security Notices

Capell shows WordPress-style notices without letting the browser run Composer. The installed site phones home to the marketplace, stores the latest response in `capell_update_advisory_snapshots`, and renders that local snapshot in the Upgrades.

The heartbeat sends:

- the verified marketplace `instance_id`;
- the local `webhook_url` and `app_url`;
- the installed Capell version;
- official `capell-app/*` Composer package names, slugs, and versions.

The marketplace returns:

- normal update notices for latest safe releases;
- bug advisories that match installed package constraints;
- security advisories that match installed package constraints;
- release notes and upgrade guide links where available.

Security advisories appear before bug advisories and normal updates. High and critical security advisories also surface as a persistent dashboard notice until the next successful check shows the affected package is no longer vulnerable.

Normal update notices may be dismissed temporarily. High and critical security notices cannot be permanently dismissed while the installed version remains affected.

Composer remains manual in v1 on purpose. Capell tells owners what needs upgrading and gives the suggested Composer command, but package updates still happen through your normal deploy workflow:

```bash
composer update capell-app/blog
php artisan capell:upgrade
```

## Standard flow

```bash
composer update capell-app/capell                  # core
composer update 'capell-app/*'                     # core + every approved package
php artisan capell:upgrade --dry-run               # preview
php artisan capell:upgrade                         # apply
```

The upgrade pipeline runs once and walks every installed package — core and approved add-ons alike. You don't need a separate command per package.

## Why this future-proofs installs

Capell treats upgrades as part of the product, not a pile of release-note errands. Composer remains the source of truth for installed versions, while `capell:upgrade` provides one repeatable operation that packages can plug into.

- **One visible entry point.** Admins can use the Upgrades; deploy scripts can use the same command.
- **Package-aware by default.** Core and approved packages publish their migrations, run their upgrade commands, and record their versions through the same pipeline.
- **Safe to retry.** Upgrade steps are idempotent, logged, and only considered applied after their data changes and success row commit together.
- **Auditable later.** The ledger records step outcomes and version snapshots, so future maintainers can see what happened without reverse-engineering deploy logs.

## What `capell:upgrade` does

`capell:upgrade` takes the cache lock `capell:upgrade` for its entire duration — concurrent runs fail fast — then executes four phases:

1. **Version audit.** Compares Composer's installed versions against the last-known values in the `capell_upgrade_log` table. Flags new packages, removed packages, and downgrades. Downgrades abort unless `--force-downgrade` is passed.
2. **Migrations.** Publishes pending schema migrations into `database/migrations/` and settings migrations into `database/settings/`, then runs `migrate --force` and `settings:migrate --force`. Already-applied migrations are skipped by Laravel's and Spatie's own tracking.
3. **Upgrade steps.** Each registered `UpgradeStepContract` is evaluated against the current `UpgradeContext`. Pending steps (not yet successfully applied, `shouldRun()` true, dependencies satisfied) run in priority order. Step body + log write happen atomically in one DB transaction.
4. **Per-package commands.** Each `$package->getUpgradeCommand()` (e.g. asset publishing) is invoked.

Finally, the current Composer versions are recorded as `type=version_snapshot` rows in the log for audit.

## Single table, two row types

All upgrade state lives in `capell_upgrade_log`. Each row has:

- `type` = `step` or `version_snapshot`
- `key` = step id, or composer package name
- `status` = `success` | `failed` | `skipped` | `rolled_back` | `superseded` | `recorded`
- `meta` (JSON) = duration, output, versions, dependencies, etc.

You can audit upgrades with plain SQL — no joins required.

## Flags

| Flag                           | Purpose                                             |
| ------------------------------ | --------------------------------------------------- |
| `--dry-run`                    | Print the plan; make no changes                     |
| `--force`                      | Skip interactive confirmations                      |
| `--force-downgrade`            | Proceed even if Composer is older than the log says |
| `--force-step=ID` (repeatable) | Re-run a specific step                              |
| `--skip-migrations`            | Don't publish/run migrations                        |
| `--skip-steps`                 | Don't run upgrade steps                             |
| `--only-migrations`            | Run only the migrations phase                       |
| `--only-steps`                 | Run only the upgrade-steps phase                    |
| `--no-clear-cache`             | Skip the cache-clear menu                           |

## Rollback

Steps that implement `rollback()` can be reverted:

```bash
php artisan capell:rollback --step=<step-id>
```

Writes a `rolled_back` row, then marks the original `success` row as `superseded`. Re-running `capell:upgrade` will re-apply the step.

## Source of truth

- **Composer** is authoritative for "what version is installed right now" (`Composer\InstalledVersions::getPrettyVersion()`).
- **`capell_upgrade_log`** (type `version_snapshot`, status `recorded`) is an audit trail — written at the end of each successful upgrade. Use it to answer "what version did we upgrade from?"
- **`capell_upgrade_log`** (type `step`, status `success` and not `superseded`) answers "which steps are currently applied?"

## CI / automated deploys

```bash
php artisan capell:upgrade --force --force-downgrade --no-clear-cache
php artisan optimize
```

Exit code is `0` on success, `1` on failure (including lock contention and downgrade without `--force-downgrade`).

## Pre-upgrade checklist

- Back up the database.
- `php artisan down` if users could be affected.
- Run in staging first.
- Use `--dry-run` in CI before applying.

## Troubleshooting

**"Another upgrade is running"** — a crashed run may have left the lock. `php artisan cache:forget capell:upgrade`.

**A step failed mid-run** — find it with `SELECT * FROM capell_upgrade_log WHERE type='step' AND status='failed';`. Re-running `capell:upgrade` retries it — failures are not considered "applied".

**"Downgrade detected"** — Composer has an older version than the log. Usually a deployment mistake. Restore the newer version, or pass `--force-downgrade` if truly intentional.