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
78 changes: 78 additions & 0 deletions .github/actions/apply/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
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: bash
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: |
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

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

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"
71 changes: 71 additions & 0 deletions .github/actions/plan/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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: bash
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: |
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

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

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"
27 changes: 17 additions & 10 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
paths:
- 'src/**'
- '!src/*.Tests.*/**'
- '.github/actions/**'
- '.github/workflows/cd.yml'
workflow_dispatch:
permissions:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
35 changes: 34 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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: bash
run: ./artifacts/cli/${{ matrix.binary }} --help
53 changes: 42 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,30 +19,61 @@ 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. This example is a workflow in the repository being released. Replace `<release-tag>` 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
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@<release-tag>
with:
version: <release-tag>
solution: src/Example.slnx
- id: apply
uses: GoSimulate/PackageCascade/.github/actions/apply@<release-tag>
with:
version: <release-tag>
plan-path: ${{ steps.plan.outputs.plan-path }}
output-directory: artifacts/packages
```

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`.

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.
Expand Down
Loading