Skip to content

Commit cc652d4

Browse files
committed
Issue #148 scope jdt2jar Dockerfile to submodule, add GHCR publish and CI Docker builds
- Move Dockerfile from repo root to jdt2jar/ (built with -f jdt2jar/Dockerfile .) - Add jdt2jar/.dockerignore to exclude build artifacts - Rewrite jdt2jar/README.md with full Docker/GHCR usage instructions - Update root README.md to signpost to jdt2jar submodule for optional tool - Add docker-build job to ci.yml (Java 25, smoke test) - Add docker-build job to maven.yml (Java 25, smoke test after tests pass) - Add docker-publish job to release-on-tag.yml (pushes version + latest to ghcr.io) - Release notes now include Docker image pull commands How to verify: docker build -t jdt2jar -f jdt2jar/Dockerfile . docker run --rm jdt2jar --help
1 parent 150b958 commit cc652d4

7 files changed

Lines changed: 212 additions & 46 deletions

File tree

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,33 @@ jobs:
4646
sys.exit(1)
4747
print(f"OK totals: {totals}")
4848
PY
49+
50+
docker-build:
51+
name: Build Docker image (Java 25)
52+
runs-on: ubuntu-latest
53+
steps:
54+
- name: Checkout
55+
uses: actions/checkout@v4
56+
57+
- name: Set up JDK 25
58+
uses: actions/setup-java@v4
59+
with:
60+
distribution: oracle
61+
java-version: '25'
62+
cache: 'maven'
63+
64+
- name: Set up Docker Buildx
65+
uses: docker/setup-buildx-action@v3
66+
67+
- name: Build Docker image
68+
uses: docker/build-push-action@v6
69+
with:
70+
context: .
71+
file: jdt2jar/Dockerfile
72+
push: false
73+
load: true
74+
tags: jdt2jar:ci
75+
76+
- name: Smoke test
77+
run: |
78+
docker run --rm jdt2jar:ci --help

.github/workflows/maven.yml

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,35 @@ jobs:
5252
restore-keys: ${{ runner.os }}-m2-java25
5353

5454
- name: Test full project (Java 25)
55-
run: mvn clean test
55+
run: mvn clean test
56+
57+
docker-build:
58+
name: Build Docker image (Java 25)
59+
runs-on: ubuntu-latest
60+
needs: test-java25
61+
62+
steps:
63+
- uses: actions/checkout@v4
64+
65+
- name: Set up JDK 25
66+
uses: actions/setup-java@v4
67+
with:
68+
java-version: '25'
69+
distribution: 'oracle'
70+
cache: 'maven'
71+
72+
- name: Set up Docker Buildx
73+
uses: docker/setup-buildx-action@v3
74+
75+
- name: Build Docker image
76+
uses: docker/build-push-action@v6
77+
with:
78+
context: .
79+
file: jdt2jar/Dockerfile
80+
push: false
81+
load: true
82+
tags: jdt2jar:ci
83+
84+
- name: Smoke test
85+
run: |
86+
docker run --rm jdt2jar:ci --help

.github/workflows/release-on-tag.yml

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@ on:
66
- 'release/[0-9]*.[0-9]*.[0-9]*'
77

88
permissions:
9-
contents: write # push tags, push commits
9+
contents: write
1010
pull-requests: write
11+
packages: write
1112

1213
concurrency:
1314
group: release-${{ github.ref }}
@@ -34,11 +35,26 @@ jobs:
3435
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
3536
gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }}
3637

38+
- name: Extract version from tag
39+
id: version
40+
run: echo "version=${GITHUB_REF_NAME#release/}" >> "$GITHUB_OUTPUT"
41+
3742
- name: Create GitHub Release with notes
3843
uses: softprops/action-gh-release@v2
3944
with:
4045
tag_name: ${{ github.ref_name }}
4146
generate_release_notes: true
47+
body: |
48+
## Docker Image
49+
50+
Pre-built distroless container image available on GitHub Container Registry:
51+
52+
```bash
53+
docker pull ghcr.io/${{ github.repository_owner }}/java.util.json.java21/jdt2jar:${{ steps.version.outputs.version }}
54+
docker pull ghcr.io/${{ github.repository_owner }}/java.util.json.java21/jdt2jar:latest
55+
```
56+
57+
See [jdt2jar/README.md](https://github.com/${{ github.repository }}/blob/main/jdt2jar/README.md) for usage.
4258
4359
- name: Build and Deploy to Central (release profile)
4460
env:
@@ -76,3 +92,42 @@ jobs:
7692
--base main \
7793
--head "${{ steps.prbranch.outputs.branch }}" \
7894
|| echo "PR already exists or nothing to compare"
95+
96+
docker-publish:
97+
name: Publish Docker image to GHCR
98+
runs-on: ubuntu-latest
99+
needs: release
100+
steps:
101+
- name: Checkout
102+
uses: actions/checkout@v4
103+
104+
- name: Set up JDK 25
105+
uses: actions/setup-java@v4
106+
with:
107+
distribution: oracle
108+
java-version: '25'
109+
cache: 'maven'
110+
111+
- name: Set up Docker Buildx
112+
uses: docker/setup-buildx-action@v3
113+
114+
- name: Log in to GHCR
115+
uses: docker/login-action@v3
116+
with:
117+
registry: ghcr.io
118+
username: ${{ github.actor }}
119+
password: ${{ secrets.GITHUB_TOKEN }}
120+
121+
- name: Extract version from tag
122+
id: version
123+
run: echo "version=${GITHUB_REF_NAME#release/}" >> "$GITHUB_OUTPUT"
124+
125+
- name: Build and push Docker image
126+
uses: docker/build-push-action@v6
127+
with:
128+
context: .
129+
file: jdt2jar/Dockerfile
130+
push: true
131+
tags: |
132+
ghcr.io/${{ github.repository_owner }}/java.util.json.java21/jdt2jar:${{ steps.version.outputs.version }}
133+
ghcr.io/${{ github.repository_owner }}/java.util.json.java21/jdt2jar:latest

README.md

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,7 @@ Such vulnerabilities existed at one point in the upstream OpenJDK sandbox implem
336336

337337
## JSON Type Definition (JTD) Validator
338338

339-
This repo contains an incubating JTD validator that has the core JSON API as its only dependency. This sub-project demonstrates how to build realistic JSON heavy logic using the API. It follows Data Oriented Programming principles: it compiles JTD schemas into an immutable structure of records. For validation it parses the JSON document to the generic structure and uses the thread-safe parsed schema and a stack to visit and validate the parsed JSON.
340-
341-
A complete JSON Type Definition validator is included (module: json-java21-jtd).
339+
A complete JSON Type Definition validator is included (module: `json-java21-jtd`), implementing RFC 8927 with a stack-machine interpreter and optional bytecode codegen (JDK 24+).
342340

343341
### Empty Schema `{}` Semantics (RFC 8927)
344342

@@ -350,43 +348,19 @@ Per **RFC 8927 (JSON Typedef)**, the empty schema `{}` is the **empty form** and
350348
> `empty = {}`
351349
> **Empty form:** A schema in the empty form accepts all JSON values and produces no errors.
352350
353-
⚠️ Note: Some tools or in-house validators mistakenly interpret `{}` as "object with no
354-
properties allowed." **That is not JTD.** This implementation follows RFC 8927 strictly.
355-
356351
```java
357352
import json.java21.jtd.Jtd;
358353
import jdk.sandbox.java.util.json.*;
359354

360-
// Compile JTD schema
361-
JsonValue schema = Json.parse("""
362-
{
363-
"properties": {
364-
"name": {"type": "string"},
365-
"age": {"type": "int32"}
366-
}
367-
}
368-
""");
369-
370-
// Validate JSON
371-
JsonValue data = Json.parse("{\"name\":\"Alice\",\"age\":30}");
355+
JsonValue schema = Json.parse("{\"properties\":{\"name\":{\"type\":\"string\"}}}");
356+
JsonValue data = Json.parse("{\"name\":\"Alice\"}");
372357
Jtd validator = new Jtd();
373358
Jtd.Result result = validator.validate(schema, data);
374359
// result.isValid() => true
375360
```
376361

377362
### JTD RFC 8927 Compliance
378363

379-
The validator provides full RFC 8927 compliance with comprehensive test coverage:
380-
381-
```bash
382-
# Run all JTD compliance tests
383-
./mvnw test -pl json-java21-jtd -Dtest=JtdSpecIT
384-
385-
# Run with detailed logging
386-
./mvnw test -pl json-java21-jtd -Djava.util.logging.ConsoleHandler.level=FINE
387-
```
388-
389-
Features:
390364
- ✅ Eight mutually-exclusive schema forms (RFC 8927 §2.2)
391365
- ✅ Standardized error format with instance and schema paths
392366
- ✅ Primitive type validation with proper ranges
@@ -395,6 +369,12 @@ Features:
395369
- ✅ Discriminator tag exemption from additional properties
396370
- ✅ Stack-based validation preventing StackOverflowError
397371

372+
## JTD to JAR Compiler (Optional)
373+
374+
An optional `jdt2jar` CLI tool and distroless Docker image are available for offline JTD validator packaging. It compiles a JTD schema into a standalone validator JAR at build time, eliminating the JDK 24+ runtime requirement for generated validators. The generated JARs run on JDK 21+.
375+
376+
See [`jdt2jar/README.md`](jdt2jar/README.md) for build instructions, container usage, and the pre-built image on GitHub Container Registry (`ghcr.io`).
377+
398378
## Building
399379

400380
Requires JDK 21 or later. Build with Maven:

jdt2jar/.dockerignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Build outputs
2+
**/target/
3+
4+
# IDE
5+
.idea/
6+
*.iml
7+
.vscode/
8+
.eclipse/
9+
10+
# OS
11+
.DS_Store
12+
13+
# Git
14+
.git/
15+
.gitignore
16+
17+
# Env
18+
.env
19+
20+
# Local test artifacts
21+
/tmp/jdt2jar-*/

Dockerfile renamed to jdt2jar/Dockerfile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@ WORKDIR /build
55
COPY . .
66
RUN ["./mvnw", "-pl", "jdt2jar", "-am", "package", "-DskipTests", "-Dsurefire.failIfNoSpecifiedTests=false"]
77
RUN ["java", "-cp", "/build/jdt2jar/target/jdt2jar.jar", "json.java21.jdt2jar.build.DockerImageBuilder", "/build/jdt2jar/target/jdt2jar.jar", "/opt/jre"]
8-
RUN ["mkdir", "-p", "/work/tmp"]
9-
RUN ["chmod", "1777", "/work/tmp"]
8+
RUN ["mkdir", "-p", "/empty-work/tmp", "/empty-app"]
109

1110
FROM gcr.io/distroless/base-debian13:nonroot
12-
WORKDIR /work
11+
COPY --from=build --chown=65532:65532 /empty-work /work
12+
COPY --from=build --chown=65532:65532 /empty-app /app
1313
ENV JAVA_TOOL_OPTIONS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0 -Djava.io.tmpdir=/work/tmp -XX:+ExitOnOutOfMemoryError"
1414
COPY --from=build /opt/jre /jre
1515
COPY --from=build /build/jdt2jar/target/jdt2jar.jar /app/jdt2jar.jar
16-
COPY --from=build /work /work
1716
ENTRYPOINT ["/jre/bin/java","-jar","/app/jdt2jar.jar"]

jdt2jar/README.md

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# jdt2jar
22

3-
`jdt2jar` compiles a JTD schema into a standalone validator JAR at build time.
3+
`jdt2jar` compiles a JTD schema into a standalone validator JAR at build time. The generated JAR runs on JDK 21+ with no JDK 24+ runtime dependency.
44

55
## CLI
66

@@ -10,21 +10,71 @@ jdt2jar <schema.json> [options]
1010

1111
Options:
1212

13-
- `--output <path>`: output JAR path
14-
- `--package <name>`: generated package name
15-
- `--class <name>`: validator class name
13+
- `--output <path>`: output JAR path (default: `<schema-name>-validator.jar`)
14+
- `--package <name>`: generated package name (default: `jtd.generated`)
15+
- `--class <name>`: validator class name (default: `SchemaValidator`)
1616
- `--main`: include a standalone `java -jar` entry point
17-
- `--runtime <version>`: target bytecode version
17+
- `--runtime <version>`: target bytecode version (default: 21)
1818
- `--include-sources`: write a companion `.java` file next to the JAR
1919
- `--help`: show usage
2020

21-
## Container
21+
## Container Image
2222

23-
Build with the root `Dockerfile` and run it as a non-root distroless image.
23+
A minimal distroless container image is available for offline schema compilation without a full JDK.
24+
25+
### Pre-built Image (GitHub Container Registry)
26+
27+
```bash
28+
# Pull the latest image
29+
docker pull ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest
30+
31+
# Pull a specific release version
32+
docker pull ghcr.io/simbo1905/java.util.json.java21/jdt2jar:2026.02.05
33+
```
34+
35+
### Build Locally
36+
37+
Requires Docker and JDK 24+ (for the build stage). Build from the repository root:
38+
39+
```bash
40+
docker build -t jdt2jar -f jdt2jar/Dockerfile .
41+
```
42+
43+
### Usage
44+
45+
```bash
46+
# Show help
47+
docker run --rm ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest --help
48+
49+
# Compile a schema to a validator JAR (using docker cp for file I/O)
50+
cid=$(docker create --name jdt2jar-build ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest /work/person.jtd.json --output /work/person-validator.jar --main)
51+
docker cp person.jtd.json jdt2jar-build:/work/person.jtd.json
52+
docker start -a jdt2jar-build
53+
docker cp jdt2jar-build:/work/person-validator.jar .
54+
docker rm jdt2jar-build
55+
56+
# Validate a payload with the generated JAR
57+
java -jar person-validator.jar --validate payload.json
58+
# Or validate inside a container
59+
cid=$(docker create --name jdt2jar-validate --entrypoint /jre/bin/java ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest -jar /work/person-validator.jar --validate /work/payload.json)
60+
docker cp person-validator.jar jdt2jar-validate:/work/person-validator.jar
61+
docker cp payload.json jdt2jar-validate:/work/payload.json
62+
docker start -a jdt2jar-validate
63+
docker rm jdt2jar-validate
64+
```
65+
66+
### Image Properties
67+
68+
- **Base**: `gcr.io/distroless/base-debian13:nonroot`
69+
- **Runtime**: jlink-minimized JDK 24 (~40 MB)
70+
- **Total size**: ~111 MB disk / ~31 MB content
71+
- **User**: `nonroot` (uid 65532)
72+
- **Shell**: none (distroless)
73+
- **Writable directories**: `/work` (for schema input and JAR output)
74+
75+
### Security Scanning
2476

2577
```bash
26-
docker build -t jdt2jar .
27-
docker run --rm jdt2jar --help
28-
syft packages image:jdt2jar
29-
grype jdt2jar
78+
syft packages image:ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest
79+
grype ghcr.io/simbo1905/java.util.json.java21/jdt2jar:latest
3080
```

0 commit comments

Comments
 (0)