create-grist-widget 0.2.19 → 0.2.20

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-grist-widget",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "Scaffold a new Grist custom widget from the grist-widget-sdk Vite template",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -101,6 +101,7 @@ jobs:
101
101
  fs.appendFileSync(out, `skip=${p.skip ? "true" : "false"}\n`);
102
102
  fs.appendFileSync(out, `version=${p.version || ""}\n`);
103
103
  fs.appendFileSync(out, `base=${p.base}\n`);
104
+ fs.appendFileSync(out, `firstRelease=${p.firstRelease ? "true" : "false"}\n`);
104
105
  ' "$PLAN"
105
106
 
106
107
  - name: Build
@@ -135,6 +136,18 @@ jobs:
135
136
  --site site --repo "$REPO_NAME" $UPDATE_VERSIONS --push \
136
137
  --commit-message "deploy: ${{ steps.plan.outputs.context }} @ ${{ github.sha }}"
137
138
 
139
+ # On this repo's first genuine release only: prune every branch except
140
+ # main/dev/gh-pages, and reset dev to main's current tip. Establishes
141
+ # the schema every widget should start from (main == dev, gh-pages is
142
+ # CI-managed only) regardless of how the repo was created -- a CLI
143
+ # scaffold already satisfies this trivially; a repo copied via GitHub's
144
+ # "Use this template" often doesn't (a stray inherited branch, or dev
145
+ # tracking the source template's own unrelated preview history).
146
+ # Placed after Finalize so it never runs if the release itself failed.
147
+ - name: Reset branches on first genuine release
148
+ if: steps.plan.outputs.context == 'release' && steps.plan.outputs.firstRelease == 'true'
149
+ run: node scripts/deploy.mjs reset-branches
150
+
138
151
  cleanup:
139
152
  # When the `dev` branch is deleted, remove /dev/ so the dev URL retires
140
153
  # (404) instead of serving a stale build forever.
@@ -102,9 +102,15 @@ automatically.
102
102
  > carries a `showcase-meta.json` naming *this* repo (written by this
103
103
  > pipeline's own `place` step), so on a repo with no genuine releases yet,
104
104
  > everything else is provably inherited noise and gets safely wiped on your
105
- > first push to `main`. Both of these only ever apply before your *first*
106
- > genuine release — once one exists, they're permanently a no-op, so it's
107
- > still safest to never manually re-seed `gh-pages` again after that point.
105
+ > first push to `main`. Also on that same first release, every branch
106
+ > except `main`, `dev`, and `gh-pages` gets pruned, and `dev` gets
107
+ > force-reset to match `main`'s tip — a fresh widget always starts from
108
+ > `main == dev`, regardless of whether it came from the CLI (already true)
109
+ > or a template copy (may have inherited a stray branch, or a `dev` full of
110
+ > the source template's own unrelated preview history). All three of these
111
+ > only ever apply before your *first* genuine release — once one exists,
112
+ > they're permanently a no-op, so it's still safest to never manually
113
+ > re-seed `gh-pages` again after that point.
108
114
 
109
115
  **One-time setup** (the workflow can't do this part for you):
110
116
 
@@ -35,6 +35,11 @@
35
35
  // finalize --site <dir> [--update-versions] [--push] [--commit-message <msg>]
36
36
  // Regenerates versions.json (release runs only) and commits + pushes the
37
37
  // gh-pages tree with a rebase-and-retry loop.
38
+ //
39
+ // reset-branches [--repo-dir <dir>]
40
+ // On a repo's first genuine release only (caller gates on plan()'s
41
+ // firstRelease): prunes every branch except main/dev/gh-pages and
42
+ // force-resets dev to main's current tip.
38
43
 
39
44
  import {
40
45
  existsSync,
@@ -109,7 +114,8 @@ export function plan(siteDir, repo, event, ref, { force = false } = {}) {
109
114
  // had been promoted there), instead of starting fresh. Once a repo has a
110
115
  // genuine release, this no longer applies -- its own version bumps are
111
116
  // its own responsibility from then on, same as `cleanupForeignVersionsIfFirstRelease`.
112
- const version = hasGenuineRelease(siteDir, repo) ? pkg.version : "0.0.1"
117
+ const firstRelease = !hasGenuineRelease(siteDir, repo)
118
+ const version = firstRelease ? "0.0.1" : pkg.version
113
119
  if (!version) throw new Error("package.json has no version")
114
120
  const versionDir = join(siteDir, `v${version}`)
115
121
  const exists = isGenuineRelease(versionDir, repo)
@@ -119,6 +125,10 @@ export function plan(siteDir, repo, event, ref, { force = false } = {}) {
119
125
  base: basePathFor(repo, "release", version),
120
126
  skip: exists && !force,
121
127
  reason: exists ? (force ? "force-rebuild" : "already-published") : "new-version",
128
+ // Exposed so the workflow can gate a one-time repo cleanup (prune stray
129
+ // branches, reset dev to main) on exactly this signal -- see
130
+ // `resetBranchesIfFirstRelease`.
131
+ firstRelease,
122
132
  }
123
133
  }
124
134
 
@@ -314,6 +324,34 @@ export function finalize({ siteDir, updateVersions, push, commitMessage, remoteB
314
324
  throw lastErr
315
325
  }
316
326
 
327
+ /**
328
+ * Establish the schema every widget repo should start from -- `main` and
329
+ * `dev` identical, `gh-pages` managed by CI only -- by pruning every other
330
+ * branch and force-resetting `dev` to `main`'s current tip (the commit
331
+ * already checked out at `repoDir`). Runs once, gated by the caller on
332
+ * `plan()`'s `firstRelease` flag, so it applies equally to a CLI scaffold
333
+ * (already satisfies this trivially -- nothing to prune, dev already equals
334
+ * main) and a repo copied via GitHub's "Use this template" (may have a
335
+ * stray inherited branch, or `dev` tracking the source template's own
336
+ * unrelated preview history). Operates on `repoDir`, the repo's own
337
+ * checkout -- a different directory than the gh-pages `siteDir` every other
338
+ * function here works with.
339
+ */
340
+ export function resetBranchesIfFirstRelease(repoDir, keep = ["main", "dev", "gh-pages"]) {
341
+ const raw = git(["ls-remote", "--heads", "origin"], repoDir)
342
+ const branches = raw
343
+ ? raw.split("\n").map((line) => line.split("refs/heads/")[1]).filter(Boolean)
344
+ : []
345
+ const deletedBranches = []
346
+ for (const b of branches) {
347
+ if (keep.includes(b)) continue
348
+ git(["push", "origin", "--delete", b], repoDir)
349
+ deletedBranches.push(b)
350
+ }
351
+ git(["push", "--force", "origin", "HEAD:refs/heads/dev"], repoDir)
352
+ return { deletedBranches, devReset: true }
353
+ }
354
+
317
355
  // ----------------------------------------------------------------------------
318
356
  // CLI
319
357
  // ----------------------------------------------------------------------------
@@ -371,6 +409,11 @@ function main() {
371
409
  console.log("finalize:", JSON.stringify(res))
372
410
  break
373
411
  }
412
+ case "reset-branches": {
413
+ const res = resetBranchesIfFirstRelease(args["repo-dir"] || ".")
414
+ console.log("reset-branches:", JSON.stringify(res))
415
+ break
416
+ }
374
417
  default:
375
418
  console.error(`unknown subcommand: ${cmd || "(none)"}`)
376
419
  process.exit(2)