diff --git a/scripts/lib/gitea.sh b/scripts/lib/gitea.sh new file mode 100755 index 0000000..6671ad4 --- /dev/null +++ b/scripts/lib/gitea.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Shared Gitea helpers. Source this; don't execute it. +# +# Token resolution, in order: +# 1. $GITEA_TOKEN +# 2. ~/.config/gitea/token (chmod 600) +# 3. macOS Keychain, service "gitea-bike-app" +# +# Store it in the Keychain (recommended — not readable as a plain file): +# security add-generic-password -a "$USER" -s gitea-bike-app -w '' -U +# +# Or as a file: +# mkdir -p ~/.config/gitea && printf '%s' '' > ~/.config/gitea/token +# chmod 600 ~/.config/gitea/token + +GITEA_URL="${GITEA_URL:-http://192.168.0.3:3000}" +GITEA_OWNER="${GITEA_OWNER:-BBergle}" +GITEA_REPO="${GITEA_REPO:-bike-app}" + +gitea_token() { + if [ -n "${GITEA_TOKEN:-}" ]; then + printf '%s' "$GITEA_TOKEN"; return 0 + fi + if [ -r "$HOME/.config/gitea/token" ]; then + tr -d '\r\n' < "$HOME/.config/gitea/token"; return 0 + fi + if command -v security >/dev/null 2>&1; then + if t="$(security find-generic-password -s gitea-bike-app -w 2>/dev/null)"; then + printf '%s' "$t"; return 0 + fi + fi + return 1 +} + +gitea_require_token() { + if ! GITEA_TOKEN="$(gitea_token)" || [ -z "$GITEA_TOKEN" ]; then + cat >&2 <<'MSG' +error: no Gitea token found. + +Store it once, either way: + + security add-generic-password -a "$USER" -s gitea-bike-app -w '' -U + + # or + mkdir -p ~/.config/gitea && printf '%s' '' > ~/.config/gitea/token + chmod 600 ~/.config/gitea/token + +Create the token at http://192.168.0.3:3000/user/settings/applications +with scopes: read:user, write:repository, write:issue +MSG + return 1 + fi + export GITEA_TOKEN +} + +# gitea_api [curl args...] — path is relative to the repo +gitea_api() { + local method="$1" path="$2"; shift 2 + curl -sS -X "$method" \ + -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + "$@" \ + "$GITEA_URL/api/v1/repos/$GITEA_OWNER/$GITEA_REPO/$path" +} diff --git a/scripts/pr.sh b/scripts/pr.sh index 427cee1..31a579a 100755 --- a/scripts/pr.sh +++ b/scripts/pr.sh @@ -2,20 +2,16 @@ # Open a pull request against main from the current branch. # # scripts/pr.sh "feat(ingest): parse Bryton FIT activities" [--draft] -# -# Requires GITEA_TOKEN (scopes: write:repository, write:issue) and optionally GITEA_URL. set -euo pipefail - -GITEA_URL="${GITEA_URL:-http://192.168.0.3:3000}" -OWNER="${GITEA_OWNER:-BBergle}" -REPO="${GITEA_REPO:-bike-app}" -BASE="${PR_BASE:-main}" +cd "$(dirname "$0")/.." +. scripts/lib/gitea.sh die() { echo "error: $*" >&2; exit 1; } -[ -n "${GITEA_TOKEN:-}" ] || die "GITEA_TOKEN is not set. See CONTRIBUTING.md." +gitea_require_token || exit 1 command -v jq >/dev/null || die "jq is required (brew install jq)." +BASE="${PR_BASE:-main}" TITLE="${1:-}" [ -n "$TITLE" ] || die "usage: scripts/pr.sh \"\" [--draft]" DRAFT=false @@ -23,7 +19,6 @@ DRAFT=false BRANCH="$(git rev-parse --abbrev-ref HEAD)" [ "$BRANCH" != "$BASE" ] || die "refusing to open a PR from $BASE onto itself." - git diff --quiet && git diff --cached --quiet || die "working tree is dirty; commit or stash first." if ! git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then @@ -31,7 +26,6 @@ if ! git rev-parse --verify "origin/$BRANCH" >/dev/null 2>&1; then git push -u origin "$BRANCH" fi -# Prefer the PR template; otherwise summarise the commits on this branch. if [ -f .gitea/PULL_REQUEST_TEMPLATE.md ]; then BODY="$(cat .gitea/PULL_REQUEST_TEMPLATE.md)" else @@ -41,13 +35,8 @@ fi RESPONSE="$(jq -n \ --arg title "$TITLE" --arg body "$BODY" \ --arg head "$BRANCH" --arg base "$BASE" \ - --argjson draft "$DRAFT" \ '{title:$title, body:$body, head:$head, base:$base}' \ - | curl -sS -X POST \ - -H "Authorization: token $GITEA_TOKEN" \ - -H "Content-Type: application/json" \ - -d @- \ - "$GITEA_URL/api/v1/repos/$OWNER/$REPO/pulls")" + | gitea_api POST pulls -d @-)" if URL="$(echo "$RESPONSE" | jq -er '.html_url' 2>/dev/null)"; then echo "PR opened: $URL" diff --git a/scripts/review.sh b/scripts/review.sh index d00bb94..53397e6 100755 --- a/scripts/review.sh +++ b/scripts/review.sh @@ -1,28 +1,31 @@ #!/usr/bin/env bash -# Fetch a PR's diff and metadata for review. +# Inspect a PR for review. # -# scripts/review.sh 7 # print the diff -# scripts/review.sh 7 --meta # title, author, branch, CI state -# -# Requires GITEA_TOKEN. +# scripts/review.sh 7 diff +# scripts/review.sh 7 --meta title, author, size, mergeability, CI state +# scripts/review.sh --list open PRs set -euo pipefail +cd "$(dirname "$0")/.." +. scripts/lib/gitea.sh +gitea_require_token || exit 1 -GITEA_URL="${GITEA_URL:-http://192.168.0.3:3000}" -OWNER="${GITEA_OWNER:-BBergle}" -REPO="${GITEA_REPO:-bike-app}" +if [ "${1:-}" = "--list" ]; then + gitea_api GET "pulls?state=open&limit=30" \ + | jq -r '.[] | "#\(.number) \(.title) [\(.head.ref)] by \(.user.login)"' + exit 0 +fi -[ -n "${GITEA_TOKEN:-}" ] || { echo "error: GITEA_TOKEN not set" >&2; exit 1; } PR="${1:-}" -[ -n "$PR" ] || { echo "usage: scripts/review.sh <pr-number> [--meta]" >&2; exit 1; } - -api() { curl -sS -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/repos/$OWNER/$REPO/$1"; } +[ -n "$PR" ] || { echo "usage: scripts/review.sh <pr-number> [--meta] | --list" >&2; exit 1; } if [ "${2:-}" = "--meta" ]; then - api "pulls/$PR" | jq '{number, title, user: .user.login, head: .head.ref, base: .base.ref, - mergeable, draft, additions, deletions, changed_files}' - echo "--- commit status ---" - SHA="$(api "pulls/$PR" | jq -r '.head.sha')" - api "commits/$SHA/status" | jq '{state, statuses: [.statuses[]? | {context, state}]}' + gitea_api GET "pulls/$PR" | jq '{number, title, user: .user.login, head: .head.ref, + base: .base.ref, draft, mergeable, + additions, deletions, changed_files}' + echo "--- CI ---" + SHA="$(gitea_api GET "pulls/$PR" | jq -r '.head.sha')" + gitea_api GET "commits/$SHA/status" \ + | jq '{state, checks: [.statuses[]? | {context, state}]}' else - api "pulls/$PR.diff" + gitea_api GET "pulls/$PR.diff" fi