Skip to content
Open
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
48 changes: 48 additions & 0 deletions .github/workflows/binding-publish-maven.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
name: Publish Java bindings (placeholder, publishes nothing)

on:
workflow_dispatch:
inputs:
targets:
description: 'Comma-separated package-key:java targets, or all'
type: string
default: ''
dry_run:
description: 'Must stay true: the workflow is a placeholder and verifies and publishes nothing'
type: boolean
default: true

permissions:
contents: read

concurrency:
group: binding-publish-maven
cancel-in-progress: false

jobs:
publish:
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-24.04
timeout-minutes: 15

steps:
- name: Reject real publication
if: ${{ !inputs.dry_run }}
run: |
echo "::error::Maven publication is not implemented. Run with dry_run enabled; nothing was published."
exit 1
- name: Record that nothing was verified or published
env:
TARGETS: ${{ inputs.targets }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
set -euo pipefail
{
echo "## Placeholder: nothing was verified or published"
echo
echo "Maven publication is not implemented. This run did not verify, build, or publish any package."
echo
echo "- targets: \`${TARGETS:-<none>}\`"
echo "- dry_run: \`${DRY_RUN}\`"
} >> "$GITHUB_STEP_SUMMARY"
echo "::notice::Placeholder only. No package was verified or published."
31 changes: 31 additions & 0 deletions .github/workflows/extension-binding-normalizer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:
- "tooling/extension-bindings/IMPLEMENTATION.md"
- "tooling/extension-bindings/README.md"
- "tooling/extension-bindings/emitters/go/**"
- "tooling/extension-bindings/emitters/java/**"
- "tooling/extension-bindings/emitters/typescript/**"
- "tooling/extension-bindings/model/**"
- "tooling/extension-bindings/normalizer/**"
Expand All @@ -19,6 +20,7 @@ on:
- "tooling/extension-bindings/IMPLEMENTATION.md"
- "tooling/extension-bindings/README.md"
- "tooling/extension-bindings/emitters/go/**"
- "tooling/extension-bindings/emitters/java/**"
- "tooling/extension-bindings/emitters/typescript/**"
- "tooling/extension-bindings/model/**"
- "tooling/extension-bindings/normalizer/**"
Expand Down Expand Up @@ -111,3 +113,32 @@ jobs:

- name: Test the TypeScript emitter and generated conformance packages
run: npm test

verify-java-emitter:
name: Verify Java emitter
runs-on: ubuntu-latest
timeout-minutes: 20

defaults:
run:
working-directory: tooling/extension-bindings/emitters/java

steps:
- name: Check out repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
persist-credentials: false

- name: Set up Java
uses: actions/setup-java@de7274f081f381c8f8158605e0321c36c376e2e6 # v6.0.1
with:
distribution: temurin
java-version: 21.0.12

- name: Set up Gradle
uses: gradle/actions/setup-gradle@3f5f9adaf7d9fecd50b5935e54106014257a94e6 # v6.4.0
with:
gradle-version: 8.14.2

- name: Test the Java emitter and generated conformance packages
run: gradle test --console=plain
52 changes: 50 additions & 2 deletions tooling/extension-bindings/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

`IMPLEMENTATION.md` is historical design guidance; the repository owner's
standing overrides govern generation and publication.
The shared Go resolver and normalizer feed native Go and Python emitters. A native TypeScript
emitter lives under `emitters/typescript/`.
The shared Go resolver and normalizer feed native Go and Python emitters. Native TypeScript
and Java emitters live under `emitters/typescript/` and `emitters/java/`.
The Go orchestrator exposes eight commands through Go's standard `flag` package.
GitHub Actions generates and commits Go and Python bindings to `main`, then a
separate manual workflow publishes verified Go module tags and release assets.
Expand Down Expand Up @@ -144,10 +144,58 @@ For local checks:
python3 -m unittest discover -s tooling/common -p 'test_*.py'
python3 -m unittest discover -s tooling/extension-bindings/ci -p 'test_*.py'
(cd tooling/extension-bindings/emitters/typescript && npm ci --ignore-scripts && npm test)
(cd tooling/extension-bindings/emitters/java && gradle test)
python3 tooling/common/build_extensions_site.py --output /tmp/extension-site
python3 tooling/extension-bindings/ci/publish.py configure --targets env-configuration:go
```

## Java emitter quickstart

The Java emitter is a standalone program under `emitters/java/`. It needs JDK 21 and
Gradle 8.14.2, the version pinned in `toolchain.lock.yaml` and used by CI; install
both before you start (the build selects its JDK through a Gradle toolchain). The
orchestrator does not run the Java emitter yet, so the three inputs the orchestrator
supplies to the other emitters are supplied by hand:

- the normalized binding model, which the shared normalizer writes. The committed
conformance models under `model/conformance/expected/` are ready-made examples;
- a package target, which selects the Maven coordinates and the Java package. Start
from `emitters/java/testdata/package-target.yaml`;
- `emitterSha256`, the SHA-256 of the emitter build, which is recorded in the
generated binding manifest. For a local run any digest of your emitter build will
do; the orchestrator will supply the pinned one.

```sh
cd tooling/extension-bindings/emitters/java
gradle installDist
EMITTER=build/install/runtimeconditions-binding-emitter-java/bin/runtimeconditions-binding-emitter-java
SHA=$(sha256sum build/install/runtimeconditions-binding-emitter-java/lib/runtimeconditions-binding-emitter-java-*.jar | cut -d' ' -f1)
MODEL=../../model/conformance/expected/01-owned-kind-interface/runtimeconditions.binding-model.yaml
{ cat testdata/package-target.yaml; echo "emitterSha256: $SHA"; } > /tmp/target.yaml

$EMITTER emit --model $MODEL --package-config /tmp/target.yaml --output /tmp/java-package
$EMITTER verify --model $MODEL --package-config /tmp/target.yaml --input /tmp/java-package
```

`emit` writes a Maven project (`pom.xml`, `src/main/java/...`, and the binding
manifest under `src/main/resources/runtimeconditions/`) into a new empty directory.
`verify` checks its API, `pom.xml`, and manifest against the same model and target
and prints the API surface as JSON. `api --input DIR` prints just that surface. A
dependency that provides another extension's declarations must be listed in the
target's `dependencies`, together with the owner of any imported declaration, even
when that owner is only a transitive dependency.

A consuming project adds the generated package as a Maven dependency and calls the
generated function with the generated field types:

```java
import io.runtimeconditions.bindings.conformance.ownedkindinterface.Bindings;
import io.runtimeconditions.bindings.conformance.ownedkindinterface.Http;
import io.runtimeconditions.bindings.conformance.ownedkindinterface.Region;

var declaration = Bindings.service(new Http("https://api.example.com"), new Region("eu-west-1"));
```

`publish.py build` takes `--rc`, `--core-schema`, and a new `--output` directory.
`publish.py promote --dry-run` validates those packaged artifacts against a clean
checkout and the live discovery pages. It takes `GH_TOKEN` or `GITHUB_TOKEN` for
Expand Down
2 changes: 2 additions & 0 deletions tooling/extension-bindings/emitters/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gradle/
build/
58 changes: 58 additions & 0 deletions tooling/extension-bindings/emitters/java/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
plugins {
application
}

group = "io.runtimeconditions.bindings"
version = "0.1.0"

repositories {
mavenCentral()
}

dependencyLocking {
lockAllConfigurations()
}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

dependencies {
implementation("org.snakeyaml:snakeyaml-engine:3.2")
implementation("com.networknt:json-schema-validator:1.5.9")
implementation("com.fasterxml.jackson.core:jackson-databind:2.22.4")
runtimeOnly("org.slf4j:slf4j-nop:2.0.17")

testImplementation(platform("org.junit:junit-bom:6.1.3"))
testImplementation("org.junit.jupiter:junit-jupiter")
testRuntimeOnly("org.junit.platform:junit-platform-launcher")
}

application {
applicationName = "runtimeconditions-binding-emitter-java"
mainClass = "io.runtimeconditions.bindings.emitter.Main"
}

tasks.processResources {
filesMatching("emitter.properties") {
expand("name" to application.applicationName, "version" to project.version)
}
from(layout.projectDirectory.dir("templates")) {
into("templates")
}
from(layout.projectDirectory.file("package-target.schema.yaml")) {
into("target")
}
from(layout.projectDirectory.file("../../model/runtimeconditions.binding-model.schema.yaml")) {
into("model")
}
}

tasks.test {
useJUnitPlatform()
systemProperty("rc.emitter.root", projectDir.absolutePath)
systemProperty("rc.gradle.version", gradle.gradleVersion)
maxHeapSize = "1g"
}
26 changes: 26 additions & 0 deletions tooling/extension-bindings/emitters/java/gradle.lockfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# This is a Gradle generated file for dependency locking.
# Manual edits can break the build and are not advised.
# This file is expected to be part of source control.
com.ethlo.time:itu:1.14.0=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-annotations:2.22=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-core:2.22.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.core:jackson-databind:2.22.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.22.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.fasterxml.jackson:jackson-bom:2.22.4=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
com.networknt:json-schema-validator:1.5.9=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath
org.jspecify:jspecify:1.0.0=testCompileClasspath
org.junit.jupiter:junit-jupiter-api:6.1.3=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter-engine:6.1.3=testRuntimeClasspath
org.junit.jupiter:junit-jupiter-params:6.1.3=testCompileClasspath,testRuntimeClasspath
org.junit.jupiter:junit-jupiter:6.1.3=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-commons:6.1.3=testCompileClasspath,testRuntimeClasspath
org.junit.platform:junit-platform-engine:6.1.3=testRuntimeClasspath
org.junit.platform:junit-platform-launcher:6.1.3=testRuntimeClasspath
org.junit:junit-bom:6.1.3=testCompileClasspath,testRuntimeClasspath
org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-api:2.0.17=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:slf4j-nop:2.0.17=runtimeClasspath,testRuntimeClasspath
org.snakeyaml:snakeyaml-engine:3.2=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
org.yaml:snakeyaml:2.5=compileClasspath,runtimeClasspath,testCompileClasspath,testRuntimeClasspath
empty=annotationProcessor,testAnnotationProcessor
102 changes: 102 additions & 0 deletions tooling/extension-bindings/emitters/java/package-target.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
$schema: https://json-schema.org/draft/2020-12/schema
$id: https://runtimeconditions.io/schemas/java-package-target/v1alpha1
title: Runtime Conditions Java package target
type: object
additionalProperties: false
required:
- apiVersion
- kind
- packageKey
- rootExtension
- groupId
- artifactId
- javaPackage
- version
- sourceDirectory
- minimumJavaVersion
- publicationMode
- repositoryUrl
properties:
apiVersion:
const: runtimeconditions.io/java-package-target/v1alpha1
kind:
const: RuntimeConditionsJavaPackageTarget
packageKey:
type: string
pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
rootExtension:
type: string
minLength: 1
groupId:
description: The Maven groupId.
type: string
maxLength: 128
pattern: '^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$'
artifactId:
description: The Maven artifactId.
type: string
maxLength: 128
pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
javaPackage:
description: The Java package every generated type is declared in. It is never inferred from extension vocabulary.
type: string
pattern: '^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$'
version:
type: string
pattern: '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-(?:alpha|beta|rc)\.(0|[1-9][0-9]*))?$'
sourceDirectory:
type: string
pattern: '^bindings/[a-z0-9]+(?:-[a-z0-9]+)*/java$'
minimumJavaVersion:
type: string
pattern: '^(?:1[7-9]|[2-9][0-9]|[1-9][0-9]{2,})$'
publicationMode:
enum: [github-tag, registry]
registryId:
type: string
minLength: 1
repositoryUrl:
type: string
pattern: '^https://[^\s]+$'
emitterSha256:
description: Required when emitting package resources; supplied by the tooling caller.
type: string
pattern: '^[0-9a-f]{64}$'
dependencies:
type: array
items:
type: object
additionalProperties: false
required: [extension, groupId, artifactId, javaPackage, version]
properties:
extension:
type: string
minLength: 1
groupId:
type: string
maxLength: 128
pattern: '^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$'
artifactId:
type: string
maxLength: 128
pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$'
javaPackage:
type: string
pattern: '^[a-z][a-z0-9_]*(?:\.[a-z][a-z0-9_]*)*$'
version:
type: string
pattern: '^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)(?:-(?:alpha|beta|rc)\.(0|[1-9][0-9]*))?$'
versionRange:
description: A Maven version range for this dependency. When absent, the range from the declared version up to the next breaking version is used.
type: string
minLength: 1
allOf:
- if:
properties:
publicationMode:
const: registry
then:
required: [registryId]
else:
not:
required: [registryId]
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
rootProject.name = "runtimeconditions-binding-emitter-java"
Loading
Loading