calkit-python 0.41.22__tar.gz → 0.41.24__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 (358) hide show
  1. {calkit_python-0.41.22 → calkit_python-0.41.24}/PKG-INFO +1 -1
  2. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/main/core.py +96 -59
  3. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/overleaf.py +26 -50
  4. calkit_python-0.41.24/calkit/cli/sync.py +80 -0
  5. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/overleaf.py +63 -0
  6. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/main/test_core.py +47 -0
  7. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/main/test_subprojects.py +77 -0
  8. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_overleaf.py +32 -0
  9. calkit_python-0.41.24/calkit/tests/cli/test_sync.py +116 -0
  10. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/cli-reference.md +93 -19
  11. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/version-control.md +20 -2
  12. {calkit_python-0.41.22 → calkit_python-0.41.24}/.claude-plugin/marketplace.json +0 -0
  13. {calkit_python-0.41.22 → calkit_python-0.41.24}/.gitattributes +0 -0
  14. {calkit_python-0.41.22 → calkit_python-0.41.24}/.gitignore +0 -0
  15. {calkit_python-0.41.22 → calkit_python-0.41.24}/.gitmodules +0 -0
  16. {calkit_python-0.41.22 → calkit_python-0.41.24}/.pre-commit-config.yaml +0 -0
  17. {calkit_python-0.41.22 → calkit_python-0.41.24}/.prettierignore +0 -0
  18. {calkit_python-0.41.22 → calkit_python-0.41.24}/.prettierrc.json +0 -0
  19. {calkit_python-0.41.22 → calkit_python-0.41.24}/.python-version +0 -0
  20. {calkit_python-0.41.22 → calkit_python-0.41.24}/.vscode/launch.json +0 -0
  21. {calkit_python-0.41.22 → calkit_python-0.41.24}/.vscode/tasks.json +0 -0
  22. {calkit_python-0.41.22 → calkit_python-0.41.24}/AGENTS.md +0 -0
  23. {calkit_python-0.41.22 → calkit_python-0.41.24}/CITATION.cff +0 -0
  24. {calkit_python-0.41.22 → calkit_python-0.41.24}/CODE_OF_CONDUCT.md +0 -0
  25. {calkit_python-0.41.22 → calkit_python-0.41.24}/CONTRIBUTING.md +0 -0
  26. {calkit_python-0.41.22 → calkit_python-0.41.24}/LICENSE +0 -0
  27. {calkit_python-0.41.22 → calkit_python-0.41.24}/Makefile +0 -0
  28. {calkit_python-0.41.22 → calkit_python-0.41.24}/README.md +0 -0
  29. {calkit_python-0.41.22 → calkit_python-0.41.24}/agent-plugin/.claude-plugin/plugin.json +0 -0
  30. {calkit_python-0.41.22 → calkit_python-0.41.24}/agent-plugin/skills/add-pipeline-stage/SKILL.md +0 -0
  31. {calkit_python-0.41.22 → calkit_python-0.41.24}/agent-plugin/skills/conventions/SKILL.md +0 -0
  32. {calkit_python-0.41.22 → calkit_python-0.41.24}/agent-plugin/skills/create-pipeline/SKILL.md +0 -0
  33. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/__init__.py +0 -0
  34. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/__main__.py +0 -0
  35. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/calc.py +0 -0
  36. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/check.py +0 -0
  37. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/__init__.py +0 -0
  38. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/check.py +0 -0
  39. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/cloud.py +0 -0
  40. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/config.py +0 -0
  41. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/core.py +0 -0
  42. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/delete.py +0 -0
  43. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/describe.py +0 -0
  44. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/dev.py +0 -0
  45. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/import_.py +0 -0
  46. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/latex.py +0 -0
  47. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/list.py +0 -0
  48. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/main/__init__.py +0 -0
  49. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/main/xr.py +0 -0
  50. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/new.py +0 -0
  51. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/notebooks.py +0 -0
  52. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/office.py +0 -0
  53. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/scheduler.py +0 -0
  54. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cli/update.py +0 -0
  55. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/cloud.py +0 -0
  56. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/conda.py +0 -0
  57. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/config.py +0 -0
  58. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/core.py +0 -0
  59. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/datasets.py +0 -0
  60. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/dependencies.py +0 -0
  61. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/detect.py +0 -0
  62. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/docker.py +0 -0
  63. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/dvc/__init__.py +0 -0
  64. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/dvc/core.py +0 -0
  65. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/dvc/zip.py +0 -0
  66. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/environments.py +0 -0
  67. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/fs.py +0 -0
  68. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/git.py +0 -0
  69. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/github.py +0 -0
  70. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/gui.py +0 -0
  71. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/install.py +0 -0
  72. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/invenio.py +0 -0
  73. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/julia.py +0 -0
  74. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/jupyter.py +0 -0
  75. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/jupyterlab/__init__.py +0 -0
  76. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/jupyterlab/routes.py +0 -0
  77. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/package.json +0 -0
  78. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
  79. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/schemas/calkit/plugin.json +0 -0
  80. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
  81. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
  82. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
  83. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/909.e3f9cc3408834a7fdcc3.js +0 -0
  84. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
  85. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
  86. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
  87. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/remoteEntry.ac9035764d5b2adbb542.js +0 -0
  88. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/style.js +0 -0
  89. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/labextension/static/third-party-licenses.json +0 -0
  90. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/licenses.py +0 -0
  91. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/magics.py +0 -0
  92. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/matlab.py +0 -0
  93. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/models/__init__.py +0 -0
  94. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/models/core.py +0 -0
  95. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/models/io.py +0 -0
  96. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/models/iteration.py +0 -0
  97. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/models/pipeline.py +0 -0
  98. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/notebooks.py +0 -0
  99. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/office.py +0 -0
  100. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/ops.py +0 -0
  101. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/pipeline.py +0 -0
  102. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/releases.py +0 -0
  103. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/server.py +0 -0
  104. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/__init__.py +0 -0
  105. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/core.py +0 -0
  106. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/__init__.py +0 -0
  107. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/article/paper.tex +0 -0
  108. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/core.py +0 -0
  109. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/jfm/jfm.bst +0 -0
  110. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/jfm/jfm.cls +0 -0
  111. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
  112. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/jfm/paper.tex +0 -0
  113. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/templates/latex/jfm/upmath.sty +0 -0
  114. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/__init__.py +0 -0
  115. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/__init__.py +0 -0
  116. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/main/__init__.py +0 -0
  117. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/main/test_lock.py +0 -0
  118. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/main/test_xr.py +0 -0
  119. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_check.py +0 -0
  120. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_cloud.py +0 -0
  121. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_config.py +0 -0
  122. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_delete.py +0 -0
  123. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_import.py +0 -0
  124. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_latex.py +0 -0
  125. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_list.py +0 -0
  126. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_new.py +0 -0
  127. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_notebooks.py +0 -0
  128. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_scheduler.py +0 -0
  129. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/cli/test_update.py +0 -0
  130. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/dvc/__init__.py +0 -0
  131. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/dvc/test_core.py +0 -0
  132. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/dvc/test_zip.py +0 -0
  133. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/jupyterlab/__init__.py +0 -0
  134. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/jupyterlab/conftest.py +0 -0
  135. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/jupyterlab/test_routes.py +0 -0
  136. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/models/__init__.py +0 -0
  137. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/models/test_core.py +0 -0
  138. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/models/test_iteration.py +0 -0
  139. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/models/test_pipeline.py +0 -0
  140. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_calc.py +0 -0
  141. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_check.py +0 -0
  142. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_cloud.py +0 -0
  143. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_conda.py +0 -0
  144. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_core.py +0 -0
  145. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_dependencies.py +0 -0
  146. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_detect.py +0 -0
  147. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_docker.py +0 -0
  148. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_environments.py +0 -0
  149. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_fs.py +0 -0
  150. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_git.py +0 -0
  151. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_install.py +0 -0
  152. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_invenio.py +0 -0
  153. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_julia.py +0 -0
  154. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_jupyter.py +0 -0
  155. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_licenses.py +0 -0
  156. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_magics.py +0 -0
  157. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_matlab.py +0 -0
  158. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_notebooks.py +0 -0
  159. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_pipeline.py +0 -0
  160. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_releases.py +0 -0
  161. {calkit_python-0.41.22 → calkit_python-0.41.24}/calkit/tests/test_templates.py +0 -0
  162. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/CNAME +0 -0
  163. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/acknowledgements.md +0 -0
  164. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/ai-tools.md +0 -0
  165. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/apps.md +0 -0
  166. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/calculations.md +0 -0
  167. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/calkit-yaml.md +0 -0
  168. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/cloud-integration.md +0 -0
  169. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/datasets.md +0 -0
  170. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/dependencies.md +0 -0
  171. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/environments.md +0 -0
  172. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/examples.md +0 -0
  173. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/governance.md +0 -0
  174. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/help.md +0 -0
  175. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/hpc.md +0 -0
  176. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/c-to-the-k-white.svg +0 -0
  177. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/calkit-fragmentation-compendium.png +0 -0
  178. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/calkit-no-bg.png +0 -0
  179. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/caltech.png +0 -0
  180. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/connect-zenodo.png +0 -0
  181. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab/all-green.png +0 -0
  182. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab/collect-data-stale.png +0 -0
  183. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab/new-env.png +0 -0
  184. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab/new-notebook.png +0 -0
  185. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab/pipeline-badge.png +0 -0
  186. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/jupyterlab-params.png +0 -0
  187. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/pipeline.png +0 -0
  188. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/plos-osi-code-2024-03.png +0 -0
  189. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/schmidt-sciences.png +0 -0
  190. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/img/vscode-nb-params.png +0 -0
  191. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/index.md +0 -0
  192. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/installation.md +0 -0
  193. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/jupyterlab.md +0 -0
  194. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/local-server.md +0 -0
  195. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/notebooks.md +0 -0
  196. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/overleaf.md +0 -0
  197. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/pipeline/index.md +0 -0
  198. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/pipeline/manual-steps.md +0 -0
  199. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/pipeline/running-and-logging.md +0 -0
  200. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/questions.md +0 -0
  201. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/quickstart.md +0 -0
  202. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/references.md +0 -0
  203. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/releases.md +0 -0
  204. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/reproducibility.md +0 -0
  205. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/subprojects.md +0 -0
  206. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/adding-latex-pub-docker.md +0 -0
  207. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/ai-agents.md +0 -0
  208. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/conda-envs.md +0 -0
  209. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/existing-project.md +0 -0
  210. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/first-project.md +0 -0
  211. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/github-actions.md +0 -0
  212. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/actions-repo-secrets.png +0 -0
  213. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
  214. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
  215. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
  216. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
  217. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
  218. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
  219. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
  220. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
  221. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
  222. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
  223. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/push.png +0 -0
  224. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
  225. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
  226. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/chart-more-rows.png +0 -0
  227. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/create-project.png +0 -0
  228. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
  229. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/excel-chart.png +0 -0
  230. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/excel-data.png +0 -0
  231. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
  232. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/needs-clone.png +0 -0
  233. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/new-stage.png +0 -0
  234. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
  235. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
  236. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/status-more-rows.png +0 -0
  237. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
  238. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/untracked-data.png +0 -0
  239. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/updated-publication.png +0 -0
  240. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
  241. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/office/workflow-page.png +0 -0
  242. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/clone.png +0 -0
  243. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/create-project.png +0 -0
  244. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
  245. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
  246. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
  247. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/new-token.png +0 -0
  248. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/reclone.png +0 -0
  249. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
  250. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/quick-actions.png +0 -0
  251. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/run-proc.png +0 -0
  252. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/create-calkit-env.png +0 -0
  253. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/create-inner-env.png +0 -0
  254. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/create-new-calkit-env.png +0 -0
  255. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/select-calkit-env.png +0 -0
  256. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/slurm-job-running.png +0 -0
  257. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/slurm-launch-options.png +0 -0
  258. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/img/vscode-slurm-notebook/starting-slurm-job.png +0 -0
  259. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/index.md +0 -0
  260. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/jupyterlab.md +0 -0
  261. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/latex-codespaces.md +0 -0
  262. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/matlab.md +0 -0
  263. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/notebook-pipeline.md +0 -0
  264. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/office.md +0 -0
  265. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/openfoam.md +0 -0
  266. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/procedures.md +0 -0
  267. {calkit_python-0.41.22 → calkit_python-0.41.24}/docs/tutorials/vscode-slurm-notebook.md +0 -0
  268. {calkit_python-0.41.22 → calkit_python-0.41.24}/flake.lock +0 -0
  269. {calkit_python-0.41.22 → calkit_python-0.41.24}/flake.nix +0 -0
  270. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/.yarnrc.yml +0 -0
  271. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/babel.config.js +0 -0
  272. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/install.json +0 -0
  273. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/jest.config.js +0 -0
  274. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/jupyter-config/server-config/calkit.json +0 -0
  275. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/package.json +0 -0
  276. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/schema/plugin.json +0 -0
  277. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/__tests__/useQueries.spec.ts +0 -0
  278. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/calkit-config.ts +0 -0
  279. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/cell-output-marker.ts +0 -0
  280. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/commit-dialog.tsx +0 -0
  281. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/environment-editor.tsx +0 -0
  282. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/notebook-registration.tsx +0 -0
  283. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/notebook-toolbar.tsx +0 -0
  284. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/pipeline-status-bar.tsx +0 -0
  285. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/project-info-editor.tsx +0 -0
  286. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/sidebar-settings.tsx +0 -0
  287. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/sidebar.tsx +0 -0
  288. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/components/stage-editor.tsx +0 -0
  289. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/feature-flags.ts +0 -0
  290. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/file-browser-menu.ts +0 -0
  291. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/hooks/__tests__/useQueries.test.tsx +0 -0
  292. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/hooks/useQueries.ts +0 -0
  293. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/icons.ts +0 -0
  294. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/index.ts +0 -0
  295. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/io-tracker.ts +0 -0
  296. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/pipeline-state.ts +0 -0
  297. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/queryClient.ts +0 -0
  298. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/request.ts +0 -0
  299. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/src/shims-mainmenu.d.ts +0 -0
  300. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/base.css +0 -0
  301. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/cell-output-marker.css +0 -0
  302. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/environment-editor.css +0 -0
  303. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/environment-selector.css +0 -0
  304. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/img/calkit-no-bg.png +0 -0
  305. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/index.css +0 -0
  306. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/index.js +0 -0
  307. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/notebook-toolbar.css +0 -0
  308. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/pipeline-status-bar.css +0 -0
  309. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/style/sidebar.css +0 -0
  310. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/tsconfig.json +0 -0
  311. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/tsconfig.test.json +0 -0
  312. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/.gitignore +0 -0
  313. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/README.md +0 -0
  314. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/jupyter_server_test_config.py +0 -0
  315. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/package.json +0 -0
  316. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/playwright.config.js +0 -0
  317. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/tests/calkit.spec.ts +0 -0
  318. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/ui-tests/yarn.lock +0 -0
  319. {calkit_python-0.41.22 → calkit_python-0.41.24}/jupyterlab-ext/yarn.lock +0 -0
  320. {calkit_python-0.41.22 → calkit_python-0.41.24}/mkdocs.yml +0 -0
  321. {calkit_python-0.41.22 → calkit_python-0.41.24}/pyproject.toml +0 -0
  322. {calkit_python-0.41.22 → calkit_python-0.41.24}/scripts/generate-docs-references.py +0 -0
  323. {calkit_python-0.41.22 → calkit_python-0.41.24}/scripts/install.ps1 +0 -0
  324. {calkit_python-0.41.22 → calkit_python-0.41.24}/scripts/install.sh +0 -0
  325. {calkit_python-0.41.22 → calkit_python-0.41.24}/scripts/make-calk9.sh +0 -0
  326. {calkit_python-0.41.22 → calkit_python-0.41.24}/scripts/sync-docs.py +0 -0
  327. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/dvc-md5-dir/osx-arm64.txt +0 -0
  328. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/nb-julia.ipynb +0 -0
  329. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/nb-params.ipynb +0 -0
  330. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/nb-subdir.ipynb +0 -0
  331. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/pipeline.ipynb +0 -0
  332. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/script.py +0 -0
  333. {calkit_python-0.41.22 → calkit_python-0.41.24}/test/test-log.log +0 -0
  334. {calkit_python-0.41.22 → calkit_python-0.41.24}/uv.lock +0 -0
  335. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/.gitignore +0 -0
  336. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/.vscodeignore +0 -0
  337. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/CHANGELOG.md +0 -0
  338. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/LICENSE +0 -0
  339. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/README.md +0 -0
  340. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/images/calkit-icon.svg +0 -0
  341. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/images/calkit-no-bg.png +0 -0
  342. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/package-lock.json +0 -0
  343. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/package.json +0 -0
  344. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/scripts/set-proposed-api.js +0 -0
  345. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/environments.ts +0 -0
  346. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/extension.ts +0 -0
  347. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/figures/core.ts +0 -0
  348. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/figures/view.ts +0 -0
  349. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/notebooks.ts +0 -0
  350. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/pipeline/core.ts +0 -0
  351. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/sidebar.ts +0 -0
  352. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/test/environments.test.ts +0 -0
  353. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/test/figures.test.ts +0 -0
  354. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/test/kernel-selection.test.ts +0 -0
  355. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/test/notebooks.test.ts +0 -0
  356. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/test/pipeline.test.ts +0 -0
  357. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/src/types.ts +0 -0
  358. {calkit_python-0.41.22 → calkit_python-0.41.24}/vscode-ext/tsconfig.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: calkit-python
3
- Version: 0.41.22
3
+ Version: 0.41.24
4
4
  Summary: Reproducibility simplified.
5
5
  Project-URL: Homepage, https://calkit.org
6
6
  Project-URL: Issues, https://github.com/calkit/calkit/issues
@@ -59,6 +59,7 @@ from calkit.cli.notebooks import notebooks_app
59
59
  from calkit.cli.office import office_app
60
60
  from calkit.cli.overleaf import overleaf_app
61
61
  from calkit.cli.scheduler import scheduler_app
62
+ from calkit.cli.sync import sync_app
62
63
  from calkit.cli.update import update_app
63
64
 
64
65
  app = typer.Typer(
@@ -89,6 +90,7 @@ app.add_typer(
89
90
  help="Work with a job scheduler (SLURM or PBS).",
90
91
  )
91
92
  app.add_typer(dev_app, name="dev", help="Developer tools.", hidden=True)
93
+ app.add_typer(sync_app, name="sync", help="Sync with external systems.")
92
94
 
93
95
 
94
96
  def _to_shell_cmd(cmd: list[str]) -> str:
@@ -1157,6 +1159,12 @@ def save(
1157
1159
  @app.command(name="pull")
1158
1160
  def pull(
1159
1161
  no_check_auth: Annotated[bool, typer.Option("--no-check-auth")] = False,
1162
+ no_dvc: Annotated[
1163
+ bool, typer.Option("--no-dvc", help="Do not pull from DVC.")
1164
+ ] = False,
1165
+ no_git: Annotated[
1166
+ bool, typer.Option("--no-git", help="Do not pull from Git.")
1167
+ ] = False,
1160
1168
  git_args: Annotated[
1161
1169
  list[str],
1162
1170
  typer.Option("--git-arg", help="Additional Git args."),
@@ -1181,58 +1189,62 @@ def pull(
1181
1189
  ] = False,
1182
1190
  ):
1183
1191
  """Pull with both Git and DVC."""
1184
- typer.echo("Git pulling")
1185
1192
  if force:
1186
1193
  if "-f" not in git_args and "--force" not in git_args:
1187
1194
  git_args.append("-f")
1188
1195
  if "-f" not in dvc_args and "--force" not in dvc_args:
1189
1196
  dvc_args.append("-f")
1190
- try:
1191
- git_cmd = ["git", "pull"]
1192
- if not no_recursive and "--recurse-submodules" not in git_args:
1193
- git_cmd.append("--recurse-submodules")
1194
- subprocess.check_call(git_cmd + git_args)
1195
- except subprocess.CalledProcessError:
1196
- raise_error("Git pull failed")
1197
- typer.echo("DVC pulling")
1198
- if not no_check_auth:
1199
- # Check that our dvc remotes all have our DVC token set for them
1200
- remotes = calkit.dvc.get_remotes()
1201
- for name, url in remotes.items():
1202
- if calkit.dvc.detect_calkit_remote_type(name, url) == "http":
1203
- typer.echo(f"Checking authentication for DVC remote: {name}")
1204
- calkit.dvc.set_remote_auth(remote_name=name)
1205
- if (
1206
- not no_recursive
1207
- and "--recursive" not in dvc_args
1208
- and "-R" not in dvc_args
1209
- ):
1210
- dvc_args.append("--recursive")
1211
- result = calkit.dvc.run_dvc_command(
1212
- ["pull"] + dvc_args,
1213
- lock_timeout=calkit.dvc.DEFAULT_RUN_LOCK_TIMEOUT,
1214
- )
1215
- if result != 0:
1216
- raise_error("DVC pull failed")
1217
- calkit.dvc.zip.sync_all(direction="to-workspace")
1218
- if not no_recursive:
1219
- # Pull DVC in isolated subprojects (those with their own .dvc folder)
1220
- ck_info = calkit.load_calkit_info()
1221
- for sp in ck_info.get("subprojects", []):
1222
- if not isinstance(sp, dict) or not sp.get("path"):
1223
- continue
1224
- sp_path = sp["path"]
1225
- if not os.path.isdir(os.path.join(sp_path, ".dvc")):
1226
- continue
1227
- typer.echo(f"DVC pulling subproject: {sp_path}")
1228
- sp_result = calkit.dvc.run_dvc_command(
1229
- ["pull"] + dvc_args,
1230
- cwd=sp_path,
1231
- lock_timeout=calkit.dvc.DEFAULT_RUN_LOCK_TIMEOUT,
1232
- )
1233
- if sp_result != 0:
1234
- raise_error(f"DVC pull failed for subproject: {sp_path}")
1235
- calkit.dvc.zip.sync_all(direction="to-workspace", wdir=sp_path)
1197
+ if not no_git:
1198
+ typer.echo("Git pulling")
1199
+ try:
1200
+ git_cmd = ["git", "pull"]
1201
+ if not no_recursive and "--recurse-submodules" not in git_args:
1202
+ git_cmd.append("--recurse-submodules")
1203
+ subprocess.check_call(git_cmd + git_args)
1204
+ except subprocess.CalledProcessError:
1205
+ raise_error("Git pull failed")
1206
+ if not no_dvc:
1207
+ typer.echo("DVC pulling")
1208
+ if not no_check_auth:
1209
+ # Check that our dvc remotes all have our DVC token set for them
1210
+ remotes = calkit.dvc.get_remotes()
1211
+ for name, url in remotes.items():
1212
+ if calkit.dvc.detect_calkit_remote_type(name, url) == "http":
1213
+ typer.echo(
1214
+ f"Checking authentication for DVC remote: {name}"
1215
+ )
1216
+ calkit.dvc.set_remote_auth(remote_name=name)
1217
+ if (
1218
+ not no_recursive
1219
+ and "--recursive" not in dvc_args
1220
+ and "-R" not in dvc_args
1221
+ ):
1222
+ dvc_args.append("--recursive")
1223
+ result = calkit.dvc.run_dvc_command(
1224
+ ["pull"] + dvc_args,
1225
+ lock_timeout=calkit.dvc.DEFAULT_RUN_LOCK_TIMEOUT,
1226
+ )
1227
+ if result != 0:
1228
+ raise_error("DVC pull failed")
1229
+ calkit.dvc.zip.sync_all(direction="to-workspace")
1230
+ if not no_recursive:
1231
+ # Pull DVC in isolated subprojects (those with their own .dvc folder)
1232
+ ck_info = calkit.load_calkit_info()
1233
+ for sp in ck_info.get("subprojects", []):
1234
+ if not isinstance(sp, dict) or not sp.get("path"):
1235
+ continue
1236
+ sp_path = sp["path"]
1237
+ if not os.path.isdir(os.path.join(sp_path, ".dvc")):
1238
+ continue
1239
+ typer.echo(f"DVC pulling subproject: {sp_path}")
1240
+ sp_result = calkit.dvc.run_dvc_command(
1241
+ ["pull"] + dvc_args,
1242
+ cwd=sp_path,
1243
+ lock_timeout=calkit.dvc.DEFAULT_RUN_LOCK_TIMEOUT,
1244
+ )
1245
+ if sp_result != 0:
1246
+ raise_error(f"DVC pull failed in subproject: {sp_path}")
1247
+ calkit.dvc.zip.sync_all(direction="to-workspace", wdir=sp_path)
1236
1248
 
1237
1249
 
1238
1250
  @app.command(name="push")
@@ -1284,16 +1296,6 @@ def push(
1284
1296
  raise_error("Git push failed")
1285
1297
 
1286
1298
 
1287
- @app.command(name="sync")
1288
- def sync(
1289
- no_check_auth: Annotated[bool, typer.Option("--no-check-auth")] = False,
1290
- ):
1291
- """Sync the project repo by pulling and then pushing."""
1292
- # TODO: Walk users through merge conflicts if they arise
1293
- pull(no_check_auth=no_check_auth)
1294
- push(no_check_auth=no_check_auth)
1295
-
1296
-
1297
1299
  @app.command(name="ignore")
1298
1300
  def ignore(
1299
1301
  path: Annotated[str, typer.Argument(help="Path to ignore.")],
@@ -1685,6 +1687,29 @@ def _concurrent_scheduler_prepass(
1685
1687
  )
1686
1688
 
1687
1689
 
1690
+ def _get_subproject_targets_for_run(
1691
+ subproject_path: str,
1692
+ targets: list[str] | None,
1693
+ include_dvc_yaml_targets: bool = False,
1694
+ ) -> tuple[bool, list[str] | None]:
1695
+ """Return whether and which subproject stages a run target selects."""
1696
+ if not targets:
1697
+ return True, None
1698
+ sp = Path(subproject_path).as_posix()
1699
+ target_prefixes = {sp, Path(sp).name}
1700
+ if include_dvc_yaml_targets:
1701
+ target_prefixes.add(f"{sp}/dvc.yaml")
1702
+ selected_stages = []
1703
+ for target in targets:
1704
+ target_prefix, separator, stage_name = target.partition(":")
1705
+ if target_prefix not in target_prefixes:
1706
+ continue
1707
+ if not separator or not stage_name:
1708
+ return True, None
1709
+ selected_stages.append(stage_name)
1710
+ return bool(selected_stages), selected_stages or None
1711
+
1712
+
1688
1713
  @app.command(name="run")
1689
1714
  def run(
1690
1715
  targets: Annotated[
@@ -1942,6 +1967,18 @@ def run(
1942
1967
  sp = Path(subproject["path"]).as_posix()
1943
1968
  if not os.path.isdir(sp):
1944
1969
  continue
1970
+ if single_item:
1971
+ sp_selected, sp_targets = _get_subproject_targets_for_run(
1972
+ subproject_path=sp,
1973
+ targets=targets,
1974
+ include_dvc_yaml_targets=not os.path.isdir(
1975
+ os.path.join(sp, ".dvc")
1976
+ ),
1977
+ )
1978
+ if not sp_selected:
1979
+ continue
1980
+ else:
1981
+ sp_targets = None
1945
1982
  os.chdir(sp)
1946
1983
  try:
1947
1984
  sp_ck_info = calkit.load_calkit_info()
@@ -1950,7 +1987,7 @@ def run(
1950
1987
  f"📦 Checking environments for subproject: {sp}"
1951
1988
  )
1952
1989
  sp_env_results = calkit.environments.check_all_in_pipeline(
1953
- ck_info=sp_ck_info, force=force
1990
+ ck_info=sp_ck_info, targets=sp_targets, force=force
1954
1991
  )
1955
1992
  for env_name, sp_result in sp_env_results.items():
1956
1993
  if verbose:
@@ -2181,7 +2218,7 @@ def run(
2181
2218
  file_handler.setFormatter(formatter)
2182
2219
  dvc.log.logger.addHandler(file_handler)
2183
2220
  # Remove newline logging in dvc.repo.reproduce
2184
- dvc.repo.reproduce.logger.setLevel(logging.ERROR)
2221
+ dvc.repo.reproduce.logger.setLevel(logging.WARNING)
2185
2222
  # Disable other misc DVC output
2186
2223
  dvc.ui.ui.write = lambda *args, **kwargs: None
2187
2224
  # Tell `calkit scheduler batch` to resubmit completed jobs under --force;
@@ -13,9 +13,24 @@ from typing_extensions import Annotated
13
13
 
14
14
  import calkit
15
15
  from calkit.cli import AliasGroup, raise_error, warn
16
+ from calkit.cli.sync import sync_app
16
17
 
17
18
  overleaf_app = typer.Typer(cls=AliasGroup, no_args_is_help=True)
18
19
 
20
+ # Shown when a pull from Overleaf fails, most commonly due to an invalid or
21
+ # expired token. Calkit authenticates only with the token in its config, but a
22
+ # stale token cached by the OS credential manager from an earlier version can
23
+ # still interfere, so we point users there as a fallback.
24
+ PULL_FAILED_MESSAGE = (
25
+ "Failed to pull from Overleaf; "
26
+ "check that your Overleaf token is valid\n"
27
+ "Run 'calkit config get overleaf_token' and ensure that it matches one in "
28
+ "your Overleaf account settings (https://overleaf.com/user/settings).\n"
29
+ "If it looks correct but pulling still fails, delete any saved "
30
+ "'git.overleaf.com' credential from your OS credential manager (Windows "
31
+ "Credential Manager, macOS Keychain, or your Linux keyring) and try again."
32
+ )
33
+
19
34
 
20
35
  def _extract_title_from_tex(tex_file_path: str) -> str | None:
21
36
  """Extract the title from a LaTeX file."""
@@ -169,8 +184,6 @@ def import_publication(
169
184
  ] = False,
170
185
  ):
171
186
  """Import a publication from an Overleaf project."""
172
- import git
173
-
174
187
  from calkit.cli.main import ignore as git_ignore
175
188
  from calkit.cli.new import new_latex_stage
176
189
 
@@ -202,16 +215,13 @@ def import_publication(
202
215
  overleaf_dir = os.path.join(".calkit", "overleaf")
203
216
  os.makedirs(overleaf_dir, exist_ok=True)
204
217
  git_ignore(overleaf_dir, no_commit=no_commit)
205
- overleaf_project_dir = os.path.join(overleaf_dir, overleaf_project_id)
206
- git_clone_url = calkit.overleaf.get_git_remote_url(
207
- project_id=overleaf_project_id, token=overleaf_token
208
- )
218
+ overleaf_project_dir = calkit.overleaf.get_project_dir(overleaf_project_id)
209
219
  if os.path.isdir(overleaf_project_dir):
210
220
  warn("This Overleaf project has already been cloned; removing")
211
221
  shutil.rmtree(overleaf_project_dir)
212
222
  # Clone the Overleaf project
213
223
  typer.echo("Cloning Overleaf project")
214
- git.Repo.clone_from(git_clone_url, overleaf_project_dir)
224
+ calkit.overleaf.clone(overleaf_project_id, overleaf_token)
215
225
  # Detect target path if not specified
216
226
  if target_path is None:
217
227
  ol_contents = os.listdir(
@@ -353,6 +363,7 @@ def import_publication(
353
363
  sync(paths=[dest_dir], no_commit=no_commit, push_only=push_only)
354
364
 
355
365
 
366
+ @sync_app.command(name="overleaf")
356
367
  @overleaf_app.command(name="sync")
357
368
  def sync(
358
369
  paths: Annotated[
@@ -428,8 +439,6 @@ def sync(
428
439
  ] = False,
429
440
  ):
430
441
  """Sync folders with Overleaf."""
431
- import git
432
-
433
442
  # TODO: We should probably ensure the pipeline isn't stale
434
443
  # Read all synced folders, fixing legacy schema if applicable
435
444
  overleaf_info = calkit.overleaf.get_sync_info(fix_legacy=True)
@@ -504,32 +513,15 @@ def sync(
504
513
  "or use --auto-commit/-a to automatically commit them."
505
514
  )
506
515
  # Ensure we've cloned the Overleaf project
507
- overleaf_project_dir = os.path.join(
508
- ".calkit", "overleaf", overleaf_project_id
516
+ overleaf_repo = calkit.overleaf.get_repo(
517
+ overleaf_project_id, str(overleaf_token)
509
518
  )
510
- overleaf_remote_url = calkit.overleaf.get_git_remote_url(
511
- project_id=overleaf_project_id, token=str(overleaf_token)
512
- )
513
- if not os.path.isdir(overleaf_project_dir):
514
- overleaf_repo = git.Repo.clone_from(
515
- overleaf_remote_url, to_path=overleaf_project_dir
516
- )
517
- else:
518
- overleaf_repo = calkit.git.get_repo(overleaf_project_dir)
519
519
  # Pull the latest version in the Overleaf project
520
520
  typer.echo("Pulling the latest version from Overleaf")
521
- # Ensure that our current Overleaf remote URL is correct
522
- overleaf_repo.git.remote("set-url", "origin", overleaf_remote_url)
523
521
  try:
524
522
  overleaf_repo.git.pull()
525
523
  except Exception:
526
- raise_error(
527
- "Failed to pull from Overleaf; "
528
- "check that your Overleaf token is valid\n"
529
- "Run 'calkit config get overleaf_token' and ensure that "
530
- "it matches one in your Overleaf account settings "
531
- "(https://overleaf.com/user/settings)"
532
- )
524
+ raise_error(PULL_FAILED_MESSAGE)
533
525
  if resolve:
534
526
  last_sync_commit = resolving_info["last_overleaf_commit"]
535
527
  else:
@@ -608,8 +600,6 @@ def get_status(
608
600
  ] = None,
609
601
  ):
610
602
  """Check the status of folders synced with Overleaf in a project."""
611
- import git
612
-
613
603
  # Read all synced folders, fixing legacy schema if applicable
614
604
  overleaf_info = calkit.overleaf.get_sync_info(fix_legacy=False)
615
605
  if not overleaf_info:
@@ -642,32 +632,18 @@ def get_status(
642
632
  )
643
633
  wdir = path_in_project
644
634
  # Ensure we've cloned the Overleaf project
645
- overleaf_project_dir = os.path.join(
646
- ".calkit", "overleaf", overleaf_project_id
635
+ overleaf_project_dir = calkit.overleaf.get_project_dir(
636
+ overleaf_project_id
647
637
  )
648
- overleaf_remote_url = calkit.overleaf.get_git_remote_url(
649
- project_id=overleaf_project_id, token=str(overleaf_token)
638
+ overleaf_repo = calkit.overleaf.get_repo(
639
+ overleaf_project_id, str(overleaf_token)
650
640
  )
651
- if not os.path.isdir(overleaf_project_dir):
652
- overleaf_repo = git.Repo.clone_from(
653
- overleaf_remote_url, to_path=overleaf_project_dir
654
- )
655
- else:
656
- overleaf_repo = calkit.git.get_repo(overleaf_project_dir)
657
641
  # Pull the latest version in the Overleaf project
658
642
  typer.echo("Pulling the latest version from Overleaf")
659
- # Ensure that our current Overleaf remote URL is correct
660
- overleaf_repo.git.remote("set-url", "origin", overleaf_remote_url)
661
643
  try:
662
644
  overleaf_repo.git.pull()
663
645
  except Exception:
664
- raise_error(
665
- "Failed to pull from Overleaf; "
666
- "check that your Overleaf token is valid\n"
667
- "Run 'calkit config get overleaf_token' and ensure that "
668
- "it matches one in your Overleaf account settings "
669
- "(https://overleaf.com/user/settings)"
670
- )
646
+ raise_error(PULL_FAILED_MESSAGE)
671
647
  last_sync_commit = sync_data.get("last_sync_commit")
672
648
  if last_sync_commit:
673
649
  commits_since = calkit.overleaf.get_commits_since_last_sync(
@@ -0,0 +1,80 @@
1
+ """CLI for syncing."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Callable
6
+
7
+ import typer
8
+ from typing_extensions import Annotated
9
+
10
+ import calkit
11
+ from calkit.cli import AliasGroup, raise_error
12
+
13
+ sync_app = typer.Typer(cls=AliasGroup, no_args_is_help=True)
14
+
15
+
16
+ @sync_app.command(name="git")
17
+ def sync_git(
18
+ no_check_auth: Annotated[bool, typer.Option("--no-check-auth")] = False,
19
+ ) -> None:
20
+ """Sync the Git repository by pulling and then pushing."""
21
+ from calkit.cli.main.core import pull, push
22
+
23
+ try:
24
+ repo = calkit.git.get_repo()
25
+ except Exception:
26
+ raise_error("No Git repository found. Run 'git init' first.")
27
+ if not repo.remotes:
28
+ raise_error(
29
+ "No Git remotes configured. Add a remote with "
30
+ "'git remote add <name> <url>'."
31
+ )
32
+ pull(no_dvc=True, no_check_auth=no_check_auth)
33
+ push(no_dvc=True, no_check_auth=no_check_auth)
34
+
35
+
36
+ @sync_app.command(name="dvc")
37
+ def sync_dvc(
38
+ no_check_auth: Annotated[bool, typer.Option("--no-check-auth")] = False,
39
+ ) -> None:
40
+ """Sync the DVC repository by pulling and then pushing."""
41
+ from calkit.cli.main.core import pull, push
42
+
43
+ try:
44
+ calkit.dvc.get_dvc_repo()
45
+ except Exception:
46
+ raise_error("No DVC repository found. Run 'calkit init' first.")
47
+ if not calkit.dvc.get_remotes():
48
+ raise_error(
49
+ "No DVC remotes configured. Add a remote with "
50
+ "'dvc remote add <name> <url>'."
51
+ )
52
+ pull(no_git=True, no_check_auth=no_check_auth)
53
+ push(no_git=True, no_check_auth=no_check_auth)
54
+
55
+
56
+ @sync_app.command(name="all")
57
+ def sync_all() -> None:
58
+ """Sync all registered systems."""
59
+ from calkit.cli.overleaf import sync as overleaf_sync
60
+
61
+ # Run each known target in a stable order, reporting and collecting any
62
+ # failures. Each target is responsible for raising a clear error if it is
63
+ # not configured, so users calling 'calkit sync <target>' directly get a
64
+ # helpful message.
65
+ sync_funcs: list[tuple[str, Callable[[], None]]] = [
66
+ ("git", sync_git),
67
+ ("dvc", sync_dvc),
68
+ ("overleaf", overleaf_sync),
69
+ ]
70
+ failures = []
71
+ for target_name, sync_func in sync_funcs:
72
+ typer.echo(f"Syncing {target_name}...")
73
+ try:
74
+ sync_func()
75
+ except Exception as e:
76
+ typer.echo(f"Failed to sync {target_name}: {e}", err=True)
77
+ failures.append(target_name)
78
+ # Exit with an error if any target failed so callers can react.
79
+ if failures:
80
+ raise typer.Exit(1)
@@ -30,6 +30,69 @@ def get_git_remote_url(project_id: str, token: str) -> str:
30
30
  return f"https://git:{token}@git.overleaf.com/{project_id}"
31
31
 
32
32
 
33
+ # Git config options that force authentication to use only the token embedded
34
+ # in the Overleaf remote URL, never a credential saved in the operating
35
+ # system's credential store (macOS Keychain, Windows Credential Manager, etc.).
36
+ # An expired token can linger in that store and shadow a freshly set one, which
37
+ # otherwise makes it impossible to recover a project after a token expires.
38
+ _CREDENTIAL_CLONE_OPTIONS = [
39
+ "-c credential.helper=",
40
+ "-c credential.interactive=false",
41
+ ]
42
+
43
+
44
+ def _disable_credential_store(repo: git.Repo) -> None:
45
+ """Configure a cloned Overleaf repo so git authenticates only with the
46
+ token embedded in its remote URL, ignoring the OS credential store.
47
+
48
+ Setting an empty ``credential.helper`` in the repo's local config resets
49
+ the list of helpers inherited from the system and global config, so no
50
+ stale token can be read from (or written to) the credential store.
51
+ """
52
+ repo.git.config("credential.helper", "")
53
+ repo.git.config("credential.interactive", "false")
54
+
55
+
56
+ def get_project_dir(project_id: str) -> str:
57
+ """Return the local directory into which an Overleaf project is cloned,
58
+ relative to the Calkit project working directory.
59
+ """
60
+ return os.path.join(".calkit", "overleaf", project_id)
61
+
62
+
63
+ def clone(project_id: str, token: str) -> git.Repo:
64
+ """Clone an Overleaf project, authenticating only with ``token``."""
65
+ repo = git.Repo.clone_from(
66
+ get_git_remote_url(project_id=project_id, token=token),
67
+ get_project_dir(project_id),
68
+ multi_options=_CREDENTIAL_CLONE_OPTIONS,
69
+ allow_unsafe_options=True,
70
+ )
71
+ _disable_credential_store(repo)
72
+ return repo
73
+
74
+
75
+ def get_repo(project_id: str, token: str) -> git.Repo:
76
+ """Return the Overleaf repo for ``project_id``, cloning it if it does not
77
+ yet exist.
78
+
79
+ When the repo already exists, its remote URL is refreshed so a freshly set
80
+ token takes effect, and credential handling is reset so a stale token from
81
+ the OS credential store is never used.
82
+ """
83
+ dest = get_project_dir(project_id)
84
+ if not os.path.isdir(dest):
85
+ return clone(project_id, token)
86
+ repo = calkit.git.get_repo(dest)
87
+ repo.git.remote(
88
+ "set-url",
89
+ "origin",
90
+ get_git_remote_url(project_id=project_id, token=token),
91
+ )
92
+ _disable_credential_store(repo)
93
+ return repo
94
+
95
+
33
96
  def project_id_to_url(project_id: str) -> str:
34
97
  return f"https://www.overleaf.com/project/{project_id}"
35
98
 
@@ -22,6 +22,7 @@ import calkit.cli.main
22
22
  from calkit.cli.core import complete_stage_names
23
23
  from calkit.cli.main.core import (
24
24
  _get_running_pipeline_status,
25
+ _get_subproject_targets_for_run,
25
26
  _prune_run_logs,
26
27
  _run_dvc_repro,
27
28
  _stage_run_info_from_log_content,
@@ -45,6 +46,52 @@ skipif_windows_mock_scheduler = pytest.mark.skipif(
45
46
  )
46
47
 
47
48
 
49
+ @pytest.mark.parametrize(
50
+ ("subproject_path", "targets", "include_dvc_yaml_targets", "expected"),
51
+ [
52
+ ("sub1", None, False, (True, None)),
53
+ ("sub1", ["parent-stage"], False, (False, None)),
54
+ ("sub1", ["sub1"], False, (True, None)),
55
+ ("nested/sub1", ["sub1:build"], False, (True, ["build"])),
56
+ (
57
+ "nested/sub1",
58
+ ["nested/sub1:build"],
59
+ False,
60
+ (True, ["build"]),
61
+ ),
62
+ (
63
+ "nested/sub1",
64
+ ["nested/sub1/dvc.yaml"],
65
+ True,
66
+ (True, None),
67
+ ),
68
+ (
69
+ "nested/sub1",
70
+ ["nested/sub1/dvc.yaml:build"],
71
+ True,
72
+ (True, ["build"]),
73
+ ),
74
+ (
75
+ "nested/sub1",
76
+ ["nested/sub1/dvc.yaml:build"],
77
+ False,
78
+ (False, None),
79
+ ),
80
+ ],
81
+ )
82
+ def test_get_subproject_targets_for_run(
83
+ subproject_path, targets, include_dvc_yaml_targets, expected
84
+ ):
85
+ assert (
86
+ _get_subproject_targets_for_run(
87
+ subproject_path,
88
+ targets,
89
+ include_dvc_yaml_targets=include_dvc_yaml_targets,
90
+ )
91
+ == expected
92
+ )
93
+
94
+
48
95
  def _repo_test_file(name: str) -> Path:
49
96
  """Find a file in the repository-level ``test`` directory."""
50
97
  for parent in Path(__file__).resolve().parents:
@@ -115,6 +115,83 @@ def test_inline_subproject(tmp_dir):
115
115
  assert "v2" in f.read()
116
116
 
117
117
 
118
+ def test_single_item_skips_unrelated_subproject_preparation(tmp_dir):
119
+ subprocess.check_call(["calkit", "init"])
120
+ os.makedirs("sub1")
121
+ write_ck_info(
122
+ "sub1/calkit.yaml",
123
+ {
124
+ "pipeline": {
125
+ "stages": {
126
+ "subproject-only": make_stage(
127
+ 'echo "subproject" > subproject.txt'
128
+ )
129
+ }
130
+ }
131
+ },
132
+ )
133
+ write_ck_info(
134
+ "calkit.yaml",
135
+ {
136
+ "subprojects": [{"path": "sub1"}],
137
+ "pipeline": {
138
+ "stages": {
139
+ "parent-only": make_stage('echo "parent" > parent.txt')
140
+ }
141
+ },
142
+ },
143
+ )
144
+ result = subprocess.run(
145
+ ["calkit", "run", "--single-item", "parent-only"],
146
+ check=True,
147
+ capture_output=True,
148
+ text=True,
149
+ )
150
+ assert os.path.isfile("parent.txt")
151
+ assert "Checking environments for subproject" not in result.stdout
152
+ selected_subproject = subprocess.run(
153
+ ["calkit", "run", "--single-item", "sub1:subproject-only"],
154
+ check=True,
155
+ capture_output=True,
156
+ text=True,
157
+ )
158
+ assert os.path.isfile("sub1/subproject.txt")
159
+ assert "Checking environments for subproject: sub1" in (
160
+ selected_subproject.stdout
161
+ )
162
+
163
+ # A DVC-style inline target should still prepare the selected subproject.
164
+ write_ck_info(
165
+ "sub1/calkit.yaml",
166
+ {
167
+ "pipeline": {
168
+ "stages": {
169
+ "subproject-only": make_stage(
170
+ 'echo "dvc target" > subproject.txt'
171
+ )
172
+ }
173
+ }
174
+ },
175
+ )
176
+ calkit.pipeline.to_dvc(write=True, manage_gitignore=False)
177
+ selected_inline_dvc_target = subprocess.run(
178
+ [
179
+ "calkit",
180
+ "run",
181
+ "--single-item",
182
+ "sub1/dvc.yaml:subproject-only",
183
+ ],
184
+ check=True,
185
+ capture_output=True,
186
+ text=True,
187
+ )
188
+ with open("sub1/subproject.txt") as f:
189
+ assert "dvc target" in f.read()
190
+ assert "Checking environments for subproject: sub1" in (
191
+ selected_inline_dvc_target.stdout
192
+ )
193
+
194
+
118
195
  def init_isolated_subproject(path, stages):
119
196
  os.makedirs(path, exist_ok=True)
120
197
  subprocess.check_call(["git", "init"], cwd=path)