{ 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 = targetRef: { name, fromUri, fromRefspec, localRefspec, ... }: { path = [ pkgs.gitFull pkgs.openssh pkgs.lix ]; script = '' set -xe trap "git worktree prune && git worktree remove -f ${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 /var/lib/onewaysync/nixpkgs:${targetRef}" echo "Current ref: $EXPECTED_REF" git worktree add -f ${cfg.workingDir}/${name} ${localRefspec} cd ${cfg.workingDir}/${name} git pull origin ${fromRefspec} EXPECTED_REF=$(git rev-list ${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 /run/onewaysync) '' + '' git merge FETCH_HEAD '' + lib.optionalString (!(lib.hasInfix "staging" localRefspec)) '' NEW_STDENV=$(nix eval -f . stdenv.outPath --store /run/onewaysync) # 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:${targetRef} ''; serviceConfig = { User = "git"; Group = "git"; Type = "oneshot"; RuntimeDirectory = "onewaysync"; WorkingDirectory = cfg.workingDir; 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"; }; 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 { systemd.timers = mapAttrs' (name: value: { name = "ows-${value.name}"; value = mkSyncTimer value; }) cfg.branches; systemd.services = mapAttrs' (name: value: { name = "ows-${value.name}"; value = mkSyncService name value; }) cfg.branches; }; }