forked from mirrors/nixpkgs
Merge pull request #264331 from foo-dogsquared/add-nixos-guix-module
nixos/guix: init
This commit is contained in:
commit
4e81387d5b
|
@ -14,6 +14,8 @@ In addition to numerous new and upgraded packages, this release has the followin
|
|||
|
||||
<!-- To avoid merge conflicts, consider adding your item at an arbitrary place in the list instead. -->
|
||||
|
||||
- [Guix](https://guix.gnu.org), a functional package manager inspired by Nix. Available as [services.guix](#opt-services.guix.enable).
|
||||
|
||||
- [maubot](https://github.com/maubot/maubot), a plugin-based Matrix bot framework. Available as [services.maubot](#opt-services.maubot.enable).
|
||||
|
||||
- [Anki Sync Server](https://docs.ankiweb.net/sync-server.html), the official sync server built into recent versions of Anki. Available as [services.anki-sync-server](#opt-services.anki-sync-server.enable).
|
||||
|
|
|
@ -684,6 +684,7 @@
|
|||
./services/misc/gollum.nix
|
||||
./services/misc/gpsd.nix
|
||||
./services/misc/greenclip.nix
|
||||
./services/misc/guix
|
||||
./services/misc/headphones.nix
|
||||
./services/misc/heisenbridge.nix
|
||||
./services/misc/homepage-dashboard.nix
|
||||
|
|
394
nixos/modules/services/misc/guix/default.nix
Normal file
394
nixos/modules/services/misc/guix/default.nix
Normal file
|
@ -0,0 +1,394 @@
|
|||
{ config, pkgs, lib, ... }:
|
||||
|
||||
let
|
||||
cfg = config.services.guix;
|
||||
|
||||
package = cfg.package.override { inherit (cfg) stateDir storeDir; };
|
||||
|
||||
guixBuildUser = id: {
|
||||
name = "guixbuilder${toString id}";
|
||||
group = cfg.group;
|
||||
extraGroups = [ cfg.group ];
|
||||
createHome = false;
|
||||
description = "Guix build user ${toString id}";
|
||||
isSystemUser = true;
|
||||
};
|
||||
|
||||
guixBuildUsers = numberOfUsers:
|
||||
builtins.listToAttrs (map
|
||||
(user: {
|
||||
name = user.name;
|
||||
value = user;
|
||||
})
|
||||
(builtins.genList guixBuildUser numberOfUsers));
|
||||
|
||||
# A set of Guix user profiles to be linked at activation.
|
||||
guixUserProfiles = {
|
||||
# The current Guix profile that is created through `guix pull`.
|
||||
"current-guix" = "\${XDG_CONFIG_HOME}/guix/current";
|
||||
|
||||
# The default Guix profile similar to $HOME/.nix-profile from Nix.
|
||||
"guix-profile" = "$HOME/.guix-profile";
|
||||
};
|
||||
|
||||
# All of the Guix profiles to be used.
|
||||
guixProfiles = lib.attrValues guixUserProfiles;
|
||||
|
||||
serviceEnv = {
|
||||
GUIX_LOCPATH = "${cfg.stateDir}/guix/profiles/per-user/root/guix-profile/lib/locale";
|
||||
LC_ALL = "C.UTF-8";
|
||||
};
|
||||
in
|
||||
{
|
||||
meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
|
||||
|
||||
options.services.guix = with lib; {
|
||||
enable = mkEnableOption "Guix build daemon service";
|
||||
|
||||
group = mkOption {
|
||||
type = types.str;
|
||||
default = "guixbuild";
|
||||
example = "guixbuild";
|
||||
description = ''
|
||||
The group of the Guix build user pool.
|
||||
'';
|
||||
};
|
||||
|
||||
nrBuildUsers = mkOption {
|
||||
type = types.ints.unsigned;
|
||||
description = ''
|
||||
Number of Guix build users to be used in the build pool.
|
||||
'';
|
||||
default = 10;
|
||||
example = 20;
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
example = [ "--max-jobs=4" "--debug" ];
|
||||
description = ''
|
||||
Extra flags to pass to the Guix daemon service.
|
||||
'';
|
||||
};
|
||||
|
||||
package = mkPackageOption pkgs "guix" {
|
||||
extraDescription = ''
|
||||
It should contain {command}`guix-daemon` and {command}`guix`
|
||||
executable.
|
||||
'';
|
||||
};
|
||||
|
||||
storeDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/gnu/store";
|
||||
description = ''
|
||||
The store directory where the Guix service will serve to/from. Take
|
||||
note Guix cannot take advantage of substitutes if you set it something
|
||||
other than {file}`/gnu/store` since most of the cached builds are
|
||||
assumed to be in there.
|
||||
|
||||
::: {.warning}
|
||||
This will also recompile all packages because the normal cache no
|
||||
longer applies.
|
||||
:::
|
||||
'';
|
||||
};
|
||||
|
||||
stateDir = mkOption {
|
||||
type = types.path;
|
||||
default = "/var";
|
||||
description = ''
|
||||
The state directory where Guix service will store its data such as its
|
||||
user-specific profiles, cache, and state files.
|
||||
|
||||
::: {.warning}
|
||||
Changing it to something other than the default will rebuild the
|
||||
package.
|
||||
:::
|
||||
'';
|
||||
example = "/gnu/var";
|
||||
};
|
||||
|
||||
publish = {
|
||||
enable = mkEnableOption "substitute server for your Guix store directory";
|
||||
|
||||
generateKeyPair = mkOption {
|
||||
type = types.bool;
|
||||
description = ''
|
||||
Whether to generate signing keys in {file}`/etc/guix` which are
|
||||
required to initialize a substitute server. Otherwise,
|
||||
`--public-key=$FILE` and `--private-key=$FILE` can be passed in
|
||||
{option}`services.guix.publish.extraArgs`.
|
||||
'';
|
||||
default = true;
|
||||
example = false;
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8181;
|
||||
example = 8200;
|
||||
description = ''
|
||||
Port of the substitute server to listen on.
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "guix-publish";
|
||||
description = ''
|
||||
Name of the user to change once the server is up.
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
description = ''
|
||||
Extra flags to pass to the substitute server.
|
||||
'';
|
||||
default = [];
|
||||
example = [
|
||||
"--compression=zstd:6"
|
||||
"--discover=no"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
gc = {
|
||||
enable = mkEnableOption "automatic garbage collection service for Guix";
|
||||
|
||||
extraArgs = mkOption {
|
||||
type = with types; listOf str;
|
||||
default = [ ];
|
||||
description = ''
|
||||
List of arguments to be passed to {command}`guix gc`.
|
||||
|
||||
When given no option, it will try to collect all garbage which is
|
||||
often inconvenient so it is recommended to set [some
|
||||
options](https://guix.gnu.org/en/manual/en/html_node/Invoking-guix-gc.html).
|
||||
'';
|
||||
example = [
|
||||
"--delete-generations=1m"
|
||||
"--free-space=10G"
|
||||
"--optimize"
|
||||
];
|
||||
};
|
||||
|
||||
dates = lib.mkOption {
|
||||
type = types.str;
|
||||
default = "03:15";
|
||||
example = "weekly";
|
||||
description = ''
|
||||
How often the garbage collection occurs. This takes the time format
|
||||
from {manpage}`systemd.time(7)`.
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable (lib.mkMerge [
|
||||
{
|
||||
environment.systemPackages = [ package ];
|
||||
|
||||
users.users = guixBuildUsers cfg.nrBuildUsers;
|
||||
users.groups.${cfg.group} = { };
|
||||
|
||||
# Guix uses Avahi (through guile-avahi) both for the auto-discovering and
|
||||
# advertising substitute servers in the local network.
|
||||
services.avahi.enable = lib.mkDefault true;
|
||||
services.avahi.publish.enable = lib.mkDefault true;
|
||||
services.avahi.publish.userServices = lib.mkDefault true;
|
||||
|
||||
# It's similar to Nix daemon so there's no question whether or not this
|
||||
# should be sandboxed.
|
||||
systemd.services.guix-daemon = {
|
||||
environment = serviceEnv;
|
||||
script = ''
|
||||
${lib.getExe' package "guix-daemon"} \
|
||||
--build-users-group=${cfg.group} \
|
||||
${lib.escapeShellArgs cfg.extraArgs}
|
||||
'';
|
||||
serviceConfig = {
|
||||
OOMPolicy = "continue";
|
||||
RemainAfterExit = "yes";
|
||||
Restart = "always";
|
||||
TasksMax = 8192;
|
||||
};
|
||||
unitConfig.RequiresMountsFor = [
|
||||
cfg.storeDir
|
||||
cfg.stateDir
|
||||
];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
|
||||
# This is based from Nix daemon socket unit from upstream Nix package.
|
||||
# Guix build daemon has support for systemd-style socket activation.
|
||||
systemd.sockets.guix-daemon = {
|
||||
description = "Guix daemon socket";
|
||||
before = [ "multi-user.target" ];
|
||||
listenStreams = [ "${cfg.stateDir}/guix/daemon-socket/socket" ];
|
||||
unitConfig = {
|
||||
RequiresMountsFor = [
|
||||
cfg.storeDir
|
||||
cfg.stateDir
|
||||
];
|
||||
ConditionPathIsReadWrite = "${cfg.stateDir}/guix/daemon-socket";
|
||||
};
|
||||
wantedBy = [ "socket.target" ];
|
||||
};
|
||||
|
||||
systemd.mounts = [{
|
||||
description = "Guix read-only store directory";
|
||||
before = [ "guix-daemon.service" ];
|
||||
what = cfg.storeDir;
|
||||
where = cfg.storeDir;
|
||||
type = "none";
|
||||
options = "bind,ro";
|
||||
|
||||
unitConfig.DefaultDependencies = false;
|
||||
wantedBy = [ "guix-daemon.service" ];
|
||||
}];
|
||||
|
||||
# Make transferring files from one store to another easier with the usual
|
||||
# case being of most substitutes from the official Guix CI instance.
|
||||
system.activationScripts.guix-authorize-keys = ''
|
||||
for official_server_keys in ${package}/share/guix/*.pub; do
|
||||
${lib.getExe' package "guix"} archive --authorize < $official_server_keys
|
||||
done
|
||||
'';
|
||||
|
||||
# Link the usual Guix profiles to the home directory. This is useful in
|
||||
# ephemeral setups where only certain part of the filesystem is
|
||||
# persistent (e.g., "Erase my darlings"-type of setup).
|
||||
system.userActivationScripts.guix-activate-user-profiles.text = let
|
||||
linkProfileToPath = acc: profile: location: let
|
||||
guixProfile = "${cfg.stateDir}/guix/profiles/per-user/\${USER}/${profile}";
|
||||
in acc + ''
|
||||
[ -d "${guixProfile}" ] && ln -sf "${guixProfile}" "${location}"
|
||||
'';
|
||||
|
||||
activationScript = lib.foldlAttrs linkProfileToPath "" guixUserProfiles;
|
||||
in ''
|
||||
# Don't export this please! It is only expected to be used for this
|
||||
# activation script and nothing else.
|
||||
XDG_CONFIG_HOME=''${XDG_CONFIG_HOME:-$HOME/.config}
|
||||
|
||||
# Linking the usual Guix profiles into the home directory.
|
||||
${activationScript}
|
||||
'';
|
||||
|
||||
# GUIX_LOCPATH is basically LOCPATH but for Guix libc which in turn used by
|
||||
# virtually every Guix-built packages. This is so that Guix-installed
|
||||
# applications wouldn't use incompatible locale data and not touch its host
|
||||
# system.
|
||||
environment.sessionVariables.GUIX_LOCPATH = lib.makeSearchPath "lib/locale" guixProfiles;
|
||||
|
||||
# What Guix profiles export is very similar to Nix profiles so it is
|
||||
# acceptable to list it here. Also, it is more likely that the user would
|
||||
# want to use packages explicitly installed from Guix so we're putting it
|
||||
# first.
|
||||
environment.profiles = lib.mkBefore guixProfiles;
|
||||
}
|
||||
|
||||
(lib.mkIf cfg.publish.enable {
|
||||
systemd.services.guix-publish = {
|
||||
description = "Guix remote store";
|
||||
environment = serviceEnv;
|
||||
|
||||
# Mounts will be required by the daemon service anyways so there's no
|
||||
# need add RequiresMountsFor= or something similar.
|
||||
requires = [ "guix-daemon.service" ];
|
||||
after = [ "guix-daemon.service" ];
|
||||
partOf = [ "guix-daemon.service" ];
|
||||
|
||||
preStart = lib.mkIf cfg.publish.generateKeyPair ''
|
||||
# Generate the keypair if it's missing.
|
||||
[ -f "/etc/guix/signing-key.sec" ] && [ -f "/etc/guix/signing-key.pub" ] || \
|
||||
${lib.getExe' package "guix"} archive --generate-key || {
|
||||
rm /etc/guix/signing-key.*;
|
||||
${lib.getExe' package "guix"} archive --generate-key;
|
||||
}
|
||||
'';
|
||||
script = ''
|
||||
${lib.getExe' package "guix"} publish \
|
||||
--user=${cfg.publish.user} --port=${builtins.toString cfg.publish.port} \
|
||||
${lib.escapeShellArgs cfg.publish.extraArgs}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Restart = "always";
|
||||
RestartSec = 10;
|
||||
|
||||
ProtectClock = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectControlGroups = true;
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"@debug"
|
||||
"@setuid"
|
||||
];
|
||||
|
||||
RestrictNamespaces = true;
|
||||
RestrictAddressFamilies = [
|
||||
"AF_UNIX"
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
];
|
||||
|
||||
# While the permissions can be set, it is assumed to be taken by Guix
|
||||
# daemon service which it has already done the setup.
|
||||
ConfigurationDirectory = "guix";
|
||||
|
||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||
CapabilityBoundingSet = [
|
||||
"CAP_NET_BIND_SERVICE"
|
||||
"CAP_SETUID"
|
||||
"CAP_SETGID"
|
||||
];
|
||||
};
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
};
|
||||
|
||||
users.users.guix-publish = lib.mkIf (cfg.publish.user == "guix-publish") {
|
||||
description = "Guix publish user";
|
||||
group = config.users.groups.guix-publish.name;
|
||||
isSystemUser = true;
|
||||
};
|
||||
users.groups.guix-publish = {};
|
||||
})
|
||||
|
||||
(lib.mkIf cfg.gc.enable {
|
||||
# This service should be handled by root to collect all garbage by all
|
||||
# users.
|
||||
systemd.services.guix-gc = {
|
||||
description = "Guix garbage collection";
|
||||
startAt = cfg.gc.dates;
|
||||
script = ''
|
||||
${lib.getExe' package "guix"} gc ${lib.escapeShellArgs cfg.gc.extraArgs}
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
Type = "oneshot";
|
||||
|
||||
MemoryDenyWriteExecute = true;
|
||||
PrivateDevices = true;
|
||||
PrivateNetworks = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelTunables = true;
|
||||
SystemCallFilter = [
|
||||
"@default"
|
||||
"@file-system"
|
||||
"@basic-io"
|
||||
"@system-service"
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.guix-gc.timerConfig.Persistent = true;
|
||||
})
|
||||
]);
|
||||
}
|
|
@ -359,6 +359,7 @@ in {
|
|||
grow-partition = runTest ./grow-partition.nix;
|
||||
grub = handleTest ./grub.nix {};
|
||||
guacamole-server = handleTest ./guacamole-server.nix {};
|
||||
guix = handleTest ./guix {};
|
||||
gvisor = handleTest ./gvisor.nix {};
|
||||
hadoop = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop; };
|
||||
hadoop_3_2 = import ./hadoop { inherit handleTestOn; package=pkgs.hadoop_3_2; };
|
||||
|
|
38
nixos/tests/guix/basic.nix
Normal file
38
nixos/tests/guix/basic.nix
Normal file
|
@ -0,0 +1,38 @@
|
|||
# Take note the Guix store directory is empty. Also, we're trying to prevent
|
||||
# Guix from trying to downloading substitutes because of the restricted
|
||||
# access (assuming it's in a sandboxed environment).
|
||||
#
|
||||
# So this test is what it is: a basic test while trying to use Guix as much as
|
||||
# we possibly can (including the API) without triggering its download alarm.
|
||||
|
||||
import ../make-test-python.nix ({ lib, pkgs, ... }: {
|
||||
name = "guix-basic";
|
||||
meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
|
||||
|
||||
nodes.machine = { config, ... }: {
|
||||
environment.etc."guix/scripts".source = ./scripts;
|
||||
services.guix.enable = true;
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import pathlib
|
||||
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_for_unit("guix-daemon.service")
|
||||
|
||||
# Can't do much here since the environment has restricted network access.
|
||||
with subtest("Guix basic package management"):
|
||||
machine.succeed("guix build --dry-run --verbosity=0 hello")
|
||||
machine.succeed("guix show hello")
|
||||
|
||||
# This is to see if the Guix API is usable and mostly working.
|
||||
with subtest("Guix API scripting"):
|
||||
scripts_dir = pathlib.Path("/etc/guix/scripts")
|
||||
|
||||
text_msg = "Hello there, NixOS!"
|
||||
text_store_file = machine.succeed(f"guix repl -- {scripts_dir}/create-file-to-store.scm '{text_msg}'")
|
||||
assert machine.succeed(f"cat {text_store_file}") == text_msg
|
||||
|
||||
machine.succeed(f"guix repl -- {scripts_dir}/add-existing-files-to-store.scm {scripts_dir}")
|
||||
'';
|
||||
})
|
8
nixos/tests/guix/default.nix
Normal file
8
nixos/tests/guix/default.nix
Normal file
|
@ -0,0 +1,8 @@
|
|||
{ system ? builtins.currentSystem
|
||||
, pkgs ? import ../../.. { inherit system; }
|
||||
}:
|
||||
|
||||
{
|
||||
basic = import ./basic.nix { inherit system pkgs; };
|
||||
publish = import ./publish.nix { inherit system pkgs; };
|
||||
}
|
95
nixos/tests/guix/publish.nix
Normal file
95
nixos/tests/guix/publish.nix
Normal file
|
@ -0,0 +1,95 @@
|
|||
# Testing out the substitute server with two machines in a local network. As a
|
||||
# bonus, we'll also test a feature of the substitute server being able to
|
||||
# advertise its service to the local network with Avahi.
|
||||
|
||||
import ../make-test-python.nix ({ pkgs, lib, ... }: let
|
||||
publishPort = 8181;
|
||||
inherit (builtins) toString;
|
||||
in {
|
||||
name = "guix-publish";
|
||||
|
||||
meta.maintainers = with lib.maintainers; [ foo-dogsquared ];
|
||||
|
||||
nodes = let
|
||||
commonConfig = { config, ... }: {
|
||||
# We'll be using '--advertise' flag with the
|
||||
# substitute server which requires Avahi.
|
||||
services.avahi = {
|
||||
enable = true;
|
||||
nssmdns = true;
|
||||
publish = {
|
||||
enable = true;
|
||||
userServices = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
in {
|
||||
server = { config, lib, pkgs, ... }: {
|
||||
imports = [ commonConfig ];
|
||||
|
||||
services.guix = {
|
||||
enable = true;
|
||||
publish = {
|
||||
enable = true;
|
||||
port = publishPort;
|
||||
|
||||
generateKeyPair = true;
|
||||
extraArgs = [ "--advertise" ];
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ publishPort ];
|
||||
};
|
||||
|
||||
client = { config, lib, pkgs, ... }: {
|
||||
imports = [ commonConfig ];
|
||||
|
||||
services.guix = {
|
||||
enable = true;
|
||||
|
||||
extraArgs = [
|
||||
# Force to only get all substitutes from the local server. We don't
|
||||
# have anything in the Guix store directory and we cannot get
|
||||
# anything from the official substitute servers anyways.
|
||||
"--substitute-urls='http://server.local:${toString publishPort}'"
|
||||
|
||||
# Enable autodiscovery of the substitute servers in the local
|
||||
# network. This machine shouldn't need to import the signing key from
|
||||
# the substitute server since it is automatically done anyways.
|
||||
"--discover=yes"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = ''
|
||||
import pathlib
|
||||
|
||||
start_all()
|
||||
|
||||
scripts_dir = pathlib.Path("/etc/guix/scripts")
|
||||
|
||||
for machine in machines:
|
||||
machine.wait_for_unit("multi-user.target")
|
||||
machine.wait_for_unit("guix-daemon.service")
|
||||
machine.wait_for_unit("avahi-daemon.service")
|
||||
|
||||
server.wait_for_unit("guix-publish.service")
|
||||
server.wait_for_open_port(${toString publishPort})
|
||||
server.succeed("curl http://localhost:${toString publishPort}/")
|
||||
|
||||
# Now it's the client turn to make use of it.
|
||||
substitute_server = "http://server.local:${toString publishPort}"
|
||||
client.wait_for_unit("network-online.target")
|
||||
response = client.succeed(f"curl {substitute_server}")
|
||||
assert "Guix Substitute Server" in response
|
||||
|
||||
# Authorizing the server to be used as a substitute server.
|
||||
client.succeed(f"curl -O {substitute_server}/signing-key.pub")
|
||||
client.succeed("guix archive --authorize < ./signing-key.pub")
|
||||
|
||||
# Since we're using the substitute server with the `--advertise` flag, we
|
||||
# might as well check it.
|
||||
client.succeed("avahi-browse --resolve --terminate _guix_publish._tcp | grep '_guix_publish._tcp'")
|
||||
'';
|
||||
})
|
52
nixos/tests/guix/scripts/add-existing-files-to-store.scm
Normal file
52
nixos/tests/guix/scripts/add-existing-files-to-store.scm
Normal file
|
@ -0,0 +1,52 @@
|
|||
;; A simple script that adds each file given from the command-line into the
|
||||
;; store and checks them if it's the same.
|
||||
(use-modules (guix)
|
||||
(srfi srfi-1)
|
||||
(ice-9 ftw)
|
||||
(rnrs io ports))
|
||||
|
||||
;; This is based from tests/derivations.scm from Guix source code.
|
||||
(define* (directory-contents dir #:optional (slurp get-bytevector-all))
|
||||
"Return an alist representing the contents of DIR"
|
||||
(define prefix-len (string-length dir))
|
||||
(sort (file-system-fold (const #t)
|
||||
(lambda (path stat result)
|
||||
(alist-cons (string-drop path prefix-len)
|
||||
(call-with-input-file path slurp)
|
||||
result))
|
||||
(lambda (path stat result) result)
|
||||
(lambda (path stat result) result)
|
||||
(lambda (path stat result) result)
|
||||
(lambda (path stat errno result) result)
|
||||
'()
|
||||
dir)
|
||||
(lambda (e1 e2)
|
||||
(string<? (car e1) (car e2)))))
|
||||
|
||||
(define* (check-if-same store drv path)
|
||||
"Check if the given path and its store item are the same"
|
||||
(let* ((filetype (stat:type (stat drv))))
|
||||
(case filetype
|
||||
((regular)
|
||||
(and (valid-path? store drv)
|
||||
(equal? (call-with-input-file path get-bytevector-all)
|
||||
(call-with-input-file drv get-bytevector-all))))
|
||||
((directory)
|
||||
(and (valid-path? store drv)
|
||||
(equal? (directory-contents path)
|
||||
(directory-contents drv))))
|
||||
(else #f))))
|
||||
|
||||
(define* (add-and-check-item-to-store store path)
|
||||
"Add PATH to STORE and check if the contents are the same"
|
||||
(let* ((store-item (add-to-store store
|
||||
(basename path)
|
||||
#t "sha256" path))
|
||||
(is-same (check-if-same store store-item path)))
|
||||
(if (not is-same)
|
||||
(exit 1))))
|
||||
|
||||
(with-store store
|
||||
(map (lambda (path)
|
||||
(add-and-check-item-to-store store (readlink* path)))
|
||||
(cdr (command-line))))
|
8
nixos/tests/guix/scripts/create-file-to-store.scm
Normal file
8
nixos/tests/guix/scripts/create-file-to-store.scm
Normal file
|
@ -0,0 +1,8 @@
|
|||
;; A script that creates a store item with the given text and prints the
|
||||
;; resulting store item path.
|
||||
(use-modules (guix))
|
||||
|
||||
(with-store store
|
||||
(display (add-text-to-store store "guix-basic-test-text"
|
||||
(string-join
|
||||
(cdr (command-line))))))
|
|
@ -28,6 +28,10 @@
|
|||
, bzip2
|
||||
, libgcrypt
|
||||
, sqlite
|
||||
|
||||
, stateDir ? "/var"
|
||||
, storeDir ? "/gnu/store"
|
||||
, confDir ? "/etc"
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
|
@ -100,8 +104,9 @@ stdenv.mkDerivation rec {
|
|||
];
|
||||
|
||||
configureFlags = [
|
||||
"--localstatedir=/var"
|
||||
"--sysconfdir=/etc"
|
||||
"--with-store-dir=${storeDir}"
|
||||
"--localstatedir=${stateDir}"
|
||||
"--sysconfdir=${confDir}"
|
||||
"--with-bash-completion-dir=$(out)/etc/bash_completion.d"
|
||||
];
|
||||
|
||||
|
@ -132,7 +137,7 @@ stdenv.mkDerivation rec {
|
|||
homepage = "http://www.gnu.org/software/guix";
|
||||
license = licenses.gpl3Plus;
|
||||
mainProgram = "guix";
|
||||
maintainers = with maintainers; [ cafkafk ];
|
||||
maintainers = with maintainers; [ cafkafk foo-dogsquared ];
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue