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
17 changes: 10 additions & 7 deletions lib/forge.func
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,8 @@ extract_version_from_json() {
# Returns:
# Latest tag name (stdout), or returns 1 on failure
# ------------------------------------------------------------------------------
# A tag whose version is directly followed by a pre-release label (11.5.0-beta1, v2.0.0-rc.1).
_FORGE_PRERELEASE_TAG='^[^0-9]*[0-9]+([.][0-9]+)*[-._+]?(dev|alpha|beta|preview|pre|rc)([-._0-9]|$)'
get_latest_gh_tag() {
local repo="$1"
local prefix="${2:-}"
Expand All @@ -419,8 +421,9 @@ get_latest_gh_tag() {
# Use git/matching-refs API for server-side prefix filtering. This avoids
# paging through unrelated tags (e.g. mongodb/mongo-tools where 100.x tags
# only appear after page 4 of /tags). Returns ALL tags matching the prefix
# in a single call, sorted lexicographically ascending; we pick the
# highest version using `sort -V`.
# in a single call, sorted lexicographically ascending. Pre-releases are
# skipped and the rest ordered by the version key: sort -V put 11.4.0
# before 11.4.0-rc1 and offered 11.5.0-beta1 as the latest Drupal.
if ! github_api_call "https://api.github.com/repos/${repo}/git/matching-refs/tags/${prefix}" "$temp_file"; then
rm -f "$temp_file"
return 22
Expand All @@ -431,16 +434,16 @@ get_latest_gh_tag() {
if [[ "$count" -gt 0 ]]; then
tag=$(jq -r '.[].ref' "$temp_file" |
sed 's|^refs/tags/||' |
sort -V |
tail -n1)
awk -v re="$_FORGE_PRERELEASE_TAG" 'tolower($0) !~ re' |
_forge_highest_version)
fi
else
# No prefix: just take the first (newest) tag from /tags
if ! github_api_call "https://api.github.com/repos/${repo}/tags?per_page=1" "$temp_file"; then
# No prefix: the first (newest) tag from /tags that is not a pre-release
if ! github_api_call "https://api.github.com/repos/${repo}/tags?per_page=30" "$temp_file"; then
rm -f "$temp_file"
return 22
fi
tag=$(jq -r '.[0].name // empty' "$temp_file")
tag=$(jq -r '.[].name' "$temp_file" | awk -v re="$_FORGE_PRERELEASE_TAG" 'tolower($0) !~ re && !n++')
fi

rm -f "$temp_file"
Expand Down
23 changes: 22 additions & 1 deletion tests/forge-release-json.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ 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|curl|designated_release|stable_filter|keep_raw|release_json|explain_failure|require_jq)[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^get_latest_gh_tag[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(awk '/^(get_latest_gh_tag|_forge_highest_version)[(][)] [{]/,/^}/' "$CORE_DIR/lib/forge.func")"
eval "$(grep '^_FORGE_PRERELEASE_TAG=' "$CORE_DIR/lib/forge.func")"

TEST_DIR="$(mktemp -d)"
trap 'rm -rf -- "$TEST_DIR" "${TOOLS_GH_REL_JSON:-}"' EXIT
Expand Down Expand Up @@ -121,5 +122,25 @@ expect "the tag is all that reaches stdout" 100.10.1 "$(cat "$OUT")"
expect "a tag lookup without jq stops" 100 "$?"
expect "a tag lookup without jq asks nothing" 0 "$(grep -c api "$CALLS")"

# drupal/drupal: sort -V ranked 11.4.0 below 11.4.0-rc1, and a major prefix offered 11.5.0-beta1.
tags_of() { printf '['; local t sep=""; for t in "$@"; do printf '%s{"ref":"refs/tags/%s","name":"%s"}' "$sep" "$t" "$t"; sep=","; done; printf ']'; }
latest_tag() {
(
ensure_dependencies() { :; }
github_api_call() { printf '%s' "$TAGS" >"$2"; }
get_latest_gh_tag drupal/drupal "$@" 2>/dev/null
)
}
TAGS="$(tags_of 11.4.0-beta1 11.4.0-rc1 11.4.0-rc2 11.4.0 11.4.11 11.4.12 11.5.0-beta1)"
expect "a prefixed tag lookup skips pre-releases" 11.4.12 "$(latest_tag 11.)"
TAGS="$(tags_of 11.4.0-beta1 11.4.0-rc1 11.4.0)"
expect "a final release outranks its release candidates" 11.4.0 "$(latest_tag 11.)"
TAGS="$(tags_of 12.0.0-alpha1 12.0.0-beta2)"
expect "only pre-releases: reported as no tag, not a crash" "" "$(set -eo pipefail; latest_tag 12.)"
TAGS="$(tags_of v2.0.0-rc.1 v1.9.3 v1.9.2)"
expect "without a prefix the newest stable tag wins" v1.9.3 "$(latest_tag)"
TAGS="$(tags_of release-2026-07-14c)"
expect "a revision letter is not a pre-release" release-2026-07-14c "$(latest_tag release-)"

printf '%s passed, %s failed\n' "$passed" "$failed"
((failed == 0))
Loading