Compare commits

..

13 Commits

Author SHA1 Message Date
julian 8cf3945f1c Update Readme.md 2026-06-22 06:41:10 +00:00
julian e7f70de19a Update Readme.md 2026-06-22 06:40:38 +00:00
julian 6cab77cf22 Add Readme 2026-06-22 08:37:36 +02:00
julian 24e54a1da0 Ignore nix build result 2026-06-22 08:37:27 +02:00
julian ed961c58dd Remove unneeded script 2026-06-22 08:37:16 +02:00
julian 0d80be4715 caddy: fix location from where to serve files 2026-06-22 08:36:57 +02:00
julian f63c603ddb Fix fdroid update in the container 2026-06-22 08:36:34 +02:00
julian 352e32adbc Update script: cd into fdroid repo before updating 2026-06-21 14:03:13 +02:00
julian 4a421a675c Make update-apks script compare directory hashes 2026-06-21 13:46:00 +02:00
julian ad4a2bfb82 Fix wrong variable used 2026-06-21 13:30:03 +02:00
julian 52c10d7223 Improve update scripts 2026-06-21 13:21:28 +02:00
julian 691014286c Switch from nginx to caddy 2026-06-21 13:21:09 +02:00
julian 66057f4e3c Switch from devenv to flake 2026-06-21 13:20:51 +02:00
14 changed files with 202 additions and 201 deletions
+1 -10
View File
@@ -1,10 +1 @@
export DIRENV_WARN_TIMEOUT=20s use flake
eval "$(devenv direnvrc)"
# `use devenv` supports the same options as the `devenv shell` command.
#
# To silence the output, use `--quiet`.
#
# Example usage: use devenv --quiet --impure --option services.postgres.enable:bool true
use devenv
+1
View File
@@ -14,3 +14,4 @@ devenv.local.nix
# pre-commit # pre-commit
.pre-commit-config.yaml .pre-commit-config.yaml
/result
+48
View File
@@ -0,0 +1,48 @@
# Frajul's F-Droid Repo
This contains the code for the deployment of my own F-Droid.
## Deployment overview
The deployment works via `docker`.
The container is defined in `flake.nix`.
Every `30 min`, it runs the script `hosting/update.sh`, which git-pulls this repo to update its scripts, then executes `scripts/update-apks.sh` and if any apks have been added or removed, it runs `fdroid update` to re-build the repo, which is located in the `fdroid` directory.
This repo is served by a `caddy` server from the container on port `:8080`.
### How to build and deploy
To build the container run:
```sh
nix build .#container
```
To load the resulting `gitlab.julian-mutter.de/julian/fdroid-frajul:latest` container into the local docker daemon, run:
```sh
docker load < result
```
For building and deploying the container to the remote registry, simply run the following command, which is provided by the `flake.nix` dev shell:
```sh
deploy
```
## Container configuration
### Environment variables
- *SOPS_AGE_KEY*
The files `fdroid/config.yml` and `fdroid/keystore.p12` are encrypted with `sops`.
The update script automatically decrypts them on the first start of the container.
For this to work the environment variable `SOPS_AGE_KEY` must be set to the age key used for encrypting.
This key can be generated by running `age-keygen -o key.txt`.
### External volumes
- */apks*
The update script downloads new apk versions into the `/apks` directory.
The apks in there are synced on each update run to the `/src/code/fdroid/repo` directory, where they are served from.
Therefore, the `/apks` directory is the perfect place to mount an external volume to persist all apks and avoid re-downloading on container restart.
This also allows manually placing apks to be served into this directory.
## Apk update scripts
To automatically pull new versions of an apk, create a bash script in the `scripts/apk-update-scripts` directory.
This script should be idempotent and download all available apk versions with differring file names into the `/apks` directory, if not already present.
All these scripts are automatically run regularly by `scripts/update-apks.sh`.
-102
View File
@@ -1,102 +0,0 @@
{
"nodes": {
"devenv": {
"locked": {
"dir": "src/modules",
"lastModified": 1781195293,
"narHash": "sha256-C9OFghpvf3RzK2rGsZjjNNrTrHgFOecEkpDhFnU4QGs=",
"owner": "cachix",
"repo": "devenv",
"rev": "5f5109c83854577191634f7b86fc6e0c8fd44964",
"type": "github"
},
"original": {
"dir": "src/modules",
"owner": "cachix",
"repo": "devenv",
"type": "github"
}
},
"mk-shell-bin": {
"locked": {
"lastModified": 1677004959,
"narHash": "sha256-/uEkr1UkJrh11vD02aqufCxtbF5YnhRTIKlx5kyvf+I=",
"owner": "rrbutani",
"repo": "nix-mk-shell-bin",
"rev": "ff5d8bd4d68a347be5042e2f16caee391cd75887",
"type": "github"
},
"original": {
"owner": "rrbutani",
"repo": "nix-mk-shell-bin",
"type": "github"
}
},
"nix2container": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1775487831,
"narHash": "sha256-2lguQpLPQaxpQCJjXhmEEAfabwsAhkP29Z7fgLzHARA=",
"owner": "nlewo",
"repo": "nix2container",
"rev": "76be9608a7f4d6c985d28b0e7be903ae2547df3e",
"type": "github"
},
"original": {
"owner": "nlewo",
"repo": "nix2container",
"type": "github"
}
},
"nixpkgs": {
"inputs": {
"nixpkgs-src": "nixpkgs-src"
},
"locked": {
"lastModified": 1778507786,
"narHash": "sha256-HzSQCKMsMr8r55LwM1JuzIOB+8bzk0FEv6sItKvsfoY=",
"owner": "cachix",
"repo": "devenv-nixpkgs",
"rev": "8f24a228a782e24576b155d1e39f0d914b380691",
"type": "github"
},
"original": {
"owner": "cachix",
"ref": "rolling",
"repo": "devenv-nixpkgs",
"type": "github"
}
},
"nixpkgs-src": {
"flake": false,
"locked": {
"lastModified": 1778274207,
"narHash": "sha256-I4puXmX1iovcCHZlRmztO3vW0mAbbRvq4F8wgIMQ1MM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b3da656039dc7a6240f27b2ef8cc6a3ef3bccae7",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"devenv": "devenv",
"mk-shell-bin": "mk-shell-bin",
"nix2container": "nix2container",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
-41
View File
@@ -1,41 +0,0 @@
{pkgs, ...}: {
packages = with pkgs; [
coreutils
curl
fdroidserver
sops
git
cacert
jq
];
scripts = {
deploy-docker.exec = ''
devenv container copy processes
'';
};
services.nginx = {
enable = true;
httpConfig = builtins.readFile ./hosting/nginx-http.conf;
};
processes.updater.exec = ''
while true; do
${pkgs.writeShellScript "run-updater" (builtins.readFile ./hosting/update.sh)}
echo "Sleeping for 30 minutes..."
sleep 1800
done
'';
# environment variables for sops
# SOPS_PGP_FP
containers."processes" = {
name = "gitlab.julian-mutter.de/julian/fdroid-frajul";
registry = "docker://";
copyToRoot = []; # avoid copying this repo
maxLayers = 10;
};
}
-10
View File
@@ -1,10 +0,0 @@
inputs:
mk-shell-bin:
url: github:rrbutani/nix-mk-shell-bin
nix2container:
url: github:nlewo/nix2container
inputs:
nixpkgs:
follows: nixpkgs
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling
Generated
+27
View File
@@ -0,0 +1,27 @@
{
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1781577229,
"narHash": "sha256-lrp67w8AulE9Ks53n27I45ADSzbOCn4H+CNW1Ck8B+8=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "567a49d1913ce81ac6e9582e3553dd90a955875f",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}
+91
View File
@@ -0,0 +1,91 @@
{
description = "Docker image with Nginx, F-Droid, and a 30-minute periodic task";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
};
outputs = {nixpkgs, ...}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
periodicScript = pkgs.writeShellScript "run-update" (builtins.readFile ./hosting/update.sh);
caddyConfig = ./hosting/Caddyfile;
entrypoint = pkgs.writeShellScript "entrypoint" ''
# Ensure necessary directories exist
mkdir -p /repo/fdroid/repo /tmp/caddy_data /tmp/caddy_config
# Start the background loop (1800 seconds = 30 minutes)
echo "Starting 30-minute background loop..."
while true; do
${periodicScript}
${pkgs.coreutils}/bin/sleep 1800
done &
# Set environment variables so Caddy writes its state to /tmp instead of /root
export XDG_DATA_HOME=/tmp/caddy_data
export XDG_CONFIG_HOME=/tmp/caddy_config
# Start Caddy in the foreground
echo "Starting Caddy..."
exec ${pkgs.caddy}/bin/caddy run --config ${caddyConfig} --adapter caddyfile
'';
deploy-script = pkgs.writeShellScriptBin "deploy" ''
${pkgs.nix}/bin/nix build .#container
${pkgs.skopeo}/bin/skopeo copy \
--registries-conf /dev/null \
docker-archive:result \
docker://gitlab.julian-mutter.de/julian/fdroid-frajul:latest
'';
dockerImage = pkgs.dockerTools.buildLayeredImage {
name = "gitlab.julian-mutter.de/julian/fdroid-frajul";
tag = "latest";
contents = with pkgs; [
dockerTools.fakeNss # Provides fake /etc/passwd for basic user emulation
dockerTools.usrBinEnv
dockerTools.binSh
dockerTools.caCertificates
coreutils
bashInteractive
caddy
fdroidserver
jq
curl
sops
git
rsync
busybox
];
config = {
Cmd = ["${entrypoint}"];
WorkingDir = "/src";
ExposedPorts = {
"8080/tcp" = {};
};
Env = [
"PATH=/bin:${pkgs.jdk21_headless}/bin"
];
};
maxLayers = 10;
};
in {
# nix build .#container
# docker load < result
packages.${system}.container = dockerImage;
# deploy
devShells.${system}.default = pkgs.mkShell {
packages = [
deploy-script
pkgs.skopeo
];
};
};
}
+12
View File
@@ -0,0 +1,12 @@
:8080 {
# 1. Exact match redirect from root to the repo
redir / /fdroid/repo/ 302
# 2. Redirect without trailing slash to ensure correct routing
redir /fdroid/repo /fdroid/repo/ 302
handle_path /fdroid/repo/* {
root * /src/code/fdroid/repo
file_server
}
}
-4
View File
@@ -1,4 +0,0 @@
#!/usr/bin/env sh
docker build . -t gitlab.julian-mutter.de/julian/fdroid-frajul:latest
docker push gitlab.julian-mutter.de/julian/fdroid-frajul:latest
-13
View File
@@ -1,13 +0,0 @@
server {
listen 8080;
server_name = fdroid.julian-mutter.de;
location = / {
return 302 /fdroid/repo;
}
location /fdroid/repo {
alias /repo/fdroid/repo/;
autoindex on;
}
}
+3 -1
View File
@@ -14,12 +14,13 @@ if [ ! -d "./code/.git" ]; then
cd ./code cd ./code
./scripts/decrypt.sh ./scripts/decrypt.sh
echo "Done" echo "Done"
else
cd "./code"
fi fi
echo "Running regular repo update..." echo "Running regular repo update..."
echo "" echo ""
cd "./code"
git fetch origin git fetch origin
LOCAL=$(git rev-parse HEAD) LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse "origin/$BRANCH") REMOTE=$(git rev-parse "origin/$BRANCH")
@@ -50,6 +51,7 @@ fi
# Run fdroid update if needed # Run fdroid update if needed
if [ "$NEED_FDROID_UPDATE" = true ]; then if [ "$NEED_FDROID_UPDATE" = true ]; then
echo "Running fdroid update..." echo "Running fdroid update..."
cd fdroid
fdroid update -c fdroid update -c
fdroid update fdroid update
echo "Done" echo "Done"
+3 -5
View File
@@ -9,13 +9,12 @@ REPO_OWNER="julian"
REPO_NAME="sheetless" REPO_NAME="sheetless"
APP_NAME="de.frajul.sheetless" APP_NAME="de.frajul.sheetless"
TARGET_ASSET_NAME="app-release.apk" TARGET_ASSET_NAME="app-release.apk"
FDROID_REPO_DIR="./fdroid/repo" APK_DIR="/apks"
FDROID_ARCHIVE_DIR="./fdroid/archive"
# ========================================== # ==========================================
# SETUP & API CALL # SETUP & API CALL
# ========================================== # ==========================================
mkdir -p "$FDROID_REPO_DIR" mkdir -p "$APK_DIR"
API_URL="${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases" API_URL="${GITEA_URL}/api/v1/repos/${REPO_OWNER}/${REPO_NAME}/releases"
@@ -44,8 +43,7 @@ while IFS=$'\t' read -r TAG DOWNLOAD_URL; do
# Skip empty lines if jq returns nothing # Skip empty lines if jq returns nothing
[[ -z "$TAG" ]] && continue [[ -z "$TAG" ]] && continue
LOCAL_FILE="$FDROID_REPO_DIR/${APP_NAME}_${TAG}.apk" LOCAL_FILE="$APK_DIR/${APP_NAME}_${TAG}.apk"
ARCHIVE_FILE="$FDROID_ARCHIVE_DIR/${APP_NAME}_${TAG}.apk"
echo "Checking release: $TAG..." echo "Checking release: $TAG..."
+15 -14
View File
@@ -1,7 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CHANGES_PRODUCED=false
# Put here all apk update scripts # Put here all apk update scripts
UPDATE_SCRIPTS=( UPDATE_SCRIPTS=(
"./scripts/apk-update-scripts/sheetless.sh" "./scripts/apk-update-scripts/sheetless.sh"
@@ -13,20 +11,23 @@ for script in "${UPDATE_SCRIPTS[@]}"; do
# Run the script # Run the script
$script $script
# Capture the exit code immediately
if [ $? -eq 10 ]; then
echo " -> $script returned 10 (change)"
CHANGES_PRODUCED=true
fi
done done
echo "--------------------------------" # Sync all apks from the mounted dir to dest
SOURCE="/apks/"
DEST="/src/code/fdroid/repo/"
if [ "$CHANGES_PRODUCED" = true ]; then HASH_BEFORE=$(ls -l "$DEST" | sha256sum)
echo "Result: At least one script produced a change"
exit 10 echo "Syncing apks in directory $DEST with $SOURCE..."
else rsync -rv --include="*.apk" --exclude="*" --delete "$SOURCE" "$DEST"
echo "Result: Not script produced a change"
HASH_AFTER=$(ls -l "$DEST" | sha256sum)
if [ "$HASH_BEFORE" == "$HASH_AFTER" ]; then
echo "No APKs were added or removed. Skipping F-Droid update."
exit 0 exit 0
else
echo "Changes detected! Files were copied or deleted."
exit 10
fi fi