33# Exit on any error, undefined variables, and pipe failures
44set -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
77SCRIPT_NAME=" $( basename " $0 " ) "
88readonly SCRIPT_NAME
99TEMP_DIR=" $( mktemp -d) "
@@ -72,11 +72,11 @@ usage() {
7272 cat << EOF
7373Usage: ${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
7777OPTIONS:
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
9393NOTES:
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
99101EOF
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
241345get_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