Skip to content

chore(catalog): add connect-stack dev helper#4833

Open
drernie wants to merge 2 commits into
masterfrom
chore/catalog-connect-stack
Open

chore(catalog): add connect-stack dev helper#4833
drernie wants to merge 2 commits into
masterfrom
chore/catalog-connect-stack

Conversation

@drernie
Copy link
Copy Markdown
Member

@drernie drernie commented Apr 17, 2026

Summary

Adds catalog/internals/scripts/connect-stack.js + npm run connect-stack that configures a local dev server against a live stack and provides two bookmarklets to shuttle auth state from the live site to localhost without manual DevTools.

What it does

  1. Prompts for a live catalog URL — default is inferred from the current `registryUrl` in catalog/static-dev/config.js (drops `-registry.` to get the catalog domain, e.g. `unstable-registry.dev.quilttest.com` → `unstable.dev.quilttest.com`). Pass as CLI arg to skip the prompt.
  2. Fetches `/config.js` and writes it to catalog/static-dev/config.js so local dev points at the same stack.
  3. Writes `catalog/static-dev/bookmarks.html` with two bookmarklets:
    • Copy Quilt Auth — reads `USER` + `TOKENS` from `localStorage` on the live catalog and copies them to clipboard as JSON.
    • Paste Quilt Auth — reads that JSON from clipboard, writes into `localStorage` on localhost:3000, reloads.
  4. Detects if port 3000 is already in use (TCP connect to 127.0.0.1:3000): skips `npm start`, prints instructions.
  5. Otherwise spawns `npm start`, pipes webpack output to `/tmp/connect-stack.log`, and waits for `compiled successfully` before printing login instructions.

Why

The existing catalog/README.md dev-setup step ("copy `config.js.example`, edit it, `npm start`") assumes you already have a backend to hand-author the config for. And the comment `Auto-generated by connect-stack.js` at the top of catalog/static-dev/config.js referenced a tool that wasn't in the repo. This PR fills that gap with something discoverable (`npm run connect-stack`) and self-documenting.

The auth bookmarklet approach replaces ad-hoc workflows like "manually copy localStorage keys via DevTools" — which works but is fragile and has asked contributors to share JWTs via chat in the past.

Test plan

  • Fresh clone: `cd catalog && npm ci && npm run connect-stack`, accept the default URL, wait for "Dev server is live", open http://localhost:3000/bookmarks.html, drag both links to bookmarks bar, log into the live site, click Copy, click Paste on localhost — should be logged in.
  • Run `npm run connect-stack` again while the dev server is already running — should print instructions and exit without spawning a second `npm start`.
  • Pass a bogus URL — should error cleanly.
  • Pass a URL whose `/config.js` doesn't return QUILT_CATALOG_CONFIG — should error cleanly.

Notes

  • Binds to node 20 (prepends `/opt/homebrew/opt/node@20/bin` to PATH) when spawning `npm start`, to match the repo's `engines.node`. Non-macOS/Linux users without node@20 in that path will need to adjust. Follow-up: make this smarter (respect `nvm`/`fnm` if available).
  • `static-dev/*` is gitignored, so the generated `config.js` and `bookmarks.html` don't pollute git.
  • Does not touch any runtime code — purely a dev-tooling addition.

Related PRs

This PR was extracted from the logo-upload feature branch so the dev-tooling change lands independently of the feature:

🤖 Generated with Claude Code

Greptile Summary

This PR adds catalog/internals/scripts/connect-stack.js — a Node.js dev-helper that fetches a live stack's config.js, writes it to static-dev/config.js, generates a bookmarks.html with auth-shuttle bookmarklets, and spawns npm start if port 3000 is free. A corresponding npm run connect-stack script is wired up in package.json. No runtime product code is touched.

Confidence Score: 5/5

Safe to merge — purely additive dev tooling with no impact on production code.

Both findings are P2 style/best-practice suggestions (no fetch timeout, macOS-only PATH prepend). The static-dev/ directory is confirmed to exist in the repo via its committed .gitignore, so the writeFileSync calls are safe on a fresh clone. No runtime product code is modified.

catalog/internals/scripts/connect-stack.js — minor hardening opportunities (fetch timeout, cross-platform PATH guard), but nothing blocking.

Important Files Changed

Filename Overview
catalog/internals/scripts/connect-stack.js New dev-helper script: fetches remote config, writes bookmarks.html, and spawns npm start; two minor P2 issues (no fetch timeout, macOS-only PATH prepend)
catalog/package.json Adds connect-stack npm script entry; one-line change, no issues

Sequence Diagram

sequenceDiagram
    participant Dev as Developer
    participant Script as connect-stack.js
    participant LiveSite as Live Catalog (HTTPS)
    participant FS as local filesystem
    participant DevServer as npm start (localhost:3000)
    participant Browser as Developer's Browser

    Dev->>Script: npm run connect-stack [url]
    Script->>Script: inferDefaultUrl() from static-dev/config.js
    Script->>Dev: prompt for live catalog URL (if not provided)
    Dev->>Script: URL confirmed
    Script->>LiveSite: GET /config.js
    LiveSite-->>Script: config.js body
    Script->>FS: write static-dev/config.js
    Script->>FS: write static-dev/bookmarks.html (copy + paste bookmarklets)
    Script->>Script: portInUse(3000)?
    alt Port 3000 free
        Script->>DevServer: spawn npm start
        DevServer-->>Script: stdout/stderr lines
        Script->>Script: watch for "compiled successfully"
        Script->>Dev: print login instructions
    else Port 3000 in use
        Script->>Dev: print instructions, skip npm start
    end
    Dev->>Browser: open localhost:3000/bookmarks.html
    Dev->>Browser: drag bookmarklets to bar
    Dev->>LiveSite: log in, click "Copy Quilt Auth"
    Browser->>Browser: copy USER+TOKENS JSON to clipboard
    Dev->>DevServer: visit localhost:3000, click "Paste Quilt Auth"
    Browser->>DevServer: write USER+TOKENS to localStorage, reload
Loading

Reviews (1): Last reviewed commit: "chore(catalog): add connect-stack dev he..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Adds `npm run connect-stack` which:
  1. Prompts for (or accepts as arg) a live catalog URL,
     defaulting to the current registryUrl with "-registry"
     stripped.
  2. Fetches <url>/config.js into static-dev/config.js so the
     local dev server points at the same stack.
  3. Writes static-dev/bookmarks.html with two bookmarklets
     (Copy / Paste Quilt Auth) that shuttle localStorage USER
     and TOKENS between the live catalog and localhost:3000 —
     no manual DevTools, no sharing tokens.
  4. If port 3000 is already in use, prints instructions and
     exits; otherwise launches `npm start`, tails webpack
     output to /tmp/connect-stack.log, and waits for
     "compiled successfully" before printing login steps.

Replaces the undocumented external "connect-stack.js" tool
referenced by the generated comment in static-dev/config.js.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 45.62%. Comparing base (ad91b95) to head (ca178e7).

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #4833   +/-   ##
=======================================
  Coverage   45.62%   45.62%           
=======================================
  Files         831      831           
  Lines       33597    33597           
  Branches     5727     5727           
=======================================
  Hits        15328    15328           
  Misses      16264    16264           
  Partials     2005     2005           
Flag Coverage Δ
api-python 93.14% <ø> (ø)
catalog 19.52% <ø> (ø)
lambda 96.63% <ø> (ø)
py-shared 98.18% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.


async function fetchConfig(url) {
const configUrl = `${url}/config.js`
console.log(`Fetching ${configUrl}`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 No fetch timeout

fetch() has no timeout, so if the target server is slow or hangs the script will wait indefinitely. Adding an AbortController with a reasonable timeout (e.g. 15 s) gives the user a clear error instead of a stalled prompt.

Suggested change
console.log(`Fetching ${configUrl}`)
const controller = new AbortController()
const timeout = setTimeout(() => controller.abort(), 15000)
const res = await fetch(configUrl, { signal: controller.signal }).finally(() => clearTimeout(timeout))

const BOOKMARKS_PATH = path.join(CATALOG_DIR, 'static-dev/bookmarks.html')
const LOG_PATH = path.join(os.tmpdir(), 'connect-stack.log')
const NODE20_BIN = '/opt/homebrew/opt/node@20/bin'
const PORT = 3000
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hardcoded macOS Homebrew path

NODE20_BIN is prepended to PATH unconditionally. On Linux or macOS without Homebrew the path simply doesn't exist and is silently ignored, so the script falls back to whatever node/npm is already on PATH — which may be the wrong version (or not found at all). Acknowledged as a known limitation in the PR notes, but worth a runtime guard so contributors on other platforms see a clear message rather than a cryptic npm start failure.

A lightweight improvement would be to only prepend the path when it exists:

Suggested change
const PORT = 3000
const NODE20_BIN = '/opt/homebrew/opt/node@20/bin'
// Inside startDevServer, replace the env line:
const extraBin = fs.existsSync(NODE20_BIN) ? `${NODE20_BIN}:` : ''
const env = { ...process.env, PATH: `${extraBin}${process.env.PATH}` }

Adds preinstall script that exits 1 with a clear message when
`npm install`/`npm ci` runs under a non-20 node major — prevents
silently regenerating the lock file under npm 11 and breaking CI.
Complements the existing `engines.node` field (which npm only warns
about, never enforces).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@fiskus fiskus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see two features here:

  1. copy config.js from stack/config.js to static-dev/config.js:
  • I think this operation is too simple to automate: wget https://stack/config.js ./static-dev/config.js. I suggest to change README.md instead of making a script.
  • But if we automate, can we clear SENTRY_SDN and make a backup of the current config at ./static-dev/config.js.bak
  • If we automate, maybe, it is clearer to fetch JSON - stack/config.json, validate and adjust the properties, and then convert to config.js
  1. auto-login:
  • I'm not sure if I fully understand this. Does the script steal credentials from original website and paste them to localhost?
  • If so, that sounds useful in general, but the implementation is too hacky and too specific for my taste

And one non-feature: start the dev server. I'd suggest keeping this simple, and making it only to connect stack.

const dflt = inferDefaultUrl()
const q = dflt
? `Live catalog URL [${dflt}]: `
: 'Live catalog URL (e.g. https://nightly.quilttest.com): '
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is opensourced, so, URL should be public

Suggested change
: 'Live catalog URL (e.g. https://nightly.quilttest.com): '
: 'Live catalog URL (e.g. https://open.quiltdata.com): '

}

function inferDefaultUrl() {
// Derive the catalog URL from registryUrl in the existing config.js, dropping "-registry".
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, this will rewrite the config with the same or updated JSON? I'd remove the default, so the action would be conscious.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants