name: Cloudflare Worker AI Studio Build on: workflow_dispatch: inputs: branch: description: 'Branch name' default: 'main' clone_url: description: 'Clone URL of repo' required: true original_url: description: 'Original URL of repo' required: false use_original: description: 'Use original repo URL' default: 'false' concurrency: group: aistudio-build-group cancel: false jobs: deploy: container: image: node:20-bullseye defaults: run: shell: bash env: BRANCH_NAME: ${{ github.event.inputs.branch || 'main' }} CLONE_URL: ${{ github.event.inputs.clone_url }} ORIGINAL_URL: ${{ github.event.inputs.original_url }} USE_ORIGINAL: ${{ github.event.inputs.use_original || 'false' }} GIT_USERNAME: ${{ vars.GIT_USERNAME }} GIT_TOKEN: ${{ vars.GIT_TOKEN }} CLOUDFLARE_API_TOKEN: ${{ github.event.inputs.branch == 'eteam_prod' && secrets.CF_API_TOKEN_ETEAM || secrets.CF_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ github.event.inputs.branch == 'eteam_prod' && vars.CF_ACCOUNT_ID_ETEAM || vars.CF_ACCOUNT_ID }} steps: - name: Log input values run: | set -euo pipefail echo "===== INPUT DETAILS =====" echo "Branch: $BRANCH_NAME" echo "Clone URL: $CLONE_URL" echo "Original URL: ${ORIGINAL_URL:-}" echo "Use Original: $USE_ORIGINAL" echo "=========================" - name: Install prerequisites (rsync, jq, git) run: | set -euo pipefail if ! command -v git >/dev/null 2>&1; then echo "[ERROR] git not found in image; cannot proceed without apt. Use an image that includes git." exit 1 fi if ! command -v jq >/dev/null 2>&1; then echo "[INFO] Installing jq (static binary)..." curl -fsSL -o /usr/local/bin/jq \ https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-linux-amd64 chmod +x /usr/local/bin/jq fi if ! command -v rsync >/dev/null 2>&1; then echo "[WARN] rsync not found; continuing (workflow does not require rsync for core steps)." fi jq --version git --version - name: Clone repository run: | set -euo pipefail TARGET_URL="$CLONE_URL" if [ "$USE_ORIGINAL" = "true" ] && [ -n "${ORIGINAL_URL:-}" ]; then TARGET_URL="$ORIGINAL_URL" fi AUTH_URL="$(echo "$TARGET_URL" | sed "s#https://#https://$GIT_USERNAME:$GIT_TOKEN@#")" git clone --branch "$BRANCH_NAME" "$AUTH_URL" repo cd repo echo "[INFO] Repository cloned successfully." git remote -v - name: Derive canonical repo name from origin and export run: | set -euo pipefail ORIGIN_URL="$(git -C repo remote get-url origin)" DERIVED_REPO_NAME="$(basename "${ORIGIN_URL%.git}")" echo "[DEBUG] Derived repo name from origin: '${DERIVED_REPO_NAME}'" echo "REPO_NAME=${DERIVED_REPO_NAME}" >> "$GITHUB_ENV" - name: Fetch metadata (slug + unique name) from AI Studio Manager API run: | set -euo pipefail ORIGIN_URL="$(git -C repo remote get-url origin)" OWNER_REPO="$(echo "$ORIGIN_URL" | awk -F[/:] '{print $(NF-1)"/"$NF}' | sed 's/\.git$//')" ENCODED_REPO_NAME="$(printf '%s' "$OWNER_REPO" | jq -s -R -r @uri)" if [[ "$BRANCH_NAME" == "main" ]]; then MANAGER_API_URL="https://www.humanizeiq.ai" elif [[ "$BRANCH_NAME" == "eteam_prod" ]]; then MANAGER_API_URL="https://www.humanizeiq.ai" elif [[ "$BRANCH_NAME" == "dev" ]]; then MANAGER_API_URL="https://www.dev.humanizeiq.ai" else MANAGER_API_URL="https://www.playtest.humanizeiq.ai" fi echo "[INFO] Using Manager API URL: $MANAGER_API_URL" echo "[INFO] Querying AI Studio Manager API for repoName=$OWNER_REPO" RESPONSE="$(curl -sS -X GET \ "${MANAGER_API_URL}/api/ai_studio_manager_api/app-builder/components/by-repo?repoName=${ENCODED_REPO_NAME}" \ -H 'accept: application/json' \ -H 'X-API-Key: system-key' || true)" echo "[DEBUG] API response: ${RESPONSE:-}" SLUG="$(echo "${RESPONSE:-}" | jq -r '.additional_info.slug' 2>/dev/null || echo "")" UNIQUE_APP_CODE="$(echo "${RESPONSE:-}" | jq -r '.additional_info.unique_app_code' 2>/dev/null || echo "")" if [ -z "${SLUG:-}" ] || [ "${SLUG:-}" = "null" ]; then echo "[WARN] slug missing; falling back to REPO_NAME#gais_." BASE_NAME_VALUE="${REPO_NAME#gais_}" else BASE_NAME_VALUE="$SLUG" fi if [ -z "${UNIQUE_APP_CODE:-}" ] || [ "${UNIQUE_APP_CODE:-}" = "null" ]; then echo "[WARN] unique_app_code missing; falling back to REPO_NAME." UNIQUE_NAME_VALUE="${REPO_NAME}" else UNIQUE_NAME_VALUE="${UNIQUE_APP_CODE}" fi echo "BASE_NAME=${BASE_NAME_VALUE}" >> "$GITHUB_ENV" echo "UNIQUE_NAME=${UNIQUE_NAME_VALUE}" >> "$GITHUB_ENV" echo "MANAGER_API_URL=${MANAGER_API_URL}" >> "$GITHUB_ENV" echo "[INFO] Using BASE_NAME=${BASE_NAME_VALUE}" echo "[INFO] Using UNIQUE_NAME=${UNIQUE_NAME_VALUE} for Worker base" - name: Install dependencies working-directory: repo run: | set -euo pipefail npm ci || npm install - name: Build AI Studio / Vite project working-directory: repo run: | set -euo pipefail BASE_NAME="${BASE_NAME:-${REPO_NAME#gais_}}" BASE_PATH="/${BASE_NAME}/" printf '[INFO] Using base path: %s\n' "$BASE_PATH" npm run build --if-present -- --base "$BASE_PATH" - name: Copy JSON assets into dist working-directory: repo run: | set -euo pipefail cp -v ./metadata.json dist/ || echo "No JSON files found in ./config" - name: Install Wrangler CLI run: | set -euo pipefail npm install -g wrangler - name: Deploy Cloudflare Worker env: CLOUDFLARE_API_TOKEN: ${{ env.BRANCH_NAME == 'eteam_prod' && secrets.CF_API_TOKEN_ETEAM || secrets.CF_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ env.BRANCH_NAME == 'eteam_prod' && vars.CF_ACCOUNT_ID_ETEAM || vars.CF_ACCOUNT_ID }} working-directory: repo run: | set -euo pipefail SAFE_BRANCH="${BRANCH_NAME//\//-}" if [ "$SAFE_BRANCH" = "playtest" ]; then SAFE_BRANCH="pt" fi SLUG="${BASE_NAME:-${REPO_NAME#gais_}}" WORKER_NAME="gais_${SLUG}_${SAFE_BRANCH}" MAX_LENGTH=54 if [ ${#WORKER_NAME} -gt $MAX_LENGTH ]; then echo "[WARN] Worker name '${WORKER_NAME}' is ${#WORKER_NAME} chars (max: ${MAX_LENGTH})" HASH=$(echo -n "$WORKER_NAME" | md5sum | cut -c1-8) MAX_SLUG_LEN=$((39 - ${#SAFE_BRANCH})) TRUNCATED_SLUG="${SLUG:0:$MAX_SLUG_LEN}" WORKER_NAME="gais_${TRUNCATED_SLUG}_${HASH}_${SAFE_BRANCH}" echo "[INFO] Truncated to: ${WORKER_NAME} (${#WORKER_NAME} chars)" fi echo "[INFO] Using Worker name: ${WORKER_NAME}" echo "[INFO] Writing wrangler.json..." cat > wrangler.json <<'EOF' { "name": "PLACEHOLDER", "compatibility_date": "1970-01-01", "workers_dev": true } EOF jq \ --arg name "$WORKER_NAME" \ --arg date "$(date +%Y-%m-%d)" \ '.name=$name | .compatibility_date=$date' \ wrangler.json > wrangler.tmp && mv wrangler.tmp wrangler.json echo "[INFO] Verifying Wrangler auth..." wrangler whoami echo "[INFO] Deploying..." echo "[INFO] CLOUDFLARE_ACCOUNT_ID=${CLOUDFLARE_ACCOUNT_ID}" wrangler deploy --assets ./dist - name: Update Route in Traefik Database if: success() run: | set -euo pipefail SAFE_BRANCH="${BRANCH_NAME//\//-}" if [ "$SAFE_BRANCH" = "playtest" ] || [ "$SAFE_BRANCH" = "pt" ]; then ENVIRONMENT="playtest" elif [ "$SAFE_BRANCH" = "dev" ]; then ENVIRONMENT="nonprod" elif [ "$SAFE_BRANCH" = "main" ]; then ENVIRONMENT="prod" elif [ "$SAFE_BRANCH" = "eteam_prod" ]; then ENVIRONMENT="prod" else echo "[WARN] Unknown branch: $SAFE_BRANCH, defaulting to playtest" ENVIRONMENT="playtest" fi echo "[INFO] Updating route for environment: $ENVIRONMENT" APP_NAME="${BASE_NAME}" echo "[INFO] Calling route update API for app: $APP_NAME using $MANAGER_API_URL" ROUTE_RESPONSE="$(curl -sS -X POST \ "${MANAGER_API_URL}/api/ai_studio_manager_api/app-builder/create-route" \ -H 'Content-Type: application/json' \ -H 'X-API-Key: system-key' \ -d "{\"appName\":\"${APP_NAME}\",\"environment\":\"${ENVIRONMENT}\"}" || echo '{"status":"error","message":"API call failed"}')" echo "[INFO] Route update response: ${ROUTE_RESPONSE}" if echo "$ROUTE_RESPONSE" | jq -e '.status == "success"' > /dev/null 2>&1; then ROUTE_URL="$(echo "$ROUTE_RESPONSE" | jq -r '.url')" echo "[INFO] ✓ Route updated successfully: $ROUTE_URL" else ERROR_MSG="$(echo "$ROUTE_RESPONSE" | jq -r '.message // "Unknown error"')" echo "[WARN] Route update may have failed: $ERROR_MSG" echo "[WARN] Continuing workflow - route can be updated manually if needed" fi - name: Create PR after successful build (NO MERGE) if: success() run: | set -euo pipefail cd repo ORIGIN_URL="$(git remote get-url origin)" ORIGIN_URL="${ORIGIN_URL%.git}" REPO_OWNER="$(echo "$ORIGIN_URL" | awk -F'/' '{print $(NF-1)}')" REPO_NAME_ONLY="$(echo "$ORIGIN_URL" | awk -F'/' '{print $NF}')" GITEA_API="https://git.code.svchub.com/api/v1" AUTH_HEADER="Authorization: token ${GIT_TOKEN}" CURRENT_BRANCH="${BRANCH_NAME}" # Determine PR direction based on current branch if [ "$CURRENT_BRANCH" = "playtest" ]; then FROM_BRANCH="playtest" TO_BRANCH="dev" elif [ "$CURRENT_BRANCH" = "dev" ]; then FROM_BRANCH="dev" TO_BRANCH="main" elif [ "$CURRENT_BRANCH" = "main" ]; then if git ls-remote --exit-code --heads origin eteam_prod >/dev/null 2>&1; then FROM_BRANCH="main" TO_BRANCH="eteam_prod" else echo "[INFO] No PR rule for branch: $CURRENT_BRANCH (eteam_prod does not exist)" exit 0 fi else echo "[INFO] No PR rule for branch: $CURRENT_BRANCH" exit 0 fi echo "[INFO] Checking if PR already exists: ${FROM_BRANCH} → ${TO_BRANCH}" # Check if PR already exists LIST_URL="${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME_ONLY}/pulls?state=open&base=${TO_BRANCH}&head=${REPO_OWNER}:${FROM_BRANCH}" EXISTING_PR="$(curl -sS -H "$AUTH_HEADER" "$LIST_URL" | jq -r '.[0].number // empty')" if [ -n "$EXISTING_PR" ]; then echo "[INFO] PR already exists: #${EXISTING_PR} (${FROM_BRANCH} → ${TO_BRANCH})" echo "[INFO] Skipping PR creation to avoid duplicates" exit 0 fi echo "[INFO] Creating PR from ${FROM_BRANCH} → ${TO_BRANCH}" PR_PAYLOAD=$(jq -n \ --arg title "Auto PR: ${FROM_BRANCH} → ${TO_BRANCH}" \ --arg head "$FROM_BRANCH" \ --arg base "$TO_BRANCH" \ --arg body "Automated PR created after successful build on ${FROM_BRANCH}" \ '{title:$title, head:$head, base:$base, body:$body}') HTTP_CODE=$(curl -s -o /tmp/resp.json -w "%{http_code}" -X POST \ -H "Content-Type: application/json" \ -H "$AUTH_HEADER" \ -d "$PR_PAYLOAD" \ "${GITEA_API}/repos/${REPO_OWNER}/${REPO_NAME_ONLY}/pulls" || true) if [ "$HTTP_CODE" != "201" ]; then echo "[WARN] PR creation failed or PR already exists (HTTP $HTTP_CODE). Response:" cat /tmp/resp.json || true else PR_NUMBER="$(jq -r '.number' /tmp/resp.json)" echo "[INFO] ✓ PR #${PR_NUMBER} created: ${FROM_BRANCH} → ${TO_BRANCH}" fi