name: Build linux_amd64 extension and upload to Packages on: workflow_dispatch: inputs: version: description: Package version (defaults to tag name or short SHA) required: false push: branches: - main tags: - 'v*' jobs: build-linux-amd64: runs-on: ubuntu-latest env: EXTENSION_NAME: ui DUCKDB_PLATFORM: linux_amd64 steps: - name: Checkout repository (manual) env: TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -euo pipefail if [ -d .git ]; then echo "Repository already present" else server="${GITHUB_SERVER_URL:-${{ github.server_url }}}" repo_full="${GITHUB_REPOSITORY:-${{ github.repository }}}" sha="${GITHUB_SHA:-${{ github.sha }}}" host="$(echo "$server" | sed -E 's#^https?://([^/]+).*#\1#')" if [ -n "${TOKEN:-}" ]; then umask 077 printf "machine %s\n login token\n password %s\n" "$host" "$TOKEN" > "$HOME/.netrc" fi git init . git config --global --add safe.directory "$(pwd)" git remote add origin "$server/$repo_full.git" git -c http.https://$host/.extraheader="" fetch --depth=1 origin "$sha" git checkout -q FETCH_HEAD fi - name: Show workspace status run: | set -e git --no-pager status | cat - name: Install build dependencies run: | set -e export DEBIAN_FRONTEND=noninteractive apt-get update apt-get install -y --no-install-recommends \ build-essential cmake ninja-build python3 python3-venv pkg-config \ libssl-dev curl git ca-certificates jq - name: Preflight Gitea upload (fast-fail) env: GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -euo pipefail : "${GITEA_TOKEN:?GITEA_TOKEN secret is required}" server="${GITHUB_SERVER_URL:-${{ github.server_url }}}" owner="${GITHUB_REPOSITORY_OWNER:-${{ github.repository_owner }}}" repo_full="${GITHUB_REPOSITORY:-${{ github.repository }}}" pkg="${repo_full##*/}-preflight" version="preflight-${GITHUB_RUN_ID:-0}-${GITHUB_RUN_ATTEMPT:-0}-$(date +%s)" name="check.bin" tmpfile="$(mktemp)" printf "auth check %s\n" "$(date -u +%FT%TZ)" > "$tmpfile" # Normalize server to effective scheme+host (handles http->https redirects) base_no_trail="$(echo "$server" | sed 's#/*$##')" # Use GET (not HEAD) to avoid servers that don't support HEAD on this endpoint effective_version_url=$(curl -sS -L -o /dev/null -w '%{url_effective}' "$base_no_trail/api/v1/version" || echo "") normalized_server=$(echo "$effective_version_url" | sed -E 's#^(https?://[^/]+).*$#\1#') if [ -n "$normalized_server" ]; then server="$normalized_server" fi url="$server/api/packages/$owner/generic/$pkg/$version/$name?replace=1" mask() { local s="$1"; local n=${#s}; if [ "$n" -le 8 ]; then printf "*** (len=%s)" "$n"; else printf "%s***%s (len=%s)" "${s:0:4}" "${s:n-4:4}" "$n"; fi; } echo "Preflight variables:" echo " server=$server" echo " owner=$owner" echo " package=$pkg" echo " version=$version" echo " url=$url" echo " token=$(mask "$GITEA_TOKEN")" echo "Validating token via /api/v1/user:" curl -sS -L -o /dev/null -w " auth check -> HTTP %{http_code}\n" \ -H "Authorization: token ${GITEA_TOKEN}" "$server/api/v1/user" || true whoami_json=$(curl -sS -L -H "Authorization: token ${GITEA_TOKEN}" "$server/api/v1/user" || echo "") auth_user=$(echo "$whoami_json" | jq -r '.login // empty') if [ -z "$auth_user" ]; then auth_user="$owner"; fi echo " auth user=$auth_user" echo "Attempting preflight upload" tmpdir_pf="$(mktemp -d)" resp_headers_pf="$tmpdir_pf/headers.txt" resp_body_pf="$tmpdir_pf/body.txt" http_code=$(curl -sS -L -i -X PUT \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/octet-stream" \ --upload-file "$tmpfile" "$url" \ -D "$resp_headers_pf" -o "$resp_body_pf" -w "%{http_code}" || true) echo "Preflight response HTTP code: $http_code" echo "Preflight response headers:"; sed -n '1,200p' "$resp_headers_pf" | sed 's/\r$//' || true if [ -s "$resp_body_pf" ]; then echo "Preflight response body (first 200 bytes):"; head -c 200 "$resp_body_pf"; echo fi case "$http_code" in 401|301|302|303|307|308) echo "Preflight got $http_code; retrying with HTTP Basic auth (owner:token)" ;; *) ;; esac if [ "$http_code" = "401" ] || [ "$http_code" = "301" ] || [ "$http_code" = "302" ] || [ "$http_code" = "303" ] || [ "$http_code" = "307" ] || [ "$http_code" = "308" ]; then http_code=$(curl -sS -L -i -X PUT \ -u "$auth_user:${GITEA_TOKEN}" \ -H "Content-Type: application/octet-stream" \ --upload-file "$tmpfile" "$url" \ -D "$resp_headers_pf" -o "$resp_body_pf" -w "%{http_code}" || true) echo "Preflight retry HTTP code: $http_code" echo "Preflight retry response headers:"; sed -n '1,200p' "$resp_headers_pf" | sed 's/\r$//' || true if [ -s "$resp_body_pf" ]; then echo "Preflight retry body (first 200 bytes):"; head -c 200 "$resp_body_pf"; echo fi fi case "$http_code" in 2*) echo "Preflight upload succeeded, cleaning up" ;; *) echo "Preflight upload failed with HTTP $http_code" >&2; exit 1 ;; esac # Cleanup the uploaded dummy package version (best effort) curl -sS -L -o /dev/null -w " delete -> HTTP %{http_code}\n" \ -H "Authorization: token ${GITEA_TOKEN}" -X DELETE \ "$server/api/packages/$owner/generic/$pkg/$version" || \ curl -sS -L -o /dev/null -w " delete (basic) -> HTTP %{http_code}\n" \ -u "$owner:${GITEA_TOKEN}" -X DELETE \ "$server/api/packages/$owner/generic/$pkg/$version" || true - name: Initialize submodules run: | set -e git submodule update --init --recursive - name: Build release (linux_amd64) env: GEN: "" run: | set -e make -j"$(nproc)" release - name: Find extension artifact id: artifact run: | set -euo pipefail path="$(ls -1 build/release/extension/${EXTENSION_NAME}/${EXTENSION_NAME}.duckdb_extension 2>/dev/null || true)" if [ -z "$path" ]; then path="$(find build/release -type f -name '*.duckdb_extension' | head -n 1 || true)" fi if [ -z "$path" ]; then echo "Extension artifact not found" >&2 exit 1 fi echo "file=$path" >> "$GITHUB_OUTPUT" echo "Found: $path" - name: Compute package version id: ver run: | set -euo pipefail version='${{ inputs.version }}' if [ -z "$version" ]; then if [ "${{ github.ref_type }}" = "tag" ]; then version='${{ github.ref_name }}' else version="dev-${GITHUB_SHA::8}" fi fi echo "version=$version" >> "$GITHUB_OUTPUT" echo "Using version: $version" - name: Upload to Gitea Packages (generic) env: GITEA_TOKEN: ${{ secrets.RELEASE_TOKEN }} run: | set -euo pipefail : "${GITEA_TOKEN:?GITEA_TOKEN secret is required}" # Derive server/owner/pkg from env if not provided server="${GITHUB_SERVER_URL:-${{ github.server_url }}}" owner="${GITHUB_REPOSITORY_OWNER:-${{ github.repository_owner }}}" repo_full="${GITHUB_REPOSITORY:-${{ github.repository }}}" pkg="${repo_full##*/}" # Use previously computed version & artifact if available version='${{ steps.ver.outputs.version }}' file='${{ steps.artifact.outputs.file }}' # Fallbacks if steps were skipped if [ -z "${version}" ]; then if [ -n "${GITHUB_REF_TYPE:-}" ] && [ "${GITHUB_REF_TYPE}" = "tag" ]; then version="${GITHUB_REF_NAME:-dev-${GITHUB_SHA::8}}" else version="dev-${GITHUB_SHA::8}" fi fi if [ -z "${file}" ]; then file="$(ls -1 build/release/extension/${EXTENSION_NAME}/${EXTENSION_NAME}.duckdb_extension 2>/dev/null || true)" if [ -z "$file" ]; then file="$(find build/release -type f -name '*.duckdb_extension' | head -n 1 || true)" fi fi [ -n "$server" ] || { echo "server not set" >&2; exit 1; } [ -n "$owner" ] || { echo "owner not set" >&2; exit 1; } [ -n "$pkg" ] || { echo "pkg not set" >&2; exit 1; } [ -n "$version" ] || { echo "version not set" >&2; exit 1; } [ -n "$file" ] || { echo "file not set" >&2; exit 1; } # Normalize server using effective URL of /api/v1/version (handles http->https) base_no_trail="$(echo "$server" | sed 's#/*$##')" effective_version_url=$(curl -sS -L -o /dev/null -w '%{url_effective}' "$base_no_trail/api/v1/version" || echo "") normalized_server=$(echo "$effective_version_url" | sed -E 's#^(https?://[^/]+).*$#\1#') if [ -n "$normalized_server" ]; then server="$normalized_server" fi # Determine authenticated username for Basic auth fallback whoami_json=$(curl -sS -L -H "Authorization: token ${GITEA_TOKEN}" "$server/api/v1/user" || echo "") auth_user=$(echo "$whoami_json" | jq -r '.login // empty') if [ -z "$auth_user" ]; then auth_user="$owner"; fi name="$(basename "$file")" url="$server/api/packages/$owner/generic/$pkg/$version/$name?replace=1" # Debug helpers (avoid leaking secrets) mask() { local s="$1"; local n=${#s}; if [ "$n" -le 8 ]; then printf "*** (len=%s)" "$n"; else printf "%s***%s (len=%s)" "${s:0:4}" "${s:n-4:4}" "$n"; fi; } filesize=$(stat -c%s "$file" 2>/dev/null || echo "?") host="$(echo "$server" | sed -E 's#^https?://([^/]+).*#\1#')" echo "Derived variables:" echo " server=$server" echo " owner=$owner" echo " package=$pkg" echo " version=$version" echo " artifact=$file (size=${filesize} bytes, name=$name)" echo " url=$url" echo " token=$(mask "$GITEA_TOKEN")" echo "Curl version:"; curl --version | head -n 1 echo "DNS for $host:"; getent hosts "$host" || true echo "Checking API reachability (no auth):" curl -sS -L -o /dev/null -w " /api/v1/version -> HTTP %{http_code}\n" "$server/api/v1/version" || true echo "Validating token via /api/v1/user:" curl -sS -L -o /dev/null -w " auth check -> HTTP %{http_code}\n" \ -H "Authorization: token ${GITEA_TOKEN}" "$server/api/v1/user" || true echo " auth user=$auth_user" echo "Uploading $file to $url" # Perform upload and capture response details without exposing token tmpdir="$(mktemp -d)" resp_headers="$tmpdir/headers.txt" resp_body="$tmpdir/body.txt" http_code=$(curl -sS -L -i -X PUT \ -H "Authorization: token ${GITEA_TOKEN}" \ -H "Content-Type: application/octet-stream" \ --retry 2 --retry-delay 2 --max-time 300 \ --upload-file "$file" "$url" \ -D "$resp_headers" -o "$resp_body" -w "%{http_code}" || true) echo "Response HTTP code: $http_code" echo "Response headers:"; sed -n '1,200p' "$resp_headers" | sed 's/\r$//' || true if [ -s "$resp_body" ]; then echo "Response body (first 200 bytes):"; head -c 200 "$resp_body"; echo fi # If unauthorized or redirected, retry once using HTTP Basic auth (per Gitea docs) if [ "$http_code" = "401" ] || [ "$http_code" = "403" ] || [ "$http_code" = "301" ] || [ "$http_code" = "302" ] || [ "$http_code" = "303" ] || [ "$http_code" = "307" ] || [ "$http_code" = "308" ]; then echo "HTTP $http_code; retrying with HTTP Basic auth (auth_user:token)" http_code=$(curl -sS -L -i -X PUT \ -u "$auth_user:${GITEA_TOKEN}" \ -H "Content-Type: application/octet-stream" \ --retry 2 --retry-delay 2 --max-time 300 \ --upload-file "$file" "$url" \ -D "$resp_headers" -o "$resp_body" -w "%{http_code}" || true) echo "Retry HTTP code: $http_code" echo "Retry response headers:"; sed -n '1,200p' "$resp_headers" | sed 's/\r$//' || true if [ -s "$resp_body" ]; then echo "Retry response body (first 200 bytes):"; head -c 200 "$resp_body"; echo fi fi case "$http_code" in 2*) echo "Upload complete." ;; *) echo "Upload failed with HTTP $http_code" >&2; exit 1 ;; esac