Skip to content

Commit d1c229a

Browse files
Merge pull request #2112 from session-foundation/release/1.33.2
Release/1.33.2
2 parents ceb5e7e + 7f91639 commit d1c229a

25 files changed

Lines changed: 1297 additions & 158 deletions

File tree

ARCHITECTURE.md

Lines changed: 719 additions & 0 deletions
Large diffs are not rendered by default.

BUILDING.md

Lines changed: 0 additions & 46 deletions
This file was deleted.

FDROID_RELEASE.md

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# F-Droid Release Architecture
2+
3+
This document explains how Session's F-Droid distribution works, why a custom script is
4+
needed, and how to perform the F-Droid release manually if the script is unavailable or broken.
5+
6+
---
7+
8+
## Background: how F-Droid works
9+
10+
F-Droid is an open-source app store for Android. Unlike the Play Store, it operates on a
11+
**repository model**: a repo is a directory of APKs and a signed index file that F-Droid
12+
clients download to discover available apps and versions.
13+
14+
There are two ways an app can appear in F-Droid:
15+
16+
1. **Official F-Droid repo** — F-Droid builds the APK itself from source, using its own
17+
signing key. The app developer has no control over the signing key or build timing.
18+
2. **Self-hosted repo** — The developer hosts their own F-Droid-compatible repository,
19+
signs the APKs and the repo index with their own keys, and F-Droid clients add it as a
20+
custom repo source.
21+
22+
Session uses a **self-hosted repo** at
23+
[session-foundation/session-fdroid](https://github.com/session-foundation/session-fdroid).
24+
This gives us control over the signing key and release timing, at the cost of needing to
25+
maintain the repo ourselves.
26+
27+
---
28+
29+
## Why a custom script is needed
30+
31+
The standard `fdroid` command-line tools are designed around a workflow where F-Droid
32+
**builds** the APK from source. Because we sign APKs ourselves (to keep the signing key
33+
private) and provide pre-built APKs, the standard workflow does not apply cleanly.
34+
35+
Our script bridges that gap by:
36+
37+
1. **Building and signing the APK** with Gradle, using our own keystore — `fdroid update`
38+
only indexes and re-signs the repo index, it does not build or sign APKs itself.
39+
2. **Managing APK inventory** — F-Droid clients expect old versions to remain available for
40+
users who cannot upgrade immediately. The script maintains a rolling window of the last
41+
four releases, removing anything older.
42+
3. **Keeping secrets out of the repo**`config.yml` (which contains keystore paths and
43+
passwords) and the generated `metadata/<package-id>.yml` (which embeds the current
44+
version code) are derived from committed template files (`.tpl`) at release time and
45+
deleted afterwards. Neither sensitive file is ever committed.
46+
4. **Automating the PR workflow** — the `session-fdroid` repo uses a PR-based flow so that
47+
every release update is reviewed by a human before it reaches users. The script handles
48+
branch creation and PR opening automatically.
49+
50+
---
51+
52+
## Repository structure of session-fdroid
53+
54+
```
55+
session-fdroid/
56+
├── fdroid/
57+
│ ├── repo/ # APKs served to F-Droid clients
58+
│ │ └── session-<version>-<abi>.apk
59+
│ ├── metadata/
60+
│ │ └── <package-id>.yml.tpl # Template; .yml is generated at release time
61+
│ ├── config.yml.tpl # Template; config.yml is generated at release time
62+
│ └── index-v1.jar / index-v2.json # Signed repo index, regenerated by `fdroid update`
63+
```
64+
65+
The `.tpl` files are committed to the repo. The generated `config.yml` and
66+
`metadata/<package-id>.yml` are produced locally, used by `fdroid update`, then deleted —
67+
they are never committed.
68+
69+
---
70+
71+
## How to publish a release using fdroidserver
72+
73+
Our script automates the steps below, but understanding them means you can recover from a
74+
broken script and reason about what the tooling is doing.
75+
76+
The core tool is `fdroid update` from the
77+
[fdroidserver](https://gitlab.com/fdroid/fdroidserver) package. Its job is to:
78+
79+
- Scan the APKs in `repo/`
80+
- Read the app metadata from `metadata/<package-id>.yml`
81+
- Produce a signed repository index (`index-v1.jar` and `index-v2.json`) that F-Droid
82+
clients download to learn what versions are available and where to get them
83+
84+
`fdroid update` does **not** build or sign APKs — it only indexes pre-built ones and signs
85+
the repo index using the key configured in `config.yml`.
86+
87+
### config.yml
88+
89+
`config.yml` tells `fdroid update` how to sign the repo index and where to find the Android
90+
SDK. The key fields are:
91+
92+
```yaml
93+
repo_keyalias: <alias>
94+
keystore: /path/to/repo-signing-keystore.p12 # PKCS12 format
95+
keystorepass: <keystore-password>
96+
keypass: <key-password>
97+
android_sdk_path: /path/to/android/sdk
98+
```
99+
100+
This file contains secrets and is **never committed**. In `session-fdroid` it is generated
101+
from `config.yml.tpl` at release time and deleted after `fdroid update` runs.
102+
103+
### metadata/<package-id>.yml
104+
105+
F-Droid uses a metadata file per app to describe the app to clients — its name, description,
106+
links, and crucially the `CurrentVersionCode`, which tells F-Droid clients which version is
107+
the latest so they know when to prompt for an upgrade.
108+
109+
The full metadata format is documented at
110+
https://f-droid.org/en/docs/Build_Metadata_Reference/
111+
112+
In `session-fdroid`, only the `CurrentVersionCode` field changes between releases. The rest
113+
of the file is static and lives in `metadata/<package-id>.yml.tpl`. The `.tpl` is rendered
114+
to `.yml` at release time with the new version code substituted in, used by `fdroid update`,
115+
then deleted.
116+
117+
### Adding a new version
118+
119+
The fdroidserver workflow for adding a new version to a self-hosted repo is:
120+
121+
1. Place the new signed APK(s) in the `repo/` directory alongside existing ones.
122+
2. Update `CurrentVersionCode` in `metadata/<package-id>.yml` to the new version code.
123+
3. Run `fdroid update` from the repo root (where `config.yml` lives). It will scan all APKs,
124+
merge in the metadata, and rewrite the signed index files.
125+
4. Commit the updated index files and new APKs (but not `config.yml` or the rendered
126+
`metadata/<package-id>.yml`).
127+
128+
When `master` of `session-fdroid` is updated, any F-Droid client that has added our repo
129+
will pick up the new index on its next refresh and offer the update to users.

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ Session integrates directly with [Oxen Service Nodes](https://docs.oxen.io/about
1212

1313
<img src="https://i.imgur.com/wcdAGBh.png" width="320" />
1414

15+
## Architecture and documentation
16+
17+
You can refer to [this document](ARCHITECTURE.md) to learn about the app's architecture and to get an overview of the important parts of the code and how they fit together.
18+
1519
## Want to contribute? Found a bug or have a feature request?
1620

1721
Please search for any [existing issues](https://github.com/session-foundation/session-android/issues) that describe your bugs in order to avoid duplicate submissions. Submissions can be made by making a pull request to our `dev` branch. If you don't know where to start contributing, try reading the Github issues page for ideas.

RELEASE.md

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
Building and Releasing Session
2+
==============================
3+
4+
## Building
5+
6+
### Dependencies
7+
8+
Session uses standard Gradle/Android project structure.
9+
10+
Ensure the following are set up on your machine:
11+
12+
- **Android Studio** (recommended) or the Android SDK command-line tools
13+
- **JDK 17** or later
14+
- The following packages installed via the Android SDK Manager:
15+
- Android SDK Build Tools (see `buildToolsVersion` in `build.gradle`)
16+
- SDK Platform matching the `compileSdkVersion` in `build.gradle`
17+
- Android Support Repository
18+
19+
### Setting up and building in Android Studio
20+
21+
[Android Studio](https://developer.android.com/studio) is the recommended development environment.
22+
23+
1. Open Android Studio. On a new installation the Quickstart panel will appear. If you have
24+
open projects, close them via **File > Close Project** to return to the Quickstart panel.
25+
2. Choose **Get from Version Control** and paste the repository URL:
26+
`https://github.com/session-foundation/session-android.git`
27+
3. The default Gradle sync and build configuration should work out of the box.
28+
29+
### Build flavors
30+
31+
The project has three product flavors:
32+
33+
| Flavor | Description |
34+
|----------|----------------------------------------------|
35+
| `play` | Google Play Store build |
36+
| `fdroid` | F-Droid build (no proprietary dependencies) |
37+
| `huawei` | Huawei AppGallery build (HMS push) |
38+
39+
To build a specific flavor manually, run the corresponding Gradle task, for example:
40+
41+
```sh
42+
./gradlew assemblePlayDebug
43+
./gradlew assembleFdroidDebug
44+
./gradlew assembleHuaweiDebug -Phuawei
45+
```
46+
47+
The `-Phuawei` flag is required for Huawei builds to include the HMS dependencies. If building
48+
in Android Studio, add `-Phuawei` under
49+
**Preferences > Build, Execution, Deployment > Gradle-Android Compiler > Command-line Options**.
50+
51+
### Building a signed release APK manually
52+
53+
The build is standard Gradle — `build-and-release.py` is a convenience wrapper and is not
54+
required if you only need to produce a signed APK. Pass the signing credentials directly as
55+
Gradle properties:
56+
57+
```sh
58+
./gradlew \
59+
-PSESSION_STORE_FILE='/path/to/keystore.jks' \
60+
-PSESSION_STORE_PASSWORD='<keystore-password>' \
61+
-PSESSION_KEY_ALIAS='<key-alias>' \
62+
-PSESSION_KEY_PASSWORD='<key-password>' \
63+
assembleFdroidRelease
64+
```
65+
66+
The keystore file can be recreated from `release-creds.toml` — the `keystore` field in each
67+
section is the JKS file base64-encoded, so it can be decoded back to a `.jks` file using any
68+
standard base64 tool.
69+
70+
---
71+
72+
## Release process
73+
74+
> **Quick checklist**
75+
>
76+
> 1. [ ] Create a `release/MAJOR.MINOR.PATCH` branch from `master`
77+
> 2. [ ] Merge `dev` into the release branch (full release), or cherry-pick the relevant patch commits
78+
> 3. [ ] Bump the version code
79+
> 4. [ ] Create a GitHub release draft for the version (e.g. `1.23.4`) in this repository
80+
> 5. [ ] Obtain `release-creds.toml` from a project maintainer and place it in the project root
81+
> 6. [ ] Run `./scripts/build-and-release.py` from the release branch
82+
> 7. [ ] Upload the AAB bundle to the Play Store
83+
> 8. [ ] Review and merge the automated F-Droid PR in `session-foundation/session-fdroid`
84+
> 9. [ ] Review and publish the GitHub release draft
85+
86+
Steps 6–9 are explained in detail below. Steps 4 and 5 must be completed **before** running
87+
the script — if no release draft exists the artifact upload is silently skipped, and without
88+
the credentials file the script will exit immediately.
89+
90+
### Branching strategy
91+
92+
| Branch | Purpose |
93+
|--------|---------|
94+
| `master` | Represents production — always reflects what is live |
95+
| `dev` | Integration branch for ongoing development |
96+
| `release/MAJOR.MINOR.PATCH` | Short-lived branch used to prepare and build a release |
97+
98+
To start a release:
99+
100+
```sh
101+
git checkout master
102+
git checkout -b release/1.23.4
103+
```
104+
105+
For a **full release**, merge `dev` into the release branch:
106+
107+
```sh
108+
git merge dev
109+
```
110+
111+
For a **patch release**, cherry-pick only the relevant commits:
112+
113+
```sh
114+
git cherry-pick <commit>...
115+
```
116+
117+
Once the branch is ready, bump the version code in `app/build.gradle` (the `versionCode` and
118+
`versionName` fields), commit the change, then proceed with the steps below.
119+
120+
### Prerequisites
121+
122+
- [**uv**](https://docs.astral.sh/uv/) — the script uses `uv run` to manage its Python
123+
dependencies (`fdroidserver`) automatically
124+
- [**gh**](https://cli.github.com/) — GitHub CLI, authenticated and with access to
125+
`session-foundation/session-android` and `session-foundation/session-fdroid`
126+
- **Android SDK** — either `ANDROID_HOME` set in the environment, or `sdk.dir` defined in
127+
`local.properties`. This would have been set up for you if you have opened the project in Android Studio.
128+
129+
### Credentials file
130+
131+
The script requires a `release-creds.toml` file in the project root. This file is not
132+
committed to the repository — ask a project maintainer for it. Its structure is:
133+
134+
```toml
135+
[build.play]
136+
keystore = "<base64-encoded JKS keystore>"
137+
keystore_password = "<password>"
138+
key_alias = "<alias>"
139+
key_password = "<password>"
140+
141+
[build.huawei]
142+
keystore = "<base64-encoded JKS keystore>"
143+
keystore_password = "<password>"
144+
key_alias = "<alias>"
145+
key_password = "<password>"
146+
147+
[fdroid]
148+
keystore = "<base64-encoded PKCS12 keystore>"
149+
keystore_password = "<password>"
150+
key_alias = "<alias>"
151+
key_password = "<password>"
152+
```
153+
154+
### Running the script
155+
156+
From the project root, run:
157+
158+
```sh
159+
./scripts/build-and-release.py
160+
```
161+
162+
This performs a full build and release. The built artifacts are placed under where they suppose
163+
to be according to standard Android project layout.
164+
165+
#### Options
166+
167+
| Flag | Description |
168+
|-----------------|-----------------------------------------------------------------------------|
169+
| `--build-only` | Build all flavors but skip the F-Droid PR and GitHub release upload steps |
170+
| `--build-type` | Gradle build type to use (default: `release`) |
171+
172+
For example, to perform builds without publishing anything:
173+
174+
```sh
175+
./scripts/build-and-release.py --build-only
176+
```
177+
178+
### What the script does in detail
179+
180+
1. **Play build** — assembles split APKs and an AAB bundle for the `play` flavor, signed with
181+
the Play keystore.
182+
2. **F-Droid build** — assembles split APKs for the `fdroid` flavor.
183+
3. **F-Droid repo update** (skipped with `--build-only`) — see [FDROID_RELEASE.md](FDROID_RELEASE.md)
184+
for a full explanation of the architecture and manual steps:
185+
- Clones `session-foundation/session-fdroid` into `build/fdroidrepo` if not already present.
186+
- Creates a `release/<version>` branch.
187+
- Copies the new APKs into the repo, pruning old versions (keeps the latest
188+
four releases).
189+
- Regenerates repository metadata using `fdroid update`.
190+
- Commits and opens a pull request against `master` for human review and merge.
191+
4. **Huawei build** — assembles a universal APK for the `huawei` flavor.
192+
5. **GitHub release upload** (skipped with `--build-only`):
193+
- Looks for a release draft in this repository matching the version name.
194+
- If found, uploads the Play split APKs, the AAB bundle, and the Huawei APKs to it.
195+
- If no draft exists, this step is skipped (no error).
196+
197+
---
198+
199+
## Contributing code
200+
201+
Code contributions should be submitted via GitHub as pull requests from feature branches,
202+
[as explained here](https://help.github.com/articles/using-pull-requests).

0 commit comments

Comments
 (0)