Maintenance guide - ankidroid/Anki-Android GitHub Wiki

Workflows

Localization Administration

Updating the main strings from Git to Crowdin is a pretty delicate thing. Uploading an empty string.xml for instance would delete all translations. And uploading changed strings delete as well all translations. This is the desired behavior in most cases, but when just some English typos are corrected this shouldn't destroy all translations.

In this case, it's necessary to:

  1. rebuild a download package at first (option "r" in script [currently broken])
  2. download all translations (update-translations.py)
  3. upload the changed strings
  4. reupload the translations (option "t" and language "all").

CI

Ensure there are no failures in the unit test workflow (scheduled daily).

Dependabot

Monitor dependabot PRs that come in against the dependency-updates branch.

Ongoing: Dependabot opens PRs targeting dependency-updates. If they pass CI and look good, a maintainer merges them into that branch (keeping the individual Dependabot commits for now). Leave dependency-updates alone between batch landings — there is no post-merge sync back from main.

Make a new dependency PR (batch to main)

Agent-ready checklist. Title the PR Dependency updates YYYYMMDD (today’s date).

  1. Rebase dependency-updates onto current main and force-push (git push --force-with-lease). Git should skip the previous batch commit as already applied (same patch-id / empty replay). Only new Dependabot commits should remain. Do not squash yet — keep commits separate so a bad bump can still be dropped easily.
  2. Open a draft PR from dependency-updates → main (or update the existing batch PR back to draft if needed). Apply the squash-merge label. Multi-commit + that label intentionally trips the Squash merge merge-requirements check; we start as draft so that expected failure does not block verifying the rest of CI, and so commits can still be dropped if a bump is bad.
  3. Monitor CI on that draft until everything except the Squash merge check is green. Treat that one failure as expected until after the local squash. If any other check fails, fix or drop the offending Dependabot commit(s) on dependency-updates, force-push, and wait for CI again.
  4. Squash locally on dependency-updates into one commit. Start the message with Dependency Updates YYYYMMDD, then append each included commit’s full subject and body in chronological order. Force-push to dependency-updates (git push --force-with-lease).
  5. Undraft the PR, remove the squash-merge label, then enable auto-merge so it uses the normal merge queue. Do not squash-merge via the GitHub UI into main — the branch is already one commit with the desired message, and the UI is poor at preserving it.

That terminates the batch. After it lands, leave dependency-updates as-is. The next batch starts again at step 1.

Handling package.json/yarn.lock conflicts
  • Rebase locally onto main until it stops with a conflict
  • Run yarn install locally (which rebuilds the lock)
  • Amend the updated files into the commit which the rebase stopped on
  • Continue the rebase

on yarn.lock

yarn.lock is just a rendering of the current install based on package.json, so once package.json is fixed (or if it is simply "good to go" already), "rendering" a new lockfile is almost an afterthought I like to have it committed for reference as semver means deps can "slip" in a range and there are sometimes problems, but I don't consider it some semantically-informative thing

-- Mike

If dependabot needs configuration changes (to ignore a dependency or a version), the configuration should be done in the .github/dependabot.yaml file, not via any UI interaction. The difference is that the YAML config is reviewable text while the UI modifies some opaque database and thus is not manageable or reviewable.

diffing releases

https://github.com/ankidroid/Anki-Android/compare/v2.24.0alpha2...v2.24.0alpha3

git bisect

Patch
Subject: [PATCH] Remove "app.brant.amazonappstorepublisher" plugin
---
Index: AnkiDroid/build.gradle
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
--- a/AnkiDroid/build.gradle	(revision d4d48b920c763668c4608994c6852f343782a966)
+++ b/AnkiDroid/build.gradle	(date 1763096652934)
@@ -2,7 +2,6 @@
     // Gradle plugin portal
     alias(libs.plugins.tripletPlay)
     alias(libs.plugins.android.application)
-    alias(libs.plugins.amazonappstorepublisher)
     alias(libs.plugins.kotlin.android)
     alias(libs.plugins.kotlin.parcelize)
     alias(libs.plugins.kotlin.serialization)
@@ -248,13 +247,6 @@
     track.set('alpha')
 }
 
-amazon {
-    securityProfile = file("${homePath}/src/AnkiDroid-Amazon-Publish-Security-Profile.json")
-    applicationId = "amzn1.devportal.mobileapp.524a424d314931494c55383833305539"
-    pathToApks = [ file("./build/outputs/apk/amazon/release/AnkiDroid-amazon-universal-release.apk") ]
-    replaceEdit = true
-}
-
 // Install Git pre-commit hook for Ktlint
 tasks.register('installGitHook', Copy) {
     from new File(rootProject.rootDir, 'pre-commit')
Index: gradle/libs.versions.toml
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
--- a/gradle/libs.versions.toml	(revision d4d48b920c763668c4608994c6852f343782a966)
+++ b/gradle/libs.versions.toml	(date 1763096652902)
@@ -1,7 +1,6 @@
 [versions]
 acra = '5.11.3'
-amazonappstorepublisher = "0.1.0"
-androidGradlePlugin = "8.4.0"
+androidGradlePlugin = "8.5.1"
 androidxActivity = "1.9.0"
 androidxAnnotation = "1.7.1"
 androidxAppCompat = "1.7.0-beta01"
@@ -159,8 +158,6 @@
 
 android-application = { id = "com.android.application", version.ref = "androidGradlePlugin" }
 android-library = { id = "com.android.library", version.ref = "androidGradlePlugin" }
-amazonappstorepublisher = { id = "app.brant.amazonappstorepublisher", version.ref = "amazonappstorepublisher" }
-
 
 tripletPlay = { id = "com.github.triplet.play", version.ref = "triplet" }
 
Index: settings.gradle.kts
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/settings.gradle.kts b/settings.gradle.kts
--- a/settings.gradle.kts	(revision d4d48b920c763668c4608994c6852f343782a966)
+++ b/settings.gradle.kts	(date 1763096652931)
@@ -3,15 +3,6 @@
         google()
         gradlePluginPortal()
         mavenCentral()
-        maven(url = "https://jitpack.io") // only needed for the "amazonappstorepublisher" plugin
-    }
-    resolutionStrategy {
-        // TODO try to find another plugin for this functionality?
-        eachPlugin {
-            if (requested.id.id == "app.brant.amazonappstorepublisher") {
-                useModule("com.github.BrantApps.gradle-amazon-app-store-publisher:amazonappstorepublisher:master-SNAPSHOT")
-            }
-        }
     }
 }
 

ACRA

Deobfuscating stack traces

  1. Download and extract z-proguard-mappings.tar.gz from the relevant build in Releases
  2. Run retrace
    • macOS command: pbpaste | "${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/retrace" "/Users/davidallison/Downloads/proguard-ad-2-23-a6/build/outputs/mapping/fullRelease/mapping.txt" | sed "1d" | pbcopy

Wiki Admin

git clone https://github.com/ankidroid/Anki-Android.wiki.git
⚠️ **GitHub.com Fallback** ⚠️