feat(macos): setup nix-darwin pour builder l'app Winnie
L'app se build sur le Mac en trois commandes, le toolchain restant dans le devShell du flake winnie (jamais installé sur le Mac) : cd ~/git/nixos && just mac # déploie ce setup winnie-sync # attrape ~/git/winnie depuis tower (rsync ssh) winnie-ios # simulateur ; npm ci + tauri ios init au besoin - modules/home-mac/winnie : winnie-doctor [--fix], winnie-sync [--delete], winnie-shell, winnie-front, winnie-macos, winnie-build-macos, winnie-ios, winnie-build-ios, winnie-verify. Scripts en writeShellApplication (shellcheck au build) ; scripts.nix est séparé du module pour être constructible depuis tower avec les pkgs linux et testable sans Mac. - profiles/macos : flakes + trusted-users dans nix.conf, et system.activationScripts.winnie-xcode (xcode-select -s, licence Xcode, -runFirstLaunch) pour que seule l'installation App Store reste manuelle. - Embarque aussi le setup SSH entrant du Mac (sshd, clé hermes@tower, tunnel inverse autossh) qui était déjà dans l'arbre de travail. Vérifié depuis tower : nix eval du darwinConfigurations.macos (toplevel et home-manager-generation) + build des scripts en x86_64-linux (shellcheck) et rsync de bout en bout depuis le dépôt de tower.
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
# Environnement de build de l'app Winnie sur le Mac (front Svelte 5 + shell
|
||||||
|
# Tauri), pour que builder soit une commande, pas une procédure.
|
||||||
|
#
|
||||||
|
# Le toolchain (rust + triples iOS, node 22, cocoapods, tauri-cli, postgres) vit
|
||||||
|
# dans le devShell du flake `winnie` : ce module ne fait qu'exposer les
|
||||||
|
# commandes qui l'utilisent, chacune re-rentrant dans `nix develop`.
|
||||||
|
# winnie-doctor état du poste (Xcode, simulateur, dépôt, API) ; --fix
|
||||||
|
# winnie-sync rafraîchit ~/git/winnie depuis tower (LAN ou rack)
|
||||||
|
# winnie-shell devShell interactif
|
||||||
|
# winnie-front serveur Vite (HMR)
|
||||||
|
# winnie-macos app macOS en dev ; winnie-build-macos = .app/.dmg
|
||||||
|
# winnie-ios app iOS dans le simulateur ; winnie-build-ios = archive
|
||||||
|
# winnie-verify front + svelte-check + cargo check
|
||||||
|
#
|
||||||
|
# Ce qui reste manuel, forcément : Xcode (App Store, Apple en interdit la
|
||||||
|
# redistribution), le runtime du simulateur (winnie-doctor --fix le télécharge)
|
||||||
|
# et un compte Apple pour signer un build pour iPhone réel.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.my.winnie;
|
||||||
|
in {
|
||||||
|
options.my.winnie = {
|
||||||
|
enable = lib.mkEnableOption "l'environnement de build Winnie sur macOS";
|
||||||
|
|
||||||
|
repoRel = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "git/winnie";
|
||||||
|
description = "Chemin du dépôt winnie, relatif à $HOME.";
|
||||||
|
};
|
||||||
|
|
||||||
|
towerRepo = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "/home/marc/git/winnie";
|
||||||
|
description = "Chemin du même dépôt sur tower, source de winnie-sync (pas de remote git).";
|
||||||
|
};
|
||||||
|
|
||||||
|
towerHosts = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.str;
|
||||||
|
default = ["towerlocal" "tower"];
|
||||||
|
description = ''
|
||||||
|
Alias ssh essayés dans l'ordre par winnie-sync : le LAN d'abord
|
||||||
|
(towerlocal = 192.168.1.44), puis le tunnel par le VPS
|
||||||
|
(tower = 10.100.0.3 via rack, tenu par l'agent autossh du Mac).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
apiUrl = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "https://api.winnie.marcpartensky.com";
|
||||||
|
description = "API interrogée par winnie-doctor et par le front.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
home.packages = lib.attrValues (import ./scripts.nix {
|
||||||
|
inherit pkgs lib;
|
||||||
|
cfg = {
|
||||||
|
inherit (cfg) repoRel towerRepo towerHosts apiUrl;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
# Commandes de build Winnie pour macOS, en dérivations writeShellApplication :
|
||||||
|
# shellcheck tourne au build et chaque commande est versionnée avec la config.
|
||||||
|
#
|
||||||
|
# Séparé du module home-manager exprès : ce fichier s'importe tel quel depuis
|
||||||
|
# tower avec les pkgs x86_64-linux pour construire et tester les mêmes scripts
|
||||||
|
# sans Mac (le Mac les reconstruit avec ses pkgs darwin).
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
cfg, # { repoRel, towerRepo, towerHosts, apiUrl }
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
subst = text:
|
||||||
|
lib.replaceStrings
|
||||||
|
["@REPO_REL@" "@TOWER_REPO@" "@TOWER_HOSTS@" "@API_URL@" "@WINNIE_SYNC@"]
|
||||||
|
[
|
||||||
|
cfg.repoRel
|
||||||
|
cfg.towerRepo
|
||||||
|
(lib.concatMapStrings (h: "'${h}' ") cfg.towerHosts)
|
||||||
|
cfg.apiUrl
|
||||||
|
(lib.getExe sync)
|
||||||
|
]
|
||||||
|
text;
|
||||||
|
|
||||||
|
# Scripts exécutés DANS le devShell du flake winnie : leurs outils (npm, cargo,
|
||||||
|
# tauri, xcrun, simctl) viennent de ce shell, d'où runtimeInputs vide.
|
||||||
|
inner = name: file: pkgs.writeShellApplication {
|
||||||
|
name = "winnie-${name}-inner";
|
||||||
|
text = subst (builtins.readFile file);
|
||||||
|
};
|
||||||
|
|
||||||
|
# Commandes exposées : cd dans le dépôt puis ré-exécution de l'inner dans
|
||||||
|
# `nix develop`, pour que rien ne dépende du PATH du Mac.
|
||||||
|
dev = name: innerScript: pkgs.writeShellApplication {
|
||||||
|
name = "winnie-${name}";
|
||||||
|
runtimeInputs = [pkgs.nix];
|
||||||
|
text = ''
|
||||||
|
repo="$HOME/${cfg.repoRel}"
|
||||||
|
if [ ! -f "$repo/flake.nix" ]; then
|
||||||
|
echo "winnie-${name} : dépôt absent ($repo). Lance winnie-sync." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cd "$repo"
|
||||||
|
exec nix --extra-experimental-features 'nix-command flakes' develop -c ${lib.getExe innerScript} "$@"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Rafraîchit le dépôt depuis tower : rsync du dépôt local de tower (le dépôt
|
||||||
|
# n'a pas de remote git, tower est le seul original).
|
||||||
|
sync = pkgs.writeShellApplication {
|
||||||
|
name = "winnie-sync";
|
||||||
|
runtimeInputs = [pkgs.rsync pkgs.openssh];
|
||||||
|
text = subst (builtins.readFile ./scripts/sync.sh);
|
||||||
|
};
|
||||||
|
|
||||||
|
# État du poste : Xcode, SDK iOS, runtime du simulateur, node_modules, API.
|
||||||
|
# Xcode n'est pas dans le store Nix (App Store), donc ce diagnostic est la
|
||||||
|
# seule source de vérité, et --fix automatise tout sauf l'installation d'Xcode.
|
||||||
|
doctor = pkgs.writeShellApplication {
|
||||||
|
name = "winnie-doctor";
|
||||||
|
runtimeInputs = [pkgs.nix pkgs.curl pkgs.coreutils];
|
||||||
|
text = subst (builtins.readFile ./scripts/doctor.sh);
|
||||||
|
};
|
||||||
|
|
||||||
|
# DevShell interactif du projet, sans deviner le chemin du dépôt.
|
||||||
|
shell = pkgs.writeShellApplication {
|
||||||
|
name = "winnie-shell";
|
||||||
|
runtimeInputs = [pkgs.nix];
|
||||||
|
text = ''
|
||||||
|
repo="$HOME/${cfg.repoRel}"
|
||||||
|
if [ ! -f "$repo/flake.nix" ]; then
|
||||||
|
echo "winnie-shell : dépôt absent ($repo). Lance winnie-sync." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
cd "$repo"
|
||||||
|
exec nix --extra-experimental-features 'nix-command flakes' develop "$@"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
inherit doctor shell sync;
|
||||||
|
|
||||||
|
# Serveur Vite (HMR) du front Svelte.
|
||||||
|
front = dev "front" (inner "front" ./scripts/front.sh);
|
||||||
|
|
||||||
|
# App Tauri : macOS (CLT suffisent) et iOS (Xcode requis).
|
||||||
|
macos = dev "macos" (inner "macos" ./scripts/macos-dev.sh);
|
||||||
|
macos-build = dev "build-macos" (inner "build-macos" ./scripts/macos-build.sh);
|
||||||
|
ios = dev "ios" (inner "ios" ./scripts/ios-dev.sh);
|
||||||
|
ios-build = dev "build-ios" (inner "build-ios" ./scripts/ios-build.sh);
|
||||||
|
|
||||||
|
# Contrôles avant commit (front + svelte-check + cargo check).
|
||||||
|
verify = dev "verify" (inner "verify" ./scripts/verify.sh);
|
||||||
|
}
|
||||||
@@ -0,0 +1,143 @@
|
|||||||
|
# winnie-doctor : état du poste Mac pour builder Winnie.
|
||||||
|
# winnie-doctor diagnostic seul
|
||||||
|
# winnie-doctor --fix répare ce qui est automatisable (dépôt, node_modules,
|
||||||
|
# developer dir Xcode, licence, runtime du simulateur)
|
||||||
|
#
|
||||||
|
# Xcode, le SDK iOS et les runtimes du simulateur viennent d'Apple, jamais du
|
||||||
|
# store Nix : ce script est la seule source de vérité sur leur disponibilité.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
api="@API_URL@"
|
||||||
|
sync_bin="@WINNIE_SYNC@"
|
||||||
|
|
||||||
|
fix=0
|
||||||
|
if [ "${1:-}" = "--fix" ]; then
|
||||||
|
fix=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
todo=0
|
||||||
|
line() { printf '%-8s %s\n' "$1" "$2"; }
|
||||||
|
ok() { line "[ok]" "$1"; }
|
||||||
|
missing() { line "[manque]" "$1"; todo=$((todo + 1)); }
|
||||||
|
info() { line "[-]" "$1"; }
|
||||||
|
|
||||||
|
printf 'winnie-doctor\n\n'
|
||||||
|
|
||||||
|
# --- nix ---------------------------------------------------------------
|
||||||
|
if command -v nix >/dev/null 2>&1; then
|
||||||
|
ok "nix : $(nix --version)"
|
||||||
|
else
|
||||||
|
missing "nix absent du PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- dépôt winnie ------------------------------------------------------
|
||||||
|
if [ -f "$repo/flake.nix" ]; then
|
||||||
|
ok "dépôt : $repo"
|
||||||
|
if nix --extra-experimental-features 'nix-command flakes' flake metadata "$repo" >/dev/null 2>&1; then
|
||||||
|
ok "flake winnie évaluable"
|
||||||
|
else
|
||||||
|
missing "flake winnie non évaluable (nix flake metadata $repo)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
missing "dépôt : $repo"
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo " -> winnie-sync"
|
||||||
|
"$sync_bin" || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- Xcode : seul le build iOS l'exige ---------------------------------
|
||||||
|
xcode_ok=0
|
||||||
|
if [ -d /Applications/Xcode.app ]; then
|
||||||
|
ok "Xcode.app installé"
|
||||||
|
xcode_ok=1
|
||||||
|
else
|
||||||
|
missing "Xcode.app absent (App Store, recherche « Xcode », ~8 Go)"
|
||||||
|
info "sans Xcode : le front et le build macOS marchent, seul winnie-ios l'exige"
|
||||||
|
info "fiche App Store : open 'macappstore://apps.apple.com/app/id497799835'"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x /usr/bin/xcode-select ]; then
|
||||||
|
devdir="$(/usr/bin/xcode-select -p 2>/dev/null || true)"
|
||||||
|
if [ "$devdir" = "/Applications/Xcode.app/Contents/Developer" ]; then
|
||||||
|
ok "xcode-select : Xcode"
|
||||||
|
elif [ "$xcode_ok" = 1 ]; then
|
||||||
|
missing "xcode-select : ${devdir:-vide} (attendu /Applications/Xcode.app/Contents/Developer)"
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo " -> sudo xcode-select -s /Applications/Xcode.app (mot de passe macOS demandé)"
|
||||||
|
sudo /usr/bin/xcode-select -s /Applications/Xcode.app
|
||||||
|
sudo /usr/bin/xcodebuild -license accept || true
|
||||||
|
/usr/bin/xcodebuild -runFirstLaunch || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "xcode-select : ${devdir:-vide} (Command Line Tools, suffisant hors iOS)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
info "xcode-select absent (poste non macOS ?)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$xcode_ok" = 1 ]; then
|
||||||
|
sdk="$(/usr/bin/xcrun --sdk iphoneos --show-sdk-version 2>/dev/null || true)"
|
||||||
|
if [ -n "$sdk" ]; then
|
||||||
|
ok "SDK iOS : $sdk"
|
||||||
|
else
|
||||||
|
missing "SDK iOS (xcrun --sdk iphoneos --show-sdk-version ; licence Xcode non acceptée ?)"
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo " -> sudo xcodebuild -license accept"
|
||||||
|
sudo /usr/bin/xcodebuild -license accept || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
runtime_ok=0
|
||||||
|
if /usr/bin/xcrun simctl list runtimes 2>/dev/null | grep -q 'iOS '; then
|
||||||
|
runtime_ok=1
|
||||||
|
fi
|
||||||
|
if [ "$runtime_ok" = 1 ]; then
|
||||||
|
ok "runtime du simulateur iOS"
|
||||||
|
else
|
||||||
|
missing "runtime du simulateur iOS"
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo " -> xcodebuild -downloadPlatform iOS (~8 Go, plusieurs minutes)"
|
||||||
|
/usr/bin/xcodebuild -downloadPlatform iOS || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- front / node ------------------------------------------------------
|
||||||
|
if [ -f "$repo/app/package.json" ]; then
|
||||||
|
if [ -d "$repo/app/node_modules" ]; then
|
||||||
|
ok "app/node_modules (npm ci fait)"
|
||||||
|
else
|
||||||
|
missing "app/node_modules"
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo " -> npm ci dans le devShell (~1 min)"
|
||||||
|
(cd "$repo" && nix --extra-experimental-features 'nix-command flakes' develop -c bash -c "cd app && npm ci") || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ -d "$repo/app/src-tauri/gen/apple" ]; then
|
||||||
|
ok "projet Xcode généré (src-tauri/gen/apple)"
|
||||||
|
else
|
||||||
|
info "src-tauri/gen/apple absent : winnie-ios le génère (tauri ios init)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- API ---------------------------------------------------------------
|
||||||
|
code="$(curl -sS -m 8 -o /dev/null -w '%{http_code}' "$api/healthz" 2>/dev/null || true)"
|
||||||
|
if [ "$code" = "200" ]; then
|
||||||
|
ok "API : $api"
|
||||||
|
else
|
||||||
|
missing "API : $api/healthz (HTTP ${code:-aucune réponse})"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- verdict -----------------------------------------------------------
|
||||||
|
printf '\n'
|
||||||
|
if [ "$todo" -eq 0 ]; then
|
||||||
|
echo "tout est prêt : winnie-ios (simulateur), winnie-front, winnie-macos."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
echo "$todo contrôle(s) en échec."
|
||||||
|
if [ "$fix" = 1 ]; then
|
||||||
|
echo "relance winnie-doctor pour vérifier ce qui reste."
|
||||||
|
else
|
||||||
|
echo "winnie-doctor --fix répare ce qui est automatisable."
|
||||||
|
fi
|
||||||
|
exit 1
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Serveur Vite (HMR) de app/ : joignable sur le LAN (http://<mac>:8080) et sur
|
||||||
|
# dev.winnie.marcpartensky.com via Pangolin. Lancé par winnie-front, donc les
|
||||||
|
# outils (node 22, npm) viennent du devShell du flake winnie.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-front] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec npm run dev "$@"
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# Archive iOS pour un iPhone réel (et export IPA si un compte Apple est
|
||||||
|
# configuré dans Xcode). Le simulateur n'exige aucun compte, pas ceci.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-build-ios] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d src-tauri/gen/apple ]; then
|
||||||
|
echo "[winnie-build-ios] tauri ios init (projet Xcode + pod install)"
|
||||||
|
./node_modules/.bin/tauri ios init
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ./node_modules/.bin/tauri ios build --target aarch64 "$@"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# App iOS en dev : simulateur (aucun compte Apple requis) ou iPhone branché.
|
||||||
|
# npm ci et `tauri ios init` (projet Xcode + pod install) sont faits au besoin,
|
||||||
|
# donc la toute première exécution est longue.
|
||||||
|
# winnie-ios premier iPhone du simulateur
|
||||||
|
# winnie-ios "iPhone 17 Pro" appareil précis, en argument positionnel
|
||||||
|
# WINNIE_IOS_DEVICE=<nom|udid> winnie-ios
|
||||||
|
# winnie-ios --force-ip-prompt sur le LAN (Vite sert déjà le LAN)
|
||||||
|
# `tauri ios dev` prend l'appareil en positionnel (le champ `device` de la CLI
|
||||||
|
# n'a pas d'attribut clap) et ouvre un menu interactif quand on ne passe rien :
|
||||||
|
# d'où la recherche du premier iPhone pour que la commande marche seule.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-ios] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d src-tauri/gen/apple ]; then
|
||||||
|
echo "[winnie-ios] tauri ios init (projet Xcode + pod install)"
|
||||||
|
./node_modules/.bin/tauri ios init
|
||||||
|
fi
|
||||||
|
|
||||||
|
args=("$@")
|
||||||
|
if [ "${#args[@]}" -eq 0 ] && [ -n "${WINNIE_IOS_DEVICE:-}" ]; then
|
||||||
|
args=("$WINNIE_IOS_DEVICE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${#args[@]}" -eq 0 ]; then
|
||||||
|
sim="$(xcrun simctl list devices available 2>/dev/null | awk -F ' \\(' '/^ *iPhone/ {sub(/^ */, "", $1); print $1; exit}' || true)"
|
||||||
|
if [ -n "$sim" ]; then
|
||||||
|
echo "[winnie-ios] simulateur : $sim"
|
||||||
|
args=("$sim")
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ./node_modules/.bin/tauri ios dev "${args[@]}"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Bundle macOS (.app + .dmg) : app/src-tauri/target/release/bundle/.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-build-macos] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ./node_modules/.bin/tauri build "$@"
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# App macOS (fenêtre Tauri + Vite en HMR) : ni Xcode ni simulateur, les
|
||||||
|
# Command Line Tools suffisent.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-macos] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec ./node_modules/.bin/tauri dev "$@"
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# winnie-sync : rafraîchit ~/git/winnie depuis tower, la source de vérité
|
||||||
|
# (le dépôt n'a pas de remote git : tower est le seul original).
|
||||||
|
# Le premier alias ssh qui répond gagne : LAN d'abord (towerlocal), puis le
|
||||||
|
# tunnel par le VPS (tower -> 10.100.0.3 via rack, tenu par l'agent autossh).
|
||||||
|
# winnie-sync copie incrémentale, garde les fichiers locaux en trop
|
||||||
|
# winnie-sync --delete miroir strict de tower (supprime les fichiers en trop)
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
tower_repo="@TOWER_REPO@"
|
||||||
|
hosts=(@TOWER_HOSTS@)
|
||||||
|
|
||||||
|
delete=()
|
||||||
|
if [ "${1:-}" = "--delete" ]; then
|
||||||
|
delete=(--delete)
|
||||||
|
elif [ -n "${1:-}" ]; then
|
||||||
|
echo "usage: winnie-sync [--delete]" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
host=""
|
||||||
|
for h in "${hosts[@]}"; do
|
||||||
|
printf 'winnie-sync : %s... ' "$h"
|
||||||
|
if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new "$h" true >/dev/null 2>&1; then
|
||||||
|
echo "ok"
|
||||||
|
host="$h"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "injoignable"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$host" ]; then
|
||||||
|
echo "winnie-sync : tower injoignable (essayé : ${hosts[*]})" >&2
|
||||||
|
echo "winnie-sync : LAN = towerlocal (192.168.1.44) ; hors LAN = tower (via rack)" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
mkdir -p "$repo"
|
||||||
|
echo "winnie-sync : $host:$tower_repo -> $repo"
|
||||||
|
|
||||||
|
# La barre de progression seulement sur un terminal (sans quoi un log
|
||||||
|
# redirigé se remplit de retours chariot).
|
||||||
|
progress=()
|
||||||
|
if [ -t 1 ]; then
|
||||||
|
progress=(--info=progress2)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ce qui ne traverse jamais : binaires par plateforme (node_modules, tauri-cli)
|
||||||
|
# et artefacts liés à la machine (target, projet Xcode généré, clés de signature).
|
||||||
|
rsync -a --no-owner --no-group "${progress[@]}" \
|
||||||
|
--exclude /.direnv/ \
|
||||||
|
--exclude /backend/target/ \
|
||||||
|
--exclude /ingest/target/ \
|
||||||
|
--exclude /app/node_modules/ \
|
||||||
|
--exclude /app/dist/ \
|
||||||
|
--exclude /app/src-tauri/target/ \
|
||||||
|
--exclude /app/src-tauri/gen/ \
|
||||||
|
--exclude /app/.android-sdk/ \
|
||||||
|
--exclude /app/.android-user/ \
|
||||||
|
--exclude /app/.keys/ \
|
||||||
|
"${delete[@]}" \
|
||||||
|
"$host:$tower_repo/" "$repo/"
|
||||||
|
|
||||||
|
echo "winnie-sync : terminé (winnie-doctor pour l'état du poste)."
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Contrôles avant commit, dans l'ordre du projet : build du front (vite),
|
||||||
|
# svelte-check, puis cargo check du backend et de l'ingest.
|
||||||
|
# `npm run check` n'est pas appelé tel quel : son `tsc -p tsconfig.node.json`
|
||||||
|
# échoue sur TS2688 (Cannot find type definition file for 'node', @types/node
|
||||||
|
# absent de app/package.json), une dette antérieure au projet.
|
||||||
|
repo="$HOME/@REPO_REL@"
|
||||||
|
cd "$repo/app"
|
||||||
|
|
||||||
|
if [ ! -d node_modules ]; then
|
||||||
|
echo "[winnie-verify] npm ci"
|
||||||
|
npm ci
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[winnie-verify] front : vite build"
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
echo "[winnie-verify] front : svelte-check"
|
||||||
|
./node_modules/.bin/svelte-check --tsconfig ./tsconfig.app.json
|
||||||
|
|
||||||
|
cd "$repo"
|
||||||
|
echo "[winnie-verify] backend : cargo check"
|
||||||
|
cargo check --manifest-path backend/Cargo.toml
|
||||||
|
|
||||||
|
if [ -f ingest/Cargo.toml ]; then
|
||||||
|
echo "[winnie-verify] ingest : cargo check"
|
||||||
|
cargo check --manifest-path ingest/Cargo.toml
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "[winnie-verify] ok"
|
||||||
@@ -38,6 +38,11 @@ in {
|
|||||||
# inputs.sops.nixosModules.sops # Inclus dans sopswarden
|
# inputs.sops.nixosModules.sops # Inclus dans sopswarden
|
||||||
|
|
||||||
# ../../services/wg-quick
|
# ../../services/wg-quick
|
||||||
|
|
||||||
|
# Tunnel SSH inverse Mac -> rack (launchd agent) : expose le sshd du Mac
|
||||||
|
# sur 127.0.0.1:2223 du VPS, pour que tower (ou marc) joigne le Mac hors
|
||||||
|
# LAN. La clé du Mac est déjà autorisée pour root@rack.
|
||||||
|
../../modules/home-mac/autossh
|
||||||
];
|
];
|
||||||
|
|
||||||
environment.systemPackages = [
|
environment.systemPackages = [
|
||||||
@@ -51,6 +56,16 @@ in {
|
|||||||
nixpkgs.config.allowUnfree = true;
|
nixpkgs.config.allowUnfree = true;
|
||||||
networking.hostName = "macos";
|
networking.hostName = "macos";
|
||||||
|
|
||||||
|
# --- Nix : flakes et utilisateurs de confiance -------------------------
|
||||||
|
# Le Mac build Winnie par `nix develop` (voir modules/home-mac/winnie) : les
|
||||||
|
# flakes doivent être actives dans /etc/nix/nix.conf, pas seulement passées en
|
||||||
|
# ligne de commande, et marc doit être trusted-user pour que le client puisse
|
||||||
|
# charger des substituts ou surcharger une option locale.
|
||||||
|
nix.settings = {
|
||||||
|
experimental-features = ["nix-command" "flakes"];
|
||||||
|
trusted-users = ["root" "@admin"];
|
||||||
|
};
|
||||||
|
|
||||||
# services.nix-daemon.enable = true; <- supprime cette ligne
|
# services.nix-daemon.enable = true; <- supprime cette ligne
|
||||||
|
|
||||||
home-manager = {
|
home-manager = {
|
||||||
@@ -69,6 +84,16 @@ in {
|
|||||||
|
|
||||||
system.primaryUser = "marc";
|
system.primaryUser = "marc";
|
||||||
|
|
||||||
|
# --- SSH entrant : Remote Login + clés -----------------------------------
|
||||||
|
# Active sshd (com.openssh.sshd) : nix-darwin écrit sshd_config.d et fait
|
||||||
|
# `launchctl enable + bootstrap` à l'activation (pas de systemsetup, qui
|
||||||
|
# exige Full Disk Access). La clé hermes@tower est posée dans
|
||||||
|
# /Users/marc/.ssh/authorized_keys par users/mac/home.nix (home.activation).
|
||||||
|
# Reachabilité : LAN direct (tower et le Mac sont sur 192.168.1.0/24) ;
|
||||||
|
# hors LAN via le tunnel inverse autossh (modules/home-mac/autossh) que
|
||||||
|
# tower joint par `ssh -J rack -p 2223 marc@localhost`.
|
||||||
|
services.openssh.enable = true;
|
||||||
|
|
||||||
# deactivated
|
# deactivated
|
||||||
homebrew = {
|
homebrew = {
|
||||||
enable = false;
|
enable = false;
|
||||||
@@ -101,5 +126,24 @@ in {
|
|||||||
cp -R ${pangolin-client}/Applications/Pangolin.app /Applications/
|
cp -R ${pangolin-client}/Applications/Pangolin.app /Applications/
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# --- Xcode : installation manuelle, reste automatique -------------------
|
||||||
|
# Apple interdit la redistribution du SDK : Xcode vient de l'App Store (aucun
|
||||||
|
# paquet nix ne peut le fournir). Tout ce qui suit l'installation est en
|
||||||
|
# revanche scriptable, et c'est ici : developer dir, licence, premiers
|
||||||
|
# composants. Le runtime du simulateur (~8 Go) n'est pas téléchargé
|
||||||
|
# silencieusement : `winnie-doctor --fix` s'en charge à la demande.
|
||||||
|
system.activationScripts.winnie-xcode.text = ''
|
||||||
|
if [ -d /Applications/Xcode.app ]; then
|
||||||
|
if [ "$(/usr/bin/xcode-select -p 2>/dev/null || true)" != "/Applications/Xcode.app/Contents/Developer" ]; then
|
||||||
|
echo "winnie : xcode-select -> /Applications/Xcode.app" >&2
|
||||||
|
/usr/bin/xcode-select -s /Applications/Xcode.app || true
|
||||||
|
/usr/bin/xcodebuild -license accept || true
|
||||||
|
/usr/bin/xcodebuild -runFirstLaunch || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "winnie : Xcode absent (App Store) ; le build macOS marche sans, seul winnie-ios l'exige" >&2
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
system.stateVersion = 6;
|
system.stateVersion = 6;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
../../modules/home-mac/aerospace
|
../../modules/home-mac/aerospace
|
||||||
../../modules/home-mac/hermes-skins
|
../../modules/home-mac/hermes-skins
|
||||||
|
../../modules/home-mac/winnie
|
||||||
# ../../modules/home-mac/autossh
|
# ../../modules/home-mac/autossh
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -120,6 +121,11 @@
|
|||||||
# Skins Hermes (pack Hermes Menagerie) -> ~/.hermes/skins/
|
# Skins Hermes (pack Hermes Menagerie) -> ~/.hermes/skins/
|
||||||
my.hermesSkins.enable = true;
|
my.hermesSkins.enable = true;
|
||||||
|
|
||||||
|
# Build de l'app Winnie sur le Mac : winnie-doctor, winnie-sync, winnie-ios,
|
||||||
|
# winnie-macos, winnie-front, winnie-verify. Le toolchain reste dans le
|
||||||
|
# devShell du flake `winnie`, ces commandes ne font que l'appeler.
|
||||||
|
my.winnie.enable = true;
|
||||||
|
|
||||||
# Connexions Eternal Terminal vers les hôtes NixOS : `et tower` = zellij enregistré.
|
# Connexions Eternal Terminal vers les hôtes NixOS : `et tower` = zellij enregistré.
|
||||||
my.et = {
|
my.et = {
|
||||||
enable = true;
|
enable = true;
|
||||||
@@ -128,4 +134,35 @@
|
|||||||
towerlocal = {};
|
towerlocal = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Clé SSH de l'agent hermes@tower : l'agent (qui tourne sur tower) peut se
|
||||||
|
# connecter au Mac en marc@… (sshd activé par services.openssh dans
|
||||||
|
# profiles/macos/configuration.nix). Append idempotent plutôt que
|
||||||
|
# home.file.".ssh/authorized_keys" : ne remplace JAMAIS un authorized_keys
|
||||||
|
# déjà en place (le .bak de home-manager perdrait les clés existantes).
|
||||||
|
home.activation.addHermesAuthorizedKey = lib.hm.dag.entryAfter ["writeBoundary"] ''
|
||||||
|
key='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKF3huPUXHP6P6SBXHHw9k7HGh6Cs8ntoRk2pnqrG2Hc hermes@tower'
|
||||||
|
mkdir -p "$HOME/.ssh"
|
||||||
|
chmod 700 "$HOME/.ssh"
|
||||||
|
touch "$HOME/.ssh/authorized_keys"
|
||||||
|
grep -qxF "$key" "$HOME/.ssh/authorized_keys" || echo "$key" >> "$HOME/.ssh/authorized_keys"
|
||||||
|
chmod 600 "$HOME/.ssh/authorized_keys"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Accès distant par rack (VPS à IP publique) : marche depuis n'importe quel
|
||||||
|
# réseau, sans VPN sur le Mac. rack joint tower par le tunnel wg1
|
||||||
|
# (10.100.0.3) et le LAN (192.168.1.0/24) que tower route pour wg1.
|
||||||
|
# mkForce : ces deux entrées remplacent celles du module partagé.
|
||||||
|
programs.ssh.settings = {
|
||||||
|
"tower" = lib.mkForce {
|
||||||
|
HostName = "10.100.0.3";
|
||||||
|
User = "marc";
|
||||||
|
ProxyJump = "rack";
|
||||||
|
};
|
||||||
|
"deck" = lib.mkForce {
|
||||||
|
HostName = "192.168.1.67";
|
||||||
|
User = "marc";
|
||||||
|
ProxyJump = "rack";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user