F-droid partial mirror
I want to ensure the longevity of the tools I use on my mobile devices. Of course I use the amazing F-droid app store. I wanted to make sure the packages I use from this repository are available even if F-droid ever disappears. (A secondary benefit is that if I ever reinstall, or update, apps from my local repo, the downloads are way faster!)
So I spent a bunch of time reading the docs and learning how to set up my own, partial binary mirror of F-droid. A future step might include building packages, but for now I'm sticking to just the binaries.
Preparing the main F-droid custom mirror
All work is done on server3.
sudo useradd fdroid # set pw in keepass sudo mkdir -p /mnt/mirror/fdroid /opt/android-sdk sudo dnf install java-1.8.0-openjdk-headless sudo dnf install --setopt=install_weak_deps=False java-1.8.0-openjdk-devel
The bare command would be this:
sudo -u fdroid -E /usr/bin/rsync -n -aHS --delete --delete-delay --info=progress2 plug-mirror.rcac.purdue.edu::fdroid/repo/ /mnt/mirror/fdroid/
But I of course wrote a script for myself, which is shown farther down in this post.
So, back to the commands for initial setup.
sudo su - fdroid python3 -m venv ~/venv1 source ~/venv1/bin/activate pip install --upgrade pip pip install fdroidserver cffi bcrypt setuptools-rust androguard==3.3.5 mkdir -p ~/git git clone https://gitlab.com/fdroid/fdroiddata ~/git/fdroiddata
The androguard version is important: it needs to be less than 4.0 for a reason that hopefully is documented in one of my references. So 3.3.5 was the last in the <3.6 or <4.0 series, and that higher version breaks something somewhere.
Preparing the keystore with my official cert for server3:
openssl pkcs12 -export -in /etc/pki/tls/certs/https-server3.ipa.internal.com.pem -inkey /etc/pki/tls/private/https-server3.ipa.internal.com-nopw.key -out /etc/pki/tls/private/https-server3.ipa.internal.com.p12 -name server3 -CAfile /etc/ipa/ca.crt -caname ipa # entered the password, in keepass sudo chown "${admin1}:fdroid" /etc/pki/tls/private/https-server3.ipa.internal.com.p12 sudo chmod 0640 /etc/pki/tls/private/https-server3.ipa.internal.com.p12
Updated keystore, keystorepass, keypass, repo_keyalias in config.yml.
files/2024/listings/fdroid-sync.sh (Source)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
#/bin/sh # vim: set ts=3 sw=3 sts=3: # File: /etc/installed/fdroid/fdroid-sync.sh # Location: server3 # Author: bgstack15 # SPDX-License-Identifier: GPL-3.0-only # Startdate: 2024-05-29-4 07:36 # Title: F-Droid Sync wrapper # Purpose: Single command to mirror down only the F-droid packages I use # History: # Usage: # Reference: # Improve: # take a list of hardcoded filenames and prepend them with "P" in the rsync file, to avoid deletion. # Documentation: # Dependencies: # user fdroid, ~/venv1, custom pip packages installed # cd ~/git ; git clone https://gitlab.com/fdroid/fdroiddata # plecho from bgscripts CONF_FILE="${CONF_FILE:-/etc/sysconfig/fdroid-sync}" test -f "${CONF_FILE}" && . "${CONF_FILE}" WORK_DIR="${WORK_DIR:-/etc/installed/fdroid}" GET_FILE="${GET_FILE:-${WORK_DIR}/get}" MIRROR_DIR="${MIRROR_DIR:-/mnt/mirror/fdroid/repo}" GIT_DIR="${GIT_DIR:-/home/fdroid/git/fdroiddata}" INSTALLED_FILES_DIR="${INSTALLED_FILES_DIR:-/mnt/public/Support/Systems/server3/fdroid}" prepare_input_file() { echo "# BEGIN preparing list of packages I want." # installed.txt was the exact output shared from my fdroid instance(s) # I do not understand how this appears to limit to just our desired apps, within the icons/ path while also grabbing still the app files. But I think it does, so we will leave it at that. { #awk -F',' '$1!~/packageName/{print $1}' < "${WORK_DIR}/installed.txt" | awk '{print "+ **/"$0"**";}' #awk -F',' '$1!~/packageName/{print "+ **/"$1"*"; print "+ **/"$1"/en*/***"; print "+ icons*/"$1"***";}' | sort printf '%s\n' '- org.fdroid.fdroid.privileged*' ; printf '%s\n' '- com.termux.api*' ; printf '%s\n' '- com.termux.boot*' ; printf '%s\n' '- com.termux.gui*' ; printf '%s\n' '- com.termux.nix*' ; printf '%s\n' '- com.termux.styling*' ; printf '%s\n' '- com.termux.tasker*' ; printf '%s\n' '- com.termux.widget*' ; printf '%s\n' '- com.termux.window*' ; # Find all the metadata/*.protect files and use that filename as a deletion-protection filter for rsync # Does this, in effect: printf '%s\n' 'P org.qtproject.bibletimemini*' ; find "${MIRROR_DIR%%/}/../metadata/"*.protect | \ xargs basename --multiple | sed -r -e 's/\.protect$/*/;' -e 's/^/P /;' cat "${INSTALLED_FILES_DIR%%/}"/installed*.txt | grep -vE 'com.google.android.marvin.talkback|org.fdroid.fdroid.privileged|\<packageName\>' | \ awk -F',' '$1!~/packageName/{print "+ **/"$1"*"; print "+ **/"$1"/en*/***"; }' | sort printf '%s\n' '+ icons*/' printf '%s\n' '- *' ; #printf '%s\n' '+ **/index*' #awk -F',' '$1!~/packageName/{print $1}' < installed.txt | awk '{print "+ **"$0"*";print "+ icons*/"$0"*";}' ; printf '%s\n' '- *' ; } > "${GET_FILE}" } sync_files() { echo "# BEGIN syncing files" if test -n "${APPLY}" ; then dashn="" else dashn="-n " fi if test "${USER}" != "fdroid" ; then sudo -u fdroid -E /usr/bin/rsync --filter "merge ${GET_FILE}" ${dashn}-v -r -d -aHS --delete --delete-delay plug-mirror.rcac.purdue.edu::fdroid/repo/ "${MIRROR_DIR}" else /usr/bin/rsync --filter "merge ${GET_FILE}" ${dashn}-v -r -aHS --delete --delete-delay plug-mirror.rcac.purdue.edu::fdroid/repo/ "${MIRROR_DIR}" fi } fix_metadata() { echo "# BEGIN fixing metadata" _oldpwd="${PWD}" cd "${GIT_DIR}" git checkout master ; git pull cat "${INSTALLED_FILES_DIR%%/}"/installed*.txt | grep -vE 'com.google.android.marvin.talkback|org.fdroid.fdroid.privileged|\<packageName\>' | \ awk -F',' '{print $1}' | while read app ; do appyml="${app}.yml" destfile="$( find "${MIRROR_DIR%%/}/../metadata/" -mindepth 1 -maxdepth 1 -name "${appyml}" -exec readlink -f {} \; )" test -z "${destfile}" && { destfile="${MIRROR_DIR%%/}/../metadata/${appyml}" echo "Making new file ${destfile}" touch "${destfile}" } descfile="${destfile%%.yml}.desc" srcfile="$( find "${GIT_DIR}/metadata" -name "${appyml}" -exec readlink -f {} \; )" echo "Need to munge ${srcfile} to ${destfile}" test -f "${descfile}" || { curl --silent -L "https://f-droid.org/en/packages/${app}/" | awk -F'"' '/meta name.*description/{print $4}' > "${descfile}" ; } if test -f "${srcfile}" ; then cp -pf "${srcfile}" "${destfile}" # too simple, and just one line. this should be rewritten in python bs4 echo "Summary: '$( cat "${descfile}" )'" >> "${destfile}" fi sed -i -r -e "/ArchivePolicy:/d" -e "/AutoName:/s/^AutoName:/Name:/g;" "${destfile}" done cd "${_oldpwd}" } fdroid_update() { ( source ~/venv1/bin/activate cd "${MIRROR_DIR}/.." fdroid update -c --use-date-from-apk ) } generate_web() { /etc/installed/fdroid/fdroid_generate_web.py } main() { cd "${WORK_DIR}" test -z "${SKIP_INPUT}" && prepare_input_file test -z "${SKIP_METADATA}" && fix_metadata test -z "${SKIP_SYNC}" && sync_files test -z "${SKIP_UPDATE}" && fdroid_update test -z "${SKIP_WEB}" && generate_web } case "${0}" in "-bash") # dot-sourced : ;; *) main | plecho "fdroid_sync" ;; esac |
Preparing the repo, after using the above custom rsync process in /etc/installed/fdroid-sync.sh
.
sudo su - fdroid cd /mnt/mirror/fdroid time fdroid init --android-home /opt/android-sdk -v
Had to add java_paths to config.yml after the init:
/usr/lib/jvm/jre-openjdk/
Using a custom icon was tricky. The program likes to warn about file repo/icons/icon.png
, but the file needs to be explicitly named in config.yml, and stored in /mnt/mirror/fdroid/
directly.
cp -pi /mnt/public/Support/Programs/f-droid/bgstack15.png /mnt/mirror/fdroid/bgstack15.png
Using this fdroid mirror
Visit https://www.example.com/mirror/fdroid/repo or http://server3/mirror/fdroid/repo in a browser. This sync process could never happen again, but what is already here will remain useful.
Operations
Some expected operations are described here.
Syncing the mirror
Use the custom script /etc/installed/fdroid/fdroid-sync.sh
. You can pass various SKIP_
parameters:
-
SKIP_INPUT
Skip customizing the list of packages from my installed*.txt raw input from mobile devices. -
SKIP_METADATA
Skip updating the metadata yml files with descriptions of the apps. If I have not changed apps or have any reason to expect the descriptions or screenshots have changed, I can skip the metadata. -
SKIP_SYNC
Skip main rsync process. -
SKIP_UPDATE
Skip the fdroid repo update process. -
SKIP_WEB
Skip the simple web index.html generation.
This is configured in cron, weekly on server3.
Updating list of used packages
You can use F-droid to share a list of packages and version numbers installed. Export this list to a text file, /etc/installed/fdroid/installed-${DEVICENAME}.txt. Note that in order to list packages, you must have the repository on from where the packages were installed. That is, if Jellyfin is only in the F-droid main repo, but you disable this repo, the f-droid client will not list Jellyfin as installed.
Rerun fdroid-sync.sh
which will automatically grab all installed*.txt
files in /etc/installed/fdroid/.
Dependencies
Storage3 is AlmaLinux 8, so it has python 3.6 and pip. A virtual environment had some packages installed, to ~fdroid/venv1. This gets activated by the steps that need it in fdroid-sync.sh
Android SDK was installed, and a command was used to get certain components of that. It is possible that only apksigner is required.
Related files
/etc/installed/fdroid/fdroid-sync.sh
- can be dot-sourced for interactively running any individuals steps.
-
/etc/installed/fdroid/fdroid_generate_web.py
(next blog post) - can be run independently. Builds the index.html for /mnt/mirror/fdroid/, not repo/ which
fdroid update
handles from inside fdroid-sync.sh. -
/etc/sysconfig/fdroid-sync
optional config file -
/etc/sysconfig/fdroid-generate-web
optional config file -
/etc/installed/fdroid/get
dynamically generated byfdroid-sync.sh
-
/mnt/public/Support/Systems/server3/fdroid/installed-*.txt
lists of my installed packages, exported from F-droid on each of my devices. -
/mnt/mirror/fdroid/config.yml
generated byfdroid init
and then customized -
/mnt/mirror/fdroid/fdroid.css
small hand-crafted css for my index.html. -
/etc/pki/tls/private/https-server3.ipa.internal.com.p12
pfx generated from the official IPA cert for server3. -
/etc/cron.d/mirror_cron
has a line for running this weekly.0 14 * * 4 fdroid /etc/installed/fdroid/fdroid-sync.sh 1>/dev/null 2>&1
Previous files
-
/mnt/mirror/fdroid/keystore.p12
generated byfdroid init
. Password is in keepass. This file was replaced with
Improvements
I might need to update the android-sdk thing somehow, or I might decide I want to try to compile packages.
Installing the Android SDK
The Android SDK provides the apksigner
utility used by fdroid update
. It's technically optional, but it makes the signing "stronger" or something.
sudo mkdir -p /opt/android-sdk sudo chown fdroid:admins -R /opt/android-sdk sudo su - fdroid cd /opt/android-sdk cd /opt/wg wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip unzip commandlinetools-*latest.zip # ran into an error about class file version 61.0, so need java-17-openjdk-headless
As an admin:
sudo dnf install java-17-openjdk-headless
Back as user fdroid:
time JAVA_HOME=/usr/lib/jvm/jre-17 /opt/android-sdk/cmdline-tools/bin/sdkmanager "platform-tools" "build-tools;27.0.3" "ndk-bundle" --sdk_root=/opt/android-sdk # had to say "y" to the google license time JAVA_HOME=/usr/lib/jvm/jre-17 /opt/android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=/opt/android-sdk --update time JAVA_HOME=/usr/lib/jvm/jre-17 /opt/android-sdk/cmdline-tools/bin/sdkmanager --sdk_root=/opt/android-sdk "build-tools;34.0.0"
References
- Running a Mirror | F-Droid - Free and Open Source Android App Repository
- Installing the Server and Repo Tools | F-Droid - Free and Open Source Android App Repository
- Setup an F-Droid App Repo | F-Droid - Free and Open Source Android App Repository
- examples/config.yml · 2.1.2 · F-Droid / fdroidserver · GitLab
- GitHub Actions 101 Continued: How to Self-Host a F-Droid Repo – Vinfall@Geekademy
Android SDK references
- Install Android Studio on Rocky Linux 8|AlmaLinux 8|CentOS 8 - TechViewLeo
- This document describes how to install Android SDK on a headless Linux server, i.e. a server with no GUI, just a shell. This proved very useful when I had to set up Android builds for Continuous Integration.
- Download Android Studio & App Tools - Android Developers
- https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
Comments