calkit-python 0.29.0__tar.gz → 0.30.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {calkit_python-0.29.0 → calkit_python-0.30.0}/PKG-INFO +1 -1
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/__init__.py +1 -1
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/check.py +2 -17
- calkit_python-0.30.0/calkit/cli/config.py +240 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/main.py +32 -6
- calkit_python-0.30.0/calkit/cli/slurm.py +344 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/config.py +10 -5
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/core.py +14 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/models/pipeline.py +65 -1
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/models/test_pipeline.py +37 -4
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_pipeline.py +37 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/environments.md +17 -2
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/pipeline/index.md +34 -1
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/pipeline/running-and-logging.md +1 -0
- calkit_python-0.30.0/docs/pipeline/slurm.md +63 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/mkdocs.yml +1 -0
- calkit_python-0.29.0/calkit/cli/config.py +0 -126
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/FUNDING.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/workflows/docs.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/workflows/format.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/workflows/publish-test.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/workflows/publish.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.github/workflows/test.yml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.gitignore +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.pre-commit-config.yaml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/.python-version +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/CITATION.cff +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/CONTRIBUTING.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/LICENSE +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/Makefile +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/README.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/__main__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/calc.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/check.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/cloud.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/core.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/describe.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/import_.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/list.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/new.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/notebooks.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/office.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/overleaf.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cli/update.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/cloud.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/conda.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/datasets.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/docker.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/dvc.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/environments.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/git.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/github.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/gui.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/jupyter.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/magics.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/matlab.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/models/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/models/core.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/models/io.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/models/iteration.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/notebooks.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/office.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/ops.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/pipeline.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/releases.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/server.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/core.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/article/paper.tex +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/core.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/jfm/jfm.bst +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/jfm/jfm.cls +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/jfm/lineno-FLM.sty +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/jfm/paper.tex +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/templates/latex/jfm/upmath.sty +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_check.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_config.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_list.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_main.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_new.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/cli/test_notebooks.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/models/__init__.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/models/test_iteration.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_calc.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_check.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_conda.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_core.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_dvc.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_jupyter.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_magics.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_notebooks.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/tests/test_templates.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/calkit/zenodo.py +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/CNAME +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/apps.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/calculations.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/calkit-yaml.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/cli-reference.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/cloud-integration.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/datasets.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/dependencies.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/examples.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/help.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/img/c-to-the-k-white.svg +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/img/calkit-no-bg.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/img/connect-zenodo.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/img/jupyterlab-params.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/img/vscode-nb-params.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/index.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/installation.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/local-server.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/notebooks.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/overleaf.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/pipeline/manual-steps.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/quickstart.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/references.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/releases.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/adding-latex-pub-docker.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/conda-envs.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/existing-project.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/first-project.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/github-actions.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/actions-repo-secrets.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/building-codespace.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/codespaces-secrets-2.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/editor-split.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/go-to-linked-code.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/issue-from-selection.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/new-project.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/new-pub-2.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/new-token.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/paper.tex.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/project-home-3.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/push.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/latex-codespaces/stage.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/anakin-excel.jpg +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/chart-more-rows.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/create-project.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/elsevier-research-data-guidelines.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/excel-chart.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/excel-data.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/insert-link-to-file.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/needs-clone.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/new-stage.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/phd-comics-version-control.webp +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/pipeline-out-of-date.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/status-more-rows.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/uncommitted-changes.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/untracked-data.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/updated-publication.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/word-to-pdf-stage-2.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/office/workflow-page.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/clone.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/create-project.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/datasets-page.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/figure-on-website-updated.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/figure-on-website.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/new-token.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/reclone.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/openfoam/status-after-import-dataset.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/quick-actions.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/img/run-proc.png +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/index.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/latex-codespaces.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/matlab.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/notebook-pipeline.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/office.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/openfoam.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/tutorials/procedures.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/docs/version-control.md +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/pyproject.toml +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/scripts/install.ps1 +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/scripts/install.sh +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/test/nb-params.ipynb +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/test/nb-subdir.ipynb +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/test/pipeline.ipynb +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/test/test-log.log +0 -0
- {calkit_python-0.29.0 → calkit_python-0.30.0}/uv.lock +0 -0
|
@@ -3,21 +3,12 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import functools
|
|
6
|
-
import hashlib
|
|
7
6
|
import json
|
|
8
7
|
import os
|
|
9
8
|
import platform as _platform
|
|
10
9
|
import subprocess
|
|
11
|
-
import warnings
|
|
12
10
|
from typing import Annotated
|
|
13
11
|
|
|
14
|
-
from calkit.environments import get_env_lock_fpath
|
|
15
|
-
|
|
16
|
-
# See https://github.com/calkit/calkit/issues/346
|
|
17
|
-
with warnings.catch_warnings():
|
|
18
|
-
warnings.filterwarnings("ignore", category=UserWarning)
|
|
19
|
-
import checksumdir
|
|
20
|
-
|
|
21
12
|
import dotenv
|
|
22
13
|
import git
|
|
23
14
|
import typer
|
|
@@ -27,6 +18,8 @@ import calkit.matlab
|
|
|
27
18
|
import calkit.pipeline
|
|
28
19
|
from calkit.check import check_reproducibility
|
|
29
20
|
from calkit.cli import raise_error, warn
|
|
21
|
+
from calkit.core import get_md5
|
|
22
|
+
from calkit.environments import get_env_lock_fpath
|
|
30
23
|
|
|
31
24
|
check_app = typer.Typer(no_args_is_help=True)
|
|
32
25
|
|
|
@@ -288,14 +281,6 @@ def check_docker_env(
|
|
|
288
281
|
resp[key] = out[0].get(key)
|
|
289
282
|
return resp
|
|
290
283
|
|
|
291
|
-
def get_md5(path: str, exclude_files: list[str] | None = None) -> str:
|
|
292
|
-
if os.path.isdir(path):
|
|
293
|
-
return checksumdir.dirhash(dep, excluded_files=exclude_files)
|
|
294
|
-
else:
|
|
295
|
-
with open(path) as f:
|
|
296
|
-
content = f.read()
|
|
297
|
-
return hashlib.md5(content.encode()).hexdigest()
|
|
298
|
-
|
|
299
284
|
outfile = open(os.devnull, "w") if quiet else None
|
|
300
285
|
typer.echo(f"Checking for existing image with tag {tag}", file=outfile)
|
|
301
286
|
# First call Docker inspect
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""Config CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import glob
|
|
6
|
+
import os
|
|
7
|
+
import subprocess
|
|
8
|
+
|
|
9
|
+
import git
|
|
10
|
+
import typer
|
|
11
|
+
from git.exc import InvalidGitRepositoryError
|
|
12
|
+
from typing_extensions import Annotated
|
|
13
|
+
|
|
14
|
+
import calkit
|
|
15
|
+
from calkit import config
|
|
16
|
+
from calkit.cli.core import raise_error
|
|
17
|
+
from calkit.dvc import configure_remote, get_remotes, set_remote_auth
|
|
18
|
+
|
|
19
|
+
config_app = typer.Typer(no_args_is_help=True)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@config_app.command(name="set")
|
|
23
|
+
def set_config_value(key: str, value: str):
|
|
24
|
+
"""Set a value in the config."""
|
|
25
|
+
keys = config.Settings.model_fields.keys()
|
|
26
|
+
if key not in keys:
|
|
27
|
+
raise_error(
|
|
28
|
+
f"Invalid config key: '{key}'; Valid keys are: {list(keys)}"
|
|
29
|
+
)
|
|
30
|
+
try:
|
|
31
|
+
cfg = config.read()
|
|
32
|
+
cfg = config.Settings.model_validate(cfg.model_dump() | {key: value})
|
|
33
|
+
except Exception as e:
|
|
34
|
+
raise_error(f"Failed to set {key} in config: {e}")
|
|
35
|
+
cfg.write()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@config_app.command(name="get")
|
|
39
|
+
def get_config_value(key: str) -> None:
|
|
40
|
+
"""Get and print a value from the config."""
|
|
41
|
+
cfg = config.read().model_dump()
|
|
42
|
+
if key not in cfg:
|
|
43
|
+
raise_error(
|
|
44
|
+
f"Invalid config key: '{key}'; Valid keys are: {list(cfg.keys())}"
|
|
45
|
+
)
|
|
46
|
+
val = cfg[key]
|
|
47
|
+
if val is not None:
|
|
48
|
+
print(val)
|
|
49
|
+
else:
|
|
50
|
+
print()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@config_app.command(name="unset")
|
|
54
|
+
def unset_config_value(key: str):
|
|
55
|
+
"""Unset a value in the config, returning it to default."""
|
|
56
|
+
model_fields = config.Settings.model_fields
|
|
57
|
+
if key not in model_fields:
|
|
58
|
+
raise_error(
|
|
59
|
+
f"Invalid config key: '{key}'; "
|
|
60
|
+
f"Valid keys: {list(model_fields.keys())}"
|
|
61
|
+
)
|
|
62
|
+
try:
|
|
63
|
+
cfg = config.read()
|
|
64
|
+
setattr(cfg, key, model_fields[key].default)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
raise_error(f"Failed to unset {key} in config: {e}")
|
|
67
|
+
cfg.write()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@config_app.command(name="setup-remote", help="Alias for 'remote'.")
|
|
71
|
+
@config_app.command(name="remote")
|
|
72
|
+
def setup_remote(
|
|
73
|
+
no_commit: Annotated[
|
|
74
|
+
bool,
|
|
75
|
+
typer.Option(
|
|
76
|
+
"--no-commit", help="Do not commit changes to DVC config."
|
|
77
|
+
),
|
|
78
|
+
] = False,
|
|
79
|
+
):
|
|
80
|
+
"""Setup the Calkit cloud as the default DVC remote and store a token in
|
|
81
|
+
the local config.
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
configure_remote()
|
|
85
|
+
set_remote_auth()
|
|
86
|
+
except subprocess.CalledProcessError:
|
|
87
|
+
if not os.path.isfile(".dvc/config"):
|
|
88
|
+
raise_error(
|
|
89
|
+
"DVC remote config failed; have you run `calkit init`?"
|
|
90
|
+
)
|
|
91
|
+
raise_error(
|
|
92
|
+
"Failed to configure DVC remote; check DVC config for errors"
|
|
93
|
+
)
|
|
94
|
+
except InvalidGitRepositoryError:
|
|
95
|
+
raise_error("Current directory is not a Git repository")
|
|
96
|
+
except (ValueError, RuntimeError) as e:
|
|
97
|
+
raise_error(f"Failed to set up DVC remote: {e}")
|
|
98
|
+
if not no_commit:
|
|
99
|
+
repo = git.Repo()
|
|
100
|
+
repo.git.add(".dvc/config")
|
|
101
|
+
if ".dvc/config" in calkit.git.get_staged_files():
|
|
102
|
+
typer.echo("Committing changes to DVC config")
|
|
103
|
+
repo.git.commit([".dvc/config", "-m", "Set DVC remote"])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@config_app.command(name="setup-remote-auth", help="Alias for 'remote-auth'.")
|
|
107
|
+
@config_app.command(name="remote-auth")
|
|
108
|
+
def setup_remote_auth():
|
|
109
|
+
"""Store a Calkit cloud token in the local DVC config for all Calkit
|
|
110
|
+
remotes.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
remotes = get_remotes()
|
|
114
|
+
except Exception:
|
|
115
|
+
raise_error("Cannot list DVC remotes; check DVC config for errors")
|
|
116
|
+
for name, url in remotes.items():
|
|
117
|
+
if name == "calkit" or name.startswith("calkit:"):
|
|
118
|
+
typer.echo(f"Setting up authentication for DVC remote: {name}")
|
|
119
|
+
set_remote_auth(remote_name=name)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@config_app.command(name="list")
|
|
123
|
+
def list_config_keys():
|
|
124
|
+
"""List keys in the config."""
|
|
125
|
+
cfg = config.read()
|
|
126
|
+
for key in cfg.model_dump():
|
|
127
|
+
typer.echo(key)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@config_app.command(name="github-ssh")
|
|
131
|
+
def config_github_ssh():
|
|
132
|
+
"""Walk through the process of adding an SSH key to GitHub."""
|
|
133
|
+
typer.echo("Checking if you can already connect to GitHub via SSH")
|
|
134
|
+
# First check if we can already connect to GitHub
|
|
135
|
+
ssh_test_cmd = ["ssh", "-T", "git@github.com"]
|
|
136
|
+
p = subprocess.run(ssh_test_cmd, capture_output=True, text=True)
|
|
137
|
+
if "successfully authenticated" in p.stderr:
|
|
138
|
+
typer.echo("You can already connect to GitHub via SSH")
|
|
139
|
+
go_on = typer.confirm("Do you want to add a new SSH key anyway?")
|
|
140
|
+
if not go_on:
|
|
141
|
+
raise typer.Exit()
|
|
142
|
+
# If we can, ask the user if they still want to add a new key
|
|
143
|
+
# First check if the user has any SSH keys
|
|
144
|
+
ssh_dir = os.path.expanduser("~/.ssh")
|
|
145
|
+
existing_pub_keys = glob.glob(os.path.join(ssh_dir, "*.pub"))
|
|
146
|
+
# If not run ssh-keygen
|
|
147
|
+
if existing_pub_keys:
|
|
148
|
+
# Ask the user if they want to use an existing key or create a new one
|
|
149
|
+
typer.echo("Existing SSH public keys found:")
|
|
150
|
+
for i, key in enumerate(existing_pub_keys):
|
|
151
|
+
typer.echo(f"{i + 1}: {key}")
|
|
152
|
+
use_existing = typer.confirm("Do you want to use one of these keys?")
|
|
153
|
+
if use_existing:
|
|
154
|
+
key_choice = typer.prompt(
|
|
155
|
+
"Enter the number of the key to use", type=int
|
|
156
|
+
)
|
|
157
|
+
if 1 <= key_choice <= len(existing_pub_keys):
|
|
158
|
+
key_path = existing_pub_keys[key_choice - 1][:-4]
|
|
159
|
+
else:
|
|
160
|
+
typer.echo("Invalid choice")
|
|
161
|
+
# Keep asking until they give a valid choice
|
|
162
|
+
while True:
|
|
163
|
+
key_choice = typer.prompt(
|
|
164
|
+
"Enter the number of the key to use", type=int
|
|
165
|
+
)
|
|
166
|
+
if 1 <= key_choice <= len(existing_pub_keys):
|
|
167
|
+
key_path = existing_pub_keys[key_choice - 1][:-4]
|
|
168
|
+
break
|
|
169
|
+
else:
|
|
170
|
+
typer.echo("Invalid choice, please try again.")
|
|
171
|
+
else:
|
|
172
|
+
key_path = typer.prompt(
|
|
173
|
+
"Enter the path to save the new SSH key",
|
|
174
|
+
default=os.path.join(ssh_dir, "id_ed25519"),
|
|
175
|
+
)
|
|
176
|
+
else:
|
|
177
|
+
typer.echo("No existing SSH keys found")
|
|
178
|
+
key_path = typer.prompt(
|
|
179
|
+
"Enter the path to save the new SSH key",
|
|
180
|
+
default=os.path.join(ssh_dir, "id_ed25519"),
|
|
181
|
+
)
|
|
182
|
+
# Get the user's email from their Git config, and ask them if they want to
|
|
183
|
+
# use that or a different one
|
|
184
|
+
try:
|
|
185
|
+
user_git_email = git.Git().config("--get", "user.email").strip()
|
|
186
|
+
except Exception:
|
|
187
|
+
user_git_email = typer.prompt(
|
|
188
|
+
"No email found in Git config; enter email for SSH key"
|
|
189
|
+
)
|
|
190
|
+
git.Git().config("--global", "user.email", user_git_email)
|
|
191
|
+
# Do the same for user name even though we don't need it
|
|
192
|
+
try:
|
|
193
|
+
user_git_name = git.Git().config("--get", "user.name").strip()
|
|
194
|
+
except Exception:
|
|
195
|
+
user_git_name = typer.prompt(
|
|
196
|
+
"No name found in Git config; enter name for SSH key"
|
|
197
|
+
)
|
|
198
|
+
git.Git().config("--global", "user.name", user_git_name)
|
|
199
|
+
keygen_cmd = [
|
|
200
|
+
"ssh-keygen",
|
|
201
|
+
"-t",
|
|
202
|
+
"ed25519",
|
|
203
|
+
"-C",
|
|
204
|
+
user_git_email,
|
|
205
|
+
"-f",
|
|
206
|
+
key_path,
|
|
207
|
+
]
|
|
208
|
+
subprocess.run(keygen_cmd)
|
|
209
|
+
# Start the SSH agent in the background
|
|
210
|
+
typer.echo("Checking that the SSH agent is running")
|
|
211
|
+
ssh_agent_cmd = subprocess.run(
|
|
212
|
+
["ssh-agent", "-s"], capture_output=True, text=True
|
|
213
|
+
).stdout
|
|
214
|
+
p = subprocess.run(ssh_agent_cmd, shell=True)
|
|
215
|
+
if p.returncode != 0:
|
|
216
|
+
raise_error("Failed to start ssh-agent")
|
|
217
|
+
# Add the SSH key to the ssh-agent
|
|
218
|
+
typer.echo(f"Adding SSH key to ssh-agent: {key_path}")
|
|
219
|
+
cmd = ["ssh-add", key_path]
|
|
220
|
+
p = subprocess.run(cmd)
|
|
221
|
+
if p.returncode != 0:
|
|
222
|
+
raise_error("Failed to add SSH key to ssh-agent; please try again")
|
|
223
|
+
# Now add to GitHub
|
|
224
|
+
gh_ssh_url = "https://github.com/settings/ssh/new"
|
|
225
|
+
typer.echo(
|
|
226
|
+
"Add the new SSH key to your GitHub account by visiting:\n"
|
|
227
|
+
f"{gh_ssh_url}"
|
|
228
|
+
)
|
|
229
|
+
with open(key_path + ".pub", "r") as f:
|
|
230
|
+
pub_key = f.read()
|
|
231
|
+
typer.echo(f"Paste this into the public key field:\n\n{pub_key}\n")
|
|
232
|
+
typer.confirm("Press Enter when done", default=True)
|
|
233
|
+
typer.echo("Testing SSH connection to GitHub")
|
|
234
|
+
p = subprocess.run(ssh_test_cmd, capture_output=True, text=True)
|
|
235
|
+
if "successfully authenticated" in p.stderr:
|
|
236
|
+
typer.echo("Successfully connected to GitHub via SSH!")
|
|
237
|
+
else:
|
|
238
|
+
raise_error(
|
|
239
|
+
"Failed to connect to GitHub via SSH; please check your setup"
|
|
240
|
+
)
|
|
@@ -51,6 +51,7 @@ from calkit.cli.new import new_app
|
|
|
51
51
|
from calkit.cli.notebooks import notebooks_app
|
|
52
52
|
from calkit.cli.office import office_app
|
|
53
53
|
from calkit.cli.overleaf import overleaf_app
|
|
54
|
+
from calkit.cli.slurm import slurm_app
|
|
54
55
|
from calkit.cli.update import update_app
|
|
55
56
|
from calkit.environments import get_env_lock_fpath
|
|
56
57
|
from calkit.models import Procedure
|
|
@@ -77,6 +78,7 @@ app.add_typer(update_app, name="update", help="Update objects.")
|
|
|
77
78
|
app.add_typer(check_app, name="check", help="Check things.")
|
|
78
79
|
app.add_typer(overleaf_app, name="overleaf", help="Interact with Overleaf.")
|
|
79
80
|
app.add_typer(cloud_app, name="cloud", help="Interact with a Calkit Cloud.")
|
|
81
|
+
app.add_typer(slurm_app, name="slurm", help="Work with SLURM.")
|
|
80
82
|
|
|
81
83
|
# Constants for version control auto-ignore
|
|
82
84
|
AUTO_IGNORE_SUFFIXES = [".DS_Store", ".env", ".pyc", ".synctex.gz"]
|
|
@@ -187,8 +189,11 @@ def clone(
|
|
|
187
189
|
no_dvc_pull: Annotated[
|
|
188
190
|
bool, typer.Option("--no-dvc-pull", help="Do not pull DVC objects.")
|
|
189
191
|
] = False,
|
|
190
|
-
|
|
191
|
-
bool,
|
|
192
|
+
non_recursive: Annotated[
|
|
193
|
+
bool,
|
|
194
|
+
typer.Option(
|
|
195
|
+
"--no-recursive", help="Do not recursively clone submodules."
|
|
196
|
+
),
|
|
192
197
|
] = False,
|
|
193
198
|
):
|
|
194
199
|
"""Clone a Git repo and by default configure and pull from the DVC
|
|
@@ -219,7 +224,7 @@ def clone(
|
|
|
219
224
|
url = url.replace("https://github.com/", "git@github.com:")
|
|
220
225
|
# Git clone
|
|
221
226
|
cmd = ["git", "clone", url]
|
|
222
|
-
if
|
|
227
|
+
if not non_recursive:
|
|
223
228
|
cmd.append("--recursive")
|
|
224
229
|
if location is not None:
|
|
225
230
|
cmd.append(location)
|
|
@@ -552,6 +557,9 @@ def save(
|
|
|
552
557
|
help="Additional DVC args to pass when pushing.",
|
|
553
558
|
),
|
|
554
559
|
] = [],
|
|
560
|
+
no_recursive: Annotated[
|
|
561
|
+
bool, typer.Option("--no-recursive", help="Do not push to submodules.")
|
|
562
|
+
] = False,
|
|
555
563
|
verbose: Annotated[
|
|
556
564
|
bool, typer.Option("--verbose", "-v", help="Print verbose output.")
|
|
557
565
|
] = False,
|
|
@@ -606,7 +614,10 @@ def save(
|
|
|
606
614
|
if verbose and not any_dvc:
|
|
607
615
|
typer.echo("Not pushing to DVC since no DVC files were staged")
|
|
608
616
|
push(
|
|
609
|
-
no_dvc=not any_dvc,
|
|
617
|
+
no_dvc=not any_dvc,
|
|
618
|
+
git_args=git_push_args,
|
|
619
|
+
dvc_args=dvc_push_args,
|
|
620
|
+
no_recursive=no_recursive,
|
|
610
621
|
)
|
|
611
622
|
|
|
612
623
|
|
|
@@ -629,6 +640,12 @@ def pull(
|
|
|
629
640
|
help="Force pull, potentially overwriting local changes.",
|
|
630
641
|
),
|
|
631
642
|
] = False,
|
|
643
|
+
no_recursive: Annotated[
|
|
644
|
+
bool,
|
|
645
|
+
typer.Option(
|
|
646
|
+
"--no-recursive", help="Do not recursively pull from submodules."
|
|
647
|
+
),
|
|
648
|
+
] = False,
|
|
632
649
|
):
|
|
633
650
|
"""Pull with both Git and DVC."""
|
|
634
651
|
typer.echo("Git pulling")
|
|
@@ -638,7 +655,10 @@ def pull(
|
|
|
638
655
|
if "-f" not in dvc_args and "--force" not in dvc_args:
|
|
639
656
|
dvc_args.append("-f")
|
|
640
657
|
try:
|
|
641
|
-
|
|
658
|
+
git_cmd = ["git", "pull"]
|
|
659
|
+
if not no_recursive and "--recurse-submodules" not in git_args:
|
|
660
|
+
git_cmd.append("--recurse-submodules")
|
|
661
|
+
subprocess.check_call(git_cmd + git_args)
|
|
642
662
|
except subprocess.CalledProcessError:
|
|
643
663
|
raise_error("Git pull failed")
|
|
644
664
|
typer.echo("DVC pulling")
|
|
@@ -667,11 +687,17 @@ def push(
|
|
|
667
687
|
list[str],
|
|
668
688
|
typer.Option("--dvc-arg", help="Additional DVC args."),
|
|
669
689
|
] = [],
|
|
690
|
+
no_recursive: Annotated[
|
|
691
|
+
bool, typer.Option("--no-recursive", help="Do not push to submodules.")
|
|
692
|
+
] = False,
|
|
670
693
|
):
|
|
671
694
|
"""Push with both Git and DVC."""
|
|
672
695
|
typer.echo("Pushing to Git remote")
|
|
673
696
|
try:
|
|
674
|
-
|
|
697
|
+
git_cmd = ["git", "push"]
|
|
698
|
+
if not no_recursive and "--recurse-submodules" not in git_args:
|
|
699
|
+
git_cmd.append("--recurse-submodules=on-demand")
|
|
700
|
+
subprocess.check_call(git_cmd + git_args)
|
|
675
701
|
except subprocess.CalledProcessError:
|
|
676
702
|
raise_error("Git push failed")
|
|
677
703
|
if not no_dvc:
|