feat: add settings schema validation #14
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: Cross-Platform Build | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| reason: | |
| description: 'Reason for running this build' | |
| required: false | |
| default: 'Manual verification' | |
| issue_comment: | |
| types: [created] | |
| milestone: | |
| types: [closed] | |
| schedule: | |
| - cron: '0 4 * * 1' # Every Monday 4:00 UTC | |
| jobs: | |
| # Post "building..." comment immediately | |
| notify-start: | |
| if: >- | |
| github.event_name != 'issue_comment' || | |
| (github.event.comment.body == '/build' && | |
| github.event.issue.state == 'open') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| outputs: | |
| comment-id: ${{ steps.comment.outputs.comment-id }} | |
| steps: | |
| - name: React to comment | |
| if: github.event_name == 'issue_comment' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| await github.rest.reactions.createForIssueComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: context.payload.comment.id, | |
| content: 'rocket' | |
| }); | |
| - name: Post progress comment | |
| id: comment | |
| if: github.event_name == 'issue_comment' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const { data } = await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.payload.issue.number, | |
| body: `⏳ **Cross-Platform Build started...**\n\n| Platform | Status |\n|----------|--------|\n| macOS | 🔄 building... |\n| Windows | 🔄 building... |\n\n[View live logs](${runUrl})`, | |
| }); | |
| core.setOutput('comment-id', data.id); | |
| build: | |
| needs: notify-start | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: macos-latest | |
| name: macOS | |
| - platform: windows-latest | |
| name: Windows | |
| name: ${{ matrix.name }} Build | |
| runs-on: ${{ matrix.platform }} | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - uses: actions/setup-node@v4 | |
| with: | |
| node-version: 24 | |
| - uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - uses: dtolnay/rust-toolchain@stable | |
| - name: Add macOS universal target | |
| if: matrix.platform == 'macos-latest' | |
| run: rustup target add x86_64-apple-darwin | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| workspaces: desktop/src-tauri -> target | |
| - name: Setup Emscripten | |
| uses: mymindstorm/setup-emsdk@v14 | |
| with: | |
| version: 5.0.0 | |
| - name: Setup ccache (non-Windows) | |
| if: matrix.platform != 'windows-latest' | |
| uses: hendrikmuhs/ccache-action@v1.2 | |
| with: | |
| key: ${{ matrix.platform }}-cross | |
| max-size: 1G | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Copy esbuild.wasm to desktop public | |
| shell: bash | |
| run: cp $(find node_modules/.pnpm -name 'esbuild.wasm' -path '*/esbuild-wasm/*' | head -1) desktop/public/esbuild.wasm | |
| # Type-check & test SDK first (editor depends on it) | |
| - name: Type-check SDK | |
| run: pnpm --filter ./sdk exec tsc --noEmit | |
| - name: Run SDK tests | |
| run: pnpm --filter ./sdk exec vitest run | |
| - name: Build SDK | |
| run: pnpm --filter ./sdk build | |
| # Now editor (depends on SDK build output) | |
| - name: Type-check Editor | |
| run: pnpm --filter ./editor exec tsc --noEmit | |
| - name: Run Editor tests | |
| run: pnpm --filter ./editor exec vitest run | |
| - name: Build Editor | |
| run: pnpm --filter ./editor build | |
| # Full engine build (WASM + SDK) | |
| - name: Build engine (WASM + SDK) | |
| run: node build-tools/cli.js build -t all | |
| # Toolchain packaging | |
| - name: Package toolchain | |
| run: node build-tools/cli.js toolchain | |
| # Rust compilation check (Tauri app) | |
| - name: Tauri build check | |
| shell: bash | |
| run: cd desktop/src-tauri && cargo check | |
| # Artifact size report | |
| - name: Collect artifact sizes | |
| id: sizes | |
| shell: bash | |
| run: | | |
| report="" | |
| if [ -f sdk/dist/index.bundled.js ]; then | |
| size=$(du -sh sdk/dist/index.bundled.js | cut -f1) | |
| report+="| SDK bundle | $size |"$'\n' | |
| fi | |
| for f in desktop/public/*.wasm; do | |
| if [ -f "$f" ]; then | |
| name=$(basename "$f") | |
| size=$(du -sh "$f" | cut -f1) | |
| report+="| WASM ($name) | $size |"$'\n' | |
| fi | |
| done | |
| if [ -d desktop/src-tauri/toolchain ]; then | |
| total=$(du -sh desktop/src-tauri/toolchain | cut -f1) | |
| report+="| Toolchain (total) | $total |"$'\n' | |
| if [ -d desktop/src-tauri/toolchain/engine-src ]; then | |
| size=$(du -sh desktop/src-tauri/toolchain/engine-src | cut -f1) | |
| report+="| - engine-src | $size |"$'\n' | |
| fi | |
| if [ -d desktop/src-tauri/toolchain/cmake ]; then | |
| size=$(du -sh desktop/src-tauri/toolchain/cmake | cut -f1) | |
| report+="| - cmake | $size |"$'\n' | |
| fi | |
| fi | |
| if [ -f desktop/public/esbuild.wasm ]; then | |
| size=$(du -sh desktop/public/esbuild.wasm | cut -f1) | |
| report+="| esbuild.wasm | $size |"$'\n' | |
| fi | |
| echo "## Artifact Size Report (${{ matrix.name }})" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Artifact | Size |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|------|" >> $GITHUB_STEP_SUMMARY | |
| echo "$report" >> $GITHUB_STEP_SUMMARY | |
| # Save for final report | |
| EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) | |
| echo "report<<$EOF" >> $GITHUB_OUTPUT | |
| echo "$report" >> $GITHUB_OUTPUT | |
| echo "$EOF" >> $GITHUB_OUTPUT | |
| # Update the progress comment with final results | |
| report: | |
| needs: [notify-start, build] | |
| if: always() && github.event_name == 'issue_comment' && needs.notify-start.outputs.comment-id | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| steps: | |
| - name: Update progress comment with results | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const jobs = await github.rest.actions.listJobsForWorkflowRun({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| run_id: context.runId, | |
| }); | |
| const buildJobs = jobs.data.jobs.filter(j => | |
| j.name.includes('Build') && !j.name.includes('notify') && !j.name.includes('report') | |
| ); | |
| const allPassed = buildJobs.every(j => j.conclusion === 'success'); | |
| const header = allPassed | |
| ? '✅ **Cross-Platform Build passed!**' | |
| : '❌ **Cross-Platform Build failed**'; | |
| const lines = buildJobs.map(j => { | |
| const icon = j.conclusion === 'success' ? '✅' : '❌'; | |
| const dur = j.completed_at && j.started_at | |
| ? Math.round((new Date(j.completed_at) - new Date(j.started_at)) / 1000) | |
| : 0; | |
| const mins = Math.floor(dur / 60); | |
| const secs = dur % 60; | |
| const time = mins > 0 ? `${mins}m${secs}s` : `${secs}s`; | |
| return `| ${j.name} | ${icon} ${j.conclusion || 'unknown'} | ${time} |`; | |
| }); | |
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | |
| const body = [ | |
| header, | |
| '', | |
| '| Platform | Status | Duration |', | |
| '|----------|--------|----------|', | |
| ...lines, | |
| '', | |
| `[View full run](${runUrl})`, | |
| ].join('\n'); | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: ${{ needs.notify-start.outputs.comment-id }}, | |
| body, | |
| }); |