@blocofy/cli 0.8.0 → 0.10.0
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/README.md +359 -38
- package/bin/blocofy.mjs +1260 -234
- package/lib/args.mjs +2 -1
- package/lib/content-sync.mjs +375 -48
- package/lib/credentials.mjs +216 -60
- package/lib/http.mjs +60 -0
- package/lib/local-theme.mjs +7 -2
- package/lib/media-uses.mjs +27 -16
- package/lib/messages.mjs +42 -2
- package/lib/output.mjs +53 -0
- package/lib/page-files.mjs +318 -0
- package/lib/page-path-codec.mjs +193 -0
- package/lib/secret-store.mjs +133 -0
- package/lib/site-migrate.mjs +132 -0
- package/lib/site-state-client.mjs +92 -0
- package/lib/site-state-fs.mjs +137 -0
- package/lib/site-state.mjs +682 -0
- package/lib/target.mjs +427 -0
- package/lib/theme-sync.mjs +113 -80
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,48 +10,362 @@ cd path/to/theme
|
|
|
10
10
|
npx @blocofy/cli theme dev # http://localhost:3030 — local theme + live data, livereload
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
`login` saves the site under a named **context** (default: the site's slug) in
|
|
14
|
+
`~/.blocofy/credentials.json`. `blocofy link <dir> --context <name>` (or a pull into an empty
|
|
15
|
+
directory) then binds a project directory to that site so every command run inside it always
|
|
16
|
+
targets the right one — see [Contexts and project binding](#contexts-and-project-binding).
|
|
17
|
+
|
|
13
18
|
## Commands
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
blocofy
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
20
|
+
### Login & contexts
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
blocofy login [--context <name>] [--url <url>] [--token <bcf_…>] [--keychain]
|
|
24
|
+
```
|
|
25
|
+
Verify a dev token against its site (`GET /api/dev/whoami`) and save it as a named context
|
|
26
|
+
(default name: the site's slug). Nothing is saved if verification fails. Get a token from the
|
|
27
|
+
admin panel → Settings → Theme CLI tokens.
|
|
28
|
+
- `--keychain` — keep the secret in the macOS keychain (or `BLOCOFY_SECRET_STORE=keychain`);
|
|
29
|
+
default is `~/.blocofy/secrets.json` (0600).
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
blocofy login --api-key [--context <name>] [--api-url <url>]
|
|
33
|
+
```
|
|
34
|
+
Verify a v1 API key (`blcf_live_…`, `GET /api/v1/ping`) and add it to a context. The key is
|
|
35
|
+
read from a **hidden prompt** — the flag takes no value, so it never lands in argv or shell
|
|
36
|
+
history. If the context already has a dev token for another site, nothing is saved
|
|
37
|
+
(`TARGET_CREDENTIAL_MISMATCH`).
|
|
38
|
+
- `--api-url <url>` — API origin (default `https://app.blocofy.com`).
|
|
39
|
+
- Non-interactive shells: set `BLOCOFY_API_KEY` + `BLOCOFY_API_URL` instead.
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
blocofy contexts [--json] # list saved contexts (never prints secrets)
|
|
43
|
+
blocofy use <name> # default context for read-only commands outside a project
|
|
44
|
+
blocofy logout --context <name> # remove a context and its secrets
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Project binding
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
blocofy link [dir] --context <name> [--adopt]
|
|
51
|
+
```
|
|
52
|
+
Bind a project directory to the context's (verified) site: writes `.blocofy/project.json`
|
|
53
|
+
(commit it), `.blocofy/local.json` (your context; git-ignored) and `.blocofy/.gitignore`.
|
|
54
|
+
Refuses to rebind a directory bound to another site unless `--adopt`.
|
|
55
|
+
|
|
56
|
+
```
|
|
57
|
+
blocofy target [dir] [--context <name>] [--json]
|
|
58
|
+
```
|
|
59
|
+
Show which site a command in `[dir]` would hit (verified), without writing anything.
|
|
60
|
+
|
|
61
|
+
### Theme
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
blocofy theme dev [dir] [--port <n>] [--no-sync] [--name <name>]
|
|
65
|
+
```
|
|
66
|
+
Start a dev server and print 3 auto-reloading views — Local, live-domain Preview, and the theme
|
|
67
|
+
Editor. Press `l` / `p` / `e` to open each, `q` to quit. Edit a file and save → every open view
|
|
68
|
+
reloads. Saves sync to a **draft** theme only (never the live site). `dir` defaults to cwd.
|
|
69
|
+
The target site is verified once at start; a long session keeps that target (restart it after
|
|
70
|
+
changing credentials, context or the project binding).
|
|
71
|
+
- `--port <n>` — local port (default 3030).
|
|
72
|
+
- `--no-sync` — local preview only (skip draft sync + remote views).
|
|
73
|
+
- `--name <name>` — name the draft when it is first created (ignored if it already exists).
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
blocofy theme pull [dir] [--draft] [--instance <handle>]
|
|
77
|
+
```
|
|
78
|
+
Download the live theme to disk. `dir` defaults to cwd.
|
|
79
|
+
- `--draft` — pull the draft theme (what `theme dev` syncs into) instead of live; creates the
|
|
80
|
+
draft if missing, so it needs a bound project.
|
|
81
|
+
- `--instance <handle>` — pull a specific theme by its handle (admin panel theme card, or
|
|
82
|
+
`blocofy status`).
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
blocofy theme push [dir] [--live] [--yes] [--instance <handle>] [--prune]
|
|
86
|
+
[--name <name>] [--dry-run | --validate] [--diff]
|
|
87
|
+
[--idempotency-key <k>]
|
|
88
|
+
```
|
|
89
|
+
By **default** writes to a draft theme (create/update; no delete) — preview & publish it from
|
|
90
|
+
the admin panel, never touching the live site. Publish it with `blocofy theme publish`.
|
|
91
|
+
- `--live` — write to the LIVE site immediately (no preview). Asks for confirmation first;
|
|
92
|
+
non-interactive shells must add `--yes`.
|
|
93
|
+
- `--draft` — explicit draft (same as the default; safe).
|
|
94
|
+
- `--yes` — confirm a `--live` push without prompting (CI/agents).
|
|
95
|
+
- `--instance <handle>` — push to a specific theme by its handle (safe targeted write — no
|
|
96
|
+
live-confirmation prompt).
|
|
97
|
+
- `--name <name>` — name the new draft (draft mode only; ignored on `--live`/`--instance`).
|
|
98
|
+
- `--dry-run` / `--validate` — validate on the server without writing (auth + snapshot + Liquid
|
|
99
|
+
check); the two flags are aliases.
|
|
100
|
+
- `--diff` — show what a push would change vs the target (read-only), then stop.
|
|
101
|
+
- `--idempotency-key <k>` — attach a key so a retried push is not double-applied.
|
|
102
|
+
- `--prune` — also remove target files that no longer exist locally (`locales/` included);
|
|
103
|
+
lists them first, and on the live theme asks to confirm (non-interactive shells add `--yes`).
|
|
104
|
+
|
|
105
|
+
```
|
|
29
106
|
blocofy theme rename <handle> <new name>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
107
|
+
```
|
|
108
|
+
Rename a theme (the name is just a label). Works on any of your themes, including the live one.
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
blocofy theme publish [--instance <handle>]
|
|
112
|
+
```
|
|
113
|
+
Publish a draft theme to the LIVE site: it replaces the live theme for every visitor. With no
|
|
114
|
+
flag, publishes the draft that `theme dev` / `theme push --draft` writes into. The server
|
|
115
|
+
refuses to publish a theme that has no pages (it would 404); preview first.
|
|
116
|
+
- `--instance <handle>` — publish a specific theme.
|
|
117
|
+
|
|
118
|
+
### Status
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
blocofy status
|
|
122
|
+
```
|
|
123
|
+
Show the live theme, page distribution per instance, drafts, and a health flag (`ok` /
|
|
124
|
+
`live_instance_empty` / `pages_split`). For a problem it names the theme holding the pages, the
|
|
125
|
+
missing pages, why, and safe preview-first next steps — never a one-line fix.
|
|
126
|
+
|
|
127
|
+
### Pages
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
blocofy pages pull [dir] [--strict]
|
|
131
|
+
```
|
|
132
|
+
Download published pages, one folder per language: `pages/<locale>/index.json` (home) and
|
|
133
|
+
`pages/<locale>/routes/<path>/index.json` (everything else). Files from the old flat layout
|
|
134
|
+
(`pages/<slug>.json`) are reported, never deleted or overwritten. If the site cannot export
|
|
135
|
+
every published page, nothing is written, every reason is printed
|
|
136
|
+
(`PAGES_EXPORT_INCOMPLETE`) and the exit code is 2 (`--strict`: warnings exit 1).
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
blocofy pages push [dir] [--dry-run] [--strict] [--force --reason <text>]
|
|
140
|
+
```
|
|
141
|
+
Write `pages/**.json` to the site. Updates **existing** pages only — never creates or deletes a
|
|
142
|
+
page; unchanged pages are skipped. Every file is checked first: an invalid file, two files
|
|
143
|
+
pointing at the same page, or a folder/locale mismatch changes **no** page. Every pulled file
|
|
144
|
+
carries `base_revision` (the page as you pulled it); the push first asks for a plan (per page:
|
|
145
|
+
action, live/draft, changed fields), prints it, then pushes exactly that plan.
|
|
146
|
+
- If a page changed on the site since you pulled it (`PAGES_REVISION_CONFLICT`) or a file has no
|
|
147
|
+
`base_revision` (`PAGES_BASE_REVISION_REQUIRED`), nothing is changed: run `blocofy pages pull`,
|
|
148
|
+
merge your edits, and push again.
|
|
149
|
+
- If the site changes between the plan and the push, nothing is changed either
|
|
150
|
+
(`PAGES_PLAN_STALE`) — run the push again.
|
|
151
|
+
- `--force --reason <text>` — overwrite anyway (reason: 1–500 characters); the forced pages are
|
|
152
|
+
listed. Against an older server that cannot check revisions, the push runs as before with a
|
|
153
|
+
warning.
|
|
154
|
+
- `--dry-run` — print the plan, write nothing.
|
|
155
|
+
- If publishing stops unexpectedly after the plan (`PAGES_APPLY_FAILED` or another publish
|
|
156
|
+
error), some pages may already be applied: the per-file result printed is authoritative, the
|
|
157
|
+
exit code is non-zero, and re-running the push is safe.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
blocofy pages check [dir] [--strict]
|
|
161
|
+
```
|
|
162
|
+
Check page files. Offline: paths, JSON, layout, duplicates, missing `base_revision`
|
|
163
|
+
(`PAGES_BASE_REVISION_MISSING` warning). Logged in: also the site's languages and a server-side
|
|
164
|
+
dry run. Exit 1 on errors (`--strict`: warnings too).
|
|
165
|
+
|
|
166
|
+
```
|
|
167
|
+
blocofy pages migrate-layout [dir] [--dry-run | --write] [--strict]
|
|
168
|
+
```
|
|
169
|
+
Move old flat-layout files (`pages/<slug>.json`) to language folders. `--dry-run` (default)
|
|
170
|
+
prints the plan; `--write` moves only proven files. Any ambiguity or conflict: nothing is moved,
|
|
171
|
+
exit 1. Files without `"locale"` use the site's default language (needs login); with `--write`
|
|
172
|
+
outside a bound project only explicit `--context`/env credentials are used.
|
|
173
|
+
|
|
174
|
+
```
|
|
38
175
|
blocofy pages media-uses <page-handle> [--json]
|
|
39
|
-
|
|
40
|
-
|
|
176
|
+
```
|
|
177
|
+
List a page's localized-media decisions on its newest **draft** (v1 API, `pages:read`). Prints
|
|
178
|
+
the draft's revision id/version needed by `media-decide`.
|
|
179
|
+
|
|
180
|
+
```
|
|
41
181
|
blocofy pages media-decide <page-handle> --decisions <file.json>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
182
|
+
[--expected-revision-id <n> --expected-version <n>] [--json]
|
|
183
|
+
```
|
|
184
|
+
Apply one or more media decisions to the page's draft atomically (v1 API, `pages:write`). The
|
|
185
|
+
file is `{ "decisions": [ { path, facet, decision, target_asset?, alt?, caption?, decorative?,
|
|
186
|
+
idempotency_key?, witness? } ] }` (max 20). Without the two `--expected-*` flags the CLI first
|
|
187
|
+
`GET`s the draft and uses its current revision id/version; items without an `idempotency_key`
|
|
188
|
+
get a random UUID. Transient failures (429/502/503/504, network) are retried against the same
|
|
189
|
+
idempotency key, so a retry replays the same batch. Exit 0 on success (or "No changes" when
|
|
190
|
+
every item was already recorded).
|
|
191
|
+
|
|
192
|
+
### Settings
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
blocofy settings pull [dir]
|
|
196
|
+
blocofy settings push [dir] (--instance <handle> | --live [--yes])
|
|
197
|
+
```
|
|
198
|
+
Download / upload `config/settings.json` (theme tokens/settings + color schemes). A push
|
|
199
|
+
**names its target** — there is no implicit live write, and the command refuses with neither
|
|
200
|
+
flag:
|
|
201
|
+
- `--instance <handle>` — write that theme's settings; a draft shows them in its preview and
|
|
202
|
+
goes live with `blocofy theme publish --instance <handle>`.
|
|
203
|
+
- `--live` — write the LIVE theme (asks to confirm; non-interactive shells add `--yes`).
|
|
204
|
+
|
|
205
|
+
After a push the CLI says where it applied (preview now / live now / after deploy).
|
|
206
|
+
|
|
207
|
+
### Site (declarative state)
|
|
208
|
+
|
|
209
|
+
The `site` commands move a whole site — pages, navigation, theme source, settings, chrome,
|
|
210
|
+
translations, media — as one declarative tree on disk (`blocofy-site.json` + `site/**` +
|
|
211
|
+
`theme/**` + `pages/**` + `media/**`), instead of the per-scope `theme`/`pages`/`settings`
|
|
212
|
+
commands above.
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
blocofy site export [dir]
|
|
216
|
+
```
|
|
217
|
+
Download the whole site as a declarative tree into `[dir]`, plus every media file's bytes at
|
|
218
|
+
`media/files/<sha256>` (downloaded and hash-verified). Refuses to overwrite a directory bound to
|
|
219
|
+
another site (same binding rule as every other pull).
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
blocofy site validate [dir]
|
|
223
|
+
```
|
|
224
|
+
Check an exported (or hand-authored) tree **offline** — zero network requests: paths, the
|
|
225
|
+
path↔content binding (a page file's locale/slug must match its folder/name, and so on),
|
|
226
|
+
duplicate identities, the size/count limits, and the tree against its own manifest digest.
|
|
227
|
+
- `--strict` — warnings also exit non-zero.
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
blocofy site migrate [dir] [--dry-run | --write]
|
|
231
|
+
```
|
|
232
|
+
Turn a directory left by the **older separate** `theme pull` / `pages pull` / `settings pull`
|
|
233
|
+
into the site-state v1 tree layout (theme directories at the root → `theme/**`,
|
|
234
|
+
`config/settings.json` → `theme/config/settings.json`). Purely local: no network request, no
|
|
235
|
+
target/identity check, `.blocofy/` project binding untouched. `pages/**` files are never moved
|
|
236
|
+
(the location is already identical); a file still at the old flat page layout is left alone with
|
|
237
|
+
a note to run `blocofy pages migrate-layout` first.
|
|
238
|
+
- `--dry-run` (default) — print the plan: every move, every file left alone, every conflict.
|
|
239
|
+
- `--write` — perform it.
|
|
240
|
+
- Any ambiguity or conflict (a target already exists with different content, an unreadable or
|
|
241
|
+
symlinked entry, a path the shared site-state rules refuse): zero moves, exit 1.
|
|
242
|
+
- Never invents the parts a directory of separate pulls never had (`blocofy-site.json`,
|
|
243
|
+
`site/locales.json`, globals, navigation, translations, `theme/chrome/**`) — it says so and
|
|
244
|
+
points at `blocofy site export` for those.
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
blocofy site plan [dir] [--target new|<handle>] [--mode same_site|restore]
|
|
248
|
+
[--accept-live-effects locales] [--json]
|
|
249
|
+
```
|
|
250
|
+
Ask the site what applying this tree **would** do — no write. Needs **both** a dev token and a
|
|
251
|
+
v1 API key (the dev token is what a theme deploy uses later). Prints the status (`planned` /
|
|
252
|
+
`awaiting_assets` / `awaiting_theme_source` / `draft_complete`), the steps, and any missing
|
|
253
|
+
assets or a differing theme source.
|
|
254
|
+
- `--target new|<handle>` — a fresh draft theme version (default), or a specific one you own
|
|
255
|
+
(from a previous plan/apply's target instance).
|
|
256
|
+
- `--mode same_site|restore` — `same_site` refuses a page that changed on this site since the
|
|
257
|
+
tree was exported; `restore` (default) does not.
|
|
258
|
+
- `--accept-live-effects locales` — required before a state that adds a language may be applied
|
|
259
|
+
(publishing a language prepares its homepage LIVE).
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
blocofy site apply [dir] [--target new|<handle>] [--mode same_site|restore]
|
|
263
|
+
[--accept-live-effects locales] [--json]
|
|
264
|
+
```
|
|
265
|
+
Build the state into a **draft** theme version — the live site is never touched. Loops: plan →
|
|
266
|
+
(upload missing media via the v1 API, or deploy the theme source via the same canonical path
|
|
267
|
+
`theme push --instance` uses) → plan → apply, until the target reports `draft_complete` or 5
|
|
268
|
+
passes are used. Safe to re-run: every step is idempotent and a re-plan picks up exactly what is
|
|
269
|
+
left, so an apply interrupted by anything (network, Ctrl-C, a crash) resumes with the same
|
|
270
|
+
command. Preview it from the admin panel, then run `blocofy site publish`.
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
blocofy site publish [dir] [--target new|<handle>] [--mode same_site|restore] [--yes]
|
|
274
|
+
```
|
|
275
|
+
Make an applied state the LIVE site: pointer swap, then navigation, then settings. Refuses (exit
|
|
276
|
+
2) if the state is not fully applied yet — run `site apply` first. Separate live gate: asks to
|
|
277
|
+
confirm (non-interactive shells must add `--yes`).
|
|
278
|
+
|
|
279
|
+
```
|
|
49
280
|
blocofy --version
|
|
50
281
|
blocofy --help
|
|
51
282
|
```
|
|
52
283
|
|
|
284
|
+
## Contexts and project binding
|
|
285
|
+
|
|
286
|
+
Every remote command verifies its site first and prints a `Target` block on stderr. The context
|
|
287
|
+
is chosen in this order:
|
|
288
|
+
|
|
289
|
+
1. `--context <name>`
|
|
290
|
+
2. `BLOCOFY_CONTEXT`
|
|
291
|
+
3. env credentials (`BLOCOFY_URL` + `BLOCOFY_TOKEN` and/or `BLOCOFY_API_URL` + `BLOCOFY_API_KEY`)
|
|
292
|
+
4. `.blocofy/local.json` (the project's own context choice)
|
|
293
|
+
5. the one saved context matching the project's site
|
|
294
|
+
6. (terminal) pick from the matches
|
|
295
|
+
|
|
296
|
+
Inside a **bound project**, `blocofy use` is ignored — the project's binding decides, not the
|
|
297
|
+
global default context. Commands that change a site (`theme push`/`publish`/`rename`, `theme
|
|
298
|
+
dev` sync, `pages push`, `settings push`, `pages media-decide`) need a bound project; a pull into
|
|
299
|
+
a new empty directory binds it automatically. A wrong project/site pairing changes nothing
|
|
300
|
+
(`TARGET_SITE_MISMATCH` — pass `--adopt` on `link` to rebind deliberately).
|
|
301
|
+
|
|
302
|
+
A binding made against an older server has no recorded `platform_origin`: it still matches the
|
|
303
|
+
same site (one warning printed; run `blocofy link --adopt` to record it). A server that reports
|
|
304
|
+
no origin cannot serve a binding that records one — that refuses with `TARGET_UNVERIFIED` (the
|
|
305
|
+
server cannot prove it is the platform the binding was made against).
|
|
306
|
+
|
|
307
|
+
A refusal in this area (`TARGET_SITE_MISMATCH`, `TARGET_UNVERIFIED`, `TARGET_CREDENTIAL_MISMATCH`,
|
|
308
|
+
`TARGET_CONTEXT_REQUIRED`, `TARGET_CONTEXT_UNKNOWN`, `TARGET_BINDING_INVALID`) always means:
|
|
309
|
+
**nothing was read or written** — the command stops before it touches the site or the local
|
|
310
|
+
project files, and exits 3 (see [Exit codes](#exit-codes)).
|
|
311
|
+
|
|
312
|
+
`blocofy site migrate` is the one exception: it never resolves a target or a context at all (no
|
|
313
|
+
network, no identity call), so it leaves any `.blocofy/` binding in the directory completely
|
|
314
|
+
untouched.
|
|
315
|
+
|
|
316
|
+
## Exit codes
|
|
317
|
+
|
|
318
|
+
| Code | Meaning |
|
|
319
|
+
| --- | --- |
|
|
320
|
+
| 0 | Ok. |
|
|
321
|
+
| 1 | Usage / network / HTTP 5xx / a local check that refuses before any request (e.g. a `site migrate` conflict, a `pages migrate-layout` ambiguity). |
|
|
322
|
+
| 2 | The server refused the request (HTTP 4xx) — its `{error}` JSON is printed. |
|
|
323
|
+
| 3 | Target/binding refusal — nothing was read or written (see [Contexts and project binding](#contexts-and-project-binding)). |
|
|
324
|
+
| 4 | `site apply` only: not finished within its bounded pass count. Every step already applied is safe — re-run the same command to resume. |
|
|
325
|
+
|
|
326
|
+
`--json`: every failure prints `{"error":{"code","message","details"}}` as the **last** stderr
|
|
327
|
+
line; the target block (`{"target":…}`) and any warning lines are printed on stderr before it.
|
|
328
|
+
|
|
329
|
+
Retries: network errors and HTTP 429/502/503/504 are retried up to 3 times (`Retry-After`
|
|
330
|
+
honoured, max 30s per wait; else 0.3s/0.9s/2s), resending the identical request (`pages push`
|
|
331
|
+
carries one `x-idempotency-key` per push). HTTP 500 is never retried. Each retry prints a notice
|
|
332
|
+
on stderr.
|
|
333
|
+
|
|
53
334
|
## Changelog
|
|
54
335
|
|
|
336
|
+
- **0.10.0** — Named contexts + verified project binding, and a declarative whole-site state.
|
|
337
|
+
- `login` now saves a **named context** (`--context <name>`, default the site's slug) instead
|
|
338
|
+
of one global credentials pair; `blocofy contexts` / `use` / `logout` manage them, and
|
|
339
|
+
`blocofy link [dir] --context <name> [--adopt]` binds a project directory to one verified
|
|
340
|
+
site (`.blocofy/project.json`, committed; `.blocofy/local.json`, git-ignored).
|
|
341
|
+
`blocofy target [dir]` shows which site a command would hit without writing anything.
|
|
342
|
+
`login --keychain` keeps the secret in the macOS keychain instead of
|
|
343
|
+
`~/.blocofy/secrets.json`. A pre-0.10 `credentials.json` is migrated on first use (backup:
|
|
344
|
+
`~/.blocofy/credentials.v1.bak.json`).
|
|
345
|
+
- New `blocofy site export/validate/migrate/plan/apply/publish`: pull a whole site as one
|
|
346
|
+
declarative tree, check it fully offline, turn an older separate-pulls directory into that
|
|
347
|
+
tree layout (`site migrate`), ask what applying it would do, build it into a draft theme
|
|
348
|
+
version, and make it live. `site apply` is resumable — every step is idempotent and picks up
|
|
349
|
+
exactly what is left.
|
|
350
|
+
- `pages push` speaks page revision CAS: every pulled file carries `base_revision`, a push
|
|
351
|
+
previews its plan before writing, and a page changed on the site since the pull (or missing
|
|
352
|
+
`base_revision`) refuses with nothing changed instead of silently overwriting — recover with
|
|
353
|
+
`pages pull` + push again, or `--force --reason <text>`.
|
|
354
|
+
- `settings push` now **requires** `--instance <handle>` or `--live` — there is no longer an
|
|
355
|
+
implicit live write.
|
|
356
|
+
- Consistent exit codes across every command (see [Exit codes](#exit-codes)) and a `--json`
|
|
357
|
+
envelope (`{"error":{code,message,details}}`) as the last stderr line on any failure.
|
|
358
|
+
|
|
359
|
+
- **0.9.0** — Locale-aware page files for multilingual sites (needs a platform with page file protocol 2;
|
|
360
|
+
against an older server page commands stop with `PAGES_SERVER_UPGRADE_REQUIRED` and write nothing).
|
|
361
|
+
`pages pull` writes `pages/<locale>/index.json` and `pages/<locale>/routes/<path>/index.json`, so the same
|
|
362
|
+
URL in two languages no longer shares one file. `pages push` preflights the whole directory and the server
|
|
363
|
+
refuses the batch before any write on an invalid file, duplicate target or folder/locale mismatch; a
|
|
364
|
+
failure after preflight prints the per-file result. New `pages check`, `pages push --dry-run` and
|
|
365
|
+
`pages migrate-layout [--dry-run | --write]`. Pull validates every server path, never follows symlinks,
|
|
366
|
+
never writes outside the target directory and stages its writes (0.8.0 could write a server-supplied
|
|
367
|
+
`../` path outside it). An incomplete export prints every reason with `PAGES_EXPORT_INCOMPLETE` and writes
|
|
368
|
+
nothing. Limits: 500 page files, 2 MiB per file, 4 MiB total.
|
|
55
369
|
- **0.8.0** — Page media decisions over the public v1 API: `pages media-uses <page-handle>` lists a
|
|
56
370
|
page's localized-media decisions on its newest draft, and `pages media-decide <page-handle>
|
|
57
371
|
--decisions <file.json>` applies a batch of decisions to that draft atomically (all or nothing;
|
|
@@ -123,11 +437,13 @@ editor — without affecting your published theme. Save a file and **every open
|
|
|
123
437
|
(the platform-rendered pages connect back to the local dev server's reload channel). Publish
|
|
124
438
|
the draft from the theme editor when you're ready.
|
|
125
439
|
|
|
126
|
-
Credentials
|
|
440
|
+
Credentials live in `~/.blocofy/credentials.json` (contexts, no secrets) + either
|
|
441
|
+
`~/.blocofy/secrets.json` (0600) or the macOS keychain — written by `blocofy login`, or the
|
|
127
442
|
`BLOCOFY_URL` + `BLOCOFY_TOKEN` environment variables (for CI/automation). The v1 API commands
|
|
128
|
-
(`pages media-uses`, `
|
|
129
|
-
(written by `blocofy login --api-key`) or `BLOCOFY_API_KEY`
|
|
130
|
-
together; with only one set the CLI fails instead of
|
|
443
|
+
(`pages media-uses`/`media-decide`, `site plan`/`export`/`publish`) use a separate
|
|
444
|
+
`blcf_live_…` key from the same file (written by `blocofy login --api-key`) or `BLOCOFY_API_KEY`
|
|
445
|
+
+ `BLOCOFY_API_URL` — both variables together; with only one set the CLI fails instead of
|
|
446
|
+
falling back to the file.
|
|
131
447
|
|
|
132
448
|
## Theme structure
|
|
133
449
|
|
|
@@ -140,12 +456,17 @@ A theme is a directory of files grouped by top-level folder:
|
|
|
140
456
|
| `block/` | Repeatable pieces used inside sections |
|
|
141
457
|
| `partial/` | Shared snippets (header/footer, …) |
|
|
142
458
|
| `asset/` | CSS and static files (`theme.css`) |
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `config/
|
|
459
|
+
| `locales/` | Legacy Liquid translation files (`<tag>.json`, `<tag>.default.json`) |
|
|
460
|
+
| `pages/<locale>/index.json`, `pages/<locale>/routes/<path>/index.json` | Page content, one folder per language (`blocofy pages pull/push/check/migrate-layout`). The old `pages/<slug>.json` layout is still read, with a warning |
|
|
461
|
+
| `config/settings.json` | Theme settings + color schemes (`blocofy settings pull`; `settings push` needs `--instance <handle>` or `--live`) |
|
|
462
|
+
| `config/settings_schema.json` | Theme settings panel schema (synced with `theme pull`/`push`) |
|
|
146
463
|
|
|
147
464
|
Liquid templates use the `.liquid` extension; files under `asset/` are served as-is.
|
|
148
465
|
|
|
466
|
+
`blocofy site export` pulls the same content plus navigation, chrome and translations as one
|
|
467
|
+
declarative tree instead (see [Site (declarative state)](#site-declarative-state)); `blocofy
|
|
468
|
+
site migrate` turns a directory built from the commands above into that tree's layout.
|
|
469
|
+
|
|
149
470
|
## Development
|
|
150
471
|
|
|
151
472
|
Zero runtime dependencies (Node built-ins only). Run the tests with:
|