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
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Custom xUnit attributes are sometimes used for conditional test execution:
#### CI/CD Pipeline
- **Build workflow**: `.github/workflows/build.yml` - runs on PR and push to main/rel/feature branches
- **Publish workflow**: Builds Native AOT archives for the six-RID matrix and attaches them (plus install.sh / install.ps1) to the GitHub Release. Does not publish to nuget.org or Sleet.
- **CI prerelease**: `.github/workflows/ci-release.yml` runs after a successful `build` on `main` and rewrites a rolling `ci` prerelease (`--latest=false`) so dogfood is `NDNX_VERSION=ci` / `ndnx --update ci`.
- **CI prerelease**: `.github/workflows/ci-release.yml` runs after a successful `build` on `main` and rewrites a rolling `ci` prerelease (`--latest=false`) so dogfood is `NDX_VERSION=ci` / `ndx --update ci`.
- **OS matrix**: Configured in `.github/workflows/os-matrix.json` (defaults to ubuntu-latest)

### Special Files and Tools
Expand Down
22 changes: 11 additions & 11 deletions .github/workflows/ci-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ env:
VersionLabel: ${{ github.event.workflow_run.head_sha || github.sha }}
GH_TOKEN: ${{ secrets.GH_TOKEN }}
MSBUILDTERMINALLOGGER: auto
NDNX_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
NDX_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}

jobs:
native-aot:
Expand Down Expand Up @@ -53,7 +53,7 @@ jobs:
run: |
$out = "artifacts/${{ matrix.rid }}"
New-Item -ItemType Directory -Force -Path $out | Out-Null
dotnet publish src/ndnx/ndnx.csproj -c $env:Configuration -r ${{ matrix.rid }} --self-contained -p:PublishAot=true -o $out -bl:"publish-${{ matrix.rid }}.binlog"
dotnet publish src/ndx/ndx.csproj -c $env:Configuration -r ${{ matrix.rid }} --self-contained -p:PublishAot=true -o $out -bl:"publish-${{ matrix.rid }}.binlog"

- name: 📦 pack
shell: pwsh
Expand All @@ -65,11 +65,11 @@ jobs:
with:
name: native-aot-${{ matrix.rid }}
path: |
artifacts/${{ matrix.rid }}/ndnx
artifacts/${{ matrix.rid }}/ndnx.exe
artifacts/ndnx-*.zip
artifacts/ndnx-*.tar.gz
artifacts/ndnx-*.sha256
artifacts/${{ matrix.rid }}/ndx
artifacts/${{ matrix.rid }}/ndx.exe
artifacts/ndx-*.zip
artifacts/ndx-*.tar.gz
artifacts/ndx-*.sha256
retention-days: 7
if-no-files-found: error

Expand Down Expand Up @@ -131,19 +131,19 @@ jobs:
Delete-CiRelease

$notes = @"
Rolling native build from ``$env:NDNX_SHA``. Not a release.
Rolling native build from ``$env:NDX_SHA``. Not a release.

Install: ``curl -fsSL https://github.com/$env:GITHUB_REPOSITORY/releases/download/ci/install.sh | NDNX_VERSION=ci sh``
Install: ``curl -fsSL https://github.com/$env:GITHUB_REPOSITORY/releases/download/ci/install.sh | NDX_VERSION=ci sh``

Update: ``ndnx --update ci``
Update: ``ndx --update ci``
"@

# Create the tag/release with no files. Passing assets to `gh release create`
# can succeed with an empty upload list; `gh release upload` is how
# publish.yml actually attaches archives.
$created = $false
for ($attempt = 1; $attempt -le 8; $attempt++) {
gh release create ci --prerelease --latest=false --title CI --target $env:NDNX_SHA --notes $notes
gh release create ci --prerelease --latest=false --title CI --target $env:NDX_SHA --notes $notes
if ($LASTEXITCODE -eq 0) { $created = $true; break }
Write-Host "ci tag still present (attempt $attempt); retrying..."
Start-Sleep -Seconds ($attempt * 3)
Expand Down
12 changes: 6 additions & 6 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
run: |
$out = "artifacts/${{ matrix.rid }}"
New-Item -ItemType Directory -Force -Path $out | Out-Null
dotnet publish src/ndnx/ndnx.csproj -c $env:Configuration -r ${{ matrix.rid }} --self-contained -p:PublishAot=true -o $out -bl:"publish-${{ matrix.rid }}.binlog"
dotnet publish src/ndx/ndx.csproj -c $env:Configuration -r ${{ matrix.rid }} --self-contained -p:PublishAot=true -o $out -bl:"publish-${{ matrix.rid }}.binlog"

- name: 📦 pack
shell: pwsh
Expand All @@ -63,11 +63,11 @@ jobs:
with:
name: native-aot-${{ matrix.rid }}
path: |
artifacts/${{ matrix.rid }}/ndnx
artifacts/${{ matrix.rid }}/ndnx.exe
artifacts/ndnx-*.zip
artifacts/ndnx-*.tar.gz
artifacts/ndnx-*.sha256
artifacts/${{ matrix.rid }}/ndx
artifacts/${{ matrix.rid }}/ndx.exe
artifacts/ndx-*.zip
artifacts/ndx-*.tar.gz
artifacts/ndx-*.sha256
retention-days: 30
if-no-files-found: error

Expand Down
6 changes: 3 additions & 3 deletions .v0.5.0-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Download `tool@version` from the constructed nupkg URL — no version-index roun

## Exact-version download

A first `ndnx stop@2.1.0` now:
A first `ndx stop@2.1.0` now:

1. Resolves `PackageBaseAddress` from the v3 service index (kept in memory for the process)
2. GETs `{base}{id}/{version}/{id}.{version}.nupkg`
Expand All @@ -19,9 +19,9 @@ Cold is empty-cache download → start; cached is start only (`tool@version`). l
| Tool | Runner | Cold linux-x64 | Cold win-x64 | Cached linux-x64 | Cached win-x64 |
| --- | --- | ---: | ---: | ---: | ---: |
| `stop@2.1.0` (native AOT) | `dnx` | 2.2 s | 2.1 s | 580 ms | 520 ms |
| | `ndnx` | **1.2 s** | **1.5 s** | **10 ms** | **34 ms** |
| | `ndx` | **1.2 s** | **1.5 s** | **10 ms** | **34 ms** |
| `dotnetsay@1.0.0` (framework-dependent) | `dnx` | 1.6 s | 1.8 s | 570 ms | 545 ms |
| | `ndnx` | **849 ms** | **1.1 s** | **12 ms** | **50 ms** |
| | `ndx` | **849 ms** | **1.1 s** | **12 ms** | **50 ms** |

Isolated `NUGET_PACKAGES`, source `https://kzu.blob.core.windows.net/nuget/index.json`. Cold is the median of 3 empty-cache runs. Cached is the median of 10 runs after one seed.

Expand Down
78 changes: 39 additions & 39 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,93 +1,93 @@
# Changelog

## [ci](https://github.com/devlooped/ndnx/tree/ci) (2026-08-25)
## [ci](https://github.com/devlooped/ndx/tree/ci) (2026-08-25)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.6...ci)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.6...ci)

## [v0.6.6](https://github.com/devlooped/ndnx/tree/v0.6.6) (2026-08-25)
## [v0.6.6](https://github.com/devlooped/ndx/tree/v0.6.6) (2026-08-25)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.5...v0.6.6)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.5...v0.6.6)

:twisted_rightwards_arrows: Merged:

- Fix ci-release when an untagged ci draft blocks create [\#15](https://github.com/devlooped/ndnx/pull/15) (@kzu)
- Fix ci-release when an untagged ci draft blocks create [\#15](https://github.com/devlooped/ndx/pull/15) (@kzu)

## [v0.6.5](https://github.com/devlooped/ndnx/tree/v0.6.5) (2026-08-25)
## [v0.6.5](https://github.com/devlooped/ndx/tree/v0.6.5) (2026-08-25)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.4...v0.6.5)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.4...v0.6.5)

:twisted_rightwards_arrows: Merged:

- Skip publish.yml for the rolling ci tag [\#14](https://github.com/devlooped/ndnx/pull/14) (@kzu)
- Skip publish.yml for the rolling ci tag [\#14](https://github.com/devlooped/ndx/pull/14) (@kzu)

## [v0.6.4](https://github.com/devlooped/ndnx/tree/v0.6.4) (2026-08-25)
## [v0.6.4](https://github.com/devlooped/ndx/tree/v0.6.4) (2026-08-25)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.3...v0.6.4)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.3...v0.6.4)

:bug: Fixed bugs:

- fix: show download progress on Windows PowerShell [\#13](https://github.com/devlooped/ndnx/pull/13) (@kzu)
- fix: show download progress on Windows PowerShell [\#13](https://github.com/devlooped/ndx/pull/13) (@kzu)

## [v0.6.3](https://github.com/devlooped/ndnx/tree/v0.6.3) (2026-08-25)
## [v0.6.3](https://github.com/devlooped/ndx/tree/v0.6.3) (2026-08-25)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.2...v0.6.3)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.2...v0.6.3)

:sparkles: Implemented enhancements:

- feat: publish a rolling ci prerelease for dogfood [\#11](https://github.com/devlooped/ndnx/pull/11) (@kzu)
- Show download progress for large native nupkgs [\#10](https://github.com/devlooped/ndnx/pull/10) (@kzu)
- feat: publish a rolling ci prerelease for dogfood [\#11](https://github.com/devlooped/ndx/pull/11) (@kzu)
- Show download progress for large native nupkgs [\#10](https://github.com/devlooped/ndx/pull/10) (@kzu)

:bug: Fixed bugs:

- fix: only start ci-release after a successful build on main [\#12](https://github.com/devlooped/ndnx/pull/12) (@kzu)
- fix: only start ci-release after a successful build on main [\#12](https://github.com/devlooped/ndx/pull/12) (@kzu)

## [v0.6.2](https://github.com/devlooped/ndnx/tree/v0.6.2) (2026-08-20)
## [v0.6.2](https://github.com/devlooped/ndx/tree/v0.6.2) (2026-08-20)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.1...v0.6.2)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.1...v0.6.2)

:sparkles: Implemented enhancements:

- Report a dnx-style error when a tool version is missing from the feed [\#8](https://github.com/devlooped/ndnx/pull/8) (@kzu)
- Report a dnx-style error when a tool version is missing from the feed [\#8](https://github.com/devlooped/ndx/pull/8) (@kzu)

## [v0.6.1](https://github.com/devlooped/ndnx/tree/v0.6.1) (2026-08-20)
## [v0.6.1](https://github.com/devlooped/ndx/tree/v0.6.1) (2026-08-20)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.6.0...v0.6.1)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.6.0...v0.6.1)

## [v0.6.0](https://github.com/devlooped/ndnx/tree/v0.6.0) (2026-08-15)
## [v0.6.0](https://github.com/devlooped/ndx/tree/v0.6.0) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.5.2...v0.6.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.5.2...v0.6.0)

## [v0.5.2](https://github.com/devlooped/ndnx/tree/v0.5.2) (2026-08-15)
## [v0.5.2](https://github.com/devlooped/ndx/tree/v0.5.2) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.5.1...v0.5.2)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.5.1...v0.5.2)

## [v0.5.1](https://github.com/devlooped/ndnx/tree/v0.5.1) (2026-08-15)
## [v0.5.1](https://github.com/devlooped/ndx/tree/v0.5.1) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.5.0...v0.5.1)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.5.0...v0.5.1)

## [v0.5.0](https://github.com/devlooped/ndnx/tree/v0.5.0) (2026-08-15)
## [v0.5.0](https://github.com/devlooped/ndx/tree/v0.5.0) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.4.0...v0.5.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.4.0...v0.5.0)

## [v0.4.0](https://github.com/devlooped/ndnx/tree/v0.4.0) (2026-08-15)
## [v0.4.0](https://github.com/devlooped/ndx/tree/v0.4.0) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.3.0...v0.4.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.3.0...v0.4.0)

## [v0.3.0](https://github.com/devlooped/ndnx/tree/v0.3.0) (2026-08-15)
## [v0.3.0](https://github.com/devlooped/ndx/tree/v0.3.0) (2026-08-15)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.2.0...v0.3.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.2.0...v0.3.0)

## [v0.2.0](https://github.com/devlooped/ndnx/tree/v0.2.0) (2026-08-14)
## [v0.2.0](https://github.com/devlooped/ndx/tree/v0.2.0) (2026-08-14)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.1.1...v0.2.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.1.1...v0.2.0)

## [v0.1.1](https://github.com/devlooped/ndnx/tree/v0.1.1) (2026-08-14)
## [v0.1.1](https://github.com/devlooped/ndx/tree/v0.1.1) (2026-08-14)

[Full Changelog](https://github.com/devlooped/ndnx/compare/v0.1.0...v0.1.1)
[Full Changelog](https://github.com/devlooped/ndx/compare/v0.1.0...v0.1.1)

## [v0.1.0](https://github.com/devlooped/ndnx/tree/v0.1.0) (2026-08-13)
## [v0.1.0](https://github.com/devlooped/ndx/tree/v0.1.0) (2026-08-13)

[Full Changelog](https://github.com/devlooped/ndnx/compare/5d6336c96c3e2b75ef31dccbf9b312230fe94afa...v0.1.0)
[Full Changelog](https://github.com/devlooped/ndx/compare/5d6336c96c3e2b75ef31dccbf9b312230fe94afa...v0.1.0)



Expand Down
46 changes: 23 additions & 23 deletions install.ps1
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
# Install ndnx from GitHub Releases.
# irm https://github.com/devlooped/ndnx/releases/latest/download/install.ps1 | iex
# Env / flags: NDNX_VERSION, NDNX_PREFIX, NDNX_ARCHIVE, NDNX_RID, NDNX_REPO, NDNX_SKIP_PATH
# Install ndx from GitHub Releases.
# irm https://github.com/devlooped/ndx/releases/latest/download/install.ps1 | iex
# Env / flags: NDX_VERSION, NDX_PREFIX, NDX_ARCHIVE, NDX_RID, NDX_REPO, NDX_SKIP_PATH
# Also accepts --version --prefix --archive --rid --repo --skip-path

$ErrorActionPreference = 'Stop'

$Repo = if ($env:NDNX_REPO) { $env:NDNX_REPO } else { 'devlooped/ndnx' }
$Version = $env:NDNX_VERSION
$Prefix = $env:NDNX_PREFIX
$Archive = $env:NDNX_ARCHIVE
$Rid = $env:NDNX_RID
$SkipPath = $env:NDNX_SKIP_PATH -eq '1'
$Repo = if ($env:NDX_REPO) { $env:NDX_REPO } else { 'devlooped/ndx' }
$Version = $env:NDX_VERSION
$Prefix = $env:NDX_PREFIX
$Archive = $env:NDX_ARCHIVE
$Rid = $env:NDX_RID
$SkipPath = $env:NDX_SKIP_PATH -eq '1'

function Get-NdnxRuntimeIdentifier {
function Get-NdxRuntimeIdentifier {
$arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture
$archName = switch ($arch) {
'X64' { 'x64' }
'Arm64' { 'arm64' }
default {
throw "ndnx: unsupported architecture '$arch'"
throw "ndx: unsupported architecture '$arch'"
}
}

Expand All @@ -31,7 +31,7 @@ function Get-NdnxRuntimeIdentifier {
if ($IsLinux) { return "linux-$archName" }
}

throw "ndnx: unsupported OS"
throw "ndx: unsupported OS"
}

function Send-EnvironmentChange {
Expand Down Expand Up @@ -59,7 +59,7 @@ public static extern IntPtr SendMessageTimeout(
[ref]$result)
}

function Add-NdnxToUserPath([string]$dir) {
function Add-NdxToUserPath([string]$dir) {
$parts = [Environment]::GetEnvironmentVariable('Path', 'User')
if (-not $parts) { $parts = '' }
$entries = $parts.Split([char]';', [StringSplitOptions]::RemoveEmptyEntries)
Expand All @@ -82,27 +82,27 @@ for ($i = 0; $i -lt $args.Count; $i++) {
'^--rid$|^-Rid$' { $Rid = $args[++$i]; continue }
'^--repo$|^-Repo$' { $Repo = $args[++$i]; continue }
'^--skip-path$|^-SkipPath$' { $SkipPath = $true; continue }
default { throw "ndnx: unrecognized argument '$($args[$i])'" }
default { throw "ndx: unrecognized argument '$($args[$i])'" }
}
}

if (-not $Rid) {
$Rid = Get-NdnxRuntimeIdentifier
$Rid = Get-NdxRuntimeIdentifier
}

$windows = $Rid.StartsWith('win', [StringComparison]::OrdinalIgnoreCase)
$binary = if ($windows) { 'ndnx.exe' } else { 'ndnx' }
$binary = if ($windows) { 'ndx.exe' } else { 'ndx' }
$ext = if ($windows) { 'zip' } else { 'tar.gz' }

if (-not $Prefix) {
$Prefix = if ($windows) {
Join-Path $env:LOCALAPPDATA 'ndnx'
Join-Path $env:LOCALAPPDATA 'ndx'
} else {
Join-Path $HOME '.local/bin'
}
}

$tmp = Join-Path ([IO.Path]::GetTempPath()) ("ndnx-install-" + [guid]::NewGuid().ToString('n'))
$tmp = Join-Path ([IO.Path]::GetTempPath()) ("ndx-install-" + [guid]::NewGuid().ToString('n'))
New-Item -ItemType Directory -Path $tmp | Out-Null
try {
if (-not $Archive) {
Expand All @@ -121,11 +121,11 @@ try {
$release = Invoke-RestMethod -Headers @{ Accept = 'application/vnd.github+json' } `
-Uri "https://api.github.com/repos/$Repo/releases/latest"
$tag = $release.tag_name
if (-not $tag) { throw "ndnx: could not resolve latest release of $Repo" }
if (-not $tag) { throw "ndx: could not resolve latest release of $Repo" }
$resolved = $tag.TrimStart('v')
}

$name = "ndnx-$resolved-$Rid.$ext"
$name = "ndx-$resolved-$Rid.$ext"
$base = "https://github.com/$Repo/releases/download/$tag"
$Archive = Join-Path $tmp $name
Invoke-WebRequest -Uri "$base/$name" -OutFile $Archive
Expand All @@ -136,7 +136,7 @@ try {
$expected = ((Get-Content -Raw "$Archive.sha256").Trim() -split '\s+')[0].ToLowerInvariant()
$actual = (Get-FileHash -Algorithm SHA256 -Path $Archive).Hash.ToLowerInvariant()
if ($actual -ne $expected) {
throw "ndnx: SHA256 mismatch for $(Split-Path $Archive -Leaf)`n expected: $expected`n actual: $actual"
throw "ndx: SHA256 mismatch for $(Split-Path $Archive -Leaf)`n expected: $expected`n actual: $actual"
}
}

Expand All @@ -150,7 +150,7 @@ try {

$source = Join-Path $extract $binary
if (-not (Test-Path $source)) {
throw "ndnx: archive did not contain $binary"
throw "ndx: archive did not contain $binary"
}

New-Item -ItemType Directory -Force -Path $Prefix | Out-Null
Expand All @@ -159,7 +159,7 @@ try {
Write-Host "installed $dest"

if (-not $SkipPath) {
Add-NdnxToUserPath $Prefix
Add-NdxToUserPath $Prefix
}
}
finally {
Expand Down
Loading
Loading