pl_vendor
pl_vendor copies Git repositories or selected upstream directories into another
repository as ordinary files and records their exact commits in a deterministic YAML
lockfile. It supports optional downstream patches and can verify that checked-in
vendor trees still match their locked upstream commits.
Requirements
- Python 3.13 or newer
- Git 2.30 or newer
Rich is installed as a Python runtime dependency for terminal progress and styling.
Every command checks the installed Git version before reading or changing vendored trees.
Configuration
Add a dependency interactively:
pl-vendor add
Or provide its settings as arguments (the path defaults to vendor/NAME and the
branch defaults to main):
pl-vendor add example https://github.com/example/example.git vendor/example release
Dependencies can instead be pinned to an exact commit. Use --subdirectory to
export only one directory from an upstream repository:
pl-vendor add prairielearn-schemas \
https://github.com/PrairieLearn/PrairieLearn.git \
.prairielearn/schemas \
--commit 2c60f70a0ef817a2dc33fe15cb8e56de5035d6ed \
--subdirectory apps/prairielearn/src/schemas/schemas
This creates or updates plvendor.toml at the root of the consuming Git repository,
fetches the dependency, and records its revision. The resulting manifest looks like:
[tool.pl-vendor]
version = "^0.3"
[dependencies.example]
path = "vendor/example"
url = "https://github.com/example/example.git"
branch = "release"
The version requirement uses caret semantics. ^0.3 accepts compatible 0.3.x
versions of pl-vendor and rejects versions from another minor release.
A commit-pinned subdirectory dependency looks like this:
[dependencies.prairielearn-schemas]
path = ".prairielearn/schemas"
url = "https://github.com/PrairieLearn/PrairieLearn.git"
commit = "2c60f70a0ef817a2dc33fe15cb8e56de5035d6ed"
subdirectory = "apps/prairielearn/src/schemas/schemas"
Every dependency must set exactly one of branch or commit. Commit values are
complete lowercase 40-character Git SHAs. subdirectory is relative to the upstream
repository and defaults to its root.
The generated plvendor-lock.yaml records the resolved commit:
lockfileVersion: 1
dependencies:
example: '0123456789abcdef0123456789abcdef01234567'
Optional patches live at .vendor-patches/<dependency-name>.patch and are applied
after selecting the upstream subdirectory, so patch paths are relative to the
installed vendored tree.
Commands
pl-vendor update
pl-vendor update example
pl-vendor add example https://github.com/example/example.git --branch release
pl-vendor remove example
pl-vendor remove first second
pl-vendor lock
pl-vendor lock --check
pl-vendor lock --upgrade
pl-vendor lock --upgrade-package example
pl-vendor sync
pl-vendor sync example
pl-vendor check
pl-vendor require-latest
pl-vendor require-latest example second
pl-vendor verify
Like uv add and uv remove, pl-vendor add and pl-vendor remove update the
manifest, lockfile, and installed files together. Remove accepts one or more dependency
names and deletes their complete managed trees, including local or generated content.
Removing the final dependency also removes the now-unused manifest and lockfile;
downstream patches are retained.
pl-vendor lock fills missing lock entries while preferring existing revisions. Pass
--upgrade or --upgrade-package NAME to explicitly advance branch dependencies;
commit-pinned dependencies never advance. A commit pin is authoritative, so lock
reconciles a stale lock entry to the manifest without contacting the remote. This does
not modify vendored files.
pl-vendor update remains a convenience command that advances branch dependencies and
syncs one or all dependencies in one step. For a pinned dependency, it installs the
manifest commit and updates the lockfile to that exact value without rewriting the
manifest. If the manifest exists but the lockfile does not, update bootstraps a
complete lockfile from the manifest instead of requiring a separate lock step.
Because the lockfile is generated state, update also replaces it when it has
uncommitted changes; uncommitted manifest and vendored-tree changes remain protected.
pl-vendor sync does not update the lockfile: it replaces the selected vendored trees
with their exact locked revisions and reapplies downstream patches. As with uv's
default exact sync, local or generated files inside those managed trees are removed.
During update, sync, and verify, entries from the same source share a temporary
blobless Git object store. Each revision is checked out once, using a sparse worktree
containing only the configured upstream subdirectories unless an entry requests the
repository root. This avoids fetching unrelated file contents and reuses unchanged
objects when entries select different commits from the same source. Branch lookups are
also batched per source URL.
Like uv, pl-vendor writes human-readable operation status to standard error and keeps
standard output empty. In an interactive terminal, add, update, and sync show
transient fetch and install progress. Redirected output retains only deterministic
phase summaries and installed dependency records:
Fetched 2 source revisions in 1.24s
Installed 3 dependencies in 84ms
+ first abcdef12 -> vendor/first
~ second 12345678 -> vendor/second (patched)
~ third 87654321 -> vendor/third
Fetch counts represent unique source URL and revision pairs, while install counts
represent configured dependencies. + marks a newly created destination and ~
marks an existing destination that was replaced. Revisions are shortened to eight
characters for display; the lockfile remains authoritative for complete revisions.
pl-vendor check reports commit-pinned dependencies as pinned and checks branch
dependencies for newer revisions. Use pl-vendor require-latest [PACKAGE ...] in CI
when pins should be forbidden: it fails for every selected commit-pinned dependency and
for every selected branch dependency whose locked revision is not the branch head. With
no package names, it checks all dependencies.
Each command discovers the consuming repository from the current directory. Pass
--root PATH after the command to operate on a different repository.
Pass --no-progress to suppress transient animation without hiding the durable
summaries. --color auto|always|never controls styling; auto is the default and
also honors standard terminal color environment variables such as NO_COLOR.
Development
This repository uses uv 0.9 or newer to manage its development environment:
uv sync --dev
make test
make format
make build
The test suite is kept in the top-level tests directory so it is separate from
the installable pl_vendor package.
To run a disposable walkthrough of the main workflow backed by local Git repositories:
./scripts/demo.sh
Pass --keep to retain the generated upstream and consumer repositories for
inspection.
To publish a release, start from a clean working tree and run:
make publish-version VERSION=0.2.0
This runs the test suite and static checks, updates pyproject.toml and uv.lock,
builds the distributions, creates a release commit and annotated v0.2.0 tag, and
atomically pushes both to origin. The tag starts the PyPI publishing workflow. Use
REMOTE=name to publish through a different Git remote. The requested version must
be semantically newer than the current package version and all local or remote
release tags.
Release files for pl_vendor 0.3.4
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pl_vendor-0.3.4.tar.gz | 36.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pl_vendor-0.3.4-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 60.8 kB
Release files / pl_vendor-0.3.4.tar.gz
| Download URL | pl_vendor-0.3.4.tar.gz |
|---|---|
| Size | 36.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b00bc2fb2859599c6bcc35559a41f482cc8b5f358dee0b4ad7f21708a7154644
|
|
BLAKE2b-256 checksum How to use checksums |
f16e7f6e4edfebc6a62bdba9589495575c93894a7c7cd88d27fab1b0109d357e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency logRelease files / pl_vendor-0.3.4-py3-none-any.whl
| Download URL | pl_vendor-0.3.4-py3-none-any.whl |
|---|---|
| Size | 24.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
62232e314563398b7ae9016f6c91c50ae5b665aad4f16424d9209d3be6acb5f1
|
|
BLAKE2b-256 checksum How to use checksums |
a1368946aa838c1b273081bd556a621bb6b85051b3d2ef30f08c6a7e69bda3f6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 19, 2026.
Transparency log