Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 0 additions & 83 deletions scripts/lib/update-migrations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -371,85 +371,6 @@ migrate_geo_to_configured_flag() {
mv -f "$tmp" "$feed_env"
}

# Rewrite the ALTITUDE value to bare metres. Idempotent — bare values
# round-trip unchanged. The website's config-sync wire format expects
# `alt.value` as a JSON number in metres; the pre-migration disk format
# carried a unit-suffixed string (`120m`, `400ft`). Writing back the
# already-validated suffix-stripped value lets every outbound payload
# emit a clean number and means a server-side echo (`alt.value: 120` →
# disk `ALTITUDE=120`) is a no-op-equality round-trip.
#
# Sidecar metadata (feed.meta.json) is intentionally NOT bumped. The
# representation flips; the operator's last-edit semantics do not. A
# website edit that arrives shortly after this migration runs must still
# win over the pre-migration feeder stamp, which means edited_at must
# stay where it was. Asymmetric with migrate_user_to_mlat_split, which
# represents a real schema change.
#
# Unparseable values (regex shape fails OR post-conversion range gate
# fails) are left untouched on disk and surface as a stderr warning.
# The next operator-triggered apply will surface them via the validator
# — the migrator's job is to flip canonical-but-suffixed values, not to
# repair broken ones.
migrate_altitude_to_bare_metres() {
local feed_env="$1"
[[ -f "$feed_env" ]] || return 0

# No ALTITUDE key on disk — nothing to migrate.
if ! grep -qE '^[[:space:]]*ALTITUDE=' "$feed_env"; then
return 0
fi

local current converted rc=0
current="$(_extract_env_value "$feed_env" ALTITUDE)"

# Empty value (present-but-empty tombstone, e.g. a recent
# alt.value:null round-trip) — leave untouched.
if [[ -z "$current" ]]; then
return 0
fi

converted="$(altitude_to_bare_metres "$current")" || rc=$?
if (( rc != 0 )); then
echo "migrate_altitude_to_bare_metres: leaving ALTITUDE=\"$current\" untouched (does not parse as a metric/imperial altitude in [-1000, 10000] metres)" >&2
return 0
fi

# Already bare — strict no-op (no write, no log spam). Without this
# short-circuit, the second-and-later migration runs would still
# rewrite the file (with identical contents) and bump mtime.
if [[ "$converted" == "$current" ]]; then
return 0
fi

local tmp escaped
tmp="$(mktemp "${feed_env}.XXXXXX")" || return 0
# Escape exactly the way _apl_feed_apply_write does so a value the
# apply layer would re-emit verbatim survives this rewrite identically.
escaped="${converted//\\/\\\\}"
escaped="${escaped//\$/\\\$}"
escaped="${escaped//\`/\\\`}"
escaped="${escaped//\"/\\\"}"
# Replace the existing ALTITUDE line. `awk` so the replacement is
# exact-key-prefix (not a partial match like `ALTITUDE_FOO`) and
# respects per-line shape. First match wins (canonical case); later
# duplicate ALTITUDE lines (operator hand-edit oddity) are passed
# through so subsequent _apl_feed_apply_read can dedup them on its
# next write cycle.
awk -v new_line="ALTITUDE=\"$escaped\"" '
BEGIN { replaced = 0 }
/^[[:space:]]*ALTITUDE=/ && !replaced {
print new_line
replaced = 1
next
}
{ print }
' "$feed_env" > "$tmp" || { rm -f "$tmp"; return 0; }
chmod --reference="$feed_env" "$tmp" 2>/dev/null || true
chown --reference="$feed_env" "$tmp" 2>/dev/null || true
mv -f "$tmp" "$feed_env"
}

# Seed /etc/airplanes/feed.meta.json from the current feed.env on installs
# that don't have it yet. The sidecar tracks per-write (edited_at, edited_by)
# tuples for the LWW remote-config sync; without this seed, existing
Expand Down Expand Up @@ -537,10 +458,6 @@ run_config_file_migrations() {
migrate_user_to_mlat_split "$feed_env"
migrate_privacy_to_mlat_private "$feed_env"
migrate_geo_to_configured_flag "$feed_env"
# Runs BEFORE migrate_seed_feed_meta_json so the seeded sidecar can
# reflect bare-metres state if it's being created in this same update
# cycle.
migrate_altitude_to_bare_metres "$feed_env"
migrate_seed_feed_meta_json "$feed_env" "$(dirname "$feed_env")/feed.meta.json"
if [[ -n "$_feed_lock_fd" ]]; then
eval "exec ${_feed_lock_fd}>&-"
Expand Down
162 changes: 0 additions & 162 deletions test/test_migrations.bats
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ setup() {
# shellcheck source=/dev/null
source "$COMMON_LIB"
# shellcheck source=/dev/null
source "$REPO_ROOT/scripts/lib/configure-validators.sh"
# shellcheck source=/dev/null
source "$LIB"
# shellcheck source=/dev/null
source "$REPO_ROOT/scripts/lib/legacy-mlat-translation.sh"
Expand Down Expand Up @@ -723,165 +721,6 @@ EOF
grep -qx 'GEO_CONFIGURED=true' "$FEED_ENV"
}

# ---------------------------------------------------------------------------
# migrate_altitude_to_bare_metres
# ---------------------------------------------------------------------------

@test "migrate_altitude_to_bare_metres: ALTITUDE=120m → ALTITUDE=120 (strip suffix)" {
cat > "$FEED_ENV" <<'EOF'
LATITUDE="52.5"
LONGITUDE="13.4"
ALTITUDE="120m"
GEO_CONFIGURED=true
EOF
migrate_altitude_to_bare_metres "$FEED_ENV"

grep -qx 'ALTITUDE="120"' "$FEED_ENV"
grep -qx 'LATITUDE="52.5"' "$FEED_ENV"
grep -qx 'LONGITUDE="13.4"' "$FEED_ENV"
grep -qx 'GEO_CONFIGURED=true' "$FEED_ENV"
}

@test "migrate_altitude_to_bare_metres: ALTITUDE=400ft → ALTITUDE=121.92" {
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="400ft"
EOF
migrate_altitude_to_bare_metres "$FEED_ENV"

grep -qx 'ALTITUDE="121.92"' "$FEED_ENV"
}

@test "migrate_altitude_to_bare_metres: bare ALTITUDE=42.5 is unchanged (idempotent)" {
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="42.5"
EOF
local before
before="$(cat "$FEED_ENV")"
migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$(cat "$FEED_ENV")" = "$before" ]
}

@test "migrate_altitude_to_bare_metres: empty ALTITUDE is unchanged" {
cat > "$FEED_ENV" <<'EOF'
ALTITUDE=""
EOF
local before
before="$(cat "$FEED_ENV")"
migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$(cat "$FEED_ENV")" = "$before" ]
}

@test "migrate_altitude_to_bare_metres: missing ALTITUDE key is a no-op" {
cat > "$FEED_ENV" <<'EOF'
LATITUDE="52.5"
LONGITUDE="13.4"
EOF
local before
before="$(cat "$FEED_ENV")"
migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$(cat "$FEED_ENV")" = "$before" ]
}

@test "migrate_altitude_to_bare_metres: garbage value is preserved with a warning" {
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="not-a-number"
LATITUDE="52.5"
EOF
local before
before="$(cat "$FEED_ENV")"
run migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$status" -eq 0 ]
[ "$(cat "$FEED_ENV")" = "$before" ]
[[ "$output" == *"leaving ALTITUDE=\"not-a-number\" untouched"* ]]
}

@test "migrate_altitude_to_bare_metres: out-of-range value is preserved with a warning" {
# 33000ft is ~10058m, just above the 10000m upper bound.
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="33000ft"
EOF
local before
before="$(cat "$FEED_ENV")"
run migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$status" -eq 0 ]
[ "$(cat "$FEED_ENV")" = "$before" ]
[[ "$output" == *"33000ft"* ]]
}

@test "migrate_altitude_to_bare_metres: second run on bare-metres state is a strict no-op" {
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="400ft"
EOF
migrate_altitude_to_bare_metres "$FEED_ENV"
grep -qx 'ALTITUDE="121.92"' "$FEED_ENV"
local after_first
after_first="$(cat "$FEED_ENV")"

migrate_altitude_to_bare_metres "$FEED_ENV"
[ "$(cat "$FEED_ENV")" = "$after_first" ]
}

@test "migrate_altitude_to_bare_metres: does NOT bump feed.meta.json metadata" {
# The migration is a representation flip, not a semantic edit. The
# sidecar's edited_at must stay where it was so a freshly-stamped
# website edit still wins over the pre-migration feeder tuple.
cat > "$FEED_ENV" <<'EOF'
ALTITUDE="120m"
EOF
META="$TMP/feed.meta.json"
cat > "$META" <<'EOF'
{"schema_version":1,"fields":{"ALTITUDE":{"edited_at":"2024-06-01T00:00:00Z","edited_by":"legacy"}}}
EOF
local meta_before
meta_before="$(cat "$META")"

migrate_altitude_to_bare_metres "$FEED_ENV"

grep -qx 'ALTITUDE="120"' "$FEED_ENV"
[ "$(cat "$META")" = "$meta_before" ]
}

@test "migrate_altitude_to_bare_metres: preserves all non-altitude keys verbatim" {
cat > "$FEED_ENV" <<'EOF'
INPUT="127.0.0.1:30005"
MLAT_USER="alice"
MLAT_ENABLED=true
MLAT_PRIVATE=false
LATITUDE="52.5"
LONGITUDE="13.4"
ALTITUDE="400ft"
GEO_CONFIGURED=true
NET_OPTIONS="--net-heartbeat 60"
GAIN=auto
EOF
migrate_altitude_to_bare_metres "$FEED_ENV"

grep -qx 'INPUT="127.0.0.1:30005"' "$FEED_ENV"
grep -qx 'MLAT_USER="alice"' "$FEED_ENV"
grep -qx 'MLAT_ENABLED=true' "$FEED_ENV"
grep -qx 'MLAT_PRIVATE=false' "$FEED_ENV"
grep -qx 'LATITUDE="52.5"' "$FEED_ENV"
grep -qx 'LONGITUDE="13.4"' "$FEED_ENV"
grep -qx 'ALTITUDE="121.92"' "$FEED_ENV"
grep -qx 'GEO_CONFIGURED=true' "$FEED_ENV"
grep -qx 'NET_OPTIONS="--net-heartbeat 60"' "$FEED_ENV"
grep -qx 'GAIN=auto' "$FEED_ENV"
}

@test "run_config_file_migrations: altitude bare-metres flip runs in the chain" {
cat > "$FEED_ENV" <<'EOF'
USER="alice"
LATITUDE="52.5"
LONGITUDE="13.4"
ALTITUDE="400ft"
EOF
run_config_file_migrations "$FEED_ENV"

grep -qx 'MLAT_USER="alice"' "$FEED_ENV"
grep -qx 'GEO_CONFIGURED=true' "$FEED_ENV"
grep -qx 'ALTITUDE="121.92"' "$FEED_ENV"
}

# ---------------------------------------------------------------------------
# migrate_seed_feed_meta_json (DEV-380)
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -989,7 +828,6 @@ EOF
run bash -c "
set -e
source '$COMMON_LIB'
source '$REPO_ROOT/scripts/lib/configure-validators.sh'
source '$LIB'
migrate_seed_feed_meta_json '$FEED_ENV' '$META'
"
Expand Down
7 changes: 0 additions & 7 deletions update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -336,13 +336,6 @@ if [[ "$IMAGE_SERVICE_LAYOUT" == "1" ]]; then
SYSTEMD_DIR="$(airplanes_path /etc/systemd/system)"
fi

# Validator + canonicalizer helpers — needed by migrate_altitude_to_bare_metres
# (calls altitude_to_bare_metres) and reused by any future config-rewriting
# migrator. Sourced before update-migrations.sh so the migrator's function
# definitions can rely on the helpers at chain-execution time.
# shellcheck source=scripts/lib/configure-validators.sh
source "$GIT/scripts/lib/configure-validators.sh"

# Migration helpers: legacy retirements, env-file rewrites, manifest-based
# pruning, finalize symlinks. See scripts/lib/update-migrations.sh for the
# function definitions and the migration ordering contract.
Expand Down