diff --git a/.github/workflows/README.md b/.github/workflows/README.md new file mode 100644 index 0000000..6571f60 --- /dev/null +++ b/.github/workflows/README.md @@ -0,0 +1,87 @@ +# GitHub Actions Setup Guide + +This project uses [GameCI](https://game.ci/) for Unity CI/CD. + +## Required Secrets + +You need to configure the following secrets in your GitHub repository settings: + +### Option 1: Unity License File (Recommended) + +1. **UNITY_LICENSE**: Your Unity license file content (`.ulf` file) + +To get your license file: + +```bash +# Run Unity activation locally +docker run -it --rm \ + -e UNITY_VERSION=6000.0.58f2 \ + -e UNITY_EMAIL=your@email.com \ + -e UNITY_PASSWORD=yourpassword \ + unityci/editor:ubuntu-6000.0.58f2-base-3 \ + unity-editor -quit -batchmode -nographics \ + -logFile /dev/stdout \ + -createManualActivationFile +``` + +Then activate manually at [license.unity3d.com](https://license.unity3d.com/) and add the resulting `.ulf` file content as the `UNITY_LICENSE` secret. + +### Option 2: Unity Credentials + +- **UNITY_EMAIL**: Your Unity account email +- **UNITY_PASSWORD**: Your Unity account password + +Note: This option may not work with 2FA enabled. + +## Workflows + +### unity.yml + +Main CI workflow that runs on push and pull requests: + +1. **Test Job**: Runs all EditMode tests +2. **Build Job**: Creates Windows (Mono) build (only runs if tests pass) + +### Triggers + +- Push to `main`, `develop`, or `feature/**` branches +- Pull requests to `main` or `develop` +- Manual trigger via workflow_dispatch + +## Artifacts + +- **Test Results**: Test output and coverage reports +- **Build-Windows-Mono**: Windows executable build + +## Important: ONNX Models + +ONNX model files (`.onnx`) are **not tracked in git** due to their large size (~4GB total). + +### Impact on CI + +- **Tests**: Model-dependent tests will fail without ONNX files +- **Build**: Compilation will succeed, but runtime requires models + +### Solutions + +1. **Git LFS** (Recommended for private repos): + ```bash + git lfs install + git lfs track "*.onnx" + git add .gitattributes + git add Assets/Models/*.onnx + git commit -m "Add ONNX models with LFS" + ``` + +2. **External Storage**: Download models in CI workflow from cloud storage (S3, GCS, etc.) + +3. **Skip Model Tests**: Add `[Category("RequiresModels")]` attribute to tests and exclude in CI + +### Current Workflow Behavior + +The workflow will attempt to run all tests. Tests that require ONNX models may fail if models are not available. This is expected behavior for repositories without model files. + +## Notes + +- Consider using Git LFS for large model files if full CI testing is needed +- The build artifact will not include ONNX models unless they are tracked diff --git a/.github/workflows/unity.yml b/.github/workflows/unity.yml new file mode 100644 index 0000000..5335246 --- /dev/null +++ b/.github/workflows/unity.yml @@ -0,0 +1,103 @@ +name: Unity CI + +on: + push: + branches: [ main, develop, feature/** ] + pull_request: + branches: [ main, develop ] + workflow_dispatch: + +env: + UNITY_VERSION: 6000.0.58f2 + +jobs: + test: + name: Run Tests + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + lfs: true + + - name: Cache Library + uses: actions/cache@v4 + with: + path: Library + key: Library-Test-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }} + restore-keys: | + Library-Test- + Library- + + # Free up disk space on Ubuntu + - name: Free Disk Space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/share/boost + + - name: Run EditMode Tests + uses: game-ci/unity-test-runner@v4 + id: tests + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + unityVersion: ${{ env.UNITY_VERSION }} + testMode: EditMode + artifactsPath: TestResults + checkName: Test Results + coverageOptions: 'generateAdditionalMetrics;generateHtmlReport;generateBadgeReport' + customParameters: -testFilter cat!=RequiresModels + + - name: Upload Test Results + uses: actions/upload-artifact@v4 + if: always() + with: + name: Test Results + path: TestResults + + build-windows: + name: Build Windows (Mono) + runs-on: ubuntu-latest + needs: test + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + lfs: true + + - name: Cache Library + uses: actions/cache@v4 + with: + path: Library + key: Library-Windows-${{ hashFiles('Assets/**', 'Packages/**', 'ProjectSettings/**') }} + restore-keys: | + Library-Windows- + Library- + + # Free up disk space on Ubuntu + - name: Free Disk Space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf /usr/local/share/boost + + - name: Build Windows Mono + uses: game-ci/unity-builder@v4 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + unityVersion: ${{ env.UNITY_VERSION }} + targetPlatform: StandaloneWindows64 + buildName: uCosyVoice + buildsPath: build + + - name: Upload Build Artifact + uses: actions/upload-artifact@v4 + with: + name: Build-Windows-Mono + path: build/StandaloneWindows64 diff --git a/Assets/uCosyVoice/Tests/Editor/AIInterfaceImportTest.cs b/Assets/uCosyVoice/Tests/Editor/AIInterfaceImportTest.cs index 767020f..70cac85 100644 --- a/Assets/uCosyVoice/Tests/Editor/AIInterfaceImportTest.cs +++ b/Assets/uCosyVoice/Tests/Editor/AIInterfaceImportTest.cs @@ -9,6 +9,7 @@ namespace uCosyVoice.Tests.Editor /// /// Tests for verifying ONNX model import and inference with Unity AI Interface /// + [Category("RequiresModels")] public class AIInterfaceImportTest { private const string ModelsPath = "Assets/Models/"; diff --git a/Assets/uCosyVoice/Tests/Editor/FlowTests.cs b/Assets/uCosyVoice/Tests/Editor/FlowTests.cs index 85f0b63..68b3148 100644 --- a/Assets/uCosyVoice/Tests/Editor/FlowTests.cs +++ b/Assets/uCosyVoice/Tests/Editor/FlowTests.cs @@ -75,6 +75,7 @@ public void EulerSolver_Step_ReturnsNewArray() #region FlowRunner Model Loading Tests [Test] + [Category("RequiresModels")] public void FlowRunner_ModelsExist() { var tokenEmbModel = AssetDatabase.LoadAssetAtPath(ModelsPath + "flow_token_embedding_fp16.onnx"); @@ -91,6 +92,7 @@ public void FlowRunner_ModelsExist() } [Test] + [Category("RequiresModels")] public void FlowRunner_CanInstantiate() { var tokenEmbAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "flow_token_embedding_fp16.onnx"); @@ -116,6 +118,7 @@ public void FlowRunner_CanInstantiate() } [Test] + [Category("RequiresModels")] public void FlowRunner_Process_ProducesMel() { var tokenEmbAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "flow_token_embedding_fp16.onnx"); @@ -172,6 +175,7 @@ public void FlowRunner_Process_ProducesMel() #region Integration Tests [Test] + [Category("RequiresModels")] public void Flow_HiFT_Integration_ProducesAudio() { // Load Flow models diff --git a/Assets/uCosyVoice/Tests/Editor/HiFTTests.cs b/Assets/uCosyVoice/Tests/Editor/HiFTTests.cs index 374dedc..c462c37 100644 --- a/Assets/uCosyVoice/Tests/Editor/HiFTTests.cs +++ b/Assets/uCosyVoice/Tests/Editor/HiFTTests.cs @@ -171,6 +171,7 @@ public void STFT_ISTFT_Roundtrip() #region HiFTInference Tests [Test] + [Category("RequiresModels")] public void HiFTInference_CanInstantiate() { var f0ModelAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "hift_f0_predictor_fp32.onnx"); @@ -192,6 +193,7 @@ public void HiFTInference_CanInstantiate() } [Test] + [Category("RequiresModels")] public void HiFTInference_Process_ProducesAudio() { var f0ModelAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "hift_f0_predictor_fp32.onnx"); diff --git a/Assets/uCosyVoice/Tests/Editor/LLMTests.cs b/Assets/uCosyVoice/Tests/Editor/LLMTests.cs index e53b009..dd0d68c 100644 --- a/Assets/uCosyVoice/Tests/Editor/LLMTests.cs +++ b/Assets/uCosyVoice/Tests/Editor/LLMTests.cs @@ -79,6 +79,7 @@ public void TopKSampler_Sample_RespectsK() #region LLMRunner Model Loading Tests [Test] + [Category("RequiresModels")] public void LLMRunner_ModelsExist() { var textEmbModel = AssetDatabase.LoadAssetAtPath(ModelsPath + "text_embedding_fp32.onnx"); @@ -97,6 +98,7 @@ public void LLMRunner_ModelsExist() } [Test] + [Category("RequiresModels")] public void LLMRunner_CanInstantiate() { var textEmbAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "text_embedding_fp32.onnx"); @@ -133,6 +135,7 @@ public void LLMRunner_SpecialTokens_AreCorrect() } [Test] + [Category("RequiresModels")] public void LLMRunner_Generate_ProducesTokens() { var textEmbAsset = AssetDatabase.LoadAssetAtPath(ModelsPath + "text_embedding_fp32.onnx"); @@ -196,6 +199,7 @@ public void LLMRunner_Generate_ProducesTokens() #region Full Pipeline Integration Test [Test] + [Category("RequiresModels")] public void LLM_Flow_HiFT_Integration_ProducesAudio() { // Load LLM models