plum-cms 0.2.0 → 0.2.2
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +196 -0
- data/README.md +79 -0
- data/app/assets/builds/tailwind.css +1 -1
- data/app/controllers/plum/cp/entries_controller.rb +88 -5
- data/app/controllers/plum/cp/static_cache_controller.rb +12 -0
- data/app/controllers/plum/form_submissions_controller.rb +13 -0
- data/app/controllers/plum/pages_controller.rb +8 -0
- data/app/controllers/plum/theme_assets_controller.rb +2 -0
- data/app/javascript/controllers/plum/write_controller.js +174 -0
- data/app/models/plum/asset.rb +1 -0
- data/app/models/plum/content_type.rb +1 -0
- data/app/models/plum/entry.rb +57 -0
- data/app/models/plum/entry_term.rb +1 -0
- data/app/models/plum/form_definition.rb +1 -0
- data/app/models/plum/global.rb +1 -0
- data/app/models/plum/nav_item.rb +1 -0
- data/app/models/plum/nav_menu.rb +1 -0
- data/app/models/plum/site.rb +2 -0
- data/app/models/plum/site_setting.rb +1 -0
- data/app/models/plum/static_cache_invalidation.rb +30 -0
- data/app/models/plum/taxonomy.rb +1 -0
- data/app/models/plum/term.rb +1 -0
- data/app/services/plum/config_sync.rb +252 -0
- data/app/services/plum/draft_diff.rb +177 -0
- data/app/services/plum/form_renderer.rb +12 -1
- data/app/services/plum/liquid_context.rb +0 -2
- data/app/services/plum/site_archive.rb +367 -0
- data/app/views/layouts/plum/write.html.erb +140 -0
- data/app/views/plum/cp/dashboard/show.html.erb +10 -3
- data/app/views/plum/cp/entries/_form.html.erb +2 -2
- data/app/views/plum/cp/entries/diff.html.erb +40 -0
- data/app/views/plum/cp/entries/edit.html.erb +24 -0
- data/app/views/plum/cp/entries/index.html.erb +3 -0
- data/app/views/plum/cp/entries/write.html.erb +60 -0
- data/config/plum_routes.rb +5 -0
- data/db/engine_migrate/20260811090000_add_draft_data_to_plum_entries.rb +5 -0
- data/docs/config-as-code.md +103 -0
- data/docs/plum-cli.md +356 -0
- data/docs/portability.md +40 -0
- data/docs/roadmap.md +71 -69
- data/docs/static-caching.md +163 -0
- data/lib/generators/plum/install/templates/plum_initializer.rb +8 -0
- data/lib/plum/configuration.rb +12 -1
- data/lib/plum/engine.rb +10 -1
- data/lib/plum/static_cache/middleware.rb +61 -0
- data/lib/plum/static_cache.rb +103 -0
- data/lib/plum/version.rb +1 -1
- data/lib/tasks/plum_config.rake +48 -0
- data/lib/tasks/plum_portability.rake +62 -0
- data/lib/tasks/plum_styles.rake +14 -9
- metadata +26 -5
data/docs/plum-cli.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# The Plum CLI
|
|
2
|
+
|
|
3
|
+
`cli/` (Go, `github.com/tableneeds/Plum/cli`) is the Plum command line. It
|
|
4
|
+
wraps the rake tasks that ship with the engine — `plum:site:export`,
|
|
5
|
+
`plum:site:replace`, `plum:config:sync`, `plum:backup:create`, and so on —
|
|
6
|
+
over a transport of your choosing. **The CLI adds transport and ergonomics,
|
|
7
|
+
never behavior.** Anything it does, you could also do by hand with `ssh` and
|
|
8
|
+
`bin/rails`; it just saves you from typing the same incantation every time
|
|
9
|
+
and from having to remember which task does what.
|
|
10
|
+
|
|
11
|
+
Status: early. Every command has been exercised end-to-end against a local
|
|
12
|
+
Plum install (`via: ssh` with `host: local`), and `via: once` has been
|
|
13
|
+
verified against a live Once deployment on a real VPS (`plum connect`,
|
|
14
|
+
tooling detection, and `plum logs` included). `via: kamal`'s
|
|
15
|
+
argument-building is unit-tested against fake binaries and smoke-tested
|
|
16
|
+
against the real `kamal` executable's argument parser, but hasn't been run
|
|
17
|
+
against a real Kamal fleet yet.
|
|
18
|
+
|
|
19
|
+
## Look and feel
|
|
20
|
+
|
|
21
|
+
On a terminal, the CLI speaks heroku-style: `▸` step headers, animated
|
|
22
|
+
spinners that settle into timed `✓ Downloading archive (700ms)` lines,
|
|
23
|
+
remote rake output framed behind a dim `│` gutter (so the server's chatter
|
|
24
|
+
reads as quoted material, not the CLI's own voice), and arrow-key prompts
|
|
25
|
+
in `plum connect`. The accent color is, naturally, plum — adaptive to
|
|
26
|
+
light and dark backgrounds.
|
|
27
|
+
|
|
28
|
+
All of it degrades automatically: pipe any command, run it in CI, set
|
|
29
|
+
`NO_COLOR`, or use `TERM=dumb`, and you get plain sequential text with the
|
|
30
|
+
classic `Prompt [default]:` stdin reads — scripted answers and log parsing
|
|
31
|
+
keep working unchanged.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
No published binary yet — build from source:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
cd cli
|
|
39
|
+
go build -o plum .
|
|
40
|
+
# put ./plum somewhere on your PATH, e.g.:
|
|
41
|
+
mv plum ~/.local/bin/plum
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## The built-in tutorial
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
plum tutorial
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
An interactive, full-screen tour of Plum and the CLI — eight short
|
|
51
|
+
chapters covering the content model, themes, writing mode and drafts,
|
|
52
|
+
static caching, config as code, and every CLI command, rendered as styled
|
|
53
|
+
markdown right in your terminal (bubbletea + glamour). Chapters slide in
|
|
54
|
+
on spring physics (harmonica), a gradient progress bar tracks where you
|
|
55
|
+
are, the CLI chapters have **live demos** — press `d` and watch
|
|
56
|
+
`plum pull` or a connect health check type itself out, spinners and all —
|
|
57
|
+
and the tour ends with a three-question quiz. Harmonica does real work:
|
|
58
|
+
a splash intro drops a bouncing plum across the springing wordmark,
|
|
59
|
+
wrong quiz answers shake on an underdamped spring, and finishing the
|
|
60
|
+
quiz bursts projectile-physics confetti.
|
|
61
|
+
|
|
62
|
+
It's also a game. Progress persists (chapters read, demos watched, quiz
|
|
63
|
+
passed) so the tour resumes where you left off; everything earns XP with
|
|
64
|
+
plum-themed ranks in the header (Seedling through Orchardist). CLI
|
|
65
|
+
chapters have typing challenges — press `t` and type the real command
|
|
66
|
+
with per-character feedback; success rolls the demo. And the title
|
|
67
|
+
screen hides **Plum Drop** (press `p`): catch falling plums with an
|
|
68
|
+
arrow-key basket, chase combos, and defend a persisted high score — it's
|
|
69
|
+
endless, so play until you've had enough. Navigate with ←/→, scroll
|
|
70
|
+
with ↑/↓, jump with 1–9, quit with q. Piped, it prints the chapters as
|
|
71
|
+
plain markdown.
|
|
72
|
+
|
|
73
|
+
And because it's bubbletea, it also serves over SSH (charm's wish):
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
plum tutorial --serve # listens on :2222
|
|
77
|
+
plum tutorial --serve :2345 # or wherever
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Anyone who can reach the port gets the full animated tour with nothing
|
|
81
|
+
installed — `ssh -p 2222 your-server` and they're in. Sessions run only
|
|
82
|
+
the tutorial program (no shell, no exec); any or no public key is
|
|
83
|
+
accepted since the content is public. The host key persists at
|
|
84
|
+
`~/.config/plum/tutorial_host_ed25519` so returning visitors don't get
|
|
85
|
+
host-key warnings.
|
|
86
|
+
|
|
87
|
+
## Two layers of configuration
|
|
88
|
+
|
|
89
|
+
This mirrors the Firebase CLI's model deliberately, because it solves the
|
|
90
|
+
same two problems: "how does this directory reach its server" and "how do I
|
|
91
|
+
work with a project without cd-ing into its directory first."
|
|
92
|
+
|
|
93
|
+
1. **`plum.yml`** — lives in a site's own repo, defines its **remotes**
|
|
94
|
+
(servers). This is the source of truth for how to reach that one site.
|
|
95
|
+
2. **The global project registry** (`~/.config/plum/config.yml`, or
|
|
96
|
+
`$XDG_CONFIG_HOME/plum/config.yml`) — lives on your dev machine, not in
|
|
97
|
+
any repo. It's just a name → directory index plus one "active project"
|
|
98
|
+
pointer, so you can run `plum pull` from your home directory and have it
|
|
99
|
+
know you mean the Table Needs site, without `cd`-ing there first.
|
|
100
|
+
|
|
101
|
+
### `plum.yml`
|
|
102
|
+
|
|
103
|
+
The easy way — guided setup, no YAML editing:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
plum connect 203.0.113.5
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Walks you through: checking for a local SSH key (offers to `ssh-keygen` one
|
|
110
|
+
if you don't have one), testing whether key-based login to the server
|
|
111
|
+
already works, offering to run `ssh-copy-id` if it doesn't, optionally
|
|
112
|
+
adding a `Host` alias to your real `~/.ssh/config` (so `ssh
|
|
113
|
+
plum-production` also works, and plum.yml can reference the alias instead
|
|
114
|
+
of raw host/user), and finally writing `plum.yml` for you.
|
|
115
|
+
|
|
116
|
+
It also **detects how the repo deploys** and asks the right questions for
|
|
117
|
+
that shape: `config/deploy.yml` means Kamal, a `Dockerfile` without one
|
|
118
|
+
usually means Once (you confirm the guess either way). A plain ssh remote
|
|
119
|
+
gets asked for the app's path on the server; a Once remote gets asked for
|
|
120
|
+
the app's hostname (`once_app`) instead, since the app lives in a container
|
|
121
|
+
rather than at a path; a Kamal remote needs neither.
|
|
122
|
+
|
|
123
|
+
Connect asks as little as it can get away with — ideally nothing.
|
|
124
|
+
**On an already-configured project, bare `plum connect` skips the interview
|
|
125
|
+
entirely**: it shows what plum.yml says and verifies it still works (SSH
|
|
126
|
+
login, server tooling, the app actually deployed), exiting nonzero if a
|
|
127
|
+
check fails — a doctor, usable from CI. The wizard only runs on first
|
|
128
|
+
setup, with `--reconfigure`, or when you pass a new ip/host explicitly.
|
|
129
|
+
|
|
130
|
+
When the wizard does run, **re-runs prefill every prompt from the existing
|
|
131
|
+
plum.yml** — Enter through everything and you reproduce the current setup.
|
|
132
|
+
Even a brand-new project isn't a blank slate: **the host prompt offers the
|
|
133
|
+
plum-* aliases already in your ~/.ssh/config** (servers this CLI set up
|
|
134
|
+
before) as a pick-list, so connecting a second site to the same VPS is all
|
|
135
|
+
Enter keys. And for Once remotes, once SSH login works it **asks the
|
|
136
|
+
server itself** (`once list`) and offers the deployed apps as a pick-list —
|
|
137
|
+
with your previous choice highlighted — instead of a blank hostname
|
|
138
|
+
prompt.
|
|
139
|
+
|
|
140
|
+
For Kamal and Once shapes it then **checks the server's tooling** — Docker
|
|
141
|
+
Engine for both, plus the `once` binary for Once — and offers to install
|
|
142
|
+
whatever's missing (`curl -fsSL https://get.docker.com | sh`, `curl
|
|
143
|
+
https://get.once.com | sh`), each only with your explicit confirmation. It
|
|
144
|
+
stops short of deploying the app itself: pushing images and registry
|
|
145
|
+
credentials stay in your hands. Finally it verifies what it can — that
|
|
146
|
+
`bin/rails` exists at the path you gave (ssh), or that `once list` shows
|
|
147
|
+
your app (once) — so you find out immediately if something's off instead of
|
|
148
|
+
on your first `plum pull`. Along the way it registers the project in the
|
|
149
|
+
global registry under the directory's name (and makes it the active project
|
|
150
|
+
if none is set), so `plum use <name>` and `--project <name>` work without a
|
|
151
|
+
separate `plum projects add`.
|
|
152
|
+
|
|
153
|
+
`plum connect` (with no IP) prompts for the host interactively too. Run it
|
|
154
|
+
again to add a second remote (e.g. staging) to an existing plum.yml — it
|
|
155
|
+
merges in the new remote rather than overwriting the file, though note that
|
|
156
|
+
re-saving via `connect` does not preserve hand-written comments in an
|
|
157
|
+
existing plum.yml.
|
|
158
|
+
|
|
159
|
+
The manual way — writes an editable template with comments intact:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
plum init
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
```yaml
|
|
166
|
+
default: production # which remote to use when none is named
|
|
167
|
+
|
|
168
|
+
remotes:
|
|
169
|
+
production:
|
|
170
|
+
host: your-server.example.com
|
|
171
|
+
user: deploy
|
|
172
|
+
path: /var/www/your-site
|
|
173
|
+
# rails: bin/rails # how to invoke rails in `path` (default shown)
|
|
174
|
+
# ssh_args: ["-p", "2222"] # extra ssh/scp args: custom port, identity file, etc.
|
|
175
|
+
|
|
176
|
+
staging:
|
|
177
|
+
host: staging.example.com
|
|
178
|
+
user: deploy
|
|
179
|
+
path: /var/www/your-site-staging
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
Omit the remote name on any command to use `default:` (or the only remote,
|
|
183
|
+
if there's just one).
|
|
184
|
+
|
|
185
|
+
### The project registry (fleet management)
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
plum projects add table-needs ~/Work/TableNeeds # register a directory
|
|
189
|
+
plum projects list # see what's registered
|
|
190
|
+
plum use table-needs # make it the active project
|
|
191
|
+
plum projects remove table-needs # forget it (files untouched)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Once a project is active, every command works from anywhere:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
cd ~ # nowhere near the TableNeeds repo
|
|
198
|
+
plum use table-needs
|
|
199
|
+
plum pull # pulls TableNeeds' production data into TableNeeds' local site
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Resolution order for any command (`pull`, `push`, `check`, `backup`, `logs`, `run`):
|
|
203
|
+
|
|
204
|
+
1. `--project NAME` on the command itself — always wins.
|
|
205
|
+
2. A `plum.yml` in the **current directory** — so working inside a repo
|
|
206
|
+
behaves exactly like today, unaffected by whatever project is active
|
|
207
|
+
elsewhere.
|
|
208
|
+
3. The globally **active project** (`plum use`) — the fallback that makes
|
|
209
|
+
fleet commands work from anywhere.
|
|
210
|
+
4. Otherwise: an error telling you to `plum init` or `plum use`.
|
|
211
|
+
|
|
212
|
+
```bash
|
|
213
|
+
plum pull --project table-needs # one-off target, doesn't change what's active
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Commands
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
plum connect [ip-or-host]
|
|
220
|
+
```
|
|
221
|
+
Guided setup — see above. The only command that touches your local SSH
|
|
222
|
+
config or generates keys, and only with your confirmation at each step.
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
plum pull [remote] [--yes]
|
|
226
|
+
```
|
|
227
|
+
Replaces your local site with the remote's: exports an archive there,
|
|
228
|
+
downloads it, and runs `plum:site:replace` locally. Content-only — the
|
|
229
|
+
underlying database engine doesn't have to match (Postgres in production,
|
|
230
|
+
SQLite locally works fine; see `docs/config-as-code.md` and the site archive
|
|
231
|
+
format). Prompts for confirmation unless `--yes` is passed.
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
plum push [remote] [--prune] [--force]
|
|
235
|
+
plum check [remote]
|
|
236
|
+
```
|
|
237
|
+
Upload the local `plum/` config-as-code directory (see
|
|
238
|
+
`docs/config-as-code.md`) and apply or verify it against the remote. On a
|
|
239
|
+
project with no `plum/` yet, both commands offer to run
|
|
240
|
+
`bin/rails plum:config:export` for you on the spot (interactively only —
|
|
241
|
+
scripts and CI still get a hard error, since a fresh export would make
|
|
242
|
+
the drift check meaningless). `check`'s exit code is nonzero on
|
|
243
|
+
drift — wire it into CI. `--prune` deletes content types missing from the
|
|
244
|
+
files; `--force` allows that even when they still have entries.
|
|
245
|
+
|
|
246
|
+
Push moves *structure* (content types, fieldsets), pull moves *content* —
|
|
247
|
+
the same asymmetry as "push code, pull data", because in Plum's model your
|
|
248
|
+
content model **is** code. Neither touches entry data on the other side.
|
|
249
|
+
(`plum sync` still works as an alias for push; it was renamed because sync
|
|
250
|
+
sounded two-way.)
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
plum backup [remote]
|
|
254
|
+
```
|
|
255
|
+
Runs `plum:backup:create` on the remote (writes a timestamped archive on the
|
|
256
|
+
server; doesn't download it — that's what `pull` is for).
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
plum logs [remote] [--follow]
|
|
260
|
+
```
|
|
261
|
+
Shows the app's recent logs; `--follow` (or `--tail`) streams them until you
|
|
262
|
+
Ctrl-C, heroku-style. On a terminal, structured JSON log lines (the
|
|
263
|
+
slog/thruster shape modern Rails containers emit) are prettified — dim
|
|
264
|
+
local-time stamps, color-coded levels and HTTP statuses, request lines
|
|
265
|
+
compacted to `GET /up 200 (2ms)` with the noisy fields dropped — while
|
|
266
|
+
plain Rails log text passes through untouched. Piped or under `NO_COLOR`
|
|
267
|
+
you get the raw bytes the server sent, so `plum logs | jq` still works.
|
|
268
|
+
|
|
269
|
+
What "the logs" means depends on the transport: plain ssh tails
|
|
270
|
+
`log/production.log` under the app path, Kamal runs `kamal app logs` (all
|
|
271
|
+
hosts, not just the primary), and Once streams `docker logs` from the app's
|
|
272
|
+
container on the server.
|
|
273
|
+
|
|
274
|
+
```
|
|
275
|
+
plum run [remote] -- TASK [ENV=value ...]
|
|
276
|
+
```
|
|
277
|
+
Escape hatch: run any rake task on the remote. `plum run production --
|
|
278
|
+
plum:site:export ARCHIVE=/tmp/x.zip` is what `pull` does internally, spelled
|
|
279
|
+
out.
|
|
280
|
+
|
|
281
|
+
## Transports (`via:`)
|
|
282
|
+
|
|
283
|
+
Every remote picks a `via:`. It only changes *how* the command above reaches
|
|
284
|
+
the server — the rake tasks and their behavior are identical either way.
|
|
285
|
+
|
|
286
|
+
### `via: ssh` (default)
|
|
287
|
+
|
|
288
|
+
Plain `ssh`/`scp`, inheriting your `~/.ssh/config`, SSH agent, and jump
|
|
289
|
+
hosts. Needs `host`, usually `user` and `path`.
|
|
290
|
+
|
|
291
|
+
### `via: kamal` — for apps deployed with [Kamal](https://kamalmanual.com)
|
|
292
|
+
|
|
293
|
+
```yaml
|
|
294
|
+
remotes:
|
|
295
|
+
production:
|
|
296
|
+
via: kamal
|
|
297
|
+
# kamal_bin: bin/kamal # default: bin/kamal if present, else `kamal` on PATH
|
|
298
|
+
# kamal_config: config/deploy.yml
|
|
299
|
+
# kamal_destination: staging # for config/deploy.staging.yml
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
No `host` or `path` needed — Kamal already knows your servers from
|
|
303
|
+
`config/deploy.yml` and owns its own SSH connection. Commands run as
|
|
304
|
+
`kamal app exec --reuse --primary "<command>"`, matching the pattern this
|
|
305
|
+
repo's own `config/deploy.yml` already uses for its `console`/`shell`
|
|
306
|
+
aliases. `--primary` targets one host even if you deploy to several, so a
|
|
307
|
+
rake task like `plum:site:export` runs exactly once.
|
|
308
|
+
|
|
309
|
+
### `via: once` — for apps deployed with [Once](https://once.com)
|
|
310
|
+
|
|
311
|
+
```yaml
|
|
312
|
+
remotes:
|
|
313
|
+
production:
|
|
314
|
+
via: once
|
|
315
|
+
host: plum-production # how to SSH into the server (IP or ~/.ssh/config alias)
|
|
316
|
+
once_app: your-app.example.com # the hostname you gave `once deploy --host ...`
|
|
317
|
+
# user: root # if host isn't an alias that already sets it
|
|
318
|
+
# once_bin: once # path to once ON THE SERVER (default shown)
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Unlike Kamal, Once's CLI runs **on the server**, not on your dev machine —
|
|
322
|
+
it talks straight to the local Docker daemon. So this transport is really
|
|
323
|
+
ssh underneath: it connects to `host` (same semantics as `via: ssh` —
|
|
324
|
+
aliases, agents, and `ssh_args` all work) and runs
|
|
325
|
+
`once exec <once_app> <command> [args...]` there.
|
|
326
|
+
|
|
327
|
+
That's why there are two identifiers: `host` is how you *reach the box*,
|
|
328
|
+
`once_app` is which *app on it* you mean — commonly different values, e.g.
|
|
329
|
+
an SSH alias pointing at a bare IP vs. the app's public DNS hostname. One
|
|
330
|
+
server can run several Once apps; `once_app` picks yours.
|
|
331
|
+
|
|
332
|
+
`plum logs` is the one command that doesn't go through `once exec`: the
|
|
333
|
+
Rails image logs to the container's stdout (there's no `log/production.log`
|
|
334
|
+
inside it), and deployed Once has no `logs` subcommand — so the CLI finds
|
|
335
|
+
your app's container by the `once` label Once stamps on it and streams
|
|
336
|
+
`docker logs`, the same daemon Once itself drives.
|
|
337
|
+
|
|
338
|
+
## What this doesn't do (yet)
|
|
339
|
+
|
|
340
|
+
- **No deploys.** `plum connect` will install the *tooling* a fresh server
|
|
341
|
+
needs (Docker Engine, the `once` binary — each with your confirmation),
|
|
342
|
+
but actually deploying the app — building images, registry credentials,
|
|
343
|
+
`once deploy` / `kamal deploy` — stays in your hands. Kamal and Once
|
|
344
|
+
already do that well; wrapping them for exec is as far as this goes on
|
|
345
|
+
purpose (see `docs/config-as-code.md` and the CLI design notes for why
|
|
346
|
+
re-inventing deploy tooling isn't the goal).
|
|
347
|
+
- **No `--all` / fleet-wide command yet.** Today you target one project (or
|
|
348
|
+
one remote) per invocation. Running one command across every registered
|
|
349
|
+
project is a natural next step, not yet built.
|
|
350
|
+
- **No downloaded database snapshot for SQLite-in-production setups.**
|
|
351
|
+
`pull` always goes through the portable site archive, which works
|
|
352
|
+
regardless of the production database engine. A faster SQLite-specific
|
|
353
|
+
path (`sqlite3 prod.db "VACUUM INTO ..."`) was considered and deliberately
|
|
354
|
+
skipped — the archive is the one code path that works whether production
|
|
355
|
+
runs SQLite or Postgres, and duplicating that logic for a marginal speedup
|
|
356
|
+
wasn't worth the two-path maintenance cost.
|
data/docs/portability.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Site export, import, backup, and restore
|
|
2
|
+
|
|
3
|
+
Plum 0.3 archives are versioned ZIP files containing a JSON manifest and the
|
|
4
|
+
original bytes of every attached asset. The manifest carries schemas, entries,
|
|
5
|
+
relationships, translations, taxonomies, navigation, globals, forms, fieldsets,
|
|
6
|
+
settings, revisions, and submissions. Import remaps database identifiers so an
|
|
7
|
+
archive can be restored into a different database without collisions.
|
|
8
|
+
|
|
9
|
+
## Export and import
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
bin/rails plum:site:export ARCHIVE=/safe/location/site.plum.zip SITE_ID=1
|
|
13
|
+
bin/rails plum:site:import ARCHIVE=/safe/location/site.plum.zip NAME="Imported site" DOMAIN=example.com
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`SITE_ID` is optional when exporting and defaults to the first site. `NAME` and
|
|
17
|
+
`DOMAIN` are optional import overrides. Import always creates a new site; it
|
|
18
|
+
never overwrites an existing site.
|
|
19
|
+
|
|
20
|
+
## Backup and restore
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
bin/rails plum:backup:create SITE_ID=1 DIRECTORY=/var/backups/plum
|
|
24
|
+
bin/rails plum:backup:restore ARCHIVE=/var/backups/plum/plum-site-1-TIMESTAMP.plum.zip
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The backup task writes a timestamped archive. Restore uses the same safe,
|
|
28
|
+
new-site import path. Assets are checked against their recorded byte size and
|
|
29
|
+
checksum before the database transaction commits. Missing, corrupt, or
|
|
30
|
+
unsupported archives fail without leaving a partially imported site.
|
|
31
|
+
|
|
32
|
+
Backups must be copied off the application host and tested regularly. An archive
|
|
33
|
+
does not include application code, environment secrets, users, or the host
|
|
34
|
+
database outside the selected Plum site.
|
|
35
|
+
|
|
36
|
+
## Archive compatibility
|
|
37
|
+
|
|
38
|
+
The manifest currently uses format version `1`. Plum rejects archive versions
|
|
39
|
+
it does not understand instead of guessing. Future format changes will either
|
|
40
|
+
remain readable or ship an explicit migration path.
|
data/docs/roadmap.md
CHANGED
|
@@ -1,76 +1,79 @@
|
|
|
1
1
|
# Roadmap
|
|
2
2
|
|
|
3
|
-
Plum's roadmap is organized by outcomes rather than promised dates.
|
|
4
|
-
as the product is exercised in real
|
|
5
|
-
feedback.
|
|
6
|
-
|
|
7
|
-
## Now: Make Plum Real and Adoptable
|
|
8
|
-
|
|
9
|
-
The current milestone is to build and publish the Plum marketing and
|
|
10
|
-
documentation site as a standalone Rails application powered by Plum and
|
|
11
|
-
SQLite.
|
|
12
|
-
|
|
13
|
-
### Product foundation
|
|
14
|
-
|
|
15
|
-
- Publish a clear vision, product principles, and supported use cases.
|
|
16
|
-
- Build `plumcms.org` with Plum as an external dependency.
|
|
17
|
-
- Define a portable production layout for the SQLite database, uploaded assets,
|
|
18
|
-
and mutable site data.
|
|
19
|
-
- Package the site as an ONCE-compatible Docker application.
|
|
20
|
-
- Exercise installation, initialization, health checks, upgrades, backup, and
|
|
21
|
-
restore on a real VM.
|
|
22
|
-
- Keep SQLite and PostgreSQL verification in CI.
|
|
23
|
-
|
|
24
|
-
### Documentation
|
|
25
|
-
|
|
26
|
-
- Document installation and the first editable page.
|
|
27
|
-
- Document sites, content types, fields, entries, and publishing.
|
|
28
|
-
- Document blocks, assets, relationships, taxonomies, navigation, globals, and
|
|
29
|
-
forms.
|
|
30
|
-
- Document Liquid themes and host content sources.
|
|
31
|
-
- Document embedded authentication, authorization, tenancy, and routing.
|
|
32
|
-
- Document standalone SQLite and embedded PostgreSQL deployments.
|
|
33
|
-
- Publish configuration, troubleshooting, and upgrade references.
|
|
34
|
-
|
|
35
|
-
### Distribution
|
|
36
|
-
|
|
37
|
-
- Publish the `plum` gem.
|
|
38
|
-
- State supported Ruby and Rails versions.
|
|
39
|
-
- Establish semantic versioning and an upgrade policy.
|
|
40
|
-
- Test fresh external installations and upgrades in CI.
|
|
41
|
-
- Provide a production-ready container contract and example application.
|
|
42
|
-
|
|
43
|
-
## Next: Editorial Confidence and Portability
|
|
44
|
-
|
|
45
|
-
Once the public site proves the basic workflow, focus on the features editors
|
|
46
|
-
and agencies need to trust Plum in production.
|
|
47
|
-
|
|
48
|
-
- Draft preview.
|
|
49
|
-
- Revisions and rollback.
|
|
50
|
-
- Scheduled publishing.
|
|
51
|
-
- Multi-entry relationships.
|
|
52
|
-
- Reusable blocks and sections.
|
|
53
|
-
- Content, schema, and asset export/import.
|
|
54
|
-
- Document hierarchy, tables of contents, and site search.
|
|
55
|
-
- SEO metadata, canonical URLs, sitemaps, feeds, and redirect management.
|
|
56
|
-
- Stronger asset organization and image editing.
|
|
57
|
-
- Form spam protection and improved submission workflows.
|
|
58
|
-
- Tested backup and restore commands.
|
|
3
|
+
Plum's roadmap is organized by outcomes rather than promised dates. Priorities
|
|
4
|
+
move as the product is exercised in real Rails applications.
|
|
59
5
|
|
|
60
|
-
##
|
|
6
|
+
## Shipped: Plum 0.2.0 — Authoring Foundation
|
|
61
7
|
|
|
62
|
-
|
|
63
|
-
adopt repeatedly and extend publicly.
|
|
8
|
+
Plum 0.2.0 established the practical authoring surface:
|
|
64
9
|
|
|
65
|
-
-
|
|
66
|
-
and
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
-
|
|
10
|
+
- a visual blueprint builder with 21 field types, reusable fieldsets, nested
|
|
11
|
+
structures, validation, field widths, sections, and conditions;
|
|
12
|
+
- assets with single and multiple image fields, metadata, focal points, and
|
|
13
|
+
responsive variants;
|
|
14
|
+
- relationships, taxonomies, navigation, globals, forms, and Liquid themes;
|
|
15
|
+
- drafts, scheduled publishing, immutable revisions, and rollback;
|
|
16
|
+
- localized entries, locale-aware public routes, and a live-only content API;
|
|
17
|
+
- registry-backed custom field types and packaged Tailwind control-panel styles.
|
|
18
|
+
|
|
19
|
+
See [the Statamic parity matrix](statamic-parity.md) for the supported surface.
|
|
20
|
+
|
|
21
|
+
## Now: Plum 0.3.0 — Production Readiness
|
|
22
|
+
|
|
23
|
+
The 0.3.0 milestone makes Plum safe to adopt, move, recover, and operate outside
|
|
24
|
+
its development repository.
|
|
25
|
+
|
|
26
|
+
### 1. Portability and recovery
|
|
27
|
+
|
|
28
|
+
- Versioned site export/import covering schemas, entries, relationships,
|
|
29
|
+
taxonomies, navigation, globals, forms, settings, and assets.
|
|
30
|
+
- Tested backup and restore commands with integrity checks and clear failure
|
|
31
|
+
behavior.
|
|
32
|
+
- A documented archive format that remains readable across compatible releases.
|
|
33
|
+
|
|
34
|
+
### 2. Real-world installation
|
|
35
|
+
|
|
36
|
+
- Build `plumcms.org` as a standalone Rails application using the released gem.
|
|
37
|
+
- Test fresh external installations and 0.2.x upgrades in CI.
|
|
38
|
+
- Publish a production Docker contract and SQLite deployment example.
|
|
39
|
+
- Exercise health checks, upgrades, persistence, backup, and restore on a VM.
|
|
40
|
+
|
|
41
|
+
### 3. Publishing and discovery
|
|
42
|
+
|
|
43
|
+
- SEO metadata, canonical URLs, redirects, XML sitemaps, and feeds.
|
|
44
|
+
- Document hierarchy, breadcrumbs, generated tables of contents, and site search.
|
|
45
|
+
- Secure, shareable preview links for draft and scheduled content.
|
|
46
|
+
|
|
47
|
+
### 4. Editorial operations
|
|
48
|
+
|
|
49
|
+
- Asset folders, search, replacement, cropping, and richer transformations.
|
|
50
|
+
- Granular editorial roles, approvals, and publish permissions.
|
|
51
|
+
- Form spam protection and improved submission review/export workflows.
|
|
52
|
+
|
|
53
|
+
### 5. Documentation
|
|
54
|
+
|
|
55
|
+
- Complete installation and first-page guides.
|
|
56
|
+
- Document every supported field and content primitive.
|
|
57
|
+
- Document standalone SQLite and embedded PostgreSQL operation.
|
|
58
|
+
- Publish configuration, troubleshooting, extension, upgrade, backup, and
|
|
59
|
+
restore references.
|
|
60
|
+
|
|
61
|
+
## Next: Plum 0.4.0 — Repeatable Adoption
|
|
62
|
+
|
|
63
|
+
- A thin `plum` CLI for diagnostics, export/import, backup/restore, upgrades,
|
|
64
|
+
themes, and packaging.
|
|
65
|
+
- Starter applications for common publishing use cases.
|
|
70
66
|
- Theme scaffolding, validation, packaging, and distribution tools.
|
|
67
|
+
- A collection of high-quality open themes and blocks.
|
|
71
68
|
- Importers for common CMS and structured-data formats.
|
|
72
|
-
-
|
|
69
|
+
- Addon discovery and a documented compatibility contract.
|
|
70
|
+
|
|
71
|
+
## Later: The Rails Content Ecosystem
|
|
72
|
+
|
|
73
|
+
- Translation-service integrations and richer localization workflows.
|
|
74
|
+
- Agency-oriented multisite operations and reusable project recipes.
|
|
73
75
|
- Community examples, case studies, talks, and contribution programs.
|
|
76
|
+
- Broader content APIs where real applications demonstrate the need.
|
|
74
77
|
|
|
75
78
|
## Plum 1.0
|
|
76
79
|
|
|
@@ -92,7 +95,6 @@ It requires:
|
|
|
92
95
|
|
|
93
96
|
## Not on the Near-Term Roadmap
|
|
94
97
|
|
|
95
|
-
Plum is not
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
belongs inside Rails.
|
|
98
|
+
Plum is not trying to become a general-purpose admin framework, a freeform
|
|
99
|
+
visual design tool, an ecommerce platform, or a hosted headless CMS. Plum's
|
|
100
|
+
focus is managed content that belongs inside Rails.
|