create-grist-widget 0.2.23 → 0.2.25
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 +1 -1
- package/template/.github/workflows/deploy.yml +23 -36
- package/template/README.md +25 -32
- package/template/scripts/deploy.mjs +65 -63
package/package.json
CHANGED
|
@@ -2,22 +2,18 @@ name: Deploy widget to GitHub Pages
|
|
|
2
2
|
|
|
3
3
|
# Two channels, one workflow:
|
|
4
4
|
# - push to main / manual dispatch -> release channel: immutable
|
|
5
|
-
# /<repo>/v<version>/ + mutable /<repo>/latest
|
|
5
|
+
# /<repo>/v<version>/ + mutable /<repo>/latest/, plus the same build at
|
|
6
|
+
# the site root (the showcase hub landing page).
|
|
6
7
|
# Rebuilt only when package.json's version has no v<version> dir yet, so
|
|
7
8
|
# re-pushing main without a version bump is a no-op deploy.
|
|
8
|
-
# A
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
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.
|
|
9
|
+
# A repo's first genuine release always resolves to v0.0.1 regardless of
|
|
10
|
+
# package.json's version, so "Use this template" (whose initial push to
|
|
11
|
+
# main by GitHub's web-flow bot fires this workflow) publishes a clean
|
|
12
|
+
# v0.0.1 + /latest/ + root right away, discarding any inherited canary
|
|
13
|
+
# version. On that same first release, the "Reset repo to a clean
|
|
14
|
+
# first-release state" step below rewrites package.json's version back to
|
|
15
|
+
# 0.0.1 in the repo itself, so the *next* release bumps to 0.0.2 rather
|
|
16
|
+
# than the inherited number.
|
|
21
17
|
# - push to dev -> dev channel: mutable /<repo>/dev/
|
|
22
18
|
# with version.json + a self-reload snippet, for live review inside Grist.
|
|
23
19
|
#
|
|
@@ -104,8 +100,7 @@ jobs:
|
|
|
104
100
|
--site site --repo "$REPO_NAME" \
|
|
105
101
|
--event "${{ github.event_name }}" \
|
|
106
102
|
--ref "${{ github.ref }}" \
|
|
107
|
-
${{ github.event.inputs.force == 'true' && '--force' || '' }}
|
|
108
|
-
${{ github.event.created == true && github.event.head_commit.committer.email == 'noreply@github.com' && '--created' || '' }})
|
|
103
|
+
${{ github.event.inputs.force == 'true' && '--force' || '' }})
|
|
109
104
|
echo "Plan: $PLAN"
|
|
110
105
|
node -e '
|
|
111
106
|
const p = JSON.parse(process.argv[1]);
|
|
@@ -113,7 +108,6 @@ jobs:
|
|
|
113
108
|
const out = process.env.GITHUB_OUTPUT;
|
|
114
109
|
fs.appendFileSync(out, `context=${p.context}\n`);
|
|
115
110
|
fs.appendFileSync(out, `skip=${p.skip ? "true" : "false"}\n`);
|
|
116
|
-
fs.appendFileSync(out, `reason=${p.reason || ""}\n`);
|
|
117
111
|
fs.appendFileSync(out, `version=${p.version || ""}\n`);
|
|
118
112
|
fs.appendFileSync(out, `base=${p.base}\n`);
|
|
119
113
|
fs.appendFileSync(out, `firstRelease=${p.firstRelease ? "true" : "false"}\n`);
|
|
@@ -138,19 +132,9 @@ jobs:
|
|
|
138
132
|
--sha "$SHA" --ref "${{ github.ref }}"
|
|
139
133
|
|
|
140
134
|
- name: Skip (already published)
|
|
141
|
-
if: steps.plan.outputs.skip == 'true'
|
|
135
|
+
if: steps.plan.outputs.skip == 'true'
|
|
142
136
|
run: echo "== skip — v${{ steps.plan.outputs.version }} already published"
|
|
143
137
|
|
|
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
|
-
|
|
154
138
|
- name: Finalize (versions.json + commit + push)
|
|
155
139
|
run: |
|
|
156
140
|
UPDATE_VERSIONS=""
|
|
@@ -161,15 +145,18 @@ jobs:
|
|
|
161
145
|
--site site --repo "$REPO_NAME" $UPDATE_VERSIONS --push \
|
|
162
146
|
--commit-message "deploy: ${{ steps.plan.outputs.context }} @ ${{ github.sha }}"
|
|
163
147
|
|
|
164
|
-
# On this repo's first genuine release only:
|
|
165
|
-
#
|
|
166
|
-
#
|
|
167
|
-
#
|
|
168
|
-
#
|
|
169
|
-
#
|
|
170
|
-
#
|
|
148
|
+
# On this repo's first genuine release only: reset package.json's version
|
|
149
|
+
# to 0.0.1 (so the next release bumps to 0.0.2 rather than any inherited
|
|
150
|
+
# canary version), prune every branch except main/dev/gh-pages, and
|
|
151
|
+
# reset dev to main's current tip. Establishes the state every widget
|
|
152
|
+
# should start from regardless of how the repo was created -- a CLI
|
|
153
|
+
# scaffold already satisfies it trivially; a repo copied via GitHub's
|
|
154
|
+
# "Use this template" often doesn't (an inherited version, a stray
|
|
155
|
+
# branch, or dev tracking the source template's own preview history).
|
|
171
156
|
# Placed after Finalize so it never runs if the release itself failed.
|
|
172
|
-
-
|
|
157
|
+
# The version-reset commit re-triggers this workflow on main, but that
|
|
158
|
+
# run sees v0.0.1 already published (firstRelease=false) and no-ops.
|
|
159
|
+
- name: Reset repo to a clean first-release state
|
|
173
160
|
if: steps.plan.outputs.context == 'release' && steps.plan.outputs.firstRelease == 'true'
|
|
174
161
|
run: node scripts/deploy.mjs reset-branches
|
|
175
162
|
|
package/template/README.md
CHANGED
|
@@ -90,39 +90,32 @@ automatically.
|
|
|
90
90
|
> grist-widget`?** That path copies this repo's default branch verbatim —
|
|
91
91
|
> it doesn't run any of the CLI's own setup (renaming `package.json`,
|
|
92
92
|
> titles, etc.), and whatever version happens to be checked into the
|
|
93
|
-
> source repo's `package.json` (which can be well past `0.0.1`
|
|
94
|
-
>
|
|
93
|
+
> source repo's `package.json` (which can be well past `0.0.1`) comes along
|
|
94
|
+
> with it. **The moment the repo is created, GitHub's own initial push to
|
|
95
|
+
> `main` fires this workflow and publishes a clean `v0.0.1`** — your first
|
|
95
96
|
> release always resolves to `v0.0.1` regardless of what `package.json`
|
|
96
|
-
> says,
|
|
97
|
-
>
|
|
98
|
-
> that first release
|
|
99
|
-
>
|
|
100
|
-
>
|
|
101
|
-
>
|
|
102
|
-
>
|
|
103
|
-
>
|
|
104
|
-
>
|
|
105
|
-
>
|
|
106
|
-
>
|
|
107
|
-
>
|
|
108
|
-
>
|
|
109
|
-
>
|
|
110
|
-
>
|
|
111
|
-
>
|
|
112
|
-
>
|
|
113
|
-
>
|
|
114
|
-
>
|
|
115
|
-
>
|
|
116
|
-
>
|
|
117
|
-
>
|
|
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.
|
|
97
|
+
> says, so `/`, `/latest/`, and `/v0.0.1/` all go live right away (once
|
|
98
|
+
> you've done the one-time Pages setup below). You don't reset the version
|
|
99
|
+
> by hand: on that first release the pipeline also **rewrites
|
|
100
|
+
> `package.json`'s version back to `0.0.1` in the repo itself** (a commit it
|
|
101
|
+
> pushes to `main`/`dev`), so your *next* release naturally bumps to
|
|
102
|
+
> `0.0.2` instead of jumping to whatever inherited number you started with.
|
|
103
|
+
> If you also checked **"Include all branches"** (to get `gh-pages`/Pages
|
|
104
|
+
> already set up, no manual Settings step), that first release also clears
|
|
105
|
+
> out every leftover bit of inherited content it finds — stray
|
|
106
|
+
> `v<version>/` dirs, and a root/`latest/` still pointing at the *source*
|
|
107
|
+
> repo's own path — before writing its own. A `v<version>/` only counts as
|
|
108
|
+
> a genuine prior release once it carries a `showcase-meta.json` naming
|
|
109
|
+
> *this* repo, so on a repo with no genuine releases yet, everything else is
|
|
110
|
+
> provably inherited noise and gets safely replaced. On that same first
|
|
111
|
+
> release, every branch except `main`, `dev`, and `gh-pages` is pruned and
|
|
112
|
+
> `dev` is force-reset to match `main`'s tip — a fresh widget always starts
|
|
113
|
+
> from `main == dev`, whether it came from the CLI (already true) or a
|
|
114
|
+
> template copy (may have inherited a stray branch, or a `dev` full of the
|
|
115
|
+
> source template's own unrelated preview history). All of this only ever
|
|
116
|
+
> applies before your *first* genuine release — once one exists, it's
|
|
117
|
+
> permanently a no-op, so it's still safest to never manually re-seed
|
|
118
|
+
> `gh-pages` again after that point.
|
|
126
119
|
|
|
127
120
|
**One-time setup** (the workflow can't do this part for you):
|
|
128
121
|
|
|
@@ -19,27 +19,18 @@
|
|
|
19
19
|
//
|
|
20
20
|
// Subcommands:
|
|
21
21
|
// plan --site <dir> --repo <name> --event <push|workflow_dispatch>
|
|
22
|
-
// --ref <ref> [--force]
|
|
22
|
+
// --ref <ref> [--force]
|
|
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
|
-
//
|
|
27
|
-
//
|
|
28
|
-
//
|
|
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
|
+
// A repo's first genuine release always resolves to v0.0.1 regardless of
|
|
27
|
+
// package.json's version -- so "Use this template" (whose initial push
|
|
28
|
+
// to main fires this workflow) publishes a clean v0.0.1 straight away.
|
|
32
29
|
//
|
|
33
30
|
// place --site <dir> --repo <name> --channel <release|dev>
|
|
34
31
|
// [--version <v>] --dist <dir> --sha <sha> [--ref <ref>]
|
|
35
32
|
// Copies a freshly built dist into the gh-pages tree.
|
|
36
33
|
//
|
|
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
|
-
//
|
|
43
34
|
// remove --site <dir>
|
|
44
35
|
// Removes dev/ from the gh-pages tree (retire the dev URL when the `dev`
|
|
45
36
|
// branch is deleted). Never touches a release or latest/.
|
|
@@ -50,7 +41,8 @@
|
|
|
50
41
|
//
|
|
51
42
|
// reset-branches [--repo-dir <dir>]
|
|
52
43
|
// On a repo's first genuine release only (caller gates on plan()'s
|
|
53
|
-
// firstRelease):
|
|
44
|
+
// firstRelease): resets package.json's version to 0.0.1, prunes every
|
|
45
|
+
// branch except main/dev/gh-pages, and
|
|
54
46
|
// force-resets dev to main's current tip.
|
|
55
47
|
|
|
56
48
|
import {
|
|
@@ -113,7 +105,7 @@ function hasGenuineRelease(siteDir, repo) {
|
|
|
113
105
|
* genuinely published by this repo before (unless force), so re-pushing main
|
|
114
106
|
* without a version bump is a no-op.
|
|
115
107
|
*/
|
|
116
|
-
export function plan(siteDir, repo, event, ref, { force = false
|
|
108
|
+
export function plan(siteDir, repo, event, ref, { force = false } = {}) {
|
|
117
109
|
const isRelease = event === "workflow_dispatch" || ref === "refs/heads/main" || ref === "main"
|
|
118
110
|
if (!isRelease) {
|
|
119
111
|
return { context: "dev", base: basePathFor(repo, "dev") }
|
|
@@ -123,35 +115,20 @@ export function plan(siteDir, repo, event, ref, { force = false, created = false
|
|
|
123
115
|
// whatever package.json's version field says -- found live: a repo copied
|
|
124
116
|
// via GitHub's "Use this template" inherited "0.2.18" from the source
|
|
125
117
|
// template repo's own main branch (itself a canary-stamped scaffold that
|
|
126
|
-
// had been promoted there), instead of starting fresh.
|
|
127
|
-
//
|
|
128
|
-
//
|
|
118
|
+
// had been promoted there), instead of starting fresh. This is what makes
|
|
119
|
+
// "Use this template" publish a clean v0.0.1 the moment the repo is
|
|
120
|
+
// created (GitHub's own initial push to main fires this workflow): the
|
|
121
|
+
// copy's inherited canary version is ignored for that first release. To
|
|
122
|
+
// keep the *next* release clean too, the workflow's first-release reset
|
|
123
|
+
// step rewrites package.json's version back to 0.0.1 in the repo itself
|
|
124
|
+
// (see `resetBranchesIfFirstRelease`), so the second release naturally
|
|
125
|
+
// bumps from 0.0.1 rather than the inherited canary number. Once a repo
|
|
126
|
+
// has a genuine release, this override no longer applies -- its own
|
|
127
|
+
// version bumps are its own responsibility from then on.
|
|
129
128
|
const firstRelease = !hasGenuineRelease(siteDir, repo)
|
|
130
129
|
const version = firstRelease ? "0.0.1" : pkg.version
|
|
131
130
|
if (!version) throw new Error("package.json has no version")
|
|
132
131
|
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
|
-
}
|
|
155
132
|
const exists = isGenuineRelease(versionDir, repo)
|
|
156
133
|
return {
|
|
157
134
|
context: "release",
|
|
@@ -254,11 +231,11 @@ function replaceDir(dest, srcDist) {
|
|
|
254
231
|
* template" -> "Include all branches"): stray `v<version>/` directories,
|
|
255
232
|
* but also a root `index.html`/`latest/` still referencing the *source*
|
|
256
233
|
* repo's own base path (found live: a fresh copy's root and `latest/`
|
|
257
|
-
* pointed at `/grist-widget-template/v0.2.22/assets/...`,
|
|
258
|
-
* repo's own Pages site
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* genuine releases can't have any of its own history to lose. Once a
|
|
234
|
+
* pointed at `/grist-widget-template/v0.2.22/assets/...`, which 404s on
|
|
235
|
+
* this repo's own Pages site). Called by `placeTarget` right before it
|
|
236
|
+
* writes this repo's own first release, so the inherited noise is replaced
|
|
237
|
+
* rather than layered under. Safe to clear unconditionally: a repo with
|
|
238
|
+
* zero genuine releases can't have any of its own history to lose. Once a
|
|
262
239
|
* genuine release exists for this repo, buildVersionsManifest is non-empty
|
|
263
240
|
* and this is permanently a no-op.
|
|
264
241
|
*/
|
|
@@ -372,19 +349,50 @@ export function finalize({ siteDir, updateVersions, push, commitMessage, remoteB
|
|
|
372
349
|
}
|
|
373
350
|
|
|
374
351
|
/**
|
|
375
|
-
* Establish the
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
352
|
+
* Establish the state every widget repo should start from, on its first
|
|
353
|
+
* genuine release only (gated by the caller on `plan()`'s `firstRelease`):
|
|
354
|
+
*
|
|
355
|
+
* 1. Reset package.json's version to 0.0.1. The first release already
|
|
356
|
+
* published v0.0.1 (forced by plan() regardless of package.json), but a
|
|
357
|
+
* repo copied via GitHub's "Use this template" still carries the source
|
|
358
|
+
* template's inherited canary version in the file (found live: a copy
|
|
359
|
+
* shipped 0.2.23) -- so its *next* release would jump straight to
|
|
360
|
+
* v0.2.24 instead of a clean v0.0.2. Rewriting the file to 0.0.1 here,
|
|
361
|
+
* committed to main, makes the next bump land on 0.0.2. A CLI scaffold
|
|
362
|
+
* already ships 0.0.1, so this is a no-op there.
|
|
363
|
+
* 2. main == dev, gh-pages CI-managed only: prune every other branch and
|
|
364
|
+
* force-reset `dev` to `main`'s current tip (which now carries the
|
|
365
|
+
* version-reset commit). A CLI scaffold satisfies this trivially; a
|
|
366
|
+
* "Use this template" copy may have a stray inherited branch, or a
|
|
367
|
+
* `dev` tracking the source template's own unrelated preview history.
|
|
368
|
+
*
|
|
369
|
+
* Operates on `repoDir`, the repo's own checkout -- a different directory
|
|
370
|
+
* than the gh-pages `siteDir` every other function here works with. The
|
|
371
|
+
* version-reset commit re-triggers this workflow on main, but that run sees
|
|
372
|
+
* a genuine release already published (firstRelease=false) and skips both
|
|
373
|
+
* the release and this reset, so there's no loop.
|
|
386
374
|
*/
|
|
387
375
|
export function resetBranchesIfFirstRelease(repoDir, keep = ["main", "dev", "gh-pages"]) {
|
|
376
|
+
let versionReset = false
|
|
377
|
+
const pkgPath = join(repoDir, "package.json")
|
|
378
|
+
if (existsSync(pkgPath)) {
|
|
379
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"))
|
|
380
|
+
if (pkg.version !== "0.0.1") {
|
|
381
|
+
pkg.version = "0.0.1"
|
|
382
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n")
|
|
383
|
+
git(["add", "package.json"], repoDir)
|
|
384
|
+
git(
|
|
385
|
+
[
|
|
386
|
+
"-c", "user.name=github-actions[bot]",
|
|
387
|
+
"-c", "user.email=41898282+github-actions[bot]@users.noreply.github.com",
|
|
388
|
+
"commit", "-m", "chore: reset version to 0.0.1 for a clean first release",
|
|
389
|
+
],
|
|
390
|
+
repoDir,
|
|
391
|
+
)
|
|
392
|
+
git(["push", "origin", "HEAD:refs/heads/main"], repoDir)
|
|
393
|
+
versionReset = true
|
|
394
|
+
}
|
|
395
|
+
}
|
|
388
396
|
const raw = git(["ls-remote", "--heads", "origin"], repoDir)
|
|
389
397
|
const branches = raw
|
|
390
398
|
? raw.split("\n").map((line) => line.split("refs/heads/")[1]).filter(Boolean)
|
|
@@ -396,7 +404,7 @@ export function resetBranchesIfFirstRelease(repoDir, keep = ["main", "dev", "gh-
|
|
|
396
404
|
deletedBranches.push(b)
|
|
397
405
|
}
|
|
398
406
|
git(["push", "--force", "origin", "HEAD:refs/heads/dev"], repoDir)
|
|
399
|
-
return { deletedBranches, devReset: true }
|
|
407
|
+
return { deletedBranches, devReset: true, versionReset }
|
|
400
408
|
}
|
|
401
409
|
|
|
402
410
|
// ----------------------------------------------------------------------------
|
|
@@ -425,7 +433,6 @@ function main() {
|
|
|
425
433
|
case "plan": {
|
|
426
434
|
const result = plan(args.site, args.repo, args.event, args.ref, {
|
|
427
435
|
force: !!args.force,
|
|
428
|
-
created: !!args.created,
|
|
429
436
|
})
|
|
430
437
|
process.stdout.write(JSON.stringify(result))
|
|
431
438
|
break
|
|
@@ -448,11 +455,6 @@ function main() {
|
|
|
448
455
|
console.log("remove:", JSON.stringify(res))
|
|
449
456
|
break
|
|
450
457
|
}
|
|
451
|
-
case "cleanup-foreign": {
|
|
452
|
-
const res = cleanupForeignVersionsIfFirstRelease(args.site, args.repo)
|
|
453
|
-
console.log("cleanup-foreign:", JSON.stringify(res))
|
|
454
|
-
break
|
|
455
|
-
}
|
|
456
458
|
case "finalize": {
|
|
457
459
|
const res = finalize({
|
|
458
460
|
siteDir: args.site,
|