21. Aktualizace na novou verzi

MyInvoice.cz denně kontroluje GitHub Releases API a v Systém → Aktualizace (jen admin) zobrazí aktuální i poslední dostupnou verzi spolu s release notes. Aplikaci se updatuje buď z UI (jedním tlačítkem), nebo ručně přes shell — záleží na typu instalace.

21.1 Co všechno se aktualizuje

Aktualizace zahrnuje všechny tři vrstvy aplikace:

Zachovají se: cfg.php, cfg.local.php, private/, storage/, log/ — tj. všechno, co obsahuje konfiguraci a uživatelská data. Migrace nikdy nepřepisují existující data, jen přidávají sloupce/tabulky/indexy.

21.2 Daily check — jak to funguje

Cron skript api/bin/cron-version-check.php se spouští 1× denně, volá GitHub API a cachuje výsledek do tabulky app_meta (klíče latest_version, latest_release_notes, latest_release_url, latest_published_at, last_check_at). UI / footer čte z cache, žádný blocking síťový call při každém načtení stránky.

Plánování cronu

ProstředíPříklad
Linux/cron0 6 * * * cd /opt/myinvoice && php api/bin/cron-version-check.php
Docker (host cron)0 6 * * * docker compose -f /opt/myinvoice/docker-compose.production.yml exec -T app php api/bin/cron-version-check.php
Windows SchedulerDaily, akce: php.exe C:\inetpub\myinvoice\api\bin\cron-version-check.php

Pokud cron nenastavíš, kontrola se nikdy nespustí — admin musí kliknout „Zkontrolovat teď" v UI.

V patičce každé stránky vidíš vX.Y.Z — to je verze, která teď běží. Pokud je k dispozici nová verze a jsi přihlášený jako admin, badge v2.5.0 vedle ní je klikatelný odkaz na Systém → Aktualizace.

Neadminové vidí jen verzi bez badge (badge je čistě admin signál — běžný uživatel s upgradem stejně nic neudělá).

21.4 Aktualizace v UI — Docker

V Systém → Aktualizace klikni na „Aktualizovat na vX.Y.Z". Aplikace zapíše flag soubor upgrade-requested.json uvnitř kontejneru do ${MYINVOICE_DATA_DIR}/storage/ (default /data/storage/ od 3.6.0; ve starších 3-volume instalacích /var/www/html/storage/) a UI začne pollovat. Vlastní upgrade ale provádí host-side watcher — proces běžící mimo container, který má přístup k docker compose na hostu a přes docker compose exec čte/píše do storage volume.

Test režim (jednorázově, ve foregroundu)

Než nainstaluješ watcher jako daemon, otestuj ho ručně v PowerShell / bash okně:

# Linux / macOS
cd /opt/myinvoice
bash cmd/docker-update-watcher.sh
# Windows
cd C:\inetpub\myinvoice
powershell -NoProfile -ExecutionPolicy Bypass -File cmd\docker-update-watcher.ps1

Vidíš [watcher] start, polling storage/upgrade-requested.json inside container every 30s — watcher poslouchá. Klikni v UI „Aktualizovat" a do 30 s zachytí flag, spustí docker-update.{sh,ps1}, výsledek napíše do kontejneru. Watcher zastav Ctrl+C.

Instalace watcheru jako daemon (na produkci)

Linux — systemd unit

sudo tee /etc/systemd/system/myinvoice-update-watcher.service <<'EOF'
[Unit]
Description=MyInvoice update watcher
After=docker.service

[Service]
Type=simple
WorkingDirectory=/opt/myinvoice
ExecStart=/opt/myinvoice/cmd/docker-update-watcher.sh
Restart=always
User=root

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now myinvoice-update-watcher

Logy: journalctl -u myinvoice-update-watcher -f.

Windows — Scheduled Task

schtasks /create /tn "MyInvoice Update Watcher" `
  /tr "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\inetpub\myinvoice\cmd\docker-update-watcher.ps1" `
  /sc onstart /ru SYSTEM /rl HIGHEST

# Spusť hned (ne až po restartu)
schtasks /run /tn "MyInvoice Update Watcher"

Stav úlohy: schtasks /query /tn "MyInvoice Update Watcher" /v /fo list. Stop: schtasks /end /tn "MyInvoice Update Watcher".

Co watcher dělá

  1. Každých 30 s: docker compose exec -T app test -f storage/upgrade-requested.json.
  2. Když ho najde → přečte target_version přes cat, přejmenuje na upgrade-inflight.json přes mv uvnitř kontejneru (zámek proti double-triggeru).
  3. Spustí na hostu cmd/docker-update.{sh,ps1} — ten dělá:
  1. Po restartu kontejneru počká až 60 s, než bude zase responzivní (docker compose exec true), pak zapíše výsledek (success / fail) přes cat > storage/upgrade-result.json zpět do kontejneru.
  2. Plný log běhu na host: /tmp/myinvoice-upgrade-YYYYMMDDTHHMMSSZ.log (Linux) nebo %TEMP%\myinvoice-upgrade-...log (Windows).
  3. UI v Systém → Aktualizace každých 5 s pollne /api/admin/update/ status, který načte upgrade-result.json z kontejneru a zobrazí „Upgrade úspěšně dokončen" nebo „Upgrade selhal" s message.

Pokud watcher neběží

UI sice flag soubor zapíše, ale nikdo ho nezpracuje (UI zůstane věčně ve stavu „Upgrade probíhá…"). Spusť na hostu ručně:

# Linux / macOS
cd /opt/myinvoice
bash cmd/docker-update.sh
docker compose -f docker-compose.production.yml exec app rm -f storage/upgrade-requested.json
# Windows
cd C:\inetpub\myinvoice
.\cmd\docker-update.ps1
docker compose -f docker-compose.production.yml exec app rm -f storage/upgrade-requested.json

(Pokud nepoužíváš production compose, vynechej -f docker-compose.production.yml.)

21.5 Migrace na single-volume layout (3.5.x → 3.6.0)

⚠️ Tohle je breaking změna pro existující Docker instalace 3.5.x a starší. Default Compose layout se mění ze 3-volume (app-log + app-storage + app-private) na single-volume (app-data:/data + MYINVOICE_DATA_DIR=/data). Migrace proběhne automaticky při běžném docker-update.{sh,ps1} — nemusíš dělat nic navíc.

Proč ta změna: v 3-volume layoutu byl soubor cfg.local.php (per-instance overrides z setup wizardu — app.url, auth.require_totp) v ephemeral container filesystému a docker-update.sh ho při recreate kontejneru smazal. Důsledek (reportovaný v issue #23): po updatu Origin mismatch a všechny mutace v UI dostaly 403. Single-volume layout drží cfg.local.php v perzistentním /data volumu, takže image updaty jsou bezpečné.

Co dělá docker-update.{sh,ps1} na 3.6.0

  1. git pull (source mode) nebo docker compose pull (registry mode).
  2. Detekuje existující 3-volume volumes (<project>_app-log, _app-storage, _app-private) a absenci nového <project>_app-data → vypíše prominentní banner a automaticky spustí cmd/docker-migrate-volumes.{sh,ps1}.
  3. Migrace:
  1. Staré volumes nemaže — vypíše docker volume rm příkazy. Smaž je až po ověření, že nová instalace vidí faktury / uploady / sessions.

Ruční migrace (pokud nepoužíváš docker-update)

# Linux / macOS
cd /opt/myinvoice
git pull --ff-only                # přinese nový docker-compose.yml (single-volume)
bash cmd/docker-migrate-volumes.sh  # snapshotne cfg.local.php, zkopíruje data, up -d
# Windows
cd C:\inetpub\myinvoice
git pull --ff-only
.\cmd\docker-migrate-volumes.ps1

Pro registry mode (jen docker-compose.production.yml, bez .git) si stáhni nové compose soubory:

curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/docker-compose.production.yml
curl -O https://raw.githubusercontent.com/radekhulan/myinvoice/master/cmd/docker-migrate-volumes.sh
chmod +x docker-migrate-volumes.sh
./docker-migrate-volumes.sh

Idempotence + recovery

Skript je idempotentní — opětovné spuštění detekuje, že staré volumes už neexistují (nebo že nový volume už obsahuje data) a jen vypíše stav.

Pokud něco selže před docker volume rm, stará data jsou pořád celá v <project>_app-log/storage/private — ručně je restoreneš přes:

docker run --rm -v myinvoice_app-storage:/old:ro -v myinvoice_app-data:/new alpine \
  sh -c "cp -a /old/. /new/storage/"

21.6 Aktualizace v UI — nativní instalace

Pro nativní deployment (sdílený hosting / VPS bez Dockeru) UI v této verzi (v3.0.0) zatím neimplementuje automatický download release tarballu — pouze ti ukáže copy-paste příkazy:

git fetch --tags
git checkout vX.Y.Z
cd api && composer install --no-dev && cd ..
cd web && pnpm install && pnpm build && cd ..
php tools/generateManualHtml.php
php tools/exportManualToPdf.php
php api/bin/migrate.php

Vyžaduje na hostu PHP CLI + Composer + Node + pnpm. Pokud Composer/ Node nemáš (typicky sdílený hosting), je nejjednodušší cesta:

  1. Stáhni production bundle z release page: https://github.com/radekhulan/myinvoice/releases/tag/vX.Y.Z → asset myinvoice-X.Y.Z.tar.gz. Tarball má všechno potřebné už vyrobené (vendor, web/dist, manual). SHA-256 checksum je v myinvoice-X.Y.Z.tar.gz.sha256.
  2. Rozbal přes web rozhraní hostingu nebo SSH:
   tar -xzf myinvoice-X.Y.Z.tar.gz --strip-components=1 \
     --exclude='cfg.php' --exclude='cfg.local.php' \
     --exclude='storage' --exclude='private' --exclude='log'
  1. Spusť migraci přes hosting cron / SSH: php api/bin/migrate.php

🛈 Phase 2 (plánováno na příští minor release) doplní automatický download bundle + extrakci přímo z UI tlačítka, takže krok 1+2 odpadne.

21.7 Co když upgrade selže

Docker watcher

Watcher zapíše storage/upgrade-result.json se status: "failed" a plným logem do storage/upgrade-YYYYMMDDTHHMMSSZ.log. UI ho zobrazí. Typické příčiny:

Container s aplikací se restartoval, ale data v DB volume zůstávají nedotčena.

Nativní

Když selže composer install nebo pnpm build, soubory v api/vendor/ nebo web/dist/ mohou být v inkonzistentním stavu. Recovery:

git checkout vPREDCHOZI-VERZE
cd api && composer install --no-dev && cd ..
cd web && pnpm install && pnpm build && cd ..

Pokud migrate.php selhal, vrátit se nejde — musíš debugovat konkrétní migraci. Záloha DB je tvoje odpovědnost (kapitola § 16 Exporty).

21.8 Dohled na nové verze bez UI

Pokud nemáš administrátorský přístup do UI, ale chceš vědět, kdy je nová verze, můžeš pollovat veřejný endpoint:

curl -s https://myinvoice.tvuj-server.cz/api/version | jq

Vrátí { "current": "3.0.0", "latest": "3.1.0", "has_update": true, "release_url": "https://github.com/.../v3.1.0" }. Tohle je veřejný endpoint bez auth, ale stejná data vidí kdokoliv s přístupem k aplikaci ve footru.