Bundled PowerShell CI orchestration layer for Delphi projects. Packages compatible versions of the standalone Continuous-Delphi tools and exposes a single opinionated command surface for local and CI use.
delphi-powershell-ci wraps and orchestrates these standalone tools:
| Tool | Role |
|---|---|
| delphi-compiler-versions | Canonical version mapping |
| delphi-inspect | Detects installed Delphi toolchains |
| delphi-clean | Removes Delphi build artifacts |
| delphi-msbuild | Drives MSBuild for Delphi projects |
| delphi-dccbuild | Drives DCC builds for Delphi projects |
The standalone tools remain individually usable and separately versioned. This repo packages compatible versions together and provides a simpler public interface for day-to-day CI workflows.
v1 supports Clean, Build, and Test steps.
Not in v1: linting, coverage, SBOM, quality gates, GitHub Actions workflows, PowerShell Gallery publication.
Runs on the widely available Windows PowerShell 5.1 (powershell.exe)
and the newer PowerShell 7+ (pwsh).
Note: the test suite requires pwsh.
No additional modules are required at runtime. The bundled tools are
included in this repository under source/bundled-tools/.
The module detects which executable is available at load time, preferring
pwsh when both are present.
Import the module, then call Invoke-DelphiCi.
Import-Module .\source\Delphi.PowerShell.CI.psd1If your repository has a single .dproj under source\:
Invoke-DelphiCiThis cleans with the lite level, detects the latest Delphi installation,
and builds Win32 Debug.
Clean + Build ConsoleProject:
Invoke-DelphiCi -ProjectFile .\examples\ConsoleProjectGroup\Source\ConsoleProject.dprojInvoke-DelphiCi -Steps Clean -ProjectFile .\source\MyApp.dprojInvoke-DelphiCi -Steps Build -ProjectFile .\source\MyApp.dprojInvoke-DelphiCi -Steps Clean,Build,Test `
-ProjectFile .\source\MyApp.dproj `
-TestProjectFile .\tests\MyApp.Tests.dproj `
-TestDefines CIThe CI define switches DUnitX from the TestInsight IDE runner to the
headless console runner. It is not injected automatically.
Invoke-DelphiCi -ProjectFile .\source\MyApp.dproj -Toolchain VER370-Toolchain Latest (the default) detects the highest ready installation.
Any other value is passed to delphi-inspect as a version name or
compiler identifier (e.g. VER370, Delphi 13 Florence).
Invoke-DelphiCi -ProjectFile .\source\MyApp.dproj `
-Configuration Release -Platform Win64Invoke-DelphiCi -ConfigFile .\delphi-ci.jsonSee Examples\delphi-ci.json for a fully annotated config file.
Invoke-DelphiCi -VersionInfoDisplays the module version and the version of each bundled tool.
Import the module and call Invoke-DelphiCi directly. The function always
returns a structured result object; your script decides what to do with it:
Import-Module .\source\Delphi.PowerShell.CI.psd1
$run = Invoke-DelphiCi -ProjectFile .\source\MyApp.dproj
if (-not $run.Success) { exit 1 }Use this when you want to inspect step details, branch on the result, or
compose Invoke-DelphiCi into a larger script.
tools\delphi-ci.ps1 imports the module and owns the process exit code.
It exits 0 on success and 1 on failure. No result object is written to the
pipeline in run mode -- the exit code is the signal.
# Clean and build
.\tools\delphi-ci.ps1 -ProjectFile .\source\MyApp.dproj
# Full pipeline with test
.\tools\delphi-ci.ps1 -Steps Clean,Build,Test `
-ProjectFile .\source\MyApp.dproj `
-TestProjectFile .\tests\MyApp.Tests.dproj `
-TestDefines CIUse this when a CI runner (GitHub Actions, GitLab CI, Jenkins, etc.) needs to read a process exit code to determine pass or fail.
- Explicit CLI parameters
- JSON config file fields
- Built-in defaults
For example, a -Platform supplied on the command line always wins over the same
field in the config file.
{
"root": ".",
"steps": ["Clean", "Build", "Test"],
"clean": {
"level": "lite"
},
"build": {
"projectFile": "source/MyApp.dproj",
"engine": "MSBuild",
"toolchain": { "version": "Latest" },
"platform": "Win32",
"configuration": "Debug",
"defines": []
},
"test": {
"testProjectFile": "tests/MyApp.Tests.dproj",
"defines": ["CI"],
"timeoutSeconds": 10,
"build": true,
"run": true
}
}All fields are optional. Absent fields fall back to built-in defaults.
root is resolved relative to the config file's directory when it is a
relative path or ..
| Level | What is removed |
|---|---|
lite |
Compiler caches, IDE state (.dcu, .identcache, __history, etc.) |
build |
Everything in lite, plus build outputs (.exe, .dll, .bpl, platform output folders, etc.) |
full |
Everything in build, plus user-local IDE files (.~*, FireDAC project cache, etc.) |
Default level is lite.
When no -ProjectFile is given, discovery searches in order:
<root>-- if exactly one.dprojis present here<root>\source-- fallback<root>\..\source-- tools-folder convention fallback
Discovery stops at the first location that yields results. It fails with
a clear error if no .dproj is found, or if more than one is found and
no explicit file was given.
The step commands can also be called directly.
# Clean only -- lite level against the current directory
Invoke-DelphiClean
# Clean with build level
Invoke-DelphiClean -Level build -Root .\source
# Build only -- latest Delphi, Win32 Debug
Invoke-DelphiBuild -ProjectFile .\source\MyApp.dproj
# Build with explicit options
Invoke-DelphiBuild -ProjectFile .\source\MyApp.dproj `
-Platform Win64 -Configuration Release -Toolchain VER370
# Build and run a DUnitX test project
Invoke-DelphiTest -TestProjectFile .\tests\MyApp.Tests.dproj -Defines CI
# Build the test project without running it
Invoke-DelphiTest -TestProjectFile .\tests\MyApp.Tests.dproj -Defines CI -Run $falseInvoke-DelphiCi always returns a structured result object.
$run = Invoke-DelphiCi -ProjectFile .\source\MyApp.dproj
if (-not $run.Success) {
$failed = $run.Steps | Where-Object { -not $_.Success }
Write-Error "Failed steps: $($failed.StepName -join ', ')"
}
Write-Host "Total time: $($run.Duration.TotalSeconds.ToString('F2'))s"Because the result is always returned, a CI wrapper script can map it to a process exit code directly:
$run = Invoke-DelphiCi -ProjectFile .\source\MyApp.dproj
exit [int](-not $run.Success)Or use the included wrapper, which does this automatically:
.\tools\delphi-ci.ps1 -ProjectFile .\source\MyApp.dprojResult shape:
| Field | Type | Notes |
|---|---|---|
Success |
Boolean | $true when every step succeeded |
Duration |
TimeSpan | Wall-clock time for the run |
ProjectFile |
String | Resolved project file path |
Steps |
Object[] | One result per step that ran |
Clean and Build step results have StepName, Success, Duration,
ExitCode, Tool, Message, and ProjectFile. Test step results
additionally have TestProjectFile and TestExecutable in place of
ProjectFile.
tools (included, no install needed)
delphi-clean.ps1
delphi-inspect.ps1
delphi-msbuild.ps1
delphi-dccbuild.ps1
source/ PowerShell module source
Delphi.PowerShell.CI.psm1
bundled-tools/ Packaged standalone
Private/ Internal helpers (not exported)
Public/ Exported commands
Examples/ Integration test projects and example config
ConsoleProjectGroup/ Simple Delphi console app and DUnitX test project
docs/ Per-command reference documentation
Get-DelphiCiConfig.md
Invoke-DelphiClean.md
Invoke-DelphiBuild.md
Invoke-DelphiCi.md
Invoke-DelphiTest.md
tests/ Pester test suite
run-tests.ps1
pwsh/
| Command | Description |
|---|---|
Invoke-DelphiCi |
Primary orchestration command |
Invoke-DelphiClean |
Clean step |
Invoke-DelphiBuild |
Build step |
Invoke-DelphiTest |
Test step (build and run a DUnitX test project) |
Get-DelphiCiConfig |
Inspect resolved configuration |
Full parameter reference and examples for each command are in docs/.
This repository is currently incubator. Both implementations are under active development.
It will graduate to stable once:
- At least one downstream consumer exists.
Until graduation, breaking changes may occur
This repository follows the Continuous Delphi organization taxonomy. See cd-meta-org for navigation and governance.
docs/org-taxonomy.md-- naming and tagging conventionsdocs/versioning-policy.md-- release and versioning rulesdocs/repo-lifecycle.md-- lifecycle states and graduation criteria

