Automate release workflow

- Automatically release when tags are pushed to master
- Release automation will build wheel and sdist + upload to pypi
- Added test pypi as initial target
- Updated version update scripts to help with automatic version
  management

Signed-off-by: Dan Ryan <dan@danryan.co>
This commit is contained in:
Dan Ryan
2020-04-24 18:01:13 -04:00
parent 8c444fb4ce
commit 60818d7b31
3 changed files with 144 additions and 49 deletions
+63
View File
@@ -0,0 +1,63 @@
name: Create Release & Upload To PyPI
on:
push:
# Sequence of patterns matched against refs/tags
tags:
- v?[0-9]+.[0-9]+.[0-9]+ # add .* to allow dev releases
jobs:
build:
name: pipenv PyPI Upload
runs-on: ubuntu-latest
env:
CI: "1"
steps:
- name: Checkout code
uses: actions/checkout@v1
- uses: webfactory/ssh-agent@v0.1.1
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
draft: false
prerelease: false
- name: Set up Python 3.7
uses: actions/setup-python@v1
with:
python-version: 3.7
- name: Install latest tools for build
run: |
python -m pip install --upgrade --upgrade-strategy=eager pip setuptools wheel invoke
python -m pip install .
python -m pipenv install --dev
- name: Build wheels
run: |
python -m pipenv runpython setup.py sdist bdist_wheel
# to upload to test pypi, pass repository_url: https://test.pypi.org/legacy/ and use secrets.TEST_PYPI_TOKEN
- name: Publish a Python distribution to PyPI
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.TEST_PYPI_TOKEN }}
repository_url: https://test.pypi.org/legacy/
packages_dir: dist/
# git push https://${{ github.actor }}:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git HEAD:master
# we need to use a deploy key for this to get around branch protection as the default token fails
- name: Pre-bump
run: |
git config --local user.name 'Github Action'
git config --local user.email action@github.com
python -m pipenv run inv release.bump-version --dev --commit
git push git@github.com:${{ github.repository }}.git HEAD:master
Generated
+30 -32
View File
@@ -7,9 +7,9 @@
"requires": {},
"sources": [
{
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
"name": "pypi",
"url": "https://pypi.org/simple",
"verify_ssl": true
}
]
},
@@ -193,30 +193,28 @@
},
"cryptography": {
"hashes": [
"sha256:02079a6addc7b5140ba0825f542c0869ff4df9a69c360e339ecead5baefa843c",
"sha256:1df22371fbf2004c6f64e927668734070a8953362cd8370ddd336774d6743595",
"sha256:369d2346db5934345787451504853ad9d342d7f721ae82d098083e1f49a582ad",
"sha256:3cda1f0ed8747339bbdf71b9f38ca74c7b592f24f65cdb3ab3765e4b02871651",
"sha256:44ff04138935882fef7c686878e1c8fd80a723161ad6a98da31e14b7553170c2",
"sha256:4b1030728872c59687badcca1e225a9103440e467c17d6d1730ab3d2d64bfeff",
"sha256:58363dbd966afb4f89b3b11dfb8ff200058fbc3b947507675c19ceb46104b48d",
"sha256:6ec280fb24d27e3d97aa731e16207d58bd8ae94ef6eab97249a2afe4ba643d42",
"sha256:7270a6c29199adc1297776937a05b59720e8a782531f1f122f2eb8467f9aab4d",
"sha256:73fd30c57fa2d0a1d7a49c561c40c2f79c7d6c374cc7750e9ac7c99176f6428e",
"sha256:7f09806ed4fbea8f51585231ba742b58cbcfbfe823ea197d8c89a5e433c7e912",
"sha256:90df0cc93e1f8d2fba8365fb59a858f51a11a394d64dbf3ef844f783844cc793",
"sha256:971221ed40f058f5662a604bd1ae6e4521d84e6cad0b7b170564cc34169c8f13",
"sha256:a518c153a2b5ed6b8cc03f7ae79d5ffad7315ad4569b2d5333a13c38d64bd8d7",
"sha256:b0de590a8b0979649ebeef8bb9f54394d3a41f66c5584fff4220901739b6b2f0",
"sha256:b43f53f29816ba1db8525f006fa6f49292e9b029554b3eb56a189a70f2a40879",
"sha256:d31402aad60ed889c7e57934a03477b572a03af7794fa8fb1780f21ea8f6551f",
"sha256:de96157ec73458a7f14e3d26f17f8128c959084931e8997b9e655a39c8fde9f9",
"sha256:df6b4dca2e11865e6cfbfb708e800efb18370f5a46fd601d3755bc7f85b3a8a2",
"sha256:ecadccc7ba52193963c0475ac9f6fa28ac01e01349a2ca48509667ef41ffd2cf",
"sha256:fb81c17e0ebe3358486cd8cc3ad78adbae58af12fc2bf2bc0bb84e8090fa5ce8"
"sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6",
"sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b",
"sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5",
"sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf",
"sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e",
"sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b",
"sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae",
"sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b",
"sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0",
"sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b",
"sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d",
"sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229",
"sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3",
"sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365",
"sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55",
"sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270",
"sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e",
"sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785",
"sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0"
],
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'",
"version": "==2.8"
"version": "==2.9.2"
},
"decorator": {
"hashes": [
@@ -513,10 +511,10 @@
},
"pathspec": {
"hashes": [
"sha256:163b0632d4e31cef212976cf57b43d9fd6b0bac6e67c26015d611a647d5e7424",
"sha256:562aa70af2e0d434367d9790ad37aed893de47f1693e4201fd1d3dca15d19b96"
],
"version": "==0.7.0"
"sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
"sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
],
"version": "==0.8.0"
},
"pbr": {
"hashes": [
@@ -640,10 +638,10 @@
},
"readme-renderer": {
"hashes": [
"sha256:1b6d8dd1673a0b293766b4106af766b6eff3654605f9c4f239e65de6076bc222",
"sha256:e67d64242f0174a63c3b727801a2fff4c1f38ebe5d71d95ff7ece081945a6cd4"
"sha256:cbe9db71defedd2428a1589cdc545f9bd98e59297449f69d721ef8f1cfced68d",
"sha256:cc4957a803106e820d05d14f71033092537a22daa4f406dfbdd61177e0936376"
],
"version": "==25.0"
"version": "==26.0"
},
"regex": {
"hashes": [
+51 -17
View File
@@ -149,7 +149,6 @@ def build_dists(ctx):
ctx.run(f"pipenv run python setup.py sdist bdist_wheel", env=env)
@invoke.task(build_dists)
def upload_dists(ctx, repo="pypi"):
dist_pattern = f'{PACKAGE_NAME.replace("-", "[-_]")}-*'
@@ -239,28 +238,63 @@ def tag_version(ctx, push=False):
ctx.run("git push --tags")
def add_one_day(dt):
return dt + datetime.timedelta(days=1)
def date_offset(dt, month_offset=0, day_offset=0, truncate=False):
new_month = (dt.month + month_offset) % 12
year_offset = month_offset // 12
replace_args = {
"month": dt.month + month_offset,
"year": dt.year + year_offset,
}
log("Getting updated date from date: {0} using month offset: {1} and year offset {2}".format(
dt, new_month, replace_args["year"]
))
if day_offset:
dt = dt + datetime.timedelta(days=day_offset)
log("updated date using day offset: {0} => {1}".format(day_offset, dt))
if truncate:
log("Truncating...")
replace_args["day"] = 1
return dt.replace(**replace_args)
@invoke.task
def bump_version(ctx, dry_run=False, dev=False, pre=False, tag=None, commit=False):
def bump_version(ctx, dry_run=False, dev=False, pre=False, tag=None, commit=False, month_offset="0", trunc_month=False):
current_version = Version.parse(__version__)
today = datetime.date.today()
day_offset = 0
tomorrow = today + datetime.timedelta(days=1)
if pre and not tag:
print('Using "pre" requires a corresponding tag.')
return
if not (dev or pre or tag):
new_version = current_version.replace(release=today.timetuple()[:3]).clear(
pre=True, dev=True
)
month_offset = int(month_offset)
if month_offset:
# if we are offsetting by a month, grab the first day of the month
trunc_month = True
else:
target_day = today
if dev or pre:
target_day = date_offset(today, day_offset=1)
target_day = date_offset(
today,
month_offset=month_offset,
day_offset=day_offset,
truncate=trunc_month
)
log("target_day: {0}".format(target_day))
target_timetuple = target_day.timetuple()[:3]
new_version = current_version.replace(release=target_timetuple)
if pre and dev:
raise RuntimeError("Can't use 'pre' and 'dev' together!")
if dev or pre:
new_version = current_version.replace(release=tomorrow.timetuple()[:3]).clear(
pre=True, dev=True
)
if dev:
new_version = new_version.bump_dev()
else:
new_version = new_version.bump_pre(tag=tag)
if dev:
new_version = new_version.replace(pre=None).bump_dev()
elif pre:
if not tag:
print('Using "pre" requires a corresponding tag.')
return
new_version = new_version.bump_pre(tag=tag)
else:
new_version = new_version.replace(pre=None, dev=None)
log("Updating version to %s" % new_version.normalize())
version = find_version(ctx)
log("Found current version: %s" % version)