Mass Release #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Mass Release | |
| # One-shot workflow to publish every Codout.Framework package at the <Version> | |
| # currently pinned in its .csproj. Designed for the initial baseline release | |
| # after migrating to per-package versions (where most packages are at 6.2.2 | |
| # and Codout.Framework.EF is at 6.3.0). Idempotent thanks to --skip-duplicate, | |
| # so it is safe to re-run. | |
| # | |
| # Recommended flow: | |
| # 1. Dispatch with dry_run = true to confirm the package list and artifacts. | |
| # 2. Re-dispatch with dry_run = false to publish. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| confirm: | |
| description: 'Type PUBLISH to confirm. Anything else is rejected.' | |
| required: true | |
| include_mcp: | |
| description: 'Also release Codout.Framework.Mcp (otherwise excluded; use mcp-release.yml).' | |
| type: boolean | |
| default: false | |
| dry_run: | |
| description: 'Build and pack only; do not push to NuGet.org.' | |
| type: boolean | |
| default: true | |
| env: | |
| DOTNET_NOLOGO: true | |
| DOTNET_CLI_TELEMETRY_OPTOUT: true | |
| CONFIGURATION: Release | |
| MAPPING: .github/release-packages.json | |
| MCP_PROJECT: Codout.Framework.Mcp/src/Tools/Codout.Framework.Mcp/Codout.Framework.Mcp.csproj | |
| jobs: | |
| mass-release: | |
| runs-on: ubuntu-latest | |
| # Note: the confirm input is validated explicitly in the first step of this | |
| # job, not via a job-level `if:`. A job-level `if: inputs.confirm == 'PUBLISH'` | |
| # silently marks the run as "Skipped" when the input does not match, which | |
| # is awful for debugging — you cannot tell whether the workflow refused you | |
| # or never started. The step-based check below fails loud with a clear | |
| # error message. | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Validate inputs | |
| env: | |
| CONFIRM: ${{ github.event.inputs.confirm }} | |
| DRY_RUN: ${{ github.event.inputs.dry_run }} | |
| INCLUDE_MCP: ${{ github.event.inputs.include_mcp }} | |
| run: | | |
| set -euo pipefail | |
| echo "confirm=${CONFIRM:-<empty>}" | |
| echo "dry_run=${DRY_RUN:-<empty>}" | |
| echo "include_mcp=${INCLUDE_MCP:-<empty>}" | |
| if [ "${CONFIRM:-}" != "PUBLISH" ]; then | |
| echo "::error::The 'confirm' input must be exactly 'PUBLISH' (case-sensitive). Got '${CONFIRM:-<empty>}'." | |
| exit 1 | |
| fi | |
| echo "Confirm input OK." | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup .NET | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: 10.0.x | |
| - name: Verify dispatched from master | |
| run: | | |
| set -euo pipefail | |
| git fetch origin master --quiet | |
| if ! git merge-base --is-ancestor HEAD origin/master; then | |
| echo "::error::Mass release must be dispatched from a commit on master." | |
| echo "Currently at $(git rev-parse HEAD); origin/master tip is $(git rev-parse origin/master)." | |
| exit 1 | |
| fi | |
| echo "Dispatched from a commit on master: OK" | |
| - name: Restore + build + test (solution) | |
| run: | | |
| dotnet restore Codout.Framework.sln | |
| dotnet build Codout.Framework.sln --configuration "$CONFIGURATION" --no-restore | |
| dotnet test Codout.Framework.sln --configuration "$CONFIGURATION" --no-build --verbosity normal | |
| - name: Pack every package | |
| run: | | |
| set -euo pipefail | |
| mkdir -p ./artifacts | |
| # Out-of-solution projects (Cosmos, DocumentDB, etc.) need their own | |
| # restore/build, so we deliberately do NOT pass --no-build here. | |
| for pkg in $(jq -r 'keys[]' "$MAPPING"); do | |
| project=$(jq -r --arg p "$pkg" '.[$p]' "$MAPPING") | |
| echo "::group::Pack $pkg ($project)" | |
| dotnet pack "$project" --configuration "$CONFIGURATION" --output ./artifacts | |
| echo "::endgroup::" | |
| done | |
| if [ "${{ github.event.inputs.include_mcp }}" = "true" ]; then | |
| echo "::group::Pack mcp ($MCP_PROJECT)" | |
| dotnet pack "$MCP_PROJECT" --configuration "$CONFIGURATION" --output ./artifacts | |
| echo "::endgroup::" | |
| fi | |
| echo "Generated artifacts:" | |
| ls -la ./artifacts | |
| - name: Upload artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: codout-framework-all-nupkg | |
| path: ./artifacts/*.nupkg | |
| if-no-files-found: error | |
| - name: Push to NuGet.org | |
| if: github.event.inputs.dry_run != 'true' | |
| env: | |
| NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${NUGET_API_KEY:-}" ]; then | |
| echo "::error::NUGET_API_KEY secret is not set; aborting." | |
| exit 1 | |
| fi | |
| # --skip-duplicate makes this idempotent: re-runs will not fail on | |
| # already-published versions. | |
| dotnet nuget push "./artifacts/*.nupkg" \ | |
| --api-key "$NUGET_API_KEY" \ | |
| --source https://api.nuget.org/v3/index.json \ | |
| --skip-duplicate | |
| - name: Summary | |
| if: always() | |
| run: | | |
| { | |
| echo "## Mass release summary" | |
| echo "" | |
| echo "- Dry run: \`${{ github.event.inputs.dry_run }}\`" | |
| echo "- Mcp included: \`${{ github.event.inputs.include_mcp }}\`" | |
| echo "" | |
| echo "### Packed artifacts" | |
| echo "" | |
| echo '```' | |
| ls -1 ./artifacts 2>/dev/null || echo "(no artifacts directory)" | |
| echo '```' | |
| } >> "$GITHUB_STEP_SUMMARY" |