From 6db671b00cec61f27eadfe50cb6b42ef0c72968c Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sat, 7 Nov 2026 17:21:07 +0100 Subject: [PATCH 1/3] Upgrade to Gephi 0.11.4 and gephi-maven-plugin 1.4.1 (#355) Co-authored-by: Claude Opus 5.5 --- ARCHITECTURE.md | 2 +- pom.xml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 9ed93fac2..605edae87 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -115,7 +115,7 @@ enough for the vast majority of plugins. Every plugin's `pom.xml` inherits from `org.gephi:gephi-plugin-parent` — an artifact built from this repo's `parent-pom` branch, published to Maven Central, and versioned alongside Gephi itself (e.g. -`0.11.3`). It supplies: +`0.11.4`). It supplies: - Java/compiler settings (JDK 17 target). - A `` entry for every Gephi and NetBeans Platform module a plugin might diff --git a/pom.xml b/pom.xml index dd87e68dd..861438b24 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ org.gephi gephi-plugins - 0.11.3 + 0.11.4 pom gephi-plugins @@ -17,7 +17,7 @@ - 0.11.3 + 0.11.4 ${project.build.directory}/plugins_clusters github @@ -82,7 +82,7 @@ org.gephi gephi-maven-plugin - 1.3.4 + 1.4.1 org.apache.maven.plugins @@ -92,7 +92,7 @@ org.sonatype.central central-publishing-maven-plugin - 0.11.0 + 0.12.0 true From 405ac6c21688808268fcc7dbfb450ed71e6975b9 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 8 Nov 2026 20:15:32 +0100 Subject: [PATCH 2/3] Run gephi-plugins-tests SPI contract tests in CI (#354) * Upgrade to Gephi 0.11.4 and gephi-maven-plugin 1.4.1 Co-Authored-By: Claude Opus 5.5 * Run gephi-plugins-tests SPI contract tests in CI Adds a standalone harness (.github/contract-tests) that runs gephi-plugins-tests' contract tests against installed plugin modules, without plugins or gephi-plugin-parent having to declare anything. build.yml tests every module of the branch, pr.yml (moved here from master-forge) tests the modules a master-forge PR changes, and test-generation.yml checks the harness still resolves for the current gephi.version. Co-Authored-By: Claude Opus 5.5 * Pin Surefire 3.6.1 in the contract-test harness and keep docs SPI-agnostic gephi-plugins-tests will cover more SPIs than layouts, so the docs describe the contract tests generically instead of naming LayoutContractTest. Co-Authored-By: Claude Opus 5.5 * Harden the SPI contract-test harness - pr.yml tests every module when the root pom.xml or the harness changes, and passes changed folders as a quoted array (no word splitting, no path quoting) - run.sh fails loudly when listing the reactor fails or gephi.version can't be read, warns about folders that aren't reactor modules, refuses to test a module that isn't installed or was rebuilt since its install, and fails if no contract test class ran at all - the harness only tests implementations from the plugin's own jar (gephi.plugins.tests.jar, honored by gephi-plugins-tests 1.1.0) and runs headless with the module access Gephi's launcher grants - test-generation.yml adds a positive control against core layout-plugin so a harness that silently tests nothing fails - build.yml skips the contract-test step on branches without plugin modules Co-Authored-By: Claude Opus 5.5 * Use gephi-plugins-tests 1.1.0 in the contract-test harness 1.1.0 only tests the implementations from the plugin's own jar (the harness already passes gephi.plugins.tests.jar), and adds the Statistics and Layout lifecycle contract tests. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- .github/contract-tests/pom.xml | 72 ++++++++++++++++ .github/contract-tests/run.sh | 115 ++++++++++++++++++++++++++ .github/workflows/build.yml | 5 +- .github/workflows/pr.yml | 41 +++++++++ .github/workflows/test-generation.yml | 18 +++- AGENTS.md | 2 + ARCHITECTURE.md | 19 ++++- CONTRIBUTING.md | 5 ++ 8 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 .github/contract-tests/pom.xml create mode 100755 .github/contract-tests/run.sh create mode 100644 .github/workflows/pr.yml diff --git a/.github/contract-tests/pom.xml b/.github/contract-tests/pom.xml new file mode 100644 index 000000000..6872d46cf --- /dev/null +++ b/.github/contract-tests/pom.xml @@ -0,0 +1,72 @@ + + + + 4.0.0 + + org.gephi + gephi-plugins-contract-tests + 1.0.0 + jar + + + UTF-8 + 1.1.0 + + + + + + + org.gephi + gephi-plugin-parent + ${gephi.version} + pom + import + + + + + + + org.gephi + gephi-plugins-tests + ${gephi-plugins-tests.version} + test-jar + test + + + ${plugin.groupId} + ${plugin.artifactId} + ${plugin.version} + test + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.6.1 + + + -Djava.awt.headless=true --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.desktop/javax.swing=ALL-UNNAMED --add-exports=java.desktop/sun.awt=ALL-UNNAMED + + + ${plugin.artifactId}-${plugin.version}.jar + + + org.gephi:gephi-plugins-tests + + + + + + diff --git a/.github/contract-tests/run.sh b/.github/contract-tests/run.sh new file mode 100755 index 000000000..724cbed58 --- /dev/null +++ b/.github/contract-tests/run.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash +# Runs gephi-plugins-tests' SPI contract tests (see pom.xml in this folder) against this +# checkout's plugin modules, one Maven run per NBM module so each failure names a single plugin. +# +# Usage: .github/contract-tests/run.sh [modules/ ...] +# +# With no arguments, every module in the root pom.xml's list is tested. Otherwise only +# NBM modules under the given folders are; a folder outside the reactor (e.g. a disabled plugin) +# is skipped with a warning. Modules must be installed in the local Maven repository first +# (mvn install): a module that is missing there, or was rebuilt since, fails instead of testing a +# stale jar. +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +HARNESS_DIR="$ROOT/.github/contract-tests" +GEPHI_VERSION=$(sed -nE 's#.*(.*).*#\1#p' "$ROOT/pom.xml" | head -1) +if [ -z "$GEPHI_VERSION" ]; then + echo "::error::Could not read from $ROOT/pom.xml" + exit 1 +fi + +# One "packaging|groupId|artifactId|version|basedir|built jar|local repository" line per project +if ! PROJECTS=$(cd "$ROOT" && mvn -B -q -ntp org.codehaus.mojo:exec-maven-plugin:3.5.0:exec \ + -Dexec.executable=echo \ + '-Dexec.args=${project.packaging}|${project.groupId}|${project.artifactId}|${project.version}|${project.basedir}|${project.build.directory}/${project.build.finalName}.jar|${settings.localRepository}'); then + echo "$PROJECTS" + echo "::error::Could not list the reactor's modules, see the Maven output above" + exit 1 +fi + +FOLDERS=() +for arg in "$@"; do + folder="${arg#"$ROOT"/}" + folder="${folder#./}" + FOLDERS+=("${folder%/}") +done + +in_folder() { + case "$1/" in + */"$2"/*) return 0 ;; + esac + return 1 +} + +TARGETS=() +while IFS='|' read -r packaging group artifact version basedir jar repo; do + [ "$packaging" = nbm ] || continue + if [ ${#FOLDERS[@]} -eq 0 ]; then + TARGETS+=("$group|$artifact|$version|$jar|$repo") + continue + fi + for folder in "${FOLDERS[@]}"; do + if in_folder "$basedir" "$folder"; then + TARGETS+=("$group|$artifact|$version|$jar|$repo") + break + fi + done +done <<< "$PROJECTS" + +for folder in ${FOLDERS[@]+"${FOLDERS[@]}"}; do + matched=false + while IFS='|' read -r packaging _ _ _ basedir _; do + if [ "$packaging" = nbm ] && in_folder "$basedir" "$folder"; then + matched=true + break + fi + done <<< "$PROJECTS" + if [ "$matched" = false ]; then + echo "::warning::$folder has no plugin module in the root pom.xml's list; skipping" + fi +done + +if [ ${#TARGETS[@]} -eq 0 ]; then + echo "No plugin modules to test." + exit 0 +fi + +PASSED=() +FAILED=() +for target in "${TARGETS[@]}"; do + IFS='|' read -r group artifact version jar repo <<< "$target" + gav="$group:$artifact:$version" + installed="$repo/${group//.//}/$artifact/$version/$artifact-$version.jar" + if [ ! -f "$installed" ]; then + echo "::error::$gav isn't installed in $repo, run mvn install first" + FAILED+=("$gav") + continue + fi + if [ -f "$jar" ] && ! cmp -s "$jar" "$installed"; then + echo "::error::$gav was rebuilt since it was installed, run mvn install again" + FAILED+=("$gav") + continue + fi + + rm -rf "$HARNESS_DIR/target" + echo "::group::Contract tests: $gav" + if mvn -B -ntp -f "$HARNESS_DIR/pom.xml" test -Dgephi.version="$GEPHI_VERSION" \ + -Dplugin.groupId="$group" -Dplugin.artifactId="$artifact" -Dplugin.version="$version"; then + echo "::endgroup::" + else + echo "::endgroup::" + echo "::error::SPI contract tests failed for $gav" + FAILED+=("$gav") + continue + fi + PASSED+=("$gav") +done + +if [ ${#PASSED[@]} -gt 0 ]; then + echo "SPI contract tests passed for: ${PASSED[*]}" +fi +if [ ${#FAILED[@]} -gt 0 ]; then + echo "SPI contract tests failed for: ${FAILED[*]}" + exit 1 +fi diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f1f4f852a..84f721c52 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,4 +24,7 @@ jobs: echo "No plugin modules present under modules/; skipping build/validate (see test-generation workflow for scaffold validation)." exit 0 fi - mvn -B package --file pom.xml \ No newline at end of file + mvn -B install --file pom.xml + - name: Run SPI contract tests + if: hashFiles('modules/*/pom.xml') != '' + run: .github/contract-tests/run.sh diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml new file mode 100644 index 000000000..a120762d2 --- /dev/null +++ b/.github/workflows/pr.yml @@ -0,0 +1,41 @@ +name: PR + +on: + pull_request: + branches: + - master-forge + +jobs: + build_and_test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + # The PR's merge commit and its first parent (the base), to diff against + fetch-depth: 2 + - name: Set up JDK 17 + uses: actions/setup-java@v5 + with: + java-version: '17' + distribution: 'temurin' + - name: Build project with Maven + run: mvn -B install --file pom.xml + - name: Run SPI contract tests on changed plugins + run: | + CHANGED_FILES=$(git -c core.quotePath=false diff --name-only HEAD^1 HEAD) + # A root pom.xml change (gephi.version bump, re-enabled , ...) or a harness change + # can affect every plugin, so test them all + if grep -qxE 'pom\.xml|\.github/contract-tests/.*' <<< "$CHANGED_FILES"; then + exec .github/contract-tests/run.sh + fi + FOLDERS=() + while IFS= read -r folder; do + if [ -d "$folder" ]; then + FOLDERS+=("$folder") + fi + done < <(grep '^modules/' <<< "$CHANGED_FILES" | cut -d/ -f1-2 | sort -u) + if [ ${#FOLDERS[@]} -eq 0 ]; then + echo "No plugin modules changed; skipping." + exit 0 + fi + .github/contract-tests/run.sh "${FOLDERS[@]}" diff --git a/.github/workflows/test-generation.yml b/.github/workflows/test-generation.yml index c1794c069..b3b20de0f 100644 --- a/.github/workflows/test-generation.yml +++ b/.github/workflows/test-generation.yml @@ -49,7 +49,23 @@ jobs: grep -q 'modules/CiIntegrationTestPlugin' pom.xml - name: Build, validate and generate the autoupdate site - run: mvn -B package -P release --file pom.xml + run: mvn -B install -P release --file pom.xml + + # The fixture implements no SPI, so this runs no test cases: it checks the contract-test + # harness still resolves gephi-plugins-tests and gephi-plugin-parent for this gephi.version. + - name: Run SPI contract tests against the fixture + run: .github/contract-tests/run.sh modules/CiIntegrationTestPlugin + + # Positive control: core Gephi's layout-plugin implements a covered SPI, so the harness must + # run actual test cases against it. Guards against the harness silently testing nothing. + - name: Check the contract-test harness runs test cases + run: | + set -e + GEPHI_VERSION=$(sed -nE 's/.*(.*)<\/gephi\.version>.*/\1/p' pom.xml | head -1) + rm -rf .github/contract-tests/target + mvn -B -f .github/contract-tests/pom.xml test -Dgephi.version="$GEPHI_VERSION" \ + -Dplugin.groupId=org.gephi -Dplugin.artifactId=layout-plugin -Dplugin.version="$GEPHI_VERSION" + grep -ho 'tests="[0-9]*"' .github/contract-tests/target/surefire-reports/TEST-*.xml | grep -qv 'tests="0"' - name: Assert the generation process produced a working autoupdate site run: | diff --git a/AGENTS.md b/AGENTS.md index 1082a1c96..83d1be150 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,6 +24,8 @@ Requires JDK 17 and Maven. - Build and validate every plugin currently listed in `pom.xml`: `mvn clean package` - Build/test a single plugin module: `mvn -pl modules/ clean package` - Run a single module's unit tests only: `mvn -pl modules/ test` +- Run the SPI contract tests CI runs (from `gephi-plugins-tests`) on a module, after `mvn install`: + `.github/contract-tests/run.sh modules/` - Run Gephi with the module(s) installed (build first): `mvn org.gephi:gephi-maven-plugin:run` - IDE run/debug configs are defined in `nbactions.xml`; the IntelliJ debug setup (remote debugger + `-Drun.params.debug` VM option) is documented in `README.md`. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 605edae87..74020a9d5 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -38,9 +38,21 @@ different purposes on three different branches. Confusion here (e.g. "why is ``. ## How Gephi can be extended @@ -144,7 +156,8 @@ everything above. Its goals, bound in this repo's root `pom.xml` or invoked dire ## Root files ```text -.github/workflows/ # build.yml (feature branches), test-generation.yml (master), release-pom.yml (parent-pom) +.github/workflows/ # build.yml (feature branches), pr.yml (PRs to master-forge), test-generation.yml (master), release-pom.yml (parent-pom) +.github/contract-tests/ # CI harness running gephi-plugins-tests' SPI contract tests against plugin modules modules/ # one folder per plugin/suite; empty list on master until you add one plugins/ # static assets (images) used by the gephi.org plugin listing, not source code pom.xml # reactor POM for this checkout's plugin modules — NOT the plugin parent POM diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 17d4c09ec..6ebd82d28 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -337,6 +337,11 @@ See "Gephi platform conventions" above for the reasoning; on review, check that using the `org-netbeans-modules-nbjunit` dependency per `README.md`'s testing section. - Tests are not disabled or skipped (`@Ignore`, `-DskipTests` baked into the module's own `pom.xml`) to force a green build. +- The PR's "Run SPI contract tests on changed plugins" CI step passes. It runs + `gephi-plugins-tests`' contract tests for every SPI implementation each changed module registers; + locally, `mvn install` then `.github/contract-tests/run.sh modules/`. The step runs the + PR's own copy of `.github/`, so a pass only counts if the PR leaves `.github/` untouched (see + "Build actually passes" above). ### PR completeness From 937f32b25b1beaedee986e8b8e120819fba2e952 Mon Sep 17 00:00:00 2001 From: Mathieu Bastian Date: Sun, 8 Nov 2026 20:19:44 +0100 Subject: [PATCH 3/3] Use gephi-plugins-tests 1.2.0 and report its warnings (#374) (#375) Checks that only warn (a layout adding nodes, a statistics without a user interface...) don't fail the run: run.sh turns the warnings the tests write to contract-warnings.tsv into ::warning:: annotations and, on GitHub Actions, a table in the job summary. The jars of the modules that depend on the tested one are put on the test classpath, so a plugin whose user interface is in another module (e.g. BoundingDiameters) has it found. Co-authored-by: Claude Sonnet 5.5 --- .github/contract-tests/pom.xml | 4 +- .github/contract-tests/run.sh | 68 +++++++++++++++++++++++++++++++++- AGENTS.md | 3 +- 3 files changed, 71 insertions(+), 4 deletions(-) diff --git a/.github/contract-tests/pom.xml b/.github/contract-tests/pom.xml index 6872d46cf..d70d903a7 100644 --- a/.github/contract-tests/pom.xml +++ b/.github/contract-tests/pom.xml @@ -14,7 +14,7 @@ UTF-8 - 1.1.0 + 1.2.0 ${plugin.artifactId}-${plugin.version}.jar + + ${project.build.directory}/contract-warnings.tsv org.gephi:gephi-plugins-tests diff --git a/.github/contract-tests/run.sh b/.github/contract-tests/run.sh index 724cbed58..2adc2836b 100755 --- a/.github/contract-tests/run.sh +++ b/.github/contract-tests/run.sh @@ -6,7 +6,9 @@ # # With no arguments, every module in the root pom.xml's list is tested. Otherwise only # NBM modules under the given folders are; a folder outside the reactor (e.g. a disabled plugin) -# is skipped with a warning. Modules must be installed in the local Maven repository first +# is skipped with a warning. Checks that are only advisory (e.g. a layout adding nodes) don't fail +# the run: they are reported as warning annotations, and in the job summary when running on GitHub +# Actions. Modules must be installed in the local Maven repository first # (mvn install): a module that is missing there, or was rebuilt since, fails instead of testing a # stale jar. set -euo pipefail @@ -75,6 +77,44 @@ if [ ${#TARGETS[@]} -eq 0 ]; then exit 0 fi +# Advisory test warnings, one "plugincheckmessage" entry each +WARNINGS_FILE="$HARNESS_DIR/target/contract-warnings.tsv" +WARNINGS=() + +# Workflow command data can't contain %, the warnings are single lines already +annotation_escape() { + printf '%s' "$1" | sed -e 's/%/%25/g' +} + +# Plugin names and messages come from the plugin's code, so don't let them format the summary +markdown_escape() { + printf '%s' "$1" | sed -e 's/&/\&/g' -e 's//\>/g' -e 's/|/\\|/g' -e 's/`/\\`/g' +} + +collect_warnings() { + [ -f "$WARNINGS_FILE" ] || return 0 + local check message + while IFS=$'\t' read -r check message; do + WARNINGS+=("$1"$'\t'"$check"$'\t'"$message") + echo "::warning title=SPI contract test warning::$(annotation_escape "$1: $check: $message")" + done < "$WARNINGS_FILE" +} + +# The jars of the modules that depend on the one being tested, installed in the local repository. A plugin split in +# modules keeps parts of it (e.g. its user interface) in modules that depend on the main one, and the tests look +# those up too. +dependent_jars() { + local packaging group artifact version basedir repo jar + while IFS='|' read -r packaging group artifact version basedir _ repo; do + [ "$packaging" = nbm ] || continue + [ "$artifact" != "$1" ] || continue + grep -q "$1" "$basedir/pom.xml" || continue + jar="$repo/${group//.//}/$artifact/$version/$artifact-$version.jar" + [ -f "$jar" ] && echo "$jar" + done <<< "$PROJECTS" + return 0 +} + PASSED=() FAILED=() for target in "${TARGETS[@]}"; do @@ -92,20 +132,44 @@ for target in "${TARGETS[@]}"; do continue fi + EXTRA_ARGS=() + EXTRA_JARS=$(dependent_jars "$artifact" | paste -sd, -) + if [ -n "$EXTRA_JARS" ]; then + EXTRA_ARGS=(-Dmaven.test.additionalClasspath="$EXTRA_JARS") + fi + rm -rf "$HARNESS_DIR/target" echo "::group::Contract tests: $gav" if mvn -B -ntp -f "$HARNESS_DIR/pom.xml" test -Dgephi.version="$GEPHI_VERSION" \ - -Dplugin.groupId="$group" -Dplugin.artifactId="$artifact" -Dplugin.version="$version"; then + -Dplugin.groupId="$group" -Dplugin.artifactId="$artifact" -Dplugin.version="$version" \ + ${EXTRA_ARGS[@]+"${EXTRA_ARGS[@]}"}; then echo "::endgroup::" else echo "::endgroup::" echo "::error::SPI contract tests failed for $gav" + collect_warnings "$gav" FAILED+=("$gav") continue fi + collect_warnings "$gav" PASSED+=("$gav") done +if [ ${#WARNINGS[@]} -gt 0 ] && [ -n "${GITHUB_STEP_SUMMARY:-}" ]; then + { + echo "### SPI contract test warnings" + echo + echo "These checks don't fail the build, but are worth a look in review." + echo + echo "| Plugin | Check | Warning |" + echo "| --- | --- | --- |" + for warning in "${WARNINGS[@]}"; do + IFS=$'\t' read -r gav check message <<< "$warning" + echo "| $(markdown_escape "$gav") | $(markdown_escape "$check") | $(markdown_escape "$message") |" + done + } >> "$GITHUB_STEP_SUMMARY" +fi + if [ ${#PASSED[@]} -gt 0 ]; then echo "SPI contract tests passed for: ${PASSED[*]}" fi diff --git a/AGENTS.md b/AGENTS.md index 83d1be150..616df2c2c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,7 +25,8 @@ Requires JDK 17 and Maven. - Build/test a single plugin module: `mvn -pl modules/ clean package` - Run a single module's unit tests only: `mvn -pl modules/ test` - Run the SPI contract tests CI runs (from `gephi-plugins-tests`) on a module, after `mvn install`: - `.github/contract-tests/run.sh modules/` + `.github/contract-tests/run.sh modules/`. A failing check fails the run; advisory checks + (e.g. a layout adding nodes) only print `::warning::` lines and, in CI, a job summary table. - Run Gephi with the module(s) installed (build first): `mvn org.gephi:gephi-maven-plugin:run` - IDE run/debug configs are defined in `nbactions.xml`; the IntelliJ debug setup (remote debugger + `-Drun.params.debug` VM option) is documented in `README.md`.