photoncloud-monorepo/nix/ci/flake.nix

313 lines
11 KiB
Nix

{
description = "PhotonCloud local CI gates (Nix-first, CI-provider-agnostic)";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
rust-overlay = {
url = "github:oxalica/rust-overlay";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { self, nixpkgs, flake-utils, rust-overlay }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs { inherit system overlays; };
# Use a complete toolchain to ensure host `std` is present under Nix
# (fixes: "can't find crate for `std`").
rustToolchain = pkgs.rust-bin.stable.latest.complete;
# rust-overlay components (provide cargo-fmt / clippy-driver reliably in PATH)
rustfmtComponent = pkgs.rust-bin.stable.latest.rustfmt;
clippyComponent = pkgs.rust-bin.stable.latest.clippy;
wsList = [
"chainfire"
"flaredb"
"iam"
"plasmavmc"
"prismnet"
"flashdns"
"fiberlb"
"lightningstor"
"nightlight"
"creditservice"
"k8shost"
"apigateway"
"deployer"
];
gate = pkgs.writeShellApplication {
name = "photoncloud-gate";
runtimeInputs = with pkgs; [
bash
coreutils
findutils
gnugrep
gawk
git
rustToolchain
rustfmtComponent
clippyComponent
protobuf
llvmPackages.libclang
llvmPackages.clang
pkg-config
openssl
rocksdb
];
text = ''
set -euo pipefail
usage() {
cat <<'USAGE'
PhotonCloud local CI gates (provider-agnostic)
Usage:
photoncloud-gate [--tier 0|1|2] [--workspace <name>] [--shared-crates] [--no-logs] [--fix]
Tiers:
0: fmt + clippy + unit tests (lib) (fast, stable default)
1: tier0 + integration tests (--tests)
2: tier1 + ignored tests (-- --ignored)
Notes:
- Requires running inside a git checkout (uses `git rev-parse`).
- Logs are written to ./work/ci/<timestamp>/ by default (NOT .cccc/).
USAGE
}
tier="0"
only_ws=""
shared_crates="0"
no_logs="0"
fix="0"
while [[ $# -gt 0 ]]; do
case "$1" in
--tier)
tier="$2"; shift 2;;
--workspace)
only_ws="$2"; shift 2;;
--shared-crates)
shared_crates="1"; shift 1;;
--no-logs)
no_logs="1"; shift 1;;
--fix)
fix="1"; shift 1;;
-h|--help)
usage; exit 0;;
*)
echo "[gate] ERROR: unknown arg: $1" >&2
usage
exit 2
;;
esac
done
if [[ "$tier" != "0" && "$tier" != "1" && "$tier" != "2" ]]; then
echo "[gate] ERROR: --tier must be 0, 1, or 2 (got: $tier)" >&2
exit 2
fi
repo_root="$(git rev-parse --show-toplevel)"
# Avoid .cccc/ (managed externally). Use work/ for local artifacts.
timestamp="$(date -u +%Y%m%d-%H%M%S)"
logdir="$repo_root/work/ci/$timestamp"
if [[ "$no_logs" == "0" ]]; then
mkdir -p "$logdir"
echo "[gate] logs: $logdir"
else
echo "[gate] logs: disabled"
fi
# Prefer Nix-provided toolchain over user's ~/.cargo binaries.
#
# `nix run` keeps the user's PATH (often including ~/.cargo/bin), which can cause
# cargo subcommands (cargo-fmt, etc.) to resolve outside Nix.
# Force PATH to Nix-provided tools only.
export PATH="${pkgs.lib.makeBinPath [
pkgs.bash
pkgs.coreutils
pkgs.findutils
pkgs.gnugrep
pkgs.gawk
pkgs.git
rustToolchain
rustfmtComponent
clippyComponent
pkgs.protobuf
pkgs.llvmPackages.libclang
pkgs.llvmPackages.clang
pkgs.pkg-config
]}"
CARGO="${rustToolchain}/bin/cargo"
export RUSTC="${rustToolchain}/bin/rustc"
CARGO_FMT="${rustToolchain}/bin/cargo-fmt"
CARGO_CLIPPY="${rustToolchain}/bin/cargo-clippy"
fmt_rustfmt_args="-- --check"
if [[ "$fix" == "1" ]]; then
fmt_rustfmt_args=""
fi
export LIBCLANG_PATH="${pkgs.llvmPackages.libclang.lib}/lib";
export PROTOC="${pkgs.protobuf}/bin/protoc"
export ROCKSDB_LIB_DIR="${pkgs.rocksdb}/lib"
run_cmd() {
local ws="$1"; shift
local title="$1"; shift
local cmd="$*"
echo ""
echo "================================================================================"
echo "[gate][$ws] $title"
echo "--------------------------------------------------------------------------------"
echo "[gate][$ws] $cmd"
echo "================================================================================"
if [[ "$no_logs" == "0" ]]; then
local out
out="$logdir/$ws.$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '_' | tr -cd 'a-z0-9_').log"
(cd "$repo_root/$ws" && bash -c "$cmd") 2>&1 | tee "$out"
else
(cd "$repo_root/$ws" && bash -c "$cmd")
fi
}
run_shared_crate_cmd() {
local crate="$1"; shift
local manifest="$1"; shift
local title="$1"; shift
local cmd="$*"
echo ""
echo "================================================================================"
echo "[gate][shared:$crate] $title"
echo "--------------------------------------------------------------------------------"
echo "[gate][shared:$crate] $cmd"
echo "================================================================================"
if [[ "$no_logs" == "0" ]]; then
local out
out="$logdir/shared_${crate}.$(echo "$title" | tr '[:upper:]' '[:lower:]' | tr ' ' '_' | tr -cd 'a-z0-9_').log"
(cd "$repo_root" && bash -c "$cmd") 2>&1 | tee "$out"
else
(cd "$repo_root" && bash -c "$cmd")
fi
}
run_shared_crates() {
local manifests=()
while IFS= read -r manifest; do
manifests+=("$manifest")
done < <(find "$repo_root/crates" -mindepth 2 -maxdepth 2 -name Cargo.toml | sort)
if [[ "''${#manifests[@]}" -eq 0 ]]; then
echo "[gate] WARN: no shared crate manifests found under crates/"
return 0
fi
for manifest in "''${manifests[@]}"; do
local crate
crate="$(basename "$(dirname "$manifest")")"
run_shared_crate_cmd "$crate" "$manifest" "fmt" "$CARGO_FMT fmt --manifest-path \"$manifest\" $fmt_rustfmt_args"
run_shared_crate_cmd "$crate" "$manifest" "clippy" "$CARGO_CLIPPY clippy --manifest-path \"$manifest\" --all-targets -- -D warnings"
run_shared_crate_cmd "$crate" "$manifest" "test (tier0 unit)" "$CARGO test --manifest-path \"$manifest\" --lib"
if [[ "$tier" == "1" || "$tier" == "2" ]]; then
run_shared_crate_cmd "$crate" "$manifest" "test (tier1 integration)" "$CARGO test --manifest-path \"$manifest\" --tests"
fi
if [[ "$tier" == "2" ]]; then
run_shared_crate_cmd "$crate" "$manifest" "test (tier2 ignored)" "$CARGO test --manifest-path \"$manifest\" --tests -- --ignored"
fi
done
}
if [[ "$shared_crates" == "1" ]]; then
run_shared_crates
echo ""
echo "[gate] OK (tier=$tier, shared-crates)"
exit 0
fi
for ws in ${pkgs.lib.concatStringsSep " " wsList}; do
if [[ -n "$only_ws" && "$only_ws" != "$ws" ]]; then
continue
fi
if [[ ! -f "$repo_root/$ws/Cargo.toml" ]]; then
echo "[gate] WARN: skipping $ws (no Cargo.toml)"
continue
fi
# Format gate: call Nix-provided `cargo-fmt` directly (avoid resolving ~/.cargo/bin/cargo-fmt).
#
# NOTE: Avoid `--all` here; with path-dependencies it may traverse outside the workspace directory.
run_cmd "$ws" "fmt" "$CARGO_FMT fmt $fmt_rustfmt_args"
# Lint gate: call Nix-provided `cargo-clippy` directly (avoid resolving ~/.cargo/bin/cargo-clippy).
run_cmd "$ws" "clippy" "$CARGO_CLIPPY clippy --workspace --all-targets -- -D warnings"
run_cmd "$ws" "test (tier0 unit)" "$CARGO test --workspace --lib"
if [[ "$tier" == "1" || "$tier" == "2" ]]; then
run_cmd "$ws" "test (tier1 integration)" "$CARGO test --workspace --tests"
fi
if [[ "$tier" == "2" ]]; then
run_cmd "$ws" "test (tier2 ignored)" "$CARGO test --workspace --tests -- --ignored"
fi
done
echo ""
echo "[gate] OK (tier=$tier)"
'';
};
in
{
packages.gate = gate;
apps.gate = flake-utils.lib.mkApp {
drv = gate;
};
# CI-optimized gate (alias)
packages.gate-ci = gate;
# Checks are minimal and mirror tier0 (provider-agnostic).
checks.gate-tier0 = pkgs.runCommand "photoncloud-gate-tier0" { } ''
mkdir -p $out
${gate}/bin/photoncloud-gate --tier 0 --no-logs
touch $out/ok
'';
devShells.default = pkgs.mkShell {
name = "photoncloud-ci-dev";
buildInputs = with pkgs; [
rustToolchain
protobuf
llvmPackages.libclang
llvmPackages.clang
pkg-config
openssl
rocksdb
git
];
LIBCLANG_PATH = "${pkgs.llvmPackages.libclang.lib}/lib";
PROTOC = "${pkgs.protobuf}/bin/protoc";
ROCKSDB_LIB_DIR = "${pkgs.rocksdb}/lib";
};
}
);
}