Skip to content

Publish: 1.0.0: main@db0d24d5fc0f4c34424149709cc491baf8804de1 #40

Publish: 1.0.0: main@db0d24d5fc0f4c34424149709cc491baf8804de1

Publish: 1.0.0: main@db0d24d5fc0f4c34424149709cc491baf8804de1 #40

Workflow file for this run

#
# Set version, tag & publish
#
name: Publish
run-name: "Publish: ${{ inputs.version }}: ${{ github.ref_name }}@${{ github.sha }}"
on:
workflow_dispatch:
inputs:
version:
description: novel library version and repository tag to apply (e.g. 1.0.2-post5)
required: true
force-version:
description: omit check for semantic versioning
type: boolean
required: false
force-pass:
description: omit check for passing tests
type: boolean
required: false
draft:
description: draft but do not publish release
type: boolean
required: false
prerelease:
description: mark as a prerelease
type: boolean
required: false
pypi-test:
description: publish to test.pypi.org
type: boolean
required: false
# these cannot yet be defined in one (unfortunately)
workflow_call:
inputs:
version:
required: true
type: string
force-version:
type: boolean
required: false
force-pass:
type: boolean
required: false
draft:
type: boolean
required: false
prerelease:
type: boolean
required: false
pypi-test:
type: boolean
required: false
env:
GIT_COMMITTER_NAME: github-actions[bot]
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GH_TOKEN: ${{ github.token }}
TEST_PYPI: https://test.pypi.org/legacy/
jobs:
check:
runs-on: ubuntu-latest
outputs:
git-tags: ${{ steps.tags.outputs.git-tags }}
steps:
- name: Check out repository
uses: actions/checkout@v3
with:
#
# Fetch ALL history s.t. tags may be sorted by date (creatordate)
# (regardless of whether the tag or ls-remote --tags command is used)
#
# Note: This might be slow! ls-remote allows us to avoid this, *except*
# that we want to sort by object creation date, which appears to require
# a complete local clone.
#
fetch-depth: 0
- name: Retrieve tags
id: tags
#
# checkout does not by default load all changesets and tags
#
# as such, this can come up empty:
#
# git tag --list
#
# instead, (and rather than check out repo history), we can query the remote:
#
# git ls-remote -q --tags --refs --sort=-creatordate | awk -F / '{print $3}'
#
# *However* the "creatordate" sort above fails without a deep clone;
# so, we'll rely on a deep clone, regardless.
#
run: |
EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "git-tags<<$EOF" >> "$GITHUB_OUTPUT"
git tag --list --sort=-creatordate >> "$GITHUB_OUTPUT"
echo "$EOF" >> "$GITHUB_OUTPUT"
- name: Check that tag is novel
env:
TAGS: ${{ steps.tags.outputs.git-tags }}
run: |
echo "$TAGS" |
grep -E "^${{ inputs.version }}$" > /dev/null && {
echo "::error::Tag ${{ inputs.version }} already exists"
exit 1
}
echo "✓ Tag ${{ inputs.version }} is novel"
- name: Check that version is semantic
if: ${{ ! inputs.force-version }}
env:
SEMVAR_PATTERN: ^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$
shell: python
run: |
import os
import re
match = re.fullmatch(os.getenv('SEMVAR_PATTERN'), '${{ inputs.version }}')
if not match:
print("::error::Version ${{ inputs.version }} is non-semantic")
raise SystemExit(1)
items = ('='.join(item) for item in match.groupdict().items() if all(item))
print("✓ Version ${{ inputs.version }} is semantic:", *items)
- name: Check for passing tests
if: ${{ ! inputs.force-pass }}
run: |
successSha=$(
gh run list -w test -b ${{ github.ref_name }} -L 1 --json headSha,status -q '
.[]
| select(.status == "completed")
| .headSha
'
)
if [ "$successSha" != ${{ github.sha }} ]
then
echo "::error::No successful test job for ${{ github.sha }}"
exit 1
else
echo "✓ Test job succeeded for $successSha"
exit 0
fi
publish:
runs-on: ubuntu-latest
needs: [check]
permissions:
contents: write
id-token: write
steps:
- name: Configure publishing changeset author
env:
SENDER: ${{ github.event.sender.login }}
run: |
USER="$(
gh api users/"$SENDER"
)"
NAME="$(echo "$USER" | jq -r .name)"
if [ -n "$NAME" ]
then
echo "GIT_AUTHOR_NAME=$NAME" >> $GITHUB_ENV
else
echo "::error::Author name empty for sender $SENDER"
exit 1
fi
EMAIL="$(echo "$USER" | jq -r .email)"
if [ -n "$EMAIL" ]
then
echo "GIT_AUTHOR_EMAIL=$EMAIL" >> $GITHUB_ENV
else
echo "::error::Author email empty for sender $SENDER"
exit 1
fi
- name: Check out repository
uses: actions/checkout@v3
- name: Install management dependencies
run: pip install poetry
- name: Set library version
run: poetry version "${{ inputs.version }}"
- name: Build
run: poetry build
- name: Publish to PyPI
if: ${{ ! inputs.pypi-test }}
uses: pypa/gh-action-pypi-publish@release/v1
- name: Publish to Test PyPI
if: inputs.pypi-test
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: ${{ env.TEST_PYPI }}
- name: Annotate publishing
run: |
LIB_SIG="$(poetry version)"
LIB_NAME="$(echo "$LIB_SIG" | awk '{print $1}')"
LIB_VER="$(echo "$LIB_SIG" | awk '{print $2}')"
if [ "${{ inputs.pypi-test }}" = true ]
then
PYPI_DOMAIN=test.pypi.org
else
PYPI_DOMAIN=pypi.org
fi
echo "### Library published :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "Build uploaded to https://$PYPI_DOMAIN/project/$LIB_NAME/$LIB_VER/" >> $GITHUB_STEP_SUMMARY
- name: Commit and push
env:
TAGS: ${{ needs.check.outputs.git-tags }}
run: |
lastTag="$(echo "$TAGS" | head -n1)"
git commit --all --message="bump version $lastTag → ${{ inputs.version }}"
git push
# write summary information
echo "### Version bumped :arrow_heading_up:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo '```console' >> $GITHUB_STEP_SUMMARY
git show --format=full --no-patch >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
- name: Create tagged release
run: |
if [ "${{ github.event.inputs.draft }}" = true ]
then
DRAFT=--draft
else
DRAFT=""
fi
if [ "${{ github.event.inputs.prerelease }}" = true ]
then
PRERELEASE=--prerelease
else
PRERELEASE=""
fi
TARGET=$(git show --format=%H --no-patch)
URL="$(
gh release create "${{ inputs.version }}" --target $TARGET --generate-notes $DRAFT $PRERELEASE
)"
# write summary information
echo "### Release created :octocat:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "See $URL" >> $GITHUB_STEP_SUMMARY