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
46 changes: 46 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: release

# Publishes the library to Maven Central (Sonatype Central Portal) when a version tag is pushed,
# then creates the matching GitHub Release. Credentials and the signing key are read from repo
# secrets at publish time only -- see RELEASING.md for the one-time setup and how to cut a release.

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Check out source
uses: actions/checkout@v4

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4

- name: Derive release version from tag
id: version
run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Publish to Maven Central
env:
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_KEY_PASSWORD }}
run: ./gradlew publishToMavenCentral -PreleaseVersion=${{ steps.version.outputs.version }} --no-daemon --no-configuration-cache

- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
69 changes: 69 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Releasing JavaSake

JavaSake is published to **Maven Central** (via the Sonatype Central Portal) so downstream projects
can depend on it directly:

```kotlin
dependencies {
implementation("org.openminimed:javasake:<version>")
}
```

Publishing is automated: pushing a `vX.Y.Z` tag builds, signs, and publishes the release, then
creates a GitHub Release (`.github/workflows/release.yml`). Local builds and PRs always use a
`-SNAPSHOT` version and never publish.

## One-time setup (repo admin)

Publishing requires four repository secrets. None are needed to build or test; they are read only by
the `release` workflow.

### 1. Maven Central namespace

The published coordinate uses the `org.openminimed` group, so the Central Portal namespace
`org.openminimed` must be verified once. Two options:

- **`org.openminimed`** (current group) — requires verifying ownership of the `openminimed.org`
domain via a DNS TXT record in the Central Portal. Preferred if the org owns that domain.
- **`io.github.openminimed`** — verified automatically against the GitHub org, no domain needed.
If you prefer this, change `group` in `lib/build.gradle.kts` accordingly before the first release.

### 2. Central Portal user token

In the Central Portal (https://central.sonatype.com) → *Account* → *Generate User Token*. Store the
two halves as:

- `MAVEN_CENTRAL_USERNAME`
- `MAVEN_CENTRAL_PASSWORD`

### 3. GPG signing key

Central requires signed artifacts. Generate a key, publish its public half to a keyserver, then
export the private key in-memory (ASCII-armored):

```bash
gpg --armor --export-secret-keys <KEY_ID>
```

Store as:

- `SIGNING_KEY` — the full ASCII-armored private key block
- `SIGNING_KEY_PASSWORD` — its passphrase

Add all four under *Settings → Secrets and variables → Actions*.

## Cutting a release

1. Decide the version (SemVer, e.g. `0.2.0`).
2. Tag and push:

```bash
git tag v0.2.0
git push origin v0.2.0
```

3. The `release` workflow publishes to Central (auto-released once validation passes) and opens a
GitHub Release. The artifact typically appears on Central within ~15-30 minutes.

Do **not** set the version in `lib/build.gradle.kts`; it is derived from the tag
(`-PreleaseVersion`), so the tag is the single source of truth.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ junitPlatform = "1.11.3"
bouncycastle = "1.79"
spotless = "6.25.0"
googleJavaFormat = "1.22.0"
mavenPublish = "0.30.0"

[libraries]
junit-jupiter-api = { group = "org.junit.jupiter", name = "junit-jupiter-api", version.ref = "junit" }
Expand All @@ -13,3 +14,4 @@ bouncycastle = { group = "org.bouncycastle", name = "bcprov-jdk18on", version.re

[plugins]
spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
55 changes: 53 additions & 2 deletions lib/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import com.vanniktech.maven.publish.JavaLibrary
import com.vanniktech.maven.publish.JavadocJar
import com.vanniktech.maven.publish.SonatypeHost

plugins {
`java-library`
alias(libs.plugins.spotless)
alias(libs.plugins.mavenPublish)
}

group = "org.openminimed"
version = "0.1.0-SNAPSHOT"

// Release version is supplied by CI from the git tag (-PreleaseVersion=x.y.z); local builds and
// PRs fall back to a -SNAPSHOT so nothing is ever accidentally published with a release version.
version = (findProperty("releaseVersion") as String?) ?: "0.1.0-SNAPSHOT"

java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
withSourcesJar()
}

repositories {
Expand Down Expand Up @@ -40,3 +47,47 @@ spotless {
endWithNewline()
}
}

// Publishing to Maven Central via the Sonatype Central Portal. Credentials and the signing key are
// injected from CI secrets at publish time only (see RELEASING.md); build and test need neither.
mavenPublishing {
configure(JavaLibrary(javadocJar = JavadocJar.Javadoc(), sourcesJar = true))
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
signAllPublications()

coordinates(group.toString(), "javasake", version.toString())

pom {
name.set("JavaSake")
description.set(
"SAKE handshake and sequenced AES-CTR/CMAC session cipher for the JVM and Android.",
)
url.set("https://github.com/OpenMinimed/JavaSake")
inceptionYear.set("2026")

licenses {
license {
name.set("GNU General Public License v3.0")
url.set("https://www.gnu.org/licenses/gpl-3.0.txt")
distribution.set("repo")
}
}
developers {
developer {
id.set("palmarci")
name.set("Pal Marci")
url.set("https://github.com/palmarci")
}
developer {
id.set("openminimed")
name.set("OpenMinimed contributors")
url.set("https://github.com/OpenMinimed")
}
}
scm {
url.set("https://github.com/OpenMinimed/JavaSake")
connection.set("scm:git:https://github.com/OpenMinimed/JavaSake.git")
developerConnection.set("scm:git:ssh://git@github.com/OpenMinimed/JavaSake.git")
}
}
}
Loading