calkit-python 0.35.5__tar.gz → 0.35.7__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.
- {calkit_python-0.35.5 → calkit_python-0.35.7}/PKG-INFO +1 -1
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/main/core.py +52 -21
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/new.py +74 -21
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/update.py +27 -2
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/dvc.py +9 -8
- calkit_python-0.35.7/calkit/git.py +297 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/jupyterlab/routes.py +2 -4
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/pipeline.py +310 -15
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/releases.py +81 -10
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/server.py +3 -3
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_new.py +91 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_git.py +59 -10
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_pipeline.py +135 -5
- calkit_python-0.35.7/calkit/tests/test_releases.py +150 -0
- calkit_python-0.35.5/calkit/git.py +0 -197
- calkit_python-0.35.5/calkit/tests/test_releases.py +0 -44
- {calkit_python-0.35.5 → calkit_python-0.35.7}/.gitignore +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/.pre-commit-config.yaml +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/.prettierignore +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/.python-version +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/.yarnrc.yml +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/CITATION.cff +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/CODE_OF_CONDUCT.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/CONTRIBUTING.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/LICENSE +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/Makefile +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/README.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/babel.config.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/__main__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/calc.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/check.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/check.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/cloud.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/config.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/describe.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/import_.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/latex.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/list.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/main/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/main/xr.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/office.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/overleaf.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cli/slurm.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/cloud.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/conda.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/config.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/datasets.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/detect.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/docker.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/environments.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/fs.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/github.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/gui.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/invenio.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/julia.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/jupyter.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/jupyterlab/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/package.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/schemas/calkit/package.json.orig +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/schemas/calkit/plugin.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/502.9a2c5772a15466e923ef.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/695.2c41003a452d43d2b358.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/867.a42a046aa5108f54f8fb.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/909.651be47ca47390b78a92.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/946.050af2abf7845cfbdbd2.js.LICENSE.txt +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/b2f1c3efe70cb539d121.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/remoteEntry.c091821b3d7f2d287a67.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/style.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/labextension/static/third-party-licenses.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/licenses.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/magics.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/matlab.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/models/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/models/core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/models/io.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/models/iteration.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/models/pipeline.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/notebooks.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/office.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/ops.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/overleaf.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/main/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/main/test_core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/main/test_xr.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_check.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_config.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_import.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_latex.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_notebooks.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/cli/test_overleaf.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/jupyterlab/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/jupyterlab/test_routes.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/models/__init__.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/models/test_iteration.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/models/test_pipeline.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_detect.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_docker.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_environments.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_fs.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_invenio.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_julia.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_matlab.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_notebooks.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/conftest.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/CNAME +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/apps.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/calculations.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/calkit-yaml.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/cli-reference.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/cloud-integration.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/datasets.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/dependencies.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/environments.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/examples.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/governance.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/help.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/calkit-fragmentation-compendium.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/connect-zenodo.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab/all-green.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab/collect-data-stale.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab/new-env.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab/new-notebook.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab/pipeline-badge.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/jupyterlab-params.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/plos-osi-code-2024-03.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/img/vscode-nb-params.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/index.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/installation.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/jupyterlab.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/local-server.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/notebooks.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/overleaf.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/pipeline/index.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/pipeline/running-and-logging.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/pipeline/slurm.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/quickstart.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/references.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/releases.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/reproducibility.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/existing-project.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/github-actions.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/actions-repo-secrets.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/quick-actions.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/index.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/jupyterlab.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/latex-codespaces.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/office.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/docs/version-control.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/install.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/jest.config.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/jupyter-config/server-config/calkit.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/mkdocs.yml +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/package.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/pyproject.toml +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/schema/plugin.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/scripts/generate-cli-reference.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/scripts/install.ps1 +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/scripts/install.sh +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/scripts/make-calk9.sh +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/scripts/sync-docs.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/__tests__/useQueries.spec.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/calkit-config.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/cell-output-marker.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/commit-dialog.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/environment-editor.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/notebook-registration.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/notebook-toolbar.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/pipeline-status-bar.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/project-info-editor.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/sidebar-settings.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/sidebar.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/components/stage-editor.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/feature-flags.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/file-browser-menu.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/hooks/__tests__/useQueries.test.tsx +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/hooks/useQueries.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/icons.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/index.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/io-tracker.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/pipeline-state.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/queryClient.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/request.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/src/shims-mainmenu.d.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/base.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/cell-output-marker.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/environment-editor.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/environment-selector.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/img/calkit-no-bg.png +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/index.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/index.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/notebook-toolbar.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/pipeline-status-bar.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/style/sidebar.css +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/dvc-md5-dir/osx-arm64.txt +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/nb-julia.ipynb +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/nb-params.ipynb +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/nb-subdir.ipynb +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/pipeline.ipynb +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/script.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/test/test-log.log +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/tsconfig.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/tsconfig.test.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/.gitignore +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/README.md +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/jupyter_server_test_config.py +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/package.json +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/playwright.config.js +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/tests/calkit.spec.ts +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/ui-tests/yarn.lock +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/uv.lock +0 -0
- {calkit_python-0.35.5 → calkit_python-0.35.7}/yarn.lock +0 -0
|
@@ -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
|
|
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
|
|
|
@@ -252,10 +252,12 @@ def get_status(
|
|
|
252
252
|
):
|
|
253
253
|
"""View status (project, version control, and/or pipeline)."""
|
|
254
254
|
ck_info = calkit.load_calkit_info()
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
255
|
+
# If there's anything in ck_info and this isn't a Git repo, initialize one
|
|
256
|
+
if ck_info:
|
|
257
|
+
try:
|
|
258
|
+
git.Repo()
|
|
259
|
+
except InvalidGitRepositoryError:
|
|
260
|
+
git.Repo.init()
|
|
259
261
|
valid_categories = ["project", "git", "dvc", "pipeline"]
|
|
260
262
|
if categories is not None:
|
|
261
263
|
for category in categories:
|
|
@@ -266,11 +268,22 @@ def get_status(
|
|
|
266
268
|
)
|
|
267
269
|
else:
|
|
268
270
|
categories = valid_categories
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
calkit.
|
|
272
|
-
|
|
273
|
-
|
|
271
|
+
pipeline_status = None
|
|
272
|
+
if "pipeline" in categories or "dvc" in categories:
|
|
273
|
+
pipeline_status = calkit.pipeline.get_status(
|
|
274
|
+
ck_info=ck_info,
|
|
275
|
+
targets=targets,
|
|
276
|
+
check_environments=True,
|
|
277
|
+
clean_notebooks=True,
|
|
278
|
+
compile_to_dvc=True,
|
|
279
|
+
)
|
|
280
|
+
for error in pipeline_status.errors:
|
|
281
|
+
warn(error)
|
|
282
|
+
if pipeline_status.failed_environment_checks:
|
|
283
|
+
warn(
|
|
284
|
+
"Failed pipeline environment checks for: "
|
|
285
|
+
+ ", ".join(pipeline_status.failed_environment_checks)
|
|
286
|
+
)
|
|
274
287
|
if "project" in categories:
|
|
275
288
|
print_sep("Project")
|
|
276
289
|
# Print latest status
|
|
@@ -307,10 +320,32 @@ def get_status(
|
|
|
307
320
|
typer.echo()
|
|
308
321
|
if "pipeline" in categories or "dvc" in categories:
|
|
309
322
|
print_sep("Pipeline")
|
|
310
|
-
|
|
311
|
-
if
|
|
312
|
-
|
|
313
|
-
|
|
323
|
+
# Nicely format the results from pipeline status
|
|
324
|
+
if pipeline_status and pipeline_status.errors:
|
|
325
|
+
typer.echo("Pipeline status unavailable due to errors.")
|
|
326
|
+
elif pipeline_status and pipeline_status.is_stale:
|
|
327
|
+
for stage_name in pipeline_status.stale_stage_names:
|
|
328
|
+
stale_stage = pipeline_status.stale_stages.get(stage_name)
|
|
329
|
+
if stale_stage is None:
|
|
330
|
+
continue
|
|
331
|
+
typer.echo(f"{typer.style(stage_name, fg='yellow')}:")
|
|
332
|
+
# Show stale outputs for this stage
|
|
333
|
+
if stale_stage.stale_outputs:
|
|
334
|
+
typer.echo(" stale outputs:")
|
|
335
|
+
for output_path in stale_stage.stale_outputs:
|
|
336
|
+
typer.echo(f" - {output_path}")
|
|
337
|
+
# Show modified outputs from this stage
|
|
338
|
+
if stale_stage.modified_outputs:
|
|
339
|
+
typer.echo(" modified outputs:")
|
|
340
|
+
for output_path in stale_stage.modified_outputs:
|
|
341
|
+
typer.echo(f" - {output_path}")
|
|
342
|
+
# Show modified inputs making the stage stale
|
|
343
|
+
if stale_stage.modified_inputs:
|
|
344
|
+
typer.echo(" modified inputs:")
|
|
345
|
+
for input_path in stale_stage.modified_inputs:
|
|
346
|
+
typer.echo(f" - {input_path}")
|
|
347
|
+
elif pipeline_status:
|
|
348
|
+
typer.echo("Pipeline is up to date.")
|
|
314
349
|
|
|
315
350
|
|
|
316
351
|
@app.command(name="diff")
|
|
@@ -403,8 +438,7 @@ def add(
|
|
|
403
438
|
raise_error("Not currently in a Git repo; run `calkit init` first")
|
|
404
439
|
repo = git.Repo()
|
|
405
440
|
try:
|
|
406
|
-
|
|
407
|
-
dvc_repo = dvc.repo.Repo()
|
|
441
|
+
dvc_repo = get_dvc_repo()
|
|
408
442
|
except NotDvcRepoError:
|
|
409
443
|
warn("DVC not initialized yet; initializing")
|
|
410
444
|
dvc_repo = dvc.repo.Repo.init()
|
|
@@ -1139,10 +1173,8 @@ def run(
|
|
|
1139
1173
|
os.environ.pop("CALKIT_PIPELINE_RUNNING", None)
|
|
1140
1174
|
raise_error(f"Pipeline compilation failed: {e}")
|
|
1141
1175
|
# Initialize DVC repo if necessary
|
|
1142
|
-
# Register the ck:// scheme before accessing DVC repo
|
|
1143
|
-
register_ck_scheme()
|
|
1144
1176
|
try:
|
|
1145
|
-
|
|
1177
|
+
get_dvc_repo()
|
|
1146
1178
|
except Exception:
|
|
1147
1179
|
if not quiet:
|
|
1148
1180
|
typer.echo("Initializing DVC repo")
|
|
@@ -1203,8 +1235,7 @@ def run(
|
|
|
1203
1235
|
git_staged_files_before = calkit.git.get_staged_files(repo=repo)
|
|
1204
1236
|
git_untracked_files_before = calkit.git.get_untracked_files(repo=repo)
|
|
1205
1237
|
# Get status of DVC repo before running
|
|
1206
|
-
|
|
1207
|
-
dvc_repo = dvc.repo.Repo()
|
|
1238
|
+
dvc_repo = get_dvc_repo()
|
|
1208
1239
|
dvc_status_before = dvc_repo.status()
|
|
1209
1240
|
dvc_data_status_before = dvc_repo.data_status()
|
|
1210
1241
|
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
|
|
@@ -2523,6 +2524,26 @@ def new_release(
|
|
|
2523
2524
|
repo = git.Repo()
|
|
2524
2525
|
if name in repo.tags:
|
|
2525
2526
|
raise_error(f"Git tag with name '{name}' already exists")
|
|
2527
|
+
# Check that the pipeline is up-to-date
|
|
2528
|
+
typer.echo("Checking pipeline is up-to-date for release")
|
|
2529
|
+
status = calkit.pipeline.get_status(
|
|
2530
|
+
ck_info=ck_info,
|
|
2531
|
+
check_environments=True,
|
|
2532
|
+
clean_notebooks=True,
|
|
2533
|
+
compile_to_dvc=True,
|
|
2534
|
+
)
|
|
2535
|
+
if status.errors:
|
|
2536
|
+
raise_error("Pipeline checks failed: " + "; ".join(status.errors))
|
|
2537
|
+
if status.failed_environment_checks:
|
|
2538
|
+
raise_error(
|
|
2539
|
+
"Pipeline environment checks failed for: "
|
|
2540
|
+
+ ", ".join(status.failed_environment_checks)
|
|
2541
|
+
)
|
|
2542
|
+
if status.is_stale:
|
|
2543
|
+
raise_error(
|
|
2544
|
+
"Pipeline is not up-to-date; out-of-date stages: "
|
|
2545
|
+
+ ", ".join(status.stale_stage_names)
|
|
2546
|
+
)
|
|
2526
2547
|
release_dir = f".calkit/releases/{name}"
|
|
2527
2548
|
release_files_dir = release_dir + "/files"
|
|
2528
2549
|
os.makedirs(release_files_dir, exist_ok=True)
|
|
@@ -2543,8 +2564,14 @@ def new_release(
|
|
|
2543
2564
|
all_paths = calkit.releases.ls_files()
|
|
2544
2565
|
typer.echo(f"Adding files to {zip_path}")
|
|
2545
2566
|
with zipfile.ZipFile(zip_path, "w") as zipf:
|
|
2546
|
-
|
|
2547
|
-
|
|
2567
|
+
calkit.releases.add_paths_to_zip(zipf, all_paths)
|
|
2568
|
+
typer.echo("Checking extracted project release archive")
|
|
2569
|
+
try:
|
|
2570
|
+
calkit.releases.check_project_release_archive(
|
|
2571
|
+
zip_path, verbose=verbose
|
|
2572
|
+
)
|
|
2573
|
+
except Exception as e:
|
|
2574
|
+
raise_error(str(e))
|
|
2548
2575
|
title = ck_info.get("title")
|
|
2549
2576
|
if title is None:
|
|
2550
2577
|
warn("Project has no title")
|
|
@@ -2888,10 +2915,12 @@ def new_release(
|
|
|
2888
2915
|
+ new_lines[first_content_line_index:]
|
|
2889
2916
|
)
|
|
2890
2917
|
readme_txt = "\n".join(new_lines)
|
|
2891
|
-
with open("README.md", "w") as f:
|
|
2892
|
-
f.write(readme_txt)
|
|
2893
2918
|
if not dry_run:
|
|
2894
|
-
|
|
2919
|
+
with open("README.md", "w") as f:
|
|
2920
|
+
f.write(readme_txt)
|
|
2921
|
+
repo.git.add("README.md")
|
|
2922
|
+
else:
|
|
2923
|
+
typer.echo(f"Would have updated README.md to:\n{readme_txt}")
|
|
2895
2924
|
# Create Git tag
|
|
2896
2925
|
git_tag_message = release_description
|
|
2897
2926
|
if git_tag_message is None:
|
|
@@ -2919,13 +2948,15 @@ def new_release(
|
|
|
2919
2948
|
ck_info["releases"] = releases
|
|
2920
2949
|
# Create CITATION.cff file
|
|
2921
2950
|
if release_type == "project":
|
|
2922
|
-
typer.echo("Writing CITATION.cff")
|
|
2923
2951
|
cff = calkit.releases.create_citation_cff(
|
|
2924
2952
|
ck_info=ck_info, release_name=name, release_date=release_date
|
|
2925
2953
|
)
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2954
|
+
if dry_run:
|
|
2955
|
+
typer.echo(f"Would write CITATION.cff:\n{cff}")
|
|
2956
|
+
else:
|
|
2957
|
+
typer.echo("Writing CITATION.cff")
|
|
2958
|
+
with open("CITATION.cff", "w") as f:
|
|
2959
|
+
calkit.ryaml.dump(cff, f)
|
|
2929
2960
|
repo.git.add("CITATION.cff")
|
|
2930
2961
|
# Add to references so it can be cited
|
|
2931
2962
|
typer.echo("Adding BibTeX entry to references")
|
|
@@ -2944,28 +2975,50 @@ def new_release(
|
|
|
2944
2975
|
reflib = bibtexparser.load(f)
|
|
2945
2976
|
else:
|
|
2946
2977
|
reflib = bibtexparser.bibdatabase.BibDatabase()
|
|
2978
|
+
bibtex_doi = doi
|
|
2979
|
+
if bibtex_doi is None and dry_run:
|
|
2980
|
+
mock_suffix = "".join(
|
|
2981
|
+
ch if (ch.isalnum() or ch in ".-_") else "-"
|
|
2982
|
+
for ch in name.lower()
|
|
2983
|
+
)
|
|
2984
|
+
bibtex_doi = f"10.0000/calkit-dry-run.{mock_suffix}"
|
|
2947
2985
|
invenio_bibtex = calkit.releases.create_bibtex(
|
|
2948
2986
|
authors=authors,
|
|
2949
2987
|
release_date=release_date,
|
|
2950
2988
|
title=title, # type: ignore
|
|
2951
|
-
doi=
|
|
2989
|
+
doi=bibtex_doi,
|
|
2952
2990
|
record_id=record_id, # type: ignore
|
|
2953
2991
|
service=to, # type: ignore
|
|
2954
2992
|
)
|
|
2955
|
-
|
|
2993
|
+
new_entries = bibtexparser.loads(invenio_bibtex).entries
|
|
2994
|
+
if not new_entries:
|
|
2995
|
+
raise ValueError("Failed to parse generated BibTeX entry")
|
|
2996
|
+
new_entry = new_entries[0]
|
|
2956
2997
|
# Search through entries for one with the same DOI, and replace if
|
|
2957
2998
|
# there is a match
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2999
|
+
new_doi = new_entry.get("doi")
|
|
3000
|
+
matching_indices = []
|
|
3001
|
+
if new_doi:
|
|
3002
|
+
matching_indices = [
|
|
3003
|
+
n
|
|
3004
|
+
for n, entry in enumerate(reflib.entries)
|
|
3005
|
+
if entry.get("doi") == new_doi
|
|
3006
|
+
]
|
|
3007
|
+
if matching_indices:
|
|
3008
|
+
typer.echo(
|
|
3009
|
+
"Found matching DOI in existing references "
|
|
3010
|
+
f"({len(matching_indices)} entr"
|
|
3011
|
+
f"{'y' if len(matching_indices) == 1 else 'ies'}); "
|
|
3012
|
+
"replacing"
|
|
3013
|
+
)
|
|
3014
|
+
for n in reversed(matching_indices):
|
|
3015
|
+
_ = reflib.entries.pop(n)
|
|
2965
3016
|
reflib.entries.append(new_entry)
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
3017
|
+
if dry_run:
|
|
3018
|
+
typer.echo(f"Would write updated references to {ref_path}")
|
|
3019
|
+
else:
|
|
3020
|
+
with open(ref_path, "w") as f:
|
|
3021
|
+
bibtexparser.dump(reflib, f)
|
|
2969
3022
|
repo.git.add(ref_path)
|
|
2970
3023
|
except Exception as e:
|
|
2971
3024
|
warn(f"Failed to add to references: {e}")
|
|
@@ -12,6 +12,7 @@ import typer
|
|
|
12
12
|
from typing_extensions import Annotated
|
|
13
13
|
|
|
14
14
|
import calkit
|
|
15
|
+
import calkit.pipeline
|
|
15
16
|
from calkit.cli import raise_error
|
|
16
17
|
|
|
17
18
|
update_app = typer.Typer(no_args_is_help=True)
|
|
@@ -156,6 +157,26 @@ def update_release(
|
|
|
156
157
|
record_id = release.get("record_id")
|
|
157
158
|
if record_id is None:
|
|
158
159
|
raise_error("Release has no record ID")
|
|
160
|
+
if publish or reupload:
|
|
161
|
+
typer.echo("Checking pipeline is up-to-date for release update")
|
|
162
|
+
status = calkit.pipeline.get_status(
|
|
163
|
+
ck_info=ck_info,
|
|
164
|
+
check_environments=True,
|
|
165
|
+
clean_notebooks=True,
|
|
166
|
+
compile_to_dvc=True,
|
|
167
|
+
)
|
|
168
|
+
if status.errors:
|
|
169
|
+
raise_error("Pipeline checks failed: " + "; ".join(status.errors))
|
|
170
|
+
if status.failed_environment_checks:
|
|
171
|
+
raise_error(
|
|
172
|
+
"Pipeline environment checks failed for: "
|
|
173
|
+
+ ", ".join(status.failed_environment_checks)
|
|
174
|
+
)
|
|
175
|
+
if status.is_stale:
|
|
176
|
+
raise_error(
|
|
177
|
+
"Pipeline is not up-to-date; out-of-date stages: "
|
|
178
|
+
+ ", ".join(status.stale_stage_names)
|
|
179
|
+
)
|
|
159
180
|
if publish:
|
|
160
181
|
try:
|
|
161
182
|
calkit.invenio.post(
|
|
@@ -245,8 +266,12 @@ def update_release(
|
|
|
245
266
|
all_paths = calkit.releases.ls_files()
|
|
246
267
|
typer.echo(f"Adding files to {zip_path}")
|
|
247
268
|
with zipfile.ZipFile(zip_path, "w") as zipf:
|
|
248
|
-
|
|
249
|
-
|
|
269
|
+
calkit.releases.add_paths_to_zip(zipf, all_paths)
|
|
270
|
+
typer.echo("Checking project release archive")
|
|
271
|
+
try:
|
|
272
|
+
calkit.releases.check_project_release_archive(zip_path)
|
|
273
|
+
except Exception as e:
|
|
274
|
+
raise_error(str(e))
|
|
250
275
|
try:
|
|
251
276
|
files_in_record = [
|
|
252
277
|
entry["key"]
|
|
@@ -9,6 +9,7 @@ import os
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
+
import dvc.repo
|
|
12
13
|
import git
|
|
13
14
|
from dvc.utils.objects import cached_property
|
|
14
15
|
from dvc_objects.fs.base import ObjectFileSystem
|
|
@@ -234,6 +235,12 @@ def run_dvc_cli(argv: list[str] | None = None) -> int:
|
|
|
234
235
|
return dvc_main(argv)
|
|
235
236
|
|
|
236
237
|
|
|
238
|
+
def get_dvc_repo(wdir: str | None = None) -> dvc.repo.Repo:
|
|
239
|
+
"""Return a DVC repo with ``ck://`` scheme support registered."""
|
|
240
|
+
register_ck_scheme()
|
|
241
|
+
return dvc.repo.Repo(wdir)
|
|
242
|
+
|
|
243
|
+
|
|
237
244
|
def run_dvc_command(argv: list[str], cwd: str | None = None) -> int:
|
|
238
245
|
"""Run a DVC command, optionally in a specific working directory.
|
|
239
246
|
|
|
@@ -367,14 +374,10 @@ def get_remotes(wdir: str | None = None) -> dict[str, str]:
|
|
|
367
374
|
"""Get a dictionary of DVC remotes, keyed by name, with URL as the
|
|
368
375
|
value.
|
|
369
376
|
"""
|
|
370
|
-
import dvc.repo
|
|
371
377
|
from dvc.exceptions import NotDvcRepoError
|
|
372
378
|
|
|
373
|
-
# Ensure DVC recognizes ck:// remotes when validating config.
|
|
374
|
-
register_ck_scheme()
|
|
375
|
-
repo_path = wdir or "."
|
|
376
379
|
try:
|
|
377
|
-
repo =
|
|
380
|
+
repo = get_dvc_repo(wdir)
|
|
378
381
|
except NotDvcRepoError:
|
|
379
382
|
return {}
|
|
380
383
|
try:
|
|
@@ -403,9 +406,7 @@ def list_files(wdir: str | None = None, recursive=True) -> list[dict]:
|
|
|
403
406
|
"""Return a list with all files in DVC, including their path and md5
|
|
404
407
|
checksum.
|
|
405
408
|
"""
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
dvc_repo = dvc.repo.Repo(wdir)
|
|
409
|
+
dvc_repo = get_dvc_repo(wdir)
|
|
409
410
|
return dvc_repo.ls(".", dvc_only=True, recursive=recursive)
|
|
410
411
|
|
|
411
412
|
|
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Git-related functionality."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from os import PathLike
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import git
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_staged_files(
|
|
13
|
+
path: str | None = None, repo: git.Repo | None = None
|
|
14
|
+
) -> list[str]:
|
|
15
|
+
"""Get a list of staged files for the repo at ``path`` or the provided
|
|
16
|
+
repo.
|
|
17
|
+
"""
|
|
18
|
+
if repo is None:
|
|
19
|
+
repo = git.Repo(path)
|
|
20
|
+
cmd = ["--staged", "--name-only"]
|
|
21
|
+
if path is not None:
|
|
22
|
+
cmd.append(path)
|
|
23
|
+
diff = repo.git.diff(cmd)
|
|
24
|
+
paths = diff.split("\n")
|
|
25
|
+
return [p for p in paths if p]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get_changed_files(
|
|
29
|
+
path: str | None = None, repo: git.Repo | None = None
|
|
30
|
+
) -> list[str]:
|
|
31
|
+
"""Get a list of files that have been changed but not staged."""
|
|
32
|
+
if repo is None:
|
|
33
|
+
repo = git.Repo(path)
|
|
34
|
+
return [
|
|
35
|
+
item.a_path
|
|
36
|
+
for item in repo.index.diff(None)
|
|
37
|
+
if item.a_path is not None
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def get_untracked_files(
|
|
42
|
+
path: str | None = None, repo: git.Repo | None = None
|
|
43
|
+
) -> list[str]:
|
|
44
|
+
"""Get a list of untracked files."""
|
|
45
|
+
if repo is None:
|
|
46
|
+
repo = git.Repo(path)
|
|
47
|
+
return repo.untracked_files
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_staged_files_with_status(
|
|
51
|
+
path: str | None = None, repo: git.Repo | None = None
|
|
52
|
+
) -> list[dict]:
|
|
53
|
+
if repo is None:
|
|
54
|
+
repo = git.Repo(path)
|
|
55
|
+
cmd = ["--staged", "--name-status"]
|
|
56
|
+
if path is not None:
|
|
57
|
+
cmd.append(path)
|
|
58
|
+
diff = repo.git.diff(cmd)
|
|
59
|
+
paths = diff.split("\n")
|
|
60
|
+
res = []
|
|
61
|
+
for pathi in paths:
|
|
62
|
+
# Make sure line is not empty, e.g., a trailing newline
|
|
63
|
+
if pathi:
|
|
64
|
+
status, p = pathi.split("\t")
|
|
65
|
+
res.append({"status": status, "path": p})
|
|
66
|
+
return res
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def ls_files(repo: git.Repo, *args, **kwargs) -> list[str]:
|
|
70
|
+
"""Get a list of all files tracked by git."""
|
|
71
|
+
output = repo.git.ls_files(*args, **kwargs)
|
|
72
|
+
return [f for f in output.split("\n") if f]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _resolve_repo_and_ignore_path(
|
|
76
|
+
repo: git.Repo, path: str | PathLike
|
|
77
|
+
) -> tuple[git.Repo, str]:
|
|
78
|
+
"""Resolve which repo should own ignore rules for ``path``."""
|
|
79
|
+
# Normalize target path to absolute from the current repo root.
|
|
80
|
+
repo_root = Path(repo.working_dir).resolve()
|
|
81
|
+
path_obj = Path(path)
|
|
82
|
+
if path_obj.is_absolute():
|
|
83
|
+
abs_path = path_obj.resolve()
|
|
84
|
+
else:
|
|
85
|
+
abs_path = (repo_root / path_obj).resolve()
|
|
86
|
+
# If the path is inside a submodule, use that repo and relative path.
|
|
87
|
+
for submodule in repo.submodules:
|
|
88
|
+
submodule_root = (repo_root / submodule.path).resolve()
|
|
89
|
+
if abs_path == submodule_root:
|
|
90
|
+
continue
|
|
91
|
+
if abs_path.is_relative_to(submodule_root):
|
|
92
|
+
sub_repo = submodule.module()
|
|
93
|
+
rel_path = abs_path.relative_to(submodule_root).as_posix()
|
|
94
|
+
return sub_repo, rel_path
|
|
95
|
+
# Fall back to a repo-relative path when possible.
|
|
96
|
+
try:
|
|
97
|
+
rel_path = abs_path.relative_to(repo_root).as_posix()
|
|
98
|
+
except ValueError:
|
|
99
|
+
rel_path = path_obj.as_posix()
|
|
100
|
+
return repo, rel_path
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _get_matching_gitignore_details(
|
|
104
|
+
repo: git.Repo, path: str
|
|
105
|
+
) -> tuple[Path | None, str | None]:
|
|
106
|
+
"""Return the repo-local gitignore file and pattern matching ``path``."""
|
|
107
|
+
try:
|
|
108
|
+
check_ignore = repo.git.check_ignore("-v", "--", path)
|
|
109
|
+
except git.GitCommandError:
|
|
110
|
+
return None, None
|
|
111
|
+
line = check_ignore.splitlines()[0]
|
|
112
|
+
try:
|
|
113
|
+
source_info, _ = line.split("\t", 1)
|
|
114
|
+
source_path, _, pattern = source_info.rsplit(":", 2)
|
|
115
|
+
except ValueError:
|
|
116
|
+
return None, None
|
|
117
|
+
if not source_path.endswith(".gitignore"):
|
|
118
|
+
return None, pattern
|
|
119
|
+
gitignore_path = (Path(repo.working_dir) / source_path).resolve()
|
|
120
|
+
try:
|
|
121
|
+
gitignore_path.relative_to(Path(repo.working_dir).resolve())
|
|
122
|
+
except ValueError:
|
|
123
|
+
return None, pattern
|
|
124
|
+
return gitignore_path, pattern
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def ensure_path_is_ignored(
|
|
128
|
+
repo: git.Repo, path: str | PathLike
|
|
129
|
+
) -> None | bool:
|
|
130
|
+
"""Ensure that the given path is ignored by Git.
|
|
131
|
+
|
|
132
|
+
Returns True if ``.gitignore`` was modified.
|
|
133
|
+
"""
|
|
134
|
+
# Resolve whether the ignore rule belongs to this repo or a submodule.
|
|
135
|
+
target_repo, target_path = _resolve_repo_and_ignore_path(repo, path)
|
|
136
|
+
# No-op if Git already ignores this path.
|
|
137
|
+
if target_repo.ignored(target_path):
|
|
138
|
+
return
|
|
139
|
+
# Read gitignore first to check if the path is already ignored
|
|
140
|
+
# If not, we don't want to add a line for it since it was added
|
|
141
|
+
# TODO: Add an option to remove cached (`git rm --cached`)
|
|
142
|
+
gitignore_path = os.path.join(target_repo.working_dir, ".gitignore")
|
|
143
|
+
if os.path.isfile(gitignore_path):
|
|
144
|
+
with open(gitignore_path) as f:
|
|
145
|
+
gitignore_txt = f.read()
|
|
146
|
+
lines = gitignore_txt.splitlines()
|
|
147
|
+
if target_path in lines:
|
|
148
|
+
return
|
|
149
|
+
with open(gitignore_path, "a") as f:
|
|
150
|
+
if (
|
|
151
|
+
os.path.isfile(gitignore_path)
|
|
152
|
+
and os.path.getsize(gitignore_path) > 0
|
|
153
|
+
):
|
|
154
|
+
f.write("\n")
|
|
155
|
+
f.write(f"{target_path}\n")
|
|
156
|
+
return True
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def ensure_path_is_not_ignored(
|
|
160
|
+
repo: git.Repo, path: str | PathLike
|
|
161
|
+
) -> None | bool:
|
|
162
|
+
"""Ensure a path is not ignored by Git."""
|
|
163
|
+
# Resolve whether the unignore rule belongs to this repo or a submodule.
|
|
164
|
+
target_repo, target_path = _resolve_repo_and_ignore_path(repo, path)
|
|
165
|
+
# No-op if Git does not ignore this path.
|
|
166
|
+
if not target_repo.ignored(target_path):
|
|
167
|
+
return
|
|
168
|
+
matching_gitignore_path, matched_pattern = _get_matching_gitignore_details(
|
|
169
|
+
target_repo, target_path
|
|
170
|
+
)
|
|
171
|
+
if matching_gitignore_path is not None:
|
|
172
|
+
gitignore_path = matching_gitignore_path.as_posix()
|
|
173
|
+
path_for_gitignore = (
|
|
174
|
+
(Path(target_repo.working_dir) / target_path)
|
|
175
|
+
.resolve()
|
|
176
|
+
.relative_to(matching_gitignore_path.parent.resolve())
|
|
177
|
+
.as_posix()
|
|
178
|
+
)
|
|
179
|
+
else:
|
|
180
|
+
gitignore_path = os.path.join(target_repo.working_dir, ".gitignore")
|
|
181
|
+
path_for_gitignore = target_path
|
|
182
|
+
if not os.path.isfile(gitignore_path):
|
|
183
|
+
with open(gitignore_path, "w") as f:
|
|
184
|
+
f.write(f"!{path_for_gitignore}\n")
|
|
185
|
+
return True
|
|
186
|
+
with open(gitignore_path) as f:
|
|
187
|
+
gitignore_txt = f.read()
|
|
188
|
+
lines = gitignore_txt.splitlines()
|
|
189
|
+
direct_rule_variants = [path_for_gitignore, f"/{path_for_gitignore}"]
|
|
190
|
+
if matched_pattern is not None and matched_pattern.startswith("/"):
|
|
191
|
+
no_ignore_line = f"!/{path_for_gitignore}"
|
|
192
|
+
else:
|
|
193
|
+
no_ignore_line = f"!{path_for_gitignore}"
|
|
194
|
+
path_parts = Path(path_for_gitignore).parts
|
|
195
|
+
|
|
196
|
+
def ancestor_requires_recursive_unignore() -> bool:
|
|
197
|
+
"""Return True if any ancestor-level ignore rule would block this path.
|
|
198
|
+
|
|
199
|
+
This includes explicit directory ignores (e.g. 'dir/' or '/dir/')
|
|
200
|
+
as well as ancestor-based glob patterns like 'dir/*' or '/dir/*',
|
|
201
|
+
i.e., any rule that would prevent reaching the nested path without
|
|
202
|
+
adding recursive unignore patterns.
|
|
203
|
+
"""
|
|
204
|
+
for i in range(1, len(path_parts)):
|
|
205
|
+
ancestor = "/".join(path_parts[:i])
|
|
206
|
+
if (
|
|
207
|
+
ancestor in lines
|
|
208
|
+
or f"/{ancestor}" in lines
|
|
209
|
+
or f"{ancestor}/" in lines
|
|
210
|
+
or f"/{ancestor}/" in lines
|
|
211
|
+
or f"{ancestor}/*" in lines
|
|
212
|
+
or f"/{ancestor}/*" in lines
|
|
213
|
+
):
|
|
214
|
+
return True
|
|
215
|
+
return False
|
|
216
|
+
|
|
217
|
+
if len(path_parts) == 1:
|
|
218
|
+
# Simple (non-nested) path: remove the direct ignore rule, or add a
|
|
219
|
+
# negation if the ignore comes from a glob or other pattern
|
|
220
|
+
direct_rule = next(
|
|
221
|
+
(rule for rule in direct_rule_variants if rule in lines), None
|
|
222
|
+
)
|
|
223
|
+
if direct_rule is not None:
|
|
224
|
+
lines.remove(direct_rule)
|
|
225
|
+
else:
|
|
226
|
+
# Remove any stale negation and re-append at the end so it takes
|
|
227
|
+
# precedence over any later re-ignore rule
|
|
228
|
+
if no_ignore_line in lines:
|
|
229
|
+
lines.remove(no_ignore_line)
|
|
230
|
+
lines.append(no_ignore_line)
|
|
231
|
+
else:
|
|
232
|
+
# Nested path: only apply recursive un-ignore rules when an ancestor
|
|
233
|
+
# directory is explicitly ignored
|
|
234
|
+
# Otherwise, remove a direct ignore
|
|
235
|
+
# rule for this path or add a simple negation if needed
|
|
236
|
+
removed_direct_rule = False
|
|
237
|
+
direct_rule = next(
|
|
238
|
+
(rule for rule in direct_rule_variants if rule in lines), None
|
|
239
|
+
)
|
|
240
|
+
if direct_rule is not None:
|
|
241
|
+
lines.remove(direct_rule)
|
|
242
|
+
removed_direct_rule = True
|
|
243
|
+
if ancestor_requires_recursive_unignore():
|
|
244
|
+
# Git will not traverse into a directory excluded by a "dir/"
|
|
245
|
+
# pattern, so a bare "!dir/sub/file" negation has no effect.
|
|
246
|
+
# We need to:
|
|
247
|
+
# 1. Convert any "ancestor/" (or "ancestor") exclude to
|
|
248
|
+
# "ancestor/*" so git traverses the directory while still
|
|
249
|
+
# ignoring direct children by default.
|
|
250
|
+
# 2. Add "!ancestor/" rules for intermediate directories.
|
|
251
|
+
# 3. Add "ancestor/*" re-ignore rules for each intermediate dir.
|
|
252
|
+
# 4. Add "!target_path" for the specific file.
|
|
253
|
+
for i in range(1, len(path_parts)):
|
|
254
|
+
ancestor = "/".join(path_parts[:i])
|
|
255
|
+
reignore_glob = f"{ancestor}/*"
|
|
256
|
+
if f"{ancestor}/" in lines:
|
|
257
|
+
idx = lines.index(f"{ancestor}/")
|
|
258
|
+
lines[idx] = reignore_glob
|
|
259
|
+
elif f"/{ancestor}/" in lines:
|
|
260
|
+
idx = lines.index(f"/{ancestor}/")
|
|
261
|
+
lines[idx] = f"/{ancestor}/*"
|
|
262
|
+
elif ancestor in lines:
|
|
263
|
+
idx = lines.index(ancestor)
|
|
264
|
+
lines[idx] = reignore_glob
|
|
265
|
+
elif f"/{ancestor}" in lines:
|
|
266
|
+
idx = lines.index(f"/{ancestor}")
|
|
267
|
+
lines[idx] = f"/{ancestor}/*"
|
|
268
|
+
no_ignore_dir = f"!{ancestor}/"
|
|
269
|
+
anchored_no_ignore_dir = f"!/{ancestor}/"
|
|
270
|
+
# The first ancestor does not need an explicit un-ignore once
|
|
271
|
+
# converted to "ancestor/*". Deeper ancestors do.
|
|
272
|
+
if i > 1:
|
|
273
|
+
# Remove stale entry and re-append so it takes precedence
|
|
274
|
+
if no_ignore_dir in lines:
|
|
275
|
+
lines.remove(no_ignore_dir)
|
|
276
|
+
elif anchored_no_ignore_dir in lines:
|
|
277
|
+
lines.remove(anchored_no_ignore_dir)
|
|
278
|
+
lines.append(no_ignore_dir)
|
|
279
|
+
if (
|
|
280
|
+
reignore_glob not in lines
|
|
281
|
+
and f"/{ancestor}/*" not in lines
|
|
282
|
+
):
|
|
283
|
+
lines.append(reignore_glob)
|
|
284
|
+
# Remove stale negation and re-append at the end so it takes
|
|
285
|
+
# precedence over any later re-ignore rule
|
|
286
|
+
if no_ignore_line in lines:
|
|
287
|
+
lines.remove(no_ignore_line)
|
|
288
|
+
lines.append(no_ignore_line)
|
|
289
|
+
elif not removed_direct_rule:
|
|
290
|
+
# The path may be ignored by a non-directory pattern (e.g., glob);
|
|
291
|
+
# remove stale negation and append at end so it takes precedence
|
|
292
|
+
if no_ignore_line in lines:
|
|
293
|
+
lines.remove(no_ignore_line)
|
|
294
|
+
lines.append(no_ignore_line)
|
|
295
|
+
with open(gitignore_path, "w") as f:
|
|
296
|
+
f.write(os.linesep.join(lines))
|
|
297
|
+
return True
|