Compare commits

..

1 commit

Author SHA1 Message Date
a89ca47df7
feat(activation): Use lix-diff instead of nvd
Some checks failed
Check meta / check_dns (pull_request) Successful in 15s
Check meta / check_meta (pull_request) Successful in 17s
Check workflows / check_workflows (pull_request) Successful in 19s
Build all the nodes / Jaccess01 (pull_request) Successful in 23s
Build all the nodes / Jaccess04 (pull_request) Successful in 25s
Run pre-commit on all files / pre-commit (pull_request) Successful in 29s
Build all the nodes / ap01 (pull_request) Successful in 42s
Build all the nodes / hypervisor02 (pull_request) Failing after 46s
Build all the nodes / netcore01 (pull_request) Successful in 26s
Build all the nodes / build01 (pull_request) Failing after 50s
Build all the nodes / hypervisor01 (pull_request) Failing after 50s
Build all the nodes / geo01 (pull_request) Failing after 52s
Build all the nodes / geo02 (pull_request) Failing after 56s
Build all the nodes / hypervisor03 (pull_request) Failing after 45s
Build all the nodes / netcore02 (pull_request) Successful in 22s
Build all the nodes / compute01 (pull_request) Failing after 1m7s
Build all the nodes / bridge01 (pull_request) Successful in 1m19s
Build all the nodes / lab-router01 (pull_request) Successful in 59s
Build all the nodes / iso (pull_request) Successful in 1m11s
Build all the nodes / cof02 (pull_request) Successful in 1m27s
Build the shell / build-shell (pull_request) Successful in 22s
Build all the nodes / tower01 (pull_request) Failing after 43s
Build all the nodes / web02 (pull_request) Failing after 45s
Build all the nodes / web03 (pull_request) Failing after 45s
Build all the nodes / krz01 (pull_request) Failing after 1m28s
Build all the nodes / vault01 (pull_request) Successful in 59s
Build all the nodes / zulip01 (pull_request) Successful in 49s
Build all the nodes / rescue01 (pull_request) Failing after 1m5s
Build all the nodes / web01 (pull_request) Failing after 1m5s
Build all the nodes / storage01 (pull_request) Failing after 1m16s
The colors are better
2025-06-12 23:52:40 +02:00
18 changed files with 691 additions and 264 deletions

View file

@ -191,11 +191,9 @@ in
# Deployment config is specified in meta.nodes.${node}.deployment
inherit (nodeMeta) deployment;
# Set NIX_PATH to the patched version of nixpkgs
environment.etc.nixpkgs.source = builtins.storePath sourcePkgs.path;
nix.nixPath = [ "nixpkgs=/etc/nixpkgs" ];
nix = {
# Set NIX_PATH to the patched version of nixpkgs
nixPath = [ "nixpkgs=${builtins.storePath sourcePkgs.path}" ];
optimise.automatic = true;
gc = {
@ -208,7 +206,14 @@ in
};
# Allow unfree packages
nixpkgs.config.allowUnfree = true;
nixpkgs = {
config.allowUnfree = true;
overlays = [
(self: _: {
lix-diff = self.callPackage (sources.lix-diff + "/package.nix") { };
})
];
};
# Use the stateVersion declared in the metadata
system = {

View file

@ -71,31 +71,12 @@ rec {
src,
name,
patches ? mkPatches name,
prePatch ? null,
postPatch ? null,
...
}@args:
if patches == [ ] && prePatch == null && postPatch == null then
src
else
pkgs.stdenvNoCC.mkDerivation (
args
// {
name = "${name}-patched";
}:
pkgs.applyPatches {
inherit patches src;
inherit patches prePatch postPatch;
preferLocalBuild = true;
allowSubstitutes = true;
phases = [
"unpackPhase"
"patchPhase"
"installPhase"
];
installPhase = "cp -R . $out";
}
);
name = "${name}-patched";
};
applyPatches' = name: src: applyPatches { inherit name src; };
};

46
lon.lock generated
View file

@ -7,9 +7,9 @@
"owner": "ryantm",
"repo": "agenix",
"branch": "main",
"revision": "531beac616433bac6f9e2a19feb8e99a22a66baf",
"url": "https://github.com/ryantm/agenix/archive/531beac616433bac6f9e2a19feb8e99a22a66baf.tar.gz",
"hash": "sha256-9P1FziAwl5+3edkfFcr5HeGtQUtrSdk/MksX39GieoA="
"revision": "4835b1dc898959d8547a871ef484930675cb47f1",
"url": "https://github.com/ryantm/agenix/archive/4835b1dc898959d8547a871ef484930675cb47f1.tar.gz",
"hash": "sha256-NwmAFuDUO/PFcgaGGr4j3ozG9Pe5hZ/ogitWhY+D81k="
},
"arkheon": {
"type": "GitHub",
@ -105,10 +105,10 @@
"type": "Git",
"fetchType": "git",
"branch": "master",
"revision": "3838db6ebbfe5ad9f904ce553543c1c301b67274",
"revision": "19b3de953c4d4e8888b90019db81852f8ad39dbb",
"url": "https://git.dgnum.eu/lbailly/kat-pkgs",
"hash": "sha256-ifgYL9gJ1XKEL45WdFqGM17r5ZUkLnTuV2tGk+ie80I=",
"lastModified": 1750258895,
"hash": "sha256-bWO5dHrwZWF2EbCuSzxigaKkJdNCBQx5nD1J/u2pdNg=",
"lastModified": 1749652165,
"submodules": false
},
"liminix": {
@ -135,12 +135,22 @@
"type": "Git",
"fetchType": "git",
"branch": "main",
"revision": "20fed838a622e48128827278db91312f580f9214",
"revision": "ee0655240270480d7f6063dcf12ec47f04d2ded6",
"url": "https://git.lix.systems/lix-project/lix.git",
"hash": "sha256-Swcajzm+JPDd32kKXdg25im9CeATuY8qji9EPVU2rVo=",
"lastModified": 1750232556,
"hash": "sha256-DDhns3NS6L5OlYR0mSX03I5D7uGLyyd3MZegd1wTCyc=",
"lastModified": 1749682763,
"submodules": false
},
"lix-diff": {
"type": "GitHub",
"fetchType": "tarball",
"owner": "tgirlcloud",
"repo": "lix-diff",
"branch": "main",
"revision": "3d52194bc78f0d7478c6d1a900f2c806c643b995",
"url": "https://github.com/tgirlcloud/lix-diff/archive/3d52194bc78f0d7478c6d1a900f2c806c643b995.tar.gz",
"hash": "sha256-apjYXFdvxLZjhcN1wV7Y/LKNuWtWtCZM0h1VFg/znVo="
},
"lix-module": {
"type": "Git",
"fetchType": "git",
@ -157,9 +167,9 @@
"owner": "nikstur",
"repo": "lon",
"branch": "main",
"revision": "c44e33ce55eed38a06fde43e69512380c4065441",
"url": "https://github.com/nikstur/lon/archive/c44e33ce55eed38a06fde43e69512380c4065441.tar.gz",
"hash": "sha256-bxu83mbdfAeDZYOnjZQYyjTs5WgZS8o6Q2irlzgbYs0="
"revision": "c29151c0adefbf2eef904a3435350356cef98da2",
"url": "https://github.com/nikstur/lon/archive/c29151c0adefbf2eef904a3435350356cef98da2.tar.gz",
"hash": "sha256-1oQ4uLI92Ih2rmNyP4wzP9xZrQp48FHirOhV/aerZPc="
},
"metis": {
"type": "Git",
@ -195,10 +205,10 @@
"type": "Git",
"fetchType": "git",
"branch": "dgnum",
"revision": "fd4ba193ea3eda529ac27b43b206e9e3618b1975",
"revision": "44ccf96bd73c1bbbbcc849cb0f2e0d1f5f75f934",
"url": "https://git.hubrecht.ovh/hubrecht/nix-modules",
"hash": "sha256-O/lMCM0qKkd+TBV43Fp9uG3aEbDSc2lI3a5TetNYs0w=",
"lastModified": 1749739595,
"hash": "sha256-mkrCWowrCje3/TuAG0eAJplrtlz1hYmusSFn93/Ccok=",
"lastModified": 1749629064,
"submodules": false
},
"nix-pkgs": {
@ -285,10 +295,10 @@
"type": "Git",
"fetchType": "git",
"branch": "main",
"revision": "f3d0a3146c64f8fe6bdb208b75cc680c96f524e1",
"revision": "62346b99c2e1085203bc2e5bb5f07e7773977b49",
"url": "https://git.dgnum.eu/DGNum/snix-cache.git",
"hash": "sha256-D6NRGdsIwvXf9MxTR1gFreefBKM3giFh8ggTM6wsh8o=",
"lastModified": 1750061908,
"hash": "sha256-6BYUWwzitWF2EV8wvJOlqensJ3x4f4ka+iZ9Zy5XnWI=",
"lastModified": 1744711329,
"submodules": false
},
"stateless-uptime-kuma": {

View file

@ -28,7 +28,6 @@ lib.extra.mkConfig {
"mastodon"
# "netbox"
"nextcloud"
"nimbolus"
"ollama-proxy"
"opengist"
"outline"

View file

@ -1,43 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
pkgs,
sources,
config,
...
}:
let
host = "nimbolus.dgnum.eu";
port = 9008;
in
{
imports = [ ./module.nix ];
services.nimbolus-tf = {
enable = true;
package = (import sources.kat-pkgs { inherit pkgs; }).nimbolus-tf-backend;
settings = {
LISTEN_ADDR = "127.0.0.1:${toString port}";
STORAGE_BACKEND = "s3";
STORAGE_S3_ENDPOINT = "s3.dgnum.eu";
STORAGE_S3_USE_SSL = "true";
STORAGE_S3_BUCKET = "nimbolus-dgnum";
STORAGE_S3_ACCESS_KEY = "GKefa111701f349de3988f0010";
# TODO: configure openBAO
# AUTH_BASIC_ENABLED = "false";
# AUTH_JWT_OIDC_ISSUER_URL = "https://vault.dgnum.eu/v1/identity/oidc";
};
credentials = {
KMS_KEY_FILE = config.age.secrets."nimbolus-kms_key".path;
STORAGE_S3_SECRET_KEY_FILE = config.age.secrets."nimbolus-s3_secret".path;
};
};
dgn-web.simpleProxies.nimbolus = {
inherit host port;
};
}

View file

@ -1,104 +0,0 @@
# SPDX-FileCopyrightText: 2025 Lubin Bailly <lubin@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{
lib,
config,
sources,
pkgs,
...
}:
let
inherit (lib)
getExe
mapAttrsToList
mkEnableOption
mkIf
mkPackageOption
mkOption
;
inherit (lib.types)
attrsOf
path
str
;
cfg = config.services.nimbolus-tf;
in
{
options.services.nimbolus-tf = {
enable = mkEnableOption "the nimbolus terraform http backend";
package = mkPackageOption (import sources.kat-pkgs { inherit pkgs; }) "nimbolus-tf-backend" {
pkgsText = "kat-pkgs";
};
user = mkOption {
type = str;
description = ''
User used by the nimbolus server.
'';
default = "nimbolus";
};
group = mkOption {
type = str;
description = ''
Group used by the nimbolus server.
'';
default = "nimbolus";
};
settings = mkOption {
type = attrsOf str;
default = { };
description = ''
Environment variables for nimbolus configuration.
'';
};
credentials = mkOption {
type = attrsOf path;
default = { };
description = ''
Files to pass by systemd LoadCredentials.
'';
};
};
config = mkIf cfg.enable {
systemd.services.nimbolus-tf = {
description = "Nimbolus terraform http backend";
wantedBy = [ "multi-user.target" ];
serviceConfig = {
ExecStart = getExe cfg.package;
Environment =
mapAttrsToList (name: value: "${name}=${value}") cfg.settings
++ mapAttrsToList (name: _: "${name}=%d/${name}") cfg.credentials;
LoadCredential = mapAttrsToList (name: file: "${name}:${file}") cfg.credentials;
StateDirectory = "nimbolus-tf";
StateDirectoryMode = "0700";
WorkingDirectory = "/var/lib/nimbolus-tf";
# Hardening
DynamicUser = true;
CapabilityBoundingSet = "";
PrivateDevices = true;
ProtectClock = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
ProtectKernelModules = true;
RestrictNamespaces = true;
ProtectHostname = true;
LockPersonality = true;
RestrictRealtime = true;
ProtectHome = true;
ProtectProc = "noaccess";
ProcSubset = "pid";
PrivateUsers = true;
UMask = "0077";
ProtectKernelTunables = true;
RestrictAddressFamilies = "AF_INET AF_INET6";
SystemCallFilter = "~@clock @cpu-emulation @debug @module @mount @obsolete @privileged @raw-io @reboot @resources @swap";
MemoryDenyWriteExecute = true;
SystemCallArchitectures = "native";
};
};
};
}

View file

@ -25,8 +25,6 @@
"netbox-environment_file"
"nextcloud-adminpass_file"
"nextcloud-s3_secret_file"
"nimbolus-kms_key"
"nimbolus-s3_secret"
"opengist-environment_file"
"outline-oidc_client_secret_file"
"outline-smtp_password_file"

View file

@ -19,10 +19,5 @@ in
};
};
fileSystems."/var/lib/victorialogs" = {
device = "/data/fast/victorialogs";
options = [ "bind" ];
};
networking.firewall.interfaces.wt0.allowedTCPPorts = [ port ];
}

View file

@ -82,7 +82,6 @@ let
"gist" # Opengist
"grafana" # Grafana
"netbox-v2" # Netbox
"nimbolus" # Nimbolus Terraform Backend
"nms" # LibreNMS
"pads" # Hedgedoc
"pass" # Vaultwarden

View file

@ -37,9 +37,8 @@
"dgn-web"
"django-apps"
"extranix"
"forgejo-multiuser-nix-runners"
"openbao"
"systemd-notify"
"forgejo-multiuser-nix-runners"
])
++ [
"${sources.agenix}/modules/age.nix"
@ -53,6 +52,7 @@
"services/forgejo-nix-runners"
"services/nginx-sni"
"services/reaction"
"services/systemd-notify"
"services/victorialogs"
"services/victoriametrics"
]

View file

@ -84,7 +84,7 @@ in
system.activationScripts.diff = {
supportsDryActivation = true;
text = ''
${pkgs.nvd}/bin/nvd --nix-bin-dir=${pkgs.nix}/bin diff /run/current-system "$systemConfig"
${lib.getExe pkgs.lix-diff} --lix-bin ${pkgs.nix}/bin /run/current-system $systemConfig
'';
};
@ -92,6 +92,7 @@ in
environment.systemPackages =
(with pkgs; [
neovim
wget
kitty.terminfo

View file

@ -54,16 +54,19 @@ in
};
services.systemd-notify = {
mail = pkgs.writeShellScriptBin "sendmail" ''
${pkgs.msmtp}/bin/sendmail -i -t <<ERRMAIL
To: admins+monitoring@dgnum.eu, ${emails}
Subject: [$HOSTNAME] Systemd failure: $1
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8
enable = true;
command = builtins.toString (
pkgs.writeShellScript "sendmail" ''
${pkgs.msmtp}/bin/sendmail -i -t <<ERRMAIL
To: admins+monitoring@dgnum.eu, ${emails}
Subject: [$HOSTNAME] Systemd failure: $1
Content-Transfer-Encoding: 8bit
Content-Type: text/plain; charset=UTF-8
$(systemctl status --full "$1")
ERRMAIL
'';
$(systemctl status --full "$1")
ERRMAIL
''
);
};
age-secrets.sources = [ ./. ];
};

View file

@ -1,49 +0,0 @@
# SPDX-FileCopyrightText: 2024 Tom Hubrecht <tom.hubrecht@dgnum.eu>
#
# SPDX-License-Identifier: EUPL-1.2
{ config, lib, ... }:
let
inherit (lib)
getExe
mapAttrs'
mapAttrsToList
mkOption
mkForce
nameValuePair
;
inherit (lib.types) attrsOf package submodule;
cfg = config.services.systemd-notify;
in
{
options.services.systemd-notify = mkOption {
type = attrsOf package;
description = ''
Commands to execute when a systemd unit fails.
Attrs keys will be the unit name and attrs value is the command that
will be run with the name of the failed unit as an argument.
'';
default = { };
};
options.systemd.services = mkOption {
type = attrsOf (submodule {
config.onFailure = mapAttrsToList (name: _: "${name}@%n.service") cfg;
});
};
config.systemd.services = mapAttrs' (
name: script:
nameValuePair "${name}@" {
description = "Run ${name} script on service failures.";
onFailure = mkForce [ ]; # Avoid recursive failures
serviceConfig = {
ExecStart = "${getExe script} %i";
Type = "oneshot";
};
}
) cfg;
}

View file

@ -20,6 +20,10 @@ with {
(local ./lix/01-disable-installChecks.patch)
];
lon = [
(local ./lon/01-npins-import.patch)
];
"nixos-25.05" = [
# Crabfit: don't depend on all google-fonts
(local ./nixpkgs/03-crabfit-karla.patch)

View file

@ -0,0 +1,625 @@
From 70877569a4ce8f5274c5e6208469c240a34993a0 Mon Sep 17 00:00:00 2001
From: Tom Hubrecht <tom@hubrecht.ovh>
Date: Tue, 10 Jun 2025 15:26:22 +0200
Subject: [PATCH 1/2] sources: Find default branch when none is supplied
---
rust/lon/Cargo.lock | 33 +++++++++++++++++++++++++++++++++
rust/lon/Cargo.toml | 1 +
rust/lon/src/cli.rs | 8 ++++----
rust/lon/src/git.rs | 29 +++++++++++++++++++++++++++++
rust/lon/src/init/niv.rs | 4 ++--
rust/lon/src/sources.rs | 18 +++++++++++++++---
6 files changed, 84 insertions(+), 9 deletions(-)
diff --git a/rust/lon/Cargo.lock b/rust/lon/Cargo.lock
index 62f6176..b9e7944 100644
--- a/rust/lon/Cargo.lock
+++ b/rust/lon/Cargo.lock
@@ -17,6 +17,15 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "android-tzdata"
version = "0.1.1"
@@ -847,6 +856,7 @@ dependencies = [
"expect-test",
"indoc",
"log",
+ "regex",
"reqwest",
"serde",
"serde_json",
@@ -1073,11 +1083,34 @@ dependencies = [
"getrandom 0.3.2",
]
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
[[package]]
name = "regex-automata"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]]
name = "reqwest"
diff --git a/rust/lon/Cargo.toml b/rust/lon/Cargo.toml
index a60c24e..d7dd633 100644
--- a/rust/lon/Cargo.toml
+++ b/rust/lon/Cargo.toml
@@ -13,6 +13,7 @@ serde_json = "1.0.140"
sha2 = "0.10.9"
tempfile = "3.20.0"
reqwest = { version = "0.12", default-features = false, features = ["blocking","http2","rustls-tls","json"] }
+regex = "1.11.1"
[dev-dependencies]
expect-test = "1.5.1"
diff --git a/rust/lon/src/cli.rs b/rust/lon/src/cli.rs
index eb850d7..5806b1d 100644
--- a/rust/lon/src/cli.rs
+++ b/rust/lon/src/cli.rs
@@ -105,7 +105,7 @@ struct AddGitArgs {
/// URL to the repository
url: String,
/// Branch to track
- branch: String,
+ branch: Option<String>,
/// Revision to lock
#[arg(short, long)]
revision: Option<String>,
@@ -122,7 +122,7 @@ struct AddGitHubArgs {
/// An identifier made up of {owner}/{repo}, e.g. nixos/nixpkgs
identifier: String,
/// Branch to track
- branch: String,
+ branch: Option<String>,
/// Name of the source
///
/// If you do not supply this, the repository name is used as the source name.
@@ -283,7 +283,7 @@ fn add_git(directory: impl AsRef<Path>, args: &AddGitArgs) -> Result<()> {
let source = GitSource::new(
&args.url,
- &args.branch,
+ args.branch.as_ref(),
args.revision.as_ref(),
args.submodules,
args.frozen,
@@ -314,7 +314,7 @@ fn add_github(directory: impl AsRef<Path>, args: &AddGitHubArgs) -> Result<()> {
let source = GitHubSource::new(
owner,
repo,
- &args.branch,
+ args.branch.as_ref(),
args.revision.as_ref(),
args.frozen,
)?;
diff --git a/rust/lon/src/git.rs b/rust/lon/src/git.rs
index cb5b4df..381c337 100644
--- a/rust/lon/src/git.rs
+++ b/rust/lon/src/git.rs
@@ -5,6 +5,7 @@ use std::{
};
use anyhow::{Context, Result, bail};
+use regex::Regex;
use tempfile::TempDir;
#[derive(Clone, Debug)]
@@ -129,6 +130,34 @@ fn find_newest_revision_for_ref(url: &str, reference: &str) -> Result<Revision>
Ok(Revision(references.remove(0).revision))
}
+/// Find the default branch for a git repository
+pub fn find_default_branch(url: &str) -> Result<String> {
+ let output = Command::new("git")
+ .arg("ls-remote")
+ .args(["--symref", url, "HEAD"])
+ .output()
+ .context("Failed to execute git ls-remote. Most likely it's not on PATH")?;
+
+ if !output.status.success() {
+ bail!(
+ "Failed to find the default branch for {}\n{}",
+ url,
+ String::from_utf8_lossy(&output.stderr)
+ )
+ }
+
+ let re = Regex::new(r"ref:.*refs/heads/(?<branch>.*)\tHEAD")?;
+
+ let Some(branch) = String::from_utf8_lossy(&output.stdout)
+ .lines()
+ .find_map(|x| re.captures(x).map(|matched| matched["branch"].into()))
+ else {
+ bail!("Failed to find the default branch for {url}",)
+ };
+
+ Ok(branch)
+}
+
/// Call `git ls-remote` with the provided args.
fn ls_remote(args: &[&str]) -> Result<Vec<RemoteInfo>> {
let output = Command::new("git")
diff --git a/rust/lon/src/init/niv.rs b/rust/lon/src/init/niv.rs
index 469fdc7..8d41670 100644
--- a/rust/lon/src/init/niv.rs
+++ b/rust/lon/src/init/niv.rs
@@ -42,7 +42,7 @@ impl Convertible for LockFile {
let source = GitHubSource::new(
owner,
&package.repo,
- &package.branch,
+ Some(&package.branch),
Some(&package.rev),
false,
)?;
@@ -51,7 +51,7 @@ impl Convertible for LockFile {
} else {
let source = GitSource::new(
&package.repo,
- &package.branch,
+ Some(&package.branch),
Some(&package.rev),
false,
false,
diff --git a/rust/lon/src/sources.rs b/rust/lon/src/sources.rs
index 92d8c2b..78bdbdb 100644
--- a/rust/lon/src/sources.rs
+++ b/rust/lon/src/sources.rs
@@ -170,11 +170,16 @@ pub struct GitSource {
impl GitSource {
pub fn new(
url: &str,
- branch: &str,
+ branch: Option<&String>,
revision: Option<&String>,
submodules: bool,
frozen: bool,
) -> Result<Self> {
+ let branch = match branch {
+ Some(branch) => branch,
+ None => &git::find_default_branch(url)?,
+ };
+
let rev = match revision {
Some(rev) => rev,
None => &git::find_newest_revision(url, branch)?.to_string(),
@@ -283,13 +288,20 @@ impl GitHubSource {
pub fn new(
owner: &str,
repo: &str,
- branch: &str,
+ branch: Option<&String>,
revision: Option<&String>,
frozen: bool,
) -> Result<Self> {
+ let repo_url = &Self::git_url(owner, repo);
+
+ let branch = match branch {
+ Some(branch) => branch,
+ None => &git::find_default_branch(repo_url)?,
+ };
+
let rev = match revision {
Some(rev) => rev,
- None => &git::find_newest_revision(&Self::git_url(owner, repo), branch)?.to_string(),
+ None => &git::find_newest_revision(repo_url, branch)?.to_string(),
};
log::info!("Locked revision: {rev}");
From eee3871a246605a7ab60714bb193846160ac8e64 Mon Sep 17 00:00:00 2001
From: Tom Hubrecht <tom@hubrecht.ovh>
Date: Tue, 10 Jun 2025 17:25:52 +0200
Subject: [PATCH 2/2] cli: init from npins
We convert three types of pins: `Git`, `GitRelease` and `Channel`
---
rust/lon/src/cli.rs | 13 ++-
rust/lon/src/init.rs | 1 +
rust/lon/src/init/npins.rs | 218 +++++++++++++++++++++++++++++++++++++
rust/lon/tests/npins.json | 86 +++++++++++++++
4 files changed, 312 insertions(+), 6 deletions(-)
create mode 100644 rust/lon/src/init/npins.rs
create mode 100644 rust/lon/tests/npins.json
diff --git a/rust/lon/src/cli.rs b/rust/lon/src/cli.rs
index 5806b1d..57dcc50 100644
--- a/rust/lon/src/cli.rs
+++ b/rust/lon/src/cli.rs
@@ -11,7 +11,7 @@ use crate::{
bot::{Forge, Forgejo, GitHub, GitLab},
commit_message::CommitMessage,
git,
- init::{Convertible, niv},
+ init::{Convertible, niv, npins},
lock::Lock,
lon_nix::LonNix,
sources::{GitHubSource, GitSource, Source, Sources},
@@ -82,6 +82,7 @@ struct InitArgs {
#[derive(Clone, ValueEnum)]
enum LockFileType {
Niv,
+ Npins,
}
#[derive(Subcommand)]
@@ -261,13 +262,13 @@ fn init(directory: impl AsRef<Path>, args: &InitArgs) -> Result<()> {
bail!("No lock file type is provided");
};
- let lock_file = match lock_file_type {
- LockFileType::Niv => niv::LockFile::from_file(path)?,
- };
-
log::info!("Initializing lon.lock from {path:?}");
- let sources = lock_file.convert()?;
+ let sources = match lock_file_type {
+ LockFileType::Niv => niv::LockFile::from_file(path)?.convert()?,
+ LockFileType::Npins => npins::LockFile::from_file(path)?.convert()?,
+ };
+
sources.write(&directory)?;
Ok(())
diff --git a/rust/lon/src/init.rs b/rust/lon/src/init.rs
index ec87afa..06e63f2 100644
--- a/rust/lon/src/init.rs
+++ b/rust/lon/src/init.rs
@@ -1,4 +1,5 @@
pub mod niv;
+pub mod npins;
use anyhow::Result;
diff --git a/rust/lon/src/init/npins.rs b/rust/lon/src/init/npins.rs
new file mode 100644
index 0000000..8a38139
--- /dev/null
+++ b/rust/lon/src/init/npins.rs
@@ -0,0 +1,218 @@
+use std::{collections::BTreeMap, fs::File, io::Read, path::Path};
+
+use anyhow::{Context, Result, bail};
+use regex::Regex;
+use serde::Deserialize;
+
+use crate::{
+ init::Convertible,
+ sources::{GitHubSource, GitSource, Source, Sources},
+};
+
+#[derive(Debug, Deserialize)]
+pub struct LockFile {
+ pins: BTreeMap<String, Pin>,
+ version: u64,
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(tag = "type")]
+pub enum Repository {
+ Git {
+ /// URL to the Git repository
+ url: String,
+ },
+ Forgejo {
+ server: String,
+ owner: String,
+ repo: String,
+ },
+ GitHub {
+ /// "owner/repo"
+ owner: String,
+ repo: String,
+ },
+ GitLab {
+ /// usually "owner/repo" or "group/owner/repo" (without leading or trailing slashes)
+ repo_path: String,
+ /// Of the kind <https://gitlab.example.org/>
+ ///
+ /// It must fit into the schema `<server>/<owner>/<repo>` to get a repository's URL.
+ server: String,
+ /// access token for private repositories
+ #[serde(skip_serializing_if = "Option::is_none")]
+ #[serde(default)]
+ private_token: Option<String>,
+ },
+}
+
+// HACK: We know that a Git pin has a branch associated to it and GitRelease has none,
+// but to unify the behaviour, we set them bot to `Option`s
+#[derive(Debug, Deserialize)]
+#[serde(tag = "type")]
+pub enum Pin {
+ Git {
+ repository: Repository,
+ branch: Option<String>,
+ revision: String,
+ submodules: bool,
+ #[serde(default)]
+ frozen: bool,
+ },
+ GitRelease {
+ repository: Repository,
+ branch: Option<String>,
+ revision: String,
+ submodules: bool,
+ #[serde(default)]
+ frozen: bool,
+ },
+ Channel {
+ #[serde(rename = "name")]
+ channel: String,
+ url: String,
+ #[serde(default)]
+ frozen: bool,
+ },
+}
+
+impl LockFile {
+ pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
+ let file = File::open(path.as_ref())
+ .with_context(|| format!("Failed to open {:?}", path.as_ref()))?;
+ Self::from_reader(file)
+ }
+
+ fn from_reader(rdr: impl Read) -> Result<Self> {
+ serde_json::from_reader(rdr).context("Failed to deserialize npins lock file")
+ }
+}
+
+impl Convertible for LockFile {
+ fn convert(&self) -> Result<Sources> {
+ let mut sources = Sources::default();
+
+ if self.version == 1 {
+ bail!("Unsupported npins lockfile version: {}", &self.version)
+ }
+
+ let re = Regex::new(
+ r"https://releases\.nixos\.org/.*\.(?<shortrev>[a-f0-9]+)/nixexprs\.tar\.xz",
+ )?;
+
+ for (name, pin) in &self.pins {
+ log::info!("Converting {name}...");
+
+ let source = match pin {
+ Pin::Channel {
+ channel,
+ url,
+ frozen,
+ } => {
+ let Some(matched) = re.captures(url) else {
+ bail!("Cannot extract revision from the channel url: {url}")
+ };
+
+ Source::GitHub(GitHubSource::new(
+ "NixOS",
+ "nixpkgs",
+ Some(channel),
+ Some(&matched["shortrev"].into()),
+ *frozen,
+ )?)
+ }
+ Pin::Git {
+ repository,
+ branch,
+ revision,
+ submodules,
+ frozen,
+ }
+ | Pin::GitRelease {
+ repository,
+ branch,
+ revision,
+ submodules,
+ frozen,
+ } => match repository {
+ Repository::Git { url } => Source::Git(GitSource::new(
+ url,
+ branch.as_ref(),
+ Some(revision),
+ *submodules,
+ *frozen,
+ )?),
+ Repository::GitHub { owner, repo } => {
+ if *submodules {
+ Source::Git(GitSource::new(
+ &format!("https://github.com/{owner}/{repo}"),
+ branch.as_ref(),
+ Some(revision),
+ *submodules,
+ *frozen,
+ )?)
+ } else {
+ Source::GitHub(GitHubSource::new(
+ owner,
+ repo,
+ branch.as_ref(),
+ Some(revision),
+ *frozen,
+ )?)
+ }
+ }
+ Repository::Forgejo {
+ server,
+ owner,
+ repo,
+ } => Source::Git(GitSource::new(
+ &format!("{server}/{owner}/{repo}"),
+ branch.as_ref(),
+ Some(revision),
+ *submodules,
+ *frozen,
+ )?),
+ Repository::GitLab {
+ repo_path,
+ server,
+ private_token,
+ } => {
+ if private_token.is_some() {
+ log::warn!(
+ "GitLab source {name} is configured with a PAT, which unsupported in lon"
+ );
+ }
+ Source::Git(GitSource::new(
+ &format!("{server}/{repo_path}"),
+ branch.as_ref(),
+ Some(revision),
+ *submodules,
+ *frozen,
+ )?)
+ }
+ },
+ };
+
+ sources.add(name, source);
+ }
+
+ Ok(sources)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ impl LockFile {
+ fn from_str(s: &str) -> Result<Self> {
+ serde_json::from_str(s).context("Failed to deserialize npins lock file")
+ }
+ }
+
+ #[test]
+ fn parse_npins_lock_file() -> Result<()> {
+ LockFile::from_str(include_str!("../../tests/npins.json"))?;
+ Ok(())
+ }
+}
diff --git a/rust/lon/tests/npins.json b/rust/lon/tests/npins.json
new file mode 100644
index 0000000..10ce4e2
--- /dev/null
+++ b/rust/lon/tests/npins.json
@@ -0,0 +1,86 @@
+{
+ "pins": {
+ "agenix": {
+ "type": "GitRelease",
+ "repository": {
+ "type": "GitHub",
+ "owner": "ryantm",
+ "repo": "agenix"
+ },
+ "pre_releases": false,
+ "version_upper_bound": null,
+ "release_prefix": null,
+ "submodules": false,
+ "version": "0.15.0",
+ "revision": "564595d0ad4be7277e07fa63b5a991b3c645655d",
+ "url": "https://api.github.com/repos/ryantm/agenix/tarball/refs/tags/0.15.0",
+ "hash": "sha256-ipqShkBmHKC9ft1ZAsA6aeKps32k7+XZSPwfxeHLsAU="
+ },
+ "arkheon": {
+ "type": "Git",
+ "repository": {
+ "type": "GitHub",
+ "owner": "RaitoBezarius",
+ "repo": "arkheon"
+ },
+ "branch": "main",
+ "submodules": false,
+ "revision": "3eea876b29217d01cf2ef03ea9fdd8779d28ad04",
+ "url": "https://github.com/RaitoBezarius/arkheon/archive/3eea876b29217d01cf2ef03ea9fdd8779d28ad04.tar.gz",
+ "hash": "sha256-+R6MhTXuSzNeGQiL4DQwlP5yNhmnhbf7pQWPUWgcZSM="
+ },
+ "colmena": {
+ "type": "Git",
+ "repository": {
+ "type": "Git",
+ "url": "https://git.dgnum.eu/DGNum/colmena"
+ },
+ "branch": "main",
+ "submodules": false,
+ "revision": "b5135dc8af1d7637b337cc2632990400221da577",
+ "url": null,
+ "hash": "sha256-7gg+K3PEYlN0sGPgDlmnM8zgDDIV505gNcwjFN61Qvk="
+ },
+ "nix-actions": {
+ "type": "GitRelease",
+ "repository": {
+ "type": "Git",
+ "url": "https://git.dgnum.eu/DGNum/nix-actions.git"
+ },
+ "pre_releases": false,
+ "version_upper_bound": null,
+ "release_prefix": null,
+ "submodules": false,
+ "version": "v0.5.1",
+ "revision": "06847b3256df402da0475dccb290832ec92a9f8c",
+ "url": null,
+ "hash": "sha256-2xOZdKiUfcriQFKG37vY96dgCJLndhLa7cGacq8+SA8="
+ },
+ "nixos-25.05": {
+ "type": "Channel",
+ "name": "nixos-25.05",
+ "url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.803579.70c74b02eac4/nixexprs.tar.xz",
+ "hash": "sha256-0RxtgAd4gHYPFFwICal8k8hvJBOkCeTjFkh4HsqYDbE="
+ },
+ "nixos-unstable": {
+ "type": "Channel",
+ "name": "nixos-unstable",
+ "url": "https://releases.nixos.org/nixos/unstable/nixos-25.05pre797896.d89fc19e405c/nixexprs.tar.xz",
+ "hash": "sha256-bFJJ/qwB3VJ0nFuVYYHJXinT4tNJ2jhXTVT6SpYiFOM="
+ },
+ "wp4nix": {
+ "type": "Git",
+ "repository": {
+ "type": "GitLab",
+ "repo_path": "helsinki-systems/wp4nix",
+ "server": "https://git.helsinki.tools/"
+ },
+ "branch": "master",
+ "submodules": false,
+ "revision": "2fc9a0734168cab536e3129efa6397d6cd3ac89f",
+ "url": "https://git.helsinki.tools/api/v4/projects/helsinki-systems%2Fwp4nix/repository/archive.tar.gz?sha=2fc9a0734168cab536e3129efa6397d6cd3ac89f",
+ "hash": "sha256-abwqAZGsWuWqfxou8XlqedBvXsUw1/xanSgljLCJxdM="
+ }
+ },
+ "version": 6
+}

View file

@ -0,0 +1,3 @@
SPDX-FileCopyrightText: 2025 Tom Hubrecht <tom.hubrecht@dgnum.eu>
SPDX-License-Identifier: MIT