chore: rename CLI and project to stoke
This commit is contained in:
parent
25670253ea
commit
9e7c204201
5 changed files with 90 additions and 58 deletions
92
README.md
92
README.md
|
|
@ -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`.
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 };
|
||||||
|
|
|
||||||
34
src/cli.js
34
src/cli.js
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue