Prepare Release #1
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" | |
| on: # yamllint disable-line rule:truthy rule:comments | |
| workflow_dispatch: | |
| inputs: | |
| bump_rule: | |
| description: "Select the version bump type" | |
| required: true | |
| default: "patch" | |
| type: "choice" | |
| options: | |
| - "prerelease" | |
| - "patch" | |
| - "minor" | |
| - "major" | |
| target_branch: | |
| description: "Create the release from this branch (default: main)." | |
| required: true | |
| default: "main" | |
| date: | |
| description: "Date of the release YYYY-MM-DD (defaults to today's date in the US Eastern TZ)." | |
| required: false | |
| default: "" | |
| previous_version: | |
| description: "The previous version tag to use for generating release notes (defaults to the latest tag or initial commit if no tags exist)." | |
| required: false | |
| default: "" | |
| jobs: | |
| prepare-release: | |
| permissions: | |
| contents: "write" | |
| pull-requests: "write" | |
| name: "Prepare Release" | |
| runs-on: "ubuntu-latest" | |
| steps: | |
| - name: "Checkout code" | |
| uses: "actions/checkout@v4" | |
| with: | |
| # If target_branch is 'main', use 'develop' as the source branch. Otherwise, the source and target branch are the same. | |
| ref: "${{ github.event.inputs['target_branch'] == 'main' && 'develop' || github.event.inputs['target_branch'] }}" | |
| fetch-depth: 0 # Fetch all history for git tags | |
| - name: "Setup environment" | |
| uses: "networktocode/gh-action-setup-poetry-environment@v7" | |
| with: | |
| poetry-version: "2.1.3" | |
| poetry-install-options: "--with dev" | |
| - name: "Validate Branch and Tags" | |
| run: | | |
| # 1. Verify branch exists | |
| if ! git rev-parse --verify origin/${{ github.event.inputs.target_branch }} > /dev/null 2>&1; then | |
| echo "Error: Branch ${{ github.event.inputs.target_branch }} does not exist." | |
| exit 1 | |
| fi | |
| # 2. Try to get the previous version tag | |
| # If it fails (no tags), get the hash of the first commit | |
| if [ -n "${{ github.event.inputs.previous_version }}" ]; then | |
| PREV_TAG="${{ github.event.inputs.previous_version }}" | |
| echo "Using user-specified previous version: $PREV_TAG" | |
| echo "PREVIOUS_TAG=$PREV_TAG" >> $GITHUB_ENV | |
| elif PREV_TAG=$(git describe --tags --abbrev=0 2>/dev/null); then | |
| echo "PREVIOUS_TAG=$PREV_TAG" >> $GITHUB_ENV | |
| echo "Found previous tag: $PREV_TAG" | |
| else | |
| # Fallback to the first commit in the repository | |
| FIRST_COMMIT=$(git rev-list --max-parents=0 HEAD) | |
| echo "PREVIOUS_TAG=$FIRST_COMMIT" >> $GITHUB_ENV | |
| echo "No tags found. Falling back to initial commit: $FIRST_COMMIT" | |
| fi | |
| - name: "Determine New Version" | |
| id: "versioning" | |
| env: | |
| GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | |
| run: | | |
| # Bump the poetry version based on the user input | |
| CURRENT_POETRY_VER=$(poetry version --short) | |
| set +e # continue running if the next command fails | |
| GITHUB_RELEASE_EXISTS=$(gh release view v$CURRENT_POETRY_VER --json publishedAt --jq '.publishedAt') | |
| set -e | |
| # Unconditionally bump the version if the current version is alpha0 or beta0 | |
| if [[ $CURRENT_POETRY_VER == *a0 || $CURRENT_POETRY_VER == *b0 || $CURRENT_POETRY_VER == *rc0 ]]; then | |
| poetry version ${{ github.event.inputs.bump_rule }} | |
| # If the bump rule is prerelease and a release doesn't already exist in github we don't bump the version | |
| # This is because we've already bumped to the prerelease version after the previous release | |
| elif [[ "${{ github.event.inputs.bump_rule }}" == "prerelease" && "$CURRENT_POETRY_VER" =~ (a|b|rc)[0-9]+$ && -z "$GITHUB_RELEASE_EXISTS" ]]; then | |
| echo "Prerelease v$CURRENT_POETRY_VER does not exist in github, using this version" | |
| else | |
| poetry version ${{ github.event.inputs.bump_rule }} | |
| fi | |
| # Capture the New version string for use in other steps | |
| NEW_VER=$(poetry version --short) | |
| echo "NEW_VERSION=$NEW_VER" >> $GITHUB_ENV | |
| echo "RELEASE_BRANCH=release/$NEW_VER" >> $GITHUB_ENV | |
| - name: "Set Date Variable" | |
| run: | | |
| if [ -z "${{ github.event.inputs.date }}" ]; then | |
| RELEASE_DATE=$(TZ=America/New_York date +%Y-%m-%d) | |
| else | |
| RELEASE_DATE="${{ github.event.inputs.date }}" | |
| fi | |
| echo "RELEASE_DATE=$RELEASE_DATE" >> $GITHUB_ENV | |
| - name: "Create Release Branch" | |
| env: | |
| GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | |
| run: | | |
| git config user.name "${{ github.actor }}" | |
| git config user.email "${{ github.actor }}@users.noreply.github.com" | |
| # Ensure release branch doesn't already exist | |
| if git rev-parse --verify origin/${{ env.RELEASE_BRANCH }} > /dev/null 2>&1; then | |
| echo "Error: Release branch ${{ env.RELEASE_BRANCH }} already exists." | |
| exit 1 | |
| fi | |
| # Create a new branch for the release | |
| git checkout -b "${{ env.RELEASE_BRANCH }}" | |
| - name: "Regenerate poetry.lock" | |
| run: "poetry lock --regenerate" | |
| - name: "Generate Github Release Notes" | |
| env: | |
| GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | |
| run: | | |
| # 1. Get Towncrier Draft | |
| TOWNCRIER_NOTES=$(poetry run towncrier build --version "${{ env.NEW_VERSION }}" --date "${{ env.RELEASE_DATE }}" --draft) | |
| # 2. Call GitHub API to generate raw notes | |
| RAW_GH_NOTES=$(gh api /repos/${{ github.repository }}/releases/generate-notes \ | |
| -f tag_name="v${{ env.NEW_VERSION }}" \ | |
| -f target_commitish="${{ github.event.inputs['target_branch'] == 'main' && 'develop' || github.event.inputs['target_branch'] }}" \ | |
| -f previous_tag_name="${{ env.PREVIOUS_TAG }}" --jq '.body') | |
| # 3. Parse usernames (Regex match) | |
| # We use grep to find "@user" patterns, sort, and uniq them | |
| USERNAMES=$(echo "$RAW_GH_NOTES" | grep -oP 'by @\K[a-zA-Z0-9-]+' | sort -u | grep -vE 'dependabot|nautobot-bot|github-actions' || true) | |
| # 4. Format the Contributors section | |
| CONTRIBUTORS_SECTION="## Contributors" | |
| for user in $USERNAMES; do | |
| CONTRIBUTORS_SECTION="$CONTRIBUTORS_SECTION"$'\n'"* @$user" | |
| done | |
| # 5. Extract the "Full Changelog" or "New Contributors" part | |
| # Using awk to grab everything from '## New Contributors' or '**Full Changelog**' to the end | |
| GH_FOOTER=$(echo "$RAW_GH_NOTES" | awk '/## New Contributors/ || /\*\*Full Changelog\*\*/ {found=1} found {print}') | |
| if [ -z "$GH_FOOTER" ]; then | |
| GH_FOOTER=$(echo "$RAW_GH_NOTES" | sed -n '/**Full Changelog**/,$p') | |
| fi | |
| # 6. Combine everything | |
| FINAL_NOTES="$TOWNCRIER_NOTES"$'\n\n'"$CONTRIBUTORS_SECTION"$'\n\n'"$GH_FOOTER" | |
| # 7. Save to a temporary file to avoid shell argument length limits | |
| echo "$FINAL_NOTES" > ../consolidated_notes.md | |
| - name: "Generate App Release Notes and update mkdocs.yml" | |
| run: "poetry run inv generate-release-notes --version '${{ env.NEW_VERSION }}' --date '${{ env.RELEASE_DATE }}'" | |
| - name: "Commit Changes and Push" | |
| run: | | |
| # Add all changes (pyproject.toml, poetry.lock, etc.) | |
| git add . | |
| git commit -m "prepare release v${{ env.NEW_VERSION }}" | |
| git push origin "${{ env.RELEASE_BRANCH }}" | |
| - name: "Create Pull Request" | |
| env: | |
| GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | |
| run: | | |
| gh pr create \ | |
| --title "Release v${{ env.NEW_VERSION }}" \ | |
| --body-file "../consolidated_notes.md" \ | |
| --base "${{ github.event.inputs.target_branch }}" \ | |
| --head "${{ env.RELEASE_BRANCH }}" | |
| - name: "Create Draft Release" | |
| env: | |
| GH_TOKEN: "${{ secrets.GITHUB_TOKEN }}" | |
| run: | | |
| if [[ "${{ github.event.inputs.bump_rule }}" == "prerelease" ]]; then | |
| RELEASE_FLAGS="--prerelease" | |
| elif [[ "${{ github.event.inputs.target_branch }}" == "main" ]]; then | |
| RELEASE_FLAGS="--latest" | |
| else | |
| RELEASE_FLAGS="--latest=false" | |
| fi | |
| gh release create "v${{ env.NEW_VERSION }}" \ | |
| --draft \ | |
| $RELEASE_FLAGS \ | |
| --title "v${{ env.NEW_VERSION }} - ${{ env.RELEASE_DATE }}" \ | |
| --notes-file "../consolidated_notes.md" \ | |
| --target "${{ github.event.inputs.target_branch }}" |