Repo cloned
This commit is contained in:
commit
496ae75f58
7988 changed files with 1451097 additions and 0 deletions
50
reproducible-builds/README.md
Normal file
50
reproducible-builds/README.md
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
# Reproducible Builds
|
||||
|
||||
[](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).
|
||||
82
reproducible-builds/apkdiff/apkdiff.py
Executable file
82
reproducible-builds/apkdiff/apkdiff.py
Executable 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)
|
||||
0
reproducible-builds/certs/.keep
Normal file
0
reproducible-builds/certs/.keep
Normal file
1
reproducible-builds/ci/beta-stable.env
Normal file
1
reproducible-builds/ci/beta-stable.env
Normal file
|
|
@ -0,0 +1 @@
|
|||
CI_BUILD_VARIANTS=prod
|
||||
5
reproducible-builds/ci/insider.env
Normal file
5
reproducible-builds/ci/insider.env
Normal 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
|
||||
19
reproducible-builds/docker-compose.yml
Normal file
19
reproducible-builds/docker-compose.yml
Normal 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
|
||||
0
reproducible-builds/outputs/.keep
Normal file
0
reproducible-builds/outputs/.keep
Normal file
Loading…
Add table
Add a link
Reference in a new issue