From 49f735e508f5731395953490f7028193443f08ce Mon Sep 17 00:00:00 2001 From: Will Fuqua Date: Thu, 4 Jun 2026 23:24:18 +0700 Subject: [PATCH] Update release-to-github process --- .github/workflows/release.yml | 81 ++++++++++++++++------------------- PublishRelease.md | 20 ++++++--- publish-release.ps1 | 23 ++++++++++ 3 files changed, 76 insertions(+), 48 deletions(-) create mode 100644 publish-release.ps1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f4ae7b7..1ffcc7e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,59 +1,54 @@ -# publishes to nuget if the version number in PrettyPrompt.csproj changes - -name: publish to nuget +# On pushing a tag like v5.0.1, publish to nuget.org and create a GitHub release. +# Use publish-release.ps1 to push a release. +name: release on: push: - branches: - - main - + tags: + - "v[0-9]+.*" # not a regex! ".*" means a period followed by any character https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#filter-pattern-cheat-sheet + +permissions: + contents: write # needed to create the GitHub release + jobs: build: runs-on: windows-latest steps: - - uses: actions/checkout@v1 - - - name: Check for new version - id: CheckReleaseRequired - shell: pwsh - run: | - $publishedversions = (Find-Package -AllVersions -AllowPrereleaseVersions -Name "PrettyPrompt").Version - echo "Found published versions: $publishedversions" - if ($publishedversions.Count -gt 0) { - $csproj = [xml](Get-Content ./src/PrettyPrompt/PrettyPrompt.csproj) - $localversion = $csproj.Project.PropertyGroup.Version[0] - echo "Version in the repository is $localversion" - $isReleaseRequired = (-not $publishedversions.Contains($localversion)).ToString().ToLower() - echo "Release required: $isReleaseRequired" - echo "::set-output name=LOCAL_VERSION::v$localversion" - echo "::set-output name=IS_RELEASE_REQUIRED::$isReleaseRequired" - } - + - uses: actions/checkout@v4 + - name: Install Dotnet - uses: actions/setup-dotnet@v1 - if: steps.CheckReleaseRequired.outputs.IS_RELEASE_REQUIRED == 'true' + uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 10.0.x + dotnet-version: '10.0.x' - - name: Create NuGet Package - if: steps.CheckReleaseRequired.outputs.IS_RELEASE_REQUIRED == 'true' + - name: Dotnet Installation Info + run: dotnet --info + + - name: Pack run: dotnet pack ./src/PrettyPrompt/PrettyPrompt.csproj -c Release -p:ContinuousIntegrationBuild=true - name: Publish to NuGet - if: steps.CheckReleaseRequired.outputs.IS_RELEASE_REQUIRED == 'true' - run: dotnet nuget push --skip-duplicate --api-key ${{secrets.NUGET_API_KEY}} --source 'https://api.nuget.org/v3/index.json' .\src\PrettyPrompt\bin\Release\PrettyPrompt.*.nupkg + run: dotnet nuget push --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }} --source 'https://api.nuget.org/v3/index.json' .\src\PrettyPrompt\bin\Release\PrettyPrompt.*.nupkg - - name: Create Git Tag - if: steps.CheckReleaseRequired.outputs.IS_RELEASE_REQUIRED == 'true' - uses: actions/github-script@v6 - with: - script: | - github.rest.git.createRef({ - owner: context.repo.owner, - repo: context.repo.repo, - ref: 'refs/tags/${{ steps.CheckReleaseRequired.outputs.LOCAL_VERSION }}', - sha: context.sha - }) + # Pull the notes for this version straight out of CHANGELOG.md so the GitHub release + # mirrors what's already curated there (CHANGELOG.md is the single source of truth). + - name: Extract release notes from CHANGELOG + shell: pwsh + run: | + $version = '${{ github.ref_name }}'.TrimStart('v') + $changelog = Get-Content -Raw CHANGELOG.md + $escaped = [regex]::Escape($version) + $match = [regex]::Match($changelog, "(?ms)^# Release $escaped[ \t]*\r?\n(.*?)(?=\r?\n# Release |\z)") + if (-not $match.Success) { + Write-Error "Could not find a '# Release $version' section in CHANGELOG.md" + exit 1 + } + Set-Content -Path release-notes.md -Value $match.Groups[1].Value.Trim() + + - name: Create GitHub release + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --notes-file release-notes.md --verify-tag diff --git a/PublishRelease.md b/PublishRelease.md index 5750a8c..260b8c4 100644 --- a/PublishRelease.md +++ b/PublishRelease.md @@ -2,9 +2,19 @@ If you want to make a new release of PrettyPrompt: -- Pull the latest `main` branch. -- Increment the version in PrettyPrompt.csproj -- Update the release notes -- Make a pull request against `main` +1. Pull the latest `main` branch. +2. Increment the `` in `src/PrettyPrompt/PrettyPrompt.csproj`. +3. Add a `# Release ` section to the top of `CHANGELOG.md` describing the changes. +4. Make a pull request with the above, and merge it into `main`. +5. From an up-to-date `main`, run `./publish-release.ps1`. -The GitHub action will handle the rest -- it will read the version from the csproj, publish and build to nuget, and create a git tag automatically. +The script verifies your checkout is clean and up to date, reads the version from the csproj, +and (after a confirmation prompt) creates and pushes a `v` git tag. + +Pushing the tag triggers `.github/workflows/release.yml`, which: + +- builds and pushes the NuGet package to nuget.org, and +- creates a GitHub release for the tag, using the matching `# Release ` section of + `CHANGELOG.md` as the release notes. + +Nothing is published until you push the tag, so bumping the version in a PR is safe on its own. diff --git a/publish-release.ps1 b/publish-release.ps1 new file mode 100644 index 0000000..36868b4 --- /dev/null +++ b/publish-release.ps1 @@ -0,0 +1,23 @@ +$localChanges = git status --short +if ( $null -ne $localChanges ) { + Write-Output "Uncommitted changes detected, aborting release." + Exit 1 +} + +git fetch origin +$remoteChanges = git log HEAD..origin/main --oneline +if ( $null -ne $remoteChanges ) { + Write-Output "The main branch is out of date, aborting release." + Exit 2 +} + +$csproj = [xml](Get-Content ./src/PrettyPrompt/PrettyPrompt.csproj) +# Select the node directly. Using xpath allows the project to have more than one +$version = $csproj.SelectSingleNode('//Project/PropertyGroup/Version').InnerText.Trim() + +Write-Output "Reminder: Did you update the CHANGELOG.md with a '# Release $version' section?" +Write-Output "Press Enter to create tag ""v$version"" and publish to nuget.org" +Read-Host + +git tag "v$version" +git push origin "v$version"