Skip to content

Commit

Permalink
feat: doc-deploy-changelog action (#452)
Browse files Browse the repository at this point in the history
Co-authored-by: Roberto Pastor Muela <37798125+RobPasMue@users.noreply.github.com>
  • Loading branch information
klmcadams and RobPasMue committed Apr 11, 2024
1 parent b191209 commit ed653a4
Show file tree
Hide file tree
Showing 6 changed files with 380 additions and 1 deletion.
9 changes: 8 additions & 1 deletion doc-changelog/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ inputs:
required: false
type: string

towncrier-version:
description: >
Towncrier version used for creating fragment files.
default: '23.11.0'
required: false
type: string

use-python-cache:
description: >
Whether to use the Python cache for installing previously downloaded
Expand Down Expand Up @@ -82,7 +89,7 @@ runs:
- name: "Install towncrier"
shell: bash
run: |
python -m pip install --upgrade pip towncrier
python -m pip install --upgrade pip towncrier==${{ inputs.towncrier-version }}
- name: "Get labels in the pull request"
env:
Expand Down
301 changes: 301 additions & 0 deletions doc-deploy-changelog/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
# Copyright (C) 2022 - 2024 ANSYS, Inc. and/or its affiliates.
# SPDX-License-Identifier: MIT
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

name: >
Build CHANGELOG from towncrier fragment files
description: |
Builds the CHANGELOG using ``towncrier`` fragment files for the tagged version.
Creates a pull request into main with the CHANGELOG updates and deleted
towncrier fragment files.
This action will only work on Linux/macOS runners.
inputs:
# Required inputs

token:
description: >
Use the PYANSYS_CI_BOT_TOKEN to do a git commit & push.
The "contents: write" and "pull-requests: write" permissions
are required for this action.
required: true
type: string

# Optional inputs

python-version:
description: >
Python version used for setting up Python.
default: '3.10'
required: false
type: string

towncrier-version:
description: >
Towncrier version used for updating the CHANGELOG file.
default: '23.11.0'
required: false
type: string

toml-version:
description: >
Toml version used for retrieving the towncrier directory.
default: '0.10.2'
required: false
type: string

use-python-cache:
description: >
Whether to use the Python cache for installing previously downloaded
libraries. If ``true``, previously downloaded libraries are installed from the
Python cache. If ``false``, libraries are downloaded from the PyPI index.
required: false
default: true
type: boolean

update-release-branch:
description: >
Whether or not to update the CHANGELOG in the release branch.
If ``true``, the branch used to update the CHANGELOG is "release/major.minor".
If ``false``, the branch used to update the CHANGELOG is
"maint/changelog-update-tag_version" and will be deleted after use.
required: false
default: true
type: boolean

runs:
using: "composite"
steps:
- name: "Install Git and clone project"
uses: actions/checkout@v4
with:
token: ${{ inputs.token }}
fetch-depth: 0

- name: "Set up Python ${{ inputs.python-version }}"
env:
PYTHON_VERSION: ${{ inputs.python-version }}
PYTHON_CACHE: ${{ inputs.use-python-cache }}
uses: ansys/actions/_setup-python@main
with:
python-version: ${{ env.PYTHON_VERSION }}
use-cache: ${{ env.PYTHON_CACHE }}

- name: Save tag version
shell: bash
run: |
# /refs/tags/v0.1.0 -> 0.1.0
echo "TAG_VERSION=${GITHUB_REF##*/v}" >> $GITHUB_ENV
- name: "Install towncrier, toml, and the project"
shell: bash
run: |
python -m pip install --upgrade pip towncrier==${{ inputs.towncrier-version }} toml==${{ inputs.toml-version }}
python -m pip install -e .
- name: "Get towncrier directory"
shell: python
run: |
import os
import toml
# Load pyproject.toml
with open('pyproject.toml', 'r') as f:
config = toml.load(f)
directory=config["tool"]["towncrier"]["directory"]
# Get the GITHUB_ENV variable
github_env = os.getenv('GITHUB_ENV')
# Append the TOWNCRIER_DIR with its value to GITHUB_ENV
with open(github_env, "a") as f:
f.write(f"TOWNCRIER_DIR={directory}")
- name: "Set CHANGELOG update branch"
shell: bash
run: |
if [[ ${{ inputs.update-release-branch }} == "true" ]]; then
# Assume release branch is "release/major.minor"
# v0.1.0 (tag) -> release/0.1
tag_version=${{ env.TAG_VERSION }}
release_branch="release/${tag_version%.*}"
# Check release_branch exists on remote
release_branch_exists=$(git ls-remote --heads origin refs/heads/$release_branch 2>&1)
# If the release branch doesn't exist, then use the temporary branch
if [ -z "$release_branch_exists" ]; then
# maint/changelog-update-v0.1.0
echo "UPDATE_BRANCH=maint/changelog-update-v${{ env.TAG_VERSION }}" >> $GITHUB_ENV
echo "RELEASE_BRANCH_EXISTS=false" >> $GITHUB_ENV
else
echo "UPDATE_BRANCH=$release_branch" >> $GITHUB_ENV
echo "RELEASE_BRANCH_EXISTS=true" >> $GITHUB_ENV
fi
else
# maint/changelog-update-v0.1.0
echo "UPDATE_BRANCH=maint/changelog-update-v${{ env.TAG_VERSION }}" >> $GITHUB_ENV
echo "RELEASE_BRANCH_EXISTS=false" >> $GITHUB_ENV
fi
- name: "Create new section in CHANGELOG and reupload tag"
shell: bash
run: |
# Get number of .md files in ${{ env.TOWNCRIER_DIR }}
count=$(find ${{ env.TOWNCRIER_DIR }} -type f -name "*.md" | wc -l)
if [ $count -gt 0 ]; then
# Checkout branch from tag
if [[ ${{ inputs.update-release-branch }} == "true" ]] && [[ ${{ env.RELEASE_BRANCH_EXISTS }} == "true" ]]; then
echo "Using release branch"
git checkout ${{ env.UPDATE_BRANCH }}
git pull
else
echo "Using temporary branch"
git checkout -b ${{ env.UPDATE_BRANCH }}
fi
# Update the changelog with the package version from pyproject.toml
# The [tool.towncrier] section in pyproject.toml requires the following line
# for the command to work:
# package = "ansys.<product>.<library>"
buildcmd=$(towncrier build --yes >> builderr.txt 2>&1 || true)
cat builderr.txt
greperr=$(grep "Error: '--version'" builderr.txt || true)
# Remove builderr.txt
rm builderr.txt
# If greperr contains the error message, run with the tag specified
if [ -n "$greperr" ]; then
# Update changelog with the tag version
towncrier build --yes --version ${{ env.TAG_VERSION }}
fi
# Configure git username & email
git config user.name 'pyansys-ci-bot'
git config user.email 'pyansys.github.bot@ansys.com'
# Add CHANGELOG.md file and deleted ${{ env.TOWNCRIER_DIR }}/*.md files
git add .
# Commit changes and save commit SHA
git commit -m "Updating CHANGELOG for v${{ env.TAG_VERSION }}"
echo "COMMIT_SHA=$(git rev-parse HEAD)" >> $GITHUB_ENV
# Push CHANGELOG.md changes
if [[ ${{ inputs.update-release-branch }} == "true" ]] && [[ ${{ env.RELEASE_BRANCH_EXISTS }} == "true" ]]; then
git push
else
git push -u origin ${{ env.UPDATE_BRANCH }}
fi
echo "UPDATED_CHANGELOG=True" >> $GITHUB_ENV
else
echo "There are no changelog fragment files to update the CHANGELOG for v${{ env.TAG_VERSION }}"
echo "UPDATED_CHANGELOG=False" >> $GITHUB_ENV
fi
- name: "Create PR into main with CHANGELOG changes"
if: contains(env.UPDATED_CHANGELOG, 'True')
env:
GH_TOKEN: ${{ inputs.token }}
shell: bash
run: |
# Get main branch name
head_branch=$(git remote show origin | grep "HEAD branch:")
formatted_head_branch=${head_branch#*: }
# Checkout main & create a branch
git checkout $formatted_head_branch
git pull
# Set branch for PR
pr_branch=maint/v${{ env.TAG_VERSION }}-changelog
# Check if pr_branch exists on remote
remote_branch_exists=$(git ls-remote --heads origin refs/heads/$pr_branch 2>&1)
# Delete the remote branch if it exists
if [ -n "$remote_branch_exists" ]; then
echo "Deleting $pr_branch from remote"
git push origin --delete $pr_branch
fi
# Create a new $pr_branch
git checkout -b $pr_branch
cherrypick=$(git cherry-pick ${{ env.COMMIT_SHA }} >> cherrypickerr.txt 2>&1 || true)
cat cherrypickerr.txt
conflict=$(grep "CONFLICT" cherrypickerr.txt || true)
# Remove cherrypickerr.txt
rm cherrypickerr.txt
if [ -n "$conflict" ]; then
# Show which files are "theirs" from the cherry-pick
git status
# Apply all of the changes from the cherry-pick
git checkout --theirs .
git add .
# Continue the cherry-pick without a commit message
git -c core.editor=true cherry-pick --continue
fi
# Push CHANGELOG.md changes to $pr_branch
git push -u origin $pr_branch
gh pr create --title "chore: update CHANGELOG for v${{ env.TAG_VERSION }}" --body "Update CHANGELOG for v${{ env.TAG_VERSION }} and remove .md files in ${{ env.TOWNCRIER_DIR }}" --reviewer ${{ github.actor }}
- name: "Delete & Re-create tag"
env:
GH_TOKEN: ${{ inputs.token }}
if: contains(env.UPDATED_CHANGELOG, 'True')
shell: bash
run: |
# Checkout branch created from tag with CHANGELOG update & pull
git checkout ${{ env.UPDATE_BRANCH }}
git pull
# Delete the tag locally
git tag -d v${{ env.TAG_VERSION }}
# Delete the tag on remote
git push origin --delete v${{ env.TAG_VERSION }}
# Create the tag locally
git tag v${{ env.TAG_VERSION }}
# Create the tag on remote
git push origin v${{ env.TAG_VERSION }}
if [[ ${{ inputs.update-release-branch }} == "false" ]] || [[ ${{ env.RELEASE_BRANCH_EXISTS }} == "false" ]]; then
# Delete branch created from deleted tag with CHANGELOG update
git push origin --delete ${{ env.UPDATE_BRANCH }}
fi
# Exit on error if the CHANGELOG is updated to prevent the release action from running
exit 1
11 changes: 11 additions & 0 deletions doc/source/doc-actions/examples/doc-deploy-changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
update-changelog:
name: "Update CHANGELOG for new tag"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: ansys/actions/doc-deploy-changelog@{{ version }}
with:
token: ${{ '{{ secrets.PYANSYS_CI_BOT_TOKEN }}' }}
1 change: 1 addition & 0 deletions doc/source/migrations/docs-changelog-setup.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ or follow these steps:
.. code:: toml
[tool.towncrier]
package = "ansys.<product>.<library>"
directory = "doc/changelog.d"
filename = "CHANGELOG.md"
start_string = "<!-- towncrier release notes start -->\n"
Expand Down
54 changes: 54 additions & 0 deletions doc/source/migrations/docs-deploy-changelog-setup.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
.. _docs_deploy_changelog_action_setup:

Doc-deploy-changelog action setup
=================================

When a new tag is pushed, the ``doc-deploy-changelog`` action generates a new section of the ``CHANGELOG.md`` file
if fragment files exist in their designated directory, for example ``doc/changelog.d``. By default, the
``CHANGELOG.md`` file is updated in the release branch corresponding to the tag being pushed, such as ``release/0.1``,
and a pull request is created to merge the CHANGELOG update and deleted fragment files into main.

Use the following link to set up the ``ansys/actions/doc-changelog`` action before setting up the ``doc-deploy-changelog`` action: :ref:`docs_changelog_action_setup`
Once the ``doc-changelog`` action is done being set up, continue with the ``doc-deploy-changelog`` action setup:

1. Add the ``doc-deploy-changelog`` as the first job of the ``ci_cd.yml`` file, and make the ``update-changelog`` job a requirement of the ``release`` job:

.. code:: yaml
update-changelog:
name: "Update CHANGELOG for new tag"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: ansys/actions/doc-deploy-changelog@{{ version }}
with:
token: ${{ '{{ secrets.PYANSYS_CI_BOT_TOKEN }}' }}
release:
name: Release project
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
needs: [update-changelog]
runs-on: ubuntu-latest
steps:
- name: Release to the public PyPI repository
uses: ansys/actions/release-pypi-public@{{ version }}
with:
library-name: ${{ '{{ env.PACKAGE_NAME }}' }}
twine-username: "__token__"
twine-token: ${{ '{{ secrets.PYPI_TOKEN }}' }}
- name: Release to GitHub
uses: ansys/actions/release-github@{{ version }}
with:
library-name: ${{ '{{ env.PACKAGE_NAME }}' }}
2. Optional - Add the ``package`` line to the ``tool.towncrier`` section of the ``pyproject.toml``. This is the same as the name under ``tool.flit.module``:

.. code:: toml
[tool.towncrier]
# Uses the version from the pyproject.toml instead of the tag being pushed
package = "ansys.<product>.<library>"

0 comments on commit ed653a4

Please sign in to comment.