calkit-python 0.41.6__tar.gz → 0.41.8__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 (339) hide show
  1. {calkit_python-0.41.6 → calkit_python-0.41.8}/.gitignore +1 -0
  2. {calkit_python-0.41.6 → calkit_python-0.41.8}/AGENTS.md +7 -0
  3. {calkit_python-0.41.6 → calkit_python-0.41.8}/PKG-INFO +1 -1
  4. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/main/core.py +10 -1
  5. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/new.py +80 -47
  6. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/overleaf.py +12 -7
  7. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/core.py +9 -0
  8. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/invenio.py +20 -0
  9. calkit_python-0.41.8/calkit/licenses.py +211 -0
  10. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/overleaf.py +215 -79
  11. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/pipeline.py +126 -3
  12. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/releases.py +129 -29
  13. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/main/test_subprojects.py +53 -0
  14. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_new.py +427 -13
  15. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_overleaf.py +128 -4
  16. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_core.py +19 -0
  17. calkit_python-0.41.8/calkit/tests/test_invenio.py +35 -0
  18. calkit_python-0.41.8/calkit/tests/test_licenses.py +105 -0
  19. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_pipeline.py +111 -0
  20. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_releases.py +118 -0
  21. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/cli-reference.md +16 -16
  22. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/overleaf.md +92 -0
  23. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/releases.md +21 -0
  24. calkit_python-0.41.6/calkit/licenses.py +0 -59
  25. calkit_python-0.41.6/calkit/tests/test_invenio.py +0 -15
  26. {calkit_python-0.41.6 → calkit_python-0.41.8}/.claude-plugin/marketplace.json +0 -0
  27. {calkit_python-0.41.6 → calkit_python-0.41.8}/.gitattributes +0 -0
  28. {calkit_python-0.41.6 → calkit_python-0.41.8}/.pre-commit-config.yaml +0 -0
  29. {calkit_python-0.41.6 → calkit_python-0.41.8}/.prettierignore +0 -0
  30. {calkit_python-0.41.6 → calkit_python-0.41.8}/.python-version +0 -0
  31. {calkit_python-0.41.6 → calkit_python-0.41.8}/.vscode/launch.json +0 -0
  32. {calkit_python-0.41.6 → calkit_python-0.41.8}/.vscode/tasks.json +0 -0
  33. {calkit_python-0.41.6 → calkit_python-0.41.8}/.yarnrc.yml +0 -0
  34. {calkit_python-0.41.6 → calkit_python-0.41.8}/CITATION.cff +0 -0
  35. {calkit_python-0.41.6 → calkit_python-0.41.8}/CODE_OF_CONDUCT.md +0 -0
  36. {calkit_python-0.41.6 → calkit_python-0.41.8}/CONTRIBUTING.md +0 -0
  37. {calkit_python-0.41.6 → calkit_python-0.41.8}/LICENSE +0 -0
  38. {calkit_python-0.41.6 → calkit_python-0.41.8}/Makefile +0 -0
  39. {calkit_python-0.41.6 → calkit_python-0.41.8}/README.md +0 -0
  40. {calkit_python-0.41.6 → calkit_python-0.41.8}/agent-plugin/.claude-plugin/plugin.json +0 -0
  41. {calkit_python-0.41.6 → calkit_python-0.41.8}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
  42. {calkit_python-0.41.6 → calkit_python-0.41.8}/agent-plugin/skills/conventions/SKILL.md +0 -0
  43. {calkit_python-0.41.6 → calkit_python-0.41.8}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
  44. {calkit_python-0.41.6 → calkit_python-0.41.8}/babel.config.js +0 -0
  45. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/__init__.py +0 -0
  46. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/__main__.py +0 -0
  47. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/calc.py +0 -0
  48. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/check.py +0 -0
  49. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/__init__.py +0 -0
  50. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/check.py +0 -0
  51. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/cloud.py +0 -0
  52. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/config.py +0 -0
  53. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/core.py +0 -0
  54. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/describe.py +0 -0
  55. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/dev.py +0 -0
  56. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/import_.py +0 -0
  57. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/latex.py +0 -0
  58. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/list.py +0 -0
  59. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/main/__init__.py +0 -0
  60. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/main/xr.py +0 -0
  61. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/notebooks.py +0 -0
  62. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/office.py +0 -0
  63. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/scheduler.py +0 -0
  64. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cli/update.py +0 -0
  65. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/cloud.py +0 -0
  66. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/conda.py +0 -0
  67. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/config.py +0 -0
  68. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/datasets.py +0 -0
  69. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/dependencies.py +0 -0
  70. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/detect.py +0 -0
  71. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/docker.py +0 -0
  72. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/dvc/__init__.py +0 -0
  73. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/dvc/core.py +0 -0
  74. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/dvc/zip.py +0 -0
  75. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/environments.py +0 -0
  76. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/fs.py +0 -0
  77. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/git.py +0 -0
  78. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/github.py +0 -0
  79. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/gui.py +0 -0
  80. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/install.py +0 -0
  81. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/julia.py +0 -0
  82. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/jupyter.py +0 -0
  83. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/jupyterlab/__init__.py +0 -0
  84. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/jupyterlab/routes.py +0 -0
  85. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/package.json +0 -0
  86. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  87. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  88. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  89. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  90. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  91. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  92. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  93. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  94. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  95. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/remoteEntry.65469af996e7a96aa983.js +0 -0
  96. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/style.js +0 -0
  97. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/labextension/static/third-party-licenses.json +0 -0
  98. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/magics.py +0 -0
  99. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/matlab.py +0 -0
  100. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/models/__init__.py +0 -0
  101. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/models/core.py +0 -0
  102. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/models/io.py +0 -0
  103. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/models/iteration.py +0 -0
  104. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/models/pipeline.py +0 -0
  105. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/notebooks.py +0 -0
  106. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/office.py +0 -0
  107. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/ops.py +0 -0
  108. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/server.py +0 -0
  109. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/__init__.py +0 -0
  110. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/core.py +0 -0
  111. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/__init__.py +0 -0
  112. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/article/paper.tex +0 -0
  113. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/core.py +0 -0
  114. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/jfm/jfm.bst +0 -0
  115. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/jfm/jfm.cls +0 -0
  116. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  117. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/jfm/paper.tex +0 -0
  118. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/templates/latex/jfm/upmath.sty +0 -0
  119. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/__init__.py +0 -0
  120. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/__init__.py +0 -0
  121. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/main/__init__.py +0 -0
  122. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/main/test_core.py +0 -0
  123. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/main/test_xr.py +0 -0
  124. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_check.py +0 -0
  125. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_cloud.py +0 -0
  126. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_config.py +0 -0
  127. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_import.py +0 -0
  128. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_latex.py +0 -0
  129. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_list.py +0 -0
  130. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_notebooks.py +0 -0
  131. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_scheduler.py +0 -0
  132. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/cli/test_update.py +0 -0
  133. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/dvc/__init__.py +0 -0
  134. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/dvc/test_core.py +0 -0
  135. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/dvc/test_zip.py +0 -0
  136. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/jupyterlab/__init__.py +0 -0
  137. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/jupyterlab/test_routes.py +0 -0
  138. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/models/__init__.py +0 -0
  139. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/models/test_iteration.py +0 -0
  140. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/models/test_pipeline.py +0 -0
  141. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_calc.py +0 -0
  142. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_check.py +0 -0
  143. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_cloud.py +0 -0
  144. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_conda.py +0 -0
  145. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_dependencies.py +0 -0
  146. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_detect.py +0 -0
  147. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_docker.py +0 -0
  148. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_environments.py +0 -0
  149. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_fs.py +0 -0
  150. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_git.py +0 -0
  151. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_install.py +0 -0
  152. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_julia.py +0 -0
  153. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_jupyter.py +0 -0
  154. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_magics.py +0 -0
  155. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_matlab.py +0 -0
  156. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_notebooks.py +0 -0
  157. {calkit_python-0.41.6 → calkit_python-0.41.8}/calkit/tests/test_templates.py +0 -0
  158. {calkit_python-0.41.6 → calkit_python-0.41.8}/conftest.py +0 -0
  159. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/CNAME +0 -0
  160. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/ai-tools.md +0 -0
  161. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/apps.md +0 -0
  162. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/calculations.md +0 -0
  163. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/calkit-yaml.md +0 -0
  164. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/cloud-integration.md +0 -0
  165. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/datasets.md +0 -0
  166. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/dependencies.md +0 -0
  167. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/environments.md +0 -0
  168. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/examples.md +0 -0
  169. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/governance.md +0 -0
  170. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/help.md +0 -0
  171. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/hpc.md +0 -0
  172. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/c-to-the-k-white.svg +0 -0
  173. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/calkit-fragmentation-compendium.png +0 -0
  174. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/calkit-no-bg.png +0 -0
  175. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/connect-zenodo.png +0 -0
  176. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab/all-green.png +0 -0
  177. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  178. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab/new-env.png +0 -0
  179. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab/new-notebook.png +0 -0
  180. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  181. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/jupyterlab-params.png +0 -0
  182. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/plos-osi-code-2024-03.png +0 -0
  183. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/img/vscode-nb-params.png +0 -0
  184. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/index.md +0 -0
  185. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/installation.md +0 -0
  186. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/jupyterlab.md +0 -0
  187. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/local-server.md +0 -0
  188. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/notebooks.md +0 -0
  189. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/pipeline/index.md +0 -0
  190. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/pipeline/manual-steps.md +0 -0
  191. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/pipeline/running-and-logging.md +0 -0
  192. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/quickstart.md +0 -0
  193. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/references.md +0 -0
  194. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/reproducibility.md +0 -0
  195. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/subprojects.md +0 -0
  196. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  197. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/conda-envs.md +0 -0
  198. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/existing-project.md +0 -0
  199. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/first-project.md +0 -0
  200. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/github-actions.md +0 -0
  201. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  202. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  203. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  204. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  205. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  206. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  207. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  208. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  209. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  210. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  211. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  212. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  213. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  214. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  215. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  216. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/create-project.png +0 -0
  217. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  218. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/excel-chart.png +0 -0
  219. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/excel-data.png +0 -0
  220. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  221. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/needs-clone.png +0 -0
  222. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/new-stage.png +0 -0
  223. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  224. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  225. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/status-more-rows.png +0 -0
  226. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  227. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/untracked-data.png +0 -0
  228. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/updated-publication.png +0 -0
  229. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  230. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/office/workflow-page.png +0 -0
  231. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/clone.png +0 -0
  232. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/create-project.png +0 -0
  233. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  234. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  235. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  236. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/new-token.png +0 -0
  237. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/reclone.png +0 -0
  238. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  239. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/quick-actions.png +0 -0
  240. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/run-proc.png +0 -0
  241. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
  242. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
  243. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
  244. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
  245. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
  246. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
  247. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
  248. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/index.md +0 -0
  249. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/jupyterlab.md +0 -0
  250. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/latex-codespaces.md +0 -0
  251. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/matlab.md +0 -0
  252. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/notebook-pipeline.md +0 -0
  253. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/office.md +0 -0
  254. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/openfoam.md +0 -0
  255. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/procedures.md +0 -0
  256. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/tutorials/vscode-slurm-notebook.md +0 -0
  257. {calkit_python-0.41.6 → calkit_python-0.41.8}/docs/version-control.md +0 -0
  258. {calkit_python-0.41.6 → calkit_python-0.41.8}/flake.lock +0 -0
  259. {calkit_python-0.41.6 → calkit_python-0.41.8}/flake.nix +0 -0
  260. {calkit_python-0.41.6 → calkit_python-0.41.8}/install.json +0 -0
  261. {calkit_python-0.41.6 → calkit_python-0.41.8}/jest.config.js +0 -0
  262. {calkit_python-0.41.6 → calkit_python-0.41.8}/jupyter-config/server-config/calkit.json +0 -0
  263. {calkit_python-0.41.6 → calkit_python-0.41.8}/mkdocs.yml +0 -0
  264. {calkit_python-0.41.6 → calkit_python-0.41.8}/package.json +0 -0
  265. {calkit_python-0.41.6 → calkit_python-0.41.8}/pyproject.toml +0 -0
  266. {calkit_python-0.41.6 → calkit_python-0.41.8}/schema/plugin.json +0 -0
  267. {calkit_python-0.41.6 → calkit_python-0.41.8}/scripts/generate-docs-references.py +0 -0
  268. {calkit_python-0.41.6 → calkit_python-0.41.8}/scripts/install.ps1 +0 -0
  269. {calkit_python-0.41.6 → calkit_python-0.41.8}/scripts/install.sh +0 -0
  270. {calkit_python-0.41.6 → calkit_python-0.41.8}/scripts/make-calk9.sh +0 -0
  271. {calkit_python-0.41.6 → calkit_python-0.41.8}/scripts/sync-docs.py +0 -0
  272. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/__tests__/useQueries.spec.ts +0 -0
  273. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/calkit-config.ts +0 -0
  274. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/cell-output-marker.ts +0 -0
  275. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/commit-dialog.tsx +0 -0
  276. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/environment-editor.tsx +0 -0
  277. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/notebook-registration.tsx +0 -0
  278. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/notebook-toolbar.tsx +0 -0
  279. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/pipeline-status-bar.tsx +0 -0
  280. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/project-info-editor.tsx +0 -0
  281. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/sidebar-settings.tsx +0 -0
  282. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/sidebar.tsx +0 -0
  283. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/components/stage-editor.tsx +0 -0
  284. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/feature-flags.ts +0 -0
  285. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/file-browser-menu.ts +0 -0
  286. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  287. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/hooks/useQueries.ts +0 -0
  288. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/icons.ts +0 -0
  289. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/index.ts +0 -0
  290. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/io-tracker.ts +0 -0
  291. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/pipeline-state.ts +0 -0
  292. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/queryClient.ts +0 -0
  293. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/request.ts +0 -0
  294. {calkit_python-0.41.6 → calkit_python-0.41.8}/src/shims-mainmenu.d.ts +0 -0
  295. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/base.css +0 -0
  296. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/cell-output-marker.css +0 -0
  297. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/environment-editor.css +0 -0
  298. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/environment-selector.css +0 -0
  299. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/img/calkit-no-bg.png +0 -0
  300. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/index.css +0 -0
  301. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/index.js +0 -0
  302. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/notebook-toolbar.css +0 -0
  303. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/pipeline-status-bar.css +0 -0
  304. {calkit_python-0.41.6 → calkit_python-0.41.8}/style/sidebar.css +0 -0
  305. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  306. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/nb-julia.ipynb +0 -0
  307. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/nb-params.ipynb +0 -0
  308. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/nb-subdir.ipynb +0 -0
  309. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/pipeline.ipynb +0 -0
  310. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/script.py +0 -0
  311. {calkit_python-0.41.6 → calkit_python-0.41.8}/test/test-log.log +0 -0
  312. {calkit_python-0.41.6 → calkit_python-0.41.8}/tsconfig.json +0 -0
  313. {calkit_python-0.41.6 → calkit_python-0.41.8}/tsconfig.test.json +0 -0
  314. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/.gitignore +0 -0
  315. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/README.md +0 -0
  316. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/jupyter_server_test_config.py +0 -0
  317. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/package.json +0 -0
  318. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/playwright.config.js +0 -0
  319. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/tests/calkit.spec.ts +0 -0
  320. {calkit_python-0.41.6 → calkit_python-0.41.8}/ui-tests/yarn.lock +0 -0
  321. {calkit_python-0.41.6 → calkit_python-0.41.8}/uv.lock +0 -0
  322. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/.gitignore +0 -0
  323. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/.vscodeignore +0 -0
  324. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/CHANGELOG.md +0 -0
  325. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/LICENSE +0 -0
  326. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/README.md +0 -0
  327. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/images/calkit-no-bg.png +0 -0
  328. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/package-lock.json +0 -0
  329. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/package.json +0 -0
  330. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/scripts/set-proposed-api.js +0 -0
  331. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/environments.ts +0 -0
  332. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/extension.ts +0 -0
  333. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/notebooks.ts +0 -0
  334. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/test/environments.test.ts +0 -0
  335. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
  336. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/test/notebooks.test.ts +0 -0
  337. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/src/types.ts +0 -0
  338. {calkit_python-0.41.6 → calkit_python-0.41.8}/vscode-ext/tsconfig.json +0 -0
  339. {calkit_python-0.41.6 → calkit_python-0.41.8}/yarn.lock +0 -0
@@ -21,3 +21,4 @@ dist
21
21
  vscode-ext/out
22
22
  .claude
23
23
  TODO.md
24
+ **/__pycache__/
@@ -22,6 +22,11 @@ not introduce new type errors.
22
22
  Wrap prose at natural breakpoints in phrases or punctuation to keep max
23
23
  line length below 80 characters.
24
24
 
25
+ In the docs, MkDocs Material admonitions (`!!! note`, `!!! tip`, etc.) must be
26
+ preceded by a `<!-- prettier-ignore -->` comment. Otherwise Prettier reformats
27
+ the block and strips the 4-space indentation of the admonition body, which
28
+ breaks rendering.
29
+
25
30
  Agents should never make commits to Git.
26
31
 
27
32
  Prefer tests that include multiple scenarios to comprehensively test
@@ -36,3 +41,5 @@ Functions should usually be used ~3 times before abstracting.
36
41
  Otherwise, split up long ones into logical sections with comments.
37
42
  The only exception here is if splitting up a function makes it easier to
38
43
  write meaningful unit tests.
44
+ If a function is only used inside one other function, nest it inside the
45
+ caller at the top.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.41.6
3
+ Version: 0.41.8
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -2418,7 +2418,16 @@ def run_in_env(
2418
2418
  )
2419
2419
  # TODO: Prefix should only be in the env file or calkit.yaml, not both?
2420
2420
  prefix = env.get("prefix")
2421
- conda_cmd = ["conda", "run"]
2421
+ # Conda is often not on the PATH (especially on Windows), so search
2422
+ # typical install locations rather than relying on the bare name,
2423
+ # which would fail with a confusing FileNotFoundError traceback.
2424
+ conda_exe = calkit.conda.find_conda_exe()
2425
+ if conda_exe is None:
2426
+ raise_error(
2427
+ "Cannot find Conda executable; "
2428
+ "ensure Conda is installed and on the PATH"
2429
+ )
2430
+ conda_cmd = [conda_exe, "run"]
2422
2431
  if prefix is not None:
2423
2432
  conda_cmd += ["--prefix", os.path.abspath(prefix)]
2424
2433
  else:
@@ -7,6 +7,7 @@ import os
7
7
  import pathlib
8
8
  import shutil
9
9
  import subprocess
10
+ import time
10
11
  from enum import Enum
11
12
 
12
13
  import typer
@@ -3124,18 +3125,7 @@ def new_release(
3124
3125
  record_id = None
3125
3126
  # Detect project license IDs if necessary
3126
3127
  if not license_ids:
3127
- license_file = None
3128
- license_txt = None
3129
- license_candidates = [
3130
- "LICENSE",
3131
- "LICENSE.rst",
3132
- "LICENSE.txt",
3133
- "LICENSE.md",
3134
- ]
3135
- for lc in license_candidates:
3136
- if os.path.isfile(lc):
3137
- license_file = lc
3138
- break
3128
+ license_file = calkit.licenses.find_license_file()
3139
3129
  if license_file is None:
3140
3130
  typer.echo("No project license found.")
3141
3131
  use_default_license = typer.confirm(
@@ -3155,15 +3145,18 @@ def new_release(
3155
3145
  f.write(license_txt)
3156
3146
  repo.git.add("LICENSE")
3157
3147
  license_ids = ["mit", "cc-by-4.0"]
3158
- if license_txt is None and license_file is not None:
3148
+ else:
3149
+ typer.echo(f"Detecting license(s) from {license_file}")
3159
3150
  with open(license_file) as f:
3160
3151
  license_txt = f.read()
3161
- # Try to detect licenses
3162
- if "the mit license" in license_txt.lower():
3163
- license_ids.append("mit")
3164
- if "cc by 4.0" in license_txt.lower():
3165
- license_ids.append("cc-by-4.0")
3166
- # TODO: Detect others
3152
+ license_ids = calkit.licenses.detect_license_ids(license_txt)
3153
+ if license_ids:
3154
+ typer.echo(f"Detected license(s): {', '.join(license_ids)}")
3155
+ else:
3156
+ warn(
3157
+ f"Could not detect any known license in {license_file}; "
3158
+ "specify one explicitly with --license"
3159
+ )
3167
3160
  if not license_ids:
3168
3161
  raise_error("Project has no license(s) defined")
3169
3162
  invenio_metadata = dict(
@@ -3189,9 +3182,13 @@ def new_release(
3189
3182
  )
3190
3183
  invenio_metadata["related_identifiers"] = related_identifiers
3191
3184
  # TODO: Add calkit.io URL if applicable?
3192
- # Determine creators from authors, adding to project if not present
3193
- authors = ck_info.get("authors", [])
3194
- if not authors:
3185
+ # Determine creators from authors. CITATION.cff is the single source of
3186
+ # truth for authors, so read them from there; if none are defined yet,
3187
+ # prompt for them and persist them to CITATION.cff.
3188
+ authors = calkit.releases.read_authors_from_cff()
3189
+ if authors:
3190
+ typer.echo(f"Read {len(authors)} author(s) from CITATION.cff")
3191
+ else:
3195
3192
  warn("No authors defined for the project")
3196
3193
  still_entering_authors = True
3197
3194
  n = 0
@@ -3218,24 +3215,27 @@ def new_release(
3218
3215
  still_entering_authors = typer.confirm(
3219
3216
  "Are there more authors to enter?", default=True
3220
3217
  )
3221
- ck_info["authors"] = authors
3222
- # Write authors out to calkit.yaml
3218
+ # Write authors out to CITATION.cff
3223
3219
  if not dry_run:
3224
- typer.echo("Adding authors to calkit.yaml")
3225
- with open("calkit.yaml", "w") as f:
3226
- calkit.ryaml.dump(ck_info, f)
3220
+ typer.echo("Writing authors to CITATION.cff")
3221
+ calkit.releases.set_cff_authors(authors, ck_info=ck_info)
3222
+ repo.git.add("CITATION.cff")
3227
3223
  invenio_creators = []
3228
3224
  for author in authors:
3229
3225
  orcid = author.get("orcid")
3230
- creator = {
3226
+ creator: dict = {
3231
3227
  "person_or_org": {
3232
3228
  "type": "personal",
3233
3229
  "given_name": author["first_name"],
3234
3230
  "family_name": author["last_name"],
3235
3231
  "identifiers": [{"identifier": orcid}] if orcid else [],
3236
3232
  },
3237
- "affiliations": [{"name": author["affiliation"]}],
3238
3233
  }
3234
+ # Affiliation is optional (e.g., authors read from CITATION.cff may
3235
+ # not specify one)
3236
+ affiliation = author.get("affiliation")
3237
+ if affiliation:
3238
+ creator["affiliations"] = [{"name": affiliation}]
3239
3239
  invenio_creators.append(creator)
3240
3240
  invenio_metadata["creators"] = invenio_creators # type: ignore
3241
3241
  # Set InvenioRDM resource_type based on release_type and artifact kind
@@ -3326,22 +3326,23 @@ def new_release(
3326
3326
  f"/records/{record_id}/draft/files/{filename}/commit",
3327
3327
  service=to, # type: ignore
3328
3328
  )
3329
- # Conditionally publish or reserve DOI based on --draft flag
3330
- if draft_only:
3331
- # Reserve a DOI for the draft record
3332
- typer.echo(f"Reserving DOI for {to} draft record ID {record_id}")
3333
- doi_resp = calkit.invenio.post(
3334
- f"/records/{record_id}/draft/pids/doi",
3335
- service=to, # type: ignore
3329
+ # Reserve a DOI on the draft before publishing. The publish action's
3330
+ # response only echoes back the DOI under "pids" if one was reserved
3331
+ # on the draft first (otherwise "pids" comes back empty), so always
3332
+ # reserve here to get a stable identifier.
3333
+ typer.echo(f"Reserving DOI for {to} draft record ID {record_id}")
3334
+ doi_resp = calkit.invenio.post(
3335
+ f"/records/{record_id}/draft/pids/doi",
3336
+ service=to, # type: ignore
3337
+ )
3338
+ if verbose:
3339
+ typer.echo(f"DOI reservation response:\n{doi_resp}")
3340
+ doi = calkit.invenio.extract_doi(doi_resp)
3341
+ if doi is None:
3342
+ raise_error(
3343
+ f"Failed to reserve DOI for {to} draft record {record_id}"
3336
3344
  )
3337
- if verbose:
3338
- typer.echo(f"DOI response for draft:\n{doi_resp}")
3339
- try:
3340
- doi = doi_resp["pids"]["doi"]["identifier"]
3341
- except KeyError:
3342
- doi = doi_resp["doi"]
3343
- except Exception as e:
3344
- raise_error(f"Failed to reserve DOI for draft: {e}")
3345
+ if draft_only:
3345
3346
  url = f"https://doi.org/{doi}"
3346
3347
  typer.echo(f"Created {to} draft with reserved DOI: {doi}")
3347
3348
  else:
@@ -3351,8 +3352,37 @@ def new_release(
3351
3352
  f"/records/{record_id}/draft/actions/publish",
3352
3353
  service=to, # type: ignore
3353
3354
  )
3355
+ if verbose:
3356
+ typer.echo(f"Publish response:\n{invenio_dep}")
3354
3357
  record_id = invenio_dep["id"]
3355
- doi = invenio_dep["pids"]["doi"]["identifier"]
3358
+ # Prefer the DOI from the publish response, but fall back to the
3359
+ # reserved DOI (and a fetch of the published record) since the
3360
+ # publish action returns 202 and may not echo "pids" immediately.
3361
+ # Polling failures are tolerated: we already have the reserved DOI,
3362
+ # so a transient error while the record settles should not abort
3363
+ # the release.
3364
+ published_doi = calkit.invenio.extract_doi(invenio_dep)
3365
+ for _ in range(10):
3366
+ if published_doi is not None:
3367
+ break
3368
+ time.sleep(1)
3369
+ try:
3370
+ record = calkit.invenio.get(
3371
+ f"/records/{record_id}",
3372
+ service=to, # type: ignore
3373
+ )
3374
+ except Exception as e:
3375
+ if verbose:
3376
+ typer.echo(f"Polling for published DOI failed: {e}")
3377
+ continue
3378
+ published_doi = calkit.invenio.extract_doi(record)
3379
+ if published_doi is not None:
3380
+ doi = published_doi
3381
+ else:
3382
+ typer.echo(
3383
+ "Could not confirm minted DOI from published record; "
3384
+ f"falling back to reserved DOI {doi}"
3385
+ )
3356
3386
  url = f"https://doi.org/{doi}"
3357
3387
  typer.echo(f"Published to {to} with DOI: {doi}")
3358
3388
  else:
@@ -3423,10 +3453,13 @@ def new_release(
3423
3453
  )
3424
3454
  releases[name] = release
3425
3455
  ck_info["releases"] = releases
3426
- # Create CITATION.cff file
3456
+ # Create/update CITATION.cff file, preserving existing authors
3427
3457
  if release_type == "project":
3428
3458
  cff = calkit.releases.create_citation_cff(
3429
- ck_info=ck_info, release_name=name, release_date=release_date
3459
+ ck_info=ck_info,
3460
+ release_name=name,
3461
+ release_date=release_date,
3462
+ authors=authors,
3430
3463
  )
3431
3464
  if dry_run:
3432
3465
  typer.echo(f"Would write CITATION.cff:\n{cff}")
@@ -12,9 +12,9 @@ import typer
12
12
  from typing_extensions import Annotated
13
13
 
14
14
  import calkit
15
- from calkit.cli import raise_error, warn
15
+ from calkit.cli import AliasGroup, raise_error, warn
16
16
 
17
- overleaf_app = typer.Typer(no_args_is_help=True)
17
+ overleaf_app = typer.Typer(cls=AliasGroup, no_args_is_help=True)
18
18
 
19
19
 
20
20
  def _extract_title_from_tex(tex_file_path: str) -> str | None:
@@ -369,8 +369,11 @@ def sync(
369
369
  typer.Option(
370
370
  "--no-commit",
371
371
  help=(
372
- "Do not commit the changes to the project repo. "
373
- "Changes will always be committed to Overleaf."
372
+ "Do not create a commit in the project repo for this sync. "
373
+ "Changes pulled from Overleaf are still applied, but are "
374
+ "left staged so you can review or commit them yourself. "
375
+ "Changes are always committed and pushed to Overleaf "
376
+ "regardless."
374
377
  ),
375
378
  ),
376
379
  ] = False,
@@ -378,6 +381,7 @@ def sync(
378
381
  bool,
379
382
  typer.Option(
380
383
  "--auto-commit",
384
+ "-a",
381
385
  help=(
382
386
  "Automatically commit changes to the project repo if a synced "
383
387
  "folder has changes."
@@ -490,8 +494,9 @@ def sync(
490
494
  )
491
495
  else:
492
496
  raise_error(
493
- f"Uncommitted changes found in {wdir}; "
494
- "please commit or stash them before syncing with Overleaf"
497
+ f"Uncommitted changes found in {wdir}. "
498
+ "Commit or stash them before syncing with Overleaf, "
499
+ "or use --auto-commit/-a to automatically commit them."
495
500
  )
496
501
  # Ensure we've cloned the Overleaf project
497
502
  overleaf_project_dir = os.path.join(
@@ -585,7 +590,7 @@ def compare_folders_recursively(
585
590
  return res
586
591
 
587
592
 
588
- @overleaf_app.command(name="status")
593
+ @overleaf_app.command(name="status|st")
589
594
  def get_status(
590
595
  paths: Annotated[
591
596
  list[str] | None,
@@ -50,6 +50,7 @@ AUTO_IGNORE_SUFFIXES = [
50
50
  ".env",
51
51
  ".pyc",
52
52
  ".synctex.gz",
53
+ ".auxlock",
53
54
  ".ipynb_checkpoints",
54
55
  ]
55
56
  AUTO_IGNORE_PATHS = [os.path.join(".dvc", "config.local")]
@@ -340,6 +341,14 @@ def check_dep_exists(
340
341
  return True
341
342
  if system_info is not None and system_info.get(f"{name}_version"):
342
343
  return True
344
+ # Conda and mamba are frequently installed but not on the PATH (most
345
+ # commonly on Windows), so search their typical install locations
346
+ # rather than relying on the bare name being directly executable.
347
+ if name in ("conda", "mamba"):
348
+ from calkit.conda import find_conda_exe, find_mamba_exe
349
+
350
+ exe = find_conda_exe() if name == "conda" else find_mamba_exe()
351
+ return exe is not None
343
352
  cmd = [name]
344
353
  # Executables with non-conventional CLIs
345
354
  if name == "matlab":
@@ -107,6 +107,26 @@ put = partial(_request, "put")
107
107
  delete = partial(_request, "delete")
108
108
 
109
109
 
110
+ def extract_doi(record: dict) -> str | None:
111
+ """Extract the DOI identifier from an InvenioRDM record or response.
112
+
113
+ Depending on the endpoint and whether the DOI has been minted yet, the
114
+ identifier can live in different places, so check all known locations and
115
+ return ``None`` if it isn't present yet.
116
+ """
117
+ pids = record.get("pids")
118
+ if isinstance(pids, dict):
119
+ doi = pids.get("doi")
120
+ if isinstance(doi, dict) and doi.get("identifier"):
121
+ return doi["identifier"]
122
+ if record.get("doi"):
123
+ return record["doi"]
124
+ metadata = record.get("metadata")
125
+ if isinstance(metadata, dict) and metadata.get("doi"):
126
+ return metadata["doi"]
127
+ return None
128
+
129
+
110
130
  def get_download_urls(
111
131
  record_id: int | str,
112
132
  service: ServiceName = DEFAULT_SERVICE,
@@ -0,0 +1,211 @@
1
+ """Functionality for working with licenses.
2
+
3
+ Calkit projects will often include code, data, text, and other content
4
+ and artifacts that may each have their own license.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import os
10
+ import re
11
+
12
+ # A list of (SPDX license ID, required substring groups) used to detect
13
+ # licenses from the text of a license file.
14
+ # Each entry maps an SPDX ID to a list of "all-of" groups; the license
15
+ # matches if *every* group has at least one of its substrings present in the
16
+ # (lowercased, whitespace-normalized) license text.
17
+ # This is intentionally tolerant so that licenses written by hand, generated
18
+ # by GitHub, or produced by Calkit's own templates are all detected.
19
+ # A single license file may contain more than one license (e.g., Calkit's
20
+ # default dual MIT/CC-BY-4.0 license), so all matching IDs are returned.
21
+ _LICENSE_MARKERS: list[tuple[str, list[list[str]]]] = [
22
+ (
23
+ "mit",
24
+ [
25
+ [
26
+ "mit license",
27
+ "permission is hereby granted, free of charge, to any "
28
+ "person obtaining a copy",
29
+ ]
30
+ ],
31
+ ),
32
+ (
33
+ "apache-2.0",
34
+ [["apache license"], ["version 2.0", "version 2,"]],
35
+ ),
36
+ (
37
+ "bsd-3-clause",
38
+ [
39
+ ["redistribution and use in source and binary forms"],
40
+ ["neither the name of"],
41
+ ],
42
+ ),
43
+ (
44
+ "bsd-2-clause",
45
+ [
46
+ ["redistribution and use in source and binary forms"],
47
+ ],
48
+ ),
49
+ (
50
+ "gpl-3.0",
51
+ [["gnu general public license"], ["version 3"]],
52
+ ),
53
+ (
54
+ "gpl-2.0",
55
+ [["gnu general public license"], ["version 2"]],
56
+ ),
57
+ (
58
+ "lgpl-3.0",
59
+ [["gnu lesser general public license"], ["version 3"]],
60
+ ),
61
+ (
62
+ "agpl-3.0",
63
+ [["gnu affero general public license"], ["version 3"]],
64
+ ),
65
+ (
66
+ "mpl-2.0",
67
+ [["mozilla public license"], ["version 2.0", "version 2,"]],
68
+ ),
69
+ (
70
+ "cc-by-4.0",
71
+ [
72
+ [
73
+ "creative commons attribution 4.0",
74
+ "cc by 4.0",
75
+ "cc-by-4.0",
76
+ "creativecommons.org/licenses/by/4.0",
77
+ ]
78
+ ],
79
+ ),
80
+ (
81
+ "cc0-1.0",
82
+ [
83
+ [
84
+ "cc0 1.0",
85
+ "creative commons zero",
86
+ "creativecommons.org/publicdomain/zero/1.0",
87
+ ]
88
+ ],
89
+ ),
90
+ (
91
+ "isc",
92
+ [
93
+ [
94
+ "isc license",
95
+ "permission to use, copy, modify, and/or distribute this "
96
+ "software",
97
+ ]
98
+ ],
99
+ ),
100
+ (
101
+ "unlicense",
102
+ [
103
+ [
104
+ "this is free and unencumbered software released into the "
105
+ "public domain"
106
+ ]
107
+ ],
108
+ ),
109
+ ]
110
+
111
+ # Candidate file names to search for a project license, in priority order.
112
+ LICENSE_FILE_CANDIDATES = [
113
+ "LICENSE",
114
+ "LICENSE.txt",
115
+ "LICENSE.md",
116
+ "LICENSE.rst",
117
+ "LICENCE",
118
+ "LICENCE.txt",
119
+ "LICENCE.md",
120
+ "COPYING",
121
+ ]
122
+
123
+
124
+ def detect_license_ids(text: str) -> list[str]:
125
+ """Detect SPDX license IDs present in license text.
126
+
127
+ The match is tolerant of whitespace and case, and a single body of text
128
+ can match multiple licenses (e.g., a dual-licensed project).
129
+ Returns the IDs in a deterministic order with duplicates removed.
130
+ """
131
+ if not text:
132
+ return []
133
+ # Lowercase and collapse all runs of whitespace so that line wrapping in
134
+ # the source license file does not break multi-word substring matches
135
+ normalized = re.sub(r"\s+", " ", text.lower())
136
+ found: list[str] = []
137
+ for license_id, groups in _LICENSE_MARKERS:
138
+ # The "bsd-3-clause" and "bsd-2-clause" markers overlap; only report
139
+ # the 2-clause variant when the 3-clause variant did not match
140
+ if license_id == "bsd-2-clause" and "bsd-3-clause" in found:
141
+ continue
142
+ if all(
143
+ any(substring in normalized for substring in group)
144
+ for group in groups
145
+ ):
146
+ found.append(license_id)
147
+ return found
148
+
149
+
150
+ def find_license_file(wdir: str | None = None) -> str | None:
151
+ """Return the path to the project license file, if one exists."""
152
+ for candidate in LICENSE_FILE_CANDIDATES:
153
+ path = candidate if wdir is None else os.path.join(wdir, candidate)
154
+ if os.path.isfile(path):
155
+ return path
156
+ return None
157
+
158
+
159
+ LICENSE_TEMPLATE_DUAL = """
160
+ This project is licensed under a dual-license structure to appropriately cover
161
+ both the software code and the non-code content (data, text, figures, etc.).
162
+
163
+ Source code (MIT License)
164
+
165
+ All source code contained within this repository, including but not limited to
166
+ files with the extensions .js, .py, .html, .css, .ts, .jsx, .tsx, shell
167
+ scripts, build configurations, and their related files, is licensed under
168
+ the MIT License.
169
+
170
+ The MIT License (MIT)
171
+
172
+ Copyright (c) {year} {copyright_holder}
173
+
174
+ Permission is hereby granted, free of charge, to any person obtaining a copy
175
+ of this software and associated documentation files (the "Software"), to deal
176
+ in the Software without restriction, including without limitation the rights
177
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
178
+ copies of the Software, and to permit persons to whom the Software is
179
+ furnished to do so, subject to the following conditions:
180
+
181
+ The above copyright notice and this permission notice shall be included in all
182
+ copies or substantial portions of the Software.
183
+
184
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
185
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
186
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
187
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
188
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
189
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
190
+ SOFTWARE.
191
+
192
+ Content, data, and documentation (CC BY 4.0)
193
+
194
+ All non-code assets, including but not limited to prose, documentation, data,
195
+ figures, images, designs, and text written in Markdown or similar formats
196
+ (e.g., files in a docs/ directory, this LICENSE file itself, or .txt files
197
+ containing prose or data), are licensed under the Creative Commons Attribution
198
+ 4.0 International Public License (CC BY 4.0).
199
+
200
+ To view a copy of this license, visit:
201
+
202
+ CC BY 4.0 Deed (summary):
203
+ https://creativecommons.org/licenses/by/4.0/
204
+
205
+ CC BY 4.0 Legal Code (full license):
206
+ https://creativecommons.org/licenses/by/4.0/legalcode
207
+
208
+ This license requires you to give appropriate credit, provide a link to the
209
+ license, and indicate if changes were made. You may do so in any reasonable
210
+ manner, but not in any way that suggests the licensor endorses you or your use.
211
+ """.strip()