hive-forge: single command with subverbs instead of per-verb scripts
This commit is contained in:
parent
0a4cde88b0
commit
a95ca22b49
2 changed files with 165 additions and 185 deletions
|
|
@ -1,18 +1,21 @@
|
||||||
{ pkgs, lib }:
|
{ pkgs, lib }:
|
||||||
# Small shell wrappers that replace ad-hoc curl pipelines for
|
# Single `hive-forge <verb>` CLI wrapping common Forgejo API operations.
|
||||||
# common Forgejo API operations. Every script reads credentials
|
# Reads credentials from the environment:
|
||||||
# from the environment:
|
|
||||||
#
|
#
|
||||||
# HIVE_FORGE_URL -- base URL, e.g. http://localhost:3000
|
# HIVE_FORGE_URL -- base URL, e.g. http://localhost:3000
|
||||||
# HIVE_FORGE_REPO -- default repo, e.g. hyperhive/hyperhive
|
# HIVE_FORGE_REPO -- default repo, e.g. hyperhive/hyperhive
|
||||||
# HYPERHIVE_STATE_DIR -- state dir; forge-token lives here
|
# HYPERHIVE_STATE_DIR -- state dir; forge-token lives here
|
||||||
#
|
#
|
||||||
# All scripts exit non-zero on HTTP errors and print diagnostics
|
# Usage: hive-forge <verb> [args...]
|
||||||
# to stderr.
|
# Verbs: view, issue, pr, comment, assign, close, labels, pr-reviews,
|
||||||
let
|
# branches, tree-sha
|
||||||
# Common prologue injected into every script: reads token and
|
pkgs.writeShellApplication {
|
||||||
# defines forge_get / forge_post / forge_patch / forge_delete.
|
name = "hive-forge";
|
||||||
commonPrologue = ''
|
runtimeInputs = [
|
||||||
|
pkgs.curl
|
||||||
|
pkgs.jq
|
||||||
|
];
|
||||||
|
text = ''
|
||||||
: "''${HIVE_FORGE_URL:=http://localhost:3000}"
|
: "''${HIVE_FORGE_URL:=http://localhost:3000}"
|
||||||
: "''${HIVE_FORGE_REPO:=hyperhive/hyperhive}"
|
: "''${HIVE_FORGE_REPO:=hyperhive/hyperhive}"
|
||||||
_state_dir="''${HYPERHIVE_STATE_DIR:-}"
|
_state_dir="''${HYPERHIVE_STATE_DIR:-}"
|
||||||
|
|
@ -30,7 +33,6 @@ let
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
"$1"
|
"$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
forge_post() {
|
forge_post() {
|
||||||
${pkgs.curl}/bin/curl -sf -X POST \
|
${pkgs.curl}/bin/curl -sf -X POST \
|
||||||
-H "Authorization: token $_token" \
|
-H "Authorization: token $_token" \
|
||||||
|
|
@ -38,7 +40,6 @@ let
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
-d "$2" "$1"
|
-d "$2" "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
forge_patch() {
|
forge_patch() {
|
||||||
${pkgs.curl}/bin/curl -sf -X PATCH \
|
${pkgs.curl}/bin/curl -sf -X PATCH \
|
||||||
-H "Authorization: token $_token" \
|
-H "Authorization: token $_token" \
|
||||||
|
|
@ -46,7 +47,6 @@ let
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
-d "$2" "$1"
|
-d "$2" "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
forge_delete() {
|
forge_delete() {
|
||||||
local _url="$1"
|
local _url="$1"
|
||||||
local _body="''${2:-}"
|
local _body="''${2:-}"
|
||||||
|
|
@ -63,87 +63,57 @@ let
|
||||||
"$_url"
|
"$_url"
|
||||||
fi
|
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}"
|
|
||||||
|
|
||||||
|
cmd_view() {
|
||||||
|
# view <number> [repo]
|
||||||
|
# Dump title + body + all comments for an issue or PR.
|
||||||
|
if [ $# -lt 1 ]; then echo "usage: hive-forge view <number> [repo]" >&2; exit 1; fi
|
||||||
|
local _n="$1" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
|
local _issue _title _body _state _user _is_pr _kind
|
||||||
_issue=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n")
|
_issue=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n")
|
||||||
_title=$(printf '%s' "$_issue" | jq -r '.title')
|
_title=$(printf '%s' "$_issue" | jq -r '.title')
|
||||||
_body=$(printf '%s' "$_issue" | jq -r '.body // ""')
|
_body=$(printf '%s' "$_issue" | jq -r '.body // ""')
|
||||||
_state=$(printf '%s' "$_issue" | jq -r '.state')
|
_state=$(printf '%s' "$_issue" | jq -r '.state')
|
||||||
_user=$(printf '%s' "$_issue" | jq -r '.user.login')
|
_user=$(printf '%s' "$_issue" | jq -r '.user.login')
|
||||||
_is_pr=$(printf '%s' "$_issue" | jq -r '.pull_request != null')
|
_is_pr=$(printf '%s' "$_issue" | jq -r '.pull_request != null')
|
||||||
|
|
||||||
_kind="issue"
|
_kind="issue"
|
||||||
if [ "$_is_pr" = "true" ]; then _kind="PR"; fi
|
if [ "$_is_pr" = "true" ]; then _kind="PR"; fi
|
||||||
|
|
||||||
printf '# %s #%s [%s] by %s\n' "$_kind" "$_n" "$_state" "$_user"
|
printf '# %s #%s [%s] by %s\n' "$_kind" "$_n" "$_state" "$_user"
|
||||||
printf '%s\n' "$_title"
|
printf '%s\n' "$_title"
|
||||||
if [ -n "$_body" ]; then
|
if [ -n "$_body" ]; then printf '\n%s\n' "$_body"; fi
|
||||||
printf '\n%s\n' "$_body"
|
local _comments _count
|
||||||
fi
|
|
||||||
|
|
||||||
_comments=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n/comments?limit=50")
|
_comments=$(forge_get "$FORGE_API/repos/$_repo/issues/$_n/comments?limit=50")
|
||||||
_count=$(printf '%s' "$_comments" | jq 'length')
|
_count=$(printf '%s' "$_comments" | jq 'length')
|
||||||
if [ "$_count" -gt 0 ]; then
|
if [ "$_count" -gt 0 ]; then
|
||||||
printf '\n---\n## Comments (%s)\n\n' "$_count"
|
printf '\n---\n## Comments (%s)\n\n' "$_count"
|
||||||
printf '%s' "$_comments" | jq -r '.[] | "**\(.user.login)**: \(.body)\n"'
|
printf '%s' "$_comments" | jq -r '.[] | "**\(.user.login)**: \(.body)\n"'
|
||||||
fi
|
fi
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-issue <number> [repo]
|
cmd_issue() {
|
||||||
# Print key fields of an issue as JSON.
|
# issue <number> [repo]
|
||||||
(mkScript "hive-forge-issue" ''
|
# Print key fields of an issue as JSON.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge issue <number> [repo]" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-issue <number> [repo]" >&2; exit 1
|
local _n="$1" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
fi
|
|
||||||
_n="$1"
|
|
||||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
|
||||||
forge_get "$FORGE_API/repos/$_repo/issues/$_n" \
|
forge_get "$FORGE_API/repos/$_repo/issues/$_n" \
|
||||||
| jq '{number,title,state,user:.user.login,assignees:[.assignees[]?.login],labels:[.labels[]?.name],body}'
|
| jq '{number,title,state,user:.user.login,assignees:[.assignees[]?.login],labels:[.labels[]?.name],body}'
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-pr <number> [repo]
|
cmd_pr() {
|
||||||
# Print key fields of a PR as JSON.
|
# pr <number> [repo]
|
||||||
(mkScript "hive-forge-pr" ''
|
# Print key fields of a PR as JSON.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge pr <number> [repo]" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-pr <number> [repo]" >&2; exit 1
|
local _n="$1" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
fi
|
|
||||||
_n="$1"
|
|
||||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
|
||||||
forge_get "$FORGE_API/repos/$_repo/pulls/$_n" \
|
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}'
|
| 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
|
|
||||||
|
|
||||||
|
cmd_comment() {
|
||||||
|
# comment <number> [body | -f <file> | - for stdin]
|
||||||
|
# Post a comment on an issue or PR.
|
||||||
|
if [ $# -lt 1 ]; then echo "usage: hive-forge comment <number> [body | -f <file> | -]" >&2; exit 1; fi
|
||||||
|
local _n="$1"; shift
|
||||||
|
local _body
|
||||||
if [ $# -eq 0 ] || [ "''${1:-}" = "-" ]; then
|
if [ $# -eq 0 ] || [ "''${1:-}" = "-" ]; then
|
||||||
_body=$(cat)
|
_body=$(cat)
|
||||||
elif [ "''${1:-}" = "-f" ]; then
|
elif [ "''${1:-}" = "-f" ]; then
|
||||||
|
|
@ -151,20 +121,18 @@ pkgs.symlinkJoin {
|
||||||
else
|
else
|
||||||
_body="$*"
|
_body="$*"
|
||||||
fi
|
fi
|
||||||
|
local _payload
|
||||||
_payload=$(jq -n --arg body "$_body" '{body:$body}')
|
_payload=$(jq -n --arg body "$_body" '{body:$body}')
|
||||||
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/comments" "$_payload" \
|
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/comments" "$_payload" \
|
||||||
| jq '{id,url:.html_url}'
|
| 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:-}"
|
|
||||||
|
|
||||||
|
cmd_assign() {
|
||||||
|
# assign <number> <user> [--remove]
|
||||||
|
# Assign or unassign a user on an issue or PR.
|
||||||
|
if [ $# -lt 2 ]; then echo "usage: hive-forge assign <number> <user> [--remove]" >&2; exit 1; fi
|
||||||
|
local _n="$1" _user="$2" _remove="''${3:-}"
|
||||||
|
local _payload
|
||||||
_payload=$(jq -n --arg u "$_user" '{assignees:[$u]}')
|
_payload=$(jq -n --arg u "$_user" '{assignees:[$u]}')
|
||||||
if [ "$_remove" = "--remove" ]; then
|
if [ "$_remove" = "--remove" ]; then
|
||||||
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
||||||
|
|
@ -173,31 +141,26 @@ pkgs.symlinkJoin {
|
||||||
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
forge_post "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/assignees" "$_payload" \
|
||||||
| jq '{number,assignees:[.assignees[]?.login]}'
|
| jq '{number,assignees:[.assignees[]?.login]}'
|
||||||
fi
|
fi
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-close <number>
|
cmd_close() {
|
||||||
# Close an issue or PR.
|
# close <number>
|
||||||
(mkScript "hive-forge-close" ''
|
# Close an issue or PR.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge close <number>" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-close <number>" >&2; exit 1
|
|
||||||
fi
|
|
||||||
forge_patch "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$1" '{"state":"closed"}' \
|
forge_patch "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$1" '{"state":"closed"}' \
|
||||||
| jq '{number,state}'
|
| jq '{number,state}'
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-labels <number> [list | add <label...> | remove <label...>]
|
cmd_labels() {
|
||||||
# Manage labels on an issue or PR. Default action is list.
|
# labels <number> [list|add|remove] [labels...]
|
||||||
(mkScript "hive-forge-labels" ''
|
# Manage labels on an issue or PR. Default action is list.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge labels <number> [list|add|remove] [labels...]" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-labels <number> [list|add|remove] [labels...]" >&2; exit 1
|
local _n="$1" _action="''${2:-list}"
|
||||||
fi
|
|
||||||
_n="$1"; _action="''${2:-list}"
|
|
||||||
shift; shift || true
|
shift; shift || true
|
||||||
|
local _all _ids _id _label
|
||||||
case "$_action" in
|
case "$_action" in
|
||||||
list)
|
list)
|
||||||
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" \
|
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" | jq '[.[].name]'
|
||||||
| jq '[.[].name]'
|
|
||||||
;;
|
;;
|
||||||
add)
|
add)
|
||||||
_all=$(forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/labels?limit=100")
|
_all=$(forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/labels?limit=100")
|
||||||
|
|
@ -213,60 +176,75 @@ pkgs.symlinkJoin {
|
||||||
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels/$_id"
|
forge_delete "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels/$_id"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" \
|
forge_get "$FORGE_API/repos/$HIVE_FORGE_REPO/issues/$_n/labels" | jq '[.[].name]'
|
||||||
| jq '[.[].name]'
|
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "unknown action: $_action (use list, add, remove)" >&2; exit 1
|
echo "unknown action: $_action (use list, add, remove)" >&2; exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-pr-reviews <number> [repo]
|
cmd_pr_reviews() {
|
||||||
# List reviews on a PR with id/state/user/body.
|
# pr-reviews <number> [repo]
|
||||||
(mkScript "hive-forge-pr-reviews" ''
|
# List reviews on a PR with id/state/user/body.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge pr-reviews <number> [repo]" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-pr-reviews <number> [repo]" >&2; exit 1
|
local _n="$1" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
fi
|
|
||||||
_n="$1"
|
|
||||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
|
||||||
forge_get "$FORGE_API/repos/$_repo/pulls/$_n/reviews" \
|
forge_get "$FORGE_API/repos/$_repo/pulls/$_n/reviews" \
|
||||||
| jq '[.[] | {id,state,user:.user.login,body,comments_count}]'
|
| jq '[.[] | {id,state,user:.user.login,body,comments_count}]'
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-branches [pattern] [repo]
|
cmd_branches() {
|
||||||
# List branches, optionally filtered by a grep pattern.
|
# branches [pattern] [repo]
|
||||||
(mkScript "hive-forge-branches" ''
|
# List branches, optionally filtered by a grep pattern.
|
||||||
_pattern="''${1:-}"
|
local _pattern="''${1:-}" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
_repo="''${2:-$HIVE_FORGE_REPO}"
|
local _result
|
||||||
_result=$(forge_get "$FORGE_API/repos/$_repo/branches?limit=100" \
|
_result=$(forge_get "$FORGE_API/repos/$_repo/branches?limit=100" | jq -r '.[].name')
|
||||||
| jq -r '.[].name')
|
|
||||||
if [ -n "$_pattern" ]; then
|
if [ -n "$_pattern" ]; then
|
||||||
printf '%s\n' "$_result" | grep "$_pattern" || true
|
printf '%s\n' "$_result" | grep "$_pattern" || true
|
||||||
else
|
else
|
||||||
printf '%s\n' "$_result"
|
printf '%s\n' "$_result"
|
||||||
fi
|
fi
|
||||||
'')
|
}
|
||||||
|
|
||||||
# hive-forge-tree-sha <ref> [repo]
|
cmd_tree_sha() {
|
||||||
# Print the tree SHA of the commit at the given branch or commit SHA.
|
# tree-sha <ref> [repo]
|
||||||
(mkScript "hive-forge-tree-sha" ''
|
# Print the tree SHA of the commit at the given branch or commit SHA.
|
||||||
if [ $# -lt 1 ]; then
|
if [ $# -lt 1 ]; then echo "usage: hive-forge tree-sha <ref> [repo]" >&2; exit 1; fi
|
||||||
echo "usage: hive-forge-tree-sha <ref> [repo]" >&2; exit 1
|
local _ref="$1" _repo="''${2:-$HIVE_FORGE_REPO}"
|
||||||
fi
|
local _commit_sha
|
||||||
_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 \
|
_commit_sha=$(forge_get "$FORGE_API/repos/$_repo/branches/$_ref" 2>/dev/null \
|
||||||
| jq -r '.commit.id // empty') || true
|
| jq -r '.commit.id // empty') || true
|
||||||
if [ -z "$_commit_sha" ]; then
|
if [ -z "$_commit_sha" ]; then
|
||||||
# Assume it's already a commit SHA
|
|
||||||
_commit_sha="$_ref"
|
_commit_sha="$_ref"
|
||||||
fi
|
fi
|
||||||
forge_get "$FORGE_API/repos/$_repo/git/commits/$_commit_sha" \
|
forge_get "$FORGE_API/repos/$_repo/git/commits/$_commit_sha" \
|
||||||
| jq -r '.tree.sha // .sha'
|
| jq -r '.tree.sha // .sha'
|
||||||
'')
|
}
|
||||||
|
|
||||||
];
|
VERB="''${1:-}"
|
||||||
|
if [ -z "$VERB" ]; then
|
||||||
|
echo "usage: hive-forge <verb> [args...]" >&2
|
||||||
|
echo "verbs: view, issue, pr, comment, assign, close, labels, pr-reviews, branches, tree-sha" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$VERB" in
|
||||||
|
view) cmd_view "$@" ;;
|
||||||
|
issue) cmd_issue "$@" ;;
|
||||||
|
pr) cmd_pr "$@" ;;
|
||||||
|
comment) cmd_comment "$@" ;;
|
||||||
|
assign) cmd_assign "$@" ;;
|
||||||
|
close) cmd_close "$@" ;;
|
||||||
|
labels) cmd_labels "$@" ;;
|
||||||
|
pr-reviews) cmd_pr_reviews "$@" ;;
|
||||||
|
branches) cmd_branches "$@" ;;
|
||||||
|
tree-sha) cmd_tree_sha "$@" ;;
|
||||||
|
*)
|
||||||
|
echo "hive-forge: unknown verb '$VERB'" >&2
|
||||||
|
echo "verbs: view, issue, pr, comment, assign, close, labels, pr-reviews, branches, tree-sha" >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,10 @@
|
||||||
# `nix flake` constantly (devshells, ad-hoc evals, fetching their
|
# `nix flake` constantly (devshells, ad-hoc evals, fetching their
|
||||||
# own MCP-server flakes); without this they hit the "experimental
|
# own MCP-server flakes); without this they hit the "experimental
|
||||||
# feature not enabled" wall on the first try.
|
# feature not enabled" wall on the first try.
|
||||||
nix.settings.experimental-features = [ "nix-command" "flakes" ];
|
nix.settings.experimental-features = [
|
||||||
|
"nix-command"
|
||||||
|
"flakes"
|
||||||
|
];
|
||||||
|
|
||||||
# `claude-code` is unfree. Each per-agent container's nixosConfiguration
|
# `claude-code` is unfree. Each per-agent container's nixosConfiguration
|
||||||
# evaluates its own `nixpkgs` instance, so the operator's host-level
|
# evaluates its own `nixpkgs` instance, so the operator's host-level
|
||||||
|
|
@ -273,9 +276,8 @@
|
||||||
jq
|
jq
|
||||||
# curl: HTTP client for forge REST API and other web requests.
|
# curl: HTTP client for forge REST API and other web requests.
|
||||||
curl
|
curl
|
||||||
# hive-forge-*: shell wrappers around the Forgejo REST API
|
# hive-forge <verb>: CLI wrapping common Forgejo REST API operations
|
||||||
# (hive-forge-view, hive-forge-pr, hive-forge-comment, etc.)
|
# (view, pr, issue, comment, assign, close, labels, branches, etc.)
|
||||||
# that replace ad-hoc curl pipelines in agent turns.
|
|
||||||
(pkgs.callPackage ../packages/hive-forge-tools.nix { })
|
(pkgs.callPackage ../packages/hive-forge-tools.nix { })
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -298,57 +300,57 @@
|
||||||
pkgs.coreutils
|
pkgs.coreutils
|
||||||
];
|
];
|
||||||
script = ''
|
script = ''
|
||||||
# No `set -e`: any subshell failure must not propagate.
|
# No `set -e`: any subshell failure must not propagate.
|
||||||
# A failed unit aborts `nixos-container update` which blocks rebuilds.
|
# A failed unit aborts `nixos-container update` which blocks rebuilds.
|
||||||
FORGE_URL=${lib.escapeShellArg config.hyperhive.forge.url}
|
FORGE_URL=${lib.escapeShellArg config.hyperhive.forge.url}
|
||||||
# Manager bind-mounts state at /state; sub-agents at
|
# Manager bind-mounts state at /state; sub-agents at
|
||||||
# /agents/<name>/state. Glob both — each container only sees
|
# /agents/<name>/state. Glob both — each container only sees
|
||||||
# its own mount, so there is exactly one hit (or zero when
|
# its own mount, so there is exactly one hit (or zero when
|
||||||
# the forge hasn't been seeded yet).
|
# the forge hasn't been seeded yet).
|
||||||
TOKEN_FILE=""
|
TOKEN_FILE=""
|
||||||
for f in /state/forge-token /agents/*/state/forge-token; do
|
for f in /state/forge-token /agents/*/state/forge-token; do
|
||||||
if [ -f "$f" ]; then
|
if [ -f "$f" ]; then
|
||||||
TOKEN_FILE="$f"
|
TOKEN_FILE="$f"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
if [ -z "$TOKEN_FILE" ]; then
|
if [ -z "$TOKEN_FILE" ]; then
|
||||||
echo "tea-login: no forge-token found; skipping"
|
echo "tea-login: no forge-token found; skipping"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
TOKEN=$(cat "$TOKEN_FILE")
|
TOKEN=$(cat "$TOKEN_FILE")
|
||||||
# Resolve the agent username from the forge API.
|
# Resolve the agent username from the forge API.
|
||||||
USER=$(curl -sf --max-time 5 \
|
USER=$(curl -sf --max-time 5 \
|
||||||
-H "Authorization: token $TOKEN" \
|
-H "Authorization: token $TOKEN" \
|
||||||
"$FORGE_URL/api/v1/user" \
|
"$FORGE_URL/api/v1/user" \
|
||||||
| python3 -c 'import sys,json; print(json.load(sys.stdin).get("login",""))' \
|
| python3 -c 'import sys,json; print(json.load(sys.stdin).get("login",""))' \
|
||||||
2>/dev/null || true)
|
2>/dev/null || true)
|
||||||
if [ -z "$USER" ]; then
|
if [ -z "$USER" ]; then
|
||||||
echo "tea-login: could not resolve username from forge API; skipping"
|
echo "tea-login: could not resolve username from forge API; skipping"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
# tea reads config from $HOME/.config/tea/config.yml.
|
# tea reads config from $HOME/.config/tea/config.yml.
|
||||||
# Write it directly so we control default:true and always
|
# Write it directly so we control default:true and always
|
||||||
# refresh a rotated token — no 'tea login add' interactive dance.
|
# refresh a rotated token — no 'tea login add' interactive dance.
|
||||||
CONFIG="$HOME/.config/tea/config.yml"
|
CONFIG="$HOME/.config/tea/config.yml"
|
||||||
mkdir -p "$(dirname "$CONFIG")" || true
|
mkdir -p "$(dirname "$CONFIG")" || true
|
||||||
cat > "$CONFIG" << EOF
|
cat > "$CONFIG" << EOF
|
||||||
logins:
|
logins:
|
||||||
- name: forge
|
- name: forge
|
||||||
url: $FORGE_URL
|
url: $FORGE_URL
|
||||||
token: $TOKEN
|
token: $TOKEN
|
||||||
default: true
|
default: true
|
||||||
ssh_host: ""
|
ssh_host: ""
|
||||||
ssh_key: ""
|
ssh_key: ""
|
||||||
insecure: false
|
insecure: false
|
||||||
ssh_agent: false
|
ssh_agent: false
|
||||||
user: $USER
|
user: $USER
|
||||||
preferences:
|
preferences:
|
||||||
editor: false
|
editor: false
|
||||||
flag_defaults:
|
flag_defaults:
|
||||||
remote: ""
|
remote: ""
|
||||||
EOF
|
EOF
|
||||||
echo "tea-login: configured for $FORGE_URL as $USER"
|
echo "tea-login: configured for $FORGE_URL as $USER"
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue