Skip to content

Commit 418cc3d

Browse files
Tom Schoenclaude
authored andcommitted
fix(launcher): survive the GitHub-ZIP download (kills the "(null)" error)
A GitHub ZIP + macOS unzip strips the executable flag and adds a quarantine flag, so double-clicking the freshly-downloaded VolScope.app failed with '…does not have permission to open "(null)"'. This is a hard limitation of shipping a committed .app via ZIP — not fixable by the bundle alone. Fix the real flow instead: - Start VolScope.command now runs the bootstrap via `bash` (bash reads a script regardless of its executable flag), so the FIRST launch works from a stripped ZIP. - The bootstrap self-heals on that first run: chmod +x on its own launchers + xattr -dr com.apple.quarantine on the bundle, so EVERY launch after the first is a normal double-click on the icon. - The bootstrap also drops a sitecustomize shim into the new venv before pip runs, so the install can't die on Anaconda Pythons (their sys.version string breaks platform.python_implementation() mid-install). README rewritten into a dead-simple, explained two-step pipeline: a one-time "type `bash ` + drag Start VolScope.command into Terminal" setup (with the WHY — macOS strips the run-flag from ZIPs), then double-click the icon forever after. Honest: this is not zero-install, but it is one-time and bulletproof. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 86e6a25 commit 418cc3d

3 files changed

Lines changed: 57 additions & 19 deletions

File tree

README.md

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ a vol desk reads: IV rank, a volatility cone, a time-travelling term structure,
2121
**own** Black-Scholes solver, **never** Yahoo's IV. The volatility screen you'd
2222
otherwise rent from Bloomberg, running in a browser tab on your own machine.
2323

24-
> **▶ Get started in one click:** download the ZIP, double-click **`VolScope.app`** — no terminal, no commands. [Full steps ](#run-volscope--just-double-click)
24+
> **▶ Get started:** download the ZIP → one quick one-time setup → then it's just a double-click on the **VolScope** icon. [How ](#-run-volscope)
2525
2626
## Why VolScope
2727

@@ -50,25 +50,32 @@ machine. What makes it strong:
5050
estimator is property-tested, and analytics never crash the UI — bad
5151
input returns nothing, not an exception.
5252

53-
## Run VolScope — just double-click
53+
## Run VolScope
5454

55-
**No terminal. No commands.** Download, unzip, double-click **`VolScope.app`**.
55+
Download the ZIP from GitHub and unzip it — you'll get a `volscope-…` folder
56+
with a **VolScope** app icon inside. It runs in two small steps: a one-time
57+
setup, then a double-click forever after.
5658

57-
1. On the [repo page](https://github.com/SchoenTom/volscope): **Code ▸ Download ZIP**, then unzip.
58-
2. Double-click **`VolScope.app`**. The first launch quietly builds a private
59-
environment and fetches a starter universe (~2 min, one time only); every
60-
launch after that opens in a couple of seconds.
61-
3. VolScope opens in its own clean window. Quit it like any app (Dock → Quit)
62-
to stop the local server.
59+
> **Why a one-time step?** macOS strips the "this file may run" flag from
60+
> anything downloaded as a ZIP (a built-in security default — nothing to do with
61+
> VolScope). So the **first** launch goes through Terminal once to install
62+
> VolScope's private environment. That same step re-enables the flag, so from
63+
> then on the **`VolScope.app`** icon opens with a normal double-click.
6364
64-
> **First time only — macOS Gatekeeper.** Because the app isn't code-signed,
65-
> macOS asks once: **right-click `VolScope.app` → Open → Open**. A normal
66-
> double-click works ever after. *(If Finder still refuses, the included
67-
> `Start VolScope.command` does the exact same thing.)*
65+
### ① First launch — once (~2 min)
6866

69-
Needs **Python 3.11+** installed on your Mac — the app builds its own isolated
70-
environment and never touches your system Python. Everything runs locally; no
71-
account, no subscription, no data leaves your machine.
67+
1. **Download & unzip.** On the [repo page](https://github.com/SchoenTom/volscope): **Code ▸ Download ZIP**, then double-click the downloaded ZIP to unzip it.
68+
2. **Open Terminal.** Press **⌘ + Space**, type `Terminal`, press **Enter**.
69+
3. In the Terminal window type **`bash `** (the word *bash* followed by one space) — then **drag the `Start VolScope.command` file** from the unzipped folder onto the Terminal window. Its full location fills in for you.
70+
4. Press **Enter**. VolScope installs its environment, fetches a starter
71+
universe, and opens. ☕ (~2 minutes — only this first time.)
72+
73+
### ② Every time after that
74+
75+
Just **double-click `VolScope.app`** 🟢 — it opens in its own clean window in a
76+
couple of seconds. Quit it like any app (**Dock → Quit**) to stop the local server.
77+
78+
> *Requires **Python 3.11+** on your Mac (free from [python.org](https://www.python.org/downloads/)). VolScope builds its own isolated environment and never touches your system Python. Everything runs locally — no account, no subscription, no data ever leaves your machine.*
7279
7380
<details>
7481
<summary><b>Developer / command-line setup</b></summary>

Start VolScope.command

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
#!/bin/bash
2-
# Fallback launcher — only needed if double-clicking VolScope.app is blocked.
3-
# Runs the exact same thing VolScope.app does.
2+
# First-run launcher for VolScope.
3+
#
4+
# A GitHub ZIP download strips the "executable" flag from files, so the very
5+
# first launch can't be a double-click — it goes through this script once
6+
# (run it with: bash "Start VolScope.command"). It runs the app's bootstrap
7+
# via `bash`, which doesn't need the executable flag, and the bootstrap then
8+
# re-sets that flag so every launch AFTER this one is a normal double-click on
9+
# VolScope.app.
410
DIR="$(cd "$(dirname "$0")" && pwd)"
5-
exec "$DIR/VolScope.app/Contents/MacOS/VolScope"
11+
exec bash "$DIR/VolScope.app/Contents/MacOS/VolScope"

VolScope.app/Contents/MacOS/VolScope

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,16 @@ APP_DIR="$(cd "$MACOS_DIR/../.." && pwd)" # …/VolScope.app
2020
REPO="$(cd "$APP_DIR/.." && pwd)" # repo root
2121
RES_DIR="$APP_DIR/Contents/Resources"
2222

23+
# ── Self-heal the download (so the icon is double-clickable afterwards) ──
24+
# A GitHub ZIP + macOS unzip strips the "executable" flag and adds a
25+
# quarantine flag, which makes a fresh double-click fail with
26+
# '…does not have permission to open "(null)"'. Re-set both here, so AFTER
27+
# this first (Terminal-launched) run, double-clicking VolScope.app just works.
28+
chmod +x "$MACOS_DIR/VolScope" 2>/dev/null || true
29+
[ -f "$REPO/Start VolScope.command" ] && chmod +x "$REPO/Start VolScope.command" 2>/dev/null || true
30+
xattr -dr com.apple.quarantine "$APP_DIR" 2>/dev/null || true
31+
xattr -dr com.apple.quarantine "$REPO/Start VolScope.command" 2>/dev/null || true
32+
2333
LOG_DIR="$HOME/Library/Logs/VolScope"
2434
mkdir -p "$LOG_DIR"
2535
SETUP_LOG="$LOG_DIR/setup.log"
@@ -75,6 +85,21 @@ if [ ! -x "$STREAMLIT" ]; then
7585
{
7686
echo "== $(date) VolScope setup =="
7787
"$PY" -m venv "$VENV"
88+
# Bulletproof pip on Anaconda Pythons: their sys.version carries an extra
89+
# "| packaged by Anaconda |" segment that breaks
90+
# platform.python_implementation() mid-install. A tiny sitecustomize shim
91+
# normalises it for every interpreter start (pip's process included).
92+
VSITE="$("$VENV/bin/python" -c 'import site; print(site.getsitepackages()[0])' 2>/dev/null)"
93+
if [ -n "$VSITE" ]; then
94+
printf '%s\n' \
95+
'import sys as _s' \
96+
'if "Anaconda" in _s.version and _s.version.count("|") >= 2:' \
97+
' _p = _s.version.split("|")' \
98+
' if len(_p) >= 3:' \
99+
' try: _s.version = _p[0].strip() + " " + " ".join(x.strip() for x in _p[2:])' \
100+
' except Exception: pass' \
101+
> "$VSITE/sitecustomize.py"
102+
fi
78103
"$VENV/bin/python" -m pip install --upgrade pip
79104
"$VENV/bin/python" -m pip install -r "$REPO/requirements.txt"
80105
} >>"$SETUP_LOG" 2>&1

0 commit comments

Comments
 (0)