v0.8.13-beta - updates voor beta en releases herstellen
All checks were successful
Deploy server-up (dev) / deploy (push) Successful in 7m43s

This commit is contained in:
Ramon 2026-08-04 16:10:59 +02:00
parent 331475415a
commit 6cc078e7b9
15 changed files with 268 additions and 63 deletions

View file

@ -23,6 +23,10 @@ PORT=5000
# hand aanpassen kan om terug te rollen naar een eerdere versie.
#SU_TAG=0.5.40-beta
# De versie die bij die image-tag hoort. De deploy-workflow en updatehelper
# houden SU_TAG en SU_VERSION samen bij; normaal hoef je dit niet zelf te zetten.
#SU_VERSION=0.5.40-beta
# Registry-image, zónder tag. Staat dit ingevuld, dan draait deze server vanaf
# een registry en werkt de knop "Nu bijwerken" in de interface. Laat het leeg
# als de server zelf bouwt na een push naar Git — dan is Git de update-route.

View file

@ -40,6 +40,7 @@ jobs:
if [ -z "$VERSION" ]; then
VERSION="$(tr -d '[:space:]' < VERSION)"
fi
VERSION="${VERSION#v}"
IMAGE="${{ vars.REGISTRY }}/${{ vars.OWNER }}/server-up"
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
echo "IMAGE=${IMAGE}" >> "$GITHUB_ENV"
@ -131,6 +132,7 @@ jobs:
--pull \
--build-arg "SU_VERSION=${VERSION}" \
-t "${IMAGE}:${VERSION}" \
-t "${IMAGE}:v${VERSION}" \
-t "${IMAGE}:${EXTRA_TAG}" \
.
@ -138,8 +140,11 @@ jobs:
run: |
set -euo pipefail
docker push "${IMAGE}:${VERSION}"
# Compatibiliteit met Server Up <= 0.8.12: die vroeg ten onrechte
# om de Forgejo-tag mét v. Nieuwe versies gebruiken de kale tag.
docker push "${IMAGE}:v${VERSION}"
docker push "${IMAGE}:${EXTRA_TAG}"
echo "Gepusht: ${IMAGE}:${VERSION} en ${IMAGE}:${EXTRA_TAG}"
echo "Gepusht: ${IMAGE}:${VERSION}, :v${VERSION} en :${EXTRA_TAG}"
- name: Uitloggen
if: always()

View file

@ -146,8 +146,8 @@ jobs:
if [ -w .env ] || { [ ! -e .env ] && [ -w . ]; }; then
touch .env
sed -i '/^SU_TAG=/d' .env
echo "SU_TAG=${VERSION}" >> .env
sed -i '/^SU_TAG=/d; /^SU_VERSION=/d' .env
printf 'SU_TAG=%s\nSU_VERSION=%s\n' "${VERSION}" "${VERSION}" >> .env
echo "Inhoud van $(pwd)/.env:"
grep -vE '^\s*(#|$)' .env | sed 's/^/ /'
else
@ -172,6 +172,11 @@ jobs:
| docker login "${{ vars.REGISTRY }}" -u "${{ vars.OWNER }}" --password-stdin
docker tag "${SU_IMAGE}:${VERSION}" "${SU_IMAGE}:${CHANNEL}" || true
docker push "${SU_IMAGE}:${VERSION}"
# Compatibiliteit met Server Up <= 0.8.12: de oude updater vroeg
# om de Forgejo-tag mét v. Hiermee kan die zichzelf één keer naar
# de gerepareerde updater brengen; nieuwe versies gebruiken de kale tag.
docker tag "${SU_IMAGE}:${VERSION}" "${SU_IMAGE}:v${VERSION}"
docker push "${SU_IMAGE}:v${VERSION}"
# Pre-releases krijgen geen 'latest': dat hoort bij stable.
case "$VERSION" in
*-*) docker tag "${SU_IMAGE}:${VERSION}" "${SU_IMAGE}:beta"

View file

@ -149,8 +149,8 @@ jobs:
if [ -w .env ] || { [ ! -e .env ] && [ -w . ]; }; then
touch .env
sed -i '/^SU_TAG=/d' .env
echo "SU_TAG=${VERSION}" >> .env
sed -i '/^SU_TAG=/d; /^SU_VERSION=/d' .env
printf 'SU_TAG=%s\nSU_VERSION=%s\n' "${VERSION}" "${VERSION}" >> .env
echo "Inhoud van $(pwd)/.env:"
grep -vE '^\s*(#|$)' .env | sed 's/^/ /'
else

View file

@ -1,3 +1,26 @@
# v0.8.13-beta — De update haalt nu de juiste release op
**Bijwerken vanuit de interface vroeg Docker om een image dat niet bestond.**
Forgejo noemt een release `v0.8.13`, terwijl de workflows bewust image
`:0.8.13` publiceren. De interface stuurde de Git-tag mét `v` door en dus
mislukte iedere beta- en stable/main-update bij `docker pull`.
- **Eén expliciete registry-tag.** De updatecheck geeft naast de Git-tag nu
`image_tag` terug. Frontend, endpoint en pull-laag normaliseren allemaal de
ene voorloop-`v`. De workflows publiceren ook `:v<versie>` als tijdelijke
compatibiliteitstag, zodat de al geïnstalleerde defecte updater zichzelf naar
deze reparatie kan brengen.
- **De herstart gebruikt exact het bestaande Compose-project.** Projectnaam,
projectmap en alle configuratiebestanden komen uit de containerlabels.
`--no-build --pull never` voorkomt dat Compose tijdens de update oude
broncode bouwt of onverwacht een andere tag ophaalt.
- **Tag en versie blijven bij elkaar.** De helper en beide deploy-workflows
schrijven `SU_TAG` en `SU_VERSION` samen. Ook na een handmatige Compose-
herstart toont en draait Server Up daardoor dezelfde beta- of releaseversie.
- **Terugrollen blijft naar een echte versie wijzen.** Als de container via de
beweeglijke alias `beta` of `latest` draaide, wordt voortaan het ingebakken
OCI-versielabel als terugrolpunt onthouden.
# v0.8.12-beta — De image-controle van twaalf minuten naar seconden
De controle of elk image nog bestaat kostte elf à twaalf minuten, bij élke push,

View file

@ -1 +1 @@
0.8.12-beta
0.8.13-beta

View file

@ -3,8 +3,8 @@ services:
build:
context: .
args:
# Bakt de versie in het image. De deploy-workflow zet SU_VERSION
# (git-tag of main-<sha>); zonder waarde valt het terug op 'dev'.
# Bakt de versie in het image. De deploy-workflow zet SU_VERSION uit
# VERSION of de release-tag; zonder waarde valt het terug op 'dev'.
SU_VERSION: ${SU_VERSION:-dev}
# Image + tag staan los, zodat bijwerken vanuit de interface werkt: die
# schrijft SU_TAG in .env en laat compose de container hercreëren.

View file

@ -56,15 +56,17 @@ aangeboden.
### Wat er gebeurt als je op "Nu bijwerken" klikt
```
1. docker pull git.example.com/bes-r/server-up:0.5.10
2. SU_TAG=0.5.10 wegschrijven in het .env van de deploy-map
3. helper-container starten: sleep 5 && docker compose up -d server-up
4. Server Up wordt vervangen; de browser pollt /healthz tot de nieuwe versie leeft
1. Forgejo-tag `v0.5.10` vertalen naar registry-tag `0.5.10`
2. docker pull git.example.com/bes-r/server-up:0.5.10
3. SU_TAG=0.5.10 en SU_VERSION=0.5.10 in de deploy-.env vastleggen
4. helper starten met exact dezelfde Compose-projectnaam en configuratiebestanden
5. Server Up vervangen; de browser pollt /healthz tot de nieuwe versie leeft
```
Stap 3 is nodig omdat een container zichzelf niet kan hercreëren: het commando
Stap 4 is nodig omdat een container zichzelf niet kan hercreëren: het commando
zou halverwege zijn eigen proces afbreken. De helper draait op het zojuist
gehaalde image — dat bevat de docker- en compose-CLI al — en ruimt zichzelf op.
Met `--no-build --pull never` gebruikt Compose gegarandeerd precies dat image.
De deploy-map wordt niet geraden maar uitgelezen uit de compose-labels op de
eigen container (`com.docker.compose.project.working_dir`). Draait Server Up niet
@ -170,6 +172,7 @@ Op de server waar Server Up draait, in de deploy-map (`/opt/server-up`):
# .env
SU_IMAGE=10.0.20.22:3000/bes-r/server-up
SU_TAG=0.5.10
SU_VERSION=0.5.10
BIND=127.0.0.1
PORT=5000
```
@ -178,10 +181,16 @@ PORT=5000
registry en werkt de updateknop. Staat het er niet, dan bouwt de server lokaal
zoals voorheen en meldt de interface netjes waarom bijwerken niet kan.
> Server Up schrijft `SU_TAG` niet zelf in je `.env`, en dat is met opzet: de
> installatiemap is nergens in de container gemount. De helper-container die de
> hercreatie doet, mount hem wél en zet de tag daar. Tot v0.7.95 probeerde
> Server Up het zelf, en faalde dat dus altijd.
De Git-release heet bijvoorbeeld `v0.5.10`, maar de canonieke image-tag heet
`:0.5.10`. Server Up verwijdert die ene voorloop-`v` automatisch voor zowel
stable/main- als beta-releases. De build publiceert daarnaast `:v0.5.10` als
compatibiliteitstag, zodat installaties met de oude updater één keer zonder
handmatige ingreep naar de gerepareerde versie kunnen overstappen.
> De draaiende Server Up-container schrijft niet rechtstreeks in je deploy-
> `.env`: de installatiemap is daar niet gemount. De updatehelper mount die map
> wel en werkt `SU_TAG` en `SU_VERSION` samen bij. Zo blijft ook een latere
> handmatige `docker compose up -d` op dezelfde versie.
Eerste keer overstappen van lokaal bouwen naar de registry:

View file

@ -407,10 +407,11 @@ def api_update_apply():
Kort na afloop verdwijnt deze container; de front-end pollt daarna /healthz.
"""
d = request.json or {}
tag = (d.get("tag") or "").strip()
tag = updater.image_tag(d.get("tag") or "")
if not tag:
info = updater.check(VERSION)
tag = info.get("latest_tag") or ""
tag = (info.get("image_tag")
or updater.image_tag(info.get("latest_tag") or ""))
if not tag:
return jsonify(ok=False, msg="Geen versie opgegeven en geen release gevonden"), 400
st = selfupdate.status()

View file

@ -6,8 +6,8 @@ Up alleen het voorwerk (image ophalen, tag wegschrijven) en laat het de
daadwerkelijke hercreatie over aan een korte **helper-container**:
1. `docker pull <registry>/<repo>:<tag>` nieuw image binnenhalen
2. `SU_TAG=<tag>` in het .env van de deploy-map schrijven
3. helper starten: sleep 5 && docker compose up -d server-up
2. `SU_TAG=<tag>` en `SU_VERSION=<tag>` in de deploy-.env schrijven
3. helper starten met exact hetzelfde Compose-project en -configuratie
4. helper stopt zichzelf (`--rm`)
De helper draait op het zojuist gepulde image: dat bevat de docker- en
@ -27,7 +27,7 @@ import time
from pathlib import Path
import core as cfg
from core import docker
from core import docker, updater
# Labels die `docker compose` op elke container zet.
_LBL_PROJECT = "com.docker.compose.project"
@ -70,6 +70,7 @@ def inspect_self() -> dict:
"working_dir": labels.get(_LBL_WORKDIR, ""),
"config_files": labels.get(_LBL_CONFIGS, ""),
"service": labels.get(_LBL_SERVICE, "") or "server-up",
"version": labels.get("org.opencontainers.image.version", ""),
}
@ -159,6 +160,10 @@ def _login(log_fn=None) -> tuple[bool, str]:
def pull(tag: str, log_fn=None) -> tuple[bool, str]:
"""Haal `<repo>:<tag>` binnen. Retourneert (ok, image of foutmelding)."""
# Forgejo noemt de Git-tag v0.8.13, maar de workflows pushen :0.8.13.
# Normaliseer hier óók, zodat oude clients die nog latest_tag versturen
# nooit een niet-bestaande :v0.8.13 proberen op te halen.
tag = updater.image_tag(tag)
if not _TAG_RE.match(tag or ""):
return False, f"Ongeldige tag: {tag!r}"
repo = image_repo()
@ -180,22 +185,19 @@ def pull(tag: str, log_fn=None) -> tuple[bool, str]:
# ── Toepassen ────────────────────────────────────────────────────────────────
def _tag_script(tag: str) -> str:
"""Sh-regels die SU_TAG in het .env van de werkmap zetten.
"""Sh-regels die de image-tag én versie in de deploy-.env vastleggen.
Dit gebeurt in de helper-container en niet hier, en dat is geen detail: de
installatiemap (`/opt/server-up`) is nérgens in deze container gemount. Er
zijn maar drie mounts de docker-socket, het `su-data`-volume en `BASE_DIR`
en de installatiemap zit daar niet bij. Zelf schrijven belandde dus in de
overlay van de container of, waarschijnlijker, mislukte omdat de map hier
niet bestaat. Bijwerken vanuit de interface kon daardoor nooit werken.
De helper mount `working_dir` wél, op hetzelfde pad, en draait als root.
Dit gebeurt in de helper-container: de installatiemap is niet in de
draaiende Server Up-container gemount, maar wordt wel expliciet aan de
helper gekoppeld. SU_VERSION is nodig voor oudere compose-bestanden die de
ingebakken imageversie met een runtimewaarde overschrijven.
"""
q = shlex.quote(f"SU_TAG={tag}")
tag_regel = shlex.quote(f"SU_TAG={tag}")
versie_regel = shlex.quote(f"SU_VERSION={tag}")
return (
"touch .env; "
"sed -i '/^SU_TAG=/d' .env; "
f"printf '%s\\n' {q} >> .env; "
"sed -i '/^SU_TAG=/d; /^SU_VERSION=/d' .env; "
f"printf '%s\\n' {tag_regel} {versie_regel} >> .env; "
)
@ -216,6 +218,7 @@ def apply(tag: str, log_fn=None) -> tuple[bool, str]:
Bij succes keert deze functie terug terwijl de helper al gestart is; enkele
seconden later verdwijnt dit proces omdat de container hercreëerd wordt.
"""
tag = updater.image_tag(tag)
st = status()
if not st["can_apply"]:
return False, st["reason"]
@ -229,39 +232,66 @@ def apply(tag: str, log_fn=None) -> tuple[bool, str]:
return False, image_of_fout
image = image_of_fout
# Onthoud waar we vandaan komen, zodat terugrollen mogelijk blijft als het
# nieuwe image niet start.
# Een alias als latest of beta verschuift bij iedere release en is dus geen
# bruikbaar terugrolpunt. Het OCI-versielabel verwijst naar de echte,
# onveranderlijke versietag; val terug op de gebruikte imagetag voor oude
# images zonder label.
versie = updater.image_tag(info.get("version", ""))
huidig = info.get("image", "")
vorige_tag = huidig.rsplit(":", 1)[-1] if _has_tag(huidig) else "latest"
if _TAG_RE.match(versie or "") and versie != "dev":
vorige_tag = versie
else:
vorige_tag = huidig.rsplit(":", 1)[-1] if _has_tag(huidig) else "latest"
cfg.patch({"UPDATE_PREVIOUS_TAG": vorige_tag,
"UPDATE_LAST_APPLIED": {"tag": tag, "ts": time.time(),
"from": vorige_tag}})
if log_fn:
log_fn(f"Vorige versie: {vorige_tag} (terugrollen kan vanuit Instellingen)")
log_fn(f"SU_TAG={tag} wordt door de helper in {werkmap}/.env gezet")
log_fn(f"SU_TAG en SU_VERSION worden in {werkmap}/.env bijgewerkt")
log_fn("Helper starten die de container hercreëert…")
ok, melding = _spawn_helper(image, werkmap, service, tag, log_fn)
ok, melding = _spawn_helper(
image, werkmap, service, tag,
project=info.get("project", ""),
config_files=info.get("config_files", ""),
log_fn=log_fn,
)
if not ok:
return False, melding
return True, (f"Bijwerken naar {tag} gestart. Server Up herstart nu; "
"de interface komt over enkele seconden terug.")
def _compose_up(working_dir: str, project: str, config_files: str,
service: str) -> str:
"""Bouw het commando voor exact het huidige Compose-project."""
cmd = ["docker", "compose", "--project-directory", working_dir]
if project:
cmd.extend(["--project-name", project])
for config in (p.strip() for p in (config_files or "").split(",")):
if config:
cmd.extend(["--file", config])
# Het doelimage is vlak hiervoor expliciet gepulld. Bouw nooit stilletjes
# oude broncode uit de deploy-map en haal tijdens de herstart niets anders.
cmd.extend(["up", "-d", "--remove-orphans", "--no-build", "--pull", "never",
service])
return shlex.join(cmd)
def _spawn_helper(image: str, working_dir: str, service: str, tag: str,
project: str = "", config_files: str = "",
log_fn=None) -> tuple[bool, str]:
"""Start een korte container die `docker compose up -d <service>` uitvoert.
"""Start een helper die het bestaande Compose-project exact hercreëert.
Draait op hetzelfde (nieuwe) image, dat de docker- en compose-CLI al bevat.
`sleep` geeft dit proces de tijd om zijn HTTP-antwoord af te maken voordat
het onder zijn eigen voeten wordt weggehaald.
De helper zet ook SU_TAG: hij mount de werkmap, deze container niet.
Projectnaam en configuratiebestanden komen uit de labels van de huidige
container. Daardoor werkt dit ook bij `docker compose -p ... -f ...`, en
wordt niet per ongeluk een tweede project gestart. `--no-build` garandeert
dat Compose het zojuist gepullde release-image gebruikt.
"""
script = (f"sleep 5; "
f"{_tag_script(tag)}"
f"docker compose up -d --remove-orphans {shlex.quote(service)}")
compose_up = _compose_up(working_dir, project, config_files, service)
script = f"sleep 5; {_tag_script(tag)}{compose_up}"
cmd = [docker._bin(), "run", "--rm", "--detach",
"--name", "server-up-updater",
"-v", "/var/run/docker.sock:/var/run/docker.sock",

View file

@ -67,6 +67,17 @@ def is_prerelease(version: str) -> bool:
return bool(t and t[3] == 0)
def image_tag(release_tag: str) -> str:
"""Vertaal een Forgejo-release-tag naar de tag uit onze registry.
Git-tags heten volgens afspraak `v0.8.13`; de build-workflows verwijderen
die voorloop-v en pushen `:0.8.13`. Alleen een geldige semver wordt
aangepast, zodat een handmatige registry-tag zoals `beta` intact blijft.
"""
tag = (release_tag or "").strip()
return tag[1:] if tag.startswith("v") and _parse(tag) else tag
def _releases(url: str) -> list[dict]:
req = urllib.request.Request(
url, headers={"Accept": "application/json", "User-Agent": "server-up"})
@ -91,7 +102,8 @@ def check(current: str, force: bool = False) -> dict:
ch = channel()
res = {"enabled": bool(url), "current": current, "channel": ch,
"current_is_prerelease": is_prerelease(current),
"latest": None, "latest_tag": "", "update_available": False,
"latest": None, "latest_tag": "", "image_tag": "",
"update_available": False,
"url": "", "notes": "", "prerelease": False, "error": "",
"checked_at": time.time()}
if not url:
@ -121,7 +133,8 @@ def check(current: str, force: bool = False) -> dict:
tag = (best.get("tag_name") or best.get("name") or "")
res["latest_tag"] = tag
res["latest"] = tag.lstrip("v")
res["image_tag"] = image_tag(tag)
res["latest"] = res["image_tag"]
res["url"] = best.get("html_url") or best.get("url") or ""
res["notes"] = best.get("body") or ""
res["prerelease"] = bool(best.get("prerelease"))

View file

@ -4078,7 +4078,7 @@ function app() {
// Bijwerken: de container vervangt zichzelf, dus na het startsein is deze
// pagina zijn server kwijt. We pollen /healthz tot de nieuwe versie leeft.
async applyUpdate() {
const naar = this.update.latest_tag || this.update.latest;
const naar = this.update.image_tag || this.update.latest || this.update.latest_tag;
if (!confirm(this.t('update_confirm', {tag: naar}))) return;
this.updating = true;
const d = await this.rpc('/api/update/apply', { tag: naar });

View file

@ -349,6 +349,18 @@ check('balk staat halverwege', c.job.progress.pct === 50);
// De poortcontrole praat met de backend, dus die checks zijn asynchroon.
// Top-level await kan niet in een CommonJS-script; vandaar deze afsluiting.
(async () => {
// Forgejo noemt de release v0.8.13-beta, maar de registry-workflow pusht de
// image-tag zonder v. De updateknop moet de expliciete image-tag gebruiken.
let updateBody = null;
global.confirm = () => true;
c.update = { current: '0.8.12-beta', latest: '0.8.13-beta',
latest_tag: 'v0.8.13-beta', image_tag: '0.8.13-beta' };
c.rpc = async (url, body) => { updateBody = body; return { job_id: 'upd1' }; };
c.pollJob = () => {};
await c.applyUpdate();
check('update gebruikt registry-tag zonder v',
updateBody && updateBody.tag === '0.8.13-beta');
// ── De hoofdmap ───────────────────────────────────────────────────────────
let gevraagdPad = null;
c.rpc = async (url) => {

View file

@ -106,11 +106,25 @@ def test_pull_weigert_ongeldige_tag(su, monkeypatch, tag):
assert "Ongeldige tag" in msg
def test_pull_stript_de_v_van_een_forgejo_release(su, monkeypatch):
"""De workflow pusht :0.8.13-beta, niet :v0.8.13-beta."""
_fake_inspect(su, monkeypatch)
getrokken = []
monkeypatch.setattr(su, "_login", lambda log_fn=None: (True, ""))
monkeypatch.setattr(
su.docker, "_stream",
lambda cmd, **kw: getrokken.append(cmd[-1]) or 0,
)
ok, image = su.pull("v0.8.13-beta")
assert ok is True
assert image == "git.example.com/bes-r/server-up:0.8.13-beta"
assert getrokken == [image]
# ── SU_TAG wegschrijven ──────────────────────────────────────────────────────
# Dit gebeurde eerder in Python, hier in de container. Maar de installatiemap is
# hier nergens gemount — er zijn maar drie mounts: de docker-socket, su-data en
# BASE_DIR. Bijwerken vanuit de interface kon daardoor nooit werken. Het gebeurt
# nu in de helper, die de werkmap wél mount, dus toetsen we het shellfragment.
# De draaiende container kan de deploy-.env niet schrijven: de installatiemap
# is daar niet gemount. De helper mount de werkmap wel en houdt tag en versie
# bij elkaar, dus toetsen we het shellfragment rechtstreeks.
def _draai_tagscript(su, map_: Path, tag: str):
import subprocess
@ -121,14 +135,19 @@ def _draai_tagscript(su, map_: Path, tag: str):
def test_tagscript_maakt_env_aan(su, tmp_path):
assert _draai_tagscript(su, tmp_path, "0.5.10").strip() == "SU_TAG=0.5.10"
assert _draai_tagscript(su, tmp_path, "0.5.10").splitlines() == [
"SU_TAG=0.5.10", "SU_VERSION=0.5.10",
]
def test_tagscript_vervangt_bestaande_regel(su, tmp_path):
(tmp_path / ".env").write_text("BIND=0.0.0.0\nSU_TAG=0.5.00\nPORT=5000\n")
(tmp_path / ".env").write_text(
"BIND=0.0.0.0\nSU_TAG=0.5.00\nSU_VERSION=0.5.00\nPORT=5000\n")
regels = _draai_tagscript(su, tmp_path, "0.5.10").splitlines()
assert regels == ["BIND=0.0.0.0", "PORT=5000", "SU_TAG=0.5.10"]
assert regels == ["BIND=0.0.0.0", "PORT=5000", "SU_TAG=0.5.10",
"SU_VERSION=0.5.10"]
assert sum(1 for r in regels if r.startswith("SU_TAG=")) == 1
assert sum(1 for r in regels if r.startswith("SU_VERSION=")) == 1
def test_tagscript_behoudt_andere_instellingen(su, tmp_path):
@ -136,6 +155,7 @@ def test_tagscript_behoudt_andere_instellingen(su, tmp_path):
inhoud = _draai_tagscript(su, tmp_path, "0.5.10")
assert "SU_IMAGE=git.example.com/bes-r/server-up" in inhoud
assert "SU_TAG=0.5.10" in inhoud
assert "SU_VERSION=0.5.10" in inhoud
def test_tagscript_laat_zich_niet_uitbreiden(su, tmp_path):
@ -144,11 +164,11 @@ def test_tagscript_laat_zich_niet_uitbreiden(su, tmp_path):
inhoud = _draai_tagscript(su, tmp_path, "1.0.0'; touch /tmp/su-inbraak; echo '")
assert not Path("/tmp/su-inbraak").exists(), "de tag brak uit het commando"
assert inhoud.count("SU_TAG=") == 1
assert inhoud.count("SU_VERSION=") == 1
def test_de_helper_zet_de_tag_en_mount_de_werkmap(su, monkeypatch):
"""Zonder de mount schrijft de helper in zijn eigen overlay, en zonder het
tag-fragment start compose gewoon het oude image opnieuw."""
"""De helper gebruikt dezelfde projectnaam en bestanden als de container."""
aanroepen = []
def nep_run(cmd, **kw):
@ -156,14 +176,82 @@ def test_de_helper_zet_de_tag_en_mount_de_werkmap(su, monkeypatch):
return type("R", (), {"returncode": 0, "stdout": "", "stderr": ""})()
monkeypatch.setattr(su.docker, "_run", nep_run)
su._spawn_helper("git.example.com/bes-r/server-up:0.5.10",
"/opt/server-up", "server-up", "0.5.10")
su._spawn_helper(
"git.example.com/bes-r/server-up:0.5.10",
"/opt/server-up", "server-up", "0.5.10",
project="eigen-project",
config_files="/opt/server-up/compose.yml,/opt/server-up/extra.yml",
)
# De eerste aanroep ruimt een blijven hangen helper op; de tweede is de start.
cmd = next(c for c in aanroepen if "run" in c)
assert "-v" in cmd and "/opt/server-up:/opt/server-up" in cmd
script = cmd[-1]
assert "SU_TAG=0.5.10" in script, "de helper zet de tag niet"
assert "docker compose up -d" in script
assert "SU_VERSION=0.5.10" in script, "de helper zet de versie niet"
assert "--project-directory /opt/server-up" in script
assert "--project-name eigen-project" in script
assert "--file /opt/server-up/compose.yml" in script
assert "--file /opt/server-up/extra.yml" in script
assert "up -d --remove-orphans --no-build --pull never server-up" in script
def test_apply_endpoint_normaliseert_release_tag(client, monkeypatch):
"""Ook een oudere UI mag :v0.8.13 nooit aan Docker doorgeven."""
from core import jobs, selfupdate
csrf = login(client)
gezien = []
monkeypatch.setattr(selfupdate, "status", lambda: {
"can_apply": True, "reason": "", "previous_tag": "",
})
monkeypatch.setattr(
selfupdate, "apply",
lambda tag, log_fn=None: (gezien.append(tag) or True, "gestart"),
)
monkeypatch.setattr(jobs, "run", lambda fn, *args: fn(*args))
r = client.post("/api/update/apply", json={"tag": "v0.8.13-beta"},
headers={"X-CSRF-Token": csrf})
assert r.status_code == 200
assert r.get_json()["tag"] == "0.8.13-beta"
assert gezien == ["0.8.13-beta"]
def test_apply_onthoudt_versie_in_plaats_van_beweeglijke_alias(
su, env, monkeypatch):
_fake_inspect(su, monkeypatch, image="git.example.com/bes-r/server-up:beta",
version="0.8.12-beta")
monkeypatch.setattr(
su, "pull",
lambda tag, log_fn=None: (
True, "git.example.com/bes-r/server-up:0.8.13-beta"),
)
monkeypatch.setattr(su, "_spawn_helper", lambda *args, **kw: (True, "ok"))
ok, _ = su.apply("v0.8.13-beta")
assert ok is True
opgeslagen = env["core"].load()
assert opgeslagen["UPDATE_PREVIOUS_TAG"] == "0.8.12-beta"
assert opgeslagen["UPDATE_LAST_APPLIED"]["tag"] == "0.8.13-beta"
@pytest.mark.parametrize("workflow", [
".forgejo/workflows/deploy.yml",
".forgejo/workflows/deploy-prod.yml",
])
def test_deploy_schrijft_tag_en_versie_samen(workflow):
tekst = (Path(__file__).resolve().parent.parent / workflow).read_text()
assert "'/^SU_TAG=/d; /^SU_VERSION=/d'" in tekst
assert "SU_TAG=%s\\nSU_VERSION=%s\\n" in tekst
@pytest.mark.parametrize("workflow,pushregel", [
(".forgejo/workflows/build.yml", 'docker push "${IMAGE}:v${VERSION}"'),
(".forgejo/workflows/deploy-prod.yml",
'docker push "${SU_IMAGE}:v${VERSION}"'),
])
def test_release_publiceert_tag_voor_oude_updater(workflow, pushregel):
tekst = (Path(__file__).resolve().parent.parent / workflow).read_text()
assert pushregel in tekst
def test_rollback_zonder_vorige_versie(su):

View file

@ -27,6 +27,8 @@ def test_stable_negeert_prereleases(upd):
res = upd.check("0.4.60", force=True)
assert res["channel"] == "stable"
assert res["latest"] == "0.5.00"
assert res["latest_tag"] == "v0.5.00"
assert res["image_tag"] == "0.5.00"
assert res["update_available"] is True
@ -35,6 +37,7 @@ def test_beta_ziet_de_prerelease(upd, env):
res = upd.check("0.5.00", force=True)
assert res["channel"] == "beta"
assert res["latest"] == "0.5.10-beta1"
assert res["image_tag"] == "0.5.10-beta1"
assert res["update_available"] is True
assert res["prerelease"] is True
@ -107,6 +110,18 @@ def test_is_prerelease():
assert updater.is_prerelease("0.5.00") is False
@pytest.mark.parametrize("release_tag,image_tag", [
("v0.8.13", "0.8.13"),
("v0.8.13-beta", "0.8.13-beta"),
("0.8.13", "0.8.13"),
("beta", "beta"),
])
def test_registry_tag_past_bij_de_workflow(release_tag, image_tag):
"""Forgejo-tags beginnen met v; de registry-workflow stript die v."""
from core import updater
assert updater.image_tag(release_tag) == image_tag
def test_ongeldig_kanaal_wordt_geweigerd(client):
from conftest import login
csrf = login(client)