docs: add MIT LICENSE and SECURITY.md #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build Docker Images | |
| on: | |
| push: | |
| branches: | |
| - main | |
| # pull_request: | |
| # branches: | |
| # - main | |
| env: | |
| AWS_REGION: eu-central-1 # Change this to your AWS region | |
| ECR_REGISTRY: ${{ secrets.AWS_ACCOUNT_ID }}.dkr.ecr.eu-central-1.amazonaws.com # Change region if needed | |
| jobs: | |
| detect-changes: | |
| name: Detect Changed Areas | |
| runs-on: ubuntu-latest | |
| outputs: | |
| orchestrator: ${{ steps.filter.outputs.orchestrator }} | |
| robot_gateway: ${{ steps.filter.outputs.robot_gateway }} | |
| frontend: ${{ steps.filter.outputs.frontend }} | |
| mobile: ${{ steps.filter.outputs.mobile }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Filter changed paths | |
| id: filter | |
| uses: dorny/paths-filter@v3 | |
| with: | |
| filters: | | |
| orchestrator: | |
| - 'backend/orchestrator/**' | |
| - 'backend/db/**' | |
| - '.github/workflows/build-docker-images.yml' | |
| robot_gateway: | |
| - 'backend/robot-gateway/**' | |
| - '.github/workflows/build-docker-images.yml' | |
| frontend: | |
| - 'frontend/**' | |
| - '.github/workflows/build-docker-images.yml' | |
| mobile: | |
| - 'mobile/**' | |
| - '.github/workflows/build-docker-images.yml' | |
| test-orchestrator: | |
| name: Test Orchestrator | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.orchestrator == 'true' | |
| runs-on: ubuntu-latest | |
| env: | |
| # Required by backend imports (llm_helpers/auth) during test collection. | |
| LLM_BASE_URL: http://localhost:11434/v1 | |
| LLM_CHAT_MODEL_FAST: ci-test-fast-model | |
| LLM_CHAT_MODEL_SMART: ci-test-smart-model | |
| GOOGLE_CLIENT_ID: ci-test-client-id.apps.googleusercontent.com | |
| defaults: | |
| run: | |
| working-directory: backend/orchestrator | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| cache-dependency-path: backend/orchestrator/requirements*.txt | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install -r requirements-dev.txt | |
| - name: Run tests | |
| run: | | |
| python -m pytest tests/ -v --tb=short | |
| - name: Run linter | |
| run: | | |
| ruff check . | |
| test-robot-gateway: | |
| name: Test Robot Gateway | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.robot_gateway == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: backend/robot-gateway | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.11' | |
| cache: 'pip' | |
| cache-dependency-path: backend/robot-gateway/requirements*.txt | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install -r requirements.txt | |
| pip install -r requirements-dev.txt | |
| - name: Run tests | |
| run: | | |
| python -m pytest tests/ -v --tb=short | |
| - name: Run linter | |
| run: | | |
| ruff check . | |
| build-orchestrator: | |
| name: Build Orchestrator Image | |
| needs: | |
| - detect-changes | |
| - test-orchestrator | |
| if: needs.detect-changes.outputs.orchestrator == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for OIDC authentication (if using) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-orchestrator | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Prepare version metadata | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION="${GITHUB_SHA}" | |
| fi | |
| BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | |
| echo "SERVICE_VERSION=${VERSION}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_GIT_SHA=${GITHUB_SHA}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_BUILD_TIME=${BUILD_TIME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_DEPLOYMENT=${GITHUB_REF_NAME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_IMAGE=${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-orchestrator:${VERSION}" >> "${GITHUB_ENV}" | |
| - name: Generate service manifest | |
| run: | | |
| python scripts/generate_service_manifest.py \ | |
| --output backend/orchestrator/config/service_manifest.json \ | |
| --deployment "${GITHUB_REF_NAME}" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./backend/orchestrator | |
| file: ./backend/orchestrator/Dockerfile | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Delete untagged ECR images | |
| if: ${{ github.event_name != 'pull_request' }} | |
| run: | | |
| NEXT_TOKEN="" | |
| while :; do | |
| if [ -n "${NEXT_TOKEN}" ]; then | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-orchestrator \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100 \ | |
| --next-token "${NEXT_TOKEN}") | |
| else | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-orchestrator \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100) | |
| fi | |
| IMAGE_IDS=$(python -c 'import json,sys;print(json.dumps(json.load(sys.stdin).get("imageIds", [])))' <<< "${RESP}") | |
| if [ "${IMAGE_IDS}" != "[]" ]; then | |
| TMP_FILE="$(mktemp)" | |
| echo "${IMAGE_IDS}" > "${TMP_FILE}" | |
| aws ecr batch-delete-image \ | |
| --repository-name appcalipse/digital-brain-orchestrator \ | |
| --image-ids "file://${TMP_FILE}" | |
| rm -f "${TMP_FILE}" | |
| fi | |
| NEXT_TOKEN=$(python -c 'import json,sys;print(json.load(sys.stdin).get("nextToken",""))' <<< "${RESP}") | |
| if [ -z "${NEXT_TOKEN}" ]; then | |
| break | |
| fi | |
| done | |
| build-robot-gateway: | |
| name: Build Robot Gateway Image | |
| needs: | |
| - detect-changes | |
| - test-robot-gateway | |
| if: needs.detect-changes.outputs.robot_gateway == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-robot-gateway | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Prepare version metadata | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION="${GITHUB_SHA}" | |
| fi | |
| BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | |
| echo "SERVICE_VERSION=${VERSION}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_GIT_SHA=${GITHUB_SHA}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_BUILD_TIME=${BUILD_TIME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_DEPLOYMENT=${GITHUB_REF_NAME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_IMAGE=${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-robot-gateway:${VERSION}" >> "${GITHUB_ENV}" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./backend/robot-gateway | |
| file: ./backend/robot-gateway/Dockerfile | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Delete untagged ECR images | |
| if: ${{ github.event_name != 'pull_request' }} | |
| run: | | |
| NEXT_TOKEN="" | |
| while :; do | |
| if [ -n "${NEXT_TOKEN}" ]; then | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-robot-gateway \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100 \ | |
| --next-token "${NEXT_TOKEN}") | |
| else | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-robot-gateway \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100) | |
| fi | |
| IMAGE_IDS=$(python -c 'import json,sys;print(json.dumps(json.load(sys.stdin).get("imageIds", [])))' <<< "${RESP}") | |
| if [ "${IMAGE_IDS}" != "[]" ]; then | |
| TMP_FILE="$(mktemp)" | |
| echo "${IMAGE_IDS}" > "${TMP_FILE}" | |
| aws ecr batch-delete-image \ | |
| --repository-name appcalipse/digital-brain-robot-gateway \ | |
| --image-ids "file://${TMP_FILE}" | |
| rm -f "${TMP_FILE}" | |
| fi | |
| NEXT_TOKEN=$(python -c 'import json,sys;print(json.load(sys.stdin).get("nextToken",""))' <<< "${RESP}") | |
| if [ -z "${NEXT_TOKEN}" ]; then | |
| break | |
| fi | |
| done | |
| lint-frontend: | |
| name: Lint Frontend | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.frontend == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: frontend/web | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| cache-dependency-path: frontend/web/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run ESLint | |
| run: npx eslint . | |
| lint-mobile: | |
| name: Lint Mobile | |
| needs: detect-changes | |
| if: needs.detect-changes.outputs.mobile == 'true' | |
| runs-on: ubuntu-latest | |
| defaults: | |
| run: | |
| working-directory: mobile | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Set up Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: npm | |
| cache-dependency-path: mobile/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run ESLint | |
| run: npm run lint | |
| build-frontend: | |
| name: Build Frontend Image | |
| needs: | |
| - detect-changes | |
| - lint-frontend | |
| if: needs.detect-changes.outputs.frontend == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| id-token: write # Required for OIDC authentication (if using) | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Configure AWS credentials | |
| uses: aws-actions/configure-aws-credentials@v4 | |
| with: | |
| aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} | |
| aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} | |
| aws-region: ${{ env.AWS_REGION }} | |
| - name: Login to Amazon ECR | |
| id: login-ecr | |
| uses: aws-actions/amazon-ecr-login@v2 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-frontend | |
| tags: | | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Prepare version metadata | |
| run: | | |
| VERSION="${{ steps.meta.outputs.version }}" | |
| if [ -z "$VERSION" ]; then | |
| VERSION="${GITHUB_SHA}" | |
| fi | |
| BUILD_TIME="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" | |
| echo "SERVICE_VERSION=${VERSION}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_GIT_SHA=${GITHUB_SHA}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_BUILD_TIME=${BUILD_TIME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_DEPLOYMENT=${GITHUB_REF_NAME}" >> "${GITHUB_ENV}" | |
| echo "SERVICE_IMAGE=${{ env.ECR_REGISTRY }}/appcalipse/digital-brain-frontend:${VERSION}" >> "${GITHUB_ENV}" | |
| - name: Build and push Docker image | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: ./frontend/web | |
| file: ./frontend/web/Dockerfile | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| build-args: | | |
| NEXT_PUBLIC_APP_VERSION=${{ env.SERVICE_VERSION }} | |
| NEXT_PUBLIC_APP_BUILD_TIME=${{ env.SERVICE_BUILD_TIME }} | |
| NEXT_PUBLIC_APP_GIT_SHA=${{ env.SERVICE_GIT_SHA }} | |
| NEXT_PUBLIC_APP_DEPLOYMENT=${{ env.SERVICE_DEPLOYMENT }} | |
| - name: Delete untagged ECR images | |
| if: ${{ github.event_name != 'pull_request' }} | |
| run: | | |
| NEXT_TOKEN="" | |
| while :; do | |
| if [ -n "${NEXT_TOKEN}" ]; then | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-frontend \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100 \ | |
| --next-token "${NEXT_TOKEN}") | |
| else | |
| RESP=$(aws ecr list-images \ | |
| --repository-name appcalipse/digital-brain-frontend \ | |
| --filter tagStatus=UNTAGGED \ | |
| --max-results 100) | |
| fi | |
| IMAGE_IDS=$(python -c 'import json,sys;print(json.dumps(json.load(sys.stdin).get("imageIds", [])))' <<< "${RESP}") | |
| if [ "${IMAGE_IDS}" != "[]" ]; then | |
| TMP_FILE="$(mktemp)" | |
| echo "${IMAGE_IDS}" > "${TMP_FILE}" | |
| aws ecr batch-delete-image \ | |
| --repository-name appcalipse/digital-brain-frontend \ | |
| --image-ids "file://${TMP_FILE}" | |
| rm -f "${TMP_FILE}" | |
| fi | |
| NEXT_TOKEN=$(python -c 'import json,sys;print(json.load(sys.stdin).get("nextToken",""))' <<< "${RESP}") | |
| if [ -z "${NEXT_TOKEN}" ]; then | |
| break | |
| fi | |
| done | |
| ci-status: | |
| name: CI Status | |
| needs: | |
| - detect-changes | |
| - test-orchestrator | |
| - build-orchestrator | |
| - test-robot-gateway | |
| - build-robot-gateway | |
| - lint-frontend | |
| - build-frontend | |
| - lint-mobile | |
| if: always() | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Validate required jobs | |
| run: | | |
| set -euo pipefail | |
| ORCHESTRATOR_CHANGED='${{ needs.detect-changes.outputs.orchestrator }}' | |
| ROBOT_GATEWAY_CHANGED='${{ needs.detect-changes.outputs.robot_gateway }}' | |
| FRONTEND_CHANGED='${{ needs.detect-changes.outputs.frontend }}' | |
| MOBILE_CHANGED='${{ needs.detect-changes.outputs.mobile }}' | |
| TEST_ORCH_RESULT='${{ needs.test-orchestrator.result }}' | |
| BUILD_ORCH_RESULT='${{ needs.build-orchestrator.result }}' | |
| TEST_GW_RESULT='${{ needs.test-robot-gateway.result }}' | |
| BUILD_GW_RESULT='${{ needs.build-robot-gateway.result }}' | |
| LINT_FRONTEND_RESULT='${{ needs.lint-frontend.result }}' | |
| BUILD_FRONTEND_RESULT='${{ needs.build-frontend.result }}' | |
| LINT_MOBILE_RESULT='${{ needs.lint-mobile.result }}' | |
| echo "orchestrator changed: ${ORCHESTRATOR_CHANGED}" | |
| echo "robot-gateway changed: ${ROBOT_GATEWAY_CHANGED}" | |
| echo "frontend changed: ${FRONTEND_CHANGED}" | |
| echo "mobile changed: ${MOBILE_CHANGED}" | |
| echo "test-orchestrator: ${TEST_ORCH_RESULT}" | |
| echo "build-orchestrator: ${BUILD_ORCH_RESULT}" | |
| echo "test-robot-gateway: ${TEST_GW_RESULT}" | |
| echo "build-robot-gateway: ${BUILD_GW_RESULT}" | |
| echo "lint-frontend: ${LINT_FRONTEND_RESULT}" | |
| echo "build-frontend: ${BUILD_FRONTEND_RESULT}" | |
| echo "lint-mobile: ${LINT_MOBILE_RESULT}" | |
| if [ "${ORCHESTRATOR_CHANGED}" = "true" ]; then | |
| if [ "${TEST_ORCH_RESULT}" != "success" ]; then | |
| echo "Orchestrator tests did not succeed." | |
| exit 1 | |
| fi | |
| if [ "${BUILD_ORCH_RESULT}" != "success" ]; then | |
| echo "Orchestrator image build did not succeed." | |
| exit 1 | |
| fi | |
| fi | |
| if [ "${ROBOT_GATEWAY_CHANGED}" = "true" ]; then | |
| if [ "${TEST_GW_RESULT}" != "success" ]; then | |
| echo "Robot gateway tests did not succeed." | |
| exit 1 | |
| fi | |
| if [ "${BUILD_GW_RESULT}" != "success" ]; then | |
| echo "Robot gateway image build did not succeed." | |
| exit 1 | |
| fi | |
| fi | |
| if [ "${FRONTEND_CHANGED}" = "true" ]; then | |
| if [ "${LINT_FRONTEND_RESULT}" != "success" ]; then | |
| echo "Frontend lint did not succeed." | |
| exit 1 | |
| fi | |
| if [ "${BUILD_FRONTEND_RESULT}" != "success" ]; then | |
| echo "Frontend image build did not succeed." | |
| exit 1 | |
| fi | |
| fi | |
| if [ "${MOBILE_CHANGED}" = "true" ]; then | |
| if [ "${LINT_MOBILE_RESULT}" != "success" ]; then | |
| echo "Mobile lint did not succeed." | |
| exit 1 | |
| fi | |
| fi | |
| echo "CI status check passed." |