diff options
Diffstat (limited to 'ci')
-rwxr-xr-x | ci/check-directional-formatting.bash | 27 | ||||
-rwxr-xr-x | ci/config/allow-ref.sample | 27 | ||||
-rwxr-xr-x | ci/install-dependencies.sh | 98 | ||||
-rwxr-xr-x | ci/install-docker-dependencies.sh | 22 | ||||
-rwxr-xr-x | ci/lib.sh | 293 | ||||
-rwxr-xr-x | ci/make-test-artifacts.sh | 12 | ||||
-rwxr-xr-x | ci/mount-fileshare.sh | 25 | ||||
-rwxr-xr-x | ci/print-test-failures.sh | 88 | ||||
-rwxr-xr-x | ci/run-build-and-tests.sh | 56 | ||||
-rwxr-xr-x | ci/run-docker-build.sh | 66 | ||||
-rwxr-xr-x | ci/run-docker.sh | 47 | ||||
-rwxr-xr-x | ci/run-static-analysis.sh | 34 | ||||
-rwxr-xr-x | ci/run-test-slice.sh | 18 | ||||
-rwxr-xr-x | ci/test-documentation.sh | 45 | ||||
-rwxr-xr-x | ci/util/extract-trash-dirs.sh | 50 |
15 files changed, 908 insertions, 0 deletions
diff --git a/ci/check-directional-formatting.bash b/ci/check-directional-formatting.bash new file mode 100755 index 0000000..e6211b1 --- /dev/null +++ b/ci/check-directional-formatting.bash @@ -0,0 +1,27 @@ +#!/bin/bash + +# This script verifies that the non-binary files tracked in the Git index do +# not contain any Unicode directional formatting: such formatting could be used +# to deceive reviewers into interpreting code differently from the compiler. +# This is intended to run on an Ubuntu agent in a GitHub workflow. +# +# To allow translated messages to introduce such directional formatting in the +# future, we exclude the `.po` files from this validation. +# +# Neither GNU grep nor `git grep` (not even with `-P`) handle `\u` as a way to +# specify UTF-8. +# +# To work around that, we use `printf` to produce the pattern as a byte +# sequence, and then feed that to `git grep` as a byte sequence (setting +# `LC_CTYPE` to make sure that the arguments are interpreted as intended). +# +# Note: we need to use Bash here because its `printf` interprets `\uNNNN` as +# UTF-8 code points, as desired. Running this script through Ubuntu's `dash`, +# for example, would use a `printf` that does not understand that syntax. + +# U+202a..U+2a2e: LRE, RLE, PDF, LRO and RLO +# U+2066..U+2069: LRI, RLI, FSI and PDI +regex='(\u202a|\u202b|\u202c|\u202d|\u202e|\u2066|\u2067|\u2068|\u2069)' + +! LC_CTYPE=C git grep -El "$(LC_CTYPE=C.UTF-8 printf "$regex")" \ + -- ':(exclude,attr:binary)' ':(exclude)*.po' diff --git a/ci/config/allow-ref.sample b/ci/config/allow-ref.sample new file mode 100755 index 0000000..af0e076 --- /dev/null +++ b/ci/config/allow-ref.sample @@ -0,0 +1,27 @@ +#!/bin/sh +# +# Sample script for enabling/disabling GitHub Actions CI runs on +# particular refs. By default, CI is run for all branches pushed to +# GitHub. You can override this by dropping the ".sample" from the script, +# editing it, committing, and pushing the result to the "ci-config" branch of +# your repository: +# +# git checkout -b ci-config +# cp allow-ref.sample allow-ref +# $EDITOR allow-ref +# git add allow-ref +# git commit -am "implement my ci preferences" +# git push +# +# This script will then be run when any refs are pushed to that repository. It +# gets the fully qualified refname as the first argument, and should exit with +# success only for refs for which you want to run CI. + +case "$1" in +# allow one-off tests by pushing to "for-ci" or "for-ci/mybranch" +refs/heads/for-ci*) true ;; +# always build your integration branch +refs/heads/my-integration-branch) true ;; +# don't build any other branches or tags +*) false ;; +esac diff --git a/ci/install-dependencies.sh b/ci/install-dependencies.sh new file mode 100755 index 0000000..4f40753 --- /dev/null +++ b/ci/install-dependencies.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# Install dependencies required to build and test Git on Linux and macOS +# + +. ${0%/*}/lib.sh + +P4WHENCE=https://cdist2.perforce.com/perforce/r21.2 +LFSWHENCE=https://github.com/github/git-lfs/releases/download/v$LINUX_GIT_LFS_VERSION +UBUNTU_COMMON_PKGS="make libssl-dev libcurl4-openssl-dev libexpat-dev + tcl tk gettext zlib1g-dev perl-modules liberror-perl libauthen-sasl-perl + libemail-valid-perl libio-socket-ssl-perl libnet-smtp-ssl-perl" + +case "$runs_on_pool" in +ubuntu-*) + sudo apt-get -q update + sudo apt-get -q -y install language-pack-is libsvn-perl apache2 \ + $UBUNTU_COMMON_PKGS $CC_PACKAGE $PYTHON_PACKAGE + mkdir --parents "$P4_PATH" + pushd "$P4_PATH" + wget --quiet "$P4WHENCE/bin.linux26x86_64/p4d" + wget --quiet "$P4WHENCE/bin.linux26x86_64/p4" + chmod u+x p4d + chmod u+x p4 + popd + mkdir --parents "$GIT_LFS_PATH" + pushd "$GIT_LFS_PATH" + wget --quiet "$LFSWHENCE/git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" + tar --extract --gunzip --file "git-lfs-linux-amd64-$LINUX_GIT_LFS_VERSION.tar.gz" + cp git-lfs-$LINUX_GIT_LFS_VERSION/git-lfs . + popd + ;; +macos-*) + export HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_CLEANUP=1 + # Uncomment this if you want to run perf tests: + # brew install gnu-time + test -z "$BREW_INSTALL_PACKAGES" || + brew install $BREW_INSTALL_PACKAGES + brew link --force gettext + mkdir -p $HOME/bin + ( + cd $HOME/bin + wget -q "$P4WHENCE/bin.macosx1015x86_64/helix-core-server.tgz" && + tar -xf helix-core-server.tgz && + sudo xattr -d com.apple.quarantine p4 p4d 2>/dev/null || true + ) + PATH="$PATH:${HOME}/bin" + export PATH + + if test -n "$CC_PACKAGE" + then + BREW_PACKAGE=${CC_PACKAGE/-/@} + brew install "$BREW_PACKAGE" + brew link "$BREW_PACKAGE" + fi + ;; +esac + +case "$jobname" in +StaticAnalysis) + sudo apt-get -q update + sudo apt-get -q -y install coccinelle libcurl4-openssl-dev libssl-dev \ + libexpat-dev gettext make + ;; +sparse) + sudo apt-get -q update -q + sudo apt-get -q -y install libssl-dev libcurl4-openssl-dev \ + libexpat-dev gettext zlib1g-dev + ;; +Documentation) + sudo apt-get -q update + sudo apt-get -q -y install asciidoc xmlto docbook-xsl-ns make + + test -n "$ALREADY_HAVE_ASCIIDOCTOR" || + sudo gem install --version 1.5.8 asciidoctor + ;; +linux-gcc-default) + sudo apt-get -q update + sudo apt-get -q -y install $UBUNTU_COMMON_PKGS + ;; +esac + +if type p4d >/dev/null 2>&1 && type p4 >/dev/null 2>&1 +then + echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)" + p4d -V + echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)" + p4 -V +else + echo >&2 "WARNING: perforce wasn't installed, see above for clues why" +fi +if type git-lfs >/dev/null 2>&1 +then + echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)" + git-lfs version +else + echo >&2 "WARNING: git-lfs wasn't installed, see above for clues why" +fi diff --git a/ci/install-docker-dependencies.sh b/ci/install-docker-dependencies.sh new file mode 100755 index 0000000..78b7e32 --- /dev/null +++ b/ci/install-docker-dependencies.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# +# Install dependencies required to build and test Git inside container +# + +case "$jobname" in +linux32) + linux32 --32bit i386 sh -c ' + apt update >/dev/null && + apt install -y build-essential libcurl4-openssl-dev \ + libssl-dev libexpat-dev gettext python >/dev/null + ' + ;; +linux-musl) + apk add --update build-base curl-dev openssl-dev expat-dev gettext \ + pcre2-dev python3 musl-libintl perl-utils ncurses >/dev/null + ;; +pedantic) + dnf -yq update >/dev/null && + dnf -yq install make gcc findutils diffutils perl python3 gettext zlib-devel expat-devel openssl-devel curl-devel pcre2-devel >/dev/null + ;; +esac diff --git a/ci/lib.sh b/ci/lib.sh new file mode 100755 index 0000000..706e3ba --- /dev/null +++ b/ci/lib.sh @@ -0,0 +1,293 @@ +# Library of functions shared by all CI scripts + +if test true != "$GITHUB_ACTIONS" +then + begin_group () { :; } + end_group () { :; } + + group () { + shift + "$@" + } + set -x +else + 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 + } + trap end_group EXIT + + group () { + set +x + begin_group "$1" + shift + # 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 + return $res + } + + begin_group "CI setup" +fi + +# 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 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 +} + +# 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 () { + 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 ! 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 () { + ! 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 +} + +# 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 -n "$SYSTEM_COLLECTIONURI" || test -n "$SYSTEM_TASKDEFINITIONSURI" +then + CI_TYPE=azure-pipelines + # We are running in Azure Pipelines + CI_BRANCH="$BUILD_SOURCEBRANCH" + CI_COMMIT="$BUILD_SOURCEVERSION" + CI_JOB_ID="$BUILD_BUILDID" + CI_JOB_NUMBER="$BUILD_BUILDNUMBER" + CI_OS_NAME="$(echo "$AGENT_OS" | tr A-Z a-z)" + test darwin != "$CI_OS_NAME" || CI_OS_NAME=osx + CI_REPO_SLUG="$(expr "$BUILD_REPOSITORY_URI" : '.*/\([^/]*/[^/]*\)$')" + CC="${CC:-gcc}" + + # use a subdirectory of the cache dir (because the file share is shared + # among *all* phases) + cache_dir="$HOME/test-cache/$SYSTEM_PHASENAME" + + export GIT_PROVE_OPTS="--timer --jobs 10 --state=failed,slow,save" + export GIT_TEST_OPTS="--verbose-log -x --write-junit-xml" + MAKEFLAGS="$MAKEFLAGS --jobs=10" + test windows_nt != "$CI_OS_NAME" || + GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS" +elif 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 () { + mkdir -p t/failed-test-artifacts + echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV + + for test_exit in 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 "t/test-results/$test_name.markup" + + trash_dir="t/trash directory.$test_name" + cp "t/test-results/$test_name.out" t/failed-test-artifacts/ + tar czf t/failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir" + done + return 1 + } + + cache_dir="$HOME/none" + + export GIT_PROVE_OPTS="--timer --jobs 10" + export GIT_TEST_OPTS="--verbose-log -x --github-workflow-markup" + MAKEFLAGS="$MAKEFLAGS --jobs=10" + test windows != "$CI_OS_NAME" || + GIT_TEST_OPTS="--no-chain-lint --no-bin-wrappers $GIT_TEST_OPTS" +else + echo "Could not identify CI type" >&2 + env >&2 + exit 1 +fi + +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 "$runs_on_pool" in +ubuntu-*) + if test "$jobname" = "linux-gcc-default" + then + break + fi + + PYTHON_PACKAGE=python2 + if test "$jobname" = linux-gcc + then + 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" + + P4_PATH="$HOME/custom/p4" + GIT_LFS_PATH="$HOME/custom/git-lfs" + export PATH="$GIT_LFS_PATH:$P4_PATH:$PATH" + ;; +macos-*) + if [ "$jobname" = osx-gcc ] + then + MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python3)" + else + MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=$(which python2)" + MAKEFLAGS="$MAKEFLAGS NO_APPLE_COMMON_CRYPTO=NoThanks" + MAKEFLAGS="$MAKEFLAGS NO_OPENSSL=NoThanks" + fi + ;; +esac + +case "$jobname" in +linux32) + CC=gcc + ;; +linux-musl) + CC=gcc + MAKEFLAGS="$MAKEFLAGS PYTHON_PATH=/usr/bin/python3 USE_LIBPCRE2=Yes" + MAKEFLAGS="$MAKEFLAGS NO_REGEX=Yes ICONV_OMITS_BOM=Yes" + MAKEFLAGS="$MAKEFLAGS GIT_TEST_UTF8_LOCALE=C.UTF-8" + ;; +linux-leaks) + export SANITIZE=leak + export GIT_TEST_PASSING_SANITIZE_LEAK=true + export GIT_TEST_SANITIZE_LEAK_LOG=true + ;; +linux-asan) + export SANITIZE=address + ;; +linux-ubsan) + export SANITIZE=undefined + ;; +esac + +MAKEFLAGS="$MAKEFLAGS CC=${CC:-cc}" + +end_group +set -x diff --git a/ci/make-test-artifacts.sh b/ci/make-test-artifacts.sh new file mode 100755 index 0000000..74141af --- /dev/null +++ b/ci/make-test-artifacts.sh @@ -0,0 +1,12 @@ +#!/bin/sh +# +# Build Git and store artifacts for testing +# + +mkdir -p "$1" # in case ci/lib.sh decides to quit early + +. ${0%/*}/lib.sh + +group Build make artifacts-tar ARTIFACTS_DIRECTORY="$1" + +check_unignored_build_artifacts diff --git a/ci/mount-fileshare.sh b/ci/mount-fileshare.sh new file mode 100755 index 0000000..26b58a8 --- /dev/null +++ b/ci/mount-fileshare.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +die () { + echo "$*" >&2 + exit 1 +} + +test $# = 4 || +die "Usage: $0 <share> <username> <password> <mountpoint>" + +mkdir -p "$4" || die "Could not create $4" + +case "$(uname -s)" in +Linux) + sudo mount -t cifs -o vers=3.0,username="$2",password="$3",dir_mode=0777,file_mode=0777,serverino "$1" "$4" + ;; +Darwin) + pass="$(echo "$3" | sed -e 's/\//%2F/g' -e 's/+/%2B/g')" && + mount -t smbfs,soft "smb://$2:$pass@${1#//}" "$4" + ;; +*) + die "No support for $(uname -s)" + ;; +esac || +die "Could not mount $4" diff --git a/ci/print-test-failures.sh b/ci/print-test-failures.sh new file mode 100755 index 0000000..57277ee --- /dev/null +++ b/ci/print-test-failures.sh @@ -0,0 +1,88 @@ +#!/bin/sh +# +# Print output of failing tests +# + +. ${0%/*}/lib.sh + +# Tracing executed commands would produce too much noise in the loop below. +set +x + +cd t/ + +if ! ls test-results/*.exit >/dev/null 2>/dev/null +then + echo "Build job failed before the tests could have been run" + exit +fi + +case "$jobname" in +osx-clang|osx-gcc) + # base64 in OSX doesn't wrap its output at 76 columns by + # default, but prints a single, very long line. + base64_opts="-b 76" + ;; +esac + +combined_trash_size=0 +for TEST_EXIT in test-results/*.exit +do + if [ "$(cat "$TEST_EXIT")" != "0" ] + then + TEST_OUT="${TEST_EXIT%exit}out" + echo "------------------------------------------------------------------------" + echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)" + echo "------------------------------------------------------------------------" + cat "${TEST_OUT}" + + test_name="${TEST_EXIT%.exit}" + test_name="${test_name##*/}" + trash_dir="trash directory.$test_name" + case "$CI_TYPE" in + azure-pipelines) + mkdir -p failed-test-artifacts + mv "$trash_dir" failed-test-artifacts + continue + ;; + github-actions) + mkdir -p failed-test-artifacts + echo "FAILED_TEST_ARTIFACTS=t/failed-test-artifacts" >>$GITHUB_ENV + cp "${TEST_EXIT%.exit}.out" failed-test-artifacts/ + tar czf failed-test-artifacts/"$test_name".trash.tar.gz "$trash_dir" + continue + ;; + *) + echo "Unhandled CI type: $CI_TYPE" >&2 + exit 1 + ;; + esac + trash_tgz_b64="trash.$test_name.base64" + if [ -d "$trash_dir" ] + then + tar czp "$trash_dir" |base64 $base64_opts >"$trash_tgz_b64" + + trash_size=$(wc -c <"$trash_tgz_b64") + if [ $trash_size -gt 1048576 ] + then + # larger than 1MB + echo "$(tput setaf 1)Didn't include the trash directory of '$test_name' in the trace log, it's too big$(tput sgr0)" + continue + fi + + new_combined_trash_size=$(($combined_trash_size + $trash_size)) + if [ $new_combined_trash_size -gt 1048576 ] + then + echo "$(tput setaf 1)Didn't include the trash directory of '$test_name' in the trace log, there is plenty of trash in there already.$(tput sgr0)" + continue + fi + combined_trash_size=$new_combined_trash_size + + # DO NOT modify these two 'echo'-ed strings below + # without updating 'ci/util/extract-trash-dirs.sh' + # as well. + echo "$(tput setaf 1)Start of trash directory of '$test_name':$(tput sgr0)" + cat "$trash_tgz_b64" + echo "$(tput setaf 1)End of trash directory of '$test_name'$(tput sgr0)" + fi + fi +done diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh new file mode 100755 index 0000000..8ebff42 --- /dev/null +++ b/ci/run-build-and-tests.sh @@ -0,0 +1,56 @@ +#!/bin/sh +# +# Build and test Git +# + +. ${0%/*}/lib.sh + +case "$CI_OS_NAME" in +windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";; +*) ln -s "$cache_dir/.prove" t/.prove;; +esac + +run_tests=t + +case "$jobname" in +linux-gcc) + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main + ;; +linux-TEST-vars) + export GIT_TEST_SPLIT_INDEX=yes + export GIT_TEST_MERGE_ALGORITHM=recursive + export GIT_TEST_FULL_IN_PACK_ARRAY=true + export GIT_TEST_OE_SIZE=10 + export GIT_TEST_OE_DELTA_SIZE=5 + export GIT_TEST_COMMIT_GRAPH=1 + export GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 + export GIT_TEST_MULTI_PACK_INDEX=1 + export GIT_TEST_MULTI_PACK_INDEX_WRITE_BITMAP=1 + export GIT_TEST_ADD_I_USE_BUILTIN=0 + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master + export GIT_TEST_WRITE_REV_INDEX=1 + export GIT_TEST_CHECKOUT_WORKERS=2 + ;; +linux-clang) + export GIT_TEST_DEFAULT_HASH=sha1 + ;; +linux-sha256) + export GIT_TEST_DEFAULT_HASH=sha256 + ;; +pedantic) + # Don't run the tests; we only care about whether Git can be + # built. + export DEVOPTS=pedantic + run_tests= + ;; +esac + +group Build make +if test -n "$run_tests" +then + group "Run tests" make test || + handle_failed_tests +fi +check_unignored_build_artifacts + +save_good_tree diff --git a/ci/run-docker-build.sh b/ci/run-docker-build.sh new file mode 100755 index 0000000..6cd832e --- /dev/null +++ b/ci/run-docker-build.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# Build and test Git inside container +# +# Usage: +# run-docker-build.sh <host-user-id> +# + +set -ex + +if test $# -ne 1 || test -z "$1" +then + echo >&2 "usage: run-docker-build.sh <host-user-id>" + exit 1 +fi + +case "$jobname" in +linux32) + switch_cmd="linux32 --32bit i386" + ;; +linux-musl) + switch_cmd= + useradd () { adduser -D "$@"; } + ;; +*) + exit 1 + ;; +esac + +"${0%/*}/install-docker-dependencies.sh" + +# If this script runs inside a docker container, then all commands are +# usually executed as root. Consequently, the host user might not be +# able to access the test output files. +# If a non 0 host user id is given, then create a user "ci" with that +# user id to make everything accessible to the host user. +HOST_UID=$1 +if test $HOST_UID -eq 0 +then + # Just in case someone does want to run the test suite as root. + CI_USER=root +else + CI_USER=ci + if test "$(id -u $CI_USER 2>/dev/null)" = $HOST_UID + then + echo "user '$CI_USER' already exists with the requested ID $HOST_UID" + else + useradd -u $HOST_UID $CI_USER + fi +fi + +# Build and test +command $switch_cmd su -m -l $CI_USER -c " + set -ex + export DEVELOPER='$DEVELOPER' + export DEFAULT_TEST_TARGET='$DEFAULT_TEST_TARGET' + export GIT_PROVE_OPTS='$GIT_PROVE_OPTS' + export GIT_TEST_OPTS='$GIT_TEST_OPTS' + export GIT_TEST_CLONE_2GB='$GIT_TEST_CLONE_2GB' + export MAKEFLAGS='$MAKEFLAGS' + export cache_dir='$cache_dir' + cd /usr/src/git + test -n '$cache_dir' && ln -s '$cache_dir/.prove' t/.prove + make + make test +" diff --git a/ci/run-docker.sh b/ci/run-docker.sh new file mode 100755 index 0000000..af89d16 --- /dev/null +++ b/ci/run-docker.sh @@ -0,0 +1,47 @@ +#!/bin/sh +# +# Download and run Docker image to build and test Git +# + +. ${0%/*}/lib.sh + +case "$jobname" in +linux32) + CI_CONTAINER="daald/ubuntu32:xenial" + ;; +linux-musl) + CI_CONTAINER=alpine + ;; +*) + exit 1 + ;; +esac + +docker pull "$CI_CONTAINER" + +# Use the following command to debug the docker build locally: +# <host-user-id> must be 0 if podman is used as drop-in replacement for docker +# $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/sh "$CI_CONTAINER" +# root@container:/# export jobname=<jobname> +# root@container:/# /usr/src/git/ci/run-docker-build.sh <host-user-id> + +container_cache_dir=/tmp/container-cache + +docker run \ + --interactive \ + --env DEVELOPER \ + --env DEFAULT_TEST_TARGET \ + --env GIT_PROVE_OPTS \ + --env GIT_TEST_OPTS \ + --env GIT_TEST_CLONE_2GB \ + --env MAKEFLAGS \ + --env jobname \ + --env cache_dir="$container_cache_dir" \ + --volume "${PWD}:/usr/src/git" \ + --volume "$cache_dir:$container_cache_dir" \ + "$CI_CONTAINER" \ + /usr/src/git/ci/run-docker-build.sh $(id -u $USER) + +check_unignored_build_artifacts + +save_good_tree diff --git a/ci/run-static-analysis.sh b/ci/run-static-analysis.sh new file mode 100755 index 0000000..0d51e5c --- /dev/null +++ b/ci/run-static-analysis.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# +# Perform various static code analysis checks +# + +. ${0%/*}/lib.sh + +make coccicheck + +set +x + +fail= +for cocci_patch in contrib/coccinelle/*.patch +do + if test -s "$cocci_patch" + then + echo "$(tput setaf 1)Coccinelle suggests the following changes in '$cocci_patch':$(tput sgr0)" + cat "$cocci_patch" + fail=UnfortunatelyYes + fi +done + +if test -n "$fail" +then + echo "$(tput setaf 1)error: Coccinelle suggested some changes$(tput sgr0)" + exit 1 +fi + +make hdr-check || +exit 1 + +make check-pot + +save_good_tree diff --git a/ci/run-test-slice.sh b/ci/run-test-slice.sh new file mode 100755 index 0000000..a3c6795 --- /dev/null +++ b/ci/run-test-slice.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# +# Test Git in parallel +# + +. ${0%/*}/lib.sh + +case "$CI_OS_NAME" in +windows*) cmd //c mklink //j t\\.prove "$(cygpath -aw "$cache_dir/.prove")";; +*) ln -s "$cache_dir/.prove" t/.prove;; +esac + +group "Run tests" make --quiet -C t T="$(cd t && + ./helper/test-tool path-utils slice-tests "$1" "$2" t[0-9]*.sh | + tr '\n' ' ')" || +handle_failed_tests + +check_unignored_build_artifacts diff --git a/ci/test-documentation.sh b/ci/test-documentation.sh new file mode 100755 index 0000000..de41888 --- /dev/null +++ b/ci/test-documentation.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# +# Perform sanity checks on documentation and build it. +# + +. ${0%/*}/lib.sh + +filter_log () { + sed -e '/^GIT_VERSION = /d' \ + -e "/constant Gem::ConfigMap is deprecated/d" \ + -e '/^ \* new asciidoc flags$/d' \ + -e '/stripped namespace before processing/d' \ + -e '/Attributed.*IDs for element/d' \ + "$1" +} + +make check-builtins +make check-docs + +# Build docs with AsciiDoc +make doc > >(tee stdout.log) 2> >(tee stderr.raw >&2) +cat stderr.raw +filter_log stderr.raw >stderr.log +test ! -s stderr.log +test -s Documentation/git.html +test -s Documentation/git.xml +test -s Documentation/git.1 +grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html + +rm -f stdout.log stderr.log stderr.raw +check_unignored_build_artifacts + +# Build docs with AsciiDoctor +make clean +make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.raw >&2) +cat stderr.raw +filter_log stderr.raw >stderr.log +test ! -s stderr.log +test -s Documentation/git.html +grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html + +rm -f stdout.log stderr.log stderr.raw +check_unignored_build_artifacts + +save_good_tree diff --git a/ci/util/extract-trash-dirs.sh b/ci/util/extract-trash-dirs.sh new file mode 100755 index 0000000..8e67bec --- /dev/null +++ b/ci/util/extract-trash-dirs.sh @@ -0,0 +1,50 @@ +#!/bin/sh + +error () { + echo >&2 "error: $@" + exit 1 +} + +find_embedded_trash () { + while read -r line + do + case "$line" in + *Start\ of\ trash\ directory\ of\ \'t[0-9][0-9][0-9][0-9]-*\':*) + test_name="${line#*\'}" + test_name="${test_name%\'*}" + + return 0 + esac + done + + return 1 +} + +extract_embedded_trash () { + while read -r line + do + case "$line" in + *End\ of\ trash\ directory\ of\ \'$test_name\'*) + return + ;; + *) + printf '%s\n' "$line" + ;; + esac + done + + error "unexpected end of input" +} + +# Raw logs from Linux build jobs have CRLF line endings, while OSX +# build jobs mostly have CRCRLF, except an odd line every now and +# then that has CRCRCRLF. 'base64 -d' from 'coreutils' doesn't like +# CRs and complains about "invalid input", so remove all CRs at the +# end of lines. +sed -e 's/\r*$//' | \ +while find_embedded_trash +do + echo "Extracting trash directory of '$test_name'" + + extract_embedded_trash |base64 -d |tar xzp +done |