Skip to content
Merged
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
16 changes: 11 additions & 5 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,18 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Publish to GitHub Packages
run: ./gradlew publish
- name: Publish to Maven Central
# Uploads the signed artifacts to the Central Portal and (automaticRelease = true) releases them.
# Requires four repository secrets — see RELEASING.md:
# MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD : a Central Portal user token
# SIGNING_KEY (ASCII-armored GPG private key) / SIGNING_PASSWORD : the signing key + passphrase
run: ./gradlew publishToMavenCentral --no-configuration-cache
env:
GITHUB_ACTOR: ${{ github.actor }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

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_PASSWORD }}

- name: Create GitHub Release
uses: softprops/action-gh-release@v1
with:
Expand Down
22 changes: 13 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ plugins {

dependencies {
// Add as a dependency to the openapi generator
openapiGenerator("de.tum.cit.aet:openapi-generator-angular21:1.0.0")
openapiGenerator("de.tum.cit.aet:openapi-generator-angular21:1.1.0")
}

openApiGenerate {
Expand All @@ -160,7 +160,7 @@ plugins {

dependencies {
// Add as a dependency to the openapi generator
openapiGenerator 'de.tum.cit.aet:openapi-generator-angular21:1.0.0'
openapiGenerator 'de.tum.cit.aet:openapi-generator-angular21:1.1.0'
}

openApiGenerate {
Expand Down Expand Up @@ -205,7 +205,7 @@ openApiGenerate {
<dependency>
<groupId>de.tum.cit.aet</groupId>
<artifactId>openapi-generator-angular21</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
</dependency>
</dependencies>
</plugin>
Expand All @@ -215,10 +215,10 @@ openApiGenerate {

```bash
# Download the generator JAR
wget https://github.com/ls1intum/openapi-generator-angular21/releases/download/v1.0.0/openapi-generator-angular21-1.0.0.jar
wget https://github.com/ls1intum/openapi-generator-angular21/releases/download/v1.1.0/openapi-generator-angular21-1.1.0.jar

# Generate code
java -cp openapi-generator-angular21-1.0.0.jar:openapi-generator-cli-7.18.0.jar \
java -cp openapi-generator-angular21-1.1.0.jar:openapi-generator-cli-7.18.0.jar \
org.openapitools.codegen.OpenAPIGenerator generate \
-g angular21 \
-i openapi.yaml \
Expand Down Expand Up @@ -328,11 +328,15 @@ This generates Angular client code into `build/generated/example` using `example

## Publishing

```bash
# To GitHub Packages
./gradlew publish
The generator is published to **Maven Central**, so consumers resolve it from a plain `mavenCentral()`
repository with no authentication. Releases are automated: pushing a `v*` tag runs the publish job in
[`.github/workflows/build.yml`](.github/workflows/build.yml), which uploads the signed artifacts to the
Central Portal. See **[RELEASING.md](RELEASING.md)** for the one-time account/secret setup and the full
release procedure.

# To Maven Local (for testing)
```bash
# Build the artifacts into your local Maven repository (no signing key required) — this is how
# downstream projects (e.g. Artemis) build the generator from source to regenerate their client.
./gradlew publishToMavenLocal
```

Expand Down
86 changes: 86 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Releasing

This generator is published to **Maven Central** (the Sonatype Central Portal). Consumers — Artemis and
any other project — then resolve it from a plain `mavenCentral()` repository, with **no authentication**:

```kotlin
repositories { mavenCentral() }
dependencies { /* openapiGenerator | classpath */ "de.tum.cit.aet:openapi-generator-angular21:1.1.0" }
```

Publishing is automated by [`.github/workflows/build.yml`](.github/workflows/build.yml): pushing a tag
that starts with `v` (e.g. `v1.1.0`) runs the `publish` job, which uploads the GPG-signed artifacts to
the Central Portal via the [`com.vanniktech.maven.publish`](https://vanniktech.github.io/gradle-maven-publish-plugin/)
plugin and (because `automaticRelease = true`) releases the deployment once it validates.

## One-time setup

These steps are done **once** by a maintainer/org admin; afterwards every release is just a tag push.

### 1. Claim the `de.tum.cit.aet` namespace on the Central Portal

1. Sign in at <https://central.sonatype.com> (GitHub or email).
2. Add the namespace `de.tum.cit.aet` and verify ownership of the `tum.de` domain by publishing the
**DNS `TXT` record** the portal shows you (a one-off verification token).
- This requires access to the `tum.de` DNS zone. If that is not available, the simplest alternative
is to switch the `group` to `io.github.ls1intum` (GitHub-verified, no DNS needed) — but that
changes the artifact coordinates everywhere, so the `tum.de` namespace is preferred.

### 2. Generate a Central Portal user token

In the Central Portal → *Account* → *Generate User Token*. This yields a username/password pair used
for the upload (it is **not** your login password).

### 3. Create a GPG signing key

```bash
gpg --gen-key # create a key for the maintainer/CI identity
gpg --list-secret-keys --keyid-format=long # note the key id
# Publish the public key so Central can verify signatures:
gpg --keyserver keyserver.ubuntu.com --send-keys <KEY_ID>
# Export the private key in the ASCII-armored form the plugin expects:
gpg --armor --export-secret-keys <KEY_ID> # the whole block, including the BEGIN/END lines
```

### 4. Add four GitHub Actions repository secrets

`Settings → Secrets and variables → Actions → New repository secret`:

| Secret | Value |
|--------------------------|-------------------------------------------------------------------|
| `MAVEN_CENTRAL_USERNAME` | Central Portal user-token **username** |
| `MAVEN_CENTRAL_PASSWORD` | Central Portal user-token **password** |
| `SIGNING_KEY` | the full ASCII-armored GPG **private** key from step 3 |
| `SIGNING_PASSWORD` | the passphrase for that key |

The workflow maps these onto the Gradle properties the plugin reads
(`ORG_GRADLE_PROJECT_mavenCentralUsername`, `…Password`, `…signingInMemoryKey`, `…signingInMemoryKeyPassword`).

## Cutting a release

1. Bump `version` in [`build.gradle.kts`](build.gradle.kts) (and the version references in
[`README.md`](README.md)) to the new release version, e.g. `1.2.0`. Use a non-`SNAPSHOT` version.
2. Merge to `main`.
3. Tag and push:
```bash
git tag v1.2.0
git push origin v1.2.0
```
4. The `publish` job signs and uploads to the Central Portal and creates a GitHub Release. The artifact
appears on Maven Central within ~15–30 minutes (search index can lag a few hours).

## Releasing locally (fallback)

If you ever need to publish without CI, provide the same four values as Gradle properties (e.g. in
`~/.gradle/gradle.properties`: `mavenCentralUsername`, `mavenCentralPassword`, `signingInMemoryKey`,
`signingInMemoryKeyPassword`) and run:

```bash
./gradlew publishToMavenCentral --no-configuration-cache
```

## Building without releasing

`./gradlew publishToMavenLocal` installs the artifacts into your local `~/.m2` repository and **requires
no signing key** (signing is gated on the key being present). This is the path downstream projects use to
build the generator from source when they want to regenerate their client before a version is on Central.
95 changes: 51 additions & 44 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import com.vanniktech.maven.publish.SonatypeHost
import org.gradle.plugins.signing.Sign

plugins {
java
`java-library`
`maven-publish`
signing
// Publishes signed artifacts to the Maven Central (Sonatype) Portal. Replaces the manual
// maven-publish + signing setup: it wires the sources/javadoc jars, the POM, GPG signing, and the
// Central Portal upload. See RELEASING.md for the required credentials and the release procedure.
id("com.vanniktech.maven.publish") version "0.30.0"
}

group = "de.tum.cit.aet"
version = "1.0.0"
version = "1.1.0"

java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
withJavadocJar()
withSourcesJar()
}

repositories {
Expand All @@ -23,7 +26,7 @@ repositories {
val openapiGeneratorCli by configurations.creating

dependencies {
val openapiGeneratorVersion = "7.18.0"
val openapiGeneratorVersion = "7.21.0"

// OpenAPI Generator core dependency
implementation("org.openapitools:openapi-generator:$openapiGeneratorVersion")
Expand Down Expand Up @@ -63,48 +66,52 @@ tasks.withType<Jar> {
}
}

publishing {
publications {
create<MavenPublication>("mavenJava") {
from(components["java"])

pom {
name.set("OpenAPI Generator Angular 21")
description.set("Custom OpenAPI Generator for modern Angular 21 with httpResource and signals")
url.set("https://github.com/ls1intum/openapi-generator-angular21")

licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}

developers {
developer {
id.set("ls1intum")
name.set("LS1 TUM")
email.set("krusche@tum.de")
}
}

scm {
connection.set("scm:git:git://github.com/ls1intum/openapi-generator-angular21.git")
developerConnection.set("scm:git:ssh://github.com/ls1intum/openapi-generator-angular21.git")
url.set("https://github.com/ls1intum/openapi-generator-angular21")
}
mavenPublishing {
// Upload to the Maven Central Portal (central.sonatype.com) and release automatically once the
// staged deployment validates. Consumers then resolve the artifact from plain mavenCentral() with
// no authentication.
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true)
// All artifacts must be GPG-signed for Maven Central. The signing key is provided via Gradle
// properties / environment variables in CI (see RELEASING.md); signing is skipped for
// publishToMavenLocal, so building from source needs no key.
signAllPublications()

coordinates(group.toString(), "openapi-generator-angular21", version.toString())

pom {
name.set("OpenAPI Generator Angular 21")
description.set("Custom OpenAPI Generator for modern Angular 21 with httpResource and signals")
url.set("https://github.com/ls1intum/openapi-generator-angular21")

licenses {
license {
name.set("MIT License")
url.set("https://opensource.org/licenses/MIT")
}
}
}

repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/ls1intum/openapi-generator-angular21")
credentials {
username = System.getenv("GITHUB_ACTOR")
password = System.getenv("GITHUB_TOKEN")
developers {
developer {
id.set("ls1intum")
name.set("LS1 TUM")
email.set("krusche@tum.de")
}
}

scm {
connection.set("scm:git:git://github.com/ls1intum/openapi-generator-angular21.git")
developerConnection.set("scm:git:ssh://github.com/ls1intum/openapi-generator-angular21.git")
url.set("https://github.com/ls1intum/openapi-generator-angular21")
}
}
}

// Only sign when a signing key is configured — i.e. during a Central Portal release in CI, where the
// SIGNING_KEY secret is provided as ORG_GRADLE_PROJECT_signingInMemoryKey. Building from source via
// `./gradlew publishToMavenLocal` (the fallback consumers use to regenerate the client) needs no GPG
// key: the local repository does not require signatures. The flag is read at configuration time so it
// stays compatible with the configuration cache.
val signingKeyPresent = providers.gradleProperty("signingInMemoryKey").isPresent
tasks.withType<Sign>().configureEach {
onlyIf { signingKeyPresent }
}
Loading
Loading