codeberg-cli 0.1.0__py3-none-any.whl

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.
Files changed (49) hide show
  1. codeberg_cli/__main__.py +8 -0
  2. codeberg_cli/client.py +70 -0
  3. codeberg_cli/config.py +29 -0
  4. codeberg_cli/git.py +49 -0
  5. codeberg_cli/helpers.py +20 -0
  6. codeberg_cli/routes/__init__.py +6 -0
  7. codeberg_cli/routes/api.py +23 -0
  8. codeberg_cli/routes/auth/__init__.py +1 -0
  9. codeberg_cli/routes/auth/login.py +23 -0
  10. codeberg_cli/routes/auth/logout.py +17 -0
  11. codeberg_cli/routes/auth/status.py +21 -0
  12. codeberg_cli/routes/auth/whoami.py +14 -0
  13. codeberg_cli/routes/issue/__init__.py +1 -0
  14. codeberg_cli/routes/issue/close.py +28 -0
  15. codeberg_cli/routes/issue/create.py +55 -0
  16. codeberg_cli/routes/issue/list.py +49 -0
  17. codeberg_cli/routes/issue/reopen.py +28 -0
  18. codeberg_cli/routes/issue/view.py +46 -0
  19. codeberg_cli/routes/label/__init__.py +1 -0
  20. codeberg_cli/routes/label/create.py +35 -0
  21. codeberg_cli/routes/label/delete.py +26 -0
  22. codeberg_cli/routes/label/list.py +41 -0
  23. codeberg_cli/routes/milestone/__init__.py +1 -0
  24. codeberg_cli/routes/milestone/create.py +35 -0
  25. codeberg_cli/routes/milestone/list.py +46 -0
  26. codeberg_cli/routes/pr/__init__.py +1 -0
  27. codeberg_cli/routes/pr/checkout.py +42 -0
  28. codeberg_cli/routes/pr/close.py +28 -0
  29. codeberg_cli/routes/pr/create.py +68 -0
  30. codeberg_cli/routes/pr/list.py +43 -0
  31. codeberg_cli/routes/pr/merge.py +32 -0
  32. codeberg_cli/routes/pr/view.py +42 -0
  33. codeberg_cli/routes/release/__init__.py +1 -0
  34. codeberg_cli/routes/release/create.py +40 -0
  35. codeberg_cli/routes/release/list.py +42 -0
  36. codeberg_cli/routes/release/upload.py +51 -0
  37. codeberg_cli/routes/release/view.py +50 -0
  38. codeberg_cli/routes/repo/__init__.py +1 -0
  39. codeberg_cli/routes/repo/clone.py +21 -0
  40. codeberg_cli/routes/repo/create.py +77 -0
  41. codeberg_cli/routes/repo/delete.py +27 -0
  42. codeberg_cli/routes/repo/fork.py +28 -0
  43. codeberg_cli/routes/repo/list.py +36 -0
  44. codeberg_cli/routes/repo/view.py +41 -0
  45. codeberg_cli-0.1.0.dist-info/METADATA +182 -0
  46. codeberg_cli-0.1.0.dist-info/RECORD +49 -0
  47. codeberg_cli-0.1.0.dist-info/WHEEL +4 -0
  48. codeberg_cli-0.1.0.dist-info/entry_points.txt +2 -0
  49. codeberg_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,36 @@
1
+ from typing import Annotated
2
+
3
+ import rich
4
+ from rich.table import Table
5
+
6
+ from codeberg_cli.helpers import require_client
7
+ from xclif import Option, command
8
+
9
+
10
+ @command("list", "ls")
11
+ def _(
12
+ owner: Annotated[str, Option(description="Owner (user or org)")] = "",
13
+ limit: Annotated[int, Option(description="Maximum number of repos")] = 30,
14
+ ) -> None:
15
+ """List repositories for a user or organization."""
16
+ client = require_client()
17
+
18
+ if owner:
19
+ repos = client.get(f"/users/{owner}/repos", params={"limit": limit, "page": 1}, action=f"Fetching {owner}'s repositories")
20
+ else:
21
+ repos = client.get("/user/repos", params={"limit": limit, "page": 1}, action="Fetching your repositories")
22
+
23
+ if not repos:
24
+ rich.print("[dim]No repositories found.[/dim]")
25
+ return
26
+
27
+ table = Table("Name", "Visibility", "Description")
28
+ for repo in repos:
29
+ table.add_row(
30
+ repo["full_name"],
31
+ "[green]public[/green]" if not repo.get("private") else "[yellow]private[/yellow]",
32
+ repo.get("description") or "",
33
+ )
34
+ rich.print(table)
35
+
36
+
@@ -0,0 +1,41 @@
1
+ import webbrowser
2
+ from typing import Annotated
3
+
4
+ import rich
5
+ from rich.markup import escape
6
+
7
+ from codeberg_cli.git import infer_repo
8
+ from codeberg_cli.helpers import require_client
9
+ from xclif import Arg, Option, command
10
+
11
+
12
+ @command("view")
13
+ def _(
14
+ repo: Annotated[str, Option(description="Repository in owner/repo format")] = "",
15
+ web: Annotated[bool, Option(description="Open in browser", name="web")] = False,
16
+ ) -> None:
17
+ """View a repository."""
18
+ client = require_client()
19
+
20
+ if not repo:
21
+ inferred = infer_repo()
22
+ if not inferred:
23
+ rich.print("[bold red]Error:[/bold red] No repo specified and not in a git directory")
24
+ return 1
25
+ repo = inferred
26
+
27
+ if web:
28
+ webbrowser.open(f"https://codeberg.org/{repo}")
29
+ return
30
+
31
+ data = client.get(f"/repos/{repo}")
32
+
33
+ rich.print(f"[bold]{data['full_name']}[/bold]")
34
+ if data.get("description"):
35
+ rich.print(escape(data["description"]))
36
+ rich.print(f" [dim]Stars:[/dim] {data.get('stars_count', 0)} [dim]Forks:[/dim] {data.get('forks_count', 0)}")
37
+ rich.print(f" [dim]Language:[/dim] {data.get('language', 'N/A')}")
38
+ rich.print(f" [dim]Visibility:[/dim] {'Private' if data.get('private') else 'Public'}")
39
+ rich.print(f" [dim]URL:[/dim] https://codeberg.org/{repo}")
40
+
41
+
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: codeberg-cli
3
+ Version: 0.1.0
4
+ Summary: A Codeberg CLI
5
+ Project-URL: Homepage, https://codeberg.org/ThatXliner/codeberg-cli
6
+ Project-URL: Repository, https://codeberg.org/ThatXliner/codeberg-cli
7
+ Project-URL: Issues, https://codeberg.org/ThatXliner/codeberg-cli/issues
8
+ Author-email: Bryan Hu <thatxliner@gmail.com>
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: cli,codeberg,forgejo
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Version Control :: Git
19
+ Requires-Python: >=3.12
20
+ Requires-Dist: httpx
21
+ Requires-Dist: platformdirs
22
+ Requires-Dist: tomlkit
23
+ Requires-Dist: xclif>=0.4.3
24
+ Description-Content-Type: text/markdown
25
+
26
+ # cb — A Codeberg CLI
27
+
28
+ [![CI](https://github.com/ThatXliner/cb/actions/workflows/ci.yml/badge.svg)](https://github.com/ThatXliner/cb/actions/workflows/ci.yml) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/codeberg-cli)](https://pypi.org/project/codeberg-cli)
29
+ [![PyPI](https://img.shields.io/pypi/v/codeberg-cli)](https://pypi.org/project/codeberg-cli)
30
+ [![PyPI - License](https://img.shields.io/pypi/l/codeberg-cli)](#license)
31
+
32
+ `cb` is a native CLI for [Codeberg](https://codeberg.org) (a [Forgejo](https://forgejo.org) instance) — think `gh` for Codeberg. Built with [Xclif](https://xclif.readthedocs.io).
33
+
34
+ ```text
35
+ # One-time setup
36
+ cb auth login
37
+
38
+ # Work with repos, issues, PRs, releases
39
+ cb repo list
40
+ cb issue create --title "Fix the thing"
41
+ cb pr create --base main --head fix
42
+ cb release create v0.2.0
43
+ ```
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install codeberg-cli # or: uv tool install codeberg-cli
49
+ ```
50
+
51
+ Or from source:
52
+
53
+ ```bash
54
+ git clone https://codeberg.org/ThatXliner/codeberg-cli
55
+ cd cb
56
+ uv tool install .
57
+ ```
58
+
59
+ ## Quickstart
60
+
61
+ ```bash
62
+ # Authenticate (tokens at https://codeberg.org/user/settings/applications)
63
+ cb auth login
64
+
65
+ # Who am I?
66
+ cb auth whoami
67
+
68
+ # List your repos
69
+ cb repo list
70
+
71
+ # Clone one
72
+ cb repo clone ThatXliner/cb
73
+
74
+ # Open an issue
75
+ cb issue create --title "suggestion" --body "what about..."
76
+ ```
77
+
78
+ ## Commands
79
+
80
+ ### `cb auth` — authentication
81
+
82
+ | Command | Description |
83
+ |---------|-------------|
84
+ | `login` | Store a Codeberg access token |
85
+ | `logout` | Remove stored credentials |
86
+ | `status` | Show login state |
87
+ | `whoami` | Print current username |
88
+
89
+ ### `cb repo` — repositories
90
+
91
+ Infer `owner/repo` from `git remote origin`. Override with `--repo`.
92
+
93
+ | Command | Description |
94
+ |---------|-------------|
95
+ | `create` | Create a repo (`--org`, `--private`, `--description`) |
96
+ | `list` | List repos for a user or org |
97
+ | `clone` | Clone via `git clone` |
98
+ | `view` | Show repo details (`--web` to open browser) |
99
+ | `fork` | Fork a repo |
100
+ | `delete` | Delete a repo (requires confirmation) |
101
+
102
+ ### `cb label` — labels
103
+
104
+ | Command | Description |
105
+ |---------|-------------|
106
+ | `list` (alias: `ls`) | List repo labels |
107
+ | `create` | Create a label (`--color`, `--description`) |
108
+ | `delete` | Delete a label by ID |
109
+
110
+ ### `cb milestone` — milestones
111
+
112
+ | Command | Description |
113
+ |---------|-------------|
114
+ | `list` (alias: `ls`) | List milestones (`--state`) |
115
+ | `create` | Create a milestone (`--description`, `--due-on`) |
116
+
117
+ ### `cb issue` — issues
118
+
119
+ | Command | Description |
120
+ |---------|-------------|
121
+ | `create` | Create an issue (`--labels`, omit `--body` to open stdin) |
122
+ | `list` | List issues (`--state`, `--label`, `--limit`) |
123
+ | `view` | View an issue with comments (`--web`) |
124
+ | `close` | Close an issue |
125
+ | `reopen` | Reopen a closed issue |
126
+
127
+ ### `cb pr` — pull requests
128
+
129
+ | Command | Description |
130
+ |---------|-------------|
131
+ | `create` | Open a PR (omit `--body` to open `$EDITOR`) |
132
+ | `list` | List PRs (alias: `ls`) |
133
+ | `view` | View a PR (`--web`) |
134
+ | `merge` | Merge with `--style merge|rebase|squash` |
135
+ | `checkout` | Fetch and checkout a PR locally (alias: `co`) |
136
+ | `close` | Close without merging |
137
+
138
+ ### `cb release` — releases
139
+
140
+ | Command | Description |
141
+ |---------|-------------|
142
+ | `create` | Tag a release (`--prerelease`, `--draft`) |
143
+ | `list` | List releases |
144
+ | `view` | View a release (`--web`) |
145
+ | `upload` | Attach a file to a release |
146
+
147
+ ### `cb api` — raw API access
148
+
149
+ ```bash
150
+ cb api GET /version
151
+ cb api POST /repos/owner/repo/issues --data '{"title": "hi"}'
152
+ ```
153
+
154
+ ## Config
155
+
156
+ Token stored in `$XDG_CONFIG_HOME/cb/config.toml` (managed by `cb auth login` / `cb auth logout`).
157
+
158
+ ## Comparison
159
+
160
+ There are two other CLI tools you can use with Codeberg:
161
+
162
+ | | **cb** | **fj** (forgejo-cli) | **berg** (codeberg-cli) |
163
+ |---|---|---|---|
164
+ | Language | Python | Rust | Rust |
165
+ | Codeberg-native | Yes (targets `codeberg.org/api/v1`) | Generic (any Forgejo instance) | Yes |
166
+ | Issues | create, list, view, close, reopen | open, edit, comment, close | yes |
167
+ | Pull requests | create, list, view, merge, checkout, close | create, merge | yes |
168
+ | Releases | create, list, view, upload | publish | — |
169
+ | Repos | create, list, clone, view, fork, delete | create, edit, star, watch | yes |
170
+ | Labels | create, list, delete | — | yes |
171
+ | Milestones | create, list | — | yes |
172
+ | Raw API | `cb api GET /path` | — | — |
173
+ | AGit PRs (no-fork) | — | yes | — |
174
+ | Org/team mgmt | — | yes | — |
175
+ | Install | `pip install codeberg-cli` | prebuilt binaries | `cargo install codeberg-cli` |
176
+ | Size | ~300 lines | Rust binary | Rust binary |
177
+
178
+ **Choose `fj`** if you self-host Forgejo or need org/team management. **Choose `berg`** if you want labels and milestones from a Rust binary. **Choose `cb`** if you want a minimal, readable Python CLI with release management and raw API access — `cb` is also the only one that uploads release assets.
179
+
180
+ ## License
181
+
182
+ MIT
@@ -0,0 +1,49 @@
1
+ codeberg_cli/__main__.py,sha256=ysV0kHP-Q-sUwHOInLcB-QEryJfNNv-amdHP0EubG-8,113
2
+ codeberg_cli/client.py,sha256=wqf5mOR9Uyku7v_GZRjFQUZsYAfqiWXcJv_DMmubQWU,2242
3
+ codeberg_cli/config.py,sha256=bjVHHM1sG3xTIU6EC7sakwR0nnP-ZsCtO4Jn2od6X4A,822
4
+ codeberg_cli/git.py,sha256=oWOU8iV4FUbtZLA7RvDqRur6whYDoJKoIcUPtBxJZto,1544
5
+ codeberg_cli/helpers.py,sha256=0JGASGM_td_q2l9YOF1KybisZFofKeT5jC4q-G_lS48,627
6
+ codeberg_cli/routes/__init__.py,sha256=LPAm_RsDHeREJ-57n8CwFAdkXkTcykx9zjrKhQZFuvI,86
7
+ codeberg_cli/routes/api.py,sha256=o8she7_o32DNuYqr1v9D0hEHjujW1xEZEhHuxIzKO_8,720
8
+ codeberg_cli/routes/auth/__init__.py,sha256=TxwcK78EFqRSsHs2iSVRQ8Og4upvq91P3fcKcOFpbFQ,41
9
+ codeberg_cli/routes/auth/login.py,sha256=jcarfwPedC2xWqbs-ncNNZXI_5Kv4mdHt7IQa3azAqg,656
10
+ codeberg_cli/routes/auth/logout.py,sha256=LfsYchVxrr77R5c2WNGbKNCLbkyQtbobkhcpw1DH8HA,351
11
+ codeberg_cli/routes/auth/status.py,sha256=fkMgFiwEOBNNNDkJ4S0ahx9epfRQZzXVdYu6KEI4oQI,640
12
+ codeberg_cli/routes/auth/whoami.py,sha256=RjXbklkr0ajFX3BT9B8k1Qr6zSooaZWmQ-9oqjFcshs,272
13
+ codeberg_cli/routes/issue/__init__.py,sha256=RRWKFz7LRRGD3shDlBXRdLSO6IebeLLlzdjY3qRCrqQ,42
14
+ codeberg_cli/routes/issue/close.py,sha256=RAAJiFPVV4FrNStlviMzUw9ZZjSj-h80sCfG0mZysl0,774
15
+ codeberg_cli/routes/issue/create.py,sha256=UW-ilstUjQ2Lu16UZJOjBH8eCMNO3DDF0dPSR-0PE3g,1726
16
+ codeberg_cli/routes/issue/list.py,sha256=bolDAmeI7saZ6AGnWsSOQmRHqgRWm21EP8rKINfXbUI,1502
17
+ codeberg_cli/routes/issue/reopen.py,sha256=QsxWU_1Qed6hdmFs9AiNKk191WkJLeeKInAINeSiiTA,782
18
+ codeberg_cli/routes/issue/view.py,sha256=7pU0vJmgGiaRFDQ_MDESOtPVu9SEoWVH8piN9bzClYo,1515
19
+ codeberg_cli/routes/label/__init__.py,sha256=7yTJhxfd1ErLUoYJVKKhtbmSmC6kwIbUwNcIsu7dAas,42
20
+ codeberg_cli/routes/label/create.py,sha256=Oqlf-xouSJg0nbDc-31e5b_d46d-XzCOXNIc9JVnKcc,1124
21
+ codeberg_cli/routes/label/delete.py,sha256=AGffQbsEwsGwFXtgIQltABWcLREAV1p4kpu4Xzamle4,759
22
+ codeberg_cli/routes/label/list.py,sha256=sVFfQsWkQVi4WqGIP03apabWzPFIgUvHktsRuqugw34,1248
23
+ codeberg_cli/routes/milestone/__init__.py,sha256=0LihqWaCxPtc9yYPmXsorKagjKt065qsnPNfnO2MGAY,46
24
+ codeberg_cli/routes/milestone/create.py,sha256=VDA9q3y8uEDRJ3oIOPn2EWsUwvrNpeOviAXQFSLv7E4,1086
25
+ codeberg_cli/routes/milestone/list.py,sha256=LRoUmHMgamOqMuo085cEoIU9tdPhXduqN5DWd-JFUg8,1514
26
+ codeberg_cli/routes/pr/__init__.py,sha256=8WsMUvMJmcd5BJaSfMXVIaTNkEnrgFyCEGynl2VKKZI,39
27
+ codeberg_cli/routes/pr/checkout.py,sha256=B5wYePvASjoFp3G1rMbpBjicbe0DPibFxIlMlO7W204,1221
28
+ codeberg_cli/routes/pr/close.py,sha256=Xge_cP9K0prup9CUvvSjuMj_NgF6ZG00SF0Ciets-t0,795
29
+ codeberg_cli/routes/pr/create.py,sha256=VT5Xu_BQQKsgzZjKnNeVHq7jsQKbf6tMUO_1Rgufa6U,2093
30
+ codeberg_cli/routes/pr/list.py,sha256=bA4C9BN4E9xlmnDTHKkFh-zClLQ2agPU8fWz2lMT6rA,1286
31
+ codeberg_cli/routes/pr/merge.py,sha256=AIP-mm_Fvq0HG7v_vm7eYwtuU61JKLzjJqbFuZqnqgg,915
32
+ codeberg_cli/routes/pr/view.py,sha256=uaIGzGtMY21K9SAF2qRN58lLpMLQ2Dw1D19nbtke6gw,1373
33
+ codeberg_cli/routes/release/__init__.py,sha256=4NdEUIvolvOMJ8XFP-GkPleiTCo0xvf6JIcXbUf25kE,44
34
+ codeberg_cli/routes/release/create.py,sha256=nFTJCWEAc2S_lAoi-moeQqi4b6l_Q10FDdZYcMa24DQ,1387
35
+ codeberg_cli/routes/release/list.py,sha256=81mSZCYM0udIEt4j9-JV4o_A3AKUjbKvBg5D5veFqtg,1289
36
+ codeberg_cli/routes/release/upload.py,sha256=QG_jmUTamOsGajRVhtZ8N7H0_oMY_DWiIMobBkSeu3g,1674
37
+ codeberg_cli/routes/release/view.py,sha256=o9I1I9nkJjTcqMfxQdtXbTITZWxVYGiwmL6JrVPyg84,1683
38
+ codeberg_cli/routes/repo/__init__.py,sha256=UB56y2Kc_0altG6JrTvSYb5cFunqeQsDMdu5d4q_ROc,41
39
+ codeberg_cli/routes/repo/clone.py,sha256=mFk3i4Qf5MZObh_bcQniO36FuNNBJNWZQhB9QR_4lSI,511
40
+ codeberg_cli/routes/repo/create.py,sha256=XUw3TQjhwB6oL0FnshnBVEKvjSQiWTs8fUOZHh-EkwY,2803
41
+ codeberg_cli/routes/repo/delete.py,sha256=ubUotlWGxLZ3MigHIrWd5Y3xV4LVlWdZZxyfVVKEz24,755
42
+ codeberg_cli/routes/repo/fork.py,sha256=ApVdzAFXetNQJ8R9WUk-gejB2BDJ11cTFb3pn0V5MBg,810
43
+ codeberg_cli/routes/repo/list.py,sha256=8Lledw3c_JVmmn3K4CFRxo6kINWErTa6Y2CdIqJ6mH0,1104
44
+ codeberg_cli/routes/repo/view.py,sha256=ypJPFPsQKiQ1xw8LFZ04PFWNcP7N_ce9J5XWD2wlMXo,1311
45
+ codeberg_cli-0.1.0.dist-info/METADATA,sha256=zMqXfnXqXzE2UwiUgnO6ukwF_PLNNL7xyXT3NO3y5Is,5812
46
+ codeberg_cli-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
47
+ codeberg_cli-0.1.0.dist-info/entry_points.txt,sha256=7Kg1K5av7D5TzPCqX_qyFCj09JTneIEvW0f2Gp4q1v4,49
48
+ codeberg_cli-0.1.0.dist-info/licenses/LICENSE,sha256=o71itnX05JiF5qOrKHEmWIvuf03sgYcwsc3r6AAW_h0,1065
49
+ codeberg_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cb = codeberg_cli.__main__:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bryan Hu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.