From cc64b0c36d65bd0606b12991f0b40510c96afaf7 Mon Sep 17 00:00:00 2001 From: Rupesh J Date: Fri, 18 Jul 2025 22:21:17 +0530 Subject: [PATCH 1/2] Add PreRelease pipeline to generate Release notes. --- .github/workflows/pr-labeler.yml | 26 +++++++ .github/workflows/pre-release.yml | 114 ++++++++++++++++++++++++++++++ .github/workflows/release.yml | 48 ++++++++++--- 3 files changed, 178 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/pr-labeler.yml create mode 100644 .github/workflows/pre-release.yml diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml new file mode 100644 index 0000000..60af799 --- /dev/null +++ b/.github/workflows/pr-labeler.yml @@ -0,0 +1,26 @@ +name: Add Default PR Label + +on: + pull_request: + types: [opened] + +jobs: + add-label: + name: Add default semver label + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - uses: actions/checkout@v4 + - name: Add semver:dev label if no other semver label exists + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + LABELS=$(gh pr view "$PR_NUMBER" --json labels --jq '.labels.[].name' 2>/dev/null || echo "") + if ! echo "$LABELS" | grep -q "semver:"; then + echo "No semver label found. Adding semver:dev." + gh pr edit "$PR_NUMBER" --add-label "semver:dev" + else + echo "A semver label is already present. Skipping." + fi diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml new file mode 100644 index 0000000..6b3d2ff --- /dev/null +++ b/.github/workflows/pre-release.yml @@ -0,0 +1,114 @@ +name: Draft release pull request + +on: + pull_request: + types: [closed] + branches: + - main + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + generate-changelog: + name: Create a PR to update version and release notes + if: github.event.pull_request.merged == true + permissions: + contents: write + actions: write + pull-requests: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 # Needed to get all tags for changelog generation + - name: Set up Python 3.12 + uses: actions/setup-python@v4 + with: + python-version: 3.12 + cache: pip + - name: Install build tool + run: python -m pip install hatch + - name: Determine Version Bump from PR Labels + id: version + run: | + BUMP="patch" + LABELS='${{ toJSON(github.event.pull_request.labels.*.name) }}' + echo "Checking labels: $LABELS" + if echo "$LABELS" | grep -q "semver:major"; then + BUMP="major" + elif echo "$LABELS" | grep -q "semver:minor"; then + BUMP="minor" + elif echo "$LABELS" | grep -q "semver:alpha"; then + BUMP="alpha" + elif echo "$LABELS" | grep -q "semver:beta"; then + BUMP="beta" + elif echo "$LABELS" | grep -q "semver:preview"; then + BUMP="preview" + elif echo "$LABELS" | grep -q "semver:dev"; then + BUMP="dev" + fi + echo "Version bump determined: $BUMP" + echo "level=${BUMP}" >> $GITHUB_OUTPUT + - name: Bump version + run: hatch version ${{ steps.version.outputs.level }} + - name: Generate release notes + if: steps.version.outputs.level == 'major' || steps.version.outputs.level == 'minor' || steps.version.outputs.level == 'patch' || steps.version.outputs.level == 'beta' + id: changelog + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + # This will be empty if there are no releases yet + PREVIOUS_VERSION=$(gh release view --json tagName --jq .tagName 2>/dev/null || echo "") + NEXT_VERSION="v$(hatch version)" + echo "## $NEXT_VERSION ($(date -I))" > changelog.md + + # Build arguments for the API call + ARGS=("--method" "POST" "-H" "Accept: application/vnd.github.v3+json" "/repos/SFDO-Tooling/CumulusCI/releases/generate-notes" "-f" "target_commitish=main" "-f" "tag_name=$NEXT_VERSION") + if [ -n "$PREVIOUS_VERSION" ]; then + echo "Generating notes between $PREVIOUS_VERSION and $NEXT_VERSION" + ARGS+=("-f" "previous_tag_name=$PREVIOUS_VERSION") + else + echo "No previous release found. Generating notes for the first release." + fi + + gh api "${ARGS[@]}" --jq '.body' | + sed -e 's_\(https.*\/\)\([0-9]*\)$_[#\2](\1\2)_' \ + -e 's_by @\(.*\) in_by [@\1](https://github.com/\1) in_' >> changelog.md + python utility/update-history.py + - name: Lint history + if: steps.version.outputs.level == 'major' || steps.version.outputs.level == 'minor' || steps.version.outputs.level == 'patch' || steps.version.outputs.level == 'beta' + run: | + npm install prettier + npx prettier --write docs/history.md + - name: Commit version and changelog + run: | + BRANCH_NAME="release-$(hatch version)" + git config user.name github-actions[bot] + git config user.email 41898282+github-actions[bot]@users.noreply.github.com + git switch -c "$BRANCH_NAME" + git add docs/history.md cumulusci/__about__.py + git commit -m "Update changelog (automated)" + # Delete the remote branch if it exists, ignoring errors if it doesn't. + git push origin --delete "$BRANCH_NAME" || true + # Push the new branch. + git push origin "$BRANCH_NAME" + - name: Create and Merge Release PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + PR_URL=$(gh pr create --title "Release v$(hatch version)" --fill --label 'auto-pr') + if [ -n "$PR_URL" ]; then + echo "Created PR: $PR_URL" + gh pr merge "$PR_URL" --merge --delete-branch + echo "PR merged and branch deleted." + else + echo "PR creation failed." + exit 1 + fi + - name: Call Release Workflow + uses: benc-uk/workflow-dispatch@v1 + with: + workflow: "Publish and release CumulusCI" + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79591df..0884624 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,31 +1,59 @@ -name: Publish and release cumulusci-plus-azure-devops +name: Publish and release CumulusCI on: + workflow_dispatch: push: branches: - main paths: - - cumulusci_ado/__about__.py + - cumulusci/__about__.py concurrency: publishing jobs: publish-to-pypi: name: Publish new release to PyPI + permissions: + contents: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - name: Set up Python + - uses: actions/checkout@main + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: "3.12" + python-version: 3.12 + cache: pip - name: Install build tools - run: pip install hatch - - name: Build package + run: python -m pip install hatch tomli tomli-w + - name: Check version type + id: version_check + run: | + VERSION=$(hatch version) + echo "Current version: $VERSION" + IS_PUBLISHABLE="false" + # Publish only stable and preview releases, which do not contain letters like a,b,d or "rc". + if ! [[ "$VERSION" =~ [abd] ]] && ! [[ "$VERSION" =~ "rc" ]]; then + IS_PUBLISHABLE="true" + fi + echo "publishable=${IS_PUBLISHABLE}" >> $GITHUB_OUTPUT + - name: Build source tarball and binary wheel + if: steps.version_check.outputs.publishable == 'true' run: hatch build -c - - name: Publish to PyPI + - name: Upload to PyPI + if: steps.version_check.outputs.publishable == 'true' + run: hatch publish env: HATCH_INDEX_USER: "__token__" HATCH_INDEX_AUTH: ${{ secrets.PYPI_TOKEN }} - # HATCH_INDEX_REPO: ${{ vars.PYPI_REPO }} # Uncomment if you have a specific repository - run: hatch publish + - name: Create release + if: steps.version_check.outputs.publishable == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="$(hatch version)" + awk '//,//' docs/history.md > changelog.md + gh release create "v$VERSION" \ + dist/*.whl \ + dist/*.tar.gz \ + --notes-file changelog.md \ + --title $VERSION From 4ace7fb6297c4432623673ca57f0e848fa743fea Mon Sep 17 00:00:00 2001 From: Rupesh J Date: Fri, 18 Jul 2025 22:24:05 +0530 Subject: [PATCH 2/2] Add script files to generate release notes. --- docs/history.md | 0 utility/generate-release-notes.py | 24 ++++++++++++++++++++++++ utility/update-history.py | 15 +++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 docs/history.md create mode 100644 utility/generate-release-notes.py create mode 100644 utility/update-history.py diff --git a/docs/history.md b/docs/history.md new file mode 100644 index 0000000..e69de29 diff --git a/utility/generate-release-notes.py b/utility/generate-release-notes.py new file mode 100644 index 0000000..8132d3f --- /dev/null +++ b/utility/generate-release-notes.py @@ -0,0 +1,24 @@ +import os + +import click + + +@click.command() +@click.option("--prev", required=True, help="Previous version tag") +@click.option("--next", required=True, help="Next version tag") +def main(prev, next): + os.system("git fetch") + cmd = ( + 'gh api --method POST -H "Accept: application/vnd.github.v3+json" ' + + "/repos/SFDO-Tooling/CumulusCI/releases/generate-notes" + + f" -f tag_name='{next}' " + + "-f target_commitish='main' " + + f"-f previous_tag_name='{prev}' --jq .body " + + "| pandoc -f gfm -t rst" + ) + print(cmd) + os.system(cmd) + + +if __name__ == "__main__": + main() diff --git a/utility/update-history.py b/utility/update-history.py new file mode 100644 index 0000000..028a279 --- /dev/null +++ b/utility/update-history.py @@ -0,0 +1,15 @@ +""" +Update the history file. +""" +from pathlib import Path + +START_MARKER = "" +STOP_MARKER = "" + +history = Path("docs/history.md").read_text() +latest = Path("changelog.md").read_text() +updated = history.replace(f"{STOP_MARKER}\n\n", "").replace( + f"{START_MARKER}\n\n", f"{START_MARKER}\n\n{latest}\n\n{STOP_MARKER}\n\n" +) + +Path("docs/history.md").write_text(updated)