aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuebinm2021-09-06 15:32:19 +0200
committerstuebinm2021-09-06 15:32:19 +0200
commit6eb992efdd68d80bc62bc7fc0562d8a2e6558096 (patch)
treebf72bcee338521b426bab20e82e15b00c48cf296
parent3afd5c2eac6e8e271457d7bf014521bd7e072411 (diff)
add nix build system and NixOS module
-rw-r--r--.gitignore1
-rw-r--r--client/elm-srcs.nix42
-rw-r--r--client/registry.datbin0 -> 113576 bytes
-rw-r--r--default.nix70
-rw-r--r--example.nix25
-rw-r--r--module.nix58
-rw-r--r--nix/sources.json26
-rw-r--r--nix/sources.nix174
8 files changed, 396 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index a38e4dd..2f6a1ba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/server/.stack-work/
/server/assets/
/client/elm-stuff/
+result*
diff --git a/client/elm-srcs.nix b/client/elm-srcs.nix
new file mode 100644
index 0000000..805c064
--- /dev/null
+++ b/client/elm-srcs.nix
@@ -0,0 +1,42 @@
+{
+
+ "elm/json" = {
+ sha256 = "0kjwrz195z84kwywaxhhlnpl3p251qlbm5iz6byd6jky2crmyqyh";
+ version = "1.1.3";
+ };
+
+ "elm/html" = {
+ sha256 = "1n3gpzmpqqdsldys4ipgyl1zacn0kbpc3g4v3hdpiyfjlgh8bf3k";
+ version = "1.0.0";
+ };
+
+ "elm/browser" = {
+ sha256 = "0nagb9ajacxbbg985r4k9h0jadqpp0gp84nm94kcgbr5sf8i9x13";
+ version = "1.0.2";
+ };
+
+ "bartavelle/json-helpers" = {
+ sha256 = "0k96qra2nq1j4j4ahfl98dkpkc6f2831mq5d5xxg27mp31qwq5nn";
+ version = "2.0.2";
+ };
+
+ "elm/core" = {
+ sha256 = "19w0iisdd66ywjayyga4kv2p1v9rxzqjaxhckp8ni6n8i0fb2dvf";
+ version = "1.0.5";
+ };
+
+ "elm/url" = {
+ sha256 = "0av8x5syid40sgpl5vd7pry2rq0q4pga28b4yykn9gd9v12rs3l4";
+ version = "1.0.0";
+ };
+
+ "elm/time" = {
+ sha256 = "0vch7i86vn0x8b850w1p69vplll1bnbkp8s383z7pinyg94cm2z1";
+ version = "1.0.0";
+ };
+
+ "elm/virtual-dom" = {
+ sha256 = "0q1v5gi4g336bzz1lgwpn5b1639lrn63d8y6k6pimcyismp2i1yg";
+ version = "1.0.2";
+ };
+}
diff --git a/client/registry.dat b/client/registry.dat
new file mode 100644
index 0000000..0aa76ff
--- /dev/null
+++ b/client/registry.dat
Binary files differ
diff --git a/default.nix b/default.nix
new file mode 100644
index 0000000..bc7afc9
--- /dev/null
+++ b/default.nix
@@ -0,0 +1,70 @@
+
+{pkgs ? import (import ./nix/sources.nix).nixpkgs {}}:
+
+let
+ defaultcards = pkgs.copyPathToStore ./cards.yaml;
+ drvs = with pkgs; rec {
+
+ # server derivation generated using cabal2nix
+ uplcg-server =
+ { mkDerivation, aeson, async, base, blaze-html, bytestring
+ , elm-bridge, fast-logger, hashable, http-types, lens, lib, mtl
+ , process, random, scotty, stm, template-haskell, text, time
+ , unordered-containers, uuid, vector, vector-algorithms
+ , vector-instances, vector-shuffling, wai, wai-extra
+ , wai-websockets, warp, websockets, yaml }:
+
+ mkDerivation {
+ pname = "uplcg";
+ version = "0.1.0";
+ src = ./server;
+ isLibrary = true;
+ isExecutable = true;
+ libraryHaskellDepends = [
+ aeson async base blaze-html bytestring elm-bridge fast-logger
+ hashable http-types lens mtl process random scotty stm
+ template-haskell text time unordered-containers uuid vector
+ vector-algorithms vector-instances vector-shuffling wai wai-extra
+ wai-websockets warp websockets yaml
+ ];
+ executableHaskellDepends = [ base ];
+ description = "Untitled PL Card Game";
+ license = lib.licenses.bsd3;
+
+ # TODO: get the actual version hash in here
+ patchPhase = ''
+ rm lib/Uplcg/Version.hs
+ echo "module Uplcg.Version (version) where" > lib/Uplcg/Version.hs
+ echo "version :: String" >> lib/Uplcg/Version.hs
+ echo "version = \"nixpkgs-uplcg\"" >> lib/Uplcg/Version.hs
+ '';
+ };
+
+ uplcg-client = { cards ? defaultcards, server}:
+ stdenv.mkDerivation {
+ name = "uplcg-client";
+ src = ./client;
+
+ buildInputs = [ elmPackages.elm ];
+
+ buildPhase = elmPackages.fetchElmDeps {
+ elmPackages = import ./client/elm-srcs.nix;
+ elmVersion = "0.19.1";
+ registryDat = ./client/registry.dat;
+ };
+
+ installPhase = ''
+ mkdir -p $out/assets
+ cp -r $src/* .
+ ${server}/bin/uplcg-generate-elm-types > src/Messages.elm
+ elm make src/Client.elm --optimize --output client.js
+ cp client.js $out/assets
+ cp style.css $out/assets
+ cp ${cards} $out/assets/cards.yaml
+ '';
+ };
+ };
+in with pkgs.lib; rec {
+ client = callPackageWith { inherit server; } drvs.uplcg-client {};
+ server = pkgs.haskellPackages.callPackage drvs.uplcg-server {};
+}
diff --git a/example.nix b/example.nix
new file mode 100644
index 0000000..249ae4a
--- /dev/null
+++ b/example.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+
+{
+ imports = [ ./module.nix ];
+
+ services.uplcg = {
+ enable = true;
+ port = 8080;
+ domain = "0.0.0.0";
+ # cards = {
+ # generic = {
+ # enabled = true;
+ # black = [
+ # "_ and white _"
+ # ];
+ # white = [
+ # "blue"
+ # "green"
+ # ];
+ # };
+ # };
+ };
+
+ networking.firewall.allowedTCPPorts = [ 8080 ];
+}
diff --git a/module.nix b/module.nix
new file mode 100644
index 0000000..169db9f
--- /dev/null
+++ b/module.nix
@@ -0,0 +1,58 @@
+{ config, lib, pkgs, ... }:
+
+let uplcg-pkgs = import ./default.nix {inherit pkgs; };
+in
+{
+ options.services.uplcg = with lib; {
+ enable = mkOption {
+ type = types.bool;
+ default = false;
+ };
+ cards = mkOption {
+ type = with types; nullOr (lazyAttrsOf (lazyAttrsOf (oneOf [
+ (listOf str)
+ bool
+ ])));
+ default = null;
+ };
+ cardsPath = mkOption {
+ type = types.nullOr types.path;
+ default = null;
+ };
+ port = mkOption {
+ type = types.port;
+ default = 4001;
+ };
+ domain = mkOption {
+ type = types.str;
+ default = "localhost";
+ };
+ };
+
+ config =
+ let cfg = config.services.uplcg;
+ in lib.mkIf cfg.enable {
+ systemd.services.uplcg = {
+ description = "The Untitled Programming Language Card Game";
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+ environment = {
+ "UPLCG_HOSTNAME" = cfg.domain;
+ "UPLCG_PORT" = builtins.toString cfg.port;
+ };
+ script = ''
+ cd ${if cfg.cards != null || cfg.cardsPath != null
+ then uplcg-pkgs.client.override {
+ cards =
+ if cfg.cards == null
+ then cfg.cardsPath
+ else pkgs.writeText "uplcg-cards"
+ (lib.generators.toYAML {} cfg.cards);
+ }
+ else uplcg-pkgs.client
+ }
+ ${uplcg-pkgs.server}/bin/uplcg-server
+ '';
+ };
+ };
+}
diff --git a/nix/sources.json b/nix/sources.json
new file mode 100644
index 0000000..9d68750
--- /dev/null
+++ b/nix/sources.json
@@ -0,0 +1,26 @@
+{
+ "niv": {
+ "branch": "master",
+ "description": "Easy dependency management for Nix projects",
+ "homepage": "https://github.com/nmattia/niv",
+ "owner": "nmattia",
+ "repo": "niv",
+ "rev": "e0ca65c81a2d7a4d82a189f1e23a48d59ad42070",
+ "sha256": "1pq9nh1d8nn3xvbdny8fafzw87mj7gsmp6pxkdl65w2g18rmcmzx",
+ "type": "tarball",
+ "url": "https://github.com/nmattia/niv/archive/e0ca65c81a2d7a4d82a189f1e23a48d59ad42070.tar.gz",
+ "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+ },
+ "nixpkgs": {
+ "branch": "release-21.05",
+ "description": "Nix Packages collection",
+ "homepage": "",
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "fd6dba47199a7c249e64c1aa1fef01ee78e58481",
+ "sha256": "0a9njyymwpz9xjq7vbyz91kxv9gf1rfww0xx7rj191ahppzgwv6b",
+ "type": "tarball",
+ "url": "https://github.com/NixOS/nixpkgs/archive/fd6dba47199a7c249e64c1aa1fef01ee78e58481.tar.gz",
+ "url_template": "https://github.com/<owner>/<repo>/archive/<rev>.tar.gz"
+ }
+}
diff --git a/nix/sources.nix b/nix/sources.nix
new file mode 100644
index 0000000..1938409
--- /dev/null
+++ b/nix/sources.nix
@@ -0,0 +1,174 @@
+# This file has been generated by Niv.
+
+let
+
+ #
+ # The fetchers. fetch_<type> fetches specs of type <type>.
+ #
+
+ fetch_file = pkgs: name: spec:
+ let
+ name' = sanitizeName name + "-src";
+ in
+ if spec.builtin or true then
+ builtins_fetchurl { inherit (spec) url sha256; name = name'; }
+ else
+ pkgs.fetchurl { inherit (spec) url sha256; name = name'; };
+
+ fetch_tarball = pkgs: name: spec:
+ let
+ name' = sanitizeName name + "-src";
+ in
+ if spec.builtin or true then
+ builtins_fetchTarball { name = name'; inherit (spec) url sha256; }
+ else
+ pkgs.fetchzip { name = name'; inherit (spec) url sha256; };
+
+ fetch_git = name: spec:
+ let
+ ref =
+ if spec ? ref then spec.ref else
+ if spec ? branch then "refs/heads/${spec.branch}" else
+ if spec ? tag then "refs/tags/${spec.tag}" else
+ abort "In git source '${name}': Please specify `ref`, `tag` or `branch`!";
+ in
+ builtins.fetchGit { url = spec.repo; inherit (spec) rev; inherit ref; };
+
+ fetch_local = spec: spec.path;
+
+ fetch_builtin-tarball = name: throw
+ ''[${name}] The niv type "builtin-tarball" is deprecated. You should instead use `builtin = true`.
+ $ niv modify ${name} -a type=tarball -a builtin=true'';
+
+ fetch_builtin-url = name: throw
+ ''[${name}] The niv type "builtin-url" will soon be deprecated. You should instead use `builtin = true`.
+ $ niv modify ${name} -a type=file -a builtin=true'';
+
+ #
+ # Various helpers
+ #
+
+ # https://github.com/NixOS/nixpkgs/pull/83241/files#diff-c6f540a4f3bfa4b0e8b6bafd4cd54e8bR695
+ sanitizeName = name:
+ (
+ concatMapStrings (s: if builtins.isList s then "-" else s)
+ (
+ builtins.split "[^[:alnum:]+._?=-]+"
+ ((x: builtins.elemAt (builtins.match "\\.*(.*)" x) 0) name)
+ )
+ );
+
+ # The set of packages used when specs are fetched using non-builtins.
+ mkPkgs = sources: system:
+ let
+ sourcesNixpkgs =
+ import (builtins_fetchTarball { inherit (sources.nixpkgs) url sha256; }) { inherit system; };
+ hasNixpkgsPath = builtins.any (x: x.prefix == "nixpkgs") builtins.nixPath;
+ hasThisAsNixpkgsPath = <nixpkgs> == ./.;
+ in
+ if builtins.hasAttr "nixpkgs" sources
+ then sourcesNixpkgs
+ else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then
+ import <nixpkgs> {}
+ else
+ abort
+ ''
+ Please specify either <nixpkgs> (through -I or NIX_PATH=nixpkgs=...) or
+ add a package called "nixpkgs" to your sources.json.
+ '';
+
+ # The actual fetching function.
+ fetch = pkgs: name: spec:
+
+ if ! builtins.hasAttr "type" spec then
+ abort "ERROR: niv spec ${name} does not have a 'type' attribute"
+ else if spec.type == "file" then fetch_file pkgs name spec
+ else if spec.type == "tarball" then fetch_tarball pkgs name spec
+ else if spec.type == "git" then fetch_git name spec
+ else if spec.type == "local" then fetch_local spec
+ else if spec.type == "builtin-tarball" then fetch_builtin-tarball name
+ else if spec.type == "builtin-url" then fetch_builtin-url name
+ else
+ abort "ERROR: niv spec ${name} has unknown type ${builtins.toJSON spec.type}";
+
+ # If the environment variable NIV_OVERRIDE_${name} is set, then use
+ # the path directly as opposed to the fetched source.
+ replace = name: drv:
+ let
+ saneName = stringAsChars (c: if isNull (builtins.match "[a-zA-Z0-9]" c) then "_" else c) name;
+ ersatz = builtins.getEnv "NIV_OVERRIDE_${saneName}";
+ in
+ if ersatz == "" then drv else
+ # this turns the string into an actual Nix path (for both absolute and
+ # relative paths)
+ if builtins.substring 0 1 ersatz == "/" then /. + ersatz else /. + builtins.getEnv "PWD" + "/${ersatz}";
+
+ # Ports of functions for older nix versions
+
+ # a Nix version of mapAttrs if the built-in doesn't exist
+ mapAttrs = builtins.mapAttrs or (
+ f: set: with builtins;
+ listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set))
+ );
+
+ # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/lists.nix#L295
+ range = first: last: if first > last then [] else builtins.genList (n: first + n) (last - first + 1);
+
+ # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L257
+ stringToCharacters = s: map (p: builtins.substring p 1 s) (range 0 (builtins.stringLength s - 1));
+
+ # https://github.com/NixOS/nixpkgs/blob/0258808f5744ca980b9a1f24fe0b1e6f0fecee9c/lib/strings.nix#L269
+ stringAsChars = f: s: concatStrings (map f (stringToCharacters s));
+ concatMapStrings = f: list: concatStrings (map f list);
+ concatStrings = builtins.concatStringsSep "";
+
+ # https://github.com/NixOS/nixpkgs/blob/8a9f58a375c401b96da862d969f66429def1d118/lib/attrsets.nix#L331
+ optionalAttrs = cond: as: if cond then as else {};
+
+ # fetchTarball version that is compatible between all the versions of Nix
+ builtins_fetchTarball = { url, name ? null, sha256 }@attrs:
+ let
+ inherit (builtins) lessThan nixVersion fetchTarball;
+ in
+ if lessThan nixVersion "1.12" then
+ fetchTarball ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+ else
+ fetchTarball attrs;
+
+ # fetchurl version that is compatible between all the versions of Nix
+ builtins_fetchurl = { url, name ? null, sha256 }@attrs:
+ let
+ inherit (builtins) lessThan nixVersion fetchurl;
+ in
+ if lessThan nixVersion "1.12" then
+ fetchurl ({ inherit url; } // (optionalAttrs (!isNull name) { inherit name; }))
+ else
+ fetchurl attrs;
+
+ # Create the final "sources" from the config
+ mkSources = config:
+ mapAttrs (
+ name: spec:
+ if builtins.hasAttr "outPath" spec
+ then abort
+ "The values in sources.json should not have an 'outPath' attribute"
+ else
+ spec // { outPath = replace name (fetch config.pkgs name spec); }
+ ) config.sources;
+
+ # The "config" used by the fetchers
+ mkConfig =
+ { sourcesFile ? if builtins.pathExists ./sources.json then ./sources.json else null
+ , sources ? if isNull sourcesFile then {} else builtins.fromJSON (builtins.readFile sourcesFile)
+ , system ? builtins.currentSystem
+ , pkgs ? mkPkgs sources system
+ }: rec {
+ # The sources, i.e. the attribute set of spec name to spec
+ inherit sources;
+
+ # The "pkgs" (evaluated nixpkgs) to use for e.g. non-builtin fetchers
+ inherit pkgs;
+ };
+
+in
+mkSources (mkConfig {}) // { __functor = _: settings: mkSources (mkConfig settings); }