create-grist-widget 0.2.19 → 0.2.21

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.21",
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
 
@@ -141,6 +151,12 @@ export function selfReloadSnippet(sha) {
141
151
  }
142
152
  } catch (e) { /* transient offline / 404 during publish: keep polling */ }
143
153
  }
154
+ check(); // don't wait a full POLL_MS for the first check -- a plain
155
+ // (non-cache-busted) request for this very page can hit a stale
156
+ // CDN-cached index.html referencing JS assets a newer deploy
157
+ // already deleted, producing a blank/black screen until this
158
+ // fires; found live, waiting the full interval left that
159
+ // black screen up for ~5s on a fresh visit.
144
160
  setInterval(check, POLL_MS);
145
161
  })();
146
162
  </script>`
@@ -314,6 +330,34 @@ export function finalize({ siteDir, updateVersions, push, commitMessage, remoteB
314
330
  throw lastErr
315
331
  }
316
332
 
333
+ /**
334
+ * Establish the schema every widget repo should start from -- `main` and
335
+ * `dev` identical, `gh-pages` managed by CI only -- by pruning every other
336
+ * branch and force-resetting `dev` to `main`'s current tip (the commit
337
+ * already checked out at `repoDir`). Runs once, gated by the caller on
338
+ * `plan()`'s `firstRelease` flag, so it applies equally to a CLI scaffold
339
+ * (already satisfies this trivially -- nothing to prune, dev already equals
340
+ * main) and a repo copied via GitHub's "Use this template" (may have a
341
+ * stray inherited branch, or `dev` tracking the source template's own
342
+ * unrelated preview history). Operates on `repoDir`, the repo's own
343
+ * checkout -- a different directory than the gh-pages `siteDir` every other
344
+ * function here works with.
345
+ */
346
+ export function resetBranchesIfFirstRelease(repoDir, keep = ["main", "dev", "gh-pages"]) {
347
+ const raw = git(["ls-remote", "--heads", "origin"], repoDir)
348
+ const branches = raw
349
+ ? raw.split("\n").map((line) => line.split("refs/heads/")[1]).filter(Boolean)
350
+ : []
351
+ const deletedBranches = []
352
+ for (const b of branches) {
353
+ if (keep.includes(b)) continue
354
+ git(["push", "origin", "--delete", b], repoDir)
355
+ deletedBranches.push(b)
356
+ }
357
+ git(["push", "--force", "origin", "HEAD:refs/heads/dev"], repoDir)
358
+ return { deletedBranches, devReset: true }
359
+ }
360
+
317
361
  // ----------------------------------------------------------------------------
318
362
  // CLI
319
363
  // ----------------------------------------------------------------------------
@@ -371,6 +415,11 @@ function main() {
371
415
  console.log("finalize:", JSON.stringify(res))
372
416
  break
373
417
  }
418
+ case "reset-branches": {
419
+ const res = resetBranchesIfFirstRelease(args["repo-dir"] || ".")
420
+ console.log("reset-branches:", JSON.stringify(res))
421
+ break
422
+ }
374
423
  default:
375
424
  console.error(`unknown subcommand: ${cmd || "(none)"}`)
376
425
  process.exit(2)