Skip to content

Strip build-variant suffix during version normalization (closes #557)#560

Merged
rainxchzed merged 1 commit intomainfrom
feat/version-suffix-normalization
May 9, 2026
Merged

Strip build-variant suffix during version normalization (closes #557)#560
rainxchzed merged 1 commit intomainfrom
feat/version-suffix-normalization

Conversation

@rainxchzed
Copy link
Copy Markdown
Member

@rainxchzed rainxchzed commented May 9, 2026

What this PR fixes

Closes #557.

Apps that emit multiple APK variants under a single GitHub tag (e.g. tag 1.8.6 published, but the APKs install as 1.8.6-f for full / 1.8.6-m for minified / 1.8.6-arm64 for an architecture split) currently always show as having an update available, because the bare GitHub tag ranks higher than the on-device versionName under semver pre-release ordering rules.

VersionMath.normalizeVersion now recognises and strips a curated set of build-variant / flavour markers from the semver pre-release identifier before comparison. Pre-release markers (alpha, beta, rc, preview, nightly, etc.) are preserved.

Algorithm

  • Apply only when the input parses as semver and has a single-segment pre-release identifier.
  • Compound pre-release identifiers (armv7-beta, rc.1, f-x86_64) are left intact — too risky to disambiguate flavour-vs-pre-release from the tail alone.
  • A guard check rejects anything whose first/only token starts with a known pre-release prefix (alpha, beta, rc, preview, prerelease, snapshot, canary, nightly, milestone, ea, dev, pre, m\d+).
  • The remaining single-segment identifiers are matched against a literal allowlist:
    • Build flavours (single-letter): f, m, l, r, d, x
    • Build flavours (words): full, mini, minified, lite, release, debug, extended
    • Distribution channel: stable, final, prod, production, gms, fdroid, github, store
    • Architecture: armv7, armv8, arm64, armeabi, x86, x64, x86_64, universal, android, ios

Behaviour matrix

Comparison Before After
isVersionNewer("1.8.6", "1.8.6-f") true (phantom) false
isVersionNewer("1.8.6", "1.8.6-m") true (phantom) false
isVersionNewer("1.8.6", "1.8.6-arm64") true (phantom) false
isVersionNewer("1.8.6", "1.8.6-stable") true (phantom) false
isVersionNewer("1.8.7", "1.8.6-f") true true (real update) ✓
isVersionNewer("1.8.6", "1.8.6-b") true true (b could be beta — conservative, untouched)
isVersionNewer("1.8.6", "1.8.6-beta") true true (real pre-release)
isVersionNewer("1.8.6", "1.8.6-rc.1") true true (compound, untouched)
isVersionNewer("1.8.6-f", "1.8.6-m") n/a false (both strip to 1.8.6)

Scope

  • normalizeVersion (and therefore isVersionNewer, compareVersions, isSameVersion, ExternalInstallVerdict, the periodic checkForUpdates path, the DetailsState.findRelease lookup) all benefit from the strip.
  • isExactSameVersion is intentionally unchanged — its existing docstring states it distinguishes build-metadata variants for the install-CTA gate (SmartInstallButton, AppHeader). Preserving that behaviour avoids a surprise change in those callers.

Test plan

  • Manual: install a known multi-flavour app (e.g. N-Zik full APK on a 1.8.6-f versionName, GitHub tag 1.8.6). Trigger update check. Verify update badge disappears.
  • Same setup, publish a real new release (1.8.7) → verify update badge re-appears.
  • Verify beta apps tagged 1.8.6-beta still surface stable 1.8.6 as available (semver: stable > pre-release).
  • Spot-check an app that uses architecture-suffixed versionNames (-arm64, -armv7) — phantom update should be gone.

Out of scope

  • Per-app override / regex strip rule (the broader proposal in the issue body). The maintainers can add an assetFilterRegex-equivalent for versionName patterns if the curated list misses anyone.
  • Touching isExactSameVersion. Could be a follow-up for the install button on the details screen, but that's a separate UX call about whether 1.8.6-f and 1.8.6 are "the same release".

Summary by CodeRabbit

  • Bug Fixes
    • Improved version normalization to better handle build-variant and flavor suffixes in version strings, ensuring more consistent comparison and display while preserving meaningful pre-release information.

Review Change Stack

…ifier.

Some maintainers ship a single GitHub tag (`1.8.6`) but emit multiple
APK variants whose installed versionName carries a flavour marker
(`1.8.6-f` for full, `1.8.6-m` for minified, `1.8.6-arm64` for
architecture splits). The bare GitHub tag then ranks higher than the
on-device versionName under semver pre-release rules, so the comparator
perpetually surfaces a phantom update.

normalizeVersion now strips a curated set of single-token build-variant
markers (build flavours, distribution channels, architectures) from the
semver pre-release identifier before comparison. Real pre-release
markers (`alpha`, `beta`, `rc`, `preview`, …) and compound
identifiers (`armv7-beta`, `rc.1`) are preserved.

Effects:
- isVersionNewer("1.8.6", "1.8.6-f") → false (was true)
- isVersionNewer("1.8.7", "1.8.6-f") → true (unchanged)
- isVersionNewer("1.8.6", "1.8.6-b") → true (`b` could be beta;
  conservative, single-letter `b` not in the strip-list)
- isVersionNewer("1.8.6", "1.8.6-beta") → true (real pre-release,
  unchanged)

isExactSameVersion is intentionally unaffected — it still distinguishes
build-metadata variants per its existing docstring (used by the
SmartInstallButton CTA and AppHeader "Install" gate).

Closes #557.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

Walkthrough

The change enhances version normalization in VersionMath to handle build-variant suffixes like -f, -m, -arm64, -stable, and -debug. A new stripBuildVariantSuffix utility identifies and removes these markers from semver pre-release tokens while preserving legitimate pre-release identifiers (alpha, beta, rc, etc.) and compound forms. The normalizeVersion function integrates this deflavoring step before applying semver validation and dotted-digit fallback extraction.

Changes

Version Normalization with Build-Variant Stripping

Layer / File(s) Summary
Build-Variant Helpers
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt
Adds BUILD_VARIANT_LITERALS constant, isBuildVariantMarker() to classify pre-release tokens, and stripBuildVariantSuffix() to remove recognized suffixes while preserving real pre-releases and ambiguous compound forms.
normalizeVersion Integration
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt
Applies stripBuildVariantSuffix() to deflavour semver-parsed inputs before normalization; uses the deflavoured intermediate instead of the previous separated intermediate for semver validation and fallback extraction.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • OpenHub-Store/GitHub-Store#555: Adds calver/word-prefix/adjacent-letter pre-release handling to VersionMath.normalizeVersion alongside this PR's build-variant suffix stripping enhancements.

Poem

🐰 A version stripped of its flavoured coat,
-f and -m floating off like a boat,
Now 1.8.6 matches its kin so bright,
While alpha and beta stay held tight!
Build variants gone, the comparisons sing. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: stripping build-variant suffixes during version normalization to address issue #557.
Linked Issues check ✅ Passed The PR implementation aligns with issue #557 requirements by stripping build-variant suffixes (e.g., -f, -m, -arm64) from versions while preserving real pre-release identifiers (alpha, beta, rc, etc.) for comparison.
Out of Scope Changes check ✅ Passed All changes are scoped to VersionMath.normalizeVersion and its helper functions; modifications directly support the build-variant stripping requirement with no unrelated code alterations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/version-suffix-normalization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt (1)

39-48: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Stale docstring example after deflavouring is applied.

The App-v1.2.0-stable example on line 44 is no longer accurate. After this PR, stable is in BUILD_VARIANT_LITERALS and stripBuildVariantSuffix will reduce 1.2.0-stable to 1.2.0. The docstring still advertises 1.2.0-stable as the result, which contradicts the very behaviour this PR introduces and is the first place a future reader/maintainer will look.

Worth either updating the example to reflect the new output or replacing it with a still-valid case (e.g. an unrecognised single-token suffix that stays intact).

📝 Suggested doc update
-     *   `App-v1.2.0-stable`    → `1.2.0-stable`
+     *   `App-v1.2.0-stable`    → `1.2.0`     (stable channel marker stripped)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt`
around lines 39 - 48, Update the docstring examples in VersionMath.kt to reflect
the new deflavouring behavior: change the `App-v1.2.0-stable` example to show
`1.2.0` (or replace it with an example that uses an unrecognised single-token
suffix if you prefer to demonstrate a preserved suffix). Specifically, edit the
examples near the top of the file that describe stripBuildVariantSuffix and
BUILD_VARIANT_LITERALS so they match the actual output produced by the
stripBuildVariantSuffix function and the BUILD_VARIANT_LITERALS set.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt`:
- Around line 39-48: Update the docstring examples in VersionMath.kt to reflect
the new deflavouring behavior: change the `App-v1.2.0-stable` example to show
`1.2.0` (or replace it with an example that uses an unrecognised single-token
suffix if you prefer to demonstrate a preserved suffix). Specifically, edit the
examples near the top of the file that describe stripBuildVariantSuffix and
BUILD_VARIANT_LITERALS so they match the actual output produced by the
stripBuildVariantSuffix function and the BUILD_VARIANT_LITERALS set.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55d5157b-feba-43ef-a92a-b6f8d0ae47b5

📥 Commits

Reviewing files that changed from the base of the PR and between 76f5ed6 and 7fa2cc8.

📒 Files selected for processing (1)
  • core/domain/src/commonMain/kotlin/zed/rainxch/core/domain/util/VersionMath.kt

@rainxchzed rainxchzed merged commit f20a4b4 into main May 9, 2026
1 check passed
@rainxchzed rainxchzed deleted the feat/version-suffix-normalization branch May 9, 2026 15:08
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.

Add support for version suffix normalization (e.g. 1.8.6 should match 1.8.6-f, 1.8.6-m)

1 participant