proj-flow 0.20.2__tar.gz → 0.21.0__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 (173) hide show
  1. {proj_flow-0.20.2 → proj_flow-0.21.0}/CHANGELOG.rst +22 -0
  2. {proj_flow-0.20.2 → proj_flow-0.21.0}/PKG-INFO +17 -7
  3. {proj_flow-0.20.2 → proj_flow-0.21.0}/pyproject.toml +25 -6
  4. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/__init__.py +1 -1
  5. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/arg.py +2 -0
  6. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/env.py +12 -2
  7. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/step.py +13 -2
  8. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/matrix.py +23 -0
  9. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/cmake/presets.py +8 -4
  10. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/cmake/steps.py +9 -3
  11. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/conan/__init__.py +2 -0
  12. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/github/cli.py +3 -0
  13. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/python/rtdocs.py +35 -6
  14. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/flow/configs.py +16 -1
  15. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/list.py +126 -10
  16. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/api.py +1 -0
  17. {proj_flow-0.20.2 → proj_flow-0.21.0}/.flow/config.yaml +0 -0
  18. {proj_flow-0.20.2 → proj_flow-0.21.0}/.gitignore +0 -0
  19. {proj_flow-0.20.2 → proj_flow-0.21.0}/.readthedocs.yaml +0 -0
  20. {proj_flow-0.20.2 → proj_flow-0.21.0}/LICENSE +0 -0
  21. {proj_flow-0.20.2 → proj_flow-0.21.0}/README.md +0 -0
  22. {proj_flow-0.20.2 → proj_flow-0.21.0}/ROADMAP.md +0 -0
  23. {proj_flow-0.20.2 → proj_flow-0.21.0}/actions/bootstrap/action.yml +0 -0
  24. {proj_flow-0.20.2 → proj_flow-0.21.0}/actions/bootstrap/version.py +0 -0
  25. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/__main__.py +0 -0
  26. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/__init__.py +0 -0
  27. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/completers.py +0 -0
  28. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/ctx.py +0 -0
  29. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/init.py +0 -0
  30. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/makefile.py +0 -0
  31. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/api/release.py +0 -0
  32. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/__cmake_version__.py +0 -0
  33. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/__init__.py +0 -0
  34. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/cmd.py +0 -0
  35. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/inspect.py +0 -0
  36. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/name_list.py +0 -0
  37. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/plugins.py +0 -0
  38. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/registry.py +0 -0
  39. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/base/uname.py +0 -0
  40. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/cli/__init__.py +0 -0
  41. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/cli/argument.py +0 -0
  42. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/cli/finder.py +0 -0
  43. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/dependency.py +0 -0
  44. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/__init__.py +0 -0
  45. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/__init__.py +0 -0
  46. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/cmake/__init__.py +0 -0
  47. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/cmake/parser.py +0 -0
  48. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/cmake/project.py +0 -0
  49. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/cplusplus/conan/_conan.py +0 -0
  50. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/github/__init__.py +0 -0
  51. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/github/hosting.py +0 -0
  52. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/github/publishing.py +0 -0
  53. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/github/switches.py +0 -0
  54. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/markdown_changelog.py +0 -0
  55. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/python/__init__.py +0 -0
  56. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/python/steps.py +0 -0
  57. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/python/version.py +0 -0
  58. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/re_structured_changelog.py +0 -0
  59. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/sign/__init__.py +0 -0
  60. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/sign/api.py +0 -0
  61. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/sign/win32.py +0 -0
  62. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/store.py +0 -0
  63. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/tools/__init__.py +0 -0
  64. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/tools/pragma_once.py +0 -0
  65. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/tools/run_linter.py +0 -0
  66. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/__init__.py +0 -0
  67. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/base/__init__.py +0 -0
  68. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/base/config.py +0 -0
  69. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/__init__.py +0 -0
  70. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/cmake.py +0 -0
  71. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/depfile.py +0 -0
  72. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/gen.py +0 -0
  73. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/init.py +0 -0
  74. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/root.py +0 -0
  75. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/cli/updater.py +0 -0
  76. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/init/flow_webidl.cmake +0 -0
  77. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/cmake.mustache +0 -0
  78. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/depfile.mustache +0 -0
  79. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/partials/cxx/attribute-decl.mustache +0 -0
  80. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/partials/cxx/in-out.mustache +0 -0
  81. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/partials/cxx/includes.mustache +0 -0
  82. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/partials/cxx/operation-decl.mustache +0 -0
  83. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/templates/partials/cxx/type.mustache +0 -0
  84. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/data/types/cxx.json +0 -0
  85. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/model/__init__.py +0 -0
  86. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/model/ast.py +0 -0
  87. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/model/builders.py +0 -0
  88. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/ext/webidl/registry.py +0 -0
  89. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/flow/__init__.py +0 -0
  90. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/flow/layer.py +0 -0
  91. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/flow/steps.py +0 -0
  92. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/__init__.py +0 -0
  93. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/commit.py +0 -0
  94. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/error.py +0 -0
  95. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/fmt.py +0 -0
  96. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/format.py +0 -0
  97. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/hosting/__init__.py +0 -0
  98. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/hosting/github.py +0 -0
  99. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/msg.py +0 -0
  100. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/release.py +0 -0
  101. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/rich_text/__init__.py +0 -0
  102. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/rich_text/api.py +0 -0
  103. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/rich_text/markdown.py +0 -0
  104. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/log/rich_text/re_structured_text.py +0 -0
  105. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/__init__.py +0 -0
  106. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/base.py +0 -0
  107. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/bootstrap.py +0 -0
  108. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/ext/bug_report.py +0 -0
  109. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/init.py +0 -0
  110. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/run.py +0 -0
  111. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/minimal/system.py +0 -0
  112. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/__init__.py +0 -0
  113. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/cplusplus/__init__.py +0 -0
  114. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/cplusplus/cmake_context.py +0 -0
  115. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/cplusplus/conan_context.py +0 -0
  116. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/cplusplus/project.py +0 -0
  117. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/data.py +0 -0
  118. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/project/interact.py +0 -0
  119. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.clang-format +0 -0
  120. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.flow/config.yml +0 -0
  121. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.flow/flow.py.mustache +0 -0
  122. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.flow/matrix.yml +0 -0
  123. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.flow/official.yml +0 -0
  124. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/.gitignore +0 -0
  125. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/README.md.mustache +0 -0
  126. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/flow +0 -0
  127. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base/flow.cmd +0 -0
  128. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/base.json +0 -0
  129. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/cmake/common.cmake.mustache +0 -0
  130. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/__init__.py.mustache +0 -0
  131. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/icons/__init__.py.mustache +0 -0
  132. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/extensions/wall/icons/magick.py.mustache +0 -0
  133. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/base.cmake.mustache +0 -0
  134. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/config.cmake +0 -0
  135. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/cpack.cmake +0 -0
  136. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix/cpack.cmake.mustache +0 -0
  137. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix/patches.in.wix +0 -0
  138. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/.flow/packages/wix.cmake.mustache +0 -0
  139. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/CMakeGraphVizOptions.cmake +0 -0
  140. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/CMakeLists.txt.mustache +0 -0
  141. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/CMakePresets.json +0 -0
  142. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/assets/appicon.ico +0 -0
  143. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/assets/appicon.png +0 -0
  144. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/assets/wix_banner.bmp +0 -0
  145. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/assets/wix_dialog.bmp +0 -0
  146. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/icons/.gitignore +0 -0
  147. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/icons/appicon-mask.svg +0 -0
  148. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/data/icons/appicon.svg +0 -0
  149. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/src/main.cc.mustache +0 -0
  150. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/src/version.hpp.in.mustache +0 -0
  151. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake/tests/test.cc.mustache +0 -0
  152. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/cmake.json +0 -0
  153. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/conan/.flow/cmake/libcxx_toolchain.cmake +0 -0
  154. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/conan/.flow/cmake/output_dirs_setup.cmake +0 -0
  155. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/conan/conanfile.txt +0 -0
  156. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/conan.json +0 -0
  157. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/.github/linters/.isort.cfg +0 -0
  158. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/.github/linters/.mypy.ini +0 -0
  159. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/.github/workflows/build.yml.auto-release +0 -0
  160. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/.github/workflows/build.yml.basic +0 -0
  161. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/.github/workflows/linter.yml +0 -0
  162. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions/CPPLINT.cfg +0 -0
  163. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_actions.json +0 -0
  164. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/bug_report.md.mustache +0 -0
  165. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_social/.github/ISSUE_TEMPLATE/feature_request.md.mustache +0 -0
  166. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_social/CODE_OF_CONDUCT.md.mustache +0 -0
  167. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_social/CONTRIBUTING.md +0 -0
  168. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/layers/github_social.json +0 -0
  169. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/licenses/0BSD.mustache +0 -0
  170. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/licenses/MIT.mustache +0 -0
  171. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/licenses/Unlicense.mustache +0 -0
  172. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/licenses/WTFPL.mustache +0 -0
  173. {proj_flow-0.20.2 → proj_flow-0.21.0}/src/proj_flow/template/licenses/Zlib.mustache +0 -0
@@ -4,6 +4,28 @@ Changelog
4
4
 
5
5
  All notable changes to this project will be documented in this file.
6
6
 
7
+ `0.21.0 <https://github.com/mzdun/proj-flow/compare/v0.20.3...v0.21.0>`_ (2026-02-03)
8
+ =====================================================================================
9
+
10
+ New Features
11
+ ------------
12
+
13
+ - recursively list known commands (`991b120 <https://github.com/mzdun/proj-flow/commit/991b120df9da90fee9047d440c2d0cc133ce740c>`_)
14
+ - **list**: wrap cmd help around terminal width (`64bd30c <https://github.com/mzdun/proj-flow/commit/64bd30cfd3c05da92be705a436f5d9cfcad14630>`_)
15
+
16
+ Bug Fixes
17
+ ---------
18
+
19
+ - **docs**: touchup on documentation (`4c374be <https://github.com/mzdun/proj-flow/commit/4c374be954f8af5597ba6a25f13cd332bf7bcc5e>`_)
20
+
21
+ `0.20.3 <https://github.com/mzdun/proj-flow/compare/v0.20.2...v0.20.3>`_ (2026-01-30)
22
+ =====================================================================================
23
+
24
+ Bug Fixes
25
+ ---------
26
+
27
+ - allow postproc includes (`07ecd17 <https://github.com/mzdun/proj-flow/commit/07ecd1770170ffb90150d1bcf622e625c39d3f50>`_)
28
+
7
29
  `0.20.2 <https://github.com/mzdun/proj-flow/compare/v0.20.1...v0.20.2>`_ (2026-01-21)
8
30
  =====================================================================================
9
31
 
@@ -1,12 +1,13 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proj-flow
3
- Version: 0.20.2
3
+ Version: 0.21.0
4
4
  Summary: C++ project maintenance, automated
5
5
  Project-URL: Changelog, https://github.com/mzdun/proj-flow/blob/main/CHANGELOG.rst
6
6
  Project-URL: Documentation, https://proj-flow.readthedocs.io/en/latest/
7
7
  Project-URL: Homepage, https://pypi.org/project/proj-flow/
8
8
  Project-URL: Source Code, https://github.com/mzdun/proj-flow
9
9
  Author-email: Marcin Zdun <marcin.zdun@gmail.com>
10
+ License-Expression: MIT
10
11
  License-File: LICENSE
11
12
  Keywords: C/C++,build-tool,c++,ci-cd,continuous-integration,cpp,dependencies,dependency-manager,developer,developer-tools,development,meta-build-tool,pipeline,tools-and-automation
12
13
  Classifier: Development Status :: 4 - Beta
@@ -17,13 +18,22 @@ Classifier: Programming Language :: Python :: 3
17
18
  Classifier: Programming Language :: Python :: 3.10
18
19
  Classifier: Topic :: Software Development :: Build Tools
19
20
  Requires-Python: >=3.10
20
- Requires-Dist: argcomplete
21
+ Requires-Dist: argcomplete~=3.5
21
22
  Requires-Dist: chevron2021
22
- Requires-Dist: prompt-toolkit
23
- Requires-Dist: pywebidl2
24
- Requires-Dist: pyyaml
25
- Requires-Dist: requests-cache
26
- Requires-Dist: toml
23
+ Requires-Dist: prompt-toolkit~=3.0
24
+ Requires-Dist: pywebidl2~=0.1
25
+ Requires-Dist: pyyaml~=6.0
26
+ Requires-Dist: requests-cache~=1.3
27
+ Requires-Dist: toml~=0.10
28
+ Provides-Extra: dev
29
+ Requires-Dist: black~=25.0; extra == 'dev'
30
+ Requires-Dist: build~=1.4; extra == 'dev'
31
+ Requires-Dist: isort~=6.0; extra == 'dev'
32
+ Requires-Dist: sphinx~=9.1; extra == 'dev'
33
+ Requires-Dist: twine<=6.0.1; extra == 'dev'
34
+ Requires-Dist: types-chevron~=0.14; extra == 'dev'
35
+ Requires-Dist: types-pyyaml~=6.0; extra == 'dev'
36
+ Requires-Dist: types-toml~=0.10; extra == 'dev'
27
37
  Description-Content-Type: text/markdown
28
38
 
29
39
  # Project Flow
@@ -23,13 +23,13 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.10",
24
24
  ]
25
25
  dependencies = [
26
- "argcomplete",
26
+ "argcomplete~=3.5",
27
27
  "chevron2021",
28
- "prompt_toolkit",
29
- "PyYAML",
30
- "toml",
31
- "pywebidl2",
32
- "requests_cache",
28
+ "prompt_toolkit~=3.0",
29
+ "PyYAML~=6.0",
30
+ "toml~=0.10",
31
+ "pywebidl2~=0.1",
32
+ "requests-cache~=1.3",
33
33
  ]
34
34
  description = "C++ project maintenance, automated"
35
35
  dynamic = ["version"]
@@ -49,6 +49,7 @@ keywords = [
49
49
  'pipeline',
50
50
  'tools-and-automation',
51
51
  ]
52
+ license = "MIT"
52
53
  name = "proj-flow"
53
54
  readme = "README.md"
54
55
  requires-python = ">=3.10"
@@ -61,3 +62,21 @@ Homepage = "https://pypi.org/project/proj-flow/"
61
62
 
62
63
  [project.scripts]
63
64
  proj-flow = "proj_flow.cli:main"
65
+
66
+ [project.optional-dependencies]
67
+ dev = [
68
+ "build~=1.4",
69
+ "twine<=6.0.1",
70
+ "sphinx~=9.1",
71
+ "black~=25.0",
72
+ "isort~=6.0",
73
+ "types-PyYAML~=6.0",
74
+ "types-chevron~=0.14",
75
+ "types-toml~=0.10",
76
+ ]
77
+
78
+ [tool.black]
79
+ target-version = ["py310", "py311", "py312", "py313"]
80
+
81
+ [tool.isort]
82
+ profile = "black"
@@ -6,4 +6,4 @@ The **proj_flow** contains only ``__version__`` to be updated, nothing more.
6
6
  This is in an attempt to make this module easy to load initially.
7
7
  """
8
8
 
9
- __version__ = "0.20.2"
9
+ __version__ = "0.21.0"
@@ -159,6 +159,8 @@ def command(*name: str):
159
159
  doc = orig_doc or ""
160
160
  if doc:
161
161
  doc += "\n\n"
162
+ doc += f":call: ``proj-flow {' '.join(name)}``\n\n"
163
+ doc += f":call: ``./flow {' '.join(name)}``\n\n"
162
164
 
163
165
  for arg in _inspect.signature(entry):
164
166
  help = ""
@@ -173,12 +173,18 @@ def load_extensions(extensions: List[str]):
173
173
  for extension in extensions:
174
174
  try:
175
175
  importlib.import_module(extension)
176
- except ModuleNotFoundError:
176
+ except ImportError as ex:
177
177
  print(
178
- f"-- error: module `{extension}` was no found, ignoring",
178
+ f"-- error: loading module `{extension}` resulted in import error: {ex}",
179
+ file=sys.stderr,
180
+ )
181
+ except BaseException as ex:
182
+ print(
183
+ f"-- error: there was an error while loading module `{extension}`: {ex}",
179
184
  file=sys.stderr,
180
185
  )
181
186
 
187
+
182
188
  class FlowConfig:
183
189
  _cfg: dict
184
190
  steps: list = []
@@ -253,6 +259,10 @@ class FlowConfig:
253
259
  def postproc_exclude(self) -> List[dict]:
254
260
  return self.postproc.get("exclude", [])
255
261
 
262
+ @property
263
+ def postproc_include(self) -> List[dict]:
264
+ return self.postproc.get("include", [])
265
+
256
266
  @property
257
267
  def shortcuts(self) -> Dict[str, dict]:
258
268
  return self._cfg.get("shortcuts", {})
@@ -87,8 +87,10 @@ def _register_step(step: Step, replace: bool):
87
87
  return
88
88
 
89
89
  if "READTHEDOCS" not in os.environ:
90
- raise NameError(f"Step {name} is marked as replacing, but there is no previous step with that name")
91
-
90
+ raise NameError(
91
+ f"Step {name} is marked as replacing, but there is no previous step with that name"
92
+ )
93
+
92
94
  if name in [step.name for step in __steps]:
93
95
  if "READTHEDOCS" not in os.environ:
94
96
  raise NameError(f"Step {name} already registered")
@@ -133,6 +135,12 @@ def _make_private(f: _inspect.Function):
133
135
  f.__doc__ = ":meta private:\n"
134
136
 
135
137
 
138
+ def _make_private_property(f):
139
+ if isinstance(f, property):
140
+ _make_private(cast(_inspect.Function, f))
141
+ return
142
+
143
+
136
144
  _dummy_config = Config(
137
145
  {
138
146
  "os": "${os}",
@@ -163,6 +171,9 @@ def _extend_docstring(conv, step: Step):
163
171
  doc = conv.__doc__ or "*Docstring is missing!*"
164
172
  conv.__doc__ = f"{doc}\n{info}"
165
173
 
174
+ _make_private_property(conv.name)
175
+ _make_private_property(conv.runs_before)
176
+ _make_private_property(conv.runs_after)
166
177
  _make_private(conv.is_active)
167
178
  _make_private(conv.run)
168
179
  _make_private(conv.platform_dependencies)
@@ -86,6 +86,29 @@ def matches(tested: dict, test: dict) -> bool:
86
86
  return True
87
87
 
88
88
 
89
+ def partially_matches(tested: dict, test: dict) -> bool:
90
+ """
91
+ Checks, if the tested dictionary contains some of the values from test
92
+ dictionary, with non-zero intersection between both dictionaries.
93
+
94
+ :param tested: Dictionary to check
95
+ :param test: Dictionary to check against
96
+
97
+ :returns: `True`, if all keys from `test` are in `tested` and have the same
98
+ values, `False` otherwise.
99
+ """
100
+
101
+ intersection_size = 0
102
+ for key, value in test.items():
103
+ if key not in tested:
104
+ continue
105
+ val = tested.get(key)
106
+ if val != value:
107
+ return False
108
+ intersection_size += 1
109
+ return intersection_size > 0
110
+
111
+
89
112
  def matches_any(tested: dict, tests: List[dict]):
90
113
  """
91
114
  Checks, if the tested dictionary contains all the values from at least one
@@ -135,10 +135,14 @@ class Presets:
135
135
 
136
136
  @staticmethod
137
137
  def __load_file(filename: Path):
138
- with open(filename, encoding="UTF-8") as f:
139
- data = json.load(f)
140
- includes = cast(list[str], data.get("include", []))
141
- presets = cast(list[dict], data.get("configurePresets", []))
138
+ try:
139
+ with open(filename, encoding="UTF-8") as f:
140
+ data = json.load(f)
141
+ includes = cast(list[str], data.get("include", []))
142
+ presets = cast(list[dict], data.get("configurePresets", []))
143
+ except FileNotFoundError:
144
+ includes: list[str] = []
145
+ presets: list[dict] = []
142
146
 
143
147
  return (includes, presets)
144
148
 
@@ -22,14 +22,17 @@ class CMakeBase(api.step.Step):
22
22
 
23
23
  @property
24
24
  def name(self):
25
+ """:meta private:"""
25
26
  return self._name
26
27
 
27
28
  @property
28
29
  def runs_after(self):
30
+ """:meta private:"""
29
31
  return self._runs_after
30
32
 
31
33
  @property
32
34
  def runs_before(self):
35
+ """:meta private:"""
33
36
  return self._runs_before
34
37
 
35
38
  def __init__(
@@ -47,6 +50,7 @@ class CMakeBase(api.step.Step):
47
50
  return [f"cmake>={CMAKE_VERSION}"]
48
51
 
49
52
  def dep_with_tool(self, tool: str):
53
+ """:meta private:"""
50
54
  return [f"cmake>={CMAKE_VERSION}", f"{tool}>={CMAKE_VERSION}"]
51
55
 
52
56
 
@@ -64,7 +68,9 @@ class CMakeConfig(CMakeBase):
64
68
  def directories_to_remove(self, config: env.Config) -> List[str]:
65
69
  binary_dir = self.binary_dirs.get(f"{config.preset}-{config.build_generator}")
66
70
  if not binary_dir:
67
- return []
71
+ if "READTHEDOCS" not in os.environ:
72
+ return []
73
+ return [f"build/{config.preset}"]
68
74
  return [binary_dir]
69
75
 
70
76
  def run(self, config: env.Config, rt: env.Runtime) -> int:
@@ -78,10 +84,10 @@ class CMakeConfig(CMakeBase):
78
84
  value = value[1:]
79
85
 
80
86
  if value.startswith("config:"):
81
- value = value[len("config:"):]
87
+ value = value[len("config:") :]
82
88
  value = config.get_path(value)
83
89
  elif value.startswith("runtime:"):
84
- value = value[len("runtime:"):]
90
+ value = value[len("runtime:") :]
85
91
  value = getattr(rt, value, None)
86
92
 
87
93
  if is_flag:
@@ -23,7 +23,9 @@ CONAN_PROFILE_GEN = "_profile-build_type"
23
23
  class ConanConfig:
24
24
  """Configures the project for ``preset`` config using ``build_type`` config."""
25
25
 
26
+ #: :meta private:
26
27
  name = "Conan"
28
+ #: :meta private:
27
29
  runs_before = ["CMake"]
28
30
 
29
31
  def platform_dependencies(self):
@@ -32,6 +32,7 @@ def github():
32
32
 
33
33
  @arg.command("github", "matrix")
34
34
  def matrix(
35
+ pretty: typing.Annotated[bool, arg.FlagArgument(help="Indent JSON document")],
35
36
  official: typing.Annotated[
36
37
  bool, arg.FlagArgument(help="Cut matrix to release builds only")
37
38
  ],
@@ -60,6 +61,8 @@ def matrix(
60
61
  print(f"matrix={var}", file=github_output)
61
62
  else:
62
63
  print(f"matrix={var}")
64
+ elif pretty:
65
+ json.dump(usable, sys.stdout, indent=2)
63
66
  else:
64
67
  json.dump(usable, sys.stdout)
65
68
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  """
5
5
  The **proj_flow.ext.python.rtdocs** defines RTDocs step (`"RTD"`), which uses
6
- .readthedocs.yaml to build the HTML documentation.
6
+ ``.readthedocs.yaml`` to build the HTML documentation.
7
7
  """
8
8
 
9
9
  import os
@@ -20,7 +20,11 @@ from proj_flow.base import cmd
20
20
 
21
21
  @step.register
22
22
  class RTDocs:
23
- name = "RTD"
23
+ """Runs the jobs defined by ``.readthedocs.yaml`` to build the docs"""
24
+
25
+ @property
26
+ def name(self):
27
+ return "RTD"
24
28
 
25
29
  def platform_dependencies(self):
26
30
  return ["python -m PyYAML"]
@@ -100,6 +104,8 @@ class RTDocs:
100
104
 
101
105
 
102
106
  class Builder(ABC):
107
+ """Base class for any recognized builder in the ``.readthedocs.yaml`` config."""
108
+
103
109
  @property
104
110
  @abstractmethod
105
111
  def READTHEDOCS_OUTPUT(self) -> str: ...
@@ -112,17 +118,40 @@ class Builder(ABC):
112
118
 
113
119
 
114
120
  class Sphinx(Builder):
115
- READTHEDOCS_OUTPUT: str = ""
121
+ """Builder used, if Sphinx config is found in the config file.
122
+
123
+ :param config: filename of a Python script Sphinx should use for the configuration
124
+ """
125
+
126
+ @property
127
+ def READTHEDOCS_OUTPUT(self) -> str:
128
+ """A ``build/`` subdirectory placed in the same dir, as ``config`` parameter"""
129
+ return self.output
116
130
 
117
131
  def __init__(self, config: str):
118
132
  self.config = config
119
133
  self.source = os.path.dirname(config)
120
- self.READTHEDOCS_OUTPUT = os.path.join(os.path.dirname(self.source), "build")
134
+ self.output = os.path.join(os.path.dirname(self.source), "build")
121
135
 
122
- def build(self, target: str):
136
+ def build(self, target: str) -> int:
137
+ """Uses ``spinx-build`` to create the documentation.
138
+
139
+ :param target: name of the docs format from YAML config
140
+ :returns: exit code forwarded from the build tool
141
+ """
123
142
  builder = "latex" if target == "pdf" else target
143
+ print(shutil.which("sphinx-build"))
144
+
124
145
  return subprocess.run(
125
- ["sphinx-build", "-M", builder, self.source, self.READTHEDOCS_OUTPUT],
146
+ [
147
+ PYTHON_EXECUTABLE,
148
+ "-m",
149
+ "sphinx.cmd.build",
150
+ "-M",
151
+ builder,
152
+ self.source,
153
+ self.READTHEDOCS_OUTPUT,
154
+ ],
126
155
  shell=False,
127
156
  ).returncode
128
157
 
@@ -9,6 +9,7 @@ using ``-D`` switches.
9
9
 
10
10
 
11
11
  import argparse
12
+ import copy
12
13
  import datetime
13
14
  import os
14
15
  import sys
@@ -152,6 +153,19 @@ def _load_flow_data(rt: env.Runtime):
152
153
  return configs, keys
153
154
 
154
155
 
156
+ def _apply_postproc_includes(config: dict, postproc_include: List[dict]):
157
+ clone = copy.deepcopy(config)
158
+ for ext in postproc_include:
159
+ if not matrix.partially_matches(config, ext):
160
+ continue
161
+
162
+ for key, value in ext.items():
163
+ if key in config:
164
+ continue
165
+ clone[key] = value
166
+ return clone
167
+
168
+
155
169
  class Configs:
156
170
  usable: List[env.Config] = []
157
171
 
@@ -186,8 +200,9 @@ class Configs:
186
200
  )
187
201
 
188
202
  postproc_exclude = rt.postproc_exclude
203
+ postproc_include = rt.postproc_include
189
204
  usable = [
190
- config
205
+ _apply_postproc_includes(config, postproc_include)
191
206
  for config in turned
192
207
  if len(postproc_exclude) == 0
193
208
  or not matrix.matches_any(config, postproc_exclude)
@@ -6,12 +6,20 @@ The **proj_flow.minimal.list** implements ``./flow list`` command.
6
6
  """
7
7
 
8
8
  import os
9
- from typing import Annotated, Dict, List, Set, cast
9
+ import re
10
+ import sys
11
+ from typing import Annotated, Dict, Iterable, List, Set, cast
10
12
 
11
13
  from proj_flow import cli
12
14
  from proj_flow.api import arg, env, step
13
15
  from proj_flow.base import matrix
14
16
 
17
+ if sys.platform == "win32":
18
+ import ctypes
19
+ import ctypes.wintypes
20
+ else:
21
+ import termios
22
+
15
23
 
16
24
  @arg.command("list")
17
25
  def main(
@@ -39,10 +47,7 @@ def main(
39
47
  configs = True
40
48
 
41
49
  if builtin:
42
- root = menu
43
- while root.parent is not None:
44
- root = root.parent
45
- builtin_entries = list(sorted((cmd.name, cmd.doc) for cmd in root.children))
50
+ builtin_entries = list(sorted(_walk_menu(menu)))
46
51
  if not pipe and len(builtin_entries) > 0:
47
52
  print("Builtin commands")
48
53
  print("----------------")
@@ -54,7 +59,10 @@ def main(
54
59
 
55
60
  name = f"{bold}{entry_name}{reset}"
56
61
  if entry_doc:
57
- print(f"- {name}: {entry_doc}")
62
+ print(f"- {name}:", end=" ")
63
+ _write_console_para(
64
+ " ".join(para.split("\n")) for para in entry_doc.split("\n\n")
65
+ )
58
66
  else:
59
67
  print(f"- {name}")
60
68
 
@@ -76,7 +84,8 @@ def main(
76
84
  continue
77
85
 
78
86
  name = f"{bold}{run_alias.name}{reset}"
79
- print(f"- {name}: {', '.join(run_alias.steps)}")
87
+ print(f"- {name}:", end=" ")
88
+ _write_console_para([", ".join(run_alias.steps)])
80
89
 
81
90
  printed_something = True
82
91
 
@@ -109,8 +118,10 @@ def main(
109
118
  print(f"- {name}*")
110
119
 
111
120
  if some_unused:
112
- print(
113
- f"*step can only be run by explicitly calling through {bold}run{reset}."
121
+ _write_console_para(
122
+ [
123
+ f"*step can only be run by explicitly calling through {bold}run{reset}."
124
+ ]
114
125
  )
115
126
 
116
127
  printed_something = True
@@ -147,7 +158,8 @@ def main(
147
158
  value = ", ".join(values.get(key, empty))
148
159
  name = f"{bold}{key}{reset}"
149
160
  if value:
150
- print(f"- {name}: {value}")
161
+ print(f"- {name}:", end=" ")
162
+ _write_console_para([value])
151
163
  else:
152
164
  print(f"- {name}")
153
165
 
@@ -157,6 +169,110 @@ def main(
157
169
  print(f"Use {bold}--help{reset} to see, which listings are available")
158
170
 
159
171
 
172
+ def _iterate_levels(menu: cli.argument.Command, prefix: str):
173
+ yield [(f"{prefix}{cmd.name}", cmd.doc) for cmd in menu.children]
174
+ for cmd in menu.children:
175
+ child_prefix = f"{prefix}{cmd.name} "
176
+ for layer in _iterate_levels(cmd, child_prefix):
177
+ yield layer
178
+
179
+
180
+ def _walk_menu(menu: cli.argument.Command):
181
+ root = menu
182
+ while root.parent is not None:
183
+ root = root.parent
184
+
185
+ items: list[tuple[str, str]] = []
186
+ for layer in _iterate_levels(root, ""):
187
+ items.extend(layer)
188
+
189
+ return items
190
+
191
+
192
+ def _cursor_pos():
193
+ if sys.platform == "win32":
194
+ old_stdin_mode = ctypes.wintypes.DWORD()
195
+ old_stdout_mode = ctypes.wintypes.DWORD()
196
+ kernel32 = ctypes.windll.kernel32
197
+ kernel32.GetConsoleMode(
198
+ kernel32.GetStdHandle(-10), ctypes.byref(old_stdin_mode)
199
+ )
200
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), 0)
201
+ kernel32.GetConsoleMode(
202
+ kernel32.GetStdHandle(-11), ctypes.byref(old_stdout_mode)
203
+ )
204
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
205
+ else:
206
+ old_stdin_mode = termios.tcgetattr(sys.stdin)
207
+ _ = termios.tcgetattr(sys.stdin)
208
+ _[3] = _[3] & ~(termios.ECHO | termios.ICANON)
209
+ termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, _)
210
+ try:
211
+ _ = ""
212
+ sys.stdout.write("\x1b[6n")
213
+ sys.stdout.flush()
214
+ while not (_ := _ + sys.stdin.read(1)).endswith("R"):
215
+ pass
216
+ res = re.match(r".*\[(?P<y>\d*);(?P<x>\d*)R", _)
217
+ finally:
218
+ if sys.platform == "win32":
219
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-10), old_stdin_mode)
220
+ kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), old_stdout_mode)
221
+ else:
222
+ termios.tcsetattr(sys.stdin, termios.TCSAFLUSH, old_stdin_mode)
223
+ if res:
224
+ return (int(res.group("x")), int(res.group("y")))
225
+ return (1, 1)
226
+
227
+
228
+ def _write_console_para(text: Iterable[str]):
229
+ term_width = os.get_terminal_size().columns
230
+ margin = min(_cursor_pos()[0], term_width // 2) - 1
231
+ width = term_width - 1
232
+ pos = margin
233
+ for para in text:
234
+ for word in para.split():
235
+ if not word:
236
+ continue
237
+ word_len = len(word)
238
+ next_pos = pos + word_len + 1
239
+
240
+ if next_pos >= width:
241
+ orig = pos
242
+ if orig == margin:
243
+ pos = margin - word_len
244
+ print(word, end="")
245
+
246
+ print()
247
+ print(" " * margin, end="")
248
+
249
+ if orig != margin:
250
+ pos = margin
251
+ print(word, end=" ")
252
+ else:
253
+ print(word, end=" ")
254
+
255
+ pos += word_len + 1
256
+
257
+ continue
258
+ if (word_len + 1) > term_width:
259
+ first_word = pos == margin
260
+ if first_word:
261
+ print(word, end="")
262
+
263
+ print(f"\n{' ' * margin}", end="")
264
+ pos = margin
265
+
266
+ if not first_word:
267
+ print(word, "", end="")
268
+ pos += word_len + 1
269
+ continue
270
+
271
+ print(word, "", end="")
272
+ pos += word_len + 1
273
+ print()
274
+
275
+
160
276
  def _load_flow_data(rt: env.Runtime):
161
277
  paths = [os.path.join(".flow", "matrix.yml")]
162
278
  m, keys = matrix.load_matrix(*paths)
@@ -67,5 +67,6 @@ def get_project_type(id: str):
67
67
  raise ProjectNotFound(id)
68
68
  return result
69
69
 
70
+
70
71
  def load_common_init_setting_extensions():
71
72
  env.load_extensions(["proj_flow.ext.github.switches"])
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes