10 KiB
forgejo-cli
A command-line interface for Forgejo, built with Commander.js. It targets the instance at https://forgejo.heavyduty.builders and is designed so that every real operation performed against Forgejo becomes a new CLI command.
Requirements
- Node.js >= 18 (uses the global
fetchAPI) - npm
Installation
cd forgejo-cli
npm install
npm link # makes the `forgejo` binary available globally
Or run it directly without linking:
node src/cli.js <command>
Configuration
Authentication state is stored in a JSON file:
- Default:
~/.config/forgejo-cli/config.json - Override with
--config <path>orFORGEJO_CONFIG_FILE
The configuration directory is created with permissions 0700 and the file with 0600 so only the owner can read the token.
Example stored config:
{
"url": "https://forgejo.heavyduty.builders",
"login": "kimi-reviewer-andresmgsl",
"username": "kimi-reviewer-andresmgsl",
"email": "andres+4@heavyduty.builders",
"token": "<sha1>",
"tokenId": 42
}
Environment variables
| Variable | Purpose |
|---|---|
FORGEJO_URL |
Default Forgejo base URL |
FORGEJO_USERNAME |
Default username/email for auth login |
FORGEJO_PASSWORD |
Default password for auth login |
FORGEJO_CONFIG_FILE |
Path to the config file |
FORGEJO_CONFIG_DIR |
Directory for the config file |
XDG_CONFIG_HOME |
Followed when resolving the default config directory |
GITHUB_TOKEN |
GitHub token used by repo import when --github-token is omitted |
Commands
Global options
-c, --config <path> path to configuration file
-h, --help display help
-V, --version display version
forgejo auth login
Authenticate and persist an access token.
Options:
-u, --url <url> Forgejo base URL (default: https://forgejo.heavyduty.builders)
-n, --username <username> account username or email
-p, --password <password> account password
--password-file <path> read account password from a file
-t, --token <token> use an existing personal access token instead of generating one
--token-file <path> read an existing personal access token from a file
--token-name <name> name for the generated token
Interactive example:
forgejo auth login
# prompts for username and password
Non-interactive example using environment variables:
export FORGEJO_USERNAME='kimi-reviewer-andresmgsl'
export FORGEJO_PASSWORD='...'
forgejo auth login
Password file example (avoids shell history and special-character issues):
chmod 600 /run/secrets/forgejo-password
forgejo auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/forgejo-password
Existing token example:
forgejo auth login -t <personal-access-token>
Flow:
- Calls
GET /api/v1/userto verify credentials and resolve the canonicalloginname. - Calls
POST /api/v1/users/{login}/tokensto generate a personal access token. - Requests the standard non-admin scopes:
read/writeforactivitypub,issue,misc,organization,package,repository, anduser. - Writes the token, token id, user details and URL to the config file.
forgejo auth logout
Revoke the stored token remotely and delete the local config.
forgejo auth logout
Flow:
- Calls
DELETE /api/v1/users/{login}/tokens/{id}using the stored token. - Removes
~/.config/forgejo-cli/config.json.
forgejo auth status
Display the currently authenticated user.
forgejo auth status
Calls GET /api/v1/user with the stored token.
forgejo repo create
Create a new repository for the authenticated user.
Options:
--name <name> repository name (required)
-d, --description <description> repository description
--private make the repository private
--public make the repository public
--auto-init initialize with a README (default: true)
--default-branch <branch> default branch name (default: "main")
Example:
forgejo repo create --name forgejo-cli-test --private \
-d "Test repository created via forgejo-cli"
Calls POST /api/v1/user/repos.
forgejo repo list
List repositories for the authenticated user.
Options:
-l, --limit <number> maximum repositories to display (default: 50; use 0 for all)
forgejo repo list
Calls GET /api/v1/user/repos and auto-paginates.
forgejo repo import
Import a remote repository into Forgejo, including git history, issues, pull requests, labels, milestones, releases and wiki.
Options:
--from <clone-addr> source clone URL (required)
--name <name> repository name in Forgejo (required)
--service <service> source service type: git, github, gitea, gitlab, ... (default: github)
--owner <owner> Forgejo owner for the imported repo (default: current user)
-d, --description <description> repository description
--private / --public visibility
--issues migrate issues (default)
--no-issues skip issues
--labels migrate labels (default)
--no-labels skip labels
--milestones migrate milestones (default)
--no-milestones skip milestones
--pull-requests migrate pull requests (default)
--no-pull-requests skip pull requests
--releases migrate releases (default)
--no-releases skip releases
--wiki migrate wiki (default)
--no-wiki skip wiki
--lfs migrate LFS objects
--github-token <token> GitHub token (defaults to GITHUB_TOKEN or `gh auth token`)
Example used to mirror heavy-duty/box:
forgejo repo import \
--from https://github.com/heavy-duty/box.git \
--name box \
--service github \
--public \
--issues --labels --milestones --pull-requests --releases --wiki
Calls POST /api/v1/repos/migrate.
forgejo repo import-batch
Import multiple repositories from a JSON manifest.
Options:
-f, --file <path> path to JSON manifest (required)
--dry-run print the manifest without importing
Manifest format:
[
{
"name": "rig",
"from": "https://github.com/heavy-duty/rig.git",
"service": "github",
"private": false,
"issues": true,
"labels": true,
"milestones": true,
"pull_requests": true,
"releases": true,
"wiki": true
}
]
Example:
forgejo repo import-batch -f repos.json
forgejo repo import-batch -f repos.json --dry-run
Calls POST /api/v1/repos/migrate once per entry.
forgejo issue list
List issues in a repository.
Options:
-o, --owner <owner> repository owner (required)
-r, --repo <repo> repository name (required)
-s, --state <state> open, closed, all (default: open)
-t, --type <type> issues or pulls (default: issues)
-l, --limit <number> maximum issues to display (default: 50; use 0 for all)
forgejo issue list -o kimi-reviewer-andresmgsl -r box -s all -l 0
Calls GET /api/v1/repos/{owner}/{repo}/issues and auto-paginates.
forgejo pr list
List pull requests in a repository.
Options:
-o, --owner <owner> repository owner (required)
-r, --repo <repo> repository name (required)
-s, --state <state> open, closed, all (default: open)
-l, --limit <number> maximum PRs to display (default: 50; use 0 for all)
forgejo pr list -o kimi-reviewer-andresmgsl -r box -s all -l 0
Calls GET /api/v1/repos/{owner}/{repo}/pulls and auto-paginates.
forgejo branch list
List branches in a repository.
Options:
-o, --owner <owner> repository owner (required)
-r, --repo <repo> repository name (required)
-l, --limit <number> maximum branches to display (default: 50; use 0 for all)
forgejo branch list -o kimi-reviewer-andresmgsl -r box
Calls GET /api/v1/repos/{owner}/{repo}/branches and auto-paginates.
Architecture
src/
├── cli.js # Commander program, commands and user I/O
├── api.js # Forgejo API client (fetch wrapper)
└── config.js # Secure filesystem-based config storage
cli.jsdefines commands and options, handles prompts and prints results.api.jsencapsulates all HTTP calls to Forgejo. It supports both Basic auth (for token generation) and token auth (for all other calls).config.jsreads/writes JSON config and enforces restrictive file permissions.
Security notes
- Tokens are stored on disk with
0600permissions. - Passwords are never persisted; they are only used to generate a token.
- Prefer
--password-fileorFORGEJO_PASSWORDover-pto keep passwords out of shell history and avoid!history-expansion issues. - The generated token name includes the hostname and a timestamp to avoid collisions.
Verification: heavy-duty repository imports
The heavy-duty repositories were imported into Forgejo under https://forgejo.heavyduty.builders/kimi-reviewer-andresmgsl.
| Repository | Visibility | Branches | Commits | Open issues | Total issues | PRs | Labels | Milestones | Releases |
|---|---|---|---|---|---|---|---|---|---|
box |
public | 1 | 306 | 11 | 65 | 92 | 21 | 0 | 4 |
rig |
public | 1 | 198 | 10 | 49 | 59 | 21 | 0 | 3 |
cast |
public | 1 | 194 | 7 | 67 | 75 | 21 | 0 | 3 |
infra |
private | 1 | 86 | 2 | 8 | 22 | 9 | 0 | 0 |
handbook |
private | 1 | 29 | 28 | 28 | 13 | 9 | 5 | 0 |
incubator |
private | 1 | 1,326 | 1 | 1 | 23 | 9 | 0 | 0 |
Counts match GitHub for all repositories. Git history, issues, pull requests, labels, milestones and releases are included. Discussions are not enabled on any of the source repositories. The cast wiki is enabled on GitHub but contains no pages, so nothing was migrated.
Next steps
The CLI is authenticated and the first full repository import is complete. Every subsequent Forgejo task you need will be added as a new command under forgejo.