Skip to content

Commit 737397e

Browse files
feat(cli): chain mat setup into repo config, add native Windows support
mat setup now configures the GitHub repository too (via a devenv shell invocation so terraform/gh resolve on PATH), instead of silently stopping short of it. Declining Nix falls back to a direct Terraform install rather than skipping repo config entirely. Adds bin/mat.ps1 for native Windows: offers WSL2 setup for full parity, or a winget-based Terraform-only fallback if declined. Also fixes stale ./setup-project.sh references in README/CONTRIBUTING/docs left over from when bin/mat replaced it.
1 parent 7aa43fb commit 737397e

6 files changed

Lines changed: 271 additions & 18 deletions

File tree

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ This project follows the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.
3737
```bash
3838
git remote add upstream https://github.com/your-org/your-repo.git
3939
```
40-
4. Run `./setup-project.sh` — activates the Git hooks (validates commit messages locally before you push) and creates `.env` from `.env.example`. See [docs/team-process/how-to/activate-git-hooks](docs/team-process/how-to/activate-git-hooks.md) for what the hooks do.
40+
4. Run `./bin/mat setup` (native Windows without WSL2: `.\bin\mat.ps1`) — activates the Git hooks (validates commit messages locally before you push), creates `.env` from `.env.example`, and sets up the reproducible dev shell (Nix/direnv/devenv). See [docs/team-process/how-to/activate-git-hooks](docs/team-process/how-to/activate-git-hooks.md) for what the hooks do.
4141
5. Follow the full setup guide: [docs/product-code/tutorials/getting-started](docs/product-code/tutorials/getting-started.md)
4242

4343
---

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,11 @@ Describe the problem this project solves, its main features, and any key design
3939
```bash
4040
git clone https://github.com/your-org/your-repo.git
4141
cd your-repo
42-
./setup-project.sh # activates Git hooks, creates .env from .env.example
42+
./bin/mat setup # Git hooks, .env, dev shell (Nix/direnv/devenv), repo config
4343
```
4444

45+
On native Windows (no WSL2), run `.\bin\mat.ps1` instead.
46+
4547
Full setup guide (prerequisites, dependency install, dev server): [docs/product-code/tutorials/getting-started](docs/product-code/tutorials/getting-started.md).
4648

4749
---

bin/mat

Lines changed: 101 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,79 @@ ensure_nix() {
4242

4343
warn "Nix is not installed. It's required for the reproducible dev shell (direnv + devenv)."
4444
if ! confirm "Install Nix now via the Determinate Systems installer?"; then
45-
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'."
45+
warn "Skipping Nix — falling back to a direct Terraform install instead."
4646
return 1
4747
fi
4848

4949
info "Installing Nix (this will prompt for your password)..."
5050
curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
51-
warn "Nix installed. Open a new terminal (or source your shell profile) so the 'nix' command is on PATH, then re-run 'mat setup'."
52-
return 1
51+
ok "Nix installed."
52+
warn "Open a new terminal (or source your shell profile) so the 'nix' command is on PATH, then re-run 'mat setup' to continue."
53+
# Nothing further can run in this process (nix isn't on this shell's PATH
54+
# yet), and treating this the same as a decline would wrongly trigger the
55+
# no-nix fallback right after a successful install. Just stop here.
56+
exit 0
57+
}
58+
59+
##### TERRAFORM (no-nix fallback) #####
60+
ensure_terraform_direct() {
61+
if command -v terraform >/dev/null 2>&1; then
62+
ok "Terraform already installed."
63+
return 0
64+
fi
65+
66+
if ! confirm "Terraform is not installed. Install it now (direct download, no Nix/sudo required)?"; then
67+
warn "Skipping Terraform install. Re-run 'mat setup' once it's available, or install it yourself and run 'mat repo'."
68+
return 1
69+
fi
70+
71+
if command -v brew >/dev/null 2>&1; then
72+
info "Installing Terraform via Homebrew..."
73+
brew install hashicorp/tap/terraform
74+
return 0
75+
fi
76+
77+
local os arch
78+
case "$(uname -s)" in
79+
Darwin) os="darwin" ;;
80+
Linux) os="linux" ;;
81+
*)
82+
warn "No direct-download path for this OS. Install manually: https://developer.hashicorp.com/terraform/install"
83+
return 1
84+
;;
85+
esac
86+
case "$(uname -m)" in
87+
x86_64 | amd64) arch="amd64" ;;
88+
arm64 | aarch64) arch="arm64" ;;
89+
*)
90+
warn "No direct-download path for architecture $(uname -m). Install manually: https://developer.hashicorp.com/terraform/install"
91+
return 1
92+
;;
93+
esac
94+
95+
# Pinned rather than queried dynamically (no jq/reliable version API without
96+
# extra dependencies here) — bump periodically.
97+
local version="1.15.7"
98+
local install_dir="$HOME/.local/bin"
99+
local tmp
100+
tmp="$(mktemp -d)"
101+
102+
info "Downloading Terraform ${version} (${os}/${arch})..."
103+
curl -sSfL -o "$tmp/terraform.zip" \
104+
"https://releases.hashicorp.com/terraform/${version}/terraform_${version}_${os}_${arch}.zip"
105+
mkdir -p "$install_dir"
106+
unzip -oq "$tmp/terraform.zip" -d "$install_dir"
107+
chmod +x "$install_dir/terraform"
108+
rm -rf "$tmp"
109+
110+
if ! echo ":$PATH:" | grep -q ":$install_dir:"; then
111+
warn "Installed to $install_dir, which isn't on PATH yet. Add this to your shell rc, then restart your shell:"
112+
echo " export PATH=\"$install_dir:\$PATH\""
113+
export PATH="$install_dir:$PATH"
114+
ok "Added it to PATH for the rest of this run."
115+
fi
116+
117+
ok "Terraform installed."
53118
}
54119

55120
##### DIRENV #####
@@ -64,8 +129,10 @@ ensure_direnv() {
64129
nix profile install nixpkgs#direnv
65130
fi
66131

67-
local hook_line="eval \"\$(direnv hook $(basename "${SHELL:-bash}"))\""
68-
local rc_file="$HOME/.$(basename "${SHELL:-bash}")rc"
132+
local shell_name
133+
shell_name="$(basename "${SHELL:-bash}")"
134+
local hook_line="eval \"\$(direnv hook $shell_name)\""
135+
local rc_file="$HOME/.${shell_name}rc"
69136
if [ -f "$rc_file" ] && grep -qF "direnv hook" "$rc_file"; then
70137
ok "direnv shell hook already present in $rc_file."
71138
else
@@ -148,8 +215,10 @@ cmd_repo() {
148215
# argument: guided flow with an interactive confirm before applying.
149216
local action="${1:-guided}"
150217

151-
if ! command -v terraform >/dev/null 2>&1 || ! command -v gh >/dev/null 2>&1; then
152-
warn "terraform and/or gh are missing. Run 'mat setup' first, then re-run this from inside the dev shell."
218+
# gh is optional — terraform/setup.sh falls back to an interactive token
219+
# prompt when it's missing or not logged in.
220+
if ! command -v terraform >/dev/null 2>&1; then
221+
warn "terraform is missing. Run 'mat setup' first."
153222
exit 1
154223
fi
155224

@@ -183,7 +252,7 @@ cmd_setup() {
183252
local os
184253
os="$(detect_os)"
185254
if [ "$os" = "unsupported" ]; then
186-
warn "Unrecognized OS. Nix/devenv support macOS and Linux (including WSL2) — on native Windows, install WSL2 first and run 'mat setup' from inside it."
255+
warn "Unrecognized OS. On native Windows, use bin/mat.ps1 instead (it offers WSL2, or a no-Nix Terraform-only fallback)."
187256
fi
188257

189258
info "Git hooks and .env (no Nix required for these)"
@@ -197,7 +266,24 @@ cmd_setup() {
197266
ensure_devenv || true
198267
if command -v direnv >/dev/null 2>&1; then
199268
direnv allow "$REPO_ROOT"
200-
ok "Ran 'direnv allow' — cd out and back into this directory to enter the dev shell."
269+
ok "Ran 'direnv allow'."
270+
fi
271+
272+
echo
273+
info "Configuring the GitHub repository (inside the devenv shell)"
274+
if command -v devenv >/dev/null 2>&1; then
275+
# terraform/gh only exist on PATH inside the devenv shell (declared in
276+
# devenv.nix's packages) — a bare 'terraform' call here would fail even
277+
# though nix/direnv/devenv are all now installed.
278+
devenv shell -- ./bin/mat repo
279+
else
280+
warn "devenv isn't available — skipping repo config. Re-run 'mat setup' once it is, or run 'mat repo' yourself from inside the dev shell."
281+
fi
282+
else
283+
echo
284+
info "Falling back to a direct Terraform install (no reproducible dev shell)"
285+
if ensure_terraform_direct; then
286+
cmd_repo
201287
fi
202288
fi
203289

@@ -211,13 +297,18 @@ usage() {
211297
Usage: mat <command>
212298
213299
Commands:
214-
setup Full local onboarding: Nix, direnv, devenv, Git hooks, .env
300+
setup Full local onboarding: Nix, direnv, devenv, Git hooks, .env,
301+
then configures the GitHub repository via terraform (or a
302+
direct Terraform install if you skip Nix)
215303
repo Configure the GitHub repository via terraform (branch
216304
protection, merge settings, collaborators, environments) —
217305
guided, asks before applying
218306
repo plan Same, but only shows the plan (no prompt, no apply)
219307
repo apply Applies the last plan (no prompt) — for scripted use once
220308
you've already reviewed a 'repo plan'
309+
310+
On native Windows (no WSL2), use bin/mat.ps1 instead — this script requires
311+
bash.
221312
EOF
222313
}
223314

bin/mat.ps1

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# mat.ps1 — native Windows entrypoint. Nix/direnv/devenv only run inside
2+
# WSL2, so this script's real job is: get you into WSL2 (where bin/mat gives
3+
# you the full reproducible dev shell), or — if you decline — fall back to a
4+
# Terraform-only path that skips the reproducible shell entirely.
5+
#
6+
# This is a separate, smaller reimplementation of bin/mat's repo-config
7+
# mechanics, not a wrapper around it — bin/mat is bash and won't run natively
8+
# on Windows. Keep the two in sync by hand if the terraform flow changes.
9+
10+
$ErrorActionPreference = "Stop"
11+
$RepoRoot = Split-Path -Parent $PSScriptRoot
12+
13+
function Confirm-Action {
14+
param([string]$Prompt)
15+
$reply = Read-Host "$Prompt [y/N]"
16+
return $reply -match '^[Yy]$'
17+
}
18+
19+
function Test-Wsl2 {
20+
try {
21+
wsl.exe -l -v *> $null
22+
return $LASTEXITCODE -eq 0
23+
} catch {
24+
return $false
25+
}
26+
}
27+
28+
function Enter-Wsl2Path {
29+
Write-Host "-> WSL2"
30+
if (Test-Wsl2) {
31+
Write-Host " WSL2 is already installed. Open a WSL2 terminal (e.g. Ubuntu) in this repo and run: ./bin/mat setup"
32+
return $true
33+
}
34+
if (Confirm-Action "WSL2 is not installed. Install it now (requires a restart)?") {
35+
wsl --install
36+
Write-Host "!! Restart your computer, then open a WSL2 terminal (e.g. Ubuntu) in this repo and run: ./bin/mat setup"
37+
exit 0
38+
}
39+
return $false
40+
}
41+
42+
function Set-GitHooksAndEnv {
43+
Push-Location $RepoRoot
44+
try {
45+
$hooksPath = git config --local --get core.hooksPath 2>$null
46+
if ($hooksPath -eq ".githooks") {
47+
Write-Host " Git hooks already activated."
48+
} else {
49+
git config core.hooksPath .githooks
50+
Write-Host " Activated Git hooks (core.hooksPath = .githooks)."
51+
}
52+
53+
if (Test-Path ".env") {
54+
Write-Host " .env already exists, leaving it alone."
55+
} else {
56+
Copy-Item ".env.example" ".env"
57+
Write-Host " Created .env from .env.example — fill in the values before running the project."
58+
}
59+
} finally {
60+
Pop-Location
61+
}
62+
}
63+
64+
function Install-TerraformWinget {
65+
if (Get-Command terraform -ErrorAction SilentlyContinue) {
66+
Write-Host " Terraform already installed."
67+
return $true
68+
}
69+
if (-not (Confirm-Action "Terraform is not installed. Install it now via winget?")) {
70+
Write-Host "!! Skipping Terraform install. Re-run bin/mat.ps1 once it's available, or install it yourself."
71+
return $false
72+
}
73+
winget install --id Hashicorp.Terraform -e --accept-source-agreements --accept-package-agreements
74+
return $true
75+
}
76+
77+
function Get-OriginSlug {
78+
$url = git -C $RepoRoot remote get-url origin 2>$null
79+
if (-not $url) { return $null }
80+
if ($url -match '[:/]([^/]+)/([^/.]+?)(\.git)?$') {
81+
return "$($Matches[1])/$($Matches[2])"
82+
}
83+
return $null
84+
}
85+
86+
function Get-GitHubToken {
87+
if (Get-Command gh -ErrorAction SilentlyContinue) {
88+
$token = gh auth token 2>$null
89+
if ($token) {
90+
Write-Host " Using GitHub token from 'gh auth token'."
91+
return $token
92+
}
93+
}
94+
$secure = Read-Host "GitHub token (repo admin scope)" -AsSecureString
95+
return [Runtime.InteropServices.Marshal]::PtrToStringAuto(
96+
[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure))
97+
}
98+
99+
function Invoke-RepoApply {
100+
$slug = Get-OriginSlug
101+
if (-not $slug) {
102+
Write-Host "!! Couldn't determine owner/repo from 'git remote get-url origin'."
103+
return
104+
}
105+
$owner, $repo = $slug -split '/', 2
106+
107+
Write-Host "-> Configuring GitHub repository: $slug"
108+
$tfvarsPath = Join-Path $RepoRoot "terraform\terraform.tfvars"
109+
@"
110+
repository_owner = "$owner"
111+
repository_name = "$repo"
112+
"@ | Set-Content -Path $tfvarsPath
113+
Write-Host " Wrote terraform/terraform.tfvars"
114+
115+
$env:GITHUB_TOKEN = Get-GitHubToken
116+
117+
Push-Location (Join-Path $RepoRoot "terraform")
118+
try {
119+
terraform init | Out-Host
120+
121+
$state = terraform state list 2>$null
122+
if (-not ($state -contains "module.github_repository.github_repository.this")) {
123+
Write-Host "-> Importing the existing repository into terraform state..."
124+
terraform import "module.github_repository.github_repository.this" $repo | Out-Host
125+
}
126+
127+
terraform plan | Out-Host
128+
129+
if (Confirm-Action "Apply these changes to $slug now?") {
130+
terraform apply | Out-Host
131+
} else {
132+
Write-Host "!! Skipped apply. Re-run bin/mat.ps1, or 'terraform apply' from terraform/, when ready."
133+
}
134+
} finally {
135+
Pop-Location
136+
}
137+
}
138+
139+
##### MAIN #####
140+
Write-Host "-> Git hooks and .env (no Nix required for these)"
141+
Set-GitHooksAndEnv
142+
143+
Write-Host ""
144+
if (-not (Enter-Wsl2Path)) {
145+
Write-Host ""
146+
Write-Host "-> Falling back to a direct Terraform install (no reproducible dev shell)"
147+
if (Install-TerraformWinget) {
148+
Invoke-RepoApply
149+
}
150+
}
151+
152+
Write-Host ""
153+
Write-Host "-> Setup complete. Full guide: docs/product-code/tutorials/getting-started.md"

docs/team-process/how-to/activate-git-hooks.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ This repo ships two shell-based hooks under `.githooks/` — no runtime or depen
1111

1212
## Steps
1313

14-
Running `./setup-project.sh` from the repository root does this for you, along
15-
with the rest of local project setup. To activate just the hooks by hand:
14+
Running `./bin/mat setup` (or `.\bin\mat.ps1` on native Windows) from the
15+
repository root does this for you, along with the rest of local project
16+
setup. To activate just the hooks by hand:
1617

1718
```bash
1819
git config core.hooksPath .githooks

terraform/README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,21 @@ variables, without touching the module itself.
3636

3737
## Running
3838

39-
Easiest path — `mat repo` (from `bin/mat`) detects the owner/repo from
40-
`git remote`, writes `terraform/terraform.tfvars`, runs `init`, imports the
41-
repo into state if it isn't already there, shows the plan, and asks before
42-
applying:
39+
`./bin/mat setup` already chains into this automatically on first-time
40+
onboarding (inside the devenv shell if you have Nix, or via a direct
41+
Terraform install if you don't — see `bin/mat`'s `cmd_setup`). To (re-)run it
42+
directly at any other time — `mat repo` (from `bin/mat`, once inside the dev
43+
shell) detects the owner/repo from `git remote`, writes
44+
`terraform/terraform.tfvars`, runs `init`, imports the repo into state if it
45+
isn't already there, shows the plan, and asks before applying:
4346

4447
```bash
4548
mat repo
4649
```
4750

51+
On native Windows without WSL2, `bin/mat.ps1` reimplements this same flow
52+
directly in PowerShell (bin/mat itself needs bash).
53+
4854
Or drive the underlying pieces yourself. `terraform/setup.sh` is the thin
4955
wrapper that sources a token from `gh` if you're logged in, or prompts for
5056
one otherwise:

0 commit comments

Comments
 (0)