valise-sh 0.1.0 → 0.3.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.
Files changed (88) hide show
  1. package/CHANGELOG.md +320 -9
  2. package/README.md +104 -127
  3. package/bin/valise +67 -3266
  4. package/docs/architecture.md +76 -51
  5. package/docs/contrib-modules.md +180 -173
  6. package/docs/installation.md +210 -69
  7. package/docs/module-abi.md +211 -148
  8. package/docs/projects.md +249 -0
  9. package/docs/release-policy.md +90 -11
  10. package/docs/shared-modules.md +149 -26
  11. package/examples/README.md +20 -8
  12. package/examples/e2e/README.md +61 -68
  13. package/examples/e2e/bundles.tsv +6 -0
  14. package/examples/e2e/module.d/e2e_aliases.meta +1 -2
  15. package/examples/e2e/module.d/e2e_completions_bash.meta +0 -1
  16. package/examples/e2e/module.d/e2e_completions_zsh.meta +0 -1
  17. package/examples/e2e/module.d/e2e_env_setup.meta +2 -3
  18. package/examples/e2e/module.d/e2e_fish_features.meta +0 -1
  19. package/examples/e2e/module.d/e2e_generated_config.meta +1 -2
  20. package/examples/e2e/module.d/e2e_ksh_features.meta +0 -1
  21. package/examples/e2e/module.d/e2e_layer_probe.meta +1 -2
  22. package/examples/e2e/module.d/e2e_optional_network.meta +1 -2
  23. package/examples/e2e/module.d/e2e_platform_tools.meta +8 -0
  24. package/examples/e2e/module.d/e2e_platform_tools_bsd.meta +2 -3
  25. package/examples/e2e/module.d/e2e_platform_tools_darwin.meta +2 -3
  26. package/examples/e2e/module.d/e2e_platform_tools_linux.meta +2 -3
  27. package/examples/e2e/valise.conf +4 -0
  28. package/examples/e2e/variants.tsv +3 -0
  29. package/examples/local/module.d/hello_valise.meta +1 -2
  30. package/examples/profile-project/README.md +151 -0
  31. package/examples/profile-project/bundles.tsv +8 -0
  32. package/examples/profile-project/layers/demo/module.d/contrib_demo_greeting.meta +9 -0
  33. package/examples/profile-project/layers/demo/modules/shared/contrib_demo_greeting.sh +4 -0
  34. package/examples/profile-project/module.d/greeting.meta +9 -0
  35. package/examples/profile-project/module.d/profile_main.meta +8 -0
  36. package/examples/profile-project/modules/greeting.sh +5 -0
  37. package/examples/profile-project/valise.conf +4 -0
  38. package/examples/profile-project/variants.tsv +3 -0
  39. package/examples/shared/README.md +13 -34
  40. package/examples/shared/module.d/contrib_shared_paths.meta +2 -3
  41. package/examples/shared/module.d/contrib_shared_umask.meta +1 -2
  42. package/examples/shared/modules/shared/contrib_shared_paths.sh +2 -2
  43. package/lib/capabilities.sh +40 -13
  44. package/lib/cli/commands.sh +278 -0
  45. package/lib/cli/common.sh +326 -0
  46. package/lib/cli/devtools.sh +539 -0
  47. package/lib/cli/fish-hook.sh +986 -0
  48. package/lib/cli/integration.sh +337 -0
  49. package/lib/cli/lifecycle.sh +578 -0
  50. package/lib/cli/project.sh +1807 -0
  51. package/lib/cli/store.sh +1023 -0
  52. package/lib/cli/usage.sh +308 -0
  53. package/lib/compiler.awk +352 -24
  54. package/lib/fish/loader.fish +163 -28
  55. package/lib/fish/metadata.fish +62 -19
  56. package/lib/fish/profile.fish +170 -0
  57. package/lib/loader.sh +736 -158
  58. package/lib/metadata.sh +137 -55
  59. package/manifests/parser-support.tsv +2 -2
  60. package/module.d/valise_editor.meta +9 -0
  61. package/module.d/valise_history.meta +9 -0
  62. package/module.d/valise_pager.meta +9 -0
  63. package/module.d/valise_path.meta +9 -0
  64. package/module.d/{platform_bsd.meta → valise_platform_bsd.meta} +3 -4
  65. package/module.d/{platform_linux.meta → valise_platform_linux.meta} +3 -4
  66. package/module.d/{platform_macos.meta → valise_platform_macos.meta} +3 -4
  67. package/module.d/{platform_posix.meta → valise_platform_posix.meta} +2 -3
  68. package/module.d/valise_xdg.meta +9 -0
  69. package/modules/system/{contrib_editor.sh → valise_editor.sh} +2 -2
  70. package/modules/system/{contrib_history.sh → valise_history.sh} +2 -2
  71. package/modules/system/{contrib_pager.sh → valise_pager.sh} +2 -2
  72. package/modules/system/{contrib_path.sh → valise_path.sh} +3 -3
  73. package/modules/system/{contrib_xdg.sh → valise_xdg.sh} +2 -2
  74. package/package.json +91 -9
  75. package/docs/variants.md +0 -63
  76. package/entrypoints/.gitkeep +0 -0
  77. package/entrypoints/fish/profile.fish +0 -99
  78. package/examples/e2e/build-manifest.sh +0 -250
  79. package/examples/local/.gitkeep +0 -0
  80. package/lib/.gitkeep +0 -0
  81. package/manifests/.gitkeep +0 -0
  82. package/module.d/.gitkeep +0 -0
  83. package/module.d/contrib_editor.meta +0 -10
  84. package/module.d/contrib_history.meta +0 -10
  85. package/module.d/contrib_pager.meta +0 -10
  86. package/module.d/contrib_path.meta +0 -10
  87. package/module.d/contrib_xdg.meta +0 -10
  88. package/modules/.gitkeep +0 -0
package/CHANGELOG.md CHANGED
@@ -3,14 +3,325 @@
3
3
  Notable user-facing changes are recorded here. Versions before 1.0 may include
4
4
  module ABI or CLI changes; see [the release policy](docs/release-policy.md).
5
5
 
6
- ## Unreleased
6
+ ## [0.3.0] - 2026-11-06
7
7
 
8
- - Made the editor-defaults test independent of host-installed editors by
8
+ 0.3.0 changes the project layout, the metadata, and the release format. A
9
+ 0.2 project must be moved to the new layout and its releases rebuilt; see
10
+ [What changed in 0.3.0](docs/release-policy.md#what-changed-in-030).
11
+
12
+ ### Added
13
+
14
+ - Published commands. Metadata `commands=` names exports to publish and
15
+ `executables=` names scripts. `valise build` writes a command plan per
16
+ module under `plans/commands/` and a launcher per name under `bin/`, and
17
+ install writes a stub for each into
18
+ `${VALISE_BIN_DIR:-${XDG_BIN_HOME:-$HOME/.local/bin}}`. valise records
19
+ the stubs it writes in `$XDG_STATE_HOME/valise/commands.tsv` and never
20
+ replaces or removes a file it did not write. `valise doctor` reports
21
+ collisions and a command directory missing from `PATH`.
22
+ - `valise lint [--require-parsers] [module...]` checks metadata, layers,
23
+ every bundle compiled for every variant and shell, and the syntax of each
24
+ body and test with the parser of every shell the module declares. The
25
+ `valise.conf` key `lint_layer_forbid` makes lint reject a pattern, such as
26
+ an employer name, anywhere under `layers/`.
27
+ - `valise test [--shell=<class>] [--require-shells] [module...]` runs each
28
+ module's `tests=` scripts in every declared shell that is installed, in
29
+ an `env -i` environment with a fresh `HOME` and XDG tree.
30
+ `valise_test_reload` lets a test write fixtures and load the module again.
31
+ - `valise run [--shell=<class>] [--module=<m>]... [--bundle=<b>]
32
+ [--variant=<v>] [--keep-env] -- <script>` loads modules into a shell and
33
+ runs a script there.
34
+ - `valise env` prints the engine, project, and storage paths as shell-quoted
35
+ `KEY=value` lines.
36
+ - `valise test` and `valise run` export `VALISE_TEST_RELEASE` and
37
+ `VALISE_TEST_PLAN`, so a test can load the same modules in a child shell.
38
+ - `valise uninstall [--keep-releases]` removes the managed blocks (keeping
39
+ every other byte and the file mode), the startup files install created
40
+ that are left empty, the Fish hook, the owned command stubs, the active
41
+ link, and the installed releases.
42
+ - `valise release rollback` makes the previously active release active
43
+ again. Every switch records the release it replaced in
44
+ `$XDG_STATE_HOME/valise/previous`.
45
+ - `valise apply --dry-run` builds the project and prints what install would
46
+ change in the release store, the startup files, the Fish hook, and the
47
+ command directory, without writing outside `build/`.
48
+ - A bundle's `fish` rows build the native Fish runtime into the release
49
+ under `runtime/fish/`, and the Fish hook loads the bundle's Fish roots
50
+ from it. `valise new` scaffolds a Fish seed module.
51
+ - `valise -C <dir>` runs a command as if started in `dir`. Project commands
52
+ find the project from any subdirectory.
53
+ - `valise.conf` key `requires_valise`: an older valise refuses to build the
54
+ project.
55
+ - `valise module new --into=<module>[,...]` and `--no-wire` for projects
56
+ whose default bundle has several POSIX roots.
57
+ - Metadata `tests=` names the test scripts of a module.
58
+ - `shells=posix` names every POSIX shell.
59
+ - `docs/projects.md` describes the project layout, bundles, variants,
60
+ compile, and build.
61
+
62
+ ### Changed
63
+
64
+ - A project is the directory holding `valise.conf`, with `module.d/`,
65
+ `modules/`, `layers/<provider>/`, `bundles.tsv`, and `variants.tsv` at its
66
+ root. `valise.conf` replaces `shell/release/project.conf`, `bundles.tsv`
67
+ replaces `shell/release/bundle-roots`, and `variants.tsv` replaces
68
+ `shell/release/install-variants`. The `shell/` layout is not read.
69
+ - The registry is internal: `valise build` generates it on demand from the
70
+ metadata, and `valise registry` prints it. It has columns for `commands`,
71
+ `executables`, and `tests` and a `source` of `module.d`,
72
+ `layers/<provider>/module.d`, or `valise`.
73
+ - The engine modules are renamed into the reserved `valise_` namespace
74
+ (`valise_path`, `valise_editor`, `valise_history`, `valise_pager`,
75
+ `valise_xdg`, `valise_platform_posix`, `valise_platform_macos`,
76
+ `valise_platform_linux`, `valise_platform_bsd`), with exports renamed to
77
+ match, such as `valise_path_prepend`. They are available to every
78
+ project without vendoring. A project or layer module may not use the
79
+ `valise_` prefix, and layer modules must use `contrib_`.
80
+ - `shells=sh` names only the generic POSIX class (dash, BusyBox ash, and
81
+ other plain `sh`), so `shells=sh bash ksh` loads in dash, bash, and ksh
82
+ and not in zsh. Use `shells=posix` for every POSIX shell.
83
+ - Release format 2. A release holds `plans/<shell>.sh` for `sh`, `bash`,
84
+ `zsh`, and `ksh`, `runtime/capabilities.sh`, `valise-release.conf`
85
+ (`format=2`, the bundle, the variant, the roots per shell, the modules,
86
+ and the commands), and `manifest.tsv`. It contains only the modules some
87
+ plan loads, so a module a variant excludes is absent with its body.
88
+ Install refuses a tree without `format=2`.
89
+ - The POSIX managed block picks the plan for `ksh`, `zsh`, `bash`, or `sh`
90
+ and sets its guard only after the plan loaded, so a later startup file
91
+ retries after a failure.
92
+ - Install is transactional. It checks every startup file, the Fish hook
93
+ path, and (when commands are published or already tracked) the command
94
+ directory before changing the active release and, if a later step fails,
95
+ restores the previous active release, the previous-release record, and
96
+ the command stubs, and names the files it already wrote. A new managed
97
+ block is appended with nothing around it, so uninstall restores the file
98
+ exactly. Text after a block keeps the file's ending, with or without a
99
+ final newline, and a repeat install rewrites nothing.
100
+ - Install warns, and `valise doctor` fails, on a line outside a managed block
101
+ that loads valise by hand.
102
+ - `valise build` moves the previous `build/release` aside and restores it if
103
+ publication fails.
104
+ - An identical installed Fish hook is left in place, and apply reports
105
+ `Fish hook current`.
106
+ - `valise status` reports the bundle, variant, and valise version the active
107
+ release was built with, separately from the project defaults.
108
+ - `valise explain-variant` counts the roots and modules a variant keeps per
109
+ shell, resolving qualified names.
110
+ - `bin/valise` is a dispatcher over the parts in `lib/cli/`.
111
+ - The package `files` list names every shipped file, so packing a local tree
112
+ ships only the runtime, the docs, and the example sources, not stray local
113
+ files. A test fails when a tracked file is missing from the list.
114
+
115
+ ### Removed
116
+
117
+ - The `shell/` project layout, `registry --check`, and the committed
118
+ registry.
119
+ - Metadata `updates=`. Metadata naming it fails with a message naming
120
+ 0.3.0.
121
+ - The engine release shape: `entrypoints/fish/profile.fish`, the POSIX
122
+ block's loader fallback, and the Fish hook's `generated/`, `vendor/`, and
123
+ entrypoint routes.
124
+ - `examples/e2e/build-manifest.sh`; `examples/e2e` is a valise project built
125
+ with `valise build`.
126
+ - `docs/variants.md`, replaced by `docs/projects.md`.
127
+
128
+ ### Fixed
129
+
130
+ - `sp_cap_secure_local_bin_check` accepts `allow-owned-symlinks`. The default
131
+ still rejects every symlink in `~/.local/bin`; the option accepts a symlink
132
+ whose link entry is owned by the current user, which is how pipx and uv
133
+ publish commands, while still rejecting unowned entries and group- or
134
+ other-writable regular files.
135
+ - A repeated metadata field was silently overwritten by the last value; the
136
+ POSIX and Fish readers now reject it.
137
+ - The Fish loader rejected caller aliases, because `functions --copy` drops
138
+ `--wraps`.
139
+ - Fish 3 lists autoloadable but undefined functions in `functions --all
140
+ --names`, and that list grows as the loader introspects it. The Fish loader
141
+ and the generated hook now take their "created by this activation" baseline
142
+ after that introspection, so Fish's own system functions are no longer
143
+ attributed to a module or an entrypoint and published in an ownership
144
+ ledger whose `/usr/share/fish` source can never be authenticated.
145
+ - A body that replaced `cd` on Fish 3, where `cd` is a file-backed system
146
+ function rather than an embedded one, was reported as a generic loader-state
147
+ mutation. It now fails closed as a mutated critical Fish function, matching
148
+ Fish 4.
149
+ - A module body that sourced a Fish completion or function file, or ran
150
+ completions, materialized Fish's own system functions. Those entered the
151
+ ownership ledger, so the next activation failed closed before its cleanup
152
+ erased real Fish functions. Fish system functions are no longer recorded as
153
+ module- or entrypoint-owned, on Fish 3 (file paths) and Fish 4 (embedded
154
+ paths) alike.
155
+ - `valise doctor` did not accept FreeBSD's `sha256`.
156
+ - `valise module new` assumed the seed module was `profile_main`.
157
+
158
+ ## [0.2.0] - 2026-10-31
159
+
160
+ ### Added
161
+
162
+ - `valise new`, `valise module new`, `valise module list`,
163
+ `valise module remove`, `valise build`, `valise apply`, and
164
+ `valise status`. A new project can be scaffolded, given a module, built,
165
+ installed, and checked without hand-writing a registry or a build script.
166
+ `valise module new` writes the module body and its declared export, and
167
+ wires the module into the seed aggregator. `valise apply` builds, installs,
168
+ writes the shell integration, and verifies the result; it is idempotent.
169
+ `valise status` reports the project, the active release, the integration
170
+ state, and whether the active release has drifted from the build output.
171
+ - `valise registry`, which generates `shell/release/module-registry.tsv` from
172
+ the project's metadata. `valise build` regenerates and validates the
173
+ registry, so a committed registry is a generated artifact rather than a
174
+ hand-maintained catalog. The row order is fixed: the primary modules of
175
+ `shell/module.d/` first, then the layer modules of each
176
+ `shell/layers/<provider>/module.d/`, each group in C-locale name order.
177
+ - `valise release list`, `valise release use`, `valise release prune`, and
178
+ `valise release verify`. They manage installed releases; source variants
179
+ remain the business of `valise build` and `valise apply`.
180
+ - Command-specific `--help` for `build`, `compile`, `install`, `init`, `new`,
181
+ `module`, `release`, `doctor`, and `status`, so each prints its own
182
+ contract rather than the global command list.
183
+ - Meta modules: metadata with `requires=` and no `load` is a
184
+ dependency-only aggregator and loads successfully without a body. `load`
185
+ and `requires` may be combined.
186
+ - `side_effect=local-state`, a source-safe side-effect class for modules
187
+ that write under the XDG state directory.
188
+ - `VALISE_RELOAD=1`, which unsets the declared exports of the requested
189
+ closure before loading so a profile can be re-sourced.
190
+ - The `sp_cap_*` capability helpers remain available at runtime after a
191
+ compiled plan or a loader load finishes, so a module can call them.
192
+ - `examples/profile-project/`, a complete consuming project with two
193
+ modules, a provider layer, two variants, and a bundle, and a README that
194
+ walks from clone to a loaded profile.
195
+ - A consumer end-to-end test target that builds the reference consumer
196
+ profile from a temporary copy, runs the consumer's own metadata, shells,
197
+ release, and features suites, installs and verifies the release, and
198
+ proves a second build is byte-identical. The original tree and the live
199
+ profile are never touched.
200
+
201
+ ### Changed
202
+
203
+ - `valise build` builds a complete release tree to `build/release` by
204
+ default, reads the variant and bundle defaults from
205
+ `shell/release/project.conf`, and requires an explicit output directory to
206
+ be empty or absent. A failed build preserves the previous build output.
207
+ - `valise build` produces no Fish surface: no Fish entrypoint, generated
208
+ plan, or native loader. A built release therefore loads nothing in Fish;
209
+ the Fish hook verifies the active release and stays silent. Only an engine
210
+ release with `entrypoints/fish/profile.fish` uses the native Fish loader.
211
+ - `valise install` writes shell integration by default. It always writes
212
+ `~/.profile`; it adds `~/.bash_profile` or `~/.bash_login` when one of
213
+ those exists, because bash reads that file instead of `~/.profile`; it
214
+ writes `~/.bashrc` when bash is the current shell or that file exists; it
215
+ writes `~/.zprofile` when zsh is on `PATH` or that file exists, because zsh
216
+ reads nothing else at login; and it writes `~/.zshrc` when zsh is the
217
+ current shell or that file exists. Existing files are kept current rather
218
+ than replaced. A symlinked startup file is reported and left untouched.
219
+ `--no-integration` installs without writing any startup file. Install also
220
+ writes the Fish `conf.d` hook when it finds Fish in a trusted location;
221
+ `valise apply` runs the same install path.
222
+ - The startup integration is a managed block between `# BEGIN valise managed
223
+ block` and `# END valise managed block`. Repeat installs are idempotent and
224
+ existing profile content is preserved. An unpaired marker is refused and
225
+ stops the install. Install prints each path it wrote and how to remove the
226
+ block.
227
+ - `valise init` takes `posix` or `fish`; any other argument is rejected.
228
+ It is a one-time snippet generator, not something to evaluate at every
229
+ prompt.
230
+ - A project's default variant is `default`. An unknown variant is an error
231
+ in every command that accepts one.
232
+ - Compiled plans and the loader now agree on required dependency closure.
233
+ Platform skips are recorded during discovery and reported once the full
234
+ required closure is known; strict mode fails when a required module is
235
+ blocked during ordering; permissive mode continues and returns zero. Fish
236
+ reports a platform-skipped optional dependency silently.
237
+ - Compiled plans reach parity with the loader: they gate modules by
238
+ `platforms=` at runtime, pre-check and post-check declared exports, report
239
+ each failing module by name, continue past a failure in permissive mode
240
+ while returning zero, stop in strict mode, and support meta modules with no
241
+ source line.
242
+ - The loader saves the logical `$PWD` and `$OLDPWD` before sourcing each
243
+ module and restores them afterwards, so a module that changes directory
244
+ during its load leaves the caller's logical directory as it was.
245
+ - The loader's export pre-check accepts a name that currently resolves to an
246
+ external command, which is how a deliberate wrapper is exported. It still
247
+ rejects a name already defined as a function, builtin, or alias.
248
+ - The loader resolves each metadata record with shell builtins instead of
249
+ repeated `sed`, `grep`, and `cat` processes, so the number of external
250
+ processes a load spawns does not grow with the number of modules.
251
+ - `valise compile` and `valise build` find the packaged
252
+ `lib/capabilities.sh` relative to the CLI, so a symlinked or relocated
253
+ install works. A compiled plan rejects a module that has neither a load
254
+ path nor dependencies, and names the module.
255
+ - Layer handling accepts any `shell/layers/<provider>` path rather than only
256
+ the `contrib` provider, and produces a deterministic manifest.
257
+ - `valise doctor` reports a missing active release as a failure and reads the
258
+ startup files the running shell actually reads. `--pre-install` skips the
259
+ release and integration checks; `--no-integration` checks only the
260
+ environment and the active release.
261
+ - `valise explain-bundles` and `valise layer-lint` are implemented in POSIX sh
262
+ and awk. `layer-lint` validates a provider layer's structure, `contrib_`
263
+ name prefix, version, load path, symlink freedom, file modes, and
264
+ dependency containment.
265
+ - The unused Python consumer-release helpers, wrapper lifecycle hooks, and
266
+ the submodule build prelude override are gone. No command requires
267
+ Python 3.
268
+
269
+ ### Removed
270
+
271
+ - `load_hook` and the compatibility bridge in the POSIX loader, the Fish
272
+ loader, and compiled plans. A module declares `load`, `requires`, or both.
273
+ Metadata naming `load_hook` fails with
274
+ `metadata field load_hook is not supported; use load`.
275
+ - `variant list`, `variant use`, and `verify-active`; installed releases are
276
+ managed by `valise release list|use|prune|verify`. `variant` now refers
277
+ only to build variants.
278
+ - The per-shell `valise init` arguments `bash`, `zsh`, `ksh`, and `sh`; use
279
+ `valise init posix` or `valise init fish`.
280
+
281
+ ### Fixed
282
+
283
+ - `valise` with no arguments: under `set -e`, a zero-argument `shift` could
284
+ terminate the CLI before it displayed its help text.
285
+ - Fish activation: functions loaded from package paths such as Homebrew are
286
+ authenticated and preserved through the load instead of being rejected or
287
+ lost.
288
+ - The primary-root symlink diagnostic was printed on stdout while every
289
+ other loader diagnostic went to stderr.
290
+ - `valise compile` reported that a Fish plan requires a Python compiler and
291
+ named a file that does not exist in this repository. Fish plans are not
292
+ generated; a Fish integration uses the native Fish loader.
293
+ - Applying to a home directory with a trailing slash could skip the zsh
294
+ startup file, because the target was compared against the raw `HOME`
295
+ string rather than a normalized path.
296
+ - A built release's Fish hook printed an error on every Fish start. It now
297
+ verifies the release quietly and returns when there is no Fish surface to
298
+ load.
299
+ - A module that makes `PWD`, `OLDPWD`, or `IFS` readonly no longer aborts the
300
+ loader mid-transaction. The module fails as caller-state modification, and
301
+ the loader's cleanup still runs.
302
+ - Loader temp-directory cleanup resolves `rm` from a fixed absolute path, so a
303
+ module that changes `PATH` cannot leave its metadata scratch directory
304
+ behind.
305
+ - Compiled plans remove a failed module's declared exports, like the loader.
306
+ A meta module that declares exports is rejected at compile time, and an
307
+ optional-only cycle inside a required graph is quarantined instead of
308
+ failing the whole plan.
309
+ - The compiler rejects an unsafe layer directory name and a reserved-word
310
+ export instead of emitting an unparseable or misleading plan.
311
+ - `release list`, `release use`, `release prune`, `doctor`, and `status`
312
+ identify the active release by its canonical directory name, so a trailing
313
+ slash or an alias symlink cannot hide it from the list or make prune delete
314
+ it.
315
+ - `module remove` keeps a body that another module still loads, and checks
316
+ every directory it will rewrite before changing anything. `module new`
317
+ checks the seed metadata before writing and rejects reserved-word exports.
318
+ - `make-manifest` rejects a directory or symlink as its output, and a
319
+ normalized in-tree output path is excluded from its own manifest.
320
+ - `valise install` holds its lock through the Fish and POSIX integration
321
+ writes, so concurrent installs cannot interleave them.
322
+ - The editor-defaults test is independent of host-installed editors by
9
323
  supplying a temporary test `vi` executable.
10
- - Fixed `valise` with no arguments: under `set -e`, a zero-argument `shift`
11
- could terminate the CLI before it displayed its help text.
12
- - Fixed Fish activation so functions loaded from package paths (for example,
13
- Homebrew) are authenticated and preserved through the load instead of being
14
- rejected or lost.
15
- - Added an isolated sample install walkthrough and documented the release
16
- checklist.
324
+
325
+ [0.3.0]: https://github.com/joeycumines/valise-sh/compare/v0.2.0...v0.3.0
326
+ [0.2.0]: https://github.com/joeycumines/valise-sh/compare/v0.1.0...v0.2.0
327
+ [0.1.0]: https://github.com/joeycumines/valise-sh/releases/tag/v0.1.0
package/README.md CHANGED
@@ -1,146 +1,123 @@
1
1
  # valise-sh
2
2
 
3
- valise-sh is a POSIX shell-profile engine. It loads shell configuration from
4
- modules described by declarative metadata files. The metadata is parsed as
5
- data and never executed; the loader resolves the dependency graph and sources
6
- module bodies in deterministic order. The POSIX core runs under `sh`, `dash`,
7
- `ksh`, and `bash`; Fish is supported through a native loader in `lib/fish/`.
8
- The repository also includes the `valise` CLI, which installs sealed
9
- shell-profile releases and verifies them before use.
10
-
11
- A consuming project can split its shell configuration into modules with
12
- declared dependencies, then test and select those modules explicitly. The
13
- engine is designed to be vendored by that project, for example as a Git
14
- submodule; the consumer owns its profile and release contents.
15
-
16
- ## Try a sample release
17
-
18
- This walkthrough builds and installs the included sample profile in an
19
- isolated temporary home. `valise install` deploys its normal shell
20
- integration there, so your real home directory and startup files are left
21
- untouched.
3
+ valise builds your shell profile from modules. Each module is a metadata file
4
+ and a shell body; valise reads the metadata as data, compiles a static
5
+ startup plan for each shell, and installs the result as a sealed, verified
6
+ release. sh, dash, BusyBox ash, bash, zsh, and ksh load the compiled POSIX
7
+ plans; Fish has a native loader.
8
+
9
+ You keep your modules in a valise project. The engine supplies the `valise`
10
+ CLI and a few generic modules (`valise_path`, `valise_editor`, and others).
11
+
12
+ ## Quick start
22
13
 
23
14
  ```sh
24
15
  git clone https://github.com/joeycumines/valise-sh.git
25
- cd valise-sh
26
- export PATH="$PWD/bin:$PATH"
16
+ export PATH="$PWD/valise-sh/bin:$PATH"
17
+
18
+ valise new my-profile
19
+ cd my-profile
20
+ valise module new hello --exports=hello
21
+ $EDITOR modules/hello.sh
22
+ valise lint
23
+ valise apply
24
+ ```
25
+
26
+ `valise apply` builds the project, installs the release, writes a managed
27
+ block into your startup files, and verifies the active release. Open a new
28
+ login shell to load it. `valise apply --dry-run` shows what would change
29
+ first, and `valise uninstall` removes everything install wrote.
27
30
 
28
- # Isolate HOME and XDG state; the trap also removes read-only release files.
31
+ To try it without touching your real home directory, point `HOME` and the
32
+ XDG directories at a scratch directory:
33
+
34
+ ```sh
29
35
  (
30
36
  set -eu
31
- _demo_state=$(mktemp -d)
32
- cleanup_demo() {
33
- chmod -R u+w "$_demo_state" 2>/dev/null || :
34
- rm -rf "$_demo_state"
35
- }
36
- trap cleanup_demo EXIT
37
- trap 'exit 130' INT
38
- trap 'exit 143' TERM
39
- mkdir -p "$_demo_state/home"
40
- export HOME="$_demo_state/home"
41
- export XDG_DATA_HOME="$_demo_state/data"
42
- export XDG_CONFIG_HOME="$_demo_state/config"
43
- export XDG_STATE_HOME="$_demo_state/state"
44
-
45
- # Build, install, and verify the sample release.
46
- sh examples/e2e/build-manifest.sh
47
- (cd examples/e2e && valise install)
48
- valise verify-active
49
-
50
- # Load two shipped utilities from the installed release.
51
- _active=$(cd "$XDG_CONFIG_HOME/valise/active" && pwd -P)
52
- VALISE_MODULE_ROOT="$_active" \
53
- VALISE_MODULES='contrib_path contrib_editor' \
54
- VALISE_MODE=strict \
55
- . "$_active/entrypoints/profile.sh"
56
- contrib_path_prepend "$HOME/.local/bin"
57
- contrib_editor_defaults
58
- printf 'EDITOR=%s\nPATH=%s\n' "${EDITOR-(unset: no supported editor found on PATH)}" "$PATH"
37
+ demo=$(mktemp -d)
38
+ trap 'chmod -R u+w "$demo"; rm -rf "$demo"' EXIT
39
+ export HOME="$demo/home" XDG_DATA_HOME="$demo/data" \
40
+ XDG_CONFIG_HOME="$demo/config" XDG_STATE_HOME="$demo/state" \
41
+ XDG_BIN_HOME="$demo/bin"
42
+ mkdir -p "$HOME"
43
+ cd valise-sh/examples/profile-project
44
+ valise apply
45
+ bash -l -c greet
59
46
  )
60
47
  ```
61
48
 
62
- The end-to-end example documents Fish startup, shared-provider composition,
63
- and other modules in
64
- [examples/e2e/README.md](examples/e2e/README.md). For the smallest possible
65
- loader example without installation, use `examples/local/` as described in
66
- [examples/README.md](examples/README.md).
67
-
68
- ## Repository layout
69
-
70
- - `entrypoints/profile.sh` — the portable entrypoint. It is sourced, not
71
- executed: it resolves the engine root, sources the three libraries, and
72
- calls the loader with the requested modules.
73
- - `lib/metadata.sh` — parses `module.d/*.meta` files as data and validates
74
- identifiers, lists, paths, shells, platforms, and side-effect classes.
75
- - `lib/loader.sh` — resolves the dependency closure, selects modules by
76
- shell/platform/phase, sources bodies, checks declared exports, and cleans
77
- up on failure.
78
- - `lib/capabilities.sh` — portable helpers (`sp_cap_*`) that modules call
79
- instead of platform-specific commands.
80
- - `lib/fish/` — native Fish loader and metadata reader. The Fish path never
81
- sources POSIX shell bodies.
82
- - `bin/valise` — the CLI: init, install, doctor, variant, make-manifest,
83
- compile, verify-active, explain-variant, explain-bundles, layer-lint,
84
- version. `init`, `install`, `doctor`, `variant`, `make-manifest`,
85
- `verify-active`, and `version` run from a bare checkout; `compile`,
86
- `explain-variant`, `explain-bundles`, and `layer-lint` require the
87
- consuming project's source tree (see
88
- [docs/variants.md](docs/variants.md) and
89
- [docs/contrib-modules.md](docs/contrib-modules.md).
90
- - `module.d/` and `modules/platform/` — generic platform modules
91
- (`platform_posix`, `platform_macos`, `platform_linux`, `platform_bsd`).
92
- - `manifests/parser-support.tsv` — declares the shell parsers used to check
93
- each source-file class.
94
- - `tests/` — the test suite. Every test runs in a quarantined environment:
95
- fresh temporary HOME, controlled TMPDIR and PATH, cleared startup hooks.
96
- - `examples/local/` — a minimal runnable module demonstrating the metadata
97
- contract.
98
-
99
- Product-specific modules, adapters, release packaging, and installer
100
- transactions belong in the consuming project, not here. See
101
- [docs/architecture.md](docs/architecture.md) for the scope.
102
-
103
- ## Documentation
49
+ ## A project
104
50
 
105
- - [docs/installation.md](docs/installation.md) — install paths, shell
106
- integration, XDG layout, publishing, and the integrity and provenance limits.
107
- - [docs/module-abi.md](docs/module-abi.md) — the metadata contract: fields,
108
- allowed values, multi-root search path, runtime detection, side-effect
109
- phases, and the capability layer.
110
- - [docs/architecture.md](docs/architecture.md) — what this repository
111
- provides and what stays in the consuming project.
112
- - [docs/variants.md](docs/variants.md) — build variant selection and
113
- exclusion lists.
114
- - [docs/contrib-modules.md](docs/contrib-modules.md) — module authoring
115
- reference.
116
- - [docs/shared-modules.md](docs/shared-modules.md) — governance and index
117
- for shared modules: namespace ownership, review, deprecation, removal.
118
- - [docs/release-policy.md](docs/release-policy.md) — compatibility surfaces,
119
- current pre-1.0 status, and the release checklist.
120
- - [CHANGELOG.md](CHANGELOG.md) — user-facing changes.
121
-
122
- ## Running the tests
51
+ ```
52
+ valise.conf project settings
53
+ module.d/ <name>.meta: one metadata file per module
54
+ modules/ module bodies
55
+ layers/<p>/ modules vendored from another provider (optional)
56
+ bundles.tsv the root modules for each shell
57
+ variants.tsv named sets of modules to leave out (optional)
58
+ ```
123
59
 
124
- ```sh
125
- make check-fast # quick contributor lane; no optional shells required
126
- make check # available shell/runtime checks, CLI and end-to-end tests
60
+ A module's metadata names its dependencies, the shells and platforms it
61
+ supports, the functions it exports, and optionally the exports to publish
62
+ as commands on `PATH`:
63
+
64
+ ```text
65
+ name=hello
66
+ version=1.0.0
67
+ side_effect=read-only
68
+ requires=valise_path
69
+ optional=
70
+ shells=posix
71
+ platforms=any
72
+ exports=hello
73
+ commands=hello
74
+ tests=tests/hello.sh
75
+ load=modules/hello.sh
127
76
  ```
128
77
 
129
- Strict mode requires the parsers declared in
130
- `manifests/parser-support.tsv`, the host shells used by the end-to-end lanes
131
- (`sh`, `bash`, `zsh`, `ksh`, `dash`, and `fish`), and a BusyBox executor.
132
- Missing tools skip with a visible message in an ordinary local run;
133
- `CI=true` or `GITHUB_ACTIONS=true` makes them fail. Lima VM lanes are optional
134
- and run only when the relevant instance is configured.
135
- To require the standard runtime set locally, run:
78
+ ## Commands
79
+
80
+ | Command | Does |
81
+ |---|---|
82
+ | `new`, `module new\|list\|remove` | Scaffold a project and its modules. |
83
+ | `lint`, `test`, `run`, `env` | Check metadata and syntax, run module tests in a clean shell, run a script with modules loaded, print paths. |
84
+ | `build`, `apply`, `status` | Build a release, build and install it, compare the active release with the build. |
85
+ | `install`, `release list\|use\|rollback\|prune\|verify`, `uninstall` | Manage installed releases. |
86
+ | `init`, `doctor` | Print the startup snippet; diagnose the installation. |
87
+ | `compile`, `registry`, `explain-bundles`, `explain-variant`, `layer-lint`, `make-manifest` | Inspect and debug a build. |
88
+
89
+ `valise <command> --help` prints each command's contract.
90
+
91
+ ## Documentation
92
+
93
+ - [docs/projects.md](docs/projects.md): the project layout, bundles,
94
+ variants, compile, and build.
95
+ - [docs/contrib-modules.md](docs/contrib-modules.md): writing, linting, and
96
+ testing modules.
97
+ - [docs/module-abi.md](docs/module-abi.md): every metadata field and what
98
+ the plans do with it.
99
+ - [docs/installation.md](docs/installation.md): installing the CLI, startup
100
+ files, published commands, the release store, and uninstalling.
101
+ - [docs/architecture.md](docs/architecture.md): how the pieces fit and what
102
+ stays in your project.
103
+ - [docs/shared-modules.md](docs/shared-modules.md): layers other people
104
+ consume.
105
+ - [docs/release-policy.md](docs/release-policy.md): compatibility and the
106
+ release checklist.
107
+ - [examples/](examples/README.md): a sample project, a multi-shell test
108
+ project, and a sample provider layer.
109
+ - [CHANGELOG.md](CHANGELOG.md)
110
+
111
+ ## Development
136
112
 
137
113
  ```sh
138
- make VALISE_REQUIRE_TOOLING=1 check
114
+ make check-fast # quick lane; no optional shells required
115
+ make check # every available shell and runtime lane
139
116
  ```
140
117
 
141
- To intentionally use non-strict behavior in CI, or require tools on a local
142
- machine, the GNU Make command-line override takes precedence. The Makefile
143
- also includes an optional untracked `config.mk` before its defaults, so
144
- machine-local preferences can be set there; use `VALISE_REQUIRE_TOOLING ?= 1`
145
- for a default that a command-line assignment can still override. The release
146
- checklist is in [docs/release-policy.md](docs/release-policy.md).
118
+ `make check` uses the parsers in `manifests/parser-support.tsv`, the shells
119
+ `sh`, `bash`, `zsh`, `ksh`, `dash`, and `fish`, and BusyBox. A missing tool
120
+ is a visible skip locally and a failure when `CI=true` or
121
+ `GITHUB_ACTIONS=true`; `make VALISE_REQUIRE_TOOLING=1 check` requires them
122
+ locally. Machine-local settings go in an untracked `config.mk` (see
123
+ `config.mk.example`). Lima VM lanes run only when the instances exist.