name: Validate Community Plugins on: schedule: # Run weekly on Sunday at 02:00 UTC - cron: '0 2 * * 0' workflow_dispatch: inputs: plugin_filter: description: 'Filter plugins by name (regex pattern, empty = all)' required: false default: '' unity_version: description: 'Override Unity version (empty = use plugin default)' required: false default: '' permissions: contents: read issues: write jobs: load-plugins: name: Load Plugin Registry runs-on: ubuntu-latest outputs: matrix: ${{ steps.parse.outputs.matrix }} plugin_count: ${{ steps.parse.outputs.count }} steps: - uses: actions/checkout@v4 - name: Parse plugin registry id: parse uses: actions/github-script@v7 with: script: | const fs = require('fs'); const yaml = require('js-yaml'); const registry = yaml.load(fs.readFileSync('community-plugins.yml', 'utf8')); let plugins = registry.plugins || []; // Apply name filter if provided const filter = '${{ github.event.inputs.plugin_filter }}'; if (filter) { const regex = new RegExp(filter, 'i'); plugins = plugins.filter(p => regex.test(p.name)); } // Expand platform matrix const matrix = []; for (const plugin of plugins) { const platforms = plugin.platforms || ['StandaloneLinux64']; for (const platform of platforms) { matrix.push({ name: plugin.name, package: plugin.package, source: plugin.source || 'git', unity: '${{ github.event.inputs.unity_version }}' || plugin.unity || '2021.3', platform: platform, timeout: plugin.timeout || 30 }); } } core.setOutput('matrix', JSON.stringify({ include: matrix })); core.setOutput('count', matrix.length); console.log(`Found ${matrix.length} plugin-platform combinations to validate`); validate: name: '${{ matrix.name }} (${{ matrix.platform }})' needs: load-plugins if: needs.load-plugins.outputs.plugin_count > 0 runs-on: ubuntu-latest timeout-minutes: ${{ fromJson(matrix.timeout) }} strategy: fail-fast: false matrix: ${{ fromJson(needs.load-plugins.outputs.matrix) }} steps: - uses: actions/checkout@v4 - name: Create test project run: | mkdir -p test-project/Assets mkdir -p test-project/Packages mkdir -p test-project/ProjectSettings # Create minimal manifest.json if [ "${{ matrix.source }}" = "git" ]; then cat > test-project/Packages/manifest.json << 'MANIFEST' { "dependencies": { "com.unity.modules.imgui": "1.0.0", "com.unity.modules.jsonserialize": "1.0.0" } } MANIFEST # Add git package via manifest cd test-project python3 -c " import sys, json manifest = json.load(sys.stdin) manifest['dependencies']['${{ matrix.name }}'] = '${{ matrix.package }}' json.dump(manifest, sys.stdout, indent=2) " < Packages/manifest.json > Packages/manifest.tmp && mv Packages/manifest.tmp Packages/manifest.json cd .. fi # Create minimal ProjectSettings cat > test-project/ProjectSettings/ProjectVersion.txt << EOF m_EditorVersion: ${{ matrix.unity }} EOF - name: Build with unity-builder uses: ./ id: build with: projectPath: test-project targetPlatform: ${{ matrix.platform }} unityVersion: ${{ matrix.unity }} continue-on-error: true - name: Record result if: always() run: | STATUS="${{ steps.build.outcome }}" { echo "## ${{ matrix.name }} — ${{ matrix.platform }}" echo "" if [ "$STATUS" = "success" ]; then echo "✅ **PASSED** — Compiled and built successfully" else echo "❌ **FAILED** — Build or compilation failed" fi echo "" echo "- Unity: ${{ matrix.unity }}" echo "- Platform: ${{ matrix.platform }}" echo "- Source: ${{ matrix.source }}" echo "- Package: \`${{ matrix.package }}\`" } >> "$GITHUB_STEP_SUMMARY" report: name: Validation Report needs: [load-plugins, validate] if: always() runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Generate summary uses: actions/github-script@v7 with: script: | const { data: run } = await github.rest.actions.listJobsForWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: context.runId }); const validateJobs = run.jobs.filter(j => j.name.startsWith('validate')); const passed = validateJobs.filter(j => j.conclusion === 'success').length; const failed = validateJobs.filter(j => j.conclusion === 'failure').length; const total = validateJobs.length; let summary = `# Community Plugin Validation Report\n\n`; summary += `**${passed}/${total} passed** | ${failed} failed\n\n`; summary += `| Plugin | Platform | Status |\n|--------|----------|--------|\n`; for (const job of validateJobs) { const icon = job.conclusion === 'success' ? '✅' : '❌'; summary += `| ${job.name} | | ${icon} ${job.conclusion} |\n`; } await core.summary.addRaw(summary).write(); // Create or update issue if there are failures if (failed > 0) { const title = `Community Plugin Validation: ${failed} failure(s) — ${new Date().toISOString().split('T')[0]}`; const body = summary + `\n\n[Workflow Run](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})`; const { data: issues } = await github.rest.issues.listForRepo({ owner: context.repo.owner, repo: context.repo.repo, state: 'open', labels: 'community-plugin-validation' }); if (issues.length > 0) { await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issues[0].number, body: body }); } else { await github.rest.issues.create({ owner: context.repo.owner, repo: context.repo.repo, title: title, body: body, labels: ['community-plugin-validation'] }); } }