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
24 changes: 21 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772 # upstream commit
with:
toolchain: 1.97.1
components: rustfmt, clippy
components: rustfmt, clippy, llvm-tools-preview
- run: cargo fmt -- --check
- run: cargo check --workspace --all-targets
- run: cargo test
Expand All @@ -32,6 +32,17 @@ jobs:
shell: pwsh
run: cargo install cargo-deny --version 0.20.2 --locked
- run: cargo deny check
- name: Install cargo-llvm-cov
shell: pwsh
run: cargo install cargo-llvm-cov --version 0.8.7 --locked
- name: Test the production coverage analyzer
shell: pwsh
run: python -m unittest discover -s tests/coverage
- name: Enforce Rust production coverage >= 80%
shell: pwsh
run: |
cargo llvm-cov --workspace --json --output-path target/llvm-cov-export.json
python scripts/rust-production-coverage.py target/llvm-cov-export.json --threshold 80 --json target/production-coverage.json

powershell:
runs-on: windows-latest
Expand All @@ -47,9 +58,9 @@ jobs:
- name: Parse and lint PowerShell
shell: pwsh
run: ./scripts/validate-powershell.ps1
- name: Run Pester tests
- name: Run Pester tests with coverage gate
shell: pwsh
run: Invoke-Pester -Path ./tests/powershell -Output Detailed
run: ./scripts/invoke-pester-coverage.ps1

distribution:
strategy:
Expand Down Expand Up @@ -83,7 +94,14 @@ jobs:
}
}
- name: Run the npm bootstrap unit tests
# The canonical Node 24 leg also enforces the coverage gate; the
# 22/26 legs only check compatibility with the plain test run.
if: matrix.node-version != '24'
run: node --test "tests/npm/**/*.test.mjs"
- name: Run the npm bootstrap tests with coverage gate
if: matrix.node-version == '24'
shell: pwsh
run: ./scripts/invoke-npm-coverage.ps1
- name: Validate the npm tarball allow-list
shell: pwsh
run: |
Expand Down
12 changes: 11 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ permissions:
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
permissions:
actions: read
contents: read
Expand All @@ -24,9 +24,19 @@ jobs:
fail-fast: false
matrix:
include:
# Rust runs the buildless extractor (build-mode: none). It was tried
# on windows-latest in #43 with no extraction improvement (9/10
# files with macro-expansion errors on both platforms), so the
# simpler Ubuntu lane is kept. The residual is documented in
# SECURITY.md.
- language: rust
os: ubuntu-latest
build-mode: none
- language: actions
os: ubuntu-latest
build-mode: none
- language: javascript-typescript
os: ubuntu-latest
build-mode: none
steps:
- name: Checkout
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

# Test, coverage and generated reports
coverage/
# ...but the coverage analyzer unit tests are tracked source.
!tests/coverage/
coverage.lcov
TestResults/
*.trx
Expand Down Expand Up @@ -44,3 +46,7 @@ devnav-*.sha256
*.pem
*.pfx
*.p12

# Python tooling caches
__pycache__/
*.pyc
102 changes: 102 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Architecture

DevNav is a Windows-only folder navigator: a native Rust TUI binary driven from
PowerShell through a thin integration module. This document describes what
exists in the repository today; it does not describe planned work (see
[ROADMAP.md](ROADMAP.md)).

## Components

### Rust native navigator (`src/`)

A single binary (`dev`) plus a small library crate, with no runtime
dependencies beyond `windows-sys`:

- `main.rs` — CLI entry point: `--version`, config-only commands
(`--set-language`, `--detect-language`, `--set-shortcut`, `--clear-shortcut`),
root resolution (`--root` argument, then configured root, then `DEV_HOME`,
then the user profile), TUI launch, and result-file writing.
- `app.rs` — application state machine: modes (normal, help, filter, path,
alias, command, confirm-root, command manager/editor/delete confirmation),
directory listing, fuzzy filtering, favorites/aliases, and frame generation
(`render_rows` is pure string generation; `render` wraps it with the real
terminal handle).
- `config.rs` — the `config.tsv` parser/writer (see below) with atomic saves
(`ReplaceFileW` on Windows) and the `Shortcut` model shared by the lib and
bin targets.
- `i18n.rs` — dependency-free Spanish/English localization.
- `input.rs` / `terminal.rs` — Win32 console input and raw-mode handling
(Windows-only by design).
- `model.rs` — the `ShellResult` payload contract (`cd` / `exec` / `update`).
- `render.rs` — ANSI frame diffing renderer.

### PowerShell integration (`powershell/DevNav.psm1`)

The module the user actually invokes (`dev`). Responsibilities:

- Locate the installed executable (managed install vs. portable layouts).
- Language detection/init, startup update check, and config helpers that read
and write the same `config.tsv` as the Rust binary.
- Run the TUI with a `--result` temp file, then interpret the result: change
directory, execute a command in the selected folder, or run the updater —
actions that only a shell can perform on the user's session.
- Shortcut management (`Set-DevShortcut`, `Remove-DevShortcut`, …) and the
self-updater (`Update-DevNavigator`).

### Installer and profile integration

- `installer/DevNav.iss` — Inno Setup script producing per-user x64/ARM64
installers into `%LOCALAPPDATA%\Programs\DevNav`.
- `installer/ProfileIntegration.ps1` — installs/removes the PowerShell module
and profile hook for the installing user.
- `install.ps1` — bootstraps a managed installation from a release.

### Distribution channels

The GitHub Release is the canonical source of every artifact; all other
channels derive from it:

- **npm bootstrap** (`packaging/npm/bin/devnav.mjs`) — a transient delivery
channel with no runtime dependencies. The tarball embeds the canonical Inno
installers plus `release-manifest.json`; the bootstrap verifies SHA-256
hashes before delegating to Inno. It never owns installed files.
- **Scoop** — portable artifacts in the release feed the separate
`JacobOptimiza/scoop-bucket` repository (`packaging/scoop/`).
- **WinGet** — immutable versioned manifests with SHA-256
(`packaging/winget/`), submitted to `microsoft/winget-pkgs`.

## `dev` invocation flow

1. The user runs `dev` in PowerShell; the module resolves the executable and
passes `--result <temp file>` (and optionally `--root`).
2. The Rust binary reads `%LOCALAPPDATA%\DevNav\config.tsv`, enters the TUI on
the current console, and handles all interaction locally.
3. On exit it writes one NUL-separated record to the result file:
`cd\0<path>\0`, `exec\0<path>\0<command>` or `update\0\0`.
4. The PowerShell module reads the file and performs the action in the user's
session (or triggers the updater). The temp file is removed afterwards.

## Configuration (`config.tsv`)

Single flat TSV file owned jointly by the Rust binary and the PowerShell
module, read and written by both through stable key prefixes: `root`,
`show_favorites`, `check_updates`, `language`, `favorite\t<path>`,
`alias\t<path>\t<alias>`, and `shortcut\t<slot>\t<alias>\t<command>`
(1–9, executed as Shift+digit in the TUI). The Rust writer escapes `%`, tab
and newline and replaces the file atomically.

## Trust boundaries

- **Rust binary**: local process with console and filesystem access only. It
never executes commands itself; it only records intents in the result file.
- **PowerShell module**: the trust pivot. It executes the `cd`/`exec` the
binary requested — commands typed by the user in the TUI or configured
shortcuts from `config.tsv` — in the user's own session and privileges.
- **Network** is used in exactly two places: the startup/friendly update check
and updater (GitHub Releases API over TLS), and package-manager downloads
during initial installation (npm registry / Scoop / WinGet). The TUI itself,
navigation, favorites, aliases, and shortcuts are fully offline.
- **Integrity of downloaded artifacts**: the npm bootstrap verifies SHA-256
hashes from `release-manifest.json` before running the installer; Scoop and
WinGet manifests carry release SHA-256 hashes. Checksums verify integrity,
not authenticity (see [SECURITY.md](SECURITY.md)).
111 changes: 111 additions & 0 deletions ASSURANCE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Assurance case

This page states verifiable claims about DevNav, the evidence that backs them,
and the residual limitations that remain. It is intentionally narrow: only
properties the repository itself can demonstrate.

## Build and test quality

**Claim.** The Rust workspace builds warning-free and its full test suite
passes on the pinned toolchain (1.97.1).

**Evidence.** CI `validate` job: `cargo fmt --check`, `cargo check
--workspace --all-targets`, `cargo test --workspace`, `cargo clippy
--workspace --all-targets -- -D warnings`, `cargo deny check`. PowerShell:
parser + PSScriptAnalyzer + Pester (`.github/workflows/ci.yml`).

**Residual limitation.** None for this claim.

## Test coverage ≥ 80% (per language)

**Claim.** Test coverage stays at or above 80% for Rust production code, the
PowerShell integration, and the npm bootstrap, enforced in CI.

**Evidence.**
- Rust: `scripts/rust-production-coverage.py` computes production-only
coverage from `cargo llvm-cov` 0.8.7 JSON, excluding only `#[cfg(test)]`
items (analyzed by its own unit tests, `tests/coverage/`). The CI `validate`
job fails below 80% lines or regions. Current: 87.60% lines / 86.30%
regions.
- PowerShell: `scripts/invoke-pester-coverage.ps1` runs Pester 6.1.0 once
with JaCoCo coverage over `powershell/DevNav.psm1`, `install.ps1` and
`installer/ProfileIntegration.ps1`; the CI `powershell` job fails below 80%
commands or lines. Current: 82.19% / 82.87%.
- JavaScript: `scripts/invoke-npm-coverage.ps1` runs the bootstrap tests under
Node's native test runner with `--experimental-test-coverage`; the
Node 24 leg of the CI `distribution` job fails below 80% lines. Current:
93.79% lines. (Node reports *line* coverage; the historical
93.78% *statement* figure is kept only as a baseline reference.)

**Residual limitation.** Rust coverage excludes host-bound code paths
(`Terminal::enter/size/drop`, `read_key`, `Renderer::draw`, `run()/main()`,
`detect_system_locale`) from the numerator only because they require a real
interactive console; they remain in the denominator and are not automatically
covered by this suite.

## Dependency policy

**Claim.** Dependencies are pinned (`Cargo.lock`), license-audited and
vulnerability-checked in CI on pushes to `main` and pull requests.

**Evidence.** `cargo deny check` (advisories, bans, licenses, sources) in the
CI `validate` job; Dependabot configuration; the npm bootstrap has zero
runtime dependencies.

**Residual limitation.** Advisory data depends on the RustSec/advisory-database
feed at check time.

## Static analysis

**Claim.** CodeQL analyzes Rust, GitHub Actions and JavaScript/TypeScript on
pushes to `main` and pull requests.

**Evidence.** `.github/workflows/codeql.yml`; results upload to
security/code-scanning.

**Residual limitation.** Rust extraction runs the buildless extractor
(`build-mode: none`) and currently reports 9 of 10 files with macro-expansion
diagnostics; running the lane on `windows-latest` was tried and did not reduce
them. A green CodeQL run is therefore not evidence of extraction-clean
analysis.

## Fuzzing

**Claim.** The `config.tsv` parser is fuzz-tested automatically on qualifying
pull requests.

**Evidence.** ClusterFuzzLite (`.clusterfuzzlite/`, `.github/workflows/cflite_pr.yml`)
on pull requests affecting its Rust source or fuzzing integration.

**Residual limitation.** Fuzzing covers the parser target only; it does not
replace the unit suites.

## Release integrity and provenance

**Claim.** Release artifacts are built once by the release workflow, checksums
(SHA-256) are published for every artifact, channels derive from the canonical
GitHub Release, and future artifacts receive GitHub build attestations.

**Evidence.** `.github/workflows/release.yml`; `release-manifest.json`
verification in the npm bootstrap; Scoop/WinGet manifests carry release
SHA-256 hashes; version consistency across `Cargo.toml`, tag, release,
`DevNav.psd1`, `package.json` and the Scoop template is enforced by CI.

**Residual limitation.** SHA-256 checksums verify integrity, not authenticity.
A build attestation proves workflow provenance and is not equivalent to a
legacy code-signing signature on the binaries.

## Package and version consistency

**Claim.** One version is shared by every channel and checked mechanically.

**Evidence.** CI consistency checks abort the release on any mismatch; npm and
Scoop enforce a `0.10.0` floor for multichannel packaging.

**Residual limitation.** None for this claim.

## Not automatable

The following remain human responsibilities and are not covered by any
automated gate: code review quality, issue triage, the decision to publish a
release, and manual verification of the interactive TUI on real consoles.
44 changes: 43 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,53 @@ cargo check --workspace --all-targets
cargo test --workspace
cargo clippy --workspace --all-targets -- -D warnings
cargo deny check
# Rust production-only coverage gate (requires cargo-llvm-cov 0.8.7):
cargo llvm-cov --workspace --json --output-path target/llvm-cov-export.json
python scripts/rust-production-coverage.py target/llvm-cov-export.json --threshold 80
python -m unittest discover -s tests/coverage # analyzer unit tests
./scripts/validate-powershell.ps1
Invoke-Pester -Path ./tests/powershell
./scripts/invoke-pester-coverage.ps1 # Pester + coverage gate
node --test "tests/npm/**/*.test.mjs"
./scripts/invoke-npm-coverage.ps1 # Node coverage gate
```

## Coding standards

### Rust

- `cargo fmt` formatting is mandatory; `clippy -D warnings` must stay clean.
- Use `Result`/`Option` and explicit error propagation (`?`) instead of
panicking in recoverable paths; keep `unwrap`/`expect` to tests and truly
invariant conditions.
- New behavior needs unit tests next to the module (`#[cfg(test)]`); bug fixes
need regression tests. Coverage gates enforce >= 80% production-only lines
and regions.
- `unsafe` is allowed only where a Win32 API boundary requires it and must
carry a `// SAFETY:` justification comment.

### PowerShell

- Must pass the repository PSScriptAnalyzer settings
(`./scripts/validate-powershell.ps1`) and Pester tests with the coverage
gate.
- Prefer testable, non-interactive functions; use `SupportsShouldProcess`
(`-WhatIf`/`-Confirm`) on cmdlets that change state.

### JavaScript (npm bootstrap)

- Node `>= 22` (CI tests 22, 24, 26; Node 24 is the release baseline); use
standard-library APIs only — no runtime dependencies.
- The bootstrap tests (`tests/npm`) must pass; line coverage is gated at
>= 80% on the Node 24 leg.

### General

- Keep changes small, scoped to one purpose, and consistent with existing
style; behavior changes require behavior tests.
- GitHub Actions references must be pinned to full commit SHAs.
- Never edit published release artifacts, versions, or the npm package in
place; versions are immutable once public.

The repository also fuzzes the `config.tsv` parser with ClusterFuzzLite on
pull requests that affect its Rust source or fuzzing integration. Fuzzing is a
supplement to, not a replacement for, the ordinary test suite.
Expand Down
Loading