Skip to content

Commit d45de49

Browse files
Update HELP.md
1 parent 4649b3b commit d45de49

1 file changed

Lines changed: 364 additions & 0 deletions

File tree

HELP.md

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,367 @@ While most of the inheritance is fine, it also inherits unwanted elements like `
1414
To prevent this, the project POM contains empty overrides for these elements.
1515
If you manually switch to a different parent and actually want the inheritance, you need to remove those overrides.
1616

17+
18+
# Publish an Open‑Source Java Library to Maven Central (2025 edition)
19+
20+
*An end‑to‑end, copy‑pasteable guide using a fictional org **OpenJavaLibs** and library **pojo-helper**. It covers the new Central Publisher Portal flow (tokens + the Central Publishing Maven Plugin), GPG signing, POM metadata, `settings.xml`, and optional CI with GitHub Actions.*
21+
22+
---
23+
24+
## 0) What you’ll need
25+
26+
* **Java**: JDK 17+ (`java -version`)
27+
* **Maven**: 3.9+ (`mvn -v`)
28+
* **GPG**: `gpg --version` (for signing)
29+
* **Git & GitHub**
30+
* A few minutes on **central.sonatype.com** to create a namespace and a **publishing token**
31+
32+
> Why this guide? As of 2024–2025, new (and migrated) projects publish via the **Central Publisher Portal** using **tokens** and the **Central Publishing Maven Plugin** — not the legacy OSSRH “staging” plugin.
33+
34+
---
35+
36+
## 1) Create your GitHub organization and repository
37+
38+
1. In GitHub, create an org: **OpenJavaLibs**
39+
2. Create a public repo: **pojo-helper**
40+
3. Initialize locally:
41+
42+
```bash
43+
mkdir pojo-helper && cd pojo-helper
44+
git init -b main
45+
cat > README.md <<'EOF'
46+
# pojo-helper
47+
Small helpers for working with POJOs.
48+
EOF
49+
cat > .gitignore <<'EOF'
50+
.target/
51+
.idea/
52+
*.class
53+
.mvn/wrapper/
54+
.DS_Store
55+
EOF
56+
57+
mkdir -p src/main/java/io/github/openjavalibs/pojohelper
58+
cat > src/main/java/io/github/openjavalibs/pojohelper/PojoUtils.java <<'EOF'
59+
package io.github.openjavalibs.pojohelper;
60+
61+
public final class PojoUtils {
62+
private PojoUtils() {}
63+
public static boolean isBlank(String s) { return s == null || s.trim().isEmpty(); }
64+
}
65+
EOF
66+
67+
git add .
68+
git commit -m "chore: bootstrap pojo-helper"
69+
# Add your remote and push
70+
# git remote add origin git@github.com:OpenJavaLibs/pojo-helper.git
71+
# git push -u origin main
72+
```
73+
74+
**Naming the coordinates:** we’ll publish under the namespace `io.github.openjavalibs`, so your Maven coordinates will be:
75+
76+
```
77+
<groupId>io.github.openjavalibs</groupId>
78+
<artifactId>pojo-helper</artifactId>
79+
<version>1.0.0</version>
80+
```
81+
82+
---
83+
84+
## 2) Claim your namespace on the Central Portal
85+
86+
1. Go to **central.sonatype.com** and **Sign in with GitHub**.
87+
2. Create/claim a **Namespace**. Two common options:
88+
89+
* **GitHub-based** (fast & free): `io.github.<your-github-org-or-user>` → e.g., `io.github.openjavalibs`.
90+
* **DNS-based** (if you own a domain): `com.example` style.
91+
3. Verify ownership as guided by the Portal. After verification, you can publish any **groupId** that **starts with** that namespace.
92+
93+
> Tip: If you previously used OSSRH (`oss.sonatype.org`) you can migrate to the Portal. For brand‑new projects, start with the Portal.
94+
95+
---
96+
97+
## 3) Generate a **publishing token** (Central Portal → Account Settings)
98+
99+
1. In the Portal, open **Account Settings → Generate User Token**.
100+
2. Copy the **token username** and **token password** once — you won’t see them again. Store securely (e.g., a password manager / CI secrets).
101+
102+
We’ll place these in Maven’s `settings.xml` as the credentials for server id **`central`**.
103+
104+
---
105+
106+
## 4) Create and publish your **GPG key**
107+
108+
Maven Central requires **PGP/GPG signatures** on every artifact.
109+
110+
```bash
111+
# 4.1 Generate a new RSA 4096-bit key (use your name/email)
112+
gpg --full-generate-key
113+
114+
# 4.2 List and copy the LONG key id (40 hex chars)
115+
gpg --list-secret-keys --keyid-format LONG
116+
117+
# 4.3 Upload your public key so others can verify signatures
118+
# (keys.openpgp.org is widely used)
119+
gpg --keyserver keys.openpgp.org --send-keys <YOUR_LONG_KEY_ID>
120+
121+
# 4.4 Optional: export keys for CI
122+
gpg --armor --export <YOUR_LONG_KEY_ID> > public.gpg
123+
gpg --armor --export-secret-keys <YOUR_LONG_KEY_ID> > private.gpg
124+
```
125+
126+
> You’ll use the **long key id** in Maven config. For CI, prefer importing the private key from a secret and passing the passphrase via env vars.
127+
128+
---
129+
130+
## 5) Create a Central‑ready `pom.xml`
131+
132+
This POM includes required metadata (**name, description, license, SCM, developers**), generates **sources** and **javadoc** JARs, signs artifacts with **GPG**, and wires the **Central Publishing Maven Plugin** (`org.sonatype.central:central-publishing-maven-plugin`).
133+
134+
> Replace names/URLs as appropriate. Keep `<groupId>` within your claimed namespace, e.g., `io.github.openjavalibs`.
135+
136+
```xml
137+
<!-- pom.xml at the project root -->
138+
<project xmlns="http://maven.apache.org/POM/4.0.0"
139+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
140+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
141+
<modelVersion>4.0.0</modelVersion>
142+
143+
<groupId>io.github.openjavalibs</groupId>
144+
<artifactId>pojo-helper</artifactId>
145+
<version>1.0.0</version>
146+
<name>${project.groupId}:${project.artifactId}</name>
147+
<description>Small helpers for working with POJOs</description>
148+
<url>https://github.com/OpenJavaLibs/pojo-helper</url>
149+
150+
<licenses>
151+
<license>
152+
<name>Apache License, Version 2.0</name>
153+
<url>https://www.apache.org/licenses/LICENSE-2.0.txt</url>
154+
<distribution>repo</distribution>
155+
</license>
156+
</licenses>
157+
158+
<developers>
159+
<developer>
160+
<id>openjavalibs</id>
161+
<name>OpenJavaLibs Team</name>
162+
<organization>OpenJavaLibs</organization>
163+
<organizationUrl>https://github.com/OpenJavaLibs</organizationUrl>
164+
</developer>
165+
</developers>
166+
167+
<scm>
168+
<connection>scm:git:https://github.com/OpenJavaLibs/pojo-helper.git</connection>
169+
<developerConnection>scm:git:ssh://git@github.com/OpenJavaLibs/pojo-helper.git</developerConnection>
170+
<url>https://github.com/OpenJavaLibs/pojo-helper</url>
171+
<tag>HEAD</tag>
172+
</scm>
173+
174+
<properties>
175+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
176+
<maven.compiler.release>17</maven.compiler.release>
177+
178+
<!-- GPG -->
179+
<gpg.executable>gpg</gpg.executable>
180+
<!-- Use your long key id; or omit to use the default key -->
181+
<gpg.keyname>80FB99EFC46EEF609CA94FC06F71D93E0823BB8B</gpg.keyname>
182+
</properties>
183+
184+
<build>
185+
<plugins>
186+
<!-- Compile with modern JDK -->
187+
<plugin>
188+
<groupId>org.apache.maven.plugins</groupId>
189+
<artifactId>maven-compiler-plugin</artifactId>
190+
<version>3.13.0</version>
191+
<configuration>
192+
<release>${maven.compiler.release}</release>
193+
</configuration>
194+
</plugin>
195+
196+
<!-- Attach -sources.jar -->
197+
<plugin>
198+
<groupId>org.apache.maven.plugins</groupId>
199+
<artifactId>maven-source-plugin</artifactId>
200+
<version>3.3.1</version>
201+
<executions>
202+
<execution>
203+
<id>attach-sources</id>
204+
<goals>
205+
<goal>jar</goal>
206+
</goals>
207+
</execution>
208+
</executions>
209+
</plugin>
210+
211+
<!-- Attach -javadoc.jar -->
212+
<plugin>
213+
<groupId>org.apache.maven.plugins</groupId>
214+
<artifactId>maven-javadoc-plugin</artifactId>
215+
<version>3.7.0</version>
216+
<executions>
217+
<execution>
218+
<id>attach-javadocs</id>
219+
<goals>
220+
<goal>jar</goal>
221+
</goals>
222+
</execution>
223+
</executions>
224+
</plugin>
225+
226+
<!-- GPG sign in the verify phase (non‑interactive CI friendly) -->
227+
<plugin>
228+
<groupId>org.apache.maven.plugins</groupId>
229+
<artifactId>maven-gpg-plugin</artifactId>
230+
<version>3.2.4</version>
231+
<executions>
232+
<execution>
233+
<id>sign-artifacts</id>
234+
<phase>verify</phase>
235+
<goals>
236+
<goal>sign</goal>
237+
</goals>
238+
<configuration>
239+
<gpgExecutable>${gpg.executable}</gpgExecutable>
240+
<keyname>${gpg.keyname}</keyname>
241+
<!-- Read passphrase from env; rely on gpg-agent if unset -->
242+
<passphrase>${env.GPG_PASSPHRASE}</passphrase>
243+
<gpgArguments>
244+
<arg>--pinentry-mode</arg>
245+
<arg>loopback</arg>
246+
</gpgArguments>
247+
</configuration>
248+
</execution>
249+
</executions>
250+
</plugin>
251+
252+
<!-- Central Publishing Maven Plugin (Portal) -->
253+
<plugin>
254+
<groupId>org.sonatype.central</groupId>
255+
<artifactId>central-publishing-maven-plugin</artifactId>
256+
<version>0.8.0</version>
257+
<extensions>true</extensions>
258+
<configuration>
259+
<!-- matches server id in settings.xml -->
260+
<publishingServerId>central</publishingServerId>
261+
<!-- Optional (CI): publish automatically after validation -->
262+
<!-- <autoPublish>true</autoPublish> -->
263+
<!-- Optional: block until published; requires autoPublish -->
264+
<!-- <waitUntil>published</waitUntil> -->
265+
</configuration>
266+
</plugin>
267+
268+
</plugins>
269+
</build>
270+
271+
<!-- No legacy distributionManagement is required for the Portal plugin -->
272+
</project>
273+
```
274+
275+
> If you’re migrating from OSSRH (the old `nexus-staging-maven-plugin`), remove that plugin and wire the Central Publishing plugin instead.
276+
277+
---
278+
279+
## 6) Configure `~/.m2/settings.xml` (credentials + GPG)
280+
281+
Add the **Portal token** under server id **`central`** and (optionally) a GPG profile. Do this on any machine/CI runner that performs publishing.
282+
283+
```xml
284+
<!-- ~/.m2/settings.xml -->
285+
<settings>
286+
<servers>
287+
<server>
288+
<!-- MUST match <publishingServerId> in pom.xml -->
289+
<id>central</id>
290+
<username>${env.CENTRAL_TOKEN_USERNAME}</username>
291+
<password>${env.CENTRAL_TOKEN_PASSWORD}</password>
292+
</server>
293+
</servers>
294+
295+
<profiles>
296+
<profile>
297+
<id>gpg</id>
298+
<properties>
299+
<gpg.executable>gpg</gpg.executable>
300+
<!-- Optional: set if you have multiple keys -->
301+
<gpg.keyname>80FB99EFC46EEF609CA94FC06F71D93E0823BB8B</gpg.keyname>
302+
<!-- Avoid plaintext: pass via env in CI. Locally you can omit to use gpg-agent -->
303+
<gpg.passphrase>${env.GPG_PASSPHRASE}</gpg.passphrase>
304+
</properties>
305+
</profile>
306+
</profiles>
307+
308+
<activeProfiles>
309+
<activeProfile>gpg</activeProfile>
310+
</activeProfiles>
311+
</settings>
312+
```
313+
314+
> **Security tip:** Use Maven’s **encrypted passwords** for local dev, or provide credentials via CI secrets/env vars. The Central plugin supports Maven password encryption.
315+
316+
---
317+
318+
## 7) First local build (unsigned vs signed)
319+
320+
* Quick local build without signing (for development only):
321+
322+
```bash
323+
mvn -q -Dgpg.skip clean verify
324+
```
325+
326+
* Full release build (signs artifacts):
327+
328+
```bash
329+
export GPG_PASSPHRASE='••••••••' # if you’re not using gpg-agent
330+
mvn -q clean deploy
331+
```
332+
333+
You should see the Central Publishing plugin stage a bundle and upload it. If `autoPublish` is **not** set, you’ll be asked to publish the validated deployment manually in the Portal UI.
334+
335+
---
336+
337+
## 8) Publish in the Portal (manual or automatic)
338+
339+
* **Manual:** After `mvn deploy`, open the **Deployments** page in the Portal, review the validation, and **Publish**.
340+
* **Automatic (CI‑friendly):** In the POM plugin config, set:
341+
342+
```xml
343+
<autoPublish>true</autoPublish>
344+
<waitUntil>published</waitUntil>
345+
```
346+
347+
This publishes automatically after validation and blocks until published (fail‑fast if validation fails).
348+
349+
---
350+
351+
## 9) Verify the release
352+
353+
* Search your coordinates on **central.sonatype.com** (Search).
354+
* Check the canonical repository URL: `https://repo1.maven.org/maven2/io/github/openjavalibs/pojo-helper/`
355+
* Consumers can now depend on it:
356+
357+
```xml
358+
<dependency>
359+
<groupId>io.github.openjavalibs</groupId>
360+
<artifactId>pojo-helper</artifactId>
361+
<version>1.0.0</version>
362+
</dependency>
363+
```
364+
365+
---
366+
367+
## 10) Optional: GitHub Actions CI for releases
368+
369+
Create `.github/workflows/release.yml` to publish on tag push.
370+
371+
```yaml
372+
name: Release to Maven Central
373+
on:
374+
push:
375+
tags:
376+
- 'v*'
377+
378+
jobs:
379+
```
380+

0 commit comments

Comments
 (0)