create-grist-widget 0.2.21 → 0.2.23

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.21",
3
+ "version": "0.2.23",
4
4
  "description": "Scaffold a new Grist custom widget from the grist-widget-sdk Vite template",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -5,6 +5,19 @@ name: Deploy widget to GitHub Pages
5
5
  # /<repo>/v<version>/ + mutable /<repo>/latest/.
6
6
  # Rebuilt only when package.json's version has no v<version> dir yet, so
7
7
  # re-pushing main without a version bump is a no-op deploy.
8
+ # A push that *creates* the main ref, authored by GitHub's own web-flow
9
+ # bot -- fired by "Use this template" copying this repo onto a brand-new
10
+ # ref -- is skipped entirely instead: it would otherwise publish
11
+ # whatever boilerplate version package.json happens to have checked in
12
+ # and permanently spend the first-release override before anyone's done
13
+ # real work. (Checking github.event.created alone isn't enough: a CLI
14
+ # scaffold's own first `git push -u origin main` also creates that ref
15
+ # for the first time, but is a real user's genuine first release and
16
+ # must not be skipped -- the web-flow committer check tells the two
17
+ # apart.) That skipped run still clears any foreign content the copy
18
+ # brought along (stray v<version>/ dirs, a root/latest/ still pointing
19
+ # at the source repo's own path) via `cleanup-foreign`, so root/latest/
20
+ # aren't left blank until the widget's real first release.
8
21
  # - push to dev -> dev channel: mutable /<repo>/dev/
9
22
  # with version.json + a self-reload snippet, for live review inside Grist.
10
23
  #
@@ -91,7 +104,8 @@ jobs:
91
104
  --site site --repo "$REPO_NAME" \
92
105
  --event "${{ github.event_name }}" \
93
106
  --ref "${{ github.ref }}" \
94
- ${{ github.event.inputs.force == 'true' && '--force' || '' }})
107
+ ${{ github.event.inputs.force == 'true' && '--force' || '' }} \
108
+ ${{ github.event.created == true && github.event.head_commit.committer.email == 'noreply@github.com' && '--created' || '' }})
95
109
  echo "Plan: $PLAN"
96
110
  node -e '
97
111
  const p = JSON.parse(process.argv[1]);
@@ -99,6 +113,7 @@ jobs:
99
113
  const out = process.env.GITHUB_OUTPUT;
100
114
  fs.appendFileSync(out, `context=${p.context}\n`);
101
115
  fs.appendFileSync(out, `skip=${p.skip ? "true" : "false"}\n`);
116
+ fs.appendFileSync(out, `reason=${p.reason || ""}\n`);
102
117
  fs.appendFileSync(out, `version=${p.version || ""}\n`);
103
118
  fs.appendFileSync(out, `base=${p.base}\n`);
104
119
  fs.appendFileSync(out, `firstRelease=${p.firstRelease ? "true" : "false"}\n`);
@@ -123,9 +138,19 @@ jobs:
123
138
  --sha "$SHA" --ref "${{ github.ref }}"
124
139
 
125
140
  - name: Skip (already published)
126
- if: steps.plan.outputs.skip == 'true'
141
+ if: steps.plan.outputs.skip == 'true' && steps.plan.outputs.reason != 'ref-created'
127
142
  run: echo "== skip — v${{ steps.plan.outputs.version }} already published"
128
143
 
144
+ # The ref-created push never reaches Build/Place, so it's the only
145
+ # path where cleanupForeignVersionsIfFirstRelease (normally a side
146
+ # effect of Place) needs to run standalone -- otherwise foreign
147
+ # content from "Use this template" (stray v<version>/ dirs, a
148
+ # root/latest/ pointing at the source repo's own path) sits untouched
149
+ # until the widget's real first release.
150
+ - name: Clean foreign content (ref-created push published nothing)
151
+ if: steps.plan.outputs.reason == 'ref-created'
152
+ run: node scripts/deploy.mjs cleanup-foreign --site site --repo "$REPO_NAME"
153
+
129
154
  - name: Finalize (versions.json + commit + push)
130
155
  run: |
131
156
  UPDATE_VERSIONS=""
@@ -111,6 +111,18 @@ automatically.
111
111
  > only ever apply before your *first* genuine release — once one exists,
112
112
  > they're permanently a no-op, so it's still safest to never manually
113
113
  > re-seed `gh-pages` again after that point.
114
+ >
115
+ > "Use this template" also immediately fires this workflow once on its own,
116
+ > the moment the repo is created — before you've cloned it, let alone
117
+ > changed anything. That run is recognized (GitHub's own web-flow bot
118
+ > creating the `main` ref) and skipped entirely: nothing gets published,
119
+ > and it doesn't count as your first release, so the `0.0.1` override above
120
+ > still applies in full once you actually push real changes. If you also
121
+ > checked "Include all branches", that same skipped run still clears out
122
+ > whatever foreign content came along with it — stray `v<version>/` dirs,
123
+ > and a root/`latest/` still pointing at the *source* repo's own path
124
+ > (which would otherwise render blank until your real first release) —
125
+ > without needing to publish anything of its own to do so.
114
126
 
115
127
  **One-time setup** (the workflow can't do this part for you):
116
128
 
@@ -19,15 +19,27 @@
19
19
  //
20
20
  // Subcommands:
21
21
  // plan --site <dir> --repo <name> --event <push|workflow_dispatch>
22
- // --ref <ref> [--force]
22
+ // --ref <ref> [--force] [--created]
23
23
  // Prints JSON { context: "release"|"dev", version?, base, skip }.
24
24
  // Release is skipped when v<version> already exists on gh-pages
25
25
  // (idempotence: re-pushing main without a version bump is a no-op).
26
+ // --created additionally skips unconditionally, without consuming the
27
+ // first-release version override -- see plan()'s own comment. Callers
28
+ // must only pass --created for GitHub's own server-side "Use this
29
+ // template" push, never a real user push that merely happens to create
30
+ // a ref for the first time (e.g. a CLI scaffold's first `git push -u
31
+ // origin main`) -- see deploy.yml's wiring for the distinguishing check.
26
32
  //
27
33
  // place --site <dir> --repo <name> --channel <release|dev>
28
34
  // [--version <v>] --dist <dir> --sha <sha> [--ref <ref>]
29
35
  // Copies a freshly built dist into the gh-pages tree.
30
36
  //
37
+ // cleanup-foreign --site <dir> --repo <name>
38
+ // Standalone entry point for `cleanupForeignVersionsIfFirstRelease` --
39
+ // used on a skipped ref-created push (see `plan`), which never reaches
40
+ // `place` (the only other caller of this cleanup) so foreign content
41
+ // from "Use this template" would otherwise sit untouched indefinitely.
42
+ //
31
43
  // remove --site <dir>
32
44
  // Removes dev/ from the gh-pages tree (retire the dev URL when the `dev`
33
45
  // branch is deleted). Never touches a release or latest/.
@@ -101,7 +113,7 @@ function hasGenuineRelease(siteDir, repo) {
101
113
  * genuinely published by this repo before (unless force), so re-pushing main
102
114
  * without a version bump is a no-op.
103
115
  */
104
- export function plan(siteDir, repo, event, ref, { force = false } = {}) {
116
+ export function plan(siteDir, repo, event, ref, { force = false, created = false } = {}) {
105
117
  const isRelease = event === "workflow_dispatch" || ref === "refs/heads/main" || ref === "main"
106
118
  if (!isRelease) {
107
119
  return { context: "dev", base: basePathFor(repo, "dev") }
@@ -118,6 +130,28 @@ export function plan(siteDir, repo, event, ref, { force = false } = {}) {
118
130
  const version = firstRelease ? "0.0.1" : pkg.version
119
131
  if (!version) throw new Error("package.json has no version")
120
132
  const versionDir = join(siteDir, `v${version}`)
133
+ // GitHub's "Use this template" copies the source repo's main branch onto a
134
+ // brand-new ref, which fires this same push-to-main event itself --
135
+ // before anyone has done any real work. Without this check, that event
136
+ // would consume the first-release override above on whatever unmodified
137
+ // boilerplate happens to be checked in, so the widget's *actual* first
138
+ // release (once someone merges real content) no longer counts as
139
+ // firstRelease and publishes under that boilerplate's version verbatim
140
+ // instead of a clean 0.0.1 -- found live (a monorepo widget moved into its
141
+ // own repo this way published its real first release as "v0.2.21").
142
+ // Skipping this event entirely (never placing anything, never touching
143
+ // gh-pages) keeps hasGenuineRelease() false, so the override survives
144
+ // intact for whenever a real release actually happens.
145
+ if (created && event === "push") {
146
+ return {
147
+ context: "release",
148
+ version,
149
+ base: basePathFor(repo, "release", version),
150
+ skip: true,
151
+ reason: "ref-created",
152
+ firstRelease,
153
+ }
154
+ }
121
155
  const exists = isGenuineRelease(versionDir, repo)
122
156
  return {
123
157
  context: "release",
@@ -212,21 +246,28 @@ function replaceDir(dest, srcDist) {
212
246
 
213
247
  /**
214
248
  * If this repo has never genuinely published a release before (no
215
- * v<version>/ directory whose showcase-meta.json matches this repo), any
216
- * v<version>/ directories present are provably inherited noise -- e.g. from
217
- * copying this template repo including its gh-pages branch (GitHub's "Use
218
- * this template" -> "Include all branches"). Safe to clear them all before
219
- * placing this repo's actual first release: there is no ambiguity, since a
220
- * repo with zero genuine releases can't have any of its own history to lose.
221
- * Never touches dev/, latest/, or site-root files. Once a genuine release
222
- * exists for this repo, buildVersionsManifest is non-empty and this is
223
- * permanently a no-op.
249
+ * v<version>/ directory whose showcase-meta.json matches this repo), *every*
250
+ * top-level entry except `dev/` (the mutable dev channel, legitimately this
251
+ * repo's own regardless of release history) and a small set of
252
+ * pipeline-managed files is provably inherited noise -- e.g. from copying
253
+ * this template repo including its gh-pages branch (GitHub's "Use this
254
+ * template" -> "Include all branches"): stray `v<version>/` directories,
255
+ * but also a root `index.html`/`latest/` still referencing the *source*
256
+ * repo's own base path (found live: a fresh copy's root and `latest/`
257
+ * pointed at `/grist-widget-template/v0.2.22/assets/...`, 404ing on this
258
+ * repo's own Pages site -- a blank page -- because the ref-created push
259
+ * that used to overwrite them with this repo's own build is now skipped
260
+ * entirely, see `plan()`). Safe to clear unconditionally: a repo with zero
261
+ * genuine releases can't have any of its own history to lose. Once a
262
+ * genuine release exists for this repo, buildVersionsManifest is non-empty
263
+ * and this is permanently a no-op.
224
264
  */
225
265
  export function cleanupForeignVersionsIfFirstRelease(siteDir, repo) {
226
266
  if (hasGenuineRelease(siteDir, repo)) return { cleaned: [] }
267
+ const PROTECTED = new Set([".nojekyll", "dev", "versions.json", ".git"])
227
268
  const cleaned = []
228
269
  for (const name of existsSync(siteDir) ? readdirSync(siteDir) : []) {
229
- if (!/^v\d+\.\d+\.\d+/.test(name)) continue
270
+ if (PROTECTED.has(name)) continue
230
271
  rmSync(join(siteDir, name), { recursive: true, force: true })
231
272
  cleaned.push(name)
232
273
  }
@@ -382,7 +423,10 @@ function main() {
382
423
 
383
424
  switch (cmd) {
384
425
  case "plan": {
385
- const result = plan(args.site, args.repo, args.event, args.ref, { force: !!args.force })
426
+ const result = plan(args.site, args.repo, args.event, args.ref, {
427
+ force: !!args.force,
428
+ created: !!args.created,
429
+ })
386
430
  process.stdout.write(JSON.stringify(result))
387
431
  break
388
432
  }
@@ -404,6 +448,11 @@ function main() {
404
448
  console.log("remove:", JSON.stringify(res))
405
449
  break
406
450
  }
451
+ case "cleanup-foreign": {
452
+ const res = cleanupForeignVersionsIfFirstRelease(args.site, args.repo)
453
+ console.log("cleanup-foreign:", JSON.stringify(res))
454
+ break
455
+ }
407
456
  case "finalize": {
408
457
  const res = finalize({
409
458
  siteDir: args.site,