diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..c409805 --- /dev/null +++ b/.envrc @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +export DIRENV_WARN_TIMEOUT=20s + +eval "$(devenv direnvrc)" + +# `use devenv` supports the same options as the `devenv shell` command. +# --quiet silences the nix/devenv build noise on activation. +use devenv diff --git a/.gitignore b/.gitignore index a27a5c4..95549c7 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,11 @@ tmp/ temp/ .cache/ +# ── Nix / direnv / devenv ──────────────────────────────── +.direnv/ +.devenv/ +devenv.local.nix + # ── Terraform ──────────────────────────────────────────── # .terraform.lock.hcl is intentionally NOT ignored — commit it so provider # version/checksum selection stays reproducible across machines. diff --git a/bin/mat b/bin/mat new file mode 100755 index 0000000..a49ac44 --- /dev/null +++ b/bin/mat @@ -0,0 +1,155 @@ +#!/usr/bin/env bash +# mat — this repo's mini CLI. Plain, dependency-free bash: must run standalone +# before Nix/direnv/devenv exist, since `mat setup` is what installs them. +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$REPO_ROOT" + +##### OUTPUT HELPERS ##### +info() { echo "-> $*"; } +ok() { echo " $*"; } +warn() { echo "!! $*" >&2; } + +confirm() { + local prompt="$1" + local reply + read -rp "$prompt [y/N] " reply + [[ "$reply" =~ ^[Yy]$ ]] +} + +##### OS DETECTION ##### +detect_os() { + case "$(uname -s)" in + Darwin*) echo "macos" ;; + Linux*) + if grep -qi microsoft /proc/version 2>/dev/null; then + echo "wsl2" + else + echo "linux" + fi + ;; + *) echo "unsupported" ;; + esac +} + +##### NIX ##### +ensure_nix() { + if command -v nix >/dev/null 2>&1; then + ok "Nix already installed." + return 0 + fi + + warn "Nix is not installed. It's required for the reproducible dev shell (direnv + devenv)." + if ! confirm "Install Nix now via the Determinate Systems installer?"; then + warn "Skipping Nix. Git hooks and .env will still be set up below, but the dev shell (mat/terraform/etc.) won't be available until you install Nix and re-run 'mat setup'." + return 1 + fi + + info "Installing Nix (this will prompt for your password)..." + curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install + warn "Nix installed. Open a new terminal (or source your shell profile) so the 'nix' command is on PATH, then re-run 'mat setup'." + return 1 +} + +##### DIRENV ##### +ensure_direnv() { + if command -v direnv >/dev/null 2>&1; then + ok "direnv already installed." + else + if ! confirm "direnv is not installed. Install it now via 'nix profile install nixpkgs#direnv'?"; then + warn "Skipping direnv install." + return 1 + fi + nix profile install nixpkgs#direnv + fi + + local hook_line="eval \"\$(direnv hook $(basename "${SHELL:-bash}"))\"" + local rc_file="$HOME/.$(basename "${SHELL:-bash}")rc" + if [ -f "$rc_file" ] && grep -qF "direnv hook" "$rc_file"; then + ok "direnv shell hook already present in $rc_file." + else + warn "direnv's shell hook isn't in $rc_file yet. Add this line to it, then restart your shell:" + echo " $hook_line" + fi +} + +##### DEVENV ##### +ensure_devenv() { + if command -v devenv >/dev/null 2>&1; then + ok "devenv already installed." + return 0 + fi + if ! confirm "devenv is not installed. Install it now via 'nix profile install nixpkgs#devenv'?"; then + warn "Skipping devenv install." + return 1 + fi + nix profile install nixpkgs#devenv --accept-flake-config +} + +##### GIT HOOKS ##### +activate_git_hooks() { + if [ "$(git config --local --get core.hooksPath || true)" = ".githooks" ]; then + ok "Git hooks already activated." + else + git config core.hooksPath .githooks + ok "Activated Git hooks (core.hooksPath = .githooks)." + fi +} + +##### ENV FILE ##### +create_env_file() { + if [ -f .env ]; then + ok ".env already exists, leaving it alone." + else + cp .env.example .env + ok "Created .env from .env.example — fill in the values before running the project." + fi +} + +##### SETUP ##### +cmd_setup() { + local os + os="$(detect_os)" + if [ "$os" = "unsupported" ]; then + warn "Unrecognized OS. Nix/devenv support macOS and Linux (including WSL2) — on native Windows, install WSL2 first and run 'mat setup' from inside it." + fi + + info "Git hooks and .env (no Nix required for these)" + activate_git_hooks + create_env_file + + echo + info "Dev shell (Nix + direnv + devenv)" + if ensure_nix; then + ensure_direnv || true + ensure_devenv || true + if command -v direnv >/dev/null 2>&1; then + direnv allow "$REPO_ROOT" + ok "Ran 'direnv allow' — cd out and back into this directory to enter the dev shell." + fi + fi + + echo + info "Setup complete. Full guide: docs/product-code/tutorials/getting-started.md" +} + +##### DISPATCH ##### +usage() { + cat <<'EOF' +Usage: mat + +Commands: + setup Full local onboarding: Nix, direnv, devenv, Git hooks, .env +EOF +} + +case "${1:-}" in + setup) cmd_setup ;; + help | --help | -h | "") usage ;; + *) + warn "Unknown command: $1" + usage + exit 1 + ;; +esac diff --git a/devenv.lock b/devenv.lock new file mode 100644 index 0000000..d1708c8 --- /dev/null +++ b/devenv.lock @@ -0,0 +1,65 @@ +{ + "nodes": { + "devenv": { + "locked": { + "dir": "src/modules", + "lastModified": 1783019250, + "narHash": "sha256-aopu8Qs3Q0mMPSKIfnCvMpRE59PdADgBAOGuw8gJFmM=", + "owner": "cachix", + "repo": "devenv", + "rev": "44ad0477baf9e18fed3eb32100c7ed018d0c11c7", + "type": "github" + }, + "original": { + "dir": "src/modules", + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "nixpkgs": { + "inputs": { + "nixpkgs-src": "nixpkgs-src" + }, + "locked": { + "lastModified": 1782924808, + "narHash": "sha256-tn2ahNv3ZNXyVHdPx/uw+N0xa7YSMtXUr6OEvu+s8ng=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "e2c881cb8d5f1cac6cef9d71f0d450a55691b999", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs-src": { + "flake": false, + "locked": { + "lastModified": 1782636521, + "narHash": "sha256-OG8laCOGtkxlB1JH3XqOnXxnkGJme4mQdXo5hkhCQIY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e1c1b84752fb0897897380a3cae9dc7fcab91ca3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} \ No newline at end of file diff --git a/devenv.nix b/devenv.nix new file mode 100644 index 0000000..e054428 --- /dev/null +++ b/devenv.nix @@ -0,0 +1,36 @@ +{ pkgs, lib, config, inputs, ... }: + +{ + # https://devenv.sh/packages/ + packages = [ + pkgs.git + pkgs.gh + pkgs.terraform + pkgs.jq + pkgs.shellcheck + pkgs.shfmt + ]; + + # https://devenv.sh/scripts/ + # Thin wrapper so the real CLI lives in a plain, dependency-free script + # (bin/mat) that also runs standalone before this shell exists. + scripts.mat.exec = '' + exec "$DEVENV_ROOT/bin/mat" "$@" + ''; + + # https://devenv.sh/integrations/dotenv/ + dotenv.enable = true; + + # https://devenv.sh/basics/ + enterShell = '' + clear + echo "$(tput bold)✨ Welcome, $(whoami)!$(tput sgr0) — $(basename "$DEVENV_ROOT") dev shell" + echo " Tools: git · gh · terraform · jq · shellcheck · shfmt" + echo " First time here? Run: mat setup" + ''; + + # https://devenv.sh/binary-caching/ + cachix.enable = false; + + # See full reference at https://devenv.sh/reference/options/ +} diff --git a/devenv.yaml b/devenv.yaml new file mode 100644 index 0000000..f319b2d --- /dev/null +++ b/devenv.yaml @@ -0,0 +1,7 @@ +inputs: + nixpkgs: + url: github:cachix/devenv-nixpkgs/rolling + +# terraform is BSL-licensed (unfree) — nixpkgs blocks unfree packages by +# default, so this needs to be explicit rather than a silent global default. +allowUnfree: true diff --git a/setup-project.sh b/setup-project.sh deleted file mode 100755 index 8a31676..0000000 --- a/setup-project.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env bash -# One-time local setup for contributors: activates the Git hooks and creates -# a local .env from the example. Safe to re-run — every step is idempotent. -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -cd "$SCRIPT_DIR" - -##### GIT HOOKS ##### -if [ "$(git config --local --get core.hooksPath || true)" = ".githooks" ]; then - echo "Git hooks already activated." -else - git config core.hooksPath .githooks - echo "Activated Git hooks (core.hooksPath = .githooks)." -fi - -##### ENV FILE ##### -if [ -f .env ]; then - echo ".env already exists, leaving it alone." -else - cp .env.example .env - echo "Created .env from .env.example — fill in the values before running the project." -fi - -##### NEXT STEPS ##### -cat <<'EOF' - -Setup complete. Next steps: - - Fill in .env with real values. - - Full setup guide: docs/product-code/tutorials/getting-started.md - -If you're a repository admin configuring GitHub settings (branch -protection, collaborators, etc.), see terraform/README.md — that's a -separate, admin-only step, not part of everyday contributor setup. -EOF