A command-line tool for managing repositories, issues, pull requests, releases, and CI/CD actions on Codeberg and any Forgejo or Gitea instance. Written in C11 with minimal runtime dependencies (libtls for HTTPS).
- Create, delete, rename repositories
- Edit repository metadata (description, visibility, features, merge styles, etc.)
- Show repository details
- List repositories (own, user, or organization)
- Transfer repository ownership
- Manage repository topics (add, remove, list, set)
- List, create, show, edit, delete releases
- Show latest release or release by tag
- Delete release by tag
- List, upload, edit, delete release assets
- Show release asset details
- List, create, show, delete tags
- List, create, show, rename, delete branches
- List, create, show, edit, delete issues
- Close and reopen issues (shorthands)
- Add comments
- Manage issue labels (add, set, clear, remove)
- List, create, edit, delete repository labels
- Show label details
- List, create, edit, delete milestones
- Show milestone details
- List, create, show, edit, merge pull requests
- Close and reopen PRs (shorthands)
- Merge with style selection (merge, rebase, squash, rebase-merge)
- Auto-merge when checks succeed
- List changed files, commits, diff (not yet implemented)
- Manage reviews (not yet implemented)
- List commits with ref/path filtering
- Show commit details, status, diff (not yet implemented)
- Compare refs (not yet implemented)
- Manage git notes (not yet implemented)
- List directory contents
- Create, update, delete files
- Show file details, raw content (not yet implemented)
- Download archives (not yet implemented)
- Create organizations
- List, add, delete deploy keys
- Show deploy key details
- List, add, remove collaborators
- View collaborator permissions (not yet implemented)
- List forks, fork a repository
- List, create, edit, delete webhooks
- Test webhooks
- Show webhook details (not yet implemented)
- List, create, edit, delete wiki pages
- View page revisions (not yet implemented)
- Show wiki page details (not yet implemented)
- List and inspect workflow runs
- View job details and step statuses
- Fetch and display build logs (with step-level filtering)
- List available runners
- Dispatch workflows
- Manage action secrets (list, set, delete)
- Manage action variables (list, show, set, delete)
- JSON output mode for scripting (
--json) - Client-side validation with clear error messages
- Token scope error detection with actionable guidance
- Manage your account's SSH public keys (
sshkey list/add/show/rm) - Manage packages in the Forgejo Package Registry (
package list/show/delete/files/link/unlink/upload/download) - Cross-platform: Linux, macOS, and Windows (MSYS2 UCRT64)
Pre-built binaries are available from the Codeberg releases page.
| File | Platform | Contents |
|---|---|---|
cb-<version>-linux-amd64.tar.gz |
Linux x86_64 | cb binary + COPYING + README.md |
cb-<version>-darwin-amd64.tar.gz |
macOS x86_64 | cb binary + COPYING + README.md |
cb-<version>-windows-amd64.zip |
Windows x86_64 | cb.exe + DLLs + COPYING + README.md |
The Linux and macOS tarballs contain the cb binary, GPLv3 license text, and README. The macOS build statically links LibreSSL, so there are no external dylib dependencies. The Windows ZIP contains cb.exe, the required LibreSSL DLLs, the license text, and README.
- GCC or Clang (C11)
- libretls (Linux/macOS) or LibreSSL (MSYS2) — provides the
libtlsAPI - autoconf, automake, make
- pkg-config
| Platform | Install command |
|---|---|
| Fedora | dnf install gcc libretls-devel pkg-config autoconf automake make |
| Debian/Ubuntu | apt install gcc libretls-dev pkg-config autoconf automake make |
| macOS | brew install libretls pkg-config autoconf automake make |
| MSYS2 UCRT64 | pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-libressl mingw-w64-ucrt-x86_64-pkg-config autoconf automake make |
MSYS2 UCRT64 has extra setup — see the MSYS2 build section below.
./autogen.sh # Generate version file + configure + Makefile.in (first time only)
./configure # Standard build
make # Build the binary
./cb # Run itBuilding on MSYS2 UCRT64 (Windows)
libretls is not packaged for MSYS2; LibreSSL provides libtls instead. The UCRT64 toolchain uses Winsock2 instead of POSIX sockets — cb_compat.h provides a portable socket layer that conditionally uses Winsock2 on Windows and POSIX sockets elsewhere.
# From an MSYS2 UCRT64 shell:
pacman -S mingw-w64-ucrt-x86_64-gcc mingw-w64-ucrt-x86_64-libressl \
mingw-w64-ucrt-x86_64-pkg-config autoconf automake make
export PKG_CONFIG_PATH=/ucrt64/lib/libressl/pkgconfig
autoreconf -i
./configure CPPFLAGS=-I/ucrt64/include/libressl LDFLAGS=-L/ucrt64/lib/libressl
make
make check # All 8 test suites passMSYS2 pkg-config path issue: The UCRT64 pkg-config rewrites
prefixpaths to Windows-style paths that the MSYS2 GCC cannot resolve. PassingCPPFLAGSandLDFLAGSwith MSYS2-style paths works around this. Thecb_compat.hheader conditionally uses Winsock2 on Windows and POSIX sockets elsewhere, so no source changes are needed.
make check # Run all 8 test suites./configure --enable-asan
make checkThe project includes a .clangd config and a make bear target for clangd-based editors (VS Code, Neovim, Helix, etc.).
./autogen.sh && ./configure # Must configure first (generates config.h)
make clean # Bear only intercepts commands that actually run
make bear # Generates compile_commands.jsonmake bear wraps the build with bear to produce compile_commands.json, giving clangd the exact compiler flags, include paths, and defines from configure. The .clangd config forces C language mode (-xc) to prevent false Objective-C++ errors on struct fields named private, protected, and template.
./configure --prefix=$HOME/.local
make installmake install installs the binary and shell completions for Bash, Zsh, and Fish. Completion directories are auto-detected via pkg-config (Bash) or use standard defaults; override them with --with-bash-completion-dir, --with-zsh-completion-dir, or --with-fish-completion-dir.
If you prefer to install completions manually, generate them from the built binary:
make completionsThis runs cb --help-spec and produces completions/cb.bash, completions/cb.zsh, and completions/cb.fish.
Bash:
cp completions/cb.bash ~/.local/share/bash-completion/completions/cbZsh:
cp completions/cb.zsh ~/.zsh/completions/_cbAdd ~/.zsh/completions to $fpath in ~/.zshrc if not already present.
Fish:
cp completions/cb.fish ~/.config/fish/completions/cb.fishGenerate a token at https://codeberg.org/user/settings/applications. The token needs these scopes:
| Scope | Needed for |
|---|---|
write:repository |
All repo CRUD and actions operations |
write:organization |
Creating/listing repos under organizations, creating organizations |
read:user |
Listing other users' repos |
all |
Everything (easy but broad) |
--base-urlCLI flagCB_BASE_URLenv var~/.config/cb/configfile (%APPDATA%\cb\configon Windows, or$XDG_CONFIG_HOME/cb/configifXDG_CONFIG_HOMEis set)- Default:
https://codeberg.org/api/v1
Token is read from CB_TOKEN env var or the config file:
# ~/.config/cb/config (mode 0600)
# %APPDATA%\cb\config on Windows (or $XDG_CONFIG_HOME/cb/config if set)
token = "your-token-here"
base_url = "https://codeberg.org/api/v1"cb [global flags] <command> [subcommand] [args] [flags]
| Flag | Description |
|---|---|
--json |
Output raw JSON instead of human-readable |
--quiet, -q |
Suppress non-essential output (lists show names only, show commands silent) |
--base-url URL |
Override API base URL |
--yes |
Skip confirmation prompts |
--version, -v |
Show version |
--help, -h |
Show help at any command level |
--help / -h works at every level of the command tree:
cb --help # top-level help
cb repo --help # repo subcommands
cb repo create --help # create flags
cb repo edit --help # edit flags
cb repo topic --help # topic subcommands
cb repo topic add --help # topic add usage
cb release --help # release subcommands
cb issue --help # issue subcommands
cb pr --help # PR subcommands
cb actions --help # actions subcommands
cb actions log --help # actions log usage
cb org create --help # org create flagsIn commands that take [owner/]repo, the owner/ part is optional — if omitted, it is automatically filled in from your authenticated user. For example, cb repo show myproj is equivalent to cb repo show thomasc/myproj.
cb repo create myproj --private -d "side project"
cb repo create myproj --org myorg --license MIT --auto-init
cb repo create myproj --object-format sha256Flags: --private, --public, --description/-d, --default-branch/-b, --license, --gitignore/-g, --auto-init, --template, --org, --object-format
cb repo delete thomasc/abandoned-proj --yes
cb repo delete abandoned-proj --yes # owner defaults to youRequires --yes or interactive confirmation.
cb repo rename thomasc/old-name new-nameOnly provided flags are sent — unset fields are not modified.
cb repo edit thomasc/myproj -d "new description" --public
cb repo edit thomasc/myproj --archived --no-issues
cb repo edit thomasc/myproj --default-merge-style squashFlags (each has --no-* counterpart for bools): --description/-d, --website/-w, --private/--public, --default-branch/-b, --archived/--unarchived, --template/--no-template, --has-issues/--no-issues, --has-wiki/--no-wiki, --has-prs/--no-prs, --has-projects/--no-projects, --has-releases/--no-releases, --has-packages/--no-packages, --has-actions/--no-actions, --allow-merge/--no-merge, --allow-rebase/--no-rebase, --allow-squash/--no-squash, --allow-ff-only/--no-ff-only, --default-merge-style, --delete-branch-after-merge/--no-delete-branch-after-merge, --allow-maintainer-edit/--no-allow-maintainer-edit
cb repo show thomasc/myproj
cb repo show myproj # owner defaults to you
cb repo show thomasc/myproj --jsoncb repo list # own repos
cb repo list --org myorg # org repos
cb repo list --user bob # another user's reposcb repo transfer thomasc/myproj codeberg-org --yesRequires --yes or interactive confirmation.
cb repo topic add thomasc/myproj go
cb repo topic rm thomasc/myproj go
cb repo topic list thomasc/myproj
cb repo topic set thomasc/myproj go,cli,codebergManage releases.
cb release list thomasc/myproj
cb release create thomasc/myproj --tag v1.0.0 --name "First Release"
cb release create thomasc/myproj --tag v2.0.0 --draft --prerelease
cb release show thomasc/myproj 42
cb release latest thomasc/myproj
cb release by-tag thomasc/myproj v1.0.0
cb release edit thomasc/myproj 42 --name "New Name" --no-draft
cb release delete thomasc/myproj 42 --yes
cb release delete-tag thomasc/myproj v1.0.0 --yes
cb release asset list thomasc/myproj 42
cb release asset edit thomasc/myproj 42 7 --name new-filename.zip
cb release asset delete thomasc/myproj 42 7 --yesSubcommands: list, create, show, latest, edit, delete, by-tag, delete-tag, asset
Manage tags.
cb tag list thomasc/myproj
cb tag create thomasc/myproj --tag v1.0.0 --message "Release 1.0"
cb tag show thomasc/myproj v1.0.0
cb tag delete thomasc/myproj v1.0.0 --yesManage branches.
cb branch list thomasc/myproj
cb branch create thomasc/myproj --name feature-x --from main
cb branch show thomasc/myproj main
cb branch rename thomasc/myproj old-name --name new-name
cb branch delete thomasc/myproj old-branch --yesManage issues.
cb issue list thomasc/myproj
cb issue list thomasc/myproj --state closed --limit 20
cb issue list thomasc/myproj --labels bug,feature
cb issue create thomasc/myproj --title "Fix crash" --body "Steps to reproduce..."
cb issue show thomasc/myproj 5
cb issue edit thomasc/myproj 5 --title "New title" --state closed
cb issue close thomasc/myproj 5
cb issue reopen thomasc/myproj 5
cb issue delete thomasc/myproj 5 --yes
cb issue comment thomasc/myproj 5 --body "This is fixed now"
cb issue label add thomasc/myproj 5 3
cb issue label clear thomasc/myproj 5Manage repository labels.
cb label list thomasc/myproj
cb label show thomasc/myproj 5
cb label create thomasc/myproj --name bug --color ff0000
cb label delete thomasc/myproj 3 --yesManage milestones.
cb milestone list thomasc/myproj
cb milestone show thomasc/myproj 3
cb milestone create thomasc/myproj --title "v2.0" --due 2025-12-31
cb milestone delete thomasc/myproj 3 --yesManage pull requests.
cb pr list thomasc/myproj
cb pr list thomasc/myproj --state closed
cb pr create thomasc/myproj --title "Add feature" --head feature-x
cb pr show thomasc/myproj 7
cb pr thomasc/myproj edit 7 --title "Updated title"
cb pr close thomasc/myproj 7
cb pr reopen thomasc/myproj 7
cb pr merge thomasc/myproj 7 --style squash --delete-branch
cb pr merge thomasc/myproj 7 --autoNote:
pr files,pr commits,pr diff, andpr revieware not yet implemented.
View commits and statuses.
cb commit list thomasc/myproj
cb commit list thomasc/myproj --sha main --path src/
cb commit list thomasc/myproj --limit 20Note:
commit show,commit status,commit diff,commit compare, andcommit noteare not yet implemented.
View and manage repository file contents.
cb content list thomasc/myproj
cb content list thomasc/myproj --ref mainNote:
content show,content raw, andcontent archiveare not yet implemented.
Manage deploy keys.
cb key list thomasc/myproj
cb key show thomasc/myproj 7
cb key add thomasc/myproj --title "CI key" --key "ssh-ed25519 AAAA..."
cb key delete thomasc/myproj 3 --yesManage collaborators.
cb collaborator list thomasc/myproj
cb collaborator add thomasc/myproj bob --permission write
cb collaborator rm thomasc/myproj bob --yes
cb collaborator thomasc/myproj perms bobNote:
collaborator permsis not yet implemented.
Manage forks.
cb fork thomasc/someproj list
cb fork thomasc/someproj create
cb fork thomasc/someproj create --org myorgManage webhooks.
cb hook list thomasc/myproj
cb hook create thomasc/myproj --type gitea --url https://example.com/hook
cb hook delete thomasc/myproj 5 --yesNote:
hook showis not yet implemented.
cb org create myorg -d "My organization"
cb org create myorg --visibility privateFlags: --description/-d, --full-name, --email, --location, --website, --visibility (public/limited/private), --repo-admin-change-team-access
Manage wiki pages.
cb wiki list thomasc/myproj
cb wiki delete thomasc/myproj OldPage --yesNote:
wiki showandwiki revisionsare not yet implemented.
List recent workflow runs.
cb actions list thomasc/cb
cb actions list thomasc/cb --jsonShow details of a specific workflow run.
run-id is the run number shown as #N in actions list output. All
actions detail commands (show, jobs, log) accept the same run number.
cb actions show thomasc/cb 69List jobs in a workflow run with status and duration.
cb actions jobs thomasc/cb 3Note:
run-idis the run number fromactions listoutput (e.g.#3).
Output:
Job Name Status Duration
0 build-linux success 59s
1 build-windows success 1m16s
2 build-macos success 2m03s
3 release skipped 3s
Show build logs for a workflow run. run-id is the run number from actions list output (e.g. #3). If job-index is omitted, defaults to 0. If step-index is omitted, shows all steps.
cb actions log thomasc/cb 3 # all steps of job 0
cb actions log thomasc/cb 3 0 # all steps of job 0
cb actions log thomasc/cb 3 0 2 # only step 2 of job 0Output includes step headers with status and duration, followed by log lines:
Job 0: build-linux (failure, 59s)
=== Install dependencies (failure, 11s) ===
Get:1 http://archive.ubuntu.com/ubuntu noble InRelease [256 kB]
...
E: Unable to locate package libretls-dev
List CI runners available to the repository.
cb actions runners thomasc/cbTrigger a workflow run.
cb actions dispatch thomasc/cb build.yml
cb actions dispatch thomasc/cb build.yml --ref masterFlags: --ref REF (git ref to dispatch on, default: master)
Manage repository action secrets. Secret values are never returned by the API.
cb actions secret list thomasc/cb
cb actions secret set thomasc/cb MY_TOKEN --value "secret-value"
cb actions secret rm thomasc/cb MY_TOKEN --yesManage repository action variables.
cb actions var list thomasc/cb
cb actions var show thomasc/cb BUILD_OPTS
cb actions var set thomasc/cb BUILD_OPTS --value "-j4"
cb actions var rm thomasc/cb BUILD_OPTS --yesManage packages in the Forgejo Package Registry. Unlike other commands, these are scoped to an owner (user or org), not a repository.
cb package list thomasc
cb package list thomasc --type generic --limit 10
cb package show thomasc generic mylib 1.0.0
cb package files thomasc generic mylib 1.0.0
cb package delete thomasc generic mylib 1.0.0 --yes
cb package link thomasc generic mylib myrepo
cb package unlink thomasc generic mylib --yes
cb package upload thomasc mylib 1.0.0 --file ./dist.tar.gz
cb package upload thomasc mylib 1.0.0 --file - < data.bin
cb package download thomasc mylib 1.0.0 dist.tar.gz --output ./dist.tar.gz
cb package download thomasc mylib 1.0.0 dist.tar.gz > dist.tar.gzThe project uses Forgejo Actions with Codeberg's hosted runners. On every push to master, the pipeline builds and tests cb on Linux, cross-compiles a Windows binary (MinGW), and cross-compiles a macOS binary (zig cc + static LibreSSL). On tag pushes (v*), it creates a release with downloadable binaries for all three platforms.
Workflow configuration: .forgejo/workflows/build.yml
The CLI decodes Forgejo API errors into actionable messages:
- 401: Invalid or expired token
- 403 (scope): Tells you exactly which scope is missing and where to regenerate the token
- 404: Repository not found
- 409: Repository already exists
- 422 (quota): Quota exceeded
- Network errors: Connection details
Exit codes: 0 success, 1 error, 2 usage error.
cb/
├── autogen.sh # Generates version file + runs autoreconf -fi
├── configure.ac # Autotools build configuration (git-derived version)
├── Makefile.am # Top-level automake
├── build-aux/git-version.sh # Git-based version generation script
├── .clang-format # clang-format style (Allman braces, 4-space indent)
├── .clangd # clangd config (forces C mode, suppresses ObjC++ false positives)
├── .forgejo/workflows/ # CI/CD pipeline (Forgejo Actions)
├── src/Makefile.am # Binary build rules (regenerates cb_version.h on every make)
├── tests/Makefile.am # Test binary rules
├── m4/ # Autoconf macros (ax_pthread, ax_check_compile_flag)
├── include/ # Public headers
│ ├── cb_json.h # JSON parser/serializer
│ ├── cb_http.h # HTTP client (sockets + libtls, Winsock2/POSIX via cb_compat)
│ ├── cb_config.h # Config loading (file + env)
│ ├── cb_validate.h # Client-side validation
│ ├── cb_api.h # Forgejo API client (repo, topic, actions, releases, issues, PRs, etc.)
│ ├── cb_compat.h # Portable compat layer (sockets, memstream, env, fs, base64, URL encode)
│ └── cb_cli.h # CLI dispatch
├── src/ # Implementation
│ ├── cb_json.c # recursive-descent parser + builder + serializer
│ ├── cb_http.c # plain HTTP + TLS via libtls
│ ├── cb_config.c # TOML-ish config + env + URL parser
│ ├── cb_validate.c # repo name, description, merge style, tag, branch, label color, SHA, org name, visibility validation
│ ├── cb_api.c # repo, topic, actions, releases, tags, branches, issues, labels, milestones,
│ │ # PRs, commits, content, keys, collaborators, forks, hooks, orgs, wiki API ops
│ ├── cb_cli.c # command parsing, flag dispatch, output (17 top-level commands)
│ ├── cb_compat.c # Portable wrappers (open_memstream, Winsock2, setenv, base64, URL encode)
│ └── main.c # Entry point
└── tests/ # Run with: make check (or: ./configure --enable-asan && make check)
├── test_helpers.h # Custom assert macros
├── mock_server.h/c # Minimal HTTP mock server for offline tests
├── test_json.c # JSON parser/serializer tests
├── test_http.c # HTTP client tests
├── test_config.c # Config loading tests
├── test_validate.c # Validation tests
├── test_api.c # API client tests (repo, topic)
├── test_cli.c # CLI integration tests
├── test_actions.c # Actions (CI/CD) tests
└── test_new_api.c # API tests for all new endpoints (releases–wiki)
Versions are derived from git state — no hardcoded version numbers to update. The build system uses build-aux/git-version.sh to compute a version string from git describe --tags, falling back to rev-count + short SHA when no tags exist. Uncommitted changes append a -dirty suffix.
./autogen.sh generates a version file (consumed by configure.ac's AC_INIT) before running autoreconf -fi. On every make, src/Makefile.am regenerates src/cb_version.h from the current git state; a cmp guard prevents unnecessary recompiles when the version hasn't changed. Distribution tarballs stamp the version into the included version file so builds without .git work correctly.
./cb --version # Show version (GNU-style output)
./cb -v # Short form-
Custom JSON parser: Hand-coded to keep dependencies low. Covers objects, arrays, strings (with escape/Unicode handling), numbers, booleans, null. The serializer's
omit_nullmode is critical forrepo edit— it ensures unset fields don't appear in the PATCH body, matching Forgejo'somitemptypointer semantics. -
libtls for TLS: Codeberg requires HTTPS. libretls (Linux/macOS) or LibreSSL (MSYS2) provides the
libtlsAPI as a thin wrapper over the system OpenSSL, keeping TLS setup minimal compared to raw OpenSSL. -
Portable socket layer:
cb_compat.habstracts the differences between POSIX sockets and Winsock2 — socket types, close/errno/poll wrappers, timeout APIs, and shutdown constants. Source code usescb_socket_t,cb_close_socket(),cb_poll(), etc. throughout, so the same.cfiles compile on both platforms without#ifdefclutter. -
Mock HTTP server for tests: A minimal
socket+pthreadserver in the test harness. Tests are fully offline — no network calls, no TLS. Each test configures canned responses per method+path. The server supports sequential responses to the same path (for multi-request flows like log pagination). -
TDD: Every module was built test-first. Run
./configure --enable-asan && make checkto test under ASan+UBSan. All 8 test suites pass with zero leaks on both Linux and MSYS2 UCRT64. -
Actions log fetching: Forgejo does not expose workflow logs through the
/api/v1REST API. Theactions logcommand uses the web UI's JSON endpoint (POST /{owner}/{repo}/actions/runs/{run_id}/jobs/{job}/attempt/{attempt}) with token auth, handling cursor-based pagination to fetch complete log output.
GPLv3+ — see COPYING for the full text. Aligning with the Forgejo ecosystem, which is GPLv3+ from v9.0.