| #!/usr/bin/env bash |
| # ----------------------------------------------------------------------- |
| # Copyright 2018-2024 Open Networking Foundation Contributors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http:#www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| # ----------------------------------------------------------------------- |
| # SPDX-FileCopyrightText: 2018-2024 Open Networking Foundation Contributors |
| # SPDX-License-Identifier: Apache-2.0 |
| # ----------------------------------------------------------------------- |
| # Intent: |
| # chart_version_check.sh |
| # checks that changes to a chart include a change to the chart version |
| # --------------------------------------------------------------------------- |
| |
| set -u |
| # set -euo pipefail |
| |
| ##-------------------## |
| ##---] GLOBALS [---## |
| ##-------------------## |
| declare -i -g force_fail=0 |
| declare -a -g error_stream=() |
| |
| # declare -A -g ARGV=() # ARGV['debug']=1 |
| # declare -i -g debug=1 # uncomment to enable |
| |
| ##--------------------## |
| ##---] INCLUDES [---## |
| ##--------------------## |
| { |
| declare pgm |
| pgm="$(realpath "${BASH_SOURCE[0]}")" |
| declare libdir="${pgm%.sh}" |
| |
| # shellcheck source=chart_version_check/filter_files.sh |
| # shellcheck disable=SC1091 |
| source "$libdir/filter_files.sh" |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Display a program banner for logging |
| ## ----------------------------------------------------------------------- |
| # shellcheck disable=SC2120 |
| function banner() |
| { |
| local program="${0##*/}" |
| |
| cat <<BANNER |
| |
| # ----------------------------------------------------------------------- |
| # Running: ${program} $@ |
| # ----------------------------------------------------------------------- |
| # Now: $(date '+%Y-%m-%d %H:%M:%S') |
| # Git: $(git --version) |
| # ----------------------------------------------------------------------- |
| # WORKSPACE: $(realpath "${WORKSPACE}") |
| # WORKSPACE_BRANCH: $(git rev-parse --abbrev-ref HEAD) |
| # COMPARISON_BRANCH: ${COMPARISON_BRANCH} |
| # ----------------------------------------------------------------------- |
| BANNER |
| |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Augment a version string for display. If the given string |
| ## is empty rewrite to make that detail painfully obvious. |
| ## ----------------------------------------------------------------------- |
| function version_for_display() |
| { |
| local -n ref=$1; shift |
| |
| if [[ ${#ref} -eq 0 ]]; then |
| ref='?????????????????' # higlight the problem for reporting |
| fi |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Use git commands to retrieve chart version embedded within a file |
| ## ----------------------------------------------------------------------- |
| ## Given: |
| ## ref Indirect var for version string return. |
| ## path Repository path to a Chart.yaml file |
| ## branch git branch to retrieve |
| ## |
| ## Return: |
| ## ref Extracted version string |
| ## ----------------------------------------------------------------------- |
| function get_version_by_git() |
| { |
| local -n ref=$1 ; shift |
| local path="$1" ; shift |
| local branch="$1" ; shift |
| |
| ref='' |
| |
| # ----------------------------------------------------------------------- |
| # cat "{branch}:{chart}" to stdout: (wanted => 'version : x.y.z') |
| # ----------------------------------------------------------------------- |
| readarray -t buffer < <(\ |
| git show "${branch}:${path}" \ |
| | grep '^[[:blank:]]*version[[:blank:]]*:'\ |
| ) |
| |
| # ----------------------------------------------------------------------- |
| # Extract version string |
| # ----------------------------------------------------------------------- |
| if [[ ${#buffer[@]} -gt 1 ]]; then |
| cat <<EOM |
| |
| ** ----------------------------------------------------------------------- |
| ** IAM: ${BASH_SOURCE[0]} (LINENO: ${LINENO}) |
| ** CHART: $path |
| ** WARNING: Unable to identify chart version, multiple strings detected. |
| ** : Consider updating the lint tool to be aware of Chart.yaml |
| ** : record syntax so versions can be extracted for comparison. |
| ** ----------------------------------------------------------------------- |
| EOM |
| ## c_v_c.sh needs to be aware of Chart.yaml record syntax |
| ## so a version can be targeted. |
| |
| # shellcheck disable=SC1003 |
| declare -p buffer \ |
| | tr '"' '\n' \ |
| | grep '\.' \ |
| | sed -e 's/^[[:blank:]]*//' \ |
| | awk -F'\\' '{print " "$1}' \ |
| | sort -nr |
| echo |
| |
| ref='' # Highly forgiving (display only) |
| elif [[ ${#buffer[@]} -ne 1 ]]; then |
| ref='' # Highly forgiving (display only) |
| else |
| local raw="${buffer[0]}" |
| # Extract value, split on whitespace, colon, comment or quote |
| readarray -t fields < <(awk -F '[[:blank:]:#"]+' '{print $2}' <<<"$raw") |
| ref="${fields[0]}" |
| fi |
| |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Display a log summary entry then exit with status. |
| ## ----------------------------------------------------------------------- |
| function summary_status_with_exit() |
| { |
| local exit_code=$1; shift |
| local program="${0##*/}" |
| local summary='' |
| [[ $# -gt 0 ]] && { summary=" - $1"; shift; } |
| |
| ## ----------------------------------- |
| ## status override courtesy of error() |
| ## ----------------------------------- |
| declare -i -g force_fail |
| if [ $force_fail -eq 1 ]; then exit_code=$force_fail; fi |
| |
| ## -------------------------- |
| ## Set summary display string |
| ## -------------------------- |
| case "$exit_code" in |
| 0) status='PASS' ;; |
| *) status='FAIL' ;; |
| esac |
| |
| [[ $# -gt 0 ]] && formatStream "$@" |
| |
| echo |
| echo "[$status] ${program} ${summary}" |
| |
| exit "$exit_code" |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Display an error and configure report summary_status as FAIL |
| ## ----------------------------------------------------------------------- |
| function error() |
| { |
| declare -i -g force_fail=1 |
| declare -a -g error_stream |
| |
| # shellcheck disable=SC2124 |
| local error="[ERROR] $@" |
| error_stream+=("$error") |
| echo "$error" |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Given a list (header and data) format contents as a log entry |
| ## ----------------------------------------------------------------------- |
| ## Usage: displayList --banner "List of something" "${data_stream[@]}" |
| ## ----------------------------------------------------------------------- |
| function displayList() |
| { |
| local indent='' |
| while [ $# -gt 0 ]; do |
| local arg="$1" |
| case "$arg" in |
| -*banner) shift; echo "$1" ;; |
| -*indent) shift; indent="$1" ;; |
| -*tab-2) echo -n ' ' ;; |
| -*tab-4) echo -n ' ' ;; |
| -*tab-6) echo -n ' ' ;; |
| -*tab-8) echo -n ' ' ;; |
| -*newline) echo; ;; |
| -*tab) echo -n ' ' ;; |
| *) break ;; |
| esac |
| shift |
| done |
| |
| for line in "$@"; |
| do |
| echo -e " ${indent}$line" |
| done |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Program init, provide defaults for globals, etc. |
| ## ----------------------------------------------------------------------- |
| function init() |
| { |
| # when not running under Jenkins, use current dir as workspace |
| # o [TODO] - Perform a secondary username check to guard against a |
| # jenkins/bash env hiccup leaving WORKSPACE= temporarily undefined. |
| WORKSPACE=${WORKSPACE:-.} |
| COMPARISON_BRANCH="${COMPARISON_BRANCH:-opencord/master}" |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Remove quotes, whitespace & garbage from a string |
| ## ----------------------------------------------------------------------- |
| ## ..seealso: https://www.regular-expressions.info/posixbrackets.html |
| ## ----------------------------------------------------------------------- |
| function filter_codes() |
| { |
| declare -n val="$1"; shift # indirect |
| val="${val//[\"\'[:blank:][:cntrl:]]}" # [:punct:] contains hyphen |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Detect and report VERSION file changes |
| ## ----------------------------------------------------------------------- |
| ## :param path: Path to a Chart.yaml file. |
| ## :type path: str |
| ## |
| ## :param branch: Comparsion baranch (?-remote-?) |
| ## :type branch: str |
| ## |
| ## :return: true if embedded version string was modified. |
| ## :rtype: int |
| ## ----------------------------------------------------------------------- |
| ## ..note: What about change detection for: apiVersion, appVersion (?) |
| ## ----------------------------------------------------------------------- |
| function version_diff() |
| { |
| local path="$1"; shift |
| local branch="$1"; shift |
| |
| declare -n old_var="$1"; shift # indirect |
| declare -n new_var="$1"; shift # indirect |
| |
| old_var='' |
| new_var='' |
| readarray -t delta < <(git diff -p "$branch" -- "$path") |
| |
| local modified=0 |
| if [ ${#delta[@]} -gt 0 ]; then # modified |
| |
| #---------------------------------------- |
| # diff --git a/voltha-adapter-openolt/Chart.yaml [...] |
| # --- a/voltha-adapter-openolt/Chart.yaml |
| # +++ b/voltha-adapter-openolt/Chart.yaml |
| # @@ -14,7 +14,7 @@ |
| # --- |
| # -version: "2.11.3" (====> 2.11.3) |
| # +version: "2.11.8" (====> 2.11.8) |
| #---------------------------------------- |
| local line |
| for line in "${delta[@]}"; |
| do |
| # [TODO] Replace awk with string builtins to reduce shell overhead. |
| case "$line" in |
| -version:*) |
| modified=1 |
| readarray -t tmp < <(awk '/^\-version:/ { print $2 }' <<<"$line") |
| # shellcheck disable=SC2034 |
| old_var="${tmp[0]}" |
| filter_codes 'old_var' |
| ;; |
| +version:*) |
| readarray -t tmp < <(awk '/^\+version:/ { print $2 }' <<<"$line") |
| # shellcheck disable=SC2034 |
| new_var="${tmp[0]}" |
| filter_codes 'new_var' |
| ;; |
| esac |
| done |
| fi |
| |
| [ $modified -ne 0 ] # set $? for if/then/elif use: "if version_diff; then" |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Gather a list of Chart.yaml files from the workspace. |
| ## ----------------------------------------------------------------------- |
| function gather_charts() |
| { |
| declare -n varname="$1"; shift |
| readarray -t varname < <(find . -name 'Chart.yaml' -print) |
| filter_files varname |
| |
| local idx |
| for (( idx=0; idx<${#varname[@]}; idx++ )); |
| do |
| local val="${varname[idx]}" |
| if [ ${#val} -lt 2 ]; then continue; fi |
| |
| # Normalize: remove './' to support path comparison |
| if [[ "${val:0:2}" == './' ]]; then |
| varname[idx]="${val:2:${#val}}" |
| [[ -v debug ]] && echo "[$idx] ${varname[$idx]}" |
| fi |
| done |
| |
| [[ -v debug ]] && declare -p varname |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Given a chart file and complete list of modified files |
| ## display a list of files modified in the chart directory. |
| ## ----------------------------------------------------------------------- |
| ## :param chart: path to a Chart.yaml file |
| ## :type chart: str |
| ## |
| ## :param varname: A list files modified in a git sandbox. |
| ## :type varname: list, indirect |
| ## ----------------------------------------------------------------------- |
| function report_modified() |
| { |
| local chart="$1"; shift |
| local dir="${chart%/*}" |
| # shellcheck disable=SC2178 |
| declare -n varname="$1"; shift |
| |
| ## --------------------------------------------- |
| ## Gather files contained in the chart directory |
| ## --------------------------------------------- |
| declare -a found=() |
| for val in "${varname[@]}"; |
| do |
| case "$val" in |
| *'/Chart.yaml') ;; # special case |
| "${dir}/"*) found+=("$val") ;; |
| esac |
| done |
| |
| ## -------------------------------------------- |
| ## Display results when modified files detected |
| ## -------------------------------------------- |
| if [ ${#found[@]} -gt 0 ]; then |
| declare -a stream=() |
| stream+=('--tab-4') |
| stream+=('--banner' "Files Changed: $dir") |
| stream+=('--tab-6') # really --tab-8 |
| stream+=("${found[@]}") |
| displayList "${stream[@]}" |
| fi |
| |
| [ ${#found[@]} -gt 0 ] # set $? for if/else |
| return |
| } |
| |
| ## ----------------------------------------------------------------------- |
| ## Intent: Gather a list of Chart.yaml files from the workspace. |
| ## ----------------------------------------------------------------------- |
| declare -a -g charts=() |
| declare -a -g changes_remote=() |
| declare -a -g untracked_files=() |
| function gather_state() |
| { |
| local branch="$1"; shift |
| |
| gather_charts 'charts' |
| [[ -v debug ]] && declare -p charts |
| |
| readarray -t changes_remote < <(git diff --name-only "$branch") |
| filter_files changes_remote |
| [[ -v debug ]] && declare -p changes_remote |
| |
| readarray -t untracked_files < <(git ls-files -o --exclude-standard) |
| filter_files untracked_files |
| [[ -v debug ]] && declare -p untracked_files |
| |
| return |
| } |
| |
| # shellcheck disable=SC1073 |
| ##----------------## |
| ##---] MAIN [---## |
| ##----------------## |
| init |
| # shellcheck disable=SC2119 |
| banner |
| gather_state "$COMPARISON_BRANCH" |
| |
| chart='' |
| for chart in "${charts[@]}"; |
| do |
| [[ -v debug ]] && echo -e "\nCHART: $chart" |
| |
| ## --------------------------- |
| ## Detect VERSION file changes |
| ## --------------------------- |
| declare -i chart_modified=0 |
| old='' |
| new='' |
| if version_diff "$chart" "$COMPARISON_BRANCH" 'old' 'new'; then |
| version_for_display new |
| suffix="($old => $new)" # display verion deltas in the right margin |
| printf '[CHART] %-60s %s\n' "$chart" "$suffix" |
| chart_modified=1 |
| else |
| if [[ ${#old} -eq 0 ]]; then |
| get_version_by_git old "$chart" "$COMPARISON_BRANCH" |
| fi |
| version_for_display old |
| suffix="($old)" |
| printf '[CHART] %s\n' "$chart" |
| new="$old" |
| fi |
| |
| ## ----------------------------------- |
| ## Report modified files for the chart |
| ## ----------------------------------- |
| declare -a combo_list=() |
| combo_list+=("${changes_remote[@]}") |
| combo_list+=("${untracked_files[@]}") |
| if report_modified "$chart" 'combo_list'; |
| then |
| if [ $chart_modified -eq 0 ]; then |
| version_for_display new |
| suffix="($old)" |
| error "Chart modified but version unchanged: ${chart} ${suffix}" |
| fi |
| fi |
| |
| done |
| |
| ## --------------------------- |
| ## Report summary: local edits |
| ## --------------------------- |
| if [[ -x changes_remote ]] && [ ${#changes_remote} -gt 0 ]; then # local_edits |
| displayList \ |
| '--newline' \ |
| '--banner' 'Changed Files:' \ |
| "${changes_remote[@]}" |
| fi |
| |
| ## ------------------------------- |
| ## Report summary: untracked files |
| ## ------------------------------- |
| if [ ${#untracked_files[@]} -gt 0 ]; then |
| displayList \ |
| '--newline' \ |
| '--banner' 'Untracked Files:' \ |
| "${untracked_files[@]}" |
| fi |
| |
| ## --------------------------------- |
| ## Report summary: problems detected |
| ## --------------------------------- |
| declare -a -g error_stream |
| if [ ${#error_stream[@]} -gt 0 ]; then |
| displayList \ |
| '--newline' \ |
| '--banner' 'Error Sumamry:' \ |
| "${error_stream[@]}" |
| fi |
| |
| summary_status_with_exit $? |
| |
| # ----------------------------------------------------------------------- |
| # Running: chart_version_check.sh |
| # ----------------------------------------------------------------------- |
| # Now: 2022-12-09 03:10:52 |
| # Git: git version 2.34.1 |
| # ----------------------------------------------------------------------- |
| # COMPARISON_BRANCH: origin/master |
| # WORKSPACE: . |
| # ----------------------------------------------------------------------- |
| # [CHART] voltha-adapter-openolt/Chart.yaml (2.11.3 => 2.11.8) |
| # [CHART] voltha-tracing/Chart.yaml |
| # [CHART] voltha/Chart.yaml |
| # Files Changed: voltha-tracing |
| # voltha-tracing/values.yaml |
| # [ERROR] Chart modified but version unchanged: voltha-tracing |
| # [CHART] voltha/Chart.yaml |
| # ----------------------------------------------------------------------- |
| |
| # [SEE ALSO |
| # --------------------------------------------------------------------------- |
| # https://www.regular-expressions.info/posixbrackets.html (character classes) |
| # --------------------------------------------------------------------------- |