Skip to content

Commit 100bcc5

Browse files
adrianreberclaude
andcommitted
check_for_package_updates: add GitLab repository support
Extend script to check for package updates from GitLab repositories in addition to existing GitHub support. Add functions to parse GitLab URLs, fetch project IDs via GitLab API, and retrieve latest tags. Features: - Support for any GitLab instance (gitlab.com, gitlab.inria.fr, etc.) - Automatic project ID lookup using GitLab search API - Latest tag retrieval from GitLab repositories - Proper error handling with set +e/set -e around command substitutions Enables version checking for GitLab-hosted packages like charliecloud, scotch, and ptscotch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> Signed-off-by: Adrian Reber <[email protected]>
1 parent 6706b15 commit 100bcc5

File tree

1 file changed

+228
-39
lines changed

1 file changed

+228
-39
lines changed

misc/check_for_package_updates.sh

Lines changed: 228 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# Exit on any error, undefined variables, and pipe failures
44
set -euo pipefail
55

6-
# Script to check for newer GitHub releases of OpenHPC packages
6+
# Script to check for newer GitHub and GitLab releases of OpenHPC packages
77
SCRIPT_NAME="$(basename "$0")"
88
readonly SCRIPT_NAME
99
TEMP_DIR="$(mktemp -d)"
@@ -72,11 +72,11 @@ usage() {
7272
cat <<EOF
7373
Usage: ${SCRIPT_NAME} [OPTIONS]
7474
75-
Check for newer GitHub releases of OpenHPC packages by analyzing spec files.
75+
Check for newer GitHub and GitLab releases of OpenHPC packages by analyzing spec files.
7676
7777
OPTIONS:
7878
-v, --verbose Enable verbose output
79-
-p, --prereleases Include pre-releases in version checks
79+
-p, --prereleases Include pre-releases in version checks (GitHub only)
8080
-o, --output FORMAT Output format: table, json, markdown (default: table)
8181
-t, --token TOKEN GitHub API token (or set GITHUB_TOKEN env var)
8282
--no-glow Disable glow formatting for markdown output
@@ -92,9 +92,11 @@ EXAMPLES:
9292
9393
NOTES:
9494
- Requires rpmspec and rpmdev-vercmp tools
95+
- Supports GitHub (github.com) and GitLab (any GitLab instance) repositories
9596
- GitHub API rate limits apply (60 requests/hour without token)
9697
- Use GITHUB_TOKEN environment variable for higher rate limits (5000 requests/hour)
9798
- Get a token at: https://github.com/settings/tokens (no special permissions needed)
99+
- GitLab repositories use public API endpoints (no authentication required)
98100
99101
EOF
100102
}
@@ -237,6 +239,108 @@ parse_github_repo() {
237239
return 1
238240
}
239241

242+
# Parse GitLab repository from URL
243+
parse_gitlab_repo() {
244+
local url="$1"
245+
246+
# Match various GitLab URL patterns
247+
# Examples:
248+
# https://gitlab.com/charliecloud/charliecloud/-/package_files/...
249+
# https://gitlab.inria.fr/scotch/scotch/-/archive/...
250+
if [[ "${url}" =~ https://([^/]+)/([^/]+)/([^/]+)/- ]]; then
251+
local hostname="${BASH_REMATCH[1]}"
252+
local owner="${BASH_REMATCH[2]}"
253+
local repo="${BASH_REMATCH[3]}"
254+
echo "${hostname}|${owner}/${repo}"
255+
return 0
256+
fi
257+
258+
return 1
259+
}
260+
261+
# Get GitLab project ID from hostname and path
262+
get_gitlab_project_id() {
263+
local hostname="$1"
264+
local project_path="$2" # format: owner/repo
265+
local search_name="${project_path##*/}" # extract repo name
266+
267+
debug_info "Searching for GitLab project ID for ${project_path} on ${hostname}"
268+
269+
local search_url="https://${hostname}/api/v4/projects?search=${search_name}"
270+
local projects
271+
272+
projects="$(curl -s "${search_url}" 2>/dev/null)" || {
273+
debug_warn "Failed to search for GitLab project ${project_path} on ${hostname}"
274+
return 1
275+
}
276+
277+
# Check if we got an error
278+
if echo "${projects}" | jq -e '.message' &>/dev/null; then
279+
local message
280+
message="$(echo "${projects}" | jq -r '.message')"
281+
debug_warn "GitLab API error for ${project_path}: ${message}"
282+
return 1
283+
fi
284+
285+
# Filter by path_with_namespace to match the organization/project pattern
286+
local project_id
287+
project_id="$(echo "${projects}" | jq -r ".[] | select(.path_with_namespace == \"${project_path}\") | .id" | head -1)" || {
288+
debug_warn "Failed to parse GitLab project search results for ${project_path}"
289+
return 1
290+
}
291+
292+
if [[ -z "${project_id}" || "${project_id}" == "null" ]]; then
293+
debug_warn "No GitLab project found for ${project_path} on ${hostname}"
294+
return 1
295+
fi
296+
297+
echo "${project_id}"
298+
}
299+
300+
# Get latest release from GitLab API
301+
get_latest_gitlab_release() {
302+
local hostname="$1"
303+
local project_id="$2"
304+
local api_url="https://${hostname}/api/v4/projects/${project_id}/repository/tags"
305+
306+
debug_info "Fetching GitLab tags from ${api_url}"
307+
308+
# Get tags (no authentication needed for public repos)
309+
local tags
310+
tags="$(curl -s "${api_url}" 2>/dev/null)" || {
311+
debug_warn "Failed to fetch tags for GitLab project ${project_id} on ${hostname}"
312+
return 1
313+
}
314+
315+
# Check if we got an error
316+
if echo "${tags}" | jq -e '.message' &>/dev/null; then
317+
local message
318+
message="$(echo "${tags}" | jq -r '.message')"
319+
debug_warn "GitLab API error for project ${project_id}: ${message}"
320+
return 1
321+
fi
322+
323+
# Check if tags list is empty
324+
if echo "${tags}" | jq -e 'length == 0' &>/dev/null; then
325+
debug_info "No tags found for GitLab project ${project_id} on ${hostname}"
326+
return 1
327+
fi
328+
329+
# Get the latest tag (first in the list, as GitLab returns them sorted by creation date desc)
330+
local latest_tag
331+
latest_tag="$(echo "${tags}" | jq -r '.[0].name' 2>/dev/null)" || {
332+
debug_warn "Failed to parse tags for GitLab project ${project_id}"
333+
return 1
334+
}
335+
336+
if [[ "${latest_tag}" == "null" || -z "${latest_tag}" ]]; then
337+
debug_warn "No suitable tags found for GitLab project ${project_id}"
338+
return 1
339+
fi
340+
341+
echo "${latest_tag}"
342+
}
343+
240344
# Get latest version from GNU FTP directory listing
241345
get_latest_gnu_version() {
242346
local project="$1" # e.g., "gcc", "gmp", "mpc", "mpfr"
@@ -286,13 +390,22 @@ check_gnu_component() {
286390
local gnu_project_name="$4" # project name for get_latest_gnu_version
287391

288392
local latest_version
289-
latest_version="$(get_latest_gnu_version "${gnu_project_name}")" || {
393+
# Temporarily disable exit-on-error for command substitution
394+
set +e
395+
latest_version="$(get_latest_gnu_version "${gnu_project_name}")"
396+
local gnu_result=$?
397+
set -e
398+
399+
if [[ ${gnu_result} -ne 0 ]]; then
290400
echo "${component_name}-${component_suffix}|${current_version}|ERROR|Failed to fetch ${gnu_project_name^^} releases|gnu.org/${gnu_project_name}" >>"${RESULTS_FILE}"
291401
return 1
292-
}
402+
fi
293403

294404
local comparison
405+
# Temporarily disable exit-on-error for command substitution
406+
set +e
295407
comparison="$(compare_versions "${current_version}" "${latest_version}")"
408+
set -e
296409

297410
local status
298411
case "${comparison}" in
@@ -521,50 +634,126 @@ process_spec_file() {
521634
IFS='|' read -r name current_version source0 <<<"${pkg_info}"
522635

523636
# Check if source is from GitHub
524-
local github_repo
525-
github_repo="$(parse_github_repo "${source0}")" || {
526-
debug_info "Skipping ${name}: not a GitHub source"
527-
return 0
528-
}
529-
530-
debug_info "Checking GitHub repo: ${github_repo}"
531-
532-
# Get latest release
533-
local latest_tag result_code
637+
local github_repo gitlab_info github_result
534638

535639
# Temporarily disable exit-on-error for command substitution
536640
set +e
537-
latest_tag="$(get_latest_github_release "${github_repo}")"
538-
result_code=$?
641+
github_repo="$(parse_github_repo "${source0}")"
642+
github_result=$?
539643
set -e
540644

541-
if [[ ${result_code} -eq 2 ]]; then
542-
# Rate limited - stop processing more packages to avoid further rate limiting
543-
echo "${name}|${current_version}|RATE_LIMITED|GitHub API rate limit exceeded|${github_repo}" >>"${RESULTS_FILE}"
544-
log_warn "Rate limit reached. Stopping further GitHub API requests."
545-
return 2 # Signal to stop processing
546-
elif [[ ${result_code} -ne 0 ]]; then
547-
echo "${name}|${current_version}|ERROR|Failed to fetch releases|${github_repo}" >>"${RESULTS_FILE}"
645+
if [[ ${github_result} -eq 0 ]]; then
646+
debug_info "Checking GitHub repo: ${github_repo}"
647+
648+
# Get latest release
649+
local latest_tag result_code
650+
651+
# Temporarily disable exit-on-error for command substitution
652+
set +e
653+
latest_tag="$(get_latest_github_release "${github_repo}")"
654+
result_code=$?
655+
set -e
656+
657+
if [[ ${result_code} -eq 2 ]]; then
658+
# Rate limited - stop processing more packages to avoid further rate limiting
659+
echo "${name}|${current_version}|RATE_LIMITED|GitHub API rate limit exceeded|${github_repo}" >>"${RESULTS_FILE}"
660+
log_warn "Rate limit reached. Stopping further GitHub API requests."
661+
return 2 # Signal to stop processing
662+
elif [[ ${result_code} -ne 0 ]]; then
663+
echo "${name}|${current_version}|ERROR|Failed to fetch releases|${github_repo}" >>"${RESULTS_FILE}"
664+
return 0
665+
fi
666+
667+
# Compare versions
668+
local comparison
669+
# Temporarily disable exit-on-error for command substitution
670+
set +e
671+
comparison="$(compare_versions "${current_version}" "${latest_tag}")"
672+
set -e
673+
674+
# Determine status
675+
local status
676+
case "${comparison}" in
677+
"older") status="UPDATE_AVAILABLE" ;;
678+
"same") status="UP_TO_DATE" ;;
679+
"newer") status="AHEAD" ;;
680+
*) status="UNKNOWN" ;;
681+
esac
682+
683+
# Write result
684+
echo "${name}|${current_version}|${latest_tag}|${status}|${github_repo}" >>"${RESULTS_FILE}"
685+
686+
debug_info "Result: ${name} ${current_version} -> ${latest_tag} (${status})"
548687
return 0
549688
fi
550689

551-
# Compare versions
552-
local comparison
553-
comparison="$(compare_versions "${current_version}" "${latest_tag}")"
690+
# Check if source is from GitLab
691+
local gitlab_result
554692

555-
# Determine status
556-
local status
557-
case "${comparison}" in
558-
"older") status="UPDATE_AVAILABLE" ;;
559-
"same") status="UP_TO_DATE" ;;
560-
"newer") status="AHEAD" ;;
561-
*) status="UNKNOWN" ;;
562-
esac
693+
# Temporarily disable exit-on-error for command substitution
694+
set +e
695+
gitlab_info="$(parse_gitlab_repo "${source0}")"
696+
gitlab_result=$?
697+
set -e
698+
699+
if [[ ${gitlab_result} -eq 0 ]]; then
700+
IFS='|' read -r hostname project_path <<<"${gitlab_info}"
701+
debug_info "Checking GitLab repo: ${project_path} on ${hostname}"
702+
703+
# Get project ID
704+
local project_id project_id_result
705+
# Temporarily disable exit-on-error for command substitution
706+
set +e
707+
project_id="$(get_gitlab_project_id "${hostname}" "${project_path}")"
708+
project_id_result=$?
709+
set -e
710+
711+
if [[ ${project_id_result} -ne 0 ]]; then
712+
echo "${name}|${current_version}|ERROR|Failed to get GitLab project ID|${hostname}/${project_path}" >>"${RESULTS_FILE}"
713+
return 0
714+
fi
715+
716+
debug_info "Found GitLab project ID: ${project_id}"
717+
718+
# Get latest tag
719+
local latest_tag tag_result
720+
# Temporarily disable exit-on-error for command substitution
721+
set +e
722+
latest_tag="$(get_latest_gitlab_release "${hostname}" "${project_id}")"
723+
tag_result=$?
724+
set -e
725+
726+
if [[ ${tag_result} -ne 0 ]]; then
727+
echo "${name}|${current_version}|ERROR|Failed to fetch GitLab tags|${hostname}/${project_path}" >>"${RESULTS_FILE}"
728+
return 0
729+
fi
730+
731+
# Compare versions
732+
local comparison
733+
# Temporarily disable exit-on-error for command substitution
734+
set +e
735+
comparison="$(compare_versions "${current_version}" "${latest_tag}")"
736+
set -e
737+
738+
# Determine status
739+
local status
740+
case "${comparison}" in
741+
"older") status="UPDATE_AVAILABLE" ;;
742+
"same") status="UP_TO_DATE" ;;
743+
"newer") status="AHEAD" ;;
744+
*) status="UNKNOWN" ;;
745+
esac
563746

564-
# Write result
565-
echo "${name}|${current_version}|${latest_tag}|${status}|${github_repo}" >>"${RESULTS_FILE}"
747+
# Write result
748+
echo "${name}|${current_version}|${latest_tag}|${status}|${hostname}/${project_path}" >>"${RESULTS_FILE}"
566749

567-
debug_info "Result: ${name} ${current_version} -> ${latest_tag} (${status})"
750+
debug_info "Result: ${name} ${current_version} -> ${latest_tag} (${status})"
751+
return 0
752+
fi
753+
754+
# Not a GitHub or GitLab source
755+
debug_info "Skipping ${name}: not a GitHub or GitLab source"
756+
return 0
568757
}
569758

570759
# Format and display results
@@ -755,7 +944,7 @@ main() {
755944
check_dependencies
756945
setup_rpm_environment
757946

758-
log_info "Scanning for OpenHPC packages with GitHub sources..."
947+
log_info "Scanning for OpenHPC packages with GitHub and GitLab sources..."
759948
debug_info "Using temporary directory: ${TEMP_DIR}"
760949

761950
# Find and process all spec files

0 commit comments

Comments
 (0)