Skip to content

feat: sign release builds and add a release skill - #38

Merged
jvsena42 merged 2 commits into
mainfrom
feat/release-skill
Jul 30, 2026
Merged

feat: sign release builds and add a release skill#38
jvsena42 merged 2 commits into
mainfrom
feat/release-skill

Conversation

@jvsena42

Copy link
Copy Markdown
Owner

Summary

Worn had no release process: one tag (v0.1.0), no GitHub releases, and no signing config in Gradle at all — assembleRelease produced an unsigned APK, and the existing release artifact came from the Android Studio "Generate Signed Bundle/APK" wizard. Meanwhile local.properties already carried the four signing constants, which nothing read.

This wires up release signing and adds a /release skill that automates the whole flow, modeled on the sibling Mandacaru project's skill and on how v0.1.0 was actually released (branch → PR → tag on the merge commit).

Changes

  • Add a release signing config to composeApp/build.gradle.kts, reading KEYSTORE_FILE, KEYSTORE_PASSWORD, KEY_ALIAS and KEY_PASSWORD from local.properties. Values stay out of git; only the constant names appear in tracked files.
  • Read local.properties via providers.fileContents(...) instead of File.inputStream(), so the configuration cache (org.gradle.configuration-cache=true) tracks it as an input and invalidates correctly.
  • Guard the signing config on KEYSTORE_FILE being present, so CI — which has no local.properties — keeps building.
  • Add .claude/skills/release/SKILL.md, the repo's first skill: validates the version tag, runs detekt and the shared tests, bumps Android versionCode/versionName and iOS MARKETING_VERSION/CURRENT_PROJECT_VERSION in one commit, opens a PR (since main is protected), then builds and verifies the signed APK, tags the merge commit, and publishes a GitHub release.

Test plan

  • ./gradlew detekt passes.
  • ./gradlew :shared:allTests passes.
  • ./gradlew :composeApp:assembleRelease succeeds and apksigner verify reports the APK as signed (v2 scheme, 1 signer) — confirming the constants are read correctly.
  • With local.properties moved aside and ANDROID_HOME set, assembleRelease still succeeds (unsigned). This is the CI path; the file was restored afterwards. The run logged "Configuration cache entry stored" rather than "reused", confirming the provider-based read tracks the file.
  • Verified no secret values land in tracked files: the diff and the skill contain only bare constant names, no paths, no TEAM_ID.

Checklist

  • ./gradlew detekt passes
  • Tested on Android
  • Tested on iOS
  • Updated documentation (if applicable)

Android/iOS boxes left unchecked: this is build configuration and tooling only — no app code or UI changes, so there is no runtime behaviour to exercise on a device. The Android release build was verified by building and checking the APK signature.

jvsena42 and others added 2 commits July 30, 2026 20:40
The four KEYSTORE_*/KEY_* constants were already present in
local.properties but nothing read them, so assembleRelease produced an
unsigned APK. Read them through providers.fileContents rather than a
plain file read: the configuration cache is enabled, and a config-time
File.inputStream() is an untracked input, so editing local.properties
would otherwise leave a stale cached configuration behind.

The null guard keeps CI green, where no local.properties exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bumps Android and iOS versions together on a release branch, opens a PR
since main is protected, then tags the merge commit and publishes a
GitHub release with the signed APK attached.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@jvsena42
jvsena42 merged commit 60372a3 into main Jul 30, 2026
2 checks passed
@jvsena42
jvsena42 deleted the feat/release-skill branch July 30, 2026 23:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant