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..5af4ffb5d --- /dev/null +++ b/.env.example @@ -0,0 +1,16 @@ +# Copy to .env and uncomment only what you need. + +# GITHUB_ACTOR= +# GITHUB_TOKEN= +# TX_TOKEN= + +# KEYSTORE_FILE= +# KEYSTORE_PASSWORD= +# KEY_ALIAS= +# KEY_PASSWORD= + +# E2E_BACKEND=local +# E2E_HOMEGATE_URL=http://127.0.0.1:6288 + +# TREZOR_BRIDGE=true +# 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..c2a46e85f --- /dev/null +++ b/Justfile @@ -0,0 +1,75 @@ +set dotenv-load := true +set dotenv-filename := ".env" +set windows-shell := ["sh", "-cu"] + +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" \ + "translations push source" \ + "e2e [network|no geo|TASK] [TASK]" \ + "changelog [all|next|hotfix]" \ + "clean" + +init: + #!/usr/bin/env sh + set -eu + + 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="": + {{ 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="": + {{ 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="": + {{ 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": + {{ 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 }} + +clean: + {{ gradle }} clean diff --git a/README.md b/README.md index 29c01e91d..7baaebd2d 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` +- 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`. + ### 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 source ``` ### 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 source` 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