diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2bdbcd0..da1c6b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -243,7 +243,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v4 with: - dotnet-version: 10.0.x + dotnet-version: 8.0.x - name: Import binaries and pack dotnet tool (dry-run) shell: pwsh @@ -265,3 +265,47 @@ jobs: name: devolutions-cirup-tool-nupkg-dry-run path: dist/nuget/Devolutions.Cirup.Tool*.nupkg if-no-files-found: error + + validate_dotnet_tool_install: + name: Validate dotnet tool install (${{ matrix.sdk }}) + runs-on: windows-latest + needs: build_release_artifacts + strategy: + fail-fast: false + matrix: + sdk: + - 8.0.x + - 10.0.x + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download native artifacts + uses: actions/download-artifact@v4 + with: + path: dist + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.sdk }} + + - name: Pack and validate dotnet tool + shell: pwsh + env: + CI_RUN_NUMBER: ${{ github.run_number }} + SDK_LABEL: ${{ matrix.sdk }} + run: | + $version = "0.0.0-ci.$env:CI_RUN_NUMBER-$($env:SDK_LABEL.Replace('.x', '').Replace('.', ''))" + $stagingRoot = Join-Path $PWD "nuget/staging" + + ./nuget/pack-cirup-dotnet-tool.ps1 ` + -Version $version ` + -ArtifactsRoot "dist" ` + -StagingRoot $stagingRoot ` + -OutputDir "dist/nuget" + + ./nuget/test-dotnet-tool-install.ps1 ` + -Version $version ` + -FeedDir "dist/nuget" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f238d75..c8e6e97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -400,6 +400,7 @@ jobs: - sign_windows - pack_nuget - pack_dotnet_tool + - validate_dotnet_tool_install steps: - name: Download platform zip artifacts @@ -560,7 +561,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v4 with: - dotnet-version: 10.0.x + dotnet-version: 8.0.x - name: Import binaries and pack dotnet tool shell: pwsh @@ -588,6 +589,50 @@ jobs: path: dist/nuget/Devolutions.Cirup.Tool*.nupkg if-no-files-found: error + validate_dotnet_tool_install: + name: Validate dotnet tool install (${{ matrix.sdk }}) + runs-on: windows-latest + needs: + - preflight + - pack_dotnet_tool + strategy: + fail-fast: false + matrix: + sdk: + - 8.0.x + - 10.0.x + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download dotnet tool artifact + uses: actions/download-artifact@v4 + with: + name: devolutions-cirup-tool-nupkg + path: dist + + - name: Setup .NET SDK + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ matrix.sdk }} + + - name: Validate packaged dotnet tool install + shell: pwsh + env: + RELEASE_TAG: ${{ needs.preflight.outputs.release_tag }} + run: | + $tag = $env:RELEASE_TAG + if (-not $tag.StartsWith('v')) { + throw "Release tag must start with 'v'. Actual: $tag" + } + + $version = $tag.Substring(1) + + ./nuget/test-dotnet-tool-install.ps1 ` + -Version $version ` + -FeedDir "dist" + publish_nuget: name: Publish NuGet packages runs-on: ubuntu-latest @@ -595,6 +640,7 @@ jobs: - preflight - pack_nuget - pack_dotnet_tool + - validate_dotnet_tool_install if: needs.preflight.outputs.publish_nuget == 'true' environment: ${{ needs.preflight.outputs.publish_env }} permissions: @@ -639,7 +685,7 @@ jobs: if: steps.publish_mode.outputs.dry_run == 'true' || steps.publish_mode.outputs.has_login_user == 'true' uses: actions/setup-dotnet@v4 with: - dotnet-version: 10.0.x + dotnet-version: 8.0.x - name: NuGet login (OIDC) if: steps.publish_mode.outputs.dry_run != 'true' && steps.publish_mode.outputs.has_login_user == 'true' @@ -648,7 +694,7 @@ jobs: with: user: ${{ secrets.NUGET_BOT_USERNAME }} - - name: Publish to NuGet.org (RID first, pointer last) + - name: Publish to NuGet.org if: steps.publish_mode.outputs.dry_run == 'true' || steps.publish_mode.outputs.has_login_user == 'true' shell: pwsh env: @@ -689,33 +735,7 @@ jobs: throw 'Missing Devolutions.Cirup.Tool package artifacts.' } - $toolRidPattern = 'Devolutions\.Cirup\.Tool\.(win-x64|win-arm64|linux-x64|linux-arm64|osx-x64|osx-arm64|any)\.' - $toolRidPackages = $toolPackages | Where-Object { $_.Name -match $toolRidPattern } - $toolPointerPackages = $toolPackages | Where-Object { $_.Name -notmatch $toolRidPattern } - - if (-not $toolRidPackages) { - throw 'Missing Devolutions.Cirup.Tool RID package artifacts.' - } - - if (-not $toolPointerPackages) { - throw 'Missing Devolutions.Cirup.Tool pointer package artifact.' - } - - foreach ($package in $toolRidPackages) { - $pushArgs = @( - 'nuget', 'push', "$($package.FullName)", - '--api-key', "$apiKey", - '--source', "$env:NUGET_SOURCE", - '--skip-duplicate', '--no-symbols' - ) - - Write-Host "dotnet $($pushArgs -join ' ')" - if (-not $dryRun) { - & dotnet @pushArgs - } - } - - foreach ($package in $toolPointerPackages) { + foreach ($package in $toolPackages) { $pushArgs = @( 'nuget', 'push', "$($package.FullName)", '--api-key', "$apiKey", diff --git a/Cargo.lock b/Cargo.lock index e742b03..6afa586 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,7 +435,7 @@ dependencies = [ [[package]] name = "cirup_cli" -version = "0.5.0" +version = "0.6.0" dependencies = [ "cirup_core", "clap", @@ -446,7 +446,7 @@ dependencies = [ [[package]] name = "cirup_core" -version = "0.5.0" +version = "0.6.0" dependencies = [ "dot_json", "lazy_static", diff --git a/README.md b/README.md index 60aa56d..377bf64 100644 --- a/README.md +++ b/README.md @@ -157,9 +157,9 @@ Run local end-to-end validation: pwsh ./nuget/test-e2e.ps1 ``` -## Dotnet tool usage (.NET 10+) +## Dotnet tool usage (.NET SDK 8+) -`cirup` is also available as a RID-specific dotnet tool package: `Devolutions.Cirup.Tool`. +`cirup` is also available as a dotnet tool package: `Devolutions.Cirup.Tool`. Install globally: @@ -168,7 +168,7 @@ dotnet tool install -g Devolutions.Cirup.Tool cirup --help ``` -Run one-shot without permanent install: +Run one-shot without permanent install on .NET 10+: ```bash dotnet tool exec Devolutions.Cirup.Tool -- --help diff --git a/cirup_cli/Cargo.toml b/cirup_cli/Cargo.toml index d18436b..2456ee1 100644 --- a/cirup_cli/Cargo.toml +++ b/cirup_cli/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cirup_cli" -version = "0.5.0" +version = "0.6.0" authors = ["Marc-André Moreau "] edition = "2024" diff --git a/cirup_core/Cargo.toml b/cirup_core/Cargo.toml index 0dca18b..5b06f37 100644 --- a/cirup_core/Cargo.toml +++ b/cirup_core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cirup_core" -version = "0.5.0" +version = "0.6.0" authors = ["Marc-André Moreau "] edition = "2024" diff --git a/nuget/Devolutions.Cirup.Build.Package.csproj b/nuget/Devolutions.Cirup.Build.Package.csproj index 5292c25..8f9022e 100644 --- a/nuget/Devolutions.Cirup.Build.Package.csproj +++ b/nuget/Devolutions.Cirup.Build.Package.csproj @@ -8,7 +8,7 @@ true Devolutions.Cirup.Build - 0.5.0 + 0.6.0 Devolutions Cross-platform cirup executable packaged for MSBuild pre-build RESX sorting. README.md diff --git a/nuget/README.md b/nuget/README.md index da0988e..d153137 100644 --- a/nuget/README.md +++ b/nuget/README.md @@ -106,14 +106,14 @@ Run the full local validation flow from the repository root: ./nuget/test-e2e.ps1 ``` -## RID-specific dotnet tool package +## Dotnet tool package -The repository also ships a separate RID-specific dotnet tool package: `Devolutions.Cirup.Tool`. +The repository also ships a separate dotnet tool package: `Devolutions.Cirup.Tool`. - Package type: dotnet tool (command name `cirup`) -- SDK requirement: .NET 10 SDK or newer +- SDK requirement: .NET 8 SDK or newer - Supported RIDs: `win-x64`, `win-arm64`, `linux-x64`, `linux-arm64`, `osx-x64`, `osx-arm64` -- Includes an `any` fallback package that prints a guidance message on unsupported runtimes +- Package includes the native executables for all supported RIDs and selects the right one at runtime Install and run: @@ -129,6 +129,8 @@ dotnet tool exec Devolutions.Cirup.Tool -- --help dnx Devolutions.Cirup.Tool --help ``` +`dotnet tool exec` and `dnx` require .NET 10 or newer. + Pack from prebuilt native artifacts (same artifact input as `Devolutions.Cirup.Build`): ```powershell diff --git a/nuget/samples/Devolutions.Cirup.Build.E2E/Devolutions.Cirup.Build.E2E.csproj b/nuget/samples/Devolutions.Cirup.Build.E2E/Devolutions.Cirup.Build.E2E.csproj index b317180..acd5a7a 100644 --- a/nuget/samples/Devolutions.Cirup.Build.E2E/Devolutions.Cirup.Build.E2E.csproj +++ b/nuget/samples/Devolutions.Cirup.Build.E2E/Devolutions.Cirup.Build.E2E.csproj @@ -5,7 +5,7 @@ enable enable false - 0.5.0 + 0.6.0 diff --git a/nuget/test-dotnet-tool-install.ps1 b/nuget/test-dotnet-tool-install.ps1 new file mode 100644 index 0000000..d6f739f --- /dev/null +++ b/nuget/test-dotnet-tool-install.ps1 @@ -0,0 +1,57 @@ +param( + [Parameter(Mandatory = $true)] + [string]$Version, + + [string]$FeedDir = (Join-Path $PSScriptRoot "..\dist\nuget"), + + [string]$WorkRoot = (Join-Path $PSScriptRoot "..\target\tmp\dotnet-tool-install") +) + +$ErrorActionPreference = "Stop" + +$feedPath = Resolve-Path $FeedDir | Select-Object -ExpandProperty Path +$packageName = "Devolutions.Cirup.Tool.$Version.nupkg" +$packagePath = Join-Path $feedPath $packageName + +if (-not (Test-Path -Path $packagePath -PathType Leaf)) { + throw "Expected tool package not found: $packagePath" +} + +if (Test-Path -Path $WorkRoot) { + Remove-Item -Path $WorkRoot -Recurse -Force +} + +$toolPath = Join-Path $WorkRoot "tools" +New-Item -Path $toolPath -ItemType Directory -Force | Out-Null + +Write-Host "Using dotnet SDK $(dotnet --version)" +Write-Host "Installing $packageName from $feedPath" + +dotnet tool install ` + --tool-path $toolPath ` + Devolutions.Cirup.Tool ` + --version $Version ` + --add-source $feedPath ` + --ignore-failed-sources + +if ($LASTEXITCODE -ne 0) { + throw "dotnet tool install failed with exit code $LASTEXITCODE" +} + +$toolExecutable = if ($IsWindows) { + Join-Path $toolPath "cirup.exe" +} +else { + Join-Path $toolPath "cirup" +} + +if (-not (Test-Path -Path $toolExecutable -PathType Leaf)) { + throw "Installed tool executable not found: $toolExecutable" +} + +& $toolExecutable --help | Out-Null +if ($LASTEXITCODE -ne 0) { + throw "Installed tool execution failed with exit code $LASTEXITCODE" +} + +Write-Host "Dotnet tool install smoke test succeeded." \ No newline at end of file diff --git a/nuget/test-e2e.ps1 b/nuget/test-e2e.ps1 index 480d6ad..4dacde7 100644 --- a/nuget/test-e2e.ps1 +++ b/nuget/test-e2e.ps1 @@ -1,5 +1,5 @@ param( - [string]$Version = "0.5.0", + [string]$Version = "0.6.0", [string]$Configuration = "Release" ) diff --git a/nuget/tool/Devolutions.Cirup.Tool.csproj b/nuget/tool/Devolutions.Cirup.Tool.csproj index eb74f98..40385d3 100644 --- a/nuget/tool/Devolutions.Cirup.Tool.csproj +++ b/nuget/tool/Devolutions.Cirup.Tool.csproj @@ -1,23 +1,22 @@ Exe - net10.0 + net8.0 + enable true cirup Devolutions.Cirup.Tool - 0.5.0 + 0.6.0 Devolutions - RID-specific dotnet tool wrapper around prebuilt cirup native executables. + Dotnet tool wrapper around prebuilt cirup native executables. README.md git https://github.com/Devolutions/cirup-rs MIT true - win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64;any - win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64;any Major @@ -30,47 +29,50 @@ + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\win-x64\cirup.exe')" /> + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\win-arm64\cirup.exe')" /> + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\linux-x64\cirup')" /> + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\linux-arm64\cirup')" /> + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\osx-x64\cirup')" /> + Condition="Exists('$(CirupNugetStagingDir)\tools\cirup\osx-arm64\cirup')" /> - - <_CirupToolBinaryPath>$(CirupNugetStagingDir)\tools\cirup\$(RuntimeIdentifier)\cirup - <_CirupToolBinaryPath Condition="'$(RuntimeIdentifier)' == 'win-x64' or '$(RuntimeIdentifier)' == 'win-arm64'">$(CirupNugetStagingDir)\tools\cirup\$(RuntimeIdentifier)\cirup.exe - + BeforeTargets="GenerateNuspec"> + + <_CirupToolBinary Include="win-x64|$(CirupNugetStagingDir)\tools\cirup\win-x64\cirup.exe" /> + <_CirupToolBinary Include="win-arm64|$(CirupNugetStagingDir)\tools\cirup\win-arm64\cirup.exe" /> + <_CirupToolBinary Include="linux-x64|$(CirupNugetStagingDir)\tools\cirup\linux-x64\cirup" /> + <_CirupToolBinary Include="linux-arm64|$(CirupNugetStagingDir)\tools\cirup\linux-arm64\cirup" /> + <_CirupToolBinary Include="osx-x64|$(CirupNugetStagingDir)\tools\cirup\osx-x64\cirup" /> + <_CirupToolBinary Include="osx-arm64|$(CirupNugetStagingDir)\tools\cirup\osx-arm64\cirup" /> + - + diff --git a/nuget/tool/Program.cs b/nuget/tool/Program.cs index a523414..de12a4d 100644 --- a/nuget/tool/Program.cs +++ b/nuget/tool/Program.cs @@ -7,12 +7,13 @@ static int Run(string[] args) { + string? supportedRid = GetSupportedRid(); string executableName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "cirup.exe" : "cirup"; - string nativeExecutablePath = Path.Combine(AppContext.BaseDirectory, executableName); + string? nativeExecutablePath = ResolveNativeExecutablePath(supportedRid, executableName); - if (!File.Exists(nativeExecutablePath)) + if (nativeExecutablePath is null) { - PrintUnsupportedRidMessage(); + PrintUnsupportedRidMessage(supportedRid); return 1; } @@ -28,7 +29,7 @@ static int Run(string[] args) processStartInfo.ArgumentList.Add(argument); } - using Process process = Process.Start(processStartInfo); + using Process? process = Process.Start(processStartInfo); if (process is null) { Console.Error.WriteLine("Unable to start native cirup executable."); @@ -39,10 +40,68 @@ static int Run(string[] args) return process.ExitCode; } -static void PrintUnsupportedRidMessage() +static string? GetSupportedRid() { - Console.Error.WriteLine("No native cirup executable is available for this runtime identifier in this package."); + Architecture architecture = RuntimeInformation.ProcessArchitecture; + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return architecture switch + { + Architecture.X64 => "win-x64", + Architecture.Arm64 => "win-arm64", + _ => null, + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + return architecture switch + { + Architecture.X64 => "linux-x64", + Architecture.Arm64 => "linux-arm64", + _ => null, + }; + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + return architecture switch + { + Architecture.X64 => "osx-x64", + Architecture.Arm64 => "osx-arm64", + _ => null, + }; + } + + return null; +} + +static string? ResolveNativeExecutablePath(string? supportedRid, string executableName) +{ + if (!string.IsNullOrWhiteSpace(supportedRid)) + { + string packagedPath = Path.Combine(AppContext.BaseDirectory, "native", supportedRid, executableName); + if (File.Exists(packagedPath)) + { + return packagedPath; + } + } + + string legacyPath = Path.Combine(AppContext.BaseDirectory, executableName); + if (File.Exists(legacyPath)) + { + return legacyPath; + } + + return null; +} + +static void PrintUnsupportedRidMessage(string? supportedRid) +{ + Console.Error.WriteLine("No native cirup executable is available for this platform in this package."); Console.Error.WriteLine($"Detected runtime identifier: {RuntimeInformation.RuntimeIdentifier}"); + Console.Error.WriteLine($"Resolved supported runtime identifier: {supportedRid ?? "unsupported"}"); Console.Error.WriteLine("Supported runtime identifiers: win-x64, win-arm64, linux-x64, linux-arm64, osx-x64, osx-arm64."); Console.Error.WriteLine("Install the tool on a supported platform, or download platform-specific binaries from cirup release assets."); } @@ -69,7 +128,7 @@ static void EnsureExecutableBit(string nativeExecutablePath) chmodStartInfo.ArgumentList.Add("+x"); chmodStartInfo.ArgumentList.Add(nativeExecutablePath); - using Process chmodProcess = Process.Start(chmodStartInfo); + using Process? chmodProcess = Process.Start(chmodStartInfo); chmodProcess?.WaitForExit(); } catch diff --git a/nuget/tool/README.md b/nuget/tool/README.md index fca8170..88a179a 100644 --- a/nuget/tool/README.md +++ b/nuget/tool/README.md @@ -1,6 +1,6 @@ # Devolutions.Cirup.Tool -`Devolutions.Cirup.Tool` is a RID-specific .NET tool package for `cirup`. +`Devolutions.Cirup.Tool` is a .NET tool package for `cirup`. ## Install @@ -14,7 +14,7 @@ dotnet tool install -g Devolutions.Cirup.Tool cirup --help ``` -## One-shot run +## One-shot run (.NET 10+) ```powershell dotnet tool exec Devolutions.Cirup.Tool -- --help @@ -28,7 +28,7 @@ dnx Devolutions.Cirup.Tool --help ## Runtime selection -The package uses RID-specific tool packaging. The .NET CLI automatically selects the best package for the current platform. +The package includes native `cirup` executables for each supported platform and selects the right one at runtime. Supported RIDs: @@ -39,4 +39,4 @@ Supported RIDs: - `osx-x64` - `osx-arm64` -An `any` fallback package is also produced. It provides a managed fallback message on unsupported runtimes. +The managed launcher prints a guidance message on unsupported runtimes.