Building and testing

What you need

  • A JDK 21. The build targets Java 21 and the tests are skipped on anything older.

  • Maven 3.6.3 or newer — or nothing at all, and use the bundled wrapper ./mvnw.

bash
./mvnw clean install

That builds every module, runs the unit tests, applies the license and formatting checks, and produces the distributions under assemblies/.

Useful invocations

Command What it does

./mvnw clean install

The full build.

./mvnw clean install -Pfast-build

Skips tests, Spotless, RAT, Javadoc and coverage. For a quick compile, never for a build you intend to trust.

./mvnw clean install -Dassemblies=false

Builds the code but not the distributions. Much faster when you are only changing Java.

./mvnw clean install -pl plugins/transforms/foo -am

One module and what it depends on.

./mvnw spotless:apply

Reformats the code. Run this before committing.

./mvnw test -pl core

Unit tests of a single module.

Profiles

Several profiles activate themselves, which is why the build usually needs no flags:

base / assemblies

Decide which module sets are in the reactor. Both are on unless you pass -Dbase=false or -Dassemblies=false.

swt-unix / swt-mac / swt-windows

Selected by your operating system, and pick the matching SWT artifact. swt-mac also adds -XstartOnFirstThread to the test JVM, which SWT needs on macOS.

assembly

Activates for any module that has a src/assembly folder, and packages it as a zip. This is why a plugin needs no assembly configuration in its pom.

filtered-resources

Activates for any module with a src/main/resources-filtered folder.

And the opt-in ones:

fast-build

Skips every check and every test.

uitest / skip-uitest

Run only, or skip, the SWTBot UI tests. See UI tests with SWTBot.

webtest

The Selenium tests that drive Hop Web in a container. They need Docker and take minutes, so they are out of the normal build: mvn -Pwebtest -pl web-tests test.

apache-release / deploy-snapshots

Release plumbing, including the CycloneDX SBOM. See the release process.

Building behind a firewall

Maven dependencies and plugins go through the <mirror> in your settings.xml. Besides Maven Central, a few modules declare their own repositories (Clojars, Confluent, Splunk, JetBrains), so your repository manager has to proxy those too.

Four files are not Maven artifacts and are downloaded directly, ignoring <mirror>:

Property Default

swtbot.plugins.url

https://download.eclipse.org/technology/swtbot/releases/${swtbot.version}/plugins (two jars)

tm4e.core.download.url

https://download.eclipse.org/tm4e/releases/${tm4e.core.version}/plugins/${tm4e.core.download.file}

monaco.download.url

https://registry.npmjs.org/monaco-editor/-/monaco-editor-${monaco.version}.tgz

To fetch them from a repository manager, set these properties in a profile in your settings.xml. download.auth.serverId names the <server> whose credentials are used for all four downloads. Keep the ${…​} placeholders: Maven fills them in, so the profile keeps working when Hop updates these versions.

xml
<settings>
  <servers>
    <server>
      <id>artifactory</id>
      <username>...</username>
      <password>...</password>
    </server>
  </servers>
  <profiles>
    <profile>
      <id>hop-downloads</id>
      <properties>
        <download.auth.serverId>artifactory</download.auth.serverId>
        <swtbot.plugins.url>https://artifactory.example.com/eclipse/technology/swtbot/releases/${swtbot.version}/plugins</swtbot.plugins.url>
        <tm4e.core.download.url>https://artifactory.example.com/eclipse/tm4e/releases/${tm4e.core.version}/plugins/${tm4e.core.download.file}</tm4e.core.download.url>
        <monaco.download.url>https://artifactory.example.com/api/npm/npm/monaco-editor/-/monaco-editor-${monaco.version}.tgz</monaco.download.url>
      </properties>
    </profile>
  </profiles>
  <activeProfiles>
    <activeProfile>hop-downloads</activeProfile>
  </activeProfiles>
</settings>

The same properties also work as -D options on the command line.

Checks that must pass

Formatting — Spotless

Java is formatted with google-java-format, imports are ordered and unused ones removed, and pom.xml files are sorted. Run ./mvnw spotless:apply before you commit; CI runs spotless:check and fails on any difference.

License headers — Apache RAT

Every source and text file needs the Apache License 2.0 header, including new .adoc pages. RAT fails the build otherwise. See Setting up your development environment for configuring your IDE to add it automatically.

Annotation index — Jandex

Bound for every module, so plugin discovery works. Nothing to do unless you are building a plugin outside this repository.

Tests

JUnit 5 through Surefire. UI tests are tagged and can be included or excluded, see below.

What CI runs

Jenkinsfile

The per-change build: full clean deploy under xvfb-run so the UI tests have a display, an assembly size check (tools/check-assembly-size.sh — ASF Artifactory rejects packages over roughly 850 MB), then the Docker images and the snapshot deploy.

Jenkinsfile.daily

Build and test plus a code quality stage, and the integration tests against the built image.

Jenkinsfile.hop-web-selenium

The Hop Web Selenium suite.

CI builds with -fae and -Dsurefire.rerunFailingTestsCount=2, so a flaky test can pass on CI and still be a real problem.

This page is a scaffold. Still to write:

  • Troubleshooting a failing build: the usual suspects and their fixes.

  • tools/find_duplicate_jars.sh finds duplicate copies, and tools/check-assembly-jar-versions.py reports different versions of one library in the client zip. See Dependencies and classloading.

  • Building only the distribution you need, and how to run from assemblies/client/target.