This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/flake.nix
T
Miguel PalhasandClaude Opus 5 401afbc812 feat(maestro): add a nix flake and Home Manager module
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>
2026-08-22 10:53:40 +01:00

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
];
};
};
};
}