This commit is contained in:
mck 2026-07-31 14:32:31 +08:00 committed by GitHub
commit 55c96670b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 307 additions and 364 deletions

View File

@ -17,6 +17,25 @@
# temporary between CASSANDRA-18133 and CASSANDRA-18594
print_help() {
echo "Usage: $0 [-c|--clean] [-s|--summary] [-h|--help]"
echo " -c, --clean Remove locally created artifacts (ant clean) before building"
echo " -s, --summary Print a summary of failures instead of the full ant output"
echo " -h, --help Print help"
}
# arguments, with defaults
clean=false
summary=false
while [ "$#" -gt 0 ]; do
case "$1" in
-c|--clean) clean=true; shift ;;
-s|--summary) summary=true; shift ;;
-h|--help) print_help; exit 0 ;;
*) echo >&2 "Unknown argument $1"; print_help >&2; exit 1 ;;
esac
done
# variables, with defaults
[ "x${CASSANDRA_DIR}" != "x" ] || CASSANDRA_DIR="$(readlink -f $(dirname -- "$0")/..)"
@ -25,6 +44,18 @@ command -v ant >/dev/null 2>&1 || { echo >&2 "ant needs to be installed"; exit 1
[ -d "${CASSANDRA_DIR}" ] || { echo >&2 "Directory ${CASSANDRA_DIR} must exist"; exit 1; }
[ -f "${CASSANDRA_DIR}/build.xml" ] || { echo >&2 "${CASSANDRA_DIR}/build.xml must exist"; exit 1; }
# run ant, summarizing failures when --summary (summary exit code mirrors the build)
run_ant() {
if ${summary}; then
ant -f "${CASSANDRA_DIR}/build.xml" "$@" 2>&1 | "${CASSANDRA_DIR}/.build/sh/ant-log-summary.py" -
else
ant -f "${CASSANDRA_DIR}/build.xml" "$@"
fi
}
# execute
ant -f "${CASSANDRA_DIR}/build.xml" jar
if ${clean}; then
run_ant clean
fi
run_ant jar
exit $?

View File

@ -15,6 +15,22 @@
# See the License for the specific language governing permissions and
# limitations under the License.
print_help() {
echo "Usage: $0 [-s|--summary] [-h|--help]"
echo " -s, --summary Print a summary of failures instead of the full ant output"
echo " -h, --help Print help"
}
# arguments, with defaults
summary=false
while [ "$#" -gt 0 ]; do
case "$1" in
-s|--summary) summary=true; shift ;;
-h|--help) print_help; exit 0 ;;
*) echo >&2 "Unknown argument $1"; print_help >&2; exit 1 ;;
esac
done
# variables, with defaults
[ "x${CASSANDRA_DIR}" != "x" ] || { CASSANDRA_DIR="$(dirname -- "$0")/.."; }
@ -24,5 +40,12 @@ command -v ant >/dev/null 2>&1 || { echo >&2 "ant needs to be installed"; exit 1
[ -f "${CASSANDRA_DIR}/build.xml" ] || { echo >&2 "${CASSANDRA_DIR}/build.xml must exist"; exit 1; }
# execute. memory needs to fit within the specified container size, see .jenkins/Jenkinsfile
ANT_OPTS="-Xmx2g -XX:+PrintClassHistogram -XX:OnOutOfMemoryError='kill -QUIT %p'" ant -f "${CASSANDRA_DIR}/build.xml" check # dependency-check # FIXME dependency-check now requires NVD key downloaded first
# dependency-check # FIXME dependency-check now requires NVD key downloaded first
export ANT_OPTS="-Xmx2g -XX:+PrintClassHistogram -XX:OnOutOfMemoryError='kill -QUIT %p'"
if ${summary}; then
# summarize failures; the summary's exit code mirrors the build
ant -f "${CASSANDRA_DIR}/build.xml" check 2>&1 | "${CASSANDRA_DIR}/.build/sh/ant-log-summary.py" -
else
ant -f "${CASSANDRA_DIR}/build.xml" check
fi
exit $?

View File

@ -31,7 +31,13 @@ set -o pipefail
# target types
TARGET_TYPES="build_dtest_jars stress-test fqltool-test sstableloader-test microbench microbench-test test-burn long-test cqlsh-test simulator-dtest test test-cdc test-compression test-oa test-system-keyspace-directory test-latest jvm-dtest jvm-dtest-upgrade jvm-dtest-novnode jvm-dtest-upgrade-novnode"
# pre-conditions
error() {
echo >&2 $2;
set -x
exit $1
}
# pre-conditions (error() must be defined above)
command -v ant >/dev/null 2>&1 || { error 1 "ant needs to be installed"; }
command -v git >/dev/null 2>&1 || { error 1 "git needs to be installed"; }
command -v uuidgen >/dev/null 2>&1 || test -f /proc/sys/kernel/random/uuid || { error 1 "uuidgen needs to be installed"; }
@ -39,13 +45,6 @@ command -v uuidgen >/dev/null 2>&1 || test -f /proc/sys/kernel/random/uuid || {
[ -f "${CASSANDRA_DIR}/build.xml" ] || { error 1 "${CASSANDRA_DIR}/build.xml must exist"; }
[ -d "${DIST_DIR}" ] || { mkdir -p "${DIST_DIR}" ; }
error() {
echo >&2 $2;
set -x
exit $1
}
print_help() {
echo "Usage: $0 [-a|-t|-c|-e|-i|-b|-s|-h]"
echo " -a Test target type: ${TARGET_TYPES}"
@ -54,13 +53,12 @@ print_help() {
echo " -b Specify the base git branch for comparison when determining changed tests to"
echo " repeat. Defaults to ${BASE_BRANCH}. Note that this option is not used when"
echo " the '-a' option is specified."
echo " -s Skip automatic detection of changed tests. Useful when you need to repeat a few ones,"
echo " or when there are too many changed tests the CI env to handle."
echo " -e <key=value> Environment variables to be used in the repeated runs:"
echo " -e REPEATED_TESTS_STOP_ON_FAILURE=false"
echo " -e REPEATED_TESTS_COUNT=500"
echo " If you want to specify multiple environment variables simply add multiple -e options."
echo " -i Ignore unknown environment variables"
echo " -s Print a summary of failed tests instead of the full ant output"
echo " -h Print help"
}
@ -84,7 +82,8 @@ fi
env_vars=""
has_env_vars=false
check_env_vars=true
detect_changed_tests=true
summary=false
while getopts "a:t:c:e:ib:shj:" opt; do
case $opt in
a ) test_target="$OPTARG"
@ -105,7 +104,7 @@ while getopts "a:t:c:e:ib:shj:" opt; do
;;
i ) check_env_vars=false
;;
s ) detect_changed_tests=false
s ) summary=true
;;
h ) print_help
exit 0
@ -331,7 +330,15 @@ _run_microbench() {
local -r arch="$(uname -m)"
local -r output_dir="${DIST_DIR}/test/output/${_target}/jdk${java_version}/${arch}/${_split_chunk//\//_}"
# microbench produces JMH json, not JUnit xml, so generate-test-report has
# nothing to summarise. Emit a "failed ..." line (matched by
# test-log-summary.py) so a benchmark failure is not reported as a pass in
# -s mode, where errexit is disabled and the ant failure would be swallowed.
set +o errexit
ant $_target ${ANT_TEST_OPTS} -Dbuild.test.output.dir=${output_dir} -Dbenchmark.name="${benchmark_pattern}" -Dmaven.test.failure.ignore=true
local -r _ant_status=$?
set -o errexit
[[ ${_ant_status} -ne 0 ]] && echo "failed ${_target} ${_split_chunk}"
# Post-process jmh-result.json to add jdk and arch parameters
local jmh_result="${output_dir}/jmh-result.json"
@ -357,7 +364,7 @@ _main() {
local -r split_chunk="${chunk:-1/1}" # Chunks formatted as "K/N" for the Kth chunk of N chunks
# check split_chunk is compatible with target (if not a regexp)
if [[ "${_split_chunk}" =~ ^\d+/\d+$ ]] && [[ "1/1" != "${split_chunk}" ]] ; then
if [[ "${split_chunk}" =~ ^[0-9]+/[0-9]+$ ]] && [[ "1/1" != "${split_chunk}" ]] ; then
case ${target} in
"stress-test" | "fqltool-test" | "cqlsh-test" | "sstableloader-test")
echo "Target ${target} does not support splits."
@ -401,8 +408,12 @@ _main() {
[ -d ${TMP_DIR} ] || mkdir -p "${TMP_DIR}"
export ANT_TEST_OPTS="-Dno-build-test=true -Dtmp.dir=${TMP_DIR} -Dbuild.test.output.dir=${DIST_DIR}/test/output/${target}"
# fresh virtualenv and test logs results everytime
[[ "/" == "${DIST_DIR}" ]] || rm -rf "${DIST_DIR}/test/{html,output,logs,reports}"
# fresh virtualenv and test logs results everytime.
# NB: braces are intentionally unquoted so brace-expansion produces the four
# paths; quoting them deletes a single literal '{html,output,logs,reports}'
# path (a no-op), leaving stale TEST-*.xml that generate-test-report would
# then merge into the summary.
[[ "/" == "${DIST_DIR}" ]] || rm -rf ${DIST_DIR}/test/{html,output,logs,reports}
# cheap trick to ensure dependency libraries are in place. allows us to stash only project specific build artifacts.
# also recreate some of the non-build files we need
@ -492,10 +503,30 @@ _main() {
;;
esac
# merge all unit xml files into one, and print summary test numbers
ant -quiet -silent generate-test-report
# merge all unit xml files into one, and print summary test numbers.
# Pin the output/report dirs to DIST_DIR; otherwise the report scans ant's
# default ${basedir}/build/test/output and, when DIST_DIR is overridden,
# finds no results and reports a false pass.
ant -quiet -silent generate-test-report \
-Dbuild.test.output.dir="${DIST_DIR}/test/output" \
-Dbuild.test.report.dir="${DIST_DIR}/test/reports"
popd >/dev/null
}
_main "$@"
if ${summary}; then
# Summarize the run: run-tests.sh keeps going past failing tests (ant still
# prints BUILD SUCCESSFUL), so the summary's exit code — derived from the
# test results — is what determines pass/fail here. stderr (where error()
# writes) is left untouched so setup failures stay visible.
# Disable errexit/pipefail so a non-zero _main doesn't pre-empt the summary.
set +o errexit +o pipefail
_main "$@" | "${CASSANDRA_DIR}/.build/sh/test-log-summary.py" -
main_status=${PIPESTATUS[0]}
summary_status=${PIPESTATUS[1]}
# a setup failure inside _main (error() exits non-zero) takes precedence
[[ ${main_status} -ne 0 ]] && exit ${main_status}
exit ${summary_status}
else
_main "$@"
fi

View File

@ -1,70 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#set -o xtrace
set -o errexit
set -o pipefail
set -o nounset
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
home="$(cd "$bin/../.." > /dev/null; pwd)" # this script lives in .build/sh
usage() {
if [[ $# -gt 0 ]]; then
echo "$*" 1>&2
fi
cat <<EOF
Usage: $(basename "$0") (options)*
Options:
--no-checkstyle - runs with all checkstyle disabled
-h|--help - this help page
EOF
exit 1
}
no_checkstyle=false
while [[ "$#" -gt 0 ]]; do
case "$1" in
--no-checkstyle)
no_checkstyle=true
shift
;;
-h|--help)
usage
shift
;;
*)
usage "Unknown argument $1"
;;
esac
done
# Switch to the project root so all commands work properly
cd "$home"
opts=(
-Dant.gen-doc.skip=true
-Drat.skip=true
)
if [ $no_checkstyle ]; then
ant -Dno-checkstyle=true "${opts[@]}" clean jar "$@" 2>&1 | "$bin"/ant-log-summary.py -
else
ant "${opts[@]}" clean jar checkstyle checkstyle-test "$@" 2>&1 | "$bin"/ant-log-summary.py -
fi

View File

@ -1,29 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#set -o xtrace
set -o errexit
set -o pipefail
set -o nounset
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
home="$(cd "$bin/../.." > /dev/null; pwd)" # this script lives in .build/sh
# Switch to the project root so all commands work properly
cd "$home"
"$bin"/ci-test "$@" 2>&1 | "$bin"/ant-log-summary.py -

View File

@ -1,149 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#set -o xtrace
set -o errexit
set -o pipefail
set -o nounset
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
home="$(cd "$bin/../.." > /dev/null; pwd)" # this script lives in .build/sh
# Switch to the project root so all commands work properly
cd "$home"
error() {
echo "ERROR: $*" 1>&2
exit 1
}
usage() {
if [[ $# -gt 0 ]]; then
echo "$*" 1>&2
fi
cat <<EOF
Usage: $(basename "$0") [class name] (options)*
Options:
--cdc - runs with cdc enabled
--compression - run with compression enabled
--jvm-dtest-version|--dtest-version - which jvm-dtest version to use
--reuse - skip calling ant realclean
--tokens|--num-tokens - number of vnode tokens to use for jvm-dtest
-h|--help - this help page
EOF
exit 1
}
if [[ $# -eq 0 ]]; then
usage "Missing required arguments"
fi
class_name=""
task="testclasslist"
fresh_build=true
jvm_dtest_version=""
num_tokens=""
while [[ "$#" -gt 0 ]]; do
case "$1" in
--tokens|--num-tokens)
num_tokens="$2"
shift 2
;;
--cdc)
task="testclasslist-cdc"
shift
;;
--compression)
task="testclasslist-compression"
shift
;;
--reuse)
fresh_build=false
shift
;;
--jvm-dtest-version|--dtest-version)
jvm_dtest_version="$2"
shift 2
;;
-h|--help)
usage
shift
;;
*)
if [[ -z "${class_name:-}" ]]; then
class_name="$1"
shift
else
usage "Unknown argument $1"
fi
;;
esac
done
if [[ -z "${class_name:-}" ]]; then
usage "No class provided"
fi
# Is the class name valid?
if [[ "$class_name" =~ [:#$] ]]; then
error "Unexpected class name: $class_name. Illegal characters were detected: ':', '#', and '\$' are not allowed! ci-test only supports class names, and not references to methods."
fi
path="$(echo "$class_name" | tr '.' '/' ).java"
prefix="$( find test | grep "$path" | awk -F/ '{print $2}' )"
JAVA_VERSION=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}' | awk -F. '{print $1}')
if [ "$JAVA_VERSION" -ge 11 ]; then
export CASSANDRA_USE_JDK11=true
fi
if [ -n "${num_tokens:-}" ]; then
export CASSANDRA_DTEST_NUM_TOKENS="$num_tokens"
fi
opts=(
-Dant.gen-doc.skip=true
-Dno-checkstyle=true
-Dant.gen-doc.skip=true
-Drat.skip=true
)
if [[ -n "${jvm_dtest_version:-}" ]]; then
opts+=( "-Ddtest-api.version=$jvm_dtest_version" )
fi
if [[ "$fresh_build" == true ]]; then
if [[ ! "$path" =~ distributed/upgrade ]]; then
ant "${opts[@]}" realclean
ant "${opts[@]}"
ant "${opts[@]}" generate-idea-files
fi
fi
# cleanup logs so w/e is around are for this run
rm -rf build/test/logs || true
test_timeout=$(grep "name=\"test.$prefix.timeout\"" build.xml | awk -F'"' '{print $4}' || true)
if [ -z "$test_timeout" ]; then
test_timeout=$(grep 'name="test.timeout"' build.xml | awk -F'"' '{print $4}')
fi
if [[ "$fresh_build" == false ]]; then
opts+=(-Dno-build-test=true)
fi
if [[ "$prefix" == "simulator" ]]; then
ant "${opts[@]}" test-simulator-dtest -Dtest.name="$(echo "$path" | awk -F'.' '{print $1}' | awk -F'/' '{print $NF}')"
else
ant "${opts[@]}" "$task" -Dtest.timeout="$test_timeout" -Dtest.classlistfile=<(echo "$path") -Dtest.classlistprefix="$prefix"
fi

View File

@ -1,86 +0,0 @@
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#set -o xtrace
set -o errexit
set -o pipefail
set -o nounset
bin="$(cd "$(dirname "$0")" > /dev/null; pwd)"
home="$(cd "$bin/../.." > /dev/null; pwd)" # this script lives in .build/sh
# Switch to the project root so all commands work properly
cd "$home"
usage() {
if [[ $# -gt 0 ]]; then
echo "$*" 1>&2
fi
cat <<EOF
Usage: $(basename "$0") [class name] (option)*
Options:
EOF
exit 1
}
_main() {
local class_name=""
fresh_build=true
while [[ "$#" -gt 0 ]]; do
case "$1" in
--reuse)
fresh_build=false
shift
;;
-h|--help)
usage
shift
;;
*)
if [[ -z "${class_name:-}" ]]; then
class_name="$1"
shift
else
usage "Unknown argument $1"
fi
;;
esac
done
if [[ -z "${class_name:-}" ]]; then
usage "No class provided"
fi
path="$(echo "$class_name" | tr '.' '/' ).java"
if $fresh_build; then
if [[ ! "$path" =~ distributed/upgrade ]]; then
ant realclean && ant && ant generate-idea-files
fi
fi
counter=1
echo ">>> Running attempt $counter"
while "$bin"/ci-test "$class_name" --reuse; do
counter=$(( counter + 1 ))
echo ">>> Running attempt $counter"
done;
echo "Exited after $counter attempts"
}
_main "$@"

183
.build/sh/test-log-summary.py Executable file
View File

@ -0,0 +1,183 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Summarize the output of .build/run-tests.sh.
#
# Unlike ant-log-summary.py (which keys off ant's BUILD FAILED/SUCCESSFUL),
# run-tests.sh deliberately continues past failing tests, as ant still prints
# BUILD SUCCESSFUL with test failures.
#
# Failures are identified from the test results instead:
# - the "[Test Summary] Run: N, Failed: N, Errors: N, Skipped: N" line
# emitted by ant's generate-test-report target,
# - run-tests.sh's own "failed <prefix> <target> ..." lines,
# - "failure rate: X/Y" lines printed for repeated runs,
# - per-test "Testcase: <name>:\tFAILED / Caused an ERROR" markers from the
# brief JUnit formatter,
# - a compile/setup "BUILD FAILED" (with the [javac] errors).
#
# The exit code mirrors the outcome: non-zero if any failure signal is seen.
import argparse
import re
import sys
TEST_SUMMARY_RE = re.compile(
r"\[Test Summary\]\s*Run:\s*(\d+),\s*Failed:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)"
)
FAILURE_RATE_RE = re.compile(r"failure rate:\s*(\d+)/(\d+)")
# run-tests.sh: echo "failed ${_target_prefix} ${_testlist_target} ..."
RUN_TESTS_FAILED_RE = re.compile(r"^failed\s+\S+")
# brief JUnit formatter: "Testcase: <name>:\tFAILED" / ":\tCaused an ERROR",
# optionally prefixed by an ant task tag such as "[junit] ".
TESTCASE_FAIL_RE = re.compile(r"^(?:\[[^\]]+\]\s*)?Testcase:\s.*\b(FAILED|Caused an ERROR)\b")
def parse_args():
parser = argparse.ArgumentParser(
description="Summarize Apache Cassandra .build/run-tests.sh output"
)
parser.add_argument(
"log_file",
nargs="?",
default="-",
help='Path to the run-tests.sh log (use "-" or omit to read from stdin)',
)
return parser.parse_args()
ANT_TAG_RE = re.compile(r"^\[[^\]]+\]\s*")
def _strip_tag(line):
"""Drop a leading ant task tag such as '[junit] ' or '[echo] '."""
return ANT_TAG_RE.sub("", line.strip())
def summarize(content):
"""Return (failed, lines) where failed is a bool and lines is the summary."""
lines = content.split("\n")
summaries = [] # [Test Summary] lines
failed_targets = [] # run-tests.sh "failed ..." lines
failure_rates = [] # "failure rate: X/Y" lines
failed_testcases = [] # per-test FAILED / ERROR markers
javac_errors = [] # compile errors, when a BUILD FAILED is present
build_failed = "BUILD FAILED" in content
for line in lines:
stripped = line.strip()
m = TEST_SUMMARY_RE.search(line)
if m:
run, failures, errors, skipped = (int(g) for g in m.groups())
summaries.append((_strip_tag(line), failures, errors))
continue
if RUN_TESTS_FAILED_RE.match(stripped):
failed_targets.append(stripped)
continue
m = FAILURE_RATE_RE.search(line)
if m:
failure_rates.append((stripped, int(m.group(1))))
continue
if TESTCASE_FAIL_RE.match(stripped):
failed_testcases.append(_strip_tag(line))
continue
if build_failed and "[javac]" in line and ("error:" in line or "errors" in line):
clean = line.replace("[javac]", "").strip()
if clean:
javac_errors.append(clean)
# decide pass/fail
failed = build_failed
for _, failures, errors in summaries:
if failures or errors:
failed = True
if failed_targets:
failed = True
for _, n in failure_rates:
if n:
failed = True
# build the summary output
out = []
if build_failed:
out.append("BUILD FAILED")
if javac_errors:
out.append("")
out.append("Compilation Errors:")
out.append("-" * 20)
out.extend(javac_errors)
if failed_testcases:
out.append("")
out.append("Failed tests:")
out.append("-" * 13)
# de-duplicate while preserving order
seen = set()
for tc in failed_testcases:
if tc not in seen:
seen.add(tc)
out.append(tc)
if failed_targets:
out.append("")
out.extend(failed_targets)
for line, _ in failure_rates:
out.append(line)
if summaries:
out.append("")
for line, _, _ in summaries:
out.append(line)
elif not build_failed and not failed:
# nothing ran a test report and nothing failed
out.append("No test summary found (nothing ran, or non-test target).")
if not failed and summaries:
out.append("")
out.append("TESTS PASSED")
return failed, out
def main():
args = parse_args()
try:
if args.log_file == "-":
content = sys.stdin.read()
else:
with open(args.log_file, "r") as f:
content = f.read()
except FileNotFoundError:
print(f"Error: Log file '{args.log_file}' not found")
sys.exit(2)
except Exception as e:
print(f"Error reading log file: {e}")
sys.exit(2)
failed, out = summarize(content)
print("\n".join(out))
sys.exit(1 if failed else 0)
if __name__ == "__main__":
main()

View File

@ -14,31 +14,38 @@ Apache Cassandra is a NoSQL distributed database. This is the official Git repos
## Build
```bash
.build/sh/ai-build # clean, build JAR, and run checkstyle (output is summarized)
```
Prefer the `.build/*.sh` helper scripts over calling `ant` directly. See
[.build/README.md](./.build/README.md) for the full set.
Do NOT call `ant` directly — always use the `ai-*` wrapper scripts which handle log summarization and correct working directory.
```bash
.build/build-jars.sh -s # build the Cassandra JAR (runs `ant jar`)
.build/build-jars.sh -s --clean # clean first, then build. `-s` summarizes output, remove for verbose
```
## Testing
- Do NOT run the entire test suite. Run only the specific test(s) relevant to your change.
- The project must be built first (e.g. `.build/build-jars.sh`).
```bash
# Run a single unit test class
.build/sh/ai-ci-test org.apache.cassandra.service.StorageServiceServerTest
# Run a single test class: -a is the test type, -t is a class-name regexp
.build/run-tests.sh -s -a test -t StorageServiceServerTest
```
- `ai-ci-test` does NOT support method-level filtering — it runs the entire test class.
- `-a` is the test type (`test`, `jvm-dtest`, `long-test`, …; run `.build/run-tests.sh -h` for the full list).
- `-t` matches the test class only, not individual methods.
- `-s`/`--summary` provides a concise list of failed tests, remove for the full ant output
- When fixing a bug, first create a regression test that reproduces the failure, then implement the fix and verify.
- Provide test(s) coverage for all new or modified code.
## Linting and Code Checks
`.build/sh/ai-build` includes checkstyle validation. There is no need to run checkstyle separately.
```bash
.build/check-code.sh -s # runs `ant check` (build + checkstyle + checkstyle-test). `-s` summarizes output, remove for verbose
```
## Code Style
Cassandra enforces style via Checkstyle (run via `.build/sh/ai-build`). The official style guide is at https://cassandra.apache.org/_/development/code_style.html. Always defer to it when in doubt.
Cassandra enforces style via Checkstyle (run via `.build/check-code.sh`). The official style guide is at https://cassandra.apache.org/_/development/code_style.html. Always defer to it when in doubt.
General style conventions:
- 4-space indentation, no tabs.
@ -51,10 +58,12 @@ General style conventions:
- Commit messages format. For example:
```
<One sentence description, usually Jira title or CHANGES.txt summary>
<One sentence description, usually Jira title or CHANGES.txt summary, no jira id>
<Optional lengthier description>
patch by <Authors,>; reviewed by <Reviewers,> for CASSANDRA-#####
Assisted-by: AGENT_NAME:MODEL_VERSION
```