mirror of
https://github.com/gensokyo-zone/infrastructure.git
synced 2026-02-09 12:29:19 -08:00
An overhaul of the module system.
This commit is contained in:
parent
2bf9997813
commit
d2a823f5bc
11 changed files with 181 additions and 230 deletions
|
|
@ -1,175 +0,0 @@
|
||||||
{ config, tf, meta, kw, pkgs, lib, sources, ... }: with lib; let
|
|
||||||
oci-root = meta.deploy.targets.oci-root.tf;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
deploy.tf =
|
|
||||||
let
|
|
||||||
compartment_id = oci-root.resources.oci_kw_compartment.importAttr "id";
|
|
||||||
inherit (tf.lib.tf) terraformExpr;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
deploy.systems.rinnosuke = {
|
|
||||||
lustrate = {
|
|
||||||
enable = true;
|
|
||||||
connection = tf.resources.rinnosuke.connection.set;
|
|
||||||
};
|
|
||||||
connection = {
|
|
||||||
port = 62954;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
providers.oci = {
|
|
||||||
inputs = {
|
|
||||||
tenancy_ocid = oci-root.outputs.oci_tenancy.import;
|
|
||||||
user_ocid = oci-root.resources.oci_kw_user.importAttr "id";
|
|
||||||
fingerprint = oci-root.resources.oci_kw_apikey.importAttr "fingerprint";
|
|
||||||
region = oci-root.outputs.oci_region.import;
|
|
||||||
private_key_path = oci-root.resources.oci_kw_key_file.importAttr "filename";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
resources = mkMerge [{
|
|
||||||
cloudinit = {
|
|
||||||
provider = "cloudinit";
|
|
||||||
type = "config";
|
|
||||||
dataSource = true;
|
|
||||||
inputs = {
|
|
||||||
part = singleton {
|
|
||||||
content_type = "text/cloud-config";
|
|
||||||
content = "#cloud-config\n" + builtins.toJSON {
|
|
||||||
disable_root = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
availability_domain = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "identity_availability_domain";
|
|
||||||
dataSource = true;
|
|
||||||
inputs = {
|
|
||||||
inherit compartment_id;
|
|
||||||
ad_number = 2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
generic_image = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_images";
|
|
||||||
dataSource = true;
|
|
||||||
inputs = {
|
|
||||||
inherit compartment_id;
|
|
||||||
inherit (tf.resources.rinnosuke.inputs) shape;
|
|
||||||
operating_system = "Canonical Ubuntu"; # "Oracle Linux"
|
|
||||||
sort_by = "TIMECREATED";
|
|
||||||
sort_order = "DESC";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
rinnosuke_vnic = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_vnic_attachments";
|
|
||||||
dataSource = true;
|
|
||||||
inputs = {
|
|
||||||
inherit compartment_id;
|
|
||||||
instance_id = tf.resources.rinnosuke.refAttr "id";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
rinnosuke_ipv6 = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_ipv6";
|
|
||||||
inputs = {
|
|
||||||
vnic_id = tf.resources.rinnosuke_vnic.refAttr "vnic_attachments[0].vnic_id";
|
|
||||||
display_name = config.networking.hostName;
|
|
||||||
ip_address = terraformExpr ''cidrhost("${oci-root.resources.oci_kw_subnet.importAttr "ipv6cidr_block"}", 7)'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
rinnosuke = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_instance";
|
|
||||||
inputs = {
|
|
||||||
inherit compartment_id;
|
|
||||||
extended_metadata = { };
|
|
||||||
metadata = {
|
|
||||||
ssh_authorized_keys = concatStringsSep "\n" config.users.users.root.openssh.authorizedKeys.keys;
|
|
||||||
user_data = tf.resources.cloudinit.refAttr "rendered";
|
|
||||||
};
|
|
||||||
shape = "VM.Standard.E2.1.Micro";
|
|
||||||
shape_config = {
|
|
||||||
memory_in_gbs = 1;
|
|
||||||
ocpus = 1;
|
|
||||||
};
|
|
||||||
source_details = {
|
|
||||||
source_type = "image";
|
|
||||||
source_id = tf.resources.generic_image.refAttr "images[0].id";
|
|
||||||
boot_volume_size_in_gbs = 50; # min 50GB, up to 200GB free
|
|
||||||
};
|
|
||||||
create_vnic_details = [
|
|
||||||
{
|
|
||||||
assign_public_ip = true;
|
|
||||||
subnet_id = oci-root.resources.oci_kw_subnet.importAttr "id";
|
|
||||||
private_ip = terraformExpr ''cidrhost("${oci-root.resources.oci_kw_subnet.importAttr "cidr_block"}", 3)'';
|
|
||||||
nsg_ids = [
|
|
||||||
(tf.resources.firewall_group.refAttr "id")
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
availability_domain = tf.resources.availability_domain.refAttr "name";
|
|
||||||
};
|
|
||||||
lifecycle.ignoreChanges = [
|
|
||||||
"source_details[0].source_id"
|
|
||||||
];
|
|
||||||
connection = {
|
|
||||||
type = "ssh";
|
|
||||||
user = "root";
|
|
||||||
host = tf.lib.tf.terraformSelf "public_ip";
|
|
||||||
timeout = "5m";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
firewall_group = {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_network_security_group";
|
|
||||||
inputs = {
|
|
||||||
display_name = "${config.networking.hostName} firewall group";
|
|
||||||
inherit compartment_id;
|
|
||||||
vcn_id = oci-root.resources.oci_vcn.importAttr "id";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
(
|
|
||||||
let
|
|
||||||
protoValues = {
|
|
||||||
TCP = 6;
|
|
||||||
UDP = 17;
|
|
||||||
};
|
|
||||||
inherit (config.networking) firewall;
|
|
||||||
ipv4 = "0.0.0.0/0";
|
|
||||||
ipv6 = "::/0";
|
|
||||||
mapPort = source: protocol: port: {
|
|
||||||
provider = "oci";
|
|
||||||
type = "core_network_security_group_security_rule";
|
|
||||||
inputs = {
|
|
||||||
network_security_group_id = tf.resources.firewall_group.refAttr "id";
|
|
||||||
inherit protocol source;
|
|
||||||
direction = "INGRESS";
|
|
||||||
${if protocol == protoValues.TCP then "tcp_options" else "udp_options"} = {
|
|
||||||
destination_port_range =
|
|
||||||
if isAttrs port then {
|
|
||||||
min = port.from;
|
|
||||||
max = port.to;
|
|
||||||
} else {
|
|
||||||
min = port;
|
|
||||||
max = port;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
mapAll = protocol: port: [ (mapPort ipv4 protocol port) (mapPort ipv6 protocol port) ];
|
|
||||||
mapAllForInterface =
|
|
||||||
let
|
|
||||||
protos = [ "TCP" "UDP" ];
|
|
||||||
types = [ "Ports" "PortRanges" ];
|
|
||||||
in
|
|
||||||
interface: concatMap (type: concatMap (proto: (concatMap (port: (mapAll protoValues.${proto}) port) interface."allowed${proto}${type}")) protos) types;
|
|
||||||
rules = concatMap mapAllForInterface ([ firewall ] ++ map (interface: firewall.interfaces.${interface}) config.network.firewall.public.interfaces);
|
|
||||||
# TODO: use `count` and index into a fancy json or something?
|
|
||||||
in
|
|
||||||
listToAttrs (imap0 (i: rule: nameValuePair "firewall${toString i}" rule) rules)
|
|
||||||
)];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,9 @@
|
||||||
{ sources, ... }:
|
{ sources, ... }: {
|
||||||
|
external = [
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
(import (sources.arcexprs + "/modules")).home-manager
|
(import (sources.arcexprs + "/modules")).home-manager
|
||||||
(import (sources.katexprs + "/modules")).home
|
(import (sources.katexprs + "/modules")).home
|
||||||
(import (sources.impermanence + "/home-manager.nix"))
|
(import (sources.impermanence + "/home-manager.nix"))
|
||||||
(import sources.anicca).modules.home
|
(import sources.anicca).modules.home
|
||||||
./deploy.nix
|
|
||||||
./theme.nix
|
|
||||||
./secrets.nix
|
|
||||||
(sources.tf-nix + "/modules/home/secrets.nix")
|
(sources.tf-nix + "/modules/home/secrets.nix")
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
{ ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./imports.nix
|
|
||||||
./deploy.nix
|
|
||||||
./network.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
@ -59,8 +59,7 @@ with lib;
|
||||||
nixos = {
|
nixos = {
|
||||||
extraModules = [
|
extraModules = [
|
||||||
"${toString sources.home-manager}/nixos"
|
"${toString sources.home-manager}/nixos"
|
||||||
../../modules/nixos
|
] ++ singleton meta.modules.nixos;
|
||||||
];
|
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit (config.network) nodes;
|
inherit (config.network) nodes;
|
||||||
inherit sources meta;
|
inherit sources meta;
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
{ meta, sources, lib, ... }:
|
|
||||||
|
|
||||||
{
|
|
||||||
imports =
|
|
||||||
[
|
|
||||||
(import (sources.arcexprs + "/modules")).nixos
|
|
||||||
(import (sources.katexprs + "/modules")).nixos
|
|
||||||
(import (sources.impermanence + "/nixos.nix"))
|
|
||||||
(import sources.anicca).modules.nixos
|
|
||||||
./deploy.nix
|
|
||||||
./monitoring.nix
|
|
||||||
./secrets.nix
|
|
||||||
(sources.tf-nix + "/modules/nixos/secrets.nix")
|
|
||||||
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
|
|
||||||
(sources.hexchen + "/modules/network/yggdrasil")
|
|
||||||
];
|
|
||||||
|
|
||||||
options.hexchen.dns = lib.mkOption { };
|
|
||||||
options.hexchen.deploy = lib.mkOption { };
|
|
||||||
|
|
||||||
/*
|
|
||||||
This maps hosts to network.nodes from the meta config. This is required for hexchen's yggdrasil network module.
|
|
||||||
*/
|
|
||||||
config = {
|
|
||||||
_module.args.hosts = lib.mapAttrs (_: config: { inherit config; }) meta.network.nodes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
13
config/modules/nixos/settings.nix
Normal file
13
config/modules/nixos/settings.nix
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
{ sources, ... }: {
|
||||||
|
external = [
|
||||||
|
(import (sources.arcexprs + "/modules")).nixos
|
||||||
|
(import (sources.katexprs + "/modules")).nixos
|
||||||
|
(import (sources.impermanence + "/nixos.nix"))
|
||||||
|
(import sources.anicca).modules.nixos
|
||||||
|
(sources.tf-nix + "/modules/nixos/secrets.nix")
|
||||||
|
(sources.tf-nix + "/modules/nixos/secrets-users.nix")
|
||||||
|
];
|
||||||
|
excludes = [
|
||||||
|
"oracle"
|
||||||
|
];
|
||||||
|
}
|
||||||
155
config/modules/nixos/yggdrasil.nix
Normal file
155
config/modules/nixos/yggdrasil.nix
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
{ config, meta, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.network.yggdrasil;
|
||||||
|
calcAddr = pubkey: lib.readFile (pkgs.runCommandNoCC "calcaddr-${pubkey}" {} ''
|
||||||
|
echo '{ EncryptionPublicKey: "${pubkey}" }' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -address | tr -d '\n' > $out
|
||||||
|
'').outPath;
|
||||||
|
in {
|
||||||
|
options.network.yggdrasil = {
|
||||||
|
enable = mkEnableOption "Enable the yggdrasil-based private hexnet";
|
||||||
|
pubkey = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Public key of this node";
|
||||||
|
};
|
||||||
|
address = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Main Yggdrasil address. Set automatically";
|
||||||
|
default = calcAddr cfg.pubkey;
|
||||||
|
};
|
||||||
|
trust = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
description = "Open Firewall completely for the network";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
listen.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
description = "Allow other hosts in the network to connect directly";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
listen.endpoints = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "Endpoints to listen on";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
dns.enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
description = "enable automatic dns record generation";
|
||||||
|
default = false;
|
||||||
|
};
|
||||||
|
dns.zone = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "Main zone to insert DNS records into";
|
||||||
|
default = "lilwit.ch";
|
||||||
|
};
|
||||||
|
dns.subdomain = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
description = "subdomain to put the records into";
|
||||||
|
default = "net";
|
||||||
|
};
|
||||||
|
tunnel.localV6 = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "v6 subnets to expose";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
tunnel.localV4 = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "v4 subnets to expose";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
tunnel.remoteV6 = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "Extra v6 subnets to route";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
tunnel.remoteV4 = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "Extra v4 subnets to route";
|
||||||
|
default = {};
|
||||||
|
};
|
||||||
|
extra.pubkeys = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
description = "Additional hosts to allow into the network. Keys won't be added to definition host.";
|
||||||
|
default = {};
|
||||||
|
example = { host = "0000000000000000000000000000000000000000000000000000000000000000"; };
|
||||||
|
};
|
||||||
|
extra.addresses = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
internal = true;
|
||||||
|
default = mapAttrs (_: c: calcAddr c) cfg.extra.pubkeys;
|
||||||
|
};
|
||||||
|
extra.localV6 = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "v6 subnets to expose, but not route";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
extra.localV4 = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
description = "v4 subnets to expose, but not route";
|
||||||
|
default = [];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable (let
|
||||||
|
yggConfigs = filter (
|
||||||
|
c: c.enable && (cfg.pubkey != c.pubkey)
|
||||||
|
) (
|
||||||
|
mapAttrsToList (_: node: node.network.yggdrasil or { enable = false; pubkey = null; }) meta.network.nodes
|
||||||
|
);
|
||||||
|
pubkeys = flatten (map (c: [ c.pubkey ] ++ (attrValues c.extra.pubkeys)) yggConfigs);
|
||||||
|
in {
|
||||||
|
assertions = [
|
||||||
|
{
|
||||||
|
assertion = !(cfg.listen.enable && (cfg.listen.endpoints == []));
|
||||||
|
message = "Specify network.yggdrasil.listen.endpoints";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.firewall.trustedInterfaces = mkIf cfg.trust [ "yggdrasil" ];
|
||||||
|
|
||||||
|
services.yggdrasil = {
|
||||||
|
enable = true;
|
||||||
|
persistentKeys = true;
|
||||||
|
config = {
|
||||||
|
AllowedEncryptionPublicKeys = pubkeys;
|
||||||
|
IfName = "yggdrasil";
|
||||||
|
Listen = cfg.listen.endpoints;
|
||||||
|
Peers = lib.flatten (map (c: c.listen.endpoints) (filter (c: c.listen.enable) yggConfigs));
|
||||||
|
SessionFirewall = {
|
||||||
|
Enable = true;
|
||||||
|
AllowFromRemote = false;
|
||||||
|
WhitelistEncryptionPublicKeys = pubkeys;
|
||||||
|
};
|
||||||
|
TunnelRouting = let
|
||||||
|
subnets = v: (
|
||||||
|
listToAttrs (flatten (map (c: map (net: nameValuePair net c.pubkey) c.tunnel."localV${toString v}") yggConfigs))
|
||||||
|
) // cfg.tunnel."remoteV${toString v}";
|
||||||
|
in {
|
||||||
|
Enable = true;
|
||||||
|
IPv4LocalSubnets = cfg.tunnel.localV4 ++ cfg.extra.localV4;
|
||||||
|
IPv6LocalSubnets = cfg.tunnel.localV6 ++ cfg.extra.localV6;
|
||||||
|
IPv4RemoteSubnets = subnets 4;
|
||||||
|
IPv6RemoteSubnets = subnets 6;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.yggdrasil.postStart = let
|
||||||
|
yggTun = config.services.yggdrasil.config.TunnelRouting;
|
||||||
|
addNets = v: nets: concatMapStringsSep "\n" (net: "${pkgs.iproute}/bin/ip -${toString v} route add ${net} dev yggdrasil") (attrNames nets);
|
||||||
|
in "sleep 1\n" + (concatMapStringsSep "\n" (v: addNets v yggTun."IPv${toString v}RemoteSubnets") [ 4 6 ]);
|
||||||
|
|
||||||
|
system.build.yggdrasilTemplate = let
|
||||||
|
json = builtins.toJSON {
|
||||||
|
inherit (config.services.yggdrasil.config) Peers SessionFirewall TunnelRouting;
|
||||||
|
EncryptionPublicKey = "";
|
||||||
|
EncryptionPrivateKey = "";
|
||||||
|
SigningPublicKey = "";
|
||||||
|
SigningPrivateKey = "";
|
||||||
|
};
|
||||||
|
in pkgs.runCommandNoCC "yggdrasil-template.json" {}
|
||||||
|
"echo '${json}' | ${config.services.yggdrasil.package}/bin/yggdrasil -useconf -normaliseconf > $out";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,7 @@ with lib;
|
||||||
{
|
{
|
||||||
options.home-manager.users = mkOption {
|
options.home-manager.users = mkOption {
|
||||||
type = types.attrsOf (types.submoduleWith {
|
type = types.attrsOf (types.submoduleWith {
|
||||||
modules = singleton ../../modules/home;
|
modules = singleton meta.modules.home;
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit sources tf meta;
|
inherit sources tf meta;
|
||||||
nixos = config;
|
nixos = config;
|
||||||
|
|
|
||||||
10
default.nix
10
default.nix
|
|
@ -32,13 +32,13 @@ let
|
||||||
If only one exists, the path for that one is returned.
|
If only one exists, the path for that one is returned.
|
||||||
Otherwise a module is generated which contains both import paths.
|
Otherwise a module is generated which contains both import paths.
|
||||||
*/
|
*/
|
||||||
xargNames = lib.unique (lib.folderList ./config [ "trusted" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]);
|
xargNames = lib.unique (lib.folderList ./config [ "trusted modules" ] ++ lib.folderList ./config/trusted [ "pkgs" "tf" ]);
|
||||||
xarg = lib.mapListToAttrs
|
xarg = (lib.mapListToAttrs
|
||||||
(folder: lib.nameValuePair folder (lib.domainMerge {
|
(folder: lib.nameValuePair folder (lib.domainMerge {
|
||||||
inherit folder;
|
inherit folder;
|
||||||
folderPaths = [ (./config + "/${folder}") (./config/trusted + "/${folder}") ];
|
folderPaths = [ (./config + "/${folder}") (./config/trusted + "/${folder}") ];
|
||||||
}))
|
}))
|
||||||
xargNames;
|
xargNames) // { modules = lib.recursiveMod { folder = ./config/modules; inherit sources; }; };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We provide the runners with this file this way. We also provide our nix args here.
|
We provide the runners with this file this way. We also provide our nix args here.
|
||||||
|
|
@ -63,6 +63,7 @@ let
|
||||||
# This is where the meta config is evaluated.
|
# This is where the meta config is evaluated.
|
||||||
eval = lib.evalModules {
|
eval = lib.evalModules {
|
||||||
modules = lib.singleton metaConfig
|
modules = lib.singleton metaConfig
|
||||||
|
++ lib.singleton xarg.modules.meta
|
||||||
++ lib.attrValues (removeAttrs xarg.targets [ "common" ])
|
++ lib.attrValues (removeAttrs xarg.targets [ "common" ])
|
||||||
++ (map
|
++ (map
|
||||||
(host: {
|
(host: {
|
||||||
|
|
@ -70,8 +71,7 @@ let
|
||||||
imports = config.lib.kw.nodeImport host;
|
imports = config.lib.kw.nodeImport host;
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.attrNames xarg.hosts))
|
(lib.attrNames xarg.hosts));
|
||||||
++ lib.singleton ./config/modules/meta/default.nix;
|
|
||||||
|
|
||||||
specialArgs = {
|
specialArgs = {
|
||||||
inherit sources root;
|
inherit sources root;
|
||||||
|
|
|
||||||
|
|
@ -89,10 +89,10 @@
|
||||||
"homepage": null,
|
"homepage": null,
|
||||||
"owner": "kittywitch",
|
"owner": "kittywitch",
|
||||||
"repo": "nixexprs",
|
"repo": "nixexprs",
|
||||||
"rev": "43c95a469d560b05880da5c9b83139da525f78c3",
|
"rev": "1ba4276349007ac90a9bda6e856752945a2ba342",
|
||||||
"sha256": "1rx2ym7crcd8yh61cfdly9nsy5y83v9bh5rwgz2cvq3s3q0lb7k0",
|
"sha256": "0m6v8aykjzvb5phr9kwazkyjp3bbzv6wys6jzvkpqgysl8mxyfhf",
|
||||||
"type": "tarball",
|
"type": "tarball",
|
||||||
"url": "https://github.com/kittywitch/nixexprs/archive/43c95a469d560b05880da5c9b83139da525f78c3.tar.gz",
|
"url": "https://github.com/kittywitch/nixexprs/archive/1ba4276349007ac90a9bda6e856752945a2ba342.tar.gz",
|
||||||
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
"url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
|
||||||
},
|
},
|
||||||
"niv": {
|
"niv": {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Subproject commit 43c95a469d560b05880da5c9b83139da525f78c3
|
Subproject commit 1ba4276349007ac90a9bda6e856752945a2ba342
|
||||||
Loading…
Add table
Add a link
Reference in a new issue