codeberg-cli 0.2.0__tar.gz → 0.4.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.
- codeberg_cli-0.4.0/PKG-INFO +232 -0
- codeberg_cli-0.4.0/README.md +207 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/pyproject.toml +2 -5
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/client.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/helpers.py +120 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/__init__.py +5 -1
- codeberg_cli-0.4.0/src/codeberg_cli/routes/actions/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/actions/dispatch.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/actions/run.py +40 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/actions/runs.py +52 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/actions/workflows.py +46 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/delete.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/labels/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/labels/add.py +27 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/labels/list.py +43 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/labels/remove.py +27 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/list.py +15 -10
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/pin.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/search.py +59 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/subscribe.py +27 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/time/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/time/add.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/time/list.py +42 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/unpin.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/issue/unsubscribe.py +27 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/view.py +5 -1
- codeberg_cli-0.4.0/src/codeberg_cli/routes/label/edit.py +41 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/label/list.py +12 -10
- codeberg_cli-0.4.0/src/codeberg_cli/routes/milestone/delete.py +38 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/milestone/edit.py +44 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/milestone/list.py +15 -15
- codeberg_cli-0.4.0/src/codeberg_cli/routes/milestone/view.py +42 -0
- codeberg_cli-0.2.0/src/codeberg_cli/routes/notification/list.py → codeberg_cli-0.4.0/src/codeberg_cli/routes/notifications.py +15 -11
- codeberg_cli-0.4.0/src/codeberg_cli/routes/org/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/org/list.py +28 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/org/members.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/org/teams.py +38 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/org/view.py +33 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/check_merge.py +33 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/commits.py +44 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/diff.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/files.py +42 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/list.py +16 -10
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/reopen.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/pr/update.py +27 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/view.py +5 -1
- codeberg_cli-0.4.0/src/codeberg_cli/routes/release/delete.py +38 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/release/edit.py +56 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/release/list.py +14 -11
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/release/view.py +12 -3
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/archive.py +36 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/branch/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/branch/create.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/branch/delete.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/branch/list.py +36 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/branch/view.py +36 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/collaborator/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/collaborator/add.py +31 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/collaborator/delete.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/collaborator/list.py +37 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/commits.py +51 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/contents.py +55 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/edit.py +64 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/forks/sync.py +32 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/forks.py +42 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/languages.py +45 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/list.py +16 -10
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/migrate.py +33 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/search.py +58 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/stargazers.py +40 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/tag/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/tag/create.py +34 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/tag/delete.py +26 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/tag/list.py +36 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/topics/__init__.py +6 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/topics/list.py +36 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/topics/set.py +27 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/transfer.py +35 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/unwatch.py +25 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/view.py +6 -2
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/watch.py +25 -0
- codeberg_cli-0.4.0/src/codeberg_cli/routes/repo/watchers.py +40 -0
- codeberg_cli-0.2.0/src/codeberg_cli/routes/user/view.py → codeberg_cli-0.4.0/src/codeberg_cli/routes/user.py +6 -2
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/uv.lock +10 -30
- codeberg_cli-0.2.0/PKG-INFO +0 -118
- codeberg_cli-0.2.0/README.md +0 -93
- codeberg_cli-0.2.0/src/codeberg_cli/helpers.py +0 -45
- codeberg_cli-0.2.0/src/codeberg_cli/routes/notification/__init__.py +0 -6
- codeberg_cli-0.2.0/src/codeberg_cli/routes/user/__init__.py +0 -6
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/.github/workflows/ci.yml +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/.github/workflows/release.yml +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/.gitignore +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/.python-version +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/Justfile +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/LICENSE +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/__main__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/config.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/git.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/api.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/auth/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/auth/login.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/auth/logout.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/auth/status.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/auth/whoami.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/close.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/comment.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/edit.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/issue/reopen.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/label/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/label/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/label/delete.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/milestone/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/milestone/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/checkout.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/close.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/comment.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/edit.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/pr/merge.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/release/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/release/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/release/upload.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/__init__.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/clone.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/create.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/delete.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/fork.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/star.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/src/codeberg_cli/routes/repo/unstar.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_api.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_auth.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_client.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_config.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_git.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_issue.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_pr.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_release.py +0 -0
- {codeberg_cli-0.2.0 → codeberg_cli-0.4.0}/tests/test_repo.py +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: codeberg-cli
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: A Forgejo CLI — works with Codeberg and any Forgejo instance
|
|
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.5.1
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# cb — A Codeberg CLI
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/codeberg-cli)
|
|
29
|
+
[](https://pypi.org/project/codeberg-cli)
|
|
30
|
+
[](#license)
|
|
31
|
+
|
|
32
|
+
`cb` is a CLI for [Codeberg](https://codeberg.org) (a [Forgejo](https://forgejo.org) instance) — think `gh` for Codeberg. It also works with any Forgejo instance. 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.git
|
|
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
|
+
# See everything you can do
|
|
78
|
+
cb --help
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Config
|
|
82
|
+
|
|
83
|
+
Token stored in `$XDG_CONFIG_HOME/codeberg-cli/config.toml` (managed by `cb auth login` / `cb auth logout`).
|
|
84
|
+
|
|
85
|
+
View or change config:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cb config path # Show config file location
|
|
89
|
+
cb config get # Print all config values
|
|
90
|
+
cb config set base_url "https://codeberg.org/api/v1" # Codeberg (default)
|
|
91
|
+
cb config set base_url "https://git.example.com/api/v1" # Self-hosted Forgejo
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Comparison
|
|
95
|
+
|
|
96
|
+
Here's how `cb` stacks up against other Forgejo CLI tools. *Last updated: May 2026.*
|
|
97
|
+
|
|
98
|
+
| | **cb** (this) | **fj** (forgejo-cli) | **berg** (codeberg-cli) | **tea** (gitea/tea) | **gcli** |
|
|
99
|
+
|---|---|---|---|---|---|
|
|
100
|
+
| Language | Python | Rust | Rust | Go | C |
|
|
101
|
+
| Version | v0.2.0 | v0.5.0 | v0.5.1 | v0.13.0 | v2.11.0 |
|
|
102
|
+
| Install | `pip install codeberg-cli` | prebuilt binaries/Cargo | `cargo install codeberg-cli` | `brew install tea` | `brew install gcli` |
|
|
103
|
+
| Multi-instance | `cb config set base_url` | `-H <instance>` | `BERG_BASE_URL` | `tea login add` | `-t forgejo` |
|
|
104
|
+
|
|
105
|
+
### Issues
|
|
106
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
107
|
+
|---|---|---|---|---|---|
|
|
108
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
109
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
110
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
111
|
+
| Close | ✅ | ✅ | ✅ | ✅ | — |
|
|
112
|
+
| Reopen | ✅ | — | ✅ | ✅ | — |
|
|
113
|
+
| Comment | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
114
|
+
| Edit | ✅ | — | ✅ | ✅ | — |
|
|
115
|
+
| Delete | ✅ | — | — | — | — |
|
|
116
|
+
| Pin/Unpin | ✅ | — | — | ✅ | — |
|
|
117
|
+
| Search | ✅ | ✅ | — | ✅ | — |
|
|
118
|
+
| Attachments | — | — | — | ✅ | — |
|
|
119
|
+
| Labels (manage on issue) | ✅ | — | — | ✅ | — |
|
|
120
|
+
| Reactions | — | — | — | — | — |
|
|
121
|
+
| Subscriptions | ✅ | — | — | ✅ | — |
|
|
122
|
+
| Tracked times | ✅ | — | — | — | ✅ |
|
|
123
|
+
| Dependencies | — | — | — | — | — |
|
|
124
|
+
| Deadline | — | — | — | ✅ | — |
|
|
125
|
+
| Templates | — | ✅ | — | — | — |
|
|
126
|
+
|
|
127
|
+
### Pull Requests
|
|
128
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
129
|
+
|---|---|---|---|---|---|
|
|
130
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
131
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
132
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
133
|
+
| Merge | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
134
|
+
| Close | ✅ | — | ✅ | ✅ | — |
|
|
135
|
+
| Reopen | ✅ | — | ✅ | ✅ | — |
|
|
136
|
+
| Comment | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
137
|
+
| Edit | ✅ | — | ✅ | ✅ | — |
|
|
138
|
+
| Checkout | ✅ | — | ✅ | ✅ | ✅ |
|
|
139
|
+
| Commits | ✅ | — | — | ✅ | — |
|
|
140
|
+
| Files/changed | ✅ | — | — | ✅ | — |
|
|
141
|
+
| Reviews | — | ✅ | — | ✅ | ✅ |
|
|
142
|
+
| Diff/Patch | ✅ | — | — | ✅ | — |
|
|
143
|
+
| Update branch | ✅ | — | — | ✅ | — |
|
|
144
|
+
| AGit (no-fork) | — | ✅ | — | — | — |
|
|
145
|
+
| CI status | — | ✅ | — | — | — |
|
|
146
|
+
| Templates | — | ✅ | — | — | — |
|
|
147
|
+
| Auto-merge | — | — | — | ✅ | — |
|
|
148
|
+
|
|
149
|
+
### Releases
|
|
150
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
151
|
+
|---|---|---|---|---|---|
|
|
152
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
153
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
154
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
155
|
+
| Upload assets | ✅ | — | ✅ | — | ✅ |
|
|
156
|
+
| Delete | ✅ | — | — | ✅ | ✅ |
|
|
157
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
158
|
+
| Latest | ✅ | — | — | ✅ | — |
|
|
159
|
+
| By tag | ✅ | — | — | ✅ | — |
|
|
160
|
+
|
|
161
|
+
### Repositories
|
|
162
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
163
|
+
|---|---|---|---|---|---|
|
|
164
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
165
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
166
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
167
|
+
| Clone | ✅ | — | ✅ | ✅ | — |
|
|
168
|
+
| Fork | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
169
|
+
| Delete | ✅ | — | ✅ | ✅ | — |
|
|
170
|
+
| Star | ✅ | ✅ | ✅ | ✅ | — |
|
|
171
|
+
| Unstar | ✅ | — | ✅ | ✅ | — |
|
|
172
|
+
| Watch/Unwatch | ✅ | ✅ | — | ✅ | — |
|
|
173
|
+
| Edit | ✅ | ✅ | — | ✅ | — |
|
|
174
|
+
| Migrate/Mirror | ✅ | ✅ | — | ✅ | — |
|
|
175
|
+
| Branches | ✅ | — | — | ✅ | — |
|
|
176
|
+
| Topics | ✅ | — | — | ✅ | — |
|
|
177
|
+
| Languages | ✅ | — | — | — | — |
|
|
178
|
+
| Hooks | — | — | — | ✅ | — |
|
|
179
|
+
| Archive | ✅ | — | — | ✅ | — |
|
|
180
|
+
| Commits | ✅ | — | — | — | — |
|
|
181
|
+
| Contents | ✅ | — | — | ✅ | — |
|
|
182
|
+
| Collaborators | ✅ | — | — | ✅ | — |
|
|
183
|
+
| Transfer | ✅ | — | — | ✅ | — |
|
|
184
|
+
| Wikis | — | — | — | ✅ | — |
|
|
185
|
+
| Push mirrors | — | — | — | ✅ | — |
|
|
186
|
+
| Search | ✅ | — | — | ✅ | — |
|
|
187
|
+
|
|
188
|
+
### Labels
|
|
189
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
190
|
+
|---|---|---|---|---|---|
|
|
191
|
+
| Create | ✅ | — | ✅ | ✅ | ✅ |
|
|
192
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
193
|
+
| Delete | ✅ | — | ✅ | ✅ | — |
|
|
194
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
195
|
+
|
|
196
|
+
### Milestones
|
|
197
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
198
|
+
|---|---|---|---|---|---|
|
|
199
|
+
| Create | ✅ | — | ✅ | ✅ | — |
|
|
200
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
201
|
+
| View | ✅ | — | — | ✅ | — |
|
|
202
|
+
| Delete | ✅ | — | — | ✅ | — |
|
|
203
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
204
|
+
|
|
205
|
+
### Notifications
|
|
206
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
207
|
+
|---|---|---|---|---|---|
|
|
208
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
209
|
+
| Mark read | — | — | — | ✅ | — |
|
|
210
|
+
| Thread details | — | — | — | ✅ | — |
|
|
211
|
+
| Per-repo | — | — | — | ✅ | — |
|
|
212
|
+
|
|
213
|
+
### Extra
|
|
214
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
215
|
+
|---|---|---|---|---|---|
|
|
216
|
+
| Raw API | ✅ | — | ✅ | — | ✅ |
|
|
217
|
+
| User profiles | ✅ | ✅ | — | — | — |
|
|
218
|
+
| SSH keys | — | ✅ | — | ✅ | ✅ |
|
|
219
|
+
| GPG keys | — | ✅ | — | — | — |
|
|
220
|
+
| Org/team mgmt | ✅ | ✅ | — | ✅ | — |
|
|
221
|
+
| Forgejo Actions | ✅ | ✅ | — | — | — |
|
|
222
|
+
| Shell completions | ✅ (auto via xclif) | ✅ | ✅ | ✅ | — |
|
|
223
|
+
| JSON output | ✅ (via `--json`) | — | ✅ | ✅ | — |
|
|
224
|
+
| Non-interactive | auto (no prompts) | — | ✅ | ✅ | — |
|
|
225
|
+
| Config management | ✅ | — | ✅ | — | — |
|
|
226
|
+
| Web browser flag | on view commands | — | — | ✅ | — |
|
|
227
|
+
|
|
228
|
+
**`cb` is the most feature-complete Forgejo CLI** — by a wide margin. It dominates on repo management (branches, topics, languages, tags, commits, contents, collaborators, search, archive, transfer, watch/unwatch, sync-fork, migrate), issues (delete, pin, labels, search, subscribe, tracked times), PRs (diff, commits, files, reopen, update), releases (delete, edit, latest, by-tag), and extras (Actions, org/team management, `--json` on every command, raw API, release uploads). Built in Python with [Xclif](https://xclif.readthedocs.io) for a clean, hackable codebase. If something's missing, open an issue!
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
MIT
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# cb — A Codeberg CLI
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/codeberg-cli)
|
|
4
|
+
[](https://pypi.org/project/codeberg-cli)
|
|
5
|
+
[](#license)
|
|
6
|
+
|
|
7
|
+
`cb` is a CLI for [Codeberg](https://codeberg.org) (a [Forgejo](https://forgejo.org) instance) — think `gh` for Codeberg. It also works with any Forgejo instance. Built with [Xclif](https://xclif.readthedocs.io).
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
# One-time setup
|
|
11
|
+
cb auth login
|
|
12
|
+
|
|
13
|
+
# Work with repos, issues, PRs, releases
|
|
14
|
+
cb repo list
|
|
15
|
+
cb issue create --title "Fix the thing"
|
|
16
|
+
cb pr create --base main --head fix
|
|
17
|
+
cb release create v0.2.0
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pip install codeberg-cli # or: uv tool install codeberg-cli
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Or from source:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
git clone https://codeberg.org/ThatXliner/codeberg-cli.git
|
|
30
|
+
cd cb
|
|
31
|
+
uv tool install .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quickstart
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Authenticate (tokens at https://codeberg.org/user/settings/applications)
|
|
38
|
+
cb auth login
|
|
39
|
+
|
|
40
|
+
# Who am I?
|
|
41
|
+
cb auth whoami
|
|
42
|
+
|
|
43
|
+
# List your repos
|
|
44
|
+
cb repo list
|
|
45
|
+
|
|
46
|
+
# Clone one
|
|
47
|
+
cb repo clone ThatXliner/cb
|
|
48
|
+
|
|
49
|
+
# Open an issue
|
|
50
|
+
cb issue create --title "suggestion" --body "what about..."
|
|
51
|
+
|
|
52
|
+
# See everything you can do
|
|
53
|
+
cb --help
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Config
|
|
57
|
+
|
|
58
|
+
Token stored in `$XDG_CONFIG_HOME/codeberg-cli/config.toml` (managed by `cb auth login` / `cb auth logout`).
|
|
59
|
+
|
|
60
|
+
View or change config:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
cb config path # Show config file location
|
|
64
|
+
cb config get # Print all config values
|
|
65
|
+
cb config set base_url "https://codeberg.org/api/v1" # Codeberg (default)
|
|
66
|
+
cb config set base_url "https://git.example.com/api/v1" # Self-hosted Forgejo
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Comparison
|
|
70
|
+
|
|
71
|
+
Here's how `cb` stacks up against other Forgejo CLI tools. *Last updated: May 2026.*
|
|
72
|
+
|
|
73
|
+
| | **cb** (this) | **fj** (forgejo-cli) | **berg** (codeberg-cli) | **tea** (gitea/tea) | **gcli** |
|
|
74
|
+
|---|---|---|---|---|---|
|
|
75
|
+
| Language | Python | Rust | Rust | Go | C |
|
|
76
|
+
| Version | v0.2.0 | v0.5.0 | v0.5.1 | v0.13.0 | v2.11.0 |
|
|
77
|
+
| Install | `pip install codeberg-cli` | prebuilt binaries/Cargo | `cargo install codeberg-cli` | `brew install tea` | `brew install gcli` |
|
|
78
|
+
| Multi-instance | `cb config set base_url` | `-H <instance>` | `BERG_BASE_URL` | `tea login add` | `-t forgejo` |
|
|
79
|
+
|
|
80
|
+
### Issues
|
|
81
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
82
|
+
|---|---|---|---|---|---|
|
|
83
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
84
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
85
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
86
|
+
| Close | ✅ | ✅ | ✅ | ✅ | — |
|
|
87
|
+
| Reopen | ✅ | — | ✅ | ✅ | — |
|
|
88
|
+
| Comment | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
89
|
+
| Edit | ✅ | — | ✅ | ✅ | — |
|
|
90
|
+
| Delete | ✅ | — | — | — | — |
|
|
91
|
+
| Pin/Unpin | ✅ | — | — | ✅ | — |
|
|
92
|
+
| Search | ✅ | ✅ | — | ✅ | — |
|
|
93
|
+
| Attachments | — | — | — | ✅ | — |
|
|
94
|
+
| Labels (manage on issue) | ✅ | — | — | ✅ | — |
|
|
95
|
+
| Reactions | — | — | — | — | — |
|
|
96
|
+
| Subscriptions | ✅ | — | — | ✅ | — |
|
|
97
|
+
| Tracked times | ✅ | — | — | — | ✅ |
|
|
98
|
+
| Dependencies | — | — | — | — | — |
|
|
99
|
+
| Deadline | — | — | — | ✅ | — |
|
|
100
|
+
| Templates | — | ✅ | — | — | — |
|
|
101
|
+
|
|
102
|
+
### Pull Requests
|
|
103
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
104
|
+
|---|---|---|---|---|---|
|
|
105
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
106
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
107
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
108
|
+
| Merge | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
109
|
+
| Close | ✅ | — | ✅ | ✅ | — |
|
|
110
|
+
| Reopen | ✅ | — | ✅ | ✅ | — |
|
|
111
|
+
| Comment | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
112
|
+
| Edit | ✅ | — | ✅ | ✅ | — |
|
|
113
|
+
| Checkout | ✅ | — | ✅ | ✅ | ✅ |
|
|
114
|
+
| Commits | ✅ | — | — | ✅ | — |
|
|
115
|
+
| Files/changed | ✅ | — | — | ✅ | — |
|
|
116
|
+
| Reviews | — | ✅ | — | ✅ | ✅ |
|
|
117
|
+
| Diff/Patch | ✅ | — | — | ✅ | — |
|
|
118
|
+
| Update branch | ✅ | — | — | ✅ | — |
|
|
119
|
+
| AGit (no-fork) | — | ✅ | — | — | — |
|
|
120
|
+
| CI status | — | ✅ | — | — | — |
|
|
121
|
+
| Templates | — | ✅ | — | — | — |
|
|
122
|
+
| Auto-merge | — | — | — | ✅ | — |
|
|
123
|
+
|
|
124
|
+
### Releases
|
|
125
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
126
|
+
|---|---|---|---|---|---|
|
|
127
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
128
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
129
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
130
|
+
| Upload assets | ✅ | — | ✅ | — | ✅ |
|
|
131
|
+
| Delete | ✅ | — | — | ✅ | ✅ |
|
|
132
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
133
|
+
| Latest | ✅ | — | — | ✅ | — |
|
|
134
|
+
| By tag | ✅ | — | — | ✅ | — |
|
|
135
|
+
|
|
136
|
+
### Repositories
|
|
137
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
138
|
+
|---|---|---|---|---|---|
|
|
139
|
+
| Create | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
140
|
+
| List | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
141
|
+
| View | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
142
|
+
| Clone | ✅ | — | ✅ | ✅ | — |
|
|
143
|
+
| Fork | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
144
|
+
| Delete | ✅ | — | ✅ | ✅ | — |
|
|
145
|
+
| Star | ✅ | ✅ | ✅ | ✅ | — |
|
|
146
|
+
| Unstar | ✅ | — | ✅ | ✅ | — |
|
|
147
|
+
| Watch/Unwatch | ✅ | ✅ | — | ✅ | — |
|
|
148
|
+
| Edit | ✅ | ✅ | — | ✅ | — |
|
|
149
|
+
| Migrate/Mirror | ✅ | ✅ | — | ✅ | — |
|
|
150
|
+
| Branches | ✅ | — | — | ✅ | — |
|
|
151
|
+
| Topics | ✅ | — | — | ✅ | — |
|
|
152
|
+
| Languages | ✅ | — | — | — | — |
|
|
153
|
+
| Hooks | — | — | — | ✅ | — |
|
|
154
|
+
| Archive | ✅ | — | — | ✅ | — |
|
|
155
|
+
| Commits | ✅ | — | — | — | — |
|
|
156
|
+
| Contents | ✅ | — | — | ✅ | — |
|
|
157
|
+
| Collaborators | ✅ | — | — | ✅ | — |
|
|
158
|
+
| Transfer | ✅ | — | — | ✅ | — |
|
|
159
|
+
| Wikis | — | — | — | ✅ | — |
|
|
160
|
+
| Push mirrors | — | — | — | ✅ | — |
|
|
161
|
+
| Search | ✅ | — | — | ✅ | — |
|
|
162
|
+
|
|
163
|
+
### Labels
|
|
164
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
165
|
+
|---|---|---|---|---|---|
|
|
166
|
+
| Create | ✅ | — | ✅ | ✅ | ✅ |
|
|
167
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
168
|
+
| Delete | ✅ | — | ✅ | ✅ | — |
|
|
169
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
170
|
+
|
|
171
|
+
### Milestones
|
|
172
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
173
|
+
|---|---|---|---|---|---|
|
|
174
|
+
| Create | ✅ | — | ✅ | ✅ | — |
|
|
175
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
176
|
+
| View | ✅ | — | — | ✅ | — |
|
|
177
|
+
| Delete | ✅ | — | — | ✅ | — |
|
|
178
|
+
| Edit | ✅ | — | — | ✅ | — |
|
|
179
|
+
|
|
180
|
+
### Notifications
|
|
181
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
182
|
+
|---|---|---|---|---|---|
|
|
183
|
+
| List | ✅ | — | ✅ | ✅ | ✅ |
|
|
184
|
+
| Mark read | — | — | — | ✅ | — |
|
|
185
|
+
| Thread details | — | — | — | ✅ | — |
|
|
186
|
+
| Per-repo | — | — | — | ✅ | — |
|
|
187
|
+
|
|
188
|
+
### Extra
|
|
189
|
+
| | **cb** | **fj** | **berg** | **tea** | **gcli** |
|
|
190
|
+
|---|---|---|---|---|---|
|
|
191
|
+
| Raw API | ✅ | — | ✅ | — | ✅ |
|
|
192
|
+
| User profiles | ✅ | ✅ | — | — | — |
|
|
193
|
+
| SSH keys | — | ✅ | — | ✅ | ✅ |
|
|
194
|
+
| GPG keys | — | ✅ | — | — | — |
|
|
195
|
+
| Org/team mgmt | ✅ | ✅ | — | ✅ | — |
|
|
196
|
+
| Forgejo Actions | ✅ | ✅ | — | — | — |
|
|
197
|
+
| Shell completions | ✅ (auto via xclif) | ✅ | ✅ | ✅ | — |
|
|
198
|
+
| JSON output | ✅ (via `--json`) | — | ✅ | ✅ | — |
|
|
199
|
+
| Non-interactive | auto (no prompts) | — | ✅ | ✅ | — |
|
|
200
|
+
| Config management | ✅ | — | ✅ | — | — |
|
|
201
|
+
| Web browser flag | on view commands | — | — | ✅ | — |
|
|
202
|
+
|
|
203
|
+
**`cb` is the most feature-complete Forgejo CLI** — by a wide margin. It dominates on repo management (branches, topics, languages, tags, commits, contents, collaborators, search, archive, transfer, watch/unwatch, sync-fork, migrate), issues (delete, pin, labels, search, subscribe, tracked times), PRs (diff, commits, files, reopen, update), releases (delete, edit, latest, by-tag), and extras (Actions, org/team management, `--json` on every command, raw API, release uploads). Built in Python with [Xclif](https://xclif.readthedocs.io) for a clean, hackable codebase. If something's missing, open an issue!
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
MIT
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "codeberg-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.0"
|
|
4
4
|
description = "A Forgejo CLI — works with Codeberg and any Forgejo instance"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "MIT"
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Topic :: Software Development :: Version Control :: Git",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"xclif>=0.
|
|
22
|
+
"xclif>=0.5.1",
|
|
23
23
|
"httpx",
|
|
24
24
|
"tomlkit",
|
|
25
25
|
"platformdirs",
|
|
@@ -37,9 +37,6 @@ cb = "codeberg_cli.__main__:cli"
|
|
|
37
37
|
requires = ["hatchling"]
|
|
38
38
|
build-backend = "hatchling.build"
|
|
39
39
|
|
|
40
|
-
[tool.uv.sources]
|
|
41
|
-
xclif = { path = "../xclif" }
|
|
42
|
-
|
|
43
40
|
[tool.hatch.build.targets.wheel]
|
|
44
41
|
packages = ["src/codeberg_cli"]
|
|
45
42
|
|
|
@@ -7,6 +7,7 @@ DEFAULT_BASE_URL = "https://codeberg.org"
|
|
|
7
7
|
_VERBS = {
|
|
8
8
|
"GET": "Fetching",
|
|
9
9
|
"POST": "Creating",
|
|
10
|
+
"PUT": "Updating",
|
|
10
11
|
"PATCH": "Updating",
|
|
11
12
|
"DELETE": "Deleting",
|
|
12
13
|
}
|
|
@@ -55,6 +56,11 @@ class Client:
|
|
|
55
56
|
) -> dict | list:
|
|
56
57
|
return self._request("PATCH", path, json=data, action=action)
|
|
57
58
|
|
|
59
|
+
def put(
|
|
60
|
+
self, path: str, data: dict | None = None, action: str | None = None
|
|
61
|
+
) -> dict | list:
|
|
62
|
+
return self._request("PUT", path, json=data, action=action)
|
|
63
|
+
|
|
58
64
|
def delete(self, path: str, action: str | None = None) -> None:
|
|
59
65
|
self._request("DELETE", path, action=action)
|
|
60
66
|
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json as _json
|
|
4
|
+
import sys
|
|
5
|
+
from collections.abc import Iterable, Mapping
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import rich
|
|
9
|
+
|
|
10
|
+
from codeberg_cli.client import Client, ClientError, DEFAULT_BASE_URL
|
|
11
|
+
from codeberg_cli.config import load_config
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def is_json_mode() -> bool:
|
|
15
|
+
"""Check whether ``--json`` was passed on the CLI."""
|
|
16
|
+
return "--json" in sys.argv[1:]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def output(
|
|
20
|
+
data: Any,
|
|
21
|
+
*,
|
|
22
|
+
json: bool | None = None,
|
|
23
|
+
default: str | None = None,
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Print *data* as JSON or fall back to *default* text.
|
|
26
|
+
|
|
27
|
+
When ``json=True`` (or global ``--json`` was passed), data is printed as
|
|
28
|
+
prettified JSON. When ``json=False`` the *default* string (if given) is
|
|
29
|
+
printed. When *default* is ``None`` and JSON mode is off, this is a no-op
|
|
30
|
+
— the caller is expected to handle rich printing themselves.
|
|
31
|
+
"""
|
|
32
|
+
if json is None:
|
|
33
|
+
json = is_json_mode()
|
|
34
|
+
if json:
|
|
35
|
+
_json.dump(
|
|
36
|
+
data,
|
|
37
|
+
sys.stdout,
|
|
38
|
+
indent=2,
|
|
39
|
+
default=str,
|
|
40
|
+
)
|
|
41
|
+
sys.stdout.write("\n")
|
|
42
|
+
elif default is not None:
|
|
43
|
+
rich.print(default)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def print_table(
|
|
47
|
+
columns: list[str],
|
|
48
|
+
rows: Iterable[Iterable[str]],
|
|
49
|
+
*,
|
|
50
|
+
json: bool | None = None,
|
|
51
|
+
json_data: Any = None,
|
|
52
|
+
title: str | None = None,
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Print a table, or JSON when ``--json`` is active.
|
|
55
|
+
|
|
56
|
+
When JSON mode is on, *json_data* is printed as JSON (or *rows* converted
|
|
57
|
+
to a list if no explicit json_data is given). When not in JSON mode, a
|
|
58
|
+
rich ``Table`` is printed.
|
|
59
|
+
"""
|
|
60
|
+
if json is None:
|
|
61
|
+
json = is_json_mode()
|
|
62
|
+
if json:
|
|
63
|
+
_json.dump(
|
|
64
|
+
json_data if json_data is not None else list(rows),
|
|
65
|
+
sys.stdout,
|
|
66
|
+
indent=2,
|
|
67
|
+
default=str,
|
|
68
|
+
)
|
|
69
|
+
sys.stdout.write("\n")
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
from rich.table import Table
|
|
73
|
+
|
|
74
|
+
table = Table(*columns, title=title)
|
|
75
|
+
for row in rows:
|
|
76
|
+
table.add_row(*row)
|
|
77
|
+
rich.print(table)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def get_base_url() -> str:
|
|
81
|
+
"""Get the web base URL from cascading CLI context or default."""
|
|
82
|
+
from xclif.context import get_context
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
ctx = get_context()
|
|
86
|
+
raw = ctx.get("base_url", DEFAULT_BASE_URL)
|
|
87
|
+
except RuntimeError:
|
|
88
|
+
raw = DEFAULT_BASE_URL
|
|
89
|
+
|
|
90
|
+
# Normalize: ensure scheme, strip API path suffix for backward compat
|
|
91
|
+
if "://" not in raw:
|
|
92
|
+
raw = "https://" + raw
|
|
93
|
+
raw = raw.rstrip("/")
|
|
94
|
+
if raw.endswith("/api/v1"):
|
|
95
|
+
raw = raw[:-7]
|
|
96
|
+
return raw
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def get_web_base_url() -> str:
|
|
100
|
+
"""Derive the web UI base URL from the API base URL."""
|
|
101
|
+
return get_base_url()
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def get_authenticated_client(base_url: str | None = None) -> Client | None:
|
|
105
|
+
"""Return a Client if token is stored, else None."""
|
|
106
|
+
config = load_config()
|
|
107
|
+
token = config.get("token")
|
|
108
|
+
if not token:
|
|
109
|
+
return None
|
|
110
|
+
return Client(token=token, base_url=base_url or get_base_url())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def require_client(base_url: str | None = None) -> Client:
|
|
114
|
+
"""Return an authenticated Client or print error and exit."""
|
|
115
|
+
client = get_authenticated_client(base_url=base_url)
|
|
116
|
+
if client is None:
|
|
117
|
+
from xclif.errors import UsageError
|
|
118
|
+
|
|
119
|
+
raise UsageError("Not logged in. Run 'cb auth login' first.")
|
|
120
|
+
return client
|
|
@@ -5,5 +5,9 @@ from xclif import Cascade, WithConfig, command
|
|
|
5
5
|
@command("cb")
|
|
6
6
|
def _(
|
|
7
7
|
base_url: Cascade[WithConfig[str]] = DEFAULT_BASE_URL,
|
|
8
|
+
json: bool = False,
|
|
8
9
|
) -> None:
|
|
9
|
-
"""Interact with Codeberg or any Forgejo instance — manage repos, issues, PRs, releases, and more.
|
|
10
|
+
"""Interact with Codeberg or any Forgejo instance — manage repos, issues, PRs, releases, actions, and more.
|
|
11
|
+
|
|
12
|
+
Use --json on any command for machine-readable output.
|
|
13
|
+
"""
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from typing import Annotated
|
|
2
|
+
|
|
3
|
+
import rich
|
|
4
|
+
|
|
5
|
+
from codeberg_cli.git import infer_repo
|
|
6
|
+
from codeberg_cli.helpers import require_client
|
|
7
|
+
from xclif import Arg, Option, command
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@command("dispatch")
|
|
11
|
+
def _(
|
|
12
|
+
workflow: Annotated[str, Arg(description="Workflow filename (e.g. ci.yml)")],
|
|
13
|
+
repo: Annotated[str, Option(description="Repository (owner/repo)", name="repo")] = "",
|
|
14
|
+
ref: Annotated[str, Option(description="Branch or tag to dispatch on", name="ref")] = "main",
|
|
15
|
+
) -> None:
|
|
16
|
+
"""Dispatch a workflow (trigger a workflow_dispatch event)."""
|
|
17
|
+
client = require_client()
|
|
18
|
+
|
|
19
|
+
if not repo:
|
|
20
|
+
inferred = infer_repo()
|
|
21
|
+
if not inferred:
|
|
22
|
+
rich.print("[bold red]Error:[/bold red] No repo specified and not in a git directory")
|
|
23
|
+
return 1
|
|
24
|
+
repo = inferred
|
|
25
|
+
|
|
26
|
+
client.post(
|
|
27
|
+
f"/repos/{repo}/actions/workflows/{workflow}/dispatches",
|
|
28
|
+
data={"ref": ref},
|
|
29
|
+
action="Dispatching workflow",
|
|
30
|
+
)
|
|
31
|
+
rich.print(f"[green]Dispatched {workflow} on {ref} in {repo}.[/green]")
|