feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
#!/usr/bin/env bash
|
2026-10-05 20:41:54 +02:00
|
|
|
# Updates the F-Droid repository at https://halkyon.app/fdroid/repo and
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
# uploads it: adds an APK if one is given, signs the index with the repository's
|
|
|
|
|
# key, and uploads everything to ramiel as the user fdroid, whose key can only
|
|
|
|
|
# write into the folder Caddy serves (rrsync). tool/release.sh runs it with each
|
|
|
|
|
# release's APK; run alone, it publishes the repository as it is, e.g. after its
|
|
|
|
|
# settings changed.
|
|
|
|
|
#
|
|
|
|
|
# tool/fdroid_repo.sh [APK]
|
|
|
|
|
# tool/fdroid_repo.sh --check (the tools, the key and its password only)
|
|
|
|
|
#
|
|
|
|
|
# The repository's working folder, outside Git, keeps every APK released so far:
|
|
|
|
|
# $HALKYON_FDROID_DIR (default below). Its settings and the apps' listings come
|
|
|
|
|
# from tool/release/fdroid/, copied in at every run. The index key is a PKCS #12
|
|
|
|
|
# keystore at $HALKYON_FDROID_KEYSTORE, alias halkyon-fdroid, whose certificate's
|
|
|
|
|
# SHA-256 is pinned in tool/release/fdroid-index-certificate.sha256: an index
|
|
|
|
|
# signed otherwise would make the F-Droid app refuse the repository. Its password
|
|
|
|
|
# comes from $HALKYON_FDROID_KEYSTORE_PASSWORD, or is asked for; it's never
|
|
|
|
|
# written down. Needs fdroidserver v2.4.5 (pipx), rsync, a JDK, the Android SDK,
|
|
|
|
|
# and the ssh host ramiel-fdroid.
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
|
|
|
|
|
|
|
|
readonly working="${HALKYON_FDROID_DIR:-$HOME/.local/share/halkyon-learn/fdroid}"
|
|
|
|
|
keystore="${HALKYON_FDROID_KEYSTORE:-$HOME/.local/share/halkyon-learn/fdroid-index.p12}"
|
|
|
|
|
readonly key_alias='halkyon-fdroid'
|
|
|
|
|
readonly pinned_file='tool/release/fdroid-index-certificate.sha256'
|
|
|
|
|
readonly upload_host='ramiel-fdroid'
|
2026-10-05 20:41:54 +02:00
|
|
|
readonly repo_url='https://halkyon.app/fdroid/repo'
|
feat: an F-Droid repository at learn.halkyon.app
Releases also publish the APK in the project's own F-Droid repository,
https://learn.halkyon.app/fdroid/repo. tool/fdroid_repo.sh adds the APK,
signs the indexes with the repository's own key and uploads them to
ramiel as the user fdroid, whose key can only write into the folder
Caddy serves (rrsync), the APKs first and the indexes last.
tool/release.sh checks that key and its password before building and
publishes after the Forgejo release.
- tool/release/fdroid/ holds the repository's settings (Halkyon, the
snap's icon) and the app's listing (the snap's text), with the
anti-features TetheredNet (learn.halkyon.eu only, for now) and
NonFreeNet (YouTube, the server's SSPL) declared with their reasons,
in F-Droid's own definitions and with its icons (fdroiddata, AGPL v3).
- The index key's certificate is pinned: a new key would make the F-Droid
app refuse the repository.
- fdroidserver v2.4.5, since Ubuntu's v2.2.1 knows neither TetheredNet
nor reasons.
README and AGENTS.md describe it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-05 20:25:02 +02:00
|
|
|
|
|
|
|
|
fail() {
|
|
|
|
|
echo "✗ $*" >&2
|
|
|
|
|
exit 1
|
|
|
|
|
}
|
|
|
|
|
step() { echo "▸ $*"; }
|
|
|
|
|
|
|
|
|
|
[ $# -le 1 ] || fail 'Usage: tool/fdroid_repo.sh [APK | --check]'
|
|
|
|
|
check_only=false
|
|
|
|
|
apk=''
|
|
|
|
|
case "${1:-}" in
|
|
|
|
|
--check) check_only=true ;;
|
|
|
|
|
'') ;;
|
|
|
|
|
*) apk="$1" ;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
for tool in fdroid rsync; do
|
|
|
|
|
command -v "$tool" >/dev/null || fail "$tool is missing."
|
|
|
|
|
done
|
|
|
|
|
# fdroid takes jarsigner and keytool from the PATH, apksigner from the SDK.
|
|
|
|
|
jdk_bin=$(dirname "$(command -v java || echo /snap/android-studio/current/jbr/bin/java)")
|
|
|
|
|
[ -x "$jdk_bin/jarsigner" ] && [ -x "$jdk_bin/keytool" ] ||
|
|
|
|
|
fail 'No JDK found (jarsigner, keytool).'
|
|
|
|
|
export PATH="$jdk_bin:$PATH"
|
|
|
|
|
export ANDROID_HOME="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-$HOME/Android/Sdk}}"
|
|
|
|
|
|
|
|
|
|
[ -f "$keystore" ] || fail "No keystore at $keystore (HALKYON_FDROID_KEYSTORE)."
|
|
|
|
|
keystore=$(realpath "$keystore")
|
|
|
|
|
export HALKYON_FDROID_KEYSTORE="$keystore"
|
|
|
|
|
[ -f "$pinned_file" ] || fail "$pinned_file is missing."
|
|
|
|
|
pinned=$(grep -v '^#' "$pinned_file" | tr -d ' :\n' | tr 'a-f' 'A-F' || true)
|
|
|
|
|
[[ "$pinned" =~ ^[0-9A-F]{64}$ ]] || fail "$pinned_file holds no SHA-256."
|
|
|
|
|
|
|
|
|
|
if [ -z "${HALKYON_FDROID_KEYSTORE_PASSWORD:-}" ]; then
|
|
|
|
|
read -rsp "Password of $keystore: " HALKYON_FDROID_KEYSTORE_PASSWORD
|
|
|
|
|
echo
|
|
|
|
|
fi
|
|
|
|
|
export HALKYON_FDROID_KEYSTORE_PASSWORD
|
|
|
|
|
# The F-Droid app knows the repository by this fingerprint: the certificate's
|
|
|
|
|
# SHA-256 in capitals, without colons.
|
|
|
|
|
fingerprint=$(keytool -list -v -keystore "$keystore" -alias "$key_alias" \
|
|
|
|
|
-storepass:env HALKYON_FDROID_KEYSTORE_PASSWORD 2>/dev/null |
|
|
|
|
|
sed -n 's/^.*SHA256: *//p' | head -n 1 | tr -d ':' | tr 'a-f' 'A-F' || true)
|
|
|
|
|
[ -n "$fingerprint" ] || fail "Wrong password, or no key $key_alias in $keystore."
|
|
|
|
|
[ "$fingerprint" = "$pinned" ] ||
|
|
|
|
|
fail "The key $key_alias in $keystore isn't the one in $pinned_file."
|
|
|
|
|
if $check_only; then
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
step 'Updating the F-Droid repository'
|
|
|
|
|
mkdir -p "$working/repo"
|
|
|
|
|
cp -r tool/release/fdroid/. "$working/"
|
|
|
|
|
# fdroid refuses a config.yml others can read.
|
|
|
|
|
chmod 600 "$working/config.yml"
|
|
|
|
|
cp snap/gui/halkyon-learn.png "$working/halkyon.png"
|
|
|
|
|
if [ -n "$apk" ]; then
|
|
|
|
|
[ -f "$apk" ] || fail "No APK at $apk."
|
|
|
|
|
cp "$apk" "$working/repo/"
|
|
|
|
|
fi
|
|
|
|
|
# Names every APK as F-Droid does (<app id>_<version code>.apk) and signs the
|
|
|
|
|
# indexes with the repository's key.
|
|
|
|
|
(cd "$working" && fdroid update --rename-apks)
|
|
|
|
|
|
|
|
|
|
step "Uploading it to $upload_host"
|
|
|
|
|
# The APKs and icons first, the indexes last, so the F-Droid app never reads an
|
|
|
|
|
# index naming a file that isn't there yet.
|
|
|
|
|
for dir in repo archive; do
|
|
|
|
|
[ -d "$working/$dir" ] || continue
|
|
|
|
|
rsync -a --chmod=D755,F644 --exclude='index*' --exclude='entry.*' \
|
|
|
|
|
"$working/$dir/" "$upload_host:$dir/"
|
|
|
|
|
done
|
|
|
|
|
for dir in repo archive; do
|
|
|
|
|
[ -d "$working/$dir" ] || continue
|
|
|
|
|
rsync -a --chmod=D755,F644 --delete-after "$working/$dir/" "$upload_host:$dir/"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
echo " $repo_url?fingerprint=$fingerprint"
|