{ lib, config, pkgs, ... }: let cfg = config.bagel.nixpkgs.one-way-sync; inherit (lib) mkIf mkOption mkEnableOption types mapAttrs'; mkSyncTimer = name: { timer, ... }: { wantedBy = [ "timers.target" ]; timerConfig = { OnCalendar = timer; Persistent = true; Unit = "ows-${name}.service"; }; }; mkSyncService = name: { fromUri, fromRefspec, localRefspec, ... }: { path = [ pkgs.gitFull pkgs.openssh pkgs.lix ]; script = '' set -xe RUNTIME_DIRECTORY="/run/onewaysync-${name}" trap "git worktree remove -f "$RUNTIME_DIRECTORY"/${name}" EXIT if [ ! -d "/var/lib/onewaysync/nixpkgs" ]; then echo "First run, synchronizing nixpkgs..." git clone https://cl.forkos.org/nixpkgs /var/lib/onewaysync/nixpkgs fi cd /var/lib/onewaysync/nixpkgs echo "Syncing ${fromUri}:${fromRefspec} to ${cfg.pushUrl}:refs/heads/${localRefspec}" echo "Current ref: $EXPECTED_REF" git worktree add -f "$RUNTIME_DIRECTORY"/${name} refs/remotes/origin/${localRefspec} cd "$RUNTIME_DIRECTORY"/${name} git pull origin ${localRefspec} --no-rebase EXPECTED_REF=$(git rev-list refs/remotes/origin/${localRefspec} | head -1) git config user.name Fork-o-Tron git config user.email noreply@forkos.org git fetch ${fromUri} ${fromRefspec} '' + lib.optionalString (!(lib.hasInfix "staging" localRefspec)) '' OLD_STDENV=$(nix eval -f . stdenv.outPath --store "$RUNTIME_DIRECTORY") '' + '' git merge FETCH_HEAD '' + lib.optionalString (!(lib.hasInfix "staging" localRefspec)) '' NEW_STDENV=$(nix eval -f . stdenv.outPath --store "$RUNTIME_DIRECTORY") # Do not allow auto-merging a staging iteration test "$OLD_STDENV" = "$NEW_STDENV" '' + '' GIT_SSH_COMMAND='ssh -i ${cfg.deployKeyPath}' git push ${cfg.pushUrl} HEAD:refs/heads/${localRefspec} ''; serviceConfig = { User = "git"; Group = "git"; Type = "oneshot"; RuntimeDirectory = "onewaysync-${name}"; WorkingDirectory = "/run/onewaysync-${name}"; StateDirectory = "onewaysync"; }; }; in { options.bagel.nixpkgs.one-way-sync = { enable = mkEnableOption "the one-way sync from GitHub repositories"; referenceDir = mkOption { type = types.str; default = "/var/lib/gerrit/git/nixpkgs.git"; description = "Local repository reference"; }; workingDir = mkOption { type = types.str; default = "/run/onewaysync/"; description = "Working directory for the service"; }; stateDirectory = mkOption { type = types.str; default = "/var/lib/onewaysync"; description = "State directory where the copies of nixpkgs are stored"; }; pushUrl = mkOption { type = types.str; example = "ssh://..."; description = "Push URL for the target repository"; }; deployKeyPath = mkOption { type = types.path; example = "/run/agenix.d/ows-priv-key"; description = "Deployment private SSH key to push to the repository"; }; branches = mkOption { type = types.attrsOf (types.submodule ({ ... }: { options = { name = mkOption { type = types.str; description = "User-friendly name"; }; fromUri = mkOption { type = types.str; description = "Git URI from which we need to sync"; }; fromRefspec = mkOption { type = types.str; description = "refspec for the fetch"; }; localRefspec = mkOption { type = types.str; default = "local refspec in the local repository to get the expected reference and avoid stale info"; }; timer = mkOption { type = types.str; description = "Calendar format everytime we need to run the sync"; }; }; })); description = "Set of branches mapping from cl.forkos.org to other Git repositories"; }; }; config = mkIf cfg.enable { fileSystems."/var/lib/onewaysync" = mkIf (cfg.stateDirectory != "/var/lib/onewaysync") { device = cfg.stateDirectory; options = [ "bind" ]; }; systemd.timers = mapAttrs' (name: value: { name = "ows-${name}"; value = mkSyncTimer name value; }) cfg.branches; systemd.services = mapAttrs' (name: value: { name = "ows-${name}"; value = mkSyncService name value; }) cfg.branches; }; }