From 47bb0634afeebad728aa92d33f640a1662aa7527 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Fri, 22 May 2026 01:50:43 +0200 Subject: [PATCH 1/5] chore: add just launcher --- .agents/commands/release.md | 5 +- .cursor/rules/rules.main.mdc | 2 +- .env.example | 18 ++++ .gitignore | 2 + AGENTS.md | 28 +++---- Justfile | 154 +++++++++++++++++++++++++++++++++++ README.md | 58 +++++++------ 7 files changed, 226 insertions(+), 41 deletions(-) create mode 100644 .env.example create mode 100644 Justfile diff --git a/.agents/commands/release.md b/.agents/commands/release.md index 7e56d29f8..532790c65 100644 --- a/.agents/commands/release.md +++ b/.agents/commands/release.md @@ -205,12 +205,13 @@ Print the path to the release notes file so the user can share it for review. ### 7. Build Mainnet Release ```bash -./gradlew assembleMainnetRelease +just release ``` Expected APK path: `app/build/outputs/apk/mainnet/release/bitkit-mainnet-release-{newVersionCode}-universal.apk` +Expected AAB path: `app/build/outputs/bundle/mainnetRelease/bitkit-mainnet-release-{newVersionCode}.aab` -Verify the file exists. If the build fails, stop and report the error to the user. +Verify both files exist. If the build fails, stop and report the error to the user. ### 8. Upload APK to Draft Release diff --git a/.cursor/rules/rules.main.mdc b/.cursor/rules/rules.main.mdc index ff0409ac5..39f159d04 100644 --- a/.cursor/rules/rules.main.mdc +++ b/.cursor/rules/rules.main.mdc @@ -75,6 +75,6 @@ alwaysApply: true --- ## Rules for Android Unit tests and Instrumentation tests: -- run unit tests for specific files like this: `./gradlew :app:testDevDebugUnitTest --tests "to.bitkit.repositories.LightningRepoTest"` +- run unit tests for specific files like this: `just test file "to.bitkit.repositories.LightningRepoTest"` - write unit tests in the same style and using same libraries as: `CurrencyRepoTest`, `LightningRepoTest`, `WalletRepoTest` - in unit tests, use asserts from `kotlin.test` and mockito-kotlin for mocks diff --git a/.env.example b/.env.example new file mode 100644 index 000000000..abcc09cc4 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +# Copy to .env and fill only what you need. + +GITHUB_ACTOR= +GITHUB_TOKEN= +TX_TOKEN= + +KEYSTORE_FILE= +KEYSTORE_PASSWORD= +KEY_ALIAS= +KEY_PASSWORD= + +E2E=false +E2E_BACKEND=local +E2E_HOMEGATE_URL=http://127.0.0.1:6288 +GEO=true + +TREZOR_BRIDGE=false +TREZOR_BRIDGE_URL=http://10.0.2.2:21325 diff --git a/.gitignore b/.gitignore index 1700753e3..5eb837641 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ local.properties # Secrets google-services.json .env +.env.* +!.env.example *.keystore !debug.keystore keystore.* diff --git a/AGENTS.md b/AGENTS.md index 3f34b1bdb..b9d6a1456 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -10,43 +10,43 @@ Durable shared agent command specs live in `.agents/commands/`. For PR creation, ```sh # compile -./gradlew compileDevDebugKotlin +just compile # Build for dev -./gradlew assembleDevDebug +just build # Run unit tests -./gradlew testDevDebugUnitTest +just test # Run specific unit test file -./gradlew testDevDebugUnitTest --tests LightningRepoTest +just test file LightningRepoTest # Run instrumented tests -./gradlew connectedDevDebugAndroidTest +just test android # Build for E2E tests (UI hooks enabled, local Electrum by default) -E2E=true ./gradlew assembleDevRelease +just e2e # Build for E2E tests with geoblocking disabled -GEO=false E2E=true ./gradlew assembleDevRelease +just e2e no geo # Build for E2E tests using network Electrum (not local; staging/mainnet based on flavor) -E2E=true E2E_BACKEND=network ./gradlew assembleTnetRelease +just e2e network assembleTnetRelease # Lint using detekt -./gradlew detekt +just lint # Auto-format using detekt -./gradlew detekt --auto-correct +just format # Update detekt baseline -./gradlew detektBaseline +just lint baseline # Install dev build -./gradlew installDevDebug +just install # Clean build artifacts -./gradlew clean +just clean ``` ## Architecture Overview @@ -162,7 +162,7 @@ suspend fun getData(): Result = withContext(Dispatchers.IO) { - USE coding rules from `.cursor/default.rules.mdc` - For multi-step changes, stacked PR surgery, and review follow-up with several small edits, batch validation instead of running the full build/check suite after every edit. Run the relevant Gradle checks once the coherent change set is ready, before updating a PR or pushing. -- Still run `./gradlew compileDevDebugKotlin`, `./gradlew testDevDebugUnitTest`, and `./gradlew detekt` before the final PR update/push for code changes, and fix failures before pushing. +- Still run `just compile`, `just test`, and `just lint` before the final PR update/push for code changes, and fix failures before pushing. - After fixing validation failures, rerun the narrowest useful check that proves the fix. If only test files changed, prefer the targeted test task and a test-focused lint/detekt check when the project tooling supports it; otherwise use the standard detekt task before pushing. - Use narrower checks earlier only when they answer an immediate risk, e.g. a single unit test after touching focused business logic or a Kotlin compile after a risky refactor. - ALWAYS ask clarifying questions to ensure an optimal plan when encountering functional or technical uncertainties in requests diff --git a/Justfile b/Justfile new file mode 100644 index 000000000..731a4aebe --- /dev/null +++ b/Justfile @@ -0,0 +1,154 @@ +set dotenv-load := true +set dotenv-filename := ".env" +set windows-shell := ["cmd.exe", "/Q", "/C"] + +gradle := "./gradlew" + +default: + @just list + +list: + @printf "%s\n" \ + "list" \ + "init" \ + "compile" \ + "build [TASK]" \ + "release" \ + "install" \ + "test" \ + "test file PATTERN" \ + "test android" \ + "test lane LANE" \ + "lint" \ + "lint baseline" \ + "format" \ + "translations pull" \ + "translations push" \ + "e2e [network|no geo|TASK] [TASK]" \ + "changelog [all|next|hotfix]" \ + "clean" + +init: + #!/usr/bin/env bash + set -euo pipefail + + if [[ -e .env ]]; then + echo ".env already exists" + exit 0 + fi + + cp .env.example .env + echo "Created .env" + +compile: + {{ gradle }} compileDevDebugKotlin + +build task="assembleDevDebug": + {{ gradle }} {{ task }} + +release: + {{ gradle }} assembleMainnetRelease bundleMainnetRelease + +install: + {{ gradle }} installDevDebug + +test target="" value="": + #!/usr/bin/env bash + set -euo pipefail + + case "{{ target }}" in + "") + {{ gradle }} testDevDebugUnitTest + ;; + "android") + {{ gradle }} connectedDevDebugAndroidTest + ;; + "file") + if [[ -z "{{ value }}" ]]; then + echo "usage: just test file PATTERN" >&2 + exit 1 + fi + {{ gradle }} testDevDebugUnitTest --tests '{{ value }}' + ;; + "lane") + if [[ -z "{{ value }}" ]]; then + echo "usage: just test lane LANE" >&2 + exit 1 + fi + {{ gradle }} connectedDevDebug{{ value }}AndroidTest + ;; + *) + echo "usage: just test [file PATTERN|android|lane LANE]" >&2 + exit 1 + ;; + esac + +lint target="": + #!/usr/bin/env bash + set -euo pipefail + + case "{{ target }}" in + "") + {{ gradle }} detekt --rerun-tasks + ;; + "baseline") + {{ gradle }} detektBaseline --rerun-tasks + ;; + *) + echo "usage: just lint [baseline]" >&2 + exit 1 + ;; + esac + +format: + {{ gradle }} detekt --auto-correct --rerun-tasks + +translations action: + #!/usr/bin/env bash + set -euo pipefail + + case "{{ action }}" in + "pull") + ./scripts/pull-translations.sh + ;; + "push") + ./scripts/push-translations.sh + ;; + *) + echo "usage: just translations pull|push" >&2 + exit 1 + ;; + esac + +e2e mode="" value="" task="assembleDevRelease": + #!/usr/bin/env bash + set -euo pipefail + + case "{{ mode }}" in + "") + E2E=true {{ gradle }} {{ task }} + ;; + "network") + task="{{ value }}" + if [[ -z "$task" ]]; then + task="{{ task }}" + fi + E2E=true E2E_BACKEND=network {{ gradle }} "$task" + ;; + "no") + if [[ "{{ value }}" != "geo" ]]; then + echo "usage: just e2e no geo [TASK]" >&2 + exit 1 + fi + GEO=false E2E=true {{ gradle }} {{ task }} + ;; + *) + E2E=true {{ gradle }} "{{ mode }}" + ;; + esac + +changelog target="all": + ./scripts/preview-changelog.sh --target {{ target }} + +clean: + {{ gradle }} clean diff --git a/README.md b/README.md index 29c01e91d..2c1c06386 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,22 @@ See also: - For LNURL dev testing see [bitkit-docker](https://github.com/synonymdev/bitkit-docker) +### Command Launcher + +This repo includes a Justfile for common Gradle and script commands. + +Install `just` ([more options](https://github.com/casey/just#packages)): +- macOS: `brew install just` +- Linux: `mkdir -p ~/.local/bin && curl --proto '=https' --tlsv1.2 -sSf https://just.systems/install.sh | bash -s -- --to ~/.local/bin` +- Windows: `winget install --id Casey.Just --exact` +- Windows shell: install Git for Windows or another `sh` provider for Bash-backed recipes. + +Set up local env: +- `just init` +- Fill only the values you need in the fresh `.env` file, such as `GITHUB_ACTOR`, `GITHUB_TOKEN`, `TX_TOKEN`, and E2E build settings. + +Run `just list` to see available commands. The common ones are `just init`, `just compile`, `just build`, `just release`, `just test`, `just lint`, and `just translations pull`. + ### Lint This project uses detekt with default ktlint and compose-rules for android code linting. @@ -57,8 +73,8 @@ The following IDE plugins are recommended for development with Android Studio or **Commands** ```sh -./gradlew detekt # run analysis + formatting check -./gradlew detekt --auto-correct # auto-fix formatting issues +just lint # run analysis + formatting check +just format # auto-fix formatting issues ``` Reports are generated in: `app/build/reports/detekt/`. @@ -78,11 +94,11 @@ Install or update all bundled plugins with: **Commands** ```sh -./gradlew testDevDebugUnitTest # run unit tests +just test # run unit tests # run android tests: -./gradlew installDevDebug # install -./gradlew connectedDevDebugAndroidTest # run +just install # install +just test android # run ``` ## Localization @@ -104,7 +120,7 @@ To pull the latest translations from Transifex: 3. **Pull translations**: ```sh - ./scripts/pull-translations.sh + just translations pull ``` ### Pushing Source Strings @@ -112,15 +128,15 @@ To pull the latest translations from Transifex: When you add or modify translation keys in the EN source file, push them to Transifex: ```sh -tx push --source +just translations push ``` ### Translation Workflow 1. **Add/modify strings** in `app/src/main/res/values/strings.xml` -2. **Push to Transifex:** `tx push --source` +2. **Push to Transifex:** `just translations push` 3. **Translators** work on translations in Transifex -4. **Pull translations:** `./scripts/pull-translations.sh` +4. **Pull translations:** `just translations pull` 5. **Commit** the updated translation files ## Build @@ -143,8 +159,8 @@ Setup the signing config: Increment `versionCode` and `versionName` in `app/build.gradle.kts`, then run: ```sh -./gradlew assembleDevRelease -# ./gradlew assembleRelease # for all flavors +just build assembleDevRelease +# just build assembleRelease # for all flavors ``` APK is generated in `app/build/outputs/apk/_flavor_/release`. (`_flavor_` can be any of 'dev', 'mainnet', 'tnet'). @@ -155,25 +171,19 @@ Example for dev: `app/build/outputs/apk/dev/release` To build the mainnet flavor for release run: ```sh -./gradlew assembleMainnetRelease +just release ``` #### Android App Bundle (AAB) -For Play Store submission, build an AAB instead of APK: - -```sh -./gradlew bundleMainnetRelease -``` - -AAB is generated in `app/build/outputs/bundle/mainnetRelease/`. +`just release` builds both the mainnet APK and Play Store AAB. AAB is generated in `app/build/outputs/bundle/mainnetRelease/`. ### Build for E2E Testing Pass `E2E=true` and build any flavor. By default, E2E uses a local Electrum override. ```sh -E2E=true ./gradlew assembleDevRelease +just e2e ``` #### Use Network Electrum (Staging/Mainnet) @@ -182,11 +192,11 @@ Set `E2E_BACKEND=network` to use the network Electrum based on the build flavor: ```sh # regtest (dev flavor) -E2E=true E2E_BACKEND=network ./gradlew assembleDevRelease +just e2e network assembleDevRelease # testnet (tnet flavor) -E2E=true E2E_BACKEND=network ./gradlew assembleTnetRelease +just e2e network assembleTnetRelease # mainnet -E2E=true E2E_BACKEND=network ./gradlew assembleMainnetRelease +just e2e network assembleMainnetRelease ``` #### Disable Geoblocking Checks @@ -194,7 +204,7 @@ E2E=true E2E_BACKEND=network ./gradlew assembleMainnetRelease By default, geoblocking checks via API are enabled. To disable at build time, use the `GEO` environment variable: ```sh -GEO=false E2E=true ./gradlew assembleDevRelease +just e2e no geo ``` ## Contributing From f1c0320df49a2008553d58b963a825e72f008ca2 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Fri, 22 May 2026 02:17:17 +0200 Subject: [PATCH 2/5] fix: add source translation command --- Justfile | 18 +++++++++++++++--- README.md | 4 ++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Justfile b/Justfile index 731a4aebe..d64ba41a5 100644 --- a/Justfile +++ b/Justfile @@ -24,6 +24,7 @@ list: "format" \ "translations pull" \ "translations push" \ + "translations push source" \ "e2e [network|no geo|TASK] [TASK]" \ "changelog [all|next|hotfix]" \ "clean" @@ -103,7 +104,7 @@ lint target="": format: {{ gradle }} detekt --auto-correct --rerun-tasks -translations action: +translations action value="": #!/usr/bin/env bash set -euo pipefail @@ -112,10 +113,21 @@ translations action: ./scripts/pull-translations.sh ;; "push") - ./scripts/push-translations.sh + case "{{ value }}" in + "") + ./scripts/push-translations.sh + ;; + "source") + tx push --source + ;; + *) + echo "usage: just translations push [source]" >&2 + exit 1 + ;; + esac ;; *) - echo "usage: just translations pull|push" >&2 + echo "usage: just translations pull|push [source]" >&2 exit 1 ;; esac diff --git a/README.md b/README.md index 2c1c06386..773fd98e9 100644 --- a/README.md +++ b/README.md @@ -128,13 +128,13 @@ To pull the latest translations from Transifex: When you add or modify translation keys in the EN source file, push them to Transifex: ```sh -just translations push +just translations push source ``` ### Translation Workflow 1. **Add/modify strings** in `app/src/main/res/values/strings.xml` -2. **Push to Transifex:** `just translations push` +2. **Push to Transifex:** `just translations push source` 3. **Translators** work on translations in Transifex 4. **Pull translations:** `just translations pull` 5. **Commit** the updated translation files From 0e9fdfb7eb4c99d3b8aaed2130b2922259763326 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Fri, 22 May 2026 02:42:00 +0200 Subject: [PATCH 3/5] fix: use sh for just on windows --- Justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Justfile b/Justfile index d64ba41a5..783f79bd1 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,6 @@ set dotenv-load := true set dotenv-filename := ".env" -set windows-shell := ["cmd.exe", "/Q", "/C"] +set windows-shell := ["sh", "-cu"] gradle := "./gradlew" From 4dd005d4b7ca536c0d527b24657244e3a39911d7 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Fri, 22 May 2026 14:03:03 +0200 Subject: [PATCH 4/5] fix: simplify just dry runs --- Justfile | 99 +++----------------------------------------------------- 1 file changed, 4 insertions(+), 95 deletions(-) diff --git a/Justfile b/Justfile index 783f79bd1..ac9b3a06f 100644 --- a/Justfile +++ b/Justfile @@ -54,110 +54,19 @@ install: {{ gradle }} installDevDebug test target="" value="": - #!/usr/bin/env bash - set -euo pipefail - - case "{{ target }}" in - "") - {{ gradle }} testDevDebugUnitTest - ;; - "android") - {{ gradle }} connectedDevDebugAndroidTest - ;; - "file") - if [[ -z "{{ value }}" ]]; then - echo "usage: just test file PATTERN" >&2 - exit 1 - fi - {{ gradle }} testDevDebugUnitTest --tests '{{ value }}' - ;; - "lane") - if [[ -z "{{ value }}" ]]; then - echo "usage: just test lane LANE" >&2 - exit 1 - fi - {{ gradle }} connectedDevDebug{{ value }}AndroidTest - ;; - *) - echo "usage: just test [file PATTERN|android|lane LANE]" >&2 - exit 1 - ;; - esac + {{ if target == "" { gradle + " testDevDebugUnitTest" } else if target == "android" { gradle + " connectedDevDebugAndroidTest" } else if target == "file" { if value == "" { error("usage: just test file PATTERN") } else { gradle + " testDevDebugUnitTest --tests '" + value + "'" } } else if target == "lane" { if value == "" { error("usage: just test lane LANE") } else { gradle + " connectedDevDebug" + value + "AndroidTest" } } else { error("usage: just test [file PATTERN|android|lane LANE]") } }} lint target="": - #!/usr/bin/env bash - set -euo pipefail - - case "{{ target }}" in - "") - {{ gradle }} detekt --rerun-tasks - ;; - "baseline") - {{ gradle }} detektBaseline --rerun-tasks - ;; - *) - echo "usage: just lint [baseline]" >&2 - exit 1 - ;; - esac + {{ if target == "" { gradle + " detekt --rerun-tasks" } else if target == "baseline" { gradle + " detektBaseline --rerun-tasks" } else { error("usage: just lint [baseline]") } }} format: {{ gradle }} detekt --auto-correct --rerun-tasks translations action value="": - #!/usr/bin/env bash - set -euo pipefail - - case "{{ action }}" in - "pull") - ./scripts/pull-translations.sh - ;; - "push") - case "{{ value }}" in - "") - ./scripts/push-translations.sh - ;; - "source") - tx push --source - ;; - *) - echo "usage: just translations push [source]" >&2 - exit 1 - ;; - esac - ;; - *) - echo "usage: just translations pull|push [source]" >&2 - exit 1 - ;; - esac + {{ if action == "pull" { "./scripts/pull-translations.sh" } else if action == "push" { if value == "" { "./scripts/push-translations.sh" } else if value == "source" { "tx push --source" } else { error("usage: just translations push [source]") } } else { error("usage: just translations pull|push [source]") } }} e2e mode="" value="" task="assembleDevRelease": - #!/usr/bin/env bash - set -euo pipefail - - case "{{ mode }}" in - "") - E2E=true {{ gradle }} {{ task }} - ;; - "network") - task="{{ value }}" - if [[ -z "$task" ]]; then - task="{{ task }}" - fi - E2E=true E2E_BACKEND=network {{ gradle }} "$task" - ;; - "no") - if [[ "{{ value }}" != "geo" ]]; then - echo "usage: just e2e no geo [TASK]" >&2 - exit 1 - fi - GEO=false E2E=true {{ gradle }} {{ task }} - ;; - *) - E2E=true {{ gradle }} "{{ mode }}" - ;; - esac + {{ if mode == "" { "E2E=true " + gradle + " " + task } else if mode == "network" { "E2E=true E2E_BACKEND=network " + gradle + " " + if value == "" { task } else { value } } else if mode == "no" { if value == "geo" { "GEO=false E2E=true " + gradle + " " + task } else { error("usage: just e2e no geo [TASK]") } } else { "E2E=true " + gradle + " " + mode } }} changelog target="all": ./scripts/preview-changelog.sh --target {{ target }} From 494a6dbbcebf967d16c2b08eaef9327973cf4132 Mon Sep 17 00:00:00 2001 From: Ovi Trif Date: Sat, 23 May 2026 04:56:30 +0200 Subject: [PATCH 5/5] fix: avoid default just env vars --- .env.example | 26 ++++++++++++-------------- Justfile | 6 +++--- README.md | 2 +- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/.env.example b/.env.example index abcc09cc4..5af4ffb5d 100644 --- a/.env.example +++ b/.env.example @@ -1,18 +1,16 @@ -# Copy to .env and fill only what you need. +# Copy to .env and uncomment only what you need. -GITHUB_ACTOR= -GITHUB_TOKEN= -TX_TOKEN= +# GITHUB_ACTOR= +# GITHUB_TOKEN= +# TX_TOKEN= -KEYSTORE_FILE= -KEYSTORE_PASSWORD= -KEY_ALIAS= -KEY_PASSWORD= +# KEYSTORE_FILE= +# KEYSTORE_PASSWORD= +# KEY_ALIAS= +# KEY_PASSWORD= -E2E=false -E2E_BACKEND=local -E2E_HOMEGATE_URL=http://127.0.0.1:6288 -GEO=true +# E2E_BACKEND=local +# E2E_HOMEGATE_URL=http://127.0.0.1:6288 -TREZOR_BRIDGE=false -TREZOR_BRIDGE_URL=http://10.0.2.2:21325 +# TREZOR_BRIDGE=true +# TREZOR_BRIDGE_URL=http://10.0.2.2:21325 diff --git a/Justfile b/Justfile index ac9b3a06f..c2a46e85f 100644 --- a/Justfile +++ b/Justfile @@ -30,10 +30,10 @@ list: "clean" init: - #!/usr/bin/env bash - set -euo pipefail + #!/usr/bin/env sh + set -eu - if [[ -e .env ]]; then + if [ -e .env ]; then echo ".env already exists" exit 0 fi diff --git a/README.md b/README.md index 773fd98e9..7baaebd2d 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Install `just` ([more options](https://github.com/casey/just#packages)): Set up local env: - `just init` -- Fill only the values you need in the fresh `.env` file, such as `GITHUB_ACTOR`, `GITHUB_TOKEN`, `TX_TOKEN`, and E2E build settings. +- Uncomment only the values you need in the fresh `.env` file, such as `GITHUB_ACTOR`, `GITHUB_TOKEN`, `TX_TOKEN`, and E2E build settings. Run `just list` to see available commands. The common ones are `just init`, `just compile`, `just build`, `just release`, `just test`, `just lint`, and `just translations pull`.