Ships `maestro`, `maestro-web` and the SPA bundle as flake packages, plus a `services.maestro` Home Manager module that runs the daemon and the web UI as systemd user services. `maestro-web`'s build.rs now honours MAESTRO_WEB_DIST and hands the resolved bundle path to `include_dir!` through MAESTRO_WEB_BUNDLE_DIR. Without it the Nix build would have to run `pnpm install` mid-compile, which the sandbox has no network for. Unset, the pnpm path is unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
177 lines
5.9 KiB
Nix
177 lines
5.9 KiB
Nix
{
|
|
description = "Maestro — terminal-session orchestrator for one machine";
|
|
|
|
inputs = {
|
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
flake-parts.url = "github:hercules-ci/flake-parts";
|
|
crane.url = "github:ipetkov/crane";
|
|
};
|
|
|
|
outputs =
|
|
inputs@{ flake-parts, ... }:
|
|
flake-parts.lib.mkFlake { inherit inputs; } {
|
|
systems = [
|
|
"x86_64-linux"
|
|
"aarch64-linux"
|
|
];
|
|
|
|
imports = [ ./nix/home-manager-module.nix ];
|
|
|
|
perSystem =
|
|
{
|
|
lib,
|
|
pkgs,
|
|
self',
|
|
...
|
|
}:
|
|
let
|
|
craneLib = inputs.crane.mkLib pkgs;
|
|
|
|
version = (lib.importTOML ./Cargo.toml).workspace.package.version;
|
|
|
|
# Cargo source filtering keeps only *.rs, *.toml and Cargo.lock. The
|
|
# SPA is built separately (below) and handed to the Rust build as a
|
|
# path, so nothing under web/ needs to be in this source tree.
|
|
commonArgs = {
|
|
src = craneLib.cleanCargoSource ./.;
|
|
inherit version;
|
|
strictDeps = true;
|
|
# rustls everywhere (reqwest, tokio-tungstenite): no openssl, no
|
|
# pkg-config, nothing to link against.
|
|
buildInputs = [ ];
|
|
nativeBuildInputs = [ ];
|
|
};
|
|
|
|
cargoArtifacts = craneLib.buildDepsOnly (commonArgs // { pname = "maestro-workspace"; });
|
|
|
|
# Runtime dependencies the daemon shells out to. Suffixed onto PATH so
|
|
# a user's own tmux/git still wins inside spawned sessions.
|
|
runtimeDeps = [
|
|
pkgs.tmux
|
|
pkgs.git
|
|
];
|
|
|
|
maestro = craneLib.buildPackage (
|
|
commonArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
pname = "maestro";
|
|
cargoExtraArgs = "--package maestro";
|
|
doCheck = false;
|
|
nativeBuildInputs = commonArgs.nativeBuildInputs ++ [ pkgs.makeWrapper ];
|
|
postInstall = ''
|
|
wrapProgram $out/bin/maestro \
|
|
--suffix PATH : ${lib.makeBinPath runtimeDeps}
|
|
'';
|
|
meta = {
|
|
description = "Unified maestro binary: CLI + per-host daemon + TUI";
|
|
homepage = "https://git.naps.pt/naps62/maestro";
|
|
license = lib.licenses.mit;
|
|
platforms = lib.platforms.linux;
|
|
mainProgram = "maestro";
|
|
};
|
|
}
|
|
);
|
|
|
|
# The SPA, built on its own so the Rust build never needs node or a
|
|
# network fetch. `pnpm.fetchDeps` pins the store from pnpm-lock.yaml;
|
|
# bump `hash` whenever that lockfile changes (set it to
|
|
# lib.fakeHash, build, copy the `got:` value).
|
|
webFrontend = pkgs.stdenv.mkDerivation (finalAttrs: {
|
|
pname = "maestro-web-frontend";
|
|
inherit version;
|
|
src = ./web;
|
|
|
|
nativeBuildInputs = [
|
|
pkgs.nodejs
|
|
# pnpm 10, not the default 11: web/pnpm-lock.yaml is
|
|
# lockfileVersion 9.0, and package.json pins pnpm@9.15.4.
|
|
pkgs.pnpm_10
|
|
(pkgs.pnpmConfigHook.override { pnpm = pkgs.pnpm_10; })
|
|
];
|
|
|
|
pnpmDeps = (pkgs.fetchPnpmDeps.override { pnpm = pkgs.pnpm_10; }) {
|
|
inherit (finalAttrs) pname version src;
|
|
fetcherVersion = 3;
|
|
hash = "sha256-Lhi41AtCK69SM4+7zih19+Njf7o2hyXrAV0W6VO9YxU=";
|
|
};
|
|
|
|
buildPhase = ''
|
|
runHook preBuild
|
|
pnpm build
|
|
runHook postBuild
|
|
'';
|
|
|
|
# $out IS the bundle directory — that is what MAESTRO_WEB_DIST and
|
|
# `include_dir!` expect, so index.html sits at its root.
|
|
installPhase = ''
|
|
runHook preInstall
|
|
cp -r dist $out
|
|
runHook postInstall
|
|
'';
|
|
|
|
meta = {
|
|
description = "Maestro browser SPA bundle";
|
|
license = lib.licenses.mit;
|
|
};
|
|
});
|
|
|
|
maestro-web = craneLib.buildPackage (
|
|
commonArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
pname = "maestro-web";
|
|
cargoExtraArgs = "--package maestro-web";
|
|
doCheck = false;
|
|
# Skips pnpm entirely in build.rs and embeds this bundle as-is.
|
|
MAESTRO_WEB_DIST = webFrontend;
|
|
meta = {
|
|
description = "Maestro web UI: serves the SPA and proxies to a co-located daemon";
|
|
homepage = "https://git.naps.pt/naps62/maestro";
|
|
license = lib.licenses.mit;
|
|
platforms = lib.platforms.linux;
|
|
mainProgram = "maestro-web";
|
|
};
|
|
}
|
|
);
|
|
in
|
|
{
|
|
packages = {
|
|
default = maestro;
|
|
inherit maestro maestro-web webFrontend;
|
|
};
|
|
|
|
checks = {
|
|
inherit maestro maestro-web;
|
|
|
|
maestro-fmt = craneLib.cargoFmt { inherit (commonArgs) src; };
|
|
|
|
maestro-clippy = craneLib.cargoClippy (
|
|
commonArgs
|
|
// {
|
|
inherit cargoArtifacts;
|
|
pname = "maestro-clippy";
|
|
# maestro-web is excluded: clippy on it would run build.rs, and
|
|
# a workspace-wide MAESTRO_WEB_DIST would make every other
|
|
# crate's cache key depend on the frontend. `checks.maestro-web`
|
|
# already compiles it.
|
|
cargoClippyExtraArgs = "--workspace --exclude maestro-web --all-targets -- --deny warnings";
|
|
}
|
|
);
|
|
};
|
|
|
|
devShells.default = craneLib.devShell {
|
|
inherit (self') checks;
|
|
packages = [
|
|
pkgs.rust-analyzer
|
|
pkgs.tmux
|
|
pkgs.git
|
|
pkgs.just
|
|
pkgs.nodejs
|
|
pkgs.pnpm_10
|
|
];
|
|
};
|
|
};
|
|
};
|
|
}
|