This fixes build failure due to incompatible Python runtime. Signed-off-by: 林博仁(Buo-ren Lin) <buo.ren.lin@gmail.com>
1867 lines
65 KiB
Bash
Executable File
1867 lines
65 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# This scriptlet enhances the pull step that will only build
|
|
# development snapshots snaps if the latest tagged release has been
|
|
# promoted to the stable channel. This ensures that there's always
|
|
# a revision of the stable release snap available in the edge channel
|
|
# for the publisher to promote to stable as currently the build
|
|
# infrastructure only supports build on code push (but not new tagged
|
|
# releases) at this time.
|
|
# https://forum.snapcraft.io/t/selective-checkout-check-out-the-tagged-release-revision-if-it-isnt-promoted-to-the-stable-channel/10617
|
|
#
|
|
# Copyright 2025 林博仁(Buo-ren Lin) <buo.ren.lin@gmail.com>
|
|
# SPDX-License-Identifier: CC-BY-SA-4.0
|
|
|
|
SELECTIVE_CHECKOUT_DEBUG="${SELECTIVE_CHECKOUT_DEBUG:-false}"
|
|
|
|
set \
|
|
-o errexit \
|
|
-o errtrace \
|
|
-o nounset \
|
|
-o pipefail
|
|
|
|
for required_command in \
|
|
curl \
|
|
cut \
|
|
head \
|
|
jq \
|
|
realpath \
|
|
sed \
|
|
sort \
|
|
tail \
|
|
tr; do
|
|
if ! command -v "${required_command}" >/dev/null; then
|
|
printf -- \
|
|
'Fatal: This script requires the "%s" command in your command search PATHs.\n' \
|
|
"${required_command}" \
|
|
>&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
init(){
|
|
script="$(
|
|
realpath \
|
|
--strip \
|
|
"${BASH_SOURCE[0]}"
|
|
)"
|
|
script_filename="${script##*/}"
|
|
script_name="${script_filename%%.*}"
|
|
export SCRIPT_NAME="${script_name}"
|
|
|
|
# checkout_mode:
|
|
# - snapshot: Build as-is
|
|
# - release: Build the latest tagged release
|
|
# tag_pattern_release: We assume all tags contains dots or underscores release tags
|
|
#
|
|
# Indirection used
|
|
# shellcheck disable=SC2034
|
|
local \
|
|
checkout_mode \
|
|
flag_append_packaging_version=false \
|
|
flag_dry_run=false \
|
|
flag_debug_tracing=false \
|
|
flag_force_snapshot=false \
|
|
flag_force_stable=false \
|
|
packaging_revision \
|
|
postfix_dirty_marker_packaging=-d \
|
|
postfix_dirty_marker_upstream=-dirty \
|
|
tag_pattern_beta='-beta[[:digit:]]+$' \
|
|
tag_pattern_release='.*[._].*' \
|
|
tag_pattern_release_candidate='-rc[[:digit:]]+$' \
|
|
tag_pattern_stable \
|
|
tag_prefix_release=v \
|
|
revision_minimal_length_packaging=4 \
|
|
revision_minimal_length_upstream=7 \
|
|
snap_version \
|
|
snap_version_postfix_seperator=+ \
|
|
upstream_version
|
|
|
|
if ! determining_runtime_parameters \
|
|
flag_append_packaging_version \
|
|
tag_pattern_beta \
|
|
flag_debug_tracing \
|
|
flag_dry_run \
|
|
flag_force_snapshot \
|
|
flag_force_stable \
|
|
postfix_dirty_marker_packaging \
|
|
revision_minimal_length_packaging \
|
|
tag_pattern_release_candidate \
|
|
tag_pattern_release \
|
|
tag_prefix_release \
|
|
snap_version_postfix_seperator \
|
|
tag_pattern_stable \
|
|
postfix_dirty_marker_upstream \
|
|
revision_minimal_length_upstream \
|
|
"${@}"; then
|
|
printf \
|
|
'Error: Error(s) occurred while determining the runtime parameters.\n' \
|
|
1>&2
|
|
exit 1
|
|
fi
|
|
|
|
if test "${flag_debug_tracing}" = true; then
|
|
set -o xtrace
|
|
fi
|
|
|
|
vcs_check_runtime_dependencies \
|
|
"${PWD}"
|
|
|
|
if test "${flag_force_snapshot}" = true; then
|
|
printf -- \
|
|
'%s: Info: Force building development snapshots\n' \
|
|
"${script_name}"
|
|
checkout_mode=snapshot
|
|
elif test "$(vcs_detect "${PWD}")" = not_found; then
|
|
printf -- \
|
|
'%s: Info: Build from source archive\n' \
|
|
"${script_name}"
|
|
checkout_mode=snapshot
|
|
elif vcs_is_dirty \
|
|
"${PWD}"; then
|
|
# If tracked files are modified
|
|
# or staging area not empty
|
|
printf -- \
|
|
'%s: Info: Working tree is dirty, building development snapshot with additional changes\n' \
|
|
"${script_name}"
|
|
checkout_mode=snapshot
|
|
elif ! \
|
|
vcs_has_release_tags \
|
|
"${PWD}" \
|
|
"${tag_pattern_release}"; then
|
|
printf -- \
|
|
'%s: Warning: No release tags found, assuming building from development snapshots.\n' \
|
|
"${script_name}" \
|
|
1>&2
|
|
checkout_mode=snapshot
|
|
else
|
|
printf -- \
|
|
'%s: Info: Determining version to be built...\n' \
|
|
"${script_name}"
|
|
local \
|
|
release_type_to_build=development-snapshot \
|
|
last_stable_tag \
|
|
last_stable_version \
|
|
last_stable_version_on_the_snap_store \
|
|
last_release_candidate_tag \
|
|
last_release_candidate_version \
|
|
last_release_candidate_version_on_the_snap_store \
|
|
last_beta_tag \
|
|
last_beta_version \
|
|
last_beta_version_on_the_snap_store
|
|
|
|
local -a all_release_tags=()
|
|
local -A \
|
|
map_of_tag_to_normalized_version \
|
|
map_of_release_type_to_snap_channel
|
|
|
|
map_of_release_type_to_snap_channel=(
|
|
[stable]=stable
|
|
[release-candidate]=candidate
|
|
[beta]=beta
|
|
[development-snapshot]=edge
|
|
)
|
|
|
|
if ! {
|
|
test -v CRAFT_PROJECT_NAME \
|
|
|| test -v SNAPCRAFT_PROJECT_NAME
|
|
}; then
|
|
printf -- \
|
|
"%s: Error: This script requires either the CRAFT_PROJECT_NAME or the SNAPCRAFT_PROJECT_NAME environment variable to be set.\\n" \
|
|
"${script_name}" \
|
|
>&2
|
|
exit 1
|
|
fi
|
|
local project_name="${CRAFT_PROJECT_NAME:-"${SNAPCRAFT_PROJECT_NAME}"}"
|
|
|
|
mapfile \
|
|
-t all_release_tags \
|
|
< <(
|
|
vcs_query_release_tags \
|
|
"${PWD}" \
|
|
"${tag_pattern_release}"
|
|
)
|
|
|
|
if ! {
|
|
test -v CRAFT_PROJECT_DIR \
|
|
|| test -v SNAPCRAFT_PROJECT_DIR
|
|
}; then
|
|
printf -- \
|
|
"%s: Error: This script requires either the CRAFT_PROJECT_DIR or the SNAPCRAFT_PROJECT_DIR environment variable to be set.\\n" \
|
|
"${script_name}" \
|
|
>&2
|
|
exit 1
|
|
fi
|
|
project_dir="${CRAFT_PROJECT_DIR:-"${SNAPCRAFT_PROJECT_DIR}"}"
|
|
|
|
printf -- \
|
|
'%s: INFO: Determining normalized release version strings.\n' \
|
|
"${script_name}"
|
|
determine_normalized_release_version_string \
|
|
map_of_tag_to_normalized_version \
|
|
"${all_release_tags[@]}"
|
|
|
|
printf -- \
|
|
'%s: INFO: Detecting stable releases...\n' \
|
|
"${script_name}"
|
|
determine_stable_release_details \
|
|
tag_pattern_stable \
|
|
"${tag_pattern_release_candidate}" \
|
|
"${tag_pattern_beta}" \
|
|
last_stable_tag \
|
|
last_stable_version \
|
|
last_stable_version_on_the_snap_store \
|
|
"${snap_version_postfix_seperator}" \
|
|
"${flag_force_stable}" \
|
|
"${all_release_tags[@]}"
|
|
|
|
printf -- \
|
|
'%s: INFO: Detecting release candidate releases...\n' \
|
|
"${script_name}"
|
|
determine_release_candidate_release_details \
|
|
"${tag_pattern_release_candidate}" \
|
|
last_release_candidate_tag \
|
|
last_release_candidate_version \
|
|
last_release_candidate_version_on_the_snap_store \
|
|
"${snap_version_postfix_seperator}" \
|
|
"${all_release_tags[@]}"
|
|
|
|
printf -- \
|
|
'%s: INFO: Detecting beta releases...\n' \
|
|
"${script_name}"
|
|
determine_beta_release_details \
|
|
"${tag_pattern_beta}" \
|
|
last_beta_tag \
|
|
last_beta_version \
|
|
last_beta_version_on_the_snap_store \
|
|
"${snap_version_postfix_seperator}" \
|
|
"${all_release_tags[@]}"
|
|
|
|
local \
|
|
selected_release_tag \
|
|
selected_release_version \
|
|
selected_snap_channel_version
|
|
release_type_to_build="$(
|
|
determine_which_version_to_build \
|
|
"${last_stable_tag}" \
|
|
"${last_stable_version}" \
|
|
"${last_stable_version_on_the_snap_store}" \
|
|
"${last_release_candidate_tag}" \
|
|
"${last_release_candidate_version}" \
|
|
"${last_release_candidate_version_on_the_snap_store}" \
|
|
"${last_beta_tag}" \
|
|
"${last_beta_version}" \
|
|
"${last_beta_version_on_the_snap_store}" \
|
|
"${flag_force_stable}" \
|
|
"${flag_force_snapshot}"
|
|
)"
|
|
|
|
|
|
case "${release_type_to_build}" in
|
|
stable)
|
|
selected_release_tag="${last_stable_tag}"
|
|
selected_release_version="${last_stable_version}"
|
|
selected_snap_channel_version="${last_stable_version_on_the_snap_store}"
|
|
;;
|
|
release-candidate)
|
|
selected_release_tag="${last_release_candidate_tag}"
|
|
selected_release_version="${last_release_candidate_version}"
|
|
selected_snap_channel_version="${last_release_candidate_version_on_the_snap_store}"
|
|
;;
|
|
beta)
|
|
selected_release_tag="${last_beta_tag}"
|
|
selected_release_version="${last_beta_version}"
|
|
selected_snap_channel_version="${last_beta_version_on_the_snap_store}"
|
|
;;
|
|
development-snapshot)
|
|
: # Nothing to do here
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: %s: FATAL: Invalid release_type_to_build(%s), report bug.\n' \
|
|
"${script_name}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${release_type_to_build}" \
|
|
1>&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
unset \
|
|
flag_version_mismatch_stable \
|
|
flag_version_mismatch_beta \
|
|
flag_version_mismatch_release_candidate \
|
|
flag_force_stable \
|
|
last_stable_tag \
|
|
last_stable_version \
|
|
last_stable_version_on_the_snap_store \
|
|
last_release_candidate_tag \
|
|
last_release_candidate_version \
|
|
last_release_candidate_version_on_the_snap_store \
|
|
last_beta_tag \
|
|
last_beta_version \
|
|
last_beta_version_on_the_snap_store
|
|
|
|
if test "${release_type_to_build}" != development-snapshot; then
|
|
printf -- \
|
|
"%s: Info: The last tagged %s release(%s) hasn't been promoted to the %s channel(%s) on the Snap Store yet, checking out %s.\\n" \
|
|
"${script_name}" \
|
|
"${release_type_to_build}" \
|
|
"${selected_release_version}" \
|
|
"${map_of_release_type_to_snap_channel["${release_type_to_build}"]}" \
|
|
"${selected_snap_channel_version}" \
|
|
"${selected_release_version}"
|
|
checkout_mode=release
|
|
else
|
|
printf -- '%s: Info: Last tagged releases is all in their respective channels, building development snapshot\n' \
|
|
"${script_name}"
|
|
checkout_mode=snapshot
|
|
fi
|
|
|
|
unset \
|
|
all_release_tags \
|
|
map_of_release_type_to_snap_channel \
|
|
release_type_to_build \
|
|
selected_release_version \
|
|
selected_snap_channel_version
|
|
fi
|
|
|
|
unset \
|
|
tag_pattern_release
|
|
|
|
case "${checkout_mode}" in
|
|
snapshot)
|
|
: # do nothing
|
|
;;
|
|
release)
|
|
if test "${flag_dry_run}" == true; then
|
|
printf -- \
|
|
'%s: Info: Would check out "%s" tag.\n' \
|
|
"${script_name}" \
|
|
"${selected_release_tag}"
|
|
else
|
|
vcs_checkout_tag \
|
|
"${PWD}" \
|
|
"${selected_release_tag}"
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: Error: Invalid checkout_mode selected.\n' \
|
|
"${script_name}" \
|
|
>&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
unset \
|
|
checkout_mode \
|
|
selected_release_tag
|
|
|
|
upstream_version="$(
|
|
vcs_describe_version \
|
|
"${PWD}" \
|
|
"${revision_minimal_length_upstream}" \
|
|
"${postfix_dirty_marker_upstream}" \
|
|
| normalize_version
|
|
)"
|
|
|
|
if test "${flag_append_packaging_version}" = true; then
|
|
packaging_revision="$(
|
|
vcs_describe_revision \
|
|
"${project_dir}" \
|
|
"${revision_minimal_length_packaging}" \
|
|
"${postfix_dirty_marker_packaging}"
|
|
)"
|
|
snap_version="${upstream_version}+pkg-${packaging_revision}"
|
|
unset \
|
|
project_dir \
|
|
packaging_revision \
|
|
postfix_dirty_marker_packaging \
|
|
revision_minimal_length_packaging
|
|
else
|
|
snap_version="${upstream_version}"
|
|
fi
|
|
unset \
|
|
postfix_dirty_marker_upstream \
|
|
revision_minimal_length_upstream \
|
|
tag_prefix_release \
|
|
upstream_version
|
|
|
|
printf -- '%s: Info: Snap version determined to be "%s".\n' \
|
|
"${script_name}" \
|
|
"${snap_version}"
|
|
if test "${flag_dry_run}" = false; then
|
|
if command -v craftctl >/dev/null; then
|
|
if ! craftctl set version="${snap_version}"; then
|
|
printf \
|
|
'Error: Unable to set the snap version string.\n' \
|
|
1>&2
|
|
exit 2
|
|
fi
|
|
else
|
|
if ! snapcraftctl set-version "${snap_version}"; then
|
|
printf \
|
|
'Error: Unable to set the snap version string.\n' \
|
|
1>&2
|
|
exit 2
|
|
fi
|
|
fi
|
|
fi
|
|
exit 0
|
|
}
|
|
|
|
determining_runtime_parameters(){
|
|
local -n flag_append_packaging_version_ref="${1}"; shift
|
|
local -n tag_pattern_beta_ref="${1}"; shift
|
|
local -n flag_debug_tracing_ref="${1}"; shift
|
|
local -n flag_dry_run_ref="${1}"; shift
|
|
local -n flag_force_snapshot_ref="${1}"; shift
|
|
local -n flag_force_stable_ref="${1}"; shift
|
|
local -n postfix_dirty_marker_packaging_ref="${1}"; shift
|
|
local -n revision_minimal_length_packaging_ref="${1}"; shift
|
|
local -n tag_pattern_release_candidate_ref="${1}"; shift
|
|
local -n tag_pattern_release_ref="${1}"; shift
|
|
local -n tag_prefix_release_ref="${1}"; shift
|
|
local -n snap_version_postfix_seperator_ref="${1}"; shift
|
|
local -n tag_pattern_stable_ref="${1}"; shift
|
|
local -n postfix_dirty_marker_upstream_ref="${1}"; shift
|
|
local -n revision_minimal_length_upstream_ref="${1}"; shift
|
|
|
|
while true; do
|
|
if test "${#}" -eq 0; then
|
|
break
|
|
else
|
|
case "${1}" in
|
|
# Append packaging revision after snap version
|
|
--append-packaging-revision)
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
flag_append_packaging_version_ref=true
|
|
;;
|
|
--beta-tag-pattern*)
|
|
if test "${1}" != --beta-tag-pattern; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_beta_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --beta-tag-pattern requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_beta_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
# Enable execution tracing
|
|
--debug)
|
|
printf -- \
|
|
'%s: %s: Warning: The --debug command option is deprecated, set the SELECTIVE_CHECKOUT_DEBUG environment variable to "true" for a better debugging experience.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
export SELECTIVE_CHECKOUT_DEBUG=true
|
|
;;
|
|
--debug-tracing)
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
flag_debug_tracing_ref=true
|
|
;;
|
|
# Don't run snapcraftctl for testing purpose
|
|
--dry-run)
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
flag_dry_run_ref=true
|
|
;;
|
|
# Force building development snapshot regardless the status of the snap
|
|
--force-snapshot)
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
flag_force_snapshot_ref=true
|
|
;;
|
|
--force-stable)
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
flag_force_stable_ref=true
|
|
;;
|
|
--packaging-dirty-marker-postfix*)
|
|
if test "${1}" != --packaging-dirty-marker-postfix; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
postfix_dirty_marker_packaging_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --packaging-dirty-marker-postfix requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
postfix_dirty_marker_packaging_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--packaging-revision-minimal-length*)
|
|
if test "${1}" != --packaging-revision-minimal-length; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
revision_minimal_length_packaging_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --packaging-revision-minimal-length requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
revision_minimal_length_packaging_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--release-candidate-tag-pattern*)
|
|
if test "${1}" != --release-candidate-tag-pattern; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_release_candidate_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --release-candidate-tag-pattern requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_release_candidate_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--release-tag-pattern*)
|
|
if test "${1}" != --release-tag-pattern; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_release_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --release-tag-pattern requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_release_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
# Set the prefix for all release tags(default: `v`), the prefix will be stripped from snap version string
|
|
--release-tag-prefix*)
|
|
if test "${1}" != --release-tag-prefix; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_prefix_release_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --release-tag-prefix requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_prefix_release_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
# Set the seperator for the postfixed string in the snap version string
|
|
# the postfixed string will be stripped before comparing with the stripped
|
|
# uptream release version
|
|
--snap-postfix-seperator*)
|
|
if test "${1}" != --snap-postfix-seperator; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
snap_version_postfix_seperator_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --snap-postfix-seperator requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
snap_version_postfix_seperator_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--stable-tag-pattern*)
|
|
if test "${1}" != --stable-tag-pattern; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_stable_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --stable-tag-pattern requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
tag_pattern_stable_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--upstream-dirty-marker-postfix*)
|
|
if test "${1}" != --upstream-dirty-marker-postfix; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
postfix_dirty_marker_upstream_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --upstream-dirty-marker-postfix requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
postfix_dirty_marker_upstream_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
--upstream-revision-minimal-length*)
|
|
if test "${1}" != --upstream-revision-minimal-length; then
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
revision_minimal_length_upstream_ref="$(
|
|
cut \
|
|
--delimiter== \
|
|
--fields=2 \
|
|
<<< "${1}"
|
|
)"
|
|
else
|
|
if test "${#}" -eq 0; then
|
|
printf -- \
|
|
'%s: %s: Error: --upstream-revision-minimal-length requires one argument.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
# Indirect access
|
|
# shellcheck disable=SC2034
|
|
revision_minimal_length_upstream_ref="${2}"
|
|
shift 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: %s: Error: Invalid command-line argument "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${1}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
shift 1
|
|
fi
|
|
done
|
|
}
|
|
|
|
normalize_version(){
|
|
# This is not a simple substitution
|
|
# shellcheck disable=SC2001
|
|
sed "s#^${tag_prefix_release}##" \
|
|
| tr _ .
|
|
}
|
|
|
|
determine_normalized_release_version_string(){
|
|
local -n map_of_tag_to_normalized_version_ref="${1}"; shift
|
|
local -a all_release_tags=("${@}"); set --
|
|
|
|
local normalized_release_version
|
|
for tag in "${all_release_tags[@]}"; do
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Found release tag: %s\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${tag}" \
|
|
1>&2
|
|
fi
|
|
normalized_release_version="$(
|
|
normalize_version <<< "${tag}"
|
|
)"
|
|
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Normalized release version: %s\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${normalized_release_version}" \
|
|
1>&2
|
|
fi
|
|
# Indirection
|
|
# shellcheck disable=SC2034
|
|
map_of_tag_to_normalized_version_ref["${tag}"]="${normalized_release_version}"
|
|
done
|
|
unset \
|
|
normalized_release_version \
|
|
tag
|
|
}
|
|
|
|
determine_stable_release_details(){
|
|
# Indirection
|
|
# shellcheck disable=SC2034
|
|
local -n tag_pattern_stable_ref="${1}"; shift
|
|
local tag_pattern_release_candidate="${1}"; shift
|
|
local tag_pattern_beta="${1}"; shift
|
|
local -n last_stable_tag_ref="${1}"; shift
|
|
local -n last_stable_version_ref="${1}"; shift
|
|
local -n last_stable_version_on_the_snap_store_ref="${1}"; shift
|
|
local snap_version_postfix_seperator="${1}"; shift
|
|
local flag_force_stable="${1}"; shift
|
|
local -a all_release_tags=("${@}"); set --
|
|
|
|
local stable_tag_pattern_grep_opts
|
|
if test -v tag_pattern_stable_ref; then
|
|
stable_tag_pattern_grep_opts=
|
|
else
|
|
stable_tag_pattern_grep_opts=--invert-match
|
|
tag_pattern_stable_ref="${tag_pattern_beta}|${tag_pattern_release_candidate}"
|
|
fi
|
|
|
|
local -a stable_tags=()
|
|
mapfile \
|
|
-t stable_tags \
|
|
< <(
|
|
echo -n "${all_release_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| grep \
|
|
--extended-regexp \
|
|
${stable_tag_pattern_grep_opts} \
|
|
--regexp="(${tag_pattern_stable_ref})"
|
|
)
|
|
|
|
if test "${#stable_tags[@]}" -eq 0; then
|
|
last_stable_tag_ref=
|
|
last_stable_version_ref=
|
|
|
|
# NOTE: The store CAN have releases, though...
|
|
last_stable_version_on_the_snap_store_ref=
|
|
else
|
|
last_stable_tag_ref="$(
|
|
echo -n "${stable_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| sort --version-sort \
|
|
| tail --lines=1
|
|
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Last stable release tag determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_stable_tag_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_stable_version_ref="${map_of_tag_to_normalized_version["${last_stable_tag}"]}"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Last stable version determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_stable_version_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_stable_version_on_the_snap_store_ref="$(
|
|
snap_query_version \
|
|
"${project_name}" \
|
|
stable \
|
|
"${snap_version_postfix_seperator}"
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Last stable version on the snap store determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_stable_version_on_the_snap_store_ref}" \
|
|
1>&2
|
|
fi
|
|
fi
|
|
unset \
|
|
stable_tag_pattern_grep_opts \
|
|
tag_pattern_stable
|
|
}
|
|
|
|
determine_release_candidate_release_details(){
|
|
local tag_pattern_release_candidate="${1}"; shift
|
|
local -n last_release_candidate_tag_ref="${1}"; shift
|
|
local -n last_release_candidate_version_ref="${1}"; shift
|
|
local -n last_release_candidate_version_on_the_snap_store_ref="${1}"; shift
|
|
local snap_version_postfix_seperator="${1}"; shift
|
|
local -a all_release_tags=("${@}"); set --
|
|
|
|
local -a release_candidate_tags
|
|
mapfile \
|
|
-t release_candidate_tags \
|
|
< <(
|
|
echo -n "${all_release_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_release_candidate}"
|
|
)
|
|
|
|
if test "${#release_candidate_tags[@]}" -eq 0; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: No release candidate release tags found.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
1>&2
|
|
fi
|
|
last_release_candidate_tag_ref=
|
|
last_release_candidate_version_ref=
|
|
last_release_candidate_version_on_the_snap_store_ref=
|
|
else
|
|
last_release_candidate_tag_ref="$(
|
|
echo -n "${release_candidate_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_release_candidate}" \
|
|
| sort --version-sort \
|
|
| tail --lines=1
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last release candidate release tag determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_release_candidate_tag_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_release_candidate_version_ref="${map_of_tag_to_normalized_version["${last_release_candidate_tag_ref}"]}"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last release candidate version determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_release_candidate_version_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_release_candidate_version_on_the_snap_store_ref="$(
|
|
snap_query_version \
|
|
"${project_name}" \
|
|
candidate \
|
|
"${snap_version_postfix_seperator}"
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last release candidate version on the snap store determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_release_candidate_version_on_the_snap_store_ref}" \
|
|
1>&2
|
|
fi
|
|
fi
|
|
unset \
|
|
tag_pattern_release_candidate
|
|
}
|
|
|
|
determine_beta_release_details(){
|
|
local tag_pattern_beta="${1}"; shift
|
|
local -n last_beta_tag_ref="${1}"; shift
|
|
local -n last_beta_version_ref="${1}"; shift
|
|
local -n last_beta_version_on_the_snap_store_ref="${1}"; shift
|
|
local snap_version_postfix_seperator="${1}"; shift
|
|
local all_release_tags=("${@}"); set --
|
|
|
|
local -a beta_tags
|
|
mapfile \
|
|
-t beta_tags \
|
|
< <(
|
|
echo -n "${all_release_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_beta}"
|
|
)
|
|
|
|
if test "${#beta_tags[@]}" -eq 0; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: No beta release tags found.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
1>&2
|
|
fi
|
|
last_beta_tag_ref=
|
|
last_beta_version_ref=
|
|
last_beta_version_on_the_snap_store=
|
|
else
|
|
last_beta_tag_ref="$(
|
|
echo -n "${beta_tags[@]}" \
|
|
| tr ' ' "\\n" \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_beta}" \
|
|
| sort --version-sort \
|
|
| tail --lines=1
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last beta release tag determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_beta_tag_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_beta_version_ref="${map_of_tag_to_normalized_version["${last_beta_tag_ref}"]}"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last beta version determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_beta_version_ref}" \
|
|
1>&2
|
|
fi
|
|
|
|
last_beta_version_on_the_snap_store="$(
|
|
snap_query_version \
|
|
"${project_name}" \
|
|
beta \
|
|
"${snap_version_postfix_seperator}"
|
|
)"
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: DEBUG: Last beta version on the snap store determines to be "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${last_beta_version_on_the_snap_store_ref}" \
|
|
1>&2
|
|
fi
|
|
fi
|
|
unset \
|
|
map_of_tag_to_normalized_version \
|
|
snap_version_postfix_seperator \
|
|
tag_pattern_beta
|
|
}
|
|
|
|
determine_which_version_to_build(){
|
|
local last_stable_tag="${1}"; shift
|
|
local last_stable_version="${1}"; shift
|
|
local last_stable_version_on_the_snap_store="${1}"; shift
|
|
local last_release_candidate_tag="${1}"; shift
|
|
local last_release_candidate_version="${1}"; shift
|
|
local last_release_candidate_version_on_the_snap_store="${1}"; shift
|
|
local last_beta_tag="${1}"; shift
|
|
local last_beta_version="${1}"; shift
|
|
local last_beta_version_on_the_snap_store="${1}"; shift
|
|
local flag_force_stable="${1}"; shift
|
|
local flag_force_snapshot="${1}"; shift
|
|
|
|
local release_type_to_build=undetermined
|
|
|
|
# Case: --force-snapshot is specified
|
|
if test "${flag_force_snapshot}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: The --force-snapshot command option is specified, development snapshot will be built.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
1>&2
|
|
release_type_to_build=development-snapshot
|
|
# Case: Build the stable version when:
|
|
#
|
|
# --force-stable command-line option is specified
|
|
# AND There is stable release version to build on
|
|
elif test "${flag_force_stable}" == true; then
|
|
if test -z "${last_stable_version}"; then
|
|
printf -- \
|
|
'%s: %s: Error: The --force-stable command-line option is specified, but no stable versions are found.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
1>&2
|
|
return 1
|
|
fi
|
|
|
|
printf -- \
|
|
'%s: %s: DEBUG: The --force-stable command-line option is specified, stable release will be built.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
1>&2
|
|
release_type_to_build=stable
|
|
# Case: Build the stable version when:
|
|
#
|
|
# The stable version is available
|
|
# AND (
|
|
# There's no snap available in the stable channel of the Snap
|
|
# Store
|
|
# OR The stable version is newer than the one on the stable
|
|
# channel of the snap store
|
|
# )
|
|
elif test -n "${last_stable_version}" \
|
|
&& {
|
|
test -z "${last_stable_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_stable_version}" \
|
|
"${last_stable_version_on_the_snap_store}"
|
|
}; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Stable version(%s) is newer than the one on the Snap Store(%s), stable release will be built.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_stable_version}" \
|
|
"${last_stable_version_on_the_snap_store:-none}" \
|
|
1>&2
|
|
fi
|
|
release_type_to_build=stable
|
|
# Case: Build release candidate version when:
|
|
#
|
|
# Release candidate version is available
|
|
# AND (
|
|
# There's no snap on the Snap Store's stable channel
|
|
# OR The release candidate version is newer than the version
|
|
# on the Snap Store's stable channel
|
|
# )
|
|
# AND (
|
|
# There's no release in the Snap Store's candidate channel
|
|
# OR The release candidate version is newer than the version
|
|
# in the Snap Store's candidate channel
|
|
# )
|
|
elif test -n "${last_release_candidate_version}" \
|
|
&& {
|
|
test -z "${last_stable_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_release_candidate_version}" \
|
|
"${last_stable_version_on_the_snap_store}"
|
|
} && {
|
|
test -z "${last_release_candidate_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_release_candidate_version}" \
|
|
"${last_release_candidate_version_on_the_snap_store}"
|
|
}; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
"%s: %s: DEBUG: Release candidate version(%s) is not on the Snap Store's candidate channel and is newer than the version on the stable channel(%s), release candidate version will be built.\\n" \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_release_candidate_version}" \
|
|
"${last_stable_version_on_the_snap_store:-none}" \
|
|
1>&2
|
|
fi
|
|
release_type_to_build=release-candidate
|
|
# Case: Build beta version when
|
|
#
|
|
# The beta version is available
|
|
# AND (
|
|
# There's NO snap on the stable channel of the Snap Store
|
|
# OR The beta version is newer than the stable version on
|
|
# the Snap Store
|
|
# )
|
|
# AND (
|
|
# There's NO snap on the candidate channel of the Snap Store
|
|
# OR The beta version is newer than the candidate version
|
|
# on the Snap Store
|
|
# )
|
|
# AND (
|
|
# There's NO snap on the beta channel of the Snap Store
|
|
# OR The beta version is newer than the beta version on
|
|
# the Snap Store
|
|
# )
|
|
elif test -n "${last_beta_version}" \
|
|
&& {
|
|
test -z "${last_stable_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_beta_version}" \
|
|
"${last_stable_version_on_the_snap_store}"
|
|
} && {
|
|
test -z "${last_release_candidate_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_beta_version}" \
|
|
"${last_release_candidate_version_on_the_snap_store}"
|
|
} && {
|
|
test -z "${last_beta_version_on_the_snap_store}" \
|
|
|| version_former_is_greater_than_latter \
|
|
"${last_beta_version}" \
|
|
"${last_beta_version_on_the_snap_store}"
|
|
}; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
"%s: %s: DEBUG: Beta version(%s) is newer than the versions in the stabler channels(stable(%s), candidate(%s)), and is newer than the one shipped in the Snap Store's beta channel(%s), beta version will be built.\\n" \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_beta_version}" \
|
|
"${last_stable_version_on_the_snap_store:-none}" \
|
|
"${last_release_candidate_version_on_the_snap_store:-none}" \
|
|
"${last_beta_version_on_the_snap_store:-none}" \
|
|
1>&2
|
|
fi
|
|
release_type_to_build=beta
|
|
# Case: Build development snapshot when:
|
|
#
|
|
# All other versions are built
|
|
else
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
"%s: %s: DEBUG: All other versions are on the Snap Store(stable(%s), candidate(%s), beta(%s)), development snapshot will be built.\\n" \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${last_stable_version_on_the_snap_store:-none}" \
|
|
"${last_release_candidate_version_on_the_snap_store:-none}" \
|
|
"${last_beta_version_on_the_snap_store:-none}" \
|
|
1>&2
|
|
fi
|
|
release_type_to_build=development-snapshot
|
|
fi
|
|
|
|
printf \
|
|
'%s' \
|
|
"${release_type_to_build}"
|
|
}
|
|
|
|
# Query snap version for specific channel, we use the Snap Store API
|
|
# Output: snap version string, or null string if the channel has no
|
|
# snap
|
|
# http://api.snapcraft.io/docs/info.html#snap_info
|
|
snap_query_version(){
|
|
if test $# -ne 3; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# positional parameters
|
|
local snap_identifier="${1}"; shift
|
|
local release_channel="${1}"; shift
|
|
local snap_version_postfix_seperator="${1}"; shift
|
|
|
|
snap_arch="${SNAP_ARCH:-amd64}"
|
|
|
|
local snap_version_in_release_channel
|
|
local info_store_api_call_response
|
|
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
'%s: %s: DEBUG: Checking what snap revisions are available for the %s snap at the %s release channel...\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${snap_identifier}" \
|
|
"${release_channel}" \
|
|
1>&2
|
|
fi
|
|
if ! info_store_api_call_response="$(
|
|
curl \
|
|
--fail \
|
|
--silent \
|
|
--show-error \
|
|
--header 'Snap-Device-Series: 16' \
|
|
"https://api.snapcraft.io/v2/snaps/info/${snap_identifier}?fields=version&architecture=${snap_arch}" \
|
|
2>&1
|
|
)"; then
|
|
# If the response is 404, the snap hasn't published to the
|
|
# the specified channel(or, at all) at the Snap Store yet
|
|
regex_curl_request_returns_404='\(22\).*: 404$'
|
|
grep_opts=(
|
|
--extended-regexp
|
|
--regexp="${regex_curl_request_returns_404}"
|
|
--quiet
|
|
)
|
|
if grep "${grep_opts[@]}" <<<"${info_store_api_call_response}"; then
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf -- \
|
|
"%s: %s: DEBUG: The /snaps/info/ Snap Store API call returns 404, interpreting as the snap hasn't published to the store...\\n" \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
1>&2
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
printf \
|
|
'%s: %s: Error: Unable to call the Snap Store info API to retrieve snap revision info: %s\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${info_store_api_call_response}" \
|
|
1>&2
|
|
return 1
|
|
fi
|
|
|
|
if ! snap_version_in_release_channel="$(
|
|
jq \
|
|
--raw-output \
|
|
".\"channel-map\"[]
|
|
| select(
|
|
.channel.name == \"${release_channel}\"
|
|
).version" \
|
|
<<< "${info_store_api_call_response}" \
|
|
2>&1
|
|
)"; then
|
|
printf \
|
|
'%s: %s: Error: Unable to query the snap version from the store info API response: %s\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${snap_version_in_release_channel}" \
|
|
1>&2
|
|
return 2
|
|
fi
|
|
if test -z "${snap_version_in_release_channel}"; then
|
|
# No snaps available in the channel
|
|
return 0
|
|
fi
|
|
if test "${SELECTIVE_CHECKOUT_DEBUG}" == true; then
|
|
printf \
|
|
'%s: %s: DEBUG: Snap version determined to be: "%s".\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
"${snap_version_in_release_channel}" \
|
|
1>&2
|
|
fi
|
|
|
|
printf %s "${snap_version_in_release_channel}"
|
|
}
|
|
|
|
# Determine which VCS is used
|
|
# FIXME: Allow specifying any node under the working directory, not just the root node
|
|
vcs_detect(){
|
|
if test $# -ne 1; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local source_tree_root_dir="${1}"; shift 1
|
|
|
|
if test -e "${source_tree_root_dir}"/.git; then
|
|
printf git
|
|
elif test -e "${source_tree_root_dir}"/.hg; then
|
|
printf mercurial
|
|
elif test -e "${source_tree_root_dir}"/.svn; then
|
|
printf subversion
|
|
else
|
|
printf not_found
|
|
fi
|
|
}
|
|
|
|
# Ensure depending software is available before using them
|
|
vcs_check_runtime_dependencies(){
|
|
if test $# -ne 1; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
if ! command -v git &>/dev/null; then
|
|
# Markdown code markup is not Bash tilde expression
|
|
# shellcheck disable=SC2016
|
|
printf -- \
|
|
'%s: %s: Error: `git` command not found in the command search PATHs.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
;;
|
|
mercurial)
|
|
if ! command -v hg &>/dev/null; then
|
|
# Markdown code markup is not Bash tilde expression
|
|
# shellcheck disable=SC2016
|
|
printf -- \
|
|
'%s: %s: Error: `hg` command not found in the command search PATHs.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
;;
|
|
subversion)
|
|
if ! command -v svn &>/dev/null; then
|
|
# Markdown code markup is not Bash tilde expression
|
|
# shellcheck disable=SC2016
|
|
printf -- \
|
|
'%s: %s: Error: `svn` command not found in the command search PATHs.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: %s: Warning: Unknown VCS type, assuming none.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
vcs_is_dirty(){
|
|
if test $# -ne 1; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
# If tracked files are modified
|
|
# or staging area not empty
|
|
if ! \
|
|
git -C "${source_tree_root_dir}" diff \
|
|
--quiet \
|
|
|| ! \
|
|
git -C "${source_tree_root_dir}" diff \
|
|
--staged \
|
|
--quiet; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
mercurial)
|
|
# NOTE:
|
|
# The existence of untracked files is not consider dirty,
|
|
# imitating Git's `--dirty` option of the describe
|
|
# subcommand
|
|
if test -n "$(
|
|
hg --cwd "${source_tree_root_dir}" status \
|
|
--added \
|
|
--deleted \
|
|
--modified \
|
|
--removed
|
|
)"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
subversion)
|
|
# NOTE: Is there any straightforward way to check this?
|
|
if test -n "$(
|
|
svn status -q
|
|
)"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: %s: Warning: Unknown VCS type, assuming dirty.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
vcs_has_release_tags() {
|
|
if test $# -ne 2; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
|
|
# Pattern to match a release tag, in extended regular expression(ERE)
|
|
local -r tag_pattern_release="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
if git -C "${source_tree_root_dir}" tag \
|
|
--list \
|
|
| grep \
|
|
--extended-regexp \
|
|
--quiet \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
mercurial)
|
|
if hg --cwd "${source_tree_root_dir}" tags \
|
|
| grep \
|
|
--extended-regexp \
|
|
--quiet \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
subversion)
|
|
local \
|
|
source_tree_root_url \
|
|
tags_dir_url
|
|
|
|
source_tree_root_url="$(
|
|
svn info \
|
|
--show-item url \
|
|
"${source_tree_root_dir}"
|
|
)"
|
|
|
|
# Supported source URLs:
|
|
# * /trunk
|
|
# * /tags/_tag_name_
|
|
case "${source_tree_root_url}" in
|
|
*/trunk)
|
|
# Strip trailing shortest matched pattern
|
|
# https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
|
tags_dir_url="${source_tree_root_url%/trunk}/tags"
|
|
;;
|
|
*/tags/*)
|
|
tags_dir_url="${source_tree_root_url%/*}"
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'Warning: %s: Unsupported SVN source URL, assuming no release tags found.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
if svn list \
|
|
"${tags_dir_url}" \
|
|
| sed 's#/$##' \
|
|
| grep \
|
|
--extended-regexp \
|
|
--quiet \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: Warning: Unknown VCS type, assuming no release tags found.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
vcs_query_release_tags(){
|
|
if test $# -ne 2; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
|
|
# Pattern to match a release tag, in extended regular expression(ERE)
|
|
local -r tag_pattern_release="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
if git -C "${source_tree_root_dir}" tag \
|
|
--list \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
mercurial)
|
|
if hg --cwd "${source_tree_root_dir}" tags \
|
|
--quiet \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
subversion)
|
|
local \
|
|
source_tree_root_url \
|
|
tags_dir_url
|
|
|
|
source_tree_root_url="$(
|
|
svn info \
|
|
--show-item url \
|
|
"${source_tree_root_dir}"
|
|
)"
|
|
|
|
# Supported source URLs:
|
|
# * /trunk
|
|
# * /tags/_tag_name_
|
|
case "${source_tree_root_url}" in
|
|
*/trunk)
|
|
# Strip trailing shortest matched pattern
|
|
# https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
|
tags_dir_url="${source_tree_root_url%/trunk}/tags"
|
|
;;
|
|
*/tags/*)
|
|
tags_dir_url="${source_tree_root_url%/*}"
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'Warning: %s: Unsupported SVN source URL, assuming no release tags found.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
if svn list \
|
|
"${tags_dir_url}" \
|
|
| sed 's#/$##' \
|
|
| grep \
|
|
--extended-regexp \
|
|
--regexp="${tag_pattern_release}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: Warning: Unknown VCS type, assuming no release tags found.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
vcs_checkout_tag(){
|
|
if test $# -ne 2; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
local -r tag_to_be_checked_out="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
git -C "${source_tree_root_dir}" checkout \
|
|
"${tag_to_be_checked_out}"
|
|
return 0
|
|
;;
|
|
mercurial)
|
|
hg --cwd "${source_tree_root_dir}" checkout \
|
|
--rev "${tag_to_be_checked_out}"
|
|
return 0
|
|
;;
|
|
subversion)
|
|
local \
|
|
source_tree_root_url \
|
|
tags_dir_url
|
|
|
|
source_tree_root_url="$(
|
|
svn info \
|
|
--show-item url \
|
|
"${source_tree_root_dir}"
|
|
)"
|
|
|
|
# Supported source URLs:
|
|
# * /trunk
|
|
# * /tags/_tag_name_
|
|
case "${source_tree_root_url}" in
|
|
*/trunk)
|
|
# Strip trailing shortest matched pattern
|
|
# https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
|
|
tags_dir_url="${source_tree_root_url%/trunk}/tags"
|
|
;;
|
|
*/tags/*)
|
|
tags_dir_url="${source_tree_root_url%/*}"
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'Warning: %s: Unsupported SVN source URL, assuming check out failed.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
if svn checkout \
|
|
"${tags_dir_url}"/"${tag_to_be_checked_out}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: Warning: Unknown VCS type, not doing anything.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Describe software version, use tags when available
|
|
vcs_describe_version(){
|
|
if test $# -ne 3; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
local -r revision_identifier_length_minimum="${1}"; shift 1
|
|
local -r dirty_postfix="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
git describe \
|
|
--abbrev="${revision_identifier_length_minimum}" \
|
|
--always \
|
|
--dirty="${dirty_postfix}" \
|
|
--tags
|
|
return 0
|
|
;;
|
|
mercurial)
|
|
hg --cwd "${source_tree_root_dir}" log \
|
|
--rev . \
|
|
--template "{latesttag}{sub('^-0-.*', '', '-{latesttagdistance}-m{shortest(node, ${revision_identifier_length_minimum})}')}"
|
|
if vcs_is_dirty "${source_tree_root_dir}"; then
|
|
printf -- %s "${dirty_postfix}"
|
|
fi
|
|
return 0
|
|
;;
|
|
subversion)
|
|
local \
|
|
source_tree_root_url
|
|
|
|
source_tree_root_url="$(
|
|
svn info \
|
|
--show-item url \
|
|
"${source_tree_root_dir}"
|
|
)"
|
|
|
|
# Supported source URLs:
|
|
# * /trunk
|
|
# * /tags/_tag_name_
|
|
case "${source_tree_root_url}" in
|
|
*/trunk)
|
|
printf %s \
|
|
"rev$(
|
|
svn info \
|
|
--show-item revision \
|
|
"${source_tree_root_dir}"
|
|
)"
|
|
;;
|
|
*/tags/*)
|
|
local tag
|
|
tag="${source_tree_root_url##*/}"
|
|
printf %s "${tag}"
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'Warning: %s: Unsupported SVN source URL.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
printf unknown
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
if vcs_is_dirty "${source_tree_root_dir}"; then
|
|
printf -- %s "${dirty_postfix}"
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: Warning: Unknown VCS type.\n' \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
printf unknown
|
|
return 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Describe version control revision, only revision identifier/hash with
|
|
# customizable minimum length
|
|
# FIXME: Some node among source tree should be enough for source_tree_root_dir
|
|
vcs_describe_revision(){
|
|
if test $# -ne 3; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r source_tree_root_dir="${1}"; shift 1
|
|
local -r revision_identifier_length_minimum="${1}"; shift 1
|
|
local -r dirty_postfix="${1}"; shift 1
|
|
|
|
case "$(vcs_detect "${source_tree_root_dir}")" in
|
|
git)
|
|
if ! git -C "${source_tree_root_dir}" describe --always >/dev/null; then
|
|
printf unknown
|
|
else
|
|
git -C "${source_tree_root_dir}" describe \
|
|
--abbrev="${revision_identifier_length_minimum}" \
|
|
--always \
|
|
--dirty="${dirty_postfix}" \
|
|
--match=nothing
|
|
fi
|
|
return 0
|
|
;;
|
|
mercurial)
|
|
if ! hg --cwd "${source_tree_root_dir}" status >/dev/null; then
|
|
printf unknown
|
|
else
|
|
# FIXME: Is there a better way of generating this only using Mercurial?
|
|
local hg_revision
|
|
|
|
hg_revision+="$(
|
|
hg --cwd "${source_tree_root_dir}" log \
|
|
--rev . \
|
|
--template "{shortest(node, ${revision_identifier_length_minimum})}"
|
|
)"
|
|
|
|
if vcs_is_dirty \
|
|
"${SCRIPT_NAME}" \
|
|
"${source_tree_root_dir}"; then
|
|
hg_revision+="${dirty_postfix}"
|
|
fi
|
|
|
|
printf -- %s "${hg_revision}"
|
|
fi
|
|
return 0
|
|
;;
|
|
subversion)
|
|
svn info \
|
|
--show-item revision \
|
|
"${source_tree_root_dir}"
|
|
|
|
if vcs_is_dirty \
|
|
"${SCRIPT_NAME}" \
|
|
"${source_tree_root_dir}"; then
|
|
printf -- %s "${dirty_postfix}"
|
|
fi
|
|
;;
|
|
*)
|
|
printf -- \
|
|
'%s: %s: Warning: Unknown VCS type.\n' \
|
|
"${SCRIPT_NAME}" \
|
|
"${FUNCNAME[0]}" \
|
|
>&2
|
|
printf unknown
|
|
return 0
|
|
;;
|
|
esac
|
|
}
|
|
|
|
# Check whether a version string is newer than the other version string
|
|
# identical version is considered "not newer"
|
|
version_former_is_greater_than_latter(){
|
|
if test $# -ne 2; then
|
|
printf 'FATAL: %s: Parameter quantity mismatch.\n' "${FUNCNAME[0]}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local -r former="${1}"; shift
|
|
local -r latter="${1}"; shift
|
|
|
|
if test "${former}" = "${latter}"; then
|
|
return 1
|
|
fi
|
|
|
|
local newer_version
|
|
newer_version="$(
|
|
printf \
|
|
-- \
|
|
'%s\n%s' \
|
|
"${former}" \
|
|
"${latter}" \
|
|
| sort \
|
|
--version-sort \
|
|
--reverse \
|
|
| head \
|
|
--lines=1
|
|
)"
|
|
|
|
if test "${newer_version}" = "${former}"; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
init "${@}"
|