Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .build/build-rat.xml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@
<!-- legal files -->
<exclude NAME="NOTICE.txt"/>
<exclude NAME="LICENSE.txt"/>
<!-- AI agent instruction files -->
<exclude NAME="AGENTS.md"/>
<exclude NAME="CLAUDE.md"/>
<!-- misc -->
<exclude NAME="**/*.patch"/>
<exclude NAME="**/*.diff"/>
Expand Down
37 changes: 37 additions & 0 deletions .build/sh/ai-build
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/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"

opts=(
-Dant.gen-doc.skip=true
-Dno-checkstyle=true
-Dant.gen-doc.skip=true
-Drat.skip=true
)

ant "${opts[@]}" clean jar checkstyle checkstyle-test "$@" 2>&1 | "$bin"/ant-log-summary.py -

29 changes: 29 additions & 0 deletions .build/sh/ai-ci-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/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 -
170 changes: 170 additions & 0 deletions .build/sh/ant-log-summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/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.

import argparse
import sys


def parse_args():
parser = argparse.ArgumentParser(
description="Parse Apache Cassandra ant build logs and summarize failures"
)
parser.add_argument(
"log_file",
help='Path to the ant build log file to analyze (use "-" to read from stdin)',
)
return parser.parse_args()


def find_build_failures(content):
"""
Parse the log content and extract failure information.
Returns a tuple: (is_failed, failed_target, failure_output)
"""
lines = content.split("\n")

# Check if build was successful first
if "BUILD FAILED" not in content:
return False, None, []

# Track what ant targets are running before the build failed
failed_target = None
failed_target_line = -1
for i, line in enumerate(lines):
if "BUILD FAILED" in line:
break
# Keep track of tasks so when we finally see the BUILD FAILED we know what the last task was
line_stripped = line.strip()
if line_stripped.endswith(":") and not line.startswith(" ") and "[" not in line:
failed_target = line_stripped[:-1]
failed_target_line = i

target_start_idx = 0
if failed_target and failed_target_line >= 0:
target_start_idx = failed_target_line

# Collect the output from the failed target up to BUILD FAILED
failure_output = []
for line in lines[target_start_idx:]:
stripped = line.strip()
if stripped:
failure_output.append(line.rstrip())

return True, failed_target, failure_output


def extract_compilation_errors(content):
"""Extract compilation error details specifically."""
lines = content.split("\n")
error_lines = []

for line in lines:
if "[javac]" in line and ("error:" in line or "errors" in line):
# Clean up the line to show just the error
clean_line = line.replace("[javac]", "").strip()
if clean_line:
error_lines.append(clean_line)

return error_lines


def extract_test_failures(content):
"""Extract test failure details specifically."""
lines = content.split("\n")
test_failures = []

for line in lines:
if "Test " in line and "FAILED" in line:
test_failures.append(line.strip())

return test_failures


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(1)
except Exception as e:
print(f"Error reading log file: {e}")
sys.exit(1)

is_failed, failed_target, failure_output = find_build_failures(content)

if not is_failed:
print("BUILD SUCCESSFUL")
sys.exit(0)

print("BUILD FAILED")
if failed_target:
print(f"Failed target: {failed_target}")

print("=" * 50)

# Special handling for compilation and test failures
if failed_target and (
"test" in failed_target.lower() or "compile" in failed_target.lower()
):
# Extract compilation errors if it's a compile-related target
compilation_errors = extract_compilation_errors("\n".join(failure_output))
if compilation_errors:
print("\nCompilation Errors:")
print("-" * 20)
for error in compilation_errors:
print(error)
# Also show the full output for context
print(f"\nFull output from failed target '{failed_target}':")
print("-" * 40)
for line in failure_output:
print(line)
sys.exit(1)

# Extract test failures if it's a test-related target
test_failures = extract_test_failures("\n".join(failure_output))
if test_failures:
print("\nTest Failures:")
print("-" * 15)
for failure in test_failures:
print(failure)
# Always show the full output for test failures - this is crucial for debugging
print(f"\nFull output from failed target '{failed_target}':")
print("-" * 40)
for line in failure_output:
print(line)
sys.exit(1)

# For all other targets or if no specific errors found, show the task output
if failure_output:
print(f"\nOutput from failed target '{failed_target}':")
print("-" * 40)
for line in failure_output:
print(line)

sys.exit(1)


if __name__ == "__main__":
main()
149 changes: 149 additions & 0 deletions .build/sh/ci-test
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/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 [ ! -z "${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 [[ ! -z "${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
Loading