# `jbx.json`
`jbx.json` is the project descriptor for publishing or installing a Java artifact with `jbx`. Keep it small: put durable artifact metadata here, and keep command-specific experiments on the command line.
JSON Schema: [`/schemas/jbx-json/v1.json`](/schemas/jbx-json/v1.json)
## Minimal descriptor
Use this when you only need local install/serve workflows or when Git metadata can fill in Maven Central metadata later:
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"main": "HelloTool.java",
"group": "dev.telegraphic.demo",
"id": "hello-tool",
"version": "1.0.0",
"java": "25",
"dependencies": ["info.picocli:picocli:4.7.7"],
"runtimeDependencies": ["org.slf4j:slf4j-nop:2.0.17"]
}
```
Try it locally before publishing:
```bash
jbx publish --file jbx.json --dry-run
jbx publish --file jbx.json --serve 0
jbx install --file jbx.json --destination build/local-m2
```
## Maven Central-ready descriptor
Real publishing needs normal Maven metadata: project URL, license, developer, and SCM. `jbx` can infer some values from GitHub, but explicit metadata is better for repeatable releases.
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"main": "dev.telegraphic.demo.HelloTool",
"group": "dev.telegraphic.demo",
"id": "hello-tool",
"version": "1.0.0",
"name": "Hello Tool",
"description": "Small CLI published with jbx.",
"url": "https://github.com/telegraphic-dev/hello-tool",
"licenses": [
{
"name": "Apache-2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
],
"developers": [
{
"name": "Telegraphic",
"organization": "Telegraphic",
"organizationUrl": "https://telegraphic.dev"
}
],
"scm": {
"connection": "scm:git:https://github.com/telegraphic-dev/hello-tool.git",
"developerConnection": "scm:git:ssh://git@github.com/telegraphic-dev/hello-tool.git",
"url": "https://github.com/telegraphic-dev/hello-tool"
},
"sources": ["HelloTool.java"],
"repositories": ["snapshots=https://repo.example.test/maven"]
}
```
Run a dry run first. Real publishing still needs Central Portal and signing credentials configured in the environment or command options.
```bash
jbx publish --file jbx.json --dry-run
jbx publish --file jbx.json --publish
```
## Fields
- `$schema` — optional editor hint. Use `https://jbx.telegraphic.dev/schemas/jbx-json/v1.json`. `jbx` ignores unknown fields, so this is safe.
- `main` — main Java source. Relative paths resolve from the descriptor directory. Existing paths win; otherwise `jbx` also checks `.java`, `.jsh`, `.jav`, and Java FQN matches under the descriptor directory.
- `group` — Maven `groupId`. Required for publish/install unless `//GAV` supplies it.
- `id` — Maven `artifactId`. Required for publish/install unless `//GAV` supplies it.
- `version` — artifact version. Maven Central publishing rejects `-SNAPSHOT`. `--version` can override it for one command.
- `package` — Java package override used when staging compact or unpackaged sources for publishing.
- `name` — human-readable POM name. Defaults to `group:id`.
- `description` — POM description. Falls back to `//DESCRIPTION` or a generated description.
- `url` — project URL. Required for Maven Central dry-run/publish.
- `licenses` — license objects with required `name` and `url`. Required for Maven Central dry-run/publish.
- `developers` — developer objects with required `name` and optional `email`, `organization`, `organizationUrl`. Required for Maven Central dry-run/publish.
- `scm` — SCM object with required `connection` and `url`, optional `developerConnection`. Required for Maven Central dry-run/publish.
- `java` — requested Java version, equivalent to `//JAVA`.
- `dependencies` — compile-time/public Maven coordinates, equivalent to `//DEPS`. Rendered as normal Maven dependencies.
- `runtimeDependencies` — runtime-only Maven coordinates, equivalent to `//RUNTIME`. Rendered with Maven `runtime` scope and not required on the compile classpath.
- `sources` — additional source files to include. Omit the field to let `jbx` auto-discover local Java sources; set it explicitly when you need a controlled source list.
- `repositories` — extra Maven repositories for resolution. Entries can be bare URLs or `id=url`.
## Real-life patterns
### Publish a small CLI with Picocli
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"main": "CleanImports.java",
"group": "com.acme.tools",
"id": "clean-imports",
"version": "0.3.0",
"dependencies": ["info.picocli:picocli:4.7.7"]
}
```
```bash
jbx publish --file jbx.json --dry-run
```
### Keep parser/logging providers runtime-only
Use `runtimeDependencies` for implementation libraries needed when the tool runs but not when callers compile against the artifact:
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"main": "dev.acme.RewriteTool",
"group": "dev.acme",
"id": "rewrite-tool",
"version": "1.2.0",
"dependencies": ["org.openrewrite:rewrite-java:8.56.1"],
"runtimeDependencies": [
"org.openrewrite:rewrite-java-21:8.56.1",
"org.slf4j:slf4j-nop:2.0.17"
]
}
```
### Pin the published source set
If the repository has helpers, examples, or generated files you do not want in the artifact, set `sources` explicitly:
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"main": "App.java",
"group": "dev.acme",
"id": "app",
"version": "1.0.0",
"sources": [
"App.java",
"AppSupport.java"
]
}
```
### Publish helper artifacts from GitHub Actions
`telegraphic-dev/jbx-utils` is a good shape for a real repository: one repo, several small helper artifacts, each with its own descriptor. The `jbx-rewrite/jbx.json` descriptor keeps shared Maven metadata explicit and only names the source and dependencies for that artifact:
```json
{
"$schema": "https://jbx.telegraphic.dev/schemas/jbx-json/v1.json",
"group": "dev.telegraphic.jbx",
"version": "0.1.0",
"package": "dev.telegraphic.jbx",
"url": "https://github.com/telegraphic-dev/jbx-utils",
"licenses": [
{ "name": "MIT License", "url": "https://opensource.org/licenses/MIT" }
],
"developers": [
{ "name": "Telegraphic", "organizationUrl": "https://github.com/telegraphic-dev" }
],
"scm": {
"connection": "scm:git:https://github.com/telegraphic-dev/jbx-utils.git",
"developerConnection": "scm:git:ssh://git@github.com/telegraphic-dev/jbx-utils.git",
"url": "https://github.com/telegraphic-dev/jbx-utils"
},
"java": "21",
"main": "src/JbxRewrite.java",
"id": "jbx-rewrite",
"name": "jbx-rewrite",
"description": "OpenRewrite runner and recipe-discovery helper used by jbx rewrite",
"dependencies": [
"org.openrewrite:rewrite-core:8.56.1",
"org.openrewrite:rewrite-java:8.56.1"
]
}
```
Use CI to prove the bundle layout before release. `jbx-utils` runs dry-run publishing without signing for every helper artifact:
```yaml
name: CI
on:
pull_request:
push:
branches: [main]
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '25'
- uses: dtolnay/rust-toolchain@stable
- name: Install jbx
run: cargo install --git https://github.com/telegraphic-dev/jbx.git --locked jbx
- name: Verify publish bundle
run: scripts/verify-publish-bundle.sh
```
Then keep real Maven Central uploads in a separate release/manual workflow. The workflow needs these GitHub secrets: `CENTRAL_TOKEN_USERNAME`, `CENTRAL_TOKEN_PASSWORD`, `GPG_PRIVATE_KEY`, `GPG_PASSPHRASE`, and `GPG_KEY_ID`.
```yaml
name: Publish to Maven Central
on:
release:
types: [published]
workflow_dispatch:
inputs:
version:
description: Maven artifact version to publish, e.g. 0.1.0
required: true
permissions:
contents: read
jobs:
publish:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project: [jbx-check, jbx-graph, jbx-rewrite]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '25'
- uses: dtolnay/rust-toolchain@stable
- name: Install jbx
run: cargo install --git https://github.com/telegraphic-dev/jbx.git --locked jbx
- name: Import GPG signing key
uses: crazy-max/ghaction-import-gpg@v6
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.GPG_PASSPHRASE }}
- name: Publish ${{ matrix.project }}
env:
CENTRAL_TOKEN_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
CENTRAL_TOKEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }}
run: |
VERSION="${{ github.event.inputs.version }}"
if [ -z "$VERSION" ]; then
VERSION="${GITHUB_REF_NAME#v}"
fi
jbx publish \
--publish \
--file "${{ matrix.project }}/jbx.json" \
--version "$VERSION" \
--gpg-key "$GPG_KEY_ID" \
--output "target/${{ matrix.project }}-central-bundle.zip" \
--target-dir "target/publish/${{ matrix.project }}" \
--cache-dir .jbx-cache
```
Two details matter:
- The PR workflow runs `--dry-run --skip-signing` through a script so contributors can verify jars, POMs, sources, javadocs, docs sidecars, and bundle layout without secrets.
- Only the release/manual workflow imports the signing key and runs `--publish`; publishing from ordinary PR CI would be reckless and noisy.
## Descriptor and directives
Descriptor fields override or replace matching source directives where both exist:
- `group`, `id`, `version` replace `//GAV`.
- `description` replaces `//DESCRIPTION`.
- `java` replaces `//JAVA`.
- `dependencies` replaces `//DEPS` when non-empty.
- `runtimeDependencies` replaces `//RUNTIME` when non-empty.
- `sources` replaces `//SOURCES` when non-empty.
- `repositories` replaces `//REPOS` when non-empty.
That keeps a library release reproducible without forcing all metadata into the Java file.
## Verification checklist
- `main` resolves from the descriptor directory.
- `group`, `id`, and `version` produce the intended Maven coordinate.
- Compile-time libraries are in `dependencies`; runtime-only providers are in `runtimeDependencies`.
- `jbx publish --file jbx.json --dry-run` succeeds before any real publish.
- For local-only checks, `jbx install --file jbx.json --destination build/local-m2` writes the expected Maven layout.