diff --git a/tests/run-tests b/tests/run-tests index 7e3f0c13..360605ab 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -6,158 +6,12 @@ echo_header() { { echo -e "==============================================================================\n$1"; } 2>/dev/null } -get_fdroid_apk_filename() { - if [ -z $aapt ]; then - appid=$(androguard apkid "$1" | sed -En 's/ +"([a-z][^"]+)",$/\1/ip') - versionCode=$(androguard apkid "$1" | sed -En 's/ +"([0-9]+)",$/\1/p') - echo "${appid}_${versionCode}.apk" - else - $aapt dump badging "$1" | sed -n "s,^package: name='\(.*\)' versionCode='\([0-9][0-9]*\)' .*,\1_\2.apk,p" - fi -} - -copy_apks_into_repo() { - set +x - find $APKDIR -type f -name '*.apk' -print0 | while IFS= read -r -d '' f; do - echo $f | grep -F -v -e unaligned -e unsigned -e badsig -e badcert -e bad-unicode -e janus.apk || continue - apk=`get_fdroid_apk_filename "$f"` - test "$f" -nt repo/$apk && rm -f repo/$apk # delete existing if $f is newer - if [ ! -e repo/$apk ] && [ ! -e archive/$apk ]; then - echo "$f --> repo/$apk" - ln "$f" $1/repo/$apk || \ - rsync -axv "$f" $1/repo/$apk # rsync if hard link is not possible - touch $1/.found-apks - fi - done - if [ ! -e $1/.found-apks ]; then - echo "ERROR: The dir APKDIR must have APKs in it! $APKDIR does not." - exit 1 - fi - set -x -} - -# keep this as an old version to test the automatic parsing of build-tools -# verion numbers in `fdroid init` -create_fake_android_home() { - mkdir $1/tools - mkdir $1/platform-tools - mkdir $1/build-tools - mkdir $1/build-tools/19.0.2 - touch $1/build-tools/19.0.2/aapt -} - -create_test_dir() { - test -e $WORKSPACE/.testfiles || mkdir $WORKSPACE/.testfiles - mktemp -d $WORKSPACE/.testfiles/run-tests.XXXX -} - -create_test_file() { - test -e $WORKSPACE/.testfiles || mkdir $WORKSPACE/.testfiles - TMPDIR=$WORKSPACE/.testfiles mktemp -} - -fdroid_init_with_prebuilt_keystore() { - if [ -z "$1" ]; then - keystore=$WORKSPACE/tests/keystore.jks - else - keystore="$1" - fi - $fdroid init --keystore $keystore --repo-keyalias=sova - echo 'keystorepass: r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=' >> config.yml - echo 'keypass: r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=' >> config.yml -} - -is_MD5_disabled() { - javac $WORKSPACE/tests/IsMD5Disabled.java && java -cp $WORKSPACE/tests IsMD5Disabled -} - -use_apksigner() { - python3 -c " -import sys -sys.path.insert(0, '$WORKSPACE') -from fdroidserver import common -c = {'sdk_path': '$ANDROID_HOME'} -common.find_apksigner(c) -exit(c.get('apksigner') is None) -" -} - -err_handler() { -# remove this to prevent git conflicts and complaining - set +x - rm -rf "$WORKSPACE"/fdroidserver.egg-info/ - rm -rf "$WORKSPACE"/.testfiles/run-tests.* - rm -rf "$WORKSPACE"/.testfiles/test_* - rm -f "$WORKSPACE"/.testfiles/tmp.* - test -d "$WORKSPACE"/.testfiles && \ - (rmdir "$WORKSPACE"/.testfiles 2> /dev/null || \ - rmdir --ignore-fail-on-non-empty "$WORKSPACE"/.testfiles 2> /dev/null) -} - -trap err_handler INT EXIT - #------------------------------------------------------------------------------# -# "main" - -if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then - set +x - echo "Usage: $0 '/path/to/folder/with/apks'" - exit 1 -fi - -if [ -z "$ANDROID_HOME" ]; then - if python3 -c "import androguard"; then - echo "ANDROID_HOME is not set, using androguard" - else - echo "ERROR: ANDROID_HOME is not set, androguard is not available!" - exit 1 - fi -else - echo "Using ANDROID_HOME=$ANDROID_HOME" -fi if [ -d tests ]; then cd tests fi -if [ -z "$1" ]; then - APKDIR=`pwd` -else - APKDIR=$1 -fi -if [ ! -d "$APKDIR" ]; then - echo "ERROR: '$APKDIR' does not exist!" - exit 1 -fi - -if [ -z "$WORKSPACE" ]; then - WORKSPACE="$(dirname "$(pwd)")" - echo "Setting Workspace to $WORKSPACE" -fi - -# allow the location of the script to be overridden -if [ -z "$fdroid" ]; then - fdroid="$WORKSPACE/fdroid" -fi - -# allow the location of aapt to be overridden -if [ -z "$aapt" ]; then - aapt=`ls -1 $ANDROID_HOME/build-tools/*/aapt 2> /dev/null | sort | tail -1` -fi - -# try to use GNU sed on OSX/BSD cuz BSD sed sucks -if which gsed; then - sed="gsed" -else - sed="sed" -fi - -# allow the location of git to be overridden -if [ -z "$git" ]; then - # shellcheck disable=SC2089 - git="env HOME= GIT_AUTHOR_NAME='Test' GIT_AUTHOR_EMAIL='no@mail' GIT_COMMITTER_NAME='Test' GIT_COMMITTER_EMAIL='no@mail' git" -fi - set -x # show each command as it is executed #------------------------------------------------------------------------------# @@ -166,1283 +20,9 @@ echo_header "run commit hooks" cd $WORKSPACE test -x ./hooks/pre-commit && ./hooks/pre-commit - #------------------------------------------------------------------------------# echo_header "run unit tests" python3 -m unittest -v -#------------------------------------------------------------------------------# -echo_header "print fdroid version" - -$fdroid --version - - -#------------------------------------------------------------------------------# -echo_header 'run process when building and signing are on separate machines' - -if use_apksigner; then - REPOROOT=`create_test_dir` - cd $REPOROOT - cp $WORKSPACE/tests/keystore.jks $REPOROOT/ - $fdroid init --keystore keystore.jks --repo-keyalias=sova - echo 'make_current_version_link: true' >> config.yml - echo 'keystorepass: r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=' >> config.yml - echo 'keypass: r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI=' >> config.yml - echo 'keydname: "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US"' >> config.yml - test -d archive || mkdir archive - test -d metadata || mkdir metadata - cp $WORKSPACE/tests/metadata/info.guardianproject.urzip.yml metadata/ - test -d repo || mkdir repo - test -d unsigned || mkdir unsigned - cp $WORKSPACE/tests/urzip-release-unsigned.apk unsigned/info.guardianproject.urzip_100.apk - $fdroid publish --verbose - $fdroid update --verbose --nosign - $fdroid signindex --verbose - test -e repo/index.xml - test -e repo/index.jar - test -e repo/index-v1.jar - test -e tmp/apkcache.json - ! test -z tmp/apkcache.json - test -L urzip.apk - grep -F '> config.yml -mkdir metadata -cp $WORKSPACE/tests/urzip.apk $WORKSPACE/tests/bad-unicode*.apk repo/ -cp $WORKSPACE/tests/metadata/info.guardianproject.urzip.yml metadata/ - -$fdroid readmeta -$fdroid update - - -#------------------------------------------------------------------------------# -echo_header 'copy git import and run "fdroid scanner" on it' - -REPOROOT=`create_test_dir` -cd $REPOROOT -mkdir metadata -echo "AutoName: Just A Test" > metadata/org.fdroid.ci.test.app.yml -echo "WebSite: " >> metadata/org.fdroid.ci.test.app.yml -echo "Builds:" >> metadata/org.fdroid.ci.test.app.yml -echo " - versionName: 0.3" >> metadata/org.fdroid.ci.test.app.yml -echo " versionCode: 300" >> metadata/org.fdroid.ci.test.app.yml -echo " commit: 0.3" >> metadata/org.fdroid.ci.test.app.yml -echo " subdir: app" >> metadata/org.fdroid.ci.test.app.yml -echo " gradle:" >> metadata/org.fdroid.ci.test.app.yml -echo " - yes" >> metadata/org.fdroid.ci.test.app.yml -echo "" >> metadata/org.fdroid.ci.test.app.yml -echo "Repo: https://gitlab.com/fdroid/ci-test-app.git" >> metadata/org.fdroid.ci.test.app.yml -echo "RepoType: git" >> metadata/org.fdroid.ci.test.app.yml -mkdir build -git clone https://gitlab.com/fdroid/ci-test-app.git build/org.fdroid.ci.test.app -ls -l build/org.fdroid.ci.test.app -$fdroid scanner org.fdroid.ci.test.app --verbose - - -#------------------------------------------------------------------------------# -echo_header "copy tests/repo, generate java/gpg keys, update, and gpgsign" - -REPOROOT=`create_test_dir` -GNUPGHOME=$REPOROOT/gnupghome -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -cp -a \ - $WORKSPACE/tests/config \ - $WORKSPACE/tests/metadata \ - $WORKSPACE/tests/repo \ - $REPOROOT/ -cp -a $WORKSPACE/tests/gnupghome $GNUPGHOME -chmod 0700 $GNUPGHOME -echo "install_list: org.adaway" >> config.yml -echo "uninstall_list: [com.android.vending, com.facebook.orca]" >> config.yml -echo "gpghome: $GNUPGHOME" >> config.yml -echo "gpgkey: CE71F7FB" >> config.yml -echo "mirrors: ['http://foobarfoobarfoobar.onion/fdroid', 'https://foo.bar/fdroid']" >> config.yml -$fdroid update --verbose --pretty -test -e repo/index.xml -test -e repo/index.jar -test -e repo/index-v1.jar -grep -F ' repo/index-v2.org.json -$sed -z 's/,\s*"ipfsCIDv1": *"[a-z0-9]*"//g' repo/index-v2.json > repo/index-v2.mod.json -$sed -i --expression='s,"timestamp": [0-9]*,"timestamp": 1676634233000,' repo/index-v2.mod.json -diff -uw repo/index-v2.org.json repo/index-v2.mod.json - - -#------------------------------------------------------------------------------# -echo_header 'test moving lots of APKs to the archive' - -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -$sed -i.tmp '/allow_disabled_algorithms/d' config.yml -test -d metadata || mkdir metadata -cp $WORKSPACE/tests/metadata/*.yml metadata/ -echo 'Summary: good test version of urzip' > metadata/info.guardianproject.urzip.yml -echo 'Summary: good MD5 sig, which is disabled algorithm' > metadata/org.bitbucket.tickytacky.mirrormirror.yml -$sed -i.tmp '/ArchivePolicy:/d' metadata/*.yml -test -d repo || mkdir repo -cp $WORKSPACE/tests/urzip.apk \ - $WORKSPACE/tests/org.bitbucket.tickytacky.mirrormirror_[0-9].apk \ - $WORKSPACE/tests/repo/com.politedroid_[0-9].apk \ - $WORKSPACE/tests/repo/obb.main.twoversions_110161[357].apk \ - repo/ -printf '\narchive_older: 3\n' >> config.yml - -$fdroid update --pretty --nosign -if use_apksigner; then - test `grep '' archive/index.xml | wc -l` -eq 2 - test `grep '' repo/index.xml | wc -l` -eq 10 -else - echo "This will fail when jarsigner allows MD5 for APK signatures" - test `grep '' archive/index.xml | wc -l` -eq 5 - test `grep '' repo/index.xml | wc -l` -eq 7 -fi - - -#------------------------------------------------------------------------------# -if ! use_apksigner; then - echo_header 'test per-app "Archive Policy"' - - REPOROOT=`create_test_dir` - cd $REPOROOT - fdroid_init_with_prebuilt_keystore - test -d metadata || mkdir metadata - cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata/ - test -d repo || mkdir repo - cp $WORKSPACE/tests/repo/com.politedroid_[0-9].apk repo/ - printf '\narchive_older: 3\n' >> config.yml - - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 0 - test `grep '' repo/index.xml | wc -l` -eq 4 - grep -F com.politedroid_3.apk repo/index.xml - grep -F com.politedroid_4.apk repo/index.xml - grep -F com.politedroid_5.apk repo/index.xml - grep -F com.politedroid_6.apk repo/index.xml - test -e repo/com.politedroid_3.apk - test -e repo/com.politedroid_4.apk - test -e repo/com.politedroid_5.apk - test -e repo/com.politedroid_6.apk - - echo "enable one app in the repo" - $sed -i.tmp 's,^ArchivePolicy: 4,ArchivePolicy: 1,' metadata/com.politedroid.yml - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 3 - test `grep '' repo/index.xml | wc -l` -eq 1 - grep -F com.politedroid_3.apk archive/index.xml - grep -F com.politedroid_4.apk archive/index.xml - grep -F com.politedroid_5.apk archive/index.xml - grep -F com.politedroid_6.apk repo/index.xml - test -e archive/com.politedroid_3.apk - test -e archive/com.politedroid_4.apk - test -e archive/com.politedroid_5.apk - test -e repo/com.politedroid_6.apk - - echo "remove all apps from the repo" - $sed -i.tmp 's,^ArchivePolicy: 1,ArchivePolicy: 0,' metadata/com.politedroid.yml - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 4 - test `grep '' repo/index.xml | wc -l` -eq 0 - grep -F com.politedroid_3.apk archive/index.xml - grep -F com.politedroid_4.apk archive/index.xml - grep -F com.politedroid_5.apk archive/index.xml - grep -F com.politedroid_6.apk archive/index.xml - test -e archive/com.politedroid_3.apk - test -e archive/com.politedroid_4.apk - test -e archive/com.politedroid_5.apk - test -e archive/com.politedroid_6.apk - ! test -e repo/com.politedroid_6.apk - - echo "move back one from archive to the repo" - $sed -i.tmp 's,^ArchivePolicy: 0,ArchivePolicy: 1,' metadata/com.politedroid.yml - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 3 - test `grep '' repo/index.xml | wc -l` -eq 1 - grep -F com.politedroid_3.apk archive/index.xml - grep -F com.politedroid_4.apk archive/index.xml - grep -F com.politedroid_5.apk archive/index.xml - grep -F com.politedroid_6.apk repo/index.xml - test -e archive/com.politedroid_3.apk - test -e archive/com.politedroid_4.apk - test -e archive/com.politedroid_5.apk - ! test -e archive/com.politedroid_6.apk - test -e repo/com.politedroid_6.apk - - echo "set an earlier version as CVC and test that it's the only one not archived" - $sed -i.tmp 's,^CurrentVersionCode: 6,CurrentVersionCode: 5,' metadata/com.politedroid.yml - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 3 - test `grep '' repo/index.xml | wc -l` -eq 1 - grep -F com.politedroid_3.apk archive/index.xml - grep -F com.politedroid_4.apk archive/index.xml - grep -F com.politedroid_5.apk repo/index.xml - grep -F com.politedroid_6.apk archive/index.xml - test -e archive/com.politedroid_3.apk - test -e archive/com.politedroid_4.apk - test -e repo/com.politedroid_5.apk - ! test -e repo/com.politedroid_6.apk - test -e archive/com.politedroid_6.apk -fi - - -#------------------------------------------------------------------------------# -echo_header 'test moving old APKs to and from the archive' - -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -test -d metadata || mkdir metadata -cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata/ -$sed -i.tmp '/ArchivePolicy:/d' metadata/com.politedroid.yml -test -d repo || mkdir repo -cp $WORKSPACE/tests/repo/com.politedroid_[0-9].apk repo/ -printf '\narchive_older: 3\n' >> config.yml - -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 1 -test `grep '' repo/index.xml | wc -l` -eq 3 -grep -F com.politedroid_3.apk archive/index.xml -grep -F com.politedroid_4.apk repo/index.xml -grep -F com.politedroid_5.apk repo/index.xml -grep -F com.politedroid_6.apk repo/index.xml -test -e archive/com.politedroid_3.apk -test -e repo/com.politedroid_4.apk -test -e repo/com.politedroid_5.apk -test -e repo/com.politedroid_6.apk - -$sed -i.tmp 's,archive_older: 3,archive_older: 1,' config.yml -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 3 -test `grep '' repo/index.xml | wc -l` -eq 1 -grep -F com.politedroid_3.apk archive/index.xml -grep -F com.politedroid_4.apk archive/index.xml -grep -F com.politedroid_5.apk archive/index.xml -grep -F com.politedroid_6.apk repo/index.xml -test -e archive/com.politedroid_3.apk -test -e archive/com.politedroid_4.apk -test -e archive/com.politedroid_5.apk -test -e repo/com.politedroid_6.apk - -# disabling deletes from the archive -$sed -i.tmp 's/\(versionCode: 4\)/\1\n disable: testing deletion/' metadata/com.politedroid.yml -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 2 -test `grep '' repo/index.xml | wc -l` -eq 1 -grep -F com.politedroid_3.apk archive/index.xml -! grep -F com.politedroid_4.apk archive/index.xml -grep -F com.politedroid_5.apk archive/index.xml -grep -F com.politedroid_6.apk repo/index.xml -test -e archive/com.politedroid_3.apk -! test -e archive/com.politedroid_4.apk -test -e archive/com.politedroid_5.apk -test -e repo/com.politedroid_6.apk - -# disabling deletes from the repo, and promotes one from the archive -$sed -i.tmp 's/\(versionCode: 6\)/\1\n disable: testing deletion/' metadata/com.politedroid.yml -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 1 -test `grep '' repo/index.xml | wc -l` -eq 1 -grep -F com.politedroid_3.apk archive/index.xml -grep -F com.politedroid_5.apk repo/index.xml -! grep -F com.politedroid_6.apk repo/index.xml -test -e archive/com.politedroid_3.apk -test -e repo/com.politedroid_5.apk -! test -e repo/com.politedroid_6.apk - - -#------------------------------------------------------------------------------# -echo_header 'test that verify can succeed and fail' - -REPOROOT=`create_test_dir` -cd $REPOROOT -test -d tmp || mkdir tmp -test -d unsigned || mkdir unsigned -cp $WORKSPACE/tests/repo/com.politedroid_6.apk tmp/ -cp $WORKSPACE/tests/repo/com.politedroid_6.apk unsigned/ -$fdroid verify --reuse-remote-apk --verbose com.politedroid -# force a fail -cp $WORKSPACE/tests/repo/com.politedroid_5.apk unsigned/com.politedroid_6.apk -! $fdroid verify --reuse-remote-apk --verbose com.politedroid - - -#------------------------------------------------------------------------------# -echo_header 'test allowing disabled signatures in repo and archive' - -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -echo 'allow_disabled_algorithms: true' >> config.yml -printf '\narchive_older: 3\n' >> config.yml -test -d metadata || mkdir metadata -cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata/ -echo 'Summary: good test version of urzip' > metadata/info.guardianproject.urzip.yml -echo 'Summary: good MD5 sig, disabled algorithm' > metadata/org.bitbucket.tickytacky.mirrormirror.yml -$sed -i.tmp '/ArchivePolicy:/d' metadata/*.yml -test -d repo || mkdir repo -cp $WORKSPACE/tests/repo/com.politedroid_[0-9].apk \ - $WORKSPACE/tests/org.bitbucket.tickytacky.mirrormirror_[0-9].apk \ - $WORKSPACE/tests/urzip-badsig.apk \ - repo/ - -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 2 -test `grep '' repo/index.xml | wc -l` -eq 6 -grep -F com.politedroid_3.apk archive/index.xml -grep -F com.politedroid_4.apk repo/index.xml -grep -F com.politedroid_5.apk repo/index.xml -grep -F com.politedroid_6.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_1.apk archive/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_2.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_3.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_4.apk repo/index.xml -! grep -F urzip-badsig.apk repo/index.xml -! grep -F urzip-badsig.apk archive/index.xml -test -e archive/com.politedroid_3.apk -test -e repo/com.politedroid_4.apk -test -e repo/com.politedroid_5.apk -test -e repo/com.politedroid_6.apk -test -e archive/org.bitbucket.tickytacky.mirrormirror_1.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_2.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_3.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_4.apk -test -e archive/urzip-badsig.apk - -sed -i.tmp '/apksigner:/d' config.yml -if ! use_apksigner; then - $sed -i.tmp '/allow_disabled_algorithms/d' config.yml - $fdroid update --pretty --nosign - test `grep '' archive/index.xml | wc -l` -eq 5 - test `grep '' repo/index.xml | wc -l` -eq 3 - grep -F org.bitbucket.tickytacky.mirrormirror_1.apk archive/index.xml - grep -F org.bitbucket.tickytacky.mirrormirror_2.apk archive/index.xml - grep -F org.bitbucket.tickytacky.mirrormirror_3.apk archive/index.xml - grep -F org.bitbucket.tickytacky.mirrormirror_4.apk archive/index.xml - grep -F com.politedroid_3.apk archive/index.xml - grep -F com.politedroid_4.apk repo/index.xml - grep -F com.politedroid_5.apk repo/index.xml - grep -F com.politedroid_6.apk repo/index.xml - ! grep -F urzip-badsig.apk repo/index.xml - ! grep -F urzip-badsig.apk archive/index.xml - test -e archive/org.bitbucket.tickytacky.mirrormirror_1.apk - test -e archive/org.bitbucket.tickytacky.mirrormirror_2.apk - test -e archive/org.bitbucket.tickytacky.mirrormirror_3.apk - test -e archive/org.bitbucket.tickytacky.mirrormirror_4.apk - test -e archive/com.politedroid_3.apk - test -e archive/urzip-badsig.apk - test -e repo/com.politedroid_4.apk - test -e repo/com.politedroid_5.apk - test -e repo/com.politedroid_6.apk -fi - -# test unarchiving when disabled_algorithms are allowed again -echo 'allow_disabled_algorithms: true' >> config.yml -$fdroid update --pretty --nosign -test `grep '' archive/index.xml | wc -l` -eq 2 -test `grep '' repo/index.xml | wc -l` -eq 6 -grep -F com.politedroid_3.apk archive/index.xml -grep -F com.politedroid_4.apk repo/index.xml -grep -F com.politedroid_5.apk repo/index.xml -grep -F com.politedroid_6.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_1.apk archive/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_2.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_3.apk repo/index.xml -grep -F org.bitbucket.tickytacky.mirrormirror_4.apk repo/index.xml -! grep -F urzip-badsig.apk repo/index.xml -! grep -F urzip-badsig.apk archive/index.xml -test -e archive/com.politedroid_3.apk -test -e repo/com.politedroid_4.apk -test -e repo/com.politedroid_5.apk -test -e repo/com.politedroid_6.apk -test -e archive/org.bitbucket.tickytacky.mirrormirror_1.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_2.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_3.apk -test -e repo/org.bitbucket.tickytacky.mirrormirror_4.apk -test -e archive/urzip-badsig.apk - -#------------------------------------------------------------------------------# -echo_header 'rename apks with `fdroid update --rename-apks`, --nosign for speed' - -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -echo 'keydname: "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US"' >> config.yml -test -d metadata || mkdir metadata -cp $WORKSPACE/tests/metadata/info.guardianproject.urzip.yml metadata/ -test -d repo || mkdir repo -cp $WORKSPACE/tests/urzip.apk "repo/asdfiuhk urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234 ö.apk" -$fdroid update --rename-apks --pretty --nosign -test -e repo/info.guardianproject.urzip_100.apk -grep -F 'info.guardianproject.urzip_100.apk' repo/index-v1.json repo/index.xml -cp $WORKSPACE/tests/urzip-release.apk repo/ -$fdroid update --rename-apks --pretty --nosign -test -e repo/info.guardianproject.urzip_100.apk -test -e repo/info.guardianproject.urzip_100_b4964fd.apk -grep -F 'info.guardianproject.urzip_100.apk' repo/index-v1.json repo/index.xml -grep -F 'info.guardianproject.urzip_100_b4964fd.apk' repo/index-v1.json -! grep -F 'info.guardianproject.urzip_100_b4964fd.apk' repo/index.xml -cp $WORKSPACE/tests/urzip-release.apk repo/ -$fdroid update --rename-apks --pretty --nosign -test -e repo/info.guardianproject.urzip_100.apk -test -e repo/info.guardianproject.urzip_100_b4964fd.apk -test -e duplicates/repo/info.guardianproject.urzip_100_b4964fd.apk -grep -F 'info.guardianproject.urzip_100.apk' repo/index-v1.json repo/index.xml -grep -F 'info.guardianproject.urzip_100_b4964fd.apk' repo/index-v1.json -! grep -F 'info.guardianproject.urzip_100_b4964fd.apk' repo/index.xml - -#------------------------------------------------------------------------------# -echo_header "test for added date being set correctly for repo and archive" -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -printf '\narchive_older: 3\n' >> config.yml -mkdir -p {repo,archive,metadata,stats} -cp $WORKSPACE/tests/repo/com.politedroid_5.apk archive -cp $WORKSPACE/tests/repo/com.politedroid_6.apk repo -cp $WORKSPACE/tests/repo/index-v2.json repo -cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata -#TODO: the timestamp of the oldest apk in the file should be used, even if that -# doesn't exist anymore -$sed -i -e 's/ArchivePolicy:.*/ArchivePolicy: 1 versions/' metadata/com.politedroid.yml -timestamp=1498176000000 # $(date -u --date=2017-01-01 +%s)000 -pwd - -$fdroid update --pretty --nosign -grep -F "\"added\": $timestamp" repo/index-v1.json -# the archive will have the added timestamp for the app and for the apk, both need to be there -if [ $(grep -F "\"added\": $timestamp" archive/index-v1.json | wc -l) == 2 ]; then true; else false;fi - -#------------------------------------------------------------------------------# -echo_header "test whatsnew from fastlane without CVC set" -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -mkdir -p metadata/com.politedroid/en-US/changelogs/ -cp $WORKSPACE/tests/repo/com.politedroid_6.apk repo -cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata -echo "whatsnew test" > metadata/com.politedroid/en-US/changelogs/6.txt -$sed -i -e '/CurrentVersion/d' metadata/com.politedroid.yml -$fdroid update --pretty --nosign -grep -F 'whatsnew' repo/index-v1.json - -#------------------------------------------------------------------------------# -echo_header "test metadata checks" - -REPOROOT=`create_test_dir` -cd $REPOROOT - -mkdir repo -cp $WORKSPACE/tests/urzip.apk $REPOROOT/repo/ - -set +e -$fdroid build -if [ $? -eq 0 ]; then - echo "This should have failed because there is no metadata!" - exit 1 -else - echo "testing metadata checks passed" -fi -set -e - -mkdir $REPOROOT/metadata/ -cp $WORKSPACE/tests/metadata/org.smssecure.smssecure.yml $REPOROOT/metadata/ -$fdroid readmeta - - -#------------------------------------------------------------------------------# -echo_header "ensure commands that don't need the JDK work without a JDK configed" - -REPOROOT=`create_test_dir` -cd $REPOROOT -mkdir repo -mkdir metadata -echo "License: GPL-2.0-only" >> metadata/fake.yml -echo "Summary: Yup still fake" >> metadata/fake.yml -echo "Categories: [Internet]" >> metadata/fake.yml -echo "Description: |" >> metadata/fake.yml -echo " this is fake" >> metadata/fake.yml - -# fake that no JDKs are available -cat > config.yml <> config.yml - -$fdroid checkupdates --allow-dirty || true -which gpg && $fdroid gpgsign -$fdroid lint -$fdroid readmeta -$fdroid rewritemeta fake -$fdroid deploy -$fdroid deploy -$fdroid scanner - -# run these to get their output, but the are not setup, so don't fail -$fdroid build || true -$fdroid import || true -$fdroid install --no || true - - -#------------------------------------------------------------------------------# -# only run this test if running from a git repo, not all files are in the tarball -if [ -e .git/config ]; then - echo_header "create a source tarball" - - cd $WORKSPACE - ./setup.py compile_catalog sdist - - REPOROOT=`create_test_dir` - cd $REPOROOT - tar xzf `ls -1 $WORKSPACE/dist/fdroidserver-*.tar.gz | sort -n | tail -1` - cd $REPOROOT - # shellcheck disable=SC2211 - ./fdroidserver-*/fdroid init - copy_apks_into_repo $REPOROOT - # shellcheck disable=SC2211 - ./fdroidserver-*/fdroid update --create-metadata --verbose -fi - -#------------------------------------------------------------------------------# -echo_header "test config checks of local_copy_dir" - -REPOROOT=`create_test_dir` -cd $REPOROOT -$fdroid init -$fdroid update --create-metadata --verbose -$fdroid readmeta -LOCAL_COPY_DIR=`create_test_dir`/fdroid -$fdroid deploy --local-copy-dir=$LOCAL_COPY_DIR -$fdroid deploy --local-copy-dir=$LOCAL_COPY_DIR --verbose - -# now test the errors work -set +e -$fdroid deploy --local-copy-dir=thisisnotanabsolutepath -if [ $? -eq 0 ]; then - echo "This should have failed because thisisnotanabsolutepath is not an absolute path!" - exit 1 -else - echo "testing absolute path checker passed" -fi -$fdroid deploy --local-copy-dir=/tmp/IReallyDoubtThisPathExistsasdfasdf -if [ $? -eq 0 ]; then - echo "This should have failed because the path does not end with 'fdroid'!" - exit 1 -else - echo "testing dirname exists checker passed" -fi -$fdroid deploy --local-copy-dir=/tmp/IReallyDoubtThisPathExistsasdfasdf/fdroid -if [ $? -eq 0 ]; then - echo "This should have failed because the dirname path does not exist!" - exit 1 -else - echo "testing dirname exists checker passed" -fi -set -e - - -#------------------------------------------------------------------------------# -echo_header "setup a new repo from scratch using ANDROID_HOME and do a local sync" - -REPOROOT=`create_test_dir` -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -copy_apks_into_repo $REPOROOT -$fdroid update --create-metadata --verbose -$fdroid readmeta -grep -F '> config.yml -echo 'repo_keyalias: foo' >> config.yml -echo 'keystorepass: foo' >> config.yml -echo 'keypass: foo' >> config.yml -set +e -$fdroid update --create-metadata --verbose -if [ $? -eq 0 ]; then - echo "This should have failed because this repo has a bad/fake keystore!" - exit 1 -else - echo '`fdroid update` prompted to add keystore' -fi -set -e - - -#------------------------------------------------------------------------------# -echo_header "copy tests/repo, update with binary transparency log" - -REPOROOT=`create_test_dir` -GIT_REMOTE=`create_test_dir` -GNUPGHOME=$REPOROOT/gnupghome -cd $REPOROOT -fdroid_init_with_prebuilt_keystore -cp -a $WORKSPACE/tests/metadata $WORKSPACE/tests/repo $REPOROOT/ -echo "binary_transparency_remote: $GIT_REMOTE" >> config.yml -$fdroid update --verbose -$fdroid deploy --verbose -test -e repo/index.xml -test -e repo/index.jar -test -e repo/index-v1.jar -grep -F '> config.yml -printf "servergitmirrors: $SERVER_GIT_MIRROR\n" >> config.yml - -cp $WORKSPACE/tests/repo/com.politedroid_[345].apk repo/ -$fdroid update --create-metadata -$fdroid deploy -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_3.apk -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_4.apk -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_5.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_3.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_4.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_5.apk -date > $GIT_MIRROR/.git/test-stamp - -# add one more APK to trigger archiving -cp $WORKSPACE/tests/repo/com.politedroid_6.apk repo/ -$fdroid update -$fdroid deploy -test -e $REPOROOT/archive/com.politedroid_3.apk -! test -e $GIT_MIRROR/fdroid/archive/com.politedroid_3.apk -! test -e $SERVER_GIT_MIRROR/fdroid/archive/com.politedroid_3.apk -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_4.apk -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_5.apk -test -e $GIT_MIRROR/fdroid/repo/com.politedroid_6.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_4.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_5.apk -test -e $SERVER_GIT_MIRROR/fdroid/repo/com.politedroid_6.apk -before=`du -s --bytes $GIT_MIRROR/.git/ | awk '{print $1}'` - -echo "git_mirror_size_limit: 60kb" >> config.yml -$fdroid update -$fdroid deploy -test -e $REPOROOT/archive/com.politedroid_3.apk -! test -e $SERVER_GIT_MIRROR/fdroid/archive/com.politedroid_3.apk -after=`du -s --bytes $GIT_MIRROR/.git/ | awk '{print $1}'` -! test -e $GIT_MIRROR/.git/test-stamp -$git -C "$GIT_MIRROR" gc -$git -C "$SERVER_GIT_MIRROR" gc -test $before -gt $after - - -#------------------------------------------------------------------------------# -echo_header "sign binary repo in offline box, then publishing from online box" - -OFFLINE_ROOT=`create_test_dir` -KEYSTORE=$WORKSPACE/tests/keystore.p12 -LOCAL_COPY_DIR=`create_test_dir`/fdroid -mkdir $LOCAL_COPY_DIR -ONLINE_ROOT=`create_test_dir` -SERVERWEBROOT=`create_test_dir`/fdroid - -# create offline binary transparency log -cd $OFFLINE_ROOT -mkdir binary_transparency -cd binary_transparency -$git init --initial-branch=master - -# fake git remote server for binary transparency log -BINARY_TRANSPARENCY_REMOTE=`create_test_dir` - -# fake git remote server for repo mirror -SERVER_GIT_MIRROR=`create_test_dir` -cd $SERVER_GIT_MIRROR -$git init --initial-branch=master -$git config receive.denyCurrentBranch updateInstead - -cd $OFFLINE_ROOT -fdroid_init_with_prebuilt_keystore -printf '\narchive_older: 3\n' >> config.yml -cp -a $WORKSPACE/tests/metadata $WORKSPACE/tests/repo $OFFLINE_ROOT/ -mkdir $OFFLINE_ROOT/unsigned -cp $WORKSPACE/tests/urzip-release-unsigned.apk $OFFLINE_ROOT/unsigned - -echo "mirrors: ['http://foo.bar/fdroid', 'http://asdflkdsfjafdsdfhkjh.onion/fdroid']" >> config.yml -echo "servergitmirrors: $SERVER_GIT_MIRROR" >> config.yml -echo "local_copy_dir: $LOCAL_COPY_DIR" >> config.yml -$fdroid update --pretty -grep -F '' repo/index.xml -grep -F '/fdroid/archive' archive/index.xml -test `grep '' repo/index.xml | wc -l` -eq 2 -test `grep '' archive/index.xml | wc -l` -eq 2 -cd binary_transparency -[ "$($git rev-list --count HEAD)" == "1" ] -cd .. -$fdroid deploy --verbose -test -e $LOCAL_COPY_DIR/unsigned/urzip-release-unsigned.apk -grep -F '> config.yml - test -d metadata || mkdir metadata - cp $WORKSPACE/tests/metadata/com.politedroid.yml metadata/ - test -d repo || mkdir repo - test -d unsigned || mkdir unsigned - cp $WORKSPACE/tests/repo/com.politedroid_6.apk unsigned/ - $fdroid signatures unsigned/com.politedroid_6.apk - test -d metadata/com.politedroid/signatures/6 - test -f metadata/com.politedroid/signatures/6/MANIFEST.MF - test -f metadata/com.politedroid/signatures/6/RELEASE.RSA - test -f metadata/com.politedroid/signatures/6/RELEASE.SF - ! test -f repo/com.politedroid_6.apk - $fdroid publish - test -f repo/com.politedroid_6.apk - if which apksigner; then - apksigner verify repo/com.politedroid_6.apk - fi - if which jarsigner; then - jarsigner -verify repo/com.politedroid_6.apk - fi -fi - - -#------------------------------------------------------------------------------# -echo_header 'test mirroring a repo' - -if which wget; then - TESTROOT=`create_test_dir` - REPOROOT=`create_test_dir` - cp -r "$WORKSPACE/tests" "$TESTROOT" - cd "$TESTROOT/tests" - test -d archive || mkdir archive - cp repo/index-v1.json $REPOROOT/ - $fdroid update - $fdroid signindex - mv $REPOROOT/index-v1.json repo/index-v1.json - - port=321${RANDOM:3} - test $(printf $port | wc -m) -le 3 && port=52734 # when $RANDOM doesn't work - timeout 5m python3 -m http.server $port --bind 127.0.0.1 > $REPOROOT/http.server.log 2>&1 & - http_server_pid=$! - - cd $REPOROOT - # shellcheck disable=SC1007 - http_proxy= HTTP_PROXY= $fdroid mirror http://127.0.0.1:${port}/ - test -e 127.0.0.1\:${port}/repo/souch.smsbypass_9.apk - test -e 127.0.0.1\:${port}/repo/icons-640/souch.smsbypass.9.png - # the index shouldn't be saved unless it was verified - ! test -e 127.0.0.1\:${port}/repo/index-v1.jar - # shellcheck disable=SC1007 - ! http_proxy= HTTP_PROXY= $fdroid mirror "http://127.0.0.1:${port}/?fingerprint=asdfasdf" - ! test -e 127.0.0.1\:${port}/repo/index-v1.jar - # shellcheck disable=SC1007 - http_proxy= HTTP_PROXY= $fdroid mirror "http://127.0.0.1:${port}/?fingerprint=F49AF3F11EFDDF20DFFD70F5E3117B9976674167ADCA280E6B1932A0601B26F6" - test -e 127.0.0.1\:${port}/repo/index-v1.jar - - # clean up - kill -9 $http_server_pid - rm -f 127.0.0.1\:${port}/repo/*.apk 127.0.0.1\:${port}/repo/*/*/*/*.png - sleep 1 # wait for webserver thread to quit -else - echo "WARNING: wget not installed, skipping" -fi - -#------------------------------------------------------------------------------# -echo_header "Test recovering from from broken git submodules" - -# On some platforms, checkupdates submodule tests need explicit perms to use file:/// -export GIT_ALLOW_PROTOCOL=file - -ROOT=$(create_test_dir) -cd "$ROOT" -mkdir foo bar -cd foo -$git init -echo a > a -$git add a -$git commit -m "a" - -cd ../bar -$git init -$git submodule add "file://$(pwd)/../foo" baz -rm .gitmodules -$git commit -am "a" -rm -rf baz -$git checkout baz -$git tag 2 - -cd .. -mkdir repo -mkdir metadata -echo "RepoType: git" >> metadata/fake.yml -echo "Repo: file://$(pwd)/bar" >> metadata/fake.yml -echo "AutoUpdateMode: Version" >> metadata/fake.yml -echo "UpdateCheckMode: Tags" >> metadata/fake.yml -echo "UpdateCheckData: '|||'" >> metadata/fake.yml -echo "CurrentVersion: 1" >> metadata/fake.yml -echo "CurrentVersionCode: 1" >> metadata/fake.yml - -$fdroid checkupdates --allow-dirty -grep "CurrentVersionCode: 2" metadata/fake.yml - -#------------------------------------------------------------------------------# -echo_header "checkupdates ignore broken submodule" - -ROOT=$(create_test_dir) -cd "$ROOT" -mkdir foo bar -cd foo -$git init -echo a > a -$git add a -$git commit -m a - -cd ../bar -$git init -$git submodule add "file://$(pwd)/../foo" baz -$git commit -am a -$git tag 2 - -cd ../foo -# delete the commit referenced in bar -$git commit --amend -m aa -$git reflog expire --expire=now --all -$git gc --aggressive --prune=now - -cd .. -mkdir repo -mkdir metadata -echo "RepoType: git" >> metadata/fake.yml -echo "Repo: file://$(pwd)/bar" >> metadata/fake.yml -echo "Builds:" >> metadata/fake.yml -echo " - versionName: 1" >> metadata/fake.yml -echo " versionCode: 1" >> metadata/fake.yml -echo " submodules: true" >> metadata/fake.yml -echo "AutoUpdateMode: Version" >> metadata/fake.yml -echo "UpdateCheckMode: Tags" >> metadata/fake.yml -echo "UpdateCheckData: '|||'" >> metadata/fake.yml -echo "CurrentVersion: 1" >> metadata/fake.yml -echo "CurrentVersionCode: 1" >> metadata/fake.yml - -$fdroid checkupdates --allow-dirty -grep "CurrentVersionCode: 2" metadata/fake.yml - - -#------------------------------------------------------------------------------# -echo_header "checkupdates check version in submodule" - -ROOT=$(create_test_dir) -cd "$ROOT" -mkdir app sub -cd sub -$git init -echo 1 > ver -$git add ver -$git commit -m 1 - -cd ../app -$git init -$git submodule add "file://$(pwd)/../sub" -$git commit -am 1 -$git tag 1 - -cd ../sub -echo 2 > ver -$git commit -am 2 - -cd ../app -$git init -$git submodule update --remote -$git commit -am 2 - -cd .. -mkdir repo -mkdir metadata -cat > metadata/fake.yml <', + Path("repo/index.xml").read_text(), + ) + self.assertTrue(Path("repo/index.jar").is_file()) + self.assertTrue(Path("repo/index-v1.jar").is_file()) + apkcache = Path("tmp/apkcache.json") + self.assertTrue(apkcache.is_file()) + self.assertTrue(apkcache.stat().st_size > 0) + self.assertTrue(Path("urzip.apk").is_symlink()) + + def test_utf8_metadata(self): + self.fdroid_init_with_prebuilt_keystore() + self.update_yaml( + "config.yml", + { + "repo_description": "获取已安装在您的设备上的应用的", + "mirrors": ["https://foo.bar/fdroid", "http://secret.onion/fdroid"], + }, + ) + shutil.copy(FILES / "urzip.apk", "repo") + shutil.copy(FILES / "bad-unicode-πÇÇ现代通用字-български-عربي1.apk", "repo") + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/info.guardianproject.urzip.yml", "metadata") + + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assert_run(self.fdroid_cmd + ["update"]) + + def test_copy_git_import_and_run_fdroid_scanner_on_it(self): + url = "https://gitlab.com/fdroid/ci-test-app.git" + Path("metadata").mkdir() + self.update_yaml( + "metadata/org.fdroid.ci.test.app.yml", + { + "AutoName": "Just A Test", + "WebSite": None, + "Builds": [ + { + "versionName": "0.3", + "versionCode": 300, + "commit": "0.3", + "subdir": "app", + "gradle": ["yes"], + } + ], + "Repo": url, + "RepoType": "git", + }, + ) + + self.assert_run(["git", "clone", url, "build/org.fdroid.ci.test.app"]) + self.assert_run( + self.fdroid_cmd + ["scanner", "org.fdroid.ci.test.app", "--verbose"] + ) + + def test_copy_repo_generate_java_gpg_keys_update_and_gpgsign(self): + self.fdroid_init_with_prebuilt_keystore() + shutil.copytree(FILES / "repo", "repo", dirs_exist_ok=True) + for dir in ["config", "metadata", "gnupghome"]: + shutil.copytree(FILES / dir, dir) + gnupghome = Path("gnupghome").resolve() + os.chmod(gnupghome, 0o700) + self.update_yaml( + "config.yml", + { + "install_list": "org.adaway", + "uninstall_list": ["com.android.vending", "com.facebook.orca"], + "gpghome": str(gnupghome), + "gpgkey": "CE71F7FB", + "mirrors": [ + "http://foobarfoobarfoobar.onion/fdroid", + "https://foo.bar/fdroid", + ], + }, + ) + self.assert_run( + self.fdroid_cmd + ["update", "--verbose", "--pretty"], + env=os.environ | {"LC_MESSAGES": "en_US.UTF-8"}, + ) + index_xml = Path("repo/index.xml").read_text() + self.assertIn("" in line) + with open("repo/index.xml") as f: + repo_cnt = sum(1 for line in f if "" in line) + if USE_APKSIGNER: + self.assertEqual(archive_cnt, 2) + self.assertEqual(repo_cnt, 10) + else: + # This will fail when jarsigner allows MD5 for APK signatures + self.assertEqual(archive_cnt, 5) + self.assertEqual(repo_cnt, 7) + + @unittest.skipIf(USE_APKSIGNER, "runs only without apksigner") + def test_per_app_archive_policy(self): + self.fdroid_init_with_prebuilt_keystore() + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") + for file in FILES.glob("repo/com.politedroid_[0-9].apk"): + shutil.copy(file, "repo") + self.update_yaml("config.yml", {"archive_older": 3}) + + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 4) + self.assertEqual(archive_cnt, 0) + self.assertIn("com.politedroid_3.apk", repo) + self.assertIn("com.politedroid_4.apk", repo) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_6.apk", repo) + self.assertTrue(Path("repo/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + + # enable one app in the repo + self.update_yaml("metadata/com.politedroid.yml", {"ArchivePolicy": 1}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertEqual(archive_cnt, 3) + self.assertIn("com.politedroid_6.apk", repo) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_5.apk", archive) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_5.apk").is_file()) + + # remove all apps from the repo + self.update_yaml("metadata/com.politedroid.yml", {"ArchivePolicy": 0}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 0) + self.assertEqual(archive_cnt, 4) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_5.apk", archive) + self.assertIn("com.politedroid_6.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_6.apk").is_file()) + self.assertFalse(Path("repo/com.politedroid_6.apk").exists()) + + # move back one from archive to the repo + self.update_yaml("metadata/com.politedroid.yml", {"ArchivePolicy": 1}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertEqual(archive_cnt, 3) + self.assertIn("com.politedroid_6.apk", repo) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_5.apk", archive) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_5.apk").is_file()) + self.assertFalse(Path("archive/com.politedroid_6.apk").exists()) + + # set an earlier version as CVC and test that it's the only one not archived + self.update_yaml("metadata/com.politedroid.yml", {"CurrentVersionCode": 5}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertEqual(archive_cnt, 3) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_6.apk", archive) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertFalse(Path("repo/com.politedroid_6.apk").exists()) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_6.apk").is_file()) + + def test_moving_old_apks_to_and_from_the_archive(self): + self.fdroid_init_with_prebuilt_keystore() + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") + self.remove_lines("metadata/com.politedroid.yml", ["ArchivePolicy:"]) + for file in FILES.glob("repo/com.politedroid_[0-9].apk"): + shutil.copy(file, "repo") + self.update_yaml("config.yml", {"archive_older": 3}) + + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + self.assertEqual(repo_cnt, 3) + self.assertIn("com.politedroid_4.apk", repo) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_6.apk", repo) + self.assertTrue(Path("repo/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(archive_cnt, 1) + self.assertIn("com.politedroid_3.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + + self.update_yaml("config.yml", {"archive_older": 1}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertIn("com.politedroid_6.apk", repo) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(archive_cnt, 3) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_5.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("archive/com.politedroid_5.apk").is_file()) + + # disabling deletes from the archive + metadata_path = Path("metadata/com.politedroid.yml") + metadata = metadata_path.read_text() + metadata = re.sub( + "versionCode: 4", "versionCode: 4\n disable: testing deletion", metadata + ) + metadata_path.write_text(metadata) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertIn("com.politedroid_6.apk", repo) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(archive_cnt, 2) + self.assertIn("com.politedroid_3.apk", archive) + self.assertNotIn("com.politedroid_4.apk", archive) + self.assertIn("com.politedroid_5.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertFalse(Path("archive/com.politedroid_4.apk").exists()) + self.assertTrue(Path("archive/com.politedroid_5.apk").is_file()) + + # disabling deletes from the repo, and promotes one from the archive + metadata = re.sub( + "versionCode: 6", "versionCode: 6\n disable: testing deletion", metadata + ) + metadata_path.write_text(metadata) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + self.assertEqual(repo_cnt, 1) + self.assertIn("com.politedroid_5.apk", repo) + self.assertNotIn("com.politedroid_6.apk", repo) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertFalse(Path("repo/com.politedroid_6.apk").exists()) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(archive_cnt, 1) + self.assertIn("com.politedroid_3.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertFalse(Path("archive/com.politedroid_6.apk").exists()) + + def test_that_verify_can_succeed_and_fail(self): + Path("tmp").mkdir() + Path("unsigned").mkdir() + shutil.copy(FILES / "repo/com.politedroid_6.apk", "tmp") + shutil.copy(FILES / "repo/com.politedroid_6.apk", "unsigned") + self.assert_run( + self.fdroid_cmd + + ["verify", "--reuse-remote-apk", "--verbose", "com.politedroid"] + ) + # force a fail + shutil.copy( + FILES / "repo/com.politedroid_5.apk", "unsigned/com.politedroid_6.apk" + ) + self.assert_run_fail( + self.fdroid_cmd + + ["verify", "--reuse-remote-apk", "--verbose", "com.politedroid"] + ) + + def test_allowing_disabled_signatures_in_repo_and_archive(self): + self.fdroid_init_with_prebuilt_keystore() + self.update_yaml( + "config.yml", {"allow_disabled_algorithms": True, "archive_older": 3} + ) + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") + self.update_yaml( + "metadata/info.guardianproject.urzip.yml", + {"Summary": "good test version of urzip"}, + replace=True, + ) + self.update_yaml( + "metadata/org.bitbucket.tickytacky.mirrormirror.yml", + {"Summary": "good MD5 sig, disabled algorithm"}, + replace=True, + ) + for file in Path("metadata").glob("*.yml"): + self.remove_lines(file, ["ArchivePolicy:"]) + for file in itertools.chain( + FILES.glob("urzip-badsig.apk"), + FILES.glob("org.bitbucket.tickytacky.mirrormirror_[0-9].apk"), + FILES.glob("repo/com.politedroid_[0-9].apk"), + ): + shutil.copy(file, "repo") + + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 6) + self.assertEqual(archive_cnt, 2) + self.assertIn("com.politedroid_4.apk", repo) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_6.apk", repo) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_2.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_3.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_4.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_1.apk", archive) + self.assertNotIn("urzip-badsig.apk", repo) + self.assertNotIn("urzip-badsig.apk", archive) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_1.apk").is_file() + ) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_2.apk").is_file() + ) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_3.apk").is_file() + ) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_4.apk").is_file() + ) + self.assertTrue(Path("archive/urzip-badsig.apk").is_file()) + + if not USE_APKSIGNER: + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + repo = Path("repo/index.xml").read_text() + repo_cnt = sum(1 for line in repo.splitlines() if "" in line) + archive = Path("archive/index.xml").read_text() + archive_cnt = sum(1 for line in archive.splitlines() if "" in line) + self.assertEqual(repo_cnt, 3) + self.assertEqual(archive_cnt, 5) + self.assertIn("com.politedroid_4.apk", repo) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_6.apk", repo) + self.assertNotIn("urzip-badsig.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_1.apk", archive) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_2.apk", archive) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_3.apk", archive) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_4.apk", archive) + self.assertIn("com.politedroid_3.apk", archive) + self.assertNotIn("urzip-badsig.apk", archive) + self.assertTrue(Path("repo/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_1.apk").is_file() + ) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_2.apk").is_file() + ) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_3.apk").is_file() + ) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_4.apk").is_file() + ) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue(Path("archive/urzip-badsig.apk").is_file()) + + # test unarchiving when disabled_algorithms are allowed again + self.update_yaml("config.yml", {"allow_disabled_algorithms": True}) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + with open("archive/index.xml") as f: + archive_cnt = sum(1 for line in f if "" in line) + with open("repo/index.xml") as f: + repo_cnt = sum(1 for line in f if "" in line) + self.assertEqual(repo_cnt, 6) + self.assertEqual(archive_cnt, 2) + self.assertIn("com.politedroid_4.apk", repo) + self.assertIn("com.politedroid_5.apk", repo) + self.assertIn("com.politedroid_6.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_2.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_3.apk", repo) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_4.apk", repo) + self.assertNotIn("urzip-badsig.apk", repo) + self.assertIn("com.politedroid_3.apk", archive) + self.assertIn("org.bitbucket.tickytacky.mirrormirror_1.apk", archive) + self.assertNotIn("urzip-badsig.apk", archive) + self.assertTrue(Path("repo/com.politedroid_4.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_5.apk").is_file()) + self.assertTrue(Path("repo/com.politedroid_6.apk").is_file()) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_2.apk").is_file() + ) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_3.apk").is_file() + ) + self.assertTrue( + Path("repo/org.bitbucket.tickytacky.mirrormirror_4.apk").is_file() + ) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertTrue( + Path("archive/org.bitbucket.tickytacky.mirrormirror_1.apk").is_file() + ) + self.assertTrue(Path("archive/urzip-badsig.apk").is_file()) + + def test_rename_apks_with_fdroid_update_rename_apks_opt_nosign_opt_for_speed(self): + self.fdroid_init_with_prebuilt_keystore() + self.update_yaml( + "config.yml", + { + "keydname": "CN=Birdman, OU=Cell, O=Alcatraz, L=Alcatraz, S=California, C=US" + }, + ) + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/info.guardianproject.urzip.yml", "metadata") + shutil.copy( + FILES / "urzip.apk", + "repo/asdfiuhk urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234 ö.apk", + ) + self.assert_run( + self.fdroid_cmd + ["update", "--rename-apks", "--pretty", "--nosign"] + ) + self.assertTrue(Path("repo/info.guardianproject.urzip_100.apk").is_file()) + index_xml = Path("repo/index.xml").read_text() + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertIn("info.guardianproject.urzip_100.apk", index_v1_json) + self.assertIn("info.guardianproject.urzip_100.apk", index_xml) + + shutil.copy(FILES / "urzip-release.apk", "repo") + self.assert_run( + self.fdroid_cmd + ["update", "--rename-apks", "--pretty", "--nosign"] + ) + self.assertTrue(Path("repo/info.guardianproject.urzip_100.apk").is_file()) + self.assertTrue( + Path("repo/info.guardianproject.urzip_100_b4964fd.apk").is_file() + ) + index_xml = Path("repo/index.xml").read_text() + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertIn("info.guardianproject.urzip_100.apk", index_v1_json) + self.assertIn("info.guardianproject.urzip_100.apk", index_xml) + self.assertIn("info.guardianproject.urzip_100_b4964fd.apk", index_v1_json) + self.assertNotIn("info.guardianproject.urzip_100_b4964fd.apk", index_xml) + + shutil.copy(FILES / "urzip-release.apk", "repo") + self.assert_run( + self.fdroid_cmd + ["update", "--rename-apks", "--pretty", "--nosign"] + ) + self.assertTrue(Path("repo/info.guardianproject.urzip_100.apk").is_file()) + self.assertTrue( + Path("repo/info.guardianproject.urzip_100_b4964fd.apk").is_file() + ) + self.assertTrue( + Path("duplicates/repo/info.guardianproject.urzip_100_b4964fd.apk").is_file() + ) + index_xml = Path("repo/index.xml").read_text() + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertIn("info.guardianproject.urzip_100.apk", index_v1_json) + self.assertIn("info.guardianproject.urzip_100.apk", index_xml) + self.assertIn("info.guardianproject.urzip_100_b4964fd.apk", index_v1_json) + self.assertNotIn("info.guardianproject.urzip_100_b4964fd.apk", index_xml) + + def test_for_added_date_being_set_correctly_for_repo_and_archive(self): + self.fdroid_init_with_prebuilt_keystore() + self.update_yaml("config.yml", {"archive_older": 3}) + Path("metadata").mkdir() + Path("archive").mkdir() + Path("stats").mkdir() + shutil.copy(FILES / "repo/com.politedroid_6.apk", "repo") + shutil.copy(FILES / "repo/index-v2.json", "repo") + shutil.copy(FILES / "repo/com.politedroid_5.apk", "archive") + shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") + + # TODO: the timestamp of the oldest apk in the file should be used, even + # if that doesn't exist anymore + self.update_yaml("metadata/com.politedroid.yml", {"ArchivePolicy": 1}) + + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + timestamp = int(datetime(2017, 6, 23, tzinfo=UTC).timestamp()) * 1000 + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertIn(f'"added": {timestamp}', index_v1_json) + # the archive will have the added timestamp for the app and for the apk, + # both need to be there + with open("archive/index-v1.json") as f: + count = sum(1 for line in f if f'"added": {timestamp}' in line) + self.assertEqual(count, 2) + + def test_whatsnew_from_fastlane_without_cvc_set(self): + self.fdroid_init_with_prebuilt_keystore() + Path("metadata/com.politedroid/en-US/changelogs").mkdir(parents=True) + shutil.copy(FILES / "repo/com.politedroid_6.apk", "repo") + shutil.copy(FILES / "metadata/com.politedroid.yml", "metadata") + self.remove_lines("metadata/com.politedroid.yml", ["CurrentVersion:"]) + Path("metadata/com.politedroid/en-US/changelogs/6.txt").write_text( + "whatsnew test" + ) + self.assert_run(self.fdroid_cmd + ["update", "--pretty", "--nosign"]) + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertIn("whatsnew test", index_v1_json) + + def test_metadata_checks(self): + Path("repo").mkdir() + shutil.copy(FILES / "urzip.apk", "repo") + # this should fail because there is no metadata + self.assert_run_fail(self.fdroid_cmd + ["build"]) + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/org.smssecure.smssecure.yml", "metadata") + self.assert_run(self.fdroid_cmd + ["readmeta"]) + + def test_ensure_commands_that_dont_need_the_jdk_work_without_a_jdk_configured(self): + Path("repo").mkdir() + Path("metadata").mkdir() + self.update_yaml( + "metadata/fake.yml", + { + "License": "GPL-2.0-only", + "Summary": "Yup still fake", + "Categories": ["Internet"], + "Description": "this is fake", + }, + ) + # fake that no JDKs are available + self.update_yaml( + "config.yml", {"categories": ["Internet"], "java_paths": {}}, replace=True + ) + local_copy_dir = self.tmp / "fdroid" + (local_copy_dir / "repo").mkdir(parents=True) + self.update_yaml( + "config.yml", {"local_copy_dir": str(local_copy_dir.resolve())} + ) + + subprocess.run(self.fdroid_cmd + ["checkupdates", "--allow-dirty"]) + if shutil.which("gpg"): + self.assert_run(self.fdroid_cmd + ["gpgsign"]) + self.assert_run(self.fdroid_cmd + ["lint"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assert_run(self.fdroid_cmd + ["rewritemeta", "fake"]) + self.assert_run(self.fdroid_cmd + ["deploy"]) + self.assert_run(self.fdroid_cmd + ["scanner"]) + + # run these to get their output, but the are not setup, so don't fail + subprocess.run(self.fdroid_cmd + ["build"]) + subprocess.run(self.fdroid_cmd + ["import"]) + subprocess.run(self.fdroid_cmd + ["install", "-n"]) + + def test_config_checks_of_local_copy_dir(self): + self.assert_run(self.fdroid_cmd + ["init"]) + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata", "--verbose"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + local_copy_dir = (self.tmp / "fdroid").resolve() + local_copy_dir.mkdir() + self.assert_run( + self.fdroid_cmd + ["deploy", "--local-copy-dir", local_copy_dir] + ) + self.assert_run( + self.fdroid_cmd + + ["deploy", "--local-copy-dir", local_copy_dir, "--verbose"] + ) + + # this should fail because thisisnotanabsolutepath is not an absolute path + self.assert_run_fail( + self.fdroid_cmd + ["deploy", "--local-copy-dir", "thisisnotanabsolutepath"] + ) + # this should fail because the path doesn't end with "fdroid" + self.assert_run_fail( + self.fdroid_cmd + + ["deploy", "--local-copy-dir", "/tmp/IReallyDoubtThisPathExistsasdfasdf"] + ) + # this should fail because the dirname path does not exist + self.assert_run_fail( + self.fdroid_cmd + + [ + "deploy", + "--local-copy-dir", + "/tmp/IReallyDoubtThisPathExistsasdfasdf/fdroid", + ] + ) + + def test_setup_a_new_repo_from_scratch_using_android_home_and_do_a_local_sync(self): + self.fdroid_init_with_prebuilt_keystore() + self.copy_apks_into_repo() + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata", "--verbose"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assertIn(" 0) + + def test_check_duplicate_files_are_properly_handled_by_fdroid_update(self): + self.fdroid_init_with_prebuilt_keystore() + Path("metadata").mkdir() + shutil.copy(FILES / "metadata/obb.mainpatch.current.yml", "metadata") + shutil.copy(FILES / "repo/obb.mainpatch.current_1619.apk", "repo") + shutil.copy( + FILES / "repo/obb.mainpatch.current_1619_another-release-key.apk", "repo" + ) + self.assert_run(self.fdroid_cmd + ["update", "--pretty"]) + index_xml = Path("repo/index.xml").read_text() + index_v1_json = Path("repo/index-v1.json").read_text() + self.assertNotIn( + "obb.mainpatch.current_1619_another-release-key.apk", index_xml + ) + self.assertIn("obb.mainpatch.current_1619.apk", index_xml) + self.assertIn("obb.mainpatch.current_1619.apk", index_v1_json) + self.assertIn( + "obb.mainpatch.current_1619_another-release-key.apk", index_v1_json + ) + # die if there are exact duplicates + shutil.copy(FILES / "repo/obb.mainpatch.current_1619.apk", "repo/duplicate.apk") + self.assert_run_fail(self.fdroid_cmd + ["update"]) + + def test_setup_new_repo_from_scratch_using_android_home_env_var_putting_apks_in_repo_first( + self, + ): + Path("repo").mkdir() + self.copy_apks_into_repo() + self.fdroid_init_with_prebuilt_keystore() + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata", "--verbose"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assertIn(" 0) + + def test_setup_a_new_repo_manually_and_generate_a_keystore(self): + self.assertFalse(Path("keystore.p12").exists()) + # this should fail because this repo has no keystore + self.assert_run_fail(self.fdroid_cmd + ["update"]) + self.assert_run(self.fdroid_cmd + ["update", "--create-key"]) + self.assertTrue(Path("keystore.p12").is_file()) + self.copy_apks_into_repo() + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata", "--verbose"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assertIn(" 0) + + def test_setup_a_new_repo_from_scratch_generate_a_keystore_then_add_apk_and_update( + self, + ): + self.assert_run(self.fdroid_cmd + ["init", "--keystore", "keystore.p12"]) + self.assertTrue(Path("keystore.p12").is_file()) + self.copy_apks_into_repo() + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata", "--verbose"]) + self.assert_run(self.fdroid_cmd + ["readmeta"]) + self.assertIn(" 0) + self.assertIn(" 0) + + # now set fake repo_keyalias + self.update_yaml("config.yml", {"repo_keyalias": "fake"}) + # this should fail because this repo has a bad repo_keyalias + self.assert_run_fail(self.fdroid_cmd + ["update"]) + + # this should fail because a keystore is already there + self.assert_run_fail(self.fdroid_cmd + ["update", "--create-key"]) + + # now actually create the key with the existing settings + Path("keystore.jks").unlink() + self.assert_run(self.fdroid_cmd + ["update", "--create-key"]) + self.assertTrue(Path("keystore.jks").is_file()) + + def test_setup_a_new_repo_from_scratch_using_android_home_env_var_with_git_mirror( + self, + ): + (server_git_mirror := self.tmp / "server_git_mirror").mkdir() + self.assert_run( + ["git", "-C", server_git_mirror, "init", "--initial-branch", "master"] + ) + self.assert_run( + [ + "git", + "-C", + server_git_mirror, + "config", + "receive.denyCurrentBranch", + "updateInstead", + ] + ) + + self.fdroid_init_with_prebuilt_keystore() + self.update_yaml( + "config.yml", + {"archive_older": 3, "servergitmirrors": str(server_git_mirror)}, + ) + for file in FILES.glob("repo/com.politedroid_[345].apk"): + shutil.copy(file, "repo") + self.assert_run(self.fdroid_cmd + ["update", "--create-metadata"]) + self.assert_run(self.fdroid_cmd + ["deploy"]) + git_mirror = Path("git-mirror") + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_3.apk").is_file()) + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_4.apk").is_file()) + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_5.apk").is_file()) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_3.apk").is_file() + ) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_4.apk").is_file() + ) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_5.apk").is_file() + ) + (git_mirror / ".git/test-stamp").write_text(str(datetime.now())) + + # add one more APK to trigger archiving + shutil.copy(FILES / "repo/com.politedroid_6.apk", "repo") + self.assert_run(self.fdroid_cmd + ["update"]) + self.assert_run(self.fdroid_cmd + ["deploy"]) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertFalse((git_mirror / "fdroid/archive/com.politedroid_3.apk").exists()) + self.assertFalse( + (server_git_mirror / "fdroid/archive/com.politedroid_3.apk").exists() + ) + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_4.apk").is_file()) + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_5.apk").is_file()) + self.assertTrue((git_mirror / "fdroid/repo/com.politedroid_6.apk").is_file()) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_4.apk").is_file() + ) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_5.apk").is_file() + ) + self.assertTrue( + (server_git_mirror / "fdroid/repo/com.politedroid_6.apk").is_file() + ) + before = sum( + f.stat().st_size for f in (git_mirror / ".git").glob("**/*") if f.is_file() + ) + + self.update_yaml("config.yml", {"git_mirror_size_limit": "60kb"}) + self.assert_run(self.fdroid_cmd + ["update"]) + self.assert_run(self.fdroid_cmd + ["deploy"]) + self.assertTrue(Path("archive/com.politedroid_3.apk").is_file()) + self.assertFalse( + (server_git_mirror / "fdroid/archive/com.politedroid_3.apk").exists() + ) + after = sum( + f.stat().st_size for f in (git_mirror / ".git").glob("**/*") if f.is_file() + ) + self.assertFalse((git_mirror / ".git/test-stamp").exists()) + self.assert_run(["git", "-C", git_mirror, "gc"]) + self.assert_run(["git", "-C", server_git_mirror, "gc"]) + self.assertGreater(before, after) + + def test_sign_binary_repo_in_offline_box_then_publishing_from_online_box(self): + (offline_root := self.tmp / "offline_root").mkdir() + (local_copy_dir := self.tmp / "local_copy_dir/fdroid").mkdir(parents=True) + (online_root := self.tmp / "online_root").mkdir() + (server_web_root := self.tmp / "server_web_root/fdroid").mkdir(parents=True) + + # create offline binary transparency log + (offline_root / "binary_transparency").mkdir() + os.chdir(offline_root / "binary_transparency") + self.assert_run(["git", "init", "--initial-branch", "master"]) + + # fake git remote server for binary transparency log + (binary_transparency_remote := self.tmp / "binary_transparency_remote").mkdir() + + # fake git remote server for repo mirror + (server_git_mirror := self.tmp / "server_git_mirror").mkdir() + os.chdir(server_git_mirror) + self.assert_run(["git", "init", "--initial-branch", "master"]) + self.assert_run(["git", "config", "receive.denyCurrentBranch", "updateInstead"]) + + os.chdir(offline_root) + self.fdroid_init_with_prebuilt_keystore() + shutil.copytree(FILES / "repo", "repo", dirs_exist_ok=True) + shutil.copytree(FILES / "metadata", "metadata") + Path("unsigned").mkdir() + shutil.copy(FILES / "urzip-release-unsigned.apk", "unsigned") + self.update_yaml( + "config.yml", + { + "archive_older": 3, + "mirrors": [ + "http://foo.bar/fdroid", + "http://asdflkdsfjafdsdfhkjh.onion/fdroid", + ], + "servergitmirrors": str(server_git_mirror), + "local_copy_dir": str(local_copy_dir), + }, + ) + self.assert_run(self.fdroid_cmd + ["update", "--pretty"]) + index_xml = Path("repo/index.xml").read_text() + self.assertIn("", index_xml) + mirror_cnt = sum(1 for line in index_xml.splitlines() if "" in line) + self.assertEqual(mirror_cnt, 2) + + archive_xml = Path("archive/index.xml").read_text() + self.assertIn("/fdroid/archive", archive_xml) + mirror_cnt = sum(1 for line in archive_xml.splitlines() if "" in line) + self.assertEqual(mirror_cnt, 2) + + os.chdir("binary_transparency") + proc = self.assert_run( + ["git", "rev-list", "--count", "HEAD"], capture_output=True + ) + self.assertEqual(int(proc.stdout), 1) + os.chdir(offline_root) + self.assert_run(self.fdroid_cmd + ["deploy", "--verbose"]) + self.assertTrue( + Path(local_copy_dir / "unsigned/urzip-release-unsigned.apk").is_file() + ) + self.assertIn( + "