Skip to content

Commit 6ae448a

Browse files
Copy composite actions from opencb/java-common-libs TASK-8067 branch
Co-authored-by: juanfeSanahuja <85166064+juanfeSanahuja@users.noreply.github.com>
1 parent 7a6de42 commit 6ae448a

File tree

2 files changed

+291
-44
lines changed

2 files changed

+291
-44
lines changed
Lines changed: 108 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,112 @@
1-
name: 'Setup Java and Maven'
2-
description: 'Composite action to set up Java and Maven environment for OpenCB/Xetabase projects'
3-
4-
# TODO: Define inputs for Java version, Maven version, cache configuration, etc.
5-
# inputs:
6-
# java-version:
7-
# description: 'Java version to set up'
8-
# required: false
9-
# default: '11'
10-
11-
# TODO: Define outputs if needed
12-
# outputs:
13-
# java-version:
14-
# description: 'Installed Java version'
15-
# value: ${{ steps.setup.outputs.java-version }}
1+
name: "Setup Java, dependencies and Maven cache"
2+
description: "Set up JDK, clone and optionally compile dependencies, and configure Maven cache using a key that includes dependencies_sha"
3+
4+
inputs:
5+
java_version:
6+
description: "Java version to use"
7+
required: false
8+
default: "11"
9+
storage_hadoop:
10+
description: "Hadoop flavour, used as part of the Maven cache key"
11+
required: false
12+
default: "hdi5.1"
13+
dependency_repos:
14+
description: "Comma-separated list of dependency repositories (e.g. 'java-common-libs,biodata,cellbase')"
15+
required: false
16+
default: ""
17+
require_cache_hit:
18+
description: "If true, fail the job when the Maven cache is not found for the current dependencies_sha"
19+
required: false
20+
default: "false"
21+
outputs:
22+
dependencies_sha:
23+
description: "Hash that represents dependency commits (OpenCGA/java-common-libs/biodata/cellbase, etc.)"
24+
value: ${{ steps.clone_dependencies.outputs.dependencies_sha }}
25+
cache-hit:
26+
description: "True if the Maven cache key with this dependencies_sha was already present"
27+
value: ${{ steps.maven_cache.outputs.cache-hit }}
1628

1729
runs:
18-
using: 'composite'
30+
using: "composite"
1931
steps:
20-
# TODO: Add steps to set up Java
21-
# TODO: Add steps to set up Maven
22-
# TODO: Add caching for Maven dependencies
23-
# TODO: Configure Maven settings if needed
24-
- name: Placeholder
32+
- name: Set up JDK
33+
# This step installs the requested JDK version for the job
34+
uses: actions/setup-java@v4
35+
with:
36+
distribution: "temurin"
37+
java-version: ${{ inputs.java_version }}
38+
39+
- name: Clone dependencies and compute dependencies_sha
40+
id: clone_dependencies
41+
if: ${{ inputs.dependency_repos != '' }}
42+
shell: bash
43+
run: |
44+
# This step clones the dependency repositories using the helper script
45+
# and computes a stable dependencies_sha based on all dependency commits.
46+
#
47+
# It expects get_same_branch.sh to:
48+
# - clone or checkout each repo on the same branch as the caller
49+
# - update DEPENDENCIES_SHA in-place
50+
# - finally write `dependencies_sha=<hash>` into $GITHUB_OUTPUT
51+
52+
if [ -f "java-common-libs/.github/workflows/scripts/get_same_branch.sh" ]; then
53+
chmod +x java-common-libs/.github/workflows/scripts/get_same_branch.sh
54+
55+
# Initialize DEPENDENCIES_SHA with the current repo SHA so that
56+
# main project changes also affect the final cache key.
57+
export DEPENDENCIES_SHA="${{ github.sha }}"
58+
59+
java-common-libs/.github/workflows/scripts/get_same_branch.sh \
60+
"${{ github.ref_name }}" \
61+
"${{ inputs.dependency_repos }}"
62+
else
63+
echo "get_same_branch.sh script not found. Skipping dependency checkout."
64+
# Fallback: use only the current repo SHA as dependencies_sha
65+
DEPENDENCIES_SHA="${{ github.sha }}"
66+
echo "dependencies_sha=${DEPENDENCIES_SHA}" >> "$GITHUB_OUTPUT"
67+
fi
68+
69+
- name: Cache local Maven repository
70+
id: maven_cache
71+
# This step restores and saves the Maven local repository cache, using
72+
# storage_hadoop and dependencies_sha as part of the cache key.
73+
uses: actions/cache@v4
74+
with:
75+
path: ~/.m2/repository
76+
key: ${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-${{ steps.clone_dependencies.outputs.dependencies_sha }}
77+
restore-keys: |
78+
${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-
79+
## Force cache hit to avoid analyzing with incomplete dependencies
80+
fail-on-cache-miss: ${{ inputs.require_cache_hit }}
81+
- name: Compile dependencies (only if cache did not hit)
82+
if: ${{ inputs.dependency_repos != '' && steps.maven_cache.outputs.cache-hit != 'true' }}
83+
shell: bash
84+
run: |
85+
# This step compiles dependency repositories only when the Maven cache
86+
# for the current dependencies_sha was not found.
87+
#
88+
# It expects compile_same_branch.sh to:
89+
# - iterate over the comma-separated dependency_repos list
90+
# - run `mvn clean install -DskipTests` on each dependency
91+
# - populate ~/.m2/repository with the latest built artifacts
92+
93+
if [ -f "java-common-libs/.github/workflows/scripts/compile_same_branch.sh" ]; then
94+
chmod +x java-common-libs/.github/workflows/scripts/compile_same_branch.sh
95+
java-common-libs/.github/workflows/scripts/compile_same_branch.sh "${{ inputs.dependency_repos }}"
96+
else
97+
echo "compile_same_branch.sh script not found. Skipping dependency compilation."
98+
fi
99+
100+
- name: Cache summary
101+
# This step writes a small summary into the GitHub job summary, so it is
102+
# easy to inspect which dependencies_sha and cache key were used.
25103
shell: bash
26-
run: echo "Setup Java and Maven action - to be implemented"
104+
run: |
105+
{
106+
echo "## Java / Maven cache summary"
107+
echo ""
108+
echo "- Java version: \`${{ inputs.java_version }}\`"
109+
echo "- Dependencies sha: \`${{ steps.clone_dependencies.outputs.dependencies_sha || 'No dependencies' }}\`"
110+
echo "- Maven cache key: \`${{ runner.os }}-maven-${{ inputs.storage_hadoop }}-${{ steps.clone_dependencies.outputs.dependencies_sha }}\`"
111+
echo "- Maven cache hit: \`${{ steps.maven_cache.outputs.cache-hit || 'false' }}\`"
112+
} >> "$GITHUB_STEP_SUMMARY"
Lines changed: 183 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,187 @@
1-
name: 'Test Summary'
2-
description: 'Composite action to generate and publish test summaries for OpenCB/Xetabase projects'
3-
4-
# TODO: Define inputs for test results paths, formats, etc.
5-
# inputs:
6-
# test-results-path:
7-
# description: 'Path to test results'
8-
# required: false
9-
# default: '**/target/surefire-reports/*.xml'
10-
11-
# TODO: Define outputs if needed
12-
# outputs:
13-
# summary:
14-
# description: 'Test summary'
15-
# value: ${{ steps.summary.outputs.summary }}
1+
name: "Test summary"
2+
description: "Generate a Markdown summary of Surefire test results and write it to GITHUB_STEP_SUMMARY"
3+
4+
inputs:
5+
report_paths:
6+
description: "Glob pattern for Surefire XML reports"
7+
required: false
8+
default: "./**/surefire-reports/TEST-*.xml"
9+
title:
10+
description: "Title for the test summary section"
11+
required: false
12+
default: "Test summary"
13+
include_module_table:
14+
description: "Whether to include the per-module breakdown table"
15+
required: false
16+
default: "true"
17+
18+
outputs:
19+
total_success:
20+
description: "Total number of successful tests"
21+
value: ${{ steps.generate_summary.outputs.total_success }}
22+
total_failed:
23+
description: "Total number of failed tests"
24+
value: ${{ steps.generate_summary.outputs.total_failed }}
25+
total_errors:
26+
description: "Total number of tests with errors"
27+
value: ${{ steps.generate_summary.outputs.total_errors }}
28+
total_skipped:
29+
description: "Total number of skipped tests"
30+
value: ${{ steps.generate_summary.outputs.total_skipped }}
31+
total_tests:
32+
description: "Total number of tests"
33+
value: ${{ steps.generate_summary.outputs.total_tests }}
1634

1735
runs:
18-
using: 'composite'
36+
using: "composite"
1937
steps:
20-
# TODO: Add steps to collect test results
21-
# TODO: Add steps to parse test results
22-
# TODO: Add steps to generate summary
23-
# TODO: Add steps to publish summary to GitHub Actions UI
24-
- name: Placeholder
38+
- name: Generate test summary
39+
id: generate_summary
2540
shell: bash
26-
run: echo "Test summary action - to be implemented"
41+
env:
42+
REPORT_GLOB: ${{ inputs.report_paths }}
43+
SUMMARY_TITLE: ${{ inputs.title }}
44+
INCLUDE_MODULE_TABLE: ${{ inputs.include_module_table }}
45+
run: |
46+
# Use Python to parse Surefire XML reports and generate a Markdown summary
47+
python - << 'PY'
48+
import glob
49+
import os
50+
import xml.etree.ElementTree as ET
51+
import sys
52+
53+
report_glob = os.environ.get("REPORT_GLOB", "./**/surefire-reports/TEST-*.xml")
54+
title = os.environ.get("SUMMARY_TITLE", "Test summary")
55+
include_module_table = os.environ.get("INCLUDE_MODULE_TABLE", "true").lower() == "true"
56+
github_step_summary = os.environ.get("GITHUB_STEP_SUMMARY")
57+
github_output = os.environ.get("GITHUB_OUTPUT")
58+
59+
# Collect all report files
60+
report_files = glob.glob(report_glob, recursive=True)
61+
62+
# Data structures for totals and per-module aggregation
63+
total = {
64+
"tests": 0,
65+
"failures": 0,
66+
"errors": 0,
67+
"skipped": 0,
68+
}
69+
modules = {}
70+
71+
def update_counts(target, tests, failures, errors, skipped):
72+
target["tests"] += tests
73+
target["failures"] += failures
74+
target["errors"] += errors
75+
target["skipped"] += skipped
76+
77+
for path in report_files:
78+
try:
79+
tree = ET.parse(path)
80+
root = tree.getroot()
81+
except Exception as e:
82+
# Skip malformed XML files
83+
continue
84+
85+
# Surefire can be <testsuite> or <testsuites> root
86+
suites = []
87+
if root.tag == "testsuite":
88+
suites = [root]
89+
elif root.tag == "testsuites":
90+
suites = list(root.findall("testsuite"))
91+
92+
if not suites:
93+
continue
94+
95+
# Deduce module name from path (dir before 'target')
96+
# Example: some-module/target/surefire-reports/TEST-*.xml -> module = 'some-module'
97+
parts = path.split(os.sep)
98+
module_name = "root"
99+
if "target" in parts:
100+
idx = parts.index("target")
101+
if idx > 0:
102+
module_name = parts[idx - 1]
103+
104+
if module_name not in modules:
105+
modules[module_name] = {
106+
"tests": 0,
107+
"failures": 0,
108+
"errors": 0,
109+
"skipped": 0,
110+
}
111+
112+
for suite in suites:
113+
def get_int(attr_name):
114+
value = suite.attrib.get(attr_name, "0")
115+
try:
116+
return int(value)
117+
except ValueError:
118+
return 0
119+
120+
tests = get_int("tests")
121+
failures = get_int("failures")
122+
errors = get_int("errors")
123+
skipped = get_int("skipped")
124+
125+
update_counts(total, tests, failures, errors, skipped)
126+
update_counts(modules[module_name], tests, failures, errors, skipped)
127+
128+
# Compute derived values
129+
def compute_success(data):
130+
return max(0, data["tests"] - data["failures"] - data["errors"] - data["skipped"])
131+
132+
total_success = compute_success(total)
133+
134+
# Build Markdown summary
135+
lines = []
136+
137+
lines.append(f"## {title}")
138+
lines.append("")
139+
if not report_files:
140+
lines.append("_No test reports were found with pattern:_")
141+
lines.append(f"`{report_glob}`")
142+
else:
143+
# Overall table
144+
lines.append("### Overall")
145+
lines.append("")
146+
lines.append("| Success | Failed | Errors | Skipped | Total |")
147+
lines.append("| --- | --- | --- | --- | --- |")
148+
lines.append(
149+
f"| {total_success} | {total['failures']} | {total['errors']} | {total['skipped']} | {total['tests']} |"
150+
)
151+
lines.append("")
152+
153+
if include_module_table and modules:
154+
lines.append("### By module")
155+
lines.append("")
156+
lines.append("| Module | Success | Failed | Errors | Skipped | Total |")
157+
lines.append("| --- | --- | --- | --- | --- | --- |")
158+
for module_name in sorted(modules.keys()):
159+
data = modules[module_name]
160+
success = compute_success(data)
161+
lines.append(
162+
f"| {module_name} | {success} | {data['failures']} | {data['errors']} | {data['skipped']} | {data['tests']} |"
163+
)
164+
lines.append("")
165+
166+
# Optional detail: number of XML report files found
167+
lines.append(f"_Processed {len(report_files)} Surefire report file(s)._")
168+
169+
markdown = "\n".join(lines)
170+
171+
# Write to step summary if available
172+
if github_step_summary:
173+
with open(github_step_summary, "a", encoding="utf-8") as f:
174+
f.write(markdown + "\n")
175+
176+
# Write outputs for reuse
177+
if github_output:
178+
def write_output(name, value):
179+
with open(github_output, "a", encoding="utf-8") as f:
180+
f.write(f"{name}={value}\n")
181+
182+
write_output("total_success", total_success)
183+
write_output("total_failed", total["failures"])
184+
write_output("total_errors", total["errors"])
185+
write_output("total_skipped", total["skipped"])
186+
write_output("total_tests", total["tests"])
187+
PY

0 commit comments

Comments
 (0)