mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
chore(resources): Merge branch 'master' into maint/resources/14483-move-build-trigger-scripts
This commit is contained in:
commit
4d9ee2ee70
10 changed files with 210 additions and 147 deletions
13
HISTORY.md
13
HISTORY.md
|
|
@ -1,5 +1,12 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.106 alpha 2025-08-27
|
||||
|
||||
* fix(windows): check if automatic updates off before prompt (#14295)
|
||||
* refactor(linux): use bash testing framework for packaging tests (#14603)
|
||||
* maint(resources): use correct version for PR title for history cherry-pick (#14610)
|
||||
* refactor(common): fix shellcheck warnings (#14580)
|
||||
|
||||
## 19.0.105 alpha 2025-08-25
|
||||
|
||||
* maint(linux): ignore generate patch directories (#14600)
|
||||
|
|
@ -656,6 +663,12 @@
|
|||
* refactor(windows): rename `TKeymanMutex.MutexOwned` to `TakeOwnership` and add `ReleaseOwnership` (#13168)
|
||||
* chore: increment to alpha 19.0 (#13187)
|
||||
|
||||
## 18.0.240 stable 2025-08-27
|
||||
|
||||
* maint(resources): automatically cherry-pick history updates to master (#14596)
|
||||
* maint(linux): fix lintian warnings (#14591)
|
||||
* maint(linux): ignore generate patch directories (#14601)
|
||||
|
||||
## 18.0.239 stable 2025-08-22
|
||||
|
||||
* docs(linux): clarify requirement of patched ibus for Keyman < 18 (#14113)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.106
|
||||
19.0.107
|
||||
|
|
@ -7,9 +7,10 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-basic.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/build/test/testing-framework.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/test.inc.sh"
|
||||
|
||||
test_check_updated_version_number__NoChange_OK() {
|
||||
function test__check_updated_version_number__NoChange_OK() {
|
||||
createBase alpha
|
||||
|
||||
echo "readme" > README.md
|
||||
|
|
@ -21,7 +22,7 @@ test_check_updated_version_number__NoChange_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineAdded_OK() {
|
||||
function test__check_updated_version_number__LineAdded_OK() {
|
||||
createBase alpha
|
||||
|
||||
sed -i 's/ km_core_actions_dispose@Base 17.0.197/ km_core_actions_dispose@Base 17.0.197\n km_core_added@Base 17.0.255/' linux/debian/libkeymancore1.symbols
|
||||
|
|
@ -33,7 +34,7 @@ test_check_updated_version_number__LineAdded_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineAddedWithoutVerUpd_ERROR() {
|
||||
function test__check_updated_version_number__LineAddedWithoutVerUpd_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
|
||||
|
|
@ -48,7 +49,7 @@ test_check_updated_version_number__LineAddedWithoutVerUpd_ERROR() {
|
|||
[[ "${output[*]}" == *"ERROR: libkeymancore1.symbols file got changed without changing the package version number of the symbol"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemovedWithAPIUpd_OK() {
|
||||
function test__check_updated_version_number__LineRemovedWithAPIUpd_OK() {
|
||||
createBase alpha
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
git add core/CORE_API_VERSION.md
|
||||
|
|
@ -64,7 +65,7 @@ test_check_updated_version_number__LineRemovedWithAPIUpd_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_OnlyCoreApiUpd_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_OnlyCoreApiUpd_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
|
|
@ -80,7 +81,7 @@ test_check_updated_version_number__LineRemoved_OnlyCoreApiUpd_ERROR() {
|
|||
[[ "${output[*]}" == *"ERROR: Missing libkeymancore2.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_OnlySymbolsFileUpd_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_OnlySymbolsFileUpd_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
git mv linux/debian/libkeymancore{1,2}.symbols
|
||||
|
|
@ -97,7 +98,7 @@ test_check_updated_version_number__LineRemoved_OnlySymbolsFileUpd_ERROR() {
|
|||
[[ "${output[*]}" == *"ERROR: Missing libkeymancore1.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemovedWithAPIUpd_NotMetadataUpd_ERROR() {
|
||||
function test__check_updated_version_number__LineRemovedWithAPIUpd_NotMetadataUpd_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
echo "2.0.0" > core/CORE_API_VERSION.md
|
||||
|
|
@ -114,7 +115,7 @@ test_check_updated_version_number__LineRemovedWithAPIUpd_NotMetadataUpd_ERROR()
|
|||
[[ "${output[*]}" == *"ERROR: API version in .symbols file and in CORE_API_VERSION.md is different"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InAlpha_ChangedBefore_OK() {
|
||||
function test__check_updated_version_number__LineRemoved_InAlpha_ChangedBefore_OK() {
|
||||
createBase alpha
|
||||
git checkout master
|
||||
# simulate a commit that already introduced an API version change
|
||||
|
|
@ -137,7 +138,7 @@ test_check_updated_version_number__LineRemoved_InAlpha_ChangedBefore_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerChanged_OK() {
|
||||
function test__check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerChanged_OK() {
|
||||
createBase alpha
|
||||
git checkout master
|
||||
# simulate a commit that renamed the .symbols file and updated the API version
|
||||
|
|
@ -164,7 +165,7 @@ test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVe
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base master verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerUnchanged_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerUnchanged_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
git checkout master
|
||||
|
|
@ -189,7 +190,7 @@ test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVe
|
|||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libfoo1.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InAlpha_ChangeFromStable_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_InAlpha_ChangeFromStable_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
|
||||
|
|
@ -204,7 +205,7 @@ test_check_updated_version_number__LineRemoved_InAlpha_ChangeFromStable_ERROR()
|
|||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libkeymancore1.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InBeta_ApiVerUnchanged_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_InBeta_ApiVerUnchanged_ERROR() {
|
||||
local output
|
||||
createBase beta
|
||||
|
||||
|
|
@ -231,7 +232,7 @@ test_check_updated_version_number__LineRemoved_InBeta_ApiVerUnchanged_ERROR() {
|
|||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libkeymancore2.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InBeta_ApiVerChanged_OK() {
|
||||
function test__check_updated_version_number__LineRemoved_InBeta_ApiVerChanged_OK() {
|
||||
createBase beta
|
||||
|
||||
# simulate a commit that already introduced an API version change in Beta
|
||||
|
|
@ -260,7 +261,7 @@ test_check_updated_version_number__LineRemoved_InBeta_ApiVerChanged_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base beta verify
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineRemoved_InBeta_FileMissingInStable_ApiVerUnchanged_ERROR() {
|
||||
function test__check_updated_version_number__LineRemoved_InBeta_FileMissingInStable_ApiVerUnchanged_ERROR() {
|
||||
local output
|
||||
createBase alpha
|
||||
git checkout -b beta
|
||||
|
|
@ -285,7 +286,7 @@ test_check_updated_version_number__LineRemoved_InBeta_FileMissingInStable_ApiVer
|
|||
[[ "${output[*]}" == *" ERROR: Major API change without updating API version number in libfoo1.symbols file"* ]]
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineInsertedInBranch_OK() {
|
||||
function test__check_updated_version_number__LineInsertedInBranch_OK() {
|
||||
createBase alpha
|
||||
|
||||
local base_sha=$(git rev-parse master)
|
||||
|
|
@ -313,21 +314,6 @@ test_check_updated_version_number__LineInsertedInBranch_OK() {
|
|||
linux/scripts/deb-packaging.sh --bin-pkg "${BINPKG_NAME}" --git-sha "$(git rev-parse HEAD)" --git-base "${base_sha}" verify
|
||||
}
|
||||
|
||||
echo "(test logs are in /tmp/<testname>.log)"
|
||||
run_test test_check_updated_version_number__NoChange_OK
|
||||
run_test test_check_updated_version_number__LineAdded_OK
|
||||
run_test test_check_updated_version_number__LineAddedWithoutVerUpd_ERROR
|
||||
run_test test_check_updated_version_number__LineRemovedWithAPIUpd_OK
|
||||
run_test test_check_updated_version_number__LineRemoved_OnlyCoreApiUpd_ERROR
|
||||
run_test test_check_updated_version_number__LineRemoved_OnlySymbolsFileUpd_ERROR
|
||||
run_test test_check_updated_version_number__LineRemovedWithAPIUpd_NotMetadataUpd_ERROR
|
||||
run_test test_check_updated_version_number__LineRemoved_InAlpha_ChangedBefore_OK
|
||||
run_test test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerChanged_OK
|
||||
run_test test_check_updated_version_number__LineRemoved_InAlpha_FileMissingInStable_ApiVerUnchanged_ERROR
|
||||
run_test test_check_updated_version_number__LineRemoved_InAlpha_ChangeFromStable_ERROR
|
||||
run_test test_check_updated_version_number__LineRemoved_InBeta_ApiVerUnchanged_ERROR
|
||||
run_test test_check_updated_version_number__LineRemoved_InBeta_ApiVerChanged_OK
|
||||
run_test test_check_updated_version_number__LineRemoved_InBeta_FileMissingInStable_ApiVerUnchanged_ERROR
|
||||
run_test test_check_updated_version_number__LineInsertedInBranch_OK
|
||||
|
||||
# TODO: still some test cases missing for the different checks
|
||||
|
||||
run_tests --quiet
|
||||
|
|
|
|||
|
|
@ -81,9 +81,3 @@ createBase() {
|
|||
BINPKG_NAME=${tmpDir}/libkeymancore1_17.0.257-1+noble1_amd64.deb
|
||||
touch "${BINPKG_NAME}"
|
||||
}
|
||||
|
||||
run_test() {
|
||||
setup
|
||||
$1 > "/tmp/$1.log" 2>&1 && echo -e "${COLOR_GREEN}$1: OK${COLOR_RESET}" || echo -e "${COLOR_RED}$1: FAILED${COLOR_RESET}"
|
||||
teardown
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,42 +9,56 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../../resources/build/builder-basic.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
# shellcheck disable=2154
|
||||
. "${KEYMAN_ROOT}/resources/build/test/testing-framework.inc.sh"
|
||||
. "${THIS_SCRIPT%/*}/test.inc.sh"
|
||||
. "${KEYMAN_ROOT}/linux/scripts/verify_api.inc.sh"
|
||||
|
||||
|
||||
fixture_setup() {
|
||||
function setup_file() {
|
||||
SONAME=1
|
||||
LIB_NAME=libkeymancore
|
||||
PKG_NAME="${LIB_NAME}${SONAME}"
|
||||
EXIT_CODE=-1
|
||||
}
|
||||
|
||||
test_compare_versions__less_patch() {
|
||||
[[ $(compare_versions "17.0.197" "17.0.198") == -1 ]]
|
||||
function test__compare_versions__less_patch() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.197" "17.0.198")"
|
||||
assert-equal "${result}" -1
|
||||
}
|
||||
|
||||
test_compare_versions__less_minor() {
|
||||
[[ $(compare_versions "17.0.197" "17.1.197") == -1 ]]
|
||||
function test__compare_versions__less_minor() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.197" "17.1.197")"
|
||||
assert-equal "${result}" -1
|
||||
}
|
||||
|
||||
test_compare_versions__less_major() {
|
||||
[[ $(compare_versions "17.0.197" "18.0.0") == -1 ]]
|
||||
function test__compare_versions__less_major() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.197" "18.0.0")"
|
||||
assert-equal "${result}" -1
|
||||
}
|
||||
|
||||
test_compare_versions__greater() {
|
||||
[[ $(compare_versions "17.0.198" "17.0.197") == 1 ]]
|
||||
function test__compare_versions__greater() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.198" "17.0.197")"
|
||||
assert-equal "${result}" 1
|
||||
}
|
||||
|
||||
test_compare_versions__same() {
|
||||
[[ $(compare_versions "17.0.197" "17.0.197") == 0 ]]
|
||||
function test__compare_versions__same() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.197" "17.0.197")"
|
||||
assert-equal "${result}" 0
|
||||
}
|
||||
|
||||
test_compare_versions__greater0() {
|
||||
[[ $(compare_versions "17.0.197" "0") == 1 ]]
|
||||
function test__compare_versions__greater0() {
|
||||
local result
|
||||
result="$(compare_versions "17.0.197" "0")"
|
||||
assert-equal "${result}" 1
|
||||
}
|
||||
|
||||
test_get_highest_version_in_symbols_file() {
|
||||
function test__get_highest_version_in_symbols_file() {
|
||||
local output
|
||||
createBase alpha
|
||||
echo ' (c++|optional)"typeinfo name for std::codecvt@Base" 17.0.244' >> linux/debian/libkeymancore1.symbols
|
||||
|
|
@ -54,10 +68,10 @@ test_get_highest_version_in_symbols_file() {
|
|||
# Execute
|
||||
output=$(get_highest_version_in_symbols_file "$(git rev-parse HEAD)")
|
||||
echo "${output[*]}" # for logging
|
||||
[[ "${output}" == "17.0.244" ]]
|
||||
assert-equal "${output}" "17.0.244"
|
||||
}
|
||||
|
||||
test_check_updated_version_number__LineInsertedInBranch_OK() {
|
||||
function test__check_updated_version_number__LineInsertedInBranch_OK() {
|
||||
local output
|
||||
createBase alpha
|
||||
|
||||
|
|
@ -89,17 +103,7 @@ test_check_updated_version_number__LineInsertedInBranch_OK() {
|
|||
|
||||
output=$(check_updated_version_number)
|
||||
echo "${output[*]}" # for logging
|
||||
[[ "${output[*]}" == *"OK: libkeymancore1.symbols file got updated with package version number"* ]]
|
||||
assert-contains "${output[*]}" "OK: libkeymancore1.symbols file got updated with package version number"
|
||||
}
|
||||
|
||||
|
||||
fixture_setup
|
||||
echo "(test logs are in /tmp/<testname>.log)"
|
||||
run_test test_compare_versions__less_patch
|
||||
run_test test_compare_versions__less_minor
|
||||
run_test test_compare_versions__less_major
|
||||
run_test test_compare_versions__greater
|
||||
run_test test_compare_versions__same
|
||||
run_test test_compare_versions__greater0
|
||||
run_test test_get_highest_version_in_symbols_file
|
||||
run_test test_check_updated_version_number__LineInsertedInBranch_OK
|
||||
run_tests --quiet
|
||||
|
|
|
|||
|
|
@ -21,19 +21,20 @@
|
|||
function test_bot_check_pr_body() {
|
||||
local PRNUM=$1
|
||||
local prinfo="$2"
|
||||
local prbody prTestCommand prTestBody
|
||||
|
||||
set -o noglob
|
||||
IFS=$'\n'
|
||||
local prbody="$(echo "$prinfo" | "${JQ}" -r '.body')"
|
||||
local prTestCommand="$(echo "$prbody" | grep 'Test-bot:' | cut -d: -f 2 - | cut -d' ' -f 1 -)"
|
||||
local prTestBody="$(echo "$prbody" | grep -i '# User Testing')"
|
||||
prbody="$(echo "${prinfo}" | "${JQ}" -r '.body')"
|
||||
prTestCommand="$(echo "${prbody}" | grep 'Test-bot:' | cut -d: -f 2 - | cut -d' ' -f 1 -)"
|
||||
prTestBody="$(echo "${prbody}" | grep -i '# User Testing' || true)"
|
||||
unset IFS
|
||||
set +o noglob
|
||||
|
||||
if ([[ "$prTestCommand" == skip ]] || [[ -z "${prTestCommand// }" ]]) && [[ -z "${prTestBody// }" ]]; then
|
||||
if { [[ "${prTestCommand}" == skip ]] || [[ -z "${prTestCommand// }" ]]; } && [[ -z "${prTestBody// }" ]]; then
|
||||
local platform
|
||||
for platform in "${!build_platforms[@]}"; do
|
||||
build_platforms[$platform]=build
|
||||
build_platforms["${platform}"]=build
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
|
@ -66,35 +67,62 @@ function build_bot_check_messages() {
|
|||
# Extract the Build-bot commands from commit messages and the PR body
|
||||
|
||||
|
||||
local buildBotShas=($(echo "$prcommits" | "${JQ}" -r '.[].sha'))
|
||||
IFS=$'\n'
|
||||
local buildBotCommands=($(echo "$prcommits" | "${JQ}" -r '.[].commit.message' | grep 'Build-bot:' | cut -c 11- -))
|
||||
local prCommands=($(echo "$prinfo" | "${JQ}" -r '.body' | tr -d '\r' | grep 'Build-bot:' | cut -c 11- -))
|
||||
unset IFS
|
||||
|
||||
# The PR body Build-bot comment will be read last, which allows it to override
|
||||
# all previous commands
|
||||
|
||||
if [[ ${#prCommands[@]} -gt 0 ]]; then
|
||||
buildBotCommands+=("${prCommands[@]}")
|
||||
fi
|
||||
local buildBotCommand
|
||||
local sha
|
||||
for sha in "${buildBotShas[@]}"; do
|
||||
|
||||
for buildBotCommand in "${buildBotCommands[@]}"; do
|
||||
# Block illegal Build-bot: commands
|
||||
if [[ ! "$buildBotCommand" =~ ^[a-z_,\ :,]+$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid command [2]: '${buildBotCommand}'"
|
||||
continue
|
||||
fi
|
||||
IFS=$'\n'
|
||||
local buildBotCommands=($(echo "$prcommits" | "${JQ}" -r '.[] | select(.sha | contains("'$sha'")) | .commit.message' | grep 'Build-bot:' | cut -c 11- -))
|
||||
unset IFS
|
||||
|
||||
# debug_echo "buildBotCommand:{$buildBotCommand}"
|
||||
for buildBotCommand in "${buildBotCommands[@]}"; do
|
||||
buildBotCommand=$(builder_trim "${buildBotCommand}")
|
||||
builder_echo heading "Build-bot: Found command in commit ${sha}: '${buildBotCommand}'"
|
||||
|
||||
# We now know that our command has only a-z, comma, colon, and space, so we
|
||||
# can parse without risking escaping our bash jail
|
||||
# Block illegal Build-bot: commands
|
||||
if [[ ! "$buildBotCommand" =~ ^[a-z_,\ :,]+$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid command: '${buildBotCommand}'"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ ! -z "${buildBotCommand// }" ]]; then
|
||||
build_bot_update_commands $buildBotCommand
|
||||
fi
|
||||
# We now know that our command has only a-z, comma, colon, and space, so we
|
||||
# can parse without risking escaping our bash jail
|
||||
|
||||
if [[ ! -z "${buildBotCommand// }" ]]; then
|
||||
_build_bot_update_commands $buildBotCommand
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ ${#prCommands[@]} -gt 0 ]]; then
|
||||
for buildBotCommand in "${prCommands[@]}"; do
|
||||
buildBotCommand=$(builder_trim "${buildBotCommand}")
|
||||
builder_echo heading "Build-bot: Found command in body of PR #${PRNUM}: '${buildBotCommand}'"
|
||||
|
||||
# Block illegal Build-bot: commands
|
||||
if [[ ! "$buildBotCommand" =~ ^[a-z_\ :,]+$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid command: '${buildBotCommand}'"
|
||||
continue
|
||||
fi
|
||||
|
||||
# We now know that our command has only a-z, comma, colon, underline, and
|
||||
# space, so we can parse without risking escaping our bash jail
|
||||
|
||||
if [[ ! -z "${buildBotCommand// }" ]]; then
|
||||
_build_bot_update_commands $buildBotCommand
|
||||
fi
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
set +o noglob
|
||||
}
|
||||
|
||||
|
|
@ -105,13 +133,13 @@ function build_bot_check_messages() {
|
|||
# Note that this function assumes that inputs are sanitized, see
|
||||
# build_bot_check_messages
|
||||
#
|
||||
function build_bot_update_commands() {
|
||||
function _build_bot_update_commands() {
|
||||
local level=
|
||||
local platforms=
|
||||
local command="$*"
|
||||
|
||||
local re='^(build|skip|release)( [a-z,]+)?$'
|
||||
if [[ "$command" =~ $re ]]; then
|
||||
if [[ "${command}" =~ ${re} ]]; then
|
||||
# legacy (until aug 2025) format is "level [platform]" (comma format never used)
|
||||
if [[ $# == 1 ]]; then
|
||||
level=$1
|
||||
|
|
@ -126,41 +154,42 @@ function build_bot_update_commands() {
|
|||
unset IFS
|
||||
fi
|
||||
|
||||
if [[ ! $level =~ ^$valid_build_levels$ ]]; then
|
||||
if [[ ! ${level} =~ ^${valid_build_levels}$ ]]; then
|
||||
# Just skip this build command
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid build level '$level' in command '$command'"
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid build level '${level}' in command '${command}'"
|
||||
return 0
|
||||
fi
|
||||
|
||||
builder_echo blue "Platforms to be updated from command '$command' are: ${platforms[@]}"
|
||||
builder_echo grey "Build-bot: Platforms to be updated from command '${command}' are: ${platforms[*]}"
|
||||
|
||||
build_bot_verify_platforms platforms
|
||||
_build_bot_verify_platforms platforms
|
||||
|
||||
local platform
|
||||
for platform in "${platforms[@]}"; do
|
||||
builder_echo "Build-bot: Updating build level for $platform to $level"
|
||||
build_platforms[$platform]=$level
|
||||
builder_echo "Build-bot: Updating build level for ${platform} to ${level}"
|
||||
build_platforms["${platform}"]=${level}
|
||||
done
|
||||
else
|
||||
# modern format is "level[:platform[,platform...]][ level[:platform[,platform...]]...]"
|
||||
declare -a commands
|
||||
IFS=' '
|
||||
read -r -a commands <<< "$command"
|
||||
read -r -a commands <<< "${command}"
|
||||
unset IFS
|
||||
|
||||
for command in "${commands[@]}"; do
|
||||
declare -a params
|
||||
IFS=:
|
||||
read -r -a params <<< "$command"
|
||||
read -r -a params <<< "${command}"
|
||||
unset IFS
|
||||
level=${params[0]}
|
||||
if [[ ! $level =~ ^$valid_build_levels$ ]]; then
|
||||
if [[ ! ${level} =~ ^${valid_build_levels}$ ]]; then
|
||||
# Just skip this build command
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid build level '$level' in command '$command'"
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid build level '${level}' in command '${command}'"
|
||||
continue
|
||||
fi
|
||||
|
||||
if [[ ${#params[@]} == 1 ]]; then
|
||||
platforms="${!build_platforms[@]}"
|
||||
read -r -a platforms <<< "${!build_platforms[@]}"
|
||||
else
|
||||
# remaining parameters are comma separated platforms
|
||||
IFS=','
|
||||
|
|
@ -168,13 +197,13 @@ function build_bot_update_commands() {
|
|||
unset IFS
|
||||
fi
|
||||
|
||||
build_bot_verify_platforms platforms
|
||||
_build_bot_verify_platforms platforms
|
||||
|
||||
local platform
|
||||
for platform in "${platforms[@]}"; do
|
||||
if [[ "${build_platforms[$platform]+x}" != "${level}" ]]; then
|
||||
builder_echo "Build-bot: Updating build level for $platform to $level"
|
||||
build_platforms[$platform]=$level
|
||||
if [[ "${build_platforms[${platform}]+x}" != "${level}" ]]; then
|
||||
builder_echo "Build-bot: Updating build level for ${platform} to ${level}"
|
||||
build_platforms["${platform}"]=${level}
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
|
@ -187,20 +216,20 @@ function build_bot_update_commands() {
|
|||
# Parameters:
|
||||
# 1: name of platforms array parameter (byref)
|
||||
#
|
||||
function build_bot_verify_platforms() {
|
||||
function _build_bot_verify_platforms() {
|
||||
local -n input_platforms=$1
|
||||
local output_platforms=()
|
||||
local platform
|
||||
for platform in "${input_platforms[@]}"; do
|
||||
# We'll emit a warning with invalid platforms, then remove them from the array
|
||||
if [[ ! $platform =~ ^(all|$available_platforms_regex)$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid platform '$platform'"
|
||||
elif [[ $platform == all ]]; then
|
||||
input_platforms=(${available_platforms[@]})
|
||||
if [[ ! ${platform} =~ ^(all|${available_platforms_regex})$ ]]; then
|
||||
builder_echo warning "WARNING[Build-bot]: ignoring invalid platform '${platform}'"
|
||||
elif [[ ${platform} == all ]]; then
|
||||
input_platforms=("${available_platforms[@]}")
|
||||
return
|
||||
else
|
||||
if [[ ! "${output_platforms[@]}" =~ [[:\<:]]$platform[[:\>:]] ]]; then
|
||||
output_platforms+=($platform)
|
||||
if [[ ! "${output_platforms[@]}" =~ [[:\<:]]${platform}[[:\>:]] ]]; then
|
||||
output_platforms+=("${platform}")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
[
|
||||
{
|
||||
"sha": "1234",
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: build windows\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sha": "5678",
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: release windows\n"
|
||||
}
|
||||
},
|
||||
{
|
||||
"sha": "9abc",
|
||||
"commit": {
|
||||
"message": "maint(common): test\nBuild-bot: skip windows\n"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#!/usr/bin/env bash
|
||||
# shellcheck disable=2154 disable=1091
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
|
|
@ -25,7 +26,7 @@ test_build_bot_check_messages() {
|
|||
builder_echo start test_build_bot_check_messages 'START TEST: build_bot_check_messages'
|
||||
|
||||
# Mostly real data (only Build-bot commands edited for test)
|
||||
_do_test_build_bot_check_messages_file 14013 "[windows]=release" "$ALL_BUILD_PLATFORMS_SKIP_EXPECTED"
|
||||
_do_test_build_bot_check_messages_file 14013 "[windows]=release" "${ALL_BUILD_PLATFORMS_SKIP_EXPECTED}"
|
||||
# Simplified data for testing Build-bot command sequences only
|
||||
_do_test_build_bot_check_messages_file 9999 "[windows]=release" '[windows]="skip" [developer]="release"'
|
||||
|
||||
|
|
@ -35,17 +36,18 @@ test_build_bot_check_messages() {
|
|||
_do_test_build_bot_check_messages_inline 10 "[windows]=release" '[common]="release" [windows]="build"' '{ "body": "Build-bot: skip:windows build release:common" }' '[]'
|
||||
|
||||
# Test some bad inputs
|
||||
_do_test_build_bot_check_messages_inline 1 "[windows]=release" '[windows]="release"' '{}' '[{ "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 1 "[windows]=release" '[windows]="release"' '{}' '[{ "sha": "1234", "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
# empty body, 'foo windows' in commit
|
||||
_do_test_build_bot_check_messages_inline 2 "[windows]=release" '[windows]="release"' '{ "body": "" }' '[{ "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 2 "[windows]=release" '[windows]="release"' '{ "body": "" }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\nBuild-bot: foo windows\n" }}]'
|
||||
# attempts to escape jail
|
||||
_do_test_build_bot_check_messages_inline 3 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: '"'"'echo foo" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 4 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: echo *" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: \\0" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: `echo escaped`" }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 6 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: ' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 3 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: '"'"'echo foo" }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 4 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: echo *" }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: \\0" }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
# shellcheck disable=SC2016
|
||||
_do_test_build_bot_check_messages_inline 5 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: `echo escaped`" }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 6 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: ' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
# incomplete command
|
||||
_do_test_build_bot_check_messages_inline 7 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: " }' '[{ "commit": { "message": "maint(common): test\n" }}]'
|
||||
_do_test_build_bot_check_messages_inline 7 "[windows]=release" '[windows]="release"' '{ "body": "Build-bot: " }' '[{ "sha": "1234", "commit": { "message": "maint(common): test\n" }}]'
|
||||
|
||||
builder_echo end test_build_bot_check_messages success 'SUCCESS: build_bot_check_messages'
|
||||
}
|
||||
|
|
@ -62,13 +64,14 @@ _do_test_build_bot_check_messages_file() {
|
|||
eval "declare -gA build_platforms=($2)"
|
||||
eval "declare -A expected_build_platforms=($3)"
|
||||
|
||||
build_bot_check_messages $prnum "$(cat ${THIS_SCRIPT_PATH}/pr-$prnum-data.txt)" "$(cat ${THIS_SCRIPT_PATH}/pr-$prnum-commits.txt)"
|
||||
# shellcheck disable=2312
|
||||
build_bot_check_messages "${prnum}" "$(cat "${THIS_SCRIPT_PATH}/pr-${prnum}-data.txt")" "$(cat "${THIS_SCRIPT_PATH}/pr-${prnum}-commits.txt")"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]:-missing}" "${expected_build_platforms[$i]:-missing}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]:-missing}" "${expected_build_platforms[${i}]:-missing}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]:-missing}" "${expected_build_platforms[$i]:-missing}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]:-missing}" "${expected_build_platforms[${i}]:-missing}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -89,13 +92,13 @@ _do_test_build_bot_check_messages_inline() {
|
|||
local prinfo="$4"
|
||||
local prcommits="$5"
|
||||
|
||||
build_bot_check_messages $prnum "$prinfo" "$prcommits"
|
||||
build_bot_check_messages "${prnum}" "${prinfo}" "${prcommits}"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]:-missing}" "${expected_build_platforms[$i]:-missing}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]:-missing}" "${expected_build_platforms[${i}]:-missing}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]:-missing}" "${expected_build_platforms[$i]:-missing}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]:-missing}" "${expected_build_platforms[${i}]:-missing}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +107,11 @@ _do_test_build_bot_check_messages_inline() {
|
|||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_update_commands() {
|
||||
builder_echo start test_build_bot_update_commands 'START TEST: build_bot_update_commands'
|
||||
builder_echo start test_build_bot_update_commands 'START TEST: _build_bot_update_commands'
|
||||
|
||||
# added for #14565
|
||||
_do_test_build_bot_update_commands '[common_windows]="build" [windows]="build" [developer]="build"' "skip build:windows,developer" '[common_windows]="skip" [windows]="build" [developer]="build"'
|
||||
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip:windows" '[windows]="skip"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip windows" '[windows]="skip"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "skip:windows,developer" '[windows]="skip" [developer]="skip"'
|
||||
|
|
@ -119,8 +126,8 @@ test_build_bot_update_commands() {
|
|||
# not testing invalid legacy command: _do_test_build_bot_update_commands '[windows]="release"' "build foo" '[windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build:common" '[common]="build" [windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build common" '[common]="build" [windows]="release"'
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build:all" "$ALL_BUILD_PLATFORMS_BUILD_EXPECTED"
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build all" "$ALL_BUILD_PLATFORMS_BUILD_EXPECTED"
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build:all" "${ALL_BUILD_PLATFORMS_BUILD_EXPECTED}"
|
||||
_do_test_build_bot_update_commands '[windows]="release"' "build all" "${ALL_BUILD_PLATFORMS_BUILD_EXPECTED}"
|
||||
builder_echo end test_build_bot_update_commands success 'SUCCESS: build_bot_update_commands'
|
||||
}
|
||||
|
||||
|
|
@ -134,39 +141,42 @@ test_build_bot_update_commands() {
|
|||
#
|
||||
_do_test_build_bot_update_commands() {
|
||||
eval "declare -gA build_platforms=($1)"
|
||||
local update_command="$2"
|
||||
local update_commands="$2"
|
||||
eval "declare -A expected_build_platforms=($3)"
|
||||
|
||||
build_bot_update_commands $update_command
|
||||
# shellcheck disable=SC2086 # intentionally no quotes
|
||||
_build_bot_update_commands ${update_commands}
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]}" "${expected_build_platforms[${i}]}" "build_platforms[${i}]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]}" "${expected_build_platforms[${i}]}" "build_platforms[${i}]"
|
||||
done
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------------------------------
|
||||
|
||||
test_build_bot_verify_platforms() {
|
||||
builder_echo start test_build_bot_verify_platforms 'START TEST: build_bot_verify_platforms'
|
||||
builder_echo start test_build_bot_verify_platforms 'START TEST: _build_bot_verify_platforms'
|
||||
_do_test_build_bot_verify_platforms "windows mac" "windows mac"
|
||||
_do_test_build_bot_verify_platforms "windows foo" "windows"
|
||||
_do_test_build_bot_verify_platforms "core" "core"
|
||||
_do_test_build_bot_verify_platforms "all" "${available_platforms[*]}"
|
||||
builder_echo end test_build_bot_verify_platforms success 'SUCCESS: build_bot_verify_platforms'
|
||||
builder_echo end test_build_bot_verify_platforms success 'SUCCESS: _build_bot_verify_platforms'
|
||||
}
|
||||
|
||||
_do_test_build_bot_verify_platforms() {
|
||||
# shellcheck disable=SC2206 # intentionally no quotes
|
||||
local platforms=($1)
|
||||
# shellcheck disable=SC2206 # intentionally no quotes
|
||||
local expected_platforms=($2)
|
||||
|
||||
build_bot_verify_platforms platforms
|
||||
_build_bot_verify_platforms platforms
|
||||
|
||||
assert-equal ${#platforms[@]} ${#expected_platforms[@]} "#platforms[@]"
|
||||
for i in "${!expected_platforms[@]}"; do
|
||||
assert-equal "${platforms[$i]}" "${expected_platforms[$i]}" "platforms[$i]"
|
||||
assert-equal "${platforms[${i}]}" "${expected_platforms[${i}]}" "platforms[${i}]"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -194,13 +204,13 @@ _do_test_test_bot_check_pr_body() {
|
|||
local prinfo="$3"
|
||||
eval "declare -A expected_build_platforms=($4)"
|
||||
|
||||
test_bot_check_pr_body $prnum "$prinfo"
|
||||
test_bot_check_pr_body "${prnum}" "${prinfo}"
|
||||
|
||||
for i in "${!expected_build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]}" "${expected_build_platforms[${i}]}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
for i in "${!build_platforms[@]}"; do
|
||||
assert-equal "${build_platforms[$i]}" "${expected_build_platforms[$i]}" "PR #$prnum: build_platforms[$i]"
|
||||
assert-equal "${build_platforms[${i}]}" "${expected_build_platforms[${i}]}" "PR #${prnum}: build_platforms[${i}]"
|
||||
done
|
||||
}
|
||||
|
||||
|
|
@ -209,4 +219,4 @@ _do_test_test_bot_check_pr_body() {
|
|||
test_build_bot_verify_platforms
|
||||
test_build_bot_update_commands
|
||||
test_build_bot_check_messages
|
||||
test_test_bot_check_pr_body
|
||||
test_test_bot_check_pr_body
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ teardown_file() {
|
|||
# Discover and run all tests in the current script
|
||||
#
|
||||
# Parameters:
|
||||
# --quiet optional first parameter. If specified suppress the
|
||||
# output of the tests and save it in a logfile in
|
||||
# /tmp/<testname>.log instead.
|
||||
# [$1 [$2]] / [first [last]] - optional range of lines in the tests script to search for tests.
|
||||
# This helps to split a test file into several sections where
|
||||
# each section can have separate setup/teardown functions.
|
||||
|
|
@ -115,6 +118,13 @@ teardown_file() {
|
|||
# }
|
||||
run_tests() {
|
||||
local test_count test_fail_count line func
|
||||
if [[ "${1:-}" == "--quiet" ]]; then
|
||||
test_suppress_output=true
|
||||
builder_echo green "(test logs are in /tmp/<testname>.log)"
|
||||
shift
|
||||
else
|
||||
test_suppress_output=false
|
||||
fi
|
||||
local first="${1:-1}"
|
||||
local last="${2:-$(wc -l < "${BASH_ARGV0}")}"
|
||||
local file="${3:-${BASH_ARGV0}}"
|
||||
|
|
@ -220,9 +230,19 @@ _get_test_func() {
|
|||
echo "${name}"
|
||||
}
|
||||
|
||||
_call() {
|
||||
local func="$1"
|
||||
local testname="$2"
|
||||
if ${test_suppress_output}; then
|
||||
${func} >> "/tmp/${testname}.log" 2>&1
|
||||
else
|
||||
${func}
|
||||
fi
|
||||
}
|
||||
|
||||
_test_wrapper() {
|
||||
local func="$1"
|
||||
${func}
|
||||
_call "${func}" "${func}"
|
||||
return "${test_failures}"
|
||||
}
|
||||
|
||||
|
|
@ -235,10 +255,14 @@ _run_single_test() {
|
|||
builder_echo_debug "Running test ${func}"
|
||||
fi
|
||||
|
||||
if ${test_suppress_output}; then
|
||||
echo "" > "/tmp/${func}.log"
|
||||
fi
|
||||
|
||||
set +e
|
||||
test_failures=0
|
||||
((test_count++))
|
||||
setup
|
||||
_call setup "${func}"
|
||||
|
||||
# shellcheck disable=SC2310
|
||||
if _test_wrapper "${func}"; then
|
||||
|
|
@ -248,7 +272,7 @@ _run_single_test() {
|
|||
_failed "${func}"
|
||||
fi
|
||||
|
||||
teardown
|
||||
_call teardown "${func}"
|
||||
set -e
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,9 +186,9 @@ if [ "$action" == "commit" ]; then
|
|||
# If HISTORY.md has been updated, then we want to create a branch and push
|
||||
# it for review
|
||||
if git status --porcelain=v1 | grep -q HISTORY.md; then
|
||||
git switch -c "auto/cherry-pick-$NEWVERSION-history-to-alpha" master
|
||||
git switch -c "auto/cherry-pick-$KEYMAN_VERSION-history-to-alpha" master
|
||||
git add HISTORY.md
|
||||
git commit -m "auto: cherry-pick $NEWVERSION history to alpha
|
||||
git commit -m "auto: cherry-pick $KEYMAN_VERSION history to alpha
|
||||
|
||||
Build-bot: skip
|
||||
Test-bot: skip
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue