diff --git a/.envrc b/.envrc index 3550a30..c8bde17 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ use flake +watch_file flake-modules/ diff --git a/flake-modules/devshell/default.nix b/flake-modules/devshell/default.nix index 67da893..8e245b1 100644 --- a/flake-modules/devshell/default.nix +++ b/flake-modules/devshell/default.nix @@ -1,5 +1,5 @@ localFlake: { ... }: { - perSystem = { pkgs, rust, inputs', ... }: let + perSystem = { pkgs, rust-toolchain, inputs', ... }: let mkShell = pkgs.devshell.mkShell; # note; there's a UTF-8 control character in the esc string below @@ -46,7 +46,7 @@ localFlake: { ... }: { devShells.default = mkShell { packages = with pkgs; [ # rust dev toolchain (with RA), built from current nixpkgs - (rust.dev-toolchain pkgs) + (rust-toolchain.dev-toolchain pkgs) # tools mprocs # runs commands in parallel @@ -75,6 +75,14 @@ localFlake: { ... }: { help = "Runs nix flake checks."; category = "[nix actions]"; } + { + name = "site-container"; + command = '' + docker load -i $(nix build .#site-server-container --print-out-paths --no-link) && \ + docker run --rm -p 3000:3000 site-server + ''; + help = "Run the ${bin-hl "site-server"} in a container."; + } ] ++ tikv-docker-commands ; diff --git a/flake-modules/rust-builds/default.nix b/flake-modules/rust-builds/default.nix index 9f0df32..a155b03 100644 --- a/flake-modules/rust-builds/default.nix +++ b/flake-modules/rust-builds/default.nix @@ -1,59 +1,6 @@ -localFlake: { inputs, ... }: { - perSystem = { pkgs, rust, ... }: let - filter = inputs.nix-filter.lib; - - # configure the source - src = filter { - root = ../../.; # project root - include = [ - "crates" "Cargo.toml" "Cargo.lock" # typical rust source - ".cargo" # extra rust config - (filter.matchExt "toml") # extra toml used by other projects - "media" # static assets - ]; - }; - - # build arguments for the whole workspace - common-args = { - inherit src; - strictDeps = true; - - pname = "picturepro"; - version = "0.1"; - doCheck = false; - - # inputs assumed to be relevant for all crates - nativeBuildInputs = with pkgs; [ - pkg-config - ]; - buildInputs = [ ]; - }; - - # build the deps for the whole workspace - cargoArtifacts = rust.craneLib.buildDepsOnly common-args; - - - in { - checks = { - # run clippy, denying warnings - rust-cargo-clippy = rust.craneLib.cargoClippy (common-args // { - inherit cargoArtifacts; - cargoClippyExtraArgs = "--all-targets --no-deps -- --deny warnings"; - }); - # run rust-doc, denying warnings - rust-cargo-docs = rust.craneLib.cargoDoc (common-args // { - inherit cargoArtifacts; - cargoClippyExtraArgs = "--no-deps"; - RUSTDOCFLAGS = "-D warnings"; - }); - # run rust tests with nextest - rust-cargo-nextest = rust.craneLib.cargoNextest (common-args // { - inherit cargoArtifacts; - partitions = 1; - partitionType = "count"; - }); - # run cargo fmt, failing if not already formatted perfectly - rust-cargo-fmt = rust.craneLib.cargoFmt common-args; - }; - }; +localFlake: { ... }: { + imports = [ + ./workspace.nix + ./leptos.nix + ]; } diff --git a/flake-modules/rust-builds/leptos.nix b/flake-modules/rust-builds/leptos.nix new file mode 100644 index 0000000..76ab037 --- /dev/null +++ b/flake-modules/rust-builds/leptos.nix @@ -0,0 +1,127 @@ +{ inputs, ... }: { + perSystem = { pkgs, rust-toolchain, rust-workspace, system, ... }: let + inherit (rust-workspace.workspace-base-args) src; + inherit (rust-toolchain) craneLib; + + workspace-cargo-manifest = builtins.fromTOML (builtins.readFile ../../Cargo.toml); + leptos-options = builtins.elemAt workspace-cargo-manifest.workspace.metadata.leptos 0; + + js2nix = pkgs.callPackage (pkgs.fetchgit { + url = "https://github.com/canva-public/js2nix"; + hash = "sha256-Bmv0ERVeb6vjYzy4MuCDgSiz9fSm/Bhg+Xk3AxPisBw="; + }) { }; + style-root = ../../crates/site-app/style/tailwind; + + style-node-env = (js2nix { + package-json = style-root + "/package.json"; + yarn-lock = style-root + "/yarn.lock"; + }).nodeModules; + + common-args = { + inherit src; + pname = leptos-options.bin-package; + version = "0.1.0"; + + doCheck = false; + + nativeBuildInputs = [ + pkgs.pkg-config + pkgs.binaryen # provides wasm-opt for cargo-leptos + ] ++ pkgs.lib.optionals (system == "x86_64-linux") [ + pkgs.nasm # wasm compiler only for x86_64-linux + ]; + buildInputs = [ + ]; + }; + # build the deps for the frontend bundle, and export the target folder + site-frontend-deps = craneLib.mkCargoDerivation (common-args // { + pname = "site-frontend-deps"; + src = craneLib.mkDummySrc common-args; + cargoArtifacts = null; + doInstallCargoArtifacts = true; + + buildPhaseCargoCommand = '' + cargo build \ + --package=${leptos-options.lib-package} \ + --lib \ + --target-dir=/build/source/target/front \ + --target=wasm32-unknown-unknown \ + --no-default-features \ + --profile=${leptos-options.lib-profile-release} + ''; + }); + # build the deps for the server binary, and export the target folder + site-server-deps = craneLib.mkCargoDerivation (common-args // { + pname = "site-server-deps"; + src = craneLib.mkDummySrc common-args; + cargoArtifacts = site-frontend-deps; + doInstallCargoArtifacts = true; + + buildPhaseCargoCommand = '' + cargo build \ + --package=${leptos-options.bin-package} \ + --no-default-features \ + --release + ''; + }); + + # build the binary and bundle using cargo leptos + site-server = craneLib.buildPackage (common-args // { + # add inputs needed for leptos build + nativeBuildInputs = common-args.nativeBuildInputs ++ (with pkgs; [ + cargo-leptos dart-sass tailwindcss + ]); + + # link the style packages node_modules into the build directory + preBuild = '' + ln -s ${style-node-env} \ + ./crates/site-app/style/tailwind/node_modules + ''; + + # enable hash_files again + buildPhaseCargoCommand = '' + LEPTOS_HASH_FILES=true cargo leptos build --release -vvv + ''; + + installPhaseCommand = '' + mkdir -p $out/bin + cp target/release/site-server $out/bin/ + cp target/release/hash.txt $out/bin/ + cp -r target/site $out/bin/ + ''; + + doCheck = false; + cargoArtifacts = site-server-deps; + }); + + site-server-container = pkgs.dockerTools.buildLayeredImage { + name = leptos-options.bin-package; + tag = "latest"; + contents = [ site-server pkgs.cacert ]; + config = { + # runs the executable with tini: https://github.com/krallin/tini + # this does signal forwarding and zombie process reaping + # this should be removed if using something like firecracker (i.e. on fly.io) + Entrypoint = [ "${pkgs.tini}/bin/tini" "site-server" "--" ]; + WorkingDir = "${site-server}/bin"; + # we provide the env variables that we get from Cargo.toml during development + # these can be overridden when the container is run, but defaults are needed + Env = [ + "LEPTOS_OUTPUT_NAME=${leptos-options.name}" + "LEPTOS_SITE_ROOT=${leptos-options.name}" + "LEPTOS_SITE_PKG_DIR=${leptos-options.site-pkg-dir}" + "LEPTOS_SITE_ADDR=0.0.0.0:3000" + "LEPTOS_RELOAD_PORT=${builtins.toString leptos-options.reload-port}" + "LEPTOS_ENV=PROD" + # https://github.com/leptos-rs/cargo-leptos/issues/271 + "LEPTOS_HASH_FILES=true" + ]; + }; + }; + in { + packages = { + site-server = site-server; + site-server-container = site-server-container; + }; + }; +} diff --git a/flake-modules/rust-builds/workspace.nix b/flake-modules/rust-builds/workspace.nix new file mode 100644 index 0000000..7125189 --- /dev/null +++ b/flake-modules/rust-builds/workspace.nix @@ -0,0 +1,61 @@ +{ inputs, ... }: { + perSystem = { pkgs, rust-toolchain, ... }: let + filter = inputs.nix-filter.lib; + + # configure the source + src = filter { + root = ../../.; # project root + include = [ + "crates" "Cargo.toml" "Cargo.lock" # typical rust source + ".cargo" # extra rust config + (filter.matchExt "toml") # extra toml used by other projects + "media" # static assets + ]; + }; + + # build arguments for the whole workspace + workspace-base-args = { + inherit src; + strictDeps = true; + + pname = "picturepro"; + version = "0.1"; + doCheck = false; + + # inputs assumed to be relevant for all crates + nativeBuildInputs = with pkgs; [ + pkg-config + ]; + buildInputs = [ ]; + }; + + # build the deps for the whole workspace + workspace-base-cargo-artifacts = rust-toolchain.craneLib.buildDepsOnly workspace-base-args; + in { + # pass back to the flake + config._module.args.rust-workspace = { + inherit workspace-base-args workspace-base-cargo-artifacts; + }; + config.checks = { + # run clippy, denying warnings + rust-cargo-clippy = rust-toolchain.craneLib.cargoClippy (workspace-base-args // { + cargoArtifacts = workspace-base-cargo-artifacts; + cargoClippyExtraArgs = "--all-targets --no-deps -- --deny warnings"; + }); + # run rust-doc, denying warnings + rust-cargo-docs = rust-toolchain.craneLib.cargoDoc (workspace-base-args // { + cargoArtifacts = workspace-base-cargo-artifacts; + cargoClippyExtraArgs = "--no-deps"; + RUSTDOCFLAGS = "-D warnings"; + }); + # run rust tests with nextest + rust-cargo-nextest = rust-toolchain.craneLib.cargoNextest (workspace-base-args // { + cargoArtifacts = workspace-base-cargo-artifacts; + partitions = 1; + partitionType = "count"; + }); + # run cargo fmt, failing if not already formatted perfectly + rust-cargo-fmt = rust-toolchain.craneLib.cargoFmt workspace-base-args; + }; + }; +} diff --git a/flake-modules/rust-toolchain/default.nix b/flake-modules/rust-toolchain/default.nix index f88ed4f..e7042bc 100644 --- a/flake-modules/rust-toolchain/default.nix +++ b/flake-modules/rust-toolchain/default.nix @@ -3,6 +3,7 @@ localFlake: { inputs, ... }: { # build the CI and dev toolchains toolchain = p: p.rust-bin.selectLatestNightlyWith (toolchain: toolchain.minimal.override { extensions = [ "rustfmt" "clippy" ]; + targets = [ "wasm32-unknown-unknown" ]; }); dev-toolchain = p: p.rust-bin.selectLatestNightlyWith (toolchain: toolchain.default.override { extensions = [ "rust-src" "rust-analyzer" ]; @@ -12,7 +13,7 @@ localFlake: { inputs, ... }: { # configure crane to use the CI toolchain craneLib = (inputs.crane.mkLib pkgs).overrideToolchain toolchain; in { - config._module.args.rust = { + config._module.args.rust-toolchain = { inherit toolchain dev-toolchain craneLib; }; };