Releases: VERSION + automatic patch tags + promote; updater installs newer versions (node-26#49)
VERSION holds MAJOR.MINOR (1.1), edited by hand. Every push to main is tagged vMAJOR.MINOR.PATCH by the "Tag release" workflow, with PATCH counted as an integer by scripts/next-version.sh. "Promote release" moves the major tag (v1) to a tested release and refuses to move it backwards. drb-update.sh: a newer release on the node's channel is now a reason to update by itself. REF=latest follows the highest vX.Y.Z tag (test nodes); REF=v1 follows the promoted tag (customer nodes). Versions compare per field as integers (1.10.0 > 1.9.3, never string or float order). It never downgrades, and an untagged target still needs a request. After an update it writes DRB_VERSION to .env; the edge node reports it in checkin. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6330609a75
commit
5549736706
@@ -6,6 +6,9 @@ class Settings(BaseSettings):
|
||||
# Node identity
|
||||
node_id: str
|
||||
node_name: str = "Unnamed Node"
|
||||
# The release installed on this node (node-26#49). The host updater writes
|
||||
# DRB_VERSION to .env after each update; "untagged" before the first one.
|
||||
drb_version: str = "untagged"
|
||||
node_lat: float = 0.0
|
||||
node_lon: float = 0.0
|
||||
|
||||
|
||||
@@ -378,6 +378,7 @@ class MQTTManager:
|
||||
"enforce_override_timeout": config.enforce_override_timeout,
|
||||
"secondary_sdr_mode": config.secondary_sdr_mode,
|
||||
"secondary_sdr_priority": config.secondary_sdr_priority,
|
||||
"version": settings.drb_version,
|
||||
}
|
||||
payload["sdr_pins"] = config.sdr_pins
|
||||
# Conventional channels (node-26#24): name/freq/NAC and whether the
|
||||
|
||||
@@ -44,6 +44,11 @@ case "$1" in
|
||||
*:docker-compose.yml) cat "$STUB_REPO/docker-compose.yml" ;;
|
||||
esac ;;
|
||||
checkout) echo "${@: -1}" > "$STUB/head" ;;
|
||||
tag)
|
||||
if [ "$2" = --points-at ]; then
|
||||
if [ "$3" = HEAD ]; then cat "$STUB/tags_head" 2>/dev/null; else cat "$STUB/tags_new" 2>/dev/null; fi
|
||||
else cat "$STUB/tags_all" 2>/dev/null; fi
|
||||
exit 0 ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
'''
|
||||
@@ -121,7 +126,7 @@ def test_no_request_means_checkin_only(env):
|
||||
calls = _calls(env)
|
||||
assert "update-check" in calls
|
||||
assert "docker" not in calls
|
||||
assert "fetch" not in calls
|
||||
assert "checkout" not in calls
|
||||
|
||||
|
||||
def test_c2_request_updates_once_and_reports(env):
|
||||
@@ -248,3 +253,74 @@ def test_target_without_pinning_still_updates(env):
|
||||
assert (env["stub"] / "head").read_text().strip() == NEW
|
||||
assert _pulled(_calls(env))
|
||||
assert "DRB_TAG_" not in (env["inst"] / ".env").read_text()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Releases (node-26#49): a newer version on the node's channel is a reason to
|
||||
# update by itself, and versions compare per field as integers.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _channel(env, ref, all_tags, target_tags, head_tags):
|
||||
env["conf"].write_text(env["conf"].read_text().replace("REF=v1", f"REF={ref}"))
|
||||
(env["stub"] / "tags_all").write_text("\n".join(all_tags) + "\n")
|
||||
(env["stub"] / "tags_new").write_text("\n".join(target_tags) + "\n")
|
||||
(env["stub"] / "tags_head").write_text("\n".join(head_tags) + "\n")
|
||||
|
||||
|
||||
def test_latest_channel_installs_a_newer_release_unasked(env):
|
||||
# 1.1.10 is newer than 1.1.9: compared as integers, not strings or floats.
|
||||
_channel(env, "latest", ["v1", "v1.1.9", "v1.1.10", "v1.1.2"], ["v1.1.10"], ["v1.1.9"])
|
||||
r = _run(env)
|
||||
assert r.returncode == 0, r.stderr + r.stdout
|
||||
assert "release 1.1.10 on latest (installed 1.1.9)" in r.stdout
|
||||
assert (env["stub"] / "head").read_text().strip() == NEW
|
||||
assert "DRB_VERSION=1.1.10" in (env["inst"] / ".env").read_text()
|
||||
|
||||
|
||||
def test_never_downgrades(env):
|
||||
_channel(env, "latest", ["v1.1.9", "v1.2.0"], ["v1.1.9"], ["v1.2.0"])
|
||||
r = _run(env)
|
||||
assert r.returncode == 0, r.stderr
|
||||
assert "docker" not in _calls(env)
|
||||
|
||||
|
||||
def test_same_release_is_not_reinstalled(env):
|
||||
_channel(env, "v1", ["v1", "v1.1.3"], ["v1", "v1.1.3"], ["v1", "v1.1.3"])
|
||||
assert _run(env).returncode == 0
|
||||
assert "docker" not in _calls(env)
|
||||
|
||||
|
||||
def test_promoted_major_tag_installs_on_stable_nodes(env):
|
||||
_channel(env, "v1", ["v1", "v1.1.0", "v1.1.4"], ["v1", "v1.1.4"], ["v1.1.0"])
|
||||
r = _run(env)
|
||||
assert r.returncode == 0, r.stderr + r.stdout
|
||||
assert "release 1.1.4 on v1" in r.stdout
|
||||
assert (env["stub"] / "head").read_text().strip() == NEW
|
||||
|
||||
|
||||
def test_untagged_target_still_needs_a_request(env):
|
||||
"""v1 before its first promotion points at a commit with no release tag."""
|
||||
_channel(env, "v1", ["v1"], ["v1"], [])
|
||||
assert _run(env).returncode == 0
|
||||
assert "docker" not in _calls(env)
|
||||
|
||||
|
||||
def test_next_version_counts_patches_as_integers(tmp_path):
|
||||
script = REPO_ROOT / "scripts" / "next-version.sh"
|
||||
g = ["git", "-c", "user.email=t@t", "-c", "user.name=t", "-C", str(tmp_path)]
|
||||
subprocess.run(["git", "init", "-q", str(tmp_path)], check=True)
|
||||
(tmp_path / "VERSION").write_text("1.1\n")
|
||||
subprocess.run(g + ["commit", "-q", "--allow-empty", "-m", "a"], check=True)
|
||||
|
||||
def nxt():
|
||||
return subprocess.run(["bash", str(script)], cwd=tmp_path, capture_output=True, text=True,
|
||||
check=True).stdout.strip()
|
||||
|
||||
assert nxt() == "v1.1.0"
|
||||
for t in ("v1.1.0", "v1.1.9", "v1.0.30"):
|
||||
subprocess.run(g + ["tag", t], check=True)
|
||||
assert nxt() == "", "a commit that already has a release gets no second one"
|
||||
subprocess.run(g + ["commit", "-q", "--allow-empty", "-m", "b"], check=True)
|
||||
assert nxt() == "v1.1.10"
|
||||
(tmp_path / "VERSION").write_text("1.2\n")
|
||||
assert nxt() == "v1.2.0"
|
||||
|
||||
Reference in New Issue
Block a user