From 1295ffb330827f04748b1995bb89d7ec34859bac Mon Sep 17 00:00:00 2001 From: copilotcoder Date: Thu, 2 Jul 2026 09:47:33 -0400 Subject: [PATCH] fix(ci): lock backend deps in cicd build and harden renovate auth --- .gitea/workflows/renovate.yml | 82 +++++++++++++++++++---------------- Dockerfile.cicd | 9 +++- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/.gitea/workflows/renovate.yml b/.gitea/workflows/renovate.yml index 0bc2461..010a872 100644 --- a/.gitea/workflows/renovate.yml +++ b/.gitea/workflows/renovate.yml @@ -242,31 +242,7 @@ jobs: LOG_LEVEL: info run: | echo "=== Running Renovate Bot ===" - - select_token() { - if [ -n "${RENOVATE_TOKEN_SECRET:-}" ]; then - echo "${RENOVATE_TOKEN_SECRET}" - return 0 - fi - if [ -n "${ACTIONS_TRIGGER_TOKEN:-}" ]; then - echo "${ACTIONS_TRIGGER_TOKEN}" - return 0 - fi - if [ -n "${PACKAGE_ACCESS_TOKEN:-}" ]; then - echo "${PACKAGE_ACCESS_TOKEN}" - return 0 - fi - return 1 - } - - if ! SELECTED_TOKEN="$(select_token)"; then - echo "❌ No token available for Renovate authentication" - echo "Configure one of: RENOVATE_TOKEN, ACTIONS_TRIGGER_TOKEN, PACKAGE_ACCESS_TOKEN" - exit 1 - fi - - export RENOVATE_TOKEN="${SELECTED_TOKEN}" - unset SELECTED_TOKEN + TARGET_REPO="DarkHelm.org/plex-playlist" CURL_INSECURE_FLAG="" if [ "${RENOVATE_ALLOW_INSECURE_TLS:-false}" = "true" ]; then @@ -275,23 +251,55 @@ jobs: export NODE_TLS_REJECT_UNAUTHORIZED=0 fi - # Validate token before starting Renovate to fail with actionable diagnostics. - AUTH_CHECK_STATUS=$(curl -sS -o /tmp/renovate-auth-check.json -w "%{http_code}" \ - ${CURL_INSECURE_FLAG} \ - -H "Authorization: token ${RENOVATE_TOKEN}" \ - "${RENOVATE_ENDPOINT}/user" || true) + select_token_with_repo_access() { + for candidate_name in RENOVATE_TOKEN_SECRET ACTIONS_TRIGGER_TOKEN PACKAGE_ACCESS_TOKEN; do + candidate_value="${!candidate_name:-}" + if [ -z "${candidate_value}" ]; then + continue + fi - if [ "${AUTH_CHECK_STATUS}" != "200" ]; then - echo "❌ Renovate token authentication preflight failed (HTTP ${AUTH_CHECK_STATUS})" - echo "Expected a personal access token with repo/issue read-write permissions." - if [ -s /tmp/renovate-auth-check.json ]; then - echo "Response body:" - cat /tmp/renovate-auth-check.json || true + USER_STATUS=$(curl -sS -o /tmp/renovate-auth-check-user.json -w "%{http_code}" \ + ${CURL_INSECURE_FLAG} \ + -H "Authorization: token ${candidate_value}" \ + "${RENOVATE_ENDPOINT}/user" || true) + + REPO_STATUS=$(curl -sS -o /tmp/renovate-auth-check-repo.json -w "%{http_code}" \ + ${CURL_INSECURE_FLAG} \ + -H "Authorization: token ${candidate_value}" \ + "${RENOVATE_ENDPOINT}/repos/${TARGET_REPO}" || true) + + if [ "${USER_STATUS}" = "200" ] && [ "${REPO_STATUS}" = "200" ]; then + echo "${candidate_name}:${candidate_value}" + return 0 + fi + + echo "⚠ Token candidate ${candidate_name} rejected (user=${USER_STATUS}, repo=${REPO_STATUS})" + done + + return 1 + } + + if ! SELECTED_TOKEN_RESULT="$(select_token_with_repo_access)"; then + echo "❌ No token available for Renovate authentication with repository access" + echo "Configure RENOVATE_TOKEN with repo+issue write and organization/user read scopes." + echo "Token preflight checks attempted: ${RENOVATE_ENDPOINT}/user and ${RENOVATE_ENDPOINT}/repos/${TARGET_REPO}" + if [ -s /tmp/renovate-auth-check-user.json ]; then + echo "Last user endpoint response body:" + cat /tmp/renovate-auth-check-user.json || true + fi + if [ -s /tmp/renovate-auth-check-repo.json ]; then + echo "Last repo endpoint response body:" + cat /tmp/renovate-auth-check-repo.json || true fi exit 1 fi - echo "✓ Renovate auth preflight passed" + SELECTED_TOKEN_SOURCE="${SELECTED_TOKEN_RESULT%%:*}" + SELECTED_TOKEN="${SELECTED_TOKEN_RESULT#*:}" + export RENOVATE_TOKEN="${SELECTED_TOKEN}" + unset SELECTED_TOKEN RESULT_TOKEN + + echo "✓ Renovate auth preflight passed with ${SELECTED_TOKEN_SOURCE}" # Run Renovate with configuration if [ "${RENOVATE_DRY_RUN}" = "true" ]; then diff --git a/Dockerfile.cicd b/Dockerfile.cicd index babe34b..8307b83 100644 --- a/Dockerfile.cicd +++ b/Dockerfile.cicd @@ -55,6 +55,7 @@ RUN --mount=type=secret,id=ssh_private_key \ # Extract only dependency files for caching optimization mkdir -p /workspace/backend /workspace/frontend && \ cp /tmp/repo/backend/pyproject.toml /workspace/backend/ 2>/dev/null || echo "No backend pyproject.toml" && \ + cp /tmp/repo/backend/uv.lock /workspace/backend/ 2>/dev/null || echo "No backend uv.lock" && \ cp /tmp/repo/frontend/package.json /workspace/frontend/ 2>/dev/null || echo "No frontend package.json" && \ cp /tmp/repo/frontend/yarn.lock /workspace/frontend/ 2>/dev/null || echo "No frontend yarn.lock" && \ cp /tmp/repo/frontend/.yarnrc.yml /workspace/frontend/ 2>/dev/null || echo "No frontend .yarnrc.yml" && \ @@ -90,7 +91,13 @@ RUN echo "=== Installing Backend Dependencies (Phase 1: Optimized Caching) ===" echo "# Temporary README for dependency caching phase" > ../README.md && \ echo "# Minimal __init__.py for build" > src/backend/__init__.py && \ # Install all dependencies including dev dependencies - uv sync --dev && \ + if [ -f "uv.lock" ]; then \ + echo "Using locked backend dependencies (uv.lock)" && \ + uv sync --dev --frozen; \ + else \ + echo "uv.lock not found, resolving dependencies from pyproject.toml" && \ + uv sync --dev; \ + fi && \ echo "✓ Backend dependencies installed and cached"; \ else \ echo "No pyproject.toml found, skipping dependency installation"; \