hyperi-ci 2.7.1__tar.gz → 2.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (284) hide show
  1. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/_release-tail.yml +10 -0
  2. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/ci.yml +27 -0
  3. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/go-ci.yml +30 -6
  4. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/python-ci.yml +30 -6
  5. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/rust-ci.yml +30 -6
  6. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/workflows/ts-ci.yml +30 -6
  7. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/PKG-INFO +1 -1
  8. hyperi_ci-2.7.2/VERSION +1 -0
  9. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/ARCHITECTURE.md +18 -7
  10. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/LESSONS.md +4 -2
  11. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/cli.py +17 -0
  12. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/dispatch.py +19 -9
  13. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/commit_validation.py +44 -6
  14. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_commit_validation.py +94 -0
  15. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_quality_ignores.py +11 -2
  16. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_workflow_consistency.py +60 -10
  17. hyperi_ci-2.7.1/VERSION +0 -1
  18. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.gitattributes +0 -0
  19. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/actions/predict-version/action.yml +0 -0
  20. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/actions/setup-osv-scanner/action.yml +0 -0
  21. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/actions/setup-runtime/action.yml +0 -0
  22. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/actions/setup-semantic-release/action.yml +0 -0
  23. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.github/actions/setup-semantic-release/default.releaserc.json +0 -0
  24. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.gitignore +0 -0
  25. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.hyperi-ci.yaml +0 -0
  26. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/.releaserc.yaml +0 -0
  27. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/AI-TRAINING-POLICY.md +0 -0
  28. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/CHANGELOG.md +0 -0
  29. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/COMMERCIAL.md +0 -0
  30. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/CONTRIBUTING.md +0 -0
  31. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/LICENSE +0 -0
  32. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/README.md +0 -0
  33. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/SECURITY.md +0 -0
  34. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/config/commit-types.yaml +0 -0
  35. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/config/org.yaml +0 -0
  36. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/config/runners.yaml +0 -0
  37. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/config/secrets-access.yaml +0 -0
  38. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/config/versions.yaml +0 -0
  39. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/ARC-RUNNERS.md +0 -0
  40. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/CI-LESSONS.md +0 -0
  41. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/DESIGN.md +0 -0
  42. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/FLOW.md +0 -0
  43. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/JFROG-MIGRATION.md +0 -0
  44. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/MIGRATION-GUIDE.md +0 -0
  45. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/PGO-WORKLOAD-GUIDE.md +0 -0
  46. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/README.md +0 -0
  47. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/dependencies/DEPS-PINNING.md +0 -0
  48. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/dependencies/WORKFLOW-PINNING.md +0 -0
  49. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/deployment/CONTRACT-IDENTITY.md +0 -0
  50. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/deployment/CONTRACT.md +0 -0
  51. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/deployment/TIERS.md +0 -0
  52. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/deployment-contract.md +0 -0
  53. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/languages/GO.md +0 -0
  54. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/languages/PYTHON.md +0 -0
  55. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/languages/RUST.md +0 -0
  56. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/languages/TYPESCRIPT.md +0 -0
  57. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/CODEBERG-SECRETS-AND-CI.md +0 -0
  58. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/JFROG.md +0 -0
  59. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/ONBOARDING.md +0 -0
  60. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/codeberg-secrets-and-ci.md +0 -0
  61. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/codeberg.md +0 -0
  62. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/migration/deployment-contract-tier3.md +0 -0
  63. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/runtime/PGO-BOLT.md +0 -0
  64. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/runtime/RUNNERS.md +0 -0
  65. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/runtime/TESTENV.md +0 -0
  66. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/rust.md +0 -0
  67. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/docs/typescript.md +0 -0
  68. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/pyproject.toml +0 -0
  69. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/renovate.json +0 -0
  70. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/robots.txt +0 -0
  71. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/check-workflow-interfaces.py +0 -0
  72. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/pre-commit-versions.sh +0 -0
  73. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/recover-tags.py +0 -0
  74. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/setup-rust-dev.py +0 -0
  75. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/sync-secrets-access.py +0 -0
  76. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/scripts/update-versions.py +0 -0
  77. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/__init__.py +0 -0
  78. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/argocd/__init__.py +0 -0
  79. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/argocd/gitops_push.py +0 -0
  80. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/argocd/stage.py +0 -0
  81. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/common.py +0 -0
  82. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/defaults.yaml +0 -0
  83. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/native-deps/golang.yaml +0 -0
  84. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/native-deps/python.yaml +0 -0
  85. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/native-deps/rust.yaml +0 -0
  86. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/native-deps/typescript.yaml +0 -0
  87. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/org.yaml +0 -0
  88. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/toolchains/gcc.yaml +0 -0
  89. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config/toolchains/llvm.yaml +0 -0
  90. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/config.py +0 -0
  91. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/__init__.py +0 -0
  92. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/binary_stage.py +0 -0
  93. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/build.py +0 -0
  94. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/compose.py +0 -0
  95. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/detect.py +0 -0
  96. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/labels.py +0 -0
  97. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/manifest.py +0 -0
  98. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/registry.py +0 -0
  99. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/stage.py +0 -0
  100. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/container/templates.py +0 -0
  101. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/__init__.py +0 -0
  102. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/cli.py +0 -0
  103. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/contract.py +0 -0
  104. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/detect.py +0 -0
  105. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/__init__.py +0 -0
  106. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/anchors/__init__.py +0 -0
  107. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/anchors/argocd.py +0 -0
  108. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/anchors/dockerfile.py +0 -0
  109. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/anchors/helm.py +0 -0
  110. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/cli.py +0 -0
  111. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/errors.py +0 -0
  112. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/model.py +0 -0
  113. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/overlay/render.py +0 -0
  114. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/registry.py +0 -0
  115. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/scaffold.py +0 -0
  116. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/stage.py +0 -0
  117. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/topology/__init__.py +0 -0
  118. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/topology/resolve.py +0 -0
  119. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/deployment/topology/stitch.py +0 -0
  120. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/detect.py +0 -0
  121. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gh.py +0 -0
  122. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/.gitbook.yaml +0 -0
  123. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/.gitignore +0 -0
  124. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/CODEOWNERS +0 -0
  125. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/LICENSE +0 -0
  126. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/README.md +0 -0
  127. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/argocd/README.md +0 -0
  128. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/argocd/applicationsets/.gitkeep +0 -0
  129. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/argocd/appprojects/.gitkeep +0 -0
  130. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/argocd/bootstrap/.gitkeep +0 -0
  131. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/concepts/architecture.md +0 -0
  132. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/concepts/overview.md +0 -0
  133. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/how-to/add-app.md +0 -0
  134. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/how-to/add-environment.md +0 -0
  135. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/how-to/add-topology.md +0 -0
  136. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/index.md +0 -0
  137. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/operations/disaster-recovery.md +0 -0
  138. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/operations/rollback.md +0 -0
  139. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/quickstart.md +0 -0
  140. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/reference/sync-waves.md +0 -0
  141. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/docs/reference/topology-schema.md +0 -0
  142. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/mkdocs.yml +0 -0
  143. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/pull_request_template.md +0 -0
  144. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/terraform/README.md +0 -0
  145. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/terraform/aws/environments/.gitkeep +0 -0
  146. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/terraform/aws/modules/.gitkeep +0 -0
  147. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/terraform/rancher/clusters/.gitkeep +0 -0
  148. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/terraform/rancher/modules/.gitkeep +0 -0
  149. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/topologies/README.md +0 -0
  150. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/values/.gitkeep +0 -0
  151. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/workflows/docs.yaml +0 -0
  152. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/workflows/stitch-and-publish.yaml +0 -0
  153. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/gitops_templates/workflows/validate.yaml +0 -0
  154. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/helm/__init__.py +0 -0
  155. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/helm/stage.py +0 -0
  156. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/init.py +0 -0
  157. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/init_gitops.py +0 -0
  158. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/install_deps.py +0 -0
  159. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/__init__.py +0 -0
  160. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/_build_common.py +0 -0
  161. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/golang/__init__.py +0 -0
  162. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/golang/build.py +0 -0
  163. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/golang/publish.py +0 -0
  164. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/golang/quality.py +0 -0
  165. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/golang/test.py +0 -0
  166. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/python/__init__.py +0 -0
  167. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/python/build.py +0 -0
  168. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/python/publish.py +0 -0
  169. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/python/quality.py +0 -0
  170. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/python/test.py +0 -0
  171. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/quality_common.py +0 -0
  172. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/__init__.py +0 -0
  173. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/build.py +0 -0
  174. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/optimize.py +0 -0
  175. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/pgo.py +0 -0
  176. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/publish.py +0 -0
  177. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/quality.py +0 -0
  178. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/rust/test.py +0 -0
  179. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/__init__.py +0 -0
  180. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/_common.py +0 -0
  181. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/build.py +0 -0
  182. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/install_deps.py +0 -0
  183. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/publish.py +0 -0
  184. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/quality.py +0 -0
  185. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/languages/typescript/test.py +0 -0
  186. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/licenses.py +0 -0
  187. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/logs.py +0 -0
  188. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/migrate.py +0 -0
  189. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/native_deps.py +0 -0
  190. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/publish/__init__.py +0 -0
  191. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/publish/binaries.py +0 -0
  192. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/publish/dispatch.py +0 -0
  193. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/publish_binaries.py +0 -0
  194. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/push.py +0 -0
  195. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/__init__.py +0 -0
  196. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/gitleaks.py +0 -0
  197. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/ignores.py +0 -0
  198. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/osv_scanner.py +0 -0
  199. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/quality/predicted_bump.py +0 -0
  200. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/release.py +0 -0
  201. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/stamp.py +0 -0
  202. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/trigger.py +0 -0
  203. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/upgrade.py +0 -0
  204. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/src/hyperi_ci/watch.py +0 -0
  205. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/README.md +0 -0
  206. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/grpc-server.sh +0 -0
  207. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/http-server.sh +0 -0
  208. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/kafka-consumer.sh +0 -0
  209. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/kafka-producer.sh +0 -0
  210. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/pgo-workload/multi-protocol.sh +0 -0
  211. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/testenv/README.md +0 -0
  212. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/testenv/clickhouse-low-mem.xml +0 -0
  213. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/testenv/clickhouse.compose.yaml +0 -0
  214. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/templates/testenv/redpanda.compose.yaml +0 -0
  215. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/__init__.py +0 -0
  216. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/argocd/__init__.py +0 -0
  217. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/argocd/test_gitops_push.py +0 -0
  218. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/argocd/test_stage.py +0 -0
  219. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/__init__.py +0 -0
  220. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/__init__.py +0 -0
  221. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/test_argocd_anchors.py +0 -0
  222. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/test_cli.py +0 -0
  223. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/test_dockerfile_anchors.py +0 -0
  224. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/test_helm_anchors.py +0 -0
  225. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/overlay/test_model.py +0 -0
  226. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/topology/__init__.py +0 -0
  227. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/topology/test_resolve.py +0 -0
  228. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/deployment/topology/test_stitch.py +0 -0
  229. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/helm/__init__.py +0 -0
  230. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/helm/test_stage.py +0 -0
  231. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/helm/test_stage_topology_mode.py +0 -0
  232. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/integration/__init__.py +0 -0
  233. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/integration/test_rust_build_optimize.py +0 -0
  234. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/__init__.py +0 -0
  235. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/__init__.py +0 -0
  236. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_contract.py +0 -0
  237. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_detect_tier.py +0 -0
  238. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_emit_artefacts_cli.py +0 -0
  239. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_registry_cascade.py +0 -0
  240. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_scaffold.py +0 -0
  241. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/deployment/test_stage.py +0 -0
  242. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_build_common.py +0 -0
  243. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_cli.py +0 -0
  244. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_cli_init_gitops.py +0 -0
  245. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_cli_stitch.py +0 -0
  246. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_common.py +0 -0
  247. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_config.py +0 -0
  248. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_binary_stage.py +0 -0
  249. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_build.py +0 -0
  250. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_compose.py +0 -0
  251. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_detect.py +0 -0
  252. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_labels.py +0 -0
  253. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_manifest.py +0 -0
  254. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_registry.py +0 -0
  255. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_stage.py +0 -0
  256. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_container_templates.py +0 -0
  257. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_detect.py +0 -0
  258. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_dispatch_alias.py +0 -0
  259. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_dispatch_status.py +0 -0
  260. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_gh.py +0 -0
  261. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_init.py +0 -0
  262. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_init_gitops.py +0 -0
  263. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_init_topology.py +0 -0
  264. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_licenses.py +0 -0
  265. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_migrate.py +0 -0
  266. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_native_deps.py +0 -0
  267. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_osv_scanner.py +0 -0
  268. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_predicted_bump.py +0 -0
  269. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_publish.py +0 -0
  270. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_publish_dispatch.py +0 -0
  271. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_push.py +0 -0
  272. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_quality_ignore_wiring.py +0 -0
  273. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_recover_tags.py +0 -0
  274. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_release_version.py +0 -0
  275. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_rust_optimize.py +0 -0
  276. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_rust_pgo.py +0 -0
  277. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_rust_quality.py +0 -0
  278. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_stamp.py +0 -0
  279. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_typescript_quality.py +0 -0
  280. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_update_versions.py +0 -0
  281. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_upgrade.py +0 -0
  282. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_watch.py +0 -0
  283. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/tests/unit/test_workflow_interfaces.py +0 -0
  284. {hyperi_ci-2.7.1 → hyperi_ci-2.7.2}/uv.lock +0 -0
@@ -70,6 +70,10 @@ on:
70
70
  type: string
71
71
  default: "stable"
72
72
  description: "Rust toolchain (only used when language=rust)"
73
+ submodules:
74
+ type: string
75
+ default: ""
76
+ description: "Space-separated submodule paths to init before the container build (e.g. 'schemas'), so a Dockerfile can bake submodule content. Empty (default) inits nothing."
73
77
 
74
78
  env:
75
79
  HYPERCI_INSTALL: uvx --no-cache --refresh hyperi-ci
@@ -108,6 +112,12 @@ jobs:
108
112
  with:
109
113
  ref: ${{ inputs.tag || github.ref }}
110
114
 
115
+ - name: Init submodules
116
+ if: ${{ inputs.submodules != '' }}
117
+ env:
118
+ SUBMODULES: ${{ inputs.submodules }}
119
+ run: git submodule update --init --depth 1 $SUBMODULES
120
+
111
121
  - name: Install uv
112
122
  # Needed to run the resolve step below. Cheap, no Docker Hub —
113
123
  # so a library never pays the Buildx/Docker-Hub cost (issue #33).
@@ -70,6 +70,33 @@ jobs:
70
70
  from-head: ${{ inputs.from-head }}
71
71
  bump: ${{ inputs.bump }}
72
72
 
73
+ commit-check:
74
+ name: Commit messages
75
+ # Landing gate for conventional-commit messages -- dogfoods the same job
76
+ # the reusable <lang>-ci.yml workflows now ship. Deliberately NOT gated on
77
+ # plan.outputs.run-checks: it must validate what actually reaches main on
78
+ # EVERY merge, not only publish-worthy pushes. Fatal on push to main (what
79
+ # lands); advisory on pull_request. Runs the LOCAL branch code (uv run) so
80
+ # this repo's own PRs exercise the change. See CLAUDE.md CI gate doctrine.
81
+ if: >-
82
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
83
+ github.event_name == 'pull_request'
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
87
+ with:
88
+ fetch-depth: 0
89
+ - name: Install uv
90
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
91
+ with:
92
+ enable-cache: true
93
+ - name: Set up Python
94
+ run: uv python install 3.12
95
+ - name: Install dependencies
96
+ run: uv sync --no-sources
97
+ - name: Validate commit messages
98
+ run: uv run --no-sources hyperi-ci check-commits
99
+
73
100
  quality:
74
101
  name: Quality
75
102
  runs-on: ubuntu-latest
@@ -155,6 +155,30 @@ jobs:
155
155
  fi
156
156
  echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
157
157
 
158
+ commit-check:
159
+ name: Commit messages
160
+ # Landing gate for conventional-commit messages. Deliberately NOT gated
161
+ # on plan.outputs.run-checks: a cheap git-log + regex check must validate
162
+ # what actually reaches main on EVERY merge, not only publish-worthy
163
+ # pushes (all the quality job covers). Fatal on push to main (what lands);
164
+ # advisory on pull_request (branch commits may be squashed away -- see
165
+ # commit_validation.run). Feature-branch pushes skip it, so the fast
166
+ # chore-skip path is untouched. See CLAUDE.md CI gate doctrine.
167
+ if: >-
168
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
169
+ github.event_name == 'pull_request'
170
+ runs-on: ubuntu-latest
171
+ steps:
172
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
173
+ with:
174
+ fetch-depth: 0
175
+ - name: Install uv
176
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
177
+ with:
178
+ enable-cache: false
179
+ - name: Validate commit messages
180
+ run: ${{ env.HYPERCI_INSTALL }} check-commits
181
+
158
182
  quality:
159
183
  name: Quality
160
184
  needs: [plan]
@@ -200,12 +224,11 @@ jobs:
200
224
  - name: Install native dependencies
201
225
  run: ${{ env.HYPERCI_INSTALL }} install-native-deps golang
202
226
 
203
- - name: Deepen history for commit validation
204
- # The quality checkout is depth-1; commit validation (issue #52) needs
205
- # the pushed range before..after, which includes `before` and any
206
- # merge-imported commits. Deepen so the range resolves. Harmless on an
207
- # already-full clone. If skipped, the quality stage validates HEAD only
208
- # and warns loudly -- it never silently passes having checked nothing.
227
+ - name: Deepen history for secret scan
228
+ # gitleaks scans the branch git log for committed secrets; a depth-1
229
+ # checkout would only see HEAD. Deepen so the scan covers the pushed
230
+ # range. Conventional-commit validation moved to the commit-check job
231
+ # (its own fetch-depth: 0 checkout). Harmless on an already-full clone.
209
232
  if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
210
233
  run: git fetch --deepen=200 origin
211
234
 
@@ -356,4 +379,5 @@ jobs:
356
379
  from-head: ${{ inputs.from-head }}
357
380
  bump: ${{ inputs.bump }}
358
381
  publish-target: ${{ inputs.publish-target }}
382
+ submodules: ${{ inputs.submodules }}
359
383
  secrets: inherit
@@ -140,6 +140,30 @@ jobs:
140
140
  from-head: ${{ inputs.from-head }}
141
141
  bump: ${{ inputs.bump }}
142
142
 
143
+ commit-check:
144
+ name: Commit messages
145
+ # Landing gate for conventional-commit messages. Deliberately NOT gated
146
+ # on plan.outputs.run-checks: a cheap git-log + regex check must validate
147
+ # what actually reaches main on EVERY merge, not only publish-worthy
148
+ # pushes (all the quality job covers). Fatal on push to main (what lands);
149
+ # advisory on pull_request (branch commits may be squashed away -- see
150
+ # commit_validation.run). Feature-branch pushes skip it, so the fast
151
+ # chore-skip path is untouched. See CLAUDE.md CI gate doctrine.
152
+ if: >-
153
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
154
+ github.event_name == 'pull_request'
155
+ runs-on: ubuntu-latest
156
+ steps:
157
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
158
+ with:
159
+ fetch-depth: 0
160
+ - name: Install uv
161
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
162
+ with:
163
+ enable-cache: false
164
+ - name: Validate commit messages
165
+ run: ${{ env.HYPERCI_INSTALL }} check-commits
166
+
143
167
  quality:
144
168
  name: Quality
145
169
  needs: [plan]
@@ -174,12 +198,11 @@ jobs:
174
198
  - name: Install dependencies
175
199
  run: uv sync --frozen --all-extras
176
200
 
177
- - name: Deepen history for commit validation
178
- # The quality checkout is depth-1; commit validation (issue #52) needs
179
- # the pushed range before..after, which includes `before` and any
180
- # merge-imported commits. Deepen so the range resolves. Harmless on an
181
- # already-full clone. If skipped, the quality stage validates HEAD only
182
- # and warns loudly -- it never silently passes having checked nothing.
201
+ - name: Deepen history for secret scan
202
+ # gitleaks scans the branch git log for committed secrets; a depth-1
203
+ # checkout would only see HEAD. Deepen so the scan covers the pushed
204
+ # range. Conventional-commit validation moved to the commit-check job
205
+ # (its own fetch-depth: 0 checkout). Harmless on an already-full clone.
183
206
  if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
184
207
  run: git fetch --deepen=200 origin
185
208
 
@@ -354,4 +377,5 @@ jobs:
354
377
  bump: ${{ inputs.bump }}
355
378
  publish-target: ${{ inputs.publish-target }}
356
379
  python-version: ${{ inputs.python-version }}
380
+ submodules: ${{ inputs.submodules }}
357
381
  secrets: inherit
@@ -173,6 +173,30 @@ jobs:
173
173
  fi
174
174
  echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
175
175
 
176
+ commit-check:
177
+ name: Commit messages
178
+ # Landing gate for conventional-commit messages. Deliberately NOT gated
179
+ # on plan.outputs.run-checks: a cheap git-log + regex check must validate
180
+ # what actually reaches main on EVERY merge, not only publish-worthy
181
+ # pushes (all the quality job covers). Fatal on push to main (what lands);
182
+ # advisory on pull_request (branch commits may be squashed away -- see
183
+ # commit_validation.run). Feature-branch pushes skip it, so the fast
184
+ # chore-skip path is untouched. See CLAUDE.md CI gate doctrine.
185
+ if: >-
186
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
187
+ github.event_name == 'pull_request'
188
+ runs-on: ubuntu-latest
189
+ steps:
190
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
191
+ with:
192
+ fetch-depth: 0
193
+ - name: Install uv
194
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
195
+ with:
196
+ enable-cache: false
197
+ - name: Validate commit messages
198
+ run: ${{ env.HYPERCI_INSTALL }} check-commits
199
+
176
200
  quality:
177
201
  name: Quality
178
202
  needs: [plan]
@@ -226,12 +250,11 @@ jobs:
226
250
  - name: Setup osv-scanner
227
251
  uses: hyperi-io/hyperi-ci/.github/actions/setup-osv-scanner@main
228
252
 
229
- - name: Deepen history for commit validation
230
- # The quality checkout is depth-1; commit validation (issue #52) needs
231
- # the pushed range before..after, which includes `before` and any
232
- # merge-imported commits. Deepen so the range resolves. Harmless on an
233
- # already-full clone. If skipped, the quality stage validates HEAD only
234
- # and warns loudly -- it never silently passes having checked nothing.
253
+ - name: Deepen history for secret scan
254
+ # gitleaks scans the branch git log for committed secrets; a depth-1
255
+ # checkout would only see HEAD. Deepen so the scan covers the pushed
256
+ # range. Conventional-commit validation moved to the commit-check job
257
+ # (its own fetch-depth: 0 checkout). Harmless on an already-full clone.
235
258
  if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
236
259
  run: git fetch --deepen=200 origin
237
260
 
@@ -411,4 +434,5 @@ jobs:
411
434
  bump: ${{ inputs.bump }}
412
435
  publish-target: ${{ inputs.publish-target }}
413
436
  rust-toolchain: ${{ inputs.rust-toolchain }}
437
+ submodules: ${{ inputs.submodules }}
414
438
  secrets: inherit
@@ -137,6 +137,30 @@ jobs:
137
137
  from-head: ${{ inputs.from-head }}
138
138
  bump: ${{ inputs.bump }}
139
139
 
140
+ commit-check:
141
+ name: Commit messages
142
+ # Landing gate for conventional-commit messages. Deliberately NOT gated
143
+ # on plan.outputs.run-checks: a cheap git-log + regex check must validate
144
+ # what actually reaches main on EVERY merge, not only publish-worthy
145
+ # pushes (all the quality job covers). Fatal on push to main (what lands);
146
+ # advisory on pull_request (branch commits may be squashed away -- see
147
+ # commit_validation.run). Feature-branch pushes skip it, so the fast
148
+ # chore-skip path is untouched. See CLAUDE.md CI gate doctrine.
149
+ if: >-
150
+ (github.event_name == 'push' && github.ref == 'refs/heads/main') ||
151
+ github.event_name == 'pull_request'
152
+ runs-on: ubuntu-latest
153
+ steps:
154
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
155
+ with:
156
+ fetch-depth: 0
157
+ - name: Install uv
158
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
159
+ with:
160
+ enable-cache: false
161
+ - name: Validate commit messages
162
+ run: ${{ env.HYPERCI_INSTALL }} check-commits
163
+
140
164
  quality:
141
165
  name: Quality
142
166
  needs: [plan]
@@ -197,12 +221,11 @@ jobs:
197
221
  echo "$HOME/.local/bin" >> "$GITHUB_PATH"
198
222
  "$HOME/.local/bin/osv-scanner" --version
199
223
 
200
- - name: Deepen history for commit validation
201
- # The quality checkout is depth-1; commit validation (issue #52) needs
202
- # the pushed range before..after, which includes `before` and any
203
- # merge-imported commits. Deepen so the range resolves. Harmless on an
204
- # already-full clone. If skipped, the quality stage validates HEAD only
205
- # and warns loudly -- it never silently passes having checked nothing.
224
+ - name: Deepen history for secret scan
225
+ # gitleaks scans the branch git log for committed secrets; a depth-1
226
+ # checkout would only see HEAD. Deepen so the scan covers the pushed
227
+ # range. Conventional-commit validation moved to the commit-check job
228
+ # (its own fetch-depth: 0 checkout). Harmless on an already-full clone.
206
229
  if: ${{ github.event_name == 'push' || github.event_name == 'pull_request' }}
207
230
  run: git fetch --deepen=200 origin
208
231
 
@@ -364,4 +387,5 @@ jobs:
364
387
  from-head: ${{ inputs.from-head }}
365
388
  bump: ${{ inputs.bump }}
366
389
  publish-target: ${{ inputs.publish-target }}
390
+ submodules: ${{ inputs.submodules }}
367
391
  secrets: inherit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperi-ci
3
- Version: 2.7.1
3
+ Version: 2.7.2
4
4
  Summary: HyperI CI/CD CLI tool — multi-language build, test, and publish automation
5
5
  Project-URL: Homepage, https://github.com/hyperi-io/hyperi-ci
6
6
  Project-URL: Repository, https://github.com/hyperi-io/hyperi-ci
@@ -0,0 +1 @@
1
+ 2.7.2
@@ -82,11 +82,21 @@ the single place language divergence is allowed.
82
82
  | Job | needs | if | Purpose |
83
83
  |---|---|---|---|
84
84
  | `plan` | — | always | Decide if this run is publish-worthy; emit gate outputs |
85
+ | `commit-check` | — | push-to-main OR `pull_request` | Conventional-commit **landing gate** — fatal on push to main (validates what lands), advisory on PRs. NOT `run-checks`-gated (see below) |
85
86
  | `quality` | `[plan]` | `run-checks` | Lint / typecheck / security scan |
86
87
  | `test` | `[plan]` | `run-checks` | Unit + integration tests |
87
88
  | `build` | `[plan, quality, test]` | `run-build` | Compile binaries / wheels / packages, stamp version, upload `dist/` |
88
89
  | `release-tail` | `[plan, build]` | own gates | Container + tag-and-publish, via shared `_release-tail.yml` |
89
90
 
91
+ `commit-check` is deliberately **independent of `plan` / `run-checks`**: that
92
+ gate skips the quality job on ordinary (non-publish) merges to main, so a bad
93
+ conventional-commit message could otherwise land unvalidated. It is a cheap
94
+ git-log + regex check (no compile/publish), fatal on the push that actually
95
+ reaches main and advisory on PRs (branch commits may be squashed away — only
96
+ the squash subject lands). Feature-branch pushes skip it, preserving the
97
+ chore-skip fast path. Logic: `hyperi_ci.quality.commit_validation.run`; the
98
+ local `hyperi-ci check` runs the same validation over `origin/main..HEAD`.
99
+
90
100
  ### Gate outputs (computed in `plan`)
91
101
 
92
102
  | Output | True when | Effect |
@@ -115,13 +125,14 @@ flowchart LR
115
125
 
116
126
  ### What runs when
117
127
 
118
- | Push type | plan | quality | test | build | container | tag+publish |
119
- |---|---|---|---|---|---|---|
120
- | `chore:` / `docs:` to main | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
121
- | `feat:`/`fix:` to main, no `Publish:` trailer | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
122
- | `feat:`/`fix:` to main + `Publish: true` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
123
- | Pull request | ✓ | ✓ | ✓ | ✗ | ✗ | ✗ |
124
- | `workflow_dispatch` (retroactive publish) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
128
+ | Push type | plan | commit-check | quality | test | build | container | tag+publish |
129
+ |---|---|---|---|---|---|---|---|
130
+ | `chore:` / `docs:` to main | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
131
+ | `feat:`/`fix:` to main, no `Publish:` trailer | ✓ | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ |
132
+ | `feat:`/`fix:` to main + `Publish: true` | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
133
+ | Pull request | ✓ | ✓ advisory | ✓ | ✓ | ✗ | ✗ | ✗ |
134
+ | `workflow_dispatch` (retroactive publish) | ✓ | ✗ | ✓ | ✓ | ✓ | ✓ | ✓ |
135
+ | push to a feature branch | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
125
136
 
126
137
  Tag-on-publish doctrine: a commit landing on main produces no tag and no
127
138
  artefacts. The operator opts in with `hyperi-ci push --publish` (adds the
@@ -299,10 +299,12 @@ build to ensure fresh artifacts, also via `hyperi-ci run build`.
299
299
  - Detection: directory-based > marker-based > conftest-based
300
300
  - Coverage: separate `.coverage.<tier>` files, combine at end
301
301
  - No test directory: exit 0 (graceful skip)
302
- - Submodule-dependent tests:
302
+ - Submodule-dependent tests or container builds:
303
303
  - Public submodule: declare `submodules: schemas` in `.hyperi-ci.yaml`;
304
304
  `hyperi-ci init` renders it as the reusable-workflow `submodules` input
305
- in `ci.yml`, and the test job checks it out so the tests run.
305
+ in `ci.yml`. The test job checks it out so the tests run, and the
306
+ container build checks it out so a Dockerfile can bake submodule
307
+ content into the image.
306
308
  - Private submodule (e.g. `dfe-schemas` while private): GITHUB_TOKEN can't
307
309
  clone it, so mark dependent tests with
308
310
  `@pytest.mark.skipif(not schemas_dir.exists(), reason="submodule not checked out")`
@@ -674,6 +674,23 @@ def check_commit_cmd(
674
674
  raise typer.Exit(1)
675
675
 
676
676
 
677
+ @app.command(name="check-commits")
678
+ def check_commits_cmd() -> None:
679
+ """Validate the conventional-commit messages in the CI push/PR range.
680
+
681
+ Landing-gate counterpart to `check-commit` (single message, local
682
+ commit-msg hook). Resolves the range from the CI event -- push
683
+ before..after (what lands on main) or PR base..HEAD -- validates each
684
+ commit, and is FATAL on push but ADVISORY on pull_request (branch
685
+ commits may be squashed away). CI-only; a no-op locally. Driven by the
686
+ dedicated `commit-check` workflow job, NOT the run-checks-gated quality
687
+ job -- so a merge to main is validated even when it is not a publish.
688
+ """
689
+ from hyperi_ci.quality.commit_validation import run
690
+
691
+ raise typer.Exit(run())
692
+
693
+
677
694
  def _publish_impl(
678
695
  tag: str | None,
679
696
  list_tags: bool,
@@ -169,22 +169,30 @@ def stage_setup(language: str, config: CIConfig) -> int:
169
169
  return rc
170
170
 
171
171
 
172
- def stage_quality(language: str, config: CIConfig) -> int:
172
+ def stage_quality(language: str, config: CIConfig, *, local: bool = False) -> int:
173
173
  """Quality checks — gitleaks + language-specific checks."""
174
174
  if not config.get("quality.enabled", True):
175
175
  info("Quality checks disabled in configuration")
176
176
  return 0
177
177
 
178
- # Cross-language checks first
178
+ # Cross-language checks first.
179
179
  with group("Gitleaks secret scanning"):
180
180
  rc = gitleaks.run(config)
181
181
  if rc != 0:
182
182
  return rc
183
183
 
184
- with group("Commit message validation"):
185
- rc = commit_validation.run(config)
186
- if rc != 0:
187
- return rc
184
+ # Commit-message validation. In CI this is the dedicated `commit-check`
185
+ # workflow job -- it runs on every merge to main (not just the
186
+ # publish-worthy pushes the run-checks-gated quality job covers) and is
187
+ # advisory on PRs. There is no such job for a LOCAL `hyperi-ci check`,
188
+ # so run it here over the unpushed range (origin/main..HEAD) as a
189
+ # pre-push backstop -- catch a bad message before the push, not after.
190
+ # See hyperi_ci.quality.commit_validation.run + CLAUDE.md CI gate doctrine.
191
+ if local:
192
+ with group("Commit message validation"):
193
+ rc = commit_validation.run(config, local=True)
194
+ if rc != 0:
195
+ return rc
188
196
 
189
197
  extra_env: dict[str, str] = {}
190
198
  if language == "rust":
@@ -440,9 +448,11 @@ def run_stage(
440
448
  info(f"Project status: {status}{_STATUS_CLARIFIER.get(status, '')}")
441
449
 
442
450
  handler = _STAGE_HANDLERS[stage]
443
- if stage == "build":
444
- # The build handler takes `local`; the others do not. _STAGE_HANDLERS
445
- # is typed to the common (no-local) signature, so cast for this branch.
451
+ if stage in ("build", "quality"):
452
+ # build + quality take `local` (build skips cross-targets; quality
453
+ # runs the commit-message backstop only for a local `hyperi-ci
454
+ # check`). _STAGE_HANDLERS is typed to the common (no-local)
455
+ # signature, so cast for this branch.
446
456
  rc = cast("Any", handler)(language, config, local=local)
447
457
  else:
448
458
  rc = handler(language, config)
@@ -498,12 +498,35 @@ def _get_commits_to_validate() -> tuple[list[tuple[str, str]], bool]:
498
498
  return [], False
499
499
 
500
500
 
501
- def run(config: CIConfig, extra_env: dict[str, str] | None = None) -> int:
502
- """Validate commit messages in the current branch.
503
-
504
- Only runs inside CI (is_ci() guard). Returns 0 on success, 1 on failure.
501
+ def run(
502
+ config: CIConfig | None = None,
503
+ extra_env: dict[str, str] | None = None,
504
+ *,
505
+ local: bool = False,
506
+ ) -> int:
507
+ """Validate commit messages in the CI push/PR range (or the local range).
508
+
509
+ ``config`` is unused (kept for call-site symmetry) so the CLI
510
+ ``check-commits`` command can call ``run()`` bare.
511
+
512
+ Behaviour by event:
513
+
514
+ - ``push`` (what lands on main) -> FATAL: a failing commit returns 1.
515
+ This is the real landing gate -- the run-checks gate skips the
516
+ quality job on non-publish main pushes, so this dedicated check is
517
+ where merge-to-main enforcement lives.
518
+ - ``pull_request`` -> ADVISORY: failures are warned, returns 0. The
519
+ branch commits validated on a PR may be discarded by a squash-merge
520
+ (only the squash subject lands) and are never re-validated on the
521
+ merge push, so a PR gets feedback rather than a hard red.
522
+
523
+ ``local=True`` runs the same check outside CI (``hyperi-ci check``
524
+ pre-push backstop): with no CI event the range falls back to
525
+ ``origin/main..HEAD`` (the unpushed commits) and is FATAL, so a bad
526
+ message is caught before the push, not after. Without ``local`` the
527
+ check is a no-op outside CI (the commit-msg hook covers authoring).
505
528
  """
506
- if not is_ci():
529
+ if not local and not is_ci():
507
530
  info("Skipping commit message validation (not in CI)")
508
531
  return 0
509
532
 
@@ -543,10 +566,25 @@ def run(config: CIConfig, extra_env: dict[str, str] | None = None) -> int:
543
566
  failures.append((commit_hash, full_msg, result))
544
567
 
545
568
  if failures:
569
+ # Advisory on a PR, fatal on push. See the run() docstring: PR
570
+ # branch commits may be squashed away and are never re-validated
571
+ # on the merge-to-main push, so a PR gets feedback -- not a hard
572
+ # red -- while the push that lands on main stays enforced.
573
+ advisory = os.environ.get("GITHUB_EVENT_NAME", "") == "pull_request"
574
+ emit = warn if advisory else error
546
575
  for commit_hash, full_msg, result in failures:
547
576
  short_hash = commit_hash[:8]
548
577
  rejection = format_rejection(result, full_msg)
549
- error(f"[{short_hash}] {rejection}")
578
+ emit(f"[{short_hash}] {rejection}")
579
+
580
+ if advisory:
581
+ warn(
582
+ f"{len(failures)} commit(s) would fail validation on merge to "
583
+ "main. Advisory on a PR -- only the commit(s) that LAND on main "
584
+ "are enforced. If you squash-merge, make the squash subject a "
585
+ "valid conventional commit (that single line is what lands)."
586
+ )
587
+ return 0
550
588
 
551
589
  error(
552
590
  f"{len(failures)} commit(s) failed validation. "
@@ -649,12 +649,17 @@ class TestRunDegraded:
649
649
  payload = _write_push_event(tmp_path, "0" * 40, "HEAD")
650
650
  monkeypatch.setenv("GITHUB_EVENT_PATH", str(payload))
651
651
  monkeypatch.setattr(cv, "is_ci", lambda: True)
652
+ # Capture the (legitimate) degraded warning so it doesn't leak into
653
+ # CI logs from the test run itself.
654
+ warns: list[str] = []
655
+ monkeypatch.setattr(cv, "warn", lambda m: warns.append(m))
652
656
 
653
657
  from hyperi_ci.config import CIConfig
654
658
 
655
659
  rc = cv.run(CIConfig())
656
660
  # Degraded, but HEAD is invalid -> must FAIL, not silent-pass.
657
661
  assert rc == 1
662
+ assert any("DEGRADED" in w for w in warns) # warns loudly, never silent
658
663
 
659
664
  def test_degraded_head_valid_passes(
660
665
  self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
@@ -666,6 +671,7 @@ class TestRunDegraded:
666
671
  payload = _write_push_event(tmp_path, "0" * 40, "HEAD")
667
672
  monkeypatch.setenv("GITHUB_EVENT_PATH", str(payload))
668
673
  monkeypatch.setattr(cv, "is_ci", lambda: True)
674
+ monkeypatch.setattr(cv, "warn", lambda _m: None) # capture degraded warning
669
675
 
670
676
  from hyperi_ci.config import CIConfig
671
677
 
@@ -673,6 +679,94 @@ class TestRunDegraded:
673
679
  assert rc == 0
674
680
 
675
681
 
682
+ def _write_pr_event(tmp_path: Path, base_sha: str) -> Path:
683
+ payload = tmp_path / "event.json"
684
+ payload.write_text(json.dumps({"pull_request": {"base": {"sha": base_sha}}}))
685
+ return payload
686
+
687
+
688
+ class TestRunAdvisoryOnPR:
689
+ """On a pull_request a failing commit is ADVISORY (warn, exit 0): branch
690
+ commits may be squashed away and are never re-validated on the merge-to-
691
+ main push. The push path stays FATAL (see TestRunDegraded)."""
692
+
693
+ def test_pr_failure_is_advisory(
694
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
695
+ ) -> None:
696
+ repo = _repo(tmp_path)
697
+ base = _commit(repo, "fix: base")
698
+ # GitHub branch-normalised squashy subject -> no valid prefix.
699
+ _git(repo, "commit", "--allow-empty", "-q", "-m", "Feat/bad squashy subject")
700
+ monkeypatch.chdir(repo)
701
+ monkeypatch.setenv("GITHUB_EVENT_NAME", "pull_request")
702
+ monkeypatch.setenv("GITHUB_EVENT_PATH", str(_write_pr_event(tmp_path, base)))
703
+ monkeypatch.setattr(cv, "is_ci", lambda: True)
704
+ warns: list[str] = []
705
+ errors: list[str] = []
706
+ monkeypatch.setattr(cv, "warn", lambda m: warns.append(m))
707
+ monkeypatch.setattr(cv, "error", lambda m: errors.append(m))
708
+
709
+ from hyperi_ci.config import CIConfig
710
+
711
+ rc = cv.run(CIConfig())
712
+ # Advisory: a bad commit must NOT hard-fail a PR...
713
+ assert rc == 0
714
+ # ...but the problem is still surfaced, via warn (not error).
715
+ assert any("would fail validation on merge to main" in w for w in warns)
716
+ assert errors == []
717
+
718
+ def test_pr_all_valid_passes(
719
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
720
+ ) -> None:
721
+ repo = _repo(tmp_path)
722
+ base = _commit(repo, "fix: base")
723
+ _commit(repo, "fix: good change")
724
+ monkeypatch.chdir(repo)
725
+ monkeypatch.setenv("GITHUB_EVENT_NAME", "pull_request")
726
+ monkeypatch.setenv("GITHUB_EVENT_PATH", str(_write_pr_event(tmp_path, base)))
727
+ monkeypatch.setattr(cv, "is_ci", lambda: True)
728
+
729
+ from hyperi_ci.config import CIConfig
730
+
731
+ rc = cv.run(CIConfig())
732
+ assert rc == 0
733
+
734
+
735
+ class TestRunLocal:
736
+ """`local=True` (hyperi-ci check pre-push) validates the unpushed range
737
+ outside CI and is FATAL -- catch a bad message before the push. Without
738
+ it, run() is a no-op outside CI."""
739
+
740
+ def test_local_validates_range_and_is_fatal(
741
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
742
+ ) -> None:
743
+ repo = _repo(tmp_path)
744
+ # Simulate origin/main at the base, one unpushed bad commit on top.
745
+ _commit(repo, "fix: base")
746
+ _git(repo, "branch", "origin/main") # local ref standing in for origin
747
+ _git(repo, "commit", "--allow-empty", "-q", "-m", "Broken No Prefix")
748
+ monkeypatch.chdir(repo)
749
+ monkeypatch.delenv("GITHUB_EVENT_NAME", raising=False)
750
+ monkeypatch.setattr(cv, "is_ci", lambda: False)
751
+ monkeypatch.setattr(cv, "warn", lambda _m: None)
752
+ monkeypatch.setattr(cv, "error", lambda _m: None)
753
+
754
+ rc = cv.run(local=True)
755
+ assert rc == 1 # not in CI, but local=True -> fatal on the bad commit
756
+
757
+ def test_not_local_and_not_ci_is_noop(
758
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
759
+ ) -> None:
760
+ repo = _repo(tmp_path)
761
+ _git(repo, "commit", "--allow-empty", "-q", "-m", "Broken No Prefix")
762
+ monkeypatch.chdir(repo)
763
+ monkeypatch.delenv("GITHUB_EVENT_NAME", raising=False)
764
+ monkeypatch.setattr(cv, "is_ci", lambda: False)
765
+
766
+ rc = cv.run() # no local, not CI -> skip entirely
767
+ assert rc == 0
768
+
769
+
676
770
  def test_is_zero_sha() -> None:
677
771
  assert cv._is_zero_sha("0" * 40) is True
678
772
  assert cv._is_zero_sha("0" * 7) is True
@@ -195,7 +195,11 @@ class TestExpires:
195
195
  assert len(entries) == 1
196
196
  assert entries[0].expires == date.today() + timedelta(days=30)
197
197
 
198
- def test_past_expiry_is_dropped(self) -> None:
198
+ def test_past_expiry_is_dropped(self, monkeypatch: pytest.MonkeyPatch) -> None:
199
+ # Capture the lapse warning so it doesn't leak into CI logs as a false
200
+ # "quality.ignore lapsed" annotation (the drop is asserted; that the
201
+ # warning fires is covered by test_lapsed_entry_is_logged).
202
+ monkeypatch.setattr("hyperi_ci.quality.ignores.warn", lambda _m: None)
199
203
  past = (date.today() - timedelta(days=1)).isoformat()
200
204
  raw = {
201
205
  "quality": {
@@ -252,7 +256,12 @@ class TestExpires:
252
256
  }
253
257
  )
254
258
 
255
- def test_batch_with_expiry_drops_whole_stanza_when_past(self) -> None:
259
+ def test_batch_with_expiry_drops_whole_stanza_when_past(
260
+ self, monkeypatch: pytest.MonkeyPatch
261
+ ) -> None:
262
+ # Capture the lapse warning (see test_past_expiry_is_dropped) so it
263
+ # doesn't surface as a false CI annotation.
264
+ monkeypatch.setattr("hyperi_ci.quality.ignores.warn", lambda _m: None)
256
265
  past = (date.today() - timedelta(days=5)).isoformat()
257
266
  raw = {
258
267
  "quality": {