2026-05-13 14:56:28 +00:00
|
|
|
use anyhow::Result;
|
|
|
|
|
use chrono::{DateTime, Utc};
|
|
|
|
|
use reqwest::Method;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
|
|
|
|
|
use crate::client::{Client, Page};
|
|
|
|
|
|
|
|
|
|
use super::user::User;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct Repo {
|
|
|
|
|
pub id: u64,
|
|
|
|
|
pub name: String,
|
|
|
|
|
pub full_name: String,
|
|
|
|
|
pub owner: User,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub description: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub private: bool,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub fork: bool,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub archived: bool,
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
#[serde(default)]
|
|
|
|
|
pub mirror: bool,
|
2026-05-13 14:56:28 +00:00
|
|
|
pub html_url: String,
|
|
|
|
|
pub clone_url: String,
|
|
|
|
|
pub ssh_url: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub default_branch: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub stars_count: u64,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub forks_count: u64,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub open_issues_count: u64,
|
|
|
|
|
pub updated_at: DateTime<Utc>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Default)]
|
|
|
|
|
pub struct ListOptions<'a> {
|
|
|
|
|
pub limit: u32,
|
|
|
|
|
pub page: u32,
|
|
|
|
|
pub query: Option<&'a str>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn list_for_user(client: &Client, opts: ListOptions<'_>) -> Result<Page<Repo>> {
|
2026-05-13 15:37:20 +00:00
|
|
|
if opts.limit > 50 {
|
|
|
|
|
let items = client
|
|
|
|
|
.get_all::<Repo>("/api/v1/user/repos", &[], opts.limit as usize)
|
|
|
|
|
.await?;
|
|
|
|
|
return Ok(Page::single(items));
|
|
|
|
|
}
|
2026-05-13 14:56:28 +00:00
|
|
|
let limit = opts.limit.clamp(1, 50);
|
|
|
|
|
let page = opts.page.max(1);
|
|
|
|
|
let query: Vec<(String, String)> = vec![
|
|
|
|
|
("limit".into(), limit.to_string()),
|
|
|
|
|
("page".into(), page.to_string()),
|
|
|
|
|
];
|
|
|
|
|
client.get_page::<Repo>("/api/v1/user/repos", &query).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn search(client: &Client, opts: ListOptions<'_>) -> Result<Page<SearchHit>> {
|
|
|
|
|
let limit = opts.limit.clamp(1, 50);
|
|
|
|
|
let page = opts.page.max(1);
|
|
|
|
|
let mut query: Vec<(String, String)> = vec![
|
|
|
|
|
("limit".into(), limit.to_string()),
|
|
|
|
|
("page".into(), page.to_string()),
|
|
|
|
|
];
|
|
|
|
|
if let Some(q) = opts.query {
|
|
|
|
|
query.push(("q".into(), q.into()));
|
|
|
|
|
}
|
|
|
|
|
let res = client
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
.request(Method::GET, "/api/v1/repos/search", &query, None)
|
2026-05-13 14:56:28 +00:00
|
|
|
.await?;
|
|
|
|
|
let headers = res.headers().clone();
|
|
|
|
|
let body: SearchResponse = res.error_for_status()?.json().await?;
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
Ok(Page::from_headers(body.data, &headers))
|
2026-05-13 14:56:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
|
|
|
struct SearchResponse {
|
|
|
|
|
data: Vec<SearchHit>,
|
|
|
|
|
#[serde(default)]
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
#[allow(dead_code)]
|
2026-05-13 14:56:28 +00:00
|
|
|
ok: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub type SearchHit = Repo;
|
|
|
|
|
|
|
|
|
|
pub async fn get(client: &Client, owner: &str, name: &str) -> Result<Repo> {
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}");
|
|
|
|
|
client.json(Method::GET, &path, &[], None::<&()>).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
|
|
pub struct CreateRepo<'a> {
|
|
|
|
|
pub name: &'a str,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub description: Option<&'a str>,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub private: bool,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub default_branch: Option<&'a str>,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub auto_init: bool,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn create_for_current_user(client: &Client, body: &CreateRepo<'_>) -> Result<Repo> {
|
|
|
|
|
client
|
|
|
|
|
.json(Method::POST, "/api/v1/user/repos", &[], Some(body))
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
pub async fn create_for_org(client: &Client, org: &str, body: &CreateRepo<'_>) -> Result<Repo> {
|
2026-05-13 14:56:28 +00:00
|
|
|
let path = format!("/api/v1/orgs/{org}/repos");
|
|
|
|
|
client.json(Method::POST, &path, &[], Some(body)).await
|
|
|
|
|
}
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Default)]
|
|
|
|
|
pub struct EditRepo<'a> {
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub description: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub website: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub private: Option<bool>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub default_branch: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub archived: Option<bool>,
|
|
|
|
|
}
|
|
|
|
|
|
batch: release, label, workflow, search, browse, status, org, keys, alias, config, extension, gist
* New top-level groups, each with full CRUD where the API supports it:
- release: list/view/create/edit/delete/upload/download
- label: list/create/edit/delete
- run: workflow runs (list/view/rerun/cancel)
- secret + variable: Actions secrets/vars (list/set/delete)
- search: cross-cutting (repos/issues/prs/users)
- browse: open repo/path on the web
- status: notifications inbox + mark-all-read
- org: list/view/teams
- ssh-key, gpg-key: list/add/delete on your account
- alias: user-defined shortcuts (e.g. `fj alias set co "pr checkout"`)
- config: local prefs (editor, pager, browser, etc.)
- extension: discover and run `fj-<name>` plugin binaries on PATH
- gist: thin wrapper over `gist-*` repos
* main.rs now expands aliases before clap and dispatches to plugins for
unknown subcommands (matching gh).
* New API modules: release, label, notification, search, org, workflow,
with the corresponding strongly-typed wrappers.
* Release asset upload uses reqwest multipart (feature flag added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:29:31 +00:00
|
|
|
pub async fn edit(client: &Client, owner: &str, name: &str, body: &EditRepo<'_>) -> Result<Repo> {
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}");
|
|
|
|
|
client.json(Method::PATCH, &path, &[], Some(body)).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn delete(client: &Client, owner: &str, name: &str) -> Result<()> {
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}");
|
|
|
|
|
let res = client.request(Method::DELETE, &path, &[], None).await?;
|
|
|
|
|
res.error_for_status()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
|
|
struct ForkBody<'a> {
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
organization: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
name: Option<&'a str>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn fork(
|
|
|
|
|
client: &Client,
|
|
|
|
|
owner: &str,
|
|
|
|
|
name: &str,
|
|
|
|
|
organization: Option<&str>,
|
|
|
|
|
new_name: Option<&str>,
|
|
|
|
|
) -> Result<Repo> {
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/forks");
|
|
|
|
|
client
|
|
|
|
|
.json(
|
|
|
|
|
Method::POST,
|
|
|
|
|
&path,
|
|
|
|
|
&[],
|
|
|
|
|
Some(&ForkBody {
|
|
|
|
|
organization,
|
|
|
|
|
name: new_name,
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn rename(client: &Client, owner: &str, name: &str, new_name: &str) -> Result<()> {
|
|
|
|
|
// Forgejo: PATCH /repos/{owner}/{name} with `name` field.
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}");
|
|
|
|
|
#[derive(Serialize)]
|
|
|
|
|
struct Body<'a> {
|
|
|
|
|
name: &'a str,
|
|
|
|
|
}
|
|
|
|
|
let res = client
|
|
|
|
|
.request(
|
|
|
|
|
Method::PATCH,
|
|
|
|
|
&path,
|
|
|
|
|
&[],
|
|
|
|
|
Some(&serde_json::to_value(Body { name: new_name })?),
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
res.error_for_status()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
|
|
pub struct MergeUpstream<'a> {
|
|
|
|
|
pub branch: &'a str,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn sync_with_upstream(
|
|
|
|
|
client: &Client,
|
|
|
|
|
owner: &str,
|
|
|
|
|
name: &str,
|
|
|
|
|
branch: &str,
|
|
|
|
|
) -> Result<()> {
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/merge-upstream");
|
|
|
|
|
let res = client
|
|
|
|
|
.request(
|
|
|
|
|
Method::POST,
|
|
|
|
|
&path,
|
|
|
|
|
&[],
|
|
|
|
|
Some(&serde_json::to_value(MergeUpstream { branch })?),
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
res.error_for_status()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct Branch {
|
|
|
|
|
pub name: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub protected: bool,
|
|
|
|
|
pub commit: BranchCommit,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct BranchCommit {
|
|
|
|
|
pub id: String,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub message: String,
|
|
|
|
|
}
|
|
|
|
|
|
batch: release, label, workflow, search, browse, status, org, keys, alias, config, extension, gist
* New top-level groups, each with full CRUD where the API supports it:
- release: list/view/create/edit/delete/upload/download
- label: list/create/edit/delete
- run: workflow runs (list/view/rerun/cancel)
- secret + variable: Actions secrets/vars (list/set/delete)
- search: cross-cutting (repos/issues/prs/users)
- browse: open repo/path on the web
- status: notifications inbox + mark-all-read
- org: list/view/teams
- ssh-key, gpg-key: list/add/delete on your account
- alias: user-defined shortcuts (e.g. `fj alias set co "pr checkout"`)
- config: local prefs (editor, pager, browser, etc.)
- extension: discover and run `fj-<name>` plugin binaries on PATH
- gist: thin wrapper over `gist-*` repos
* main.rs now expands aliases before clap and dispatches to plugins for
unknown subcommands (matching gh).
* New API modules: release, label, notification, search, org, workflow,
with the corresponding strongly-typed wrappers.
* Release asset upload uses reqwest multipart (feature flag added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:29:31 +00:00
|
|
|
pub async fn list_branches(client: &Client, owner: &str, name: &str) -> Result<Vec<Branch>> {
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/branches");
|
|
|
|
|
client.json(Method::GET, &path, &[], None::<&()>).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct Topic {
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub topics: Vec<String>,
|
|
|
|
|
}
|
|
|
|
|
|
batch: release, label, workflow, search, browse, status, org, keys, alias, config, extension, gist
* New top-level groups, each with full CRUD where the API supports it:
- release: list/view/create/edit/delete/upload/download
- label: list/create/edit/delete
- run: workflow runs (list/view/rerun/cancel)
- secret + variable: Actions secrets/vars (list/set/delete)
- search: cross-cutting (repos/issues/prs/users)
- browse: open repo/path on the web
- status: notifications inbox + mark-all-read
- org: list/view/teams
- ssh-key, gpg-key: list/add/delete on your account
- alias: user-defined shortcuts (e.g. `fj alias set co "pr checkout"`)
- config: local prefs (editor, pager, browser, etc.)
- extension: discover and run `fj-<name>` plugin binaries on PATH
- gist: thin wrapper over `gist-*` repos
* main.rs now expands aliases before clap and dispatches to plugins for
unknown subcommands (matching gh).
* New API modules: release, label, notification, search, org, workflow,
with the corresponding strongly-typed wrappers.
* Release asset upload uses reqwest multipart (feature flag added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:29:31 +00:00
|
|
|
pub async fn list_topics(client: &Client, owner: &str, name: &str) -> Result<Vec<String>> {
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/topics");
|
|
|
|
|
let t: Topic = client.json(Method::GET, &path, &[], None::<&()>).await?;
|
|
|
|
|
Ok(t.topics)
|
|
|
|
|
}
|
|
|
|
|
|
batch: release, label, workflow, search, browse, status, org, keys, alias, config, extension, gist
* New top-level groups, each with full CRUD where the API supports it:
- release: list/view/create/edit/delete/upload/download
- label: list/create/edit/delete
- run: workflow runs (list/view/rerun/cancel)
- secret + variable: Actions secrets/vars (list/set/delete)
- search: cross-cutting (repos/issues/prs/users)
- browse: open repo/path on the web
- status: notifications inbox + mark-all-read
- org: list/view/teams
- ssh-key, gpg-key: list/add/delete on your account
- alias: user-defined shortcuts (e.g. `fj alias set co "pr checkout"`)
- config: local prefs (editor, pager, browser, etc.)
- extension: discover and run `fj-<name>` plugin binaries on PATH
- gist: thin wrapper over `gist-*` repos
* main.rs now expands aliases before clap and dispatches to plugins for
unknown subcommands (matching gh).
* New API modules: release, label, notification, search, org, workflow,
with the corresponding strongly-typed wrappers.
* Release asset upload uses reqwest multipart (feature flag added).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:29:31 +00:00
|
|
|
pub async fn set_topics(client: &Client, owner: &str, name: &str, topics: &[String]) -> Result<()> {
|
expand: repo auto-detect, --web, editor, PR diff/checks/ready/review/status, repo lifecycle, api headers/paginate
* CI: pre-push hook (fmt, clippy -D warnings, test, release build) plus
opt-in FJ_E2E=1 smoke. Install via scripts/install-hooks.sh.
* Repo auto-detection from git remote: -R/--repo becomes optional on all
repo-scoped subcommands. Detection prefers `upstream` then `origin`,
honors --host, and parses https/ssh/scp-style URLs.
* `--web` flag wired to every list/view command (open in default browser).
* `$EDITOR` integration for issue/pr create + comment + edit (omit
`--body` to launch your editor; `-` keeps stdin).
* PR: new `diff`, `commits`, `files`, `checks`, `ready`, `review`,
`edit`, `status`, `reopen` subcommands. `view --comments` now shows
reviews too.
* Issue: `edit` and `develop` (creates a branch for the issue).
* Repo: `fork`, `sync`, `edit`, `rename`, `archive`, `unarchive`,
`delete` (with slug-confirmation), `branches`, `topics`.
* `fj api` gains `-H` headers, `--paginate` (follows Link rel=next),
`--include` (response headers), `--silent`. The jq-ish projector now
supports `[N]`/`[-N]` brackets and `|` pipes.
* MSRV bumped to 1.82 (uses `is_none_or`).
* 46 unit tests covering pure logic: hosts CRUD, remote URL parsing,
link header parser, jq projection, branch label fallback, slugify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 15:22:40 +00:00
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/topics");
|
|
|
|
|
let res = client
|
|
|
|
|
.request(
|
|
|
|
|
Method::PUT,
|
|
|
|
|
&path,
|
|
|
|
|
&[],
|
|
|
|
|
Some(&serde_json::json!({ "topics": topics })),
|
|
|
|
|
)
|
|
|
|
|
.await?;
|
|
|
|
|
res.error_for_status()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
2026-05-13 15:37:20 +00:00
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Serialize)]
|
|
|
|
|
pub struct Migrate<'a> {
|
|
|
|
|
pub clone_addr: &'a str,
|
|
|
|
|
pub repo_name: &'a str,
|
|
|
|
|
/// User or org login that will own the migrated repo.
|
|
|
|
|
pub repo_owner: &'a str,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub mirror: bool,
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub private: bool,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub description: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub auth_username: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub auth_password: Option<&'a str>,
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub auth_token: Option<&'a str>,
|
|
|
|
|
/// Service: "git", "github", "gitea", "gitlab", "gogs", "onedev", ...
|
|
|
|
|
pub service: &'a str,
|
|
|
|
|
/// For pull-mirror: how often to refresh from the source.
|
|
|
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
|
|
|
pub mirror_interval: Option<&'a str>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn migrate(client: &Client, body: &Migrate<'_>) -> Result<Repo> {
|
|
|
|
|
client
|
|
|
|
|
.json(Method::POST, "/api/v1/repos/migrate", &[], Some(body))
|
|
|
|
|
.await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub async fn mirror_sync(client: &Client, owner: &str, name: &str) -> Result<()> {
|
|
|
|
|
let path = format!("/api/v1/repos/{owner}/{name}/mirror-sync");
|
|
|
|
|
let res = client.request(Method::POST, &path, &[], None).await?;
|
|
|
|
|
res.error_for_status()?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|