Skip to main content

fastws

Fast workspace tools for multi-repo management.

Install

pip install fastws-cli

Setup

Create a repos.txt file listing your repos (one per line):

AnswerDotAI/fastcore
AnswerDotAI/fastgit
AnswerDotAI/fastship
AnswerDotAI/fastws

A line may add a checkout location after the repo, for repos that live outside the workspace root (~ ok). Every command then reads and writes them there, and ws-sync editable-installs each Python project found inside (the repo root if it has a pyproject.toml, else its immediate subdirectories), so one out-of-tree repo can hold several small packages alongside non-Python content:

AnswerDotAI/fastcore
jph00/private ~/private

Commands

ws-clone

Clone all repos from your repos file:

ws-clone
ws-clone --repos-file myrepos.txt
ws-clone --workers 8

ws-pull

Pull updates for all repos (parallel):

ws-pull

ws-status

Show uncommitted changes and unpushed commits:

ws-status
ws-status --branches

ws-branches

Check if all repos are on the expected branch:

ws-branches
ws-branches --expected develop

ws-build

Build an sdist of each workspace project (including repos.txt checkouts outside the root) into a dists directory, .dists by default. A project is rebuilt only when a file in it is newer than its existing sdist, and older versions of a rebuilt package are pruned, so the directory always holds one current sdist per project. A failing project gets a warning without stopping the others, but the command then exits 1.

Progress goes to stderr; on success the dists path prints to stdout, so $(ws-build) in a script both refreshes the pool and yields its location. The result suits any resolver that takes a candidate pool, e.g. uv pip install --find-links or a docker build context for images that must test unreleased workspace packages:

ws-build
ws-build --force  # rebuild everything
ws-build --out /tmp/dists

ws-sync

Sync the workspace metadata, pull local repos, and install updates. By default it uses the active venv parent as the workspace root, so you do not need to cd first. It respects tool.uv.workspace.members and exclude when scanning local projects, and if any member directory isn't a Python project yet (no pyproject.toml, e.g. a fresh empty clone), it warns and skips the uv sync step instead of letting uv fail on the whole workspace.

The workspace exclude list is auto-managed: a top-level directory that isn't a valid Python project and isn't a repos.txt checkout gets excluded automatically, and is un-excluded once it gains a real pyproject.toml. Globs, entries for missing directories, and entries for repos.txt checkouts are always kept, and exclude = [...] under [tool.fastws] in the workspace pyproject.toml declares intent the scan can't infer (e.g. keeping a real project out of the workspace). Hand-written [tool.uv.sources] entries (path, git, ...) are preserved when syncing adds new members.

At most once per day (tracked by a stamp file inside the workspace's .git, so git never sees it), the sync also floats dependencies: uv sync -U instead of plain uv sync, plus a parallel cargo update in every member with a Cargo.toml, printing what moved. Pass --upgrade to force that pass regardless of when it last ran.

Before every uv sync, ws-sync writes .git/fastws-cargo-key for each member crate. The key hashes Cargo.lock contents and the workspace Cargo patch configuration. For Git dependencies redirected to local paths by [patch."<url>"], it also hashes each patched crate's Cargo.toml, build.rs, and src tree, recursively. The file is rewritten only when that content changes, so projects can use { file = ".git/fastws-cargo-key" } in tool.uv.cache-keys without rebuilding after a timestamp-only Cargo.lock write.

ws-sync
ws-sync --workspace ~/aai-ws
ws-sync --upgrade

ws-add

Add a repo to repos.txt, then run ws-sync. Given owner/repo, it clones; given the name of an existing local folder (e.g. one just scaffolded with nbdev-new or ship-new), it resolves owner/repo from the folder's origin remote instead, telling you exactly what's missing if the folder has no git repo, no GitHub origin, or no pyproject.toml. Given a path outside the workspace root, the repo stays where it is and its location is recorded in repos.txt (no root pyproject.toml needed: its packages are discovered on sync):

ws-add AnswerDotAI/fastws
ws-add answerdotai/fastws
ws-add fastws  # existing local folder, resolved via its origin remote
ws-add ~/private  # a path outside the workspace: stays where it is, recorded in repos.txt with its location

ws-remove

Remove a repo: delete its clone, and drop it from repos.txt and the workspace pyproject.toml, then run uv sync. It refuses if the directory has uncommitted changes, unpushed commits, no origin remote, or isn't a clean git checkout, and always prompts for confirmation before deleting anything:

ws-remove AnswerDotAI/fastws
ws-remove fastws  # bare folder name also works if the directory exists

ws-releases

Report repos with commits since their newest GitHub release, so nothing reviewed sits unshipped:

ws-releases                 # at the workspace root: sweep every repo in repos.txt
ws-releases solveit         # only solveit's transitive workspace dependencies
ws-releases --nodeps        # inside a repo: just that repo
ws-releases --skip 'wip'    # extra start-of-message regex for commits that need no release

Run from inside a workspace checkout, the sweep narrows to that repo and its transitive workspace dependencies; --nodeps narrows it to the repo alone (an error when no project is in play).

Each pending repo lists its unreleased commit summaries; repos with no releases yet get one quiet no releases: line, and fully-released repos appear in up to date:. The newest release is picked by version number (publish timestamps can be out of order), and repos whose default branch isn't main are handled automatically.

Commits whose message matches a start-anchored regex from the skip set need no release and aren't reported. The built-in set covers version bumps and housekeeping (bump, nbdev regen, .gitignore, docs, CI, ...: DEFAULT_SKIP in fastws.releases); [tool.fastws] in the workspace root pyproject.toml adds to it, and names repos that should never be swept (apps that deploy rather than release):

[tool.fastws]
release_skip = ["docs only"]
release_exclude = ["solveit", "md_site"]

From Python (the primary interface - the CLI is a thin wrapper over fastws.releases):

from fastws import check_releases, check_release
await check_releases()            # ReleaseReport: the repr is the report
await check_releases('solveit')   # dependency-closure mode
await check_release('mdhtml')     # one repo: list of unreleased commit summaries (None = no releases)

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastws_cli-0.0.9.tar.gz (30.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastws_cli-0.0.9-py3-none-any.whl (21.3 kB view details)

Uploaded Python 3

File details

Details for the file fastws_cli-0.0.9.tar.gz.

File metadata

  • Download URL: fastws_cli-0.0.9.tar.gz
  • Upload date:
  • Size: 30.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for fastws_cli-0.0.9.tar.gz
Algorithm Hash digest
SHA256 2cd3a3936f426ac1cbf0a0d18b7f31b26aaa59f76c37eb828a583ed323a135b3
MD5 ce0a39abfc2d665350d87fedc4befe51
BLAKE2b-256 47fd2b226ea1c739f385d7a51fa0ca5d626f3dc287ec8143d8c4d342136fedc0

See more details on using hashes here.

File details

Details for the file fastws_cli-0.0.9-py3-none-any.whl.

File metadata

  • Download URL: fastws_cli-0.0.9-py3-none-any.whl
  • Upload date:
  • Size: 21.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.2

File hashes

Hashes for fastws_cli-0.0.9-py3-none-any.whl
Algorithm Hash digest
SHA256 68877047a472a168ca10fd7e8b41521aec2f426efc3f38770cd8dbe0f43a696c
MD5 4699a4837269c11ad08300fea5d523e5
BLAKE2b-256 aa8c2bccbbcf993c89a11ebadc68adb82d99cf410eb16cdb7d11917cfe89fee8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page