Skip to content
Merged
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
130 changes: 108 additions & 22 deletions .github/actions/setup-java-maven/action.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,112 @@
name: 'Setup Java and Maven'
description: 'Composite action to set up Java and Maven environment for OpenCB/Xetabase projects'

# TODO: Define inputs for Java version, Maven version, cache configuration, etc.
# inputs:
# java-version:
# description: 'Java version to set up'
# required: false
# default: '11'

# TODO: Define outputs if needed
# outputs:
# java-version:
# description: 'Installed Java version'
# value: ${{ steps.setup.outputs.java-version }}
name: "Setup Java, dependencies and Maven cache"
description: "Set up JDK, clone and optionally compile dependencies, and configure Maven cache using a key that includes dependencies_sha"

inputs:
java_version:
description: "Java version to use"
required: false
default: "11"
storage_hadoop:
description: "Hadoop flavour, used as part of the Maven cache key"
required: false
default: "hdi5.1"
dependency_repos:
description: "Comma-separated list of dependency repositories (e.g. 'java-common-libs,biodata,cellbase')"
required: false
default: ""
require_cache_hit:
description: "If true, fail the job when the Maven cache is not found for the current dependencies_sha"
required: false
default: "false"
outputs:
dependencies_sha:
description: "Hash that represents dependency commits (OpenCGA/java-common-libs/biodata/cellbase, etc.)"
value: ${{ steps.clone_dependencies.outputs.dependencies_sha }}
cache-hit:
description: "True if the Maven cache key with this dependencies_sha was already present"
value: ${{ steps.maven_cache.outputs.cache-hit }}

runs:
using: 'composite'
using: "composite"
steps:
# TODO: Add steps to set up Java
# TODO: Add steps to set up Maven
# TODO: Add caching for Maven dependencies
# TODO: Configure Maven settings if needed
- name: Placeholder
- name: Set up JDK
# This step installs the requested JDK version for the job
uses: actions/setup-java@v4
with:
distribution: "temurin"
java-version: ${{ inputs.java_version }}

- name: Clone dependencies and compute dependencies_sha
id: clone_dependencies
if: ${{ inputs.dependency_repos != '' }}
shell: bash
run: |
# This step clones the dependency repositories using the helper script
# and computes a stable dependencies_sha based on all dependency commits.
#
# It expects get_same_branch.sh to:
# - clone or checkout each repo on the same branch as the caller
# - update DEPENDENCIES_SHA in-place
# - finally write `dependencies_sha=<hash>` into $GITHUB_OUTPUT

if [ -f "java-common-libs/.github/workflows/scripts/get_same_branch.sh" ]; then
chmod +x java-common-libs/.github/workflows/scripts/get_same_branch.sh

# Initialize DEPENDENCIES_SHA with the current repo SHA so that
# main project changes also affect the final cache key.
export DEPENDENCIES_SHA="${{ github.sha }}"

java-common-libs/.github/workflows/scripts/get_same_branch.sh \
"${{ github.ref_name }}" \
"${{ inputs.dependency_repos }}"
else
echo "get_same_branch.sh script not found. Skipping dependency checkout."
# Fallback: use only the current repo SHA as dependencies_sha
DEPENDENCIES_SHA="${{ github.sha }}"
echo "dependencies_sha=${DEPENDENCIES_SHA}" >> "$GITHUB_OUTPUT"
fi

- name: Cache local Maven repository
id: maven_cache
# This step restores and saves the Maven local repository cache, using
# storage_hadoop and dependencies_sha as part of the cache key.
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-${{ steps.clone_dependencies.outputs.dependencies_sha || github.sha }}
restore-keys: |
${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-
## Force cache hit to avoid analyzing with incomplete dependencies
fail-on-cache-miss: ${{ inputs.require_cache_hit }}
- name: Compile dependencies (only if cache did not hit)
if: ${{ inputs.dependency_repos != '' && steps.maven_cache.outputs.cache-hit != 'true' }}
shell: bash
run: |
# This step compiles dependency repositories only when the Maven cache
# for the current dependencies_sha was not found.
#
# It expects compile_same_branch.sh to:
# - iterate over the comma-separated dependency_repos list
# - run `mvn clean install -DskipTests` on each dependency
# - populate ~/.m2/repository with the latest built artifacts

if [ -f "java-common-libs/.github/workflows/scripts/compile_same_branch.sh" ]; then
chmod +x java-common-libs/.github/workflows/scripts/compile_same_branch.sh
java-common-libs/.github/workflows/scripts/compile_same_branch.sh "${{ inputs.dependency_repos }}"
else
echo "compile_same_branch.sh script not found. Skipping dependency compilation."
fi

- name: Cache summary
# This step writes a small summary into the GitHub job summary, so it is
# easy to inspect which dependencies_sha and cache key were used.
shell: bash
run: echo "Setup Java and Maven action - to be implemented"
run: |
{
echo "## Java / Maven cache summary"
echo ""
echo "- Java version: \`${{ inputs.java_version }}\`"
echo "- Dependencies sha: \`${{ steps.clone_dependencies.outputs.dependencies_sha || 'No dependencies' }}\`"
echo "- Maven cache key: \`${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-${{ steps.clone_dependencies.outputs.dependencies_sha }}\`"
echo "- Maven cache hit: \`${{ steps.maven_cache.outputs.cache-hit || 'false' }}\`"
} >> "$GITHUB_STEP_SUMMARY"
205 changes: 183 additions & 22 deletions .github/actions/test-summary/action.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,187 @@
name: 'Test Summary'
description: 'Composite action to generate and publish test summaries for OpenCB/Xetabase projects'

# TODO: Define inputs for test results paths, formats, etc.
# inputs:
# test-results-path:
# description: 'Path to test results'
# required: false
# default: '**/target/surefire-reports/*.xml'

# TODO: Define outputs if needed
# outputs:
# summary:
# description: 'Test summary'
# value: ${{ steps.summary.outputs.summary }}
name: "Test summary"
description: "Generate a Markdown summary of Surefire test results and write it to GITHUB_STEP_SUMMARY"

inputs:
report_paths:
description: "Glob pattern for Surefire XML reports"
required: false
default: "./**/surefire-reports/TEST-*.xml"
title:
description: "Title for the test summary section"
required: false
default: "Test summary"
include_module_table:
description: "Whether to include the per-module breakdown table"
required: false
default: "true"

outputs:
total_success:
description: "Total number of successful tests"
value: ${{ steps.generate_summary.outputs.total_success }}
total_failed:
description: "Total number of failed tests"
value: ${{ steps.generate_summary.outputs.total_failed }}
total_errors:
description: "Total number of tests with errors"
value: ${{ steps.generate_summary.outputs.total_errors }}
total_skipped:
description: "Total number of skipped tests"
value: ${{ steps.generate_summary.outputs.total_skipped }}
total_tests:
description: "Total number of tests"
value: ${{ steps.generate_summary.outputs.total_tests }}

runs:
using: 'composite'
using: "composite"
steps:
# TODO: Add steps to collect test results
# TODO: Add steps to parse test results
# TODO: Add steps to generate summary
# TODO: Add steps to publish summary to GitHub Actions UI
- name: Placeholder
- name: Generate test summary
id: generate_summary
shell: bash
run: echo "Test summary action - to be implemented"
env:
REPORT_GLOB: ${{ inputs.report_paths }}
SUMMARY_TITLE: ${{ inputs.title }}
INCLUDE_MODULE_TABLE: ${{ inputs.include_module_table }}
run: |
# Use Python to parse Surefire XML reports and generate a Markdown summary
python - << 'PY'
import glob
import os
import xml.etree.ElementTree as ET
import sys

report_glob = os.environ.get("REPORT_GLOB", "./**/surefire-reports/TEST-*.xml")
title = os.environ.get("SUMMARY_TITLE", "Test summary")
include_module_table = os.environ.get("INCLUDE_MODULE_TABLE", "true").lower() == "true"
github_step_summary = os.environ.get("GITHUB_STEP_SUMMARY")
github_output = os.environ.get("GITHUB_OUTPUT")

# Collect all report files
report_files = glob.glob(report_glob, recursive=True)

# Data structures for totals and per-module aggregation
total = {
"tests": 0,
"failures": 0,
"errors": 0,
"skipped": 0,
}
modules = {}

def update_counts(target, tests, failures, errors, skipped):
target["tests"] += tests
target["failures"] += failures
target["errors"] += errors
target["skipped"] += skipped

for path in report_files:
try:
tree = ET.parse(path)
root = tree.getroot()
except Exception as e:
# Skip malformed XML files
continue

# Surefire can be <testsuite> or <testsuites> root
suites = []
if root.tag == "testsuite":
suites = [root]
elif root.tag == "testsuites":
suites = list(root.findall("testsuite"))

if not suites:
continue

# Deduce module name from path (dir before 'target')
# Example: some-module/target/surefire-reports/TEST-*.xml -> module = 'some-module'
parts = path.split(os.sep)
module_name = "root"
if "target" in parts:
idx = parts.index("target")
if idx > 0:
module_name = parts[idx - 1]

if module_name not in modules:
modules[module_name] = {
"tests": 0,
"failures": 0,
"errors": 0,
"skipped": 0,
}

for suite in suites:
def get_int(attr_name):
value = suite.attrib.get(attr_name, "0")
try:
return int(value)
except ValueError:
return 0

tests = get_int("tests")
failures = get_int("failures")
errors = get_int("errors")
skipped = get_int("skipped")

update_counts(total, tests, failures, errors, skipped)
update_counts(modules[module_name], tests, failures, errors, skipped)

# Compute derived values
def compute_success(data):
return max(0, data["tests"] - data["failures"] - data["errors"] - data["skipped"])

total_success = compute_success(total)

# Build Markdown summary
lines = []

lines.append(f"## {title}")
lines.append("")
if not report_files:
lines.append("_No test reports were found with pattern:_")
lines.append(f"`{report_glob}`")
else:
# Overall table
lines.append("### Overall")
lines.append("")
lines.append("| Success | Failed | Errors | Skipped | Total |")
lines.append("| --- | --- | --- | --- | --- |")
lines.append(
f"| {total_success} | {total['failures']} | {total['errors']} | {total['skipped']} | {total['tests']} |"
)
lines.append("")

if include_module_table and modules:
lines.append("### By module")
lines.append("")
lines.append("| Module | Success | Failed | Errors | Skipped | Total |")
lines.append("| --- | --- | --- | --- | --- | --- |")
for module_name in sorted(modules.keys()):
data = modules[module_name]
success = compute_success(data)
lines.append(
f"| {module_name} | {success} | {data['failures']} | {data['errors']} | {data['skipped']} | {data['tests']} |"
)
lines.append("")

# Optional detail: number of XML report files found
lines.append(f"_Processed {len(report_files)} Surefire report file(s)._")

markdown = "\n".join(lines)

# Write to step summary if available
if github_step_summary:
with open(github_step_summary, "a", encoding="utf-8") as f:
f.write(markdown + "\n")

# Write outputs for reuse
if github_output:
def write_output(name, value):
with open(github_output, "a", encoding="utf-8") as f:
f.write(f"{name}={value}\n")

write_output("total_success", total_success)
write_output("total_failed", total["failures"])
write_output("total_errors", total["errors"])
write_output("total_skipped", total["skipped"])
write_output("total_tests", total["tests"])
PY
Loading