Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
5268630
feat(orchestrator): enterprise feature support — CLI provider, submod…
frostebite Mar 5, 2026
d856336
feat(orchestrator): add experimental GCP Cloud Run and Azure ACI prov…
frostebite Mar 5, 2026
f4bc5d2
feat(orchestrator): multi-storage support for GCP and Azure providers
frostebite Mar 5, 2026
786ee37
feat(orchestrator): automatic provider fallback with runner availabil…
frostebite Mar 5, 2026
8194790
feat(orchestrator): add retry-on-fallback and provider init timeout
frostebite Mar 5, 2026
d17b099
style: format changed files with prettier
frostebite Mar 5, 2026
cfac5f1
test(orchestrator): expand local cache service test coverage
frostebite Mar 5, 2026
7e9d0bf
test(orchestrator): add runner availability service tests
frostebite Mar 5, 2026
17a0ea3
test(orchestrator): add unit tests for untested core services
frostebite Mar 5, 2026
f445106
ci(orchestrator): add fast unit test gate to integrity workflow
frostebite Mar 5, 2026
a0c79bd
test(orchestrator): expand unit tests for enterprise services
frostebite Mar 5, 2026
8a41533
fix(orchestrator): use http.extraHeader for secure git authentication
frostebite Mar 5, 2026
e4c156e
feat(orchestrator): add premade secret sources and YAML definitions
frostebite Mar 5, 2026
7f89530
feat(secrets): add HashiCorp Vault as first-class premade secret source
frostebite Mar 5, 2026
cf3478c
feat(lfs): add built-in elastic-git-storage support with auto-install
frostebite Mar 5, 2026
26903e9
feat(hooks): add Unity Git Hooks integration and runHookGroups
frostebite Mar 5, 2026
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
12f2871
feat(orchestrator): CI platform providers — Remote PowerShell, GitHub…
frostebite Mar 5, 2026
7db70a7
style: fix prettier formatting and eslint errors on test files
frostebite Mar 5, 2026
4f07508
feat(orchestrator): build reliability features — git integrity, reser…
frostebite Mar 5, 2026
47670cf
feat(reliability): implement build reliability service with git integ…
frostebite Mar 5, 2026
ff56194
test(providers): add comprehensive unit tests for GitHub Actions, Git…
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
007852a
feat(cache): add child workspace isolation for multi-product CI build…
frostebite Mar 5, 2026
5e54bcd
fix(testing): use async exec for parallel test group execution
frostebite Mar 5, 2026
fe63d7b
fix(cli-provider): add timeout protection for external CLI processes
frostebite Mar 5, 2026
1f3affe
fix(secrets): prevent shell injection in secret key names and mask va…
frostebite Mar 5, 2026
f06f99b
chore: rebuild dist for cli-provider timeout changes
frostebite Mar 5, 2026
7615bbd
fix(artifacts): validate rclone availability before storage upload
frostebite Mar 5, 2026
cff7597
fix(load-balancing): add pagination limits and rate-limit detection
frostebite Mar 5, 2026
40dd436
fix(reliability): add disk space validation before build archival
frostebite Mar 5, 2026
7c0c4c2
fix(hot-runner): validate persisted registry state and add dispatcher…
frostebite Mar 5, 2026
120c3c5
fix(providers): add polling timeouts, fix credential parsing, validat…
frostebite Mar 5, 2026
4d7e871
chore: rebuild dist for provider timeout and credential fixes
frostebite Mar 5, 2026
b3bd405
fix: prettier formatting for orchestrator-folders-auth test
frostebite Mar 5, 2026
9789eb5
ci: split orchestrator integrity into parallel jobs for faster valida…
frostebite Mar 5, 2026
3976b7c
style: fix prettier formatting
frostebite Mar 5, 2026
e9c247f
style: fix prettier formatting
frostebite Mar 5, 2026
1e2bb88
style: fix prettier formatting
frostebite Mar 5, 2026
79ae558
style: fix prettier formatting
frostebite Mar 5, 2026
81ba9c3
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
b4ffa3e
ci: split orchestrator integrity into 4 parallel jobs to fix timeout
frostebite Mar 5, 2026
bcf32b4
Merge remote-tracking branch 'origin/feature/orchestrator-enterprise-…
frostebite Mar 5, 2026
4724eea
Merge remote-tracking branch 'origin/feature/cloud-run-azure-provider…
frostebite Mar 5, 2026
a16f663
Merge remote-tracking branch 'origin/feature/provider-load-balancing'…
frostebite Mar 5, 2026
011e341
Merge remote-tracking branch 'origin/feature/orchestrator-unit-tests'…
frostebite Mar 5, 2026
d53cd98
Merge remote-tracking branch 'origin/fix/secure-git-token-usage' into…
frostebite Mar 5, 2026
ae296a7
Merge remote-tracking branch 'origin/feature/premade-secret-sources' …
frostebite Mar 5, 2026
e34af96
Merge remote-tracking branch 'origin/feature/ci-platform-providers' i…
frostebite Mar 5, 2026
750c10b
Merge remote-tracking branch 'origin/feature/build-reliability' into …
frostebite Mar 5, 2026
c7422c0
Merge remote-tracking branch 'origin/ci/orchestrator-integrity-speedu…
frostebite Mar 5, 2026
415d9e2
Merge remote-tracking branch 'origin/feature/test-workflow-engine' in…
frostebite Mar 5, 2026
a864249
Merge remote-tracking branch 'origin/feature/hot-runner-protocol' int…
frostebite Mar 5, 2026
c5043df
Merge remote-tracking branch 'origin/feature/generic-artifact-system'…
frostebite Mar 5, 2026
1f5f6b7
Merge remote-tracking branch 'origin/feature/incremental-sync-protoco…
frostebite Mar 5, 2026
90d7b26
Merge remote-tracking branch 'origin/feature/community-plugin-validat…
frostebite Mar 5, 2026
2eb5bd3
Merge remote-tracking branch 'origin/feature/cli-support' into releas…
frostebite Mar 5, 2026
4979a39
chore: add integration branch update scripts for release/lts-2.0.0
frostebite Mar 5, 2026
f2e7568
ci: set macOS builds to continue-on-error
frostebite Mar 5, 2026
d82481b
chore: add release/lts-infrastructure to update-all script
frostebite Mar 5, 2026
db42c76
ci: set macOS builds to continue-on-error
frostebite Mar 5, 2026
7b8f109
fix: make git hooks opt-in only — do not modify hooks when disabled
frostebite Mar 6, 2026
488c259
test: add integration wiring and input parsing tests for enterprise f…
frostebite Mar 6, 2026
cefebca
Merge remote-tracking branch 'origin/feature/orchestrator-enterprise-…
frostebite Mar 6, 2026
b19ced7
ci: mark failed macOS builds as neutral instead of failure
frostebite Mar 7, 2026
222665a
revert: restore build-tests-mac.yml to match main
frostebite Mar 9, 2026
e2a778d
fix(test): add gitAuthMode to orchestrator-folders test mock
frostebite Mar 9, 2026
0cb3cb3
fix(ci): bump node version to 20 in integrity-check
frostebite Mar 9, 2026
39f885d
fix: downgrade yargs to ^17.7.2 and revert Node to 18 for CI compatib…
frostebite Mar 9, 2026
25dbf37
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/orchestrator-async-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ jobs:
# AWS_STACK_NAME: game-ci-github-pipelines
CHECKS_UPDATE: ${{ github.event.inputs.checksObject }}
run: |
git clone -b orchestrator-develop https://github.com/game-ci/unity-builder
git clone -b main https://github.com/game-ci/unity-builder
cd unity-builder
yarn
ls
Expand Down
1,027 changes: 584 additions & 443 deletions .github/workflows/orchestrator-integrity.yml

Large diffs are not rendered by default.

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 about 1 month ago

In general, the fix is to explicitly declare a permissions block restricting the GITHUB_TOKEN to the least privileges needed. For this specific workflow, the build-binaries job only needs to read the repository contents (for actions/checkout) and upload artifacts; upload-artifact uses workflow-scoped storage and does not require additional repository write permissions. Therefore, we can safely add permissions: contents: read to that job. The other jobs (create-checksums-and-upload and publish-npm) already have explicit permissions and don’t need changes.

Concretely, in .github/workflows/release-cli.yml, under jobs: build-binaries:, add a permissions: section (aligned with runs-on) with contents: read. No imports or additional methods are needed, as this is YAML configuration for GitHub Actions only. This change keeps existing behavior but ensures the GITHUB_TOKEN for build-binaries cannot be used to modify repository contents or other resources.

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
Comment on lines +104 to +106
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

find . -name "validate-community-plugins.yml" -o -name "community-plugins.yml" | head -20

Repository: game-ci/unity-builder

Length of output: 139


🏁 Script executed:

cat .github/workflows/validate-community-plugins.yml

Repository: game-ci/unity-builder

Length of output: 7418


🏁 Script executed:

cat .github/workflows/community-plugins.yml

Repository: game-ci/unity-builder

Length of output: 136


🌐 Web query:

Unity UPM Packages/manifest.json dependency keys format requirements package name or display name

💡 Result:

In Unity UPM:

  • In Packages/manifest.json (project manifest), the dependencies object is a map of package name → version (or other locator). The key must be the package’s registered name (the same value as the package’s "name" in its package.json), not its "displayName". [1]

  • In a package’s package.json (package manifest):

    • "name" is the official/unique identifier and must follow Unity’s naming convention (reverse-domain style). Unity explicitly notes this is not the user-friendly name. [2]
    • "displayName" is only the UI-friendly label shown in the Editor. [2]
    • "dependencies" keys are package names. [2]

Package name format rules (for "name" and thus dependency keys):

  • Uses reverse-domain notation, starts with com.<company-name>.
  • Lowercase; allowed chars: lowercase letters, digits, -, _, . (period).
  • Periods can indicate nested namespaces. [2]

Dependency value formats (project manifest):

  • Most commonly a SemVer version string (Unity doesn’t support version ranges in dependencies). [2]
  • Can also be a local disk path reference for local packages (Unity docs and vendor instructions commonly show "<package-name>": "file:..." / local mappings). [3]

Example (correct):

"dependencies": {
  "com.unity.timeline": "1.8.7",
  "com.mycompany.mypackage": "2.3.1"
}

Sources: Unity Manual on project manifest dependencies [1], Unity Manual on package manifest (name, displayName, dependency keys, naming rules) [2], Google’s Unity UPM install instructions (local/path-style dependency mapping examples) [3].

[1] (docs.unity3d.com)
[2] (docs.unity3d.com)
[3] (developers.google.com)

Citations:


🏁 Script executed:

cat community-plugins.yml | head -100

Repository: game-ci/unity-builder

Length of output: 1096


🏁 Script executed:

wc -l community-plugins.yml

Repository: game-ci/unity-builder

Length of output: 89


Use package identifiers for dependency keys, not plugin display names.

Line 104 writes dependencies as "<display name>": "<git url>" (e.g., "UniTask", "NaughtyAttributes", "Unity Atoms"). However, Unity's Packages/manifest.json requires dependency keys to be valid package identifiers in reverse-domain format (e.g., com.company.package). The current implementation will produce invalid manifests that fail validation in any Unity project.

To fix, add a packageName field to each plugin entry in community-plugins.yml containing the actual package identifier, then use it in line 104:

🔧 Suggested fix

In community-plugins.yml, add the package identifier for each plugin:

  - name: UniTask
+   packageName: com.cysharp.unitask
    package: https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask

In the matrix construction step, include packageName:

matrix.push({
  name: plugin.name,
  packageName: plugin.packageName,
  package: plugin.package,
  ...
});

Then update line 104:

-          manifest['dependencies']['${{ matrix.name }}'] = '${{ matrix.package }}'
+          manifest['dependencies']['${{ matrix.packageName }}'] = '${{ matrix.package }}'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/validate-community-plugins.yml around lines 104 - 106, The
manifest is being written using display names as dependency keys
(manifest['dependencies']['${{ matrix.name }}'] = '${{ matrix.package }}'),
which produces invalid Unity package IDs; update the matrix construction to
include a packageName (e.g., matrix.push({ name: plugin.name, packageName:
plugin.packageName, package: plugin.package, ... })) and change the manifest
write to use matrix.packageName as the dependency key instead of matrix.name so
manifest['dependencies'][ '${{ matrix.packageName }}' ] = '${{ matrix.package
}}'.

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
Comment on lines +122 to +134
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's check the workflow file exists and view the relevant section
find . -name "validate-community-plugins.yml" -type f

Repository: game-ci/unity-builder

Length of output: 115


🌐 Web query:

GitHub Actions continue-on-error behavior job conclusion success

💡 Result:

  • continue-on-error: true does not change what actually happened (the command still exits non‑zero), but it changes the recorded “conclusion” used for overall success.
  • For a step with continue-on-error: true that fails:
    • steps.<id>.outcome == 'failure' (real result, before applying continue-on-error)
    • steps.<id>.conclusion == 'success' (final result, after applying continue-on-error) [1]
  • For a job with jobs.<job_id>.continue-on-error: true (often used with matrix “experimental” jobs), the job is treated as allowed-to-fail so it won’t make the workflow fail in the normal way [2]. As a result, downstream needs.<job_id>.result can show success even when something inside that job failed (because the allowed failure is effectively “masked” for dependency/result purposes) [3].

How to fail the workflow/job based on a “continue-on-error” failure (common pattern):

  • Put continue-on-error: true on the step, then later add a step that checks steps.<id>.outcome and explicitly exit 1 if it was failure (so you keep running later steps, but still control final failure based on your own logic) [1].

Sources

  1. GitHub Docs (steps context: outcome vs conclusion, and continue-on-error behavior) [1]
  2. GitHub Docs (job-level continue-on-error, incl. matrix/experimental usage) [2]
  3. actions/toolkit issue discussing needs.<job_id>.result showing success when the job uses continue-on-error) [3]

🏁 Script executed:

# Let's read the workflow file to verify the code and check for a report job
cat -n ./.github/workflows/validate-community-plugins.yml | sed -n '110,150p'

Repository: game-ci/unity-builder

Length of output: 1839


🏁 Script executed:

# Also search for any report job or downstream jobs
grep -n "job\|report\|Report" ./.github/workflows/validate-community-plugins.yml

Repository: game-ci/unity-builder

Length of output: 486


🏁 Script executed:

# Check if there are more steps in the validate job after line 139
cat -n ./.github/workflows/validate-community-plugins.yml | sed -n '139,145p'

Repository: game-ci/unity-builder

Length of output: 310


🏁 Script executed:

# Also get the full validate job structure to see all steps
sed -n '22,141p' ./.github/workflows/validate-community-plugins.yml | tail -50

Repository: game-ci/unity-builder

Length of output: 1959


🏁 Script executed:

# Let's see the full report job implementation to understand how it reads job conclusions
sed -n '141,180p' ./.github/workflows/validate-community-plugins.yml

Repository: game-ci/unity-builder

Length of output: 1812


Failed builds are masked in the validation report due to continue-on-error: true preventing job failure.

With continue-on-error: true on the Build step (line 122), a failed build doesn't fail the job. Although the "Record result" step correctly checks steps.build.outcome and reports the failure to the step summary, the report job reads job.conclusion from the GitHub API (line 169), which remains success because no subsequent step failed. This causes failed validations to appear as ✅ passed in the final report.

Preserve summary + fail job when build fails
       - 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
+
+      - name: Fail validation job when build failed
+        if: steps.build.outcome != 'success'
+        run: exit 1
🧰 Tools
🪛 actionlint (1.7.11)

[error] 126-126: shellcheck reported issue in this script: SC2086:info:10:40: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:11:46: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:12:42: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:13:48: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:2:58: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:3:12: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:5:60: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:7:56: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2086:info:9:12: Double quote to prevent globbing and word splitting

(shellcheck)


[error] 126-126: shellcheck reported issue in this script: SC2129:style:9:1: Consider using { cmd1; cmd2; } >> file instead of individual redirects

(shellcheck)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/validate-community-plugins.yml around lines 122 - 134, The
Build step currently uses continue-on-error: true which masks failures from
job.conclusion; remove continue-on-error: true from the Build step so a failing
build actually fails the job (job.conclusion) while keeping the "Record result"
step (if: always()) to append the summary using steps.build.outcome;
alternatively, if you must not fail the job immediately, add a short follow-up
step that runs when always() and exits non-zero when steps.build.outcome !=
"success" to force the job to fail so job.conclusion reflects real failures
(references: continue-on-error, "Record result" step, steps.build.outcome,
job.conclusion).

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