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
131 changes: 16 additions & 115 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 <path>` 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 <path>` 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 |
|-------------------|--------------------------------------------------|
Expand All @@ -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.

Expand All @@ -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.

Expand All @@ -185,7 +92,6 @@ Success (`-Version`):
"command": "version",
"tool": {
"name": "delphi-inspect",
"impl": "pwsh|delphi",
"version": "X.Y.Z"
},
"result": {
Expand All @@ -204,7 +110,6 @@ Success (`-Resolve`):
"command": "resolve",
"tool": {
"name": "delphi-inspect",
"impl": "pwsh|delphi",
"version": "X.Y.Z"
},
"result": {
Expand All @@ -229,7 +134,6 @@ Error:
"command": "version",
"tool": {
"name": "delphi-inspect",
"impl": "pwsh|delphi",
"version": "X.Y.Z"
},
"error": {
Expand All @@ -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)

Expand Down
6 changes: 1 addition & 5 deletions docs/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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]

------------------------------------------------------------------------

Expand Down Expand Up @@ -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.
Expand Down
14 changes: 7 additions & 7 deletions source/pwsh/delphi-inspect.ps1 → source/delphi-inspect.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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 }
} )
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/pwsh/PSScriptAnalyzer.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/pwsh/Resolve-DefaultDataFilePath.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/pwsh/TestHelpers.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
4 changes: 0 additions & 4 deletions tests/pwsh/Write-JsonError.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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'
}
Expand Down
2 changes: 1 addition & 1 deletion tests/pwsh/fixtures/detect-registry-error-shim.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading