From 849dec99b52d163b893b97c331f29bfb2274c719 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 22 Jan 2026 12:15:50 -0700 Subject: [PATCH 1/3] ensure testing of built version after deployment - fix #158 - remember version that was built - ensure that we download the built version from TestPyPi or PyPi - built with AI assistance (cursor) --- .../actions/wait-for-pypi-version/action.yaml | 86 +++++++++++++++++++ .github/workflows/deploy.yaml | 42 ++++++++- 2 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 .github/actions/wait-for-pypi-version/action.yaml diff --git a/.github/actions/wait-for-pypi-version/action.yaml b/.github/actions/wait-for-pypi-version/action.yaml new file mode 100644 index 0000000..f27bff1 --- /dev/null +++ b/.github/actions/wait-for-pypi-version/action.yaml @@ -0,0 +1,86 @@ +name: 'Wait for PyPI version' +description: 'Wait for a specific package version to become available on PyPI or TestPyPI' +inputs: + repository: + description: 'PyPI repository type: "pypi" or "testpypi"' + required: true + package: + description: 'Package name' + required: true + version: + description: 'Package version to wait for' + required: true + max_attempts: + description: 'Maximum number of retry attempts' + required: false + default: '30' + wait_seconds: + description: 'Seconds to wait between attempts' + required: false + default: '10' + +runs: + using: composite + steps: + - name: Install requests + shell: bash + run: | + python -m pip install --upgrade pip + pip install requests + + - name: Wait for version to be available + shell: bash + env: + REPOSITORY: ${{ inputs.repository }} + PACKAGE: ${{ inputs.package }} + VERSION: ${{ inputs.version }} + MAX_ATTEMPTS: ${{ inputs.max_attempts }} + WAIT_SECONDS: ${{ inputs.wait_seconds }} + run: | + if [ "$REPOSITORY" = "testpypi" ]; then + API_URL="https://test.pypi.org/pypi/$PACKAGE/json" + REPO_NAME="TestPyPI" + elif [ "$REPOSITORY" = "pypi" ]; then + API_URL="https://pypi.org/pypi/$PACKAGE/json" + REPO_NAME="PyPI" + else + echo "ERROR: repository must be 'pypi' or 'testpypi', got '$REPOSITORY'" + exit 1 + fi + + # Export shell variables so Python can access them via os.environ + export API_URL REPO_NAME + + ATTEMPT=0 + while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do + if python -c " + import os + import requests + import sys + try: + api_url = os.environ['API_URL'] + version = os.environ['VERSION'] + repo_name = os.environ['REPO_NAME'] + r = requests.get(api_url, timeout=10) + r.raise_for_status() + versions = r.json().get('releases', {}) + print('Available versions:', list(versions.keys())[-10:]) # Show last 10 versions + if version in versions: + print(f'✓ Version {version} is available on {repo_name}') + sys.exit(0) + else: + print(f'✗ Version {version} is NOT available on {repo_name}') + sys.exit(1) + except Exception as e: + print(f'Error checking version: {e}') + sys.exit(1) + " 2>/dev/null; then + echo "Version $VERSION is now available on $REPO_NAME" + exit 0 + fi + ATTEMPT=$((ATTEMPT + 1)) + echo "Attempt $ATTEMPT/$MAX_ATTEMPTS: Version $VERSION not yet available on $REPO_NAME, waiting $WAIT_SECONDS seconds..." + sleep $WAIT_SECONDS + done + echo "ERROR: Version $VERSION did not become available on $REPO_NAME after $MAX_ATTEMPTS attempts" + exit 1 diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e1cca34..5e434a9 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -21,6 +21,8 @@ jobs: build: name: Build package runs-on: ubuntu-latest + outputs: + version: ${{ steps.extract-version.outputs.version }} steps: - name: Checkout uses: actions/checkout@v4 @@ -43,6 +45,24 @@ jobs: run: | twine check dist/* + - name: Extract package version + id: extract-version + run: | + WHEEL_FILE=$(ls dist/*.whl) + # Extract version from wheel filename (format: griddataformats-VERSION-py3-none-any.whl) + VERSION=$(basename "$WHEEL_FILE" | sed -n 's/griddataformats-\([^-]*\)-.*/\1/p') + # Fallback: install wheel temporarily and get version + if [ -z "$VERSION" ]; then + # This is a bit dirty; running in a virtual environment would be cleaner. + # (pip install only works because our package's dependencies are easy to install.) + python -m pip install --upgrade pip + pip install "$WHEEL_FILE" --quiet + VERSION=$(python -c "import gridData; print(gridData.__version__)") + pip uninstall -y griddataformats --quiet + fi + echo "version=$VERSION" >> $GITHUB_OUTPUT + echo "Extracted version: $VERSION" + - name: Upload dist files uses: actions/upload-artifact@v4 with: @@ -133,7 +153,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - needs: deploy-testpypi + needs: [build, deploy-testpypi] if: | github.repository == 'MDAnalysis/GridDataFormats' && (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) @@ -143,10 +163,17 @@ jobs: with: python-version: "3.14" + - name: Wait for version to be available on TestPyPI + uses: ./.github/actions/wait-for-pypi-version + with: + repository: testpypi + package: GridDataFormats + version: ${{ needs.build.outputs.version }} + - name: Install from TestPyPI run: | python -m pip install --upgrade pip - pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ GridDataFormats[test] + pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "GridDataFormats==${{ needs.build.outputs.version }}[test]" - name: Test import run: | @@ -163,7 +190,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] - needs: deploy-pypi + needs: [build, deploy-pypi] if: | github.repository == 'MDAnalysis/GridDataFormats' && (github.event_name == 'release' && github.event.action == 'published') @@ -173,10 +200,17 @@ jobs: with: python-version: "3.14" + - name: Wait for version to be available on PyPI + uses: ./.github/actions/wait-for-pypi-version + with: + repository: pypi + package: GridDataFormats + version: ${{ needs.build.outputs.version }} + - name: Install from PyPI run: | python -m pip install --upgrade pip - pip install GridDataFormats[test] + pip install "GridDataFormats==${{ needs.build.outputs.version }}[test]" - name: Test import run: | From 370dd068d81f929fc615b30d25d3dce7f29fd4db Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 22 Jan 2026 16:46:33 -0700 Subject: [PATCH 2/3] checkout repo for local action --- .github/workflows/deploy.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 5e434a9..f66dd03 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -163,6 +163,9 @@ jobs: with: python-version: "3.14" + - name: Checkout repository for actions + uses: actions/checkout@v4 + - name: Wait for version to be available on TestPyPI uses: ./.github/actions/wait-for-pypi-version with: @@ -200,6 +203,9 @@ jobs: with: python-version: "3.14" + - name: Checkout repository for actions + uses: actions/checkout@v4 + - name: Wait for version to be available on PyPI uses: ./.github/actions/wait-for-pypi-version with: From 329b18f8e57b9e1576590239c7b4367e9bcb17b4 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Thu, 22 Jan 2026 17:00:25 -0700 Subject: [PATCH 3/3] fixed installation of specific version with extra --- .github/workflows/deploy.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index f66dd03..53ce330 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -176,7 +176,7 @@ jobs: - name: Install from TestPyPI run: | python -m pip install --upgrade pip - pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "GridDataFormats==${{ needs.build.outputs.version }}[test]" + pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ "GridDataFormats[test]==${{ needs.build.outputs.version }}" - name: Test import run: | @@ -216,7 +216,7 @@ jobs: - name: Install from PyPI run: | python -m pip install --upgrade pip - pip install "GridDataFormats==${{ needs.build.outputs.version }}[test]" + pip install "GridDataFormats[test]==${{ needs.build.outputs.version }}" - name: Test import run: |