1
0
Fork 0

module draft

This commit is contained in:
Lukas Wurzinger 2025-06-01 23:22:42 +02:00
parent e2e0f134da
commit 22c105586b
No known key found for this signature in database
4 changed files with 339 additions and 54 deletions

105
flake.lock generated
View file

@ -1,5 +1,21 @@
{
"nodes": {
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1696426674,
"narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=",
"owner": "edolstra",
"repo": "flake-compat",
"rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
"type": "github"
},
"original": {
"owner": "edolstra",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
@ -18,13 +34,56 @@
"type": "github"
}
},
"gitignore": {
"inputs": {
"nixpkgs": [
"hooks",
"nixpkgs"
]
},
"locked": {
"lastModified": 1709087332,
"narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=",
"owner": "hercules-ci",
"repo": "gitignore.nix",
"rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "gitignore.nix",
"type": "github"
}
},
"hooks": {
"inputs": {
"flake-compat": "flake-compat",
"gitignore": "gitignore",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1747372754,
"narHash": "sha256-2Y53NGIX2vxfie1rOW0Qb86vjRZ7ngizoo+bnXU9D9k=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "80479b6ec16fefd9c1db3ea13aeb038c60530f46",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1745526057,
"narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=",
"lastModified": 1748460289,
"narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b",
"rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
"type": "github"
},
"original": {
@ -62,11 +121,11 @@
]
},
"locked": {
"lastModified": 1744599653,
"narHash": "sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU=",
"lastModified": 1748562898,
"narHash": "sha256-STk4QklrGpM3gliPKNJdBLSQvIrqRuwHI/rnYb/5rh8=",
"owner": "pyproject-nix",
"repo": "build-system-pkgs",
"rev": "7dba6dbc73120e15b558754c26024f6c93015dd7",
"rev": "33bd58351957bb52dd1700ea7eeefe34de06a892",
"type": "github"
},
"original": {
@ -82,11 +141,11 @@
]
},
"locked": {
"lastModified": 1743438845,
"narHash": "sha256-1GSaoubGtvsLRwoYwHjeKYq40tLwvuFFVhGrG8J9Oek=",
"lastModified": 1746540146,
"narHash": "sha256-QxdHGNpbicIrw5t6U3x+ZxeY/7IEJ6lYbvsjXmcxFIM=",
"owner": "pyproject-nix",
"repo": "pyproject.nix",
"rev": "8063ec98edc459571d042a640b1c5e334ecfca1e",
"rev": "e09c10c24ebb955125fda449939bfba664c467fd",
"type": "github"
},
"original": {
@ -98,12 +157,34 @@
"root": {
"inputs": {
"flake-parts": "flake-parts",
"hooks": "hooks",
"nixpkgs": "nixpkgs",
"pyproject-build-systems": "pyproject-build-systems",
"pyproject-nix": "pyproject-nix",
"treefmt": "treefmt",
"uv2nix": "uv2nix"
}
},
"treefmt": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1748243702,
"narHash": "sha256-9YzfeN8CB6SzNPyPm2XjRRqSixDopTapaRsnTpXUEY8=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "1f3f7b784643d488ba4bf315638b2b0a4c5fb007",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"uv2nix": {
"inputs": {
"nixpkgs": [
@ -114,11 +195,11 @@
]
},
"locked": {
"lastModified": 1745697651,
"narHash": "sha256-r4A/fkiCenEapHkjJWPiNUZEfviuXMCr6mRozJ5dC4o=",
"lastModified": 1748398512,
"narHash": "sha256-99mf47Kjl/rj716cSjeA6ubZLlhNudmC4HRg/6UMfvs=",
"owner": "pyproject-nix",
"repo": "uv2nix",
"rev": "cb6508484d534dafd097713b575f2aebc3417de0",
"rev": "f006d191d4ff5894d2ead6299e2eaf3659bc46b0",
"type": "github"
},
"original": {

View file

@ -4,6 +4,14 @@
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
hooks = {
url = "github:cachix/git-hooks.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
treefmt = {
url = "github:numtide/treefmt-nix";
inputs.nixpkgs.follows = "nixpkgs";
};
pyproject-nix = {
url = "github:pyproject-nix/pyproject.nix";
inputs.nixpkgs.follows = "nixpkgs";
@ -21,29 +29,41 @@
};
};
outputs = {
outputs =
{
self,
nixpkgs,
flake-parts,
hooks,
treefmt,
uv2nix,
pyproject-nix,
pyproject-build-systems,
...
} @ inputs: let
}@inputs:
let
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
overlay = workspace.mkPyprojectOverlay {
sourcePreference = "wheel";
};
in
flake-parts.lib.mkFlake { inherit inputs; } {
systems = ["x86_64-linux" "aarch64-linux"];
imports = [
hooks.flakeModule
treefmt.flakeModule
];
perSystem = {
systems = nixpkgs.lib.systems.flakeExposed;
perSystem =
{
config,
pkgs,
inputs',
lib,
...
}: let
}:
let
python = pkgs.python313;
pythonSet =
(pkgs.callPackage pyproject-nix.build.packages {
@ -55,7 +75,23 @@
overlay
]
);
in {
in
{
treefmt = {
projectRootFile = "flake.nix";
programs.nixfmt = {
enable = true;
package = pkgs.nixfmt-rfc-style;
};
programs.ruff.enable = true;
};
pre-commit.settings.hooks = {
treefmt.enable = true;
};
devShells.default = pkgs.mkShell {
packages = [
python
@ -71,11 +107,14 @@
LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1;
};
shellHook = ''
${config.pre-commit.installationScript}
unset PYTHONPATH
'';
};
packages.default = pythonSet.mkVirtualEnv "forgesync" workspace.deps.default;
};
flake.nixosModules.default = import ./module.nix self;
};
}

167
module.nix Normal file
View file

@ -0,0 +1,167 @@
self:
{
lib,
pkgs,
utils,
config,
...
}:
let
cfg = config.services.forgesync;
inherit (lib) types;
inherit (utils.systemdUtils.unitOptions) unitOption;
in
{
options.services.forgesync = {
enable = lib.mkEnableOption "Forgesync";
package = lib.mkPackageOption self.packages.${pkgs.system} "default" { };
jobs = lib.mkOption {
description = ''
Synchronization jobs to run.
'';
default = { };
type = types.attrsOf (
types.submodule {
options = {
settings = lib.mkOption {
default = { };
example = {
from-instance = "https://codeberg.org/api/v1";
to = "github";
to-instance = "https://api.github.com";
remirror = true;
description-template = "{description} (Mirror of {url})";
mirror-interval = "8h0m0s";
immediate = true;
log = "INFO";
};
description = ''
Settings for this Forgesync job.
'';
type =
let
simples = [
types.bool
types.str
types.int
types.float
];
in
types.attrsOf (
types.oneOf (
simples
++ [
(types.listOf (types.oneOf simples))
]
)
);
};
secretFile = lib.mkOption {
type = types.path;
description = ''
The EnvironmentFile for secrets required for Forgesync: `FROM_TOKEN`, `TO_TOKEN` and `MIRROR_TOKEN`.
'';
};
timerConfig = lib.mkOption {
type = types.nullOr (types.attrsOf unitOption);
default = {
OnCalendar = "daily";
Persistent = true;
};
description = ''
When to run the job.
'';
};
inhibit = lib.mkOption {
default = [ ];
type = types.listOf (types.strMatching "^[^:]+$");
example = [
"sleep"
];
description = ''
Run the Forgesync process with an inhibition lock taken;
see {manpage}`systemd-inhibit(1)` for a list of possible operations.
'';
};
};
}
);
};
};
config = lib.mkIf cfg.enable {
systemd = lib.mkMerge (
lib.mapAttrsToList (
jobName: job:
let
unitName = "forgesync-job-${jobName}";
description = "Forgesync job ${jobName}";
in
{
timers.${unitName} = {
wantedBy = [ "timers.target" ];
inherit description;
inherit (job) timerConfig;
};
services.${unitName} = {
after = [ "network.target" ];
inherit description;
serviceConfig = {
Type = "oneshot";
DynamicUser = true;
ExecStart =
let
inhibitArgs = [
(lib.getExe' config.systemd.package "systemd-inhibit")
"--mode"
"block"
"--who"
description
"--what"
(lib.concatStringsSep ":" job.inhibit)
"--why"
"Scheduled Forgesync job ${jobName}"
"--"
];
args =
(lib.optionals (job.inhibit != [ ]) inhibitArgs)
++ [ (lib.getExe cfg.package) ]
++ (lib.cli.toGNUCommandLine { mkOptionName = k: "--${k}"; } job.settings);
in
utils.escapeSystemdExecArgs args;
EnvironmentFile = job.secretFile;
NoNewPrivileges = true;
PrivateDevices = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectControlGroups = true;
MemoryDenyWriteExecute = true;
LockPersonality = true;
RestrictAddressFamilies = [
"AF_INET"
"AF_INET6"
];
DevicePolicy = "closed";
RestrictNamespaces = true;
RestrictRealtime = true;
RestrictSUIDSGID = true;
};
};
}
) cfg.jobs
);
};
}

View file

@ -20,11 +20,9 @@ class PushMirrorConfig:
def overlay(self: Self, other: Self) -> Self:
result = type(self)()
for f in fields(self):
value = ( # pyright: ignore[reportAny]
getattr(other, f.name)
if getattr(other, f.name) is not None
else getattr(self, f.name)
)
other_attr = getattr(other, f.name) # pyright: ignore[reportAny]
self_attr = getattr(self, f.name) # pyright: ignore[reportAny]
value = other_attr if other_attr is not None else self_attr # pyright: ignore[reportAny]
setattr(result, f.name, value)
return result