uvault 0.2.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.
@@ -0,0 +1,34 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+
18
+ - name: Install uv
19
+ uses: astral-sh/setup-uv@v8.2.0
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ enable-cache: true
23
+
24
+ - name: Run tests and coverage
25
+ run: uv run --frozen pytest
26
+
27
+ pre-commit:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v6
31
+ - uses: actions/setup-python@v6
32
+ with:
33
+ python-version: "3.13"
34
+ - uses: pre-commit/action@v3.0.1
@@ -0,0 +1,30 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ jobs:
9
+ publish:
10
+ name: Upload release to PyPI
11
+ runs-on: ubuntu-latest
12
+ environment:
13
+ name: pypi
14
+ url: https://pypi.org/p/uvault
15
+ permissions:
16
+ id-token: write
17
+ steps:
18
+ - uses: actions/checkout@v6
19
+
20
+ - name: Install uv
21
+ uses: astral-sh/setup-uv@v8.2.0
22
+ with:
23
+ python-version: "3.13"
24
+ enable-cache: true
25
+
26
+ - name: Build package
27
+ run: uv build
28
+
29
+ - name: Publish to PyPI
30
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,13 @@
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
+ .specs/.thirdparty/
12
+ .specs/
13
+ site/
@@ -0,0 +1,14 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.6.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-toml
9
+ - repo: https://github.com/astral-sh/ruff-pre-commit
10
+ rev: v0.4.8 # Matches compatible pre-commit version hook, will auto-update
11
+ hooks:
12
+ - id: ruff
13
+ args: [ --fix ]
14
+ - id: ruff-format
@@ -0,0 +1 @@
1
+ 3.14
uvault-0.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pierre Verkest
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.
uvault-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: uvault
3
+ Version: 0.2.0
4
+ Summary: Development and vaulting workflow for uv VCS dependencies.
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: diskcache>=5.6.3
8
+ Requires-Dist: tomlkit>=0.15.0
9
+ Description-Content-Type: text/markdown
10
+
11
+ # uvault
12
+
13
+ Development and vaulting workflow for [uv](https://github.com/astral-sh/uv) VCS dependencies.
14
+
15
+ Secure your Python projects against deleted or force-pushed upstream commits. `uvault` automatically vaults transient VCS references (like GitHub PRs) into your organization's own repositories, while allowing developers to instantly switch dependencies into local editable mode—fully integrated with `pyproject.toml` and `uv`.
16
+
17
+ ## Documentation
18
+
19
+ The complete documentation is available in the `docs/` folder:
20
+
21
+ * [Quickstart & Key Features](docs/index.md) - Learn what `uvault` is and how to get started quickly.
22
+ * [How-To Guides](docs/how-to.md) - Step-by-step guides for installing and using `uvault` in your day-to-day workflow.
23
+ * [CLI & Configuration Reference](docs/reference.md) - Detailed information on `pyproject.toml` configuration (`[tool.uvault]`) and all CLI commands (`sync`, `add`, `develop`).
uvault-0.2.0/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # uvault
2
+
3
+ Development and vaulting workflow for [uv](https://github.com/astral-sh/uv) VCS dependencies.
4
+
5
+ Secure your Python projects against deleted or force-pushed upstream commits. `uvault` automatically vaults transient VCS references (like GitHub PRs) into your organization's own repositories, while allowing developers to instantly switch dependencies into local editable mode—fully integrated with `pyproject.toml` and `uv`.
6
+
7
+ ## Documentation
8
+
9
+ The complete documentation is available in the `docs/` folder:
10
+
11
+ * [Quickstart & Key Features](docs/index.md) - Learn what `uvault` is and how to get started quickly.
12
+ * [How-To Guides](docs/how-to.md) - Step-by-step guides for installing and using `uvault` in your day-to-day workflow.
13
+ * [CLI & Configuration Reference](docs/reference.md) - Detailed information on `pyproject.toml` configuration (`[tool.uvault]`) and all CLI commands (`sync`, `add`, `develop`).
@@ -0,0 +1,84 @@
1
+ # Tutorials / How-To
2
+
3
+ ## How to manage and vault dependencies
4
+
5
+ `uvault` is built around a typical workflow: declaring a dependency intention, synchronizing it to your vault, and (optionally) switching to local development mode.
6
+
7
+ ### Installation (Recommended)
8
+
9
+ To get the most out of `uvault`, especially its ability to automatically discover repository URLs via package metadata when using the `uvault add` command, we highly recommend installing it as a development dependency in your project:
10
+
11
+ ```bash
12
+ uv add --dev uvault
13
+ ```
14
+
15
+ By installing it within your project's environment, `uvault` gains access to the local site-packages, allowing it to easily read the metadata of your project's existing dependencies.
16
+
17
+ ### Step 1: Add a new dependency (`uvault add`)
18
+
19
+ Instead of manually editing the `pyproject.toml` file, use the `uvault add` command to declare an intention in `[tool.uvault.sources]`.
20
+
21
+ ```bash
22
+ uvault add my-addon https://github.com/OCA/my-addon --pr 123 --subdirectory my_addon
23
+ ```
24
+
25
+ If the repository URL is already known (published package), you can omit it and `uvault` will try to guess it from the package metadata:
26
+ ```bash
27
+ uvault add my-addon --branch 16.0
28
+ ```
29
+
30
+ *Note: This command only configures your intention. It does not update your `uv` lockfile.*
31
+
32
+ ### Step 2: Synchronize and Vault (`uvault sync`)
33
+
34
+ Once you have declared your dependencies, run the synchronization command to fetch them and push them to your organization's vault repository.
35
+
36
+ ```bash
37
+ uvault sync
38
+ ```
39
+
40
+ The command will:
41
+ 1. Fetch the exact commit of the targeted branch or PR.
42
+ 2. Clone this repository into the local cache (if necessary).
43
+ 3. Push this commit to your vault repository as a frozen tag (e.g. `ppr-<sha>`).
44
+ 4. Automatically update the `[tool.uv.sources]` block in `pyproject.toml` to point to this immutable reference.
45
+
46
+ **Crucial Step:** After syncing, you must instruct `uv` to resolve the new dependencies:
47
+ ```bash
48
+ uv lock
49
+ # or
50
+ uv sync
51
+ ```
52
+
53
+ #### Update an existing reference
54
+ If the original PR has been updated (new commits):
55
+
56
+ ```bash
57
+ uvault sync --update
58
+ ```
59
+
60
+ This will force the retrieval of new changes and update the vault reference. You can also target specific packages (which automatically implies an update):
61
+
62
+ ```bash
63
+ uvault sync --package my-addon
64
+ uvault sync -P my-addon -P another-addon
65
+ ```
66
+
67
+ ### Step 3: Develop Locally (`uvault develop`)
68
+
69
+ When you need to make modifications to a vaulted dependency, you can switch it into local editable mode in seconds.
70
+
71
+ ```bash
72
+ uvault develop my-addon my-feature-branch
73
+ ```
74
+
75
+ This command will:
76
+ 1. Clone the repository into your local workspace (e.g., `./.src/my-addon`).
77
+ 2. Automatically configure your custom vault remotes (defined in `~/.config/uvault/config.toml`) so you can push your work easily.
78
+ 3. Replace the remote reference in `[tool.uv.sources]` with a local, editable path (`editable = true`).
79
+ 4. Creates and checks out the new branch (`git checkout -b <branch>`), or simply switches to it if it already exists.
80
+
81
+ **Crucial Step:** Once the local path is set, make sure to synchronize your environment:
82
+ ```bash
83
+ uv sync
84
+ ```
@@ -0,0 +1,42 @@
1
+ # uvault
2
+
3
+ Development and vaulting workflow for [uv](https://github.com/astral-sh/uv) VCS dependencies.
4
+
5
+ Secure your Python projects against deleted or force-pushed upstream commits.
6
+
7
+ `uvault` automatically vaults transient VCS references (like GitHub PRs)
8
+ into your organization's own repositories, while allowing developers to instantly
9
+ switch dependencies into local editable mode—fully integrated with `pyproject.toml` and `uv`.
10
+
11
+ ## Key Features
12
+
13
+ 1. **Vaulting of Commits**: Never lose code again! Upstream pull requests and branches can be force-pushed or deleted. `uvault` fetches the exact commits your project depends on and pushes them as immutable tags to your own organization's vault repository.
14
+ 2. **Easy Local Development**: Switch any VCS dependency to local "editable" mode in seconds. `uvault develop` clones the package locally and seamlessly configures `uv` to use your local copy so you can test changes and contribute back.
15
+
16
+ ## Quickstart
17
+
18
+ ### 1. Add a Dependency
19
+
20
+ Use `uvault add` to register an intention for a dependency. You can pass a PEP 508 URL or rely on automatic URL guessing if the package is already published.
21
+
22
+ ```bash
23
+ uvault add odoo-addon-my-package https://github.com/OCA/my-repo --pr 123 --subdirectory my_package
24
+ ```
25
+
26
+ ### 2. Vault and Lock
27
+
28
+ Synchronize the dependency with your vault and lock it in `uv`.
29
+
30
+ ```bash
31
+ uvault sync
32
+ uv lock
33
+ ```
34
+
35
+ ### 3. Develop Locally
36
+
37
+ Need to edit the dependency? Clone it instantly and set it as an editable dependency in `[tool.uv.sources]`.
38
+
39
+ ```bash
40
+ uvault develop my-package feat-branch
41
+ uv sync
42
+ ```
@@ -0,0 +1,100 @@
1
+ # Reference
2
+
3
+ ## Installation & Usage Modes
4
+
5
+ `uvault` can be executed in two different ways depending on your needs:
6
+
7
+ ### 1. As a Project Development Dependency (Recommended)
8
+
9
+ Installing `uvault` as a dev dependency gives it access to your project's environment. This is crucial if you want `uvault add` to automatically guess repository URLs based on the metadata of your installed packages.
10
+
11
+ ```bash
12
+ uv add --dev uvault
13
+ uv run uvault --help
14
+ ```
15
+
16
+ ### 2. As an Independent Tool via `uvx`
17
+
18
+ If you prefer keeping your dependencies minimal and don't need the automatic URL resolution feature, you can run `uvault` purely as an isolated, ephemeral tool using `uvx`.
19
+
20
+ ```bash
21
+ uvx uvault sync
22
+ ```
23
+
24
+ ## `pyproject.toml` Configuration
25
+
26
+ The intention configuration is located in the `[tool.uvault]` section:
27
+
28
+ ```toml
29
+ [tool.uvault]
30
+ tag_prefix = "apycod-" # (Optional) Prefix for generated tags. Defaults to "".
31
+ include_project_version = true # (Optional) Includes the current project's version in the vault tag. Defaults to true.
32
+ dev_directory = ".src/" # (Optional) Directory for developed sources. Defaults to ".src/".
33
+
34
+ # VCS Vault Configuration
35
+ [[tool.uvault.vcs_vaults]]
36
+ provider = "github.com" # (Required) The git hosting provider (e.g., "github.com" or "gitlab.com").
37
+ owner = "petrus-v" # (Optional) The user or organization owning the vault repository.
38
+ fetch_ssh = true # (Optional) If true, generates ssh:// URLs in [tool.uv.sources] instead of https://. Defaults to false.
39
+ push_ssh = true # (Optional) If true, pushes tags to the vault using ssh://. Defaults to true.
40
+ default = true # (Optional) Marks this vault as the default for synchronizing packages.
41
+
42
+ # Declaration of dependencies to synchronize
43
+ [tool.uvault.sources]
44
+ my-package = { git = "https://github.com/OCA/repository", rev = "refs/pull/100/head", subdirectory = "my_package" }
45
+ ```
46
+
47
+ ## User Configuration
48
+
49
+ You can define local machine-specific configurations, such as custom git remotes, in `~/.config/uvault/config.toml`. These remotes will be automatically added to the repository when running `uvault develop`.
50
+
51
+ ```toml
52
+ [remotes]
53
+ myorg = "https://gitlab.com/myorg/"
54
+ perso = "ssh://git@github.com/personal/"
55
+ ```
56
+
57
+ ## CLI Commands
58
+
59
+ ### `uvault sync`
60
+
61
+ Synchronizes remote references to the vault repository and updates `[tool.uv.sources]`.
62
+
63
+ **Note:** By default, `uvault sync` will skip any package that is already declared in `[tool.uv.sources]`.
64
+
65
+ **Flags:**
66
+ * `-P <package>, --package <package>` : Only sync the specified package. Can be used multiple times.
67
+ * `--update` : Forces an update of the package(s) even if they are already present in `[tool.uv.sources]`.
68
+ * `--delete-extra` : Removes any package found in `[tool.uv.sources]` that is not declared in `[tool.uvault.sources]`.
69
+ * `--keep-develop` : By default, packages in develop mode (`editable = true` or using `path`) are restored to their vaulted state during sync. Use this flag to keep them in develop mode.
70
+
71
+ ### `uvault add`
72
+
73
+ Adds a new dependency intention directly into `[tool.uvault.sources]` without manual file editing.
74
+
75
+ **Arguments:**
76
+ * `<package>` : The name of the package to add.
77
+ * `[url]` : (Optional) The VCS URL (e.g. `https://github.com/OCA/my-addon`). If omitted, `uvault` will attempt to guess it from PyPI metadata.
78
+
79
+ **Options:**
80
+ * `--branch <name>` : Target a specific branch.
81
+ * `--tag <name>` : Target a specific tag.
82
+ * `--pr <number>` : Target a pull/merge request number.
83
+ * `--rev <hash>` : Target a specific commit hash or exact reference.
84
+ * `--subdirectory <path>` : The subdirectory within the repository where the Python package is located.
85
+
86
+ ### `uvault develop`
87
+
88
+ Switches a vaulted dependency into local editable mode for active development.
89
+
90
+ **Arguments:**
91
+ * `<package>` : The name of the package to develop locally (must be declared in `[tool.uvault.sources]`).
92
+ * `<branch>` : The name of the branch to checkout or create.
93
+
94
+ **Workflow Details:**
95
+ 1. Clones (using a fast partial blobless clone) or fetches the repository into your `.src/` directory.
96
+ 2. Creates and checks out the new branch (`git checkout -b <branch>`), or switches to it if it exists (`git checkout <branch>`).
97
+ 3. Sets up remotes, including `origin`, `vault`, and any other custom remotes defined in `~/.config/uvault/config.toml`.
98
+ 4. Modifies `[tool.uv.sources]` to use `{ path = "./.src/<package>", editable = true }`.
99
+
100
+ ::: uvault.cli
@@ -0,0 +1,15 @@
1
+ site_name: uvault documentation
2
+ theme:
3
+ name: material
4
+ features:
5
+ - navigation.tabs
6
+ - navigation.sections
7
+
8
+ plugins:
9
+ - search
10
+ - mkdocstrings
11
+
12
+ nav:
13
+ - Quickstart: index.md
14
+ - Tutorials / How-To: how-to.md
15
+ - Reference: reference.md
@@ -0,0 +1,53 @@
1
+ [project]
2
+ name = "uvault"
3
+ version = "0.2.0"
4
+ description = "Development and vaulting workflow for uv VCS dependencies."
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "diskcache>=5.6.3",
9
+ "tomlkit>=0.15.0",
10
+ ]
11
+
12
+ [dependency-groups]
13
+ dev = [
14
+ "bump-my-version>=1.3.0",
15
+ "mkdocs-material>=9.7.6",
16
+ "mkdocstrings[python]>=1.0.4",
17
+ "pre-commit>=4.6.0",
18
+ "pytest>=9.0.3",
19
+ "pytest-cov>=7.1.0",
20
+ "ruff>=0.15.16",
21
+ ]
22
+
23
+
24
+ [build-system]
25
+ requires = ["hatchling"]
26
+ build-backend = "hatchling.build"
27
+
28
+ [project.scripts]
29
+ uvault = "uvault.cli:main"
30
+
31
+ [tool.pytest.ini_options]
32
+ addopts = ["--cov=src/uvault", "--cov-report=term-missing", "--cov-fail-under=100"]
33
+ testpaths = ["tests"]
34
+
35
+ [tool.ruff]
36
+ line-length = 88
37
+ target-version = "py310"
38
+
39
+ [tool.bumpversion]
40
+ current_version = "0.2.0"
41
+ commit = true
42
+ tag = true
43
+ tag_name = "v{new_version}"
44
+
45
+ [[tool.bumpversion.files]]
46
+ filename = "pyproject.toml"
47
+ search = 'version = "{current_version}"'
48
+ replace = 'version = "{new_version}"'
49
+
50
+ [[tool.bumpversion.files]]
51
+ filename = "src/uvault/__init__.py"
52
+ search = '__version__ = "{current_version}"'
53
+ replace = '__version__ = "{new_version}"'
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -0,0 +1,108 @@
1
+ from pathlib import Path
2
+ import urllib.parse
3
+ import tomlkit
4
+
5
+ from uvault.vcs import guess_repository_url
6
+
7
+
8
+ class AddCommand:
9
+ def __init__(
10
+ self,
11
+ package: str,
12
+ url: str | None = None,
13
+ pr: str | None = None,
14
+ branch: str | None = None,
15
+ tag: str | None = None,
16
+ rev: str | None = None,
17
+ subdirectory: str | None = None,
18
+ pyproject_path: str = "pyproject.toml",
19
+ ):
20
+ self.package = package
21
+ self.url = url
22
+ self.pr = pr
23
+ self.branch = branch
24
+ self.tag = tag
25
+ self.rev = rev
26
+ self.subdirectory = subdirectory
27
+ self.pyproject_path = Path(pyproject_path)
28
+
29
+ def _parse_url(self):
30
+ if not self.url:
31
+ return
32
+
33
+ # Handle git+https://... format
34
+ if self.url.startswith("git+"):
35
+ url_no_git = self.url[4:]
36
+ parsed = urllib.parse.urlparse(url_no_git)
37
+
38
+ # Extract rev/branch/tag from @...
39
+ if "@" in parsed.path:
40
+ path, ref = parsed.path.split("@", 1)
41
+ self.url = f"{parsed.scheme}://{parsed.netloc}{path}"
42
+ self.rev = (
43
+ ref # by default it's a rev, could be branch/tag but uv accepts rev
44
+ )
45
+ else:
46
+ self.url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
47
+
48
+ # Extract subdirectory from #subdirectory=...
49
+ if parsed.fragment:
50
+ fragments = urllib.parse.parse_qs(parsed.fragment)
51
+ if "subdirectory" in fragments:
52
+ self.subdirectory = fragments["subdirectory"][0]
53
+
54
+ def run(self):
55
+ if not self.pyproject_path.exists():
56
+ print("pyproject.toml not found")
57
+ return 1
58
+
59
+ self._parse_url()
60
+
61
+ if not self.url:
62
+ guessed_url = guess_repository_url(self.package)
63
+ if not guessed_url:
64
+ print(f"Could not find or guess repository URL for {self.package}")
65
+ return 1
66
+ self.url = guessed_url
67
+ print(f"Guessed repository URL: {self.url}")
68
+
69
+ with open(self.pyproject_path, "r", encoding="utf-8") as f:
70
+ doc = tomlkit.parse(f.read())
71
+
72
+ if "tool" not in doc:
73
+ doc.add("tool", tomlkit.table())
74
+ if "uvault" not in doc["tool"]:
75
+ doc["tool"].add("uvault", tomlkit.table())
76
+ if "sources" not in doc["tool"]["uvault"]:
77
+ doc["tool"]["uvault"].add("sources", tomlkit.table())
78
+
79
+ uvault_sources = doc["tool"]["uvault"]["sources"]
80
+
81
+ new_source = tomlkit.inline_table()
82
+ new_source["git"] = self.url
83
+
84
+ # Priority: pr, rev, branch, tag
85
+ if self.pr:
86
+ if "github.com" in self.url:
87
+ new_source["rev"] = f"refs/pull/{self.pr}/head"
88
+ else:
89
+ new_source["rev"] = f"refs/merge-requests/{self.pr}/head"
90
+ elif self.rev:
91
+ new_source["rev"] = self.rev
92
+ elif self.branch:
93
+ new_source["branch"] = self.branch
94
+ elif self.tag:
95
+ new_source["tag"] = self.tag
96
+
97
+ if self.subdirectory:
98
+ new_source["subdirectory"] = self.subdirectory
99
+
100
+ uvault_sources[self.package] = new_source
101
+
102
+ with open(self.pyproject_path, "w", encoding="utf-8") as f:
103
+ f.write(tomlkit.dumps(doc))
104
+
105
+ print(f"Added {self.package} to [tool.uvault.sources]")
106
+ print("Run `uvault sync` to lock this dependency in [tool.uv.sources]")
107
+
108
+ return 0
@@ -0,0 +1,87 @@
1
+ import argparse
2
+ import sys
3
+ from uvault.sync import SyncCommand
4
+ from uvault.develop import DevelopCommand
5
+ from uvault.add import AddCommand
6
+
7
+
8
+ def main(argv=None):
9
+ if argv is None:
10
+ argv = sys.argv[1:] # pragma: no cover
11
+
12
+ parser = argparse.ArgumentParser(
13
+ description="Development and vaulting workflow for uv VCS dependencies."
14
+ )
15
+ subparsers = parser.add_subparsers(dest="command", required=True)
16
+
17
+ # Sync command
18
+ sync_parser = subparsers.add_parser("sync", help="Sync and vault VCS dependencies.")
19
+ sync_parser.add_argument(
20
+ "-U",
21
+ "--update",
22
+ action="store_true",
23
+ help="Force update the vaulted reference.",
24
+ )
25
+ sync_parser.add_argument(
26
+ "-P",
27
+ "--package",
28
+ action="append",
29
+ help="Specific package to sync. Can be used multiple times.",
30
+ )
31
+
32
+ sync_parser.add_argument(
33
+ "--delete-extra",
34
+ action="store_true",
35
+ help="Delete references in uv.sources that are not in uvault.sources.",
36
+ )
37
+ sync_parser.add_argument(
38
+ "--keep-develop",
39
+ action="store_true",
40
+ help="Keep packages that are in local develop mode instead of restoring them.",
41
+ )
42
+
43
+ # Add command
44
+ add_parser = subparsers.add_parser("add", help="Add a new vaulting intention.")
45
+ add_parser.add_argument("package", help="The package to add")
46
+ add_parser.add_argument("url", nargs="?", help="The VCS URL or PEP 508 string")
47
+ add_parser.add_argument("--pr", help="Pull request number")
48
+ add_parser.add_argument("--branch", help="Branch name")
49
+ add_parser.add_argument("--tag", help="Tag name")
50
+ add_parser.add_argument("--rev", help="Revision SHA")
51
+ add_parser.add_argument("--subdirectory", help="Subdirectory in the repository")
52
+
53
+ # Develop command
54
+ dev_parser = subparsers.add_parser("develop", help="Develop a package locally.")
55
+ dev_parser.add_argument("package", help="The package to develop")
56
+ dev_parser.add_argument("branch", help="Branch name to checkout or create")
57
+
58
+ args = parser.parse_args(argv)
59
+
60
+ if args.command == "sync":
61
+ cmd = SyncCommand(
62
+ packages=args.package,
63
+ update=args.update,
64
+ delete_extra=args.delete_extra,
65
+ keep_develop=args.keep_develop,
66
+ )
67
+ return cmd.run()
68
+ elif args.command == "develop":
69
+ cmd = DevelopCommand(package=args.package, branch=args.branch)
70
+ return cmd.run()
71
+ elif args.command == "add":
72
+ cmd = AddCommand(
73
+ package=args.package,
74
+ url=args.url,
75
+ pr=args.pr,
76
+ branch=args.branch,
77
+ tag=args.tag,
78
+ rev=args.rev,
79
+ subdirectory=args.subdirectory,
80
+ )
81
+ return cmd.run()
82
+
83
+ return 0 # pragma: no cover
84
+
85
+
86
+ if __name__ == "__main__": # pragma: no cover
87
+ sys.exit(main())