forked from heavy-duty/stoke
350 lines
10 KiB
Markdown
350 lines
10 KiB
Markdown
|
|
# forgejo-cli
|
||
|
|
|
||
|
|
A command-line interface for [Forgejo](https://forgejo.org/), built with [Commander.js](https://github.com/tj/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 `fetch` API)
|
||
|
|
- npm
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
```bash
|
||
|
|
cd forgejo-cli
|
||
|
|
npm install
|
||
|
|
npm link # makes the `forgejo` binary available globally
|
||
|
|
```
|
||
|
|
|
||
|
|
Or run it directly without linking:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
node src/cli.js <command>
|
||
|
|
```
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
|
||
|
|
Authentication state is stored in a JSON file:
|
||
|
|
|
||
|
|
- Default: `~/.config/forgejo-cli/config.json`
|
||
|
|
- Override with `--config <path>` or `FORGEJO_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:
|
||
|
|
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"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
|
||
|
|
|
||
|
|
```text
|
||
|
|
-c, --config <path> path to configuration file
|
||
|
|
-h, --help display help
|
||
|
|
-V, --version display version
|
||
|
|
```
|
||
|
|
|
||
|
|
### `forgejo auth login`
|
||
|
|
|
||
|
|
Authenticate and persist an access token.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
forgejo auth login
|
||
|
|
# prompts for username and password
|
||
|
|
```
|
||
|
|
|
||
|
|
Non-interactive example using environment variables:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
export FORGEJO_USERNAME='kimi-reviewer-andresmgsl'
|
||
|
|
export FORGEJO_PASSWORD='...'
|
||
|
|
forgejo auth login
|
||
|
|
```
|
||
|
|
|
||
|
|
Password file example (avoids shell history and special-character issues):
|
||
|
|
|
||
|
|
```bash
|
||
|
|
chmod 600 /run/secrets/forgejo-password
|
||
|
|
forgejo auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/forgejo-password
|
||
|
|
```
|
||
|
|
|
||
|
|
Existing token example:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
forgejo auth login -t <personal-access-token>
|
||
|
|
```
|
||
|
|
|
||
|
|
Flow:
|
||
|
|
|
||
|
|
1. Calls `GET /api/v1/user` to verify credentials and resolve the canonical `login` name.
|
||
|
|
2. Calls `POST /api/v1/users/{login}/tokens` to generate a personal access token.
|
||
|
|
3. Requests the standard non-admin scopes: `read/write` for `activitypub`, `issue`, `misc`, `organization`, `package`, `repository`, and `user`.
|
||
|
|
4. 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.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
forgejo auth logout
|
||
|
|
```
|
||
|
|
|
||
|
|
Flow:
|
||
|
|
|
||
|
|
1. Calls `DELETE /api/v1/users/{login}/tokens/{id}` using the stored token.
|
||
|
|
2. Removes `~/.config/forgejo-cli/config.json`.
|
||
|
|
|
||
|
|
### `forgejo auth status`
|
||
|
|
|
||
|
|
Display the currently authenticated user.
|
||
|
|
|
||
|
|
```bash
|
||
|
|
forgejo auth status
|
||
|
|
```
|
||
|
|
|
||
|
|
Calls `GET /api/v1/user` with the stored token.
|
||
|
|
|
||
|
|
### `forgejo repo create`
|
||
|
|
|
||
|
|
Create a new repository for the authenticated user.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
Options:
|
||
|
|
-l, --limit <number> maximum repositories to display (default: 50; use 0 for all)
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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`:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
Options:
|
||
|
|
-f, --file <path> path to JSON manifest (required)
|
||
|
|
--dry-run print the manifest without importing
|
||
|
|
```
|
||
|
|
|
||
|
|
Manifest format:
|
||
|
|
|
||
|
|
```json
|
||
|
|
[
|
||
|
|
{
|
||
|
|
"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:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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)
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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)
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
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.
|
||
|
|
|
||
|
|
```text
|
||
|
|
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)
|
||
|
|
```
|
||
|
|
|
||
|
|
```bash
|
||
|
|
forgejo branch list -o kimi-reviewer-andresmgsl -r box
|
||
|
|
```
|
||
|
|
|
||
|
|
Calls `GET /api/v1/repos/{owner}/{repo}/branches` and auto-paginates.
|
||
|
|
|
||
|
|
## Architecture
|
||
|
|
|
||
|
|
```text
|
||
|
|
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.js` defines commands and options, handles prompts and prints results.
|
||
|
|
- `api.js` encapsulates all HTTP calls to Forgejo. It supports both Basic auth (for token generation) and token auth (for all other calls).
|
||
|
|
- `config.js` reads/writes JSON config and enforces restrictive file permissions.
|
||
|
|
|
||
|
|
## Security notes
|
||
|
|
|
||
|
|
- Tokens are stored on disk with `0600` permissions.
|
||
|
|
- Passwords are never persisted; they are only used to generate a token.
|
||
|
|
- Prefer `--password-file` or `FORGEJO_PASSWORD` over `-p` to 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`.
|