ci.yml 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. name: CI
  2. on:
  3. push:
  4. branches:
  5. - main
  6. pull_request:
  7. concurrency:
  8. group: ${{ github.head_ref || github.run_id }}
  9. cancel-in-progress: true
  10. env:
  11. POETRY_VIRTUALENVS_IN_PROJECT: "true"
  12. jobs:
  13. lint:
  14. runs-on: ubuntu-latest
  15. steps:
  16. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  17. - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  18. with:
  19. python-version: "3.13"
  20. - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1
  21. test:
  22. strategy:
  23. fail-fast: false
  24. matrix:
  25. python-version:
  26. - "3.11"
  27. - "3.12"
  28. - "3.13"
  29. - "3.14"
  30. os:
  31. - ubuntu-latest
  32. runs-on: ${{ matrix.os }}
  33. steps:
  34. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  35. - name: Set up uv
  36. uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
  37. with:
  38. enable-cache: true
  39. - name: Install poetry
  40. run: uv tool install poetry
  41. - name: Set up Python
  42. id: setup-python
  43. uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
  44. with:
  45. python-version: ${{ matrix.python-version }}
  46. cache: "poetry"
  47. allow-prereleases: true
  48. - name: Cache poetry venv
  49. id: cache-venv
  50. uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
  51. with:
  52. path: .venv
  53. key: venv-v1-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles('poetry.lock', 'pyproject.toml') }}
  54. - name: Install Dependencies
  55. if: steps.cache-venv.outputs.cache-hit != 'true'
  56. run: poetry install --only=main,dev
  57. - name: Test with Pytest
  58. run: poetry run pytest --cov=switchbot --cov-report=xml --durations=30 tests
  59. - name: Upload coverage to Codecov
  60. uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
  61. with:
  62. token: ${{ secrets.CODECOV_TOKEN }}
  63. # PRs and non-main pushes: validate the PSR config with a read-only dry run.
  64. # No environment and no elevated permissions, so PR runs can never enter the
  65. # release environment (avoiding approval-gate hangs) or hold OIDC/write tokens.
  66. release_dry_run:
  67. needs:
  68. - test
  69. - lint
  70. if: github.ref_name != 'main'
  71. runs-on: ubuntu-latest
  72. permissions:
  73. contents: read
  74. steps:
  75. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  76. with:
  77. fetch-depth: 0
  78. ref: ${{ github.ref }}
  79. - name: Create local branch name
  80. env:
  81. BRANCH: ${{ github.head_ref || github.ref_name }}
  82. run: git switch -C "$BRANCH"
  83. - name: Test release
  84. uses: python-semantic-release/python-semantic-release@350c48fcb3ffcdfd2e0a235206bc2ecea6b69df0 # v10.5.3
  85. with:
  86. no_operation_mode: true
  87. # main only: real release + publish. The environment and OIDC/write
  88. # permissions live here so they are never granted on PR runs.
  89. release:
  90. needs:
  91. - test
  92. - lint
  93. if: github.ref_name == 'main'
  94. runs-on: ubuntu-latest
  95. environment: release
  96. concurrency:
  97. group: release-${{ github.ref }}
  98. cancel-in-progress: false
  99. permissions:
  100. id-token: write
  101. contents: write
  102. steps:
  103. # Mint a short-lived installation token for the release-bot GitHub App,
  104. # which is in the default-branch ruleset's bypass_actors list so PSR's
  105. # version-bump commit/tag push isn't blocked by the required PR and
  106. # status checks. Per PSR's docs, the same token must be passed to
  107. # actions/checkout (via `token`) so its persisted http.extraheader
  108. # doesn't override PSR's URL-embedded auth at push time and re-attribute
  109. # the push to github-actions[bot].
  110. - name: Generate release App token
  111. id: app-token
  112. uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
  113. with:
  114. app-id: ${{ secrets.RELEASE_APP_ID }}
  115. private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }}
  116. - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
  117. with:
  118. fetch-depth: 0
  119. ref: ${{ github.ref }}
  120. token: ${{ steps.app-token.outputs.token }}
  121. - name: Create local branch name
  122. env:
  123. BRANCH: ${{ github.ref_name }}
  124. run: git switch -C "$BRANCH"
  125. - name: Release
  126. uses: python-semantic-release/python-semantic-release@350c48fcb3ffcdfd2e0a235206bc2ecea6b69df0 # v10.5.3
  127. id: release
  128. with:
  129. github_token: ${{ steps.app-token.outputs.token }}
  130. - name: Publish package distributions to PyPI
  131. uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
  132. if: steps.release.outputs.released == 'true'
  133. - name: Publish package distributions to GitHub Releases
  134. uses: python-semantic-release/upload-to-gh-release@0a92b5d7ebfc15a84f9801ebd1bf706343d43711 # main
  135. if: steps.release.outputs.released == 'true'
  136. with:
  137. github_token: ${{ steps.app-token.outputs.token }}