Skip to content

Commit 8d08c63

Browse files
committed
fix(pypi): skip build+upload if version already exists on PyPI
Add pre-check step that queries PyPI JSON API to detect if the version is already published. If it exists, skip the build and upload steps entirely to prevent 400 'File already exists' errors on re-runs.
1 parent 042bb6a commit 8d08c63

1 file changed

Lines changed: 61 additions & 17 deletions

File tree

‎.github/workflows/pypi-publish.yml‎

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,82 +21,126 @@ jobs:
2121
environment:
2222
name: testpypi
2323
url: https://test.pypi.org/projects/a3m-router
24-
24+
2525
steps:
2626
- uses: actions/checkout@v4
27-
27+
2828
- name: Set up Python
2929
uses: actions/setup-python@v5
3030
with:
3131
python-version: '3.11'
32-
32+
3333
- name: Install dependencies
3434
run: |
35-
python -m pip install build twine
36-
35+
python -m pip install build twine toml
36+
37+
- name: Get version
38+
id: version
39+
run: |
40+
VERSION=$(python -c "import toml; print(toml.load('python/pyproject.toml')['project']['version'])")
41+
echo "version=$VERSION" >> $GITHUB_OUTPUT
42+
43+
- name: Check if version exists on Test PyPI
44+
id: exists-testpypi
45+
run: |
46+
EXISTS=$(curl -s "https://test.pypi.org/pypi/a3m-router/${{ steps.version.outputs.version }}/json" | python3 -c "import sys,json; d=json.load(sys.stdin); print('yes' if d.get('info',{}).get('version') == '${{ steps.version.outputs.version }}' else 'no')" 2>/dev/null || echo "no")
47+
echo "exists=$EXISTS" >> $GITHUB_OUTPUT
48+
echo "Version ${{ steps.version.outputs.version }} on Test PyPI: $EXISTS"
49+
if [ "$EXISTS" == "yes" ]; then
50+
echo "SKIP_UPLOAD=true" >> $GITHUB_OUTPUT
51+
fi
52+
3753
- name: Build package
54+
if: steps.exists-testpypi.outputs.SKIP_UPLOAD != 'true'
3855
run: |
3956
cd python
4057
python -m build
41-
58+
4259
- name: Publish to Test PyPI
60+
if: steps.exists-testpypi.outputs.SKIP_UPLOAD != 'true'
4361
env:
4462
TWINE_USERNAME: __token__
4563
TWINE_PASSWORD: ${{ secrets.PYPI_TEST_TOKEN }}
4664
run: |
4765
cd python
4866
python -m twine upload --repository testpypi dist/*
4967
68+
- name: Already published notice
69+
if: steps.exists-testpypi.outputs.SKIP_UPLOAD == 'true'
70+
run: echo "Version ${{ steps.version.outputs.version }} already exists on Test PyPI — skipping upload"
71+
5072
publish-pypi:
5173
if: github.event.inputs.environment == 'prod' || github.event_name == 'release'
5274
runs-on: ubuntu-latest
5375
environment:
5476
name: pypi
5577
url: https://pypi.org/projects/a3m-router
56-
78+
5779
steps:
5880
- uses: actions/checkout@v4
59-
81+
6082
- name: Set up Python
6183
uses: actions/setup-python@v5
6284
with:
6385
python-version: '3.11'
64-
86+
6587
- name: Install dependencies
6688
run: |
67-
python -m pip install build twine
68-
89+
python -m pip install build twine toml
90+
91+
- name: Get version
92+
id: version
93+
run: |
94+
VERSION=$(python -c "import toml; print(toml.load('python/pyproject.toml')['project']['version'])")
95+
echo "version=$VERSION" >> $GITHUB_OUTPUT
96+
97+
- name: Check if version exists on PyPI
98+
id: exists-pypi
99+
run: |
100+
EXISTS=$(curl -s "https://pypi.org/pypi/a3m-router/${{ steps.version.outputs.version }}/json" | python3 -c "import sys,json; d=json.load(sys.stdin); print('yes' if d.get('info',{}).get('version') == '${{ steps.version.outputs.version }}' else 'no')" 2>/dev/null || echo "no")
101+
echo "exists=$EXISTS" >> $GITHUB_OUTPUT
102+
echo "Version ${{ steps.version.outputs.version }} on PyPI: $EXISTS"
103+
if [ "$EXISTS" == "yes" ]; then
104+
echo "SKIP_UPLOAD=true" >> $GITHUB_OUTPUT
105+
fi
106+
69107
- name: Build package
108+
if: steps.exists-pypi.outputs.SKIP_UPLOAD != 'true'
70109
run: |
71110
cd python
72111
python -m build
73-
112+
74113
- name: Publish to PyPI
114+
if: steps.exists-pypi.outputs.SKIP_UPLOAD != 'true'
75115
env:
76116
TWINE_USERNAME: __token__
77117
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
78118
run: |
79119
cd python
80-
python -m twine upload --verbose dist/*
120+
python -m twine upload dist/*
121+
122+
- name: Already published notice
123+
if: steps.exists-pypi.outputs.SKIP_UPLOAD == 'true'
124+
run: echo "Version ${{ steps.version.outputs.version }} already exists on PyPI — skipping upload"
81125

82126
test-package:
83127
runs-on: ubuntu-latest
84128
needs: publish-testpypi
85-
129+
86130
steps:
87131
- uses: actions/checkout@v4
88-
132+
89133
- name: Set up Python
90134
uses: actions/setup-python@v5
91135
with:
92136
python-version: '3.11'
93-
137+
94138
- name: Install from Test PyPI
95139
env:
96140
TWINE_PASSWORD: ${{ secrets.PYPI_TEST_TOKEN }}
97141
run: |
98142
pip install --index-url https://test.pypi.org/simple/ a3m-router
99-
143+
100144
- name: Test import
101145
run: |
102146
python -c "from a3m import A3MRouter, __version__; print(f'A3M Router SDK v{__version__} installed successfully!')"

0 commit comments

Comments
 (0)