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.
- package/CHANGELOG.md +320 -9
- package/README.md +104 -127
- package/bin/valise +67 -3266
- package/docs/architecture.md +76 -51
- package/docs/contrib-modules.md +180 -173
- package/docs/installation.md +210 -69
- package/docs/module-abi.md +211 -148
- package/docs/projects.md +249 -0
- package/docs/release-policy.md +90 -11
- package/docs/shared-modules.md +149 -26
- package/examples/README.md +20 -8
- package/examples/e2e/README.md +61 -68
- package/examples/e2e/bundles.tsv +6 -0
- package/examples/e2e/module.d/e2e_aliases.meta +1 -2
- package/examples/e2e/module.d/e2e_completions_bash.meta +0 -1
- package/examples/e2e/module.d/e2e_completions_zsh.meta +0 -1
- package/examples/e2e/module.d/e2e_env_setup.meta +2 -3
- package/examples/e2e/module.d/e2e_fish_features.meta +0 -1
- package/examples/e2e/module.d/e2e_generated_config.meta +1 -2
- package/examples/e2e/module.d/e2e_ksh_features.meta +0 -1
- package/examples/e2e/module.d/e2e_layer_probe.meta +1 -2
- package/examples/e2e/module.d/e2e_optional_network.meta +1 -2
- package/examples/e2e/module.d/e2e_platform_tools.meta +8 -0
- package/examples/e2e/module.d/e2e_platform_tools_bsd.meta +2 -3
- package/examples/e2e/module.d/e2e_platform_tools_darwin.meta +2 -3
- package/examples/e2e/module.d/e2e_platform_tools_linux.meta +2 -3
- package/examples/e2e/valise.conf +4 -0
- package/examples/e2e/variants.tsv +3 -0
- package/examples/local/module.d/hello_valise.meta +1 -2
- package/examples/profile-project/README.md +151 -0
- package/examples/profile-project/bundles.tsv +8 -0
- package/examples/profile-project/layers/demo/module.d/contrib_demo_greeting.meta +9 -0
- package/examples/profile-project/layers/demo/modules/shared/contrib_demo_greeting.sh +4 -0
- package/examples/profile-project/module.d/greeting.meta +9 -0
- package/examples/profile-project/module.d/profile_main.meta +8 -0
- package/examples/profile-project/modules/greeting.sh +5 -0
- package/examples/profile-project/valise.conf +4 -0
- package/examples/profile-project/variants.tsv +3 -0
- package/examples/shared/README.md +13 -34
- package/examples/shared/module.d/contrib_shared_paths.meta +2 -3
- package/examples/shared/module.d/contrib_shared_umask.meta +1 -2
- package/examples/shared/modules/shared/contrib_shared_paths.sh +2 -2
- package/lib/capabilities.sh +40 -13
- package/lib/cli/commands.sh +278 -0
- package/lib/cli/common.sh +326 -0
- package/lib/cli/devtools.sh +539 -0
- package/lib/cli/fish-hook.sh +986 -0
- package/lib/cli/integration.sh +337 -0
- package/lib/cli/lifecycle.sh +578 -0
- package/lib/cli/project.sh +1807 -0
- package/lib/cli/store.sh +1023 -0
- package/lib/cli/usage.sh +308 -0
- package/lib/compiler.awk +352 -24
- package/lib/fish/loader.fish +163 -28
- package/lib/fish/metadata.fish +62 -19
- package/lib/fish/profile.fish +170 -0
- package/lib/loader.sh +736 -158
- package/lib/metadata.sh +137 -55
- package/manifests/parser-support.tsv +2 -2
- package/module.d/valise_editor.meta +9 -0
- package/module.d/valise_history.meta +9 -0
- package/module.d/valise_pager.meta +9 -0
- package/module.d/valise_path.meta +9 -0
- package/module.d/{platform_bsd.meta → valise_platform_bsd.meta} +3 -4
- package/module.d/{platform_linux.meta → valise_platform_linux.meta} +3 -4
- package/module.d/{platform_macos.meta → valise_platform_macos.meta} +3 -4
- package/module.d/{platform_posix.meta → valise_platform_posix.meta} +2 -3
- package/module.d/valise_xdg.meta +9 -0
- package/modules/system/{contrib_editor.sh → valise_editor.sh} +2 -2
- package/modules/system/{contrib_history.sh → valise_history.sh} +2 -2
- package/modules/system/{contrib_pager.sh → valise_pager.sh} +2 -2
- package/modules/system/{contrib_path.sh → valise_path.sh} +3 -3
- package/modules/system/{contrib_xdg.sh → valise_xdg.sh} +2 -2
- package/package.json +91 -9
- package/docs/variants.md +0 -63
- package/entrypoints/.gitkeep +0 -0
- package/entrypoints/fish/profile.fish +0 -99
- package/examples/e2e/build-manifest.sh +0 -250
- package/examples/local/.gitkeep +0 -0
- package/lib/.gitkeep +0 -0
- package/manifests/.gitkeep +0 -0
- package/module.d/.gitkeep +0 -0
- package/module.d/contrib_editor.meta +0 -10
- package/module.d/contrib_history.meta +0 -10
- package/module.d/contrib_pager.meta +0 -10
- package/module.d/contrib_path.meta +0 -10
- package/module.d/contrib_xdg.meta +0 -10
- package/modules/.gitkeep +0 -0
package/docs/architecture.md
CHANGED
|
@@ -1,77 +1,102 @@
|
|
|
1
1
|
# Architecture and scope
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
valise-sh is a shell-profile engine. It reads module metadata as data,
|
|
4
|
+
compiles a static startup plan for each shell, packages the plans and module
|
|
5
|
+
bodies into a sealed release, installs the release, and verifies it. Your
|
|
6
|
+
profile lives in a separate valise project; the engine supplies the tools
|
|
7
|
+
and a few generic modules.
|
|
6
8
|
|
|
7
9
|
## Components
|
|
8
10
|
|
|
9
11
|
| Path | Responsibility |
|
|
10
12
|
|---|---|
|
|
11
|
-
| `
|
|
12
|
-
| `lib/
|
|
13
|
-
| `lib/
|
|
14
|
-
| `lib/
|
|
15
|
-
| `lib/
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
18
|
-
| `
|
|
13
|
+
| `bin/valise` | The CLI. It sources the parts under `lib/cli/` and dispatches commands. |
|
|
14
|
+
| `lib/cli/` | CLI parts: `project.sh` (project config, registry, compile, build), `store.sh` (release store, install, `release`), `integration.sh` (startup-file blocks, `doctor`), `fish-hook.sh`, `commands.sh` (published commands), `lifecycle.sh` (rollback, transactions, uninstall, dry runs), `devtools.sh` (`lint`, `test`, `run`, `env`), `usage.sh`, `common.sh`. |
|
|
15
|
+
| `lib/metadata.sh` | Parses and validates `.meta` files as data. |
|
|
16
|
+
| `lib/compiler.awk` | Turns the resolved module graph into a static POSIX plan. |
|
|
17
|
+
| `lib/capabilities.sh` | The `sp_cap_*` helpers. A release carries it as `runtime/capabilities.sh`. |
|
|
18
|
+
| `lib/fish/` | The native Fish metadata reader, loader, and adapter. A release carries them under `runtime/fish/`. |
|
|
19
|
+
| `lib/loader.sh`, `entrypoints/profile.sh` | The development loader, which resolves and sources modules at load time from a checkout. Installed profiles do not use it. |
|
|
20
|
+
| `module.d/`, `modules/` | The engine modules (`valise_*`). |
|
|
21
|
+
| `manifests/parser-support.tsv` | The parsers each shipped file class must pass. |
|
|
19
22
|
| `tests/`, `examples/` | Quarantined tests and runnable examples. |
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
[
|
|
23
|
-
[
|
|
24
|
+
[projects.md](projects.md) describes a project and the compile and build
|
|
25
|
+
contract, [module-abi.md](module-abi.md) the metadata,
|
|
26
|
+
[installation.md](installation.md) installation and the release store, and
|
|
27
|
+
[contrib-modules.md](contrib-modules.md) writing and testing modules.
|
|
24
28
|
|
|
25
|
-
##
|
|
29
|
+
## From metadata to a running shell
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
|
|
31
|
+
1. `valise build` reads every `.meta` file in the project (`module.d/`, each
|
|
32
|
+
`layers/<provider>/module.d/`) and the engine's own `module.d/`, as data,
|
|
33
|
+
into an internal table. `valise registry` prints that table; nothing reads
|
|
34
|
+
a saved copy.
|
|
35
|
+
2. For each POSIX shell in the bundle, the compiler resolves the roots'
|
|
36
|
+
closure, applies the variant's exclusions, orders the modules by
|
|
37
|
+
dependency, and writes `plans/<shell>.sh`. A module that publishes
|
|
38
|
+
commands gets its own plan under `plans/commands/`.
|
|
39
|
+
3. The build copies the modules some shell loads, the capability prelude,
|
|
40
|
+
and the Fish runtime when the bundle has Fish roots, then writes
|
|
41
|
+
`manifest.tsv`. The release id is the SHA-256 of the manifest.
|
|
42
|
+
4. `valise install` copies the tree into the release store, verifies it,
|
|
43
|
+
switches the active link, writes the startup-file blocks and the Fish
|
|
44
|
+
hook, and publishes command stubs. If any step fails, install restores
|
|
45
|
+
the previous state.
|
|
46
|
+
5. At startup, the POSIX block picks the plan for the running shell class
|
|
47
|
+
(`bash`, `zsh`, `ksh`, or `sh`) and sources it. The plan contains the
|
|
48
|
+
load order; it reads no metadata. Fish verifies the release, then runs
|
|
49
|
+
the native Fish loader over the release's Fish metadata.
|
|
50
|
+
|
|
51
|
+
There is one release shape. The plan is generated ahead of time so that
|
|
52
|
+
startup does no resolution work and so that you can read exactly what will
|
|
53
|
+
run.
|
|
54
|
+
|
|
55
|
+
## The development loader
|
|
56
|
+
|
|
57
|
+
`entrypoints/profile.sh` and `lib/loader.sh` load modules straight from a
|
|
58
|
+
checkout, resolving dependencies when they are sourced. They are for working
|
|
59
|
+
on the engine's own modules and for the loader's tests; installed profiles
|
|
60
|
+
and `valise run` use compiled plans. The loader honors the same metadata,
|
|
61
|
+
including optional dependencies, platform gating, export checks, and failure
|
|
62
|
+
cleanup:
|
|
29
63
|
|
|
30
64
|
```sh
|
|
31
65
|
VALISE_ROOT=/path/to/valise-sh
|
|
32
|
-
VALISE_MODULE_ROOT=/path/to/
|
|
66
|
+
VALISE_MODULE_ROOT=/path/to/modules
|
|
33
67
|
VALISE_MODULES='module_a module_b'
|
|
34
68
|
VALISE_MODE=strict
|
|
35
69
|
. "$VALISE_ROOT/entrypoints/profile.sh"
|
|
36
70
|
```
|
|
37
71
|
|
|
38
|
-
`VALISE_MODULES` unset means
|
|
39
|
-
|
|
40
|
-
its startup integration. Searching upward from the current directory when
|
|
41
|
-
`VALISE_ROOT` is omitted is a checkout convenience, not an installed-profile
|
|
42
|
-
configuration strategy.
|
|
43
|
-
|
|
44
|
-
Metadata is validated before module bodies run. Required dependencies load
|
|
45
|
-
first; present optional dependencies are also considered. Cycles and invalid
|
|
46
|
-
or conflicting records are rejected. Strict mode returns failure for a
|
|
47
|
-
requested module's failure; permissive mode reports the affected module and
|
|
48
|
-
continues with unrelated requests. The loader has no unload operation.
|
|
49
|
-
|
|
50
|
-
The Fish adapter uses the native Fish loader and native `.fish` bodies; it
|
|
51
|
-
never sources POSIX bodies. Fish startup integration verifies the installed
|
|
52
|
-
release before loading it. See [installation.md](installation.md) for the
|
|
53
|
-
specific integrity boundary.
|
|
72
|
+
`VALISE_MODULES` unset means nothing is loaded. Its search path is described
|
|
73
|
+
in [module-abi.md](module-abi.md#the-development-loaders-search-path).
|
|
54
74
|
|
|
55
75
|
## Scope boundaries
|
|
56
76
|
|
|
57
|
-
The engine does not define
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
The engine does not define your modules, your bundles, your workstation
|
|
78
|
+
policy, or wrappers around other tools. Those belong in your project.
|
|
79
|
+
`valise new` writes an empty project so you do not have to invent the
|
|
80
|
+
layout.
|
|
81
|
+
|
|
82
|
+
Project commands (`module`, `build`, `apply`, `status`, `lint`, `test`,
|
|
83
|
+
`run`, `compile`, `registry`, `explain-variant`, `explain-bundles`) run
|
|
84
|
+
inside a directory that holds `valise.conf`, found by walking up from the
|
|
85
|
+
working directory. Release commands (`install`, `release`, `uninstall`,
|
|
86
|
+
`init`, `doctor`, `make-manifest`) and `new`, `env`, `layer-lint`, and
|
|
87
|
+
`version` run anywhere.
|
|
63
88
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
releases; its manifest check detects changes to bytes after the manifest is
|
|
69
|
-
created.
|
|
89
|
+
Shared modules are vendored copies. You review a provider's changes, copy
|
|
90
|
+
them into `layers/<provider>/`, and rebuild. The engine does not fetch
|
|
91
|
+
updates, authenticate authors, or sign releases; the manifest detects bytes
|
|
92
|
+
that changed after the release was built.
|
|
70
93
|
|
|
71
94
|
## Verification
|
|
72
95
|
|
|
73
|
-
Run `make check-fast` while developing and `make check` for the
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
96
|
+
Run `make check-fast` while developing and `make check` for the full suite.
|
|
97
|
+
Extended runtime lanes run when their tools are installed; missing tools are
|
|
98
|
+
visible skips locally and failures in CI. Set `VALISE_REQUIRE_TOOLING=1` to
|
|
99
|
+
require them on a developer machine.
|
|
100
|
+
|
|
101
|
+
The Makefile and the tests live in the source repository. The npm package
|
|
102
|
+
ships the runtime, the docs, and the examples.
|
package/docs/contrib-modules.md
CHANGED
|
@@ -1,193 +1,200 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Writing modules
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A module is a metadata file and, usually, a shell body. This guide covers
|
|
4
|
+
writing one in a valise project, checking and testing it, and deciding
|
|
5
|
+
whether it belongs in a shareable layer. [module-abi.md](module-abi.md) is
|
|
6
|
+
the full metadata contract; [projects.md](projects.md) covers bundles,
|
|
7
|
+
variants, and builds.
|
|
6
8
|
|
|
7
|
-
##
|
|
8
|
-
|
|
9
|
-
A file-backed module has a metadata file and a body. POSIX bodies usually
|
|
10
|
-
end in `.sh`; Fish bodies end in `.fish`:
|
|
9
|
+
## Create a module
|
|
11
10
|
|
|
11
|
+
```sh
|
|
12
|
+
valise new demo
|
|
13
|
+
cd demo
|
|
14
|
+
valise module new hello --exports=hello
|
|
12
15
|
```
|
|
13
|
-
|
|
14
|
-
modules
|
|
16
|
+
|
|
17
|
+
`valise module new` writes `module.d/hello.meta` and `modules/hello.sh` and
|
|
18
|
+
adds `hello` to the `requires=` of the bundle's POSIX root, so a build
|
|
19
|
+
loads it. When the default bundle has several POSIX roots, name the ones to
|
|
20
|
+
change with `--into=<module>[,<module>...]`, or pass `--no-wire` to wire
|
|
21
|
+
the module yourself. Edit the body, then:
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
valise lint # check metadata, bundles, variants, and syntax
|
|
25
|
+
valise apply # build, install, write the integration, verify
|
|
15
26
|
```
|
|
16
27
|
|
|
17
|
-
|
|
18
|
-
one `key=value` field per line:
|
|
28
|
+
A module's metadata looks like this:
|
|
19
29
|
|
|
20
30
|
```text
|
|
21
|
-
name=
|
|
22
|
-
|
|
31
|
+
name=hello
|
|
32
|
+
version=1.0.0
|
|
33
|
+
side_effect=read-only
|
|
34
|
+
requires=valise_path
|
|
23
35
|
optional=
|
|
24
|
-
shells=
|
|
36
|
+
shells=posix
|
|
25
37
|
platforms=any
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
load=modules/system/example_tool.sh
|
|
38
|
+
exports=hello
|
|
39
|
+
commands=hello
|
|
40
|
+
tests=tests/hello.sh
|
|
41
|
+
load=modules/hello.sh
|
|
31
42
|
```
|
|
32
43
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
- `
|
|
67
|
-
- `
|
|
68
|
-
- `
|
|
69
|
-
- `
|
|
70
|
-
- `
|
|
71
|
-
- `
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
[shared-modules.md](shared-modules.md).
|
|
82
|
-
|
|
83
|
-
##
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
Strict mode fails closed on any problem: missing dependency, a declared
|
|
108
|
-
export missing after the body runs, an export that already exists,
|
|
109
|
-
reserved-utility shadowing, shell/platform mismatch. The loader checks
|
|
110
|
-
what `exports=` declares; it does not reject a body that defines extra
|
|
111
|
-
private functions, so list everything you consider public. Under zsh
|
|
112
|
-
the entrypoint handles word-splitting itself; source it the same way.
|
|
113
|
-
4. Check parser coverage. `manifests/parser-support.tsv` declares which
|
|
114
|
-
POSIX shells must parse each file class. Place portable bodies under a
|
|
115
|
-
path pattern the table already covers (for example `modules/system/*.sh`)
|
|
116
|
-
or add a row for your pattern, then run the gate:
|
|
117
|
-
|
|
118
|
-
```sh
|
|
119
|
-
make test-parser-support
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
5. Publish a release containing your module tree and prove the whole
|
|
123
|
-
path end to end (make-manifest, install, verify-active):
|
|
124
|
-
|
|
125
|
-
```sh
|
|
126
|
-
valise make-manifest ./mytree
|
|
127
|
-
cd mytree && valise install && valise verify-active
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
See [installation.md](installation.md) for release integrity and
|
|
131
|
-
provenance.
|
|
132
|
-
|
|
133
|
-
## Shipped modules
|
|
134
|
-
|
|
135
|
-
The reusable `contrib_` modules are listed in
|
|
136
|
-
[shared-modules.md](shared-modules.md). For example, load PATH and editor
|
|
137
|
-
defaults from a checkout with:
|
|
44
|
+
Choose `shells` by what the body needs:
|
|
45
|
+
|
|
46
|
+
- `posix` for a body that runs in every POSIX shell: dash, BusyBox ash,
|
|
47
|
+
bash, zsh, and ksh.
|
|
48
|
+
- `sh` for a body that runs only in the generic class (dash, ash, and other
|
|
49
|
+
plain `sh` implementations), listed together with the explicit shells it
|
|
50
|
+
also supports: `shells=sh bash ksh` loads in dash, bash, and ksh, and not
|
|
51
|
+
in zsh.
|
|
52
|
+
- `bash`, `zsh`, or `ksh` for a body that uses that shell's features.
|
|
53
|
+
- `fish` for a native Fish body (`.fish`). A Fish module never runs a POSIX
|
|
54
|
+
body, so a feature that needs both shells is two modules.
|
|
55
|
+
|
|
56
|
+
Zsh runs module functions with the user's options. A body that relies on
|
|
57
|
+
word splitting of unquoted expansions behaves differently in zsh, where it
|
|
58
|
+
is off by default; quote expansions, split explicitly, or leave zsh out of
|
|
59
|
+
`shells`.
|
|
60
|
+
|
|
61
|
+
`side_effect` says what the body does when sourced. Startup modules use
|
|
62
|
+
`read-only`, `path-env`, `local-state` (writes under the XDG state
|
|
63
|
+
directory), `local-secret` (reads secrets that live on this machine), or
|
|
64
|
+
`work-only`. A build refuses `generated-file`, `update-network`, and
|
|
65
|
+
`install-time` in a startup closure.
|
|
66
|
+
|
|
67
|
+
List every public function in `exports`. The plan refuses to load a module
|
|
68
|
+
whose export already exists, checks that each export is defined after the
|
|
69
|
+
body runs, and removes them again when the module fails. List exports that
|
|
70
|
+
should also run from scripts and other programs in `commands`; see
|
|
71
|
+
[module-abi.md](module-abi.md#published-commands).
|
|
72
|
+
|
|
73
|
+
Use the `sp_cap_*` helpers from the capability prelude instead of
|
|
74
|
+
platform-specific commands. They stay defined after the profile loads, so
|
|
75
|
+
your functions can call them later:
|
|
76
|
+
|
|
77
|
+
- `sp_cap_os`, `sp_cap_cpu_count`, `sp_cap_homebrew_prefix`
|
|
78
|
+
- `sp_cap_command_exists <name>`, `sp_cap_command_path <name>`
|
|
79
|
+
- `sp_cap_canonical_path <path>`, `sp_cap_path_under <base> <target>`
|
|
80
|
+
- `sp_cap_file_mtime <path>`
|
|
81
|
+
- `sp_cap_secure_local_bin_check <home> [reject|allow-owned-symlinks]`
|
|
82
|
+
- `sp_cap_mktemp_dir [prefix] [parent]`, `sp_cap_mktemp_file <parent> <prefix>`
|
|
83
|
+
|
|
84
|
+
`sp_cap_secure_local_bin_check` defaults to `reject`, which refuses every
|
|
85
|
+
symlink in `~/.local/bin`. `allow-owned-symlinks` accepts a symlink whose
|
|
86
|
+
link entry is owned by the current user (the pipx and uv shape) while still
|
|
87
|
+
rejecting unowned entries and group- or other-writable regular files.
|
|
88
|
+
|
|
89
|
+
The engine also ships modules a project can require: `valise_path`
|
|
90
|
+
(`valise_path_prepend` and `valise_path_append`), `valise_editor`,
|
|
91
|
+
`valise_history`, `valise_pager`, `valise_xdg`, and the `valise_platform_*`
|
|
92
|
+
modules. [shared-modules.md](shared-modules.md) lists them.
|
|
93
|
+
|
|
94
|
+
## Checking and testing modules
|
|
95
|
+
|
|
96
|
+
`valise lint [--require-parsers] [module...]` checks the project and reports
|
|
97
|
+
every problem it finds:
|
|
98
|
+
|
|
99
|
+
- each metadata record, its name, its prefix, and the files it names;
|
|
100
|
+
- each layer: `contrib_` names, `version=`, safe file modes, and
|
|
101
|
+
dependencies that stay inside the layer or the engine;
|
|
102
|
+
- the `lint_layer_forbid` pattern from `valise.conf`, if set;
|
|
103
|
+
- every bundle compiled for every variant and shell, which catches unknown
|
|
104
|
+
roots and exclusions, missing dependencies, and exclusions that break a
|
|
105
|
+
remaining module;
|
|
106
|
+
- the syntax of each body and test, with the parser of every shell the module
|
|
107
|
+
declares (`dash` or a plain `sh`, `bash -n`, `zsh -n`, `ksh -n`, and
|
|
108
|
+
`fish --no-execute`). A missing parser is reported as a skip, or as a
|
|
109
|
+
failure with `--require-parsers`.
|
|
110
|
+
|
|
111
|
+
`valise test [--shell=<class>] [--require-shells] [module...]` runs each
|
|
112
|
+
module's `tests=` scripts. For every shell class the module declares whose
|
|
113
|
+
interpreter is installed, it builds a release with the module as its only
|
|
114
|
+
root, starts the shell with `env -i` and a fresh `HOME`, `TMPDIR`, and XDG
|
|
115
|
+
tree, loads the module in strict mode, and runs the test. `PATH` is
|
|
116
|
+
`${VALISE_TEST_PATH:-/usr/bin:/bin:/usr/sbin:/sbin}` plus the interpreter's
|
|
117
|
+
directory. A test passes when it exits 0:
|
|
138
118
|
|
|
139
119
|
```sh
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
VALISE_MODE=strict \
|
|
143
|
-
. ./entrypoints/profile.sh
|
|
120
|
+
# tests/hello.sh
|
|
121
|
+
[ "$(hello world)" = 'hello world' ] || { echo "got: $(hello world)"; exit 1; }
|
|
144
122
|
```
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
2. Source the module in a clean environment — the body prints nothing when
|
|
154
|
-
its prerequisites are absent and defines every name in `exports=`:
|
|
155
|
-
|
|
156
|
-
```sh
|
|
157
|
-
env -i HOME="$PWD/.fresh-home" PATH=/usr/bin:/bin \
|
|
158
|
-
VALISE_MODULE_ROOT="$PWD" VALISE_MODULES='<name>' VALISE_MODE=strict \
|
|
159
|
-
/bin/sh -c '. ./entrypoints/profile.sh'
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
(Create `./.fresh-home` first or point `HOME` at any empty directory;
|
|
163
|
-
never source against your live profile.)
|
|
164
|
-
3. Source it twice in one strict session or call the entrypoint repeatedly:
|
|
165
|
-
the second load must fail closed with `declared export already exists`
|
|
166
|
-
(the loader forbids double-definition), and calling an idempotent
|
|
167
|
-
function twice must leave the same state.
|
|
168
|
-
4. The body performs no writes at source time: `side_effect` is
|
|
169
|
-
`read-only` or `path-env`, and `generated-file`/`update-network`/
|
|
170
|
-
`install-time` bodies are rejected during ordinary source.
|
|
171
|
-
5. Every public function appears in `exports=` — the loader verifies each
|
|
172
|
-
declared export exists after the body runs, and cleans them up on
|
|
173
|
-
failure.
|
|
174
|
-
6. Tests run in quarantined homes: this repository's suites already use
|
|
175
|
-
fresh temporary `HOME`, controlled `TMPDIR` and `PATH`, and cleared
|
|
176
|
-
startup hooks (`make check-fast` to confirm the engine gate). Stub
|
|
177
|
-
network and secrets in your own tests; never read a live profile.
|
|
178
|
-
|
|
179
|
-
Run the engine's own fast gate while authoring:
|
|
123
|
+
|
|
124
|
+
The output names each run (`ok hello [bash] tests/hello.sh`), shows a
|
|
125
|
+
failing test's output, and ends with a count; any failure makes the command
|
|
126
|
+
fail. A test that needs a file before the module loads (a secret, a config
|
|
127
|
+
file) writes it and calls `valise_test_reload`, which loads the module again.
|
|
128
|
+
A test that starts another shell loads the module there from
|
|
129
|
+
`VALISE_TEST_PLAN`, the plan the test was loaded with, with
|
|
130
|
+
`VALISE_MODULE_ROOT` set to `VALISE_TEST_RELEASE`:
|
|
180
131
|
|
|
181
132
|
```sh
|
|
182
|
-
|
|
133
|
+
bash -c 'VALISE_MODULE_ROOT=$VALISE_TEST_RELEASE; . "$VALISE_TEST_PLAN"; hello world'
|
|
183
134
|
```
|
|
184
135
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
136
|
+
Tests see `VALISE_TEST_ROOT` (the scratch directory, which holds `HOME`),
|
|
137
|
+
`VALISE_TEST_MODULE`, `VALISE_TEST_MODULE_DIR`, `VALISE_TEST_SHELL`,
|
|
138
|
+
`VALISE_TEST_RELEASE`, `VALISE_TEST_PLAN` (`runtime/fish/profile.fish` in
|
|
139
|
+
Fish), and `VALISE_PROJECT_ROOT`. Fish tests (`.fish`) run only in Fish; other tests
|
|
140
|
+
run only in POSIX shells.
|
|
141
|
+
|
|
142
|
+
`valise run [--shell=<class>] [--module=<m>]... [--bundle=<b>]
|
|
143
|
+
[--variant=<v>] [--keep-env] -- <script> [args...]` loads modules into a
|
|
144
|
+
shell and runs a script there, for quick checks:
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
valise run --shell=zsh --module=hello -- 'hello "$@"' a b
|
|
148
|
+
valise run --bundle=work -- 'command -v my_work_function'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Its environment is quarantined like `valise test` unless `--keep-env` is
|
|
152
|
+
given, and its exit status is the script's.
|
|
153
|
+
|
|
154
|
+
None of these commands installs anything or reads your startup files. To
|
|
155
|
+
check a layer tree outside a project, run `valise layer-lint <path>`.
|
|
156
|
+
|
|
157
|
+
## What belongs in a layer
|
|
158
|
+
|
|
159
|
+
A layer under `layers/<provider>/` is a set of modules another person could
|
|
160
|
+
vendor unchanged. Before moving a module into a layer, check:
|
|
161
|
+
|
|
162
|
+
- **No employer material**: no account IDs, hostnames, cloud profiles,
|
|
163
|
+
internal project names, or anything else tied to one organization.
|
|
164
|
+
- **No personal configuration**: no home-directory paths, chosen editor,
|
|
165
|
+
toolchain, model, or theme.
|
|
166
|
+
- **No dependency outside the layer** other than engine modules. A
|
|
167
|
+
dependency on one of your primary modules cannot be satisfied in someone
|
|
168
|
+
else's project.
|
|
169
|
+
- **Mechanism, not preset.** Keep a reusable engine (a git helper, a path
|
|
170
|
+
function) separate from the presets that configure it for one person. Ship
|
|
171
|
+
the engine in the layer and keep presets in the primary modules, or in a
|
|
172
|
+
separate layer module that requires the engine.
|
|
173
|
+
- **One purpose per module.** A grab-bag module forces a consumer to take
|
|
174
|
+
everything or nothing.
|
|
175
|
+
- **An owner segment**: name modules `contrib_<owner>_<thing>`, so two
|
|
176
|
+
providers do not collide on a name.
|
|
177
|
+
- **A version.** Every layer module needs `version=`; bump it on an
|
|
178
|
+
incompatible change.
|
|
179
|
+
|
|
180
|
+
Set `lint_layer_forbid` in `valise.conf` to a pattern for the identifiers
|
|
181
|
+
that must never reach the layer, such as an employer name, and `valise lint`
|
|
182
|
+
will fail when one appears. [shared-modules.md](shared-modules.md) covers
|
|
183
|
+
ownership, review, and deprecation for layers other people consume.
|
|
184
|
+
|
|
185
|
+
## Modules in the engine repository
|
|
186
|
+
|
|
187
|
+
The engine's own modules live in this repository's `module.d/` and
|
|
188
|
+
`modules/` and use the `valise_` prefix. `manifests/parser-support.tsv`
|
|
189
|
+
declares which parsers must accept each file class; it ships in the package,
|
|
190
|
+
and `make test-parser-support` checks it in the source repository. Load an
|
|
191
|
+
engine module from a checkout without installing anything through the
|
|
192
|
+
development entrypoint:
|
|
193
|
+
|
|
194
|
+
```sh
|
|
195
|
+
env -i HOME="$(mktemp -d)" PATH=/usr/bin:/bin /bin/sh -c '
|
|
196
|
+
VALISE_MODULE_ROOT="$PWD" VALISE_MODULES="valise_path" VALISE_MODE=strict
|
|
197
|
+
. ./entrypoints/profile.sh && command -v valise_path_prepend'
|
|
198
|
+
```
|
|
188
199
|
|
|
189
|
-
`
|
|
190
|
-
structure, namespace compliance, file permissions, and metadata integrity,
|
|
191
|
-
but it requires a consuming project's `shell/layers/` tree and
|
|
192
|
-
`hack/validate-layer.py`, so it does not run from a bare checkout and is
|
|
193
|
-
not part of this workflow.
|
|
200
|
+
Run `make check-fast` while working and `make check` before committing.
|