calkit-python 0.35.6__tar.gz → 0.36.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 (304) hide show
  1. {calkit_python-0.35.6 → calkit_python-0.36.0}/.gitignore +1 -1
  2. calkit_python-0.36.0/.vscode/launch.json +17 -0
  3. calkit_python-0.36.0/.vscode/tasks.json +15 -0
  4. {calkit_python-0.35.6 → calkit_python-0.36.0}/PKG-INFO +1 -1
  5. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/main/core.py +131 -21
  6. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/new.py +227 -51
  7. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/notebooks.py +119 -13
  8. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/update.py +154 -2
  9. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/dvc.py +9 -8
  10. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/jupyterlab/routes.py +2 -4
  11. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/package.json +14 -0
  12. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/schemas/calkit/package.json.orig +14 -0
  13. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/pipeline.py +305 -0
  14. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/releases.py +81 -10
  15. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/server.py +3 -3
  16. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_new.py +107 -0
  17. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_notebooks.py +36 -0
  18. calkit_python-0.36.0/calkit/tests/cli/test_update.py +22 -0
  19. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_pipeline.py +77 -0
  20. calkit_python-0.36.0/calkit/tests/test_releases.py +150 -0
  21. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/cli-reference.md +149 -41
  22. {calkit_python-0.35.6 → calkit_python-0.36.0}/package.json +14 -0
  23. {calkit_python-0.35.6 → calkit_python-0.36.0}/pyproject.toml +1 -0
  24. calkit_python-0.36.0/vscode-ext/.gitignore +3 -0
  25. calkit_python-0.36.0/vscode-ext/.vscodeignore +5 -0
  26. calkit_python-0.36.0/vscode-ext/CHANGELOG.md +6 -0
  27. calkit_python-0.36.0/vscode-ext/LICENSE +19 -0
  28. calkit_python-0.36.0/vscode-ext/README.md +66 -0
  29. calkit_python-0.36.0/vscode-ext/images/calkit-no-bg.png +0 -0
  30. calkit_python-0.36.0/vscode-ext/package-lock.json +77 -0
  31. calkit_python-0.36.0/vscode-ext/package.json +138 -0
  32. calkit_python-0.36.0/vscode-ext/scripts/set-proposed-api.js +25 -0
  33. calkit_python-0.36.0/vscode-ext/src/environments.ts +224 -0
  34. calkit_python-0.36.0/vscode-ext/src/extension.ts +3510 -0
  35. calkit_python-0.36.0/vscode-ext/src/notebooks.ts +79 -0
  36. calkit_python-0.36.0/vscode-ext/src/test/environments.test.ts +130 -0
  37. calkit_python-0.36.0/vscode-ext/src/test/notebooks.test.ts +70 -0
  38. calkit_python-0.36.0/vscode-ext/src/types.ts +25 -0
  39. calkit_python-0.36.0/vscode-ext/tsconfig.json +14 -0
  40. calkit_python-0.35.6/calkit/tests/test_releases.py +0 -44
  41. {calkit_python-0.35.6 → calkit_python-0.36.0}/.pre-commit-config.yaml +0 -0
  42. {calkit_python-0.35.6 → calkit_python-0.36.0}/.prettierignore +0 -0
  43. {calkit_python-0.35.6 → calkit_python-0.36.0}/.python-version +0 -0
  44. {calkit_python-0.35.6 → calkit_python-0.36.0}/.yarnrc.yml +0 -0
  45. {calkit_python-0.35.6 → calkit_python-0.36.0}/CITATION.cff +0 -0
  46. {calkit_python-0.35.6 → calkit_python-0.36.0}/CODE_OF_CONDUCT.md +0 -0
  47. {calkit_python-0.35.6 → calkit_python-0.36.0}/CONTRIBUTING.md +0 -0
  48. {calkit_python-0.35.6 → calkit_python-0.36.0}/LICENSE +0 -0
  49. {calkit_python-0.35.6 → calkit_python-0.36.0}/Makefile +0 -0
  50. {calkit_python-0.35.6 → calkit_python-0.36.0}/README.md +0 -0
  51. {calkit_python-0.35.6 → calkit_python-0.36.0}/babel.config.js +0 -0
  52. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/__init__.py +0 -0
  53. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/__main__.py +0 -0
  54. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/calc.py +0 -0
  55. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/check.py +0 -0
  56. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/__init__.py +0 -0
  57. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/check.py +0 -0
  58. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/cloud.py +0 -0
  59. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/config.py +0 -0
  60. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/core.py +0 -0
  61. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/describe.py +0 -0
  62. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/import_.py +0 -0
  63. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/latex.py +0 -0
  64. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/list.py +0 -0
  65. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/main/__init__.py +0 -0
  66. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/main/xr.py +0 -0
  67. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/office.py +0 -0
  68. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/overleaf.py +0 -0
  69. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cli/slurm.py +0 -0
  70. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/cloud.py +0 -0
  71. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/conda.py +0 -0
  72. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/config.py +0 -0
  73. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/core.py +0 -0
  74. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/datasets.py +0 -0
  75. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/detect.py +0 -0
  76. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/docker.py +0 -0
  77. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/environments.py +0 -0
  78. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/fs.py +0 -0
  79. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/git.py +0 -0
  80. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/github.py +0 -0
  81. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/gui.py +0 -0
  82. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/invenio.py +0 -0
  83. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/julia.py +0 -0
  84. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/jupyter.py +0 -0
  85. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/jupyterlab/__init__.py +0 -0
  86. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  87. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  88. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  89. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  90. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/909.651be47ca47390b78a92.js +0 -0
  91. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  92. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  93. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  94. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/remoteEntry.c091821b3d7f2d287a67.js +0 -0
  95. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/style.js +0 -0
  96. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/labextension/static/third-party-licenses.json +0 -0
  97. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/licenses.py +0 -0
  98. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/magics.py +0 -0
  99. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/matlab.py +0 -0
  100. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/models/__init__.py +0 -0
  101. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/models/core.py +0 -0
  102. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/models/io.py +0 -0
  103. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/models/iteration.py +0 -0
  104. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/models/pipeline.py +0 -0
  105. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/notebooks.py +0 -0
  106. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/office.py +0 -0
  107. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/ops.py +0 -0
  108. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/overleaf.py +0 -0
  109. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/__init__.py +0 -0
  110. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/core.py +0 -0
  111. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/__init__.py +0 -0
  112. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/article/paper.tex +0 -0
  113. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/core.py +0 -0
  114. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/jfm/jfm.bst +0 -0
  115. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/jfm/jfm.cls +0 -0
  116. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  117. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/jfm/paper.tex +0 -0
  118. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/templates/latex/jfm/upmath.sty +0 -0
  119. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/__init__.py +0 -0
  120. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/__init__.py +0 -0
  121. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/main/__init__.py +0 -0
  122. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/main/test_core.py +0 -0
  123. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/main/test_xr.py +0 -0
  124. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_check.py +0 -0
  125. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_config.py +0 -0
  126. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_import.py +0 -0
  127. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_latex.py +0 -0
  128. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_list.py +0 -0
  129. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/cli/test_overleaf.py +0 -0
  130. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/jupyterlab/__init__.py +0 -0
  131. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/jupyterlab/test_routes.py +0 -0
  132. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/models/__init__.py +0 -0
  133. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/models/test_iteration.py +0 -0
  134. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/models/test_pipeline.py +0 -0
  135. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_calc.py +0 -0
  136. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_check.py +0 -0
  137. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_conda.py +0 -0
  138. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_core.py +0 -0
  139. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_detect.py +0 -0
  140. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_docker.py +0 -0
  141. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_dvc.py +0 -0
  142. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_environments.py +0 -0
  143. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_fs.py +0 -0
  144. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_git.py +0 -0
  145. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_invenio.py +0 -0
  146. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_julia.py +0 -0
  147. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_jupyter.py +0 -0
  148. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_magics.py +0 -0
  149. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_matlab.py +0 -0
  150. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_notebooks.py +0 -0
  151. {calkit_python-0.35.6 → calkit_python-0.36.0}/calkit/tests/test_templates.py +0 -0
  152. {calkit_python-0.35.6 → calkit_python-0.36.0}/conftest.py +0 -0
  153. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/CNAME +0 -0
  154. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/apps.md +0 -0
  155. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/calculations.md +0 -0
  156. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/calkit-yaml.md +0 -0
  157. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/cloud-integration.md +0 -0
  158. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/datasets.md +0 -0
  159. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/dependencies.md +0 -0
  160. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/environments.md +0 -0
  161. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/examples.md +0 -0
  162. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/governance.md +0 -0
  163. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/help.md +0 -0
  164. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/c-to-the-k-white.svg +0 -0
  165. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/calkit-fragmentation-compendium.png +0 -0
  166. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/calkit-no-bg.png +0 -0
  167. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/connect-zenodo.png +0 -0
  168. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab/all-green.png +0 -0
  169. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  170. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab/new-env.png +0 -0
  171. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab/new-notebook.png +0 -0
  172. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  173. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/jupyterlab-params.png +0 -0
  174. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/plos-osi-code-2024-03.png +0 -0
  175. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/img/vscode-nb-params.png +0 -0
  176. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/index.md +0 -0
  177. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/installation.md +0 -0
  178. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/jupyterlab.md +0 -0
  179. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/local-server.md +0 -0
  180. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/notebooks.md +0 -0
  181. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/overleaf.md +0 -0
  182. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/pipeline/index.md +0 -0
  183. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/pipeline/manual-steps.md +0 -0
  184. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/pipeline/running-and-logging.md +0 -0
  185. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/pipeline/slurm.md +0 -0
  186. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/quickstart.md +0 -0
  187. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/references.md +0 -0
  188. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/releases.md +0 -0
  189. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/reproducibility.md +0 -0
  190. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  191. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/conda-envs.md +0 -0
  192. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/existing-project.md +0 -0
  193. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/first-project.md +0 -0
  194. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/github-actions.md +0 -0
  195. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  196. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  197. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  198. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  199. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  200. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  201. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  202. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  203. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  204. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  205. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  206. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  207. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  208. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  209. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  210. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/create-project.png +0 -0
  211. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  212. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/excel-chart.png +0 -0
  213. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/excel-data.png +0 -0
  214. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  215. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/needs-clone.png +0 -0
  216. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/new-stage.png +0 -0
  217. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  218. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  219. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/status-more-rows.png +0 -0
  220. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  221. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/untracked-data.png +0 -0
  222. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/updated-publication.png +0 -0
  223. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  224. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/office/workflow-page.png +0 -0
  225. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/clone.png +0 -0
  226. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/create-project.png +0 -0
  227. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  228. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  229. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  230. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/new-token.png +0 -0
  231. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/reclone.png +0 -0
  232. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  233. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/quick-actions.png +0 -0
  234. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/img/run-proc.png +0 -0
  235. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/index.md +0 -0
  236. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/jupyterlab.md +0 -0
  237. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/latex-codespaces.md +0 -0
  238. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/matlab.md +0 -0
  239. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/notebook-pipeline.md +0 -0
  240. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/office.md +0 -0
  241. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/openfoam.md +0 -0
  242. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/tutorials/procedures.md +0 -0
  243. {calkit_python-0.35.6 → calkit_python-0.36.0}/docs/version-control.md +0 -0
  244. {calkit_python-0.35.6 → calkit_python-0.36.0}/install.json +0 -0
  245. {calkit_python-0.35.6 → calkit_python-0.36.0}/jest.config.js +0 -0
  246. {calkit_python-0.35.6 → calkit_python-0.36.0}/jupyter-config/server-config/calkit.json +0 -0
  247. {calkit_python-0.35.6 → calkit_python-0.36.0}/mkdocs.yml +0 -0
  248. {calkit_python-0.35.6 → calkit_python-0.36.0}/schema/plugin.json +0 -0
  249. {calkit_python-0.35.6 → calkit_python-0.36.0}/scripts/generate-cli-reference.py +0 -0
  250. {calkit_python-0.35.6 → calkit_python-0.36.0}/scripts/install.ps1 +0 -0
  251. {calkit_python-0.35.6 → calkit_python-0.36.0}/scripts/install.sh +0 -0
  252. {calkit_python-0.35.6 → calkit_python-0.36.0}/scripts/make-calk9.sh +0 -0
  253. {calkit_python-0.35.6 → calkit_python-0.36.0}/scripts/sync-docs.py +0 -0
  254. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/__tests__/useQueries.spec.ts +0 -0
  255. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/calkit-config.ts +0 -0
  256. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/cell-output-marker.ts +0 -0
  257. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/commit-dialog.tsx +0 -0
  258. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/environment-editor.tsx +0 -0
  259. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/notebook-registration.tsx +0 -0
  260. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/notebook-toolbar.tsx +0 -0
  261. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/pipeline-status-bar.tsx +0 -0
  262. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/project-info-editor.tsx +0 -0
  263. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/sidebar-settings.tsx +0 -0
  264. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/sidebar.tsx +0 -0
  265. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/components/stage-editor.tsx +0 -0
  266. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/feature-flags.ts +0 -0
  267. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/file-browser-menu.ts +0 -0
  268. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/hooks/__tests__/useQueries.test.tsx +0 -0
  269. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/hooks/useQueries.ts +0 -0
  270. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/icons.ts +0 -0
  271. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/index.ts +0 -0
  272. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/io-tracker.ts +0 -0
  273. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/pipeline-state.ts +0 -0
  274. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/queryClient.ts +0 -0
  275. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/request.ts +0 -0
  276. {calkit_python-0.35.6 → calkit_python-0.36.0}/src/shims-mainmenu.d.ts +0 -0
  277. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/base.css +0 -0
  278. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/cell-output-marker.css +0 -0
  279. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/environment-editor.css +0 -0
  280. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/environment-selector.css +0 -0
  281. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/img/calkit-no-bg.png +0 -0
  282. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/index.css +0 -0
  283. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/index.js +0 -0
  284. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/notebook-toolbar.css +0 -0
  285. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/pipeline-status-bar.css +0 -0
  286. {calkit_python-0.35.6 → calkit_python-0.36.0}/style/sidebar.css +0 -0
  287. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  288. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/nb-julia.ipynb +0 -0
  289. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/nb-params.ipynb +0 -0
  290. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/nb-subdir.ipynb +0 -0
  291. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/pipeline.ipynb +0 -0
  292. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/script.py +0 -0
  293. {calkit_python-0.35.6 → calkit_python-0.36.0}/test/test-log.log +0 -0
  294. {calkit_python-0.35.6 → calkit_python-0.36.0}/tsconfig.json +0 -0
  295. {calkit_python-0.35.6 → calkit_python-0.36.0}/tsconfig.test.json +0 -0
  296. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/.gitignore +0 -0
  297. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/README.md +0 -0
  298. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/jupyter_server_test_config.py +0 -0
  299. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/package.json +0 -0
  300. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/playwright.config.js +0 -0
  301. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/tests/calkit.spec.ts +0 -0
  302. {calkit_python-0.35.6 → calkit_python-0.36.0}/ui-tests/yarn.lock +0 -0
  303. {calkit_python-0.35.6 → calkit_python-0.36.0}/uv.lock +0 -0
  304. {calkit_python-0.35.6 → calkit_python-0.36.0}/yarn.lock +0 -0
@@ -1,7 +1,7 @@
1
1
  dev.ipynb
2
2
  *.pyc
3
3
  .DS_Store
4
- .vscode
4
+ .vscode/settings.json
5
5
  site
6
6
  .env
7
7
  .venv
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Run Calkit VS Code Extension",
6
+ "type": "extensionHost",
7
+ "request": "launch",
8
+ "runtimeExecutable": "${execPath}",
9
+ "args": [
10
+ "--extensionDevelopmentPath=${workspaceFolder}/vscode-ext",
11
+ "--enable-proposed-api=calkit.calkit-vscode"
12
+ ],
13
+ "outFiles": ["${workspaceFolder}/vscode-ext/out/**/*.js"],
14
+ "preLaunchTask": "npm: compile (vscode-ext)"
15
+ }
16
+ ]
17
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "type": "shell",
6
+ "command": "npm",
7
+ "args": ["run", "dev:compile"],
8
+ "options": {
9
+ "cwd": "${workspaceFolder}/vscode-ext"
10
+ },
11
+ "label": "npm: compile (vscode-ext)",
12
+ "problemMatcher": ["$tsc"]
13
+ }
14
+ ]
15
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.35.6
3
+ Version: 0.36.0
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -55,7 +55,7 @@ from calkit.cli.office import office_app
55
55
  from calkit.cli.overleaf import overleaf_app
56
56
  from calkit.cli.slurm import slurm_app
57
57
  from calkit.cli.update import update_app
58
- from calkit.dvc import register_ck_scheme, run_dvc_command
58
+ from calkit.dvc import get_dvc_repo, run_dvc_command
59
59
  from calkit.environments import get_env_lock_fpath
60
60
  from calkit.models import Procedure
61
61
 
@@ -249,13 +249,18 @@ def get_status(
249
249
  ),
250
250
  ),
251
251
  ] = None,
252
+ as_json: Annotated[
253
+ bool, typer.Option("--json", help="Output status as JSON.")
254
+ ] = False,
252
255
  ):
253
256
  """View status (project, version control, and/or pipeline)."""
254
257
  ck_info = calkit.load_calkit_info()
255
- try:
256
- calkit.pipeline.to_dvc(ck_info=ck_info, write=True)
257
- except Exception as e:
258
- warn(f"Failed to compile pipeline: {e.__class__.__name__}: {e}")
258
+ # If there's anything in ck_info and this isn't a Git repo, initialize one
259
+ if ck_info:
260
+ try:
261
+ git.Repo()
262
+ except InvalidGitRepositoryError:
263
+ git.Repo.init()
259
264
  valid_categories = ["project", "git", "dvc", "pipeline"]
260
265
  if categories is not None:
261
266
  for category in categories:
@@ -266,11 +271,94 @@ def get_status(
266
271
  )
267
272
  else:
268
273
  categories = valid_categories
269
- # Clean all notebooks in the pipeline
270
- try:
271
- calkit.notebooks.clean_all_in_pipeline(ck_info=ck_info)
272
- except Exception as e:
273
- warn(f"Failed to clean notebooks: {e.__class__.__name__}: {e}")
274
+ pipeline_status = None
275
+ if "pipeline" in categories or "dvc" in categories:
276
+ pipeline_status = calkit.pipeline.get_status(
277
+ ck_info=ck_info,
278
+ targets=targets,
279
+ check_environments=True,
280
+ clean_notebooks=True,
281
+ compile_to_dvc=True,
282
+ )
283
+ for error in pipeline_status.errors:
284
+ warn(error)
285
+ if pipeline_status.failed_environment_checks:
286
+ warn(
287
+ "Failed pipeline environment checks for: "
288
+ + ", ".join(pipeline_status.failed_environment_checks)
289
+ )
290
+ if as_json:
291
+ status_dict = {}
292
+ if "project" in categories:
293
+ status = calkit.get_latest_project_status()
294
+ status_dict["project"] = (
295
+ None
296
+ if status is None
297
+ else {
298
+ "status": status.status,
299
+ "message": status.message,
300
+ "timestamp": status.timestamp.isoformat(),
301
+ }
302
+ )
303
+ if "git" in categories:
304
+ try:
305
+ repo = git.Repo()
306
+ changed_files = calkit.git.get_changed_files(repo=repo)
307
+ staged_files = calkit.git.get_staged_files(repo=repo)
308
+ untracked_files = calkit.git.get_untracked_files(repo=repo)
309
+ if targets:
310
+ target_prefixes = [
311
+ Path(target).as_posix().rstrip("/")
312
+ for target in targets
313
+ ]
314
+
315
+ def _matches_target(path: str) -> bool:
316
+ path = Path(path).as_posix()
317
+ return any(
318
+ path == prefix or path.startswith(prefix + "/")
319
+ for prefix in target_prefixes
320
+ )
321
+
322
+ changed_files = [
323
+ path for path in changed_files if _matches_target(path)
324
+ ]
325
+ staged_files = [
326
+ path for path in staged_files if _matches_target(path)
327
+ ]
328
+ untracked_files = [
329
+ path
330
+ for path in untracked_files
331
+ if _matches_target(path)
332
+ ]
333
+ status_dict["git"] = {
334
+ "branch": None
335
+ if repo.head.is_detached
336
+ else repo.active_branch.name,
337
+ "is_dirty": repo.is_dirty(untracked_files=True),
338
+ "changed_files": changed_files,
339
+ "staged_files": staged_files,
340
+ "untracked_files": untracked_files,
341
+ }
342
+ except InvalidGitRepositoryError:
343
+ status_dict["git"] = {"error": "Not a Git repository"}
344
+ if "dvc" in categories:
345
+ try:
346
+ dvc_repo = calkit.dvc.get_dvc_repo()
347
+ data_status = dvc_repo.data_status()
348
+ if isinstance(data_status, dict):
349
+ data_status.pop("git", None)
350
+ status_dict["dvc"] = data_status
351
+ except Exception as e:
352
+ status_dict["dvc"] = {"error": f"{e.__class__.__name__}: {e}"}
353
+ if "pipeline" in categories or "dvc" in categories:
354
+ if pipeline_status is None:
355
+ status_dict["pipeline"] = None
356
+ else:
357
+ status_dict["pipeline"] = pipeline_status.model_dump(
358
+ mode="json"
359
+ )
360
+ print(json.dumps(status_dict, indent=2, default=str))
361
+ return
274
362
  if "project" in categories:
275
363
  print_sep("Project")
276
364
  # Print latest status
@@ -307,10 +395,36 @@ def get_status(
307
395
  typer.echo()
308
396
  if "pipeline" in categories or "dvc" in categories:
309
397
  print_sep("Pipeline")
310
- dvc_pipeline_cmd = ["status"]
311
- if targets:
312
- dvc_pipeline_cmd += targets
313
- run_dvc_command(dvc_pipeline_cmd)
398
+ # Nicely format the results from pipeline status
399
+ if pipeline_status and pipeline_status.errors:
400
+ warn("Pipeline status unavailable due to errors.")
401
+ return
402
+ if pipeline_status and not pipeline_status.has_pipeline:
403
+ typer.echo("This project has no pipeline.")
404
+ return
405
+ elif pipeline_status and pipeline_status.is_stale:
406
+ for stage_name in pipeline_status.stale_stage_names:
407
+ stale_stage = pipeline_status.stale_stages.get(stage_name)
408
+ if stale_stage is None:
409
+ continue
410
+ typer.echo(f"{typer.style(stage_name, fg='yellow')}:")
411
+ # Show stale outputs for this stage
412
+ if stale_stage.stale_outputs:
413
+ typer.echo(" stale outputs:")
414
+ for output_path in stale_stage.stale_outputs:
415
+ typer.echo(f" {output_path}")
416
+ # Show modified outputs from this stage
417
+ if stale_stage.modified_outputs:
418
+ typer.echo(" modified outputs:")
419
+ for output_path in stale_stage.modified_outputs:
420
+ typer.echo(f" {output_path}")
421
+ # Show modified inputs making the stage stale
422
+ if stale_stage.modified_inputs:
423
+ typer.echo(" modified inputs:")
424
+ for input_path in stale_stage.modified_inputs:
425
+ typer.echo(f" {input_path}")
426
+ elif pipeline_status:
427
+ typer.echo("Pipeline is up to date.")
314
428
 
315
429
 
316
430
  @app.command(name="diff")
@@ -403,8 +517,7 @@ def add(
403
517
  raise_error("Not currently in a Git repo; run `calkit init` first")
404
518
  repo = git.Repo()
405
519
  try:
406
- register_ck_scheme()
407
- dvc_repo = dvc.repo.Repo()
520
+ dvc_repo = get_dvc_repo()
408
521
  except NotDvcRepoError:
409
522
  warn("DVC not initialized yet; initializing")
410
523
  dvc_repo = dvc.repo.Repo.init()
@@ -1139,10 +1252,8 @@ def run(
1139
1252
  os.environ.pop("CALKIT_PIPELINE_RUNNING", None)
1140
1253
  raise_error(f"Pipeline compilation failed: {e}")
1141
1254
  # Initialize DVC repo if necessary
1142
- # Register the ck:// scheme before accessing DVC repo
1143
- register_ck_scheme()
1144
1255
  try:
1145
- dvc.repo.Repo()
1256
+ get_dvc_repo()
1146
1257
  except Exception:
1147
1258
  if not quiet:
1148
1259
  typer.echo("Initializing DVC repo")
@@ -1203,8 +1314,7 @@ def run(
1203
1314
  git_staged_files_before = calkit.git.get_staged_files(repo=repo)
1204
1315
  git_untracked_files_before = calkit.git.get_untracked_files(repo=repo)
1205
1316
  # Get status of DVC repo before running
1206
- register_ck_scheme()
1207
- dvc_repo = dvc.repo.Repo()
1317
+ dvc_repo = get_dvc_repo()
1208
1318
  dvc_status_before = dvc_repo.status()
1209
1319
  dvc_data_status_before = dvc_repo.data_status()
1210
1320
  dvc_data_status_before.pop("git", None) # Remove git status
@@ -17,6 +17,7 @@ from git.exc import GitCommandError, InvalidGitRepositoryError, NoSuchPathError
17
17
  from typing_extensions import Annotated
18
18
 
19
19
  import calkit
20
+ import calkit.pipeline
20
21
  from calkit.cli import raise_error, warn
21
22
  from calkit.cli.check import check_environment
22
23
  from calkit.cli.update import update_devcontainer
@@ -1332,6 +1333,75 @@ def new_uv_env(
1332
1333
  repo.git.commit(["-m", f"Add uv environment {name}"])
1333
1334
 
1334
1335
 
1336
+ @new_app.command("slurm-env")
1337
+ def new_slurm_env(
1338
+ name: Annotated[
1339
+ str, typer.Option("--name", "-n", help="Environment name.")
1340
+ ],
1341
+ host: Annotated[
1342
+ str,
1343
+ typer.Option("--host", help="Host where SLURM commands should run."),
1344
+ ] = "localhost",
1345
+ default_options: Annotated[
1346
+ list[str],
1347
+ typer.Option(
1348
+ "--default-option",
1349
+ help=(
1350
+ "Default sbatch/srun option string (for example --gpus=1). "
1351
+ "Repeat for multiple options."
1352
+ ),
1353
+ ),
1354
+ ] = [],
1355
+ description: Annotated[
1356
+ str | None, typer.Option("--description", help="Description.")
1357
+ ] = None,
1358
+ overwrite: Annotated[
1359
+ bool,
1360
+ typer.Option(
1361
+ "--overwrite",
1362
+ "-f",
1363
+ help="Overwrite any existing environment with this name.",
1364
+ ),
1365
+ ] = False,
1366
+ no_commit: Annotated[
1367
+ bool, typer.Option("--no-commit", help="Do not commit changes.")
1368
+ ] = False,
1369
+ ):
1370
+ """Create a new SLURM environment."""
1371
+ host = host.strip()
1372
+ if not host:
1373
+ raise_error("Host is required")
1374
+ ck_info = calkit.load_calkit_info()
1375
+ envs = ck_info.get("environments", {})
1376
+ if isinstance(envs, list):
1377
+ typer.echo("Converting environments from list to dict")
1378
+ envs = {env.pop("name"): env for env in envs}
1379
+ if name in envs and not overwrite:
1380
+ raise_error(
1381
+ f"Environment with name {name} already exists "
1382
+ "(use -f to overwrite)"
1383
+ )
1384
+ normalized_default_options = [
1385
+ opt.strip() for opt in default_options if opt.strip()
1386
+ ]
1387
+ env = {"kind": "slurm", "host": host}
1388
+ if normalized_default_options:
1389
+ env["default_options"] = normalized_default_options # type: ignore
1390
+ if description is not None:
1391
+ env["description"] = description
1392
+ envs[name] = env
1393
+ ck_info["environments"] = envs
1394
+ with open("calkit.yaml", "w") as f:
1395
+ ryaml.dump(ck_info, f)
1396
+ if not no_commit:
1397
+ repo = git.Repo()
1398
+ repo.git.add("calkit.yaml")
1399
+ if repo.git.diff(["--staged", "calkit.yaml"]):
1400
+ repo.git.commit(
1401
+ ["calkit.yaml", "-m", f"Add SLURM environment {name}"]
1402
+ )
1403
+
1404
+
1335
1405
  @new_app.command("uv-venv")
1336
1406
  def new_uv_venv(
1337
1407
  name: Annotated[
@@ -1614,21 +1684,29 @@ def new_pixi_env(
1614
1684
  @new_app.command("julia-env")
1615
1685
  def new_julia_env(
1616
1686
  packages: Annotated[
1617
- list[str],
1618
- typer.Argument(help="Packages to include in the environment."),
1619
- ],
1687
+ list[str] | None,
1688
+ typer.Argument(
1689
+ help="Optional packages to include in the environment."
1690
+ ),
1691
+ ] = None,
1620
1692
  name: Annotated[
1621
1693
  str, typer.Option("--name", "-n", help="Environment name.")
1622
- ],
1694
+ ] = "main",
1623
1695
  path: Annotated[
1624
- str, typer.Option("--path", help="Path for Project.toml file.")
1625
- ] = "Project.toml",
1696
+ str | None,
1697
+ typer.Option("--path", help="Path for Project.toml file."),
1698
+ ] = None,
1626
1699
  description: Annotated[
1627
1700
  str | None, typer.Option("--description", help="Description.")
1628
1701
  ] = None,
1629
1702
  julia_version: Annotated[
1630
- str, typer.Option("--julia", "-j", help="Julia version.")
1631
- ] = "1.11",
1703
+ str | None,
1704
+ typer.Option(
1705
+ "--julia",
1706
+ "-j",
1707
+ help="Julia version. Auto-detected if not supplied.",
1708
+ ),
1709
+ ] = None,
1632
1710
  overwrite: Annotated[
1633
1711
  bool,
1634
1712
  typer.Option(
@@ -1640,8 +1718,17 @@ def new_julia_env(
1640
1718
  no_commit: Annotated[
1641
1719
  bool, typer.Option("--no-commit", help="Do not commit changes.")
1642
1720
  ] = False,
1721
+ no_check: Annotated[
1722
+ bool,
1723
+ typer.Option(
1724
+ "--no-check",
1725
+ help="Do not check environment is up-to-date after creation.",
1726
+ ),
1727
+ ] = False,
1643
1728
  ):
1644
- """Create a new Julia environment."""
1729
+ """Create a new Julia environment or add an existing one to calkit.yaml."""
1730
+ if path is None:
1731
+ path = "Project.toml"
1645
1732
  if not os.path.basename(path) == "Project.toml":
1646
1733
  raise_error(
1647
1734
  "Julia environment paths must point to a Project.toml file"
@@ -1664,27 +1751,57 @@ def new_julia_env(
1664
1751
  f"Environment with name {name} already exists "
1665
1752
  "(use -f to overwrite)"
1666
1753
  )
1667
- # Create the environment now
1754
+ # Detect Julia version if not supplied
1755
+ if julia_version is None:
1756
+ try:
1757
+ julia_version = calkit.julia.get_version()
1758
+ except ValueError as e:
1759
+ raise_error(
1760
+ f"Failed to detect Julia version: {e}. "
1761
+ "Use --julia to specify a version."
1762
+ )
1763
+ # Check for duplicate Julia environments with same path and version
1764
+ for env_name, env_config in envs.items():
1765
+ if (
1766
+ env_config.get("kind") == "julia"
1767
+ and env_config.get("path") == path
1768
+ and env_config.get("julia") == julia_version
1769
+ ):
1770
+ raise_error(
1771
+ f"Julia environment '{env_name}' already exists with path "
1772
+ f"'{path}' and version '{julia_version}'; "
1773
+ f"Use a different path, version, or --overwrite to replace it"
1774
+ )
1668
1775
  env_dir = os.path.dirname(path)
1669
1776
  if env_dir:
1670
1777
  os.makedirs(env_dir, exist_ok=True)
1671
1778
  else:
1672
1779
  env_dir = "."
1673
- # First make sure the Julia version is installed
1674
- cmd = ["juliaup", "add", julia_version]
1675
- try:
1676
- subprocess.run(cmd, check=True)
1677
- except subprocess.CalledProcessError:
1678
- raise_error(f"Failed to install Julia version {julia_version}")
1679
- cmd = ["julia", f"+{julia_version}", f"--project={env_dir}", "-e"]
1680
- install_cmd = "using Pkg;"
1681
- for package in packages:
1682
- install_cmd += f' Pkg.add("{package}");'
1683
- cmd.append(install_cmd)
1684
- try:
1685
- subprocess.run(cmd, check=True)
1686
- except subprocess.CalledProcessError:
1687
- raise_error("Failed to create new Julia environment")
1780
+ project_exists = os.path.isfile(path)
1781
+ created_project_file = False
1782
+ # If packages are provided, initialize via Julia/Pkg.
1783
+ if packages:
1784
+ # First make sure the Julia version is installed
1785
+ cmd = ["juliaup", "add", julia_version]
1786
+ try:
1787
+ subprocess.run(cmd, check=True)
1788
+ except subprocess.CalledProcessError:
1789
+ raise_error(f"Failed to install Julia version {julia_version}")
1790
+ cmd = ["julia", f"+{julia_version}", f"--project={env_dir}", "-e"]
1791
+ install_cmd = f'using Pkg; Pkg.activate("{env_dir}");'
1792
+ for package in packages or []:
1793
+ install_cmd += f' Pkg.add("{package}");'
1794
+ cmd.append(install_cmd)
1795
+ try:
1796
+ subprocess.run(cmd, check=True)
1797
+ except subprocess.CalledProcessError:
1798
+ raise_error("Failed to create new Julia environment")
1799
+ elif not project_exists:
1800
+ # Allow creating a Julia environment scaffold without requiring Julia
1801
+ # tooling at creation time. IJulia or other packages can be added later.
1802
+ with open(path, "w") as f:
1803
+ f.write("[deps]\n")
1804
+ created_project_file = True
1688
1805
  typer.echo("Adding environment to calkit.yaml")
1689
1806
  env = dict(kind="julia", path=path, julia=julia_version)
1690
1807
  if description is not None:
@@ -1693,11 +1810,18 @@ def new_julia_env(
1693
1810
  ck_info["environments"] = envs
1694
1811
  with open("calkit.yaml", "w") as f:
1695
1812
  ryaml.dump(ck_info, f)
1696
- repo.git.add(path)
1697
- repo.git.add(os.path.join(env_dir, "Manifest.toml"))
1698
- repo.git.add("calkit.yaml")
1699
- if not no_commit and repo.git.diff("--staged"):
1700
- repo.git.commit(["-m", f"Add Julia env {name}"])
1813
+ if not no_commit:
1814
+ repo.git.add("calkit.yaml")
1815
+ if packages or created_project_file:
1816
+ repo.git.add(path)
1817
+ if packages:
1818
+ repo.git.add(os.path.join(env_dir, "Manifest.toml"))
1819
+ if not no_check and packages:
1820
+ env_lock_fpath = check_environment(env_name=name)
1821
+ if env_lock_fpath is not None:
1822
+ repo.git.add(env_lock_fpath)
1823
+ if repo.git.diff("--staged"):
1824
+ repo.git.commit(["-m", f"Add Julia env {name}"])
1701
1825
 
1702
1826
 
1703
1827
  @new_app.command("renv")
@@ -2523,6 +2647,26 @@ def new_release(
2523
2647
  repo = git.Repo()
2524
2648
  if name in repo.tags:
2525
2649
  raise_error(f"Git tag with name '{name}' already exists")
2650
+ # Check that the pipeline is up-to-date
2651
+ typer.echo("Checking pipeline is up-to-date for release")
2652
+ status = calkit.pipeline.get_status(
2653
+ ck_info=ck_info,
2654
+ check_environments=True,
2655
+ clean_notebooks=True,
2656
+ compile_to_dvc=True,
2657
+ )
2658
+ if status.errors:
2659
+ raise_error("Pipeline checks failed: " + "; ".join(status.errors))
2660
+ if status.failed_environment_checks:
2661
+ raise_error(
2662
+ "Pipeline environment checks failed for: "
2663
+ + ", ".join(status.failed_environment_checks)
2664
+ )
2665
+ if status.is_stale:
2666
+ raise_error(
2667
+ "Pipeline is not up-to-date; out-of-date stages: "
2668
+ + ", ".join(status.stale_stage_names)
2669
+ )
2526
2670
  release_dir = f".calkit/releases/{name}"
2527
2671
  release_files_dir = release_dir + "/files"
2528
2672
  os.makedirs(release_files_dir, exist_ok=True)
@@ -2543,8 +2687,14 @@ def new_release(
2543
2687
  all_paths = calkit.releases.ls_files()
2544
2688
  typer.echo(f"Adding files to {zip_path}")
2545
2689
  with zipfile.ZipFile(zip_path, "w") as zipf:
2546
- for fpath in all_paths:
2547
- zipf.write(fpath)
2690
+ calkit.releases.add_paths_to_zip(zipf, all_paths)
2691
+ typer.echo("Checking extracted project release archive")
2692
+ try:
2693
+ calkit.releases.check_project_release_archive(
2694
+ zip_path, verbose=verbose
2695
+ )
2696
+ except Exception as e:
2697
+ raise_error(str(e))
2548
2698
  title = ck_info.get("title")
2549
2699
  if title is None:
2550
2700
  warn("Project has no title")
@@ -2888,10 +3038,12 @@ def new_release(
2888
3038
  + new_lines[first_content_line_index:]
2889
3039
  )
2890
3040
  readme_txt = "\n".join(new_lines)
2891
- with open("README.md", "w") as f:
2892
- f.write(readme_txt)
2893
3041
  if not dry_run:
2894
- repo.git.add("README.md")
3042
+ with open("README.md", "w") as f:
3043
+ f.write(readme_txt)
3044
+ repo.git.add("README.md")
3045
+ else:
3046
+ typer.echo(f"Would have updated README.md to:\n{readme_txt}")
2895
3047
  # Create Git tag
2896
3048
  git_tag_message = release_description
2897
3049
  if git_tag_message is None:
@@ -2919,13 +3071,15 @@ def new_release(
2919
3071
  ck_info["releases"] = releases
2920
3072
  # Create CITATION.cff file
2921
3073
  if release_type == "project":
2922
- typer.echo("Writing CITATION.cff")
2923
3074
  cff = calkit.releases.create_citation_cff(
2924
3075
  ck_info=ck_info, release_name=name, release_date=release_date
2925
3076
  )
2926
- with open("CITATION.cff", "w") as f:
2927
- calkit.ryaml.dump(cff, f)
2928
- if not dry_run:
3077
+ if dry_run:
3078
+ typer.echo(f"Would write CITATION.cff:\n{cff}")
3079
+ else:
3080
+ typer.echo("Writing CITATION.cff")
3081
+ with open("CITATION.cff", "w") as f:
3082
+ calkit.ryaml.dump(cff, f)
2929
3083
  repo.git.add("CITATION.cff")
2930
3084
  # Add to references so it can be cited
2931
3085
  typer.echo("Adding BibTeX entry to references")
@@ -2944,28 +3098,50 @@ def new_release(
2944
3098
  reflib = bibtexparser.load(f)
2945
3099
  else:
2946
3100
  reflib = bibtexparser.bibdatabase.BibDatabase()
3101
+ bibtex_doi = doi
3102
+ if bibtex_doi is None and dry_run:
3103
+ mock_suffix = "".join(
3104
+ ch if (ch.isalnum() or ch in ".-_") else "-"
3105
+ for ch in name.lower()
3106
+ )
3107
+ bibtex_doi = f"10.0000/calkit-dry-run.{mock_suffix}"
2947
3108
  invenio_bibtex = calkit.releases.create_bibtex(
2948
3109
  authors=authors,
2949
3110
  release_date=release_date,
2950
3111
  title=title, # type: ignore
2951
- doi=doi, # type: ignore
3112
+ doi=bibtex_doi,
2952
3113
  record_id=record_id, # type: ignore
2953
3114
  service=to, # type: ignore
2954
3115
  )
2955
- new_entry = bibtexparser.loads(invenio_bibtex).entries[0]
3116
+ new_entries = bibtexparser.loads(invenio_bibtex).entries
3117
+ if not new_entries:
3118
+ raise ValueError("Failed to parse generated BibTeX entry")
3119
+ new_entry = new_entries[0]
2956
3120
  # Search through entries for one with the same DOI, and replace if
2957
3121
  # there is a match
2958
- existing_index = None
2959
- for n, entry in enumerate(reflib.entries):
2960
- if entry.get("doi") == doi:
2961
- typer.echo("Found matching DOI in existing references")
2962
- existing_index = n
2963
- if existing_index is not None:
2964
- _ = reflib.entries.pop(existing_index)
3122
+ new_doi = new_entry.get("doi")
3123
+ matching_indices = []
3124
+ if new_doi:
3125
+ matching_indices = [
3126
+ n
3127
+ for n, entry in enumerate(reflib.entries)
3128
+ if entry.get("doi") == new_doi
3129
+ ]
3130
+ if matching_indices:
3131
+ typer.echo(
3132
+ "Found matching DOI in existing references "
3133
+ f"({len(matching_indices)} entr"
3134
+ f"{'y' if len(matching_indices) == 1 else 'ies'}); "
3135
+ "replacing"
3136
+ )
3137
+ for n in reversed(matching_indices):
3138
+ _ = reflib.entries.pop(n)
2965
3139
  reflib.entries.append(new_entry)
2966
- with open(ref_path, "w") as f:
2967
- bibtexparser.dump(reflib, f)
2968
- if not dry_run:
3140
+ if dry_run:
3141
+ typer.echo(f"Would write updated references to {ref_path}")
3142
+ else:
3143
+ with open(ref_path, "w") as f:
3144
+ bibtexparser.dump(reflib, f)
2969
3145
  repo.git.add(ref_path)
2970
3146
  except Exception as e:
2971
3147
  warn(f"Failed to add to references: {e}")