Skip to content

Commit 31f8604

Browse files
dmealingclaude
andcommitted
ci: add publish-java + switch publish-python to token auth
publish-java.yml: tag-triggered (java-v*) Maven Central deploy of the com.metaobjects:* reactor (Java + Kotlin), signed with the GPG key, mirroring the manual 'mvn -Prelease deploy'. Uses the central-publishing plugin's autoPublish + waitUntil=published, so a green run means live on Central. publish-python.yml: use PYPI_API_TOKEN (the pypi.org trusted publisher is unconfigured; the token works today), keeping the OIDC switch documented. All required secrets are set in the repo (NPM_TOKEN, MAVEN_CENTRAL_USERNAME/ PASSWORD, GPG_PRIVATE_KEY, MAVEN_GPG_PASSPHRASE, PYPI_API_TOKEN), so every port now auto-publishes on its tag like C#/NuGet. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LuZWKnWzYGVnESijL7uuky
1 parent d724fec commit 31f8604

2 files changed

Lines changed: 83 additions & 18 deletions

File tree

.github/workflows/publish-java.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
name: publish-java
2+
3+
# Publishes the com.metaobjects:* modules (Java AND Kotlin — they share the
4+
# server/java Maven reactor) to Maven Central via the Sonatype Central Portal,
5+
# signed with the maintainer's GPG key. Mirrors publish-csharp.yml: tag-triggered
6+
# or manual. This is the CI equivalent of the documented manual
7+
# `mvn -Prelease deploy` from server/java (docs/RELEASING.md).
8+
#
9+
# Publishes the versions already in the parent + module poms at the tagged commit
10+
# (the `7.x` line) — bump them first, commit, then tag. The central-publishing
11+
# plugin runs with <autoPublish>true</autoPublish> + <waitUntil>published</waitUntil>,
12+
# so a green run means the artifacts are live on Central (no manual staging
13+
# promotion). Maven Central versions are immutable.
14+
#
15+
# One-time setup (repo Settings -> Secrets and variables -> Actions) — ALREADY SET:
16+
# MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD = the Central Portal token pair
17+
# (the `central` server in the maintainer's ~/.m2/settings.xml).
18+
# GPG_PRIVATE_KEY = ASCII-armored secret key (`gpg --armor --export-secret-keys`).
19+
# MAVEN_GPG_PASSPHRASE = the key's passphrase (empty here — the signing key is
20+
# passphraseless; the empty secret is intentional).
21+
#
22+
# Triggers: a `java-v*` tag (e.g. `java-v7.4.3`), or manual workflow_dispatch.
23+
24+
on:
25+
workflow_dispatch:
26+
push:
27+
tags:
28+
- 'java-v*'
29+
30+
jobs:
31+
publish:
32+
runs-on: ubuntu-latest
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- name: Set up JDK 21 + Maven settings (Central server + GPG import)
37+
uses: actions/setup-java@v4
38+
with:
39+
distribution: temurin
40+
java-version: '21'
41+
# Writes ~/.m2/settings.xml with a `central` server whose creds come from
42+
# the env vars below, and imports the GPG key for maven-gpg-plugin.
43+
server-id: central
44+
server-username: MAVEN_CENTRAL_USERNAME
45+
server-password: MAVEN_CENTRAL_PASSWORD
46+
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
47+
gpg-passphrase: MAVEN_GPG_PASSPHRASE
48+
49+
- name: Allow non-interactive (loopback) GPG signing
50+
run: |
51+
# maven-gpg-plugin 3.x signs via the agent; in CI it must use loopback
52+
# pinentry. The signing key is passphraseless, so an empty passphrase
53+
# plus loopback signs without a prompt.
54+
mkdir -p ~/.gnupg
55+
echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
56+
echo "allow-loopback-pinentry" >> ~/.gnupg/gpg-agent.conf
57+
gpg-connect-agent reloadagent /bye || true
58+
59+
- name: Deploy to Maven Central (signed, auto-published)
60+
working-directory: server/java
61+
env:
62+
MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
63+
MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
64+
MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }}
65+
# Tests run on every PR via the conformance workflow; the release tag is cut
66+
# from already-green main, so the publish build skips them (it still builds
67+
# + signs + attaches the javadoc/sources jars Central requires via -Prelease).
68+
run: mvn -B -Prelease deploy -DskipTests -Dgpg.passphrase="${MAVEN_GPG_PASSPHRASE}"
Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
name: publish-python
22

3-
# Publishes the `metaobjects` Python package to PyPI via Trusted Publishing (OIDC) —
4-
# no API token. PyPI mints short-lived credentials after validating GitHub's OIDC
5-
# token against the trusted publisher configured for the project. See docs/RELEASING.md.
3+
# Publishes the `metaobjects` Python package to PyPI using an API token.
4+
# Mirrors publish-csharp.yml / publish-npm.yml: tag-triggered or manual.
65
#
7-
# One-time setup on PyPI (project `metaobjects` -> Settings -> Publishing ->
8-
# Add a new GitHub publisher):
9-
# Owner: metaobjectsdev
10-
# Repository: metaobjects
11-
# Workflow: publish-python.yml
12-
# Environment: (leave empty unless you uncomment `environment:` below)
13-
# Once that's added you can DELETE any long-lived PyPI API token.
6+
# We use a token (not OIDC Trusted Publishing) because the pypi.org trusted
7+
# publisher for this project isn't configured — the token works today. To switch
8+
# to OIDC later: add a GitHub publisher on PyPI (Owner `metaobjectsdev`, Repo
9+
# `metaobjects`, Workflow `publish-python.yml`), then drop `password:` below and
10+
# add `permissions: id-token: write`.
1411
#
15-
# Triggers: manual (workflow_dispatch) or a python-v* tag.
12+
# One-time setup (repo Settings -> Secrets and variables -> Actions) — ALREADY SET:
13+
# PYPI_API_TOKEN = a PyPI API token (pypi-...) with upload rights to `metaobjects`.
14+
#
15+
# PyPI versions are immutable — bump server/python's version, commit, then tag.
16+
#
17+
# Triggers: a `python-v*` tag (e.g. `python-v0.12.1`), or manual workflow_dispatch.
1618

1719
on:
1820
workflow_dispatch:
@@ -23,12 +25,6 @@ on:
2325
jobs:
2426
publish:
2527
runs-on: ubuntu-latest
26-
# If you scope the PyPI trusted publisher to a GitHub environment, create it in
27-
# repo Settings and uncomment (must match the publisher's Environment exactly):
28-
# environment: release
29-
permissions:
30-
id-token: write # required: lets this job mint a GitHub OIDC token for PyPI
31-
contents: read
3228
steps:
3329
- uses: actions/checkout@v4
3430

@@ -39,7 +35,8 @@ jobs:
3935
working-directory: server/python
4036
run: uv build --out-dir dist
4137

42-
- name: Publish to PyPI (Trusted Publishing)
38+
- name: Publish to PyPI
4339
uses: pypa/gh-action-pypi-publish@release/v1
4440
with:
4541
packages-dir: server/python/dist
42+
password: ${{ secrets.PYPI_API_TOKEN }}

0 commit comments

Comments
 (0)