@renault-digital/bash-base 2.4.1 → 2.5.1
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/CONTRIBUTING.md +83 -10
- package/README.md +87 -6
- package/bin/bash-base +596 -312
- package/docs/bash-zsh-compatibility.md +202 -0
- package/docs/coverage.md +289 -0
- package/docs/design-principles.md +176 -0
- package/docs/references.md +122 -68
- package/docs/roadmap.md +289 -0
- package/man/bash-base.1 +71 -4
- package/package.json +4 -3
- package/scripts/test-shell-compat.sh +391 -0
- package/scripts/test.sh +82 -14
- package/spec/array.spec.sh +8 -2
- package/spec/coverage_edges.spec.sh +88 -0
- package/spec/coverage_zsh_array_map.spec.sh +24 -0
- package/spec/process.spec.sh +109 -15
- package/spec/reflection.spec.sh +1 -1
- package/spec/spec_helper.sh +17 -0
- package/spec/string.spec.sh +14 -17
- package/src/alias.sh +35 -4
- package/src/argument.sh +222 -124
- package/src/array.sh +96 -98
- package/src/constant.sh +1 -1
- package/src/document.sh +17 -11
- package/src/out.sh +26 -15
- package/src/process.sh +4 -4
- package/src/reflection.sh +76 -17
- package/src/string.sh +92 -27
- package/src/tool.sh +28 -12
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Bash and zsh compatibility
|
|
2
|
+
|
|
3
|
+
`bash-base` can be sourced by both Bash and zsh.
|
|
4
|
+
|
|
5
|
+
The project name and most executable examples remain Bash-oriented for backward
|
|
6
|
+
compatibility, but the reusable functions in `src/` are maintained within a
|
|
7
|
+
shared Bash/zsh contract.
|
|
8
|
+
|
|
9
|
+
## Why this matters
|
|
10
|
+
|
|
11
|
+
A sourced file is interpreted by the **current shell**. Its shebang does not
|
|
12
|
+
start another shell.
|
|
13
|
+
|
|
14
|
+
For example, on current macOS systems the interactive shell is normally zsh:
|
|
15
|
+
|
|
16
|
+
```text
|
|
17
|
+
zsh
|
|
18
|
+
↓
|
|
19
|
+
source ~/.bash_profile
|
|
20
|
+
↓
|
|
21
|
+
source bash-base
|
|
22
|
+
↓
|
|
23
|
+
bash-base functions execute inside zsh
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Therefore a file named `bash-base`, or a source file beginning with
|
|
27
|
+
`#!/usr/bin/env bash`, can still fail when it is sourced by zsh if its function
|
|
28
|
+
bodies contain Bash-only constructs.
|
|
29
|
+
|
|
30
|
+
The compatibility work removes that accidental dependency while preserving the
|
|
31
|
+
existing Bash behavior and Bash ShellSpec regression suite.
|
|
32
|
+
|
|
33
|
+
## Supported model
|
|
34
|
+
|
|
35
|
+
The production modules covered by the shared-shell contract are:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
src/
|
|
39
|
+
alias.sh
|
|
40
|
+
argument.sh
|
|
41
|
+
array.sh
|
|
42
|
+
constant.sh
|
|
43
|
+
document.sh
|
|
44
|
+
out.sh
|
|
45
|
+
process.sh
|
|
46
|
+
reflection.sh
|
|
47
|
+
string.sh
|
|
48
|
+
tool.sh
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Executable scripts may still deliberately use:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
#!/usr/bin/env bash
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
That is separate from the requirement that the reusable `src/` library can be
|
|
58
|
+
**sourced** by either Bash or zsh.
|
|
59
|
+
|
|
60
|
+
## Compatibility rules
|
|
61
|
+
|
|
62
|
+
Production source should prefer the common Bash/zsh subset.
|
|
63
|
+
|
|
64
|
+
In particular:
|
|
65
|
+
|
|
66
|
+
- function declarations use `name() { ...; }`;
|
|
67
|
+
- prompts use `printf` plus `IFS= read -r`, not Bash `read -p` or zsh
|
|
68
|
+
`read "name?prompt"`;
|
|
69
|
+
- output that depends on escape interpretation uses `printf` instead of
|
|
70
|
+
implementation-dependent `echo -e`;
|
|
71
|
+
- Bash-only case conversion such as `${value^^}`, `${value,,}` and `${value^}`
|
|
72
|
+
is not used;
|
|
73
|
+
- Bash-only indirect array expansion such as `${!array}` is not used;
|
|
74
|
+
- library code does not require global `shopt` or `setopt` changes;
|
|
75
|
+
- shell-specific introspection is isolated behind an explicit
|
|
76
|
+
`BASH_VERSION`/`ZSH_VERSION` branch;
|
|
77
|
+
- data values are kept out of `eval` program text where practical.
|
|
78
|
+
|
|
79
|
+
## Array portability boundary
|
|
80
|
+
|
|
81
|
+
Bash and zsh do not use the same native indexed-array model. Bash normally
|
|
82
|
+
starts indexed arrays at 0; zsh normally starts them at 1, and sparse-array
|
|
83
|
+
behavior also differs.
|
|
84
|
+
|
|
85
|
+
Logical operations therefore work on ordered values rather than assuming a
|
|
86
|
+
particular numeric index base. This applies to operations such as:
|
|
87
|
+
|
|
88
|
+
```text
|
|
89
|
+
join
|
|
90
|
+
contains
|
|
91
|
+
sort
|
|
92
|
+
intersection
|
|
93
|
+
subtract
|
|
94
|
+
union
|
|
95
|
+
remove
|
|
96
|
+
map
|
|
97
|
+
filter
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
`array_describe` / `array_from_describe` intentionally use each shell's native
|
|
101
|
+
`typeset -p` representation. Their supported contract is:
|
|
102
|
+
|
|
103
|
+
> serialize and restore in the same shell.
|
|
104
|
+
|
|
105
|
+
The serialized Bash sparse-array text is not promised to be a portable zsh
|
|
106
|
+
serialization, or vice versa.
|
|
107
|
+
|
|
108
|
+
## Reflection boundary
|
|
109
|
+
|
|
110
|
+
Function and variable reflection is implemented explicitly per shell where
|
|
111
|
+
necessary:
|
|
112
|
+
|
|
113
|
+
```text
|
|
114
|
+
Bash → compgen / typeset
|
|
115
|
+
zsh → functions / parameters tables
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The shell-specific zsh parameter-expansion syntax is kept inside the zsh branch
|
|
119
|
+
so that the complete source still parses in Bash.
|
|
120
|
+
|
|
121
|
+
## Argument parsing and `$0`
|
|
122
|
+
|
|
123
|
+
zsh can expose a function-local value through `$0` while a shell function runs.
|
|
124
|
+
Where bash-base needs the original script identity, it uses `ZSH_ARGZERO` under
|
|
125
|
+
zsh and `$0` under Bash.
|
|
126
|
+
|
|
127
|
+
The compatibility test suite includes an actual script-level `args_parse -h`
|
|
128
|
+
case so rule discovery is tested in both shells, not only syntax-checked.
|
|
129
|
+
|
|
130
|
+
## Test architecture
|
|
131
|
+
|
|
132
|
+
The existing ShellSpec suite remains the authoritative **Bash regression suite**.
|
|
133
|
+
It protects the historical Bash behavior and coverage.
|
|
134
|
+
|
|
135
|
+
A separate compatibility gate is implemented in:
|
|
136
|
+
|
|
137
|
+
```text
|
|
138
|
+
scripts/test-shell-compat.sh
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
The harness itself uses Bash but launches both Bash and zsh as test targets.
|
|
142
|
+
That avoids requiring the test runner itself to live in the common subset.
|
|
143
|
+
|
|
144
|
+
It verifies:
|
|
145
|
+
|
|
146
|
+
- `bash -n` for every shared source module;
|
|
147
|
+
- `zsh -n` for every shared source module;
|
|
148
|
+
- absence of known shell-specific constructs that the migration removed;
|
|
149
|
+
- common function declaration syntax;
|
|
150
|
+
- runtime behavior in Bash and zsh for strings, arrays, reflection, argument
|
|
151
|
+
parsing/validation, confirmations, and legacy compatibility names;
|
|
152
|
+
- script-level argument-rule discovery in both shells.
|
|
153
|
+
|
|
154
|
+
Run the shared-shell gate directly with:
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
bash scripts/test-shell-compat.sh
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The normal local gate remains:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm test
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
which runs:
|
|
167
|
+
|
|
168
|
+
```text
|
|
169
|
+
scripts/test.sh
|
|
170
|
+
↓
|
|
171
|
+
scripts/test-shell-compat.sh
|
|
172
|
+
├── Bash compatibility/runtime
|
|
173
|
+
└── zsh compatibility/runtime
|
|
174
|
+
↓
|
|
175
|
+
ShellSpec
|
|
176
|
+
└── existing Bash regression suite
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## CI acceptance rule
|
|
180
|
+
|
|
181
|
+
GitHub Actions installs both Bash and zsh. A change is accepted only when both
|
|
182
|
+
of these layers pass:
|
|
183
|
+
|
|
184
|
+
```text
|
|
185
|
+
Bash/zsh compatibility gate: PASS
|
|
186
|
+
Bash ShellSpec regression + coverage gate: PASS
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
A syntax-only pass is not sufficient: several historical incompatibilities are
|
|
190
|
+
runtime differences, especially prompt handling, array indirection, case
|
|
191
|
+
conversion, reflection and `$0` semantics.
|
|
192
|
+
|
|
193
|
+
## Contributing
|
|
194
|
+
|
|
195
|
+
When adding or changing a public helper:
|
|
196
|
+
|
|
197
|
+
1. keep production syntax in the Bash/zsh common subset when possible;
|
|
198
|
+
2. if behavior genuinely differs by shell, use a small explicit shell branch;
|
|
199
|
+
3. add or update ShellSpec for Bash regression behavior;
|
|
200
|
+
4. extend `scripts/test-shell-compat.sh` when the change depends on shell
|
|
201
|
+
semantics;
|
|
202
|
+
5. run `npm test` before committing.
|
package/docs/coverage.md
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# Coverage collection, validation and reporting
|
|
2
|
+
|
|
3
|
+
## What the percentage means
|
|
4
|
+
|
|
5
|
+
The authoritative gate is `coverage-macos` in
|
|
6
|
+
[the CI workflow](../.github/workflows/cicd.yml). It requires the union of the
|
|
7
|
+
measurable lines in `src/*.sh` to reach **100%**, using reports collected from
|
|
8
|
+
one checkout by [the strict verifier](../scripts/verify-final-coverage-v3.sh).
|
|
9
|
+
|
|
10
|
+
The runs are Bash 3.2, Homebrew Bash 5+, selected zsh ShellSpec tests, and an
|
|
11
|
+
independent Bash TTY probe. “Full report” describes a complete instrumentation
|
|
12
|
+
inventory; it does not mean that the entire ShellSpec suite runs under every
|
|
13
|
+
shell. The zsh test selection remains the selection in the verifier.
|
|
14
|
+
|
|
15
|
+
A line is covered when at least one of these runs records a positive hit for
|
|
16
|
+
that exact file and line. This is not per-shell 100% coverage, branch coverage,
|
|
17
|
+
all-input coverage, or proof of complete correctness. The denominator is taken
|
|
18
|
+
from the validated reports, not a fixed historical line count.
|
|
19
|
+
|
|
20
|
+
The separate Linux job continues to run compatibility/regression checks and
|
|
21
|
+
collect diagnostic reports. Its reports are retained as an artifact but are
|
|
22
|
+
not uploaded to Codecov or added to the strict macOS union.
|
|
23
|
+
|
|
24
|
+
## CI test matrix
|
|
25
|
+
|
|
26
|
+
This matrix describes the commands configured in
|
|
27
|
+
[the workflow](../.github/workflows/cicd.yml) and
|
|
28
|
+
[the strict verifier](../scripts/verify-final-coverage-v3.sh), not the outcome of
|
|
29
|
+
an individual run. Read the logs for executed examples, skips, installed
|
|
30
|
+
versions, source SHA and results. Update this section when those commands change.
|
|
31
|
+
|
|
32
|
+
### Operating system, shell and execution location
|
|
33
|
+
|
|
34
|
+
`ubuntu-22.04` and `macos-15` select the GitHub-hosted runner operating system.
|
|
35
|
+
Bash and zsh are the interpreters running on that operating system. These are
|
|
36
|
+
separate dimensions; macOS is not another name for zsh. The workflow's
|
|
37
|
+
`shell: bash` runs the orchestration step, while ShellSpec's `--shell` selects
|
|
38
|
+
the interpreter under test.
|
|
39
|
+
|
|
40
|
+
`coverage-macos` checks out source on GitHub's macOS runner, installs its tools,
|
|
41
|
+
and calls the verifier there. The verifier creates a fresh temporary report
|
|
42
|
+
directory on that machine. The workflow uploads that run's validated union;
|
|
43
|
+
it does not retrieve reports from a contributor's Mac. Running the verifier
|
|
44
|
+
on a development Mac produces a separate local result and does not itself run
|
|
45
|
+
the Codecov upload step.
|
|
46
|
+
|
|
47
|
+
Keep `macOS` in operating-system requirements, runner labels, platform-specific
|
|
48
|
+
notes and report-origin labels. Do not replace it globally with `zsh`. This
|
|
49
|
+
matrix keeps the existing job IDs and release dependencies unchanged.
|
|
50
|
+
|
|
51
|
+
### Configured tests and report destinations
|
|
52
|
+
|
|
53
|
+
| Runner | Job | Interpreter or tool | Configured scope / entry point | Included in Codecov union? |
|
|
54
|
+
| --- | --- | --- | --- | --- |
|
|
55
|
+
| `ubuntu-22.04` | `test` | Installed Bash and zsh | `bash scripts/test.sh --kcov -f t`: the workflow's Bash/zsh compatibility entry point followed by the existing Bash ShellSpec regression suite. Compatibility cases are maintained by the test entry point, not redefined here. | No. Linux coverage remains diagnostic evidence. |
|
|
56
|
+
| `ubuntu-22.04` | `test` | Installed zsh, via ShellSpec `--shell zsh` | Selected `spec/string.spec.sh` and `spec/reflection.spec.sh` tests; separate `coverage-zsh` report. | No. Retained with Linux diagnostics. |
|
|
57
|
+
| `macos-15` | `coverage-macos` | `/bin/bash`, expected Bash 3.2 | ShellSpec over `spec/*.spec.sh`, subject to the suite's conditional skips; report label `bash32`. | Yes, as a full-inventory report. |
|
|
58
|
+
| `macos-15` | `coverage-macos` | Homebrew Bash, expected Bash 5+ | ShellSpec over `spec/*.spec.sh`, subject to the suite's conditional skips; report label `bash5`. | Yes, as a full-inventory report. |
|
|
59
|
+
| `macos-15` | `coverage-macos` | `/bin/zsh` | Four selected spec files listed below; report label `zsh`. This is not the complete ShellSpec suite. | Yes, as a full-inventory report. |
|
|
60
|
+
| `macos-15` | `coverage-macos` | Python PTY driver launching `/bin/bash` | `bash_base_tty_probe_v3.py --shell /bin/bash --preload-input`; the verifier requires `TTY_COVERAGE_PASS 2/2`. | Yes, as the supplemental TTY report. |
|
|
61
|
+
| `ubuntu-22.04` and `macos-15` | `test` and `coverage-macos`, respectively | Python 3 | `python3 -m unittest discover -s tests -p 'test_coverage_union_v3.py' -v`; synthetic report fixtures exercise validation and export. | No. These are tests of the coverage tooling, not additional production-source coverage. |
|
|
62
|
+
|
|
63
|
+
The macOS zsh selection is exactly:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
spec/string.spec.sh
|
|
67
|
+
spec/reflection.spec.sh
|
|
68
|
+
spec/coverage_edges.spec.sh
|
|
69
|
+
spec/coverage_zsh_array_map.spec.sh
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The macOS job calls the strict verifier directly, not `scripts/test.sh`. Do not
|
|
73
|
+
infer that the separate Bash/zsh compatibility entry point is also invoked on
|
|
74
|
+
macOS merely because the strict verifier tests both interpreters.
|
|
75
|
+
|
|
76
|
+
The Linux zsh probe's **coverage report** is diagnostic. The probe itself is
|
|
77
|
+
not best-effort: the workflow propagates its test exit status and does not
|
|
78
|
+
ignore a failing probe.
|
|
79
|
+
|
|
80
|
+
### Limits, versions and evidence
|
|
81
|
+
|
|
82
|
+
A **full-inventory report** contains the baseline measurable files and lines;
|
|
83
|
+
it need not execute every spec file or cover every measured line. An unexecuted
|
|
84
|
+
line remains in the inventory with zero hits. In particular, the zsh inventory
|
|
85
|
+
must match the Bash inventories even though the zsh spec selection is smaller.
|
|
86
|
+
|
|
87
|
+
The four macOS reports are collected sequentially from one checkout and merged
|
|
88
|
+
as described below. This is a documented testing matrix, not a
|
|
89
|
+
`strategy.matrix` expansion, a full operating-system-by-shell Cartesian product,
|
|
90
|
+
or an average of coverage percentages. Linux reports are not merged into it.
|
|
91
|
+
|
|
92
|
+
The configured checks do not establish a complete zsh ShellSpec run, an explicit
|
|
93
|
+
Linux Bash 3.2 job, TTY testing under every interpreter, or testing across every
|
|
94
|
+
macOS release and CPU architecture. A 100% union does not establish any of these
|
|
95
|
+
claims. The compatibility contract remains in
|
|
96
|
+
[Bash and zsh compatibility](bash-zsh-compatibility.md); this table records test
|
|
97
|
+
scope rather than expanding the public support contract.
|
|
98
|
+
|
|
99
|
+
ShellSpec is explicitly pinned to `0.28.1` in both collection jobs. Bash/zsh,
|
|
100
|
+
Homebrew packages and the runner images are not all pinned to immutable versions.
|
|
101
|
+
The verifier selects `/bin/bash`, `/bin/zsh` and `BASH5` (or Homebrew's Bash); its
|
|
102
|
+
labels are not substitutes for checking actual version output. It is configured
|
|
103
|
+
for the macOS tool layout, so running it unchanged on a different OS does not
|
|
104
|
+
prove that its `bash32` row actually used Bash 3.2.
|
|
105
|
+
|
|
106
|
+
For PRs, the strict job uses the PR head SHA and the Linux job uses the default
|
|
107
|
+
PR merge checkout. Keep their evidence separate. For a run-specific result,
|
|
108
|
+
record the source SHA, runner/image and architecture, actual interpreter versions,
|
|
109
|
+
executed/skipped tests and validated line totals alongside the CI result.
|
|
110
|
+
Historical local versions and line counts are not permanent CI guarantees.
|
|
111
|
+
|
|
112
|
+
Runner labels and workflow shell semantics are documented in GitHub's
|
|
113
|
+
[hosted-runner reference](https://docs.github.com/en/actions/reference/runners/github-hosted-runners)
|
|
114
|
+
and [workflow syntax](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax).
|
|
115
|
+
|
|
116
|
+
## Validation and export
|
|
117
|
+
|
|
118
|
+
[bash_base_coverage_union_v3.py](../bash_base_coverage_union_v3.py) uses the first
|
|
119
|
+
full report as the measurable-line baseline. The three full reports must have
|
|
120
|
+
identical file and line inventories. The TTY supplement may omit whole files,
|
|
121
|
+
but a file present in that report must have the baseline's complete line
|
|
122
|
+
inventory. Additional or missing lines are not silently reconciled.
|
|
123
|
+
|
|
124
|
+
The verifier compares `src/*.sh` SHA-256 hashes before and after collection.
|
|
125
|
+
All test commands must succeed before it invokes the merger. Cobertura XML
|
|
126
|
+
alone cannot establish the source revision or prove that tests succeeded; do
|
|
127
|
+
not merge reports from unrelated source revisions or independent old runs.
|
|
128
|
+
|
|
129
|
+
The optional `--output-cobertura FILE` switch exports only the validated union.
|
|
130
|
+
It requires strict validation and `--source-dir` pointing to `src`. The output
|
|
131
|
+
must be outside `src` and must not already exist; use a fresh output path.
|
|
132
|
+
|
|
133
|
+
The exported report:
|
|
134
|
+
|
|
135
|
+
- preserves every baseline measurable line, including lines with zero hits;
|
|
136
|
+
- records union hits as `0` or `1`, not cumulative execution counts;
|
|
137
|
+
- uses repository-relative `src/<filename>.sh` paths and `<source>.</source>`;
|
|
138
|
+
- derives per-file and overall totals/rates from the same line records used by
|
|
139
|
+
the gate, with no asserted branch coverage.
|
|
140
|
+
|
|
141
|
+
The verifier writes the export to `$COVERAGE_ROOT/union/cobertura.xml`. Raw
|
|
142
|
+
reports, logs, source hashes, the tested commit and the export remain available
|
|
143
|
+
in the `strict-macos-coverage` artifact.
|
|
144
|
+
|
|
145
|
+
### Failure behaviour
|
|
146
|
+
|
|
147
|
+
| Outcome | Union exported/upload eligible? | Strict gate |
|
|
148
|
+
| --- | --- | --- |
|
|
149
|
+
| Tests pass, source hashes unchanged, inventories valid, all lines covered | Yes | Pass |
|
|
150
|
+
| Same validation succeeds, but genuine uncovered lines remain | Yes, including all zero-hit lines | Fail |
|
|
151
|
+
| A test fails, source hashes differ, reports are missing/malformed, or inventories differ | No | Fail |
|
|
152
|
+
| The export cannot be written or the workflow cannot save its evidence | No upload | Fail |
|
|
153
|
+
| A Codecov upload fails after the gate passes | Existing export remains an artifact | The upload step makes `coverage-macos` fail |
|
|
154
|
+
|
|
155
|
+
Reporting a valid below-100% result is intentional: otherwise a coverage
|
|
156
|
+
regression could leave an old green percentage visible. The verifier still
|
|
157
|
+
returns nonzero and its failed step is not marked `continue-on-error`.
|
|
158
|
+
|
|
159
|
+
If no valid new upload exists, Codecov may continue to display an earlier
|
|
160
|
+
processed commit. Always compare commit SHAs and CI results, not only the badge.
|
|
161
|
+
An upload accepted by the client is not proof that server-side processing and
|
|
162
|
+
all UI widgets have finished updating.
|
|
163
|
+
|
|
164
|
+
## Codecov upload and authentication
|
|
165
|
+
|
|
166
|
+
The workflow uploads exactly one report using the official Codecov action,
|
|
167
|
+
pinned to the v7.1.1 commit. `files` explicitly names the union XML and
|
|
168
|
+
`disable_search: true` prevents discovery of other reports. `plugins: noop`
|
|
169
|
+
disables additional collection plugins; `disable_file_fixes: true` avoids
|
|
170
|
+
client-side removal of measured lines. CLI integrity verification is not
|
|
171
|
+
bypassed. The action itself is pinned; its CLI download still uses the action's
|
|
172
|
+
default version policy, so this is not a claim that all transitive tools are
|
|
173
|
+
version-pinned.
|
|
174
|
+
|
|
175
|
+
Configure a **repository-specific Codecov upload token** as the GitHub Actions
|
|
176
|
+
secret `CODECOV_TOKEN`:
|
|
177
|
+
|
|
178
|
+
1. In Codecov, open this repository's **Configuration / General** page and obtain
|
|
179
|
+
its repository upload token. Prefer this over an organization-wide token.
|
|
180
|
+
2. In the GitHub repository, open **Settings → Secrets and variables → Actions**
|
|
181
|
+
and create or verify the repository secret named `CODECOV_TOKEN`.
|
|
182
|
+
3. Set only the token value. Do not put credentials in `codecov.yml`, a commit,
|
|
183
|
+
a screenshot, or a shared log.
|
|
184
|
+
|
|
185
|
+
This credential is unrelated to `NPM_TOKEN`, `GITHUB_TOKEN`, Docker credentials,
|
|
186
|
+
or npm Trusted Publishing. The coverage job only requests `contents: read`;
|
|
187
|
+
it does not gain `id-token: write`. The two npm OIDC release jobs are unchanged.
|
|
188
|
+
|
|
189
|
+
Public fork PRs do not receive repository secrets. The official action supports
|
|
190
|
+
public fork uploads without the upstream token. Do not enable
|
|
191
|
+
`pull_request_target` to run untrusted PR code with secrets. For Dependabot PRs,
|
|
192
|
+
configure the upload token as a Dependabot secret as described by Codecov's
|
|
193
|
+
action documentation. Actual upload acceptance remains subject to Codecov's
|
|
194
|
+
repository/organization settings and service availability.
|
|
195
|
+
|
|
196
|
+
The action also supports Codecov OIDC, but this patch deliberately does not add
|
|
197
|
+
OIDC permission to test jobs in the existing npm publishing workflow. A future
|
|
198
|
+
OIDC change should review the identity trust boundary separately.
|
|
199
|
+
|
|
200
|
+
## Commit identity and release boundary
|
|
201
|
+
|
|
202
|
+
For pull requests, `coverage-macos` checks out the PR head SHA explicitly and
|
|
203
|
+
passes the actual `git rev-parse HEAD` value to Codecov as `override_commit`.
|
|
204
|
+
The separate Linux job continues to test the default PR merge checkout. These
|
|
205
|
+
are distinct checks; they are not presented as reports from the same checkout.
|
|
206
|
+
For push/manual runs, coverage uses the triggering SHA.
|
|
207
|
+
|
|
208
|
+
Do not label a feature-branch upload as `master` or relabel a merge-commit report
|
|
209
|
+
as a different source SHA. The default-branch README badge will not change just
|
|
210
|
+
because a PR's coverage passes.
|
|
211
|
+
|
|
212
|
+
The `semantic-release-preview` job still depends on `coverage-macos`, and the
|
|
213
|
+
real release job still depends on the preview. Thus either the strict gate or
|
|
214
|
+
its Codecov upload failing blocks the existing release dependency chain. A
|
|
215
|
+
Codecov outage can therefore block publishing even when the local gate passes;
|
|
216
|
+
this is an explicit consequence of `fail_ci_if_error: true`.
|
|
217
|
+
|
|
218
|
+
The workflow already supports `workflow_dispatch`, with both release jobs
|
|
219
|
+
restricted to `push` on `master`. A manual run of this workflow on the proposed
|
|
220
|
+
branch can validate coverage/upload without invoking those release jobs.
|
|
221
|
+
Merging to `master` is not a side-effect-free upload test: it can trigger the
|
|
222
|
+
existing npm/GitHub/Docker release path.
|
|
223
|
+
|
|
224
|
+
## Why codecov.yml is present
|
|
225
|
+
|
|
226
|
+
A repository-level Codecov YAML is optional. Without one, Codecov uses its
|
|
227
|
+
service defaults and any organization-level settings; its absence did not
|
|
228
|
+
cause the missing union upload.
|
|
229
|
+
|
|
230
|
+
The [minimal project configuration](../codecov.yml) makes the project and patch
|
|
231
|
+
coverage targets explicitly 100%, with zero tolerance and no carryforward for
|
|
232
|
+
`strict-union`. It adds no ignore patterns or source-path filters. This is a
|
|
233
|
+
reporting policy, not a replacement for the source inventory validator.
|
|
234
|
+
|
|
235
|
+
`require_ci_to_pass: false` and `wait_for_ci: false` allow Codecov notifications
|
|
236
|
+
to report genuine regressions even when CI is already red, or before unrelated
|
|
237
|
+
release steps finish. They do **not** make a failed strict gate successful or
|
|
238
|
+
change GitHub branch protection. There is one authoritative upload, so
|
|
239
|
+
`after_n_builds` is 1, not 4.
|
|
240
|
+
|
|
241
|
+
Codecov merges repository YAML with organization YAML; account-level settings
|
|
242
|
+
and service-side processing must still be checked in the first real run.
|
|
243
|
+
Validate the YAML with Codecov's official validator before rollout.
|
|
244
|
+
|
|
245
|
+
## README display
|
|
246
|
+
|
|
247
|
+
The tree remains clickable and points to the `master/src` detail view. A brief
|
|
248
|
+
caption explains union line coverage. The badge remains a dynamic Codecov
|
|
249
|
+
badge; it is never replaced with a static “100%”. Historical commits keep their
|
|
250
|
+
historical coverage unless new reports for those commits are intentionally
|
|
251
|
+
submitted.
|
|
252
|
+
|
|
253
|
+
The GitHub Actions badge uses Shields' supported workflow-filename endpoint,
|
|
254
|
+
with `cicd.yml`, `branch=master` and `event=push`. It reports the **whole
|
|
255
|
+
workflow**, not just `coverage-macos`. A release failure may make that badge red
|
|
256
|
+
while Codecov shows full line coverage.
|
|
257
|
+
|
|
258
|
+
## Verification and troubleshooting
|
|
259
|
+
|
|
260
|
+
Run the exporter self-tests (synthetic report fixtures, not production coverage):
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
python3 -m unittest discover -s tests -p 'test_coverage_union_v3.py' -v
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Run the actual coverage gate on the configured macOS development environment:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
bash scripts/verify-final-coverage-v3.sh "$PWD"
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
In the CI log, inspect `SOURCE_COMMIT`, `SOURCE_SHA256_UNCHANGED`,
|
|
273
|
+
`MEASURABLE_LINE_SETS`, `VALIDATED_COBERTURA` and `COVERAGE_GATE`. Open the artifact
|
|
274
|
+
and compare the XML's `lines-valid` / `lines-covered` counts and `src/*.sh`
|
|
275
|
+
filenames with the merger log and Codecov for that same commit.
|
|
276
|
+
|
|
277
|
+
When Codecov disagrees, check in this order: commit/branch identity; uploader
|
|
278
|
+
success and server processing; source paths and measured-line counts; any other
|
|
279
|
+
uploaders or organization-level YAML. Do not lower the gate, hard-code a badge,
|
|
280
|
+
add ignore rules, or rerun an obsolete uploader to make the display green.
|
|
281
|
+
|
|
282
|
+
## Official references
|
|
283
|
+
|
|
284
|
+
- [Codecov YAML and its optional status](https://docs.codecov.com/docs/codecov-yaml)
|
|
285
|
+
- [Codecov YAML reference](https://docs.codecov.com/docs/codecovyml-reference)
|
|
286
|
+
- [Codecov upload tokens and public fork behaviour](https://docs.codecov.com/docs/codecov-tokens)
|
|
287
|
+
- [Pinned Codecov action inputs](https://github.com/codecov/codecov-action/blob/303a32d7a59b442fa8d48b6a1cc6825c09c847a5/action.yml)
|
|
288
|
+
- [Codecov report fixes](https://docs.codecov.com/docs/fixing-reports)
|
|
289
|
+
- [GitHub Actions workflow status badge](https://shields.io/badges/git-hub-actions-workflow-status)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Bash-base design principles
|
|
2
|
+
|
|
3
|
+
## Purpose and status
|
|
4
|
+
|
|
5
|
+
Bash-base is a Shell function library for composing system commands and
|
|
6
|
+
expressing automation logic in Bash and zsh. Its purpose is to reduce repeated
|
|
7
|
+
research, implementation and verification work through reusable higher-level
|
|
8
|
+
interfaces.
|
|
9
|
+
|
|
10
|
+
The central question is:
|
|
11
|
+
|
|
12
|
+
> Does this change make command-oriented Shell programming easier to understand,
|
|
13
|
+
> combine and maintain, while reducing recurring implementation effort or risk?
|
|
14
|
+
|
|
15
|
+
This document guides maintenance, feature review and future API design. It does
|
|
16
|
+
not retroactively redefine existing APIs, certify arbitrary inputs as safe, or
|
|
17
|
+
announce unimplemented features. Existing behavior must be checked against the
|
|
18
|
+
[API reference](references.md), the relevant source comments and tests, and the
|
|
19
|
+
[Bash/zsh compatibility contract](bash-zsh-compatibility.md). Conflicts between
|
|
20
|
+
these sources require investigation rather than an assumption that one silently
|
|
21
|
+
changes another.
|
|
22
|
+
|
|
23
|
+
## 1. Start from a real pain point
|
|
24
|
+
|
|
25
|
+
A feature should address a concrete scripting problem that recurs or is costly
|
|
26
|
+
to get right. Time spent searching, comparing approaches, checking edge cases
|
|
27
|
+
and maintaining duplicated implementations is part of that cost.
|
|
28
|
+
|
|
29
|
+
Code length alone is not a measure of value. A small wrapper may be worthwhile
|
|
30
|
+
when it provides a useful contract or isolates a difficult compatibility issue.
|
|
31
|
+
Conversely, a large implementation is not justified simply because another
|
|
32
|
+
language or library provides the same feature.
|
|
33
|
+
|
|
34
|
+
Consider built-ins, existing Bash-base helpers, established external tools,
|
|
35
|
+
documentation improvements and examples before adding a new public API.
|
|
36
|
+
Personal or internal use cases are valid evidence; widespread adoption is not a
|
|
37
|
+
prerequisite for proposing a useful change.
|
|
38
|
+
|
|
39
|
+
## 2. Use abstractions to support command composition
|
|
40
|
+
|
|
41
|
+
Higher-level interfaces should help callers organize arguments, transform
|
|
42
|
+
command outputs and express automation logic without repeatedly implementing
|
|
43
|
+
Shell-specific mechanics.
|
|
44
|
+
|
|
45
|
+
String and array operations serve that purpose. List, Set and Map are useful
|
|
46
|
+
concepts for reasoning about order, duplicates and key/value relationships, but
|
|
47
|
+
mentioning them here does not establish separate implemented APIs. Any extension
|
|
48
|
+
must first justify its role in real command-oriented workflows.
|
|
49
|
+
|
|
50
|
+
The goal is not a complete object system, a replacement Shell interpreter, a
|
|
51
|
+
cloud SDK collection, or a general-purpose application framework. Prefer
|
|
52
|
+
integration with suitable tools over duplicating them without a clear benefit.
|
|
53
|
+
|
|
54
|
+
## 3. Define behavior before adding an interface
|
|
55
|
+
|
|
56
|
+
For a new public helper, or a deliberate contract change, document the relevant
|
|
57
|
+
parts of the following before accepting the implementation:
|
|
58
|
+
|
|
59
|
+
| Area | Questions to resolve |
|
|
60
|
+
| --- | --- |
|
|
61
|
+
| Inputs | Arguments, stdin or named variables? Required arguments? Accepted data, code and identifier forms? |
|
|
62
|
+
| Outputs | stdout, named variables or mutation? Output format and record boundaries? |
|
|
63
|
+
| Outcomes | Success, an empty result, a false predicate, invalid input and execution failure: how are they distinguished? |
|
|
64
|
+
| Side effects | Can the helper prompt, execute commands, change files, affect processes, mutate variables or alter Shell state? |
|
|
65
|
+
| Data semantics | Are order, duplicates, empty elements, whitespace, newlines and trailing newlines preserved? What is unsupported? |
|
|
66
|
+
| Environment | Which Shell versions, options, locales and external-command variants are supported? |
|
|
67
|
+
| Composition | How does the result feed another helper or command, and how is failure propagated? |
|
|
68
|
+
|
|
69
|
+
Do not impose a new universal exit-code table on existing functions without a
|
|
70
|
+
compatibility review. Similarly, changing an output channel, ordering rule or
|
|
71
|
+
empty-value representation can break callers even when the function name stays
|
|
72
|
+
the same.
|
|
73
|
+
|
|
74
|
+
## 4. Preserve meaningful boundaries
|
|
75
|
+
|
|
76
|
+
Composable interfaces must keep the distinction between data and executable
|
|
77
|
+
Shell code explicit. A helper that intentionally accepts trusted code must say
|
|
78
|
+
so; it must not be presented as an arbitrary-data interface.
|
|
79
|
+
|
|
80
|
+
For new or reviewed interfaces, examine argument boundaries, variable-name
|
|
81
|
+
validation, quoting, output framing and error propagation. A convenient wrapper
|
|
82
|
+
must not hide unexpected prompting, discarded failures or changes to the
|
|
83
|
+
caller's environment.
|
|
84
|
+
|
|
85
|
+
Do not describe newline-separated output as lossless for arbitrary records
|
|
86
|
+
unless the input domain or an encoding makes that true. A text representation,
|
|
87
|
+
a native array declaration and a portable interchange format are different
|
|
88
|
+
contracts. Serialization must state whether it supports restoration within one
|
|
89
|
+
Shell, cross-Shell exchange, or both. Follow the existing compatibility contract;
|
|
90
|
+
this design document does not broaden any serialization promise.
|
|
91
|
+
|
|
92
|
+
Diagnostic output, normal data output and presentation-oriented helpers also
|
|
93
|
+
need distinct contracts. Changing an existing helper's stdout/stderr behavior
|
|
94
|
+
requires review, not merely a new general rule in this document.
|
|
95
|
+
|
|
96
|
+
## 5. Make portability an explicit boundary
|
|
97
|
+
|
|
98
|
+
Target the documented Bash/zsh environment, not every implementation of Shell.
|
|
99
|
+
Use common syntax where practical and isolate necessary Shell-specific behavior
|
|
100
|
+
behind small, explicit implementation branches.
|
|
101
|
+
|
|
102
|
+
The public behavior should be consistent within the declared boundary. Where a
|
|
103
|
+
native representation or environment-dependent operation is intentionally
|
|
104
|
+
Shell-specific, document that exception and test it accordingly.
|
|
105
|
+
|
|
106
|
+
A sourced library must not acquire undocumented dependencies on caller options
|
|
107
|
+
or unexpectedly change `IFS`, options, traps, the working directory or global
|
|
108
|
+
variables. Audit existing behavior before changing it; new APIs should make
|
|
109
|
+
required effects explicit and avoid unrelated ones.
|
|
110
|
+
|
|
111
|
+
## 6. Support reliability with evidence
|
|
112
|
+
|
|
113
|
+
Keep the existing Bash regression tests, Bash/zsh compatibility checks and
|
|
114
|
+
mandatory coverage gate. Add tests relevant to the behavior changed, including
|
|
115
|
+
error paths and boundary inputs rather than only typical examples.
|
|
116
|
+
|
|
117
|
+
The 100% requirement concerns measurable source lines under the project's
|
|
118
|
+
required coverage configuration. A passing report from one environment does not
|
|
119
|
+
replace the required gate. Cover relevant supported-Shell and interactive or
|
|
120
|
+
non-interactive behavior; coverage is not proof of complete branch coverage, all
|
|
121
|
+
input combinations or the absence of security defects.
|
|
122
|
+
|
|
123
|
+
Documentation-only changes do not justify artificial test changes. Validate the
|
|
124
|
+
changed documentation or metadata, explain why executable behavior is unchanged,
|
|
125
|
+
and keep all required status checks and coverage gates in force.
|
|
126
|
+
|
|
127
|
+
For an implementation review, identify the tested environment and the evidence
|
|
128
|
+
that supports each portability or safety claim. Do not weaken tests, exclusions
|
|
129
|
+
or coverage requirements merely to obtain a passing result.
|
|
130
|
+
|
|
131
|
+
## 7. Keep installation and execution predictable
|
|
132
|
+
|
|
133
|
+
Favor fixed-version, local loading for reproducible or offline workflows. Make
|
|
134
|
+
network access and update behavior clear for every documented installation or
|
|
135
|
+
loading method rather than treating all methods as equivalent.
|
|
136
|
+
|
|
137
|
+
The existing build already generates the single-file `bin/bash-base` artifact.
|
|
138
|
+
Its presence does not by itself prove that every installation method is offline
|
|
139
|
+
or that every published version contains the same compatibility work as the
|
|
140
|
+
current source branch.
|
|
141
|
+
|
|
142
|
+
Distinguish installation and build tooling from commands needed by a helper at
|
|
143
|
+
runtime. Offline loading is not a claim that the application needs no external
|
|
144
|
+
utilities or network access of its own. Declare dependencies and prefer explicit
|
|
145
|
+
user control over updates.
|
|
146
|
+
|
|
147
|
+
## 8. Evolve without silently breaking callers
|
|
148
|
+
|
|
149
|
+
Preserve existing public behavior unless a change is deliberately reviewed,
|
|
150
|
+
documented and released under the project's compatibility and versioning rules.
|
|
151
|
+
New wrappers, compatibility layers or parallel interfaces may provide a migration
|
|
152
|
+
path, but they also have maintenance costs and should not be added automatically.
|
|
153
|
+
|
|
154
|
+
When a legacy interface conflicts with a desired design principle, record its
|
|
155
|
+
actual behavior and relevant tests first. Distinguish a defect fix from an
|
|
156
|
+
intentional API redesign. A new principle is not permission for an unrelated
|
|
157
|
+
behavior change, rename, execution-mode change or dependency upgrade.
|
|
158
|
+
|
|
159
|
+
## Feature review and acceptance
|
|
160
|
+
|
|
161
|
+
Early proposals may start with a use case and an unresolved problem. Before an
|
|
162
|
+
implementation is merged, the contributor and maintainer should establish the
|
|
163
|
+
need, alternatives, scope, public contract, test evidence and compatibility
|
|
164
|
+
impact described in [CONTRIBUTING](../CONTRIBUTING.md).
|
|
165
|
+
|
|
166
|
+
The core review dimensions are **Pain-driven, Contract-driven and
|
|
167
|
+
Evidence-driven**. A feature should save more research, implementation or
|
|
168
|
+
maintenance work than it introduces, with an understandable learning cost.
|
|
169
|
+
There is no function-count target and no requirement to add a whole family of
|
|
170
|
+
APIs when only one well-defined operation is needed.
|
|
171
|
+
|
|
172
|
+
Keep this document focused on design decisions. The API reference belongs to
|
|
173
|
+
the existing source-comment generation workflow; specific enhancement proposals
|
|
174
|
+
and scheduling decisions belong in issues and pull requests. The
|
|
175
|
+
[roadmap](roadmap.md) tracks possible future directions and their acceptance
|
|
176
|
+
criteria; it does not override existing API contracts or these principles.
|