release.yml 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. name: Build and publish Python package
  2. # Least-privilege default for every job; the publish job grants itself id-token below.
  3. permissions:
  4. contents: read
  5. on:
  6. pull_request:
  7. paths:
  8. - '.github/workflows/release.yml'
  9. - 'pyproject.toml'
  10. - 'base_requirements.txt'
  11. - 'requirements.txt'
  12. - 'upgrade.sh'
  13. - 'contrib/**'
  14. - 'netbox/**'
  15. - 'scripts/packaging/**'
  16. - 'scripts/verify_*.py'
  17. - 'scripts/smoketest_configuration.py'
  18. push:
  19. tags:
  20. - 'v*'
  21. workflow_dispatch:
  22. jobs:
  23. build:
  24. name: Build package artifacts
  25. runs-on: ubuntu-latest
  26. steps:
  27. - name: Check out repository
  28. uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  29. with:
  30. persist-credentials: false
  31. - name: Set up Python
  32. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  33. with:
  34. python-version: '3.12'
  35. cache: pip
  36. - name: Install build tooling
  37. run: python -m pip install --upgrade build twine
  38. - name: Build sdist and wheel
  39. run: python -m build
  40. - name: Check package metadata
  41. run: twine check dist/*
  42. - name: Upload package artifacts
  43. uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
  44. with:
  45. name: python-package-distributions
  46. path: dist/
  47. if-no-files-found: error
  48. verify-dependencies:
  49. name: Verify dependency pins are in sync
  50. runs-on: ubuntu-latest
  51. needs: build
  52. steps:
  53. - name: Check out repository
  54. uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  55. with:
  56. persist-credentials: false
  57. - name: Set up Python
  58. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  59. with:
  60. python-version: '3.12'
  61. cache: pip
  62. - name: Install packaging
  63. run: python -m pip install packaging
  64. - name: Verify requirements.txt is consistent with base_requirements.txt
  65. run: python scripts/verify_dependencies.py
  66. - name: Download package artifacts
  67. uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
  68. with:
  69. name: python-package-distributions
  70. path: dist/
  71. - name: Verify wheel Requires-Dist matches requirements.txt
  72. run: python scripts/verify_wheel_metadata.py dist/*.whl
  73. - name: Verify wheel excludes live configuration files
  74. run: python scripts/verify_wheel_contents.py dist/*.whl
  75. verify-sdist:
  76. name: Verify the sdist builds a wheel
  77. runs-on: ubuntu-latest
  78. needs: build
  79. steps:
  80. - name: Check out repository
  81. uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  82. with:
  83. persist-credentials: false
  84. - name: Set up Python
  85. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  86. with:
  87. python-version: '3.12'
  88. cache: pip
  89. - name: Install tooling
  90. run: python -m pip install --upgrade pip packaging
  91. - name: Download package artifacts
  92. uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
  93. with:
  94. name: python-package-distributions
  95. path: dist/
  96. - name: Verify the sdist contents
  97. run: |
  98. python scripts/verify_sdist_contents.py dist/*.tar.gz
  99. - name: Build a wheel from the sdist
  100. run: |
  101. python -m pip wheel --no-deps dist/*.tar.gz -w sdist-wheel/
  102. - name: Verify the sdist-built wheel
  103. run: |
  104. python scripts/verify_wheel_metadata.py sdist-wheel/*.whl
  105. python scripts/verify_wheel_contents.py sdist-wheel/*.whl
  106. smoke-test:
  107. name: Smoke test wheel install
  108. runs-on: ubuntu-latest
  109. needs: build
  110. # The wheel install + database migration is expensive; only run it for tag
  111. # pushes and manual dispatch, not on every packaging-related pull request.
  112. if: github.event_name != 'pull_request'
  113. services:
  114. postgres:
  115. image: postgres:17
  116. env:
  117. POSTGRES_DB: netbox
  118. POSTGRES_USER: netbox
  119. POSTGRES_PASSWORD: netbox
  120. ports:
  121. - 5432:5432
  122. options: >-
  123. --health-cmd "pg_isready -U netbox -d netbox"
  124. --health-interval 10s
  125. --health-timeout 5s
  126. --health-retries 5
  127. redis:
  128. image: redis:7
  129. ports:
  130. - 6379:6379
  131. options: >-
  132. --health-cmd "redis-cli ping"
  133. --health-interval 10s
  134. --health-timeout 5s
  135. --health-retries 5
  136. env:
  137. NETBOX_CONFIGURATION: smoketest_configuration
  138. NETBOX_SMOKETEST_BASE: /tmp/netbox-smoketest
  139. POSTGRES_DB: netbox
  140. POSTGRES_USER: netbox
  141. POSTGRES_PASSWORD: netbox
  142. POSTGRES_HOST: 127.0.0.1
  143. POSTGRES_PORT: 5432
  144. REDIS_HOST: 127.0.0.1
  145. REDIS_PORT: 6379
  146. steps:
  147. - name: Check out repository
  148. uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  149. with:
  150. persist-credentials: false
  151. - name: Set up Python
  152. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  153. with:
  154. python-version: '3.12'
  155. cache: pip
  156. - name: Download package artifacts
  157. uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
  158. with:
  159. name: python-package-distributions
  160. path: dist/
  161. - name: Install system build dependencies for psycopg
  162. run: sudo apt-get update && sudo apt-get install -y libpq-dev
  163. - name: Install wheel into a clean virtual environment
  164. run: |
  165. python -m venv /tmp/netbox-wheel-venv
  166. /tmp/netbox-wheel-venv/bin/python -m pip install --upgrade pip
  167. /tmp/netbox-wheel-venv/bin/python -m pip install dist/*.whl
  168. - name: Run NetBox smoke checks
  169. env:
  170. PYTHONPATH: ${{ github.workspace }}/scripts
  171. run: |
  172. /tmp/netbox-wheel-venv/bin/netbox check
  173. /tmp/netbox-wheel-venv/bin/netbox upgrade --no-input
  174. - name: Smoke-test netbox setup from the wheel
  175. run: |
  176. /tmp/netbox-wheel-venv/bin/netbox setup --target /tmp/nbroot --systemd-dir /tmp/systemd
  177. for f in /tmp/nbroot/conf/configuration.py /tmp/nbroot/gunicorn.py /tmp/nbroot/netbox.env \
  178. /tmp/nbroot/local_requirements.txt /tmp/systemd/netbox.service /tmp/systemd/netbox-rq.service; do
  179. test -f "$f" || { echo "missing $f"; exit 1; }
  180. done
  181. grep -qx 'NETBOX_ROOT=/tmp/nbroot' /tmp/nbroot/netbox.env || { echo "netbox.env missing active NETBOX_ROOT=/tmp/nbroot"; exit 1; }
  182. grep -q '/tmp/nbroot' /tmp/systemd/netbox.service || { echo "service not rendered to target"; exit 1; }
  183. ! grep -q '/opt/netbox' /tmp/systemd/netbox.service || { echo "service still references /opt/netbox"; exit 1; }
  184. ! grep -q 'pythonpath' /tmp/systemd/netbox.service || { echo "pip unit still passes --pythonpath"; exit 1; }
  185. grep -q 'ExecStart=/tmp/nbroot/venv/bin/netbox rqworker high default low' /tmp/systemd/netbox-rq.service || { echo "rq unit not adapted to the console script"; exit 1; }
  186. grep -q 'EnvironmentFile=-/tmp/nbroot/netbox.env' /tmp/systemd/netbox.service || { echo "unit missing EnvironmentFile"; exit 1; }
  187. /tmp/netbox-wheel-venv/bin/netbox secret-key | grep -Eq '^.{50}$' || { echo "secret-key not 50 chars"; exit 1; }
  188. publish-testpypi:
  189. name: Publish package to Test PyPI
  190. runs-on: ubuntu-latest
  191. needs: [smoke-test, verify-dependencies, verify-sdist]
  192. # Publishing always requires a v* tag ref: a tag push publishes to Test PyPI
  193. # automatically, and a manual dispatch does the same when the chosen ref is a v* tag.
  194. # Branch dispatches still run the build, verify, and smoke-test jobs (a useful dry run)
  195. # but the publish job is skipped. Production PyPI publishing is intentionally absent
  196. # during the v4.6.x preview; it arrives with the v4.7.0 feature branch.
  197. if: startsWith(github.ref, 'refs/tags/v') && (github.event_name == 'push' || github.event_name == 'workflow_dispatch')
  198. environment:
  199. name: testpypi
  200. url: https://test.pypi.org/p/netbox
  201. permissions:
  202. contents: read
  203. id-token: write
  204. steps:
  205. - name: Check out repository
  206. uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  207. with:
  208. persist-credentials: false
  209. - name: Set up Python
  210. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  211. with:
  212. python-version: '3.12'
  213. - name: Install tooling
  214. run: python -m pip install --upgrade pip packaging
  215. - name: Download package artifacts
  216. uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
  217. with:
  218. name: python-package-distributions
  219. path: dist/
  220. - name: Verify the git tag matches the built version
  221. run: python scripts/verify_release_tag.py "${{ github.ref_name }}" dist/*.whl
  222. - name: Publish package distributions to Test PyPI
  223. uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
  224. with:
  225. repository-url: https://test.pypi.org/legacy/