Skip to content

refactor(trends): extract buildDerivedConfigs helper + perf fixes #42661

refactor(trends): extract buildDerivedConfigs helper + perf fixes

refactor(trends): extract buildDerivedConfigs helper + perf fixes #42661

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."