Compare commits

..

1 Commits

Author SHA1 Message Date
Frostebite
d829bfc901 chore: v5 prep — dep bumps, linux64 extension, legacy CLI removal, Cli→PluginOptions rename (#837)
* fix: remove concurrency block from reusable workflow to prevent deadlock

When integrity-check.yml calls validate-orchestrator-integration.yml via
workflow_call, both workflows resolve github.workflow to the same name
("Integrity"), creating identical concurrency groups. GitHub detects this
as a deadlock and cancels the run.

Fix: remove concurrency from the reusable workflow entirely — the caller
already manages concurrency for the group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* feat: add linux64RemoveExecutableExtension parameter (default: false)

Adds configurable control over the `.x86_64` file extension for
StandaloneLinux64 builds. Default is `false` (keep the extension),
matching Unity's native behavior.

Set `linux64RemoveExecutableExtension: true` to restore the
extensionless behavior from v4.

Rebased from kitlith's original PR #726. Default flipped for v5.

Closes #722

Co-Authored-By: kitlith <kitlith@users.noreply.github.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: bump production dependencies (minor/patch)

- @actions/cache ^4.0.0 → ^4.1.0
- @actions/github ^6.0.0 → ^6.0.1
- commander ^9.0.0 → ^9.5.0
- nanoid ^3.3.1 → ^3.3.12
- reflect-metadata ^0.1.13 → ^0.2.2
- semver ^7.5.2 → ^7.7.4
- yaml ^2.2.2 → ^2.8.4

All minor/patch bumps. Major bumps (@actions/core 3.x, nanoid 5.x ESM)
deferred to a separate PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* chore: remove legacy CLI bootstrap and unused deps

Remove InitCliMode/RunCli, @CliFunction decorator, and
CliFunctionsRepository. The only registered CLI mode was `print-input`
which is unused — all real CLI functionality lives in the orchestrator
repo now.

This drops 3 dependencies:
- commander-ts (decorator-based CLI, needed reflect-metadata)
- reflect-metadata (peer dep of commander-ts)
- commander (only used for OptionValues type)

Cli.options, Cli.isCliMode, and Cli.query remain — the orchestrator
plugin sets these directly without commander.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: rename Cli to PluginOptions, remove cli directory

Cli class was a legacy name from when unity-builder had its own CLI.
Now it's just an options bridge for plugins — renamed to PluginOptions
with a backwards-compatible Cli alias for the orchestrator.

Moved from src/model/cli/cli.ts to src/model/plugin-options.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): fix orchestrator integration test failures

Two issues:
1. jest → vitest: the repo migrated to vitest but the integration
   workflow still called `npx jest`. Changed to `npx vitest run`.

2. Git checkout corruption: when the orchestrator branch matching the
   PR doesn't exist, the first checkout fails leaving a corrupted .git
   directory. The fallback step then hits `fatal: ambiguous argument
   'HEAD'`. Fix: add `clean: true` to all fallback checkout steps so
   they wipe the broken state before re-cloning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(ci): wipe .git before fallback checkout, fix remaining jest syntax

- Add `rm -rf .git` step before fallback checkout to clear corrupted
  state when branch-matching checkout fails
- Fix unit test step: replace jest --testPathPattern with vitest
  positional filters (same fix as orchestrator PR #18)
- Replace all --detectOpenHandles --forceExit --runInBand with
  vitest --no-file-parallelism

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: simplify plugin mode check

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: kitlith <kitlith@users.noreply.github.com>
2026-05-07 04:42:29 +01:00
10 changed files with 159 additions and 74 deletions

View File

@@ -95,7 +95,7 @@ jobs:
- name: Run plugin interface unit tests - name: Run plugin interface unit tests
run: | run: |
echo "Running orchestrator-plugin unit tests..." echo "Running orchestrator-plugin unit tests..."
npx jest orchestrator-plugin --verbose --detectOpenHandles --forceExit npx vitest run orchestrator-plugin --reporter=verbose
- name: Build and pack orchestrator - name: Build and pack orchestrator
working-directory: orchestrator-standalone working-directory: orchestrator-standalone
@@ -167,6 +167,10 @@ jobs:
continue-on-error: true continue-on-error: true
id: orch-branch id: orch-branch
- name: Clean corrupted checkout
if: steps.orch-branch.outcome == 'failure'
run: rm -rf .git || true
- name: Fallback to orchestrator main branch - name: Fallback to orchestrator main branch
if: steps.orch-branch.outcome == 'failure' if: steps.orch-branch.outcome == 'failure'
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -320,9 +324,20 @@ jobs:
- name: Run orchestrator unit tests (fast, no infra) - name: Run orchestrator unit tests (fast, no infra)
timeout-minutes: 2 timeout-minutes: 2
run: >- run: >-
yarn run test yarn vitest run
--testPathPattern="orchestrator-guid|orchestrator-folders|task-parameter-serializer|follow-log-stream-service|runner-availability-service|provider-url-parser|provider-loader|provider-git-manager|orchestrator-image|orchestrator-hooks|orchestrator-github-checks|middleware-service" "orchestrator-guid"
--verbose --detectOpenHandles --forceExit --runInBand "orchestrator-folders"
"task-parameter-serializer"
"follow-log-stream-service"
"runner-availability-service"
"provider-url-parser"
"provider-loader"
"provider-git-manager"
"orchestrator-image"
"orchestrator-hooks"
"orchestrator-github-checks"
"middleware-service"
--reporter=verbose --no-file-parallelism
# --- K8s cluster setup --- # --- K8s cluster setup ---
- name: Clean up disk space before K8s tests - name: Clean up disk space before K8s tests
@@ -372,7 +387,7 @@ jobs:
# --- K8s Test 1: orchestrator-image --- # --- K8s Test 1: orchestrator-image ---
- name: Run orchestrator-image test (K8s) - name: Run orchestrator-image test (K8s)
timeout-minutes: 10 timeout-minutes: 10
run: yarn run test "orchestrator-image" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-image" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -396,7 +411,7 @@ jobs:
# --- K8s Test 2: orchestrator-kubernetes --- # --- K8s Test 2: orchestrator-kubernetes ---
- name: Run orchestrator-kubernetes test - name: Run orchestrator-kubernetes test
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-kubernetes" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-kubernetes" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -428,7 +443,7 @@ jobs:
# --- K8s Test 3: orchestrator-s3-steps --- # --- K8s Test 3: orchestrator-s3-steps ---
- name: Run orchestrator-s3-steps test (K8s) - name: Run orchestrator-s3-steps test (K8s)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-s3-steps" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-s3-steps" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -461,7 +476,7 @@ jobs:
- name: Run orchestrator-end2end-caching test (K8s) - name: Run orchestrator-end2end-caching test (K8s)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-caching" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-caching" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -500,7 +515,7 @@ jobs:
- name: Run orchestrator-end2end-retaining test (K8s) - name: Run orchestrator-end2end-retaining test (K8s)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-retaining" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-retaining" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -560,6 +575,10 @@ jobs:
continue-on-error: true continue-on-error: true
id: orch-branch id: orch-branch
- name: Clean corrupted checkout
if: steps.orch-branch.outcome == 'failure'
run: rm -rf .git || true
- name: Fallback to orchestrator main branch - name: Fallback to orchestrator main branch
if: steps.orch-branch.outcome == 'failure' if: steps.orch-branch.outcome == 'failure'
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -650,7 +669,7 @@ jobs:
# --- AWS Test 1: orchestrator-image --- # --- AWS Test 1: orchestrator-image ---
- name: Run orchestrator-image test (AWS) - name: Run orchestrator-image test (AWS)
timeout-minutes: 10 timeout-minutes: 10
run: yarn run test "orchestrator-image" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-image" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -668,7 +687,7 @@ jobs:
# --- AWS Test 2: orchestrator-environment --- # --- AWS Test 2: orchestrator-environment ---
- name: Run orchestrator-environment test (AWS) - name: Run orchestrator-environment test (AWS)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-environment" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-environment" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -686,7 +705,7 @@ jobs:
# --- AWS Test 3: orchestrator-s3-steps --- # --- AWS Test 3: orchestrator-s3-steps ---
- name: Run orchestrator-s3-steps test (AWS) - name: Run orchestrator-s3-steps test (AWS)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-s3-steps" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-s3-steps" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -704,7 +723,7 @@ jobs:
# --- AWS Test 4: orchestrator-hooks --- # --- AWS Test 4: orchestrator-hooks ---
- name: Run orchestrator-hooks test (AWS) - name: Run orchestrator-hooks test (AWS)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-hooks" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-hooks" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -722,7 +741,7 @@ jobs:
# --- AWS Test 5: orchestrator-caching --- # --- AWS Test 5: orchestrator-caching ---
- name: Run orchestrator-caching test (AWS) - name: Run orchestrator-caching test (AWS)
timeout-minutes: 60 timeout-minutes: 60
run: yarn run test "orchestrator-caching" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-caching" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -740,7 +759,7 @@ jobs:
# --- AWS Test 6: orchestrator-locking-core --- # --- AWS Test 6: orchestrator-locking-core ---
- name: Run orchestrator-locking-core test (AWS) - name: Run orchestrator-locking-core test (AWS)
timeout-minutes: 60 timeout-minutes: 60
run: yarn run test "orchestrator-locking-core" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-locking-core" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -758,7 +777,7 @@ jobs:
# --- AWS Test 7: orchestrator-locking-get-locked --- # --- AWS Test 7: orchestrator-locking-get-locked ---
- name: Run orchestrator-locking-get-locked test (AWS) - name: Run orchestrator-locking-get-locked test (AWS)
timeout-minutes: 60 timeout-minutes: 60
run: yarn run test "orchestrator-locking-get-locked" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-locking-get-locked" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -782,7 +801,7 @@ jobs:
- name: Run orchestrator-end2end-caching test (AWS) - name: Run orchestrator-end2end-caching test (AWS)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-caching" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-caching" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -801,7 +820,7 @@ jobs:
- name: Run orchestrator-end2end-retaining test (AWS) - name: Run orchestrator-end2end-retaining test (AWS)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-retaining" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-retaining" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -820,7 +839,7 @@ jobs:
- name: Run orchestrator-end2end-locking test (AWS) - name: Run orchestrator-end2end-locking test (AWS)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-locking" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-locking" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -865,6 +884,10 @@ jobs:
continue-on-error: true continue-on-error: true
id: orch-branch id: orch-branch
- name: Clean corrupted checkout
if: steps.orch-branch.outcome == 'failure'
run: rm -rf .git || true
- name: Fallback to orchestrator main branch - name: Fallback to orchestrator main branch
if: steps.orch-branch.outcome == 'failure' if: steps.orch-branch.outcome == 'failure'
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -954,7 +977,7 @@ jobs:
# --- Local Docker Test 1: orchestrator-image --- # --- Local Docker Test 1: orchestrator-image ---
- name: Run orchestrator-image test (local-docker) - name: Run orchestrator-image test (local-docker)
timeout-minutes: 10 timeout-minutes: 10
run: yarn run test "orchestrator-image" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-image" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -972,7 +995,7 @@ jobs:
# --- Local Docker Test 2: orchestrator-hooks --- # --- Local Docker Test 2: orchestrator-hooks ---
- name: Run orchestrator-hooks test (local-docker) - name: Run orchestrator-hooks test (local-docker)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-hooks" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-hooks" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -990,7 +1013,7 @@ jobs:
# --- Local Docker Test 3: orchestrator-local-persistence --- # --- Local Docker Test 3: orchestrator-local-persistence ---
- name: Run orchestrator-local-persistence test (local-docker) - name: Run orchestrator-local-persistence test (local-docker)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-local-persistence" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-local-persistence" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1008,7 +1031,7 @@ jobs:
# --- Local Docker Test 4: orchestrator-caching --- # --- Local Docker Test 4: orchestrator-caching ---
- name: Run orchestrator-caching test (local-docker) - name: Run orchestrator-caching test (local-docker)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-caching" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-caching" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1026,7 +1049,7 @@ jobs:
# --- Local Docker Test 5: orchestrator-github-checks --- # --- Local Docker Test 5: orchestrator-github-checks ---
- name: Run orchestrator-github-checks test (local-docker) - name: Run orchestrator-github-checks test (local-docker)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-github-checks" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-github-checks" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1044,7 +1067,7 @@ jobs:
# --- Local Docker Test 6: orchestrator-locking-core (with S3) --- # --- Local Docker Test 6: orchestrator-locking-core (with S3) ---
- name: Run orchestrator-locking-core test (local-docker + S3) - name: Run orchestrator-locking-core test (local-docker + S3)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-locking-core" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-locking-core" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1062,7 +1085,7 @@ jobs:
# --- Local Docker Test 7: orchestrator-locking-get-locked (with S3) --- # --- Local Docker Test 7: orchestrator-locking-get-locked (with S3) ---
- name: Run orchestrator-locking-get-locked test (local-docker + S3) - name: Run orchestrator-locking-get-locked test (local-docker + S3)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-locking-get-locked" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-locking-get-locked" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1080,7 +1103,7 @@ jobs:
# --- Local Docker Test 8: orchestrator-s3-steps (with S3) --- # --- Local Docker Test 8: orchestrator-s3-steps (with S3) ---
- name: Run orchestrator-s3-steps test (local-docker + S3) - name: Run orchestrator-s3-steps test (local-docker + S3)
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-s3-steps" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-s3-steps" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1099,7 +1122,7 @@ jobs:
- name: Run orchestrator-end2end-caching test (local-docker + S3) - name: Run orchestrator-end2end-caching test (local-docker + S3)
timeout-minutes: 60 timeout-minutes: 60
continue-on-error: true continue-on-error: true
run: yarn run test "orchestrator-end2end-caching" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-end2end-caching" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
@@ -1144,6 +1167,10 @@ jobs:
continue-on-error: true continue-on-error: true
id: orch-branch id: orch-branch
- name: Clean corrupted checkout
if: steps.orch-branch.outcome == 'failure'
run: rm -rf .git || true
- name: Fallback to orchestrator main branch - name: Fallback to orchestrator main branch
if: steps.orch-branch.outcome == 'failure' if: steps.orch-branch.outcome == 'failure'
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -1220,7 +1247,7 @@ jobs:
# --- Rclone Test --- # --- Rclone Test ---
- name: Run orchestrator-rclone-steps test - name: Run orchestrator-rclone-steps test
timeout-minutes: 30 timeout-minutes: 30
run: yarn run test "orchestrator-rclone-steps" --detectOpenHandles --forceExit --runInBand run: yarn run test "orchestrator-rclone-steps" --no-file-parallelism
env: env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}

View File

@@ -178,6 +178,11 @@ inputs:
default: 'false' default: 'false'
required: false required: false
description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.' description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.'
linux64RemoveExecutableExtension:
default: 'false'
required: false
description:
'When building for StandaloneLinux64, remove the default file extension of `.x86_64`. Set to true to restore the extensionless behavior from v4.'
outputs: outputs:
volume: volume:

View File

@@ -32,16 +32,16 @@
"node": ">=18.x" "node": ">=18.x"
}, },
"dependencies": { "dependencies": {
"@actions/cache": "^4.0.0", "@actions/cache": "^4.1.0",
"@actions/core": "^1.11.1", "@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1", "@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0", "@actions/github": "^6.0.1",
"md5": "^2.3.0", "md5": "^2.3.0",
"nanoid": "^3.3.1", "nanoid": "^3.3.12",
"semver": "^7.5.2", "semver": "^7.7.4",
"ts-md5": "^1.3.1", "ts-md5": "^1.3.1",
"unity-changeset": "^3.1.0", "unity-changeset": "^3.1.0",
"yaml": "^2.2.2" "yaml": "^2.8.4"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^17.0.23", "@types/node": "^17.0.23",

View File

@@ -117,18 +117,21 @@ describe('BuildParameters', () => {
}); });
test.each` test.each`
targetPlatform | expectedExtension | androidExportType targetPlatform | expectedExtension | androidExportType | linux64RemoveExecutableExtension
${Platform.types.Android} | ${'.apk'} | ${'androidPackage'} ${Platform.types.Android} | ${'.apk'} | ${'androidPackage'} | ${false}
${Platform.types.Android} | ${'.aab'} | ${'androidAppBundle'} ${Platform.types.Android} | ${'.aab'} | ${'androidAppBundle'} | ${true}
${Platform.types.Android} | ${''} | ${'androidStudioProject'} ${Platform.types.Android} | ${''} | ${'androidStudioProject'} | ${false}
${Platform.types.StandaloneWindows} | ${'.exe'} | ${'n/a'} ${Platform.types.StandaloneWindows} | ${'.exe'} | ${'n/a'} | ${true}
${Platform.types.StandaloneWindows64} | ${'.exe'} | ${'n/a'} ${Platform.types.StandaloneWindows64} | ${'.exe'} | ${'n/a'} | ${false}
${Platform.types.StandaloneLinux64} | ${'.x86_64'} | ${'n/a'} | ${false}
${Platform.types.StandaloneLinux64} | ${''} | ${'n/a'} | ${true}
`( `(
'appends $expectedExtension for $targetPlatform with androidExportType $androidExportType', 'appends $expectedExtension for $targetPlatform with linux64RemoveExecutableExtension=$linux64RemoveExecutableExtension',
async ({ targetPlatform, expectedExtension, androidExportType }) => { async ({ targetPlatform, expectedExtension, androidExportType, linux64RemoveExecutableExtension }) => {
vi.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform); vi.spyOn(Input, 'targetPlatform', 'get').mockReturnValue(targetPlatform);
vi.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform); vi.spyOn(Input, 'buildName', 'get').mockReturnValue(targetPlatform);
vi.spyOn(Input, 'androidExportType', 'get').mockReturnValue(androidExportType); vi.spyOn(Input, 'androidExportType', 'get').mockReturnValue(androidExportType);
vi.spyOn(Input, 'linux64RemoveExecutableExtension', 'get').mockReturnValue(linux64RemoveExecutableExtension);
await expect(BuildParameters.create()).resolves.toEqual( await expect(BuildParameters.create()).resolves.toEqual(
expect.objectContaining({ buildFile: `${targetPlatform}${expectedExtension}` }), expect.objectContaining({ buildFile: `${targetPlatform}${expectedExtension}` }),
); );

View File

@@ -6,7 +6,7 @@ import UnityVersioning from './unity-versioning';
import Versioning from './versioning'; import Versioning from './versioning';
import { GitRepoReader } from './input-readers/git-repo'; import { GitRepoReader } from './input-readers/git-repo';
import { GithubCliReader } from './input-readers/github-cli'; import { GithubCliReader } from './input-readers/github-cli';
import { Cli } from './cli/cli'; import { PluginOptions } from './plugin-options';
import GitHub from './github'; import GitHub from './github';
import * as core from '@actions/core'; import * as core from '@actions/core';
@@ -73,6 +73,7 @@ class BuildParameters {
Input.buildName, Input.buildName,
Input.targetPlatform, Input.targetPlatform,
Input.androidExportType, Input.androidExportType,
Input.linux64RemoveExecutableExtension,
); );
const editorVersion = UnityVersioning.determineUnityVersion( const editorVersion = UnityVersioning.determineUnityVersion(
Input.projectPath, Input.projectPath,
@@ -128,7 +129,7 @@ class BuildParameters {
} }
const providerStrategy = const providerStrategy =
Input.getInput('providerStrategy') || (Cli.isCliMode ? 'aws' : 'local'); Input.getInput('providerStrategy') || (PluginOptions.isPluginMode ? 'aws' : 'local');
return { return {
editorVersion, editorVersion,
@@ -181,14 +182,19 @@ class BuildParameters {
'0123456789abcdefghijklmnopqrstuvwxyz', '0123456789abcdefghijklmnopqrstuvwxyz',
4, 4,
)()}`, )()}`,
isCliMode: Cli.isCliMode, isCliMode: PluginOptions.isPluginMode,
cacheUnityInstallationOnMac: Input.cacheUnityInstallationOnMac, cacheUnityInstallationOnMac: Input.cacheUnityInstallationOnMac,
unityHubVersionOnMac: Input.unityHubVersionOnMac, unityHubVersionOnMac: Input.unityHubVersionOnMac,
dockerWorkspacePath: Input.dockerWorkspacePath, dockerWorkspacePath: Input.dockerWorkspacePath,
}; };
} }
static parseBuildFile(filename: string, platform: string, androidExportType: string): string { static parseBuildFile(
filename: string,
platform: string,
androidExportType: string,
linux64RemoveExecutableExtension: boolean,
): string {
if (Platform.isWindows(platform)) { if (Platform.isWindows(platform)) {
return `${filename}.exe`; return `${filename}.exe`;
} }
@@ -208,6 +214,10 @@ class BuildParameters {
} }
} }
if (platform === Platform.types.StandaloneLinux64 && !linux64RemoveExecutableExtension) {
return `${filename}.x86_64`;
}
return filename; return filename;
} }

View File

@@ -1,16 +0,0 @@
export class Cli {
public static options: Record<string, any> | undefined;
static get isCliMode() {
return Cli.options !== undefined && Cli.options.mode !== undefined && Cli.options.mode !== '';
}
public static query(key: string, alternativeKey: string) {
if (Cli.options && Cli.options[key] !== undefined) {
return Cli.options[key];
}
if (Cli.options && alternativeKey && Cli.options[alternativeKey] !== undefined) {
return Cli.options[alternativeKey];
}
return;
}
}

View File

@@ -353,4 +353,22 @@ describe('Input', () => {
expect(spy).toHaveBeenCalledTimes(1); expect(spy).toHaveBeenCalledTimes(1);
}); });
}); });
describe('linux64RemoveExecutableExtension', () => {
it('returns the default value', () => {
expect(Input.linux64RemoveExecutableExtension).toStrictEqual(false);
});
it('returns true when string true is passed', () => {
const spy = vi.spyOn(core, 'getInput').mockReturnValue('true');
expect(Input.linux64RemoveExecutableExtension).toStrictEqual(true);
expect(spy).toHaveBeenCalledTimes(1);
});
it('returns false when string false is passed', () => {
const spy = vi.spyOn(core, 'getInput').mockReturnValue('false');
expect(Input.linux64RemoveExecutableExtension).toStrictEqual(false);
expect(spy).toHaveBeenCalledTimes(1);
});
});
}); });

View File

@@ -1,6 +1,6 @@
import fs from 'node:fs'; import fs from 'node:fs';
import path from 'node:path'; import path from 'node:path';
import { Cli } from './cli/cli'; import { PluginOptions } from './plugin-options';
import Platform from './platform'; import Platform from './platform';
import GitHub from './github'; import GitHub from './github';
import os from 'node:os'; import os from 'node:os';
@@ -28,8 +28,8 @@ class Input {
const alternativeQuery = Input.ToEnvVarFormat(query); const alternativeQuery = Input.ToEnvVarFormat(query);
// Query input sources // Query input sources
if (Cli.query(query, alternativeQuery)) { if (PluginOptions.query(query, alternativeQuery)) {
return Cli.query(query, alternativeQuery); return PluginOptions.query(query, alternativeQuery);
} }
if (process.env[query] !== undefined) { if (process.env[query] !== undefined) {
@@ -284,6 +284,12 @@ class Input {
return Input.getInput('skipActivation')?.toLowerCase() ?? 'false'; return Input.getInput('skipActivation')?.toLowerCase() ?? 'false';
} }
static get linux64RemoveExecutableExtension(): boolean {
const input = Input.getInput('linux64RemoveExecutableExtension') ?? 'false';
return input === 'true';
}
public static ToEnvVarFormat(input: string) { public static ToEnvVarFormat(input: string) {
if (input.toUpperCase() === input) { if (input.toUpperCase() === input) {
return input; return input;

View File

@@ -0,0 +1,32 @@
/**
* Shared options bridge between unity-builder and plugins (e.g. @game-ci/orchestrator).
*
* Plugins set PluginOptions.options to pass configuration into BuildParameters
* and Input. When options are set, isPluginMode is true and query() reads
* from the options map instead of @actions/core.getInput().
*/
export class PluginOptions {
public static options: Record<string, any> | undefined;
static get isPluginMode() {
return Boolean(PluginOptions.options?.mode);
}
public static query(key: string, alternativeKey: string) {
if (PluginOptions.options && PluginOptions.options[key] !== undefined) {
return PluginOptions.options[key];
}
if (
PluginOptions.options &&
alternativeKey &&
PluginOptions.options[alternativeKey] !== undefined
) {
return PluginOptions.options[alternativeKey];
}
return;
}
}
// Backwards-compatible alias — the orchestrator still imports { Cli }
export { PluginOptions as Cli };

View File

@@ -5,7 +5,7 @@ __metadata:
version: 9 version: 9
cacheKey: 10 cacheKey: 10
"@actions/cache@npm:^4.0.0": "@actions/cache@npm:^4.1.0":
version: 4.1.0 version: 4.1.0
resolution: "@actions/cache@npm:4.1.0" resolution: "@actions/cache@npm:4.1.0"
dependencies: dependencies:
@@ -42,7 +42,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@actions/github@npm:^6.0.0": "@actions/github@npm:^6.0.1":
version: 6.0.1 version: 6.0.1
resolution: "@actions/github@npm:6.0.1" resolution: "@actions/github@npm:6.0.1"
dependencies: dependencies:
@@ -3782,7 +3782,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nanoid@npm:^3.3.1, nanoid@npm:^3.3.11": "nanoid@npm:^3.3.11, nanoid@npm:^3.3.12":
version: 3.3.12 version: 3.3.12
resolution: "nanoid@npm:3.3.12" resolution: "nanoid@npm:3.3.12"
bin: bin:
@@ -4782,10 +4782,10 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "unity-builder@workspace:." resolution: "unity-builder@workspace:."
dependencies: dependencies:
"@actions/cache": "npm:^4.0.0" "@actions/cache": "npm:^4.1.0"
"@actions/core": "npm:^1.11.1" "@actions/core": "npm:^1.11.1"
"@actions/exec": "npm:^1.1.1" "@actions/exec": "npm:^1.1.1"
"@actions/github": "npm:^6.0.0" "@actions/github": "npm:^6.0.1"
"@types/node": "npm:^17.0.23" "@types/node": "npm:^17.0.23"
"@types/semver": "npm:^7.3.9" "@types/semver": "npm:^7.3.9"
"@typescript/native-preview": "npm:^7.0.0-dev.20260505.1" "@typescript/native-preview": "npm:^7.0.0-dev.20260505.1"
@@ -4798,18 +4798,18 @@ __metadata:
js-yaml: "npm:^4.1.0" js-yaml: "npm:^4.1.0"
lint-staged: "npm:^16.4.0" lint-staged: "npm:^16.4.0"
md5: "npm:^2.3.0" md5: "npm:^2.3.0"
nanoid: "npm:^3.3.1" nanoid: "npm:^3.3.12"
node-fetch: "npm:2" node-fetch: "npm:2"
oxfmt: "npm:^0.48.0" oxfmt: "npm:^0.48.0"
oxlint: "npm:^1.63.0" oxlint: "npm:^1.63.0"
semver: "npm:^7.5.2" semver: "npm:^7.7.4"
ts-md5: "npm:^1.3.1" ts-md5: "npm:^1.3.1"
ts-node: "npm:10.8.1" ts-node: "npm:10.8.1"
typescript: "npm:4.7.4" typescript: "npm:4.7.4"
unity-changeset: "npm:^3.1.0" unity-changeset: "npm:^3.1.0"
vite: "npm:^7" vite: "npm:^7"
vitest: "npm:^4" vitest: "npm:^4"
yaml: "npm:^2.2.2" yaml: "npm:^2.8.4"
yarn-audit-fix: "npm:^9.3.8" yarn-audit-fix: "npm:^9.3.8"
dependenciesMeta: dependenciesMeta:
lefthook: lefthook:
@@ -5181,7 +5181,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"yaml@npm:^2.2.2, yaml@npm:^2.8.2": "yaml@npm:^2.8.2, yaml@npm:^2.8.4":
version: 2.8.4 version: 2.8.4
resolution: "yaml@npm:2.8.4" resolution: "yaml@npm:2.8.4"
bin: bin: