opkg считает пакет уже установленным при равенстве версий, поэтому
пересборка под тем же тегом молча не применялась: задание завершалось
успехом, билд оставался старым. Добавлен --force-reinstall в
pkg_install_files_command (opkg) и в install.sh; apk не тронут, он
всегда пишет переданный файл.
Детект пересборки больше не зависит только от SHA, которого нет в
релизах старше f974e4a0: добавлен отпечаток билда по published_at и
метаданным ipk-ассета, решение вынесено в tachyon_build_differs с
префиксным сравнением SHA разной длины. Отпечаток установленного
билда пишется в state-файл и попадает в extra.
Telegram уведомляет о пересборках: check_notified_updates пропускает
outdated_same_release, дедуп идёт по версии и SHA, текст показывает
короткие SHA вместо пары одинаковых версий.
Кэш проверки обновлений сохраняет current_sha из результата действия
вместо безусловного latest, иначе неудачная установка выглядела как
"актуально".
Фронт пересобран из TS, ручная правка main.js снята. Добавлен
describeSameReleaseBuild с фолбэк-текстом, когда SHA недоступен.
102 lines
5.0 KiB
Bash
102 lines
5.0 KiB
Bash
#!/usr/bin/env bash
|
|
set -eo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
UPDATER_UC="$ROOT_DIR/tachyon/files/usr/lib/components/updater.uc"
|
|
TACHYON_LIB="$ROOT_DIR/tachyon/files/usr/lib"
|
|
|
|
ucode() {
|
|
command ucode -L "$TACHYON_LIB" "$@"
|
|
}
|
|
|
|
fail() {
|
|
printf 'FAIL: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
assert_eq() {
|
|
local expected="$1"
|
|
local actual="$2"
|
|
local label="$3"
|
|
|
|
[ "$actual" = "$expected" ] || fail "$label: expected '$expected', got '$actual'"
|
|
}
|
|
|
|
fingerprint() {
|
|
printf '%s' "$1" | ucode "$UPDATER_UC" release-build-fingerprint
|
|
}
|
|
|
|
commit_sha() {
|
|
printf '%s' "$1" | ucode "$UPDATER_UC" release-commit-sha
|
|
}
|
|
|
|
# A release whose body carries "Commit: <sha>" is identified by that sha, so the
|
|
# fingerprint must not fall back to timestamps and lose precision.
|
|
BODY_SHA_RELEASE='{"tag_name":"1.2.66","html_url":"https://example.invalid/r","target_commitish":"main","body":"Notes\nCommit: a1b2c3d4e5f6\n","published_at":"2026-08-01T10:00:00Z","assets":[{"name":"tachyon_1.2.66_all.ipk","updated_at":"2026-08-01T10:05:00Z","size":123456}]}'
|
|
assert_eq "sha:a1b2c3d4e5f6" "$(fingerprint "$BODY_SHA_RELEASE")" "fingerprint prefers body commit sha"
|
|
assert_eq "a1b2c3d4e5f6" "$(commit_sha "$BODY_SHA_RELEASE")" "commit sha read from body"
|
|
|
|
# target_commitish holds a branch name on every release cut from a branch, so it
|
|
# must not be mistaken for a sha.
|
|
BRANCH_ONLY_RELEASE='{"tag_name":"1.2.66","target_commitish":"main","body":"No commit line here","published_at":"2026-08-01T10:00:00Z","assets":[{"name":"tachyon_1.2.66_all.ipk","updated_at":"2026-08-01T10:05:00Z","size":123456}]}'
|
|
assert_eq "" "$(commit_sha "$BRANCH_ONLY_RELEASE")" "branch name is not a commit sha"
|
|
assert_eq "build:pub=2026-08-01T10:00:00Z|upd=2026-08-01T10:05:00Z|size=123456" \
|
|
"$(fingerprint "$BRANCH_ONLY_RELEASE")" \
|
|
"fingerprint falls back to publish stamps and asset size"
|
|
|
|
# A rebuild replaces the asset, so the fingerprint has to move even when
|
|
# published_at stays exactly the same.
|
|
REBUILT_RELEASE='{"tag_name":"1.2.66","target_commitish":"main","body":"No commit line here","published_at":"2026-08-01T10:00:00Z","assets":[{"name":"tachyon_1.2.66_all.ipk","updated_at":"2026-08-02T11:30:00Z","size":123999}]}'
|
|
[ "$(fingerprint "$BRANCH_ONLY_RELEASE")" != "$(fingerprint "$REBUILT_RELEASE")" ] ||
|
|
fail "rebuilt release must not share a fingerprint with the original build"
|
|
|
|
# created_at stands in for a release that has not been published yet.
|
|
CREATED_ONLY_RELEASE='{"tag_name":"1.2.66","target_commitish":"main","created_at":"2026-07-30T08:00:00Z","assets":[]}'
|
|
assert_eq "build:pub=2026-07-30T08:00:00Z" "$(fingerprint "$CREATED_ONLY_RELEASE")" \
|
|
"created_at substitutes for published_at"
|
|
|
|
# Assets that are not the backend package must not drive the fingerprint.
|
|
FOREIGN_ASSET_RELEASE='{"tag_name":"1.2.66","target_commitish":"main","published_at":"2026-08-01T10:00:00Z","assets":[{"name":"luci-app-tachyon_1.2.66_all.ipk","updated_at":"2026-08-01T10:09:00Z","size":777}]}'
|
|
assert_eq "build:pub=2026-08-01T10:00:00Z" "$(fingerprint "$FOREIGN_ASSET_RELEASE")" \
|
|
"only the tachyon_ backend asset contributes"
|
|
|
|
# Nothing identifiable at all must produce no fingerprint rather than a constant
|
|
# one, or every release would look identical.
|
|
assert_eq "" "$(fingerprint '{"tag_name":"1.2.66","target_commitish":"main"}')" \
|
|
"release without stamps yields no fingerprint"
|
|
assert_eq "" "$(fingerprint '{}')" "empty release yields no fingerprint"
|
|
|
|
differs() {
|
|
if ucode "$UPDATER_UC" tachyon-build-differs "$1" "$2" "$3" "$4" >/dev/null 2>&1; then
|
|
printf 'yes'
|
|
else
|
|
printf 'no'
|
|
fi
|
|
}
|
|
|
|
assert_eq no "$(differs abc1234 abc1234 '' '')" "equal shas match"
|
|
assert_eq yes "$(differs abc1234 def5678 '' '')" "different shas differ"
|
|
# build.sh and the OpenWrt Makefile stamp a short sha while the release may carry
|
|
# the full one; the comparison happens on the shorter of the two.
|
|
assert_eq no "$(differs abc1234 abc1234ffffffffffffffffffffffffffffffff '' '')" \
|
|
"short local sha is a prefix of the full remote sha"
|
|
assert_eq yes "$(differs abc1234 abc1299ffffffffffffffffffffffffffffffff '' '')" \
|
|
"prefix mismatch within the short sha differs"
|
|
|
|
assert_eq no "$(differs '' '' 'build:pub=A' 'build:pub=A')" "equal fingerprints match"
|
|
assert_eq yes "$(differs '' '' 'build:pub=A' 'build:pub=B')" "different fingerprints differ"
|
|
|
|
# An unknown state must never be reported as an available update: the UI would
|
|
# offer an install that changes nothing.
|
|
assert_eq no "$(differs '' '' '' '')" "no information means no update"
|
|
assert_eq no "$(differs abc1234 '' '' '')" "remote sha alone missing means no update"
|
|
assert_eq no "$(differs '' def5678 '' '')" "local sha alone missing means no update"
|
|
assert_eq no "$(differs '' '' 'build:pub=A' '')" "remote fingerprint missing means no update"
|
|
assert_eq no "$(differs '' '' '' 'build:pub=B')" "local fingerprint missing means no update"
|
|
|
|
# When both shas are known they decide, even if the fingerprints disagree.
|
|
assert_eq no "$(differs abc1234 abc1234 'build:pub=A' 'build:pub=B')" \
|
|
"matching shas win over disagreeing fingerprints"
|
|
|
|
printf 'PASS: tachyon build fingerprint\n'
|