mirror of
https://github.com/git/git.git
synced 2026-03-04 14:37:35 +01:00
We have started to see the following assert happen in our GitLab CI
pipelines for jobs that use Windows with Meson:
assertion "bc_ctl.arg_max >= LINE_MAX" failed: file "xargs.c", line 512, function: main
The assert in question verifies that we have enough room available to
pass at least `LINE_MAX` many bytes via the command line. The xargs(1)
binary in those jobs comes from Git for Windows, which in turn sources
the binaries from MSYS2, and has the following limits in place:
$ & "C:/Program Files/Git/usr/bin/bash.exe" -l -c 'xargs --show-limits </dev/null'
Your environment variables take up 17373 bytes
POSIX upper limit on argument length (this system): 12579
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 18446744073709546822
Size of command buffer we are actually using: 12579
Maximum parallelism (--max-procs must be no greater): 2147483647
What's interesting to see is the limit of 16 exabits for the maximum
command line length. This value might seem a bit high, and it is indeed
the result of an underflow: our environment is larger than the POSIX
upper limit on argument length, and the value is computed by subtracting
the former from the latter. So what we get is the result of `2^64 -
(17373 - 12579)`.
This makes it clear that the problem here is the size of our environment
variables. A listing sorted by length yields the following result:
$ Get-ChildItem "Env:" |
Sort-Object { $_.Value.Length } -Descending |
Select-Object Name, @{Name="Length"; Expression={$_.Value.Length}}
Name Length
---- ------
GITLAB_FEATURES 6386
Path 706
PSModulePath 229
The GITLAB_FEATURES environment variable makes up for roughly a third of
the complete environment. This variable is a comma-separated list of
features available for the GitLab instance, and seemingly it has been
growing over time as GitLab added more and more features.
Fix the issue by unsetting the environment variable in "ci/lib.sh". This
ensures that the environment variables are now smaller than the upper
limit on argument length again, and that in turn fixes the assert in
xargs(1).
Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
381 lines
9.4 KiB
Bash
Executable File
381 lines
9.4 KiB
Bash
Executable File
# Library of functions shared by all CI scripts
|
|
|
|
if test true = "$GITHUB_ACTIONS"
|
|
then
|
|
begin_group () {
|
|
need_to_end_group=t
|
|
echo "::group::$1" >&2
|
|
set -x
|
|
}
|
|
|
|
end_group () {
|
|
test -n "$need_to_end_group" || return 0
|
|
set +x
|
|
need_to_end_group=
|
|
echo '::endgroup::' >&2
|
|
}
|
|
elif test true = "$GITLAB_CI"
|
|
then
|
|
begin_group () {
|
|
need_to_end_group=t
|
|
printf '\e[0Ksection_start:%s:%s[collapsed=true]\r\e[0K%s\n' \
|
|
"$(date +%s)" "$(echo "$1" | tr ' ' _)" "$1"
|
|
trap "end_group '$1'" EXIT
|
|
set -x
|
|
}
|
|
|
|
end_group () {
|
|
test -n "$need_to_end_group" || return 0
|
|
set +x
|
|
need_to_end_group=
|
|
printf '\e[0Ksection_end:%s:%s\r\e[0K\n' \
|
|
"$(date +%s)" "$(echo "$1" | tr ' ' _)"
|
|
trap - EXIT
|
|
}
|
|
else
|
|
begin_group () { :; }
|
|
end_group () { :; }
|
|
|
|
set -x
|
|
fi
|
|
|
|
group () {
|
|
group="$1"
|
|
shift
|
|
begin_group "$group"
|
|
|
|
# work around `dash` not supporting `set -o pipefail`
|
|
(
|
|
"$@" 2>&1
|
|
echo $? >exit.status
|
|
) |
|
|
sed 's/^\(\([^ ]*\):\([0-9]*\):\([0-9]*:\) \)\(error\|warning\): /::\5 file=\2,line=\3::\1/'
|
|
res=$(cat exit.status)
|
|
rm exit.status
|
|
|
|
end_group "$group"
|
|
return $res
|
|
}
|
|
|
|
begin_group "CI setup via $(basename $0)"
|
|
|
|
# Set 'exit on error' for all CI scripts to let the caller know that
|
|
# something went wrong.
|
|
#
|
|
# We already enabled tracing executed commands earlier. This helps by showing
|
|
# how # environment variables are set and dependencies are installed.
|
|
set -e
|
|
|
|
skip_branch_tip_with_tag () {
|
|
# Sometimes, a branch is pushed at the same time the tag that points
|
|
# at the same commit as the tip of the branch is pushed, and building
|
|
# both at the same time is a waste.
|
|
#
|
|
# When the build is triggered by a push to a tag, $CI_BRANCH will
|
|
# have that tagname, e.g. v2.14.0. Let's see if $CI_BRANCH is
|
|
# exactly at a tag, and if so, if it is different from $CI_BRANCH.
|
|
# That way, we can tell if we are building the tip of a branch that
|
|
# is tagged and we can skip the build because we won't be skipping a
|
|
# build of a tag.
|
|
|
|
if TAG=$(git describe --exact-match "$CI_BRANCH" 2>/dev/null) &&
|
|
test "$TAG" != "$CI_BRANCH"
|
|
then
|
|
echo "$(tput setaf 2)Tip of $CI_BRANCH is exactly at $TAG$(tput sgr0)"
|
|
exit 0
|
|
fi
|
|
}
|
|
|
|
# Check whether we can use the path passed via the first argument as Git
|
|
# repository.
|
|
is_usable_git_repository () {
|
|
# We require Git in our PATH, otherwise we cannot access repositories
|
|
# at all.
|
|
if ! command -v git >/dev/null
|
|
then
|
|
return 1
|
|
fi
|
|
|
|
# And the target directory needs to be a proper Git repository.
|
|
if ! git -C "$1" rev-parse 2>/dev/null
|
|
then
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Save some info about the current commit's tree, so we can skip the build
|
|
# job if we encounter the same tree again and can provide a useful info
|
|
# message.
|
|
save_good_tree () {
|
|
if ! is_usable_git_repository .
|
|
then
|
|
return
|
|
fi
|
|
|
|
echo "$(git rev-parse $CI_COMMIT^{tree}) $CI_COMMIT $CI_JOB_NUMBER $CI_JOB_ID" >>"$good_trees_file"
|
|
# limit the file size
|
|
tail -1000 "$good_trees_file" >"$good_trees_file".tmp
|
|
mv "$good_trees_file".tmp "$good_trees_file"
|
|
}
|
|
|
|
# Skip the build job if the same tree has already been built and tested
|
|
# successfully before (e.g. because the branch got rebased, changing only
|
|
# the commit messages).
|
|
skip_good_tree () {
|
|
if test true = "$GITHUB_ACTIONS"
|
|
then
|
|
return
|
|
fi
|
|
|
|
if ! is_usable_git_repository .
|
|
then
|
|
return
|
|
fi
|
|
|
|
if ! good_tree_info="$(grep "^$(git rev-parse $CI_COMMIT^{tree}) " "$good_trees_file")"
|
|
then
|
|
# Haven't seen this tree yet, or no cached good trees file yet.
|
|
# Continue the build job.
|
|
return
|
|
fi
|
|
|
|
echo "$good_tree_info" | {
|
|
read tree prev_good_commit prev_good_job_number prev_good_job_id
|
|
|
|
if test "$CI_JOB_ID" = "$prev_good_job_id"
|
|
then
|
|
cat <<-EOF
|
|
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
|
|
This commit has already been built and tested successfully by this build job.
|
|
To force a re-build delete the branch's cache and then hit 'Restart job'.
|
|
EOF
|
|
else
|
|
cat <<-EOF
|
|
$(tput setaf 2)Skipping build job for commit $CI_COMMIT.$(tput sgr0)
|
|
This commit's tree has already been built and tested successfully in build job $prev_good_job_number for commit $prev_good_commit.
|
|
The log of that build job is available at $SYSTEM_TASKDEFINITIONSURI$SYSTEM_TEAMPROJECT/_build/results?buildId=$prev_good_job_id
|
|
To force a re-build delete the branch's cache and then hit 'Restart job'.
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
exit 0
|
|
}
|
|
|
|
check_unignored_build_artifacts () {
|
|
if ! is_usable_git_repository .
|
|
then
|
|
return
|
|
fi
|
|
|
|
! git ls-files --other --exclude-standard --error-unmatch \
|
|
-- ':/*' 2>/dev/null ||
|
|
{
|
|
echo "$(tput setaf 1)error: found unignored build artifacts$(tput sgr0)"
|
|
false
|
|
}
|
|
}
|
|
|
|
handle_failed_tests () {
|
|
return 1
|
|
}
|
|
|
|
create_failed_test_artifacts () {
|
|
mkdir -p "${TEST_OUTPUT_DIRECTORY:-t}"/failed-test-artifacts
|
|
|
|
for test_exit in "${TEST_OUTPUT_DIRECTORY:-t}"/test-results/*.exit
|
|
do
|
|
test 0 != "$(cat "$test_exit")" || continue
|
|
|
|
test_name="${test_exit%.exit}"
|
|
test_name="${test_name##*/}"
|
|
printf "\\e[33m\\e[1m=== Failed test: ${test_name} ===\\e[m\\n"
|
|
echo "The full logs are in the 'print test failures' step below."
|
|
echo "See also the 'failed-tests-*' artifacts attached to this run."
|
|
cat "${TEST_OUTPUT_DIRECTORY:-t}/test-results/$test_name.markup"
|
|
|
|
trash_dir="${TEST_OUTPUT_DIRECTORY:-t}/trash directory.$test_name"
|
|
cp "${TEST_OUTPUT_DIRECTORY:-t}/test-results/$test_name.out" "${TEST_OUTPUT_DIRECTORY:-t}"/failed-test-artifacts/
|
|
tar czf "${TEST_OUTPUT_DIRECTORY:-t}/failed-test-artifacts/$test_name.trash.tar.gz" "$trash_dir"
|
|
done
|
|
}
|
|
|
|
# GitHub Action doesn't set TERM, which is required by tput
|
|
export TERM=${TERM:-dumb}
|
|
|
|
# Clear MAKEFLAGS that may come from the outside world.
|
|
export MAKEFLAGS=
|
|
|
|
if test true = "$GITHUB_ACTIONS"
|
|
then
|
|
CI_TYPE=github-actions
|
|
CI_BRANCH="$GITHUB_REF"
|
|
CI_COMMIT="$GITHUB_SHA"
|
|
CI_OS_NAME="$(echo "$RUNNER_OS" | tr A-Z a-z)"
|
|
test macos != "$CI_OS_NAME" || CI_OS_NAME=osx
|
|
CI_REPO_SLUG="$GITHUB_REPOSITORY"
|
|
CI_JOB_ID="$GITHUB_RUN_ID"
|
|
CC="${CC_PACKAGE:-${CC:-gcc}}"
|
|
DONT_SKIP_TAGS=t
|
|
handle_failed_tests () {
|
|
echo "FAILED_TEST_ARTIFACTS=${TEST_OUTPUT_DIRECTORY:-t}/failed-test-artifacts" >>$GITHUB_ENV
|
|
create_failed_test_artifacts
|
|
return 1
|
|
}
|
|
|
|
cache_dir="$HOME/none"
|
|
|
|
GIT_TEST_OPTS="--github-workflow-markup"
|
|
JOBS=10
|
|
|
|
distro=$(echo "$CI_JOB_IMAGE" | tr : -)
|
|
elif test true = "$GITLAB_CI"
|
|
then
|
|
# This environment is multiple kB in size and may cause us to exceed
|
|
# xargs(1) limits on Windows.
|
|
unset GITLAB_FEATURES
|
|
|
|
CI_TYPE=gitlab-ci
|
|
CI_BRANCH="$CI_COMMIT_REF_NAME"
|
|
CI_COMMIT="$CI_COMMIT_SHA"
|
|
|
|
case "$OS,$CI_JOB_IMAGE" in
|
|
Windows_NT,*)
|
|
CI_OS_NAME=windows
|
|
JOBS=$NUMBER_OF_PROCESSORS
|
|
;;
|
|
*,macos-*)
|
|
# GitLab CI has Python installed via multiple package managers,
|
|
# most notably via asdf and Homebrew. Ensure that our builds
|
|
# pick up the Homebrew one by prepending it to our PATH as the
|
|
# asdf one breaks tests.
|
|
export PATH="$(brew --prefix)/bin:$PATH"
|
|
|
|
CI_OS_NAME=osx
|
|
JOBS=$(nproc)
|
|
;;
|
|
*,alpine:*|*,fedora:*|*,ubuntu:*|*,i386/ubuntu:*)
|
|
CI_OS_NAME=linux
|
|
JOBS=$(nproc)
|
|
;;
|
|
*)
|
|
echo "Could not identify OS image" >&2
|
|
env >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
CI_REPO_SLUG="$CI_PROJECT_PATH"
|
|
CI_JOB_ID="$CI_JOB_ID"
|
|
CC="${CC_PACKAGE:-${CC:-gcc}}"
|
|
DONT_SKIP_TAGS=t
|
|
|
|
handle_failed_tests () {
|
|
create_failed_test_artifacts
|
|
return 1
|
|
}
|
|
|
|
cache_dir="$HOME/none"
|
|
|
|
distro=$(echo "$CI_JOB_IMAGE" | tr : -)
|
|
else
|
|
echo "Could not identify CI type" >&2
|
|
env >&2
|
|
exit 1
|
|
fi
|
|
|
|
MAKEFLAGS="$MAKEFLAGS --jobs=$JOBS"
|
|
GIT_PROVE_OPTS="--timer --jobs $JOBS"
|
|
|
|
GIT_TEST_OPTS="$GIT_TEST_OPTS --verbose-log -x"
|
|
case "$CI_OS_NAME" in
|
|
windows|windows_nt)
|
|
GIT_TEST_OPTS="$GIT_TEST_OPTS --no-chain-lint --no-bin-wrappers"
|
|
;;
|
|
esac
|
|
|
|
export GIT_TEST_OPTS
|
|
export GIT_PROVE_OPTS
|
|
|
|
good_trees_file="$cache_dir/good-trees"
|
|
|
|
mkdir -p "$cache_dir"
|
|
|
|
test -n "${DONT_SKIP_TAGS-}" ||
|
|
skip_branch_tip_with_tag
|
|
skip_good_tree
|
|
|
|
if test -z "$jobname"
|
|
then
|
|
jobname="$CI_OS_NAME-$CC"
|
|
fi
|
|
|
|
export DEVELOPER=1
|
|
export DEFAULT_TEST_TARGET=prove
|
|
export GIT_TEST_CLONE_2GB=true
|
|
export SKIP_DASHED_BUILT_INS=YesPlease
|
|
|
|
case "$distro" in
|
|
ubuntu-*)
|
|
# Python 2 is end of life, and Ubuntu 23.04 and newer don't actually
|
|
# have it anymore. We thus only test with Python 2 on older LTS
|
|
# releases.
|
|
if test "$distro" = "ubuntu-20.04"
|
|
then
|
|
PYTHON_PACKAGE=python2
|
|
else
|
|
PYTHON_PACKAGE=python3
|
|
fi
|
|
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/$PYTHON_PACKAGE"
|
|
|
|
export GIT_TEST_HTTPD=true
|
|
|
|
# The Linux build installs the defined dependency versions below.
|
|
# The OS X build installs much more recent versions, whichever
|
|
# were recorded in the Homebrew database upon creating the OS X
|
|
# image.
|
|
# Keep that in mind when you encounter a broken OS X build!
|
|
export LINUX_GIT_LFS_VERSION="1.5.2"
|
|
;;
|
|
macos-*)
|
|
MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)"
|
|
if [ "$jobname" != osx-gcc ]
|
|
then
|
|
MAKEFLAGS="$MAKEFLAGS APPLE_COMMON_CRYPTO_SHA1=Yes"
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
CUSTOM_PATH="${CUSTOM_PATH:-$HOME/path}"
|
|
export PATH="$CUSTOM_PATH:$PATH"
|
|
|
|
case "$jobname" in
|
|
linux32)
|
|
CC=gcc
|
|
;;
|
|
linux-meson)
|
|
MESONFLAGS="$MESONFLAGS -Dcredential_helpers=libsecret,netrc"
|
|
;;
|
|
linux-musl-meson)
|
|
MESONFLAGS="$MESONFLAGS -Dtest_utf8_locale=C.UTF-8"
|
|
;;
|
|
linux-leaks|linux-reftable-leaks)
|
|
export SANITIZE=leak
|
|
export NO_CVS_TESTS=LetsSaveSomeTime
|
|
export NO_SVN_TESTS=LetsSaveSomeTime
|
|
export NO_P4_TESTS=LetsSaveSomeTime
|
|
;;
|
|
linux-asan-ubsan)
|
|
export SANITIZE=address,undefined
|
|
export NO_SVN_TESTS=LetsSaveSomeTime
|
|
MAKEFLAGS="$MAKEFLAGS NO_PYTHON=YepBecauseP4FlakesTooOften"
|
|
;;
|
|
osx-meson)
|
|
MESONFLAGS="$MESONFLAGS -Dcredential_helpers=osxkeychain"
|
|
;;
|
|
esac
|
|
|
|
MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}"
|
|
|
|
end_group "CI setup via $(basename $0)"
|
|
set -x
|