diff --git a/BUILDING.md b/BUILDING.md deleted file mode 100644 index 3a90cf4d0c..0000000000 --- a/BUILDING.md +++ /dev/null @@ -1,46 +0,0 @@ -Building Session -=============== - -Basics ------- - -Session uses [Gradle](http://gradle.org) to build the project and to maintain -dependencies. However, you needn't install it yourself; the -"gradle wrapper" `gradlew`, mentioned below, will do that for you. - -Dependencies ---------------- -You will need Java 8 set up on your machine. - -Ensure that the following packages are installed from the Android SDK manager: - -* Android SDK Build Tools (see buildToolsVersion in build.gradle) -* SDK Platform (all API levels) -* Android Support Repository -* Google Repository - -In Android studio, this can be done from the Quickstart panel. Just choose "Configure", then "SDK Manager". In the SDK Tools tab of the SDK Manager, make sure that "Android Support Repository" is installed, and that the latest "Android SDK build-tools" are installed. Click "OK" to return to the Quickstart panel. You may also need to install API version 28 in the SDK platforms tab. - -Setting up a development environment and building from Android Studio ------------------------------------- - -[Android Studio](https://developer.android.com/sdk/installing/studio.html) is the recommended development environment. - -1. Open Android Studio. On a new installation, the Quickstart panel will appear. If you have open projects, close them using "File > Close Project" to see the Quickstart panel. -2. From the Quickstart panel, choose "Checkout from Version Control" then "git". -3. Paste the URL for the session-android project when prompted (https://github.com/session-foundation/session-android.git). -4. Android Studio should detect the presence of a project file and ask you whether to open it. Click "yes". -5. Default config options should be good enough. -6. Project initialization and building should proceed. -7. Clone submodules with `git submodule update --init --recursive` - -If you would like to build the Huawei Flavor with Huawei HMS push notifications you will need to pass 'huawei' as a command line arg to include the required dependencies. - -e.g. `./gradlew assembleHuaweiDebug -Phuawei` - -If you are building in Android Studio then add `-Phuawei` to `Preferences > Build, Execution, Deployment > Gradle-Android Compiler > Command-line Options` - -Contributing code ------------------ - -Code contributions should be sent via Github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests). diff --git a/FDROID_RELEASE.md b/FDROID_RELEASE.md new file mode 100644 index 0000000000..4655a3d84c --- /dev/null +++ b/FDROID_RELEASE.md @@ -0,0 +1,129 @@ +# F-Droid Release Architecture + +This document explains how Session's F-Droid distribution works, why a custom script is +needed, and how to perform the F-Droid release manually if the script is unavailable or broken. + +--- + +## Background: how F-Droid works + +F-Droid is an open-source app store for Android. Unlike the Play Store, it operates on a +**repository model**: a repo is a directory of APKs and a signed index file that F-Droid +clients download to discover available apps and versions. + +There are two ways an app can appear in F-Droid: + +1. **Official F-Droid repo** — F-Droid builds the APK itself from source, using its own + signing key. The app developer has no control over the signing key or build timing. +2. **Self-hosted repo** — The developer hosts their own F-Droid-compatible repository, + signs the APKs and the repo index with their own keys, and F-Droid clients add it as a + custom repo source. + +Session uses a **self-hosted repo** at +[session-foundation/session-fdroid](https://github.com/session-foundation/session-fdroid). +This gives us control over the signing key and release timing, at the cost of needing to +maintain the repo ourselves. + +--- + +## Why a custom script is needed + +The standard `fdroid` command-line tools are designed around a workflow where F-Droid +**builds** the APK from source. Because we sign APKs ourselves (to keep the signing key +private) and provide pre-built APKs, the standard workflow does not apply cleanly. + +Our script bridges that gap by: + +1. **Building and signing the APK** with Gradle, using our own keystore — `fdroid update` + only indexes and re-signs the repo index, it does not build or sign APKs itself. +2. **Managing APK inventory** — F-Droid clients expect old versions to remain available for + users who cannot upgrade immediately. The script maintains a rolling window of the last + four releases, removing anything older. +3. **Keeping secrets out of the repo** — `config.yml` (which contains keystore paths and + passwords) and the generated `metadata/.yml` (which embeds the current + version code) are derived from committed template files (`.tpl`) at release time and + deleted afterwards. Neither sensitive file is ever committed. +4. **Automating the PR workflow** — the `session-fdroid` repo uses a PR-based flow so that + every release update is reviewed by a human before it reaches users. The script handles + branch creation and PR opening automatically. + +--- + +## Repository structure of session-fdroid + +``` +session-fdroid/ +├── fdroid/ +│ ├── repo/ # APKs served to F-Droid clients +│ │ └── session--.apk +│ ├── metadata/ +│ │ └── .yml.tpl # Template; .yml is generated at release time +│ ├── config.yml.tpl # Template; config.yml is generated at release time +│ └── index-v1.jar / index-v2.json # Signed repo index, regenerated by `fdroid update` +``` + +The `.tpl` files are committed to the repo. The generated `config.yml` and +`metadata/.yml` are produced locally, used by `fdroid update`, then deleted — +they are never committed. + +--- + +## How to publish a release using fdroidserver + +Our script automates the steps below, but understanding them means you can recover from a +broken script and reason about what the tooling is doing. + +The core tool is `fdroid update` from the +[fdroidserver](https://gitlab.com/fdroid/fdroidserver) package. Its job is to: + +- Scan the APKs in `repo/` +- Read the app metadata from `metadata/.yml` +- Produce a signed repository index (`index-v1.jar` and `index-v2.json`) that F-Droid + clients download to learn what versions are available and where to get them + +`fdroid update` does **not** build or sign APKs — it only indexes pre-built ones and signs +the repo index using the key configured in `config.yml`. + +### config.yml + +`config.yml` tells `fdroid update` how to sign the repo index and where to find the Android +SDK. The key fields are: + +```yaml +repo_keyalias: +keystore: /path/to/repo-signing-keystore.p12 # PKCS12 format +keystorepass: +keypass: +android_sdk_path: /path/to/android/sdk +``` + +This file contains secrets and is **never committed**. In `session-fdroid` it is generated +from `config.yml.tpl` at release time and deleted after `fdroid update` runs. + +### metadata/.yml + +F-Droid uses a metadata file per app to describe the app to clients — its name, description, +links, and crucially the `CurrentVersionCode`, which tells F-Droid clients which version is +the latest so they know when to prompt for an upgrade. + +The full metadata format is documented at +https://f-droid.org/en/docs/Build_Metadata_Reference/ + +In `session-fdroid`, only the `CurrentVersionCode` field changes between releases. The rest +of the file is static and lives in `metadata/.yml.tpl`. The `.tpl` is rendered +to `.yml` at release time with the new version code substituted in, used by `fdroid update`, +then deleted. + +### Adding a new version + +The fdroidserver workflow for adding a new version to a self-hosted repo is: + +1. Place the new signed APK(s) in the `repo/` directory alongside existing ones. +2. Update `CurrentVersionCode` in `metadata/.yml` to the new version code. +3. Run `fdroid update` from the repo root (where `config.yml` lives). It will scan all APKs, + merge in the metadata, and rewrite the signed index files. +4. Commit the updated index files and new APKs (but not `config.yml` or the rendered + `metadata/.yml`). + +When `master` of `session-fdroid` is updated, any F-Droid client that has added our repo +will pick up the new index on its next refresh and offer the update to users. diff --git a/RELEASE.md b/RELEASE.md new file mode 100644 index 0000000000..2c2aa13437 --- /dev/null +++ b/RELEASE.md @@ -0,0 +1,202 @@ +Building and Releasing Session +============================== + +## Building + +### Dependencies + +Session uses standard Gradle/Android project structure. + +Ensure the following are set up on your machine: + +- **Android Studio** (recommended) or the Android SDK command-line tools +- **JDK 17** or later +- The following packages installed via the Android SDK Manager: + - Android SDK Build Tools (see `buildToolsVersion` in `build.gradle`) + - SDK Platform matching the `compileSdkVersion` in `build.gradle` + - Android Support Repository + +### Setting up and building in Android Studio + +[Android Studio](https://developer.android.com/studio) is the recommended development environment. + +1. Open Android Studio. On a new installation the Quickstart panel will appear. If you have + open projects, close them via **File > Close Project** to return to the Quickstart panel. +2. Choose **Get from Version Control** and paste the repository URL: + `https://github.com/session-foundation/session-android.git` +3. The default Gradle sync and build configuration should work out of the box. + +### Build flavors + +The project has three product flavors: + +| Flavor | Description | +|----------|----------------------------------------------| +| `play` | Google Play Store build | +| `fdroid` | F-Droid build (no proprietary dependencies) | +| `huawei` | Huawei AppGallery build (HMS push) | + +To build a specific flavor manually, run the corresponding Gradle task, for example: + +```sh +./gradlew assemblePlayDebug +./gradlew assembleFdroidDebug +./gradlew assembleHuaweiDebug -Phuawei +``` + +The `-Phuawei` flag is required for Huawei builds to include the HMS dependencies. If building +in Android Studio, add `-Phuawei` under +**Preferences > Build, Execution, Deployment > Gradle-Android Compiler > Command-line Options**. + +### Building a signed release APK manually + +The build is standard Gradle — `build-and-release.py` is a convenience wrapper and is not +required if you only need to produce a signed APK. Pass the signing credentials directly as +Gradle properties: + +```sh +./gradlew \ + -PSESSION_STORE_FILE='/path/to/keystore.jks' \ + -PSESSION_STORE_PASSWORD='' \ + -PSESSION_KEY_ALIAS='' \ + -PSESSION_KEY_PASSWORD='' \ + assembleFdroidRelease +``` + +The keystore file can be recreated from `release-creds.toml` — the `keystore` field in each +section is the JKS file base64-encoded, so it can be decoded back to a `.jks` file using any +standard base64 tool. + +--- + +## Release process + +> **Quick checklist** +> +> 1. [ ] Create a `release/MAJOR.MINOR.PATCH` branch from `master` +> 2. [ ] Merge `dev` into the release branch (full release), or cherry-pick the relevant patch commits +> 3. [ ] Bump the version code +> 4. [ ] Create a GitHub release draft for the version (e.g. `1.23.4`) in this repository +> 5. [ ] Obtain `release-creds.toml` from a project maintainer and place it in the project root +> 6. [ ] Run `./scripts/build-and-release.py` from the release branch +> 7. [ ] Upload the AAB bundle to the Play Store +> 8. [ ] Review and merge the automated F-Droid PR in `session-foundation/session-fdroid` +> 9. [ ] Review and publish the GitHub release draft + +Steps 6–9 are explained in detail below. Steps 4 and 5 must be completed **before** running +the script — if no release draft exists the artifact upload is silently skipped, and without +the credentials file the script will exit immediately. + +### Branching strategy + +| Branch | Purpose | +|--------|---------| +| `master` | Represents production — always reflects what is live | +| `dev` | Integration branch for ongoing development | +| `release/MAJOR.MINOR.PATCH` | Short-lived branch used to prepare and build a release | + +To start a release: + +```sh +git checkout master +git checkout -b release/1.23.4 +``` + +For a **full release**, merge `dev` into the release branch: + +```sh +git merge dev +``` + +For a **patch release**, cherry-pick only the relevant commits: + +```sh +git cherry-pick ... +``` + +Once the branch is ready, bump the version code in `app/build.gradle` (the `versionCode` and +`versionName` fields), commit the change, then proceed with the steps below. + +### Prerequisites + +- [**uv**](https://docs.astral.sh/uv/) — the script uses `uv run` to manage its Python + dependencies (`fdroidserver`) automatically +- [**gh**](https://cli.github.com/) — GitHub CLI, authenticated and with access to + `session-foundation/session-android` and `session-foundation/session-fdroid` +- **Android SDK** — either `ANDROID_HOME` set in the environment, or `sdk.dir` defined in + `local.properties`. This would have been set up for you if you have opened the project in Android Studio. + +### Credentials file + +The script requires a `release-creds.toml` file in the project root. This file is not +committed to the repository — ask a project maintainer for it. Its structure is: + +```toml +[build.play] +keystore = "" +keystore_password = "" +key_alias = "" +key_password = "" + +[build.huawei] +keystore = "" +keystore_password = "" +key_alias = "" +key_password = "" + +[fdroid] +keystore = "" +keystore_password = "" +key_alias = "" +key_password = "" +``` + +### Running the script + +From the project root, run: + +```sh +./scripts/build-and-release.py +``` + +This performs a full build and release. The built artifacts are placed under where they suppose +to be according to standard Android project layout. + +#### Options + +| Flag | Description | +|-----------------|-----------------------------------------------------------------------------| +| `--build-only` | Build all flavors but skip the F-Droid PR and GitHub release upload steps | +| `--build-type` | Gradle build type to use (default: `release`) | + +For example, to perform builds without publishing anything: + +```sh +./scripts/build-and-release.py --build-only +``` + +### What the script does in detail + +1. **Play build** — assembles split APKs and an AAB bundle for the `play` flavor, signed with + the Play keystore. +2. **F-Droid build** — assembles split APKs for the `fdroid` flavor. +3. **F-Droid repo update** (skipped with `--build-only`) — see [FDROID_RELEASE.md](FDROID_RELEASE.md) + for a full explanation of the architecture and manual steps: + - Clones `session-foundation/session-fdroid` into `build/fdroidrepo` if not already present. + - Creates a `release/` branch. + - Copies the new APKs into the repo, pruning old versions (keeps the latest + four releases). + - Regenerates repository metadata using `fdroid update`. + - Commits and opens a pull request against `master` for human review and merge. +4. **Huawei build** — assembles a universal APK for the `huawei` flavor. +5. **GitHub release upload** (skipped with `--build-only`): + - Looks for a release draft in this repository matching the version name. + - If found, uploads the Play split APKs, the AAB bundle, and the Huawei APKs to it. + - If no draft exists, this step is skipped (no error). + +--- + +## Contributing code + +Code contributions should be submitted via GitHub as pull requests from feature branches, +[as explained here](https://help.github.com/articles/using-pull-requests).