Skip to main content

Publish to Maven Central

Bleep ships a built-in bleep publish command. There is no script to write, no plugin to import — your bleep.yaml declares what to publish and bleep publish sonatype does it. PGP signing, Sonatype Central upload, and dynver-based versioning from git tags are all wired in.

Prerequisites

  • A working bleep project (see Your First Project).
  • A Sonatype Central Portal account with a verified namespace (group ID).
  • A PGP key pair. If you don't have one, the setup wizard below will generate one for you.

Step 1: Run the setup wizard

bleep publish setup

This is an interactive TUI that walks you through:

  • Sonatype / Maven Central — generates a PGP key, exports the passphrase to ~/.config/bleep/config.yaml, optionally publishes the public key to a key server.
  • GitHub Packages, GitLab Package Registry, and Google Artifact Registry — same, with provider-specific auth flows.

Skip this step if you already have credentials configured.

Step 2: Add publish metadata to bleep.yaml

Each project that should be published gets a publish: block. Maven Central requires groupId, url, description, at least one developer, and at least one license:

$schema: https://raw.githubusercontent.com/oyvindberg/bleep/master/schema.json
$version: 1.0.0-M9
jvm:
name: graalvm-community:25.0.1
projects:
mylib:
platform:
name: jvm
publish:
groupId: io.github.myusername
url: https://github.com/myusername/mylib
description: A useful library that does useful things.
developers:
- id: myusername
name: My Name
url: https://github.com/myusername
licenses:
- name: Apache-2.0
url: https://www.apache.org/licenses/LICENSE-2.0.txt
sonatypeCredentialHost: central.sonatype.com

Field reference:

fieldrequirednotes
enablednodefaults to true when publish is present. Set to false on a project that inherits a publish block from a template but shouldn't itself be published (e.g. a scripts module).
groupIdyesMaven group, e.g. io.github.myusername. Must match your verified Sonatype namespace.
urlfor CentralProject homepage.
descriptionfor CentralOne-line summary.
developersfor CentralList of { id, name, url }.
licensesfor CentralList of { name, url, distribution? }.
organizationoptionalOrganization name (rendered as <organization> in the POM).
sonatypeProfileNameoptionalDefaults to groupId. Override if your Sonatype profile name differs from your group.
sonatypeCredentialHostoptionalDefaults to central.sonatype.com. Set to oss.sonatype.org if you're still on legacy OSSRH.

The artifact name is the project name. A project called mylib publishes as <groupId>:mylib:<version>. There is no name field — rename the project in bleep.yaml if you want a different artifact id.

Versions come from git tags via dynver. A clean tag like v1.2.3 produces version 1.2.3. Any commit after the tag, or a dirty working tree, produces a -SNAPSHOT suffix. There is no version: field on the project for publishing.

Step 3: Set credentials in your environment

export SONATYPE_USERNAME=your-token-username
export SONATYPE_PASSWORD=your-token-password
export PGP_SECRET=base64-encoded-private-key
export PGP_PASSPHRASE=your-key-passphrase

For Sonatype Central, the username/password are user tokens generated at https://central.sonatype.com/account, not your account login.

To produce PGP_SECRET:

gpg --armor --export-secret-keys YOUR_KEY_ID | base64 | tr -d '\n'

(bleep publish setup does this for you.)

Step 4: Test the package locally

Before pushing to Central, publish to your local ivy cache:

bleep publish local-ivy

Inspect ~/.ivy2/local/<groupId>/<projectName>/ — you should see the JAR, sources JAR, javadoc JAR, and POM. Pull the artifact into another project to verify it resolves cleanly.

Step 5: Publish to Maven Central

bleep publish sonatype

What happens:

  1. Bleep compiles every project that has publish: configured.
  2. dynver infers the version from git tags. With --assert-release, bleep refuses to proceed unless you're on a clean tag.
  3. Each project is packaged into JAR + sources JAR + javadoc JAR + POM, PGP-signed with PGP_SECRET.
  4. The bundle is uploaded to Sonatype Central, which closes and releases the staging repository on success.

Useful flags:

  • --version 1.2.3 — override the dynver-inferred version.
  • --assert-release — fail rather than publish a snapshot. Use this in CI release jobs.
  • mylib — publish only the named project (and its publishable transitive deps).

Cross-published artifacts

A project with a cross: block publishes one Maven artifact per cross variant. Scala cross variants get the standard Scala suffix (mylib_2.13, mylib_3), Scala.js variants pick up _sjs1_3, and so on — the convention matches what sbt produces, so consumers can depend on mylib_3 from any tool. See Cross-building for the workspace shape.

GitHub Actions release workflow

name: Release

on:
push:
tags:
- 'v*'

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # dynver needs full history

- uses: bleep-build/bleep-setup-action@0.0.3

- name: Publish to Maven Central
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
PGP_SECRET: ${{ secrets.PGP_SECRET }}
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
run: bleep publish --assert-release sonatype

fetch-depth: 0 is required so dynver can read the tag history.

Publishing to other repositories

Sonatype is the default for bleep publish sonatype, but bleep publish also generates a subcommand for every named resolver in your bleep.yaml. Declare the resolver:

resolvers:
- name: company-nexus
uri: https://nexus.company.com/repository/maven-releases/

Then:

bleep publish company-nexus

bleep publish setup configures auth for GitHub Packages, GitLab Package Registry, and Google Artifact Registry interactively.

Troubleshooting

"No publishable projects found"

No project in your build has a publish: block. Add one to at least one project.

"version would be a snapshot"

You ran bleep publish sonatype --assert-release on a commit that's not at a clean tag. Either tag the commit (git tag v1.2.3), drop --assert-release to publish a -SNAPSHOT, or pass --version 1.2.3 explicitly.

"Invalid signature" on Sonatype side

Your PGP_SECRET is wrong or your key isn't published to a key server. Re-run bleep publish setup — the wizard publishes your key to the default keyserver as part of setup.

"Repository not found" / staging close fails

Check that your Sonatype namespace (group ID) is verified and matches the groupId in publish:. Sonatype's UI at https://central.sonatype.com/publishing shows recent attempts and their errors.

Next steps