Files
create-glstack/template/devenv.nix
T
2026-10-07 15:35:04 +02:00

202 lines
7.3 KiB
Nix

{ pkgs, lib, config, inputs, ... }:
<@if(eq(context.project.mobile,"expo"))>
let
avdName = "Pixel_5_API36";
# Fixed console port, so scripts can address this emulator as emulator-5554 even when others run.
emulatorSerial = "emulator-5554";
# Apple's xcrun, for tools that need `xcrun simctl`, which the Nix SDK's xcrun lacks.
systemXcrun = pkgs.runCommand "system-xcrun" { } "mkdir -p $out/bin && ln -s /usr/bin/xcrun $out/bin/xcrun";
# Sets dev_dir to Xcode's developer dir. devenv points DEVELOPER_DIR at the Nix SDK,
# which xcode-select would echo back, so it is unset for the lookup.
xcodeDevDir = ''
dev_dir="$(/usr/bin/env -u DEVELOPER_DIR /usr/bin/xcode-select -p 2>/dev/null)"
[ -x "$dev_dir/usr/bin/simctl" ] || dev_dir=/Applications/Xcode.app/Contents/Developer
'';
in
<@endif>
{
packages = [
pkgs.bun
pkgs.nodejs_24
pkgs.watchexec
pkgs.sqlc
pkgs.dbmate
pkgs.air
pkgs.buf
pkgs.protoc-gen-go
pkgs.protoc-gen-connect-go
pkgs.protoc-gen-es
pkgs.cobra-cli
]<@if(eq(context.project.mobile,"expo"))> ++ lib.optionals pkgs.stdenv.isDarwin [ pkgs.cocoapods ]<@endif>;
<@if(eq(context.project.mobile,"expo"))>
android = {
enable = true;
emulator = {
enable = true;
};
buildTools.version = [ "34.0.0" "35.0.0" "36.0.0" ];
reactNative.enable = true;
android-studio = {
enable = false;
};
ndk = {
enable = true;
version = [ "27.1.12297006" ];
};
};
# The emulator's bundled libs are only needed on Linux; macOS uses DYLD paths.
enterShell = lib.optionalString pkgs.stdenv.isLinux ''
export LD_LIBRARY_PATH=$ANDROID_HOME/emulator/lib64:$LD_LIBRARY_PATH
'' + lib.optionalString pkgs.stdenv.isDarwin ''
# Re-check at most daily once the iOS requirements are met. SKIP_IOS_CHECK=1 turns this off.
ios_check_ok="$DEVENV_STATE/ios-check-ok"
if [ -z "''${SKIP_IOS_CHECK:-}" ] && [ -z "$(find "$ios_check_ok" -mtime -1 2>/dev/null)" ]; then
if ios-check; then touch "$ios_check_ok"; else echo "Set SKIP_IOS_CHECK=1 to hide this." >&2; fi
fi
'';
# Use a system image matching the host CPU (arm64 on Apple Silicon, x86_64 elsewhere).
scripts.create-avd.exec = ''
if ! avdmanager list avd -c | grep -qx "${avdName}"; then
echo no | avdmanager create avd -n "${avdName}" -k "system-images;android-36;google_apis_playstore;${if pkgs.stdenv.hostPlatform.isAarch64 then "arm64-v8a" else "x86_64"}" -d "pixel_5"
fi
'';
# macOS only: Nix can't provide Xcode, so report what the iOS build still needs.
scripts.ios-check = lib.mkIf pkgs.stdenv.isDarwin {
exec = ''
${xcodeDevDir}
export DEVELOPER_DIR="$dev_dir"
problems=""
missing() { problems+=" - $1"$'\n'; }
if [ ! -x "$dev_dir/usr/bin/simctl" ]; then
missing "Xcode is not installed. Install it from the App Store: https://apps.apple.com/app/xcode/id497799835"
else
if ! /usr/bin/xcodebuild -license check >/dev/null 2>&1 || ! /usr/bin/xcodebuild -checkFirstLaunchStatus >/dev/null 2>&1; then
missing "Xcode setup is incomplete (license or first-launch components). Run: sudo xcodebuild -runFirstLaunch"
fi
if ! /usr/bin/xcrun simctl list runtimes 2>/dev/null | grep -q '^iOS '; then
missing "No iOS simulator runtime is installed. Run: xcodebuild -downloadPlatform iOS"
elif ! /usr/bin/xcrun simctl list devices available 2>/dev/null | grep -q iPhone; then
missing "No iPhone simulator exists. Create one in Xcode > Window > Devices and Simulators"
fi
fi
if [ -n "$problems" ]; then
printf 'iOS simulator builds are disabled until this is fixed:\n%s' "$problems" >&2
exit 1
fi
'';
};
# macOS only: run on the iOS simulator when ios-check passes.
scripts.run-ios = lib.mkIf pkgs.stdenv.isDarwin {
exec = ''
if ! ios-check; then
echo "Skipping iOS."
exit 0
fi
${xcodeDevDir}
# devenv's Nix Apple SDK, xcrun and compiler wrappers break xcodebuild, so build in a clean env.
exec /usr/bin/env -i HOME="$HOME" USER="$USER" TERM="$TERM" LANG=en_US.UTF-8 \
DEVELOPER_DIR="$dev_dir" \
PATH="${lib.makeBinPath [ pkgs.bun pkgs.nodejs_24 pkgs.cocoapods ]}:/usr/bin:/bin:/usr/sbin:/sbin" \
bunx expo run:ios --no-bundler "$@"
'';
};
<@endif>
languages.go.enable = true;
languages.typescript.enable = true;
services.postgres = {
enable = true;
listen_addresses = "127.0.0.1";
initialDatabases = [
{
name = "<@var(context.project.name)>";
user = "pp";
pass = "<@var(context.project.name)>";
}
];
};
processes = {
<@if(neq(context.project.frontend,"none"))>
bundev = {
exec = "bun dev";
cwd = "./apps/web";
after= ["devenv:processes:air@started"];
};
<@endif>
air = {
exec = "air";
cwd = "./services/api";
after = ["devenv:processes:postgres"];
};
protowatcher = {
exec = "buf generate";
cwd = "./packages/proto";
watch = {
paths = [ ./packages/proto ];
extensions = [ "proto" ];
};
};
protojswatcher = {
exec = "bun run ./scripts/gen-rpc-index.ts";
cwd = "./";
watch = {
paths = [ ./packages/rpc/src ];
extensions = ["js" "ts"];
};
after= ["devenv:processes:protowatcher@completed"];
};
<@if(eq(context.project.mobile,"expo"))>
createavd = {
exec = "create-avd";
};
emulator = {
exec = "emulator -avd ${avdName} -port 5554";
after = ["devenv:processes:createavd@completed"];
};
# One Metro bundler serves both apps, so iOS doesn't depend on the Android build.
metro = {
exec = lib.optionalString pkgs.stdenv.isDarwin ''
# Expo's CLI calls `xcrun simctl` to talk to the iOS simulator.
${xcodeDevDir}
export DEVELOPER_DIR="$dev_dir" PATH="${systemXcrun}/bin:$PATH"
'' + "exec bunx expo start --dev-client";
cwd = "./apps/mobile";
ready.http.get = { port = 8081; path = "/status"; };
};
expodev = {
exec = ''
# Wait for the emulator to boot, otherwise expo tries to start a second copy of the AVD.
for _ in $(seq 150); do
if [ "$(adb -s ${emulatorSerial} shell getprop sys.boot_completed 2>/dev/null | tr -d '\r')" = 1 ]; then
# Lets the emulator and USB-connected devices reach the API on the host at 127.0.0.1:8080.
for device in $(adb devices | awk 'NR > 1 && $2 == "device" { print $1 }'); do
adb -s "$device" reverse tcp:8080 tcp:8080
done
exec bunx expo run:android --no-bundler --device ${avdName}
fi
sleep 2
done
echo "The Android emulator didn't finish booting within 5 minutes, see the emulator log." >&2
exit 1
'';
cwd = "./apps/mobile";
after = [ "devenv:processes:emulator@started" "devenv:processes:metro" ];
};
expoios = lib.mkIf pkgs.stdenv.isDarwin {
exec = "run-ios";
cwd = "./apps/mobile";
after = [ "devenv:processes:metro" ];
};
<@endif>
sqlwatcher = {
exec = "sqlc generate";
watch = {
paths = [ ./db/migrations ./db/query ];
extensions = ["sql" "sqlc"];
};
cwd = "./services/api";
after= ["devenv:processes:air@started"];
};
};
}