From 364d6c2278aeb876b0910098d68a4d6a4e0d2fae Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Mon, 23 Feb 2026 12:54:03 -0600 Subject: [PATCH] fix: use Woodpecker v3 numeric repo IDs in API calls Woodpecker v3 requires numeric repo IDs in API endpoints, not owner/repo path segments. The old paths hit the SPA frontend catch-all and return HTML, which downstream tools misinterpret as auth failure (401). - Add tools/woodpecker/_lib.sh with wp_resolve_repo_id() helper that calls /api/repos/lookup/{owner}/{repo} to get numeric ID - Update all 3 pipeline scripts to resolve repo ID before API calls Co-Authored-By: Claude Opus 4.6 --- tools/woodpecker/_lib.sh | 50 ++++++++++++++++++++++++++++ tools/woodpecker/pipeline-list.sh | 14 ++++---- tools/woodpecker/pipeline-status.sh | 16 ++++----- tools/woodpecker/pipeline-trigger.sh | 14 ++++---- 4 files changed, 69 insertions(+), 25 deletions(-) create mode 100644 tools/woodpecker/_lib.sh diff --git a/tools/woodpecker/_lib.sh b/tools/woodpecker/_lib.sh new file mode 100644 index 0000000..fdb8a89 --- /dev/null +++ b/tools/woodpecker/_lib.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# +# _lib.sh — Shared helpers for Woodpecker CI tool scripts +# +# Usage: source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh" +# +# Requires: WOODPECKER_URL and WOODPECKER_TOKEN to be set (via load_credentials) + +# Resolve owner/repo name to numeric repo ID (required by Woodpecker v3 API) +# Usage: REPO_ID=$(wp_resolve_repo_id "owner/repo") +wp_resolve_repo_id() { + local full_name="$1" + local response http_code body repo_id + + response=$(curl -sk -w "\n%{http_code}" \ + -H "Authorization: Bearer $WOODPECKER_TOKEN" \ + "${WOODPECKER_URL}/api/repos/lookup/${full_name}") + + http_code=$(echo "$response" | tail -n1) + body=$(echo "$response" | sed '$d') + + if [[ "$http_code" != "200" ]]; then + echo "Error: Failed to look up repo '${full_name}' (HTTP $http_code)" >&2 + if echo "$body" | jq -e '.message' &>/dev/null; then + echo " $(echo "$body" | jq -r '.message')" >&2 + fi + return 1 + fi + + repo_id=$(echo "$body" | jq -r '.id // empty') + if [[ -z "$repo_id" ]]; then + echo "Error: Repo lookup returned no ID for '${full_name}'" >&2 + return 1 + fi + + echo "$repo_id" +} + +# Auto-detect repo name from git remote origin +# Usage: REPO=$(wp_detect_repo) +wp_detect_repo() { + local remote_url + remote_url=$(git remote get-url origin 2>/dev/null || true) + if [[ -n "$remote_url" ]]; then + echo "$remote_url" | sed -E 's|.*[:/]([^/]+/[^/]+?)(\.git)?$|\1|' + else + echo "Error: -r owner/repo required (not in a git repository)" >&2 + return 1 + fi +} diff --git a/tools/woodpecker/pipeline-list.sh b/tools/woodpecker/pipeline-list.sh index ee75207..8eae7b7 100755 --- a/tools/woodpecker/pipeline-list.sh +++ b/tools/woodpecker/pipeline-list.sh @@ -16,6 +16,7 @@ set -euo pipefail MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" source "$MOSAIC_HOME/tools/_lib/credentials.sh" +source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh" REPO="" LIMIT=20 @@ -41,18 +42,15 @@ fi # Auto-detect repo from git remote if not specified if [[ -z "$REPO" ]]; then - remote_url=$(git remote get-url origin 2>/dev/null || true) - if [[ -n "$remote_url" ]]; then - REPO=$(echo "$remote_url" | sed -E 's|.*[:/]([^/]+/[^/]+?)(\.git)?$|\1|') - else - echo "Error: -r owner/repo required (not in a git repository)" >&2 - exit 1 - fi + REPO=$(wp_detect_repo) || exit 1 fi +# Resolve owner/repo to numeric ID (Woodpecker v3 API) +REPO_ID=$(wp_resolve_repo_id "$REPO") || exit 1 + response=$(curl -sk -w "\n%{http_code}" \ -H "Authorization: Bearer $WOODPECKER_TOKEN" \ - "${WOODPECKER_URL}/api/repos/${REPO}/pipelines?per_page=${LIMIT}") + "${WOODPECKER_URL}/api/repos/${REPO_ID}/pipelines?per_page=${LIMIT}") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d') diff --git a/tools/woodpecker/pipeline-status.sh b/tools/woodpecker/pipeline-status.sh index 73b959d..08fe636 100755 --- a/tools/woodpecker/pipeline-status.sh +++ b/tools/woodpecker/pipeline-status.sh @@ -16,6 +16,7 @@ set -euo pipefail MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" source "$MOSAIC_HOME/tools/_lib/credentials.sh" +source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh" REPO="" NUMBER="" @@ -40,20 +41,17 @@ else fi if [[ -z "$REPO" ]]; then - remote_url=$(git remote get-url origin 2>/dev/null || true) - if [[ -n "$remote_url" ]]; then - REPO=$(echo "$remote_url" | sed -E 's|.*[:/]([^/]+/[^/]+?)(\.git)?$|\1|') - else - echo "Error: -r owner/repo required (not in a git repository)" >&2 - exit 1 - fi + REPO=$(wp_detect_repo) || exit 1 fi +# Resolve owner/repo to numeric ID (Woodpecker v3 API) +REPO_ID=$(wp_resolve_repo_id "$REPO") || exit 1 + if [[ -z "$NUMBER" ]]; then # Get latest pipeline - endpoint="${WOODPECKER_URL}/api/repos/${REPO}/pipelines?per_page=1" + endpoint="${WOODPECKER_URL}/api/repos/${REPO_ID}/pipelines?per_page=1" else - endpoint="${WOODPECKER_URL}/api/repos/${REPO}/pipelines/${NUMBER}" + endpoint="${WOODPECKER_URL}/api/repos/${REPO_ID}/pipelines/${NUMBER}" fi response=$(curl -sk -w "\n%{http_code}" \ diff --git a/tools/woodpecker/pipeline-trigger.sh b/tools/woodpecker/pipeline-trigger.sh index cf09132..d8e497a 100755 --- a/tools/woodpecker/pipeline-trigger.sh +++ b/tools/woodpecker/pipeline-trigger.sh @@ -15,6 +15,7 @@ set -euo pipefail MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" source "$MOSAIC_HOME/tools/_lib/credentials.sh" +source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh" REPO="" BRANCH="main" @@ -37,22 +38,19 @@ else fi if [[ -z "$REPO" ]]; then - remote_url=$(git remote get-url origin 2>/dev/null || true) - if [[ -n "$remote_url" ]]; then - REPO=$(echo "$remote_url" | sed -E 's|.*[:/]([^/]+/[^/]+?)(\.git)?$|\1|') - else - echo "Error: -r owner/repo required (not in a git repository)" >&2 - exit 1 - fi + REPO=$(wp_detect_repo) || exit 1 fi +# Resolve owner/repo to numeric ID (Woodpecker v3 API) +REPO_ID=$(wp_resolve_repo_id "$REPO") || exit 1 + echo "Triggering pipeline for $REPO on branch $BRANCH..." response=$(curl -sk -w "\n%{http_code}" -X POST \ -H "Authorization: Bearer $WOODPECKER_TOKEN" \ -H "Content-Type: application/json" \ -d "$(jq -n --arg b "$BRANCH" '{branch: $b}')" \ - "${WOODPECKER_URL}/api/repos/${REPO}/pipelines") + "${WOODPECKER_URL}/api/repos/${REPO_ID}/pipelines") http_code=$(echo "$response" | tail -n1) body=$(echo "$response" | sed '$d')