Files
TheRock/rocm-libraries/projects/hipdnn/scripts/bump-hipdnn-versions.sh
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

414 lines
15 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# check-version-bumps.sh - report/apply hipDNN per-component version bumps.
# Run with --help for full usage.
set -euo pipefail
usage() {
cat <<'EOF'
check-version-bumps.sh
For each hipDNN component (identified by a version.json file), report whether
its source has drifted since the last release branch was cut, and whether its
version.json still needs to be bumped.
Classification per component:
OK (clean) - no CODE changed since the release ref (cmake-only churn is OK)
OK (bumped) - code changed, but version.json already changed too
NEEDS BUMP - code changed and version.json is unchanged since release
CMake project files (CMakeLists.txt, CMakePresets.json, CMakeUserPresets.json,
*.cmake / *.cmake.in, and anything under a cmake/ directory) are build plumbing,
not shipped source, so by default they do NOT by themselves trigger a bump. Pass
--cmake-bumps to make cmake changes count toward the bump decision too. The
CHANGES column classifies each component's drift regardless of that flag:
code - only non-cmake files changed
cmake-only - only cmake project files changed
mixed - both code and cmake files changed
- - nothing changed
The SRC-CHG column counts the files that drive the decision: code only by
default, or code+cmake with --cmake-bumps.
A component's "scope" is its version.json directory, EXCLUDING any nested
subtree that has its own version.json (so hip-kernel-provider and its nested
rocke/.../version.json are scored independently).
Usage:
check-version-bumps.sh [-C|--repo <path>] [--release <ref>] [--target <ref>]
[--diff <component>] [--cmake-bumps] [--check]
[--apply] [--major[=paths]] [--minor[=paths]] [--patch[=paths]]
Defaults:
-C, --repo <path> Worktree folder to operate in (default: current directory)
--release highest origin/release/therock-* branch (version-sorted)
--target Endpoint to compare the release against (default HEAD). Use your
mainline ref (e.g. origin/develop) to measure drift since the
release was cut, rather than HEAD which also includes your
feature-branch commits.
Inspecting drift:
--diff <component> Print the git diff of the exact files that count as drift
for one component (its scope dir, excluding version.json and any
nested component subtree), then exit. <component> is a scope dir,
e.g. projects/hipdnn/backend. Honors --release and --target.
--cmake-bumps Count CMake project-file changes toward the bump decision too
(by default they are excluded as build plumbing).
--check Exit non-zero (1) if any component still needs a bump; exit 0 only
when all components are OK. For CI gating. Honors --cmake-bumps.
Bumping (writes to working-tree version.json files; report-only otherwise):
--apply Write bumps. Default level is MINOR for every NEEDS BUMP component
unless overridden by the level flags below. Implied when any level
flag is given.
--major [paths] Bump the listed paths (comma-separated) at MAJOR level.
--minor [paths] Bump the listed paths at MINOR level.
--patch [paths] Bump the listed paths at PATCH level.
A level flag with NO path list becomes the DEFAULT level for every path not
explicitly listed under another level flag. Only ONE of the three may omit
its list. Paths match a component by its scope dir (e.g. projects/hipdnn/backend)
or an ancestor of it.
Increment rules (lower fields reset to zero):
major: X.Y.Z -> (X+1).0.0 minor: X.Y.Z -> X.(Y+1).0 patch: X.Y.Z -> X.Y.(Z+1)
NOTE: the semver level is a human judgement the script cannot make for you.
Examples:
check-version-bumps.sh
check-version-bumps.sh --target origin/develop
check-version-bumps.sh --target origin/develop --diff projects/hipdnn/backend
check-version-bumps.sh --target origin/develop --apply
check-version-bumps.sh --target origin/develop --major dnn-providers/hip-kernel-provider --patch projects/hipdnn/flatbuffers_sdk
check-version-bumps.sh --target origin/develop --patch --minor projects/hipdnn/backend,projects/hipdnn/frontend
EOF
}
REPO="$(pwd)"
RELEASE_REF=""
TARGET_REF="HEAD"
APPLY=false
DIFF_COMPONENT=""
CMAKE_BUMPS=false
CHECK=false
declare -a MAJOR_PATHS=() MINOR_PATHS=() PATCH_PATHS=()
MAJOR_GIVEN=false; MINOR_GIVEN=false; PATCH_GIVEN=false
MAJOR_LISTLESS=false; MINOR_LISTLESS=false; PATCH_LISTLESS=false
while [[ $# -gt 0 ]]; do
case "$1" in
-C|--repo) REPO="$2"; shift 2 ;;
--release) RELEASE_REF="$2"; shift 2 ;;
--target) TARGET_REF="$2"; shift 2 ;;
--diff) DIFF_COMPONENT="$2"; shift 2 ;;
--cmake-bumps) CMAKE_BUMPS=true; shift ;;
--check) CHECK=true; shift ;;
--apply) APPLY=true; shift ;;
--major)
APPLY=true; MAJOR_GIVEN=true
if [[ $# -ge 2 && "$2" != -* ]]; then IFS=',' read -ra MAJOR_PATHS <<<"$2"; shift 2
else MAJOR_LISTLESS=true; shift; fi ;;
--minor)
APPLY=true; MINOR_GIVEN=true
if [[ $# -ge 2 && "$2" != -* ]]; then IFS=',' read -ra MINOR_PATHS <<<"$2"; shift 2
else MINOR_LISTLESS=true; shift; fi ;;
--patch)
APPLY=true; PATCH_GIVEN=true
if [[ $# -ge 2 && "$2" != -* ]]; then IFS=',' read -ra PATCH_PATHS <<<"$2"; shift 2
else PATCH_LISTLESS=true; shift; fi ;;
-h|--help)
usage; exit 0 ;;
*) echo "Unknown argument: $1" >&2; exit 2 ;;
esac
done
# Determine the default bump level (from the single listless level flag, if any).
DEFAULT_LEVEL="minor"
listless_count=0
$MAJOR_LISTLESS && { DEFAULT_LEVEL="major"; listless_count=$((listless_count + 1)); }
$MINOR_LISTLESS && { DEFAULT_LEVEL="minor"; listless_count=$((listless_count + 1)); }
$PATCH_LISTLESS && { DEFAULT_LEVEL="patch"; listless_count=$((listless_count + 1)); }
if [[ "$listless_count" -gt 1 ]]; then
echo "ERROR: only one of --major/--minor/--patch may omit its path list." >&2
exit 2
fi
git_c() { git -C "$REPO" "$@"; }
# Validate that the repo path is a git repository.
if ! git_c rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "ERROR: not a git repository: $REPO" >&2
exit 1
fi
# --- Resolve release ref -----------------------------------------------------
if [[ -z "$RELEASE_REF" ]]; then
RELEASE_REF=$(git_c for-each-ref --format='%(refname:short)' \
'refs/remotes/origin/release/therock-*' \
| sort -V | tail -1)
if [[ -z "$RELEASE_REF" ]]; then
echo "ERROR: no origin/release/therock-* branches found. Fetch them first." >&2
exit 1
fi
fi
# Validate refs exist.
for ref in "$RELEASE_REF" "$TARGET_REF"; do
if ! git_c rev-parse --verify --quiet "$ref^{commit}" >/dev/null; then
echo "ERROR: ref not found: $ref" >&2
exit 1
fi
done
echo "Repo: $REPO"
echo "Release: $RELEASE_REF ($(git_c rev-parse --short "$RELEASE_REF"))"
echo "Target: $TARGET_REF ($(git_c rev-parse --short "$TARGET_REF"))"
echo
# --- Discover component version.json files -----------------------------------
mapfile -t VERSION_FILES < <(
git_c ls-files 'projects/hipdnn/**/version.json' 'dnn-providers/**/version.json' \
| sort
)
if [[ ${#VERSION_FILES[@]} -eq 0 ]]; then
echo "No hipDNN version.json files found under projects/hipdnn or dnn-providers." >&2
exit 1
fi
# Directories that own a version.json (used to compute nested exclusions).
declare -a SCOPE_DIRS=()
for vf in "${VERSION_FILES[@]}"; do
SCOPE_DIRS+=("$(dirname "$vf")")
done
# Extract the version value from a version.json at a given ref (key-name agnostic).
get_version() {
local ref="$1" file="$2"
{ git_c show "$ref:$file" 2>/dev/null \
| grep -oE '"[0-9]+\.[0-9]+\.[0-9]+[^"]*"' || true; } | head -1 | tr -d '"'
}
# Read the version value from a working-tree version.json.
get_worktree_version() {
{ grep -oE '"[0-9]+\.[0-9]+\.[0-9]+[^"]*"' "$REPO/$1" || true; } | head -1 | tr -d '"'
}
# Print the git pathspec for a component's scope: its dir, excluding its own
# version.json and any nested component subtree (one line per pathspec element).
# Usage: build_pathspec <version.json-path>
build_pathspec() {
local vf="$1" dir other
dir="$(dirname "$vf")"
printf '%s\n' "$dir" ":(exclude)$vf"
for other in "${SCOPE_DIRS[@]}"; do
if [[ "$other" != "$dir" && "$other" == "$dir"/* ]]; then
printf '%s\n' ":(exclude)$other/**"
fi
done
}
# True if a path is a CMake project file (build plumbing, not shipped source):
# CMakeLists.txt, CMakePresets.json, CMakeUserPresets.json, *.cmake / *.cmake.in,
# or anything living in a cmake/ directory.
is_cmake_file() {
local path="$1" base
base="$(basename "$path")"
case "$base" in
CMakeLists.txt|CMakePresets.json|CMakeUserPresets.json) return 0 ;;
*.cmake|*.cmake.in) return 0 ;;
esac
[[ "$path" == cmake/* || "$path" == */cmake/* ]] && return 0
return 1
}
# Compute the next semver given a current X.Y.Z (any suffix is dropped) and level.
bump_version() {
local current="$1" level="$2"
local core="${current%%-*}" # strip any -prerelease suffix
IFS='.' read -r maj min pat <<<"$core"
case "$level" in
patch) pat=$((pat + 1)) ;;
minor) min=$((min + 1)); pat=0 ;;
major) maj=$((maj + 1)); min=0; pat=0 ;;
esac
echo "$maj.$min.$pat"
}
# Rewrite the version value in a working-tree version.json, preserving its key.
write_version() {
local file="$1" old="$2" new="$3"
# Replace the first occurrence of the old quoted version with the new one.
sed -i "0,/\"$old\"/s//\"$new\"/" "$REPO/$file"
}
# True if component scope dir matches (equals or is nested under) any listed path.
scope_matches() {
local dir="$1"; shift
local p
for p in "$@"; do
p="${p%/}"
[[ "$dir" == "$p" || "$dir" == "$p"/* ]] && return 0
done
return 1
}
# Resolve the bump level for a component's scope dir from the level path lists,
# falling back to DEFAULT_LEVEL when it matches none.
resolve_level() {
local dir="$1"
if [[ ${#MAJOR_PATHS[@]} -gt 0 ]] && scope_matches "$dir" "${MAJOR_PATHS[@]}"; then echo major; return; fi
if [[ ${#MINOR_PATHS[@]} -gt 0 ]] && scope_matches "$dir" "${MINOR_PATHS[@]}"; then echo minor; return; fi
if [[ ${#PATCH_PATHS[@]} -gt 0 ]] && scope_matches "$dir" "${PATCH_PATHS[@]}"; then echo patch; return; fi
echo "$DEFAULT_LEVEL"
}
# --- --diff: show the drift diff for one component, then exit ----------------
if [[ -n "$DIFF_COMPONENT" ]]; then
want="${DIFF_COMPONENT%/}"
match_vf=""
for vf in "${VERSION_FILES[@]}"; do
d="$(dirname "$vf")"
if [[ "$d" == "$want" ]]; then match_vf="$vf"; break; fi
done
if [[ -z "$match_vf" ]]; then
echo "ERROR: no component with scope dir '$want'." >&2
echo "Known components:" >&2
for vf in "${VERSION_FILES[@]}"; do echo " $(dirname "$vf")" >&2; done
exit 1
fi
mapfile -t pathspec < <(build_pathspec "$match_vf")
echo "Diff for $want ($RELEASE_REF..$TARGET_REF):"
echo
git_c diff "$RELEASE_REF..$TARGET_REF" -- "${pathspec[@]}"
exit 0
fi
# --- Per-component drift analysis -------------------------------------------
# Populates the global arrays BUMP_FILES / BUMP_LEVELS (NEEDS BUMP components)
# and sets NEEDS_BUMP_COUNT. Reads current versions from the working tree so a
# re-run after --apply reflects the just-written bumps.
declare -a BUMP_FILES=() BUMP_LEVELS=()
declare -A BUMPED_BY_SCRIPT=() # version.json paths this run wrote via --apply
NEEDS_BUMP_COUNT=0
# run_analysis [post] - pass "post" to render the after-apply header labels.
run_analysis() {
local mode="${1:-pre}"
local old_hdr="VERSION" new_hdr="PROPOSED"
if [[ "$mode" == "post" ]]; then old_hdr="OLD VER"; new_hdr="NEW VER"; fi
NEEDS_BUMP_COUNT=0
BUMP_FILES=(); BUMP_LEVELS=()
printf '%-58s %-9s %-9s %-10s %-11s %-9s %s\n' \
"COMPONENT (scope dir)" "$old_hdr" "$new_hdr" "SRC-CHG" "CHANGES" "VER-CHGD" "STATUS"
printf '%-58s %-9s %-9s %-10s %-11s %-9s %s\n' \
"$(printf '%.0s-' {1..58})" "$(printf '%.0s-' {1..9})" "$(printf '%.0s-' {1..9})" \
"$(printf '%.0s-' {1..10})" "$(printf '%.0s-' {1..11})" \
"$(printf '%.0s-' {1..9})" "$(printf '%.0s-' {1..11})"
local vf dir pathspec f rel_version cur_version
local code_count cmake_count bump_count changes src ver status proposed level
for vf in "${VERSION_FILES[@]}"; do
dir="$(dirname "$vf")"
mapfile -t pathspec < <(build_pathspec "$vf")
# Partition changed files (release..target) into cmake plumbing vs code.
# Only code changes drive the bump decision.
code_count=0; cmake_count=0
while IFS= read -r f; do
[[ -z "$f" ]] && continue
if is_cmake_file "$f"; then cmake_count=$((cmake_count + 1))
else code_count=$((code_count + 1)); fi
done < <(git_c diff --name-only "$RELEASE_REF..$TARGET_REF" -- "${pathspec[@]}")
# CHANGES classification.
if [[ "$code_count" -gt 0 && "$cmake_count" -gt 0 ]]; then changes="mixed"
elif [[ "$code_count" -gt 0 ]]; then changes="code"
elif [[ "$cmake_count" -gt 0 ]]; then changes="cmake-only"
else changes="-"; fi
# Files that drive the bump decision: code only, or code+cmake with --cmake-bumps.
if $CMAKE_BUMPS; then bump_count=$((code_count + cmake_count)); else bump_count=$code_count; fi
# Version bumped since release? Compare working-tree value to release value.
rel_version="$(get_version "$RELEASE_REF" "$vf")"
cur_version="$(get_worktree_version "$vf")"
cur_version="${cur_version:-?}"
if [[ "$rel_version" != "$cur_version" ]]; then ver="yes"; else ver="no"; fi
proposed="$cur_version"
if [[ "$bump_count" -eq 0 ]]; then
# No drift that counts toward a bump -> no bump needed.
src="no"; status="OK (clean)"
else
src="yes ($bump_count)"
if [[ "$ver" == "yes" ]]; then
# (bumped) marks rows this script wrote this run; pre-existing bumps show OK.
if [[ -n "${BUMPED_BY_SCRIPT[$vf]:-}" ]]; then status="OK (bumped)"; else status="OK"; fi
else
status="NEEDS BUMP"
level="$(resolve_level "$dir")"
proposed="$(bump_version "$cur_version" "$level")"
NEEDS_BUMP_COUNT=$((NEEDS_BUMP_COUNT + 1))
BUMP_FILES+=("$vf")
BUMP_LEVELS+=("$level")
fi
fi
printf '%-58s %-9s %-9s %-10s %-11s %-9s %s\n' \
"$dir" "$cur_version" "$proposed" "$src" "$changes" "$ver" "$status"
done
}
run_analysis
echo
if [[ "$NEEDS_BUMP_COUNT" -eq 0 ]]; then
echo "All components OK. No version bumps required."
exit 0
fi
echo "$NEEDS_BUMP_COUNT component(s) NEED a version bump."
# --- Apply bumps (opt-in) ----------------------------------------------------
if ! $APPLY; then
echo "(dry run - pass --apply, or --major/--minor/--patch, to write these bumps)"
$CHECK && exit 1
exit 0
fi
echo
echo "Applying bumps to working-tree version.json files (default level: $DEFAULT_LEVEL):"
for i in "${!BUMP_FILES[@]}"; do
vf="${BUMP_FILES[$i]}"
level="${BUMP_LEVELS[$i]}"
dir="$(dirname "$vf")"
old="$(get_worktree_version "$vf")"
if [[ -z "$old" ]]; then
echo " SKIP $vf (could not parse current version)"
continue
fi
new="$(bump_version "$old" "$level")"
write_version "$vf" "$old" "$new"
BUMPED_BY_SCRIPT["$vf"]=1
printf ' %-6s %-52s %s -> %s\n' "$level" "$dir" "$old" "$new"
done
echo
echo "Re-running analysis after applying bumps:"
echo
run_analysis post
echo
if [[ "$NEEDS_BUMP_COUNT" -eq 0 ]]; then
echo "All components OK after bumping."
else
echo "WARNING: $NEEDS_BUMP_COUNT component(s) still show NEEDS BUMP."
fi
echo "Review with: git -C $REPO diff -- '**/version.json'"
if $CHECK && [[ "$NEEDS_BUMP_COUNT" -ne 0 ]]; then exit 1; fi