Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,129 @@ extract_version_from_branch() {
return 1
}

# Compare two X.Y version strings numerically by major then minor.
# Echoes -1, 0, or 1 (a <, ==, > b).
compare_versions() {
local a_major="${1%%.*}"
local a_minor="${1##*.}"
local b_major="${2%%.*}"
local b_minor="${2##*.}"

if [[ $a_major -lt $b_major ]]; then
echo "-1"
elif [[ $a_major -gt $b_major ]]; then
echo "1"
elif [[ $a_minor -lt $b_minor ]]; then
echo "-1"
elif [[ $a_minor -gt $b_minor ]]; then
echo "1"
else
echo "0"
fi
}

# Compare a Tekton file's embedded version against a target X.Y version.
# Prefers the semantic version embedded in the file content (e.g. a
# "release-5.0" / "backplane-5.0" branch reference), since compact filename
# versions (e.g. "50", "217") don't sort correctly once a minor version
# reaches double digits (mirrors get_highest_tekton_version's approach).
# Falls back to comparing the compact filename version numerically if no
# semantic version can be extracted from the file content.
# Echoes -1, 0, or 1 (file version <, ==, > target).
tekton_file_version_compare() {
local file=$1
local branch_prefix=$2
local file_ver_compact=$3 # e.g. "50" or "5-0"
local target_version=$4 # e.g. "5.0"

local semantic_version
# `|| true` guards against `grep` finding no match (exit 1) so the
# fallback below always runs, regardless of any future `set -e` change.
semantic_version=$(grep -oE "${branch_prefix}-[0-9]+\.[0-9]+" "$file" 2>/dev/null | head -1 | cut -d'-' -f2 || true)

if [[ -n "$semantic_version" ]]; then
compare_versions "$semantic_version" "$target_version"
return
fi

# Fallback: parse the compact filename version into major.minor and
# compare with compare_versions, instead of comparing the compact
# representations as plain integers. A plain-integer comparison
# misorders versions once a minor version reaches double digits, e.g.
# compact "217" (2.17) vs "50" (5.0) would read as 217 > 50 and treat
# 2.17 as newer than 5.0.
local file_major file_minor
if [[ "$file_ver_compact" == *-* ]]; then
# globalhub-style compact version, already major-minor delimited (e.g. "5-0")
file_major="${file_ver_compact%%-*}"
file_minor="${file_ver_compact##*-}"
else
# acm/mce-style compact version: 3+ digits is MAJOR + 2-digit MINOR
# (e.g. "217" -> 2.17), otherwise MAJOR + 1-digit MINOR (e.g. "50" -> 5.0)
# (mirrors the same convention used in create_tekton_files).
local compact_num=$((10#${file_ver_compact}))
if [[ ${#file_ver_compact} -ge 3 ]]; then
file_major=$((compact_num / 100))
file_minor=$((compact_num % 100))
else
file_major=$((compact_num / 10))
file_minor=$((compact_num % 10))
fi
fi

compare_versions "${file_major}.${file_minor}" "${target_version}"
}

# Remove Tekton files whose embedded version is <= max_version (X.Y format).
# Must be called with cwd inside the target git working tree, on the branch
# already checked out. Stages removals with `git rm`. All log output goes to
# stderr; only the result is written to stdout as "<removed> <failed>", so
# callers can safely capture it via command substitution and must treat a
# non-zero <failed> count as an error rather than silently proceeding.
cleanup_stale_tekton_files() {
local product_prefix=$1
local branch_prefix=$2
local max_version=$3 # e.g. "5.0" - files at or below this are removed

local removed=0
local failed=0

for old_file in .tekton/*-"${product_prefix}"-*-*.yaml; do
[[ -f "$old_file" ]] || continue

local old_filename
old_filename=$(basename "$old_file")

local old_ver_compact=""
if [[ "${product_prefix}" == "globalhub" ]]; then
if [[ "$old_filename" =~ ${product_prefix}-([0-9]+-[0-9]+)- ]]; then
old_ver_compact="${BASH_REMATCH[1]}"
fi
else
if [[ "$old_filename" =~ ${product_prefix}-([0-9]+)- ]]; then
old_ver_compact="${BASH_REMATCH[1]}"
fi
fi

[[ -n "$old_ver_compact" ]] || continue

local cmp
cmp=$(tekton_file_version_compare "$old_file" "${branch_prefix}" "${old_ver_compact}" "${max_version}")

if [[ "$cmp" == "-1" || "$cmp" == "0" ]]; then
echo "INFO: Removing stale Tekton file: ${old_filename} (<= ${max_version})" >&2
if git rm -q "$old_file" >/dev/null 2>&1; then
removed=$((removed + 1))
else
echo "ERROR: Could not remove ${old_file}" >&2
failed=$((failed + 1))
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi
done

echo "$removed $failed"
}

# Transform Tekton files from source version to destination version
# After fast-forward, renames and updates Tekton files for new branch
transform_tekton_files() {
Expand Down Expand Up @@ -246,18 +369,47 @@ transform_tekton_files() {
files_transformed=$((files_transformed + 1))
done

if [[ "$files_found" == "false" ]]; then
echo "INFO: No ${source_pattern}*.yaml files found to transform"
# Remove any remaining Tekton files older than or equal to
# LAST_RELEASE_VERSION. The exact source_version files (if any) were
# already renamed above via `git mv`; this sweeps up stragglers from
# earlier release cycles that rode along via fast-forward (e.g. an
# acm-50- template that was never cleaned up on this branch).
local removed_stale=0
local failed_cleanup=0
if [[ -n "${last_release_version:-}" ]]; then
local cleanup_result
cleanup_result=$(cleanup_stale_tekton_files "${product_prefix}" "${branch_prefix}" "${last_release_version}")
removed_stale="${cleanup_result%% *}"
failed_cleanup="${cleanup_result##* }"
fi

if [[ "${failed_cleanup}" -gt 0 ]]; then
echo "ERROR: Failed to remove ${failed_cleanup} stale Tekton file(s) <= ${last_release_version}, aborting"
exit 1
fi

if [[ "$files_found" == "false" ]] && [[ "${removed_stale}" -eq 0 ]]; then
echo "INFO: No ${source_pattern}*.yaml files found to transform, nothing stale to clean up"
exit 0
fi

echo "INFO: Transformed ${files_transformed} files"
if [[ "${removed_stale}" -gt 0 ]]; then
echo "INFO: Removed ${removed_stale} stale Tekton file(s) <= ${last_release_version}"
fi

# Commit transformation
git config user.email "${GITHUB_USER}@users.noreply.github.com"
git config user.name "${GITHUB_USER}"

git commit -m "Transform Tekton files from ${source_version} to ${dest_version}
local commit_message="Transform Tekton files from ${source_version} to ${dest_version}"
if [[ "${removed_stale}" -gt 0 ]]; then
commit_message="${commit_message}

Remove ${removed_stale} stale Tekton file(s) <= ${last_release_version}"
fi

git commit -m "${commit_message}

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"

Expand Down Expand Up @@ -543,62 +695,18 @@ create_tekton_files() {

cd "$repo" || exit 1

# First check if files already exist on DEFAULT branch
log "INFO Checking if files already exist on ${default_branch}"
local all_versions_exist=true
for dest_version in ${dest_versions}; do
local dest_ver_compact
if [[ "${product}" == "globalhub" ]]; then
dest_ver_compact="${dest_version//./-}"
else
dest_ver_compact="${dest_version//./}"
fi

if ! compgen -G ".tekton/*-${product_prefix}-${dest_ver_compact}-*.yaml" >/dev/null; then
all_versions_exist=false
log "INFO ${product_prefix}-${dest_ver_compact} files missing on ${default_branch}"
break
else
log "INFO ${product_prefix}-${dest_ver_compact} files already exist on ${default_branch}"
fi
done

if [[ "$all_versions_exist" == "true" ]]; then
log "INFO All requested versions already exist on ${default_branch}, nothing to do"

# Clean up stale PR branch if exists
local pr_branch="add-tekton-files-${dest_versions// /-}"
if git ls-remote --heads origin "${pr_branch}" | grep -q "${pr_branch}"; then
log "INFO Stale PR branch ${pr_branch} found, cleaning up"

# Close PR if exists
if command -v gh >/dev/null 2>&1; then
export GH_TOKEN="${token}"
local pr_num
pr_num=$(gh pr list --repo "${owner}/${repo}" --head "${pr_branch}" --json number --jq '.[0].number' 2>/dev/null || echo "")

if [[ -n "${pr_num}" ]]; then
log "INFO Closing obsolete PR #${pr_num}"
if gh pr close "${pr_num}" --repo "${owner}/${repo}" \
--comment "Closing - all Tekton files already merged to ${default_branch}" 2>&1; then
log "INFO Closed PR #${pr_num}"
else
log "WARNING Failed to close PR #${pr_num}"
fi
fi
fi

# Delete branch
log "INFO Deleting stale branch ${pr_branch}"
if git push origin --delete "${pr_branch}" 2>&1; then
log "INFO Deleted branch ${pr_branch}"
else
log "WARNING Failed to delete branch ${pr_branch}"
fi
fi

exit 0
fi
# NOTE: We intentionally do not short-circuit here even when every
# requested version already exists on ${default_branch}. An earlier
# version of this function returned immediately in that case (after
# only tidying up an obsolete PR branch), which skipped the stale
# Tekton file cleanup below entirely - so files <= LAST_RELEASE_VERSION
# could persist indefinitely as long as no new destination version
# needed to be created. The per-version loop further down already
# no-ops correctly (via `continue`) for any version that already
# exists, and the "no new files" handling after cleanup already closes
# obsolete PRs/branches based on an actual diff against
# ${default_branch}, so falling through here is both simpler and
# strictly more correct than special-casing "all versions exist".

# Create branch for PR
local pr_branch="add-tekton-files-${dest_versions// /-}"
Expand Down Expand Up @@ -781,6 +889,26 @@ create_tekton_files() {
fi
done

# Remove Tekton files for versions <= LAST_RELEASE_VERSION.
# LAST_RELEASE_VERSION was only kept around as a template for creating
# the versions above; now that those new versions have been created on
# ${default_branch}, the old template is stale and should not persist.
local removed_stale=0
local failed_cleanup=0
if [[ -n "${LAST_RELEASE_VERSION:-}" ]]; then
log "INFO Cleaning up Tekton files <= LAST_RELEASE_VERSION (${LAST_RELEASE_VERSION})"
local cleanup_result
cleanup_result=$(cleanup_stale_tekton_files "${product_prefix}" "${branch_prefix}" "${LAST_RELEASE_VERSION}")
removed_stale="${cleanup_result%% *}"
failed_cleanup="${cleanup_result##* }"
log "INFO Removed ${removed_stale} stale Tekton file(s)"

if [[ "${failed_cleanup}" -gt 0 ]]; then
log "ERROR Failed to remove ${failed_cleanup} stale Tekton file(s) <= LAST_RELEASE_VERSION, aborting"
exit 1
fi
Comment thread
coderabbitai[bot] marked this conversation as resolved.
fi

# Check if PR exists for branch (even if no new files)
local pr_title
pr_title="Add Tekton files for ${product_prefix} versions: ${dest_versions}"
Expand All @@ -789,24 +917,36 @@ create_tekton_files() {
$(for v in ${dest_versions}; do echo "- ${product_prefix}-${v//./}"; done)

Generated from existing ${product_prefix}-${highest_version} templates."
if [[ "${removed_stale}" -gt 0 ]]; then
pr_body="${pr_body}

Also removes ${removed_stale} stale Tekton file(s) for version <= ${LAST_RELEASE_VERSION}, which are no longer needed now that the versions above exist."
fi

local pr_exists=false
local pr_num=""
if command -v gh >/dev/null 2>&1; then
export GH_TOKEN="${token}"

log "INFO Checking if PR already exists for ${pr_branch}"
if gh pr list --head "${pr_branch}" --json number --jq '.[0].number' 2>&1 | grep -q '^[0-9]'; then
pr_num=$(gh pr list --head "${pr_branch}" --json number --jq '.[0].number' 2>/dev/null || echo "")
if [[ -n "${pr_num}" ]]; then
pr_exists=true
log "INFO PR already exists for ${pr_branch}"
log "INFO PR already exists for ${pr_branch} (#${pr_num})"
fi
fi

if [[ "$files_created" == "false" ]]; then
log "INFO No new files to create"

# Create PR if branch existed on remote (has commits) but no PR
if [[ "$pr_exists" == "false" ]] && [[ "$branch_existed_on_remote" == "true" ]] && command -v gh >/dev/null 2>&1; then
# Check if branch differs from default branch
if [[ "$files_created" == "false" ]] && [[ "${removed_stale}" -eq 0 ]]; then
log "INFO No new files to create and nothing stale to clean up"

if [[ "$branch_existed_on_remote" == "true" ]] && command -v gh >/dev/null 2>&1; then
# Check if branch differs from default branch. This covers both:
# - a pre-existing PR branch that turned out to need no changes
# (e.g. all requested versions already existed on default_branch
# and there was nothing stale to remove), which should be closed
# and deleted rather than left open indefinitely
# - a pre-existing PR branch with real, uncommitted-here changes
# from a previous run, which should get its PR (re)created
log "INFO Checking if ${pr_branch} differs from ${default_branch}"
if ! git fetch origin "${default_branch}" 2>&1; then
log "WARNING Could not fetch ${default_branch}"
Expand All @@ -815,6 +955,17 @@ Generated from existing ${product_prefix}-${highest_version} templates."

if git diff --quiet "origin/${default_branch}" HEAD; then
log "INFO Branch ${pr_branch} is identical to ${default_branch}, no PR needed"

if [[ -n "${pr_num}" ]]; then
log "INFO Closing obsolete PR #${pr_num}"
if gh pr close "${pr_num}" --repo "${owner}/${repo}" \
--comment "Closing - all Tekton files already merged to ${default_branch}" 2>&1; then
log "INFO Closed PR #${pr_num}"
else
log "WARNING Failed to close PR #${pr_num}"
fi
fi

log "INFO Deleting orphaned branch ${pr_branch}"
if git push origin --delete "${pr_branch}" 2>&1; then
log "INFO Successfully deleted ${pr_branch}"
Expand All @@ -824,14 +975,16 @@ Generated from existing ${product_prefix}-${highest_version} templates."
exit 0
fi

log "INFO Creating PR for existing branch with changes"
if [[ "$pr_exists" == "false" ]]; then
log "INFO Creating PR for existing branch with changes"

if ! gh pr create \
--title "${pr_title}" \
--body "${pr_body}" \
--base "${default_branch}" \
--head "${pr_branch}" 2>&1; then
log "WARNING PR creation failed"
if ! gh pr create \
--title "${pr_title}" \
--body "${pr_body}" \
--base "${default_branch}" \
--head "${pr_branch}" 2>&1; then
log "WARNING PR creation failed"
fi
fi
fi

Expand All @@ -843,8 +996,15 @@ Generated from existing ${product_prefix}-${highest_version} templates."
git config user.name "OpenShift CI Robot"
git config user.email "noreply@openshift.io"

log "INFO Committing: Add Tekton files for versions: ${dest_versions}"
git commit -s -m "Add Tekton files for versions: ${dest_versions}" 2>&1
local commit_message="Add Tekton files for versions: ${dest_versions}"
if [[ "${removed_stale}" -gt 0 ]]; then
commit_message="${commit_message}

Remove ${removed_stale} stale Tekton file(s) for version <= ${LAST_RELEASE_VERSION}"
fi

log "INFO Committing: ${commit_message}"
git commit -s -m "${commit_message}" 2>&1

# Push branch (use -u for new branch, --force-with-lease if we reset existing branch)
log "INFO Pushing ${pr_branch} to origin"
Expand Down