Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 163 additions & 43 deletions .github/workflows/build-tests-windows.yml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions .github/workflows/upstream-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ jobs:
mkdir -p /tmp/upstream-verifier
npm install --prefix /tmp/upstream-verifier --ignore-scripts --no-save --package-lock=false --no-audit --no-fund yaml@2.8.4
cp scripts/verify-resource-cleanup-contract.mjs /tmp/upstream-verifier/verify.mjs
cp scripts/workflow-credential-policy.mjs /tmp/upstream-verifier/workflow-credential-policy.mjs
node /tmp/upstream-verifier/verify.mjs .
}
report_error() {
Expand Down Expand Up @@ -248,6 +249,7 @@ jobs:
mkdir -p /tmp/upstream-verifier
npm install --prefix /tmp/upstream-verifier --ignore-scripts --no-save --package-lock=false --no-audit --no-fund yaml@2.8.4
cp _policy/scripts/verify-resource-cleanup-contract.mjs /tmp/upstream-verifier/verify.mjs
cp _policy/scripts/workflow-credential-policy.mjs /tmp/upstream-verifier/workflow-credential-policy.mjs
- name: Verify lifecycle contract with trusted policy
run: node /tmp/upstream-verifier/verify.mjs _candidate
- name: Install package manager and dependencies
Expand Down
204 changes: 21 additions & 183 deletions .github/workflows/validate-community-plugins.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
name: Validate Community Plugins
name: Validate Community Plugin Registry

on:
schedule:
# Run weekly on Sunday at 02:00 UTC
# Validate the checked-in registry weekly without consuming a Unity seat.
- cron: '0 2 * * 0'
workflow_dispatch:
inputs:
Expand All @@ -11,195 +11,33 @@ on:
required: false
default: ''
unity_version:
description: 'Override Unity version (empty = use plugin default)'
description: 'Validate a Unity version override (empty = use plugin default)'
required: false
default: ''

permissions:
contents: read
issues: write
permissions: {}

jobs:
load-plugins:
name: Load Plugin Registry
registry-contract:
name: Community plugin registry contract
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.parse.outputs.matrix }}
plugin_count: ${{ steps.parse.outputs.count }}
permissions:
contents: read
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Parse plugin registry
id: parse
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
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
});
}
}
persist-credentials: false

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@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Create test project
- name: Install package manager and dependencies
env:
YARN_ENABLE_HARDENED_MODE: 'false'
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@34e114876b0b11c390a56381ad16ebd13914f8d5

- name: Generate summary
uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b
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']
});
}
}
corepack enable
corepack install
yarn install --immutable

- name: Validate plugin registry
env:
PLUGIN_FILTER: ${{ inputs.plugin_filter }}
UNITY_VERSION_OVERRIDE: ${{ inputs.unity_version }}
run: yarn node scripts/community-plugin-matrix.mjs
20 changes: 8 additions & 12 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

50 changes: 31 additions & 19 deletions dist/platforms/windows/activate.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,28 @@ if ( ($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($
#
# This will activate unity, using the serial activation process.
#
Write-Output "Requesting activation"

$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-NoNewWindow `
-PassThru `
-ArgumentList "-batchmode `
-quit `
-nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-serial $Env:UNITY_SERIAL `
-projectPath c:/BlankProject `
-logfile -"

# Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
$unityHandle = $ACTIVATION_OUTPUT.Handle

while ($true) {
$MAX_ACTIVATION_ATTEMPTS = 2
$ACTIVATION_RETRY_DELAY_SECONDS = 360
for ($ACTIVATION_ATTEMPT = 1; $ACTIVATION_ATTEMPT -le $MAX_ACTIVATION_ATTEMPTS; $ACTIVATION_ATTEMPT++) {
Write-Output "Requesting activation (attempt $ACTIVATION_ATTEMPT/$MAX_ACTIVATION_ATTEMPTS)"

$ACTIVATION_OUTPUT = Start-Process -FilePath "$Env:UNITY_PATH/Editor/Unity.exe" `
-NoNewWindow `
-PassThru `
-ArgumentList "-batchmode `
-quit `
-nographics `
-username $Env:UNITY_EMAIL `
-password $Env:UNITY_PASSWORD `
-serial $Env:UNITY_SERIAL `
-projectPath c:/BlankProject `
-logfile -"

# Cache the handle so exit code works properly
# https://stackoverflow.com/questions/10262231/obtaining-exitcode-using-start-process-and-waitforexit-instead-of-wait
$unityHandle = $ACTIVATION_OUTPUT.Handle

while ($true) {
if ($ACTIVATION_OUTPUT.HasExited) {
$ACTIVATION_EXIT_CODE = $ACTIVATION_OUTPUT.ExitCode

Expand All @@ -48,6 +51,15 @@ if ( ($null -ne ${env:UNITY_SERIAL}) -and ($null -ne ${env:UNITY_EMAIL}) -and ($
}

Start-Sleep -Seconds 3
}

if ($ACTIVATION_EXIT_CODE -eq 0) {
break
}
if ($ACTIVATION_ATTEMPT -lt $MAX_ACTIVATION_ATTEMPTS) {
Write-Output "Activation failed; waiting $ACTIVATION_RETRY_DELAY_SECONDS seconds for the Unity license cooldown before one bounded retry."
Start-Sleep -Seconds $ACTIVATION_RETRY_DELAY_SECONDS
}
}
}
elseif( ($null -ne ${env:UNITY_LICENSING_SERVER}))
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"test": "node scripts/ensure-husky.mjs && vitest run",
"test:watch": "vitest",
"test:ci": "vitest run",
"test:workflow-policy": "node --test scripts/workflow-credential-policy.test.mjs",
"test:workflow-policy": "node --test scripts/*.test.mjs",
"coverage": "vitest run --coverage",
"lint": "yarn oxlint --report-unused-disable-directives",
"format": "oxfmt --write",
Expand Down
15 changes: 15 additions & 0 deletions scripts/assert-current-pr-head.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
$ErrorActionPreference = 'Stop'

$headers = @{
Accept = 'application/vnd.github+json'
Authorization = "Bearer $env:GH_TOKEN"
'X-GitHub-Api-Version' = '2022-11-28'
}
$pullRequest = Invoke-RestMethod -Uri $env:PULL_REQUEST_API_URL -Headers $headers -TimeoutSec 30
$eligible = $pullRequest.state -eq 'open' -and
$pullRequest.base.ref -eq $env:EXPECTED_BASE_REF -and
$pullRequest.head.repo.full_name -eq $env:EXPECTED_HEAD_REPOSITORY -and
$pullRequest.head.sha -eq $env:EXPECTED_HEAD_SHA
if (-not $eligible) {
throw "Refusing licensed work for stale, closed, or ineligible PR revision $env:EXPECTED_HEAD_SHA."
}
Loading
Loading