# Build + push compliance service images to registry.meghsakha.com # and trigger orca redeploy after CI passes on main. # # This workflow is gated on the CI workflow completing successfully. # It does not run independently — if CI fails, builds + deploy are skipped. # Per-service builds are gated on detect-changes so only services with # modified files are rebuilt; trigger-orca runs only if at least one build # succeeded and none failed. # # Requires Gitea Actions secrets: # REGISTRY_USERNAME / REGISTRY_PASSWORD — registry.meghsakha.com credentials # ORCA_WEBHOOK_SECRET — must match webhooks.json on orca master name: Build + Deploy on: workflow_run: workflows: ["CI"] types: [completed] branches: [main] jobs: # ── gate: only proceed if CI succeeded ──────────────────────────────────── ci-passed: runs-on: docker container: alpine:3.20 if: github.event.workflow_run.conclusion == 'success' steps: - name: CI passed, proceeding with build + deploy run: echo "CI run ${{ github.event.workflow_run.id }} succeeded on ${{ github.event.workflow_run.head_branch }} @ ${{ github.event.workflow_run.head_sha }}" # ── detect which services changed since the last successful build ──────── # Diff base = the last-build/main git tag, set by mark-last-build at the # end of every successful run. Works across squash merges, multi-commit # raw pushes, and force pushes (force pushes leave a stale tag → diff # shows symmetric differences → safe over-rebuild). If the tag doesn't # exist yet, scripts/detect-changes.sh falls back to rebuilding all. detect-changes: runs-on: docker container: alpine:3.20 needs: ci-passed outputs: admin: ${{ steps.diff.outputs.admin }} backend: ${{ steps.diff.outputs.backend }} sdk: ${{ steps.diff.outputs.sdk }} portal: ${{ steps.diff.outputs.portal }} tts: ${{ steps.diff.outputs.tts }} crawler: ${{ steps.diff.outputs.crawler }} dsms_gateway: ${{ steps.diff.outputs.dsms_gateway }} dsms_node: ${{ steps.diff.outputs.dsms_node }} steps: - name: Checkout run: | apk add --no-cache git bash git clone --depth 200 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . git fetch --tags origin || true - name: Resolve base SHA from last-build/main tag run: | BASE=$(git rev-parse --verify refs/tags/last-build/main 2>/dev/null || true) echo "Base SHA: ${BASE:-}" # Deepen if base isn't yet in the shallow clone. if [ -n "$BASE" ] && ! git rev-parse --verify "${BASE}^{commit}" >/dev/null 2>&1; then git fetch --unshallow origin 2>/dev/null \ || git fetch --depth=10000 origin 2>/dev/null \ || true fi echo "BASE_SHA=${BASE}" >> "$GITHUB_ENV" - name: Detect changes id: diff run: bash scripts/detect-changes.sh # ── per-service builds run in parallel (only changed services) ──────────── build-admin-compliance: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.admin == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-admin:latest \ -t registry.meghsakha.com/breakpilot/compliance-admin:${SHORT_SHA} \ admin-compliance/ docker push registry.meghsakha.com/breakpilot/compliance-admin:latest docker push registry.meghsakha.com/breakpilot/compliance-admin:${SHORT_SHA} build-backend-compliance: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.backend == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-backend:latest \ -t registry.meghsakha.com/breakpilot/compliance-backend:${SHORT_SHA} \ backend-compliance/ docker push registry.meghsakha.com/breakpilot/compliance-backend:latest docker push registry.meghsakha.com/breakpilot/compliance-backend:${SHORT_SHA} build-ai-sdk: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.sdk == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-sdk:latest \ -t registry.meghsakha.com/breakpilot/compliance-sdk:${SHORT_SHA} \ ai-compliance-sdk/ docker push registry.meghsakha.com/breakpilot/compliance-sdk:latest docker push registry.meghsakha.com/breakpilot/compliance-sdk:${SHORT_SHA} build-developer-portal: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.portal == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-portal:latest \ -t registry.meghsakha.com/breakpilot/compliance-portal:${SHORT_SHA} \ developer-portal/ docker push registry.meghsakha.com/breakpilot/compliance-portal:latest docker push registry.meghsakha.com/breakpilot/compliance-portal:${SHORT_SHA} build-tts: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.tts == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-tts:latest \ -t registry.meghsakha.com/breakpilot/compliance-tts:${SHORT_SHA} \ compliance-tts-service/ docker push registry.meghsakha.com/breakpilot/compliance-tts:latest docker push registry.meghsakha.com/breakpilot/compliance-tts:${SHORT_SHA} build-document-crawler: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.crawler == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-crawler:latest \ -t registry.meghsakha.com/breakpilot/compliance-crawler:${SHORT_SHA} \ document-crawler/ docker push registry.meghsakha.com/breakpilot/compliance-crawler:latest docker push registry.meghsakha.com/breakpilot/compliance-crawler:${SHORT_SHA} build-dsms-gateway: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.dsms_gateway == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build \ -t registry.meghsakha.com/breakpilot/compliance-dsms-gateway:latest \ -t registry.meghsakha.com/breakpilot/compliance-dsms-gateway:${SHORT_SHA} \ dsms-gateway/ docker push registry.meghsakha.com/breakpilot/compliance-dsms-gateway:latest docker push registry.meghsakha.com/breakpilot/compliance-dsms-gateway:${SHORT_SHA} build-dsms-node: runs-on: docker container: docker:27-cli needs: detect-changes if: needs.detect-changes.outputs.dsms_node == 'true' steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Login env: REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} run: echo "$REGISTRY_PASSWORD" | docker login registry.meghsakha.com -u "$REGISTRY_USERNAME" --password-stdin - name: Build + push run: | SHORT_SHA=$(git rev-parse --short HEAD) docker build --platform linux/amd64 \ -t registry.meghsakha.com/breakpilot/compliance-dsms-node:latest \ -t registry.meghsakha.com/breakpilot/compliance-dsms-node:${SHORT_SHA} \ dsms-node/ docker push registry.meghsakha.com/breakpilot/compliance-dsms-node:latest docker push registry.meghsakha.com/breakpilot/compliance-dsms-node:${SHORT_SHA} # ── advance the last-build/main tag — the diff base for future runs ────── # Runs when no build failed. Covers two cases: # - at least one service was rebuilt → mark this SHA as the new baseline # - all services were skipped (nothing changed) → still advance the tag # so we don't keep re-evaluating the same skipped commits forever # Skips if any build failed → tag stays put → next push retries those # services from the previous known-good base. mark-last-build: runs-on: docker container: alpine:3.20 needs: - build-admin-compliance - build-backend-compliance - build-ai-sdk - build-developer-portal - build-tts - build-document-crawler - build-dsms-gateway - build-dsms-node if: | always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') env: GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }} HEAD_SHA: ${{ github.event.workflow_run.head_sha }} steps: - name: Checkout run: | apk add --no-cache git git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Force-push last-build/main tag run: | set -e SHA="${HEAD_SHA:-$(git rev-parse HEAD)}" echo "Advancing last-build/main → ${SHA}" git tag -f last-build/main "$SHA" # Encode token into the push URL (no on-disk credential persistence). PUSH_URL="${GITHUB_SERVER_URL/https:\/\//https:\/\/x-access-token:${GITEA_TOKEN}@}/${GITHUB_REPOSITORY}.git" git push --force "$PUSH_URL" "refs/tags/last-build/main" echo "Tag last-build/main now at ${SHA}" # ── orca redeploy — runs only if at least one build succeeded ───────────── # `always()` lets this run when some builds are skipped (unchanged services). # The contains() checks ensure we only redeploy when something actually built # and no build failed. trigger-orca: runs-on: docker container: docker:27-cli needs: - build-admin-compliance - build-backend-compliance - build-ai-sdk - build-developer-portal - build-tts - build-document-crawler - build-dsms-gateway - build-dsms-node if: | always() && contains(needs.*.result, 'success') && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') steps: - name: Checkout (for SHA) run: | apk add --no-cache git curl openssl git clone --depth 1 --branch ${GITHUB_REF_NAME} ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git . - name: Trigger orca redeploy env: ORCA_WEBHOOK_SECRET: ${{ secrets.ORCA_WEBHOOK_SECRET }} ORCA_WEBHOOK_URL: http://46.225.100.82:6880/api/v1/webhooks/github run: | SHA=$(git rev-parse HEAD) PAYLOAD="{\"ref\":\"refs/heads/main\",\"repository\":{\"full_name\":\"${GITHUB_REPOSITORY}\"},\"head_commit\":{\"id\":\"$SHA\",\"message\":\"ci: compliance images built\"}}" SIG=$(printf '%s' "$PAYLOAD" | openssl dgst -sha256 -hmac "$ORCA_WEBHOOK_SECRET" -r | awk '{print $1}') curl -sSf -k \ -X POST \ -H "Content-Type: application/json" \ -H "X-GitHub-Event: push" \ -H "X-Hub-Signature-256: sha256=$SIG" \ -d "$PAYLOAD" \ "$ORCA_WEBHOOK_URL" \ || { echo "Orca redeploy failed"; exit 1; } echo "Orca redeploy triggered for compliance services"