Skip to content

Commit 68835e8

Browse files
authored
Merge pull request #8 from RaphaelBajon/feature/deployment
update docs for xarray accessor and deployment
2 parents e483dfc + 524d7f2 commit 68835e8

File tree

7 files changed

+322
-115
lines changed

7 files changed

+322
-115
lines changed

.github/workflows/deploy.yml

Lines changed: 207 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -6,104 +6,220 @@ on:
66
workflow_dispatch:
77

88
jobs:
9-
test-deploy:
9+
# ---------------------------------------------------------------------------
10+
# 1. Run the full test suite before attempting any deployment
11+
# ---------------------------------------------------------------------------
12+
test:
13+
name: Run tests (Python ${{ matrix.python-version }})
14+
runs-on: ubuntu-latest
15+
strategy:
16+
fail-fast: true
17+
matrix:
18+
python-version: ["3.9", "3.10", "3.11", "3.12"]
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- uses: actions/setup-python@v5
23+
with:
24+
python-version: ${{ matrix.python-version }}
25+
26+
- name: Install dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install pytest
30+
pip install -r requirements.txt
31+
32+
- name: Run pytest
33+
run: pytest --tb=short
34+
35+
# ---------------------------------------------------------------------------
36+
# 2. Build the distribution once — all deploy jobs reuse the same artifact
37+
# ---------------------------------------------------------------------------
38+
build:
39+
name: Build distribution
40+
runs-on: ubuntu-latest
41+
needs: test
42+
steps:
43+
- uses: actions/checkout@v4
44+
45+
- uses: actions/setup-python@v5
46+
with:
47+
python-version: "3.x"
48+
49+
- name: Clean build cache
50+
run: |
51+
rm -rf build dist .eggs
52+
find . -name "*.pyc" -delete
53+
find . -name "__pycache__" -delete
54+
55+
- name: Install build tools
56+
run: pip install --upgrade pip build tomli
57+
58+
- name: Read version from pyproject.toml
59+
id: version
60+
run: |
61+
VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['project']['version'])")
62+
echo "version=$VERSION" >> $GITHUB_OUTPUT
63+
echo "Building version $VERSION"
64+
65+
- name: Build sdist and wheel
66+
run: python -m build
67+
68+
- name: Upload distribution artifact
69+
uses: actions/upload-artifact@v4
70+
with:
71+
name: dist-${{ steps.version.outputs.version }}
72+
path: dist/
73+
retention-days: 7
74+
75+
outputs:
76+
version: ${{ steps.version.outputs.version }}
77+
78+
# ---------------------------------------------------------------------------
79+
# 3. Deploy to TestPyPI and run a smoke test
80+
# ---------------------------------------------------------------------------
81+
deploy-test:
1082
name: Deploy to TestPyPI
1183
runs-on: ubuntu-latest
84+
needs: build
85+
environment: testpypi
1286
permissions:
1387
id-token: write
1488
contents: read
15-
steps:
16-
- uses: actions/checkout@v3
17-
18-
- name: Set up Python
19-
uses: actions/setup-python@v4
20-
with:
21-
python-version: "3.x"
22-
23-
- name: Clean build cache
24-
run: |
25-
rm -rf build dist .eggs
26-
find . -name "*.pyc" -delete
27-
find . -name "__pycache__" -delete
28-
29-
- name: Install dependencies
30-
run: |
31-
python -m pip install --upgrade pip
32-
pip install build twine tomli
33-
34-
- name: Install dependencies requirements
35-
run: |
36-
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
37-
38-
- name: Debug directory content
39-
run: |
40-
pwd
41-
ls -la
42-
43-
- name: Build package
44-
run: python -m build
45-
46-
- name: Check version on TestPyPI and Upload if new
47-
env:
48-
TWINE_USERNAME: __token__
49-
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }}
50-
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
51-
continue-on-error: true
52-
run: |
53-
# Get current version from pyproject.toml
54-
VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['project']['version'])")
55-
echo "Package version: $VERSION"
56-
57-
# Try to install current version from TestPyPI
58-
if pip install --index-url https://test.pypi.org/simple/ --no-deps canyonbpy==$VERSION 2>/dev/null; then
59-
echo "Version $VERSION already exists on TestPyPI"
60-
else
61-
echo "Version $VERSION not found on TestPyPI, uploading..."
62-
python -m twine upload --verbose dist/*
63-
fi
64-
65-
- name: Wait for TestPyPI to process upload
66-
run: sleep 60
67-
68-
- name: Test installation from TestPyPI
69-
run: |
70-
python -m pip install --index-url https://test.pypi.org/simple/ --no-deps canyonbpy
71-
python -c "from canyonbpy import canyonb"
7289

90+
steps:
91+
- uses: actions/checkout@v4
92+
93+
- uses: actions/setup-python@v5
94+
with:
95+
python-version: "3.x"
96+
97+
- name: Download distribution artifact
98+
uses: actions/download-artifact@v4
99+
with:
100+
name: dist-${{ needs.build.outputs.version }}
101+
path: dist/
102+
103+
- name: Check if version already exists on TestPyPI
104+
id: testpypi-check
105+
run: |
106+
VERSION=${{ needs.build.outputs.version }}
107+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
108+
https://test.pypi.org/pypi/canyonbpy/$VERSION/json)
109+
if [ "$STATUS" = "200" ]; then
110+
echo "exists=true" >> $GITHUB_OUTPUT
111+
echo "Version $VERSION already on TestPyPI — skipping upload."
112+
else
113+
echo "exists=false" >> $GITHUB_OUTPUT
114+
echo "Version $VERSION not found on TestPyPI — will upload."
115+
fi
116+
117+
- name: Upload to TestPyPI
118+
if: steps.testpypi-check.outputs.exists == 'false'
119+
env:
120+
TWINE_USERNAME: __token__
121+
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_TOKEN }}
122+
TWINE_REPOSITORY_URL: https://test.pypi.org/legacy/
123+
run: |
124+
pip install twine
125+
twine upload --verbose dist/*
126+
127+
- name: Wait for TestPyPI to index the release
128+
if: steps.testpypi-check.outputs.exists == 'false'
129+
run: sleep 60
130+
131+
- name: Install from TestPyPI
132+
run: |
133+
pip install -r requirements.txt
134+
pip install \
135+
--index-url https://test.pypi.org/simple/ \
136+
--extra-index-url https://pypi.org/simple/ \
137+
canyonbpy==${{ needs.build.outputs.version }}
138+
139+
- name: Smoke test — verify imports, version and accessor
140+
run: |
141+
python - <<'EOF'
142+
import canyonbpy
143+
from canyonbpy import canyonb, DatasetToNumpy
144+
145+
assert canyonbpy.__version__ == "${{ needs.build.outputs.version }}", \
146+
f"Version mismatch: {canyonbpy.__version__}"
147+
148+
import xarray as xr
149+
import numpy as np
150+
ds = xr.Dataset({"pressure": ("x", [100.0])})
151+
assert hasattr(ds, "canyonb"), "ds.canyonb accessor not registered"
152+
153+
print(f"OK canyonbpy {canyonbpy.__version__} smoke test passed")
154+
EOF
155+
156+
# ---------------------------------------------------------------------------
157+
# 4. Deploy to PyPI — only runs after TestPyPI smoke test passes
158+
# ---------------------------------------------------------------------------
73159
deploy-prod:
74160
name: Deploy to PyPI
75-
needs: test-deploy
76161
runs-on: ubuntu-latest
77-
if: success()
162+
needs: [build, deploy-test]
163+
environment: pypi
164+
permissions:
165+
id-token: write
166+
contents: read
167+
168+
steps:
169+
- uses: actions/setup-python@v5
170+
with:
171+
python-version: "3.x"
172+
173+
- name: Download distribution artifact
174+
uses: actions/download-artifact@v4
175+
with:
176+
name: dist-${{ needs.build.outputs.version }}
177+
path: dist/
178+
179+
- name: Check if version already exists on PyPI
180+
id: pypi-check
181+
run: |
182+
VERSION=${{ needs.build.outputs.version }}
183+
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
184+
https://pypi.org/pypi/canyonbpy/$VERSION/json)
185+
if [ "$STATUS" = "200" ]; then
186+
echo "exists=true" >> $GITHUB_OUTPUT
187+
echo "Version $VERSION already on PyPI — skipping upload."
188+
else
189+
echo "exists=false" >> $GITHUB_OUTPUT
190+
echo "Version $VERSION not found on PyPI — will upload."
191+
fi
192+
193+
- name: Upload to PyPI
194+
if: steps.pypi-check.outputs.exists == 'false'
195+
env:
196+
TWINE_USERNAME: __token__
197+
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
198+
run: |
199+
pip install twine
200+
twine upload --verbose dist/*
201+
202+
# ---------------------------------------------------------------------------
203+
# 5. Attach the wheel and sdist to the GitHub Release
204+
# This also triggers Zenodo archiving (Zenodo watches release events)
205+
# ---------------------------------------------------------------------------
206+
attach-release-assets:
207+
name: Attach dist to GitHub Release
208+
runs-on: ubuntu-latest
209+
needs: [build, deploy-prod]
210+
# Only run when triggered by an actual published release, not workflow_dispatch
211+
if: github.event_name == 'release'
212+
permissions:
213+
contents: write
214+
78215
steps:
79-
- uses: actions/checkout@v3
80-
81-
- name: Set up Python
82-
uses: actions/setup-python@v4
83-
with:
84-
python-version: "3.x"
85-
86-
- name: Install dependencies
87-
run: |
88-
python -m pip install --upgrade pip
89-
pip install build twine tomli
90-
91-
- name: Build package
92-
run: python -m build
93-
94-
- name: Check version on PyPI and Upload if new
95-
env:
96-
TWINE_USERNAME: __token__
97-
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
98-
run: |
99-
# Get current version from pyproject.toml
100-
VERSION=$(python -c "import tomli; print(tomli.load(open('pyproject.toml', 'rb'))['project']['version'])")
101-
echo "Package version: $VERSION"
102-
103-
# Try to install current version from PyPI
104-
if pip install canyonbpy==$VERSION 2>/dev/null; then
105-
echo "Version $VERSION already exists on PyPI"
106-
else
107-
echo "Version $VERSION not found on PyPI, uploading..."
108-
python -m twine upload --verbose dist/*
109-
fi
216+
- name: Download distribution artifact
217+
uses: actions/download-artifact@v4
218+
with:
219+
name: dist-${{ needs.build.outputs.version }}
220+
path: dist/
221+
222+
- name: Upload wheel and sdist to GitHub Release
223+
uses: softprops/action-gh-release@v2
224+
with:
225+
files: dist/*

.zenodo.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"title": "canyonbpy: A Python implementation of CANYON-B",
3+
"description": "canyonbpy is a Python package implementing the CANYON-B neural network for predicting ocean biogeochemical parameters (Total Alkalinity, Dissolved Inorganic Carbon, pH, pCO2, NO3, PO4, SiOH4) from temperature, salinity and dissolved oxygen. Based on Bittig et al. (2018).",
4+
"creators": [
5+
{
6+
"name": "Bajon, Raphaël",
7+
"affiliation": "UH Manoa, SOEST",
8+
"orcid": "0000-0003-1984-0539"
9+
}
10+
],
11+
"keywords": [
12+
"oceanography",
13+
"biogeochemistry",
14+
"neural network",
15+
"CANYON-B",
16+
"carbon cycle",
17+
"Argo",
18+
"xarray",
19+
"python"
20+
],
21+
"license": "MIT",
22+
"upload_type": "software",
23+
"access_right": "open",
24+
"related_identifiers": [
25+
{
26+
"identifier": "10.3389/fmars.2018.00328",
27+
"relation": "isDerivedFrom",
28+
"scheme": "doi",
29+
"resource_type": "publication-article"
30+
},
31+
{
32+
"identifier": "https://github.com/HCBScienceProducts/CANYON-B",
33+
"relation": "isDerivedFrom",
34+
"scheme": "url"
35+
}
36+
],
37+
"language": "eng"
38+
}

0 commit comments

Comments
 (0)