chore: rename CLI and project to stoke

This commit is contained in:
kimi-reviewer-andresmgsl 2026-07-22 15:16:42 +00:00
parent 25670253ea
commit 9e7c204201
5 changed files with 90 additions and 58 deletions

View file

@ -1,4 +1,6 @@
# forgejo-cli # stoke
> CLI for the heavy-duty forge (Forgejo). Named after the act of feeding and tending a fire — the operator's hand on the forge.
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. 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.
@ -10,9 +12,9 @@ A command-line interface for [Forgejo](https://forgejo.org/), built with [Comman
## Installation ## Installation
```bash ```bash
cd forgejo-cli cd stoke
npm install npm install
npm link # makes the `forgejo` binary available globally npm link # makes the `stoke` binary available globally
``` ```
Or run it directly without linking: Or run it directly without linking:
@ -25,8 +27,8 @@ node src/cli.js <command>
Authentication state is stored in a JSON file: Authentication state is stored in a JSON file:
- Default: `~/.config/forgejo-cli/config.json` - Default: `~/.config/stoke/config.json`
- Override with `--config <path>` or `FORGEJO_CONFIG_FILE` - Override with `--config <path>` or `STOKE_CONFIG_FILE` (or `FORGEJO_CONFIG_FILE` as a fallback)
The configuration directory is created with permissions `0700` and the file with `0600` so only the owner can read the token. The configuration directory is created with permissions `0700` and the file with `0600` so only the owner can read the token.
@ -47,14 +49,16 @@ Example stored config:
| Variable | Purpose | | Variable | Purpose |
| --- | --- | | --- | --- |
| `FORGEJO_URL` | Default Forgejo base URL | | `STOKE_URL` | Default Forgejo base URL |
| `FORGEJO_USERNAME` | Default username/email for `auth login` | | `STOKE_USERNAME` | Default username/email for `auth login` |
| `FORGEJO_PASSWORD` | Default password for `auth login` | | `STOKE_PASSWORD` | Default password for `auth login` |
| `FORGEJO_CONFIG_FILE` | Path to the config file | | `STOKE_CONFIG_FILE` | Path to the config file |
| `FORGEJO_CONFIG_DIR` | Directory for the config file | | `STOKE_CONFIG_DIR` | Directory for the config file |
| `XDG_CONFIG_HOME` | Followed when resolving the default config directory | | `XDG_CONFIG_HOME` | Followed when resolving the default config directory |
| `GITHUB_TOKEN` | GitHub token used by `repo import` when `--github-token` is omitted | | `GITHUB_TOKEN` | GitHub token used by `repo import` when `--github-token` is omitted |
`FORGEJO_*` variants are still accepted as fallbacks for backward compatibility.
## Commands ## Commands
### Global options ### Global options
@ -65,7 +69,7 @@ Example stored config:
-V, --version display version -V, --version display version
``` ```
### `forgejo auth login` ### `stoke auth login`
Authenticate and persist an access token. Authenticate and persist an access token.
@ -83,29 +87,29 @@ Options:
Interactive example: Interactive example:
```bash ```bash
forgejo auth login stoke auth login
# prompts for username and password # prompts for username and password
``` ```
Non-interactive example using environment variables: Non-interactive example using environment variables:
```bash ```bash
export FORGEJO_USERNAME='kimi-reviewer-andresmgsl' export STOKE_USERNAME='kimi-reviewer-andresmgsl'
export FORGEJO_PASSWORD='...' export STOKE_PASSWORD='...'
forgejo auth login stoke auth login
``` ```
Password file example (avoids shell history and special-character issues): Password file example (avoids shell history and special-character issues):
```bash ```bash
chmod 600 /run/secrets/forgejo-password chmod 600 /run/secrets/stoke-password
forgejo auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/forgejo-password stoke auth login -n kimi-reviewer-andresmgsl --password-file /run/secrets/stoke-password
``` ```
Existing token example: Existing token example:
```bash ```bash
forgejo auth login -t <personal-access-token> stoke auth login -t <personal-access-token>
``` ```
Flow: Flow:
@ -115,30 +119,30 @@ Flow:
3. Requests the standard non-admin scopes: `read/write` for `activitypub`, `issue`, `misc`, `organization`, `package`, `repository`, and `user`. 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. 4. Writes the token, token id, user details and URL to the config file.
### `forgejo auth logout` ### `stoke auth logout`
Revoke the stored token remotely and delete the local config. Revoke the stored token remotely and delete the local config.
```bash ```bash
forgejo auth logout stoke auth logout
``` ```
Flow: Flow:
1. Calls `DELETE /api/v1/users/{login}/tokens/{id}` using the stored token. 1. Calls `DELETE /api/v1/users/{login}/tokens/{id}` using the stored token.
2. Removes `~/.config/forgejo-cli/config.json`. 2. Removes `~/.config/stoke/config.json`.
### `forgejo auth status` ### `stoke auth status`
Display the currently authenticated user. Display the currently authenticated user.
```bash ```bash
forgejo auth status stoke auth status
``` ```
Calls `GET /api/v1/user` with the stored token. Calls `GET /api/v1/user` with the stored token.
### `forgejo repo create` ### `stoke repo create`
Create a new repository for the authenticated user. Create a new repository for the authenticated user.
@ -155,13 +159,13 @@ Options:
Example: Example:
```bash ```bash
forgejo repo create --name forgejo-cli-test --private \ stoke repo create --name stoke-test --private \
-d "Test repository created via forgejo-cli" -d "Test repository created via stoke"
``` ```
Calls `POST /api/v1/user/repos`. Calls `POST /api/v1/user/repos`.
### `forgejo repo list` ### `stoke repo list`
List repositories for the authenticated user. List repositories for the authenticated user.
@ -171,12 +175,12 @@ Options:
``` ```
```bash ```bash
forgejo repo list stoke repo list
``` ```
Calls `GET /api/v1/user/repos` and auto-paginates. Calls `GET /api/v1/user/repos` and auto-paginates.
### `forgejo repo import` ### `stoke repo import`
Import a remote repository into Forgejo, including git history, issues, pull requests, labels, milestones, releases and wiki. Import a remote repository into Forgejo, including git history, issues, pull requests, labels, milestones, releases and wiki.
@ -207,7 +211,7 @@ Options:
Example used to mirror `heavy-duty/box`: Example used to mirror `heavy-duty/box`:
```bash ```bash
forgejo repo import \ stoke repo import \
--from https://github.com/heavy-duty/box.git \ --from https://github.com/heavy-duty/box.git \
--name box \ --name box \
--service github \ --service github \
@ -217,7 +221,7 @@ forgejo repo import \
Calls `POST /api/v1/repos/migrate`. Calls `POST /api/v1/repos/migrate`.
### `forgejo repo import-batch` ### `stoke repo import-batch`
Import multiple repositories from a JSON manifest. Import multiple repositories from a JSON manifest.
@ -249,13 +253,13 @@ Manifest format:
Example: Example:
```bash ```bash
forgejo repo import-batch -f repos.json stoke repo import-batch -f repos.json
forgejo repo import-batch -f repos.json --dry-run stoke repo import-batch -f repos.json --dry-run
``` ```
Calls `POST /api/v1/repos/migrate` once per entry. Calls `POST /api/v1/repos/migrate` once per entry.
### `forgejo issue list` ### `stoke issue list`
List issues in a repository. List issues in a repository.
@ -269,12 +273,12 @@ Options:
``` ```
```bash ```bash
forgejo issue list -o kimi-reviewer-andresmgsl -r box -s all -l 0 stoke issue list -o kimi-reviewer-andresmgsl -r box -s all -l 0
``` ```
Calls `GET /api/v1/repos/{owner}/{repo}/issues` and auto-paginates. Calls `GET /api/v1/repos/{owner}/{repo}/issues` and auto-paginates.
### `forgejo pr list` ### `stoke pr list`
List pull requests in a repository. List pull requests in a repository.
@ -287,12 +291,12 @@ Options:
``` ```
```bash ```bash
forgejo pr list -o kimi-reviewer-andresmgsl -r box -s all -l 0 stoke pr list -o kimi-reviewer-andresmgsl -r box -s all -l 0
``` ```
Calls `GET /api/v1/repos/{owner}/{repo}/pulls` and auto-paginates. Calls `GET /api/v1/repos/{owner}/{repo}/pulls` and auto-paginates.
### `forgejo branch list` ### `stoke branch list`
List branches in a repository. List branches in a repository.
@ -304,12 +308,12 @@ Options:
``` ```
```bash ```bash
forgejo branch list -o kimi-reviewer-andresmgsl -r box stoke branch list -o kimi-reviewer-andresmgsl -r box
``` ```
Calls `GET /api/v1/repos/{owner}/{repo}/branches` and auto-paginates. Calls `GET /api/v1/repos/{owner}/{repo}/branches` and auto-paginates.
### `forgejo collaborator add` ### `stoke collaborator add`
Add a collaborator to a repository. Add a collaborator to a repository.
@ -324,8 +328,8 @@ Options:
Example: Example:
```bash ```bash
forgejo collaborator add -o kimi-reviewer-andresmgsl -r infra -u andres --permission admin stoke collaborator add -o kimi-reviewer-andresmgsl -r infra -u andres --permission admin
forgejo collaborator add -o kimi-reviewer-andresmgsl -r infra -u dan --permission admin stoke collaborator add -o kimi-reviewer-andresmgsl -r infra -u dan --permission admin
``` ```
Calls `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}`. Calls `PUT /api/v1/repos/{owner}/{repo}/collaborators/{user}`.
@ -347,7 +351,7 @@ src/
- Tokens are stored on disk with `0600` permissions. - Tokens are stored on disk with `0600` permissions.
- Passwords are never persisted; they are only used to generate a token. - 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. - Prefer `--password-file` or `STOKE_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. - The generated token name includes the hostname and a timestamp to avoid collisions.
## Verification: heavy-duty repository imports ## Verification: heavy-duty repository imports
@ -367,4 +371,4 @@ Counts match GitHub for all repositories. Git history, issues, pull requests, la
## Next steps ## 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`. 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 `stoke`.

View file

@ -1,8 +1,8 @@
{ {
"name": "forgejo-cli", "name": "stoke",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "CLI for the heavy-duty forge (Forgejo)",
"main": "index.js", "main": "src/cli.js",
"scripts": { "scripts": {
"test": "node --test test/**/*.test.js", "test": "node --test test/**/*.test.js",
"start": "node src/cli.js" "start": "node src/cli.js"
@ -15,6 +15,6 @@
"commander": "^15.0.0" "commander": "^15.0.0"
}, },
"bin": { "bin": {
"forgejo": "src/cli.js" "stoke": "src/cli.js"
} }
} }

View file

@ -149,6 +149,12 @@ class ForgejoClient {
permission, permission,
}); });
} }
async renameRepo(owner, repo, newName) {
return this.request('PATCH', `/repos/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}`, {
name: newName,
});
}
} }
module.exports = { ForgejoClient }; module.exports = { ForgejoClient };

View file

@ -12,8 +12,8 @@ const pkg = require('../package.json');
const program = new Command(); const program = new Command();
program program
.name('forgejo') .name('stoke')
.description('CLI for Forgejo (https://forgejo.heavyduty.builders)') .description('CLI for the heavy-duty forge (https://forgejo.heavyduty.builders)')
.version(pkg.version) .version(pkg.version)
.configureOutput({ outputError: (str, write) => write(`Error: ${str}`) }); .configureOutput({ outputError: (str, write) => write(`Error: ${str}`) });
@ -21,7 +21,7 @@ program
.option('-c, --config <path>', 'path to configuration file') .option('-c, --config <path>', 'path to configuration file')
.hook('preAction', (thisCommand) => { .hook('preAction', (thisCommand) => {
if (thisCommand.opts().config) { if (thisCommand.opts().config) {
process.env.FORGEJO_CONFIG_FILE = thisCommand.opts().config; process.env.STOKE_CONFIG_FILE = thisCommand.opts().config;
} }
}); });
@ -53,7 +53,7 @@ function readSecretFile(filePath, label) {
function makeTokenName() { function makeTokenName() {
const host = require('node:os').hostname() || 'unknown'; const host = require('node:os').hostname() || 'unknown';
return `forgejo-cli-${host}-${Date.now()}`; return `stoke-${host}-${Date.now()}`;
} }
const DEFAULT_TOKEN_SCOPES = [ const DEFAULT_TOKEN_SCOPES = [
@ -84,9 +84,9 @@ const auth = program
auth auth
.command('login') .command('login')
.description('Authenticate against a Forgejo instance and store an access token') .description('Authenticate against a Forgejo instance and store an access token')
.option('-u, --url <url>', 'Forgejo base URL', process.env.FORGEJO_URL || 'https://forgejo.heavyduty.builders') .option('-u, --url <url>', 'Forgejo base URL', process.env.STOKE_URL || process.env.FORGEJO_URL || 'https://forgejo.heavyduty.builders')
.option('-n, --username <username>', 'account username or email', process.env.FORGEJO_USERNAME) .option('-n, --username <username>', 'account username or email', process.env.STOKE_USERNAME || process.env.FORGEJO_USERNAME)
.option('-p, --password <password>', 'account password', process.env.FORGEJO_PASSWORD) .option('-p, --password <password>', 'account password', process.env.STOKE_PASSWORD || process.env.FORGEJO_PASSWORD)
.option('--password-file <path>', 'read account password from a file') .option('--password-file <path>', 'read account password from a file')
.option('-t, --token <token>', 'use an existing personal access token instead of generating one') .option('-t, --token <token>', 'use an existing personal access token instead of generating one')
.option('--token-file <path>', 'read an existing personal access token from a file') .option('--token-file <path>', 'read an existing personal access token from a file')
@ -350,6 +350,26 @@ repo
} }
}); });
repo
.command('rename')
.description('Rename a repository')
.requiredOption('-o, --owner <owner>', 'repository owner')
.requiredOption('-r, --repo <repo>', 'current repository name')
.requiredOption('--name <new-name>', 'new repository name')
.action(async (options) => {
try {
const config = loadConfig();
const client = ForgejoClient.fromConfig(config);
const result = await client.renameRepo(options.owner, options.repo, options.name);
console.log(`Repository renamed to ${result.full_name}`);
console.log(`URL: ${result.html_url}`);
} catch (err) {
console.error(`Repository rename failed: ${err.message}`);
if (err.status) console.error(`HTTP status: ${err.status}`);
process.exit(1);
}
});
repo repo
.command('import-batch') .command('import-batch')
.description('Import multiple repositories from a JSON manifest') .description('Import multiple repositories from a JSON manifest')

View file

@ -2,9 +2,11 @@ const path = require('node:path');
const os = require('node:os'); const os = require('node:os');
const fs = require('node:fs'); const fs = require('node:fs');
const CONFIG_DIR = process.env.FORGEJO_CONFIG_DIR const CONFIG_DIR = process.env.STOKE_CONFIG_DIR
|| path.join(process.env.XDG_CONFIG_HOME || os.homedir(), '.config', 'forgejo-cli'); || process.env.FORGEJO_CONFIG_DIR
const CONFIG_PATH = process.env.FORGEJO_CONFIG_FILE || path.join(process.env.XDG_CONFIG_HOME || os.homedir(), '.config', 'stoke');
const CONFIG_PATH = process.env.STOKE_CONFIG_FILE
|| process.env.FORGEJO_CONFIG_FILE
|| path.join(CONFIG_DIR, 'config.json'); || path.join(CONFIG_DIR, 'config.json');
const CONFIG_MODE = 0o600; const CONFIG_MODE = 0o600;
const DIR_MODE = 0o700; const DIR_MODE = 0o700;