Skip to content

Commit 5216c7c

Browse files
authored
chore: Check whether version in PR is already released (#157)
We have a recurring issue that you often forget to bump the package version number in a PR, and then when you merge it to `master`, the package publication fails because the version you're trying to publish already exists. This adds a check to CI which detects these issues early and fails the CI, so you can't merge those PRs without bumping the package version number. It gets skipped if the title of the PR is `docs: ...`, because such PRs don't trigger package publication when merged. Same as apify/apify-sdk-python#127.
1 parent 6f8b538 commit 5216c7c

File tree

6 files changed

+64
-14
lines changed

6 files changed

+64
-14
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Check package version availability
2+
3+
on:
4+
workflow_call:
5+
6+
jobs:
7+
check_version_availability:
8+
name: Check version availability
9+
runs-on: ubuntu-latest
10+
if: (!startsWith(github.event.pull_request.title, 'docs:'))
11+
12+
steps:
13+
- name: Checkout repository
14+
uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v4
18+
with:
19+
python-version: "3.8"
20+
21+
- name: Install dependencies
22+
run: make install-dev
23+
24+
- name: Check version availability
25+
run: make check-version-availability

.github/workflows/run_checks.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
pull_request:
55

66
jobs:
7+
check_version_availability:
8+
name: Check version availability
9+
uses: ./.github/workflows/check_version_availability.yaml
10+
711
lint_and_type_checks:
812
name: Run lint and type checks
913
uses: ./.github/workflows/lint_and_type_checks.yaml

Makefile

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: clean install-dev build publish twine-check lint unit-tests integration-tests type-check check-code format check-async-docstrings fix-async-docstrings check-changelog-entry build-api-reference
1+
.PHONY: clean install-dev build publish twine-check lint unit-tests integration-tests type-check check-code format check-async-docstrings fix-async-docstrings check-version-availability check-changelog-entry build-api-reference
22

33
# This is default for local testing, but GitHub workflows override it to a higher value in CI
44
INTEGRATION_TESTS_CONCURRENCY = 1
@@ -44,6 +44,9 @@ check-async-docstrings:
4444
fix-async-docstrings:
4545
python3 scripts/fix_async_docstrings.py
4646

47+
check-version-availability:
48+
python3 scripts/check_version_availability.py
49+
4750
check-changelog-entry:
4851
python3 scripts/check_version_in_changelog.py
4952

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#!/usr/bin/env python3
2+
from utils import get_current_package_version, get_published_package_versions
3+
4+
# Checks whether the current package version number was not already used in a published release.
5+
if __name__ == '__main__':
6+
current_version = get_current_package_version()
7+
8+
# Load the version numbers of the currently published versions from PyPI
9+
published_versions = get_published_package_versions()
10+
11+
# We don't want to try to publish a version with the same version number as an already released stable version
12+
if current_version in published_versions:
13+
raise RuntimeError(f'The current version {current_version} was already released!')

scripts/update_version_for_prerelease.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
#!/usr/bin/env python3
22

3-
import json
43
import re
54
import sys
6-
import urllib.request
75

8-
from utils import PACKAGE_NAME, get_current_package_version, set_current_package_version
6+
from utils import get_current_package_version, get_published_package_versions, set_current_package_version
97

108
# Checks whether the current package version number was not already used in a published release,
119
# and if not, modifies the package version number in pyproject.toml
@@ -32,16 +30,7 @@
3230
raise RuntimeError(f'The current version {current_version} does not match the proper semver format for stable releases (X.Y.Z)')
3331

3432
# Load the version numbers of the currently published versions from PyPI
35-
# If the URL returns 404, it means the package has no releases yet (which is okay in our case)
36-
package_info_url = f'https://pypi.org/pypi/{PACKAGE_NAME}/json'
37-
try:
38-
conn = urllib.request.urlopen(package_info_url)
39-
package_data = json.load(urllib.request.urlopen(package_info_url))
40-
published_versions = list(package_data['releases'].keys())
41-
except urllib.error.HTTPError as e:
42-
if e.code != 404:
43-
raise e
44-
published_versions = []
33+
published_versions = get_published_package_versions()
4534

4635
# We don't want to publish a prerelease version with the same version number as an already released stable version
4736
if current_version in published_versions:

scripts/utils.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import json
12
import pathlib
23
import re
4+
import urllib.request
35

46
PACKAGE_NAME = 'apify_client'
57
REPO_ROOT = pathlib.Path(__file__).parent.resolve() / '..'
@@ -47,3 +49,17 @@ def sync_to_async_docstring(docstring: str) -> str:
4749
for (pattern, replacement) in substitutions:
4850
res = re.sub(pattern, replacement, res, flags=re.M)
4951
return res
52+
53+
54+
# Load the version numbers of the currently published versions from PyPI
55+
def get_published_package_versions() -> list:
56+
package_info_url = f'https://pypi.org/pypi/{PACKAGE_NAME}/json'
57+
try:
58+
package_data = json.load(urllib.request.urlopen(package_info_url))
59+
published_versions = list(package_data['releases'].keys())
60+
# If the URL returns 404, it means the package has no releases yet (which is okay in our case)
61+
except urllib.error.HTTPError as e:
62+
if e.code != 404:
63+
raise e
64+
published_versions = []
65+
return published_versions

0 commit comments

Comments
 (0)