diff --git a/README.md b/README.md index 3b71f50..feb3bdb 100644 --- a/README.md +++ b/README.md @@ -5,29 +5,12 @@ [![CI](https://github.com/continuous-delphi/delphi-inspect/actions/workflows/ci.yml/badge.svg)](https://github.com/continuous-delphi/delphi-inspect/actions/workflows/ci.yml) ![Status](https://img.shields.io/badge/status-incubator-orange) ![License](https://img.shields.io/github/license/continuous-delphi/delphi-inspect.svg) -![Version](https://img.shields.io/badge/version-0.1.0-blue) ![Delphi](https://img.shields.io/badge/delphi-red) ![PowerShell](https://img.shields.io/badge/powershell-blue) ![Continuous Delphi](https://img.shields.io/badge/org-continuous--delphi-red) Deterministic Delphi toolchain discovery and normalization for Delphi systems. -This repository provides two fully independent implementations that share a mission and a -contract: - -- `source/delphi` -- Native Delphi Windows console executable -- `source/pwsh` -- Multi-platform PowerShell 7.4+ implementation - -Neither implementation is primary. They serve overlapping but distinct audiences and are both -first-class deliverables. - -## PowerShell Compatibility - -Runs on the widely available Windows PowerShell 5.1 (`powershell.exe`) -and the newer PowerShell 7+ (`pwsh`). - -Note: the test suite requires `pwsh`. - ## TLDR; ```powershell @@ -67,72 +50,14 @@ requiring you to change everything at once. The goal is _not_ to replace your workflow - the goal is to _incrementally enhance_ it. -## Two Implementations, One Mission - -### Delphi executable (`source/delphi`) - -**Audience:** - -- Security-conscious shops that will not use cloud CI -- Teams maintaining legacy infrastructure -- Air-gapped environments -- Single-developer systems with tribal build knowledge -- Organizations building their first repeatable build process - -**Operational requirements:** - -- Windows (Win32/Win64) -- No PowerShell required -- No Git required - -The Delphi executable embeds the dataset as a compiled resource and requires no external -files for basic operation. It is a true single-file xcopy deployment. - -Dataset resolution priority: - -1. `-DataFile ` if specified on the command line -2. `delphi-compiler-versions.json` found alongside the executable -3. Embedded resource compiled into the executable - -This means the executable works out of the box, but can be updated to a newer dataset -by placing the JSON file alongside it without recompiling. All output indicates which -data source was used via the `datasetSource` field. - -For many shops, this will be the only implementation used. - -### PowerShell implementation (`source/pwsh`) - -**Audience:** - -- Teams using modern CI (GitHub Actions, GitLab CI, Jenkins, etc.) -- Shops comfortable with scripting -- Hybrid environments combining scripting and native builds - -**Operational requirements:** - -- PowerShell 7.4+ -- Windows for registry-based detection commands (RAD Studio currently only installs on Windows) - -Dataset resolution priority (if `-DataFile` is not specified): - -1. `-DataFile ` if specified on the command line -2. `delphi-compiler-versions.json` found alongside the script -3. Embedded `here-string` compiled into the script by the generator - -**Development and test requirements:** - -- PowerShell 7.4+ -- Pester 5.7+ -- CI pins Pester to a specific patch version for reproducibility +## PowerShell Compatibility -## Shared Contract +Runs on the widely available Windows PowerShell 5.1 (`powershell.exe`) +and the newer PowerShell 7+ (`pwsh`). -- Both implementations provide equivalent behavior and identical exit codes for shared commands -- Human-readable text output may differ between implementations -- Machine-readable JSON output will remain _semantically equivalent_ across both implementations - regardless of formatting or whitespace. +Note: the test suite requires `pwsh`. -### Shared commands +## Commands | Command | Description | |-------------------|--------------------------------------------------| @@ -142,10 +67,6 @@ Dataset resolution priority (if `-DataFile` is not specified): | `DetectLatest` | Return the single highest-versioned ready install | | `Resolve` | Resolve an alias or VER### to a canonical entry | -Both implementations use single-dash PascalCase switches (`-Version`, `-ListKnown`). -This is the recognized PowerShell standard and is adopted for both implementations to -ensure identical parameter syntax. - See [docs/commands.md](docs/commands.md) for full command reference including switches, output formats, exit codes, and any functionality differences between implementations. @@ -159,21 +80,7 @@ output formats, exit codes, and any functionality differences between implementa - `json` -- machine envelope with `ok`/`command`/`tool`/`result` structure. Suitable for CI pipelines and non-PowerShell consumers. -### -Readiness filter (-ListInstalled only) - -`-Readiness` controls which readiness states are included in `-ListInstalled` -output. Applies to all formats. Default is `@('ready')`. - -Valid values: `ready`, `partialInstall`, `notFound`, `notApplicable`, `all`. - -The special value `all` bypasses filtering entirely and returns every entry. -Use `-Readiness all` to restore the previous behavior of returning all entries -regardless of state (prior releases always returned all entries for `-Format json`). - -### Machine output contract - -When JSON output is requested (`-Format json`), both implementations emit a stable JSON -envelope. +### Machine output Property names in `result` match the dataset field names exactly. @@ -185,7 +92,6 @@ Success (`-Version`): "command": "version", "tool": { "name": "delphi-inspect", - "impl": "pwsh|delphi", "version": "X.Y.Z" }, "result": { @@ -204,7 +110,6 @@ Success (`-Resolve`): "command": "resolve", "tool": { "name": "delphi-inspect", - "impl": "pwsh|delphi", "version": "X.Y.Z" }, "result": { @@ -229,7 +134,6 @@ Error: "command": "version", "tool": { "name": "delphi-inspect", - "impl": "pwsh|delphi", "version": "X.Y.Z" }, "error": { @@ -241,31 +145,28 @@ Error: ## Dataset -Both implementations consume the canonical dataset from +The implementations consumes the canonical dataset from [delphi-compiler-versions](https://github.com/continuous-delphi/delphi-compiler-versions). -The JSON dataset is the single source of truth. Version tables should not be duplicated in code. -During development, the dataset is referenced as a Git submodule. +The JSON dataset is the single source of truth. + +_todo_ The `gen/` folder produces a standalone `pwsh` script with the dataset embedded as a -PowerShell `here-string`. (The Delphi executable references the dataset directly as a project -resource.) +PowerShell `here-string`. -Both standalone artifacts support the same three-tier dataset resolution priority. Placing -a newer `delphi-compiler-versions.json` alongside either artifact will take precedence over +Artifacts support the same three-tier dataset resolution priority. A newer +`delphi-compiler-versions.json` file found will take precedence over the embedded data without regenerating or recompiling. ## Maturity -This repository is currently `incubator`. Both implementations are under active development. -It will graduate to `stable` once: +This repository is currently `incubator`. It will graduate to `stable` once: -- The shared command contract is considered frozen. -- Both implementations pass the shared contract test suite. -- CI is in place for the PowerShell implementation. +- The command contract is considered frozen. - At least one downstream consumer exists. -Until graduation, breaking changes may occur in both implementations. +Until graduation, breaking changes may occur. ![continuous-delphi logo](https://continuous-delphi.github.io/assets/logos/continuous-delphi-480x270.png) diff --git a/docs/commands.md b/docs/commands.md index b91c4f3..5c0a431 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -23,7 +23,7 @@ By default, invoking the script with **no switches** performs the # Usage - pwsh ./source/pwsh/delphi-inspect.ps1 [action] [options] + pwsh ./source/delphi-inspect.ps1 [action] [options] ------------------------------------------------------------------------ @@ -290,10 +290,6 @@ bypasses filtering entirely and returns every entry regardless of state. Default is `@('ready')`, meaning only fully ready installations are returned. Exit code 6 fires when the filtered list is empty. -**Behavior change vs prior releases:** Previous releases always returned -all entries in JSON format regardless of state. If you relied on that -behavior, add `-Readiness all` to restore it. - - `DCC` -- direct invocation of the command-line compiler (`dcc32.exe` or `dcc64.exe` depending on platform). Requires the compiler binary and a correctly configured `.cfg` file. diff --git a/source/pwsh/delphi-inspect.ps1 b/source/delphi-inspect.ps1 similarity index 97% rename from source/pwsh/delphi-inspect.ps1 rename to source/delphi-inspect.ps1 index fb00f1c..d0adf16 100644 --- a/source/pwsh/delphi-inspect.ps1 +++ b/source/delphi-inspect.ps1 @@ -130,7 +130,7 @@ function Resolve-DefaultDataFilePath { # Prefer the submodule layout: # ../delphi-compiler-versions/data/delphi-compiler-versions.json # Use Join-Path to remain path-separator-safe if invoked on non-Windows runners. - $repoRoot = Split-Path (Split-Path $scriptDir -Parent) -Parent + $repoRoot = Split-Path $scriptDir -Parent $specRoot = Join-Path (Join-Path $repoRoot 'submodules') 'delphi-compiler-versions' $dataDir = Join-Path $specRoot 'data' $defaultPath = Join-Path $dataDir 'delphi-compiler-versions.json' @@ -174,7 +174,7 @@ function Write-JsonError { Write-JsonOutput ([pscustomobject]@{ ok = $false command = $Command - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } error = [pscustomobject]@{ code = $Code; message = $Message } } ) } @@ -208,7 +208,7 @@ function Write-VersionInfo { Write-JsonOutput ([pscustomobject]@{ ok = $true command = 'version' - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } result = [pscustomobject]@{ schemaVersion = $schemaVersion dataVersion = $dataVersion @@ -278,7 +278,7 @@ function Write-ResolveOutput { Write-JsonOutput ([pscustomobject]@{ ok = $true command = 'resolve' - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } result = [pscustomobject]@{ verDefine = $Entry.verDefine productName = $Entry.productName @@ -343,7 +343,7 @@ function Write-ListKnownOutput { Write-JsonOutput ([pscustomobject]@{ ok = $true command = 'listKnown' - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } result = [pscustomobject]@{ schemaVersion = $Data.schemaVersion dataVersion = $Data.dataVersion @@ -588,7 +588,7 @@ function Write-ListInstalledOutput { Write-JsonOutput ([pscustomobject]@{ ok = $true command = 'listInstalled' - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } result = [pscustomobject]@{ platform = $Platform buildSystem = $BuildSystem @@ -685,7 +685,7 @@ function Write-DetectLatestOutput { Write-JsonOutput ([pscustomobject]@{ ok = $true command = 'detectLatest' - tool = [pscustomobject]@{ name = 'delphi-inspect'; impl = 'pwsh'; version = $ToolVersion } + tool = [pscustomobject]@{ name = 'delphi-inspect'; version = $ToolVersion } result = [pscustomobject]@{ platform = $Platform buildSystem = $BuildSystem diff --git a/tests/pwsh/PSScriptAnalyzer.Tests.ps1 b/tests/pwsh/PSScriptAnalyzer.Tests.ps1 index 47f9163..9e676c5 100644 --- a/tests/pwsh/PSScriptAnalyzer.Tests.ps1 +++ b/tests/pwsh/PSScriptAnalyzer.Tests.ps1 @@ -2,7 +2,7 @@ #Requires -Modules @{ ModuleName='PSScriptAnalyzer'; ModuleVersion='1.21.0' } <# .SYNOPSIS - PSScriptAnalyzer lint test for source/pwsh/delphi-inspect.ps1 + PSScriptAnalyzer lint test for source/delphi-inspect.ps1 .DESCRIPTION Runs Invoke-ScriptAnalyzer against delphi-inspect.ps1 using the default diff --git a/tests/pwsh/Resolve-DefaultDataFilePath.Tests.ps1 b/tests/pwsh/Resolve-DefaultDataFilePath.Tests.ps1 index 2b1c2b7..12566ec 100644 --- a/tests/pwsh/Resolve-DefaultDataFilePath.Tests.ps1 +++ b/tests/pwsh/Resolve-DefaultDataFilePath.Tests.ps1 @@ -32,11 +32,11 @@ Describe 'Resolve-DefaultDataFilePath' { . $script:scriptUnderTest } - Context 'Given a script path in a standard source/pwsh layout' { + Context 'Given a script path in a standard source layout' { BeforeAll { $fakeRepo = Join-Path ([System.IO.Path]::GetTempPath()) 'delphi-inspect-test-repo' - $fakeScriptDir = Join-Path $fakeRepo 'source' 'pwsh' + $fakeScriptDir = Join-Path $fakeRepo 'source' $script:fakeScriptPath = Join-Path $fakeScriptDir 'delphi-inspect.ps1' # Create a placeholder file so the guard's Test-Path check passes. $null = New-Item -ItemType Directory -Path $fakeScriptDir -Force diff --git a/tests/pwsh/TestHelpers.ps1 b/tests/pwsh/TestHelpers.ps1 index 325512d..ed031fb 100644 --- a/tests/pwsh/TestHelpers.ps1 +++ b/tests/pwsh/TestHelpers.ps1 @@ -35,7 +35,7 @@ # the loaded functions land in the correct scope for It blocks. function Get-ScriptUnderTestPath { - $path = Join-Path $PSScriptRoot '..' '..' 'source' 'pwsh' 'delphi-inspect.ps1' + $path = Join-Path $PSScriptRoot '..' '..' 'source' 'delphi-inspect.ps1' return [System.IO.Path]::GetFullPath($path) } diff --git a/tests/pwsh/Write-JsonError.Tests.ps1 b/tests/pwsh/Write-JsonError.Tests.ps1 index c0a18ae..1393986 100644 --- a/tests/pwsh/Write-JsonError.Tests.ps1 +++ b/tests/pwsh/Write-JsonError.Tests.ps1 @@ -48,10 +48,6 @@ Describe 'Write-JsonError' { $script:json.tool.name | Should -Be 'delphi-inspect' } - It 'tool.impl is pwsh' { - $script:json.tool.impl | Should -Be 'pwsh' - } - It 'tool.version is 0.1.0' { $script:json.tool.version | Should -Be '0.1.0' } diff --git a/tests/pwsh/fixtures/detect-registry-error-shim.ps1 b/tests/pwsh/fixtures/detect-registry-error-shim.ps1 index 3d27b38..35cda5c 100644 --- a/tests/pwsh/fixtures/detect-registry-error-shim.ps1 +++ b/tests/pwsh/fixtures/detect-registry-error-shim.ps1 @@ -26,7 +26,7 @@ $ErrorActionPreference = 'Stop' # The dot-source guard ($MyInvocation.InvocationName -eq '.') fires and returns # before the top-level try/catch, so only functions and variables defined above # the guard (including all $Exit* constants) are imported. -$mainScript = Join-Path $PSScriptRoot '..' '..' '..' 'source' 'pwsh' 'delphi-inspect.ps1' +$mainScript = Join-Path $PSScriptRoot '..' '..' '..' 'source' 'delphi-inspect.ps1' $mainScript = [System.IO.Path]::GetFullPath($mainScript) # Save the shim's parameter values before dot-sourcing. When a script is dot-sourced