Add a release workflow for PyPI in CI (Github Actions) (#1095)

* Fixes for new CI pipeline (Github Actions)

- Support ci skip tag on commits to avoid build dupes
- Add smarter path detection so we don't spam tons of tests
- Allow steps to cancel if previous steps were cancelled (don't always
  run)
- Separate workflows to be more modular
- Update release workflow to do a few more things
- Add helpful messages
- Be more strict in version checking (now with added regex)
- Make changelog smarter about when to trigger
- Add some functionality for changelog to handle releases and
  prereleases separately
- Better error handling
- Split up the version validation and the release to make way for more
  releases

* add step in workflow to merge to release branch after a release

* add check for git diff so commit doesn't error out constantly
This commit is contained in:
Jonathan Wren 2020-11-28 11:50:13 -08:00 committed by GitHub
parent e9d909a3ba
commit 4cad215560
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 260 additions and 72 deletions

View file

@ -2,11 +2,20 @@ name: Changelog
on:
push:
branches: [ develop ]
branches:
- develop
create:
branches:
- develop
jobs:
generate:
if: contains(toJson(github.event.commits), 'Update changelog') == false
if: >
! contains(github.event.head_commit.message, '[ci skip]') &&
! (
startsWith(github.event.head_commit.message, 'Increment version to v') &&
startsWith(github.ref, 'refs/heads/')
)
runs-on: ubuntu-latest
steps:
@ -14,31 +23,70 @@ jobs:
with:
token: ${{ secrets.JRNL_BOT_TOKEN }}
- name: Check branch for new commits
- name: Check branch for new commits, and env vars
run: |
git fetch origin
git fetch --tags origin
echo "::group::git fetch origin --tags --force"
git fetch origin --tags --force
echo "::endgroup::"
TAG_REGEX='include-all'
echo "::debug::GITHUB_REF: $GITHUB_REF"
BRANCH="${GITHUB_REF##*/}"
if [[ $(git rev-parse "origin/$BRANCH") != $GITHUB_SHA ]]; then
echo "BRANCH: $BRANCH"
echo "GITHUB_SHA: $GITHUB_SHA"
echo "$BRANCH has been updated since build started. Aborting changelog."
if [[ $GITHUB_REF =~ ^refs/tags/ ]]; then
# This is a tag build (i.e. a release)
echo '::debug::Release build'
if [[ ! $BRANCH =~ ^v[0-9]+(\.[0-9]+){1,2}(-(alpha|beta)(\.[0-9]+)?)?$ ]]; then
echo "::error::Invalid tag format: ${BRANCH}"
exit 1
fi
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
- name: Prep environment variables
run: |
echo "BRANCH=${GITHUB_REF##*/}" >> $GITHUB_ENV
RELEASE=1
BRANCH=develop
git checkout $BRANCH
# if actual release (not pre), then changelog should exclude prereleases on update
prerelease_regex='(alpha|beta)'
if [[ ! ${GITHUB_REF##*/} =~ $prerelease_regex ]]; then
echo '::debug::Actual release (not a prerelease)'
TAG_REGEX="$prerelease_regex"
echo "FULL_RELEASE=true" >> $GITHUB_ENV
fi
fi
echo "::debug::TAG_REGEX: $TAG_REGEX"
if [[ "$(git rev-parse "origin/$BRANCH")" != $GITHUB_SHA ]]; then
# Normal build on a branch (no tag)
echo "::debug::BRANCH: $BRANCH"
echo "::debug::GITHUB_SHA: $GITHUB_SHA"
echo "::error::$BRANCH has been updated since build started. Aborting changelog."
exit 1
fi
SINCE_TAG=$(git tag --sort=-creatordate | grep -Ev "$TAG_REGEX" | awk "NR==$(( 1 + ${RELEASE:-0} ))")
echo "::debug::BRANCH: $BRANCH"
echo "::debug::TAG_REGEX: $TAG_REGEX"
echo "::debug::FILENAME: CHANGELOG.md"
echo "::debug::SINCE_TAG: $SINCE_TAG"
echo "BRANCH=$BRANCH" >> $GITHUB_ENV
echo "TAG_REGEX=$TAG_REGEX" >> $GITHUB_ENV
echo "FILENAME=CHANGELOG.md" >> $GITHUB_ENV
echo "gittag=$(git tag --sort=-creatordate | grep -Ev '(alpha|beta|rc)' | awk 'NR==1')" >> $GITHUB_ENV
echo "SINCE_TAG=$SINCE_TAG" >> $GITHUB_ENV
- name: Prep changelog file (clear out old lines)
run: |
# delete the top of the changelog up to the correct tag
tagline=$(grep -n "^## \[\?$gittag\]\?" "$FILENAME" | awk '{print $1}' FS=':' | head -1)
tagline=$(grep -n "^## \[\?${SINCE_TAG}\]\?" "$FILENAME" | awk '{print $1}' FS=':' | head -1)
echo "tagline: ${tagline}"
[[ ! -z $tagline ]] && sed -i "1,$(expr $tagline - 1)d" "$FILENAME"
if [[ -z $tagline ]]; then
echo "::error::Something is wrong. ${SINCE_TAG} isn't in the changelog."
exit 1
fi
sed -i "1,$(expr $tagline - 1)d" "$FILENAME"
# delete generated line (or it will be added multiple times)
sed -i '/This Changelog was automatically generated by/d' "$FILENAME"
# delete trailing empty lines
@ -59,10 +107,11 @@ jobs:
compareLink: true
includeLabels: bug,enhancement,documentation,build,deprecated
excludeLabels: stale,wontfix
excludeTagsRegex: '(alpha|beta|rc)'
sinceTag: ${{ env.gittag }}
excludeTagsRegex: ${{ env.TAG_REGEX }}
sinceTag: ${{ env.SINCE_TAG }}
maxIssues: 150
releaseUrl: https://pypi.org/project/jrnl/%s/
releaseBranch: develop
verbose: false
- name: Small fixes
@ -70,19 +119,28 @@ jobs:
# Change unreleased link to correct url
sed -i 's!https://pypi.org/project/jrnl/HEAD/!https://github.com/jrnl-org/jrnl/!' "$FILENAME"
- name: Consistency check
- name: Diff and consistency check
run: |
git diff
if [[ $(grep -c '^# Changelog$' "$FILENAME") != 1 ]]; then
echo 'Something is wrong with the changelog.'
git diff -- "$FILENAME"
exit 1
echo '::error::Something is wrong with the changelog.'
fi
SOMETHING_CHANGED=false
git diff --exit-code || SOMETHING_CHANGED=true
- name: Commit
if: env.SOMETHING_CHANGED
run: |
git config user.email "jrnl.bot@gmail.com"
git config user.name "Jrnl Bot"
git add "$FILENAME"
git commit -m "Update changelog"
git commit -m "Update changelog [ci skip]"
git push origin $BRANCH
- name: Merge to Release branch
if: env.FULL_RELEASE
run: |
git checkout release
git merge --ff-only $BRANCH
git push origin release

82
.github/workflows/docs.yaml vendored Normal file
View file

@ -0,0 +1,82 @@
name: Docs
on:
push:
branches: [ develop, release ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'readthedocs.yml'
pull_request_target:
branches: [ develop ]
paths:
- 'docs/**'
- 'mkdocs.yml'
- 'readthedocs.yml'
jobs:
accessibility:
if: contains(toJson(github.event.commits), '[ci skip]') == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Setup Node.js environment
uses: actions/setup-node@main
- name: poetry cache
uses: actions/cache@v2
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install poetry
poetry config --local virtualenvs.in-project true
poetry install --no-root --remove-untracked
npm install pa11y pa11y-reporter-junit
- name: Start docs server
run: poetry run mkdocs serve &
- name: Accessibility testing (Pa11y)
run: poetry run .github/workflows/pa11y.sh
- name: Upload Unit Test Results
if: always()
uses: actions/upload-artifact@v2
with:
name: Unit Test Results (pa11y)
path: reports/pa11y/*.xml
publish-test-results:
if: success() || failure()
name: "Publish Unit Tests Results"
needs: accessibility
runs-on: ubuntu-latest
steps:
- name: Download Artifacts
uses: actions/download-artifact@v2
with:
path: artifacts
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1.4
if: always()
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
check_name: Unit Test Results
hide_comments: all but latest
comment_on_pr: true
files: '**/*.xml'
report_individual_runs: true
deduplicate_classes_by_file_name: false

76
.github/workflows/release.yaml vendored Normal file
View file

@ -0,0 +1,76 @@
name: Release
on:
workflow_dispatch:
inputs:
version:
description: 'Version (e.g. v2.5 or v2.5.1-beta)'
required: true
jobs:
validate:
name: "Validate version string"
runs-on: ubuntu-latest
steps:
- name: Validate version
run: |
JRNL_VERSION="${{ github.event.inputs.version }}"
if [[ ! $JRNL_VERSION =~ ^v[0-9]+(\.[0-9]+){1,2}(-(alpha|beta)(\.[0-9]+)?)?$ ]]; then
echo
echo "::error::Bad version"
echo
echo "Version string should match pattern above."
echo "Here are some examples of valid version numbers:"
echo
echo " v2.5"
echo " v2.5-alpha"
echo " v2.5-beta"
echo " v2.5.1"
echo " v2.5.1-alpha"
echo " v2.5.1-beta"
exit 1
fi
echo "::debug::version: $JRNL_VERSION"
echo "JRNL_VERSION=$JRNL_VERSION" >> $GITHUB_ENV
release_pypi:
needs: validate
name: "Release to PyPI"
runs-on: ubuntu-latest
steps:
- name: Get version
run: |
JRNL_VERSION="${{ github.event.inputs.version }}"
echo "::debug::version: $JRNL_VERSION"
echo "JRNL_VERSION=$JRNL_VERSION" >> $GITHUB_ENV
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- uses: actions/checkout@v2
with:
token: ${{ secrets.JRNL_BOT_TOKEN }}
- name: Install dependencies
run: pip install poetry
- name: Update version in files
run: |
poetry version "$JRNL_VERSION"
echo __version__ = \"$JRNL_VERSION\" > jrnl/__version__.py
- name: Commit updated files
run: |
git config user.email "jrnl.bot@gmail.com"
git config user.name "Jrnl Bot"
git add pyproject.toml jrnl/__version__.py
git commit -m "Increment version to ${JRNL_VERSION}"
git tag -a -m "$JRNL_VERSION" "$JRNL_VERSION"
git push origin develop --tags
- name: Deploy to PyPI
run: |
poetry config http-basic.pypi "__token__" "${POETRY_PYPI_TOKEN_PYPI}"
poetry publish --build

View file

@ -3,11 +3,25 @@ name: Testing
on:
push:
branches: [ develop, release ]
paths:
- 'jrnl/**'
- 'features/**'
- 'tests/**'
- 'poetry.lock'
- 'pyproject.toml'
pull_request_target:
branches: [ develop ]
paths:
- 'jrnl/**'
- 'features/**'
- 'tests/**'
- 'poetry.lock'
- 'pyproject.toml'
jobs:
test:
if: >
! contains(github.event.head_commit.message, '[ci skip]')
runs-on: ${{ matrix.os }}
strategy:
matrix:
@ -35,79 +49,37 @@ jobs:
poetry install --remove-untracked
- name: Code formatting (Black)
if: always()
if: success() || failure()
run: |
poetry run black --version
poetry run black --check --diff .
- name: Code Style (PyFlakes)
if: always()
if: success() || failure()
run: |
poetry run pyflakes --version
poetry run pyflakes jrnl features tests
- name: Test with pytest
if: always()
if: success() || failure()
run: poetry run pytest --junitxml=reports/pytest/results.xml
- name: Test with behave
if: always()
if: success() || failure()
run: poetry run behave --no-skipped --format progress2 --junit --junit-directory reports/behave
- name: Upload Unit Test Results
if: always()
if: success() || failure()
uses: actions/upload-artifact@v2
with:
name: Unit Test Results
path: reports/**/*.xml
accessibility:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Setup Node.js environment
uses: actions/setup-node@main
- name: poetry cache
uses: actions/cache@v2
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('poetry.lock') }}-${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install poetry
poetry config --local virtualenvs.in-project true
poetry install --no-root --remove-untracked
npm install pa11y pa11y-reporter-junit
- name: Start docs server
run: poetry run mkdocs serve &
- name: Accessibility testing (Pa11y)
run: poetry run .github/workflows/pa11y.sh
# leaving this out for now (there are too many warnings)
# - name: Upload Unit Test Results
# if: always()
# uses: actions/upload-artifact@v2
# with:
# name: Unit Test Results (pa11y)
# path: reports/pa11y/*.xml
publish-test-results:
if: success() || failure()
name: "Publish Unit Tests Results"
needs: [test, accessibility]
needs: test
runs-on: ubuntu-latest
if: always()
steps:
- name: Download Artifacts