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
9 changes: 9 additions & 0 deletions docs/lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ have their own helpers outside that pair: `fetch_and_deploy_gh_tag`,
Deploy modes: prebuilt release asset, source tarball, `.deb` package, or a
plain URL through `fetch_and_deploy_from_url`.

Both entry points keep a version stamp in `~/.<app>`. It is written when the
deploy finishes, so a build that fails afterwards leaves it at the new
version; the marker `~/.<app>.pending` beside it holds the version the run
started from and stays until a run that deployed exits cleanly.
`check_for_release` redoes the update while the marker exists, and both
helpers export `CS_PREVIOUS_VERSION`, the version from before the update,
which stays right on that redo. Prefer it over reading `~/.<app>` when a
migration depends on the old version.

## [`runtime.func`](../lib/runtime.func)

Language runtimes and the application-level installers: `setup_nodejs`,
Expand Down
1 change: 1 addition & 0 deletions lib/API.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ _forge_highest_version
_forge_http_get
_forge_is_current
_forge_keep_raw
_forge_previous_version
_forge_release_json
_forge_same_shape
_forge_scan_older
Expand Down
30 changes: 25 additions & 5 deletions lib/forge.func
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,17 @@ _forge_release_json() {
# still reads as an update. Version-aware ordering would change what every
# app sees and belongs in its own change
# ------------------------------------------------------------------------------
# The version a run started from: the marker of an unfinished run, otherwise
# the stamp. Empty on a fresh install.
_forge_previous_version() {
local version_file="${1:?version file}"
if [[ -f "${version_file}.pending" ]]; then
printf '%s' "$(<"${version_file}.pending")"
elif [[ -f "$version_file" ]]; then
printf '%s' "$(<"$version_file")"
fi
}

check_for_release() {
local forge="${1:?forge}" app="${2:?app}" source="${3:?repo}"
local pinned_version_in="${4:-}" pin_reason="${5:-}" tag_prefix="${6:-}"
Expand Down Expand Up @@ -1794,8 +1805,13 @@ check_for_release() {
fi
fi
current="$(_forge_strip_v "$current")"
CS_PREVIOUS_VERSION="$(_forge_strip_v "$(_forge_previous_version "$current_file")")"
export CS_PREVIOUS_VERSION
local unfinished=""
[[ -f "${current_file}.pending" ]] && unfinished=" (the last run did not finish, doing it again)"
if [[ -f "${current_file}.pending" ]]; then
unfinished=" (the last run did not finish, doing it again)"
current="$CS_PREVIOUS_VERSION"
fi

if [[ -n "$pinned_version_in" ]]; then
local pin_clean match_raw="" i
Expand Down Expand Up @@ -2097,10 +2113,14 @@ fetch_and_deploy_release() {
# throughout, which mangles tags like version-2.
version="$(_forge_strip_v "$tag_name")"

# The stamp written at the end only says "deployed". This marker says the
# run around it still has to finish: on_exit removes it when the run exits 0,
# and check_for_release redoes the update while it exists (ProxmoxVE #18050).
touch "${version_file}.pending" 2>/dev/null || true
# The stamp written at the end only says "deployed". The marker beside it
# holds the version this update started from and stays until a run that
# deployed exits 0 (on_exit); check_for_release redoes the update while it
# exists, and CS_PREVIOUS_VERSION still names the real old version on that
# redo (ProxmoxVE #18050).
CS_PREVIOUS_VERSION="$(_forge_previous_version "$version_file")"
export CS_PREVIOUS_VERSION
[[ -f "${version_file}.pending" ]] || printf '%s' "$CS_PREVIOUS_VERSION" >"${version_file}.pending" 2>/dev/null || true
_CS_DEPLOYED=1

local current_version=""
Expand Down
17 changes: 16 additions & 1 deletion tests/release-stamp-pending.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -uo pipefail
CORE_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
eval "$(sed -n '/^_FORGE_JQ_VKEY_DEF=/,/^_FORGE_JQ_VKEY=/p' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^_forge_(descriptor|strip_v|is_current|highest_version|same_shape|stable_filter)[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^check_for_release[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^(check_for_release|_forge_previous_version)[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^on_exit[(][)] [{]/,/^}/' "$CORE_DIR/core/error_handler.func")"

TEST_DIR="$(mktemp -d)"
Expand Down Expand Up @@ -66,6 +66,21 @@ expect "no stamp at all: a normal install" 0 "$(check "" no)"
expect "pinned and current: held back" 1 "$(check 2.16.0 no 2.16.0 "testing")"
expect "pinned, current, marker: redone anyway" 0 "$(check 2.16.0 yes 2.16.0 "testing")"

# The marker carries the version the run started from; the redo reports it
: >"$LOG"
echo 2.16.0 >"$HOME/.npm"
printf '%s' 2.15.1 >"$HOME/.npm.pending"
rc=0
check_for_release github NPM owner/repo >/dev/null 2>&1 || rc=$?
expect "a marker with the old version: redone" 0 "$rc"
expect "the message names the version the run started from" 1 "$(grep -c '2.15.1 -> 2.16.0' "$LOG")"
expect "CS_PREVIOUS_VERSION is the old version on the redo" 2.15.1 "$CS_PREVIOUS_VERSION"
rm -f "$HOME/.npm.pending"
check_for_release github NPM owner/repo >/dev/null 2>&1 || true
expect "without a marker it is the stamp" 2.16.0 "$CS_PREVIOUS_VERSION"
rm -f "$HOME/.npm"
expect "a fresh install has no previous version" "" "$(_forge_previous_version "$HOME/.npm")"

# on_exit: only a run that deployed and exits 0 removes the marker
touch "$HOME/.npm.pending"
(_CS_DEPLOYED=1; on_exit) 2>/dev/null
Expand Down
Loading