Skip to content

Commit 0c9c8be

Browse files
Add first version of automatic deployment workflow
1 parent c73a50a commit 0c9c8be

1 file changed

Lines changed: 126 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
repository:
7+
description: "Target index"
8+
type: choice
9+
options: [pypi, testpypi]
10+
default: testpypi
11+
ref:
12+
description: "Git ref to build (branch/tag/SHA). Leave empty to use the UI-selected ref."
13+
required: false
14+
default: ""
15+
16+
concurrency:
17+
group: pypi-publish
18+
cancel-in-progress: false
19+
20+
jobs:
21+
publish:
22+
runs-on: ubuntu-latest
23+
container:
24+
image: cubertgmbh/cuvis_pyil:3.5.0-ubuntu24.04
25+
26+
environment: ${{ inputs.repository }}
27+
28+
permissions:
29+
contents: read
30+
id-token: write # required for PyPI Trusted Publishing (OIDC)
31+
32+
steps:
33+
- name: Checkout
34+
uses: actions/checkout@v5
35+
with:
36+
ref: ${{ inputs.ref || github.ref }}
37+
38+
# Optional hard stop: only allow repo admins to proceed
39+
- name: Enforce admin-only trigger
40+
uses: actions/github-script@v7
41+
with:
42+
script: |
43+
const owner = context.repo.owner;
44+
const repo = context.repo.repo;
45+
const username = context.actor;
46+
47+
const { data } = await github.rest.repos.getCollaboratorPermissionLevel({
48+
owner, repo, username
49+
});
50+
51+
core.info(`Actor permission: ${data.permission}`);
52+
if (data.permission !== "admin") {
53+
core.setFailed(`Only repository admins may publish. (${username} has: ${data.permission})`);
54+
}
55+
56+
57+
- name: Install build tooling
58+
run: python -m pip install --upgrade pip build
59+
60+
- name: Read package name/version from pyproject.toml
61+
id: meta
62+
run: |
63+
python - <<'PY'
64+
import sys, json
65+
try:
66+
import tomllib # py3.11+
67+
except ModuleNotFoundError:
68+
import tomli as tomllib # fallback if needed
69+
from pathlib import Path
70+
71+
data = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))
72+
proj = data.get("project", {})
73+
name = proj.get("name")
74+
version = proj.get("version")
75+
if not name or not version:
76+
print("Missing [project].name or [project].version in pyproject.toml", file=sys.stderr)
77+
sys.exit(2)
78+
79+
print(f"name={name}")
80+
print(f"version={version}")
81+
with open("pkg_meta.json", "w", encoding="utf-8") as f:
82+
json.dump({"name": name, "version": version}, f)
83+
PY
84+
echo "name=$(python -c "import json; print(json.load(open('pkg_meta.json'))['name'])")" >> "$GITHUB_OUTPUT"
85+
echo "version=$(python -c "import json; print(json.load(open('pkg_meta.json'))['version'])")" >> "$GITHUB_OUTPUT"
86+
87+
- name: Abort if this version already exists on the target index
88+
env:
89+
NAME: ${{ steps.meta.outputs.name }}
90+
VERSION: ${{ steps.meta.outputs.version }}
91+
TARGET: ${{ inputs.repository }}
92+
run: |
93+
python - <<'PY'
94+
import json, os, sys, urllib.request, urllib.error
95+
96+
name = os.environ["NAME"]
97+
version = os.environ["VERSION"]
98+
target = os.environ["TARGET"]
99+
100+
base = "https://pypi.org/pypi" if target == "pypi" else "https://test.pypi.org/pypi"
101+
url = f"{base}/{name}/json"
102+
103+
try:
104+
with urllib.request.urlopen(url) as resp:
105+
data = json.load(resp)
106+
except urllib.error.HTTPError as e:
107+
if e.code == 404:
108+
print(f"{name} not found on {target}; OK to publish {version}.")
109+
sys.exit(0)
110+
raise
111+
112+
releases = data.get("releases", {})
113+
if version in releases and releases[version]:
114+
print(f"Version {name}=={version} already exists on {target}. Aborting.")
115+
sys.exit(1)
116+
117+
print(f"Version {name}=={version} not present on {target}; OK to publish.")
118+
PY
119+
120+
- name: Build sdist and wheel
121+
run: python -m build
122+
123+
- name: Publish (Trusted Publishing / OIDC)
124+
uses: pypa/gh-action-pypi-publish@release/v1
125+
with:
126+
repository-url: ${{ inputs.repository == 'pypi' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }}

0 commit comments

Comments
 (0)