module draft
This commit is contained in:
parent
e2e0f134da
commit
22c105586b
4 changed files with 339 additions and 54 deletions
105
flake.lock
generated
105
flake.lock
generated
|
@ -1,5 +1,21 @@
|
||||||
{
|
{
|
||||||
"nodes": {
|
"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": {
|
"flake-parts": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs-lib": "nixpkgs-lib"
|
"nixpkgs-lib": "nixpkgs-lib"
|
||||||
|
@ -18,13 +34,56 @@
|
||||||
"type": "github"
|
"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": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1745526057,
|
"lastModified": 1748460289,
|
||||||
"narHash": "sha256-ITSpPDwvLBZBnPRS2bUcHY3gZSwis/uTe255QgMtTLA=",
|
"narHash": "sha256-7doLyJBzCllvqX4gszYtmZUToxKvMUrg45EUWaUYmBg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "f771eb401a46846c1aebd20552521b233dd7e18b",
|
"rev": "96ec055edbe5ee227f28cdbc3f1ddf1df5965102",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -62,11 +121,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744599653,
|
"lastModified": 1748562898,
|
||||||
"narHash": "sha256-nysSwVVjG4hKoOjhjvE6U5lIKA8sEr1d1QzEfZsannU=",
|
"narHash": "sha256-STk4QklrGpM3gliPKNJdBLSQvIrqRuwHI/rnYb/5rh8=",
|
||||||
"owner": "pyproject-nix",
|
"owner": "pyproject-nix",
|
||||||
"repo": "build-system-pkgs",
|
"repo": "build-system-pkgs",
|
||||||
"rev": "7dba6dbc73120e15b558754c26024f6c93015dd7",
|
"rev": "33bd58351957bb52dd1700ea7eeefe34de06a892",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -82,11 +141,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1743438845,
|
"lastModified": 1746540146,
|
||||||
"narHash": "sha256-1GSaoubGtvsLRwoYwHjeKYq40tLwvuFFVhGrG8J9Oek=",
|
"narHash": "sha256-QxdHGNpbicIrw5t6U3x+ZxeY/7IEJ6lYbvsjXmcxFIM=",
|
||||||
"owner": "pyproject-nix",
|
"owner": "pyproject-nix",
|
||||||
"repo": "pyproject.nix",
|
"repo": "pyproject.nix",
|
||||||
"rev": "8063ec98edc459571d042a640b1c5e334ecfca1e",
|
"rev": "e09c10c24ebb955125fda449939bfba664c467fd",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -98,12 +157,34 @@
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
|
"hooks": "hooks",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"pyproject-build-systems": "pyproject-build-systems",
|
"pyproject-build-systems": "pyproject-build-systems",
|
||||||
"pyproject-nix": "pyproject-nix",
|
"pyproject-nix": "pyproject-nix",
|
||||||
|
"treefmt": "treefmt",
|
||||||
"uv2nix": "uv2nix"
|
"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": {
|
"uv2nix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
|
@ -114,11 +195,11 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1745697651,
|
"lastModified": 1748398512,
|
||||||
"narHash": "sha256-r4A/fkiCenEapHkjJWPiNUZEfviuXMCr6mRozJ5dC4o=",
|
"narHash": "sha256-99mf47Kjl/rj716cSjeA6ubZLlhNudmC4HRg/6UMfvs=",
|
||||||
"owner": "pyproject-nix",
|
"owner": "pyproject-nix",
|
||||||
"repo": "uv2nix",
|
"repo": "uv2nix",
|
||||||
"rev": "cb6508484d534dafd097713b575f2aebc3417de0",
|
"rev": "f006d191d4ff5894d2ead6299e2eaf3659bc46b0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|
113
flake.nix
113
flake.nix
|
@ -4,6 +4,14 @@
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
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 = {
|
pyproject-nix = {
|
||||||
url = "github:pyproject-nix/pyproject.nix";
|
url = "github:pyproject-nix/pyproject.nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
@ -21,42 +29,70 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs =
|
||||||
self,
|
{
|
||||||
nixpkgs,
|
self,
|
||||||
flake-parts,
|
nixpkgs,
|
||||||
uv2nix,
|
flake-parts,
|
||||||
pyproject-nix,
|
hooks,
|
||||||
pyproject-build-systems,
|
treefmt,
|
||||||
...
|
uv2nix,
|
||||||
} @ inputs: let
|
pyproject-nix,
|
||||||
workspace = uv2nix.lib.workspace.loadWorkspace {workspaceRoot = ./.;};
|
pyproject-build-systems,
|
||||||
overlay = workspace.mkPyprojectOverlay {
|
...
|
||||||
sourcePreference = "wheel";
|
}@inputs:
|
||||||
};
|
let
|
||||||
in
|
workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
|
||||||
flake-parts.lib.mkFlake {inherit inputs;} {
|
overlay = workspace.mkPyprojectOverlay {
|
||||||
systems = ["x86_64-linux" "aarch64-linux"];
|
sourcePreference = "wheel";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||||
|
imports = [
|
||||||
|
hooks.flakeModule
|
||||||
|
treefmt.flakeModule
|
||||||
|
];
|
||||||
|
|
||||||
perSystem = {
|
systems = nixpkgs.lib.systems.flakeExposed;
|
||||||
pkgs,
|
|
||||||
inputs',
|
perSystem =
|
||||||
lib,
|
{
|
||||||
...
|
config,
|
||||||
}: let
|
pkgs,
|
||||||
python = pkgs.python313;
|
inputs',
|
||||||
pythonSet =
|
lib,
|
||||||
(pkgs.callPackage pyproject-nix.build.packages {
|
...
|
||||||
inherit python;
|
}:
|
||||||
}).overrideScope
|
let
|
||||||
(
|
python = pkgs.python313;
|
||||||
lib.composeManyExtensions [
|
pythonSet =
|
||||||
pyproject-build-systems.overlays.default
|
(pkgs.callPackage pyproject-nix.build.packages {
|
||||||
overlay
|
inherit python;
|
||||||
]
|
}).overrideScope
|
||||||
);
|
(
|
||||||
in {
|
lib.composeManyExtensions [
|
||||||
devShells.default = pkgs.mkShell {
|
pyproject-build-systems.overlays.default
|
||||||
|
overlay
|
||||||
|
]
|
||||||
|
);
|
||||||
|
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 = [
|
packages = [
|
||||||
python
|
python
|
||||||
pkgs.libffi
|
pkgs.libffi
|
||||||
|
@ -71,11 +107,14 @@
|
||||||
LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1;
|
LD_LIBRARY_PATH = lib.makeLibraryPath pkgs.pythonManylinuxPackages.manylinux1;
|
||||||
};
|
};
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
|
${config.pre-commit.installationScript}
|
||||||
unset PYTHONPATH
|
unset PYTHONPATH
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
packages.default = pythonSet.mkVirtualEnv "forgesync" workspace.deps.default;
|
packages.default = pythonSet.mkVirtualEnv "forgesync" workspace.deps.default;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
flake.nixosModules.default = import ./module.nix self;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
167
module.nix
Normal file
167
module.nix
Normal 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
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
|
@ -20,11 +20,9 @@ class PushMirrorConfig:
|
||||||
def overlay(self: Self, other: Self) -> Self:
|
def overlay(self: Self, other: Self) -> Self:
|
||||||
result = type(self)()
|
result = type(self)()
|
||||||
for f in fields(self):
|
for f in fields(self):
|
||||||
value = ( # pyright: ignore[reportAny]
|
other_attr = getattr(other, f.name) # pyright: ignore[reportAny]
|
||||||
getattr(other, f.name)
|
self_attr = getattr(self, f.name) # pyright: ignore[reportAny]
|
||||||
if getattr(other, f.name) is not None
|
value = other_attr if other_attr is not None else self_attr # pyright: ignore[reportAny]
|
||||||
else getattr(self, f.name)
|
|
||||||
)
|
|
||||||
setattr(result, f.name, value)
|
setattr(result, f.name, value)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue