Skip to content
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
8d81236
feat(orchestrator): add test workflow engine placeholder
frostebite Mar 5, 2026
49b37f7
feat(orchestrator): add hot runner protocol placeholder
frostebite Mar 5, 2026
b3e1639
feat(orchestrator): generic artifact system — output types, manifests…
frostebite Mar 5, 2026
3033ee0
feat(orchestrator): incremental sync protocol — git delta, direct inp…
frostebite Mar 5, 2026
ccbe1bc
feat: community plugin validation workflow (#800)
frostebite Mar 5, 2026
1bb31f3
feat(hot-runner): implement hot runner protocol with registry, health…
frostebite Mar 5, 2026
aa2e05d
feat(artifacts): complete generic artifact system with upload handler…
frostebite Mar 5, 2026
1186717
feat(testing): implement test workflow engine with YAML suites, taxon…
frostebite Mar 5, 2026
4870fb5
feat(sync): complete incremental sync protocol with storage-pull, sta…
frostebite Mar 5, 2026
5e54bcd
fix(testing): use async exec for parallel test group execution
frostebite Mar 5, 2026
7615bbd
fix(artifacts): validate rclone availability before storage upload
frostebite Mar 5, 2026
7c0c4c2
fix(hot-runner): validate persisted registry state and add dispatcher…
frostebite Mar 5, 2026
1e2bb88
style: fix prettier formatting
frostebite Mar 5, 2026
5a42214
feat: add official game-ci CLI with build, activate, and orchestrate …
frostebite Mar 5, 2026
5bdcf12
feat(cli): add npm publish workflow and CLI tests
frostebite Mar 5, 2026
79d12aa
feat(cli): add release workflow, install scripts, and self-update com…
frostebite Mar 5, 2026
280a10d
fix(cli): address review findings — exit codes, missing inputs, null …
frostebite Mar 5, 2026
f42930d
Merge remote-tracking branch 'origin/feature/test-workflow-engine' in…
frostebite Mar 5, 2026
8d670d7
Merge remote-tracking branch 'origin/feature/hot-runner-protocol' int…
frostebite Mar 5, 2026
708e6d7
Merge remote-tracking branch 'origin/feature/generic-artifact-system'…
frostebite Mar 5, 2026
8b05655
Merge remote-tracking branch 'origin/feature/incremental-sync-protoco…
frostebite Mar 5, 2026
d81990c
Merge remote-tracking branch 'origin/feature/community-plugin-validat…
frostebite Mar 5, 2026
9f0c5b3
Merge remote-tracking branch 'origin/feature/cli-support' into releas…
frostebite Mar 5, 2026
4c91a33
chore: add integration branch update scripts for release/next-gen
frostebite Mar 5, 2026
db27f91
chore: add release/lts-infrastructure to update-all script
frostebite Mar 5, 2026
676855f
ci: set macOS builds to continue-on-error
frostebite Mar 5, 2026
78a7f6c
ci: mark failed macOS builds as neutral instead of failure
frostebite Mar 7, 2026
c0ca4b6
revert: restore build-tests-mac.yml to match main
frostebite Mar 9, 2026
b774d7f
fix(test): add gitAuthMode to orchestrator-folders test mock
frostebite Mar 9, 2026
0633ca9
fix(ci): bump node version to 20 in integrity-check
frostebite Mar 9, 2026
bf25e1f
fix: downgrade yargs to ^17.7.2 for Node 18 compatibility
frostebite Mar 9, 2026
4b44327
fix: replace orchestrator-develop branch references with main
frostebite Mar 9, 2026
d53ed6c
fix: remove stale merge conflict marker from action.yml
frostebite Mar 9, 2026
bc9332e
refactor(cli): move cache command under orchestrate subcommand
frostebite Mar 9, 2026
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
1 change: 1 addition & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"root": true,
"plugins": ["jest", "@typescript-eslint", "prettier", "unicorn"],
"extends": ["plugin:unicorn/recommended", "plugin:github/recommended", "plugin:prettier/recommended"],
"parser": "@typescript-eslint/parser",
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/integrity-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '20'
- run: yarn
- run: yarn lint
- run: yarn test:ci --coverage
Expand Down
170 changes: 170 additions & 0 deletions .github/workflows/release-cli.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
name: Release CLI

on:
release:
types: [published]
workflow_dispatch:
inputs:
tag:
description: 'Release tag to build (e.g., v2.0.0). Uses latest release if empty.'
required: false
type: string
publish-npm:
description: 'Publish to npm'
required: false
default: false
type: boolean

concurrency:
group: ${{ github.workflow }}-${{ github.event.release.tag_name || inputs.tag || github.ref }}
cancel-in-progress: true

jobs:
build-binaries:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- target: linux-x64
os: ubuntu-latest
pkg-target: node20-linux-x64
binary-name: game-ci-linux-x64
- target: linux-arm64
os: ubuntu-latest
pkg-target: node20-linux-arm64
binary-name: game-ci-linux-arm64
- target: macos-x64
os: macos-latest
pkg-target: node20-macos-x64
binary-name: game-ci-macos-x64
- target: macos-arm64
os: macos-latest
pkg-target: node20-macos-arm64
binary-name: game-ci-macos-arm64
- target: windows-x64
os: windows-latest
pkg-target: node20-win-x64
binary-name: game-ci-windows-x64.exe
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}

- uses: actions/setup-node@v4
with:
node-version: '20'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build TypeScript
run: yarn build

- name: Verify CLI before packaging
run: node lib/cli.js version

- name: Build standalone binary
run: npx pkg lib/cli.js --target ${{ matrix.pkg-target }} --output ${{ matrix.binary-name }} --compress GZip

- name: Verify standalone binary (non-cross-compiled)
if: |
(matrix.target == 'linux-x64' && runner.os == 'Linux') ||
(matrix.target == 'macos-arm64' && runner.os == 'macOS' && runner.arch == 'ARM64') ||
(matrix.target == 'macos-x64' && runner.os == 'macOS' && runner.arch == 'X64') ||
(matrix.target == 'windows-x64' && runner.os == 'Windows')
run: ./${{ matrix.binary-name }} version
shell: bash

- uses: actions/upload-artifact@v4
with:
name: binary-${{ matrix.target }}
path: ${{ matrix.binary-name }}
retention-days: 5

create-checksums-and-upload:
Comment on lines +24 to +86

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 16 days ago

In general, the fix is to explicitly define permissions: for each job (or at the workflow root) to avoid relying on repository defaults. For the build-binaries job, it only needs to read source code and use artifacts; it does not need to write to repository contents or perform other privileged operations. Therefore, we can safely grant it contents: read as a minimal token permission.

The best targeted fix is to add a permissions: section under the build-binaries job definition, right after the runs-on: or before strategy:. We’ll set contents: read because the job checks out the repository and builds binaries but does not modify GitHub resources. Other jobs already have their own explicit permissions blocks and do not need changes.

Concretely, in .github/workflows/release-cli.yml, under jobs: build-binaries:, add:

permissions:
  contents: read

indented to align with other job-level keys. No imports or additional definitions are required, and this change does not affect existing functionality.

Suggested changeset 1
.github/workflows/release-cli.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/release-cli.yml b/.github/workflows/release-cli.yml
--- a/.github/workflows/release-cli.yml
+++ b/.github/workflows/release-cli.yml
@@ -23,6 +23,8 @@
   build-binaries:
     name: Build ${{ matrix.target }}
     runs-on: ${{ matrix.os }}
+    permissions:
+      contents: read
     strategy:
       fail-fast: false
       matrix:
EOF
@@ -23,6 +23,8 @@
build-binaries:
name: Build ${{ matrix.target }}
runs-on: ${{ matrix.os }}
permissions:
contents: read
strategy:
fail-fast: false
matrix:
Copilot is powered by AI and may make mistakes. Always verify output.
name: Checksums and release upload
needs: build-binaries
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/download-artifact@v4
with:
path: binaries
pattern: binary-*
merge-multiple: true

- name: List binaries
run: ls -la binaries/

- name: Generate SHA256 checksums
run: |
cd binaries
sha256sum game-ci-* > checksums.txt
echo "=== checksums.txt ==="
cat checksums.txt

- name: Determine release tag
id: tag
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "tag=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
elif [ -n "${{ inputs.tag }}" ]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "No release tag available. Skipping upload."
echo "tag=" >> "$GITHUB_OUTPUT"
fi

- name: Upload binaries to release
if: steps.tag.outputs.tag != ''
env:
GH_TOKEN: ${{ github.token }}
run: |
cd binaries
for f in game-ci-* checksums.txt; do
echo "Uploading $f..."
gh release upload "${{ steps.tag.outputs.tag }}" "$f" \
--repo "${{ github.repository }}" \
--clobber
done

publish-npm:
name: Publish to npm
needs: build-binaries
runs-on: ubuntu-latest
if: >-
(github.event_name == 'release') || (github.event_name == 'workflow_dispatch' && inputs.publish-npm)
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag || github.ref }}

- uses: actions/setup-node@v4
with:
node-version: '20'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build
run: yarn build

- name: Run tests
run: yarn test

- name: Verify CLI
run: |
node lib/cli.js version
node lib/cli.js --help

- name: Publish to npm
run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
203 changes: 203 additions & 0 deletions .github/workflows/validate-community-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
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
cat Packages/manifest.json | 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.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 }}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$STATUS" = "success" ]; then
echo "✅ **PASSED** — Compiled and built successfully" >> $GITHUB_STEP_SUMMARY
else
echo "❌ **FAILED** — Build or compilation failed" >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "- Unity: ${{ matrix.unity }}" >> $GITHUB_STEP_SUMMARY
echo "- Platform: ${{ matrix.platform }}" >> $GITHUB_STEP_SUMMARY
echo "- Source: ${{ matrix.source }}" >> $GITHUB_STEP_SUMMARY
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']
});
}
}
Loading
Loading