cluster-uv 0.0.1.post1__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.
- cluster_uv-0.0.1.post1/.claude-plugin/plugin.json +8 -0
- cluster_uv-0.0.1.post1/.copier-answers.yml +8 -0
- cluster_uv-0.0.1.post1/.github/ISSUE_TEMPLATE/bug_report.md +24 -0
- cluster_uv-0.0.1.post1/.github/ISSUE_TEMPLATE/feature_proposal.md +18 -0
- cluster_uv-0.0.1.post1/.github/PULL_REQUEST_TEMPLATE.md +5 -0
- cluster_uv-0.0.1.post1/.github/workflows/build.yaml +136 -0
- cluster_uv-0.0.1.post1/.github/workflows/docs.yaml +35 -0
- cluster_uv-0.0.1.post1/.github/workflows/publish.yaml +47 -0
- cluster_uv-0.0.1.post1/.gitignore +18 -0
- cluster_uv-0.0.1.post1/.pre-commit-config.yaml +51 -0
- cluster_uv-0.0.1.post1/.python-version +1 -0
- cluster_uv-0.0.1.post1/CLAUDE.md +52 -0
- cluster_uv-0.0.1.post1/CONTRIBUTING.md +39 -0
- cluster_uv-0.0.1.post1/LICENSE +21 -0
- cluster_uv-0.0.1.post1/PKG-INFO +183 -0
- cluster_uv-0.0.1.post1/README.md +167 -0
- cluster_uv-0.0.1.post1/cluv/__init__.py +0 -0
- cluster_uv-0.0.1.post1/cluv/__main__.py +317 -0
- cluster_uv-0.0.1.post1/cluv/cache.py +99 -0
- cluster_uv-0.0.1.post1/cluv/cli/__init__.py +13 -0
- cluster_uv-0.0.1.post1/cluv/cli/dashboard.py +23 -0
- cluster_uv-0.0.1.post1/cluv/cli/init.py +313 -0
- cluster_uv-0.0.1.post1/cluv/cli/login.py +60 -0
- cluster_uv-0.0.1.post1/cluv/cli/run.py +32 -0
- cluster_uv-0.0.1.post1/cluv/cli/status.py +562 -0
- cluster_uv-0.0.1.post1/cluv/cli/submit.py +645 -0
- cluster_uv-0.0.1.post1/cluv/cli/sync.py +627 -0
- cluster_uv-0.0.1.post1/cluv/config.py +210 -0
- cluster_uv-0.0.1.post1/cluv/job.py +143 -0
- cluster_uv-0.0.1.post1/cluv/py.typed +0 -0
- cluster_uv-0.0.1.post1/cluv/remote.py +252 -0
- cluster_uv-0.0.1.post1/cluv/slurm.py +391 -0
- cluster_uv-0.0.1.post1/cluv/ssh.py +12 -0
- cluster_uv-0.0.1.post1/cluv/utils.py +35 -0
- cluster_uv-0.0.1.post1/docs/guides/introduction.md +73 -0
- cluster_uv-0.0.1.post1/docs/guides/syncing-datasets.md +53 -0
- cluster_uv-0.0.1.post1/docs/index.md +61 -0
- cluster_uv-0.0.1.post1/docs/reference/cli/init.md +4 -0
- cluster_uv-0.0.1.post1/docs/reference/cli/login.md +1 -0
- cluster_uv-0.0.1.post1/docs/reference/cli/run.md +3 -0
- cluster_uv-0.0.1.post1/docs/reference/cli/submit.md +3 -0
- cluster_uv-0.0.1.post1/docs/reference/cli/sync.md +1 -0
- cluster_uv-0.0.1.post1/docs/reference/config.md +1 -0
- cluster_uv-0.0.1.post1/docs/reference/remote.md +1 -0
- cluster_uv-0.0.1.post1/examples/pytorch-example/.gitignore +2 -0
- cluster_uv-0.0.1.post1/examples/pytorch-example/README.md +23 -0
- cluster_uv-0.0.1.post1/examples/pytorch-example/main.py +101 -0
- cluster_uv-0.0.1.post1/examples/pytorch-example/pyproject.toml +90 -0
- cluster_uv-0.0.1.post1/examples/pytorch-example/scripts/job.sh +13 -0
- cluster_uv-0.0.1.post1/mkdocs.yaml +51 -0
- cluster_uv-0.0.1.post1/pyproject.toml +149 -0
- cluster_uv-0.0.1.post1/scripts/job.sh +9 -0
- cluster_uv-0.0.1.post1/scripts/safe_job.sh +45 -0
- cluster_uv-0.0.1.post1/tests/__init__.py +0 -0
- cluster_uv-0.0.1.post1/tests/conftest.py +20 -0
- cluster_uv-0.0.1.post1/tests/data/.gitkeep +1 -0
- cluster_uv-0.0.1.post1/tests/data/dataset.txt +1 -0
- cluster_uv-0.0.1.post1/tests/test_config.py +334 -0
- cluster_uv-0.0.1.post1/tests/test_init.py +210 -0
- cluster_uv-0.0.1.post1/tests/test_integration.py +399 -0
- cluster_uv-0.0.1.post1/tests/test_slurm.py +424 -0
- cluster_uv-0.0.1.post1/tests/test_ssh.py +32 -0
- cluster_uv-0.0.1.post1/tests/test_submit.py +667 -0
- cluster_uv-0.0.1.post1/tests/test_sync.py +93 -0
- cluster_uv-0.0.1.post1/tests/test_sync_refs.py +28 -0
- cluster_uv-0.0.1.post1/uv.lock +1934 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changes here will be overwritten by Copier
|
|
2
|
+
_commit: v0.0.5
|
|
3
|
+
_src_path: gh:mila-iqia/tool_template
|
|
4
|
+
project_description: A new project/tool called cluv.
|
|
5
|
+
python_version: '3.13'
|
|
6
|
+
tool_name: cluv
|
|
7
|
+
your_email: normandf@mila.quebec
|
|
8
|
+
your_name: Fabrice Normandin
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
name: Bug report
|
|
4
|
+
about: Create a report to help us improve
|
|
5
|
+
title: '[BUG]: '
|
|
6
|
+
labels: bug
|
|
7
|
+
assignees: ''
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
### What command did you run?
|
|
12
|
+
<!-- Please paste the exact command you ran that caused the bug. -->
|
|
13
|
+
|
|
14
|
+
### Describe the bug
|
|
15
|
+
<!-- A clear and concise description of what the bug is. If there is an error traceback, please paste it here. -->
|
|
16
|
+
|
|
17
|
+
### Screenshots
|
|
18
|
+
<!-- If applicable, add screenshots to help explain your problem. -->
|
|
19
|
+
|
|
20
|
+
### Setup
|
|
21
|
+
<!-- Please provide details about your setup : OS, Cluv config, Clusters... -->
|
|
22
|
+
|
|
23
|
+
### Additional context
|
|
24
|
+
<!-- Add any other context about the problem here. -->
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
name: Feature proposal
|
|
4
|
+
about: Propose a new feature or improvement
|
|
5
|
+
title: '[FEAT]: '
|
|
6
|
+
labels: enhancement
|
|
7
|
+
assignees: ''
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
### Context
|
|
12
|
+
<!-- Why is this feature needed? What problem does it solve or what use case does it enable? -->
|
|
13
|
+
|
|
14
|
+
### Proposed solution
|
|
15
|
+
<!-- Describe how you'd like this to work. Include example commands or expected output if applicable. -->
|
|
16
|
+
|
|
17
|
+
### Additional context
|
|
18
|
+
<!-- Any other context, references, or screenshots that might be helpful. -->
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
|
3
|
+
|
|
4
|
+
name: Python application
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
branches:
|
|
9
|
+
- master
|
|
10
|
+
pull_request:
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
# https://stackoverflow.com/a/72408109/6388696
|
|
16
|
+
# https://docs.github.com/en/actions/using-jobs/using-concurrency#example-using-concurrency-to-cancel-any-in-progress-job-or-run
|
|
17
|
+
concurrency:
|
|
18
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
19
|
+
cancel-in-progress: true
|
|
20
|
+
|
|
21
|
+
jobs:
|
|
22
|
+
linting:
|
|
23
|
+
name: Run linting/pre-commit checks
|
|
24
|
+
runs-on: ubuntu-latest
|
|
25
|
+
timeout-minutes: 5
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
- uses: actions/setup-python@v4
|
|
29
|
+
with:
|
|
30
|
+
python-version: "3.13"
|
|
31
|
+
- run: pip install 'pre-commit<4.0.0'
|
|
32
|
+
- run: pre-commit --version
|
|
33
|
+
- run: pre-commit install
|
|
34
|
+
- run: pre-commit run --all-files --show-diff-on-failure
|
|
35
|
+
|
|
36
|
+
check_docs:
|
|
37
|
+
needs: [ linting ]
|
|
38
|
+
runs-on: ubuntu-latest
|
|
39
|
+
steps:
|
|
40
|
+
- uses: actions/checkout@v4
|
|
41
|
+
- name: Install the latest version of uv
|
|
42
|
+
uses: astral-sh/setup-uv@v3
|
|
43
|
+
with:
|
|
44
|
+
version: "latest"
|
|
45
|
+
enable-cache: true
|
|
46
|
+
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#github-authentication-token
|
|
47
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
48
|
+
cache-suffix: "3.13"
|
|
49
|
+
- name: Install dependencies
|
|
50
|
+
run: uv sync --frozen
|
|
51
|
+
- name: Build the documentation (strict mode)
|
|
52
|
+
run: uv run mkdocs build --strict
|
|
53
|
+
|
|
54
|
+
unit_tests:
|
|
55
|
+
needs: [ linting ]
|
|
56
|
+
runs-on: ${{ matrix.platform }}
|
|
57
|
+
strategy:
|
|
58
|
+
max-parallel: 4
|
|
59
|
+
matrix:
|
|
60
|
+
platform: [ "ubuntu-latest", "macos-latest" ]
|
|
61
|
+
python-version: [ "3.13" ]
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
- name: Install the latest version of uv
|
|
65
|
+
uses: astral-sh/setup-uv@v3
|
|
66
|
+
with:
|
|
67
|
+
version: "latest"
|
|
68
|
+
enable-cache: true
|
|
69
|
+
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#github-authentication-token
|
|
70
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
71
|
+
cache-suffix: ${{ matrix.python-version }}
|
|
72
|
+
- name: Pin python-version ${{ matrix.python-version }}
|
|
73
|
+
run: uv python pin ${{ matrix.python-version }}
|
|
74
|
+
- name: Install dependencies
|
|
75
|
+
run: uv sync --frozen
|
|
76
|
+
- name: Test with pytest
|
|
77
|
+
run: uv run pytest -v --cov=cluv --cov-report=xml --cov-append
|
|
78
|
+
- name: Store coverage report as an artifact
|
|
79
|
+
uses: actions/upload-artifact@v4
|
|
80
|
+
with:
|
|
81
|
+
name: coverage-reports-unit-tests-${{ matrix.platform }}-${{
|
|
82
|
+
matrix.python-version }}
|
|
83
|
+
path: ./coverage.xml
|
|
84
|
+
integration_tests:
|
|
85
|
+
needs: [ unit_tests ]
|
|
86
|
+
runs-on: self-hosted
|
|
87
|
+
# TODO: remove this once we have a decently solid setup, not too reliant on the dev machine
|
|
88
|
+
# having active SSH connections to the Slurm clusters.
|
|
89
|
+
continue-on-error: true
|
|
90
|
+
steps:
|
|
91
|
+
- uses: actions/checkout@v6
|
|
92
|
+
with:
|
|
93
|
+
# Trying to checkout the branch (not just a commit) so that the cluv command
|
|
94
|
+
# that do a git push work in the self-hosted CI.
|
|
95
|
+
fetch-tags: true
|
|
96
|
+
fetch-depth: 0
|
|
97
|
+
- name: Install the latest version of uv
|
|
98
|
+
uses: astral-sh/setup-uv@v7
|
|
99
|
+
with:
|
|
100
|
+
version: "latest"
|
|
101
|
+
enable-cache: true
|
|
102
|
+
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#github-authentication-token
|
|
103
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
104
|
+
- name: Install dependencies
|
|
105
|
+
run: uv sync --frozen
|
|
106
|
+
- name: Run all tests
|
|
107
|
+
run: uv run pytest -v --slow --cov=cluv --cov-report=xml --cov-append
|
|
108
|
+
- name: Store coverage report as an artifact
|
|
109
|
+
uses: actions/upload-artifact@v4
|
|
110
|
+
with:
|
|
111
|
+
name: coverage-reports-integration-tests
|
|
112
|
+
path: ./coverage.xml
|
|
113
|
+
|
|
114
|
+
# https://about.codecov.io/blog/uploading-code-coverage-in-a-separate-job-on-github-actions/
|
|
115
|
+
upload-coverage-codecov:
|
|
116
|
+
needs: [ unit_tests, integration_tests ]
|
|
117
|
+
runs-on: ubuntu-latest
|
|
118
|
+
name: Upload coverage reports to Codecov
|
|
119
|
+
timeout-minutes: 5
|
|
120
|
+
steps:
|
|
121
|
+
- name: Checkout
|
|
122
|
+
uses: actions/checkout@v4
|
|
123
|
+
- name: Download artifacts
|
|
124
|
+
uses: actions/download-artifact@v4
|
|
125
|
+
with:
|
|
126
|
+
pattern: coverage-reports-*
|
|
127
|
+
merge-multiple: false
|
|
128
|
+
# download all the artifacts in this directory (each .coverage.xml will be in a subdirectory)
|
|
129
|
+
# Next step if this doesn't work would be to give the coverage files a unique name and use merge-multiple: true
|
|
130
|
+
path: coverage_reports
|
|
131
|
+
- name: Upload coverage reports to Codecov
|
|
132
|
+
uses: codecov/codecov-action@v4
|
|
133
|
+
with:
|
|
134
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
135
|
+
directory: coverage_reports
|
|
136
|
+
fail_ci_if_error: true
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Publish docs via GitHub Pages
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches:
|
|
5
|
+
- master
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
build:
|
|
9
|
+
name: Deploy docs
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- name: Checkout
|
|
13
|
+
uses: actions/checkout@v4
|
|
14
|
+
- name: Install the latest version of uv
|
|
15
|
+
uses: astral-sh/setup-uv@v3
|
|
16
|
+
with:
|
|
17
|
+
version: "latest"
|
|
18
|
+
enable-cache: true # no need, uses the local uv cache.
|
|
19
|
+
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#github-authentication-token
|
|
20
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
21
|
+
cache-suffix: "3.13"
|
|
22
|
+
|
|
23
|
+
- name: Pin python-version
|
|
24
|
+
run: uv python pin 3.13
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: uv sync --frozen
|
|
28
|
+
|
|
29
|
+
- name: Deploy docs
|
|
30
|
+
run: uv run mkdocs gh-deploy --force
|
|
31
|
+
# note: Checking if we really need the one below:
|
|
32
|
+
# uses: mhausenblas/mkdocs-deploy-gh-pages@1.9
|
|
33
|
+
# # Or use mhausenblas/mkdocs-deploy-gh-pages@nomaterial to build without the mkdocs-material theme
|
|
34
|
+
# env:
|
|
35
|
+
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# This workflow will upload a Python Package using Poetry when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
on:
|
|
9
|
+
release:
|
|
10
|
+
types: [published]
|
|
11
|
+
workflow_dispatch:
|
|
12
|
+
jobs:
|
|
13
|
+
# https://docs.pypi.org/trusted-publishers/using-a-publisher/
|
|
14
|
+
publish:
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: [3.13]
|
|
18
|
+
os: [ubuntu-latest]
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
environment:
|
|
21
|
+
name: pypi
|
|
22
|
+
url: https://pypi.org/p/cluster-uv
|
|
23
|
+
permissions:
|
|
24
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
25
|
+
id-token: write
|
|
26
|
+
steps:
|
|
27
|
+
- uses: actions/checkout@v4
|
|
28
|
+
|
|
29
|
+
- name: Install the latest version of uv
|
|
30
|
+
uses: astral-sh/setup-uv@v3
|
|
31
|
+
with:
|
|
32
|
+
version: "latest"
|
|
33
|
+
enable-cache: true
|
|
34
|
+
# https://github.com/astral-sh/setup-uv?tab=readme-ov-file#github-authentication-token
|
|
35
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
36
|
+
cache-suffix: ${{ matrix.python-version }}
|
|
37
|
+
- name: Pin python-version ${{ matrix.python-version }}
|
|
38
|
+
run: uv python pin ${{ matrix.python-version }}
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
run: uv sync --frozen
|
|
41
|
+
|
|
42
|
+
- name: Build package
|
|
43
|
+
run: |
|
|
44
|
+
uv build
|
|
45
|
+
|
|
46
|
+
- name: Publish package distributions to PyPI
|
|
47
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# uv lockfile
|
|
13
|
+
# uv.lock
|
|
14
|
+
.claude
|
|
15
|
+
.vscode
|
|
16
|
+
logs/
|
|
17
|
+
# created by `uv run mkdocs serve` and such.
|
|
18
|
+
site/
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
default_language_version:
|
|
2
|
+
python: python3
|
|
3
|
+
|
|
4
|
+
repos:
|
|
5
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
6
|
+
rev: v6.0.0
|
|
7
|
+
hooks:
|
|
8
|
+
# list of supported hooks: https://pre-commit.com/hooks.html
|
|
9
|
+
- id: trailing-whitespace
|
|
10
|
+
require_serial: true
|
|
11
|
+
- id: end-of-file-fixer
|
|
12
|
+
require_serial: true
|
|
13
|
+
# - id: check-docstring-first
|
|
14
|
+
- id: check-added-large-files
|
|
15
|
+
require_serial: true
|
|
16
|
+
exclude: uv.lock
|
|
17
|
+
- id: check-ast
|
|
18
|
+
require_serial: true
|
|
19
|
+
- id: check-yaml
|
|
20
|
+
require_serial: true
|
|
21
|
+
exclude: "mkdocs.yml"
|
|
22
|
+
- id: debug-statements
|
|
23
|
+
require_serial: true
|
|
24
|
+
- id: detect-private-key
|
|
25
|
+
require_serial: true
|
|
26
|
+
- id: check-executables-have-shebangs
|
|
27
|
+
require_serial: true
|
|
28
|
+
- id: check-toml
|
|
29
|
+
require_serial: true
|
|
30
|
+
- id: check-case-conflict
|
|
31
|
+
require_serial: true
|
|
32
|
+
|
|
33
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
34
|
+
# Ruff version.
|
|
35
|
+
rev: v0.15.16
|
|
36
|
+
hooks:
|
|
37
|
+
# Run the linter.
|
|
38
|
+
- id: ruff-check
|
|
39
|
+
args: [ --fix ]
|
|
40
|
+
# Run the formatter.
|
|
41
|
+
- id: ruff-format
|
|
42
|
+
|
|
43
|
+
# word spelling linter
|
|
44
|
+
- repo: https://github.com/codespell-project/codespell
|
|
45
|
+
rev: v2.4.2
|
|
46
|
+
hooks:
|
|
47
|
+
- id: codespell
|
|
48
|
+
args:
|
|
49
|
+
- --skip=logs/**,data/**
|
|
50
|
+
# - --ignore-words-list=abc,def
|
|
51
|
+
require_serial: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# Install dependencies and activate the venv
|
|
9
|
+
uv sync
|
|
10
|
+
|
|
11
|
+
# Run cluv directly (no install needed)
|
|
12
|
+
uv run cluv <command>
|
|
13
|
+
|
|
14
|
+
# Run with verbose logging (-v, -vv, -vvv)
|
|
15
|
+
uv run cluv -v status
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Run tests with `uv run pytest`.
|
|
19
|
+
|
|
20
|
+
## Architecture
|
|
21
|
+
|
|
22
|
+
`cluv` is a CLI tool for managing UV-based Python projects across multiple HPC clusters (Mila, DRAC/Narval, etc.).
|
|
23
|
+
|
|
24
|
+
**Entry point**: `cluv/__main__.py:main()` — builds an `argparse`/`simple_parsing` parser with subcommands, then dispatches to the appropriate async or sync function. Each subcommand registers its args in an `add_<cmd>_args()` function in `__main__.py`, and its implementation lives in `cluv/cli/<cmd>.py`.
|
|
25
|
+
|
|
26
|
+
**Config**: `cluv/config.py` — reads the `[tool.cluv]` section from the nearest `pyproject.toml`. Key fields: `clusters` (list of hostnames or table with per-cluster settings), `slurm` (global `SBATCH_*` env var defaults), `cluster_configs` (per-cluster `SBATCH_*` overrides). Config is cached with `@functools.cache`.
|
|
27
|
+
|
|
28
|
+
**SSH connections**: All remote operations go through `milatools.utils.remote_v2.Remote`. Connections reuse existing SSH ControlMaster sockets (checked via `control_socket_is_running_async`) to avoid triggering 2FA prompts. `cluv login` establishes fresh connections sequentially (to avoid concurrent 2FA prompts). The `login.get_remote_without_2fa_prompt()` helper is used by `sync` to only operate on already-connected clusters.
|
|
29
|
+
|
|
30
|
+
**Async pattern**: Multi-cluster operations use `asyncio.gather` for parallelism. The `sync` command uses `milatools.utils.parallel_progress.run_tasks_with_progress_bar` to display per-cluster progress. The top-level `main()` uses `asyncio.run()` when the subcommand function is a coroutine.
|
|
31
|
+
|
|
32
|
+
**Rich console**: A single `rich.Console` instance is created in `cluv/utils.py` and patched into `milatools` internals (`milatools.cli.console`, etc.) so all output goes through one stream.
|
|
33
|
+
|
|
34
|
+
**`cluv status`** (`cluv/cli/status.py`): Currently entirely mock data. The `ClusterStatus` / `JobStats` / `StorageStats` dataclasses define the data model. The `get_mock_cluster_status()` function is the stub to replace with real implementations. The display logic (`_build_cluster_table`, `_build_my_jobs_table`) is separate and should not need changes when real data is wired in.
|
|
35
|
+
|
|
36
|
+
**`cluv sync`** (`cluv/cli/sync.py`): The most complete command. Runs: `git push` locally → `git clone`/`fetch`/`checkout`/`pull` on each remote → `uv sync` on each remote → optional `rsync` of results back. Each cluster's work is encapsulated in `sync_task_function`.
|
|
37
|
+
|
|
38
|
+
**`cluv submit`** (`cluv/cli/submit.py`): `cluv submit <cluster> <job.sh> [--no-sync] [sbatch-flags...] [-- program-args...]`. Enforces a clean git tree, injects `GIT_COMMIT`, merges global + per-cluster `SBATCH_*` env vars from config, then runs `sbatch` on the remote. Arguments before `--` are forwarded as sbatch flags; arguments after `--` are passed to the job script.
|
|
39
|
+
|
|
40
|
+
**`cluv init`** and **`cluv run`**: Not yet implemented (stubs/`NotImplementedError`).
|
|
41
|
+
|
|
42
|
+
## Cluster notes
|
|
43
|
+
|
|
44
|
+
- **Mila cluster** is typically the "home base". Detected via `Path("/home/mila").exists()` in `utils.current_cluster()`.
|
|
45
|
+
- **DRAC clusters** (narval, tamia, rorqual, fir, etc.) are detected via `$CC_CLUSTER` env var. They expose a `partition-stats` command that prints a text table of queued/running/idle node counts by partition type (Regular vs GPU) and walltime bucket. See `partiton-stats_output.txt` for a sample.
|
|
46
|
+
- DRAC clusters require a special `uv.toml` pointing to the DRAC wheelhouse for offline installs.
|
|
47
|
+
- Cluster hostnames in `[tool.cluv]` must match the SSH hostnames configured in `~/.ssh/config`.
|
|
48
|
+
|
|
49
|
+
## Work etiquette
|
|
50
|
+
|
|
51
|
+
- Make small, clean commits regularly. Each commit should only contain changes related to a single "action" or "theme".
|
|
52
|
+
- Write minimalist, clean, pythonic code. Avoid being overly general or abstract.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Contributing to cluv
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to cluv!
|
|
4
|
+
|
|
5
|
+
## Issues
|
|
6
|
+
|
|
7
|
+
Use [GitHub Issues](https://github.com/mila-iqia/cluv/issues) to report bugs or suggest features.
|
|
8
|
+
|
|
9
|
+
## Development setup
|
|
10
|
+
|
|
11
|
+
Clone and install project:
|
|
12
|
+
```bash
|
|
13
|
+
git clone https://github.com/mila-iqia/cluv
|
|
14
|
+
cd cluv
|
|
15
|
+
uv sync
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Use your local version as the cluv tool:
|
|
19
|
+
```bash
|
|
20
|
+
uv tool install --editable <path_to_cluv_repo>
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Testing
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv run pytest
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Tests marked `integration` require live SSH connections to real clusters. Skip them locally:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
uv run pytest -m "not integration"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Linting
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
uv run pre-commit run --all-files
|
|
39
|
+
```
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mila
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cluster-uv
|
|
3
|
+
Version: 0.0.1.post1
|
|
4
|
+
Summary: cluv (from 'cluster' + 'uv') - A versatile tool to work with uv python projects across HPC clusters.
|
|
5
|
+
Author-email: Fabrice Normandin <normandf@mila.quebec>
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.13
|
|
8
|
+
Requires-Dist: milatools>=0.1.9
|
|
9
|
+
Requires-Dist: platformdirs>=4.9.6
|
|
10
|
+
Requires-Dist: pydantic>=2.0
|
|
11
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
12
|
+
Requires-Dist: rich-argparse>=1.7.2
|
|
13
|
+
Requires-Dist: rich>=13.0
|
|
14
|
+
Requires-Dist: simple-parsing>=0.1.8
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# cluv
|
|
18
|
+
|
|
19
|
+
cluv — sync UV-based Python projects across HPC clusters.
|
|
20
|
+
|
|
21
|
+
## Status
|
|
22
|
+
|
|
23
|
+
In early development. Commands are functional, but expect bugs or missing features.
|
|
24
|
+
|
|
25
|
+
## Requirements
|
|
26
|
+
|
|
27
|
+
- Python >= 3.13
|
|
28
|
+
- [UV](https://docs.astral.sh/uv/)
|
|
29
|
+
- SSH access configured for each cluster in `~/.ssh/config` (run `cluv login` to open ControlMaster sessions)
|
|
30
|
+
- A GitHub repository with your project
|
|
31
|
+
|
|
32
|
+
## Installation
|
|
33
|
+
|
|
34
|
+
Install as a UV tool:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv tool install git+https://github.com/mila-iqia/cluv
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Then you can run `cluv` directly as a command:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cluv init
|
|
44
|
+
cluv login mila
|
|
45
|
+
cluv sync mila
|
|
46
|
+
cluv submit mila job.sh
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
1. Initialize your project with:
|
|
52
|
+
```bash
|
|
53
|
+
cluv init
|
|
54
|
+
```
|
|
55
|
+
2. Establish SSH connections to all configured clusters:
|
|
56
|
+
```bash
|
|
57
|
+
cluv login
|
|
58
|
+
```
|
|
59
|
+
3. Sync your project to all clusters and run `uv sync` on each:
|
|
60
|
+
```bash
|
|
61
|
+
cluv sync
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
See the [examples](examples) folder for sample projects using cluv. Each example includes a README with instructions specific to that project.
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
Add a `[tool.cluv]` section to the `pyproject.toml` of your project. `cluv init` generates a default config, or you can write it by hand.
|
|
71
|
+
See the config at the project root for an example, or refer to the schema below.
|
|
72
|
+
|
|
73
|
+
### Top-level fields
|
|
74
|
+
|
|
75
|
+
| Field | Type | Description |
|
|
76
|
+
|-------|------|-------------|
|
|
77
|
+
| `clusters` | table | Per-cluster settings, keyed by SSH hostname from `~/.ssh/config`. |
|
|
78
|
+
| `env` | table | Global environment variables applied to all clusters. |
|
|
79
|
+
| `results_path` | string | Path relative to the project root for storing results. `cluv sync` rsyncs that directory back from each remote cluster. |
|
|
80
|
+
|
|
81
|
+
### `[tool.cluv.clusters.<name>.env]`
|
|
82
|
+
Environment variables for a specific cluster. Values here are merged on top of `[tool.cluv.env]` when submitting.
|
|
83
|
+
|
|
84
|
+
### Variables priority
|
|
85
|
+
Environment variables can be set at multiple levels when submitting jobs, with the following precedence (highest to lowest):
|
|
86
|
+
1. Command-line arguments to `cluv submit`.
|
|
87
|
+
2. Cluster-specific variables in `[tool.cluv.clusters.<name>.env]`
|
|
88
|
+
3. Global variables in `[tool.cluv.env]`
|
|
89
|
+
4. SBATCH directives inside the job script (e.g. `#SBATCH --export=VAR=value`)
|
|
90
|
+
5. Default values from the cluster (e.g. `SBATCH_PARTITION`)
|
|
91
|
+
|
|
92
|
+
### Example
|
|
93
|
+
|
|
94
|
+
Here's an example `pyproject.toml` with cluv configuration for three clusters, and some global and cluster-specific environment variables:
|
|
95
|
+
|
|
96
|
+
```toml
|
|
97
|
+
[tool.cluv]
|
|
98
|
+
results_path = "logs"
|
|
99
|
+
|
|
100
|
+
[tool.cluv.env]
|
|
101
|
+
SBATCH_TIMELIMIT = "3:00:00"
|
|
102
|
+
WANDB_MODE = "offline"
|
|
103
|
+
|
|
104
|
+
[tool.cluv.clusters.mila]
|
|
105
|
+
env = { WANDB_MODE="online", SBATCH_PARTITION="long" }
|
|
106
|
+
|
|
107
|
+
[tool.cluv.clusters.narval]
|
|
108
|
+
|
|
109
|
+
[tool.cluv.clusters.tamia]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Commands
|
|
113
|
+
|
|
114
|
+
### `cluv init`
|
|
115
|
+
|
|
116
|
+
Initialize the current directory as a cluv project. Must be run from inside your `$HOME` directory.
|
|
117
|
+
|
|
118
|
+
```
|
|
119
|
+
cluv init
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Default project structure after `cluv init`:
|
|
123
|
+
```
|
|
124
|
+
my_project/
|
|
125
|
+
├── README.md
|
|
126
|
+
├── logs -> $SCRATCH/logs/my_project # symlink to $SCRATCH
|
|
127
|
+
├── pyproject.toml # includes [tool.cluv] config
|
|
128
|
+
├── scripts/
|
|
129
|
+
│ ├── job.sh # Slurm job script template
|
|
130
|
+
│ └── safe_job.sh # Slurm job script template (copies .venv and prior results)
|
|
131
|
+
└── src/
|
|
132
|
+
└── my_project/
|
|
133
|
+
└── __init__.py
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### `cluv login`
|
|
137
|
+
|
|
138
|
+
Open SSH ControlMaster connections to all configured clusters. Run this before any command that requires a live connection.
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
cluv login [<cluster> ...]
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `cluv sync`
|
|
145
|
+
|
|
146
|
+
Push local git changes, then on each cluster: clone or fetch the repo, check out the current branch, and run `uv sync`. Optionally rsyncs results back if `results_path` is set in the config.
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
cluv sync [<cluster> ...]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
### `cluv status`
|
|
154
|
+
|
|
155
|
+
Display an overview of :
|
|
156
|
+
* Cluster: GPU availability, running/queued jobs and disk usage.
|
|
157
|
+
* Jobs: cached jobs from `cluv submit` with their status.
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
cluv status [<table>]
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `cluv submit`
|
|
164
|
+
|
|
165
|
+
Submit a SLURM job on a remote cluster.
|
|
166
|
+
|
|
167
|
+
```
|
|
168
|
+
cluv submit <cluster> <job.sh> [<sbatch-flags> ...] [-- <program-args> ...]
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
For example:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
cluv submit rorqual script/job.sh --time=00:10:00 -- python main.py
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### `cluv run`
|
|
178
|
+
|
|
179
|
+
Sync the project to a cluster, then run a command there with `uv run`.
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
cluv run <cluster> <command> [<args> ...]
|
|
183
|
+
```
|