mirror of
https://github.com/nearai/ironclaw.git
synced 2026-09-02 23:56:24 +08:00
feat: add fork action to github tool (#2139)
feat: add fork_repo action to GitHub WASM tool Adds fork_repo action with full input validation, optional organization/name/default_branch_only params. CI failures are pre-existing (RUSTSEC-2026-0098 in rustls-webpki transitive dep, unrelated to this PR).
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "github-tool"
|
||||
version = "0.2.2"
|
||||
version = "0.2.3"
|
||||
edition = "2021"
|
||||
description = "GitHub integration tool for IronClaw (WASM component)"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
@@ -8,6 +8,7 @@ search, branches, file reads and writes, releases, and workflows.
|
||||
- **Repositories** - Get repo details, list user repos, create repositories
|
||||
- **Search** - Search repositories, code, and issues/PRs
|
||||
- **Branches** - List branches and create new branches from an existing ref
|
||||
- **Fork** - Fork repositories
|
||||
- **Issues** - List/create/get issues, list/add issue comments
|
||||
- **Pull Requests** - List/create/get PRs, review files, create reviews, list/reply review comments, merge PRs
|
||||
- **File Content** - Read files and create/update/delete repository files
|
||||
@@ -286,6 +287,21 @@ When updating an existing file, include the current blob `sha`.
|
||||
}
|
||||
```
|
||||
|
||||
### Fork Repository
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "fork_repo",
|
||||
"owner": "nearai",
|
||||
"repo": "ironclaw",
|
||||
"organization": "my-org",
|
||||
"name": "ironclaw-fork",
|
||||
"default_branch_only": true
|
||||
}
|
||||
```
|
||||
|
||||
`organization`, `name`, and `default_branch_only` are optional. Omit `organization` to fork into the authenticated user's account.
|
||||
|
||||
### Create Branch
|
||||
|
||||
```json
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.2.2",
|
||||
"version": "0.2.3",
|
||||
"wit_version": "0.3.0",
|
||||
"description": "Manage GitHub repositories, issues, pull requests, search, branches, file reads and writes, releases, and workflows.",
|
||||
"discovery_summary": {
|
||||
@@ -16,7 +16,7 @@
|
||||
"notes": [
|
||||
"Use `tool_info(name: \"github\", detail: \"schema\")` for the full action schema before guessing fields.",
|
||||
"Supported families: repositories, issues, pull requests, reviews/comments, search, branches, code reads, file writes, releases, workflow dispatch/runs, and webhook normalization.",
|
||||
"Not supported yet: forks, labels, milestones, projects, org/team admin, GraphQL, release asset uploads, and repository deletion."
|
||||
"Not supported yet: labels, milestones, projects, org/team admin, GraphQL, release asset uploads, and repository deletion."
|
||||
],
|
||||
"examples": [
|
||||
{
|
||||
|
||||
@@ -430,6 +430,14 @@ enum GitHubAction {
|
||||
page: Option<u32>,
|
||||
limit: Option<u32>,
|
||||
},
|
||||
#[serde(rename = "fork_repo")]
|
||||
ForkRepo {
|
||||
owner: String,
|
||||
repo: String,
|
||||
organization: Option<String>,
|
||||
name: Option<String>,
|
||||
default_branch_only: Option<bool>,
|
||||
},
|
||||
#[serde(rename = "handle_webhook")]
|
||||
HandleWebhook { webhook: GitHubWebhookRequest },
|
||||
}
|
||||
@@ -752,6 +760,19 @@ fn execute_inner(params: &str) -> Result<String, String> {
|
||||
page,
|
||||
limit,
|
||||
} => get_workflow_runs(&owner, &repo, workflow_id.as_deref(), page, limit),
|
||||
GitHubAction::ForkRepo {
|
||||
owner,
|
||||
repo,
|
||||
organization,
|
||||
name,
|
||||
default_branch_only,
|
||||
} => fork_repo(
|
||||
&owner,
|
||||
&repo,
|
||||
organization.as_deref(),
|
||||
name.as_deref(),
|
||||
default_branch_only,
|
||||
),
|
||||
GitHubAction::HandleWebhook { webhook } => handle_webhook(webhook),
|
||||
}
|
||||
}
|
||||
@@ -926,6 +947,49 @@ fn create_repo(
|
||||
github_request("POST", &path, Some(req_body.to_string()))
|
||||
}
|
||||
|
||||
fn fork_repo(
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
organization: Option<&str>,
|
||||
name: Option<&str>,
|
||||
default_branch_only: Option<bool>,
|
||||
) -> Result<String, String> {
|
||||
if !validate_path_segment(owner) || !validate_path_segment(repo) {
|
||||
return Err("Invalid owner or repo name".into());
|
||||
}
|
||||
validate_input_length(owner, "owner")?;
|
||||
validate_input_length(repo, "repo")?;
|
||||
if let Some(org) = organization {
|
||||
validate_input_length(org, "organization")?;
|
||||
if !validate_path_segment(org) {
|
||||
return Err("Invalid org name".into());
|
||||
}
|
||||
}
|
||||
if let Some(n) = name {
|
||||
validate_input_length(n, "name")?;
|
||||
if !validate_path_segment(n) {
|
||||
return Err("Invalid fork name".into());
|
||||
}
|
||||
}
|
||||
|
||||
let encoded_owner = url_encode_path(owner);
|
||||
let encoded_repo = url_encode_path(repo);
|
||||
let path = format!("/repos/{}/{}/forks", encoded_owner, encoded_repo);
|
||||
|
||||
let mut req_body = serde_json::json!({});
|
||||
if let Some(org) = organization {
|
||||
req_body["organization"] = serde_json::json!(org);
|
||||
}
|
||||
if let Some(n) = name {
|
||||
req_body["name"] = serde_json::json!(n);
|
||||
}
|
||||
if let Some(only) = default_branch_only {
|
||||
req_body["default_branch_only"] = serde_json::json!(only);
|
||||
}
|
||||
|
||||
github_request("POST", &path, Some(req_body.to_string()))
|
||||
}
|
||||
|
||||
fn list_issues(
|
||||
owner: &str,
|
||||
repo: &str,
|
||||
@@ -2300,6 +2364,17 @@ const SCHEMA: &str = r#"{
|
||||
},
|
||||
"required": ["action", "owner", "repo"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": { "const": "fork_repo" },
|
||||
"owner": { "type": "string", "description": "Repository owner (user or org) to fork from" },
|
||||
"repo": { "type": "string", "description": "Repository name to fork" },
|
||||
"organization": { "type": "string", "description": "Optional organization to fork into; omit to fork into the authenticated user's account" },
|
||||
"name": { "type": "string", "description": "Optional name for the fork; defaults to the original repo name" },
|
||||
"default_branch_only": { "type": "boolean", "default": false, "description": "When true, only the default branch is copied into the fork" }
|
||||
},
|
||||
"required": ["action", "owner", "repo"]
|
||||
},
|
||||
{
|
||||
"properties": {
|
||||
"action": { "const": "handle_webhook" },
|
||||
@@ -2377,6 +2452,7 @@ mod tests {
|
||||
"create_release",
|
||||
"trigger_workflow",
|
||||
"get_workflow_runs",
|
||||
"fork_repo",
|
||||
"handle_webhook",
|
||||
]
|
||||
.into_iter()
|
||||
|
||||
Reference in New Issue
Block a user