Prepare Release #20
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: Prepare Release | |
| # Creates a release PR from develop -> main. | |
| # Maintainers trigger this from Actions -> Prepare Release -> Run workflow. | |
| # After reviewing, merge the PR; the Finalize Release workflow creates the tag, | |
| # GitHub Release, and back-merge PR. | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| release_type: | |
| description: 'Release type' | |
| required: true | |
| type: choice | |
| options: | |
| - beta | |
| - rc | |
| - stable | |
| default: stable | |
| version_override: | |
| description: 'Version override (optional, e.g., 2.6.0). Defaults to GitVersion''s next patch (latest tag + 1).' | |
| required: false | |
| type: string | |
| concurrency: | |
| group: prepare-release | |
| cancel-in-progress: false | |
| jobs: | |
| prepare-release: | |
| name: Create Release PR | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Checkout develop | |
| uses: actions/checkout@v5 | |
| with: | |
| ref: develop | |
| fetch-depth: 0 | |
| token: ${{ secrets.RELEASE_PAT }} | |
| - name: Configure Git | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| - name: Install GitVersion | |
| uses: gittools/actions/gitversion/setup@v4.5.0 | |
| with: | |
| versionSpec: '6.x' | |
| - name: Run GitVersion | |
| id: gitversion | |
| # GitVersion.yml at the repo root is picked up automatically. | |
| uses: gittools/actions/gitversion/execute@v4.5.0 | |
| - name: Require a stable Terminal.Gui pin for stable releases | |
| if: github.event.inputs.release_type == 'stable' | |
| shell: bash | |
| run: | | |
| TG_PIN=$(sed -n 's|.*<TerminalGuiVersion[^>]*>\(.*\)</TerminalGuiVersion>.*|\1|p' Directory.Build.props | head -1) | |
| if [ -z "$TG_PIN" ]; then | |
| echo "::error::Could not read <TerminalGuiVersion> from Directory.Build.props." | |
| exit 1 | |
| fi | |
| if echo "$TG_PIN" | grep -q -- '-'; then | |
| echo "::error::TerminalGuiVersion is '${TG_PIN}', a pre-release. A stable Editor release must depend on a stable Terminal.Gui (NuGet rejects stable→prerelease dependencies, NU5104). Pin a stable TG on develop first (e.g. via the Bump Terminal.Gui workflow with channel=stable)." | |
| exit 1 | |
| fi | |
| echo "TerminalGuiVersion pin is stable: ${TG_PIN}" | |
| - name: Compute release version | |
| id: version | |
| shell: bash | |
| run: | | |
| if [ -n "${{ github.event.inputs.version_override }}" ]; then | |
| VERSION="${{ github.event.inputs.version_override }}" | |
| else | |
| # Next patch over the latest tag reachable from develop (GitVersion.yml: increment Patch). | |
| VERSION="${{ steps.gitversion.outputs.MajorMinorPatch }}" | |
| fi | |
| RELEASE_TYPE="${{ github.event.inputs.release_type }}" | |
| if [ -z "$VERSION" ]; then | |
| echo "::error::Could not resolve release version." | |
| exit 1 | |
| fi | |
| if ! echo "$VERSION" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then | |
| echo "::error::Release version must be a stable SemVer core like 2.2.4; got '$VERSION'." | |
| exit 1 | |
| fi | |
| if [ "$RELEASE_TYPE" = "stable" ]; then | |
| SEMVER="$VERSION" | |
| TAG="v${VERSION}" | |
| else | |
| LATEST_TAG=$(git tag -l "v${VERSION}-${RELEASE_TYPE}.*" --sort=-v:refname | head -1) | |
| if [ -z "$LATEST_TAG" ]; then | |
| NEXT_NUM=1 | |
| else | |
| CURRENT_NUM="${LATEST_TAG##*.}" | |
| NEXT_NUM=$((CURRENT_NUM + 1)) | |
| fi | |
| SEMVER="${VERSION}-${RELEASE_TYPE}.${NEXT_NUM}" | |
| TAG="v${SEMVER}" | |
| fi | |
| echo "version=${VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "semver=${SEMVER}" >> "$GITHUB_OUTPUT" | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "release_type=${RELEASE_TYPE}" >> "$GITHUB_OUTPUT" | |
| echo "::notice::Computed version: ${SEMVER} (tag: ${TAG})" | |
| - name: Check for conflicts | |
| shell: bash | |
| run: | | |
| TAG="${{ steps.version.outputs.tag }}" | |
| BRANCH="release/${TAG}" | |
| if git rev-parse "${TAG}" >/dev/null 2>&1; then | |
| echo "::error::Tag ${TAG} already exists." | |
| exit 1 | |
| fi | |
| if git ls-remote --heads origin "${BRANCH}" | grep -q .; then | |
| echo "::error::Branch ${BRANCH} already exists on the remote." | |
| exit 1 | |
| fi | |
| if curl -fsS https://api.nuget.org/v3-flatcontainer/terminal.gui.editor/index.json \ | |
| | jq -r '.versions[]' | grep -Fxq "${{ steps.version.outputs.semver }}"; then | |
| echo "::error::Terminal.Gui.Editor ${{ steps.version.outputs.semver }} already exists on NuGet.org." | |
| echo "::error::Choose a new version_override." | |
| exit 1 | |
| fi | |
| if gh pr list --state open --base main --head "${BRANCH}" --json number --jq 'length' | grep -vq '^0$'; then | |
| echo "::error::An open release PR already exists for ${BRANCH}." | |
| exit 1 | |
| fi | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PAT }} | |
| - name: Create release branch | |
| shell: bash | |
| run: | | |
| BRANCH="release/${{ steps.version.outputs.tag }}" | |
| git checkout -b "$BRANCH" | |
| git push origin "$BRANCH" | |
| - name: Create Pull Request | |
| env: | |
| GH_TOKEN: ${{ secrets.RELEASE_PAT }} | |
| SEMVER: ${{ steps.version.outputs.semver }} | |
| TAG: ${{ steps.version.outputs.tag }} | |
| RELEASE_TYPE: ${{ steps.version.outputs.release_type }} | |
| shell: bash | |
| run: | | |
| BRANCH="release/${TAG}" | |
| if [ "$RELEASE_TYPE" = "stable" ]; then | |
| PRERELEASE_NOTE="" | |
| else | |
| PRERELEASE_NOTE="This is a **${RELEASE_TYPE}** pre-release." | |
| fi | |
| cat > /tmp/pr_body.md << EOF | |
| ## Release ${TAG} | |
| ${PRERELEASE_NOTE} | |
| **Version:** \`${SEMVER}\` | |
| **NuGet Package:** \`Terminal.Gui.Editor ${SEMVER}\` | |
| ### What happens when this PR is merged | |
| 1. The **Finalize Release** workflow creates tag \`${TAG}\` | |
| 2. The **Release** workflow builds and pushes \`Terminal.Gui.Editor ${SEMVER}\` to NuGet.org | |
| 3. A **GitHub Release** is created with auto-generated notes | |
| 4. A **back-merge PR** from \`main\` -> \`develop\` is opened | |
| ### Checklist | |
| - [ ] CI passes on this PR | |
| - [ ] Version looks correct: \`${SEMVER}\` | |
| - [ ] Release notes reviewed | |
| EOF | |
| gh pr create \ | |
| --base main \ | |
| --head "$BRANCH" \ | |
| --title "Release ${TAG}" \ | |
| --body-file /tmp/pr_body.md | |
| - name: Summary | |
| run: | | |
| echo "## Release PR Created" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Version:** ${{ steps.version.outputs.semver }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Tag:** ${{ steps.version.outputs.tag }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Type:** ${{ steps.version.outputs.release_type }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "- **Branch:** release/${{ steps.version.outputs.tag }}" >> "$GITHUB_STEP_SUMMARY" | |
| echo "" >> "$GITHUB_STEP_SUMMARY" | |
| echo "Review and merge the PR to trigger the release." >> "$GITHUB_STEP_SUMMARY" |