refactor(trends): extract buildDerivedConfigs helper + perf fixes #42661
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: MCP UI Apps CI | |
| on: | |
| push: | |
| branches: [master] | |
| pull_request: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 5 | |
| name: Determine need to run UI Apps checks | |
| outputs: | |
| ui-apps: ${{ steps.filter.outputs.ui-apps || 'true' }} | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1 | |
| id: app-token | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| with: | |
| client-id: ${{ secrets.GH_APP_POSTHOG_PATHS_FILTER_APP_ID }} | |
| private-key: ${{ secrets.GH_APP_POSTHOG_PATHS_FILTER_PRIVATE_KEY }} | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| if: github.event_name != 'push' | |
| with: | |
| token: ${{ steps.app-token.outputs.token || github.token }} | |
| filters: | | |
| ui-apps: | |
| - 'services/mcp/src/ui-apps/**' | |
| - 'services/mcp/src/resources/ui-apps.generated.ts' | |
| - 'services/mcp/vite.ui-apps.config.ts' | |
| - 'services/mcp/scripts/build-ui-apps.ts' | |
| - 'services/mcp/scripts/generate-ui-apps.ts' | |
| - 'services/mcp/scripts/yaml-config-schema.ts' | |
| - 'products/*/mcp/**' | |
| - 'services/mcp/definitions/*.yaml' | |
| - 'common/mosaic/**' | |
| - '.github/workflows/ci-mcp-ui-apps.yml' | |
| build-and-report: | |
| name: Build and validate UI Apps | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 10 | |
| needs: changes | |
| if: needs.changes.outputs.ui-apps == 'true' | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v6 | |
| - name: Install uv # Needed for formatting generated code and running scripts | |
| uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0 | |
| with: | |
| version: '0.10.2' # pinned: unpinned setup-uv calls GH API on every job, exhausts rate limit | |
| enable-cache: true | |
| cache-dependency-glob: uv.lock | |
| save-cache: ${{ github.ref == 'refs/heads/master' }} | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5.0.0 | |
| - name: Fix node-gyp permissions | |
| run: chmod +x ~/setup-pnpm/node_modules/.pnpm/pnpm@*/node_modules/pnpm/dist/node_modules/node-gyp/gyp/gyp_main.py | |
| - name: Setup Node.js | |
| uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0 | |
| with: | |
| node-version-file: .nvmrc | |
| cache: 'pnpm' | |
| - name: Install dependencies | |
| run: pnpm --filter=@posthog/mcp... --filter='./products/*' install --frozen-lockfile | |
| - name: Check generated UI apps are up to date | |
| run: | | |
| cd services/mcp && pnpm run generate:ui-apps | |
| if ! git diff --exit-code -- src/ui-apps/apps/generated/ src/resources/ui-apps.generated.ts; then | |
| echo "" | |
| echo "::warning::The following generated files are out of date:" | |
| git diff --name-only -- src/ui-apps/apps/generated/ src/resources/ui-apps.generated.ts | |
| echo "" | |
| echo "::error::Generated UI app files are out of date. Run 'pnpm --filter=@posthog/mcp run generate:ui-apps' and commit the result." | |
| exit 1 | |
| fi | |
| - name: Build UI Apps | |
| run: cd services/mcp && pnpm run build:ui-apps | |
| - name: Validate build output | |
| run: | | |
| # Each app should produce main.js and styles.css | |
| # Use find to recurse into wrapper dirs like generated/ | |
| missing=0 | |
| for app_dir in $(find services/mcp/public/ui-apps -mindepth 1 -type d ! -name generated | sort); do | |
| app=$(basename "$app_dir") | |
| if [ ! -f "$app_dir/main.js" ]; then | |
| echo "::error::Missing main.js for $app" | |
| missing=1 | |
| fi | |
| if [ ! -f "$app_dir/styles.css" ]; then | |
| echo "::error::Missing styles.css for $app" | |
| missing=1 | |
| fi | |
| done | |
| if [ "$missing" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| echo "All UI Apps built successfully." | |
| - name: Post size report comment | |
| if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository | |
| uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 | |
| with: | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| script: | | |
| const fs = require('fs'); | |
| const path = require('path'); | |
| const marker = '<!-- mcp-ui-apps-size-report -->'; | |
| const distDir = 'services/mcp/public/ui-apps'; | |
| const rows = []; | |
| // Recursively collect app dirs (those containing main.js) | |
| function collectApps(dir) { | |
| for (const entry of fs.readdirSync(dir).sort()) { | |
| const full = path.join(dir, entry); | |
| if (!fs.statSync(full).isDirectory()) continue; | |
| const jsPath = path.join(full, 'main.js'); | |
| if (fs.existsSync(jsPath)) { | |
| const cssPath = path.join(full, 'styles.css'); | |
| const jsSize = (fs.statSync(jsPath).size / 1024).toFixed(1); | |
| const cssSize = fs.existsSync(cssPath) ? (fs.statSync(cssPath).size / 1024).toFixed(1) : '—'; | |
| rows.push(`| ${entry} | ${jsSize} KB | ${cssSize} KB |`); | |
| } else { | |
| collectApps(full); | |
| } | |
| } | |
| } | |
| collectApps(distDir); | |
| if (rows.length === 0) return; | |
| const table = `| App | JS | CSS |\n|-----|-----|-----|\n${rows.join('\n')}`; | |
| const body = `${marker}\n### MCP UI Apps size report\n\n${table}`; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => c.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body, | |
| }); | |
| } | |
| ci_mcp_ui_apps: | |
| needs: [changes, build-and-report] | |
| name: MCP UI Apps CI | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 5 | |
| if: always() | |
| steps: | |
| - name: Check results | |
| run: | | |
| if [[ "${{ needs.changes.result }}" == "failure" ]]; then | |
| echo "Change detection job failed." | |
| exit 1 | |
| fi | |
| if [[ "${{ needs.changes.outputs.ui-apps }}" != "true" ]]; then | |
| echo "UI Apps checks were skipped (no relevant changes)." | |
| exit 0 | |
| fi | |
| if [[ "${{ needs.build-and-report.result }}" != "success" && "${{ needs.build-and-report.result }}" != "skipped" ]]; then | |
| echo "UI Apps build failed." | |
| exit 1 | |
| fi | |
| echo "All MCP UI Apps checks passed." |