Skip to content

Commit a65ae03

Browse files
authored
feat: Add vcspull import command for remote repository discovery (#510)
Add a new `vcspull import` command that discovers repositories from hosted services and writes them into vcspull configuration files. Users can bulk-import repos from GitHub, GitLab, Codeberg, Gitea, Forgejo, and AWS CodeCommit in a single command, eliminating the need to manually author YAML entries for each repository. Supported workflows: - Import all repos for a user, organization, or search query - Filter by language, topics, star count, archived/fork status - Preview changes with --dry-run before writing config - Output as human-readable table, JSON, or NDJSON for scripting - Skip repos already present in the config file Clone URLs default to SSH. Use --https for HTTPS URLs instead. GitLab subgroup namespaces are preserved as workspace directories by default, mapping the remote tree structure to the local filesystem. Use --flatten-groups to collapse into a single root. No new runtime dependencies -- uses stdlib urllib for HTTP and subprocess for AWS CLI integration. Closes #416
2 parents e4d1e88 + 90bbd7d commit a65ae03

56 files changed

Lines changed: 9516 additions & 329 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,24 @@ $ vcspull search django
332332
$ vcspull search "name:flask"
333333
```
334334

335+
**Prefer longform flags** — use `--workspace` not `-w`, `--file` not `-f`.
336+
337+
**Split multi-flag commands** — when a command has 2+ flags/options, place each on its own `\`-continuation line, indented by 4 spaces.
338+
339+
Good:
340+
341+
```console
342+
$ vcspull import gh my-org \
343+
--mode org \
344+
--workspace ~/code/
345+
```
346+
347+
Bad:
348+
349+
```console
350+
$ vcspull import gh my-org --mode org -w ~/code/
351+
```
352+
335353
## Debugging Tips
336354

337355
When stuck in debugging loops:

CHANGES

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,125 @@ $ uvx --from 'vcspull' --prerelease allow vcspull
3333
_Notes on upcoming releases will be added here_
3434
<!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
3535

36+
### New features
37+
38+
#### New command: `vcspull import` (#510)
39+
40+
Import repositories from GitHub, GitLab, Codeberg/Gitea/Forgejo, and AWS
41+
CodeCommit directly into your vcspull configuration.
42+
43+
Import a user's repositories:
44+
45+
```console
46+
$ vcspull import github torvalds \
47+
--workspace ~/repos/linux \
48+
--mode user
49+
```
50+
51+
Import an organization's repositories:
52+
53+
```console
54+
$ vcspull import github django \
55+
--workspace ~/study/python \
56+
--mode org
57+
```
58+
59+
Search and import repositories:
60+
61+
```console
62+
$ vcspull import github "machine learning" \
63+
--workspace ~/ml-repos \
64+
--mode search \
65+
--min-stars 1000
66+
```
67+
68+
Use with self-hosted GitLab:
69+
70+
```console
71+
$ vcspull import gitlab myuser \
72+
--workspace ~/work \
73+
--url https://gitlab.company.com
74+
```
75+
76+
Import from AWS CodeCommit:
77+
78+
```console
79+
$ vcspull import codecommit \
80+
--workspace ~/work/aws \
81+
--region us-east-1
82+
```
83+
84+
Preview without writing (dry run):
85+
86+
```console
87+
$ vcspull import codeberg user \
88+
--workspace ~/oss \
89+
--dry-run
90+
```
91+
92+
**Key features:**
93+
94+
- Service aliases: `gh`, `gl`, `cb`, `cc`, `aws`
95+
- Filtering: `--language`, `--topics`, `--min-stars`, `--archived`, `--forks`
96+
- Output modes: human-readable (default), `--json`, `--ndjson`
97+
- Interactive confirmation before writing; use `--yes`/`-y` to skip
98+
- Repositories already in the config are detected and skipped
99+
- Non-zero exit code on errors (for CI/automation)
100+
- No new dependencies (uses stdlib `urllib` for HTTP)
101+
102+
#### `vcspull import`: SSH clone URLs by default (#510)
103+
104+
Clone URLs default to SSH. Use `--https` to get HTTPS URLs instead:
105+
106+
SSH (default):
107+
108+
```console
109+
$ vcspull import github torvalds \
110+
--workspace ~/repos/linux \
111+
--mode user
112+
```
113+
114+
Use `--https` for HTTPS clone URLs:
115+
116+
```console
117+
$ vcspull import github torvalds \
118+
--workspace ~/repos/linux \
119+
--mode user \
120+
--https
121+
```
122+
123+
#### `vcspull import`: GitLab subgroups map to workspace roots (#510)
124+
125+
For GitLab organization/group imports, subgroup namespaces are preserved
126+
under the workspace root by default:
127+
128+
```console
129+
$ vcspull import gitlab vcs-python-group-test \
130+
--workspace ~/projects/python \
131+
--mode org
132+
```
133+
134+
This writes repositories into workspace sections like:
135+
136+
- `~/projects/python/`
137+
- `~/projects/python/<subgroup>/`
138+
- `~/projects/python/<subgroup>/<subsubgroup>/`
139+
140+
Use `--flatten-groups` to collapse subgroup repositories into a single
141+
workspace root:
142+
143+
```console
144+
$ vcspull import gitlab vcs-python-group-test \
145+
--workspace ~/projects/python \
146+
--mode org \
147+
--flatten-groups
148+
```
149+
150+
### Bug fixes
151+
152+
- Config writes now use atomic temp-file-then-rename to prevent data loss
153+
during interrupted writes (#510)
154+
36155
### Tests
37156

38157
- Fix `pytest-asyncio` deprecation warning in isolated `pytester` runs by

README.md

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ You can test the unpublished version of vcspull before its released.
6868
## Configuration
6969

7070
Add your repos to `~/.vcspull.yaml`. You can edit the file by hand or let
71-
`vcspull add` or `vcspull discover` create entries for you.
71+
`vcspull add`, `vcspull discover`, or `vcspull import` create entries for you.
7272

7373
```yaml
7474
~/code/:
@@ -119,10 +119,32 @@ $ vcspull discover ~/code --recursive
119119
```
120120

121121
The scan shows each repository before import unless you opt into `--yes`. Add
122-
`-w ~/code/` to pin the resulting workspace root or `-f` to write somewhere other
122+
`--workspace ~/code/` to pin the resulting workspace root or `-f/--file` to write somewhere other
123123
than the default `~/.vcspull.yaml`. Duplicate workspace roots are merged by
124124
default; include `--no-merge` to keep them separate while you review the log.
125125

126+
### Import from remote services
127+
128+
Pull repository lists from GitHub, GitLab, Codeberg, Gitea, Forgejo, or AWS
129+
CodeCommit directly into your configuration:
130+
131+
```console
132+
$ vcspull import github myuser \
133+
--workspace ~/code/ \
134+
--mode user
135+
```
136+
137+
```console
138+
$ vcspull import gitlab my-group \
139+
--workspace ~/work/ \
140+
--mode org
141+
```
142+
143+
Use `--dry-run` to preview changes, `--https` for HTTPS clone URLs, and
144+
`--language`/`--topics`/`--min-stars` to filter results. See the
145+
[import documentation](https://vcspull.git-pull.com/cli/import/) for all
146+
supported services and options.
147+
126148
### Inspect configured repositories
127149

128150
List what vcspull already knows about without mutating anything:
@@ -164,7 +186,9 @@ After importing or editing by hand, run the formatter to tidy up keys, merge
164186
duplicate workspace sections, and keep entries sorted:
165187

166188
```console
167-
$ vcspull fmt -f ~/.vcspull.yaml --write
189+
$ vcspull fmt \
190+
--file ~/.vcspull.yaml \
191+
--write
168192
```
169193

170194
Use `vcspull fmt --all --write` to format every YAML file that vcspull can
@@ -205,7 +229,7 @@ or svn project with a git dependency:
205229
Clone / update repos via config file:
206230

207231
```console
208-
$ vcspull sync -f external_deps.yaml '*'
232+
$ vcspull sync --file external_deps.yaml '*'
209233
```
210234

211235
See the [Quickstart](https://vcspull.git-pull.com/quickstart.html) for

docs/api/cli/import.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# vcspull import - `vcspull.cli.import_cmd`
2+
3+
```{eval-rst}
4+
.. automodule:: vcspull.cli.import_cmd
5+
:members:
6+
:show-inheritance:
7+
:undoc-members:
8+
```
9+
10+
```{eval-rst}
11+
.. automodule:: vcspull.cli.import_cmd._common
12+
:members:
13+
:show-inheritance:
14+
:undoc-members:
15+
```

docs/api/cli/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
sync
1212
add
13+
import
1314
discover
1415
list
1516
search

docs/cli/add.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ merges duplicate workspace roots by default, and prompts before writing unless
88
you pass `--yes`.
99

1010
```{note}
11-
This command replaces the manual import functionality from `vcspull import`.
12-
For bulk scanning of existing repositories, see {ref}`cli-discover`.
11+
This command replaces the old `vcspull import <name> <url>` from v1.36--v1.39.
12+
For bulk scanning of local repositories, see {ref}`cli-discover`.
13+
For bulk import from remote services (GitHub, GitLab, etc.), see {ref}`cli-import`.
1314
```
1415

1516
## Command
@@ -97,7 +98,8 @@ vcspull searches for configuration files in this order:
9798
Specify a file explicitly with `-f/--file`:
9899

99100
```console
100-
$ vcspull add ~/study/python/pytest-docker -f ~/configs/python.yaml
101+
$ vcspull add ~/study/python/pytest-docker \
102+
--file ~/configs/python.yaml
101103
```
102104

103105
## Handling duplicates
@@ -114,22 +116,27 @@ a summary of the merge. Prefer to inspect duplicates yourself? Add
114116
2. Run `vcspull list` to verify the new entry (see {ref}`cli-list`).
115117
3. Run `vcspull sync` to clone or update the working tree (see {ref}`cli-sync`).
116118

117-
## Migration from vcspull import
119+
## Migration from the old vcspull import
118120

119-
If you previously used `vcspull import <name> <url>`, switch to the path-first
120-
workflow:
121+
The `vcspull import <name> <url>` command from v1.36--v1.39 has been replaced
122+
by `vcspull add`:
121123

122124
```diff
123125
- $ vcspull import flask https://github.com/pallets/flask.git -c ~/.vcspull.yaml
124-
+ $ vcspull add ~/code/flask --url https://github.com/pallets/flask.git -f ~/.vcspull.yaml
126+
+ $ vcspull add ~/code/flask --url https://github.com/pallets/flask.git --file ~/.vcspull.yaml
125127
```
126128

127129
Key differences:
128130

129-
- `vcspull add` now derives the name from the filesystem unless you pass
130-
`--name`.
131+
- `vcspull add` derives the name from the filesystem unless you pass `--name`.
131132
- The parent directory becomes the workspace automatically; use `--workspace`
132133
to override.
133134
- Use `--url` to record a remote when the checkout does not have one.
134135

136+
```{note}
137+
Starting with v1.55, `vcspull import` is a *different* command that bulk-imports
138+
repositories from remote services (GitHub, GitLab, etc.). See {ref}`cli-import`
139+
for details.
140+
```
141+
135142
[pip vcs url]: https://pip.pypa.io/en/stable/topics/vcs-support/

docs/cli/discover.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ $ vcspull discover ~ --recursive --workspace-root ~/code/ --yes
129129
Specify a custom config file with `-f/--file`:
130130

131131
```console
132-
$ vcspull discover ~/company --recursive -f ~/company/.vcspull.yaml
132+
$ vcspull discover ~/company \
133+
--recursive \
134+
--file ~/company/.vcspull.yaml
133135
```
134136

135137
If the config file doesn't exist, it will be created.
@@ -195,7 +197,7 @@ Scan to specific config:
195197
$ vcspull discover ~/company/repos \
196198
--recursive \
197199
--yes \
198-
-f ~/company/.vcspull.yaml
200+
--file ~/company/.vcspull.yaml
199201
```
200202

201203
## After discovering repositories
@@ -229,7 +231,7 @@ If you previously used `vcspull import --scan`:
229231

230232
```diff
231233
- $ vcspull import --scan ~/code --recursive -c ~/.vcspull.yaml --yes
232-
+ $ vcspull discover ~/code --recursive -f ~/.vcspull.yaml --yes
234+
+ $ vcspull discover ~/code --recursive --file ~/.vcspull.yaml --yes
233235
```
234236

235237
Changes:
@@ -273,7 +275,7 @@ $ vcspull discover ~/projects --recursive --yes
273275
```console
274276
$ vcspull discover ~/company \
275277
--recursive \
276-
-f ~/company/.vcspull.yaml \
278+
--file ~/company/.vcspull.yaml \
277279
--workspace-root ~/work/ \
278280
--yes
279281
```

docs/cli/fmt.md

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,12 @@ Run the formatter in dry-run mode first to preview the adjustments:
5959
$ vcspull fmt --file ~/.vcspull.yaml
6060
```
6161

62-
Then add `--write` (or `-w`) to persist them back to disk:
62+
Then add `--write` to persist them back to disk:
6363

6464
```console
65-
$ vcspull fmt --file ~/.vcspull.yaml --write
66-
```
67-
68-
Short form for preview:
69-
70-
```console
71-
$ vcspull fmt -f ~/.vcspull.yaml
72-
```
73-
74-
Short form to apply:
75-
76-
```console
77-
$ vcspull fmt -f ~/.vcspull.yaml -w
65+
$ vcspull fmt \
66+
--file ~/.vcspull.yaml \
67+
--write
7868
```
7969

8070
Use `--all` to iterate over the default search locations: the current working

docs/cli/import/codeberg.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
(cli-import-codeberg)=
2+
3+
# vcspull import codeberg
4+
5+
Import repositories from Codeberg.
6+
7+
## Command
8+
9+
```{eval-rst}
10+
.. argparse::
11+
:module: vcspull.cli
12+
:func: create_parser
13+
:prog: vcspull
14+
:path: import codeberg
15+
```
16+
17+
## Authentication
18+
19+
- **Env vars**: `CODEBERG_TOKEN` (primary), `GITEA_TOKEN` (fallback)
20+
- **Token type**: API token
21+
- **Scope**: no scopes needed for public repos; token required for private repos
22+
- **Create at**: <https://codeberg.org/user/settings/applications>
23+
24+
Set the token:
25+
26+
```console
27+
$ export CODEBERG_TOKEN=...
28+
```
29+
30+
Then import:
31+
32+
```console
33+
$ vcspull import codeberg myuser --workspace ~/code/
34+
```

0 commit comments

Comments
 (0)