name: Run QA Test # Runs automated tests on self-hosted QA machines permissions: contents: read on: workflow_run: workflows: ["Build"] types: - completed workflow_dispatch: inputs: build_id: description: 'Build workflow run ID (e.g. For github.com/secondlife/viewer/actions/runs/1234567890 the ID is 1234567890)' required: true default: '14806728332' jobs: debug-workflow: runs-on: ubuntu-latest steps: - name: Debug Workflow Variables run: | echo "Workflow Conclusion: ${{ github.event.workflow_run.conclusion }}" echo "Workflow Head Branch: ${{ github.event.workflow_run.head_branch }}" echo "Workflow Run ID: ${{ github.event.workflow_run.id }}" echo "Head Commit Message: ${{ github.event.workflow_run.head_commit.message }}" echo "GitHub Ref: ${{ github.ref }}" echo "GitHub Ref Name: ${{ github.ref_name }}" echo "GitHub Event Name: ${{ github.event_name }}" echo "GitHub Workflow Name: ${{ github.workflow }}" install-viewer-and-run-tests: concurrency: group: ${{ github.workflow }}-${{ matrix.runner }} cancel-in-progress: false # Prevents cancellation of in-progress jobs strategy: matrix: include: - os: windows runner: qa-windows-atlas artifact: Windows-installer install-path: 'C:\viewer-automation-main' - os: windows runner: qa-dan-asus artifact: Windows-installer install-path: 'C:\viewer-automation-main' # Commented out until mac runner is available # - os: mac # runner: qa-mac-atlas # artifact: Mac-installer # install-path: '$HOME/Documents/viewer-automation' fail-fast: false runs-on: [self-hosted, "${{ matrix.runner }}"] # Run test only on successful builds of Second_Life_X branches or on manual dispatch if: > (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success' && startsWith(github.event.workflow_run.head_branch, 'Second_Life')) || github.event_name == 'workflow_dispatch' steps: # Windows-specific steps - name: Set Build ID if: matrix.os == 'windows' shell: pwsh run: | if ("${{ github.event_name }}" -eq "workflow_dispatch") { echo "BUILD_ID=${{ github.event.inputs.build_id }}" | Out-File -FilePath $env:GITHUB_ENV -Append echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append } else { echo "BUILD_ID=${{ github.event.workflow_run.id }}" | Out-File -FilePath $env:GITHUB_ENV -Append echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" | Out-File -FilePath $env:GITHUB_ENV -Append } - name: Temporarily Allow PowerShell Scripts (Windows) if: matrix.os == 'windows' shell: pwsh run: | Set-ExecutionPolicy RemoteSigned -Scope Process -Force - name: Verify viewer-automation-main Exists (Windows) if: matrix.os == 'windows' shell: pwsh run: | if (-Not (Test-Path -Path '${{ matrix.install-path }}')) { Write-Host '❌ Error: viewer-automation folder not found on runner!' exit 1 } Write-Host '✅ viewer-automation folder is provided.' - name: Verify viewer-automation-main is Up-To-Date (Windows) if: matrix.os == 'windows' shell: pwsh continue-on-error: true run: | cd ${{ matrix.install-path }} Write-Host "Checking for repository updates..." # Check if .git directory exists if (Test-Path -Path ".git") { try { # Save local changes instead of discarding them git stash push -m "Automated stash before update $(Get-Date)" Write-Host "Local changes saved (if any)" # Update the repository git pull Write-Host "✅ Repository updated successfully" # Try to restore local changes if any were stashed $stashList = git stash list if ($stashList -match "Automated stash before update") { try { git stash pop Write-Host "✅ Local changes restored successfully" } catch { Write-Host "⚠️ Conflict when restoring local changes" # Save the conflicted state in a new branch for later review $branchName = "conflict-recovery-$(Get-Date -Format 'yyyyMMdd-HHmmss')" git checkout -b $branchName Write-Host "✅ Created branch '$branchName' with conflicted state" # For test execution, revert to a clean state git reset --hard HEAD Write-Host "✅ Reset to clean state for test execution" } } } catch { Write-Host "⚠️ Could not update repository: $_" Write-Host "Continuing with existing files..." } } else { Write-Host "⚠️ Not a Git repository, using existing files" } - name: Verify Python Installation (Windows) if: matrix.os == 'windows' shell: pwsh run: | try { $pythonVersion = (python --version) Write-Host "✅ Python found: $pythonVersion" } catch { Write-Host "❌ Error: Python not found in PATH. Please install Python on this runner." exit 1 } - name: Setup Python Virtual Environment (Windows) if: matrix.os == 'windows' shell: pwsh run: | Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force cd ${{ matrix.install-path }} if (-Not (Test-Path -Path ".venv")) { Write-Host "Creating virtual environment..." python -m venv .venv } else { Write-Host "Using existing virtual environment" } # Direct environment activation to avoid script execution issues $env:VIRTUAL_ENV = "$PWD\.venv" $env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH" # Install dependencies if (Test-Path -Path "requirements.txt") { Write-Host "Installing dependencies from requirements.txt..." pip install -r requirements.txt # Install Playwright browsers - add this line Write-Host "Installing Playwright browsers..." python -m playwright install } else { pip install outleap requests behave playwright # Install Playwright browsers - add this line Write-Host "Installing Playwright browsers..." python -m playwright install } - name: Fetch & Download Installer Artifact (Windows) if: matrix.os == 'windows' shell: pwsh run: | $BUILD_ID = "${{ env.BUILD_ID }}" $ARTIFACTS_URL = "${{ env.ARTIFACTS_URL }}" # Fetch the correct artifact URL $response = Invoke-RestMethod -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}" } -Uri $ARTIFACTS_URL $ARTIFACT_NAME = ($response.artifacts | Where-Object { $_.name -eq "${{ matrix.artifact }}" }).archive_download_url if (-Not $ARTIFACT_NAME) { Write-Host "❌ Error: ${{ matrix.artifact }} artifact not found!" exit 1 } Write-Host "✅ Artifact found: $ARTIFACT_NAME" # Secure download path $DownloadPath = "$env:TEMP\secondlife-build-$BUILD_ID" New-Item -ItemType Directory -Path $DownloadPath -Force | Out-Null $InstallerPath = "$DownloadPath\installer.zip" # Download the ZIP Invoke-WebRequest -Uri $ARTIFACT_NAME -Headers @{Authorization="token ${{ secrets.GITHUB_TOKEN }}"} -OutFile $InstallerPath # Ensure download succeeded if (-Not (Test-Path $InstallerPath)) { Write-Host "❌ Error: Failed to download ${{ matrix.artifact }}.zip" exit 1 } # Set the path for other steps echo "DOWNLOAD_PATH=$DownloadPath" | Out-File -FilePath $env:GITHUB_ENV -Append - name: Extract Installer & Locate Executable (Windows) if: matrix.os == 'windows' shell: pwsh run: | $BUILD_ID = "${{ env.BUILD_ID }}" $ExtractPath = "${{ env.DOWNLOAD_PATH }}" $InstallerZip = "$ExtractPath\installer.zip" # Print paths for debugging Write-Host "Extract Path: $ExtractPath" Write-Host "Installer ZIP Path: $InstallerZip" # Verify ZIP exists before extracting if (-Not (Test-Path $InstallerZip)) { Write-Host "❌ Error: ZIP file not found at $InstallerZip!" exit 1 } Write-Host "✅ ZIP file exists and is valid. Extracting..." Expand-Archive -Path $InstallerZip -DestinationPath $ExtractPath -Force # Find installer executable $INSTALLER_PATH = (Get-ChildItem -Path $ExtractPath -Filter '*.exe' -Recurse | Select-Object -First 1).FullName if (-Not $INSTALLER_PATH -or $INSTALLER_PATH -eq "") { Write-Host "❌ Error: No installer executable found in the extracted files!" Write-Host "📂 Extracted Files:" Get-ChildItem -Path $ExtractPath -Recurse | Format-Table -AutoSize exit 1 } Write-Host "✅ Installer found: $INSTALLER_PATH" echo "INSTALLER_PATH=$INSTALLER_PATH" | Out-File -FilePath $env:GITHUB_ENV -Append - name: Install Second Life (Windows) if: matrix.os == 'windows' shell: pwsh run: | # Windows - Use Task Scheduler to bypass UAC $action = New-ScheduledTaskAction -Execute "${{ env.INSTALLER_PATH }}" -Argument "/S" $principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest $task = New-ScheduledTask -Action $action -Principal $principal Register-ScheduledTask -TaskName "SilentSLInstaller" -InputObject $task -Force Start-ScheduledTask -TaskName "SilentSLInstaller" - name: Wait for Installation to Complete (Windows) if: matrix.os == 'windows' shell: pwsh run: | Write-Host "Waiting for the Second Life installer to finish..." do { Start-Sleep -Seconds 5 $installerProcess = Get-Process | Where-Object { $_.Path -eq "${{ env.INSTALLER_PATH }}" } } while ($installerProcess) Write-Host "✅ Installation completed!" - name: Cleanup After Installation (Windows) if: matrix.os == 'windows' shell: pwsh run: | # Cleanup Task Scheduler Entry Unregister-ScheduledTask -TaskName "SilentSLInstaller" -Confirm:$false Write-Host "✅ Task Scheduler entry removed." # Delete Installer ZIP $DeletePath = "${{ env.DOWNLOAD_PATH }}\installer.zip" Write-Host "Checking if installer ZIP exists: $DeletePath" # Ensure the ZIP file exists before trying to delete it if (Test-Path $DeletePath) { Remove-Item -Path $DeletePath -Force Write-Host "✅ Successfully deleted: $DeletePath" } else { Write-Host "⚠️ Warning: ZIP file does not exist, skipping deletion." } - name: Run QA Test Script (Windows) if: matrix.os == 'windows' shell: pwsh run: | Write-Host "Running QA Test script on Windows runner: ${{ matrix.runner }}..." cd ${{ matrix.install-path }} # Activate virtual environment Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force $env:VIRTUAL_ENV = "$PWD\.venv" $env:PATH = "$env:VIRTUAL_ENV\Scripts;$env:PATH" # Set runner name as environment variable $env:RUNNER_NAME = "${{ matrix.runner }}" # Run the test script python runTests.py # Mac-specific steps - name: Set Build ID (Mac) if: matrix.os == 'mac' shell: bash run: | if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then echo "BUILD_ID=${{ github.event.inputs.build_id }}" >> $GITHUB_ENV echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.inputs.build_id }}/artifacts" >> $GITHUB_ENV else echo "BUILD_ID=${{ github.event.workflow_run.id }}" >> $GITHUB_ENV echo "ARTIFACTS_URL=https://api.github.com/repos/secondlife/viewer/actions/runs/${{ github.event.workflow_run.id }}/artifacts" >> $GITHUB_ENV fi - name: Verify viewer-automation-main Exists (Mac) if: matrix.os == 'mac' shell: bash run: | if [ ! -d "${{ matrix.install-path }}" ]; then echo "❌ Error: viewer-automation folder not found on runner!" exit 1 fi echo "✅ viewer-automation is provided." - name: Verify viewer-automation-main is Up-To-Date (Mac) if: matrix.os == 'mac' shell: bash continue-on-error: true run: | cd ${{ matrix.install-path }} echo "Checking for repository updates..." # Check if .git directory exists if [ -d ".git" ]; then # Save local changes instead of discarding them git stash push -m "Automated stash before update $(date)" echo "Local changes saved (if any)" # Update the repository git pull || echo "⚠️ Could not update repository" echo "✅ Repository updated (or attempted update)" # Try to restore local changes if any were stashed if git stash list | grep -q "Automated stash before update"; then # Try to pop the stash, but be prepared for conflicts if ! git stash pop; then echo "⚠️ Conflict when restoring local changes" # Save the conflicted state in a new branch for later review branch_name="conflict-recovery-$(date +%Y%m%d-%H%M%S)" git checkout -b "$branch_name" echo "✅ Created branch '$branch_name' with conflicted state" # For test execution, revert to a clean state git reset --hard HEAD echo "✅ Reset to clean state for test execution" else echo "✅ Local changes restored successfully" fi fi else echo "⚠️ Not a Git repository, using existing files" fi - name: Verify Python Installation (Mac) if: matrix.os == 'mac' shell: bash run: | if command -v python3 &> /dev/null; then PYTHON_VERSION=$(python3 --version) echo "✅ Python found: $PYTHON_VERSION" else echo "❌ Error: Python3 not found in PATH. Please install Python on this runner." exit 1 fi - name: Setup Python Virtual Environment (Mac) if: matrix.os == 'mac' shell: bash run: | cd ${{ matrix.install-path }} # Create virtual environment if it doesn't exist if [ ! -d ".venv" ]; then echo "Creating virtual environment..." python3 -m venv .venv else echo "Using existing virtual environment" fi # Activate virtual environment source .venv/bin/activate # Install dependencies if [ -f "requirements.txt" ]; then pip install -r requirements.txt echo "✅ Installed dependencies from requirements.txt" # Install Playwright browsers - add this line echo "Installing Playwright browsers..." python -m playwright install else pip install outleap requests behave playwright echo "⚠️ requirements.txt not found, installed basic dependencies" # Install Playwright browsers - add this line echo "Installing Playwright browsers..." python -m playwright install fi - name: Fetch & Download Installer Artifact (Mac) if: matrix.os == 'mac' shell: bash run: | # Mac-specific Bash commands response=$(curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -s ${{ env.ARTIFACTS_URL }}) ARTIFACT_NAME=$(echo $response | jq -r '.artifacts[] | select(.name=="${{ matrix.artifact }}") | .archive_download_url') if [ -z "$ARTIFACT_NAME" ]; then echo "❌ Error: ${{ matrix.artifact }} artifact not found!" exit 1 fi echo "✅ Artifact found: $ARTIFACT_NAME" # Secure download path DOWNLOAD_PATH="/tmp/secondlife-build-${{ env.BUILD_ID }}" mkdir -p $DOWNLOAD_PATH INSTALLER_PATH="$DOWNLOAD_PATH/installer.zip" # Download the ZIP curl -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" -L $ARTIFACT_NAME -o $INSTALLER_PATH # Ensure download succeeded if [ ! -f "$INSTALLER_PATH" ]; then echo "❌ Error: Failed to download ${{ matrix.artifact }}.zip" exit 1 fi # Set the path for other steps echo "DOWNLOAD_PATH=$DOWNLOAD_PATH" >> $GITHUB_ENV - name: Extract Installer & Locate Executable (Mac) if: matrix.os == 'mac' shell: bash run: | EXTRACT_PATH="${{ env.DOWNLOAD_PATH }}" INSTALLER_ZIP="$EXTRACT_PATH/installer.zip" # Debug output echo "Extract Path: $EXTRACT_PATH" echo "Installer ZIP Path: $INSTALLER_ZIP" # Verify ZIP exists if [ ! -f "$INSTALLER_ZIP" ]; then echo "❌ Error: ZIP file not found at $INSTALLER_ZIP!" exit 1 fi echo "✅ ZIP file exists and is valid. Extracting..." # Extract the ZIP unzip -o "$INSTALLER_ZIP" -d "$EXTRACT_PATH" # Find DMG file INSTALLER_PATH=$(find "$EXTRACT_PATH" -name "*.dmg" -type f | head -1) if [ -z "$INSTALLER_PATH" ]; then echo "❌ Error: No installer DMG found in the extracted files!" echo "📂 Extracted Files:" ls -la "$EXTRACT_PATH" exit 1 fi echo "✅ Installer found: $INSTALLER_PATH" echo "INSTALLER_PATH=$INSTALLER_PATH" >> $GITHUB_ENV - name: Install Second Life (Mac) if: matrix.os == 'mac' shell: bash run: | # Mac installation echo "Mounting DMG installer..." MOUNT_POINT="/tmp/secondlife-dmg" mkdir -p "$MOUNT_POINT" # Mount the DMG hdiutil attach "${{ env.INSTALLER_PATH }}" -mountpoint "$MOUNT_POINT" -nobrowse echo "✅ DMG mounted at $MOUNT_POINT" # Find the app in the mounted DMG APP_PATH=$(find "$MOUNT_POINT" -name "*.app" -type d | head -1) if [ -z "$APP_PATH" ]; then echo "❌ Error: No .app bundle found in the mounted DMG!" exit 1 fi echo "Installing application to Applications folder..." # Copy the app to the Applications folder (or specified install path) cp -R "$APP_PATH" "${{ matrix.install-path }}" # Verify the app was copied successfully if [ ! -d "${{ matrix.install-path }}/$(basename "$APP_PATH")" ]; then echo "❌ Error: Failed to install application to ${{ matrix.install-path }}!" exit 1 fi echo "✅ Application installed successfully to ${{ matrix.install-path }}" # Save mount point for cleanup echo "MOUNT_POINT=$MOUNT_POINT" >> $GITHUB_ENV - name: Wait for Installation to Complete (Mac) if: matrix.os == 'mac' shell: bash run: | echo "Waiting for installation to complete..." # Sleep to allow installation to finish (adjust as needed) sleep 30 echo "✅ Installation completed" - name: Cleanup After Installation (Mac) if: matrix.os == 'mac' shell: bash run: | # Mac cleanup # Unmount the DMG echo "Unmounting DMG..." hdiutil detach "${{ env.MOUNT_POINT }}" -force # Clean up temporary files echo "Cleaning up temporary files..." rm -rf "${{ env.DOWNLOAD_PATH }}" rm -rf "${{ env.MOUNT_POINT }}" echo "✅ Cleanup completed" - name: Run QA Test Script (Mac) if: matrix.os == 'mac' shell: bash run: | echo "Running QA Test script on Mac runner: ${{ matrix.runner }}..." cd ${{ matrix.install-path }} # Activate virtual environment source .venv/bin/activate # Set runner name as environment variable export RUNNER_NAME="${{ matrix.runner }}" # Run the test script python runTests.py # - name: Upload Test Results # if: always() # uses: actions/upload-artifact@v4 # with: # name: test-results-${{ matrix.runner }} # path: ${{ matrix.install-path }}/regressionTest/test_results.html