fastws-cli 0.0.7__tar.gz → 0.0.9__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,59 @@
1
+ <!-- do not remove -->
2
+
3
+ ## 0.0.9
4
+
5
+ ### New Features
6
+
7
+ - Add ws-build: build sdists of all workspace projects into a dists dir, skipping unchanged projects and pruning old versions ([#23](https://github.com/AnswerDotAI/fastws/issues/23))
8
+ - detect project from cwd, add --nodeps to skip the dependency closure, and map dir names to packages ([#22](https://github.com/AnswerDotAI/fastws/issues/22))
9
+ - Move `dep_key`, member graph, and closure helpers to ghapi (>=2.0.6) and use `dep_key`/`local_dep_graph`/`dep_closure` in core and releases ([#21](https://github.com/AnswerDotAI/fastws/issues/21))
10
+ - ws-sync: write content-hashed .git/fastws-cargo-key per crate, covering Cargo.lock and locally patched git deps, for uv cache-keys ([#20](https://github.com/AnswerDotAI/fastws/issues/20))
11
+ - Run cargo update before uv sync in `ws_sync` so Rust deps refresh first ([#19](https://github.com/AnswerDotAI/fastws/issues/19))
12
+ - Add out-of-tree repo support, auto-managed workspace excludes, and uv.sources preservation to ws-sync ([#18](https://github.com/AnswerDotAI/fastws/issues/18))
13
+
14
+
15
+ ## 0.0.8
16
+
17
+ ### New Features
18
+
19
+ - Add ws-releases: report unreleased commits across the workspace, with configurable skip patterns and repo exclusions ([#17](https://github.com/AnswerDotAI/fastws/pull/17)), thanks to [@jph00](https://github.com/jph00)
20
+ - Add once-daily dependency floating to ws_sync (uv sync -U + cargo update), with --upgrade flag to force ([#15](https://github.com/AnswerDotAI/fastws/issues/15))
21
+ - ws-sync skips uv sync for member dirs lacking pyproject.toml; ws-add resolves local folder names via origin remote ([#12](https://github.com/AnswerDotAI/fastws/issues/12))
22
+ - Allow bare folder name in ws-remove; separate directory deletion confirmation from metadata removal ([#9](https://github.com/AnswerDotAI/fastws/issues/9))
23
+ - Add `ws-remove` command to delete a repo from the workspace; refactor CLI wrappers with `@delegates` ([#8](https://github.com/AnswerDotAI/fastws/issues/8))
24
+ - Add [dev] extra to workspace dependencies and filter spurious warnings ([#7](https://github.com/AnswerDotAI/fastws/pull/7)), thanks to [@RensDimmendaal](https://github.com/RensDimmendaal)
25
+ - add --workers flag to ws-sync ([#6](https://github.com/AnswerDotAI/fastws/pull/6)), thanks to [@RensDimmendaal](https://github.com/RensDimmendaal)
26
+
27
+ ### Bugs Squashed
28
+
29
+ - Preserving existing tool.uv.sources entries during ws-sync ([#14](https://github.com/AnswerDotAI/fastws/pull/14)), thanks to [@kafkasl](https://github.com/kafkasl)
30
+
31
+
32
+ ## 0.0.7
33
+
34
+ ### New Features
35
+
36
+ - Refactor Pyright editable path setup to write pyrightconfig.json instead of .pth files ([#4](https://github.com/AnswerDotAI/fastws/issues/4))
37
+
38
+
39
+ ## 0.0.6
40
+
41
+ ### New Features
42
+
43
+ - Add root-aware clone/pull helpers, auto-pull in ws-sync, and docstrings to CLI entry points ([#2](https://github.com/AnswerDotAI/fastws/issues/2))
44
+
45
+
46
+ ## 0.0.5
47
+
48
+ ### New Features
49
+
50
+ - Add ws-sync and ws-add commands with workspace metadata sync and Pyright editable path support ([#1](https://github.com/AnswerDotAI/fastws/issues/1))
51
+
52
+
53
+ ## 0.0.3
54
+
55
+ - change names
56
+
57
+ ## 0.0.2
58
+
59
+ - init release
@@ -0,0 +1,193 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastws-cli
3
+ Version: 0.0.9
4
+ Summary: Fast workspace tools for multi-repo management
5
+ Author: Jeremy Howard
6
+ License: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/AnswerDotAI/fastws
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Requires-Python: >=3.10
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: fastcore>=1.14.3
14
+ Requires-Dist: fastgit>=0.0.7
15
+ Requires-Dist: ghapi>=2.0.6
16
+ Requires-Dist: packaging
17
+ Requires-Dist: tomli; python_version < "3.11"
18
+ Provides-Extra: dev
19
+ Requires-Dist: fastship; extra == "dev"
20
+ Requires-Dist: build; extra == "dev"
21
+ Requires-Dist: twine; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ # fastws
25
+
26
+ Fast workspace tools for multi-repo management.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install fastws-cli
32
+ ```
33
+
34
+ ## Setup
35
+
36
+ Create a `repos.txt` file listing your repos (one per line):
37
+
38
+ ```
39
+ AnswerDotAI/fastcore
40
+ AnswerDotAI/fastgit
41
+ AnswerDotAI/fastship
42
+ AnswerDotAI/fastws
43
+ ```
44
+
45
+ A line may add a checkout location after the repo, for repos that live outside
46
+ the workspace root (`~` ok). Every command then reads and writes them there, and
47
+ `ws-sync` editable-installs each Python project found inside (the repo root if it
48
+ has a `pyproject.toml`, else its immediate subdirectories), so one out-of-tree
49
+ repo can hold several small packages alongside non-Python content:
50
+
51
+ ```
52
+ AnswerDotAI/fastcore
53
+ jph00/private ~/private
54
+ ```
55
+
56
+ ## Commands
57
+
58
+ ### `ws-clone`
59
+
60
+ Clone all repos from your repos file:
61
+
62
+ ```bash
63
+ ws-clone
64
+ ws-clone --repos-file myrepos.txt
65
+ ws-clone --workers 8
66
+ ```
67
+
68
+ ### `ws-pull`
69
+
70
+ Pull updates for all repos (parallel):
71
+
72
+ ```bash
73
+ ws-pull
74
+ ```
75
+
76
+ ### `ws-status`
77
+
78
+ Show uncommitted changes and unpushed commits:
79
+
80
+ ```bash
81
+ ws-status
82
+ ws-status --branches
83
+ ```
84
+
85
+ ### `ws-branches`
86
+
87
+ Check if all repos are on the expected branch:
88
+
89
+ ```bash
90
+ ws-branches
91
+ ws-branches --expected develop
92
+ ```
93
+
94
+ ### `ws-build`
95
+
96
+ 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.
97
+
98
+ 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:
99
+
100
+ ```bash
101
+ ws-build
102
+ ws-build --force # rebuild everything
103
+ ws-build --out /tmp/dists
104
+ ```
105
+
106
+ ### `ws-sync`
107
+
108
+ Sync the workspace metadata, pull local repos, and install updates.
109
+ By default it uses the active venv parent as the workspace root, so you do not need to `cd` first.
110
+ It respects `tool.uv.workspace.members` and `exclude` when scanning local projects, and if any
111
+ member directory isn't a Python project yet (no `pyproject.toml`, e.g. a fresh empty clone), it
112
+ warns and skips the `uv sync` step instead of letting uv fail on the whole workspace.
113
+
114
+ The workspace `exclude` list is auto-managed: a top-level directory that isn't a valid Python
115
+ project and isn't a `repos.txt` checkout gets excluded automatically, and is un-excluded once it
116
+ gains a real `pyproject.toml`. Globs, entries for missing directories, and entries for `repos.txt`
117
+ checkouts are always kept, and `exclude = [...]` under `[tool.fastws]` in the workspace
118
+ `pyproject.toml` declares intent the scan can't infer (e.g. keeping a real project out of the
119
+ workspace). Hand-written `[tool.uv.sources]` entries (path, git, ...) are preserved when syncing
120
+ adds new members.
121
+
122
+ At most once per day (tracked by a stamp file inside the workspace's `.git`, so git never
123
+ sees it), the sync also floats dependencies: `uv sync -U` instead of plain `uv sync`, plus
124
+ a parallel `cargo update` in every member with a `Cargo.toml`, printing what moved. Pass
125
+ `--upgrade` to force that pass regardless of when it last ran.
126
+
127
+ 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.
128
+
129
+ ```bash
130
+ ws-sync
131
+ ws-sync --workspace ~/aai-ws
132
+ ws-sync --upgrade
133
+ ```
134
+
135
+ ### `ws-add`
136
+
137
+ Add a repo to `repos.txt`, then run `ws-sync`. Given `owner/repo`, it clones; given the
138
+ name of an existing local folder (e.g. one just scaffolded with `nbdev-new` or `ship-new`),
139
+ it resolves `owner/repo` from the folder's `origin` remote instead, telling you exactly
140
+ what's missing if the folder has no git repo, no GitHub origin, or no `pyproject.toml`.
141
+ Given a path outside the workspace root, the repo stays where it is and its location is
142
+ recorded in `repos.txt` (no root `pyproject.toml` needed: its packages are discovered on sync):
143
+
144
+ ```bash
145
+ ws-add AnswerDotAI/fastws
146
+ ws-add answerdotai/fastws
147
+ ws-add fastws # existing local folder, resolved via its origin remote
148
+ ws-add ~/private # a path outside the workspace: stays where it is, recorded in repos.txt with its location
149
+ ```
150
+
151
+ ### `ws-remove`
152
+
153
+ Remove a repo: delete its clone, and drop it from `repos.txt` and the workspace
154
+ `pyproject.toml`, then run `uv sync`. It refuses if the directory has uncommitted
155
+ changes, unpushed commits, no `origin` remote, or isn't a clean git checkout, and
156
+ always prompts for confirmation before deleting anything:
157
+
158
+ ```bash
159
+ ws-remove AnswerDotAI/fastws
160
+ ws-remove fastws # bare folder name also works if the directory exists
161
+ ```
162
+
163
+ ### `ws-releases`
164
+
165
+ Report repos with commits since their newest GitHub release, so nothing reviewed sits unshipped:
166
+
167
+ ```bash
168
+ ws-releases # at the workspace root: sweep every repo in repos.txt
169
+ ws-releases solveit # only solveit's transitive workspace dependencies
170
+ ws-releases --nodeps # inside a repo: just that repo
171
+ ws-releases --skip 'wip' # extra start-of-message regex for commits that need no release
172
+ ```
173
+
174
+ 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).
175
+
176
+ 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.
177
+
178
+ 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):
179
+
180
+ ```toml
181
+ [tool.fastws]
182
+ release_skip = ["docs only"]
183
+ release_exclude = ["solveit", "md_site"]
184
+ ```
185
+
186
+ From Python (the primary interface - the CLI is a thin wrapper over `fastws.releases`):
187
+
188
+ ```python
189
+ from fastws import check_releases, check_release
190
+ await check_releases() # ReleaseReport: the repr is the report
191
+ await check_releases('solveit') # dependency-closure mode
192
+ await check_release('mdhtml') # one repo: list of unreleased commit summaries (None = no releases)
193
+ ```
@@ -0,0 +1,170 @@
1
+ # fastws
2
+
3
+ Fast workspace tools for multi-repo management.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install fastws-cli
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ Create a `repos.txt` file listing your repos (one per line):
14
+
15
+ ```
16
+ AnswerDotAI/fastcore
17
+ AnswerDotAI/fastgit
18
+ AnswerDotAI/fastship
19
+ AnswerDotAI/fastws
20
+ ```
21
+
22
+ A line may add a checkout location after the repo, for repos that live outside
23
+ the workspace root (`~` ok). Every command then reads and writes them there, and
24
+ `ws-sync` editable-installs each Python project found inside (the repo root if it
25
+ has a `pyproject.toml`, else its immediate subdirectories), so one out-of-tree
26
+ repo can hold several small packages alongside non-Python content:
27
+
28
+ ```
29
+ AnswerDotAI/fastcore
30
+ jph00/private ~/private
31
+ ```
32
+
33
+ ## Commands
34
+
35
+ ### `ws-clone`
36
+
37
+ Clone all repos from your repos file:
38
+
39
+ ```bash
40
+ ws-clone
41
+ ws-clone --repos-file myrepos.txt
42
+ ws-clone --workers 8
43
+ ```
44
+
45
+ ### `ws-pull`
46
+
47
+ Pull updates for all repos (parallel):
48
+
49
+ ```bash
50
+ ws-pull
51
+ ```
52
+
53
+ ### `ws-status`
54
+
55
+ Show uncommitted changes and unpushed commits:
56
+
57
+ ```bash
58
+ ws-status
59
+ ws-status --branches
60
+ ```
61
+
62
+ ### `ws-branches`
63
+
64
+ Check if all repos are on the expected branch:
65
+
66
+ ```bash
67
+ ws-branches
68
+ ws-branches --expected develop
69
+ ```
70
+
71
+ ### `ws-build`
72
+
73
+ 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.
74
+
75
+ 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:
76
+
77
+ ```bash
78
+ ws-build
79
+ ws-build --force # rebuild everything
80
+ ws-build --out /tmp/dists
81
+ ```
82
+
83
+ ### `ws-sync`
84
+
85
+ Sync the workspace metadata, pull local repos, and install updates.
86
+ By default it uses the active venv parent as the workspace root, so you do not need to `cd` first.
87
+ It respects `tool.uv.workspace.members` and `exclude` when scanning local projects, and if any
88
+ member directory isn't a Python project yet (no `pyproject.toml`, e.g. a fresh empty clone), it
89
+ warns and skips the `uv sync` step instead of letting uv fail on the whole workspace.
90
+
91
+ The workspace `exclude` list is auto-managed: a top-level directory that isn't a valid Python
92
+ project and isn't a `repos.txt` checkout gets excluded automatically, and is un-excluded once it
93
+ gains a real `pyproject.toml`. Globs, entries for missing directories, and entries for `repos.txt`
94
+ checkouts are always kept, and `exclude = [...]` under `[tool.fastws]` in the workspace
95
+ `pyproject.toml` declares intent the scan can't infer (e.g. keeping a real project out of the
96
+ workspace). Hand-written `[tool.uv.sources]` entries (path, git, ...) are preserved when syncing
97
+ adds new members.
98
+
99
+ At most once per day (tracked by a stamp file inside the workspace's `.git`, so git never
100
+ sees it), the sync also floats dependencies: `uv sync -U` instead of plain `uv sync`, plus
101
+ a parallel `cargo update` in every member with a `Cargo.toml`, printing what moved. Pass
102
+ `--upgrade` to force that pass regardless of when it last ran.
103
+
104
+ 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.
105
+
106
+ ```bash
107
+ ws-sync
108
+ ws-sync --workspace ~/aai-ws
109
+ ws-sync --upgrade
110
+ ```
111
+
112
+ ### `ws-add`
113
+
114
+ Add a repo to `repos.txt`, then run `ws-sync`. Given `owner/repo`, it clones; given the
115
+ name of an existing local folder (e.g. one just scaffolded with `nbdev-new` or `ship-new`),
116
+ it resolves `owner/repo` from the folder's `origin` remote instead, telling you exactly
117
+ what's missing if the folder has no git repo, no GitHub origin, or no `pyproject.toml`.
118
+ Given a path outside the workspace root, the repo stays where it is and its location is
119
+ recorded in `repos.txt` (no root `pyproject.toml` needed: its packages are discovered on sync):
120
+
121
+ ```bash
122
+ ws-add AnswerDotAI/fastws
123
+ ws-add answerdotai/fastws
124
+ ws-add fastws # existing local folder, resolved via its origin remote
125
+ ws-add ~/private # a path outside the workspace: stays where it is, recorded in repos.txt with its location
126
+ ```
127
+
128
+ ### `ws-remove`
129
+
130
+ Remove a repo: delete its clone, and drop it from `repos.txt` and the workspace
131
+ `pyproject.toml`, then run `uv sync`. It refuses if the directory has uncommitted
132
+ changes, unpushed commits, no `origin` remote, or isn't a clean git checkout, and
133
+ always prompts for confirmation before deleting anything:
134
+
135
+ ```bash
136
+ ws-remove AnswerDotAI/fastws
137
+ ws-remove fastws # bare folder name also works if the directory exists
138
+ ```
139
+
140
+ ### `ws-releases`
141
+
142
+ Report repos with commits since their newest GitHub release, so nothing reviewed sits unshipped:
143
+
144
+ ```bash
145
+ ws-releases # at the workspace root: sweep every repo in repos.txt
146
+ ws-releases solveit # only solveit's transitive workspace dependencies
147
+ ws-releases --nodeps # inside a repo: just that repo
148
+ ws-releases --skip 'wip' # extra start-of-message regex for commits that need no release
149
+ ```
150
+
151
+ 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).
152
+
153
+ 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.
154
+
155
+ 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):
156
+
157
+ ```toml
158
+ [tool.fastws]
159
+ release_skip = ["docs only"]
160
+ release_exclude = ["solveit", "md_site"]
161
+ ```
162
+
163
+ From Python (the primary interface - the CLI is a thin wrapper over `fastws.releases`):
164
+
165
+ ```python
166
+ from fastws import check_releases, check_release
167
+ await check_releases() # ReleaseReport: the repr is the report
168
+ await check_releases('solveit') # dependency-closure mode
169
+ await check_release('mdhtml') # one repo: list of unreleased commit summaries (None = no releases)
170
+ ```
@@ -0,0 +1,8 @@
1
+ __version__ = "0.0.9"
2
+
3
+
4
+
5
+
6
+ from .core import *
7
+ from .releases import *
8
+