1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
|
#!/usr/bin/env bash
# shellcheck disable=SC2154 # (variables are set in build-utils.sh)
output_log() {
echo "$1" >&2
builder_echo "$1"
}
output_ok() {
echo ":heavy_check_mark: $1" >&2
builder_echo green "OK: $1"
}
output_warning() {
echo ":warning: $1" >&2
builder_echo warning "WARNING: $1"
}
output_error() {
echo ":x: $1" >&2
builder_echo error "ERROR: $1"
}
check_api_not_changed() {
if [[ -z "${BIN_PKG:-}" ]]; then
output_warning "Skipping check for API change because binary Debian package not specified"
return
fi
# Checks that the API did not change compared to what's documented in the .symbols file
tmpDir=$(mktemp -d)
# shellcheck disable=SC2064
trap "rm -rf \"${tmpDir}\"" ERR
dpkg -x "${BIN_PKG}" "${tmpDir}"
mkdir -p debian/tmp/DEBIAN
if dpkg-gensymbols -v"${VERSION}" -p"${PKG_NAME}" -e"${tmpDir}"/usr/lib/x86_64-linux-gnu/"${LIB_NAME}".so* -c4; then
output_ok "${LIB_NAME} API didn't change"
else
output_error "${LIB_NAME} API changed"
EXIT_CODE=4
fi
cd "${REPO_ROOT}/linux"
rm -rf "${tmpDir}"
trap ERR
}
#
# Compare the SHA of the base and head commits for changes to the .symbols file
#
is_symbols_file_changed() {
local CHANGED_REF CHANGED_BASE
CHANGED_REF=$(git rev-parse "${GIT_SHA}":"linux/debian/${PKG_NAME}.symbols")
CHANGED_BASE=$(git rev-parse "${GIT_BASE}":"linux/debian/${PKG_NAME}.symbols")
if [[ "${CHANGED_REF}" == "${CHANGED_BASE}" ]]; then
return 1
fi
return 0
}
get_changes() {
local WHAT_CHANGED
WHAT_CHANGED=$(git diff -I "^${LIB_NAME}.so" "$1".."$2" | diffstat -m -t | grep "${PKG_NAME}.symbols" )
IFS=',' read -r -a CHANGES <<< "${WHAT_CHANGED:-0,0,0}"
}
check_updated_version_number() {
# Checks that the package version number got updated in the .symbols file if it got changed
# shellcheck disable=SC2310
if is_symbols_file_changed; then
# .symbols file changed, now check if the package version got updated as well
# Note: We don't check that ALL changes in that file have an updated package version -
# we hope this gets flagged in code review.
# Note: This version number check may not match the actual released version, if the branch
# is out of date when it is merged to the release branch (master/beta/stable-x.y). If this
# is considered important, then make sure the branch is up to date, and wait for test
# builds to complete, before merging.
get_changes "${GIT_BASE}" "${GIT_SHA}"
INSERTED="${CHANGES[0]}"
DELETED="${CHANGES[1]}"
MODIFIED="${CHANGES[2]}"
if (( DELETED > 0 )) && (( MODIFIED == 0 )) && (( INSERTED == 0)); then
# If only lines got removed we basically skip this test. A later check will
# test that the API version got updated.
output_ok "${PKG_NAME}.symbols file did change but only removed lines"
else
local version_base version_head
version_head=$(get_highest_version_in_symbols_file "${GIT_SHA}")
version_base=$(get_highest_version_in_symbols_file "${GIT_BASE}")
if (( $(compare_versions "${version_head}" "${version_base}") > 0 )); then
output_ok "${PKG_NAME}.symbols file got updated with package version number"
else
output_error "${PKG_NAME}.symbols file got changed without changing the package version number of the symbol"
EXIT_CODE=1
fi
fi
else
output_ok "${PKG_NAME}.symbols file didn't change"
fi
}
compare_versions() {
local first_parts second_parts
IFS='.' read -r -a first_parts <<< "$1"
IFS='.' read -r -a second_parts <<< "$2"
if (( first_parts[0] < second_parts[0] )); then
echo -1
elif (( first_parts[0] > second_parts[0] )); then
echo 1
elif (( first_parts[1] < second_parts[1] )); then
echo -1
elif (( first_parts[1] > second_parts[1] )); then
echo 1
elif (( first_parts[2] < second_parts[2] )); then
echo -1
elif (( first_parts[2] > second_parts[2] )); then
echo 1
else
echo 0
fi
}
get_highest_version_in_symbols_file() {
local sha="$1"
local symbol_lines line line_version
local max_version=0
tmpfile=$(mktemp)
if ! git cat-file blob "${sha}:linux/debian/${PKG_NAME}.symbols" > "${tmpfile}" 2>/dev/null; then
rm "${tmpfile}"
return 1
fi
# Start with fourth line which is where symbols start
mapfile -s 3 symbol_lines < "${tmpfile}"
for line in "${symbol_lines[@]}"; do
# km_core_actions_dispose@Base 17.0.197
line_version=${line##* }
if (( $(compare_versions "${line_version}" "${max_version}") > 0 )); then
max_version="${line_version}"
fi
done
echo "${max_version}"
rm "${tmpfile}"
return 0
}
get_api_version_in_symbols_file() {
# Retrieve symbols file at commit $1 and extract "1" from
# "libkeymancore.so.1 libkeymancore1 #MINVER#"
local firstline tmpfile
local sha="$1"
tmpfile=$(mktemp)
if ! git cat-file blob "${sha}:linux/debian/${PKG_NAME}.symbols" > "${tmpfile}" 2>/dev/null; then
rm "${tmpfile}"
echo "-1"
return
fi
firstline="$(head -1 "${tmpfile}")"
firstline="${firstline#"${LIB_NAME}".so.}"
firstline="${firstline%% *}"
rm "${tmpfile}"
echo "${firstline}"
}
get_api_version_from_core() {
# Retrieve CORE_API_VERSION.md from commit $1 and extract major version
# number ("1") from "1.0.0"
local api_version tmpfile
local sha="$1"
tmpfile=$(mktemp)
if ! git cat-file blob "${sha}:core/CORE_API_VERSION.md" > "${tmpfile}" 2>/dev/null; then
rm "${tmpfile}"
echo "-1"
return
fi
api_version=$(cat "${tmpfile}")
api_version=${api_version%%.*}
rm "${tmpfile}"
echo "${api_version}"
}
# Check if the API version got updated
# Returns:
# 0 - if the API version got updated
# 1 - the .symbols file got changed but the API version didn't get updated
# 2 - if we're in the alpha tier and the API version got updated since
# the last stable version
# NOTE: it is up to the caller to check if this is a major version
# change that requires an API version update.
# Check if the API version got updated
# Returns:
# 0 - if the API version got updated
# 1 - the .symbols file got changed but the API version didn't get updated
# 2 - if we're in the alpha tier and the API version got updated since
# the last stable version
# NOTE: it is up to the caller to check if this is a major version
# change that requires an API version update.
is_api_version_updated() {
local OLD_API_VERSION NEW_API_VERSION TIER
OLD_API_VERSION=$(get_api_version_in_symbols_file "${GIT_BASE}")
NEW_API_VERSION=$(get_api_version_in_symbols_file "${GIT_SHA}")
if (( NEW_API_VERSION > OLD_API_VERSION )); then
echo "0"
return
fi
# API version didn't change. However, that might be ok if we're in alpha
# and a major change happened previously.
TIER=$(cat "${REPO_ROOT}/TIER.md")
case ${TIER} in
alpha)
local STABLE_VERSION STABLE_API_VERSION STABLE_BRANCH
STABLE_VERSION=$((${VERSION%%.*} - 1))
STABLE_BRANCH="origin/stable-${STABLE_VERSION}.0"
STABLE_API_VERSION=$(get_api_version_in_symbols_file "${STABLE_BRANCH}")
if (( STABLE_API_VERSION == -1 )); then
# .symbols file doesn't exist in stable branch, so let's check CORE_API_VERSION.md. That
# doesn't exist in 16.0 but appeared in 17.0.
STABLE_API_VERSION=$(get_api_version_from_core "${STABLE_BRANCH}")
if (( STABLE_API_VERSION == -1 )); then
# CORE_API_VERSION.md doesn't exist either
if (( NEW_API_VERSION > 0 )); then
# .symbols and CORE_API_VERSION.md file don't exist in stable branch; however, we
# incremented the version number compared to 16.0, so that's ok
echo "2"
return
fi
fi
fi
if (( NEW_API_VERSION > STABLE_API_VERSION )); then
echo "2"
return
fi ;;
*)
;;
esac
echo "1"
}
check_for_major_api_changes() {
# Checks that API version number gets updated if API changes
local WHAT_CHANGED CHANGES INSERTED DELETED MODIFIED UPDATED
# shellcheck disable=2310
if ! is_symbols_file_changed; then
output_ok "No major API change"
return
fi
get_changes "${GIT_BASE}" "${GIT_SHA}"
INSERTED="${CHANGES[0]}"
DELETED="${CHANGES[1]}"
MODIFIED="${CHANGES[2]}"
if (( DELETED > 0 )) || (( MODIFIED > 0 )); then
builder_echo "Major API change: ${DELETED} lines deleted and ${MODIFIED} lines modified"
UPDATED=$(is_api_version_updated)
if [[ ${UPDATED} == 1 ]]; then
output_error "Major API change without updating API version number in ${PKG_NAME}.symbols file"
EXIT_CODE=2
elif [[ ${UPDATED} == 2 ]]; then
output_ok "API version number got previously updated in ${PKG_NAME}.symbols file after major API change; no change within alpha necessary"
else
output_ok "API version number got updated in ${PKG_NAME}.symbols file after major API change"
fi
elif (( INSERTED > 0 )); then
output_ok "Minor API change: ${INSERTED} lines added"
# We currently don't check version number for minor API changes
else
output_ok "No major API change"
fi
}
check_for_api_version_consistency() {
# Checks that the (major) API version number in the .symbols file and
# in CORE_API_VERSION.md are the same
local symbols_version api_version
symbols_version=$(get_api_version_in_symbols_file "HEAD")
api_version=$(get_api_version_from_core "HEAD")
if (( symbols_version == api_version )); then
output_ok "API version in .symbols file and in CORE_API_VERSION.md is the same"
else
output_error "API version in .symbols file and in CORE_API_VERSION.md is different"
EXIT_CODE=3
fi
}
verify_api_action() {
local SONAME
SONAME=$(get_api_version_from_core "HEAD")
LIB_NAME=libkeymancore
PKG_NAME="${LIB_NAME}${SONAME}"
if [[ ! -f debian/${PKG_NAME}.symbols ]]; then
output_error "Missing ${PKG_NAME}.symbols file"
exit 0
fi
EXIT_CODE=0
check_api_not_changed
check_updated_version_number
check_for_major_api_changes
check_for_api_version_consistency
exit "${EXIT_CODE}"
}
|