add hive-forge-tools: shell wrappers for common forge API operations
This commit is contained in:
parent
d348ce885f
commit
0a4cde88b0
2 changed files with 276 additions and 0 deletions
272
nix/packages/hive-forge-tools.nix
Normal file
272
nix/packages/hive-forge-tools.nix
Normal file
|
|
@ -0,0 +1,272 @@
|
|||
{ pkgs, lib }:
|
||||
# Small shell wrappers that replace ad-hoc curl pipelines for
|
||||
# common Forgejo API operations. Every script reads credentials
|
||||
# from the environment:
|
||||
#
|
||||
# HIVE_FORGE_URL -- base URL, e.g. http://localhost:3000
|
||||
# HIVE_FORGE_REPO -- default repo, e.g. hyperhive/hyperhive
|
||||
# HYPERHIVE_STATE_DIR -- state dir; forge-token lives here
|
||||
#
|
||||
# All scripts exit non-zero on HTTP errors and print diagnostics
|
||||
# to stderr.
|
||||
let
|
||||
# Common prologue injected into every script: reads token and
|
||||
# defines forge_get / forge_post / forge_patch / forge_delete.
|
||||
commonPrologue = ''
|
||||
: "''${HIVE_FORGE_URL:=http://localhost:3000}"
|
||||
: "''${HIVE_FORGE_REPO:=hyperhive/hyperhive}"
|
||||
_state_dir="''${HYPERHIVE_STATE_DIR:-}"
|
||||
_token_file="''${_state_dir:+$_state_dir/}forge-token"
|
||||
if [ ! -f "$_token_file" ]; then
|
||||
echo "hive-forge: no forge-token at $_token_file" >&2
|
||||
exit 1
|
||||
fi
|
||||
_token=$(cat "$_token_file")
|
||||
FORGE_API="$HIVE_FORGE_URL/api/v1"
|
||||
|
||||
forge_get() {
|
||||
${pkgs.curl}/bin/curl -sf \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Accept: application/json" \
|
||||
"$1"
|
||||
}
|
||||
|
||||
forge_post() {
|
||||
${pkgs.curl}/bin/curl -sf -X POST \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-d "$2" "$1"
|
||||
}
|
||||
|
||||
forge_patch() {
|
||||
${pkgs.curl}/bin/curl -sf -X PATCH \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-d "$2" "$1"
|
||||
}
|
||||
|
||||
forge_delete() {
|
||||
local _url="$1"
|
||||
local _body="''${2:-}"
|
||||
if [ -n "$_body" ]; then
|
||||
${pkgs.curl}/bin/curl -sf -X DELETE \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Accept: application/json" \
|
||||
-d "$_body" "$_url"
|
||||
else
|
||||
${pkgs.curl}/bin/curl -sf -X DELETE \
|
||||
-H "Authorization: token $_token" \
|
||||
-H "Accept: application/json" \
|
||||
"$_url"
|
||||
fi
|
||||
}
|
||||
'';
|
||||
|
||||
mkScript = name: text:
|
||||
pkgs.writeShellApplication {
|
||||
inherit name;
|
||||
runtimeInputs = [ pkgs.curl pkgs.jq ];
|
||||
text = commonPrologue + "\n" + text;
|
||||
};
|
||||
in
|
||||
pkgs.symlinkJoin {
|
||||
name = "hive-forge-tools";
|
||||
paths = [
|
||||
|
||||
# hive-forge-view <number> [repo]
|
||||
# Dump title + body + all comments for an issue or PR.
|
||||
(mkScript "hive-forge-view" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-view <number> [repo]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
|
||||
_issue=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n")
|
||||
_title=$(printf '%s' "$_issue" | jq -r '.title')
|
||||
_body=$(printf '%s' "$_issue" | jq -r '.body // ""')
|
||||
_state=$(printf '%s' "$_issue" | jq -r '.state')
|
||||
_user=$(printf '%s' "$_issue" | jq -r '.user.login')
|
||||
_is_pr=$(printf '%s' "$_issue" | jq -r '.pull_request != null')
|
||||
|
||||
_kind="issue"
|
||||
if [ "$_is_pr" = "true" ]; then _kind="PR"; fi
|
||||
|
||||
printf '# %s #%s [%s] by %s\n' "$_kind" "$_n" "$_state" "$_user"
|
||||
printf '%s\n' "$_title"
|
||||
if [ -n "$_body" ]; then
|
||||
printf '\n%s\n' "$_body"
|
||||
fi
|
||||
|
||||
_comments=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n/comments?limit=50")
|
||||
_count=$(printf '%s' "$_comments" | jq 'length')
|
||||
if [ "$_count" -gt 0 ]; then
|
||||
printf '\n---\n## Comments (%s)\n\n' "$_count"
|
||||
printf '%s' "$_comments" | jq -r '.[] | "**\(.user.login)**: \(.body)\n"'
|
||||
fi
|
||||
'')
|
||||
|
||||
# hive-forge-issue <number> [repo]
|
||||
# Print key fields of an issue as JSON.
|
||||
(mkScript "hive-forge-issue" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-issue <number> [repo]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
forge_get "$FORGE_API/repos/$_repo/issues/$_n" \
|
||||
| jq '{number,title,state,user:.user.login,assignees:[.assignees[]?.login],labels:[.labels[]?.name],body}'
|
||||
'')
|
||||
|
||||
# hive-forge-pr <number> [repo]
|
||||
# Print key fields of a PR as JSON.
|
||||
(mkScript "hive-forge-pr" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-pr <number> [repo]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
forge_get "$FORGE_API/repos/$_repo/pulls/$_n" \
|
||||
| jq '{number,title,state,merged,user:.user.login,head_sha:.head.sha,head_branch:.head.label,base_branch:.base.label}'
|
||||
'')
|
||||
|
||||
# hive-forge-comment <number> [body | -f <file> | - for stdin]
|
||||
# Post a comment on an issue or PR. Body can be passed as:
|
||||
# - trailing args (joined with spaces)
|
||||
# - -f <file> to read from a file
|
||||
# - - or no args to read from stdin
|
||||
(mkScript "hive-forge-comment" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-comment <number> [body | -f <file> | -]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"; shift
|
||||
|
||||
if [ $# -eq 0 ] || [ "''${1:-}" = "-" ]; then
|
||||
_body=$(cat)
|
||||
elif [ "''${1:-}" = "-f" ]; then
|
||||
_body=$(cat "$2")
|
||||
else
|
||||
_body="$*"
|
||||
fi
|
||||
|
||||
_payload=$(jq -n --arg body "$_body" '{body:$body}')
|
||||
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/comments" "$_payload" \
|
||||
| jq '{id,url:.html_url}'
|
||||
'')
|
||||
|
||||
# hive-forge-assign <number> <user> [--remove]
|
||||
# Assign or unassign a user on an issue or PR.
|
||||
(mkScript "hive-forge-assign" ''
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "usage: hive-forge-assign <number> <user> [--remove]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"; _user="$2"; _remove="''${3:-}"
|
||||
|
||||
_payload=$(jq -n --arg u "$_user" '{assignees:[$u]}')
|
||||
if [ "$_remove" = "--remove" ]; then
|
||||
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
||||
| jq '{number,assignees:[.assignees[]?.login]}'
|
||||
else
|
||||
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
||||
| jq '{number,assignees:[.assignees[]?.login]}'
|
||||
fi
|
||||
'')
|
||||
|
||||
# hive-forge-close <number>
|
||||
# Close an issue or PR.
|
||||
(mkScript "hive-forge-close" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-close <number>" >&2; exit 1
|
||||
fi
|
||||
forge_patch "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$1" '{"state":"closed"}' \
|
||||
| jq '{number,state}'
|
||||
'')
|
||||
|
||||
# hive-forge-labels <number> [list | add <label...> | remove <label...>]
|
||||
# Manage labels on an issue or PR. Default action is list.
|
||||
(mkScript "hive-forge-labels" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-labels <number> [list|add|remove] [labels...]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"; _action="''${2:-list}"
|
||||
shift; shift || true
|
||||
|
||||
case "$_action" in
|
||||
list)
|
||||
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" \
|
||||
| jq '[.[].name]'
|
||||
;;
|
||||
add)
|
||||
_all=$(forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/labels?limit=100")
|
||||
_ids=$(printf '%s' "$_all" | jq -r --args '[.[] | select(.name == ($ARGS.positional[])) | .id]' -- "$@")
|
||||
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" "{\"labels\":$_ids}" \
|
||||
| jq '[.[].name]'
|
||||
;;
|
||||
remove)
|
||||
_all=$(forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/labels?limit=100")
|
||||
for _label in "$@"; do
|
||||
_id=$(printf '%s' "$_all" | jq -r --arg n "$_label" '.[] | select(.name==$n) | .id')
|
||||
if [ -n "$_id" ]; then
|
||||
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels/$_id"
|
||||
fi
|
||||
done
|
||||
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" \
|
||||
| jq '[.[].name]'
|
||||
;;
|
||||
*)
|
||||
echo "unknown action: $_action (use list, add, remove)" >&2; exit 1
|
||||
;;
|
||||
esac
|
||||
'')
|
||||
|
||||
# hive-forge-pr-reviews <number> [repo]
|
||||
# List reviews on a PR with id/state/user/body.
|
||||
(mkScript "hive-forge-pr-reviews" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-pr-reviews <number> [repo]" >&2; exit 1
|
||||
fi
|
||||
_n="$1"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
forge_get "$FORGE_API/repos/$_repo/pulls/$_n/reviews" \
|
||||
| jq '[.[] | {id,state,user:.user.login,body,comments_count}]'
|
||||
'')
|
||||
|
||||
# hive-forge-branches [pattern] [repo]
|
||||
# List branches, optionally filtered by a grep pattern.
|
||||
(mkScript "hive-forge-branches" ''
|
||||
_pattern="''${1:-}"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
_result=$(forge_get "$FORGE_API/repos/$_repo/branches?limit=100" \
|
||||
| jq -r '.[].name')
|
||||
if [ -n "$_pattern" ]; then
|
||||
printf '%s\n' "$_result" | grep "$_pattern" || true
|
||||
else
|
||||
printf '%s\n' "$_result"
|
||||
fi
|
||||
'')
|
||||
|
||||
# hive-forge-tree-sha <ref> [repo]
|
||||
# Print the tree SHA of the commit at the given branch or commit SHA.
|
||||
(mkScript "hive-forge-tree-sha" ''
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: hive-forge-tree-sha <ref> [repo]" >&2; exit 1
|
||||
fi
|
||||
_ref="$1"
|
||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
||||
# Resolve branch -> commit SHA first, then get the tree SHA
|
||||
# from the git commit object (tree.sha field).
|
||||
_commit_sha=$(forge_get "$FORGE_API/repos/$_repo/branches/$_ref" 2>/dev/null \
|
||||
| jq -r '.commit.id // empty') || true
|
||||
if [ -z "$_commit_sha" ]; then
|
||||
# Assume it's already a commit SHA
|
||||
_commit_sha="$_ref"
|
||||
fi
|
||||
forge_get "$FORGE_API/repos/$_repo/git/commits/$_commit_sha" \
|
||||
| jq -r '.tree.sha // .sha'
|
||||
'')
|
||||
|
||||
];
|
||||
}
|
||||
|
|
@ -273,6 +273,10 @@
|
|||
jq
|
||||
# curl: HTTP client for forge REST API and other web requests.
|
||||
curl
|
||||
# hive-forge-*: shell wrappers around the Forgejo REST API
|
||||
# (hive-forge-view, hive-forge-pr, hive-forge-comment, etc.)
|
||||
# that replace ad-hoc curl pipelines in agent turns.
|
||||
(pkgs.callPackage ../packages/hive-forge-tools.nix { })
|
||||
];
|
||||
|
||||
# One-shot: write tea's config.yml from the seeded forge token so
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue