Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@ on:
- 'tools/cli-fp-gen/**'
- 'tests/**'
- 'src/**'
- 'examples/**'
- 'clean-all-examples.sh'
- 'clean-all-examples.ps1'
- '.github/workflows/tests.yml'
pull_request:
paths:
- 'tools/cli-fp-gen/**'
- 'tests/**'
- 'src/**'
- 'examples/**'
- 'clean-all-examples.sh'
- 'clean-all-examples.ps1'
- '.github/workflows/tests.yml'

permissions:
Expand All @@ -31,6 +37,8 @@ jobs:
sudo apt-get install -y fp-compiler fp-units-fcl python3
- name: Framework Unit Tests
run: bash tests/run_tests.sh
- name: Example Build and Cleanup Smoke Test
run: bash tests/run_cleanup_smoke.sh
- name: Generator Unit Tests
run: bash tests/codegen/run_unit_tests.sh
- name: Golden Output Test
Expand Down Expand Up @@ -64,6 +72,9 @@ jobs:
- name: Framework Unit Tests
shell: powershell
run: powershell -ExecutionPolicy Bypass -File tests\run_tests.ps1
- name: Example Build and Cleanup Smoke Test
shell: powershell
run: powershell -ExecutionPolicy Bypass -File tests\run_cleanup_smoke.ps1
- name: Codegen Test Suite
shell: powershell
run: powershell -ExecutionPolicy Bypass -File tests\codegen\run_all_tests.ps1
36 changes: 35 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.3.3] - 2026-08-14

### Fixed

- Cleanup scripts now remove only generated example build artifacts and retain
tracked completion scripts, documentation, and other repository files.
- Registered integer and float options now accept separated negative values,
such as `--count -1` and `--rate -2.5`, in addition to equals syntax.
- Debug output now redacts values supplied to registered password parameters
in both separated and equals forms.

### Changed

- Split help rendering, completion calculation, and parameter-value handling
into focused internal units while preserving the existing public facade.
- Decomposed application dispatch into smaller command-selection, global-
request, help, and execution stages.
- Removed unreachable private completion callback paths and unused temporary
allocations. Deprecated public 1.x compatibility methods remain unchanged.

### Testing

- Replaced placeholder help tests with assertions against generated general,
command, complete, and subcommand help output, including usage,
descriptions, required options, and defaults.
- Added Linux and Windows cleanup smoke checks that compile all seven canonical
examples, run cleanup, and verify tracked files remain intact.
- CI now runs the seven-example build and cleanup smoke check on both Windows
and Linux.
- Framework test runners now force an isolated rebuild so stale compiler units
cannot bypass test-only defines or change whether the suite compiles.
- Added password-redaction and broader completion characterization coverage.

## [1.3.2] - 2026-07-30

### Fixed
Expand Down Expand Up @@ -377,7 +410,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- README with quick start guide
- System requirements and compatibility information

[Unreleased]: https://github.com/ikelaiah/cli-fp/compare/v1.3.2...HEAD
[Unreleased]: https://github.com/ikelaiah/cli-fp/compare/v1.3.3...HEAD
[1.3.3]: https://github.com/ikelaiah/cli-fp/compare/v1.3.2...v1.3.3
[1.3.2]: https://github.com/ikelaiah/cli-fp/compare/v1.3.1...v1.3.2
[1.3.1]: https://github.com/ikelaiah/cli-fp/compare/v1.3.0...v1.3.1
[1.3.0]: https://github.com/ikelaiah/cli-fp/compare/v1.2.0...v1.3.0
Expand Down
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,12 @@ if GetParameterValue('--count', RawCount) and
WriteLn('Count: ', Count);
```

`Password` values are stored as strings and are not automatically redacted.
`Path` values are not checked for existence. If a value starts with `-`, use
the equals form, such as `--count=-1`, so it is not interpreted as another
option.
`Password` values are stored as strings. Framework debug diagnostics redact
them, but output produced by your command or external logging does not.
`Path` values are not checked for existence. Registered integer and float
options accept negative values in both equals and separated forms, for example
`--count=-1` and `--count -1`. For other value types that begin with `-`, use
the equals form so the value is not interpreted as another option.

See the [user manual](docs/user-manual.md#parameter-types-and-validation) for
the complete registration and validation rules.
Expand Down Expand Up @@ -259,6 +261,7 @@ Run the framework tests on Linux or macOS:

```bash
bash tests/run_tests.sh
bash tests/run_cleanup_smoke.sh
```

Run the generator suites:
Expand All @@ -275,11 +278,22 @@ On Windows:
```powershell
powershell -ExecutionPolicy Bypass -File tests\run_tests.ps1
powershell -ExecutionPolicy Bypass -File tests\codegen\run_all_tests.ps1
powershell -ExecutionPolicy Bypass -File tests\run_cleanup_smoke.ps1
```

The cleanup smoke check compiles all seven canonical examples in an isolated
copy, runs the cleanup script, and verifies that generated artifacts are
removed without changing tracked files. CI runs the equivalent Bash and
PowerShell checks on Linux and Windows.

CI runs the framework and generator suites on Windows and Linux. See
[CONTRIBUTING.md](CONTRIBUTING.md) for coding style and pull-request guidance.

The framework runners force a complete unit rebuild into a temporary output
directory, so stale non-test `.ppu` files cannot affect the result. Test output
capture is compiled only when `CLI_FP_TESTING` is defined and is absent from
normal runtime builds.

## Repository map

| Path | Purpose |
Expand Down Expand Up @@ -315,6 +329,8 @@ sudo apt-get install fp-compiler fp-units-fcl
- [Generator guide](docs/codegen.md) — use and maintain `cli-fp-gen`
- [Roadmap](ROADMAP.md) — planned simplification work
- [Changelog](CHANGELOG.md) — release history
- [v1.3.3 release notes](docs/RELEASE_NOTES_v1.3.3.md) — stabilization changes
dated 2026-08-14

## Contributing

Expand Down
52 changes: 42 additions & 10 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ paths. Examples are executable documentation and should be verified in CI.
framework supports, and command execution no longer relies on a hidden unsafe
downcast.

## v1.3.3 — Stabilize Before Expanding (next)
## v1.3.3 — Stabilize Before Expanding (implementation complete; release 2026-08-14)

This is a focused stabilization release. It should make the current framework
safer to maintain before v1.4.0 adds another public entry point.
This focused stabilization release makes the current framework safer to
maintain before v1.4.0 adds another public entry point.

### Safe repository maintenance

Expand All @@ -48,25 +48,56 @@ safer to maintain before v1.4.0 adds another public entry point.
- Preserve unknown-option detection and add regression coverage for both
numeric forms.

### Hermetic tests and safe diagnostics

- Make the Windows and Linux framework test runners rebuild the unit graph
with the test define into an isolated output directory. A previous normal
build must not leave a stale `.ppu` that changes whether the tests compile.
- Keep output-capture state and entry points out of normal runtime builds while
retaining one production execution and help-rendering path.
- Redact values for registered password parameters from debug output and add a
regression test proving credentials are never printed.

### Internal maintenance boundaries

- Move help formatting into one internal renderer shared by the application
and base-command paths.
- Move completion calculation into a focused internal engine and delete
unreachable private callback branches and unused temporary allocations,
while retaining the deprecated public 1.x no-op methods.
- Single-source parameter lookup and password redaction for validation,
execution, and diagnostics.
- Decompose application dispatch into focused stages without changing the
`TCLIApplication` facade or `ICLIApplication` contract.

### Release acceptance criteria

- Cleanup scripts leave all tracked files intact.
- Framework, generator, and example compilation checks pass on Windows and
Linux.
- Help tests fail when required help content is removed or changed incorrectly.
- Negative integer and float values work in equals and separated forms.
- Framework tests pass after a normal non-test build has produced reusable
units in the source tree or another configured unit-search directory.
- Normal runtime builds contain no test-output capture state or entry points.
- Debug output never prints values supplied to password parameters.
- Help rendering, completion calculation, and parameter-value semantics each
have one internal implementation covered by characterization tests.
- Every behaviour changed in v1.3.3 is documented and has automated coverage.

### Non-goals

- No new public command API or breaking API changes.
- No new parameter kinds, generator capabilities, or completion features.
- No broad `TCLIApplication` split; that remains planned for v1.5.0.
- No replacement of the `TCLIApplication` facade or execution-state contract.
- No removal of public compatibility APIs or broad completion/help cleanup;
those changes remain planned for v1.5.0 and v2.0.0.
- No large historical-documentation cleanup mixed into the behavioural fixes.

**Maintenance outcome:** the repository can be cleaned safely, examples remain
buildable, and the test suite provides a dependable safety net for the v1.4.0
ergonomics work.
buildable, test results do not depend on stale compiler units, diagnostics do
not expose password values, and the test suite provides a dependable safety
net for the v1.4.0 ergonomics work.

## v1.4.0 — Make Simple CLIs Simple

Expand All @@ -83,13 +114,14 @@ ergonomics work.
**Maintenance outcome:** beginner-oriented ergonomics improve without creating
a second framework to maintain.

## v1.5.0 — Split the Application Core
## v1.5.0 — Finish the Application Core Boundaries

- Separate command selection and execution orchestration from parsing and
validation.
- Extract help rendering from `TCLIApplication` behind the output seam proven
in v1.3.3.
- Separate completion calculation from Bash and PowerShell script rendering.
- Extract Bash and PowerShell script rendering from `TCLIApplication`, building
on the completion engine introduced in v1.3.3.
- Strengthen the internal help and completion boundaries introduced in v1.3.3
without exposing them as new public APIs.
- Preserve existing observable behaviour with the v1.3.3 characterization
tests and focused tests around each extracted component.
- Keep these internal changes behind the stable public facade.
Expand Down
62 changes: 31 additions & 31 deletions clean-all-examples.ps1
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
# clean-all-examples.ps1
# Remove all built example binaries and unit output
# Remove generated example build artifacts while preserving tracked files.

$exampleBin = "example-bin"
if (Test-Path $exampleBin) {
Write-Host "🧹 Removing example-bin/ directory..." -ForegroundColor Yellow
Remove-Item $exampleBin -Recurse -Force
Write-Host "✅ example-bin/ cleaned." -ForegroundColor Green
} else {
Write-Host "ℹ️ example-bin/ does not exist. Nothing to clean." -ForegroundColor Gray
}
$RootDir = Split-Path -Parent $PSCommandPath

$examples = @(
$Examples = @(
'ColorDemo',
'ErrorHandlingDemo',
'LongRunningOpDemo',
Expand All @@ -19,28 +12,35 @@ $examples = @(
'SimpleDemo',
'SubCommandDemo'
)
$GeneratedExtensions = @(
'.o', '.ppu', '.compiled', '.or', '.a', '.rst', '.res', '.dbg', '.tds', '.lps'
)
$GeneratedFileNames = @('link.res')
foreach ($Example in $Examples) {
$GeneratedFileNames += $Example
$GeneratedFileNames += "$Example.exe"
}

foreach ($ex in $examples) {
$libPath = "examples/$ex/lib"
if (Test-Path $libPath) {
Write-Host "🧹 Removing old lib/ from examples/$ex..." -ForegroundColor Yellow
Remove-Item $libPath -Recurse -Force
}
$win64Path = "examples/$ex/x86_64-win64"
if (Test-Path $win64Path) {
Write-Host "🧹 Removing old x86_64-win64/ from examples/$ex..." -ForegroundColor Yellow
Remove-Item $win64Path -Recurse -Force
}
$linuxPath = "examples/$ex/x86_64-linux"
if (Test-Path $linuxPath) {
Write-Host "🧹 Removing old x86_64-linux/ from examples/$ex..." -ForegroundColor Yellow
Remove-Item $linuxPath -Recurse -Force
}
$backupPath = "examples/$ex/backup"
if (Test-Path $backupPath) {
Write-Host "🧹 Cleaning backup/ in examples/$ex..." -ForegroundColor Yellow
Get-ChildItem $backupPath -Include *.exe,*.dbg,*.o,*.ppu -Recurse | Remove-Item -Force
function Remove-GeneratedArtifacts([string]$Path) {
if (-not (Test-Path -LiteralPath $Path)) {
return
}

Get-ChildItem -LiteralPath $Path -File -Recurse -Force |
Where-Object {
($GeneratedExtensions -contains $_.Extension.ToLowerInvariant()) -or
($_.Name -like '*.lps.bak') -or
($GeneratedFileNames -contains $_.Name)
} |
ForEach-Object {
Write-Host "🧹 Removing generated artifact: $($_.FullName)" -ForegroundColor Yellow
Remove-Item -LiteralPath $_.FullName -Force
}
}

Remove-GeneratedArtifacts (Join-Path $RootDir 'example-bin')
foreach ($Example in $Examples) {
Remove-GeneratedArtifacts (Join-Path $RootDir "examples\$Example")
}

Write-Host "`n✅ Cleanup complete." -ForegroundColor Green
Write-Host "✅ Generated example build artifacts removed." -ForegroundColor Green
70 changes: 40 additions & 30 deletions clean-all-examples.sh
Original file line number Diff line number Diff line change
@@ -1,34 +1,44 @@
#!/bin/bash
# clean-all-examples.sh
# Remove all built example binaries and unit output

set -e

if [ -d "example-bin" ]; then
echo "🧹 Removing example-bin/ directory..."
rm -rf example-bin
echo "✅ example-bin/ cleaned."
else
echo "ℹ️ example-bin/ does not exist. Nothing to clean."
fi

for ex in ColorDemo ErrorHandlingDemo LongRunningOpDemo ProgressDemo RootCommandDemo SimpleDemo SubCommandDemo; do
if [ -d "examples/$ex/lib" ]; then
echo "🧹 Removing old lib/ from examples/$ex..."
rm -rf "examples/$ex/lib"
fi
if [ -d "examples/$ex/x86_64-win64" ]; then
echo "🧹 Removing old x86_64-win64/ from examples/$ex..."
rm -rf "examples/$ex/x86_64-win64"
fi
if [ -d "examples/$ex/x86_64-linux" ]; then
echo "🧹 Removing old x86_64-linux/ from examples/$ex..."
rm -rf "examples/$ex/x86_64-linux"
fi
if [ -d "examples/$ex/backup" ]; then
echo "🧹 Cleaning backup/ in examples/$ex..."
find "examples/$ex/backup" -type f \( -name '*.exe' -o -name '*.dbg' -o -name '*.o' -o -name '*.ppu' \) -delete
fi
# Remove generated example build artifacts while preserving tracked files.

set -eu

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

EXAMPLES="ColorDemo ErrorHandlingDemo LongRunningOpDemo ProgressDemo RootCommandDemo SimpleDemo SubCommandDemo"

remove_generated_artifacts() {
directory="$1"

[ -d "$directory" ] || return

find "$directory" -type f \( \
-name '*.o' -o \
-name '*.ppu' -o \
-name '*.compiled' -o \
-name '*.or' -o \
-name '*.a' -o \
-name '*.rst' -o \
-name '*.res' -o \
-name '*.dbg' -o \
-name '*.tds' -o \
-name '*.lps' -o \
-name '*.lps.bak' -o \
-name 'ColorDemo' -o -name 'ColorDemo.exe' -o \
-name 'ErrorHandlingDemo' -o -name 'ErrorHandlingDemo.exe' -o \
-name 'LongRunningOpDemo' -o -name 'LongRunningOpDemo.exe' -o \
-name 'ProgressDemo' -o -name 'ProgressDemo.exe' -o \
-name 'RootCommandDemo' -o -name 'RootCommandDemo.exe' -o \
-name 'SimpleDemo' -o -name 'SimpleDemo.exe' -o \
-name 'SubCommandDemo' -o -name 'SubCommandDemo.exe' \
\) -print -delete
}

remove_generated_artifacts "$ROOT_DIR/example-bin"

for ex in $EXAMPLES; do
remove_generated_artifacts "$ROOT_DIR/examples/$ex"
done

echo "\n✅ Cleanup complete."
echo "✅ Generated example build artifacts removed."
Loading