#!/usr/bin/env bash set -euo pipefail # Default options APPLY=false DRY_RUN=true while [[ $# -gt 0 ]]; do case $1 in --apply) APPLY=true DRY_RUN=false shift ;; --dry-run) APPLY=false DRY_RUN=true shift ;; *) echo "Unknown option: $1" >&2 exit 1 ;; esac done AGE_KEY="${APPSOK_SOPS_AGE_KEY_FILE:-$HOME/.config/sops/age/appsok-ci-key.txt}" SECRET_FILE="${APPSOK_CI_SECRET_FILE:-secrets/appsok.ci.sops.json}" export PATH="$HOME/SDK/flutter/bin:/opt/homebrew/bin:/usr/local/bin:$PATH" cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)" # SCM URL extraction SCM_URL="${APPSOK_SCM_URL:-$(git config --get remote.origin.url 2>/dev/null || git remote get-url origin 2>/dev/null || echo "https://github.com/toki-labs/appsok.git")}" # Reject HTTP(S) SCM URLs with userinfo (credential injection prevention) reject_http_scm_userinfo() { local url="$1" if [[ "$url" =~ ^https?://[^/]*@ ]]; then echo "Error: SCM remote URL must be credential-free for Jenkins job XML. Configure a credential-free remote and store credentials in Jenkins." >&2 exit 1 fi } # XML text escaping helper xml_escape() { python3 -c 'import html, sys; print(html.escape(sys.stdin.read(), quote=True), end="")' } reject_http_scm_userinfo "$SCM_URL" SCM_URL_XML="$(printf '%s' "$SCM_URL" | xml_escape)" # Prepare XML template # Make sure to exactly match the XML template in the PLAN XML_CONTENT=$(cat < AppSok certified macOS artifact thin launcher (appsok-macos-certified). false */main ${SCM_URL_XML} macbook-ultra false -1 10 -1 10 set -euo pipefail ./scripts/build-certified-macos.sh build/macos/Build/Products/Release/AppSok-certified.zip,build/macos/Build/Products/Release/AppSok-certified.zip.sha256 false true EOF ) if [ "$DRY_RUN" = "true" ]; then echo "$XML_CONTENT" exit 0 fi # Apply logic if [ ! -f "$AGE_KEY" ]; then echo "Error: missing SOPS age key at $AGE_KEY" >&2 exit 1 fi if [ ! -f "$SECRET_FILE" ]; then echo "Error: missing encrypted CI secret file at $SECRET_FILE" >&2 exit 1 fi export SOPS_AGE_KEY_FILE="$AGE_KEY" read_secret() { local key="$1" sops -d "$SECRET_FILE" | python3 -c "import json, sys; print(json.load(sys.stdin).get('$key', ''))" } JENKINS_URL="$(read_secret jenkins_url)" JENKINS_USER="$(read_secret jenkins_username)" JENKINS_TOKEN="$(read_secret jenkins_api_token)" if [ -z "$JENKINS_URL" ] || [ -z "$JENKINS_USER" ] || [ -z "$JENKINS_TOKEN" ]; then echo "Error: Missing Jenkins configurations in secrets." >&2 exit 1 fi # Remove trailing slash if present in JENKINS_URL JENKINS_URL="${JENKINS_URL%/}" CURL_CONFIG="$(mktemp)" chmod 600 "$CURL_CONFIG" trap 'rm -f "$CURL_CONFIG"' EXIT curl_config_escape() { local value="$1" value="${value//\\/\\\\}" value="${value//\"/\\\"}" printf '%s' "$value" } { printf 'user = "' curl_config_escape "$JENKINS_USER" printf ':' curl_config_escape "$JENKINS_TOKEN" printf '"\n' } >"$CURL_CONFIG" # 1. Fetch Crumb CRUMB_HEADER="" CRUMB_RESPONSE=$(curl -s --config "$CURL_CONFIG" "${JENKINS_URL}/crumbIssuer/api/json" || true) if [ -n "$CRUMB_RESPONSE" ] && echo "$CRUMB_RESPONSE" | grep -q "crumb"; then CRUMB_FIELD=$(echo "$CRUMB_RESPONSE" | python3 -c "import json, sys; print(json.load(sys.stdin)['crumbRequestField'])") CRUMB_VALUE=$(echo "$CRUMB_RESPONSE" | python3 -c "import json, sys; print(json.load(sys.stdin)['crumb'])") CRUMB_HEADER="${CRUMB_FIELD}:${CRUMB_VALUE}" fi # 2. Check if job exists CHECK_URL="${JENKINS_URL}/job/appsok-macos-certified/config.xml" HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" --config "$CURL_CONFIG" "$CHECK_URL" || echo "500") CURL_OPTS=(-s -w "%{http_code}" -o /dev/null) if [ -n "$CRUMB_HEADER" ]; then CURL_OPTS+=(-H "$CRUMB_HEADER") fi if [ "$HTTP_STATUS" = "200" ]; then echo "Updating existing job appsok-macos-certified..." UPDATE_URL="${JENKINS_URL}/job/appsok-macos-certified/config.xml" STATUS=$(curl "${CURL_OPTS[@]}" --config "$CURL_CONFIG" -H "Content-Type: application/xml" --data-binary "$XML_CONTENT" "$UPDATE_URL" || echo "500") if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ] || [ "$STATUS" = "302" ]; then echo "Successfully updated job configuration." else echo "Failed to update job. HTTP status: $STATUS" >&2 exit 1 fi elif [ "$HTTP_STATUS" = "404" ]; then echo "Creating new job appsok-macos-certified..." CREATE_URL="${JENKINS_URL}/createItem?name=appsok-macos-certified" STATUS=$(curl "${CURL_OPTS[@]}" --config "$CURL_CONFIG" -H "Content-Type: application/xml" --data-binary "$XML_CONTENT" "$CREATE_URL" || echo "500") if [ "$STATUS" = "200" ] || [ "$STATUS" = "201" ] || [ "$STATUS" = "302" ]; then echo "Successfully created job." else echo "Failed to create job. HTTP status: $STATUS" >&2 exit 1 fi else echo "Failed to query job status. HTTP status: $HTTP_STATUS" >&2 exit 1 fi