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
6 changes: 5 additions & 1 deletion .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,8 @@ jobs:
# display is available, as on a bare ubuntu-latest runner.
run: sudo apt-get update && sudo apt-get install -y xvfb
- name: Build with Maven
run: xvfb-run --auto-servernum mvn -B clean verify
# -Pstandard names a library for the viewer applications, which since jmsfx#112 have no
# default - naming none is refused rather than producing an application with nothing to
# render with. CI covers the standard library; the per-library bundles are the release
# workflow's job.
run: xvfb-run --auto-servernum mvn -B clean verify -Pstandard
53 changes: 38 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
# Publishes a GitHub Release for a tagged jmsfx version.
#
# Tag a commit whose pom version has had -SNAPSHOT removed everywhere, and whose tag equals that version. Edit
# each module's pom.xml by hand rather than mvn versions:set - this reactor has picked up drifted per-module
# versions before when relying on the plugin - then:
# Tag a commit whose pom version has had -SNAPSHOT removed everywhere, and whose tag equals that version:
#
# mvn versions:set -DnewVersion=1.2.0 -DprocessAllModules=true
# git commit -am "Release 1.2.0"
# git tag 1.2.0 && git push origin master 1.2.0
#
# processAllModules is not optional, and its absence is what made versions:set look unreliable here. Since
# jmsfx#102 split the parent from the aggregator, no module declares the aggregator as its parent - so the
# default behaviour, which walks the parent chain, changes the aggregator's own version and nothing else, and
# leaves a tree where one pom says 1.2.0 and nine say 1.1.0-SNAPSHOT. Verify before tagging:
#
# grep -rn "<version>" --include=pom.xml . | grep -v target | grep SNAPSHOT
#
# jmsfx isn't published to Maven Central yet (unlike foxglove) - this only builds jars and attaches them to a
# GitHub Release. Once Central Portal publishing is set up here too, add a deploy step matching foxglove's
# release.yml (central-publishing-maven-plugin, GPG signing, the release profile).
Expand Down Expand Up @@ -74,16 +80,24 @@ jobs:

# Tests already ran via maven.yml's own push-to-master trigger, before this commit was ever tagged - no need
# to run them again here.
# -Pstandard because jmsfx#112 gives the applications one build product per library and no
# default - a build naming none is refused by an enforcer rule rather than quietly producing an
# application with no library in it. The library named here is the one whose artifacts go to
# Central, which is the standard APP-6E one.
- name: Build
run: mvn -B --no-transfer-progress -DskipTests package
run: mvn -B --no-transfer-progress -DskipTests -Pstandard package

# Tag only: a manual run carries a snapshot version, which the Portal will not take, and
# publishing is not what a rehearsal is for. autoPublish is false in the pom, so this stages
# a validated deployment and publishing to Central stays a deliberate click - a bad staging
# can be dropped rather than having to be superseded.
- name: Publish to the Maven Central Portal
if: github.ref_type == 'tag'
run: mvn -B --no-transfer-progress -DskipTests -Prelease deploy
# Both profiles: -Prelease alone now fails the enforcer rule, which is the point of it. That
# is exactly the trap jmsfx#112 describes - naming a profile deactivates an activeByDefault
# one, so a defaulted library would have silently dropped out here and published an
# application that starts and then answers 500 to everything.
run: mvn -B --no-transfer-progress -DskipTests -Pstandard,release deploy
env:
MAVEN_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
MAVEN_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
Expand All @@ -104,13 +118,19 @@ jobs:
# cross-compiling. Maven resolving JavaFX's native classifier from the build host
# works in our favour here: each runner picks up its own platform's natives.
package:
name: Package (${{ matrix.os }})
name: Package (${{ matrix.library }}, ${{ matrix.target.platform }})
needs: build
strategy:
# One platform failing should not deny the others their artifacts.
# One combination failing should not deny the others their artifacts.
fail-fast: false
# A genuine cross product: every library on every platform, which is what jmsfx#112 means by
# one build product per library. Two libraries by three platforms is six jobs today, and
# jmsfx-battleorder makes it nine once jmsfx#81 can generate it - one word on the line below.
matrix:
include:
library:
- standard
- hallux
target:
- os: windows-latest
platform: windows
- os: macos-latest
Expand All @@ -121,7 +141,7 @@ jobs:
# decision about who can run the download, not a housekeeping detail.
- os: ubuntu-24.04
platform: linux
runs-on: ${{ matrix.os }}
runs-on: ${{ matrix.target.os }}
steps:
# The brand submodule feeds jmsfx-server's stylesheet. Maven skips a missing
# resource directory with only a warning, so without this the reactor builds
Expand Down Expand Up @@ -152,7 +172,7 @@ jobs:

- name: Build jmsfx-creator and collect its dependencies
shell: bash
run: mvn -B --no-transfer-progress -DskipTests -pl :jmsfx-creator -am package
run: mvn -B --no-transfer-progress -DskipTests -P${{ matrix.library }} -pl :jmsfx-creator -am package

- name: Stage the application jar alongside its dependencies
shell: bash
Expand Down Expand Up @@ -186,7 +206,7 @@ jobs:
cd jmsfx-viewer/jmsfx-creator/target/dist
ls -la . | sed 's/^/ /'
find . -name "*.jar" | wc -l | xargs echo "jars in image:"
case "${{ matrix.platform }}" in
case "${{ matrix.target.platform }}" in
windows) LAUNCHER="JMSFX Icon Creator/JMSFX Icon Creator.exe" ;;
linux) LAUNCHER="JMSFX Icon Creator/bin/JMSFX Icon Creator" ;;
macos) LAUNCHER="JMSFX Icon Creator.app/Contents/MacOS/JMSFX Icon Creator" ;;
Expand All @@ -198,7 +218,7 @@ jobs:
# Only Linux can do this: a virtual display is available there, where the Windows
# and macOS runners cannot be relied on to have an interactive session. So macOS
# in particular ships without ever having been seen to run - see issue #41.
if: matrix.platform == 'linux'
if: matrix.target.platform == 'linux'
run: |
sudo apt-get update && sudo apt-get install -y xvfb
cd "jmsfx-viewer/jmsfx-creator/target/dist/JMSFX Icon Creator/bin"
Expand All @@ -213,8 +233,11 @@ jobs:
shell: bash
run: |
cd jmsfx-viewer/jmsfx-creator/target/dist
ARCHIVE="jmsfx-creator-${VERSION}-${{ matrix.platform }}"
case "${{ matrix.platform }}" in
# library then platform, matching CreatorDownloads in jmsfx-server - the download page
# builds these names to link to, and nothing checks that the two agree, so
# CreatorDownloadsTest spells them out in full.
ARCHIVE="jmsfx-creator-${VERSION}-${{ matrix.library }}-${{ matrix.target.platform }}"
case "${{ matrix.target.platform }}" in
linux)
tar czf "../${ARCHIVE}.tar.gz" "JMSFX Icon Creator"
;;
Expand All @@ -233,7 +256,7 @@ jobs:
if: github.ref_type != 'tag'
uses: actions/upload-artifact@v7
with:
name: jmsfx-creator-${{ matrix.platform }}
name: jmsfx-creator-${{ matrix.library }}-${{ matrix.target.platform }}
path: |
jmsfx-viewer/jmsfx-creator/target/jmsfx-creator-*.zip
jmsfx-viewer/jmsfx-creator/target/jmsfx-creator-*.tar.gz
Expand Down
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Modules are grouped by deliverable rather than flat (#102), which is also how th
- `jmsfx-viewer/` - `jmsfx-creator`, `jmsfx-server`. A real parent pom: one capability, two front ends.
- `library/` - `jmsfx-standard`, `jmsfx-hallux`, `jmsfx-battleorder`. A **plain directory, not a module** - these are exactly the lifecycles that should not move together. `jmsfx-battleorder` is documentation only until #81 can generate it, so it is not in `<modules>` yet.

**Every build must name a library**: `mvn -Pstandard verify` or `-Phallux` (#112). The profiles live in `jmsfx-viewer` and supply the runtime library both applications ship with; there is no `activeByDefault`, because naming any other profile would deactivate it silently, and an enforcer rule refuses a build that names none. `jmsfx-server`'s fat jar carries the library as a **classifier** - `jmsfx-server-2.0.0-standard.jar` - so the thin unclassified jar beside it is not the one to run.

**`mvn versions:set` needs `-DprocessAllModules=true`.** Since #102 no module declares the root aggregator as its parent, so the default parent-chain walk bumps the aggregator alone and leaves every other pom behind. This is what made the plugin look unreliable here.

A dependency that crosses a group boundary resolves through `${jmsfx.core.version}` or `${jmsfx.standard.version}`, declared in `jmsfx-parent`; both default to `${project.version}`, so pinning one to a released version is what splits that lifecycle off. In-group dependencies still use `${project.version}` directly. Prefer `-pl :jmsfx-server` over `-pl jmsfx-viewer/jmsfx-server` - selecting by artifactId survives a directory move.

## Domain background
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,20 @@ Requires **JDK 25** and Maven. JMSFX is not yet published to Maven Central, so b
```sh
git clone --recurse-submodules https://github.com/ctgnz/jmsfx.git
cd jmsfx
mvn install
mvn -Pstandard install
```

`-Pstandard` names the symbology library the two applications ship with. There is deliberately no
default - a build that names none is refused rather than producing an application with nothing to
render with - so every `mvn` invocation here carries `-Pstandard` or `-Phallux`.

The `--recurse-submodules` matters: `jmsfx-server` takes its branding stylesheet from the [ctg-brand](https://github.com/ctgnz/ctg-brand) submodule, and Maven will quietly skip the missing directory rather than fail if it is absent.

To run the web application locally:

```sh
mvn -pl :jmsfx-server -am verify
java -jar jmsfx-viewer/jmsfx-server/target/jmsfx-server-*.jar
mvn -Pstandard -pl :jmsfx-server -am verify
java -jar jmsfx-viewer/jmsfx-server/target/jmsfx-server-*-standard.jar
```

It serves on port 8080 by default.
Expand Down
4 changes: 2 additions & 2 deletions docs/fragments.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ ends up dominated by changes that have nothing to do with the drawing.
`FragmentNormaliser` puts it back:

```
mvn -q -pl :jmsfx-generator exec:java \
mvn -q -Pstandard -pl :jmsfx-generator exec:java \
-Dexec.mainClass=io.github.ctgnz.jmsfx.generator.FragmentNormaliser -Dexec.args=--apply
```

Expand Down Expand Up @@ -150,7 +150,7 @@ something that is not about its shape would only be noise.
with its ancestors' attributes folded in, and re-run `FragmentMeasurer` to confirm no bounds moved.
4. Mirror it into the other tree. The shared fragments are byte-identical between `jmsfx-standard` and
`hallux`, and are meant to stay that way.
5. `mvn verify`.
5. `mvn -Pstandard verify`.

## Related

Expand Down
12 changes: 0 additions & 12 deletions jmsfx-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,6 @@
<relativePath>../jmsfx-parent/pom.xml</relativePath>
</parent>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>6.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<dependency>
Expand All @@ -39,7 +28,6 @@
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
52 changes: 41 additions & 11 deletions jmsfx-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,32 @@
-->
<jmsfx.core.version>${project.version}</jmsfx.core.version>
<jmsfx.standard.version>${project.version}</jmsfx.standard.version>
<jmsfx.hallux.version>${project.version}</jmsfx.hallux.version>
</properties>

<!--
The test stack, declared once. The junit BOM and hamcrest's version were repeated verbatim in
three module poms, which is three places to forget when either moves. Modules now name the
artefacts and inherit the versions.
-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>6.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>

<profiles>
<profile>
<id>release</id>
Expand Down Expand Up @@ -130,22 +154,23 @@
Portal and publishing stays a deliberate step, so a bad staging can be dropped rather
than having to be superseded.

Only the parent, jmsfx-core and jmsfx-standard belong on Central: the API a consumer
compiles against and the generated library it renders with. The parent is easy to
overlook and resolution breaks for everyone without it. The rest are excluded -
creator, editor and server are applications, delivered as bundles and a deployment.
What goes to Central is jmsfx-parent, jmsfx-core and the libraries: the API a
consumer compiles against, and the generated libraries they render with. The parent
is easy to overlook and resolution breaks for everyone without it. The rest are
excluded - creator, editor and server are applications, delivered as bundles and a
deployment, not things anyone resolves as a dependency.

The generator stays unpublished: jmsfx#102 settled on a monorepo rather than split
library repositories, so every library that generates itself does so from the
generator in the same tree and never resolves it as a dependency. If a library ever
does live elsewhere, jmsfx-generator and jmsfx-tools come off this list together.

jmsfx-hallux is excluded deliberately, and the exclusion is load-bearing rather
than tidy-up. jmsfx#106 brought it into this reactor after 1.5.0 shipped, so the
release profile has never run with it present; without an exclusion the next
-Prelease deploy would publish it to Central, and a publication to Central cannot
be withdrawn. Whether an extension library belongs there is a decision that has
not been taken. Until it is, it stays off.
jmsfx-hallux publishes too, which was an open question until it was settled
deliberately rather than by default: jmsfx#106 brought it into this reactor after
1.5.0 shipped, so the release profile had never run with it present, and it was
held out until someone decided. An extension library is a library - a consumer
renders with it exactly as they would with jmsfx-standard - so Central is where
it belongs.
-->
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
Expand Down Expand Up @@ -175,7 +200,6 @@
<excludeArtifact>jmsfx-viewer</excludeArtifact>
<excludeArtifact>jmsfx-creator</excludeArtifact>
<excludeArtifact>jmsfx-server</excludeArtifact>
<excludeArtifact>jmsfx-hallux</excludeArtifact>
</excludeArtifacts>
</configuration>
</plugin>
Expand Down Expand Up @@ -203,6 +227,12 @@
<artifactId>maven-source-plugin</artifactId>
<version>3.3.0</version>
</plugin>
<plugin>
<!-- Bound in jmsfx-viewer, to refuse a build that names no library. See jmsfx#112. -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
</plugin>
<!-- Tells m2e to skip Spotless during its own incremental/full builds, rather than fail with
"Plugin execution not covered by lifecycle configuration" - `mvn` from the command line still
runs it normally, since pluginManagement doesn't affect that. -->
Expand Down
12 changes: 0 additions & 12 deletions jmsfx-tools/jmsfx-generator/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,6 @@
<jackson.version>2.22.2</jackson.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit</groupId>
<artifactId>junit-bom</artifactId>
<version>6.1.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>
<!-- Used only by FragmentMeasurer, which measures the SVG fragments and writes their bounds
Expand Down Expand Up @@ -94,7 +83,6 @@
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>3.0</version>
<scope>test</scope>
</dependency>
</dependencies>
Expand Down
Loading
Loading