docs: refresh public documentation for current runtime #483
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: CI/CD Pipeline | |
| on: | |
| pull_request: | |
| branches: [ main, develop ] | |
| push: | |
| branches: [ main, develop ] | |
| workflow_dispatch: | |
| # Cancel previous runs for the same PR/branch | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| env: | |
| NODE_VERSION: '18' | |
| jobs: | |
| # Check if files have changed to skip unnecessary jobs | |
| changes: | |
| name: Detect Changes | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| source: ${{ steps.filter.outputs.source }} | |
| config: ${{ steps.filter.outputs.config }} | |
| i18n: ${{ steps.filter.outputs.i18n }} | |
| styles: ${{ steps.filter.outputs.styles }} | |
| tests: ${{ steps.filter.outputs.tests }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check for file changes | |
| uses: dorny/paths-filter@v3 | |
| id: filter | |
| with: | |
| filters: | | |
| source: | |
| - 'app/**/*.{ts,tsx,js,jsx}' | |
| - 'apps/**/*.{ts,tsx,js,jsx}' | |
| - 'components/**/*.{ts,tsx,js,jsx}' | |
| - 'lib/**/*.{ts,tsx,js,jsx}' | |
| - 'packages/**/*.{ts,tsx,js,jsx}' | |
| - 'middleware.ts' | |
| - '*.{ts,tsx,js,jsx}' | |
| config: | |
| - '.github/workflows/**/*.{yml,yaml}' | |
| - 'package.json' | |
| - 'pnpm-workspace.yaml' | |
| - 'pnpm-lock.yaml' | |
| - 'tsconfig.json' | |
| - 'apps/*/package.json' | |
| - 'apps/*/tsconfig.json' | |
| - 'packages/*/package.json' | |
| - 'packages/*/tsconfig.json' | |
| - 'next.config.ts' | |
| - 'tailwind.config.js' | |
| - 'eslint.config.mjs' | |
| - 'commitlint.config.mjs' | |
| - 'postcss.config.mjs' | |
| - '.prettierrc.json' | |
| - '.prettierignore' | |
| - 'database/migrations/**/*.sql' | |
| - 'scripts/check-*.mjs' | |
| - 'scripts/gate-quality-verify.sh' | |
| tests: | |
| - '__tests__/**/*.{ts,tsx,js,jsx}' | |
| - '**/*.test.{ts,tsx,js,jsx}' | |
| - '**/*.spec.{ts,tsx,js,jsx}' | |
| i18n: | |
| - 'messages/**/*.json' | |
| - 'i18n/**/*.{ts,js}' | |
| - 'scripts/**/*i18n*' | |
| styles: | |
| - 'styles/**/*.css' | |
| - 'app/globals.css' | |
| # Install dependencies and cache them | |
| install: | |
| name: Install Dependencies | |
| runs-on: ubuntu-latest | |
| needs: changes | |
| if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' || needs.changes.outputs.tests == 'true' || needs.changes.outputs.styles == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Cache dependencies | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.pnpm-store | |
| node_modules | |
| key: ${{ runner.os }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pnpm- | |
| # Code formatting and linting checks | |
| format-and-lint: | |
| name: Format and Lint Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes, install] | |
| if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' || needs.changes.outputs.styles == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Check formatting on changed files | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| RANGE="${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}" | |
| elif [[ "${{ github.event.before }}" != "" && "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then | |
| RANGE="${{ github.event.before }}..${{ github.sha }}" | |
| else | |
| RANGE="$(git hash-object -t tree /dev/null)..${{ github.sha }}" | |
| fi | |
| echo "Using diff range: $RANGE" | |
| mapfile -t CHANGED_FILES < <(git diff --name-only --diff-filter=ACMRT "$RANGE") | |
| if [ ${#CHANGED_FILES[@]} -gt 0 ]; then | |
| echo "Running Prettier on changed files" | |
| pnpm exec prettier --check --ignore-unknown "${CHANGED_FILES[@]}" | |
| else | |
| echo "No changed files found in this event" | |
| fi | |
| - name: Run ESLint on changed files | |
| run: | | |
| if [[ "${{ github.event_name }}" == "pull_request" ]]; then | |
| RANGE="${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}" | |
| elif [[ "${{ github.event.before }}" != "" && "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then | |
| RANGE="${{ github.event.before }}..${{ github.sha }}" | |
| else | |
| RANGE="$(git hash-object -t tree /dev/null)..${{ github.sha }}" | |
| fi | |
| echo "Using diff range: $RANGE" | |
| CHANGED_FILES=$(git diff --name-only --diff-filter=ACMRT "$RANGE" | grep -E '\.(js|jsx|ts|tsx)$' | tr '\n' ' ' || true) | |
| if [ -n "$CHANGED_FILES" ]; then | |
| echo "Running ESLint on changed files: $CHANGED_FILES" | |
| pnpm exec eslint --cache --cache-location node_modules/.cache/eslint/.eslint-cache $CHANGED_FILES | |
| else | |
| echo "No JS/TS files changed in this event" | |
| fi | |
| # TypeScript type checking | |
| type-check: | |
| name: TypeScript Type Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes, install] | |
| if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Run TypeScript type checks | |
| run: | | |
| pnpm run type-check | |
| pnpm --filter @agentifui/shared type-check | |
| pnpm --filter @agentifui/api type-check | |
| # Build the application | |
| build: | |
| name: Build Application | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes, install] | |
| if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' | |
| permissions: | |
| contents: read | |
| services: | |
| postgres: | |
| image: postgres:18-alpine | |
| env: | |
| POSTGRES_DB: agentifui_test | |
| POSTGRES_USER: agentif | |
| POSTGRES_PASSWORD: agentif | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U agentif -d agentifui_test" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| redis: | |
| image: redis:7.4-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| env: | |
| NODE_ENV: production | |
| NEXT_PUBLIC_APP_URL: http://127.0.0.1:3000 | |
| BETTER_AUTH_URL: http://127.0.0.1:3000 | |
| DATABASE_URL: postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test | |
| MIGRATOR_DATABASE_URL: postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test | |
| REDIS_URL: redis://127.0.0.1:6379/2 | |
| REDIS_PREFIX: agentifui-test | |
| CACHE_L2_KEY_PREFIX: agentifui-test:cache:l2 | |
| CACHE_L2_INVALIDATION_CHANNEL: agentifui-test:cache:l2:invalidate | |
| REALTIME_REDIS_CHANNEL: agentifui-test:realtime:events | |
| REALTIME_REDIS_STREAM_KEY: agentifui-test:realtime:events:stream | |
| S3_ENDPOINT: http://127.0.0.1:9000 | |
| S3_BUCKET: agentifui-test | |
| S3_ACCESS_KEY_ID: minioadmin | |
| S3_SECRET_ACCESS_KEY: minioadmin | |
| S3_REGION: us-east-1 | |
| S3_ENABLE_PATH_STYLE: '1' | |
| BETTER_AUTH_SECRET: ci-build-only-secret-do-not-use-in-prod | |
| API_ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Start MinIO | |
| run: | | |
| docker rm -f agentifui-ci-minio >/dev/null 2>&1 || true | |
| docker run -d \ | |
| --name agentifui-ci-minio \ | |
| -p 9000:9000 \ | |
| -p 9001:9001 \ | |
| -e MINIO_ROOT_USER="$S3_ACCESS_KEY_ID" \ | |
| -e MINIO_ROOT_PASSWORD="$S3_SECRET_ACCESS_KEY" \ | |
| minio/minio:latest server /data --console-address ":9001" | |
| - name: Initialize MinIO bucket | |
| run: | | |
| for _ in {1..60}; do | |
| if curl -fsS "$S3_ENDPOINT/minio/health/live" >/dev/null 2>&1; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| curl -fsS "$S3_ENDPOINT/minio/health/live" >/dev/null | |
| docker run --rm --network host --entrypoint /bin/sh \ | |
| -e S3_ENDPOINT \ | |
| -e S3_BUCKET \ | |
| -e S3_ACCESS_KEY_ID \ | |
| -e S3_SECRET_ACCESS_KEY \ | |
| minio/mc:latest -c ' | |
| until mc alias set local "$S3_ENDPOINT" "$S3_ACCESS_KEY_ID" "$S3_SECRET_ACCESS_KEY"; do | |
| sleep 1 | |
| done | |
| mc mb --ignore-existing "local/$S3_BUCKET" | |
| ' | |
| env: | |
| S3_ENDPOINT: ${{ env.S3_ENDPOINT }} | |
| S3_BUCKET: ${{ env.S3_BUCKET }} | |
| S3_ACCESS_KEY_ID: ${{ env.S3_ACCESS_KEY_ID }} | |
| S3_SECRET_ACCESS_KEY: ${{ env.S3_SECRET_ACCESS_KEY }} | |
| - name: Ensure PostgreSQL client | |
| run: | | |
| if command -v psql >/dev/null 2>&1; then | |
| exit 0 | |
| fi | |
| sudo apt-get update | |
| sudo apt-get install -y postgresql-client | |
| - name: Apply database migrations | |
| run: | | |
| shopt -s nullglob | |
| migrations=(database/migrations/*.sql) | |
| if [ ${#migrations[@]} -eq 0 ]; then | |
| echo "No SQL migrations found" >&2 | |
| exit 1 | |
| fi | |
| for migration in "${migrations[@]}"; do | |
| psql "$MIGRATOR_DATABASE_URL" -v ON_ERROR_STOP=1 -f "$migration" | |
| done | |
| - name: Build shared workspace package | |
| run: pnpm --filter @agentifui/shared build | |
| - name: Build API workspace package | |
| run: pnpm --filter @agentifui/api build | |
| - name: Build application | |
| run: pnpm run build | |
| - name: Upload build artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: build-artifacts | |
| path: .next/ | |
| retention-days: 7 | |
| # Run tests | |
| test: | |
| name: Run Tests | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: [changes, install] | |
| if: needs.changes.outputs.tests == 'true' || needs.changes.outputs.source == 'true' | |
| permissions: | |
| contents: read | |
| services: | |
| postgres: | |
| image: postgres:18-alpine | |
| env: | |
| POSTGRES_DB: agentifui_test | |
| POSTGRES_USER: agentif | |
| POSTGRES_PASSWORD: agentif | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U agentif -d agentifui_test" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| redis: | |
| image: redis:7.4-alpine | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 5s | |
| --health-timeout 5s | |
| --health-retries 20 | |
| env: | |
| NODE_ENV: test | |
| NEXT_PUBLIC_APP_URL: http://127.0.0.1:3000 | |
| BETTER_AUTH_URL: http://127.0.0.1:3000 | |
| DATABASE_URL: postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test | |
| MIGRATOR_DATABASE_URL: postgresql://agentif:agentif@127.0.0.1:5432/agentifui_test | |
| REDIS_URL: redis://127.0.0.1:6379/2 | |
| REDIS_PREFIX: agentifui-test | |
| CACHE_L2_KEY_PREFIX: agentifui-test:cache:l2 | |
| CACHE_L2_INVALIDATION_CHANNEL: agentifui-test:cache:l2:invalidate | |
| REALTIME_REDIS_CHANNEL: agentifui-test:realtime:events | |
| REALTIME_REDIS_STREAM_KEY: agentifui-test:realtime:events:stream | |
| S3_ENDPOINT: http://127.0.0.1:9000 | |
| S3_BUCKET: agentifui-test | |
| S3_ACCESS_KEY_ID: minioadmin | |
| S3_SECRET_ACCESS_KEY: minioadmin | |
| S3_REGION: us-east-1 | |
| S3_ENABLE_PATH_STYLE: '1' | |
| BETTER_AUTH_SECRET: ci-test-only-secret-do-not-use-in-prod | |
| API_ENCRYPTION_KEY: 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Start MinIO | |
| run: | | |
| docker rm -f agentifui-ci-minio >/dev/null 2>&1 || true | |
| docker run -d \ | |
| --name agentifui-ci-minio \ | |
| -p 9000:9000 \ | |
| -p 9001:9001 \ | |
| -e MINIO_ROOT_USER="$S3_ACCESS_KEY_ID" \ | |
| -e MINIO_ROOT_PASSWORD="$S3_SECRET_ACCESS_KEY" \ | |
| minio/minio:latest server /data --console-address ":9001" | |
| - name: Initialize MinIO bucket | |
| run: | | |
| for _ in {1..60}; do | |
| if curl -fsS "$S3_ENDPOINT/minio/health/live" >/dev/null 2>&1; then | |
| break | |
| fi | |
| sleep 1 | |
| done | |
| curl -fsS "$S3_ENDPOINT/minio/health/live" >/dev/null | |
| docker run --rm --network host --entrypoint /bin/sh \ | |
| -e S3_ENDPOINT \ | |
| -e S3_BUCKET \ | |
| -e S3_ACCESS_KEY_ID \ | |
| -e S3_SECRET_ACCESS_KEY \ | |
| minio/mc:latest -c ' | |
| until mc alias set local "$S3_ENDPOINT" "$S3_ACCESS_KEY_ID" "$S3_SECRET_ACCESS_KEY"; do | |
| sleep 1 | |
| done | |
| mc mb --ignore-existing "local/$S3_BUCKET" | |
| ' | |
| env: | |
| S3_ENDPOINT: ${{ env.S3_ENDPOINT }} | |
| S3_BUCKET: ${{ env.S3_BUCKET }} | |
| S3_ACCESS_KEY_ID: ${{ env.S3_ACCESS_KEY_ID }} | |
| S3_SECRET_ACCESS_KEY: ${{ env.S3_SECRET_ACCESS_KEY }} | |
| - name: Ensure PostgreSQL client | |
| run: | | |
| if command -v psql >/dev/null 2>&1; then | |
| exit 0 | |
| fi | |
| sudo apt-get update | |
| sudo apt-get install -y postgresql-client | |
| - name: Apply database migrations | |
| run: | | |
| shopt -s nullglob | |
| migrations=(database/migrations/*.sql) | |
| if [ ${#migrations[@]} -eq 0 ]; then | |
| echo "No SQL migrations found" >&2 | |
| exit 1 | |
| fi | |
| for migration in "${migrations[@]}"; do | |
| psql "$MIGRATOR_DATABASE_URL" -v ON_ERROR_STOP=1 -f "$migration" | |
| done | |
| - name: Run tests | |
| run: pnpm test | |
| # Security checks | |
| security-check: | |
| name: Security Check | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| needs: [changes, install] | |
| if: needs.changes.outputs.source == 'true' || needs.changes.outputs.config == 'true' | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - 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: Run dependency audit | |
| run: | | |
| set +e | |
| pnpm audit --audit-level=moderate | |
| status=$? | |
| set -e | |
| if [[ $status -ne 0 ]]; then | |
| echo "::warning::pnpm audit reported known dependency vulnerabilities. Keeping CI green while baseline dependency debt is being tracked separately." | |
| fi | |
| - name: Scan for secrets | |
| run: | | |
| echo "Scanning for hardcoded secrets..." | |
| # Check for potential secrets in source code | |
| if grep -r -i -E "(api_key|apikey|secret|token|password)\s*[:=]\s*['\"][a-zA-Z0-9+/]{20,}['\"]" \ | |
| --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \ | |
| app components lib | grep -v -E "(placeholder|example|test|demo|sample|mock|dummy)"; then | |
| echo "⚠️ Potential hardcoded secret detected" | |
| exit 1 | |
| fi | |
| echo "✅ No hardcoded secrets found" | |
| # Final status check - all jobs must pass | |
| ci-status: | |
| name: CI Status Check | |
| runs-on: ubuntu-latest | |
| needs: | |
| - changes | |
| - format-and-lint | |
| - type-check | |
| - build | |
| - test | |
| - security-check | |
| if: always() | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Check all jobs status | |
| run: | | |
| echo "Checking CI pipeline status..." | |
| # Check if any job failed | |
| if [[ "${{ needs.format-and-lint.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.type-check.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.build.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.test.result }}" == "failure" ]] || \ | |
| [[ "${{ needs.security-check.result }}" == "failure" ]]; then | |
| echo "❌ CI pipeline failed" | |
| exit 1 | |
| fi | |
| # Check if any required job was skipped when it shouldn't be | |
| if [[ "${{ needs.changes.outputs.source }}" == "true" ]] || \ | |
| [[ "${{ needs.changes.outputs.config }}" == "true" ]] || \ | |
| [[ "${{ needs.changes.outputs.styles }}" == "true" ]]; then | |
| if [[ "${{ needs.format-and-lint.result }}" == "skipped" ]]; then | |
| echo "❌ Format and lint job was skipped" | |
| exit 1 | |
| fi | |
| fi | |
| if [[ "${{ needs.changes.outputs.source }}" == "true" ]] || [[ "${{ needs.changes.outputs.config }}" == "true" ]]; then | |
| if [[ "${{ needs.type-check.result }}" == "skipped" ]] || \ | |
| [[ "${{ needs.build.result }}" == "skipped" ]] || \ | |
| [[ "${{ needs.security-check.result }}" == "skipped" ]]; then | |
| echo "❌ Required job was skipped" | |
| exit 1 | |
| fi | |
| fi | |
| # Check test job when tests or source files changed | |
| if [[ "${{ needs.changes.outputs.tests }}" == "true" ]] || [[ "${{ needs.changes.outputs.source }}" == "true" ]]; then | |
| if [[ "${{ needs.test.result }}" == "skipped" ]]; then | |
| echo "❌ Test job was skipped when it should run" | |
| exit 1 | |
| fi | |
| fi | |
| echo "✅ All CI checks passed successfully" |