diff options
Diffstat (limited to '.github')
-rw-r--r-- | .github/pull_request_template.md | 37 | ||||
-rw-r--r-- | .github/workflows/.gitattributes | 1 | ||||
-rw-r--r-- | .github/workflows/build.yaml | 13 | ||||
-rw-r--r-- | .github/workflows/check-pr.yaml | 21 | ||||
-rw-r--r-- | .github/workflows/cla.yaml | 4 | ||||
-rw-r--r-- | .github/workflows/qatest.yaml | 598 | ||||
-rw-r--r-- | .github/workflows/tag-release.yaml | 7 |
7 files changed, 672 insertions, 9 deletions
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..5fe8870337 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,37 @@ +## Description + +<!-- +Describe what this PR changes or adds. Include any relevant context or motivation. +--> + +## Related Issues + +- [ ] **Please link to a relevant GitHub issue for additional context.** + - **Bug Fix:** Link to an issue that includes reproduction steps and testing guidance. + - **Feature/Enhancement:** Link to an issue with a write-up, rationale, and requirements. + +Issue Link: <!-- e.g., closes #123 or relates to #456 --> + +--- + +## Checklist + +Please ensure the following before requesting review: + +- [ ] I have provided a clear title and detailed description for this pull request. +- [ ] If useful, I have included media such as screenshots and video to show off my changes. +- [ ] The PR is linked to a relevant issue with sufficient context. +- [ ] I have tested the changes locally and verified they work as intended. +- [ ] All new and existing tests pass. +- [ ] Code follows the project's style guidelines. +- [ ] Documentation has been updated if needed. +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] I have reviewed the [contributing guidelines](https://github.com/secondlife/viewer/blob/develop/CONTRIBUTING.md). + +--- + +## Additional Notes + +<!-- +Add any other information, screenshots, or suggestions for reviewers here. +--> diff --git a/.github/workflows/.gitattributes b/.github/workflows/.gitattributes new file mode 100644 index 0000000000..6dc1cca42a --- /dev/null +++ b/.github/workflows/.gitattributes @@ -0,0 +1 @@ +qatest.yaml -text eol=crlf diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 2cb94c9c90..4bf2af644a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -42,8 +42,8 @@ jobs: needs: setup strategy: matrix: - runner: [windows-large, macos-12-xl] - configuration: ${{ fromJSON(needs.setup.outputs.configurations) }} + runner: ${{ fromJson((github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/Second_Life')) && '["windows-large","macos-15-xlarge"]' || '["windows-latest","macos-15"]') }} + configuration: ${{ fromJson(needs.setup.outputs.configurations) }} runs-on: ${{ matrix.runner }} outputs: viewer_channel: ${{ steps.build.outputs.viewer_channel }} @@ -64,7 +64,7 @@ jobs: # autobuild-package.xml. AUTOBUILD_VCS_INFO: "true" AUTOBUILD_VSVER: "170" - DEVELOPER_DIR: "/Applications/Xcode_14.0.1.app/Contents/Developer" + DEVELOPER_DIR: "/Applications/Xcode_16.1.app/Contents/Developer" # Ensure that Linden viewer builds engage Bugsplat. BUGSPLAT_DB: ${{ needs.setup.outputs.bugsplat_db }} build_coverity: false @@ -218,8 +218,10 @@ jobs: prefix=${ba[0]} if [ "$prefix" == "project" ]; then IFS='_' read -ra prj <<< "${ba[1]}" + prj_str="${prj[*]}" # uppercase first letter of each word - export viewer_channel="Second Life Project ${prj[*]^}" + capitalized=$(echo "$prj_str" | awk '{for (i=1; i<=NF; i++) $i = toupper(substr($i,1,1)) substr($i,2); print}') + export viewer_channel="Second Life Project $capitalized" elif [[ "$prefix" == "release" || "$prefix" == "main" ]]; then export viewer_channel="Second Life Release" @@ -304,7 +306,7 @@ jobs: AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} needs: build - runs-on: windows-large + runs-on: windows-latest steps: - name: Sign and package Windows viewer if: env.AZURE_KEY_VAULT_URI && env.AZURE_CERT_NAME && env.AZURE_CLIENT_ID && env.AZURE_CLIENT_SECRET && env.AZURE_TENANT_ID @@ -455,7 +457,6 @@ jobs: prerelease: true generate_release_notes: true target_commitish: ${{ github.sha }} - previous_tag: release append_body: true fail_on_unmatched_files: true files: | diff --git a/.github/workflows/check-pr.yaml b/.github/workflows/check-pr.yaml new file mode 100644 index 0000000000..a5cee9157c --- /dev/null +++ b/.github/workflows/check-pr.yaml @@ -0,0 +1,21 @@ +name: Check PR + +on: + pull_request: + types: [opened, edited, reopened, synchronize] + +permissions: + contents: read + +jobs: + check-description: + runs-on: ubuntu-latest + steps: + - name: Check PR description + uses: actions/github-script@v7 + with: + script: | + const description = context.payload.pull_request.body || ''; + if (description.trim().length < 20) { + core.setFailed("❌ PR description is too short. Please provide at least 20 characters."); + } diff --git a/.github/workflows/cla.yaml b/.github/workflows/cla.yaml index 3f4bf21864..7866f943b5 100644 --- a/.github/workflows/cla.yaml +++ b/.github/workflows/cla.yaml @@ -13,7 +13,7 @@ jobs: steps: - name: CLA Assistant if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' - uses: secondlife-3p/contributor-assistant@v2 + uses: secondlife-3p/contributor-assistant@v2.6.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} PERSONAL_ACCESS_TOKEN: ${{ secrets.SHARED_CLA_TOKEN }} @@ -23,4 +23,4 @@ jobs: path-to-signatures: signatures.json remote-organization-name: secondlife remote-repository-name: cla-signatures - allowlist: callum@mbp.localdomain + allowlist: callum@mbp.localdomain,rye@lindenlab.com diff --git a/.github/workflows/qatest.yaml b/.github/workflows/qatest.yaml new file mode 100644 index 0000000000..4e10900441 --- /dev/null +++ b/.github/workflows/qatest.yaml @@ -0,0 +1,598 @@ +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' + - os: mac + runner: qa-mac-atlas + artifact: macOS-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 "$INSTALLER_PATH" -mountpoint "$MOUNT_POINT" -nobrowse + + echo "✅ DMG mounted at $MOUNT_POINT" + + echo "Installing application to default location from DMG..." + + # Find the .app bundle in the 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 + + APP_NAME=$(basename "$APP_PATH") + DEST_PATH="/Applications/$APP_NAME" + + # Handle existing installation + if [ -d "$DEST_PATH" ]; then + echo "Found existing installation at: $DEST_PATH" + echo "Moving existing installation to trash..." + + # Move to trash instead of force removing + TRASH_PATH="$HOME/.Trash/$(date +%Y%m%d_%H%M%S)_$APP_NAME" + mv "$DEST_PATH" "$TRASH_PATH" || { + echo "⚠️ Could not move to trash, trying direct removal..." + rm -rf "$DEST_PATH" || { + echo "❌ Could not remove existing installation" + echo "Please manually remove: $DEST_PATH" + exit 1 + } + } + + echo "✅ Existing installation handled successfully" + fi + + # Copy the .app to /Applications + echo "Copying app from: $APP_PATH" + echo "To destination: /Applications/" + cp -R "$APP_PATH" /Applications/ + + # Verify the app was copied successfully + if [ ! -d "$DEST_PATH" ]; then + echo "❌ Error: Failed to install application to /Applications!" + exit 1 + fi + + echo "✅ Application installed successfully to /Applications" + + # 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 diff --git a/.github/workflows/tag-release.yaml b/.github/workflows/tag-release.yaml index 65d1d43a83..24ee2de794 100644 --- a/.github/workflows/tag-release.yaml +++ b/.github/workflows/tag-release.yaml @@ -37,7 +37,12 @@ jobs: - name: Update Tag uses: actions/github-script@v7.0.1 with: - github-token: ${{ secrets.GITHUB_TOKEN }} + # use a real access token instead of GITHUB_TOKEN default. + # required so that the results of this tag creation can trigger the build workflow + # https://stackoverflow.com/a/71372524 + # https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#triggering-a-workflow-from-a-workflow + # this token will need to be renewed anually in January + github-token: ${{ secrets.LL_TAG_RELEASE_TOKEN }} script: | github.rest.git.createRef({ owner: context.repo.owner, |