From 3866c9b41ba2187e868220b4957cf5964ae57b9e Mon Sep 17 00:00:00 2001 From: James McMahon Date: Wed, 28 Oct 2026 18:17:28 +0000 Subject: [PATCH 1/3] feat: add cross-platform CLI releases and GitHub actions --- .github/actions/apply/action.yml | 68 ++++++++++++++++++++++++++++++++ .github/actions/plan/action.yml | 59 +++++++++++++++++++++++++++ .github/workflows/cd.yml | 27 ++++++++----- .github/workflows/ci.yml | 35 +++++++++++++++- README.md | 44 +++++++++++++++------ 5 files changed, 211 insertions(+), 22 deletions(-) create mode 100644 .github/actions/apply/action.yml create mode 100644 .github/actions/plan/action.yml diff --git a/.github/actions/apply/action.yml b/.github/actions/apply/action.yml new file mode 100644 index 0000000..8c661cd --- /dev/null +++ b/.github/actions/apply/action.yml @@ -0,0 +1,68 @@ +name: PackageCascade apply +description: Download a pinned PackageCascade release and apply a prepared release plan locally + +inputs: + version: + description: PackageCascade release tag (for example, v1.2.3) + required: true + plan-path: + description: Path to a PackageCascade plan JSON file + required: true + output-directory: + description: Directory for generated package archives + required: true + repository: + description: Path to the Git repository (defaults to the working directory) + required: false + include-symbols: + description: Generate symbol packages (true or false; defaults to false) + required: false + default: 'false' + +outputs: + result-path: + description: Absolute path to the apply result JSON + value: ${{ steps.run.outputs.result-path }} + +runs: + using: composite + steps: + - id: run + shell: pwsh + env: + PACKAGECASCADE_VERSION: ${{ inputs.version }} + PACKAGECASCADE_PLAN_PATH: ${{ inputs.plan-path }} + PACKAGECASCADE_OUTPUT_DIRECTORY: ${{ inputs.output-directory }} + PACKAGECASCADE_REPOSITORY: ${{ inputs.repository }} + PACKAGECASCADE_INCLUDE_SYMBOLS: ${{ inputs.include-symbols }} + PACKAGECASCADE_RELEASE_URL: https://github.com/GoSimulate/PackageCascade/releases/download + run: | + $ErrorActionPreference = 'Stop' + $asset = switch ("$env:RUNNER_OS/$env:RUNNER_ARCH") { + 'Linux/X64' { 'PackageCascade-linux-x64' } + 'Windows/X64' { 'PackageCascade-win-x64.exe' } + 'macOS/X64' { 'PackageCascade-osx-x64' } + 'macOS/ARM64' { 'PackageCascade-osx-arm64' } + default { throw "PackageCascade has no release asset for $env:RUNNER_OS/$env:RUNNER_ARCH." } + } + + $downloadDirectory = Join-Path $env:RUNNER_TEMP ([guid]::NewGuid().ToString('N')) + New-Item -ItemType Directory -Path $downloadDirectory | Out-Null + $executable = Join-Path $downloadDirectory $asset + $tag = [uri]::EscapeDataString($env:PACKAGECASCADE_VERSION) + $url = "$env:PACKAGECASCADE_RELEASE_URL/$tag/$asset" + Invoke-WebRequest -Uri $url -OutFile $executable + if ($env:RUNNER_OS -ne 'Windows') { & chmod +x $executable } + + $resultPath = Join-Path $downloadDirectory 'packagecascade-apply-result.json' + $cliArguments = @('apply', '--infile', $env:PACKAGECASCADE_PLAN_PATH, '--output', $env:PACKAGECASCADE_OUTPUT_DIRECTORY) + if ($env:PACKAGECASCADE_REPOSITORY) { $cliArguments += @('--repository', $env:PACKAGECASCADE_REPOSITORY) } + switch ($env:PACKAGECASCADE_INCLUDE_SYMBOLS.ToLowerInvariant()) { + 'true' { $cliArguments += '--include-symbols' } + 'false' { } + default { throw "include-symbols must be 'true' or 'false'." } + } + & $executable @cliArguments > $resultPath + if ($LASTEXITCODE -ne 0) { throw "PackageCascade apply failed with exit code $LASTEXITCODE." } + if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw 'PackageCascade did not create an apply result.' } + "result-path=$resultPath" >> $env:GITHUB_OUTPUT diff --git a/.github/actions/plan/action.yml b/.github/actions/plan/action.yml new file mode 100644 index 0000000..4dcbdf0 --- /dev/null +++ b/.github/actions/plan/action.yml @@ -0,0 +1,59 @@ +name: PackageCascade plan +description: Download a pinned PackageCascade release and create a release plan + +inputs: + version: + description: PackageCascade release tag (for example, v1.2.3) + required: true + solution: + description: Path to the solution file + required: true + repository: + description: Path to the Git repository (defaults to the working directory) + required: false + configuration: + description: MSBuild configuration (defaults to Release) + required: false + +outputs: + plan-path: + description: Absolute path to the generated plan JSON + value: ${{ steps.run.outputs.plan-path }} + +runs: + using: composite + steps: + - id: run + shell: pwsh + env: + PACKAGECASCADE_VERSION: ${{ inputs.version }} + PACKAGECASCADE_SOLUTION: ${{ inputs.solution }} + PACKAGECASCADE_REPOSITORY: ${{ inputs.repository }} + PACKAGECASCADE_CONFIGURATION: ${{ inputs.configuration }} + PACKAGECASCADE_RELEASE_URL: https://github.com/GoSimulate/PackageCascade/releases/download + run: | + $ErrorActionPreference = 'Stop' + $asset = switch ("$env:RUNNER_OS/$env:RUNNER_ARCH") { + 'Linux/X64' { 'PackageCascade-linux-x64' } + 'Windows/X64' { 'PackageCascade-win-x64.exe' } + 'macOS/X64' { 'PackageCascade-osx-x64' } + 'macOS/ARM64' { 'PackageCascade-osx-arm64' } + default { throw "PackageCascade has no release asset for $env:RUNNER_OS/$env:RUNNER_ARCH." } + } + + $downloadDirectory = Join-Path $env:RUNNER_TEMP ([guid]::NewGuid().ToString('N')) + New-Item -ItemType Directory -Path $downloadDirectory | Out-Null + $executable = Join-Path $downloadDirectory $asset + $tag = [uri]::EscapeDataString($env:PACKAGECASCADE_VERSION) + $url = "$env:PACKAGECASCADE_RELEASE_URL/$tag/$asset" + Invoke-WebRequest -Uri $url -OutFile $executable + if ($env:RUNNER_OS -ne 'Windows') { & chmod +x $executable } + + $planPath = Join-Path $downloadDirectory 'packagecascade-plan.json' + $cliArguments = @('plan', $env:PACKAGECASCADE_SOLUTION, '--outfile', $planPath) + if ($env:PACKAGECASCADE_REPOSITORY) { $cliArguments += @('--repository', $env:PACKAGECASCADE_REPOSITORY) } + if ($env:PACKAGECASCADE_CONFIGURATION) { $cliArguments += @('--configuration', $env:PACKAGECASCADE_CONFIGURATION) } + & $executable @cliArguments + if ($LASTEXITCODE -ne 0) { throw "PackageCascade plan failed with exit code $LASTEXITCODE." } + if (-not (Test-Path -LiteralPath $planPath -PathType Leaf)) { throw 'PackageCascade did not create the plan file.' } + "plan-path=$planPath" >> $env:GITHUB_OUTPUT diff --git a/.github/workflows/cd.yml b/.github/workflows/cd.yml index f8ae4b7..0076c81 100644 --- a/.github/workflows/cd.yml +++ b/.github/workflows/cd.yml @@ -5,6 +5,7 @@ on: paths: - 'src/**' - '!src/*.Tests.*/**' + - '.github/actions/**' - '.github/workflows/cd.yml' workflow_dispatch: permissions: @@ -64,16 +65,22 @@ jobs: dotnet pack src/PackageCascade.slnx -c Release --no-build --no-restore -o artifacts/packages -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true -p:RepositoryUrl="$REPOSITORY_URL" - - name: Publish Linux CLI + - name: Publish CLI binaries run: | - dotnet publish src/PackageCascade.Cli/PackageCascade.Cli.csproj \ - -c Release -r linux-x64 --self-contained true -o artifacts/cli \ - -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true \ - -p:DebugType=embedded \ - -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true -p:RepositoryUrl="$REPOSITORY_URL" - mv artifacts/cli/PackageCascade.Cli artifacts/cli/PackageCascade-linux-x64 - - name: Check Linux CLI - run: artifacts/cli/PackageCascade-linux-x64 --help + for rid in linux-x64 win-x64 osx-x64 osx-arm64; do + output="artifacts/cli/$rid" + dotnet publish src/PackageCascade.Cli/PackageCascade.Cli.csproj \ + -c Release -r "$rid" --self-contained true -o "$output" \ + -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true \ + -p:DebugType=embedded \ + -p:Version="$VERSION" -p:ContinuousIntegrationBuild=true -p:RepositoryUrl="$REPOSITORY_URL" + + if [ "$rid" = win-x64 ]; then + mv "$output/PackageCascade.Cli.exe" "$output/PackageCascade-$rid.exe" + else + mv "$output/PackageCascade.Cli" "$output/PackageCascade-$rid" + fi + done - name: Generate release notes id: notes uses: requarks/changelog-action@v1.10.3 @@ -112,7 +119,7 @@ jobs: tag: ${{ needs.version.outputs.next }} commit: ${{ github.sha }} body: ${{ steps.notes.outputs.changes }} - artifacts: artifacts/packages/*.nupkg,artifacts/packages/*.snupkg,artifacts/cli/PackageCascade-linux-x64 + artifacts: artifacts/packages/*.nupkg,artifacts/packages/*.snupkg,artifacts/cli/*/PackageCascade-* artifactErrorsFailBuild: true makeLatest: true - uses: actions/upload-artifact@v7.0.2 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 88e7b75..b8b3d9c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: run: | if [ "$GITHUB_EVENT_NAME" = workflow_dispatch ] || [ -z "$BASE_SHA" ] || [ "$BASE_SHA" = 0000000000000000000000000000000000000000 ]; then echo 'run=true' >> "$GITHUB_OUTPUT" - elif [ -n "$(git diff --name-only "$BASE_SHA" HEAD -- src/ global.json .github/workflows/ci.yml)" ]; then + elif [ -n "$(git diff --name-only "$BASE_SHA" HEAD -- src/ global.json .github/actions/ .github/workflows/ci.yml .github/workflows/cd.yml)" ]; then echo 'run=true' >> "$GITHUB_OUTPUT" else echo 'run=false' >> "$GITHUB_OUTPUT" @@ -47,3 +47,36 @@ jobs: run: dotnet build src/PackageCascade.slnx -c Release - name: Test run: dotnet test --solution src/PackageCascade.slnx -c Release --no-build --no-restore + cli-platforms: + needs: select + if: needs.select.outputs.run_tests == 'true' + strategy: + fail-fast: false + matrix: + include: + - os: blacksmith-2vcpu-ubuntu-2404 + rid: linux-x64 + binary: PackageCascade.Cli + - os: windows-2025 + rid: win-x64 + binary: PackageCascade.Cli.exe + - os: macos-15-intel + rid: osx-x64 + binary: PackageCascade.Cli + - os: macos-15 + rid: osx-arm64 + binary: PackageCascade.Cli + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-dotnet@v6.0.0 + with: + global-json-file: global.json + - name: Publish CLI + run: dotnet publish src/PackageCascade.Cli/PackageCascade.Cli.csproj -c Release -r ${{ matrix.rid }} --self-contained true -o artifacts/cli -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=embedded + - name: Run CLI help + shell: pwsh + run: ./artifacts/cli/${{ matrix.binary }} --help diff --git a/README.md b/README.md index d30cdea..116f939 100644 --- a/README.md +++ b/README.md @@ -19,30 +19,52 @@ Your pipeline owns tests, package publication, pushing tags, and any recovery or ### Run the CLI -The CLI currently runs from a build of this repository. Install the SDK specified in `global.json`, then build it: +[GitHub releases](https://github.com/GoSimulate/PackageCascade/releases) containing these actions include a self-contained CLI for Linux x64, Windows x64, macOS x64, and macOS arm64. Download the binary for your runner; the tool itself does not require a .NET runtime. Planning and applying still require the SDK used by the consumer projects. -```sh -dotnet build src/PackageCascade.Cli/PackageCascade.Cli.csproj -c Release -``` - -From the repository you want to release, point `cli` at that build. These examples use Bash: +From the repository you want to release, point `cli` at the downloaded binary. These examples use Bash: ```sh -cli=/absolute/path/to/PackageCascade/src/PackageCascade.Cli/bin/Release/net11.0/PackageCascade.Cli.dll +cli=/absolute/path/to/PackageCascade-linux-x64 +chmod +x "$cli" -dotnet "$cli" plan src/Example.slnx --outfile release-plan.json +"$cli" plan src/Example.slnx --outfile release-plan.json # Inspect release-plan.json before applying it. -dotnet "$cli" apply --infile release-plan.json --output artifacts/packages +"$cli" apply --infile release-plan.json --output artifacts/packages ``` For a single pipeline step, the plan can be streamed directly: ```sh set -o pipefail -dotnet "$cli" plan src/Example.slnx | - dotnet "$cli" apply --output artifacts/packages +"$cli" plan src/Example.slnx | + "$cli" apply --output artifacts/packages ``` +### Use the GitHub Actions + +The `plan` and `apply` composite actions download a specified PackageCascade release and expose paths to their JSON results. Use the same release tag in each `uses` reference and `version` input. Replace `` with a published tag that contains the actions and the binaries for your runner. + +```yaml +- uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 +- id: plan + uses: GoSimulate/PackageCascade/.github/actions/plan@ + with: + version: + solution: src/Example.slnx +- id: apply + uses: GoSimulate/PackageCascade/.github/actions/apply@ + with: + version: + plan-path: ${{ steps.plan.outputs.plan-path }} + output-directory: artifacts/packages +``` + +Install the SDK required by your solution before these steps, and run your own tests before applying the plan. The apply action exposes `result-path` for subsequent publication or tag-push steps. Both actions accept an optional `repository` path; plan also accepts `configuration`, while apply accepts `include-symbols`. + +To build the CLI from source instead, install the SDK specified in this repository's `global.json` and run `dotnet build src/PackageCascade.Cli/PackageCascade.Cli.csproj -c Release`. + Both commands default to the current directory as the Git repository; use `--repository` to select another. Planning defaults to `Release`; `plan --configuration Debug` or a custom configuration records that choice for apply. Add `apply --include-symbols` to generate `.snupkg` files. Apply writes JSON containing the output package paths and created tag names to standard output. The SDK needed to build PackageCascade does not dictate the target frameworks of your packages. Apply uses the consumer solution's SDK selection and project settings. Versions are supplied during the build, without editing your project files; a reference to another package keeps that package's own version. From 55e3b9c48cbb51e0a39cd91c0aeaf87a7c91e2bc Mon Sep 17 00:00:00 2001 From: James McMahon Date: Wed, 28 Oct 2026 18:25:38 +0000 Subject: [PATCH 2/3] refactor: use bash for release actions --- .github/actions/apply/action.yml | 66 ++++++++++++++++++-------------- .github/actions/plan/action.yml | 60 +++++++++++++++++------------ .github/workflows/ci.yml | 2 +- 3 files changed, 75 insertions(+), 53 deletions(-) diff --git a/.github/actions/apply/action.yml b/.github/actions/apply/action.yml index 8c661cd..b445b4d 100644 --- a/.github/actions/apply/action.yml +++ b/.github/actions/apply/action.yml @@ -28,7 +28,7 @@ runs: using: composite steps: - id: run - shell: pwsh + shell: bash env: PACKAGECASCADE_VERSION: ${{ inputs.version }} PACKAGECASCADE_PLAN_PATH: ${{ inputs.plan-path }} @@ -37,32 +37,42 @@ runs: PACKAGECASCADE_INCLUDE_SYMBOLS: ${{ inputs.include-symbols }} PACKAGECASCADE_RELEASE_URL: https://github.com/GoSimulate/PackageCascade/releases/download run: | - $ErrorActionPreference = 'Stop' - $asset = switch ("$env:RUNNER_OS/$env:RUNNER_ARCH") { - 'Linux/X64' { 'PackageCascade-linux-x64' } - 'Windows/X64' { 'PackageCascade-win-x64.exe' } - 'macOS/X64' { 'PackageCascade-osx-x64' } - 'macOS/ARM64' { 'PackageCascade-osx-arm64' } - default { throw "PackageCascade has no release asset for $env:RUNNER_OS/$env:RUNNER_ARCH." } - } + set -euo pipefail + case "$RUNNER_OS/$RUNNER_ARCH" in + Linux/X64) asset=PackageCascade-linux-x64 ;; + Windows/X64) asset=PackageCascade-win-x64.exe ;; + macOS/X64) asset=PackageCascade-osx-x64 ;; + macOS/ARM64) asset=PackageCascade-osx-arm64 ;; + *) echo "No PackageCascade release asset for $RUNNER_OS/$RUNNER_ARCH" >&2; exit 1 ;; + esac - $downloadDirectory = Join-Path $env:RUNNER_TEMP ([guid]::NewGuid().ToString('N')) - New-Item -ItemType Directory -Path $downloadDirectory | Out-Null - $executable = Join-Path $downloadDirectory $asset - $tag = [uri]::EscapeDataString($env:PACKAGECASCADE_VERSION) - $url = "$env:PACKAGECASCADE_RELEASE_URL/$tag/$asset" - Invoke-WebRequest -Uri $url -OutFile $executable - if ($env:RUNNER_OS -ne 'Windows') { & chmod +x $executable } + temp_root="$RUNNER_TEMP" + output_file="$GITHUB_OUTPUT" + if [[ "$RUNNER_OS" == Windows ]]; then + temp_root="$(cygpath -u "$temp_root")" + output_file="$(cygpath -u "$output_file")" + fi - $resultPath = Join-Path $downloadDirectory 'packagecascade-apply-result.json' - $cliArguments = @('apply', '--infile', $env:PACKAGECASCADE_PLAN_PATH, '--output', $env:PACKAGECASCADE_OUTPUT_DIRECTORY) - if ($env:PACKAGECASCADE_REPOSITORY) { $cliArguments += @('--repository', $env:PACKAGECASCADE_REPOSITORY) } - switch ($env:PACKAGECASCADE_INCLUDE_SYMBOLS.ToLowerInvariant()) { - 'true' { $cliArguments += '--include-symbols' } - 'false' { } - default { throw "include-symbols must be 'true' or 'false'." } - } - & $executable @cliArguments > $resultPath - if ($LASTEXITCODE -ne 0) { throw "PackageCascade apply failed with exit code $LASTEXITCODE." } - if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw 'PackageCascade did not create an apply result.' } - "result-path=$resultPath" >> $env:GITHUB_OUTPUT + download_dir="$(mktemp -d "$temp_root/packagecascade.XXXXXXXX")" + executable="$download_dir/$asset" + curl --fail --location --silent --show-error \ + "$PACKAGECASCADE_RELEASE_URL/$PACKAGECASCADE_VERSION/$asset" --output "$executable" + chmod +x "$executable" + + result_path="$download_dir/packagecascade-apply-result.json" + arguments=(apply --infile "$PACKAGECASCADE_PLAN_PATH" --output "$PACKAGECASCADE_OUTPUT_DIRECTORY") + if [[ -n "$PACKAGECASCADE_REPOSITORY" ]]; then + arguments+=(--repository "$PACKAGECASCADE_REPOSITORY") + fi + case "$PACKAGECASCADE_INCLUDE_SYMBOLS" in + true) arguments+=(--include-symbols) ;; + false) ;; + *) echo 'include-symbols must be true or false' >&2; exit 1 ;; + esac + "$executable" "${arguments[@]}" > "$result_path" + test -s "$result_path" + + if [[ "$RUNNER_OS" == Windows ]]; then + result_path="$(cygpath -w "$result_path")" + fi + printf 'result-path=%s\n' "$result_path" >> "$output_file" diff --git a/.github/actions/plan/action.yml b/.github/actions/plan/action.yml index 4dcbdf0..e9d749e 100644 --- a/.github/actions/plan/action.yml +++ b/.github/actions/plan/action.yml @@ -24,7 +24,7 @@ runs: using: composite steps: - id: run - shell: pwsh + shell: bash env: PACKAGECASCADE_VERSION: ${{ inputs.version }} PACKAGECASCADE_SOLUTION: ${{ inputs.solution }} @@ -32,28 +32,40 @@ runs: PACKAGECASCADE_CONFIGURATION: ${{ inputs.configuration }} PACKAGECASCADE_RELEASE_URL: https://github.com/GoSimulate/PackageCascade/releases/download run: | - $ErrorActionPreference = 'Stop' - $asset = switch ("$env:RUNNER_OS/$env:RUNNER_ARCH") { - 'Linux/X64' { 'PackageCascade-linux-x64' } - 'Windows/X64' { 'PackageCascade-win-x64.exe' } - 'macOS/X64' { 'PackageCascade-osx-x64' } - 'macOS/ARM64' { 'PackageCascade-osx-arm64' } - default { throw "PackageCascade has no release asset for $env:RUNNER_OS/$env:RUNNER_ARCH." } - } + set -euo pipefail + case "$RUNNER_OS/$RUNNER_ARCH" in + Linux/X64) asset=PackageCascade-linux-x64 ;; + Windows/X64) asset=PackageCascade-win-x64.exe ;; + macOS/X64) asset=PackageCascade-osx-x64 ;; + macOS/ARM64) asset=PackageCascade-osx-arm64 ;; + *) echo "No PackageCascade release asset for $RUNNER_OS/$RUNNER_ARCH" >&2; exit 1 ;; + esac - $downloadDirectory = Join-Path $env:RUNNER_TEMP ([guid]::NewGuid().ToString('N')) - New-Item -ItemType Directory -Path $downloadDirectory | Out-Null - $executable = Join-Path $downloadDirectory $asset - $tag = [uri]::EscapeDataString($env:PACKAGECASCADE_VERSION) - $url = "$env:PACKAGECASCADE_RELEASE_URL/$tag/$asset" - Invoke-WebRequest -Uri $url -OutFile $executable - if ($env:RUNNER_OS -ne 'Windows') { & chmod +x $executable } + temp_root="$RUNNER_TEMP" + output_file="$GITHUB_OUTPUT" + if [[ "$RUNNER_OS" == Windows ]]; then + temp_root="$(cygpath -u "$temp_root")" + output_file="$(cygpath -u "$output_file")" + fi - $planPath = Join-Path $downloadDirectory 'packagecascade-plan.json' - $cliArguments = @('plan', $env:PACKAGECASCADE_SOLUTION, '--outfile', $planPath) - if ($env:PACKAGECASCADE_REPOSITORY) { $cliArguments += @('--repository', $env:PACKAGECASCADE_REPOSITORY) } - if ($env:PACKAGECASCADE_CONFIGURATION) { $cliArguments += @('--configuration', $env:PACKAGECASCADE_CONFIGURATION) } - & $executable @cliArguments - if ($LASTEXITCODE -ne 0) { throw "PackageCascade plan failed with exit code $LASTEXITCODE." } - if (-not (Test-Path -LiteralPath $planPath -PathType Leaf)) { throw 'PackageCascade did not create the plan file.' } - "plan-path=$planPath" >> $env:GITHUB_OUTPUT + download_dir="$(mktemp -d "$temp_root/packagecascade.XXXXXXXX")" + executable="$download_dir/$asset" + curl --fail --location --silent --show-error \ + "$PACKAGECASCADE_RELEASE_URL/$PACKAGECASCADE_VERSION/$asset" --output "$executable" + chmod +x "$executable" + + plan_path="$download_dir/packagecascade-plan.json" + arguments=(plan "$PACKAGECASCADE_SOLUTION" --outfile "$plan_path") + if [[ -n "$PACKAGECASCADE_REPOSITORY" ]]; then + arguments+=(--repository "$PACKAGECASCADE_REPOSITORY") + fi + if [[ -n "$PACKAGECASCADE_CONFIGURATION" ]]; then + arguments+=(--configuration "$PACKAGECASCADE_CONFIGURATION") + fi + "$executable" "${arguments[@]}" + test -s "$plan_path" + + if [[ "$RUNNER_OS" == Windows ]]; then + plan_path="$(cygpath -w "$plan_path")" + fi + printf 'plan-path=%s\n' "$plan_path" >> "$output_file" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8b3d9c..4a1f9b4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -78,5 +78,5 @@ jobs: - name: Publish CLI run: dotnet publish src/PackageCascade.Cli/PackageCascade.Cli.csproj -c Release -r ${{ matrix.rid }} --self-contained true -o artifacts/cli -p:PublishSingleFile=true -p:IncludeNativeLibrariesForSelfExtract=true -p:DebugType=embedded - name: Run CLI help - shell: pwsh + shell: bash run: ./artifacts/cli/${{ matrix.binary }} --help From 3d4dede14bff949f9bd2fdd6347689f22d1aa044 Mon Sep 17 00:00:00 2001 From: James McMahon Date: Wed, 28 Oct 2026 18:28:21 +0000 Subject: [PATCH 3/3] docs: show how to use the release actions --- README.md | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 116f939..85ca0af 100644 --- a/README.md +++ b/README.md @@ -42,26 +42,35 @@ set -o pipefail ### Use the GitHub Actions -The `plan` and `apply` composite actions download a specified PackageCascade release and expose paths to their JSON results. Use the same release tag in each `uses` reference and `version` input. Replace `` with a published tag that contains the actions and the binaries for your runner. +The `plan` and `apply` composite actions download a specified PackageCascade release and expose paths to their JSON results. This example is a workflow in the repository being released. Replace `` with a published PackageCascade tag, and use that same tag in each `uses` reference and `version` input. Change the solution path to match your repository. ```yaml -- uses: actions/checkout@v7.0.1 - with: - fetch-depth: 0 -- id: plan - uses: GoSimulate/PackageCascade/.github/actions/plan@ - with: - version: - solution: src/Example.slnx -- id: apply - uses: GoSimulate/PackageCascade/.github/actions/apply@ - with: - version: - plan-path: ${{ steps.plan.outputs.plan-path }} - output-directory: artifacts/packages +name: Prepare packages +on: workflow_dispatch +jobs: + prepare: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 + - uses: actions/setup-dotnet@v6.0.0 + with: + global-json-file: global.json + - id: plan + uses: GoSimulate/PackageCascade/.github/actions/plan@ + with: + version: + solution: src/Example.slnx + - id: apply + uses: GoSimulate/PackageCascade/.github/actions/apply@ + with: + version: + plan-path: ${{ steps.plan.outputs.plan-path }} + output-directory: artifacts/packages ``` -Install the SDK required by your solution before these steps, and run your own tests before applying the plan. The apply action exposes `result-path` for subsequent publication or tag-push steps. Both actions accept an optional `repository` path; plan also accepts `configuration`, while apply accepts `include-symbols`. +The example reads the SDK version from the consumer repository's `global.json`; configure SDK installation for your repository if it uses another method. Run your own tests before applying the plan. The apply action exposes `result-path` for subsequent publication or tag-push steps. Both actions accept an optional `repository` path; plan also accepts `configuration`, while apply accepts `include-symbols`. To build the CLI from source instead, install the SDK specified in this repository's `global.json` and run `dotnet build src/PackageCascade.Cli/PackageCascade.Cli.csproj -c Release`.