434 lines
9.5 KiB
Text
434 lines
9.5 KiB
Text
# Gitea API Client
|
|
#
|
|
# REST API v1 client for Gitea operations
|
|
#
|
|
# Version: 1.0.0
|
|
# Dependencies: http, nu_plugin_tera
|
|
|
|
use ../config/loader.nu get-config
|
|
|
|
# Get Gitea configuration from global config
|
|
export def get-gitea-config [] -> record {
|
|
let config = get-config
|
|
|
|
if "gitea" not-in $config {
|
|
error make {
|
|
msg: "Gitea not configured"
|
|
help: "Add gitea section to configuration"
|
|
}
|
|
}
|
|
|
|
$config.gitea
|
|
}
|
|
|
|
# Get Gitea auth token
|
|
export def get-gitea-token [
|
|
gitea_config: record
|
|
] -> string {
|
|
let token_path = $gitea_config.auth.token_path | path expand
|
|
|
|
if not ($token_path | path exists) {
|
|
error make {
|
|
msg: $"Token file not found: ($token_path)"
|
|
help: "Create token file with Gitea access token"
|
|
}
|
|
}
|
|
|
|
# Check if encrypted (SOPS)
|
|
if ($token_path | str ends-with ".enc") {
|
|
# Decrypt with SOPS
|
|
^sops --decrypt $token_path | str trim
|
|
} else {
|
|
open $token_path | str trim
|
|
}
|
|
}
|
|
|
|
# Get base API URL
|
|
export def get-api-url [
|
|
gitea_config: record
|
|
] -> string {
|
|
if $gitea_config.mode == "local" {
|
|
let port = $gitea_config.local.port
|
|
$"http://localhost:($port)/api/v1"
|
|
} else {
|
|
$gitea_config.remote.api_url
|
|
}
|
|
}
|
|
|
|
# Make API call to Gitea
|
|
export def gitea-api-call [
|
|
endpoint: string
|
|
method: string = "GET"
|
|
body?: record
|
|
--gitea-config: record
|
|
] -> record {
|
|
let config = if ($gitea_config | is-empty) {
|
|
get-gitea-config
|
|
} else {
|
|
$gitea_config
|
|
}
|
|
|
|
let token = get-gitea-token $config
|
|
let base_url = get-api-url $config
|
|
|
|
let url = $"($base_url)/($endpoint)"
|
|
|
|
let headers = [
|
|
"Authorization" $"token ($token)"
|
|
"Content-Type" "application/json"
|
|
"Accept" "application/json"
|
|
]
|
|
|
|
let result = if $method == "GET" {
|
|
http get --headers $headers $url
|
|
} else if $method == "POST" {
|
|
if ($body | is-empty) {
|
|
http post --headers $headers $url ""
|
|
} else {
|
|
http post --headers $headers $url ($body | to json)
|
|
}
|
|
} else if $method == "PUT" {
|
|
if ($body | is-empty) {
|
|
http put --headers $headers $url ""
|
|
} else {
|
|
http put --headers $headers $url ($body | to json)
|
|
}
|
|
} else if $method == "DELETE" {
|
|
http delete --headers $headers $url
|
|
} else if $method == "PATCH" {
|
|
if ($body | is-empty) {
|
|
http patch --headers $headers $url ""
|
|
} else {
|
|
http patch --headers $headers $url ($body | to json)
|
|
}
|
|
} else {
|
|
error make {
|
|
msg: $"Unsupported HTTP method: ($method)"
|
|
}
|
|
}
|
|
|
|
$result
|
|
}
|
|
|
|
# Repository Operations
|
|
|
|
# Create repository
|
|
export def create-repository [
|
|
org: string
|
|
repo_name: string
|
|
description?: string
|
|
private: bool = false
|
|
auto_init: bool = true
|
|
] -> record {
|
|
let body = {
|
|
name: $repo_name
|
|
description: ($description | default "")
|
|
private: $private
|
|
auto_init: $auto_init
|
|
default_branch: "main"
|
|
}
|
|
|
|
gitea-api-call $"orgs/($org)/repos" "POST" $body
|
|
}
|
|
|
|
# Get repository
|
|
export def get-repository [
|
|
owner: string
|
|
repo_name: string
|
|
] -> record {
|
|
gitea-api-call $"repos/($owner)/($repo_name)" "GET"
|
|
}
|
|
|
|
# Delete repository
|
|
export def delete-repository [
|
|
owner: string
|
|
repo_name: string
|
|
--force (-f)
|
|
] -> bool {
|
|
if not $force {
|
|
print $"⚠️ About to delete repository ($owner)/($repo_name)"
|
|
let confirm = input "Type repository name to confirm: "
|
|
if $confirm != $repo_name {
|
|
print "Deletion cancelled"
|
|
return false
|
|
}
|
|
}
|
|
|
|
gitea-api-call $"repos/($owner)/($repo_name)" "DELETE"
|
|
true
|
|
}
|
|
|
|
# List repositories in organization
|
|
export def list-repositories [
|
|
org: string
|
|
] -> list {
|
|
gitea-api-call $"orgs/($org)/repos" "GET"
|
|
}
|
|
|
|
# List user repositories
|
|
export def list-user-repositories [
|
|
username?: string
|
|
] -> list {
|
|
let user = if ($username | is-empty) {
|
|
(get-current-user).login
|
|
} else {
|
|
$username
|
|
}
|
|
|
|
gitea-api-call $"users/($user)/repos" "GET"
|
|
}
|
|
|
|
# Release Operations
|
|
|
|
# Create release
|
|
export def create-release [
|
|
owner: string
|
|
repo_name: string
|
|
tag_name: string
|
|
release_name: string
|
|
body?: string
|
|
draft: bool = false
|
|
prerelease: bool = false
|
|
] -> record {
|
|
let release_body = {
|
|
tag_name: $tag_name
|
|
name: $release_name
|
|
body: ($body | default "")
|
|
draft: $draft
|
|
prerelease: $prerelease
|
|
target_commitish: "main"
|
|
}
|
|
|
|
gitea-api-call $"repos/($owner)/($repo_name)/releases" "POST" $release_body
|
|
}
|
|
|
|
# Upload release asset
|
|
export def upload-release-asset [
|
|
owner: string
|
|
repo_name: string
|
|
release_id: int
|
|
file_path: string
|
|
] -> bool {
|
|
let config = get-gitea-config
|
|
let token = get-gitea-token $config
|
|
let base_url = get-api-url $config
|
|
|
|
let filename = $file_path | path basename
|
|
let url = $"($base_url)/repos/($owner)/($repo_name)/releases/($release_id)/assets?name=($filename)"
|
|
|
|
# Use curl for file upload (http command doesn't support multipart/form-data well)
|
|
let result = ^curl -X POST \
|
|
-H $"Authorization: token ($token)" \
|
|
-F $"attachment=@($file_path)" \
|
|
$url
|
|
|
|
true
|
|
}
|
|
|
|
# Get release by tag
|
|
export def get-release-by-tag [
|
|
owner: string
|
|
repo_name: string
|
|
tag: string
|
|
] -> record {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/releases/tags/($tag)" "GET"
|
|
}
|
|
|
|
# List releases
|
|
export def list-releases [
|
|
owner: string
|
|
repo_name: string
|
|
] -> list {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/releases" "GET"
|
|
}
|
|
|
|
# Delete release
|
|
export def delete-release [
|
|
owner: string
|
|
repo_name: string
|
|
release_id: int
|
|
] -> bool {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/releases/($release_id)" "DELETE"
|
|
true
|
|
}
|
|
|
|
# Issue Operations (for locking)
|
|
|
|
# Create issue
|
|
export def create-issue [
|
|
owner: string
|
|
repo_name: string
|
|
title: string
|
|
body: string
|
|
labels: list = []
|
|
assignee?: string
|
|
] -> record {
|
|
let issue_body = {
|
|
title: $title
|
|
body: $body
|
|
labels: $labels
|
|
}
|
|
|
|
let issue_with_assignee = if ($assignee | is-not-empty) {
|
|
$issue_body | merge {assignee: $assignee}
|
|
} else {
|
|
$issue_body
|
|
}
|
|
|
|
gitea-api-call $"repos/($owner)/($repo_name)/issues" "POST" $issue_with_assignee
|
|
}
|
|
|
|
# Close issue
|
|
export def close-issue [
|
|
owner: string
|
|
repo_name: string
|
|
issue_number: int
|
|
] -> bool {
|
|
let body = {state: "closed"}
|
|
gitea-api-call $"repos/($owner)/($repo_name)/issues/($issue_number)" "PATCH" $body
|
|
true
|
|
}
|
|
|
|
# List issues
|
|
export def list-issues [
|
|
owner: string
|
|
repo_name: string
|
|
state: string = "open"
|
|
labels?: string
|
|
] -> list {
|
|
let endpoint = if ($labels | is-empty) {
|
|
$"repos/($owner)/($repo_name)/issues?state=($state)"
|
|
} else {
|
|
$"repos/($owner)/($repo_name)/issues?state=($state)&labels=($labels)"
|
|
}
|
|
|
|
gitea-api-call $endpoint "GET"
|
|
}
|
|
|
|
# Get issue
|
|
export def get-issue [
|
|
owner: string
|
|
repo_name: string
|
|
issue_number: int
|
|
] -> record {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/issues/($issue_number)" "GET"
|
|
}
|
|
|
|
# Organization Operations
|
|
|
|
# Create organization
|
|
export def create-organization [
|
|
org_name: string
|
|
description?: string
|
|
visibility: string = "private"
|
|
] -> record {
|
|
let body = {
|
|
username: $org_name
|
|
description: ($description | default "")
|
|
visibility: $visibility
|
|
}
|
|
|
|
gitea-api-call "orgs" "POST" $body
|
|
}
|
|
|
|
# Get organization
|
|
export def get-organization [
|
|
org_name: string
|
|
] -> record {
|
|
gitea-api-call $"orgs/($org_name)" "GET"
|
|
}
|
|
|
|
# List organizations
|
|
export def list-organizations [] -> list {
|
|
gitea-api-call "user/orgs" "GET"
|
|
}
|
|
|
|
# User/Auth Operations
|
|
|
|
# Get current user
|
|
export def get-current-user [] -> record {
|
|
gitea-api-call "user" "GET"
|
|
}
|
|
|
|
# Validate token
|
|
export def validate-token [
|
|
gitea_config?: record
|
|
] {
|
|
let config = if ($gitea_config | is-empty) {
|
|
get-gitea-config
|
|
} else {
|
|
$gitea_config
|
|
}
|
|
|
|
let result = (do {
|
|
gitea-api-call "user" "GET" --gitea-config $config
|
|
} | complete)
|
|
|
|
if $result.exit_code == 0 {
|
|
let user = $result.stdout | from json
|
|
$user.login? != null
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
|
|
# Branch Operations
|
|
|
|
# Create branch
|
|
export def create-branch [
|
|
owner: string
|
|
repo_name: string
|
|
branch_name: string
|
|
base_branch: string = "main"
|
|
] -> record {
|
|
let body = {
|
|
new_branch_name: $branch_name
|
|
old_branch_name: $base_branch
|
|
}
|
|
|
|
gitea-api-call $"repos/($owner)/($repo_name)/branches" "POST" $body
|
|
}
|
|
|
|
# List branches
|
|
export def list-branches [
|
|
owner: string
|
|
repo_name: string
|
|
] -> list {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/branches" "GET"
|
|
}
|
|
|
|
# Get branch
|
|
export def get-branch [
|
|
owner: string
|
|
repo_name: string
|
|
branch_name: string
|
|
] -> record {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/branches/($branch_name)" "GET"
|
|
}
|
|
|
|
# Tag Operations
|
|
|
|
# Create tag
|
|
export def create-tag [
|
|
owner: string
|
|
repo_name: string
|
|
tag_name: string
|
|
message?: string
|
|
target?: string
|
|
] -> record {
|
|
let body = {
|
|
tag_name: $tag_name
|
|
message: ($message | default "")
|
|
target: ($target | default "main")
|
|
}
|
|
|
|
gitea-api-call $"repos/($owner)/($repo_name)/tags" "POST" $body
|
|
}
|
|
|
|
# List tags
|
|
export def list-tags [
|
|
owner: string
|
|
repo_name: string
|
|
] -> list {
|
|
gitea-api-call $"repos/($owner)/($repo_name)/tags" "GET"
|
|
}
|