26-add-ios-target (#28)

* Refactor: Modernize build and publishing workflow

- Upgraded Kotlin from 1.7.20 to 1.9.20.
- Upgraded JDK from 11 to 21 across all GitHub Actions workflows.
- Replaced the legacy `maven-publish` plugin with `com.vanniktech.maven.publish` for simplified publishing to Maven Central.
- Removed the `publishSnapshot.yml` workflow, as snapshot publishing is now managed by the new plugin.
- Updated `publishRelease.yml` to use the `publishToMavenCentral` task and new secrets (`MAVEN_CENTRAL_USERNAME`, `MAVEN_CENTRAL_PASSWORD`).
- Simplified `build.gradle.kts` by removing manual publishing and signing logic.
- Set the JVM target to "21".
- Bumped the project version to `3.1.1`.
- Removed `js` targets and related configurations.
- Added a new code style configuration file for the project (`.idea/codeStyles/Project.xml`).

* fix: Add JavaScript targets

- Added JS (IR) targets (`browser` and `nodejs`) to the Kotlin Multiplatform configuration for publishing.

* Improve README and document new publishing workflow

- Expanded the project description to highlight key features like Kotlin Multiplatform support, traversal iterators, DSL, and utility functions.
- Added a new "Installation" section with instructions for Gradle (Kotlin/Groovy) and Maven.
- Added a "Traversal and utilities" section to demonstrate usage of iterators and helper functions.
- Replaced the old publishing documentation with updated instructions for publishing to Maven Central via the Central Portal.
- Detailed the two publishing methods: automatically via GitHub Actions on release and manually via local Gradle commands.
- Clarified required secrets and environment variables for the new publishing process.
This commit is contained in:
2025-10-14 18:46:05 +02:00
committed by GitHub
parent 772eeb0465
commit 54f5074483
7 changed files with 197 additions and 209 deletions

View File

@@ -19,20 +19,18 @@ jobs:
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up JDK 11 - name: Set up JDK 21
uses: actions/setup-java@v2 uses: actions/setup-java@v4
with: with:
distribution: adopt distribution: temurin
java-version: 11 java-version: '21'
# Runs upload, and then closes & releases the repository # Runs upload, and then closes & releases the repository
- name: Publish to MavenCentral - name: Publish to MavenCentral
run: ./gradlew publish --max-workers 1 run: ./gradlew publishToMavenCentral
env: env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_KEY }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
SNAPSHOT: false SNAPSHOT: false

View File

@@ -1,35 +0,0 @@
name: Publish Snapshot
on:
push:
branches: [master]
jobs:
test:
uses: ./.github/workflows/test.yml
secrets: inherit
publish:
needs: test
name: Publish Snapshot
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: adopt
java-version: 11
# Runs upload, and then closes & releases the repository
- name: Publish to snapshot to Sonatype
run: ./gradlew publish --max-workers 1
env:
OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
SONATYPE_STAGING_PROFILE_ID: ${{ secrets.SONATYPE_STAGING_PROFILE_ID }}
SNAPSHOT: true

View File

@@ -12,12 +12,12 @@ jobs:
steps: steps:
- name: Check out code - name: Check out code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Set up JDK 11 - name: Set up JDK 21
uses: actions/setup-java@v2 uses: actions/setup-java@v4
with: with:
distribution: adopt distribution: temurin
java-version: 11 java-version: '21'
# Builds the release artifacts of the library # Builds the release artifacts of the library
- name: Test - name: Test
run: ./gradlew test run: ./gradlew cleanJvmTest jvmTest

View File

@@ -1,5 +1,40 @@
<component name="ProjectCodeStyleConfiguration"> <component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173"> <code_scheme name="Project" version="173">
<JavaCodeStyleSettings>
<option name="IMPORT_LAYOUT_TABLE">
<value>
<package name="" withSubpackages="true" static="false" module="true" />
<package name="android" withSubpackages="true" static="true" />
<package name="androidx" withSubpackages="true" static="true" />
<package name="com" withSubpackages="true" static="true" />
<package name="junit" withSubpackages="true" static="true" />
<package name="net" withSubpackages="true" static="true" />
<package name="org" withSubpackages="true" static="true" />
<package name="java" withSubpackages="true" static="true" />
<package name="javax" withSubpackages="true" static="true" />
<package name="" withSubpackages="true" static="true" />
<emptyLine />
<package name="android" withSubpackages="true" static="false" />
<emptyLine />
<package name="androidx" withSubpackages="true" static="false" />
<emptyLine />
<package name="com" withSubpackages="true" static="false" />
<emptyLine />
<package name="junit" withSubpackages="true" static="false" />
<emptyLine />
<package name="net" withSubpackages="true" static="false" />
<emptyLine />
<package name="org" withSubpackages="true" static="false" />
<emptyLine />
<package name="java" withSubpackages="true" static="false" />
<emptyLine />
<package name="javax" withSubpackages="true" static="false" />
<emptyLine />
<package name="" withSubpackages="true" static="false" />
<emptyLine />
</value>
</option>
</JavaCodeStyleSettings>
<JetCodeStyleSettings> <JetCodeStyleSettings>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings> </JetCodeStyleSettings>

6
.idea/studiobot.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="StudioBotProjectSettings">
<option name="shareContext" value="OptedIn" />
</component>
</project>

121
README.md
View File

@@ -1,9 +1,40 @@
# Tree (Data Structure) # Tree (Data Structure)
[![maven](https://img.shields.io/maven-central/v/com.github.adriankuta/tree-structure?style=plastic)](https://mvnrepository.com/artifact/com.github.adriankuta/tree-structure) [![maven](https://img.shields.io/maven-central/v/com.github.adriankuta/tree-structure?style=plastic)](https://mvnrepository.com/artifact/com.github.adriankuta/tree-structure)
[![License: MIT](https://img.shields.io/github/license/AdrianKuta/Tree-Data-Structure?style=plastic)](https://github.com/AdrianKuta/Tree-Data-Structure/blob/master/LICENSE) [![License: MIT](https://img.shields.io/github/license/AdrianKuta/Tree-Data-Structure?style=plastic)](https://github.com/AdrianKuta/Tree-Data-Structure/blob/master/LICENSE)
[![Publish](https://github.com/AdrianKuta/Tree-Data-Structure/actions/workflows/publishRelease.yml/badge.svg)](https://github.com/AdrianKuta/Tree-Data-Structure/actions/workflows/publish.yml) [![Publish](https://github.com/AdrianKuta/Tree-Data-Structure/actions/workflows/publishRelease.yml/badge.svg)](https://github.com/AdrianKuta/Tree-Data-Structure/actions/workflows/publishRelease.yml)
Simple implementation to store object in tree structure. Lightweight Kotlin Multiplatform tree data structure for Kotlin and Java. Includes a small DSL, multiple traversal iterators, and pretty-print support.
- Kotlin Multiplatform (JVM, JS, iOS, and Native host)
- Pre-order, Post-order, and Level-order iteration
- Simple DSL: tree { child(...) }
- Utilities: nodeCount(), height(), depth(), path(), prettyString(), clear(), removeChild()
## Installation
Gradle (Kotlin DSL):
```kotlin
// commonMain for KMP projects, or any sourceSet/module where you need it
dependencies {
implementation("com.github.adriankuta:tree-structure:3.1.1") // see badge above for the latest version
}
```
Gradle (Groovy):
```groovy
dependencies {
implementation "com.github.adriankuta:tree-structure:3.1.1" // see badge above for the latest
}
```
Maven:
```xml
<dependency>
<groupId>com.github.adriankuta</groupId>
<artifactId>tree-structure</artifactId>
<version>3.1.1</version>
</dependency>
```
## Usage ## Usage
@@ -25,19 +56,15 @@ europe.addChild(france)
println(root.prettyString()) println(root.prettyString())
``` ```
**Pretty Kotlin** **Pretty Kotlin (DSL)**
```kotlin ```kotlin
val root = val root = tree("World") {
tree("World") { child("North America") { child("USA") }
child("North America") {
child("USA")
}
child("Europe") { child("Europe") {
child("Poland") child("Poland")
child("Germany") child("Germany")
} }
} }
``` ```
**Java** **Java**
@@ -58,8 +85,7 @@ europe.addChild(france);
System.out.println(root.prettyString()); System.out.println(root.prettyString());
``` ```
*Output:* Output:
``` ```
World World
├── North America ├── North America
@@ -69,10 +95,58 @@ World
└── France └── France
``` ```
### Traversal and utilities
```kotlin
val root = TreeNode("root")
// ... build your tree
## Download // Choose iteration order (default is PreOrder)
root.treeIterator = TreeNodeIterators.PostOrder
for (node in root) println(node.value)
implementation "com.github.adriankuta:tree-structure:$latest_versions" // Utilities
root.nodeCount() // number of descendants
root.height() // longest path to a leaf (in edges)
root.depth() // distance from current node to the root
val path = root.path(root.children.first()) // nodes from descendant up to root
// Mutations
val child = root.children.first()
root.removeChild(child)
root.clear() // remove entire subtree
```
## Publishing to Maven Central (central.sonatype.com)
This project is configured to publish artifacts to Maven Central via the Sonatype Central Portal.
There are two supported ways to publish:
1) Via GitHub Actions (recommended)
- Create a GitHub Release (tag) in this repository. When a release is published, the workflow .github/workflows/publishRelease.yml runs automatically.
- The workflow uses the Gradle task publishToMavenCentral to upload artifacts through the Central Portal.
- Make sure these repository secrets are configured in GitHub:
- MAVEN_CENTRAL_USERNAME — Your Sonatype Central username (not email).
- MAVEN_CENTRAL_PASSWORD — Your Sonatype Central password or a token from central.sonatype.com.
- SIGNING_KEY — ASCIIarmored GPG private key (exported, single line; for inmemory signing).
- SIGNING_PASSWORD — Passphrase for the key above.
- The workflow uses JDK 21 and publishes the version defined in build.gradle.kts.
2) Locally via Gradle
- Ensure you have a Sonatype Central account and that the groupId com.github.adriankuta is verified in central.sonatype.com (Namespace Rules → Verify).
- Export the same credentials/signing values as environment variables or pass them as Gradle properties:
- ORG_GRADLE_PROJECT_mavenCentralUsername
- ORG_GRADLE_PROJECT_mavenCentralPassword
- ORG_GRADLE_PROJECT_signingInMemoryKey
- ORG_GRADLE_PROJECT_signingInMemoryKeyPassword
- Then run:
- ./gradlew publishToMavenCentral
- For snapshot publishing, set -Psnapshot=true (the version is derived from PUBLISH_VERSION with -SNAPSHOT).
Notes
- Publishing is powered by the com.vanniktech.maven.publish Gradle plugin and Sonatype Central Portal (no legacy Nexus staging URLs needed).
- The plugin is configured to sign all publications. Coordinates and POM metadata are defined in build.gradle.kts.
- If using the combined task is preferred, you can also run publishAndReleaseToMavenCentral when automatic release is enabled; this repository currently uploads with publishToMavenCentral from CI.
## License ## License
@@ -99,22 +173,3 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.
--- ---
## Publishing (Maven Central migration)
This project is configured to publish via Sonatype's s01.oss.sonatype.org (Nexus) which is compatible with the new Central (central.sonatype.com). The old oss.sonatype.org host is no longer used.
Environment variables supported by the build:
- CENTRAL_USERNAME / CENTRAL_PASSWORD — Central Portal user/token (preferred)
- OSSRH_USERNAME / OSSRH_PASSWORD — legacy credentials (fallback)
- SIGNING_KEY_ID / SIGNING_KEY / SIGNING_PASSWORD — PGP signing (ASCII-armored key)
- SNAPSHOT — set to true to append -SNAPSHOT to version
Gradle tasks:
- Publish all publications to Sonatype: `./gradlew publishAllPublicationsToSonatypeS01Repository`
- Or, standard publish (selects snapshots vs releases by version): `./gradlew publish`
Notes:
- Releases are uploaded to: https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
- Snapshots are uploaded to: https://s01.oss.sonatype.org/content/repositories/snapshots/
- Staging/release close and promote are handled by Sonatype. If you use CI, set the env vars in your secrets.

View File

@@ -1,147 +1,88 @@
import org.jetbrains.kotlin.konan.properties.Properties import org.jetbrains.kotlin.konan.properties.Properties
plugins { plugins {
kotlin("multiplatform") version "1.7.20" kotlin("multiplatform") version "1.9.20"
id("org.jetbrains.dokka") version "1.7.20" id("org.jetbrains.dokka") version "1.9.20"
id("maven-publish") id("com.vanniktech.maven.publish") version "0.34.0"
signing signing
} }
val PUBLISH_GROUP_ID = "com.github.adriankuta" val PUBLISH_GROUP_ID = "com.github.adriankuta"
val PUBLISH_ARTIFACT_ID = "tree-structure" val PUBLISH_ARTIFACT_ID = "tree-structure" // base artifact; KMP will add -jvm, -ios*, etc.
val PUBLISH_VERSION = "3.1.0" val PUBLISH_VERSION = "3.1.1"
val secretFile = File(rootProject.rootDir, "local.properties")
if (secretFile.exists()) {
secretFile.reader().use {
Properties().apply {
load(it)
}
}.onEach { (name, value) ->
project.ext[name.toString()] = value
}
} else {
// Prefer Central Portal credentials via environment variables
project.ext["centralUsername"] = System.getenv("CENTRAL_USERNAME")
project.ext["centralPassword"] = System.getenv("CENTRAL_PASSWORD")
// Fallback legacy OSSRH credentials (still supported on s01)
project.ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME")
project.ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD")
project.ext["sonatypeStagingProfileId"] = System.getenv("SONATYPE_STAGING_PROFILE_ID")
project.ext["signingKeyId"] = System.getenv("SIGNING_KEY_ID")
project.ext["signingPassword"] = System.getenv("SIGNING_PASSWORD")
project.ext["signingKey"] = System.getenv("SIGNING_KEY")
project.ext["snapshot"] = System.getenv("SNAPSHOT")
}
val snapshot: String? by project val snapshot: String? by project
group = PUBLISH_GROUP_ID group = PUBLISH_GROUP_ID
version = if (snapshot.toBoolean()) "$PUBLISH_VERSION-SNAPSHOT" else PUBLISH_VERSION version = if (snapshot.toBoolean()) "$PUBLISH_VERSION-SNAPSHOT" else PUBLISH_VERSION
val dokkaHtml by tasks.getting(org.jetbrains.dokka.gradle.DokkaTask::class) mavenPublishing {
// Central Portal + auto release when we call publishAndReleaseToMavenCentral
publishToMavenCentral(automaticRelease = false)
signAllPublications()
val javadocJar: TaskProvider<Jar> by tasks.registering(Jar::class) { coordinates(PUBLISH_GROUP_ID, PUBLISH_ARTIFACT_ID, version.toString())
dependsOn(dokkaHtml)
archiveClassifier.set("javadoc")
from(dokkaHtml.outputDirectory)
}
publishing {
publications {
withType<MavenPublication> {
artifact(javadocJar)
pom { pom {
name.set("Tree Data Structure") name.set("Tree Data Structure")
description.set("Simple implementation to store object in tree structure.") description.set("Simple implementation to store object in tree structure.")
url.set("https://github.com/AdrianKuta/Tree-Data-Structure") url.set("https://github.com/AdrianKuta/Tree-Data-Structure")
licenses { licenses {
license { license {
name.set("MIT License") name.set("MIT License")
url.set("https://www.mit.edu/~amini/LICENSE.md") url.set("https://opensource.org/licenses/MIT")
distribution.set("repo")
} }
} }
developers { developers {
developer { developer {
id.set("AdrianKuta")
name.set("Adrian Kuta") name.set("Adrian Kuta")
email.set("adrian.kuta93@gmail.com") email.set("adrian.kuta93@gmail.com")
} }
} }
scm { scm {
connection.set("scm:git:github.com/AdrianKuta/Tree-Data-Structure.git") url.set("https://github.com/AdrianKuta/Tree-Data-Structure")
developerConnection.set("scm:git:ssh://github.com/AdrianKuta/Tree-Data-Structure.git") connection.set("scm:git:https://github.com/AdrianKuta/Tree-Data-Structure.git")
url.set("https://github.com/AdrianKuta/Tree-Data-Structure/tree/master") developerConnection.set("scm:git:ssh://git@github.com/AdrianKuta/Tree-Data-Structure.git")
}
}
}
}
repositories {
maven {
name = "SonatypeS01"
// s01 is the supported Nexus host for Central publishing via Maven-compatible uploads
val releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/"
val snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/"
url = uri(if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl)
credentials {
// Prefer Central Portal credentials when provided, fallback to legacy OSSRH credentials
val centralUsername: String? by project
val centralPassword: String? by project
val CENTRAL_USERNAME: String? by project
val CENTRAL_PASSWORD: String? by project
val ossrhUsername: String? by project
val ossrhPassword: String? by project
username = centralUsername
?: CENTRAL_USERNAME
?: ossrhUsername
?: System.getenv("CENTRAL_USERNAME")
?: System.getenv("OSSRH_USERNAME")
password = centralPassword
?: CENTRAL_PASSWORD
?: ossrhPassword
?: System.getenv("CENTRAL_PASSWORD")
?: System.getenv("OSSRH_PASSWORD")
}
} }
} }
} }
signing { // No legacy publishing {} block or s01 repos — Central Portal handles it.
val signingKeyId: String? by project
val signingPassword: String? by project
val signingKey: String? by project
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
sign(publishing.publications)
}
repositories { repositories {
mavenCentral() mavenCentral()
} }
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
kotlin { kotlin {
jvmToolchain(21);
jvm { jvm {
compilations.all { compilations.all {
kotlinOptions.jvmTarget = "1.8" kotlinOptions {
} jvmTarget = "21" // <- was "1.8"
withJava()
testRuns["test"].executionTask.configure {
useJUnitPlatform()
}
}
js(BOTH) {
browser {
commonWebpackConfig {
cssSupport.enabled = true
} }
} }
} }
// Add iOS targets // JS targets (IR) for publishing
js(IR) {
browser()
nodejs()
}
// iOS targets
iosX64() iosX64()
iosArm64() iosArm64()
iosSimulatorArm64() iosSimulatorArm64()
// Native host target
val hostOs = System.getProperty("os.name") val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows") val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when { val nativeTarget = when {
@@ -153,16 +94,8 @@ kotlin {
sourceSets { sourceSets {
val commonMain by getting val commonMain by getting
val commonTest by getting { val commonTest by getting { dependencies { implementation(kotlin("test")) } }
dependencies { val jvmMain by getting { dependencies { implementation(kotlin("script-runtime")) } }
implementation(kotlin("test"))
}
}
val jvmMain by getting {
dependencies {
implementation(kotlin("script-runtime"))
}
}
val jvmTest by getting val jvmTest by getting
val jsMain by getting val jsMain by getting
val jsTest by getting val jsTest by getting
@@ -170,12 +103,8 @@ kotlin {
val nativeTest by getting val nativeTest by getting
// Shared iOS source sets // Shared iOS source sets
val iosMain by creating { val iosMain by creating { dependsOn(commonMain) }
dependsOn(commonMain) val iosTest by creating { dependsOn(commonTest) }
}
val iosTest by creating {
dependsOn(commonTest)
}
val iosX64Main by getting { dependsOn(iosMain) } val iosX64Main by getting { dependsOn(iosMain) }
val iosArm64Main by getting { dependsOn(iosMain) } val iosArm64Main by getting { dependsOn(iosMain) }
val iosSimulatorArm64Main by getting { dependsOn(iosMain) } val iosSimulatorArm64Main by getting { dependsOn(iosMain) }