From c8b08fe2588c93b5c4e20e005ee968d933e768d5 Mon Sep 17 00:00:00 2001 From: kurogeek Date: Wed, 14 Oct 2026 10:19:36 +0000 Subject: [PATCH] Add Nix dev shell with local MariaDB and dev orchestration script --- .gitignore | 2 + README.md | 25 +++++++++ dev/dev | 158 +++++++++++++++++++++++++++++++++++++++++++++++++++++ flake.lock | 27 +++++++++ flake.nix | 33 +++++++++++ 5 files changed, 245 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100755 dev/dev create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e583195 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/.dev/ +/.direnv/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..c70f468 --- /dev/null +++ b/README.md @@ -0,0 +1,25 @@ +# think.greaterchiangmai.com + +Two Laravel 10 apps sharing one MySQL database (`greaterc_db2024`): + +- `think.greaterchiangmai.com/` — public site +- `think-backend.greaterchiangmai.com/` — CMS (`/login`) +- `upload-large-file/` — standalone S3 multipart upload helper (Node) + +## Local development + +Requires [Nix](https://nixos.org) with flakes (direnv optional: `.envrc` → `use flake`). + +```sh +nix develop # php 8.3 + composer + mariadb + node on PATH +dev setup # composer install both apps, init MariaDB under .dev/ +dev db import dump.sql # load a production dump — the article/category schema only exists in prod +dev up # http://127.0.0.1:8000 (site), http://127.0.0.1:8001 (CMS); Ctrl-C stops all +``` + +Other commands: `dev db start|stop|status|shell`, `dev serve frontend|backend`, +`dev artisan ...`, `dev down`. See `dev help`. + +The local DB listens on `127.0.0.1:3307` (override with `DEV_DB_PORT`); the `dev` script +injects `DB_*`, `APP_DEBUG=true`, `TEST_LOCAL=true` via the process environment so each +app's `.env` is left untouched. Ports: `DEV_FRONTEND_PORT`, `DEV_BACKEND_PORT`. diff --git a/dev/dev b/dev/dev new file mode 100755 index 0000000..a4ae161 --- /dev/null +++ b/dev/dev @@ -0,0 +1,158 @@ +#!/usr/bin/env bash +# Local development orchestration. Run inside `nix develop`. +set -euo pipefail + +ROOT="${DEV_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}" +FRONTEND="$ROOT/think.greaterchiangmai.com" +BACKEND="$ROOT/think-backend.greaterchiangmai.com" +STATE="$ROOT/.dev" +MYSQL_DATA="$STATE/mysql" +MYSQL_SOCK="$STATE/mysql.sock" +MYSQL_PID="$STATE/mysql.pid" +MYSQL_LOG="$STATE/mysql.log" + +# Must match DB_* in both apps' .env (only the port differs, to avoid clashing with a system MySQL). +DB_PORT="${DEV_DB_PORT:-3307}" +DB_NAME="greaterc_db2024" +DB_USER="greaterc_dbname2024" +DB_PASS="6vK25y6Pz8LA6Z3y78qH" + +FRONTEND_PORT="${DEV_FRONTEND_PORT:-8000}" +BACKEND_PORT="${DEV_BACKEND_PORT:-8001}" + +# Process env beats .env in Laravel, so this redirects both apps to the local DB without editing .env. +export DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT="$DB_PORT" DB_DATABASE="$DB_NAME" DB_USERNAME="$DB_USER" DB_PASSWORD="$DB_PASS" +export APP_ENV=local APP_DEBUG=true TEST_LOCAL=true + +usage() { + cat < + + setup composer install both apps + init local MariaDB + db start|stop|status manage local MariaDB (data in .dev/mysql, port $DB_PORT) + db shell mysql client on the local DB + db import load a production dump (schema lives only in the prod DB) + serve frontend php artisan serve -> http://127.0.0.1:$FRONTEND_PORT + serve backend php artisan serve -> http://127.0.0.1:$BACKEND_PORT + up db + both apps; Ctrl-C stops everything + down stop db and any background servers + artisan ... run artisan for frontend|backend with local env +EOF +} + +app_dir() { + case "$1" in + frontend) echo "$FRONTEND" ;; + backend) echo "$BACKEND" ;; + *) echo "unknown app: $1 (frontend|backend)" >&2; exit 2 ;; + esac +} + +db_running() { + [ -f "$MYSQL_PID" ] && kill -0 "$(cat "$MYSQL_PID")" 2>/dev/null +} + +db_init() { + [ -d "$MYSQL_DATA" ] && return + mkdir -p "$MYSQL_DATA" + mariadb-install-db --datadir="$MYSQL_DATA" --auth-root-authentication-method=normal >"$STATE/install.log" 2>&1 +} + +db_start() { + mkdir -p "$STATE" + db_init + if db_running; then + echo "db: already running (pid $(cat "$MYSQL_PID"))" + return + fi + mariadbd --datadir="$MYSQL_DATA" --socket="$MYSQL_SOCK" --pid-file="$MYSQL_PID" \ + --port="$DB_PORT" --bind-address=127.0.0.1 --skip-name-resolve \ + >"$MYSQL_LOG" 2>&1 & + for _ in $(seq 1 50); do + mariadb-admin --socket="$MYSQL_SOCK" -uroot ping >/dev/null 2>&1 && break + sleep 0.2 + done + mariadb-admin --socket="$MYSQL_SOCK" -uroot ping >/dev/null 2>&1 || { echo "db: failed to start, see $MYSQL_LOG" >&2; exit 1; } + mariadb --socket="$MYSQL_SOCK" -uroot <&2; exit 1; } + (cd "$app" && composer install --no-interaction) + done +} + +serve() { + local dir port + dir="$(app_dir "$1")" + case "$1" in frontend) port="$FRONTEND_PORT" ;; backend) port="$BACKEND_PORT" ;; esac + cd "$dir" && exec php artisan serve --host=127.0.0.1 --port="$port" +} + +up() { + db_start + local pids=() + trap 'kill "${pids[@]}" 2>/dev/null; wait; db_stop' EXIT INT TERM + (cd "$FRONTEND" && php artisan serve --host=127.0.0.1 --port="$FRONTEND_PORT") & pids+=($!) + (cd "$BACKEND" && php artisan serve --host=127.0.0.1 --port="$BACKEND_PORT") & pids+=($!) + echo "frontend: http://127.0.0.1:$FRONTEND_PORT" + echo "backend: http://127.0.0.1:$BACKEND_PORT" + wait +} + +down() { + pkill -f "artisan serve --host=127.0.0.1 --port=($FRONTEND_PORT|$BACKEND_PORT)" 2>/dev/null || true + db_stop +} + +cmd="${1:-help}"; shift || true +case "$cmd" in + setup) composer_install; db_start ;; + db) + sub="${1:-status}"; shift || true + case "$sub" in + start) db_start ;; + stop) db_stop ;; + status) db_status ;; + shell) db_running || db_start; exec mariadb --socket="$MYSQL_SOCK" -uroot "$DB_NAME" ;; + import) db_import "$@" ;; + *) usage; exit 2 ;; + esac ;; + serve) serve "${1:?usage: dev serve frontend|backend}" ;; + up) up ;; + down) down ;; + artisan) dir="$(app_dir "${1:?usage: dev artisan frontend|backend ...}")"; shift; cd "$dir" && exec php artisan "$@" ;; + help|-h|--help) usage ;; + *) usage; exit 2 ;; +esac diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..cd19318 --- /dev/null +++ b/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1791734076, + "narHash": "sha256-i5qk1kLiit98rRbh1AXmWBNT9QXC/y4l5Wk/u5EdpDQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "7fd4004d0511991fff3a0524856a9be09e28c5f0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..bd04a18 --- /dev/null +++ b/flake.nix @@ -0,0 +1,33 @@ +{ + description = "Dev shell for think.greaterchiangmai.com (frontend + backend Laravel apps)"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ]; + forAll = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); + in + { + devShells = forAll (pkgs: + let + php = pkgs.php83; + in + { + default = pkgs.mkShell { + packages = [ + php + php.packages.composer + pkgs.mariadb + pkgs.nodejs_22 + ]; + + shellHook = '' + export DEV_ROOT="$PWD" + export PATH="$DEV_ROOT/dev:$PATH" + echo "think-greaterchiangmai dev shell — run 'dev help'" + ''; + }; + }); + }; +}