From 02f7015186f748eab1a50dbcd59b712657eff42c Mon Sep 17 00:00:00 2001 From: w-bonelli Date: Fri, 16 Dec 2022 12:44:13 -0500 Subject: [PATCH] ci: update scripts and workflow for automatic release --- .github/workflows/ci.yml | 2 + .github/workflows/release.yml | 244 +++++++++++++++++-- .gitignore | 2 + CONTRIBUTING.md | 84 +++++++ MANIFEST.in | 1 + README.md | 12 +- cliff.toml | 46 ++++ modflow_devtools/__init__.py | 4 +- modflow_devtools/test/test_misc.py | 4 + scripts/{pull_request_prepare.py => lint.py} | 0 scripts/update_version.py | 183 ++++++++++++++ setup.cfg | 7 +- version.txt | 1 + 13 files changed, 568 insertions(+), 22 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 cliff.toml rename scripts/{pull_request_prepare.py => lint.py} (100%) create mode 100644 scripts/update_version.py create mode 100644 version.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81d4ad3..c48f4b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -155,6 +155,7 @@ jobs: key: modflow6-examples-${{ hashFiles('modflow6-examples/data/**') }} - name: Install extra Python packages + # can't build examples on Python 3.7, requires Python 3.8 if: steps.cache-examples.outputs.cache-hit != 'true' && matrix.python != '3.7' working-directory: modflow6-examples/etc run: | @@ -162,6 +163,7 @@ jobs: pip install -r requirements.usgs.txt - name: Build modflow6 example models + # can't build examples on Python 3.7, requires Python 3.8 if: steps.cache-examples.outputs.cache-hit != 'true' && matrix.python != '3.7' working-directory: modflow6-examples/etc run: python ci_build_files.py diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index de93269..8596805 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,50 +1,264 @@ name: Release on: push: - tags: + branches: + - master - v* -jobs: release: - name: Release & publish + types: + - published +jobs: + prep: + name: Prepare release runs-on: ubuntu-latest + if: ${{ github.event_name == 'push' && github.ref_name != 'master' }} + permissions: + contents: write + pull-requests: write + defaults: + run: + shell: bash steps: - - name: Checkout repo + - name: Checkout release branch uses: actions/checkout@v3 + with: + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v4 with: python-version: 3.7 + cache: 'pip' + cache-dependency-path: setup.cfg - - name: Install Python packages + - name: Install Python dependencies run: | pip install --upgrade pip pip install build twine pip install . + pip install ".[lint, test, optional]" - - name: Print version + - name: Update version + id: version run: | - python -c "import modflow_devtools; print(modflow_devtools.__version__)" + ref="${{ github.ref_name }}" + version="${ref#"v"}" + python scripts/update_version.py -v "$version" + python scripts/lint.py + python -c "import modflow_devtools; print('Version: ', modflow_devtools.__version__)" + echo "version=$version" >> $GITHUB_OUTPUT - - name: Build package + - name: Touch changelog + run: touch HISTORY.md + + - name: Generate changelog + id: cliff + uses: orhun/git-cliff-action@v1 + with: + config: cliff.toml + args: --verbose --unreleased --tag ${{ steps.version.outputs.version }} + env: + OUTPUT: CHANGELOG.md + + - name: Update changelog + id: update-changelog run: | - python -m build - - - name: Check distribution + # move changelog + clog="CHANGELOG_${{ steps.version.outputs.version }}.md" + echo "changelog=$clog" >> $GITHUB_OUTPUT + sudo cp "${{ steps.cliff.outputs.changelog }}" "$clog" + + # show current release changelog + cat "$clog" + + # substitute full group names + sed -i 's/#### Ci/#### Continuous integration/' "$clog" + sed -i 's/#### Feat/#### New features/' "$clog" + sed -i 's/#### Fix/#### Bug fixes/' "$clog" + sed -i 's/#### Refactor/#### Refactoring/' "$clog" + sed -i 's/#### Test/#### Testing/' "$clog" + + cat "$clog" HISTORY.md > temp_history.md + sudo mv temp_history.md HISTORY.md + + # show full changelog + cat HISTORY.md + + - name: Upload changelog + uses: actions/upload-artifact@v3 + with: + name: changelog + path: ${{ steps.update-changelog.outputs.changelog }} + + - name: Push release branch + run: | + rm ${{ steps.update-changelog.outputs.changelog }} + git config core.sharedRepository true + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "ci(release): set version to ${{ steps.version.outputs.version }}, update changelog" + git push origin "${{ github.ref_name }}" + + - name: Draft pull request + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + ver="${{ steps.version.outputs.version }}" + title="Release $ver" + changelog=$(cat ${{ steps.cliff.outputs.changelog }} | grep -v "### Version \[$ver\]") + body=' + # Release '$ver' + + The release can be approved by merging this pull request into `master`. This will trigger jobs to publish the release to PyPI and reset `develop` from `master`, incrementing the patch version number. + + ## Changelog + + '$changelog' + ' + gh pr create -B "main" -H "${{ github.ref_name }}" --title "$title" --draft --body "$body" + + release: + name: Draft release + # runs only when changes are merged to master + if: ${{ github.event_name == 'push' && github.ref_name == 'master' }} + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + + - name: Checkout repo + uses: actions/checkout@v3 + with: + ref: master + + # actions/download-artifact won't look at previous workflow runs but we need to in order to get changelog + - name: Download artifacts + uses: dawidd6/action-download-artifact@v2 + + - name: Draft release + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + version=$(cat version.txt) + title="MODFLOW developer tools $version" + notes=$(cat changelog/CHANGELOG.md) + gh release create "$version" \ + --target master \ + --title "$title" \ + --notes "$notes" \ + --draft \ + --latest + + publish: + name: Publish package + # runs only after release is published (manually promoted from draft) + if: ${{ github.event_name == 'release' }} + runs-on: ubuntu-22.04 + permissions: + contents: write + pull-requests: write + steps: + + - name: Checkout master branch + uses: actions/checkout@v3 + with: + ref: master + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.7 + + - name: Install Python dependencies run: | - twine check --strict dist/* + pip install --upgrade pip + pip install build twine + pip install . + + - name: Build package + run: python -m build + + - name: Check package + run: twine check --strict dist/* - name: Publish package if: ${{ env.TWINE_USERNAME != '' }} env: TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} - run: | - twine upload dist/* + run: twine upload dist/* - name: Create release uses: marvinpinto/action-automatic-releases@latest with: prerelease: true - repo_token: ${{ github.token }} \ No newline at end of file + repo_token: ${{ github.token }} + + reset: + name: Draft reset PR + if: ${{ github.event_name == 'release' }} + runs-on: ubuntu-22.04 + permissions: + contents: write + pull-requests: write + steps: + + - name: Checkout master branch + uses: actions/checkout@v3 + with: + ref: master + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: 3.7 + cache: 'pip' + cache-dependency-path: setup.cfg + + - name: Install Python dependencies + run: | + pip install --upgrade pip + pip install . + pip install ".[lint, test, optional]" + + - name: Get release tag + uses: oprypin/find-latest-tag@v1 + id: latest_tag + with: + repository: ${{ github.repository }} + releases-only: true + + - name: Draft pull request + env: + GITHUB_TOKEN: ${{ github.token }} + run: | + # create reset branch from master + reset_branch="post-release-${{ steps.latest_tag.outputs.tag }}-reset" + git switch -c $reset_branch + + # increment patch version + major_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f1) + minor_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f2) + patch_version=$(echo "${{ steps.latest_tag.outputs.tag }}" | cut -d. -f3) + version="$major_version.$minor_version.$((patch_version + 1))" + python scripts/update_version.py -v "$version" + python scripts/lint.py + + # commit and push reset branch + git config core.sharedRepository true + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add -A + git commit -m "ci(release): update to development version $version" + git push -u origin $reset_branch + + # create PR into develop + body=' + # Reinitialize for development + + Updates the `develop` branch from `master` following a successful release. Increments the patch version number. + ' + gh pr create -B "develop" -H "$reset_branch" --title "Reinitialize develop branch" --draft --body "$body" \ No newline at end of file diff --git a/.gitignore b/.gitignore index a2f68f8..4ec3575 100644 --- a/.gitignore +++ b/.gitignore @@ -135,3 +135,5 @@ dmypy.json modflow_devtools/bin/ modflow_devtools/utilities/temp/ +# git-cliff-action likes to add app/ folder to the project root +app \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..88ce25e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,84 @@ +# Contributing + +Contributions to this repository are welcome. To make a contribution we ask that you follow a few guidelines. + + + + +- [Issues and features](#issues-and-features) +- [Pull requests](#pull-requests) +- [Commit messages](#commit-messages) + - [Commit Message Format](#commit-message-format) + - [Type](#type) + - [Subject](#subject) + - [Body](#body) + - [Footer](#footer) + + + +## Issues and features + +Before filing a bug report or making a feature request, please check the issues to make sure yours isn't a duplicate. + +## Pull requests + +Feel free to submit pull requests to the `develop` branch with small fixes or improvements. Before implementing new features or contributing broadly-scoped changes we ask that you first open an issue or discussion. + +Before submitting a PR, please test your changes in your own fork. Please do not open a pull request immediately and add to it frequently during development — this will saturate the `modflowpy` organization's CI. + +If `develop` changes while your work is still in progress, please rebase and fix any conflicts, then force push your branch to update the pull request. + +## Commit messages + +Commit messages must conform to the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. This makes the commit history easier to follow and allows an automatically generated changelog. + +### Commit Message Format + +Each commit message consists of a **header**, a **body** and a **footer**. The **header** is mandatory, while **body** and **footer** are optional. + +``` +(): + + + +