fuzzyworld-tools / commit
fw deploy --as: a second copy of an app under another path
README.md | 4 ++-- lib/deploy.mjs | 10 +++++++--- package.json | 2 +- vite/index.js | 3 ++- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1fa6e37..5928e03 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Add to the app repo's `package.json`: "devDependencies": { "fuzzyworld-tools": "git+https://code.fuzzyworld.net/fuzzyworld-tools.git#semver:^1.0.0" } ``` -For vite, add the plugin; it sets `base` to `/<app>/` for builds (dev server unchanged): +For vite, add the plugin; it sets `base` to `/<app>/` for builds (dev server unchanged; `FW_APP`, set by `fw deploy --as`, overrides the app name): ```ts import fuzzyworld from 'fuzzyworld-tools/vite'; @@ -69,7 +69,7 @@ On the laptop, `bin/fw` runs `deploy` locally and forwards repo commands to Drea | Command | What it does | |---|---| -| `fw deploy [--ref B] [--here] [--dry-run]` | build and publish this repo's app (above) | +| `fw deploy [--ref B] [--as NAME] [--here] [--dry-run]` | build and publish this repo's app (above). `--as NAME` publishes it at `apps.fuzzyworld.net/NAME/` instead, a second copy of the same repo (e.g. `explodorama-next` built from `main` while `/explodorama/` is deployed from a tag with `--ref`); the build gets `FW_APP=NAME`, which the vite plugin uses for `base`, and the index title gets the suffix | | `fw repoint [DIR] [--https] [--dry-run]` | for each repo in DIR whose `origin` is on gitlab.com: rename it `gitlab`, add `origin` → `dh:git/<repo>.git` (or read-only `https://code.fuzzyworld.net/<repo>.git` with `--https`, public repos only), fetch, and retarget branch tracking. Changes only remotes; checks that uncommitted work, HEAD and stashes are unchanged | | `fw usage [--all\|--now]` | Claude Code token use per session from `~/.claude/projects` (runs locally): messages, current context size, cache-read, output (cache-read and output include subagents). `--now` prints one line for the newest session, to decide when to put it down | | `fw list` | every repo, its visibility and last commit | diff --git a/lib/deploy.mjs b/lib/deploy.mjs index 67df114..f9e455f 100644 --- a/lib/deploy.mjs +++ b/lib/deploy.mjs @@ -2,6 +2,7 @@ // // fw deploy build branch `main` of dh:git/<repo>.git (a fresh clone) // fw deploy --ref <branch> build another branch or tag +// fw deploy --as <app> publish under another path (another copy of the same repo, e.g. a `-next` build of main) // fw deploy --here build the current directory as it is (uncommitted work included) // fw deploy --dry-run build and check, but don't upload // @@ -51,14 +52,16 @@ function git(args, cwd) { } function parseArgs(argv) { - const o = { ref: 'main', here: false, dryRun: false }; + const o = { ref: 'main', here: false, dryRun: false, as: '' }; for (let i = 0; i < argv.length; i++) { const a = argv[i]; if (a === '--here') o.here = true; else if (a === '--dry-run') o.dryRun = true; + else if (a === '--as') o.as = argv[++i] || die('--as needs a value'); else if (a === '--ref') o.ref = argv[++i] || die('--ref needs a value'); else die(`unknown option ${a}`); } + if (o.as && !NAME_RE.test(o.as)) die(`invalid --as name ${JSON.stringify(o.as)}`); return o; } @@ -180,7 +183,8 @@ export function deploy(argv) { run('git', ['clone', '--quiet', '--depth', '1', '--branch', o.ref, `${HOST}:git/${repo}.git`, src]); ({ cfg } = readConfig(src)); // the committed config is what gets deployed } - const app = cfg.app; + // `--as`: the same repo under another path; the repo it is cloned from stays cfg.app's (or cfg.repo). + const app = o.as || cfg.app; const commit = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: src, encoding: 'utf8' }).stdout.trim() || 'unknown'; const { pkg } = readConfig(src); const env = { ...process.env, FW_APP: app, FW_BASE: `/${app}/` }; @@ -221,7 +225,7 @@ export function deploy(argv) { console.log(' ✓ all URLs, manifest and service worker stay inside', `/${app}/`); fs.writeFileSync(path.join(out, '.fuzzyworld.json'), JSON.stringify({ - app, repo, title: cfg.title || app, description: cfg.description || '', + app, repo, title: (cfg.title || cfg.app) + (o.as ? ` (${o.as.startsWith(cfg.app + '-') ? o.as.slice(cfg.app.length + 1) : o.as})` : ''), description: cfg.description || '', commit, ref: o.here ? '(working copy)' : o.ref, deployed: new Date().toISOString(), }, null, 2)); diff --git a/package.json b/package.json index 29fffb4..3abe84e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fuzzyworld-tools", - "version": "1.2.0", + "version": "1.3.0", "description": "Tooling for code.fuzzyworld.net and apps.fuzzyworld.net: fw command, deploy, vite plugin.", "type": "module", "scripts": { diff --git a/vite/index.js b/vite/index.js index a286937..1191368 100644 --- a/vite/index.js +++ b/vite/index.js @@ -39,7 +39,8 @@ export default function fuzzyworld() { name: 'fuzzyworld', config(config, { command }) { const root = config.root ? path.resolve(config.root) : process.cwd(); - const { app } = readAppConfig(root); + // `fw deploy --as <name>` (lib/deploy.mjs) builds the same app under another path. + const app = process.env.FW_APP || readAppConfig(root).app; const base = `/${app}/`; // `vite dev` keeps the developer's own base; builds always use the app path. if (command === 'build' && config.base && config.base !== base && config.base !== './') {