Repo cloned

This commit is contained in:
Fr4nz D13trich 2025-12-29 13:18:34 +01:00
commit 496ae75f58
7988 changed files with 1451097 additions and 0 deletions

View file

@ -0,0 +1,50 @@
# Reproducible Builds
[![Reproducible build](https://github.com/mollyim/mollyim-android/actions/workflows/reprocheck.yml/badge.svg)](https://github.com/mollyim/mollyim-android/actions/workflows/reprocheck.yml)
Follow these instructions to verify that this source code is exactly the same code that was used to compile the APK distributed on the website.
The [reproducible-builds.org](https://reproducible-builds.org/) project has more information about this general topic.
## Prerequisites
- Docker
- Docker Compose
- Python 3
## Build and Verify
You can compile you own release of Molly inside a Docker container and compare the resulted APK to the APK that is officially distributed. To do so, execute the following:
```shell
# Set the release version you want to check
export VERSION=v5.42.8-2
# Clone the source code repository
git clone https://github.com/mollyim/mollyim-android.git
# Go to this directory
cd mollyim-android/reproducible-builds
# Check out the release tag
git checkout $VERSION
# The following steps might be different for the chosen version.
# Before proceeding, you should open this tutorial (README.md file) and review the instructions.
# Build the APK using the Docker environment
docker compose up --build
# Download the official APK
wget https://github.com/mollyim/mollyim-android/releases/download/$VERSION/Molly-$VERSION.apk
# Run the diff script to compare the APKs
python apkdiff/apkdiff.py Molly-$VERSION.apk outputs/apk/prodWebsite/release/Molly-unsigned-$VERSION.apk
# Clean up the Docker environment
docker compose down
```
If you get `APKs match`, you have **successfully verified** that the official release matches with your own self-built version of Molly. Congratulations!
If you get `APKs don't match`, please [report the issue](https://github.com/mollyim/mollyim-android/issues).

View file

@ -0,0 +1,82 @@
#! /usr/bin/env python3
import sys
import fnmatch
from zipfile import ZipFile
class ApkDiff:
IGNORE_FILES = [
# Related to app signing. Not expected to be present in unsigned builds. Doesn't affect app code.
"META-INF/MANIFEST.MF",
"META-INF/*.RSA",
"META-INF/*.SF",
"META-INF/code_transparency_signed.jwt",
"stamp-cert-sha256"
]
# MOLLY: Allow to exclude files with glob patterns
def isIncluded(self, filepath):
for ignoreFile in self.IGNORE_FILES:
if fnmatch.fnmatchcase(filepath, ignoreFile):
return False
return True
def compare(self, firstApk, secondApk):
firstZip = ZipFile(firstApk, 'r')
secondZip = ZipFile(secondApk, 'r')
return self.compareEntryNames(firstZip, secondZip) and self.compareEntryContents(firstZip, secondZip) == True
def compareEntryNames(self, firstZip, secondZip):
firstNameListSorted = sorted(n for n in firstZip.namelist() if self.isIncluded(n))
secondNameListSorted = sorted(n for n in secondZip.namelist() if self.isIncluded(n))
if len(firstNameListSorted) != len(secondNameListSorted):
print("Manifest lengths differ!")
for (firstEntryName, secondEntryName) in zip(firstNameListSorted, secondNameListSorted):
if firstEntryName != secondEntryName:
print("Sorted manifests don't match, %s vs %s" % (firstEntryName, secondEntryName))
return False
return True
def compareEntryContents(self, firstZip, secondZip):
firstInfoList = list(filter(lambda info: self.isIncluded(info.filename), firstZip.infolist()))
secondInfoList = list(filter(lambda info: self.isIncluded(info.filename), secondZip.infolist()))
if len(firstInfoList) != len(secondInfoList):
print("APK info lists of different length!")
return False
success = True
for firstEntryInfo in firstInfoList:
for secondEntryInfo in list(secondInfoList):
if firstEntryInfo.filename == secondEntryInfo.filename:
firstEntryBytes = firstZip.read(firstEntryInfo.filename)
secondEntryBytes = secondZip.read(secondEntryInfo.filename)
if firstEntryBytes != secondEntryBytes:
firstZip.extract(firstEntryInfo, "mismatches/first")
secondZip.extract(secondEntryInfo, "mismatches/second")
print("APKs differ on file %s! Files extracted to the mismatches/ directory." % (firstEntryInfo.filename))
success = False
secondInfoList.remove(secondEntryInfo)
break
return success
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage: apkdiff <pathToFirstApk> <pathToSecondApk>")
sys.exit(1)
if ApkDiff().compare(sys.argv[1], sys.argv[2]):
print("APKs match!")
sys.exit(0)
else:
print("APKs don't match!")
sys.exit(1)

View file

View file

@ -0,0 +1 @@
CI_BUILD_VARIANTS=prod

View file

@ -0,0 +1,5 @@
CI_BUILD_VARIANTS=(prod|staging)Website
CI_APP_TITLE=Molly Insider
CI_APP_FILENAME=Molly-Insider
CI_PACKAGE_ID=im.molly.insider
CI_FORCE_INTERNAL_USER_FLAG=true

View file

@ -0,0 +1,19 @@
services:
assemble:
image: reproducible-molly
build:
context: ..
command: :app:assembleRelease :app:bundleRelease --no-daemon
volumes:
- ./certs:/molly/app/certs:ro
- ./outputs:/molly/app/build/outputs
environment:
- GRADLE_OPTS
- CI_APP_TITLE
- CI_APP_FILENAME
- CI_PACKAGE_ID
- CI_BUILD_VARIANTS
- CI_FORCE_INTERNAL_USER_FLAG
- CI_KEYSTORE_PATH
- CI_KEYSTORE_PASSWORD
- CI_KEYSTORE_ALIAS

View file