# CI Pipeline: Validates code on PRs, builds images on push # # Flow: # PR → dev/main : Runs validation (required status check) # Push → dev/main : Builds Docker images (only changed services) # # Selective Builds: # - Only builds services that have changed files # - Detects changes in service path, shared packages, and root config # - Use workflow_dispatch to force rebuild all services # # Deployments are triggered separately: # - Manual: workflow_dispatch on cd-staging.yml / cd-production.yml # - Tag-based: push tag like "chat-staging-v1.0.0" triggers cd-staging-tagged.yml name: CI on: push: branches: - main - dev pull_request: branches: - main - dev workflow_dispatch: inputs: force_build_all: description: 'Force rebuild all services' required: false default: 'false' type: boolean concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true env: NODE_VERSION: '20' PNPM_VERSION: '9.15.0' jobs: # =========================================== # Detect Changes - determines which services to build # =========================================== detect-changes: name: Detect Changes runs-on: ubuntu-latest if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' outputs: mana-core-auth: ${{ steps.changes.outputs.mana-core-auth }} mana-search: ${{ steps.changes.outputs.mana-search }} manacore-web: ${{ steps.changes.outputs.manacore-web }} chat-backend: ${{ steps.changes.outputs.chat-backend }} chat-web: ${{ steps.changes.outputs.chat-web }} todo-backend: ${{ steps.changes.outputs.todo-backend }} todo-web: ${{ steps.changes.outputs.todo-web }} calendar-backend: ${{ steps.changes.outputs.calendar-backend }} calendar-web: ${{ steps.changes.outputs.calendar-web }} clock-backend: ${{ steps.changes.outputs.clock-backend }} clock-web: ${{ steps.changes.outputs.clock-web }} contacts-backend: ${{ steps.changes.outputs.contacts-backend }} contacts-web: ${{ steps.changes.outputs.contacts-web }} presi-backend: ${{ steps.changes.outputs.presi-backend }} presi-web: ${{ steps.changes.outputs.presi-web }} storage-backend: ${{ steps.changes.outputs.storage-backend }} storage-web: ${{ steps.changes.outputs.storage-web }} telegram-stats-bot: ${{ steps.changes.outputs.telegram-stats-bot }} nutriphi-backend: ${{ steps.changes.outputs.nutriphi-backend }} nutriphi-web: ${{ steps.changes.outputs.nutriphi-web }} skilltree-backend: ${{ steps.changes.outputs.skilltree-backend }} skilltree-web: ${{ steps.changes.outputs.skilltree-web }} mana-matrix-bot: ${{ steps.changes.outputs.mana-matrix-bot }} zitare-backend: ${{ steps.changes.outputs.zitare-backend }} any-changes: ${{ steps.changes.outputs.any-changes }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check for changes id: changes run: | # Force build all if workflow_dispatch with force_build_all if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.force_build_all }}" == "true" ]; then echo "Force rebuild all services requested" echo "mana-core-auth=true" >> $GITHUB_OUTPUT echo "mana-search=true" >> $GITHUB_OUTPUT echo "manacore-web=true" >> $GITHUB_OUTPUT echo "chat-backend=true" >> $GITHUB_OUTPUT echo "chat-web=true" >> $GITHUB_OUTPUT echo "todo-backend=true" >> $GITHUB_OUTPUT echo "todo-web=true" >> $GITHUB_OUTPUT echo "calendar-backend=true" >> $GITHUB_OUTPUT echo "calendar-web=true" >> $GITHUB_OUTPUT echo "clock-backend=true" >> $GITHUB_OUTPUT echo "clock-web=true" >> $GITHUB_OUTPUT echo "contacts-backend=true" >> $GITHUB_OUTPUT echo "contacts-web=true" >> $GITHUB_OUTPUT echo "presi-backend=true" >> $GITHUB_OUTPUT echo "presi-web=true" >> $GITHUB_OUTPUT echo "storage-backend=true" >> $GITHUB_OUTPUT echo "storage-web=true" >> $GITHUB_OUTPUT echo "telegram-stats-bot=true" >> $GITHUB_OUTPUT echo "nutriphi-backend=true" >> $GITHUB_OUTPUT echo "nutriphi-web=true" >> $GITHUB_OUTPUT echo "skilltree-backend=true" >> $GITHUB_OUTPUT echo "skilltree-web=true" >> $GITHUB_OUTPUT echo "mana-matrix-bot=true" >> $GITHUB_OUTPUT echo "zitare-backend=true" >> $GITHUB_OUTPUT echo "any-changes=true" >> $GITHUB_OUTPUT exit 0 fi # Get changed files between current commit and previous if [ "${{ github.event_name }}" == "push" ]; then BASE_SHA="${{ github.event.before }}" HEAD_SHA="${{ github.sha }}" # Handle initial commit case if [ "$BASE_SHA" == "0000000000000000000000000000000000000000" ]; then CHANGED_FILES=$(git ls-files) else CHANGED_FILES=$(git diff --name-only $BASE_SHA $HEAD_SHA 2>/dev/null || git ls-files) fi else # workflow_dispatch without force - build all echo "Workflow dispatch without force_build_all - building all" echo "mana-core-auth=true" >> $GITHUB_OUTPUT echo "mana-search=true" >> $GITHUB_OUTPUT echo "manacore-web=true" >> $GITHUB_OUTPUT echo "chat-backend=true" >> $GITHUB_OUTPUT echo "chat-web=true" >> $GITHUB_OUTPUT echo "todo-backend=true" >> $GITHUB_OUTPUT echo "todo-web=true" >> $GITHUB_OUTPUT echo "calendar-backend=true" >> $GITHUB_OUTPUT echo "calendar-web=true" >> $GITHUB_OUTPUT echo "clock-backend=true" >> $GITHUB_OUTPUT echo "clock-web=true" >> $GITHUB_OUTPUT echo "contacts-backend=true" >> $GITHUB_OUTPUT echo "contacts-web=true" >> $GITHUB_OUTPUT echo "presi-backend=true" >> $GITHUB_OUTPUT echo "presi-web=true" >> $GITHUB_OUTPUT echo "storage-backend=true" >> $GITHUB_OUTPUT echo "storage-web=true" >> $GITHUB_OUTPUT echo "telegram-stats-bot=true" >> $GITHUB_OUTPUT echo "nutriphi-backend=true" >> $GITHUB_OUTPUT echo "nutriphi-web=true" >> $GITHUB_OUTPUT echo "skilltree-backend=true" >> $GITHUB_OUTPUT echo "skilltree-web=true" >> $GITHUB_OUTPUT echo "mana-matrix-bot=true" >> $GITHUB_OUTPUT echo "zitare-backend=true" >> $GITHUB_OUTPUT echo "any-changes=true" >> $GITHUB_OUTPUT exit 0 fi echo "Changed files:" echo "$CHANGED_FILES" # Common files that trigger all builds COMMON_PATTERNS="pnpm-lock.yaml|pnpm-workspace.yaml|package.json|.github/workflows/ci.yml" # Shared packages that affect multiple services SHARED_AUTH_PATTERN="packages/shared-auth/|packages/shared-types/" SHARED_UI_PATTERN="packages/shared-ui/|packages/shared-theme/|packages/shared-icons/|packages/shared-tailwind/|packages/shared-branding/" SHARED_WEB_PATTERN="packages/shared-auth-ui/|packages/shared-theme-ui/|packages/shared-feedback-ui/|packages/shared-profile-ui/|packages/shared-subscription-ui/|packages/shared-splitscreen/" # Function to check if any pattern matches check_pattern() { echo "$CHANGED_FILES" | grep -qE "$1" && echo "true" || echo "false" } # Check common changes COMMON_CHANGED=$(check_pattern "$COMMON_PATTERNS") SHARED_AUTH_CHANGED=$(check_pattern "$SHARED_AUTH_PATTERN") SHARED_UI_CHANGED=$(check_pattern "$SHARED_UI_PATTERN") SHARED_WEB_CHANGED=$(check_pattern "$SHARED_WEB_PATTERN") echo "Common changed: $COMMON_CHANGED" echo "Shared auth changed: $SHARED_AUTH_CHANGED" echo "Shared UI changed: $SHARED_UI_CHANGED" echo "Shared web changed: $SHARED_WEB_CHANGED" # mana-core-auth: services/mana-core-auth + packages/shared-nestjs-auth AUTH_CHANGED=$(check_pattern "services/mana-core-auth/|packages/shared-nestjs-auth/") if [ "$COMMON_CHANGED" == "true" ] || [ "$AUTH_CHANGED" == "true" ]; then echo "mana-core-auth=true" >> $GITHUB_OUTPUT else echo "mana-core-auth=false" >> $GITHUB_OUTPUT fi # mana-search: services/mana-search SEARCH_CHANGED=$(check_pattern "services/mana-search/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SEARCH_CHANGED" == "true" ]; then echo "mana-search=true" >> $GITHUB_OUTPUT else echo "mana-search=false" >> $GITHUB_OUTPUT fi # manacore-web: apps/manacore/apps/web + shared packages MANACORE_WEB_CHANGED=$(check_pattern "apps/manacore/apps/web/|apps/manacore/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$MANACORE_WEB_CHANGED" == "true" ]; then echo "manacore-web=true" >> $GITHUB_OUTPUT else echo "manacore-web=false" >> $GITHUB_OUTPUT fi # chat-backend: apps/chat/apps/backend + packages CHAT_BACKEND_CHANGED=$(check_pattern "apps/chat/apps/backend/|apps/chat/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$CHAT_BACKEND_CHANGED" == "true" ]; then echo "chat-backend=true" >> $GITHUB_OUTPUT else echo "chat-backend=false" >> $GITHUB_OUTPUT fi # chat-web: apps/chat/apps/web + packages + shared web CHAT_WEB_CHANGED=$(check_pattern "apps/chat/apps/web/|apps/chat/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$CHAT_WEB_CHANGED" == "true" ]; then echo "chat-web=true" >> $GITHUB_OUTPUT else echo "chat-web=false" >> $GITHUB_OUTPUT fi # todo-backend TODO_BACKEND_CHANGED=$(check_pattern "apps/todo/apps/backend/|apps/todo/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$TODO_BACKEND_CHANGED" == "true" ]; then echo "todo-backend=true" >> $GITHUB_OUTPUT else echo "todo-backend=false" >> $GITHUB_OUTPUT fi # todo-web TODO_WEB_CHANGED=$(check_pattern "apps/todo/apps/web/|apps/todo/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$TODO_WEB_CHANGED" == "true" ]; then echo "todo-web=true" >> $GITHUB_OUTPUT else echo "todo-web=false" >> $GITHUB_OUTPUT fi # calendar-backend CALENDAR_BACKEND_CHANGED=$(check_pattern "apps/calendar/apps/backend/|apps/calendar/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$CALENDAR_BACKEND_CHANGED" == "true" ]; then echo "calendar-backend=true" >> $GITHUB_OUTPUT else echo "calendar-backend=false" >> $GITHUB_OUTPUT fi # calendar-web CALENDAR_WEB_CHANGED=$(check_pattern "apps/calendar/apps/web/|apps/calendar/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$CALENDAR_WEB_CHANGED" == "true" ]; then echo "calendar-web=true" >> $GITHUB_OUTPUT else echo "calendar-web=false" >> $GITHUB_OUTPUT fi # clock-backend CLOCK_BACKEND_CHANGED=$(check_pattern "apps/clock/apps/backend/|apps/clock/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$CLOCK_BACKEND_CHANGED" == "true" ]; then echo "clock-backend=true" >> $GITHUB_OUTPUT else echo "clock-backend=false" >> $GITHUB_OUTPUT fi # clock-web CLOCK_WEB_CHANGED=$(check_pattern "apps/clock/apps/web/|apps/clock/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$CLOCK_WEB_CHANGED" == "true" ]; then echo "clock-web=true" >> $GITHUB_OUTPUT else echo "clock-web=false" >> $GITHUB_OUTPUT fi # contacts-backend CONTACTS_BACKEND_CHANGED=$(check_pattern "apps/contacts/apps/backend/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$CONTACTS_BACKEND_CHANGED" == "true" ]; then echo "contacts-backend=true" >> $GITHUB_OUTPUT else echo "contacts-backend=false" >> $GITHUB_OUTPUT fi # contacts-web CONTACTS_WEB_CHANGED=$(check_pattern "apps/contacts/apps/web/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$CONTACTS_WEB_CHANGED" == "true" ]; then echo "contacts-web=true" >> $GITHUB_OUTPUT else echo "contacts-web=false" >> $GITHUB_OUTPUT fi # presi-backend PRESI_BACKEND_CHANGED=$(check_pattern "apps/presi/apps/backend/|apps/presi/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$PRESI_BACKEND_CHANGED" == "true" ]; then echo "presi-backend=true" >> $GITHUB_OUTPUT else echo "presi-backend=false" >> $GITHUB_OUTPUT fi # presi-web PRESI_WEB_CHANGED=$(check_pattern "apps/presi/apps/web/|apps/presi/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$PRESI_WEB_CHANGED" == "true" ]; then echo "presi-web=true" >> $GITHUB_OUTPUT else echo "presi-web=false" >> $GITHUB_OUTPUT fi # storage-backend STORAGE_BACKEND_CHANGED=$(check_pattern "apps/storage/apps/backend/|apps/storage/packages/|packages/shared-storage/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$STORAGE_BACKEND_CHANGED" == "true" ]; then echo "storage-backend=true" >> $GITHUB_OUTPUT else echo "storage-backend=false" >> $GITHUB_OUTPUT fi # storage-web STORAGE_WEB_CHANGED=$(check_pattern "apps/storage/apps/web/|apps/storage/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$STORAGE_WEB_CHANGED" == "true" ]; then echo "storage-web=true" >> $GITHUB_OUTPUT else echo "storage-web=false" >> $GITHUB_OUTPUT fi # telegram-stats-bot TELEGRAM_STATS_BOT_CHANGED=$(check_pattern "services/telegram-stats-bot/") if [ "$COMMON_CHANGED" == "true" ] || [ "$TELEGRAM_STATS_BOT_CHANGED" == "true" ]; then echo "telegram-stats-bot=true" >> $GITHUB_OUTPUT else echo "telegram-stats-bot=false" >> $GITHUB_OUTPUT fi # nutriphi-backend NUTRIPHI_BACKEND_CHANGED=$(check_pattern "apps/nutriphi/apps/backend/|apps/nutriphi/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$NUTRIPHI_BACKEND_CHANGED" == "true" ]; then echo "nutriphi-backend=true" >> $GITHUB_OUTPUT else echo "nutriphi-backend=false" >> $GITHUB_OUTPUT fi # nutriphi-web NUTRIPHI_WEB_CHANGED=$(check_pattern "apps/nutriphi/apps/web/|apps/nutriphi/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$NUTRIPHI_WEB_CHANGED" == "true" ]; then echo "nutriphi-web=true" >> $GITHUB_OUTPUT else echo "nutriphi-web=false" >> $GITHUB_OUTPUT fi # skilltree-backend SKILLTREE_BACKEND_CHANGED=$(check_pattern "apps/skilltree/apps/backend/|apps/skilltree/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SKILLTREE_BACKEND_CHANGED" == "true" ]; then echo "skilltree-backend=true" >> $GITHUB_OUTPUT else echo "skilltree-backend=false" >> $GITHUB_OUTPUT fi # skilltree-web SKILLTREE_WEB_CHANGED=$(check_pattern "apps/skilltree/apps/web/|apps/skilltree/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$SHARED_UI_CHANGED" == "true" ] || [ "$SHARED_WEB_CHANGED" == "true" ] || [ "$SKILLTREE_WEB_CHANGED" == "true" ]; then echo "skilltree-web=true" >> $GITHUB_OUTPUT else echo "skilltree-web=false" >> $GITHUB_OUTPUT fi # mana-matrix-bot (consolidated Go bot) MANA_MATRIX_BOT_CHANGED=$(check_pattern "services/mana-matrix-bot/") if [ "$MANA_MATRIX_BOT_CHANGED" == "true" ]; then echo "mana-matrix-bot=true" >> $GITHUB_OUTPUT else echo "mana-matrix-bot=false" >> $GITHUB_OUTPUT fi # zitare-backend ZITARE_BACKEND_CHANGED=$(check_pattern "apps/zitare/apps/backend/|apps/zitare/packages/") if [ "$COMMON_CHANGED" == "true" ] || [ "$SHARED_AUTH_CHANGED" == "true" ] || [ "$ZITARE_BACKEND_CHANGED" == "true" ]; then echo "zitare-backend=true" >> $GITHUB_OUTPUT else echo "zitare-backend=false" >> $GITHUB_OUTPUT fi # Check if any service needs building if grep -q "=true" $GITHUB_OUTPUT; then echo "any-changes=true" >> $GITHUB_OUTPUT else echo "any-changes=false" >> $GITHUB_OUTPUT fi - name: Summary run: | echo "## Build Summary" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "| Service | Will Build |" >> $GITHUB_STEP_SUMMARY echo "|---------|------------|" >> $GITHUB_STEP_SUMMARY echo "| mana-core-auth | ${{ steps.changes.outputs.mana-core-auth }} |" >> $GITHUB_STEP_SUMMARY echo "| mana-search | ${{ steps.changes.outputs.mana-search }} |" >> $GITHUB_STEP_SUMMARY echo "| manacore-web | ${{ steps.changes.outputs.manacore-web }} |" >> $GITHUB_STEP_SUMMARY echo "| chat-backend | ${{ steps.changes.outputs.chat-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| chat-web | ${{ steps.changes.outputs.chat-web }} |" >> $GITHUB_STEP_SUMMARY echo "| todo-backend | ${{ steps.changes.outputs.todo-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| todo-web | ${{ steps.changes.outputs.todo-web }} |" >> $GITHUB_STEP_SUMMARY echo "| calendar-backend | ${{ steps.changes.outputs.calendar-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| calendar-web | ${{ steps.changes.outputs.calendar-web }} |" >> $GITHUB_STEP_SUMMARY echo "| clock-backend | ${{ steps.changes.outputs.clock-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| clock-web | ${{ steps.changes.outputs.clock-web }} |" >> $GITHUB_STEP_SUMMARY echo "| contacts-backend | ${{ steps.changes.outputs.contacts-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| contacts-web | ${{ steps.changes.outputs.contacts-web }} |" >> $GITHUB_STEP_SUMMARY echo "| presi-backend | ${{ steps.changes.outputs.presi-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| presi-web | ${{ steps.changes.outputs.presi-web }} |" >> $GITHUB_STEP_SUMMARY echo "| storage-backend | ${{ steps.changes.outputs.storage-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| storage-web | ${{ steps.changes.outputs.storage-web }} |" >> $GITHUB_STEP_SUMMARY echo "| telegram-stats-bot | ${{ steps.changes.outputs.telegram-stats-bot }} |" >> $GITHUB_STEP_SUMMARY echo "| nutriphi-backend | ${{ steps.changes.outputs.nutriphi-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| nutriphi-web | ${{ steps.changes.outputs.nutriphi-web }} |" >> $GITHUB_STEP_SUMMARY echo "| skilltree-backend | ${{ steps.changes.outputs.skilltree-backend }} |" >> $GITHUB_STEP_SUMMARY echo "| skilltree-web | ${{ steps.changes.outputs.skilltree-web }} |" >> $GITHUB_STEP_SUMMARY echo "| zitare-backend | ${{ steps.changes.outputs.zitare-backend }} |" >> $GITHUB_STEP_SUMMARY # =========================================== # Validation job - runs on PRs # =========================================== validate: name: Validate runs-on: ubuntu-latest if: github.event_name == 'pull_request' steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup pnpm uses: pnpm/action-setup@v2 with: version: ${{ env.PNPM_VERSION }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Validate monorepo best practices run: pnpm run validate:monorepo - name: Type check run: pnpm run type-check - name: Format check run: pnpm run format:check - name: Lint run: pnpm run lint - name: Test run: pnpm run test || true - name: Security Audit run: | # Run pnpm audit and capture exit code # Exit 0 if only moderate/low vulnerabilities pnpm audit --audit-level=high || { echo "::warning::Security vulnerabilities found. Run 'pnpm audit' locally for details." exit 0 # Don't fail build on audit issues (just warn) } - name: Check for known vulnerable packages run: | # Check for packages with known critical vulnerabilities # This is a basic check - for production, consider Snyk or similar if grep -r "lodash@[0-3]\." pnpm-lock.yaml 2>/dev/null; then echo "::warning::Potentially vulnerable lodash version detected" fi if grep -r "axios@0\.[0-9]\." pnpm-lock.yaml 2>/dev/null; then echo "::warning::Potentially vulnerable axios version detected" fi # =========================================== # Build Docker images - only changed services # =========================================== build-mana-core-auth: name: Build mana-core-auth runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.mana-core-auth == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/mana-core-auth tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: services/mana-core-auth/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-mana-search: name: Build mana-search runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.mana-search == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/mana-search tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: services/mana-search/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-manacore-web: name: Build manacore-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.manacore-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/manacore-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/manacore/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-chat-backend: name: Build chat-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.chat-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/chat-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/chat/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-chat-web: name: Build chat-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.chat-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/chat-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/chat/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-todo-backend: name: Build todo-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.todo-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/todo-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/todo/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-todo-web: name: Build todo-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.todo-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/todo-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/todo/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-calendar-backend: name: Build calendar-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.calendar-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/calendar-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/calendar/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-calendar-web: name: Build calendar-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.calendar-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/calendar-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/calendar/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-clock-backend: name: Build clock-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.clock-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/clock-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/clock/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-clock-web: name: Build clock-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.clock-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/clock-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/clock/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-contacts-backend: name: Build contacts-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.contacts-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/contacts-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/contacts/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-contacts-web: name: Build contacts-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.contacts-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/contacts-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/contacts/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-presi-backend: name: Build presi-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.presi-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/presi-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/presi/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-presi-web: name: Build presi-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.presi-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/presi-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/presi/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-storage-backend: name: Build storage-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.storage-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/storage-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/storage/apps/backend/Dockerfile # Note: arm64 disabled due to QEMU emulation issues with native deps platforms: linux/amd64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-storage-web: name: Build storage-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.storage-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/storage-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/storage/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-telegram-stats-bot: name: Build telegram-stats-bot runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.telegram-stats-bot == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/telegram-stats-bot tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: services/telegram-stats-bot/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-nutriphi-backend: name: Build nutriphi-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.nutriphi-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/nutriphi-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/nutriphi/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-nutriphi-web: name: Build nutriphi-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.nutriphi-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/nutriphi-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/nutriphi/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-skilltree-backend: name: Build skilltree-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.skilltree-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/skilltree-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/skilltree/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max build-skilltree-web: name: Build skilltree-web runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.skilltree-web == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/skilltree-web tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/skilltree/apps/web/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max # =========================================== # Matrix Bots # =========================================== build-mana-matrix-bot: name: Build mana-matrix-bot (Go) runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.mana-matrix-bot == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/mana-matrix-bot tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: services/mana-matrix-bot/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max # =========================================== # Zitare Backend # =========================================== build-zitare-backend: name: Build zitare-backend runs-on: ubuntu-latest needs: detect-changes if: needs.detect-changes.outputs.zitare-backend == 'true' steps: - uses: actions/checkout@v4 - uses: docker/setup-qemu-action@v3 - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository_owner }}/zitare-backend tags: type=raw,value=latest - uses: docker/build-push-action@v5 with: context: . file: apps/zitare/apps/backend/Dockerfile platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.meta.outputs.tags }} cache-from: type=gha cache-to: type=gha,mode=max