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

Comment thread
halotukozak marked this conversation as resolved.
on:
push:
tags:
- 'v*'

permissions:
contents: read

jobs:
ci:
uses: ./.github/workflows/ci.yml
permissions:
contents: read
pull-requests: write

validate-tag:
runs-on: ubuntu-latest
steps:
- name: Validate semver tag
run: |
TAG="${GITHUB_REF_NAME}"
if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(.*)$ ]]; then
echo "::error::Tag '$TAG' does not match semver format (expected v<major>.<minor>.<patch>)"
exit 1
fi

publish:
needs: [ ci, validate-tag ]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21

- uses: gradle/actions/setup-gradle@v4

- name: Publish to Maven Central
env:
RELEASE_VERSION: ${{ github.ref_name }}
ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SIGNING_KEY }}
ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SIGNING_PASSWORD }}
run: ./gradlew publishAndReleaseToMavenCentral --no-configuration-cache
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [ master ]
pull_request:
branches: [ master ]
workflow_call:

permissions:
contents: read
Expand Down
91 changes: 91 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,92 @@
# justworks

A Gradle plugin that generates type-safe Kotlin [Ktor](https://ktor.io/) client code from OpenAPI 3.0 specifications.

## Installation

Add the plugin to your `build.gradle.kts`:

```kotlin
plugins {
id("com.avsystem.justworks") version "<version>"
}
```

The plugin and core library are published to Maven Central:

- `com.avsystem.justworks:plugin` -- Gradle plugin
- `com.avsystem.justworks:core` -- OpenAPI parser and code generator

## Usage

Configure one or more OpenAPI specs in the `justworks` extension:

```kotlin
justworks {
specs {
register("petstore") {
specFile = file("api/petstore.yaml")
packageName = "com.example.petstore"
}
register("payments") {
specFile = file("api/payments.yaml")
packageName = "com.example.payments"
// Optional: override default sub-packages
apiPackage = "com.example.payments.client"
modelPackage = "com.example.payments.dto"
}
}
}
```

Each spec gets its own Gradle task (`justworksGenerate<Name>`) and output directory. Run all generators at once with:

```bash
./gradlew justworksGenerateAll
```

Generated sources are automatically wired into Kotlin source sets, so `compileKotlin` depends on code generation -- no extra configuration needed.

### Configuration options

| Property | Required | Default | Description |
|----------------|----------|--------------------------|--------------------------------------|
| `specFile` | Yes | -- | Path to the OpenAPI spec (.yaml/.json) |
| `packageName` | Yes | -- | Base package for generated code |
| `apiPackage` | No | `$packageName.api` | Package for API client classes |
| `modelPackage` | No | `$packageName.model` | Package for model/data classes |

## Publishing

Releases are published to [Maven Central](https://central.sonatype.com/) automatically when a version tag (`v*`) is pushed. The CD pipeline runs CI checks first, then publishes signed artifacts via the [vanniktech maven-publish](https://github.com/vanniktech/gradle-maven-publish-plugin) plugin.

To trigger a release:

```bash
git tag v1.0.0
git push origin v1.0.0
```

The version is derived from the tag (stripping the `v` prefix). Without a tag, the version defaults to `0.0.1-SNAPSHOT`.

## Development

Requires **JDK 21+**.

```bash
# Run tests and linting
./gradlew check

# Run only unit tests
./gradlew test

# Run functional tests (plugin module)
./gradlew plugin:functionalTest

# Lint check
./gradlew ktlintCheck
```

## License

Apache License 2.0
55 changes: 53 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,15 +1,66 @@
import com.vanniktech.maven.publish.MavenPublishBaseExtension
import com.vanniktech.maven.publish.SonatypeHost

plugins {
kotlin("jvm") version "2.3.0" apply false
id("org.jlleitschuh.gradle.ktlint") version "12.1.2" apply false
id("org.jetbrains.kotlinx.kover") version "0.9.1" apply false
Comment thread
halotukozak marked this conversation as resolved.
id("com.vanniktech.maven.publish") version "0.30.0" apply false
Comment thread
halotukozak marked this conversation as resolved.
}

allprojects {
group = "com.avsystem"
version = "0.0.1"
group = "com.avsystem.justworks"
version = System
.getenv("RELEASE_VERSION")
Comment thread
halotukozak marked this conversation as resolved.
?.let { Regex("""^v(\d+\.\d+\.\d+.*)$""").matchEntire(it)?.groupValues?.get(1) }
?: "0.0.1-SNAPSHOT"

repositories {
mavenCentral()
}

apply(plugin = "org.jlleitschuh.gradle.ktlint")
}

subprojects {
pluginManager.withPlugin("com.vanniktech.maven.publish") {
extensions.configure<MavenPublishBaseExtension> {
publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL)
signAllPublications()

pom {
url = "https://github.com/AVSystem/justworks"

licenses {
license {
name = "The Apache License, Version 2.0"
url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
}
}

developers {
developer {
id = "halotukozak"
name = "Bartłomiej Kozak"
email = "b.kozak@avsystem.com"
organization = "AVSystem"
organizationUrl = "https://www.avsystem.com"
}
developer {
id = "mzielu"
name = "Marcin Zieliński"
email = "m.zielinski@avsystem.com"
organization = "AVSystem"
organizationUrl = "https://www.avsystem.com"
}
}

scm {
connection = "scm:git:git://github.com/AVSystem/justworks.git"
developerConnection = "scm:git:ssh://github.com/AVSystem/justworks.git"
url = "https://github.com/AVSystem/justworks"
}
}
}
}
}
20 changes: 10 additions & 10 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
kotlin("jvm")
`maven-publish`
id("org.jetbrains.kotlinx.kover") version "0.9.1"
id("org.jetbrains.kotlinx.kover")
id("com.vanniktech.maven.publish")
}

kotlin {
Expand All @@ -12,9 +12,15 @@ kotlin {

// todo: remove when https://github.com/JLLeitschuh/ktlint-gradle/issues/912 resolved
ktlint {
version.set("1.8.0")
version = "1.8.0"
}

mavenPublishing {
pom {
name = "justworks-core"
description = "OpenAPI 3.0 parser and Kotlin Ktor client code generator"
}
}

dependencies {
implementation("io.swagger.parser.v3:swagger-parser:2.1.39")
Expand All @@ -28,13 +34,7 @@ tasks.test {
useJUnitPlatform()
}

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

tasks.withType<KotlinCompile>().configureEach {
compilerOptions {
freeCompilerArgs.add("-Xcontext-parameters")
Expand Down
16 changes: 14 additions & 2 deletions plugin/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import com.vanniktech.maven.publish.GradlePlugin
import com.vanniktech.maven.publish.JavadocJar

plugins {
kotlin("jvm")
`java-gradle-plugin`
`maven-publish`
id("org.jetbrains.kotlinx.kover") version "0.9.1"
id("com.vanniktech.maven.publish")
id("org.jetbrains.kotlinx.kover")
}

kotlin {
Expand All @@ -23,6 +26,15 @@ gradlePlugin {
}
}

mavenPublishing {
configure(GradlePlugin(javadocJar = JavadocJar.Empty()))

pom {
name = "justworks-plugin"
description = "Gradle plugin for generating Kotlin Ktor client code from OpenAPI 3.0 specifications"
}
}

// Functional test source set
val functionalTest by sourceSets.creating {
compileClasspath += sourceSets["main"].output
Expand Down
Loading