# Upgrading an existing site

This is the procedure for moving a running shopdb-flask instance to a newer
version. For a first-time install use [DEPLOY.md](DEPLOY.md) instead.

Each site is single-tenant (ADR-004), so an upgrade touches only that site's
own stack. Read the ADRs added since your last update (`docs/adr/`) and the
`CHANGELOG.md` before starting; a breaking ADR may require coordinated work.

## Step 0: Back up first

Never upgrade without a fresh backup you have tested. Take a full database dump
and copy the `instance/` directory. See [BACKUP-RESTORE.md](BACKUP-RESTORE.md).

```bash
# Docker:
docker compose exec -T db mysqldump -u root -p"${MYSQL_ROOT_PASSWORD}" shopdb_flask | gzip > pre-upgrade-$(date +%F).sql.gz
cp -a instance/ instance-backup-$(date +%F)/
```

## Step 1: Get the new code / image

```bash
git pull origin main
```

The application is distributed through the internal GE Aerospace git server; pull
from there. There is no external image registry.

## Step 2: Rebuild

The Docker image builds the Vue frontend in-image, so a container rebuild picks
up frontend changes automatically:

```bash
docker compose build api
docker compose up -d api
```

Bare-metal / venv install: rebuild the frontend by hand and refresh Python
dependencies:

```bash
source venv/bin/activate
pip install -r requirements.txt
cd frontend && npm ci && npm run build && cd ..
```

## Step 3: Apply migrations

```bash
# Docker:
docker compose exec api flask db upgrade
docker compose exec api flask plugin upgrade-all
# venv:
flask db upgrade
flask plugin upgrade-all
```

`flask db upgrade` applies any new migrations in the core Alembic chain.
`flask plugin upgrade-all` then applies any new per-plugin migrations (each
bundled plugin owns its schema going forward - see ADR-008). Both are
idempotent; running them when already at head is a no-op.

## Step 4: Re-seed permissions and settings

New versions may add RBAC permissions or default Settings keys. Both seeders are
idempotent - they add anything missing and leave existing rows untouched, so
your site's customized values are preserved.

```bash
# Docker:
docker compose exec api flask seed permissions
docker compose exec api flask seed settings
# venv:
flask seed permissions
flask seed settings
```

## Step 5: Restart

```bash
docker compose restart api
# venv: restart your process manager, e.g. pm2 restart shopdb-flask-api shopdb-flask-ui
```

Confirm the app is healthy (login page renders, `/api/auth/login` returns a
`VALIDATION_ERROR` for an empty body rather than a 500).

## Version-specific notes

### Upgrading to v0.11.0: the floor map became buildings and levels

Nothing to do, and nothing moves on screen. Run both migration steps as always:

```bash
flask db upgrade
flask plugin upgrade-all
```

`7d33_buildings_and_levels` creates one building ("Main") and one default level
("Ground floor") carrying the blueprint paths and dimensions from your `map_*`
settings, then assigns every already-placed asset and location to that level.
Unplaced things stay unplaced. The old settings rows are deliberately left in
place, so a downgrade still finds the blueprint.

Two things to know afterwards:

- **A second floor or building is a Settings task**, not a migration: add it under
  Settings > Buildings and levels, upload its blueprint, then move the markers
  that belong on it. `POST /api/mappositions/transform` moves a whole level's
  markers onto a redrawn plan from landmark pairs, dry run by default.
- **Replacing a blueprint with one of DIFFERENT dimensions moves every marker on
  that level**, because the coordinates are pixels of the old size. Upload it, then
  recalibrate with landmarks rather than editing the level's width and height by
  hand. The API refuses to adopt a new size on a level that already has markers,
  for this reason.

If you wrote a plugin that stores map positions, see CONTRACT-STABILITY.md: the
contract is 0.20.0 and a position must now carry its `levelid`.

### Upgrading to v0.5.0 or later: bundled the reference site floor plan removed

Versions before 0.5 shipped the the reference site facility floor-plan PNGs as the
map default (`/static/images/sitemap2025-light.png` and `-dark.png`). v0.5+
removes those bundled PNGs and ships a generic placeholder SVG instead.

If your instance's `map_blueprint_light` / `map_blueprint_dark` Settings still
point at `/static/images/sitemap2025-*`, the map will 404 those images after the
upgrade. Re-upload your own floor plan in **Settings > Floor Map**. Uploaded floor
plans are stored under `instance/` and survive upgrades, so a site that already
uploaded its own plan is unaffected. Only instances still using the old bundled
default need to act.

To check what your instance points at:

```bash
docker compose exec -T api flask shell <<'PY'
from shopdb.core.models.setting import Setting
print(Setting.query.filter(Setting.key.like('map_blueprint%')).all())
PY
```

## Windows sites (installer-built)

Run a newer installer `.exe` over the existing install. That is the whole
procedure - none of the manual steps above apply.

[UPDATES-WINDOWS.md](UPDATES-WINDOWS.md) is the operator-facing version of this:
downtime, what is and is not touched, security updates, and the effect on other
sites sharing the same IIS server.

It backs the database up first and verifies the dump, applies the core and plugin
migrations, restores from that backup if a migration fails, and refuses to
install an older build over a newer one. Your `.env`, your data and your
`web.config` are kept.

Before the first upgrade, confirm `mysqldump` is available
(`.\shopdb-admin.ps1 check`). Without it the pre-upgrade backup is skipped, which
is the one you would want if a migration went wrong.

Afterwards:

```powershell
.\shopdb-admin.ps1 status
.\shopdb-admin.ps1 verify
```

See [OPERATE-WINDOWS.md](OPERATE-WINDOWS.md).

## See also

- [BACKUP-RESTORE.md](BACKUP-RESTORE.md) - what to back up and how to restore
- [INSTALL-WINDOWS.md](INSTALL-WINDOWS.md) - Windows Server install
- [CONFIG.md](CONFIG.md) - environment variables and Setting keys
- [DEPLOY.md](DEPLOY.md) - first-time deploy runbook
- `CHANGELOG.md` - what changed in each release
