mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-05 11:18:24 +00:00
chore(release): bump to 1.15.1, unblocking the AAB on AGP 9.4.0
v1.15.0 was tagged but never shipped. Its `Create Release Assets` run failed in
`deploy-android` at the first packaging task:
Execution failed for task ':amethyst:buildFdroidReleasePreBundle'
> Entry name contains invalid characters:
root/META-INF/zoomable-root:zoomable.kotlin_module
so no AAB, no APK, and none of the 47 assets were produced.
A `.kotlin_module` is named after the Gradle project path that produced it,
colons included. 14 of the 98 modules merged into the app carry one: zoomable,
Negentropy, vico, the seven coil3 artifacts, and four of ours -- Amethyst:quartz,
Amethyst:commons, Amethyst:quic and Amethyst:nestsClient -- so renaming our own
would not have been enough.
Bisected against the two toolchain bumps this cycle, since both landed after the
last good release: AGP 9.3.1 -> 9.4.0 and Kotlin 2.4.10 -> 2.4.20. With Kotlin
held at 2.4.20 and AGP reverted, both flavours' bundle tasks pass, so Kotlin is
not the trigger. The R8 output jar carries the identical 14 colon entries under
BOTH AGP versions -- 9.4.0 added the rejection rather than the names, in
JarFlinger.addJar, reached from PerModuleBundleTask.addHybridFolder.
`packaging.resources.excludes` was tried first and cannot work, at either
`META-INF/*.kotlin_module` or `**/*.kotlin_module`: with minification on, R8
emits the java resources itself and addHybridFolder hands JarFlinger its own
predicate, so those filters are never consulted. Confirmed by deleting the R8
output and re-running rather than reading a stale intermediate --
mergeJavaResource's jar holds zero kotlin_modules while R8's holds all 98.
So the entries are stripped from R8's jar in the moment before the bundle task
opens it, and the jar is put back exactly as R8 left it afterwards. Two details
carry their weight:
- the strip is doFirst on the CONSUMER rather than doLast on R8, so a
build-cache hit on R8 cannot skip it;
- the restore is what keeps R8 up to date. Without it Gradle sees a modified
output and re-runs R8 on every build -- measured here at ~2 min for an
otherwise no-op build. With it, a second run reports
minifyFdroidReleaseWithR8 UP-TO-DATE and finishes in 1s.
Pinning back to 9.3.1 was the alternative and is one line away; the catalog
comment records that, and says to drop the workaround when AGP fixes it.
Verified from a cleaned R8 output on both flavours:
buildFdroidReleasePreBundle and buildPlayReleasePreBundle both BUILD SUCCESSFUL,
each reporting "Stripped 14 colon-named entries from base.jar".
appCode 458 -> 459. RELEASE_NOTES_ID deliberately stays on the v1.15.0 note:
RELEASE_OPS has it repointed on x.y.0 only, and 1.15.1 ships that release's
contents.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VgVDQQXAg4cmzsWHoJj61k
This commit is contained in:
co-authored by
Claude Opus 5
parent
9449bce038
commit
a2f60eeeaf
@@ -7,7 +7,7 @@ description: Integration guide for using the Quartz Nostr KMP library in externa
|
||||
|
||||
Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr KMP projects.
|
||||
|
||||
**Published artifact**: `com.vitorpamplona.quartz:quartz:1.15.0` (Maven Central)
|
||||
**Published artifact**: `com.vitorpamplona.quartz:quartz:1.15.1` (Maven Central)
|
||||
**Targets**: JVM 21+, Android (minSdk 21+), iOS (XCFramework `quartz-kmpKit`)
|
||||
**License**: MIT
|
||||
|
||||
@@ -19,7 +19,7 @@ Reference for integrating `com.vitorpamplona.quartz:quartz` into external Nostr
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
quartz = "1.15.0"
|
||||
quartz = "1.15.1"
|
||||
|
||||
[libraries]
|
||||
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
||||
@@ -41,7 +41,7 @@ kotlin {
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.0")
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.1")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
## Current version
|
||||
|
||||
```
|
||||
com.vitorpamplona.quartz:quartz:1.15.0
|
||||
com.vitorpamplona.quartz:quartz:1.15.1
|
||||
```
|
||||
|
||||
Check latest: https://central.sonatype.com/artifact/com.vitorpamplona.quartz/quartz
|
||||
@@ -16,7 +16,7 @@ Check latest: https://central.sonatype.com/artifact/com.vitorpamplona.quartz/qua
|
||||
|
||||
```toml
|
||||
[versions]
|
||||
quartz = "1.15.0"
|
||||
quartz = "1.15.1"
|
||||
|
||||
[libraries]
|
||||
quartz = { module = "com.vitorpamplona.quartz:quartz", version.ref = "quartz" }
|
||||
@@ -55,7 +55,7 @@ kotlin {
|
||||
```kotlin
|
||||
// build.gradle.kts (app module)
|
||||
dependencies {
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.0")
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.1")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -70,7 +70,7 @@ plugins {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.0")
|
||||
implementation("com.vitorpamplona.quartz:quartz:1.15.1")
|
||||
// JNA needed for libsodium (NIP-44) on JVM
|
||||
implementation("net.java.dev.jna:jna:5.18.1")
|
||||
}
|
||||
|
||||
+3
-3
@@ -533,7 +533,7 @@ reads an optional per-release changelog from
|
||||
|
||||
## Bootstrap runbook (one-time)
|
||||
|
||||
> **Status as of v1.15.0:** both Homebrew packages are now live upstream — the
|
||||
> **Status as of v1.15.1:** both Homebrew packages are now live upstream — the
|
||||
> `amethyst-nostr` cask (`Homebrew/homebrew-cask`, at 1.14.0) and the `amy`
|
||||
> formula (`Homebrew/homebrew-core`) both answer 200 on `formulae.brew.sh`, so
|
||||
> `bump-homebrew.yml` finally has something to bump. **Winget is still not
|
||||
@@ -587,7 +587,7 @@ The token then lives only in that maintainer's shell:
|
||||
|
||||
```bash
|
||||
export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope
|
||||
scripts/bump-homebrew-cask.sh v1.15.0
|
||||
scripts/bump-homebrew-cask.sh v1.15.1
|
||||
```
|
||||
|
||||
Create one at
|
||||
@@ -603,7 +603,7 @@ Same split, and it needs **no token at all**. `scripts/bump-winget.sh` drives
|
||||
runs fine from macOS or Linux:
|
||||
|
||||
```bash
|
||||
scripts/bump-winget.sh v1.15.0
|
||||
scripts/bump-winget.sh v1.15.1
|
||||
```
|
||||
|
||||
CI (`bump-winget.yml`, `GITHUB_TOKEN` only) does the bookkeeping: downloads the
|
||||
|
||||
@@ -328,16 +328,16 @@ repositories {
|
||||
Add the following line to your `commonMain` dependencies:
|
||||
|
||||
```gradle
|
||||
implementation('com.vitorpamplona.quartz:quartz:1.15.0')
|
||||
implementation('com.vitorpamplona.quartz:quartz:1.15.1')
|
||||
```
|
||||
|
||||
Variations to each platform are also available:
|
||||
|
||||
```gradle
|
||||
implementation('com.vitorpamplona.quartz:quartz-android:1.15.0')
|
||||
implementation('com.vitorpamplona.quartz:quartz-jvm:1.15.0')
|
||||
implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.15.0')
|
||||
implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.15.0')
|
||||
implementation('com.vitorpamplona.quartz:quartz-android:1.15.1')
|
||||
implementation('com.vitorpamplona.quartz:quartz-jvm:1.15.1')
|
||||
implementation('com.vitorpamplona.quartz:quartz-iosarm64:1.15.1')
|
||||
implementation('com.vitorpamplona.quartz:quartz-iossimulatorarm64:1.15.1')
|
||||
```
|
||||
|
||||
Check versions on [MavenCentral](https://central.sonatype.com/search?q=com.vitorpamplona.quartz)
|
||||
|
||||
+2
-2
@@ -240,9 +240,9 @@ readable by anyone with push access here), so a maintainer runs the last step:
|
||||
```bash
|
||||
# after merging the sync PRs
|
||||
export HOMEBREW_GITHUB_API_TOKEN=ghp_... # classic PAT, `repo` scope
|
||||
scripts/bump-homebrew-cask.sh v1.15.0
|
||||
scripts/bump-homebrew-cask.sh v1.15.1
|
||||
|
||||
scripts/bump-winget.sh v1.15.0 # no token — uses your `gh` auth
|
||||
scripts/bump-winget.sh v1.15.1 # no token — uses your `gh` auth
|
||||
```
|
||||
|
||||
Both scripts re-verify the published artifact's sha256 before submitting, and
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import java.io.File
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
import org.gradle.api.services.BuildService
|
||||
import org.gradle.api.services.BuildServiceParameters
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
@@ -611,3 +615,75 @@ dependencies {
|
||||
implementation(libs.androidx.camera.view)
|
||||
implementation(libs.androidx.camera.extensions)
|
||||
}
|
||||
|
||||
// AGP 9.4.0's PerModuleBundleTask refuses to write an AAB entry whose name contains a colon:
|
||||
//
|
||||
// Entry name contains invalid characters: root/META-INF/zoomable-root:zoomable.kotlin_module
|
||||
//
|
||||
// A .kotlin_module is named after the Gradle project path that produced it, colons included, and
|
||||
// 14 of the 98 merged into this app carry one -- zoomable, Negentropy, vico, seven coil3 artifacts
|
||||
// and four of ours. The entries are identical under 9.3.1, which writes them without complaint, so
|
||||
// 9.4.0 added the rejection rather than the names.
|
||||
//
|
||||
// packaging.resources.excludes cannot remove them: with minification on, R8 emits the java
|
||||
// resources and PerModuleBundleTask.addHybridFolder hands JarFlinger its own predicate, so those
|
||||
// filters are never consulted. Nothing in the AAB reads a .kotlin_module either -- it exists for
|
||||
// the Kotlin compiler to resolve top-level declarations across modules at COMPILE time.
|
||||
//
|
||||
// So drop them from R8's java-res jar in the moment before the bundle task opens it, then put the
|
||||
// jar back exactly as R8 left it. The strip is doFirst on the CONSUMER rather than doLast on R8, so
|
||||
// a build-cache hit on R8 cannot skip it; the restore is what keeps R8 up to date next build --
|
||||
// without it Gradle sees a modified output and re-runs R8 every time, which measured ~2 min a build
|
||||
// here for no work.
|
||||
fun stripColonNamedEntries(jar: File): Int {
|
||||
val offenders = ZipFile(jar).use { zip -> zip.entries().toList().count { ':' in it.name } }
|
||||
if (offenders == 0) return 0
|
||||
|
||||
val rewritten = File(jar.parentFile, "${jar.name}.stripped")
|
||||
ZipFile(jar).use { zip ->
|
||||
ZipOutputStream(rewritten.outputStream().buffered()).use { out ->
|
||||
zip.entries().asSequence().filterNot { ':' in it.name }.forEach { entry ->
|
||||
out.putNextEntry(ZipEntry(entry.name))
|
||||
zip.getInputStream(entry).use { it.copyTo(out) }
|
||||
out.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
rewritten.copyTo(jar, overwrite = true)
|
||||
rewritten.delete()
|
||||
return offenders
|
||||
}
|
||||
|
||||
androidComponents.onVariants { variant ->
|
||||
val variantName = variant.name
|
||||
val capitalized = variantName.replaceFirstChar { it.uppercase() }
|
||||
tasks.matching { it.name == "build${capitalized}PreBundle" }.configureEach {
|
||||
val javaResDir = layout.buildDirectory.dir("intermediates/merged_java_res/$variantName")
|
||||
val backups = mutableMapOf<File, File>()
|
||||
|
||||
doFirst {
|
||||
javaResDir.get().asFile
|
||||
.walkTopDown()
|
||||
.filter { it.isFile && it.extension == "jar" }
|
||||
.forEach { jar ->
|
||||
val backup = File(jar.parentFile, "${jar.name}.orig")
|
||||
jar.copyTo(backup, overwrite = true)
|
||||
val dropped = stripColonNamedEntries(jar)
|
||||
if (dropped > 0) {
|
||||
backups[jar] = backup
|
||||
logger.lifecycle("Stripped $dropped colon-named entries from ${jar.name}")
|
||||
} else {
|
||||
backup.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doLast {
|
||||
backups.forEach { (jar, backup) ->
|
||||
backup.copyTo(jar, overwrite = true)
|
||||
backup.delete()
|
||||
}
|
||||
backups.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
Release notes for Amethyst, one file per version. Files are named with zero-padded version numbers so they sort correctly in any file browser. Use [`TEMPLATE.md`](TEMPLATE.md) as the starting point for the next release.
|
||||
|
||||
- [v1.15.1 — Release Build Fix](v1.15.01.md)
|
||||
- [v1.15.0 — Marmot Updates, a Advanced Search Box, and Books, DVM Updates](v1.15.00.md)
|
||||
- [v1.14.0 — Highlights, Relay Login, and a Much Faster Start](v1.14.00.md)
|
||||
- [v1.13.1 — Follow-up Fixes for Buzz, Concord and the Keyboard](v1.13.01.md)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
# v1.15.1: Release Build Fix
|
||||
|
||||
v1.15.0 was tagged but never shipped: its `Create Release Assets` run failed in
|
||||
`deploy-android` before producing a single artifact. This release carries the
|
||||
whole of [v1.15.0](v1.15.00.md) plus the fix for that failure — read those notes
|
||||
for what actually changed in the app.
|
||||
|
||||
## Build
|
||||
|
||||
- Strips colon-named `.kotlin_module` entries out of R8's java-resource jar
|
||||
before the app bundle is written. AGP 9.4.0 refuses them:
|
||||
|
||||
```
|
||||
Execution failed for task ':amethyst:buildFdroidReleasePreBundle'
|
||||
> Entry name contains invalid characters: root/META-INF/zoomable-root:zoomable.kotlin_module
|
||||
```
|
||||
|
||||
A `.kotlin_module` is named after the Gradle project path that produced it,
|
||||
colons included, and 14 of the 98 modules merged into the app carry one —
|
||||
zoomable, Negentropy, vico, seven coil3 artifacts, and four of ours
|
||||
(`Amethyst:quartz`, `:commons`, `:quic`, `:nestsClient`). The entries are
|
||||
byte-identical under AGP 9.3.1, which writes the bundle without complaint, so
|
||||
9.4.0 added the rejection rather than the names.
|
||||
|
||||
`packaging.resources.excludes` cannot remove them — with minification on, R8
|
||||
emits the java resources and `PerModuleBundleTask` hands `JarFlinger` its own
|
||||
predicate, so those filters are never consulted. Nothing reads a
|
||||
`.kotlin_module` out of an installed app either; the file exists for the
|
||||
Kotlin compiler to resolve top-level declarations across modules at compile
|
||||
time.
|
||||
+3
-3
@@ -32,7 +32,7 @@ docker run -d --name geode -p 7447:7447 \
|
||||
```
|
||||
|
||||
with `in_memory = false` and `file = "/var/lib/geode/events.db"` in your
|
||||
`geode.toml`. Pin a version (`:1.15.0`) instead of `:latest` for reproducible
|
||||
`geode.toml`. Pin a version (`:1.15.1`) instead of `:latest` for reproducible
|
||||
deploys. To build the image yourself, from the repo root:
|
||||
|
||||
```bash
|
||||
@@ -47,7 +47,7 @@ it — a minimal JRE is bundled, so no system Java is required. It installs to
|
||||
`/opt/geode/` with the launcher at `/opt/geode/bin/geode`.
|
||||
|
||||
```bash
|
||||
sudo dpkg -i geode-1.15.0-linux-x64.deb # or: sudo rpm -i geode-1.15.0-linux-x64.rpm
|
||||
sudo dpkg -i geode-1.15.1-linux-x64.deb # or: sudo rpm -i geode-1.15.1-linux-x64.rpm
|
||||
```
|
||||
|
||||
To run it as a managed service, wire up the shipped systemd unit
|
||||
@@ -69,7 +69,7 @@ formula: [`packaging/homebrew/geode-relay.rb`](packaging/homebrew/geode-relay.rb
|
||||
Download `geode-<version>-<os>-<arch>.tar.gz`, unpack, and run:
|
||||
|
||||
```bash
|
||||
tar xzf geode-1.15.0-linux-x64.tar.gz
|
||||
tar xzf geode-1.15.1-linux-x64.tar.gz
|
||||
./geode/bin/geode --config geode/share/geode/config.example.toml
|
||||
```
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
# Amethyst app version — single source of truth consumed by both Android (amethyst/)
|
||||
# and Desktop (desktopApp/). `appCode` is the Android versionCode: a monotonic
|
||||
# integer that must increment on every release, even when `app` is unchanged.
|
||||
app = "1.15.0"
|
||||
appCode = "458"
|
||||
app = "1.15.1"
|
||||
appCode = "459"
|
||||
accompanistAdaptive = "0.37.3"
|
||||
# Pinned: 0.3.0 turns CacheMap.entries/keys/values into DeprecationLevel.ERROR (and
|
||||
# throws UnsupportedOperationException at runtime), which breaks quartz's appleMain
|
||||
@@ -13,6 +13,9 @@ accompanistAdaptive = "0.37.3"
|
||||
cachemapVersion = "0.2.4"
|
||||
composeMultiplatform = "1.12.0"
|
||||
activityCompose = "1.13.0"
|
||||
# 9.4.0's PerModuleBundleTask rejects an AAB entry whose name contains a colon, which every
|
||||
# .kotlin_module named after a Gradle project path has. Worked around in amethyst/build.gradle.kts
|
||||
# (stripColonNamedEntries) rather than by pinning to 9.3.1; drop that block if AGP fixes it.
|
||||
agp = "9.4.0"
|
||||
android-compileSdk = "37"
|
||||
android-minSdk = "26"
|
||||
|
||||
Reference in New Issue
Block a user