valise-sh 0.1.0 → 0.2.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/CHANGELOG.md CHANGED
@@ -3,14 +3,175 @@
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
+ ## [Unreleased]
7
7
 
8
- - Made the editor-defaults test independent of host-installed editors by
8
+ ## [0.2.0] - 2026-10-31
9
+
10
+ ### Added
11
+
12
+ - `valise new`, `valise module new`, `valise module list`,
13
+ `valise module remove`, `valise build`, `valise apply`, and
14
+ `valise status`. A new project can be scaffolded, given a module, built,
15
+ installed, and checked without hand-writing a registry or a build script.
16
+ `valise module new` writes the module body and its declared export, and
17
+ wires the module into the seed aggregator. `valise apply` builds, installs,
18
+ writes the shell integration, and verifies the result; it is idempotent.
19
+ `valise status` reports the project, the active release, the integration
20
+ state, and whether the active release has drifted from the build output.
21
+ - `valise registry`, which generates `shell/release/module-registry.tsv` from
22
+ the project's metadata. `valise build` regenerates and validates the
23
+ registry, so a committed registry is a generated artifact rather than a
24
+ hand-maintained catalog. The row order is fixed: the primary modules of
25
+ `shell/module.d/` first, then the layer modules of each
26
+ `shell/layers/<provider>/module.d/`, each group in C-locale name order.
27
+ - `valise release list`, `valise release use`, `valise release prune`, and
28
+ `valise release verify`. They manage installed releases; source variants
29
+ remain the business of `valise build` and `valise apply`.
30
+ - Command-specific `--help` for `build`, `compile`, `install`, `init`, `new`,
31
+ `module`, `release`, `doctor`, and `status`, so each prints its own
32
+ contract rather than the global command list.
33
+ - Meta modules: metadata with `requires=` and no `load` is a
34
+ dependency-only aggregator and loads successfully without a body. `load`
35
+ and `requires` may be combined.
36
+ - `side_effect=local-state`, a source-safe side-effect class for modules
37
+ that write under the XDG state directory.
38
+ - `VALISE_RELOAD=1`, which unsets the declared exports of the requested
39
+ closure before loading so a profile can be re-sourced.
40
+ - The `sp_cap_*` capability helpers remain available at runtime after a
41
+ compiled plan or a loader load finishes, so a module can call them.
42
+ - `examples/profile-project/`, a complete consuming project with two
43
+ modules, a provider layer, two variants, and a bundle, and a README that
44
+ walks from clone to a loaded profile.
45
+ - A consumer end-to-end test target that builds the reference consumer
46
+ profile from a temporary copy, runs the consumer's own metadata, shells,
47
+ release, and features suites, installs and verifies the release, and
48
+ proves a second build is byte-identical. The original tree and the live
49
+ profile are never touched.
50
+
51
+ ### Changed
52
+
53
+ - `valise build` builds a complete release tree to `build/release` by
54
+ default, reads the variant and bundle defaults from
55
+ `shell/release/project.conf`, and requires an explicit output directory to
56
+ be empty or absent. A failed build preserves the previous build output.
57
+ - `valise build` produces no Fish surface: no Fish entrypoint, generated
58
+ plan, or native loader. A built release therefore loads nothing in Fish;
59
+ the Fish hook verifies the active release and stays silent. Only an engine
60
+ release with `entrypoints/fish/profile.fish` uses the native Fish loader.
61
+ - `valise install` writes shell integration by default. It always writes
62
+ `~/.profile`; it adds `~/.bash_profile` or `~/.bash_login` when one of
63
+ those exists, because bash reads that file instead of `~/.profile`; it
64
+ writes `~/.bashrc` when bash is the current shell or that file exists; it
65
+ writes `~/.zprofile` when zsh is on `PATH` or that file exists, because zsh
66
+ reads nothing else at login; and it writes `~/.zshrc` when zsh is the
67
+ current shell or that file exists. Existing files are kept current rather
68
+ than replaced. A symlinked startup file is reported and left untouched.
69
+ `--no-integration` installs without writing any startup file. Install also
70
+ writes the Fish `conf.d` hook when it finds Fish in a trusted location;
71
+ `valise apply` runs the same install path.
72
+ - The startup integration is a managed block between `# BEGIN valise managed
73
+ block` and `# END valise managed block`. Repeat installs are idempotent and
74
+ existing profile content is preserved. An unpaired marker is refused and
75
+ stops the install. Install prints each path it wrote and how to remove the
76
+ block.
77
+ - `valise init` takes `posix` or `fish`; any other argument is rejected.
78
+ It is a one-time snippet generator, not something to evaluate at every
79
+ prompt.
80
+ - A project's default variant is `default`. An unknown variant is an error
81
+ in every command that accepts one.
82
+ - Compiled plans and the loader now agree on required dependency closure.
83
+ Platform skips are recorded during discovery and reported once the full
84
+ required closure is known; strict mode fails when a required module is
85
+ blocked during ordering; permissive mode continues and returns zero. Fish
86
+ reports a platform-skipped optional dependency silently.
87
+ - Compiled plans reach parity with the loader: they gate modules by
88
+ `platforms=` at runtime, pre-check and post-check declared exports, report
89
+ each failing module by name, continue past a failure in permissive mode
90
+ while returning zero, stop in strict mode, and support meta modules with no
91
+ source line.
92
+ - The loader saves the logical `$PWD` and `$OLDPWD` before sourcing each
93
+ module and restores them afterwards, so a module that changes directory
94
+ during its load leaves the caller's logical directory as it was.
95
+ - The loader's export pre-check accepts a name that currently resolves to an
96
+ external command, which is how a deliberate wrapper is exported. It still
97
+ rejects a name already defined as a function, builtin, or alias.
98
+ - The loader resolves each metadata record with shell builtins instead of
99
+ repeated `sed`, `grep`, and `cat` processes, so the number of external
100
+ processes a load spawns does not grow with the number of modules.
101
+ - `valise compile` and `valise build` find the packaged
102
+ `lib/capabilities.sh` relative to the CLI, so a symlinked or relocated
103
+ install works. A compiled plan rejects a module that has neither a load
104
+ path nor dependencies, and names the module.
105
+ - Layer handling accepts any `shell/layers/<provider>` path rather than only
106
+ the `contrib` provider, and produces a deterministic manifest.
107
+ - `valise doctor` reports a missing active release as a failure and reads the
108
+ startup files the running shell actually reads. `--pre-install` skips the
109
+ release and integration checks; `--no-integration` checks only the
110
+ environment and the active release.
111
+ - `valise explain-bundles` and `valise layer-lint` are implemented in POSIX sh
112
+ and awk. `layer-lint` validates a provider layer's structure, `contrib_`
113
+ name prefix, version, load path, symlink freedom, file modes, and
114
+ dependency containment.
115
+ - The unused Python consumer-release helpers, wrapper lifecycle hooks, and
116
+ the submodule build prelude override are gone. No command requires
117
+ Python 3.
118
+
119
+ ### Removed
120
+
121
+ - `load_hook` and the compatibility bridge in the POSIX loader, the Fish
122
+ loader, and compiled plans. A module declares `load`, `requires`, or both.
123
+ Metadata naming `load_hook` fails with
124
+ `metadata field load_hook is not supported; use load`.
125
+ - `variant list`, `variant use`, and `verify-active`; installed releases are
126
+ managed by `valise release list|use|prune|verify`. `variant` now refers
127
+ only to build variants.
128
+ - The per-shell `valise init` arguments `bash`, `zsh`, `ksh`, and `sh`; use
129
+ `valise init posix` or `valise init fish`.
130
+
131
+ ### Fixed
132
+
133
+ - `valise` with no arguments: under `set -e`, a zero-argument `shift` could
134
+ terminate the CLI before it displayed its help text.
135
+ - Fish activation: functions loaded from package paths such as Homebrew are
136
+ authenticated and preserved through the load instead of being rejected or
137
+ lost.
138
+ - The primary-root symlink diagnostic was printed on stdout while every
139
+ other loader diagnostic went to stderr.
140
+ - `valise compile` reported that a Fish plan requires a Python compiler and
141
+ named a file that does not exist in this repository. Fish plans are not
142
+ generated; a Fish integration uses the native Fish loader.
143
+ - Applying to a home directory with a trailing slash could skip the zsh
144
+ startup file, because the target was compared against the raw `HOME`
145
+ string rather than a normalized path.
146
+ - A built release's Fish hook printed an error on every Fish start. It now
147
+ verifies the release quietly and returns when there is no Fish surface to
148
+ load.
149
+ - A module that makes `PWD`, `OLDPWD`, or `IFS` readonly no longer aborts the
150
+ loader mid-transaction. The module fails as caller-state modification, and
151
+ the loader's cleanup still runs.
152
+ - Loader temp-directory cleanup resolves `rm` from a fixed absolute path, so a
153
+ module that changes `PATH` cannot leave its metadata scratch directory
154
+ behind.
155
+ - Compiled plans remove a failed module's declared exports, like the loader.
156
+ A meta module that declares exports is rejected at compile time, and an
157
+ optional-only cycle inside a required graph is quarantined instead of
158
+ failing the whole plan.
159
+ - The compiler rejects an unsafe layer directory name and a reserved-word
160
+ export instead of emitting an unparseable or misleading plan.
161
+ - `release list`, `release use`, `release prune`, `doctor`, and `status`
162
+ identify the active release by its canonical directory name, so a trailing
163
+ slash or an alias symlink cannot hide it from the list or make prune delete
164
+ it.
165
+ - `module remove` keeps a body that another module still loads, and checks
166
+ every directory it will rewrite before changing anything. `module new`
167
+ checks the seed metadata before writing and rejects reserved-word exports.
168
+ - `make-manifest` rejects a directory or symlink as its output, and a
169
+ normalized in-tree output path is excluded from its own manifest.
170
+ - `valise install` holds its lock through the Fish and POSIX integration
171
+ writes, so concurrent installs cannot interleave them.
172
+ - The editor-defaults test is independent of host-installed editors by
9
173
  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.
174
+
175
+ [Unreleased]: https://github.com/joeycumines/valise-sh/compare/v0.2.0...HEAD
176
+ [0.2.0]: https://github.com/joeycumines/valise-sh/compare/v0.1.0...v0.2.0
177
+ [0.1.0]: https://github.com/joeycumines/valise-sh/releases/tag/v0.1.0
package/README.md CHANGED
@@ -42,29 +42,44 @@ export PATH="$PWD/bin:$PATH"
42
42
  export XDG_CONFIG_HOME="$_demo_state/config"
43
43
  export XDG_STATE_HOME="$_demo_state/state"
44
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"
45
+ # Build, install, and apply the sample profile project.
46
+ (cd examples/profile-project && valise apply)
47
+
48
+ # Run the greeting function exposed by the active profile.
49
+ bash -l -c 'greet'
59
50
  )
60
51
  ```
61
52
 
62
- The end-to-end example documents Fish startup, shared-provider composition,
63
- and other modules in
53
+ For the multi-shell test fixture and verification scenarios, see
64
54
  [examples/e2e/README.md](examples/e2e/README.md). For the smallest possible
65
55
  loader example without installation, use `examples/local/` as described in
66
56
  [examples/README.md](examples/README.md).
67
57
 
58
+ ## Build your own profile
59
+
60
+ The sample above loads modules from an example profile. To build your own,
61
+ start from the project scaffold:
62
+
63
+ ```sh
64
+ export PATH="$PWD/bin:$PATH"
65
+ valise new my-profile
66
+ cd my-profile
67
+ ```
68
+
69
+ That writes a project with a metadata directory, a module body, bundle
70
+ roots, variants, and `project.conf`. Add modules with `valise module new`,
71
+ then build and install:
72
+
73
+ ```sh
74
+ valise build # build a release under build/release/
75
+ valise apply # install it and write the managed shell integration
76
+ ```
77
+
78
+ `examples/profile-project/` is a worked version of this: it has two modules,
79
+ a provider layer, two variants, and a bundle. Its
80
+ [README](examples/profile-project/README.md) walks through each step and
81
+ explains what the generated release contains.
82
+
68
83
  ## Repository layout
69
84
 
70
85
  - `entrypoints/profile.sh` — the portable entrypoint. It is sourced, not
@@ -79,14 +94,16 @@ loader example without installation, use `examples/local/` as described in
79
94
  instead of platform-specific commands.
80
95
  - `lib/fish/` — native Fish loader and metadata reader. The Fish path never
81
96
  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).
97
+ - `bin/valise` — the CLI: init, install, release, doctor, make-manifest,
98
+ registry, new, module, build, apply, status, compile, explain-variant,
99
+ explain-bundles, layer-lint, version.
100
+ `init`, `install`, `release`, `doctor`, `make-manifest`, `new`, and
101
+ `version` run from a bare checkout. `registry`, `module`, `build`, `apply`,
102
+ `status`, `compile`, `explain-variant`,
103
+ `explain-bundles`, and `layer-lint` require the consuming project's source
104
+ tree (see [docs/variants.md](docs/variants.md) and
105
+ [docs/contrib-modules.md](docs/contrib-modules.md)). No command requires
106
+ Python 3.
90
107
  - `module.d/` and `modules/platform/` — generic platform modules
91
108
  (`platform_posix`, `platform_macos`, `platform_linux`, `platform_bsd`).
92
109
  - `manifests/parser-support.tsv` — declares the shell parsers used to check
@@ -95,6 +112,10 @@ loader example without installation, use `examples/local/` as described in
95
112
  fresh temporary HOME, controlled TMPDIR and PATH, cleared startup hooks.
96
113
  - `examples/local/` — a minimal runnable module demonstrating the metadata
97
114
  contract.
115
+ - `examples/profile-project/` — a complete consuming project: metadata,
116
+ bodies, a provider layer, bundles, variants, and project defaults. Its
117
+ [README](examples/profile-project/README.md) walks from clone to a loaded
118
+ profile.
98
119
 
99
120
  Product-specific modules, adapters, release packaging, and installer
100
121
  transactions belong in the consuming project, not here. See
@@ -121,6 +142,9 @@ transactions belong in the consuming project, not here. See
121
142
 
122
143
  ## Running the tests
123
144
 
145
+ The Makefile and the test suite live in the source repository. The npm
146
+ package ships the runtime, the docs, and the examples, not the Makefile.
147
+
124
148
  ```sh
125
149
  make check-fast # quick contributor lane; no optional shells required
126
150
  make check # available shell/runtime checks, CLI and end-to-end tests