@oaklandzoo/ostup 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -39
- package/bin/cli.mjs +63 -1
- package/package.json +2 -1
- package/scripts/install.ps1 +115 -0
- package/scripts/install.sh +144 -0
- package/src/bootstrap.mjs +201 -0
- package/src/credential-prompts.mjs +80 -15
- package/src/doctor.mjs +188 -0
- package/src/exec.mjs +48 -11
- package/src/mvp-flow.mjs +6 -1
- package/src/preflight.mjs +3 -6
- package/src/tool-registry.mjs +164 -0
- package/src/tracer.mjs +116 -0
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# ostup
|
|
2
2
|
|
|
3
3
|
Scaffold a new client project with one command. Get a live GitHub repo
|
|
4
|
-
and live Vercel deploy URL in under
|
|
4
|
+
and live Vercel deploy URL in under ten minutes, even from a stock
|
|
5
|
+
computer.
|
|
5
6
|
|
|
6
7
|
## What this does
|
|
7
8
|
|
|
@@ -17,60 +18,79 @@ When you run this tool, it will:
|
|
|
17
18
|
prior materials (research, reference repos, screenshots, brand
|
|
18
19
|
assets) you want the agent to have on hand
|
|
19
20
|
|
|
20
|
-
##
|
|
21
|
+
## Quick Start
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
Pick the path that matches your computer.
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
### Mac (beginner — stock computer with nothing installed)
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
```
|
|
28
|
-
brew install gh
|
|
29
|
-
```
|
|
30
|
-
Then log in:
|
|
31
|
-
```
|
|
32
|
-
gh auth login
|
|
33
|
-
```
|
|
34
|
-
Choose: GitHub.com, then HTTPS, then "Login with a web browser."
|
|
35
|
-
Follow the prompts.
|
|
27
|
+
Open Terminal and paste:
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```
|
|
45
|
-
Choose your email login method.
|
|
29
|
+
```
|
|
30
|
+
/bin/bash -c "$(curl -fsSL https://ostup-install.vercel.app/install.sh)"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
That script installs Homebrew and Node (if missing), then runs `ostup init`,
|
|
34
|
+
which installs Git, GitHub CLI, and Vercel CLI and walks you through signing
|
|
35
|
+
in. Total time: about 5-10 minutes the first time, mostly download waits.
|
|
46
36
|
|
|
47
|
-
|
|
37
|
+
To pre-set flags (advanced):
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
-
|
|
39
|
+
```
|
|
40
|
+
/bin/bash -c "$(curl -fsSL https://ostup-install.vercel.app/install.sh)" -- --yes --profile=default
|
|
41
|
+
```
|
|
51
42
|
|
|
52
|
-
|
|
53
|
-
|
|
43
|
+
### Windows (beginner — stock computer with nothing installed)
|
|
44
|
+
|
|
45
|
+
Open PowerShell and paste:
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
irm https://ostup-install.vercel.app/install.ps1 | iex
|
|
49
|
+
```
|
|
54
50
|
|
|
55
|
-
|
|
51
|
+
That script installs Node via WinGet (if missing), then runs `ostup init`,
|
|
52
|
+
which installs Git, GitHub CLI, and Vercel CLI and walks you through signing
|
|
53
|
+
in. Total time: about 5-10 minutes the first time.
|
|
56
54
|
|
|
57
|
-
|
|
55
|
+
To pre-set flags (advanced):
|
|
58
56
|
|
|
59
57
|
```
|
|
60
|
-
|
|
58
|
+
irm https://ostup-install.vercel.app/install.ps1 -OutFile install.ps1
|
|
59
|
+
./install.ps1 --yes --profile=default
|
|
61
60
|
```
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
### Developer (you already have Node 20+ and npm)
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
npx @oaklandzoo/ostup@latest init
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
ostup will detect any missing tools (`git`, `gh`, `vercel`) and offer to
|
|
69
|
+
install them. Pass `--skip-bootstrap` to bypass detection or `--no-install`
|
|
70
|
+
to see what would be installed without making changes.
|
|
65
71
|
|
|
66
72
|
If you'd rather install it globally so the command is just `ostup`:
|
|
67
73
|
|
|
68
74
|
```
|
|
69
75
|
npm install -g @oaklandzoo/ostup
|
|
70
|
-
ostup --version # should print 0.1.0
|
|
71
76
|
ostup init
|
|
72
77
|
```
|
|
73
78
|
|
|
79
|
+
## What ostup needs (handled for you by the beginner installer)
|
|
80
|
+
|
|
81
|
+
The beginner installer scripts above handle this list automatically.
|
|
82
|
+
This is what they're installing under the hood:
|
|
83
|
+
|
|
84
|
+
- Node.js 20+
|
|
85
|
+
- Git
|
|
86
|
+
- GitHub CLI (`gh`)
|
|
87
|
+
- Vercel CLI (`vercel`)
|
|
88
|
+
- A free GitHub account (https://github.com/signup) — sign in during the flow
|
|
89
|
+
- A free Vercel account (https://vercel.com/signup) — sign in during the flow
|
|
90
|
+
|
|
91
|
+
If you do not have GitHub or Vercel accounts yet, the tool pauses and
|
|
92
|
+
walks you through creating them when you reach those steps.
|
|
93
|
+
|
|
74
94
|
### Alternate: install from source
|
|
75
95
|
|
|
76
96
|
If you cloned https://github.com/DubsFan/goodshin and want to run from
|
|
@@ -168,16 +188,17 @@ you do not have to repeat them every session.
|
|
|
168
188
|
|
|
169
189
|
| Problem | Fix |
|
|
170
190
|
|---|---|
|
|
171
|
-
| "
|
|
172
|
-
| "gh
|
|
173
|
-
| "vercel
|
|
174
|
-
| "gh auth required" | Run `gh auth login` |
|
|
175
|
-
| "vercel auth required" | Run `vercel login` |
|
|
191
|
+
| "command not found" for node/gh/vercel | Run the beginner installer for your OS (top of this README). Or run `ostup bootstrap` if you already have Node. |
|
|
192
|
+
| "gh auth required" | Re-run `ostup init` and pick "Sign in to GitHub in your browser". Or run `gh auth login`. |
|
|
193
|
+
| "vercel auth required" | Re-run `ostup init` and pick "Sign in to Vercel in your browser". Or run `vercel login`. |
|
|
176
194
|
| "Project name invalid" | Use only lowercase letters, numbers, and hyphens |
|
|
177
195
|
| "Repo already exists" | Pick a different project name |
|
|
178
196
|
| Vercel deploy hangs more than 5 minutes | Cancel with Ctrl-C, run again |
|
|
179
197
|
| Deploy URL returns 401 | The tool tried to auto-disable Vercel deployment protection but could not. Open Vercel dashboard, find your project, Settings, Deployment Protection, Disable. |
|
|
180
198
|
| "ingest path not found" | The path you gave does not exist. Re-run and provide a valid absolute or relative path. |
|
|
199
|
+
| Beginner installer failed | The bash and PowerShell installers log to `~/.ostup/logs/install-*.log`. The ostup CLI logs to `~/.ostup/logs/init-*.log`. Open the most recent log for the failing command's stderr. |
|
|
200
|
+
| "BOOTSTRAP_FAILED: Homebrew missing" | You ran `npx ostup init` on a Mac without Homebrew. Run the Mac beginner installer at the top of this README instead. |
|
|
201
|
+
| "BOOTSTRAP_FAILED: WinGet missing" | Update App Installer from the Microsoft Store: `ms-windows-store://pdp/?productid=9NBLGGH4NNS1`. |
|
|
181
202
|
|
|
182
203
|
## Advanced: API tokens instead of interactive login
|
|
183
204
|
|
package/bin/cli.mjs
CHANGED
|
@@ -5,12 +5,13 @@ import { dirname, resolve } from 'node:path';
|
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { loadDotEnv } from '../src/env-loader.mjs';
|
|
7
7
|
import { setDryRun } from '../src/exec.mjs';
|
|
8
|
+
import { startRun, endRun, disableTracer, traceError } from '../src/tracer.mjs';
|
|
8
9
|
|
|
9
10
|
loadDotEnv();
|
|
10
11
|
|
|
11
12
|
const PKG_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
12
13
|
|
|
13
|
-
const SUBCOMMANDS = new Set(['init', 'update', 'brief', 'export-pro']);
|
|
14
|
+
const SUBCOMMANDS = new Set(['init', 'update', 'brief', 'export-pro', 'doctor', 'bootstrap']);
|
|
14
15
|
|
|
15
16
|
async function readPkg() {
|
|
16
17
|
const raw = await readFile(resolve(PKG_ROOT, 'package.json'), 'utf8');
|
|
@@ -44,6 +45,9 @@ function parseArgs(argv) {
|
|
|
44
45
|
else if (a === '--output') flags.output = argv[++i];
|
|
45
46
|
else if (a.startsWith('--output=')) flags.output = a.slice('--output='.length);
|
|
46
47
|
else if (a === '--white-label') flags.whiteLabel = true;
|
|
48
|
+
else if (a === '--no-log') flags.noLog = true;
|
|
49
|
+
else if (a === '--skip-bootstrap') flags.skipBootstrap = true;
|
|
50
|
+
else if (a === '--no-install') flags.noInstall = true;
|
|
47
51
|
else if (a.startsWith('-')) {
|
|
48
52
|
process.stderr.write(`unknown flag: ${a}\n`);
|
|
49
53
|
process.exit(1);
|
|
@@ -59,13 +63,21 @@ function printHelp() {
|
|
|
59
63
|
[
|
|
60
64
|
'ostup: scaffold a new repo with the Ostup Agent Kit plus GitHub and Vercel.',
|
|
61
65
|
'',
|
|
66
|
+
'First time on this computer? Use the beginner installer for your OS:',
|
|
67
|
+
' Mac: /bin/bash -c "$(curl -fsSL https://ostup-install.vercel.app/install.sh)"',
|
|
68
|
+
' Windows: irm https://ostup-install.vercel.app/install.ps1 | iex',
|
|
69
|
+
'',
|
|
70
|
+
'Already have Node 20+ and npm? Use this CLI directly.',
|
|
71
|
+
'',
|
|
62
72
|
'Usage:',
|
|
63
73
|
' ostup <command> [flags]',
|
|
64
74
|
'',
|
|
65
75
|
'Commands:',
|
|
66
76
|
' init Scaffold a new project (interactive or with --yes).',
|
|
77
|
+
' bootstrap Install missing tools (git, gh, vercel) on this machine.',
|
|
67
78
|
' brief Run the 10-question operator intake; write docs/brief.md + brief.json.',
|
|
68
79
|
' export-pro Bundle brief + brand + content + initial PRD into a ZIP for client handoff.',
|
|
80
|
+
' doctor Self-diagnosis: tool detection + auth + permissions + disk + Chrome. Read-only.',
|
|
69
81
|
' update Refresh bundled templates from the pinned source.',
|
|
70
82
|
'',
|
|
71
83
|
'Flags for `ostup init`:',
|
|
@@ -79,6 +91,12 @@ function printHelp() {
|
|
|
79
91
|
' --white-label Strip OSTUP / Goodshin attribution from generated docs (Studio tier).',
|
|
80
92
|
' --kit-only Drop the markdown kit into a target dir, no GitHub or Vercel.',
|
|
81
93
|
' --config <path> Read .ostup-config.yml from this path (kit-only mode).',
|
|
94
|
+
' --skip-bootstrap Skip the in-CLI tool detection / install step (advanced).',
|
|
95
|
+
' --no-install Detect missing tools, print the install plan, exit without installing.',
|
|
96
|
+
'',
|
|
97
|
+
'Flags for `ostup bootstrap`:',
|
|
98
|
+
' --yes, -y Auto-accept the install prompt and choose browser auth where possible.',
|
|
99
|
+
' --no-install Print the install plan, exit without installing.',
|
|
82
100
|
'',
|
|
83
101
|
'Flags for `ostup brief`:',
|
|
84
102
|
' --brief <path> Load brief.json from <path> instead of running the intake.',
|
|
@@ -93,6 +111,7 @@ function printHelp() {
|
|
|
93
111
|
'Global flags:',
|
|
94
112
|
' --version, -v Print version and exit.',
|
|
95
113
|
' --help, -h Print this help and exit.',
|
|
114
|
+
' --no-log Disable auto-logging to ~/.ostup/logs/. (default: write a log per run)',
|
|
96
115
|
'',
|
|
97
116
|
].join('\n')
|
|
98
117
|
);
|
|
@@ -171,6 +190,36 @@ if (subcommand === 'export-pro') {
|
|
|
171
190
|
}
|
|
172
191
|
}
|
|
173
192
|
|
|
193
|
+
if (subcommand === 'doctor') {
|
|
194
|
+
const { runDoctor, printDoctorReport } = await import('../src/doctor.mjs');
|
|
195
|
+
try {
|
|
196
|
+
const result = await runDoctor();
|
|
197
|
+
process.stdout.write(printDoctorReport(result));
|
|
198
|
+
process.exit(result.failCount > 0 ? 1 : 0);
|
|
199
|
+
} catch (err) {
|
|
200
|
+
process.stderr.write(`doctor failed: ${err.message}\n`);
|
|
201
|
+
process.exit(2);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (subcommand === 'bootstrap') {
|
|
206
|
+
const { runBootstrapStandalone } = await import('../src/bootstrap.mjs');
|
|
207
|
+
try {
|
|
208
|
+
await runBootstrapStandalone({ flags });
|
|
209
|
+
process.exit(0);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
process.stderr.write(`${err.message}\n`);
|
|
212
|
+
const userErrors = new Set([
|
|
213
|
+
'NO_TTY_BOOTSTRAP',
|
|
214
|
+
'BOOTSTRAP_DECLINED',
|
|
215
|
+
'BOOTSTRAP_FAILED',
|
|
216
|
+
'BOOTSTRAP_VERIFY_FAILED',
|
|
217
|
+
'UNSUPPORTED_OS_INSTALL',
|
|
218
|
+
]);
|
|
219
|
+
process.exit(userErrors.has(err.code) ? 1 : 2);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
174
223
|
// subcommand === 'init'
|
|
175
224
|
if (flags.kitOnly) {
|
|
176
225
|
const { scaffold } = await import('../src/scaffold.mjs');
|
|
@@ -185,12 +234,20 @@ if (flags.kitOnly) {
|
|
|
185
234
|
}
|
|
186
235
|
}
|
|
187
236
|
|
|
237
|
+
if (flags.noLog) disableTracer();
|
|
238
|
+
const logPath = await startRun({ label: 'init' });
|
|
239
|
+
if (logPath) process.stdout.write(`Logging this run to ${logPath}\n`);
|
|
240
|
+
|
|
188
241
|
const { runMvp } = await import('../src/mvp-flow.mjs');
|
|
189
242
|
try {
|
|
190
243
|
await runMvp({ flags });
|
|
244
|
+
await endRun({ ok: true });
|
|
191
245
|
process.exit(0);
|
|
192
246
|
} catch (err) {
|
|
247
|
+
await traceError('init', err);
|
|
248
|
+
await endRun({ ok: false });
|
|
193
249
|
process.stderr.write(`${err.message}\n`);
|
|
250
|
+
if (logPath) process.stderr.write(`Full run log: ${logPath}\n`);
|
|
194
251
|
const userErrors = new Set([
|
|
195
252
|
'NO_TTY',
|
|
196
253
|
'USER_ABORT',
|
|
@@ -200,6 +257,11 @@ try {
|
|
|
200
257
|
'INGEST_PATH_NOT_FOUND',
|
|
201
258
|
'BRIEF_NOT_FOUND',
|
|
202
259
|
'BRIEF_INVALID',
|
|
260
|
+
'NO_TTY_BOOTSTRAP',
|
|
261
|
+
'BOOTSTRAP_DECLINED',
|
|
262
|
+
'BOOTSTRAP_FAILED',
|
|
263
|
+
'BOOTSTRAP_VERIFY_FAILED',
|
|
264
|
+
'UNSUPPORTED_OS_INSTALL',
|
|
203
265
|
]);
|
|
204
266
|
process.exit(userErrors.has(err.code) ? 1 : 2);
|
|
205
267
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oaklandzoo/ostup",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Scaffolds a new repo with the Ostup Agent Kit pre-installed: slash commands, doc templates, and a clean working state.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -44,6 +44,7 @@
|
|
|
44
44
|
"files": [
|
|
45
45
|
"bin/",
|
|
46
46
|
"src/",
|
|
47
|
+
"scripts/",
|
|
47
48
|
"templates/",
|
|
48
49
|
"LICENSE",
|
|
49
50
|
"README.md"
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# ostup beginner installer for Windows.
|
|
2
|
+
#
|
|
3
|
+
# Stage 1: get this PC to "can run Node 20+". That means installing Node via WinGet
|
|
4
|
+
# (if missing or below v20). Nothing else. Git, GitHub CLI, Vercel CLI, auth, scaffold,
|
|
5
|
+
# and deploy all happen in stage 2 — the ostup CLI's in-CLI bootstrap.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# irm https://ostup-install.vercel.app/install.ps1 | iex
|
|
9
|
+
#
|
|
10
|
+
# Forward flags to `ostup init` (download then run):
|
|
11
|
+
# irm https://ostup-install.vercel.app/install.ps1 -OutFile install.ps1
|
|
12
|
+
# ./install.ps1 --yes --profile=default
|
|
13
|
+
|
|
14
|
+
#Requires -Version 5.1
|
|
15
|
+
$ErrorActionPreference = 'Stop'
|
|
16
|
+
|
|
17
|
+
# --- logging -----------------------------------------------------------------
|
|
18
|
+
$LogDir = Join-Path $env:USERPROFILE '.ostup\logs'
|
|
19
|
+
New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
|
|
20
|
+
$ts = (Get-Date).ToUniversalTime().ToString('yyyyMMddTHHmmssZ')
|
|
21
|
+
$LogFile = Join-Path $LogDir "install-$ts.log"
|
|
22
|
+
try { Start-Transcript -Path $LogFile -ErrorAction Stop | Out-Null } catch {}
|
|
23
|
+
|
|
24
|
+
# --- detect ------------------------------------------------------------------
|
|
25
|
+
$hasWinget = $null -ne (Get-Command winget -ErrorAction SilentlyContinue)
|
|
26
|
+
$hasNodeOk = $false
|
|
27
|
+
$nodeVerDetected = ''
|
|
28
|
+
if (Get-Command node -ErrorAction SilentlyContinue) {
|
|
29
|
+
try {
|
|
30
|
+
$nodeVerDetected = (& node --version).Trim()
|
|
31
|
+
if ($nodeVerDetected -match '^v(\d+)') {
|
|
32
|
+
$nodeMajor = [int]$Matches[1]
|
|
33
|
+
if ($nodeMajor -ge 20) { $hasNodeOk = $true }
|
|
34
|
+
}
|
|
35
|
+
} catch {}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
# --- WinGet gate -------------------------------------------------------------
|
|
39
|
+
if (-not $hasWinget) {
|
|
40
|
+
Write-Host ''
|
|
41
|
+
Write-Host 'WinGet (Microsoft App Installer) is required to install Node automatically.'
|
|
42
|
+
Write-Host 'On Windows 11, WinGet is built in. On Windows 10, you may need to update App Installer:'
|
|
43
|
+
Write-Host ' ms-windows-store://pdp/?productid=9NBLGGH4NNS1'
|
|
44
|
+
Write-Host 'Open that URL in your browser, install or update App Installer, then re-run.'
|
|
45
|
+
try { Stop-Transcript | Out-Null } catch {}
|
|
46
|
+
exit 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
$needNode = -not $hasNodeOk
|
|
50
|
+
|
|
51
|
+
# --- explainer + consent (only when something is missing) -------------------
|
|
52
|
+
if ($needNode) {
|
|
53
|
+
Write-Host ''
|
|
54
|
+
Write-Host "You're about to set up your PC to run modern web tools."
|
|
55
|
+
Write-Host ''
|
|
56
|
+
if ($nodeVerDetected) {
|
|
57
|
+
Write-Host " 1. Node.js - The engine that runs the website setup tool (ostup)."
|
|
58
|
+
Write-Host " Your current Node $nodeVerDetected is too old; we need v20+."
|
|
59
|
+
} else {
|
|
60
|
+
Write-Host " 1. Node.js - The engine that runs the website setup tool (ostup)."
|
|
61
|
+
}
|
|
62
|
+
Write-Host ''
|
|
63
|
+
Write-Host 'Node will install via WinGet (Microsoft package manager). Windows may show a prompt'
|
|
64
|
+
Write-Host 'asking you to allow the install.'
|
|
65
|
+
Write-Host ''
|
|
66
|
+
Write-Host 'After this, ostup will continue and install Git, GitHub CLI, and Vercel CLI,'
|
|
67
|
+
Write-Host 'then walk you through signing in.'
|
|
68
|
+
Write-Host ''
|
|
69
|
+
Write-Host 'Total time: about 3-5 minutes for this step. Requires an internet connection.'
|
|
70
|
+
Write-Host ''
|
|
71
|
+
|
|
72
|
+
if ([Environment]::UserInteractive) {
|
|
73
|
+
$yn = Read-Host 'Continue? [Y/n]'
|
|
74
|
+
if ($yn -and $yn -notmatch '^(y|yes)$') {
|
|
75
|
+
Write-Host 'Cancelled. Re-run when ready.'
|
|
76
|
+
try { Stop-Transcript | Out-Null } catch {}
|
|
77
|
+
exit 1
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
Write-Host '(non-interactive session; proceeding)'
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Write-Host ''
|
|
84
|
+
Write-Host 'Installing Node via WinGet...'
|
|
85
|
+
& winget install -e --id OpenJS.NodeJS.LTS --accept-package-agreements --accept-source-agreements
|
|
86
|
+
if ($LASTEXITCODE -ne 0) {
|
|
87
|
+
Write-Host ''
|
|
88
|
+
Write-Host "ERROR: WinGet returned exit code $LASTEXITCODE."
|
|
89
|
+
Write-Host "Log: $LogFile"
|
|
90
|
+
try { Stop-Transcript | Out-Null } catch {}
|
|
91
|
+
exit 1
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Patch PATH for this PowerShell session so npx works without restarting.
|
|
95
|
+
$env:Path = "C:\Program Files\nodejs;$env:Path"
|
|
96
|
+
|
|
97
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
|
|
98
|
+
Write-Host 'ERROR: Node install completed but node is not on PATH in this session.'
|
|
99
|
+
Write-Host 'Open a new PowerShell window and re-run, or check the log:'
|
|
100
|
+
Write-Host " $LogFile"
|
|
101
|
+
try { Stop-Transcript | Out-Null } catch {}
|
|
102
|
+
exit 1
|
|
103
|
+
}
|
|
104
|
+
$postVer = (& node --version).Trim()
|
|
105
|
+
Write-Host "Node $postVer installed."
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# --- hand off to the ostup CLI ----------------------------------------------
|
|
109
|
+
Write-Host ''
|
|
110
|
+
Write-Host 'Stage 1 complete. Continuing with ostup...'
|
|
111
|
+
Write-Host ''
|
|
112
|
+
|
|
113
|
+
try { Stop-Transcript | Out-Null } catch {}
|
|
114
|
+
& npx '@oaklandzoo/ostup@latest' init @args
|
|
115
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# ostup beginner installer for macOS.
|
|
3
|
+
#
|
|
4
|
+
# Stage 1: get this Mac to "can run Node 20+". That means installing Homebrew (if missing)
|
|
5
|
+
# and Node (if missing or below v20). Nothing else. Git, GitHub CLI, Vercel CLI, auth,
|
|
6
|
+
# scaffold, and deploy all happen in stage 2 — the ostup CLI's in-CLI bootstrap.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# /bin/bash -c "$(curl -fsSL https://ostup-install.vercel.app/install.sh)"
|
|
10
|
+
#
|
|
11
|
+
# Forward flags to `ostup init`:
|
|
12
|
+
# /bin/bash -c "$(curl -fsSL ...install.sh)" -- --yes --profile=default
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
# --- logging -----------------------------------------------------------------
|
|
17
|
+
LOG_DIR="${HOME}/.ostup/logs"
|
|
18
|
+
mkdir -p "$LOG_DIR"
|
|
19
|
+
LOG_FILE="${LOG_DIR}/install-$(date -u +%Y%m%dT%H%M%SZ).log"
|
|
20
|
+
exec > >(tee -a "$LOG_FILE") 2>&1
|
|
21
|
+
|
|
22
|
+
# --- platform check ----------------------------------------------------------
|
|
23
|
+
UNAME_S="$(uname -s 2>/dev/null || echo unknown)"
|
|
24
|
+
if [ "$UNAME_S" != "Darwin" ]; then
|
|
25
|
+
cat <<EOF
|
|
26
|
+
This installer is for macOS only.
|
|
27
|
+
|
|
28
|
+
On Windows, open PowerShell and run:
|
|
29
|
+
irm https://ostup-install.vercel.app/install.ps1 | iex
|
|
30
|
+
|
|
31
|
+
On Linux, install Node 20+ from https://nodejs.org or your package manager, then run:
|
|
32
|
+
npx @oaklandzoo/ostup@latest init
|
|
33
|
+
EOF
|
|
34
|
+
exit 1
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# --- detect ------------------------------------------------------------------
|
|
38
|
+
HAS_BREW=0
|
|
39
|
+
HAS_NODE_OK=0
|
|
40
|
+
NODE_VER_DETECTED=""
|
|
41
|
+
|
|
42
|
+
if command -v brew >/dev/null 2>&1; then
|
|
43
|
+
HAS_BREW=1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
if command -v node >/dev/null 2>&1; then
|
|
47
|
+
NODE_VER_DETECTED="$(node --version 2>/dev/null || echo '')"
|
|
48
|
+
NODE_MAJOR="$(echo "$NODE_VER_DETECTED" | sed -E 's/^v([0-9]+).*/\1/')"
|
|
49
|
+
if [ -n "$NODE_MAJOR" ] && [ "$NODE_MAJOR" -ge 20 ] 2>/dev/null; then
|
|
50
|
+
HAS_NODE_OK=1
|
|
51
|
+
fi
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# --- explainer + consent (only when something is missing) --------------------
|
|
55
|
+
NEED_BREW=$(( 1 - HAS_BREW ))
|
|
56
|
+
NEED_NODE=$(( 1 - HAS_NODE_OK ))
|
|
57
|
+
|
|
58
|
+
if [ "$NEED_BREW" -eq 1 ] || [ "$NEED_NODE" -eq 1 ]; then
|
|
59
|
+
echo ""
|
|
60
|
+
echo "You're about to set up your Mac to run modern web tools."
|
|
61
|
+
echo ""
|
|
62
|
+
i=1
|
|
63
|
+
if [ "$NEED_BREW" -eq 1 ]; then
|
|
64
|
+
echo " $i. Homebrew - Apple's package manager. Used to install Node."
|
|
65
|
+
i=$((i + 1))
|
|
66
|
+
fi
|
|
67
|
+
if [ "$NEED_NODE" -eq 1 ]; then
|
|
68
|
+
if [ -n "$NODE_VER_DETECTED" ]; then
|
|
69
|
+
echo " $i. Node.js - The engine that runs the website setup tool (ostup)."
|
|
70
|
+
echo " Your current Node $NODE_VER_DETECTED is too old; we need v20+."
|
|
71
|
+
else
|
|
72
|
+
echo " $i. Node.js - The engine that runs the website setup tool (ostup)."
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
echo ""
|
|
76
|
+
if [ "$NEED_BREW" -eq 1 ]; then
|
|
77
|
+
echo "You'll be asked for your Mac login password once (for Homebrew). It goes"
|
|
78
|
+
echo "straight to your Mac, not to ostup - we never see or store it."
|
|
79
|
+
echo ""
|
|
80
|
+
fi
|
|
81
|
+
echo "After this, ostup will continue and install GitHub CLI and Vercel CLI,"
|
|
82
|
+
echo "then walk you through signing in."
|
|
83
|
+
echo ""
|
|
84
|
+
echo "Total time: about 3-5 minutes for this step, depending on your internet."
|
|
85
|
+
echo "Requires an internet connection."
|
|
86
|
+
echo ""
|
|
87
|
+
if [ -t 0 ]; then
|
|
88
|
+
read -r -p "Continue? [Y/n] " yn
|
|
89
|
+
case "$yn" in
|
|
90
|
+
""|y|Y|yes|YES) ;;
|
|
91
|
+
*)
|
|
92
|
+
echo "Cancelled. Re-run the command when ready."
|
|
93
|
+
exit 1
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
else
|
|
97
|
+
echo "(no terminal detected; proceeding non-interactively)"
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# --- install Homebrew if missing --------------------------------------------
|
|
102
|
+
if [ "$HAS_BREW" -eq 0 ]; then
|
|
103
|
+
echo ""
|
|
104
|
+
echo "[1/2] Installing Homebrew..."
|
|
105
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
106
|
+
# Add brew to current shell's PATH.
|
|
107
|
+
if [ -x /opt/homebrew/bin/brew ]; then
|
|
108
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
109
|
+
elif [ -x /usr/local/bin/brew ]; then
|
|
110
|
+
eval "$(/usr/local/bin/brew shellenv)"
|
|
111
|
+
else
|
|
112
|
+
echo "ERROR: Homebrew installation appears to have failed (brew not found at /opt/homebrew/bin or /usr/local/bin)."
|
|
113
|
+
echo "Log: $LOG_FILE"
|
|
114
|
+
exit 1
|
|
115
|
+
fi
|
|
116
|
+
echo "Homebrew installed."
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# --- install Node if missing or too old --------------------------------------
|
|
120
|
+
if [ "$HAS_NODE_OK" -eq 0 ]; then
|
|
121
|
+
echo ""
|
|
122
|
+
echo "[2/2] Installing Node via Homebrew..."
|
|
123
|
+
brew install node
|
|
124
|
+
# Confirm node is now on PATH and at ≥20.
|
|
125
|
+
if ! command -v node >/dev/null 2>&1; then
|
|
126
|
+
echo "ERROR: Node install completed but 'node' is not on PATH. Open a new Terminal window and re-run."
|
|
127
|
+
echo "Log: $LOG_FILE"
|
|
128
|
+
exit 1
|
|
129
|
+
fi
|
|
130
|
+
POST_VER="$(node --version)"
|
|
131
|
+
POST_MAJOR="$(echo "$POST_VER" | sed -E 's/^v([0-9]+).*/\1/')"
|
|
132
|
+
if [ "$POST_MAJOR" -lt 20 ] 2>/dev/null; then
|
|
133
|
+
echo "ERROR: Node $POST_VER installed but version is still below 20. Run: brew upgrade node"
|
|
134
|
+
echo "Log: $LOG_FILE"
|
|
135
|
+
exit 1
|
|
136
|
+
fi
|
|
137
|
+
echo "Node $POST_VER installed."
|
|
138
|
+
fi
|
|
139
|
+
|
|
140
|
+
# --- hand off to the ostup CLI ----------------------------------------------
|
|
141
|
+
echo ""
|
|
142
|
+
echo "Stage 1 complete. Continuing with ostup..."
|
|
143
|
+
echo ""
|
|
144
|
+
exec npx @oaklandzoo/ostup@latest init "$@"
|