1+ name : Release validation and publish
2+
3+ on :
4+ push :
5+ tags :
6+ - " v*"
7+ workflow_dispatch :
8+
9+ permissions :
10+ contents : read
11+
12+ jobs :
13+ prepare-release :
14+ name : prepare artifacts + metadata
15+ runs-on : ubuntu-latest
16+ outputs :
17+ package_name : ${{ steps.meta.outputs.package_name }}
18+ version : ${{ steps.meta.outputs.version }}
19+
20+ steps :
21+ - name : Checkout repository
22+ uses : actions/checkout@v6
23+
24+ - name : Set up Python 3.13
25+ uses : actions/setup-python@v6
26+ with :
27+ python-version : " 3.13"
28+
29+ - name : Set up uv
30+ uses : astral-sh/setup-uv@v7
31+
32+ - name : Validate package metadata and tag
33+ id : meta
34+ env :
35+ GITHUB_REF_NAME : ${{ github.ref_name }}
36+ GITHUB_EVENT_NAME : ${{ github.event_name }}
37+ run : |
38+ python - <<'PY'
39+ import os
40+ import pathlib
41+ import tomllib
42+
43+ data = tomllib.loads(pathlib.Path('pyproject.toml').read_text(encoding='utf-8'))
44+ project_name = data['project']['name']
45+ version = data['project']['version']
46+ tag = os.getenv('GITHUB_REF_NAME', '')
47+ event = os.getenv('GITHUB_EVENT_NAME', '')
48+ if project_name != 'netbox-proxbox':
49+ raise SystemExit(f'Expected project name netbox-proxbox, got {project_name!r}')
50+ if event == 'push' and tag and tag != f'v{version}':
51+ raise SystemExit(f'Tag/version mismatch: tag={tag!r}, version={version!r}')
52+ with open(os.environ['GITHUB_OUTPUT'], 'a', encoding='utf-8') as fh:
53+ fh.write(f'package_name={project_name}\nversion={version}\n')
54+ PY
55+
56+ - name : Build distribution
57+ run : uv run --with build python -m build
58+
59+ - name : Upload dist artifacts
60+ uses : actions/upload-artifact@v7
61+ with :
62+ name : dist
63+ path : dist/*
64+ if-no-files-found : error
65+
66+ publish-testpypi :
67+ name : publish to TestPyPI
68+ needs : prepare-release
69+ runs-on : ubuntu-latest
70+ steps :
71+ - name : Set up Python 3.13
72+ uses : actions/setup-python@v6
73+ with :
74+ python-version : ' 3.13'
75+
76+ - name : Set up uv
77+ uses : astral-sh/setup-uv@v7
78+
79+ - name : Download dist artifacts
80+ uses : actions/download-artifact@v8
81+ with :
82+ name : dist
83+ path : dist
84+
85+ - name : Upload to TestPyPI
86+ env :
87+ TEST_PYPI_USERNAME : ${{ secrets.TEST_PYPI_USERNAME }}
88+ TEST_PYPI_TOKEN : ${{ secrets.TEST_PYPI_TOKEN }}
89+ run : |
90+ uv run --with twine python -m twine upload \
91+ --non-interactive \
92+ --skip-existing \
93+ --repository-url https://test.pypi.org/legacy/ \
94+ --username "$TEST_PYPI_USERNAME" \
95+ --password "$TEST_PYPI_TOKEN" \
96+ dist/*
97+
98+ validate-testpypi :
99+ name : validate TestPyPI / py${{ matrix.python-version }}
100+ needs : [prepare-release, publish-testpypi]
101+ runs-on : ubuntu-latest
102+ strategy :
103+ fail-fast : false
104+ matrix :
105+ python-version : ['3.12', '3.13']
106+ steps :
107+ - name : Checkout repository
108+ uses : actions/checkout@v6
109+
110+ - name : Set up Python ${{ matrix.python-version }}
111+ uses : actions/setup-python@v6
112+ with :
113+ python-version : ${{ matrix.python-version }}
114+
115+ - name : Set up uv
116+ uses : astral-sh/setup-uv@v7
117+
118+ - name : Sync development environment
119+ run : uv sync --extra test --extra dev --locked
120+
121+ - name : Compile package
122+ run : uv run python -m compileall netbox_proxbox tests
123+
124+ - name : Run full test suite
125+ run : uv run pytest --cov=netbox_proxbox --cov-report=term-missing --cov-report=xml tests
126+
127+ publish-pypi :
128+ name : publish to PyPI
129+ if : github.event_name == 'push'
130+ needs : [prepare-release, validate-testpypi]
131+ runs-on : ubuntu-latest
132+ steps :
133+ - name : Set up Python 3.13
134+ uses : actions/setup-python@v6
135+ with :
136+ python-version : ' 3.13'
137+
138+ - name : Set up uv
139+ uses : astral-sh/setup-uv@v7
140+
141+ - name : Download dist artifacts
142+ uses : actions/download-artifact@v8
143+ with :
144+ name : dist
145+ path : dist
146+
147+ - name : Upload to PyPI
148+ env :
149+ PYPI_USERNAME : ${{ secrets.PYPI_USERNAME }}
150+ PYPI_TOKEN : ${{ secrets.PYPI_TOKEN }}
151+ run : |
152+ uv run --with twine python -m twine upload \
153+ --non-interactive \
154+ --skip-existing \
155+ --repository-url https://upload.pypi.org/legacy/ \
156+ --username "$PYPI_USERNAME" \
157+ --password "$PYPI_TOKEN" \
158+ dist/*
0 commit comments