From 9f544ef8c185ff5e45619b2333dd63c445636e0f Mon Sep 17 00:00:00 2001 From: Fintan Halpenny Date: Wed, 6 Dec 2023 20:04:45 +0000 Subject: [PATCH] nix: switch to use nix flakes Nix Flakes[[0]] are a more modular way of expressing nix derivations. The `flake.nix` file describes: - How to build the project, or components of a projet, via `nix build` - What "checks" can run for a project, which can be executed via `nix flake check` - What binaries can be run, e.g. `nix run .#rad` to execute the CLI This approach is preferable since a lot of the Nix ecosystem is using Flakes -- despite it still being marked as experimental. One interesting benefit, that needs confirming, is that a check can be run from a remote Git source. So if someone creates a patch on Radicle, someone else with the `nix` binary can execute: nix flake check git+https://app.radicle.xyz/nodes/seed.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5?refs=patches/ This means that anyone with `nix` can check patches without having to fetch the patch itself. Another obvious benefit will be that it allows the Radicle binaries to enter the Nix ecosystem. The big caveat to both of the above is that, currently, the tests are failing while running `nix flake check`. They are currently disabled with `doCheck = false`. They are potentially failing due to some strange sandboxing issues which are being looked into. [0]: https://nixos.wiki/wiki/Flakes Signed-off-by: Fintan Halpenny X-Clacks-Overhead: GNU Terry Pratchett --- .config/nextest.toml | 4 + .envrc | 1 + .nix/default.nix | 21 --- .nix/shell.nix | 21 --- .nix/sources.json | 38 ----- .nix/sources.nix | 198 ----------------------- Cargo.toml | 1 + flake.lock | 121 ++++++++++++++ flake.nix | 227 +++++++++++++++++++++++++++ radicle-node/src/test/environment.rs | 2 +- radicle-term/src/element.rs | 2 +- 11 files changed, 356 insertions(+), 280 deletions(-) create mode 100644 .config/nextest.toml create mode 100644 .envrc delete mode 100644 .nix/default.nix delete mode 100644 .nix/shell.nix delete mode 100644 .nix/sources.json delete mode 100644 .nix/sources.nix create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.config/nextest.toml b/.config/nextest.toml new file mode 100644 index 00000000..1cd9d388 --- /dev/null +++ b/.config/nextest.toml @@ -0,0 +1,4 @@ +# We want the tests to stop running if they are running +# longer than 1min +[profile.default] +slow-timeout = { period = "30s", terminate-after = 2, grace-period = "10s" } diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..44610e56 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake; diff --git a/.nix/default.nix b/.nix/default.nix deleted file mode 100644 index 84aa6676..00000000 --- a/.nix/default.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ sources ? import ./sources.nix -, pkgs ? import sources.nixpkgs { - overlays = [ (import sources.rust-overlay) ]; - } -, rust-overlay ? pkgs.rust-bin.stable.latest.default -}: - with pkgs; - mkShell { - name = "build"; - buildInputs = [ - # cargo tooling - cargo-deny - cargo-watch - - # hard dependencies - cmake - openssl - pkgconfig - rust-overlay - ]; - } diff --git a/.nix/shell.nix b/.nix/shell.nix deleted file mode 100644 index d11dfbf7..00000000 --- a/.nix/shell.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ sources ? import ./sources.nix -, pkgs ? import sources.nixpkgs { - overlays = [ (import sources.rust-overlay) ]; - } -}: -let - stable = pkgs.rust-bin.stable.latest.default; - rust-overlay = stable.override { - extensions = [ "rust-src" "rust-analysis" ]; - }; - devault = (pkgs.callPackage ./default.nix {}); -in - with pkgs; - mkShell { - name = "development"; - buildInputs = devault.buildInputs ++ [ - gcc - ripgrep - rust-analyzer - ]; - } diff --git a/.nix/sources.json b/.nix/sources.json deleted file mode 100644 index 7f896819..00000000 --- a/.nix/sources.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "niv": { - "branch": "master", - "description": "Easy dependency management for Nix projects", - "homepage": "https://github.com/nmattia/niv", - "owner": "nmattia", - "repo": "niv", - "rev": "723f0eeb969a730db3c30f977c2b66b9dce9fe4a", - "sha256": "0016l7230gd2kdh0g2w573r9a2krqb7x4ifcjhhsn4h1bwap7qr0", - "type": "tarball", - "url": "https://github.com/nmattia/niv/archive/723f0eeb969a730db3c30f977c2b66b9dce9fe4a.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - }, - "nixpkgs": { - "branch": "release-21.11", - "description": "Nix Packages collection", - "homepage": "", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2766f77c32e171a04d59b636a91083bae862274e", - "sha256": "1xk1f62n00z7q5i3pf4c8c4rlv5k4jwpgh0pqgzw1l40vhdkixk9", - "type": "tarball", - "url": "https://github.com/NixOS/nixpkgs/archive/2766f77c32e171a04d59b636a91083bae862274e.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - }, - "rust-overlay": { - "branch": "master", - "description": "Pure and reproducible nix overlay of binary distributed rust toolchains", - "homepage": "", - "owner": "oxalica", - "repo": "rust-overlay", - "rev": "e17bfe3baa0487f0671c9ed0e9057d10987ba7f7", - "sha256": "1kg6kfkc115gcip69vrxz74ryfmh44fdx2480l6qahkgl0d3pzxb", - "type": "tarball", - "url": "https://github.com/oxalica/rust-overlay/archive/e17bfe3baa0487f0671c9ed0e9057d10987ba7f7.tar.gz", - "url_template": "https://github.com///archive/.tar.gz" - } -} diff --git a/.nix/sources.nix b/.nix/sources.nix deleted file mode 100644 index fe3dadf7..00000000 --- a/.nix/sources.nix +++ /dev/null @@ -1,198 +0,0 @@ -# This file has been generated by Niv. - -let - - # - # The fetchers. fetch_ fetches specs of 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 = - spec.ref or ( - 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`!" - ); - submodules = spec.submodules or false; - submoduleArg = - let - nixSupportsSubmodules = builtins.compareVersions builtins.nixVersion "2.4" >= 0; - emptyArgWithWarning = - if submodules - then - builtins.trace - ( - "The niv input \"${name}\" uses submodules " - + "but your nix's (${builtins.nixVersion}) builtins.fetchGit " - + "does not support them" - ) - { } - else { }; - in - if nixSupportsSubmodules - then { inherit submodules; } - else emptyArgWithWarning; - in - builtins.fetchGit - ({ url = spec.repo; inherit (spec) rev; inherit ref; } // submoduleArg); - - 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 = == ./.; - in - if builtins.hasAttr "nixpkgs" sources - then sourcesNixpkgs - else if hasNixpkgsPath && ! hasThisAsNixpkgsPath then - import { } - else - abort - '' - Please specify either (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 (builtins.match "[a-zA-Z0-9]" c) == null 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 (name != null) { 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 (name != null) { 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 sourcesFile == null 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); } diff --git a/Cargo.toml b/Cargo.toml index 0dcf8142..d08e756c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +package.version = "0.9.0" members = [ "radicle", "radicle-cob", diff --git a/flake.lock b/flake.lock new file mode 100644 index 00000000..e13dbde6 --- /dev/null +++ b/flake.lock @@ -0,0 +1,121 @@ +{ + "nodes": { + "advisory-db": { + "flake": false, + "locked": { + "lastModified": 1701193254, + "narHash": "sha256-Hr7efA3GjwqBkGYKmd3XmGckdPQikbcCmOrq7fmTp3A=", + "owner": "rustsec", + "repo": "advisory-db", + "rev": "43af5fef0591531a72ebb86c5f1c623ee95c62fe", + "type": "github" + }, + "original": { + "owner": "rustsec", + "repo": "advisory-db", + "type": "github" + } + }, + "crane": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1701622587, + "narHash": "sha256-o3XhxCCyrUHZ0tlta2W7/MuXzy+n0+BUt3rKFK3DIK4=", + "owner": "ipetkov", + "repo": "crane", + "rev": "c09d2cbe84cc2adfe1943cb2a0b55a71c835ca9a", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, + "fenix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ], + "rust-analyzer-src": [] + }, + "locked": { + "lastModified": 1701930186, + "narHash": "sha256-t9uIiU1fE7VXi3t460iOuAUW8Ece9mowRKva1aw48zI=", + "owner": "nix-community", + "repo": "fenix", + "rev": "aeb764a8e4c16da3f36b200a302552d8e457b9f4", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "fenix", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1701680307, + "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1701961404, + "narHash": "sha256-ieWHyh6kJtabQYUam/dXXi22MgcgLQvl+f2x/95n+us=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "8fef9eee026f0d95c06b5880ef9c1af0f643aadf", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "release-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "advisory-db": "advisory-db", + "crane": "crane", + "fenix": "fenix", + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 00000000..e930c2fe --- /dev/null +++ b/flake.nix @@ -0,0 +1,227 @@ +{ + description = "Radicle"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/release-23.11"; + + crane = { + url = "github:ipetkov/crane"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + + fenix = { + url = "github:nix-community/fenix"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.rust-analyzer-src.follows = ""; + }; + + flake-utils.url = "github:numtide/flake-utils"; + + advisory-db = { + url = "github:rustsec/advisory-db"; + flake = false; + }; + }; + + nixConfig = { + keepOutputs = true; + }; + + outputs = { + self, + nixpkgs, + crane, + fenix, + flake-utils, + advisory-db, + ... + }: + flake-utils.lib.eachDefaultSystem (system: let + pname = "Heartwood"; + pkgs = nixpkgs.legacyPackages.${system}; + + inherit (pkgs) lib; + + craneLib = crane.lib.${system}; + + srcFilters = path: type: + # Allow sql schemas + (lib.hasSuffix "\.sql" path) + || + # Allow diff files for testing purposes + (lib.hasSuffix "\.diff" path) + || + # Allow md files for testing purposes + (lib.hasSuffix "\.md" path) + || + # Default filter from crane (allow .rs files) + (craneLib.filterCargoSources path type); + + src = lib.cleanSourceWith { + src = ./.; + filter = srcFilters; + }; + + # Common arguments can be set here to avoid repeating them later + commonArgs = { + inherit pname; + inherit src; + strictDeps = true; + + buildInputs = + [ + pkgs.git + # Add additional build inputs here + ] + ++ lib.optionals pkgs.stdenv.isDarwin [ + # Additional darwin specific inputs can be set here + pkgs.libiconv + ]; + }; + + # Build *just* the cargo dependencies, so we can reuse + # all of that work (e.g. via cachix) when running in CI + cargoArtifacts = + craneLib.buildDepsOnly commonArgs; + + # Build the actual crate itself, reusing the dependency + # artifacts from above. + radicle = craneLib.buildPackage (commonArgs + // { + inherit (craneLib.crateNameFromCargoToml {cargoToml = ./radicle/Cargo.toml;}); + doCheck = false; + inherit cargoArtifacts; + }); + in { + # Formatter + formatter = pkgs.alejandra; + + # Set of checks that are run: `nix flake check` + checks = { + # Build the crate as part of `nix flake check` for convenience + inherit radicle; + + # Run clippy (and deny all warnings) on the crate source, + # again, reusing the dependency artifacts from above. + # + # Note that this is done as a separate derivation so that + # we can block the CI if there are issues here, but not + # prevent downstream consumers from building our crate by itself. + clippy = craneLib.cargoClippy (commonArgs + // { + inherit cargoArtifacts; + cargoClippyExtraArgs = "--all-targets -- --deny warnings"; + }); + + doc = craneLib.cargoDoc (commonArgs + // { + inherit cargoArtifacts; + }); + + # Check formatting + fmt = craneLib.cargoFmt { + inherit pname; + inherit src; + }; + + # TODO: audits are failing so skip this check for now + # Audit dependencies + # audit = craneLib.cargoAudit { + # inherit src advisory-db; + # }; + + # Audit licenses + deny = craneLib.cargoDeny { + inherit pname; + inherit src; + }; + + # Run tests with cargo-nextest + nextest = craneLib.cargoNextest (commonArgs + // { + inherit cargoArtifacts; + partitions = 1; + partitionType = "count"; + nativeBuildInputs = [ + # git is required so the sandbox can access it. + pkgs.git + # Ensure that `git-remote-rad` is present for testing. + self.packages.${system}.radicle-remote-helper + ]; + # Ensure dev is used since we rely on env variables being + # set in tests. + buildPhase = '' + export CARGO_PROFILE=dev; + ''; + }); + }; + + packages = { + default = radicle; + radicle-remote-helper = craneLib.buildPackage (commonArgs + // { + inherit (craneLib.crateNameFromCargoToml {cargoToml = ./radicle-remote-helper/Cargo.toml;}); + inherit cargoArtifacts; + cargoBuildCommand = "cargo build --release -p radicle-remote-helper"; + doCheck = false; + }); + radicle-cli = craneLib.buildPackage (commonArgs + // { + inherit (craneLib.crateNameFromCargoToml {cargoToml = ./radicle-cli/Cargo.toml;}); + inherit cargoArtifacts; + cargoBuildCommand = "cargo build --release -p radicle-cli"; + doCheck = false; + }); + radicle-node = craneLib.buildPackage (commonArgs + // { + inherit (craneLib.crateNameFromCargoToml {cargoToml = ./radicle-node/Cargo.toml;}); + inherit cargoArtifacts; + cargoBuildCommand = "cargo build --release -p radicle-node"; + doCheck = false; + }); + radicle-httpd = craneLib.buildPackage (commonArgs + // { + inherit (craneLib.crateNameFromCargoToml {cargoToml = ./radicle-httpd/Cargo.toml;}); + inherit cargoArtifacts; + cargoBuildCommand = "cargo build --release -p radicle-httpd"; + doCheck = false; + }); + }; + + apps.default = flake-utils.lib.mkApp { + drv = radicle; + }; + + apps.rad = flake-utils.lib.mkApp { + name = "rad"; + drv = self.packages.${system}.radicle-cli; + }; + + apps.git-remote-rad = flake-utils.lib.mkApp { + name = "git-remote-rad"; + drv = self.packages.${system}.radicle-remote-helper; + }; + + apps.radicle-node = flake-utils.lib.mkApp { + name = "radicle-node"; + drv = self.packages.${system}.radicle-node; + }; + + apps.radicle-httpd = flake-utils.lib.mkApp { + name = "radicle-httpd"; + drv = self.packages.${system}.radicle-httpd; + }; + + devShells.default = craneLib.devShell { + # Inherit inputs from checks. + checks = self.checks.${system}; + + # Extra inputs can be added here; cargo and rustc are provided by default. + packages = [ + pkgs.cargo-watch + pkgs.ripgrep + pkgs.rust-analyzer + ]; + }; + }); +} diff --git a/radicle-node/src/test/environment.rs b/radicle-node/src/test/environment.rs index c80c90e9..6f6be171 100644 --- a/radicle-node/src/test/environment.rs +++ b/radicle-node/src/test/environment.rs @@ -220,7 +220,7 @@ impl NodeHandle { &'a self, remotes: impl IntoIterator>, ) -> BTreeSet<(Id, NodeId)> { - converge(iter::once(self).chain(remotes.into_iter())) + converge(iter::once(self).chain(remotes)) } /// Wait until this node's routing table contains the given routes. diff --git a/radicle-term/src/element.rs b/radicle-term/src/element.rs index 52a0ef71..6b504fd9 100644 --- a/radicle-term/src/element.rs +++ b/radicle-term/src/element.rs @@ -201,7 +201,7 @@ impl Line { /// Add multiple items to this line. pub fn extend(mut self, items: impl IntoIterator) -> Self { - self.items.extend(items.into_iter()); + self.items.extend(items); self }