pluck-cli 0.1.0__tar.gz → 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.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pluck-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Pluck any git repo from any forge — auto-detect, auto-install, done!
5
5
  Author: pluck contributors
6
- License-Expression: MIT
6
+ License: MIT
7
7
  Keywords: git,forge,installer,cli,github,gitlab,codeberg
8
8
  Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Environment :: Console
@@ -36,7 +36,9 @@ Dynamic: license-file
36
36
  <img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen" alt="Zero dependencies">
37
37
  <img src="https://img.shields.io/badge/Tests-111%20passing-brightgreen" alt="111 passing tests">
38
38
  <img src="https://img.shields.io/badge/Forges-11%20supported-blue" alt="11 forges supported">
39
+ <img src="https://img.shields.io/badge/PyPI-pluck--cli-blue?logo=pypi" alt="PyPI: pluck-cli">
39
40
  <img src="https://img.shields.io/badge/Code%20style-ruff-EF5552" alt="Code style: ruff">
41
+ <img src="https://gitlab.com/mabodu/pluck/badges/main/pipeline.svg" alt="GitLab CI">
40
42
  </p>
41
43
 
42
44
  ---
@@ -138,6 +140,10 @@ pluck install https://bitbucket.org/owner/repo
138
140
 
139
141
  ## 🌐 Supported Forges
140
142
 
143
+ <p align="center">
144
+ <img src="assets/images/pluck_front_baubles.png" alt="All the forges" width="500"/>
145
+ </p>
146
+
141
147
  | Forge | Host | Built-in | Notes |
142
148
  |-------|------|----------|-------|
143
149
  | **GitHub** | `github.com` | ✅ | Full support including gists |
@@ -169,7 +175,7 @@ Any git hosting platform that follows the standard `host/owner/repo` URL pattern
169
175
  | `stats` | Show statistics | `pluck stats` |
170
176
  | `doctor` | Check tool availability | `pluck doctor` |
171
177
  | `config [key] [val]` | View/set config | `pluck config install_dir ~/Apps` |
172
- | `search <query>` | Search GitHub repos (other forges coming) | `pluck search python installer` |
178
+ | `search <query> [--forge <name>]` | Search repos (github|gitlab|codeberg) | `pluck search python installer --forge gitlab` |
173
179
  | `export <file>` | Export registry | `pluck export ~/backup.json` |
174
180
  | `import <file>` | Import registry | `pluck import ~/backup.json` |
175
181
  | `completion <shell>` | Generate shell completion | `pluck completion bash` |
@@ -208,15 +214,12 @@ pip install -e .
208
214
  ./src/gh_install.py install https://gitlab.com/user/project
209
215
  ```
210
216
 
211
- ### Via pip (not yet on PyPI)
217
+ ### Via pip
212
218
 
213
219
  ```bash
214
- # Once published to PyPI:
215
220
  pip install pluck-cli
216
221
  ```
217
222
 
218
- > **Note**: `pluck-cli` is not yet published to PyPI. Install from source above, or use the Docker image.
219
-
220
223
  ### Via Docker
221
224
 
222
225
  ```bash
@@ -261,6 +264,10 @@ Available environment variables:
261
264
 
262
265
  ## 🖱️ Browser Integration
263
266
 
267
+ <p align="center">
268
+ <img src="assets/images/pluck_stalking2.png" alt="pluck stalking repos in your browser" width="500"/>
269
+ </p>
270
+
264
271
  Pluck includes a **right-click context menu** integration. Install a repo from any
265
272
  forge without leaving your browser.
266
273
 
@@ -11,7 +11,9 @@
11
11
  <img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen" alt="Zero dependencies">
12
12
  <img src="https://img.shields.io/badge/Tests-111%20passing-brightgreen" alt="111 passing tests">
13
13
  <img src="https://img.shields.io/badge/Forges-11%20supported-blue" alt="11 forges supported">
14
+ <img src="https://img.shields.io/badge/PyPI-pluck--cli-blue?logo=pypi" alt="PyPI: pluck-cli">
14
15
  <img src="https://img.shields.io/badge/Code%20style-ruff-EF5552" alt="Code style: ruff">
16
+ <img src="https://gitlab.com/mabodu/pluck/badges/main/pipeline.svg" alt="GitLab CI">
15
17
  </p>
16
18
 
17
19
  ---
@@ -113,6 +115,10 @@ pluck install https://bitbucket.org/owner/repo
113
115
 
114
116
  ## 🌐 Supported Forges
115
117
 
118
+ <p align="center">
119
+ <img src="assets/images/pluck_front_baubles.png" alt="All the forges" width="500"/>
120
+ </p>
121
+
116
122
  | Forge | Host | Built-in | Notes |
117
123
  |-------|------|----------|-------|
118
124
  | **GitHub** | `github.com` | ✅ | Full support including gists |
@@ -144,7 +150,7 @@ Any git hosting platform that follows the standard `host/owner/repo` URL pattern
144
150
  | `stats` | Show statistics | `pluck stats` |
145
151
  | `doctor` | Check tool availability | `pluck doctor` |
146
152
  | `config [key] [val]` | View/set config | `pluck config install_dir ~/Apps` |
147
- | `search <query>` | Search GitHub repos (other forges coming) | `pluck search python installer` |
153
+ | `search <query> [--forge <name>]` | Search repos (github|gitlab|codeberg) | `pluck search python installer --forge gitlab` |
148
154
  | `export <file>` | Export registry | `pluck export ~/backup.json` |
149
155
  | `import <file>` | Import registry | `pluck import ~/backup.json` |
150
156
  | `completion <shell>` | Generate shell completion | `pluck completion bash` |
@@ -183,15 +189,12 @@ pip install -e .
183
189
  ./src/gh_install.py install https://gitlab.com/user/project
184
190
  ```
185
191
 
186
- ### Via pip (not yet on PyPI)
192
+ ### Via pip
187
193
 
188
194
  ```bash
189
- # Once published to PyPI:
190
195
  pip install pluck-cli
191
196
  ```
192
197
 
193
- > **Note**: `pluck-cli` is not yet published to PyPI. Install from source above, or use the Docker image.
194
-
195
198
  ### Via Docker
196
199
 
197
200
  ```bash
@@ -236,6 +239,10 @@ Available environment variables:
236
239
 
237
240
  ## 🖱️ Browser Integration
238
241
 
242
+ <p align="center">
243
+ <img src="assets/images/pluck_stalking2.png" alt="pluck stalking repos in your browser" width="500"/>
244
+ </p>
245
+
239
246
  Pluck includes a **right-click context menu** integration. Install a repo from any
240
247
  forge without leaving your browser.
241
248
 
@@ -4,10 +4,10 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pluck-cli"
7
- version = "0.1.0"
7
+ version = "0.2.0"
8
8
  description = "Pluck any git repo from any forge — auto-detect, auto-install, done!"
9
9
  readme = "README.md"
10
- license = "MIT"
10
+ license = {text = "MIT"}
11
11
  requires-python = ">=3.6"
12
12
  authors = [
13
13
  {name = "pluck contributors"}
@@ -18,7 +18,7 @@ import urllib.request
18
18
  from datetime import datetime
19
19
  from pathlib import Path
20
20
 
21
- __version__ = "0.1.0"
21
+ __version__ = "0.2.0"
22
22
 
23
23
  # Configuration
24
24
  DEFAULT_INSTALL_DIR_MACOS = Path.home() / "Applications"
@@ -632,6 +632,7 @@ def download_and_install(
632
632
  shallow=False,
633
633
  ref=None,
634
634
  method_override=None,
635
+ verbose=False,
635
636
  timeout=None,
636
637
  retries=0,
637
638
  ):
@@ -689,7 +690,9 @@ def download_and_install(
689
690
  print(" Downloading...")
690
691
 
691
692
  try:
692
- subprocess.run(clone_cmd, check=True, timeout=timeout)
693
+ subprocess.run(clone_cmd, check=True, timeout=timeout,
694
+ stdout=None if verbose else subprocess.DEVNULL,
695
+ stderr=None if verbose else subprocess.DEVNULL)
693
696
  break
694
697
  except subprocess.TimeoutExpired:
695
698
  print_error(f"Clone timed out after {timeout}s")
@@ -1037,31 +1040,31 @@ def search_codeberg(query, limit=10):
1037
1040
  repo.get("html_url", ""),
1038
1041
  )
1039
1042
 
1040
- url = f"https://api.github.com/search/repositories?q={urllib.parse.quote(query)}&sort=stars&order=desc&per_page={limit}"
1043
+
1044
+ def search_bitbucket(query, limit=10):
1045
+ """Search repositories using the Bitbucket Cloud API."""
1046
+ print(f" Searching Bitbucket for '{query}'...")
1047
+ url = f"https://api.bitbucket.org/2.0/repositories?q=name~\"{urllib.parse.quote(query)}\"&sort=-updated_on"
1041
1048
  try:
1042
- req = urllib.request.Request(url, headers={"Accept": "application/vnd.github.v3+json"})
1049
+ req = urllib.request.Request(url, headers={"Accept": "application/json"})
1043
1050
  with urllib.request.urlopen(req, timeout=10) as resp:
1044
1051
  data = json.loads(resp.read().decode())
1045
1052
  except Exception as e:
1046
1053
  print_error(f"Search failed: {e}")
1047
1054
  return
1048
1055
 
1049
- items = data.get("items", [])
1056
+ items = data.get("values", [])
1050
1057
  if not items:
1051
1058
  print_warning("No results found")
1052
1059
  return
1053
1060
 
1054
- print_header(f"Search Results for '{query}' ({len(items)} found)")
1061
+ print_header(f"Bitbucket Results '{query}' ({len(items)} found)")
1055
1062
  for i, repo in enumerate(items, 1):
1056
- name = repo["full_name"]
1057
- stars = repo["stargazers_count"]
1063
+ full_name = repo.get("full_name", "unknown")
1058
1064
  desc = repo.get("description") or "No description"
1059
1065
  lang = repo.get("language") or "Unknown"
1060
- print(f" {i}. {Colors.GREEN}{name}{Colors.END}")
1061
- print(f" {desc}")
1062
- print(f" {Colors.CYAN}★{Colors.END} {stars:,} | Language: {lang}")
1063
- print(f" URL: {repo['html_url']}")
1064
- print()
1066
+ url = repo.get("links", {}).get("html", {}).get("href", "")
1067
+ _search_print_result(i, full_name, desc, 0, lang, url, star_char="\u2022")
1065
1068
 
1066
1069
 
1067
1070
  def export_registry(filepath):
@@ -1122,7 +1125,7 @@ def register_app(repo_name, repo_url, install_path, install_method, skip_hook=Fa
1122
1125
 
1123
1126
  def _run_post_install_hook(repo_name, install_path, method):
1124
1127
  """Run user-defined post-install hook if configured."""
1125
- hook_dir = CONFIG_FILE.parent / "pluck" / "hooks"
1128
+ hook_dir = CONFIG_FILE.parent / "hooks"
1126
1129
  hook_file = hook_dir / "post-install.sh"
1127
1130
 
1128
1131
  if hook_file.exists():
@@ -1296,6 +1299,7 @@ def _parse_args(args):
1296
1299
  method = None
1297
1300
  json_output = False
1298
1301
  no_color = False
1302
+ verbose = False
1299
1303
  timeout = None
1300
1304
  retries = 0
1301
1305
  urls = []
@@ -1326,6 +1330,9 @@ def _parse_args(args):
1326
1330
  elif args[i] == "--json":
1327
1331
  json_output = True
1328
1332
  i += 1
1333
+ elif args[i] == "--verbose":
1334
+ verbose = True
1335
+ i += 1
1329
1336
  elif args[i] == "--no-color":
1330
1337
  no_color = True
1331
1338
  i += 1
@@ -1350,7 +1357,7 @@ def _parse_args(args):
1350
1357
  if no_color:
1351
1358
  _enable_colors(False)
1352
1359
 
1353
- return install_dir, dry_run, force, shallow, ref, method, json_output, timeout, retries, urls
1360
+ return install_dir, dry_run, force, shallow, ref, method, json_output, verbose, no_color, timeout, retries, urls
1354
1361
 
1355
1362
 
1356
1363
  def verify_apps(json_output=False):
@@ -1525,7 +1532,7 @@ def main():
1525
1532
  command = sys.argv[1]
1526
1533
 
1527
1534
  if command == "install":
1528
- install_dir, dry_run, force, shallow, ref, method, json_output, timeout, retries, urls = (
1535
+ install_dir, dry_run, force, shallow, ref, method, json_output, verbose, no_color, timeout, retries, urls = (
1529
1536
  _parse_args(sys.argv[2:])
1530
1537
  )
1531
1538
 
@@ -1549,12 +1556,13 @@ def main():
1549
1556
  shallow=shallow,
1550
1557
  ref=ref,
1551
1558
  method_override=method,
1559
+ verbose=verbose,
1552
1560
  timeout=timeout,
1553
1561
  retries=retries,
1554
1562
  )
1555
1563
 
1556
1564
  elif command == "update":
1557
- install_dir, dry_run, force, shallow, ref, method, json_output, timeout, retries, rest = (
1565
+ install_dir, dry_run, force, shallow, ref, method, json_output, verbose, no_color, timeout, retries, rest = (
1558
1566
  _parse_args(sys.argv[2:])
1559
1567
  )
1560
1568
  if not rest:
@@ -1588,7 +1596,7 @@ def main():
1588
1596
  list_installed(json_output=json_output)
1589
1597
 
1590
1598
  elif command in ("uninstall", "remove"):
1591
- install_dir, dry_run, force, shallow, ref, method, json_output, timeout, retries, rest = (
1599
+ install_dir, dry_run, force, shallow, ref, method, json_output, verbose, no_color, timeout, retries, rest = (
1592
1600
  _parse_args(sys.argv[2:])
1593
1601
  )
1594
1602
  if not rest:
@@ -1603,7 +1611,7 @@ def main():
1603
1611
  verify_apps(json_output=json_output)
1604
1612
 
1605
1613
  elif command == "clean":
1606
- install_dir, dry_run, force, shallow, ref, method, json_output, timeout, retries, rest = (
1614
+ install_dir, dry_run, force, shallow, ref, method, json_output, verbose, no_color, timeout, retries, rest = (
1607
1615
  _parse_args(sys.argv[2:])
1608
1616
  )
1609
1617
  clean_registry(dry_run=dry_run, force=force, json_output=json_output)
@@ -1642,6 +1650,7 @@ def main():
1642
1650
  "github": search_github,
1643
1651
  "gitlab": search_gitlab,
1644
1652
  "codeberg": search_codeberg,
1653
+ "bitbucket": search_bitbucket,
1645
1654
  }
1646
1655
  searcher = forge_searchers.get(forge)
1647
1656
  if searcher:
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pluck-cli
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Pluck any git repo from any forge — auto-detect, auto-install, done!
5
5
  Author: pluck contributors
6
- License-Expression: MIT
6
+ License: MIT
7
7
  Keywords: git,forge,installer,cli,github,gitlab,codeberg
8
8
  Classifier: Development Status :: 3 - Alpha
9
9
  Classifier: Environment :: Console
@@ -36,7 +36,9 @@ Dynamic: license-file
36
36
  <img src="https://img.shields.io/badge/Dependencies-Zero-brightgreen" alt="Zero dependencies">
37
37
  <img src="https://img.shields.io/badge/Tests-111%20passing-brightgreen" alt="111 passing tests">
38
38
  <img src="https://img.shields.io/badge/Forges-11%20supported-blue" alt="11 forges supported">
39
+ <img src="https://img.shields.io/badge/PyPI-pluck--cli-blue?logo=pypi" alt="PyPI: pluck-cli">
39
40
  <img src="https://img.shields.io/badge/Code%20style-ruff-EF5552" alt="Code style: ruff">
41
+ <img src="https://gitlab.com/mabodu/pluck/badges/main/pipeline.svg" alt="GitLab CI">
40
42
  </p>
41
43
 
42
44
  ---
@@ -138,6 +140,10 @@ pluck install https://bitbucket.org/owner/repo
138
140
 
139
141
  ## 🌐 Supported Forges
140
142
 
143
+ <p align="center">
144
+ <img src="assets/images/pluck_front_baubles.png" alt="All the forges" width="500"/>
145
+ </p>
146
+
141
147
  | Forge | Host | Built-in | Notes |
142
148
  |-------|------|----------|-------|
143
149
  | **GitHub** | `github.com` | ✅ | Full support including gists |
@@ -169,7 +175,7 @@ Any git hosting platform that follows the standard `host/owner/repo` URL pattern
169
175
  | `stats` | Show statistics | `pluck stats` |
170
176
  | `doctor` | Check tool availability | `pluck doctor` |
171
177
  | `config [key] [val]` | View/set config | `pluck config install_dir ~/Apps` |
172
- | `search <query>` | Search GitHub repos (other forges coming) | `pluck search python installer` |
178
+ | `search <query> [--forge <name>]` | Search repos (github|gitlab|codeberg) | `pluck search python installer --forge gitlab` |
173
179
  | `export <file>` | Export registry | `pluck export ~/backup.json` |
174
180
  | `import <file>` | Import registry | `pluck import ~/backup.json` |
175
181
  | `completion <shell>` | Generate shell completion | `pluck completion bash` |
@@ -208,15 +214,12 @@ pip install -e .
208
214
  ./src/gh_install.py install https://gitlab.com/user/project
209
215
  ```
210
216
 
211
- ### Via pip (not yet on PyPI)
217
+ ### Via pip
212
218
 
213
219
  ```bash
214
- # Once published to PyPI:
215
220
  pip install pluck-cli
216
221
  ```
217
222
 
218
- > **Note**: `pluck-cli` is not yet published to PyPI. Install from source above, or use the Docker image.
219
-
220
223
  ### Via Docker
221
224
 
222
225
  ```bash
@@ -261,6 +264,10 @@ Available environment variables:
261
264
 
262
265
  ## 🖱️ Browser Integration
263
266
 
267
+ <p align="center">
268
+ <img src="assets/images/pluck_stalking2.png" alt="pluck stalking repos in your browser" width="500"/>
269
+ </p>
270
+
264
271
  Pluck includes a **right-click context menu** integration. Install a repo from any
265
272
  forge without leaving your browser.
266
273
 
@@ -433,7 +433,7 @@ class TestParseArgs:
433
433
  def test_urls_only(self):
434
434
  (
435
435
  install_dir, dry_run, force, shallow, ref,
436
- method, json_output, timeout, retries, urls,
436
+ method, json_output, verbose, no_color, timeout, retries, urls,
437
437
  ) = _parse_args(["https://github.com/a/b"])
438
438
  assert install_dir is None
439
439
  assert dry_run is False
@@ -446,7 +446,7 @@ class TestParseArgs:
446
446
  def test_dir_flag(self):
447
447
  (
448
448
  install_dir, dry_run, force, shallow, ref,
449
- method, json_output, timeout, retries, urls,
449
+ method, json_output, verbose, no_color, timeout, retries, urls,
450
450
  ) = _parse_args(["--dir", "/tmp/test", "https://github.com/a/b"])
451
451
  assert install_dir == Path("/tmp/test")
452
452
  assert urls == ["https://github.com/a/b"]
@@ -454,49 +454,49 @@ class TestParseArgs:
454
454
  def test_dry_run_flag(self):
455
455
  (
456
456
  install_dir, dry_run, force, shallow, ref,
457
- method, json_output, timeout, retries, urls,
457
+ method, json_output, verbose, no_color, timeout, retries, urls,
458
458
  ) = _parse_args(["--dry-run", "https://github.com/a/b"])
459
459
  assert dry_run is True
460
460
 
461
461
  def test_force_flag(self):
462
462
  (
463
463
  install_dir, dry_run, force, shallow, ref,
464
- method, json_output, timeout, retries, urls,
464
+ method, json_output, verbose, no_color, timeout, retries, urls,
465
465
  ) = _parse_args(["--force", "https://github.com/a/b"])
466
466
  assert force is True
467
467
 
468
468
  def test_yes_flag(self):
469
469
  (
470
470
  install_dir, dry_run, force, shallow, ref,
471
- method, json_output, timeout, retries, urls,
471
+ method, json_output, verbose, no_color, timeout, retries, urls,
472
472
  ) = _parse_args(["--yes", "https://github.com/a/b"])
473
473
  assert force is True
474
474
 
475
475
  def test_shallow_flag(self):
476
476
  (
477
477
  install_dir, dry_run, force, shallow, ref,
478
- method, json_output, timeout, retries, urls,
478
+ method, json_output, verbose, no_color, timeout, retries, urls,
479
479
  ) = _parse_args(["--shallow", "https://github.com/a/b"])
480
480
  assert shallow is True
481
481
 
482
482
  def test_ref_flag(self):
483
483
  (
484
484
  install_dir, dry_run, force, shallow, ref,
485
- method, json_output, timeout, retries, urls,
485
+ method, json_output, verbose, no_color, timeout, retries, urls,
486
486
  ) = _parse_args(["--ref", "v2.0", "https://github.com/a/b"])
487
487
  assert ref == "v2.0"
488
488
 
489
489
  def test_method_flag(self):
490
490
  (
491
491
  install_dir, dry_run, force, shallow, ref,
492
- method, json_output, timeout, retries, urls,
492
+ method, json_output, verbose, no_color, timeout, retries, urls,
493
493
  ) = _parse_args(["--method", "python", "https://github.com/a/b"])
494
494
  assert method == "python"
495
495
 
496
496
  def test_combined_flags(self):
497
497
  (
498
498
  install_dir, dry_run, force, shallow, ref,
499
- method, json_output, timeout, retries, urls,
499
+ method, json_output, verbose, no_color, timeout, retries, urls,
500
500
  ) = _parse_args([
501
501
  "--dir", "/custom", "--dry-run", "--shallow",
502
502
  "--ref", "main", "--method", "python",
@@ -511,7 +511,7 @@ class TestParseArgs:
511
511
  def test_flags_between_urls(self):
512
512
  (
513
513
  install_dir, dry_run, force, shallow, ref,
514
- method, json_output, timeout, retries, urls,
514
+ method, json_output, verbose, no_color, timeout, retries, urls,
515
515
  ) = _parse_args([
516
516
  "https://github.com/a/b", "--dir", "/opt", "https://github.com/c/d",
517
517
  ])
File without changes
File without changes