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
109 changes: 109 additions & 0 deletions .claude/commands/release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
---
allowed-tools: Bash(grep:*), Bash(git describe:*), Bash(git branch:*), Bash(git log:*), Bash(git diff:*), Bash(git status:*), Bash(git tag:*), Bash(git add:*), Bash(git commit:*), Bash(git reset:*), Bash(gh repo view:*), Bash(gh release view:*), Bash(gh run:*), Bash(gh run list:*), Bash(gh run watch:*), Bash(gh run rerun:*), Bash(gh workflow run:*), Bash(./gradlew:*), Edit, Read
description: Release a new version - bump version, commit, tag, push, publish to Maven Central, create GitHub release
argument-hint: "[major|minor|patch|<explicit-version>]"
---

# Release

## Current State

- Current version: !`grep '^library' gradle/libs.versions.toml`
- Latest git tag: !`git describe --tags --abbrev=0 2>/dev/null || echo "(no tags)"`
- Current branch: !`git branch --show-current`
- GitHub repo: !`gh repo view --json nameWithOwner -q .nameWithOwner`

## Instructions

Follow these steps precisely to create a new release.

### Step 1: Preflight checks

1. Verify we are on the `master` branch. If not, **stop** and warn the user.
2. Verify the working tree is clean (`git status --porcelain`). If not, **stop** and warn the user.
3. Pull latest: `git pull --ff-only`. If this fails, **stop** and warn the user.

### Step 2: Determine version bump

The current version uses pre-release identifiers (e.g., `1.0.0-alpha03`). Version bumping rules:

If `$ARGUMENTS` is one of `major`, `minor`, or `patch`:
- **patch**: increment the last numeric segment (e.g., `1.0.0-alpha03` → `1.0.0-alpha04`, or `1.2.3` → `1.2.4`)
- **minor**: `X.Y.Z` → `X.Y+1.0` (drops any pre-release suffix)
- **major**: `X.Y.Z` → `X+1.0.0` (drops any pre-release suffix) — always confirm with user first

If `$ARGUMENTS` is an explicit version string (e.g., `1.0.0-beta01` or `1.0.0`), use it directly.

Otherwise, analyze the commits since the last tag and suggest an appropriate bump. Present the proposed new version to the user and ask them to confirm using `AskUserQuestion`.

### Step 3: Summarize changes

List the commits since the last tag using `git log <last-tag>..HEAD --oneline`. Write concise, user-facing bullet points for each meaningful change. These will be used in the GitHub release body alongside the auto-generated changelog.

### Step 4: Execute the release

Do all of the following in order:

1. Edit `gradle/libs.versions.toml` to update the `library` version on line 9 to the new version
2. Build to verify nothing is broken:
```
./gradlew apiDump
./gradlew :core:build
```
If the build fails, **stop** and help the user fix the issue before continuing.
3. Stage and commit:
```
git add gradle/libs.versions.toml core/api/
git commit -m "release: vX.Y.Z"
```
4. Tag: `git tag vX.Y.Z`
5. Push: `git push && git push --tags`

### Step 5: Wait for CI

After pushing, monitor the CI workflows triggered by the push:

1. Wait a moment for workflows to trigger, then use `gh run list --branch master --limit 5` to find the runs
2. Use `gh run watch <run-id>` on the test workflow run

If all checks pass, proceed to Step 6.

If any check fails:
- **Flaky/random failure**: Rerun with `gh run rerun <run-id> --failed`, then wait again
- **Real failure**: We need to roll back and fix the issue:
1. Delete the remote tag: `git push --delete origin vX.Y.Z`
2. Delete the local tag: `git tag -d vX.Y.Z`
3. Revert the release commit: `git reset --hard HEAD~1 && git push --force`
4. Help the user fix the issue, then re-release

### Step 6: Create GitHub release

Create the release on GitHub using the auto-generated changelog:

```
gh release create vX.Y.Z --title "vX.Y.Z" --generate-notes
```

This will:
- Create a GitHub release with auto-generated PR-based changelog
- Automatically trigger the `apple-binaries.yml` workflow (attaches XCFramework archives to the release)

### Step 7: Trigger Maven Central publish

Trigger the publish workflow manually:

```
gh workflow run publish.yml
```

### Step 8: Monitor post-release workflows

1. Use `gh run list --limit 5` to find the publish and apple-binaries workflow runs
2. Watch both with `gh run watch <run-id>`
3. If the publish workflow fails, inform the user — they may need to check Maven Central / Sonatype credentials
4. If apple-binaries fails, it can be re-triggered: `gh workflow run apple-binaries.yml`

Once all workflows complete successfully, report:
- Link to the GitHub release
- Confirm Maven Central publish status
- Confirm Apple XCFramework archives attached to release
158 changes: 0 additions & 158 deletions .junie/guidelines.md

This file was deleted.

119 changes: 119 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# AGENTS.md

Guidance for AI coding agents working on the Tolgee Mobile Kotlin SDK.

Main branch: `master`

## Project Structure

```
tolgee-mobile-kotlin-sdk/
├── core/ # KMP base library (translations, caching, CDN API)
├── compose/ # Compose Multiplatform integration
├── compiler-plugin/ # Kotlin compiler plugin (currently disabled)
├── gradle-plugin/ # Gradle plugin for project integration
└── demo/ # Example apps (Android Views, Jetpack Compose, KMP Compose)
```

The compiler plugin is commented out in `settings.gradle.kts` (broken after Kotlin 2.2).

## Building

```bash
./gradlew :core:build
./gradlew :compose:build
./gradlew :gradle-plugin:build
```

## Testing

Only the gradle-plugin module has tests currently:

```bash
./gradlew :gradle-plugin:test
./gradlew :gradle-plugin:test --tests "TolgeeTest"
```

CI (`test.yml`) runs gradle-plugin tests on every push. Requires Tolgee CLI (`npm install --global @tolgee/cli`).

## API Compatibility

The project uses the Binary Compatibility Validator plugin. After any changes to public APIs, run:

```bash
./gradlew apiDump
```

This updates `.api` dump files in each module's `api/` directory. These files **must be committed** with the change. The build will fail (`apiCheck`) if the dump is out of date.

## Module Architecture

### Core Module

Main entry point: `Tolgee` singleton class in `core/src/commonMain/kotlin/io/tolgee/Tolgee.kt`

Key components:
- `Tolgee` — Singleton with locale management, translation resolution, and configuration
- `TolgeeApi` — CDN communication and local caching
- `TolgeeTranslation` — Interface with ICU and sprintf formatting implementations
- `TolgeeStorageProvider` — Platform-specific persistent caching interface
- `TolgeeManifest` — Available locales metadata from CDN (internal)

Data flow:
```
tFlow("key", params)
→ localeFlow emits locale
→ loadManifest() fetches available locales from CDN
→ resolveLocale() applies progressive BCP 47 fallback (zh-Hans-CN → zh-Hans → zh)
→ loadTranslations() fetches from CDN or cache (LRU in-memory + persistent storage)
→ Translation formatted and emitted via Flow
```

Thread safety: `Mutex` for translation loading, `AtomicFU` for manifest cache.

### Compose Module

Composable wrappers around Core with graceful fallback to default Compose resources when Tolgee is not initialized. Provides `stringResource()`, `pluralStringResource()`, `stringArrayResource()`.

### Gradle Plugin

Build-time configuration DSL bridging Gradle with the compiler plugin. Configures replacement of `getString()` and `stringResource()` calls.

### Compiler Plugin (disabled)

Kotlin IR transformations to replace standard resource calls with Tolgee calls at compile-time. Currently disabled in `settings.gradle.kts`.

## Multiplatform

The core module targets 20+ platforms:

| Category | Targets |
|----------|---------|
| Android | androidTarget, androidNativeX64/X86/Arm64/Arm32 |
| JVM | jvm (toolchain 21) |
| Apple | iOS (x64, arm64, simulatorArm64), tvOS (×3), watchOS (×4), macOS (x64, arm64) |
| Other | linuxX64, linuxArm64, mingwX64, js (IR), wasmJs |

Source set hierarchy follows `applyDefaultHierarchyTemplate()`:
- `commonMain` — All shared code
- `androidMain` — Android-specific (Views integration, storage)
- `appleMain` — Apple platforms (iOS/macOS/tvOS/watchOS)
- `jvmMain` — JVM-specific
- `jsMain` / `wasmJsMain` — JS/WASM

## Key Design Patterns

- **Builder pattern** for configuration: `Tolgee.Config.Builder`, `ContentDelivery.Builder`, `Network.Builder`
- **Sealed interfaces** for type safety: `Formatter` (ICU/Sprintf), `TolgeeMessageParams` (None/Indexed/Mapped)
- **Reactive Flows**: `localeFlow`, `changeFlow`, `tFlow()` returning `Flow<String>`
- **Expect/actual** for platform-specific implementations (`platformHttpClient`, `platformStorage`, etc.)

## Dependencies

Managed via Version Catalog in `gradle/libs.versions.toml`. Add new dependencies there, not directly in build scripts.

Key libraries: Ktor (HTTP), kotlinx-serialization (JSON), kotlinx-coroutines (async), i18n4k (locale handling), AtomicFU (thread safety), SKIE (Swift interop for Apple targets).

## Publishing

Manual trigger via `publish.yml` workflow on macOS. Publishes to Maven Central via `publishAllPublicationsToMavenCentralRepository`. Requires signing keys and Sonatype credentials.
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@AGENTS.md
2 changes: 2 additions & 0 deletions core/api/android/core.api
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ public class io/tolgee/Tolgee {
public static final field Companion Lio/tolgee/Tolgee$Companion;
public fun <init> (Lio/tolgee/Tolgee$Config;)V
public final fun addChangeListener (Lio/tolgee/Tolgee$ChangeListener;)V
public fun getAvailableLocaleTags ()Ljava/util/List;
public fun getAvailableLocales ()Ljava/util/List;
public final fun getChangeFlow ()Lkotlinx/coroutines/flow/MutableSharedFlow;
public fun getConfig ()Lio/tolgee/Tolgee$Config;
public static final fun getInstance ()Lio/tolgee/TolgeeAndroid;
Expand Down
Loading
Loading