Skip to content

Commit aad5017

Browse files
committed
ci: gate releases on reproducible builds
1 parent 73f3590 commit aad5017

9 files changed

Lines changed: 67 additions & 20 deletions

File tree

.github/workflows/release.yml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
name: Release
22

33
# ROADMAP §6 N6.2 — manual-trigger release workflow:
4-
# 1. Builds the release variant.
5-
# 2. Signs with the keystore stored as an encrypted secret.
6-
# 3. Uploads the signed APK + SHA-256 manifest to a GitHub Release.
7-
# 4. NOTICE / LICENSES attribution check (Apache-2.0 + SCOWL + LDNOOBW + FlorisBoard upstream).
4+
# 1. Requires the reproducible-build verifier to pass for the same commit.
5+
# 2. Builds the release variant.
6+
# 3. Signs with the keystore stored as an encrypted secret.
7+
# 4. Uploads the signed APK + SHA-256 manifest to a GitHub Release.
8+
# 5. NOTICE / LICENSES attribution check (Apache-2.0 + SCOWL + LDNOOBW + FlorisBoard upstream).
89
#
910
# Required repo secrets (set up under Settings → Secrets and variables → Actions):
1011
# SIGNING_KEYSTORE_BASE64 — base64-encoded contents of the .jks file
@@ -33,7 +34,14 @@ permissions:
3334
contents: write
3435

3536
jobs:
37+
reproducible:
38+
name: Reproducible APK verification
39+
uses: ./.github/workflows/reproducible-build.yml
40+
permissions:
41+
contents: read
42+
3643
build:
44+
needs: reproducible
3745
runs-on: ubuntu-latest
3846
env:
3947
SIGNING_KEYSTORE_BASE64: ${{ secrets.SIGNING_KEYSTORE_BASE64 }}

.github/workflows/reproducible-build.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ name: Reproducible APK Check
66

77
on:
88
workflow_dispatch:
9+
workflow_call:
910
push:
1011
branches: [ main, master ]
1112
paths:

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,43 @@
22

33
All SwiftFloris release history is consolidated here. This replaces the former root-level `RELEASE_NOTES_v*.md` file-per-release pattern.
44

5+
<a id="v1.8.212"></a>
6+
## v1.8.212
7+
8+
Released: 2026-06-04
9+
10+
### Release reproducibility gate
11+
12+
F23 makes release publication depend on the existing reproducible-build verifier. The build-twice APK workflow is now reusable via `workflow_call`, and the manual Release workflow has a required `reproducible` job before the signing/build/publish job. If the reproducibility check fails for the selected commit, the signed APK and GitHub Release creation steps never run.
13+
14+
This keeps the existing standalone reproducible-build triggers intact (`workflow_dispatch`, push, and pull request path filters) while closing the release-flow gap where a maintainer could publish before the matching reproducibility signal completed.
15+
16+
### Changes
17+
18+
- **`reproducible-build.yml`** - added `workflow_call` so the verifier can be used as a required job by other workflows.
19+
- **`release.yml`** - added a `Reproducible APK verification` reusable-workflow job and made the release build job depend on it.
20+
- **`REPRODUCIBLE_BUILDS.md`** - documented that release dispatches are now blocked by the build-twice verifier.
21+
- **`ROADMAP.md` / `COMPLETED.md`** - moved F23 out of active work and into shipped state.
22+
23+
### Verification
24+
25+
- `python -c "import yaml, pathlib; [yaml.safe_load(p.read_text(encoding='utf-8')) for p in pathlib.Path('.github/workflows').glob('*.yml')]; print('pyyaml ok')"` -> green.
26+
- `./gradlew.bat :app:verifyNoInternetPermission :app:testDebugUnitTest :app:lintDebug :app:assembleDebug` -> green with `JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-21.0.11.10-hotspot`.
27+
- `./gradlew.bat :app:verifyRoborazziDebug` -> green with `JAVA_HOME=C:\Program Files\Eclipse Adoptium\jdk-21.0.11.10-hotspot`.
28+
- `git diff --check` -> green; CRLF normalization warnings only.
29+
- `bash scripts/check-fastlane-metadata.sh` -> green for versionCode 2012.
30+
- `bash scripts/check-repo-hygiene.sh` -> green.
31+
32+
### Files Touched
33+
34+
- `.github/workflows/release.yml`
35+
- `.github/workflows/reproducible-build.yml`
36+
- `docs/REPRODUCIBLE_BUILDS.md`
37+
- `fastlane/metadata/android/en-US/changelogs/2012.txt` (new)
38+
- `gradle.properties` (versionCode 2011->2012, versionName 1.8.211->1.8.212)
39+
- `README.md` (version badge/current release/highlights)
40+
- `ROADMAP.md` / `COMPLETED.md` (F23 moved to shipped work)
41+
542
<a id="v1.8.211"></a>
643
## v1.8.211
744

COMPLETED.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ Consolidated from the archived open-work checklist (closed items). Full per-rele
5050
- [x] EI3 (P2) — Personal dictionary bulk-import preview for modular imports, with first-50-row review, row exclusion before commit, excluded-row summary counts, and persisted skip-preview opt-out. Shipped v1.8.209. — *Source: TODO_2026-06-03.md*
5151
- [x] F6 (P2) — Per-app accent opt-in discovery hint and Settings preview, with process-local three-app threshold tracking, persisted hint state only, Settings search coverage, and privacy docs. Shipped v1.8.210. — *Source: TODO_2026-06-03.md*
5252
- [x] EI2 (P3, reframed) — Settings home regrouped into Typing experience, Personalization, Privacy & data, Advanced, and About buckets, with Physical keyboard surfaced directly under Advanced while preserving existing deep links. Shipped v1.8.211. — *Source: TODO_2026-06-03.md*
53+
- [x] F23 (P1) — Release workflow now depends on the reusable reproducible-build verifier before signing or GitHub Release publication, blocking release dispatches when the build-twice APK check fails. Shipped v1.8.212. — *Source: TODO_2026-06-03.md*
5354
- [x] F1, F2, F15, F16, F17, F19, F20, F25, F26, F32, F34, F35, F36, F41, F42, EI8, EI11, EI4 (doc) — Closed across v1.8.174 -> v1.8.187. — *Source: TODO_2026-06-03.md*
5455
- [x] IMPROVEMENT_PLAN Workstreams 1, 3, 4, 5, 6 complete; Workstream 2 (lint) monotonically decreasing. — *Source: IMPROVEMENT_PLAN_2026-05-18.md*
5556

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# SwiftFloris
22

3-
![Version](https://img.shields.io/badge/version-v1.8.211-blue) ![License](https://img.shields.io/badge/license-Apache%202.0-green) ![Platform](https://img.shields.io/badge/platform-Android%208.0+-orange) ![Network](https://img.shields.io/badge/network-none-lightgrey) ![Dictionary imports](https://img.shields.io/badge/dictionary%20imports-local%20files-green)
3+
![Version](https://img.shields.io/badge/version-v1.8.212-blue) ![License](https://img.shields.io/badge/license-Apache%202.0-green) ![Platform](https://img.shields.io/badge/platform-Android%208.0+-orange) ![Network](https://img.shields.io/badge/network-none-lightgrey) ![Dictionary imports](https://img.shields.io/badge/dictionary%20imports-local%20files-green)
44

55
**SwiftFloris** is a privacy-first Android keyboard, forked from FlorisBoard and pushed toward SwiftKey-class multilingual typing without the cloud. It ships under Apache-2.0, holds no `INTERNET` permission, and binds zero accounts.
66

@@ -37,7 +37,7 @@
3737
3838
## Highlights
3939

40-
| Area | What's in v1.8.211 | Privacy posture |
40+
| Area | What's in v1.8.212 | Privacy posture |
4141
|------|-------------------|-----------------|
4242
| **Autocorrect / prediction** | SCOWL 117k English dictionary, SymSpell d1+d2, bigram + trigram next-word, capitalization-aware completions, contraction handling, instant-remember user-dictionary overlay | On-device |
4343
| **Multilingual typing** | Bilingual subtype presets (EN+ES / EN+FR / EN+DE), per-token Latin language identification, top-two straddle guard, sentence-local context scoring, and opt-in remembered keyboard language per app | On-device |
@@ -54,7 +54,7 @@
5454
| **Migration** | First-run local dictionary import hint; preview-before-save personal dictionary imports with row exclusion; Gboard / FlorisBoard / SwiftKey JSON export importer; passphrase-encrypted SwiftFloris dictionary export/import; Settings-based Keyman LDML / `.kmp` metadata + Windows KLC + macOS hardware-keyboard imports | All file-system based |
5555
| **Alternative layouts** | Colemak / Dvorak / Workman from the FlorisBoard layout pack, plus selectable honeycomb hex layout with clipped hex keys and hex-aware hit testing (only FOSS Android keyboard shipping this — Typewise vacated the consumer market early 2026; see [docs/HONEYCOMB_LAYOUT.md](docs/HONEYCOMB_LAYOUT.md)) | On-device |
5656
| **AI transparency** | First-run AI/ML explainer plus Settings → About → AI features screen covering next-word, glide, voice, translation, and smart compose | On-device, no account, no telemetry |
57-
| **CI / build** | No-network gate, repo-hygiene gate, OSV dep scan, Dependabot version review, lint baseline-drift wrapper, manual emulator settings smoke, reproducible-build toolchain pins + build-twice APK self-check, Roborazzi visual-regression hard gate with committed theme/Addons baselines, Macrobenchmark trace sections in 6 hot paths | Audit-friendly |
57+
| **CI / build** | No-network gate, repo-hygiene gate, OSV dep scan, Dependabot version review, lint baseline-drift wrapper, manual emulator settings smoke, reproducible-build toolchain pins + build-twice APK self-check chained into release publication, Roborazzi visual-regression hard gate with committed theme/Addons baselines, Macrobenchmark trace sections in 6 hot paths | Audit-friendly |
5858

5959
## Distribution
6060

@@ -442,7 +442,7 @@ limitations under the License.
442442

443443
## Status
444444

445-
🚀 **Active development.** Current release: **v1.8.211** (2026-06-04). The SwiftKey account export window closed on **2026-05-31**; local/on-device migration paths remain documented above.
445+
🚀 **Active development.** Current release: **v1.8.212** (2026-06-04). The SwiftKey account export window closed on **2026-05-31**; local/on-device migration paths remain documented above.
446446

447447
---
448448

ROADMAP.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
> Single source of truth for all planned work. Items above the --- are existing plans; items below are research conducted 2026-06-03.
44
5-
**Current release:** v1.8.211 (versionCode 2011). **Baseline green:** `:app:verifyNoInternetPermission :app:testDebugUnitTest :app:lintDebug :app:assembleDebug`.
5+
**Current release:** v1.8.212 (versionCode 2012). **Baseline green:** `:app:verifyNoInternetPermission :app:testDebugUnitTest :app:lintDebug :app:assembleDebug`.
66

77
Hard rules still apply (see `AGENTS.md`): no `INTERNET` permission in `:app`; Apache-2.0 ceiling on `:app`; no closed-source blobs; one logical change per commit; every shipped release bumps `gradle.properties` version, writes a `CHANGELOG.md` section, and adds a `fastlane/metadata/android/en-US/changelogs/<versionCode>.txt` (draft <=480 chars for headroom).
88

@@ -43,11 +43,6 @@ Item IDs trace to their origin research: `F#`/`EI#` from the archived 2026-05-25
4343

4444
### CI, build & release hardening
4545

46-
- [ ] P1 — Chain reproducible-build verification to release-tag flow (F23)
47-
- Why: Reproducible-build verification is not gated on release.
48-
- Touches: `workflow_call` from `release.yml`, or block tag publish until repro is green.
49-
- Acceptance: a tag cannot publish unless reproducible-build verification passes.
50-
- Source: TODO.md A4 / research feature plan F23.
5146
- [ ] P1 — `:app:verifyRoborazziRelease` gate (F24)
5247
- Why: R8/minify can rename Compose semantics nodes and nothing catches it today.
5348
- Touches: add a release-variant Roborazzi verification task.

docs/REPRODUCIBLE_BUILDS.md

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,14 @@ Gradle build cache disabled and tasks re-run, then compares the two APKs
4343
byte-for-byte.
4444

4545
`.github/workflows/reproducible-build.yml` runs this verifier on
46-
`workflow_dispatch`, and on pushes / pull requests that touch app, Gradle,
47-
workflow, or reproducible-build documentation surfaces. On mismatch, the
48-
script writes per-entry SHA-256 manifests excluding `META-INF/` so maintainers
49-
can tell whether the drift is payload content or signing / ZIP metadata.
46+
`workflow_dispatch`, as a reusable `workflow_call` from the release workflow,
47+
and on pushes / pull requests that touch app, Gradle, workflow, or
48+
reproducible-build documentation surfaces. On mismatch, the script writes
49+
per-entry SHA-256 manifests excluding `META-INF/` so maintainers can tell
50+
whether the drift is payload content or signing / ZIP metadata. The release
51+
workflow's signing and GitHub Release publication job depends on this reusable
52+
verifier, so a release dispatch cannot publish a tag unless the build-twice
53+
reproducibility check has passed for the same commit.
5054

5155
## What can still drift?
5256

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Release publication now waits for SwiftFloris's reproducible-build verifier. The build-twice APK check is reusable from the Release workflow, and signing/GitHub Release creation cannot run unless that check passes for the same commit. No app behavior, permission, network, or telemetry changes.

gradle.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ projectMinSdk=26
1515
projectTargetSdk=36
1616
projectCompileSdk=36
1717

18-
projectVersionCode=2011
19-
projectVersionName=1.8.211
18+
projectVersionCode=2012
19+
projectVersionName=1.8.212

0 commit comments

Comments
 (0)