Skip to content

Commit 0d8c4d6

Browse files
committed
deployment workflow enhancement to test correct release
- pass version number from build to installing from pypi - new action/wait-for-pypi-version that waits until the correct package is on pypi or testpypi so that we can download and test properly - testing after pypi deployment fix - ensure that testing does not see the checked out sources because then it will be missing authors.py and fail the author test - check out repo for the wait-for-pypi action under source - based on Becksteinlab/multibind/.github/workflows (MIT license)
1 parent 376e794 commit 0d8c4d6

2 files changed

Lines changed: 145 additions & 12 deletions

File tree

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
name: 'Wait for PyPI version'
2+
description: 'Wait for a specific package version to become available on PyPI or TestPyPI'
3+
inputs:
4+
repository:
5+
description: 'PyPI repository type: "pypi" or "testpypi"'
6+
required: true
7+
package:
8+
description: 'Package name'
9+
required: true
10+
version:
11+
description: 'Package version to wait for'
12+
required: true
13+
max_attempts:
14+
description: 'Maximum number of retry attempts'
15+
required: false
16+
default: '30'
17+
wait_seconds:
18+
description: 'Seconds to wait between attempts'
19+
required: false
20+
default: '10'
21+
22+
runs:
23+
using: composite
24+
steps:
25+
- name: Install requests
26+
shell: bash
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install requests
30+
31+
- name: Wait for version to be available
32+
shell: python
33+
env:
34+
REPOSITORY: ${{ inputs.repository }}
35+
PACKAGE: ${{ inputs.package }}
36+
VERSION: ${{ inputs.version }}
37+
MAX_ATTEMPTS: ${{ inputs.max_attempts }}
38+
WAIT_SECONDS: ${{ inputs.wait_seconds }}
39+
run: |
40+
import os
41+
import sys
42+
import time
43+
44+
import requests
45+
46+
repository = os.environ["REPOSITORY"].strip().lower()
47+
package = os.environ["PACKAGE"]
48+
version = os.environ["VERSION"]
49+
max_attempts = int(os.environ.get("MAX_ATTEMPTS", "30"))
50+
wait_seconds = int(os.environ.get("WAIT_SECONDS", "10"))
51+
52+
if repository == "testpypi":
53+
api_url = f"https://test.pypi.org/pypi/{package}/json"
54+
repo_name = "TestPyPI"
55+
elif repository == "pypi":
56+
api_url = f"https://pypi.org/pypi/{package}/json"
57+
repo_name = "PyPI"
58+
else:
59+
print(
60+
f"ERROR: repository must be 'pypi' or 'testpypi', got {repository!r}",
61+
file=sys.stderr,
62+
)
63+
sys.exit(1)
64+
65+
for attempt in range(max_attempts):
66+
try:
67+
r = requests.get(api_url, timeout=10)
68+
r.raise_for_status()
69+
data = r.json()
70+
versions = data.get("releases", {})
71+
keys = list(versions.keys())
72+
print("Available versions:", keys[-10:]) # Show last 10 versions
73+
if version in versions:
74+
print(f"✓ Version {version} is available on {repo_name}")
75+
print(f"Version {version} is now available on {repo_name}")
76+
sys.exit(0)
77+
print(f"✗ Version {version} is NOT available on {repo_name}")
78+
except Exception as e:
79+
print(f"Error checking version: {e}")
80+
81+
current = attempt + 1
82+
print(
83+
f"Attempt {current}/{max_attempts}: Version {version} not yet available "
84+
f"on {repo_name}, waiting {wait_seconds} seconds..."
85+
)
86+
time.sleep(wait_seconds)
87+
88+
print(
89+
f"ERROR: Version {version} did not become available on {repo_name} "
90+
f"after {max_attempts} attempts",
91+
file=sys.stderr,
92+
)
93+
sys.exit(1)

.github/workflows/deploy.yml

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,19 @@ concurrency:
1515
defaults:
1616
run:
1717
shell: bash -l {0}
18-
1918

2019
jobs:
2120
build:
2221
name: Build package
2322
runs-on: ubuntu-latest
23+
outputs:
24+
version: ${{ steps.extract-version.outputs.version }}
2425
steps:
2526
- name: Checkout
2627
uses: actions/checkout@v6
28+
with:
29+
fetch-depth: 0
30+
fetch-tags: true
2731

2832
- name: Set up Python
2933
uses: actions/setup-python@v6
@@ -36,12 +40,24 @@ jobs:
3640
pip install build twine
3741
3842
- name: Build package (binary wheel and source distribution package)
39-
run: |
40-
python -m build
43+
run: python -m build
4144

4245
- name: Check package
46+
run: twine check dist/*
47+
48+
- name: Extract package version
49+
id: extract-version
4350
run: |
44-
twine check dist/*
51+
WHEEL_FILE=$(ls dist/*.whl)
52+
VERSION=$(basename "$WHEEL_FILE" | sed -n 's/mdanalysisdata-\([^-]*\)-.*/\1/p')
53+
if [ -z "$VERSION" ]; then
54+
python -m pip install --upgrade pip
55+
pip install "$WHEEL_FILE" --quiet
56+
VERSION=$(python -c "import MDAnalysisData; print(MDAnalysisData.__version__)")
57+
pip uninstall -y MDAnalysisData --quiet
58+
fi
59+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
60+
echo "Extracted version: $VERSION"
4561
4662
- name: Upload dist files
4763
uses: actions/upload-artifact@v7
@@ -70,13 +86,13 @@ jobs:
7086
run: |
7187
python -m pip install --upgrade pip
7288
WHEEL_FILE=$(ls dist/*.whl)
73-
pip install "$WHEEL_FILE"[test]
89+
pip install "${WHEEL_FILE}[test]"
7490
7591
- name: Test import
7692
run: |
7793
python -c "import MDAnalysisData; print(f'Package {MDAnalysisData.__version__} imported successfully')"
7894
79-
- name: Run basic tests
95+
- name: Run tests (without data file downloads)
8096
run: |
8197
pytest --verbose -m "not online" --pyargs MDAnalysisData
8298
@@ -135,7 +151,7 @@ jobs:
135151
fail-fast: false
136152
matrix:
137153
os: [ubuntu-latest, macos-latest]
138-
needs: deploy-testpypi
154+
needs: [build, deploy-testpypi]
139155
if: |
140156
github.repository == 'MDAnalysis/MDAnalysisData' &&
141157
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
@@ -145,16 +161,28 @@ jobs:
145161
with:
146162
python-version: "3.14"
147163

164+
- name: Checkout repository for actions
165+
uses: actions/checkout@v6
166+
with:
167+
path: source # put code under different path and do not cd to avoid interference with pytest invocation (missing authors.py)
168+
169+
- name: Wait for version to be available on TestPyPI
170+
uses: ./source/.github/actions/wait-for-pypi-version
171+
with:
172+
repository: testpypi
173+
package: MDAnalysisData
174+
version: ${{ needs.build.outputs.version }}
175+
148176
- name: Install from TestPyPI
149177
run: |
150178
python -m pip install --upgrade pip
151-
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ MDAnalysisData[test]
179+
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "MDAnalysisData[test]==${{ needs.build.outputs.version }}"
152180
153181
- name: Test import
154182
run: |
155183
python -c "import MDAnalysisData; print(f'Package {MDAnalysisData.__version__} imported successfully from TestPyPi')"
156184
157-
- name: Run basic tests
185+
- name: Run tests
158186
run: |
159187
pytest --verbose -m "not online" --pyargs MDAnalysisData
160188
@@ -165,7 +193,7 @@ jobs:
165193
fail-fast: false
166194
matrix:
167195
os: [ubuntu-latest, macos-latest]
168-
needs: deploy-pypi
196+
needs: [build, deploy-pypi]
169197
if: |
170198
github.repository == 'MDAnalysis/MDAnalysisData' &&
171199
(github.event_name == 'release' && github.event.action == 'published')
@@ -175,15 +203,27 @@ jobs:
175203
with:
176204
python-version: "3.14"
177205

206+
- name: Checkout repository for actions
207+
uses: actions/checkout@v6
208+
with:
209+
path: source # put code under different path and do not cd to avoid interference with pytest invocation (missing authors.py)
210+
211+
- name: Wait for version to be available on PyPI
212+
uses: ./source/.github/actions/wait-for-pypi-version
213+
with:
214+
repository: pypi
215+
package: MDAnalysisData
216+
version: ${{ needs.build.outputs.version }}
217+
178218
- name: Install from PyPI
179219
run: |
180220
python -m pip install --upgrade pip
181-
pip install MDAnalysisData[test]
221+
pip install "MDAnalysisData[test]==${{ needs.build.outputs.version }}"
182222
183223
- name: Test import
184224
run: |
185225
python -c "import MDAnalysisData; print(f'Package {MDAnalysisData.__version__} imported successfully from PyPi')"
186226
187-
- name: Run basic tests
227+
- name: Run tests
188228
run: |
189229
pytest --verbose -m "not online" --pyargs MDAnalysisData

0 commit comments

Comments
 (0)