diff --git a/.github/scripts/prepare-vsix.ps1 b/.github/scripts/prepare-vsix.ps1 new file mode 100644 index 00000000..c826150c --- /dev/null +++ b/.github/scripts/prepare-vsix.ps1 @@ -0,0 +1,162 @@ +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [string] $Sources, + [Parameter(Mandatory = $true)] + [string] $OutputDirectory +) + +$ErrorActionPreference = "Stop" +$ProgressPreference = "SilentlyContinue" +$platformIds = @("win32-x64", "linux-x64", "darwin-arm64") +$urls = @($Sources -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } | Select-Object -Unique) +if ($urls.Count -eq 0) { + throw "No VSIX sources supplied" +} +if (Test-Path -LiteralPath $OutputDirectory) { + throw "VSIX output directory already exists: $OutputDirectory" +} + +$downloadCache = [System.Collections.Generic.Dictionary[string, string]]::new([System.StringComparer]::Ordinal) +$runCache = @{} +$cacheDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "java-pack-vsix-$([guid]::NewGuid())" +New-Item -ItemType Directory -Path $cacheDirectory | Out-Null + +function Get-GitHubHeaders { + param([switch] $Actions) + + $token = if ($Actions -and $env:VSCODE_JAVA_ARTIFACT_TOKEN) { $env:VSCODE_JAVA_ARTIFACT_TOKEN } else { $env:GITHUB_TOKEN } + if (-not $token) { + throw "GitHub API requests require GITHUB_TOKEN or, for Actions artifacts, VSCODE_JAVA_ARTIFACT_TOKEN" + } + return @{ + Accept = "application/vnd.github+json" + "X-GitHub-Api-Version" = "2022-11-28" + "User-Agent" = "vscode-java-pack-autotest" + Authorization = "Bearer $token" + } +} + +function Select-VsixForPlatform { + param( + [AllowEmptyCollection()] + [object[]] $VsixFiles, + [string] $PlatformId + ) + + $platformVsix = $VsixFiles | Where-Object { $_.Name -like "*-$PlatformId-*" } | Sort-Object Name | Select-Object -First 1 + if ($platformVsix) { + return $platformVsix + } + $universalVsix = $VsixFiles | Where-Object { $_.Name -notmatch '-(darwin|linux|win32)-' } | Sort-Object Name | Select-Object -First 1 + if ($universalVsix) { + return $universalVsix + } + throw "No matching VSIX found for platform $PlatformId. Available VSIX files: $($VsixFiles.Name -join ', ')" +} + +function Get-DownloadedVsix { + param([string] $Url) + + if (-not $downloadCache.ContainsKey($Url)) { + $uri = [uri] $Url + if (-not $uri.IsAbsoluteUri -or $uri.Scheme -notin @("https", "http")) { + throw "VSIX downloads require an absolute HTTP(S) URL" + } + $fileName = [System.IO.Path]::GetFileName([uri]::UnescapeDataString($uri.AbsolutePath)) + if (-not $fileName.EndsWith(".vsix", [System.StringComparison]::OrdinalIgnoreCase)) { + throw "VSIX download URL must name a .vsix file" + } + $directory = Join-Path $cacheDirectory "download-$($downloadCache.Count)" + New-Item -ItemType Directory -Path $directory | Out-Null + $filePath = Join-Path $directory $fileName + Write-Host "Downloading VSIX: $fileName" + # Direct and release-asset URLs must not receive GitHub API credentials. + Invoke-WebRequest -Uri $uri -OutFile $filePath -UseBasicParsing | Out-Null + $downloadCache.Add($Url, $filePath) + } + return Get-Item -LiteralPath $downloadCache[$Url] +} + +function Get-GitHubActionsRunVsix { + param([string] $Owner, [string] $Repo, [string] $RunId) + + $key = "$Owner/$Repo/$RunId" + if (-not $runCache.ContainsKey($key)) { + Write-Host "Resolving GitHub Actions run: $Owner/$Repo#$RunId" + $headers = Get-GitHubHeaders -Actions + $artifactsUrl = "https://api.github.com/repos/$Owner/$Repo/actions/runs/$RunId/artifacts?per_page=100" + $artifacts = @((Invoke-RestMethod -Uri $artifactsUrl -Headers $headers -UseBasicParsing).artifacts | Where-Object { -not $_.expired }) + if ($artifacts.Count -eq 0) { + throw "No non-expired artifacts found for GitHub Actions run $Owner/$Repo#$RunId" + } + $artifact = $artifacts | Where-Object { $_.name -eq "vscode-java" } | Select-Object -First 1 + if (-not $artifact -and $artifacts.Count -eq 1) { + $artifact = $artifacts[0] + } + if (-not $artifact) { + throw "No vscode-java artifact found for GitHub Actions run $Owner/$Repo#$RunId. Available artifacts: $($artifacts.name -join ', ')" + } + $archiveUri = [uri] $artifact.archive_download_url + if ($archiveUri.Scheme -ne "https" -or $archiveUri.Host -ne "api.github.com") { + throw "GitHub artifact archive URL must use https://api.github.com" + } + $directory = Join-Path $cacheDirectory "run-$($runCache.Count)" + $zipPath = "$directory.zip" + Write-Host "Downloading Actions artifact: $($artifact.name)" + try { + Invoke-WebRequest -Uri $archiveUri -OutFile $zipPath -Headers $headers -UseBasicParsing | Out-Null + } catch { + throw "Failed to download artifact $($artifact.name) from $Owner/$Repo#$RunId. Cross-repo downloads may require VSCODE_JAVA_ARTIFACT_TOKEN with actions:read access. $($_.Exception.Message)" + } + Expand-Archive -LiteralPath $zipPath -DestinationPath $directory -Force + $files = @(Get-ChildItem -LiteralPath $directory -Recurse -File -Filter "*.vsix") + if ($files.Count -eq 0) { + throw "Artifact $($artifact.name) from $Owner/$Repo#$RunId does not contain VSIX files" + } + $runCache[$key] = $files + } + return $runCache[$key] +} + +try { + foreach ($platformId in $platformIds) { + New-Item -ItemType Directory -Path (Join-Path $OutputDirectory $platformId) -Force | Out-Null + } + foreach ($url in $urls) { + if ($url -match '^https://github\.com/([^/]+)/([^/]+)/releases/tag/(.+)$') { + $owner = $Matches[1]; $repo = $Matches[2]; $tag = $Matches[3] + Write-Host "Resolving GitHub release: $owner/$repo@$tag" + $apiUrl = "https://api.github.com/repos/$owner/$repo/releases/tags/$tag" + $release = Invoke-RestMethod -Uri $apiUrl -Headers (Get-GitHubHeaders) -UseBasicParsing + $assets = @($release.assets | Where-Object { $_.name -like "*.vsix" }) + foreach ($platformId in $platformIds) { + $asset = Select-VsixForPlatform -VsixFiles $assets -PlatformId $platformId + $file = Get-DownloadedVsix -Url $asset.browser_download_url + Copy-Item -LiteralPath $file.FullName -Destination (Join-Path $OutputDirectory "$platformId/$($file.Name)") -Force + } + } elseif ($url -match '^https://github\.com/([^/]+)/([^/]+)/actions/runs/(\d+)') { + $files = @(Get-GitHubActionsRunVsix -Owner $Matches[1] -Repo $Matches[2] -RunId $Matches[3]) + foreach ($platformId in $platformIds) { + $file = Select-VsixForPlatform -VsixFiles $files -PlatformId $platformId + Copy-Item -LiteralPath $file.FullName -Destination (Join-Path $OutputDirectory "$platformId/$($file.Name)") -Force + } + } elseif ($url -match '^\d+$') { + $files = @(Get-GitHubActionsRunVsix -Owner "redhat-developer" -Repo "vscode-java" -RunId $url) + foreach ($platformId in $platformIds) { + $file = Select-VsixForPlatform -VsixFiles $files -PlatformId $platformId + Copy-Item -LiteralPath $file.FullName -Destination (Join-Path $OutputDirectory "$platformId/$($file.Name)") -Force + } + } else { + $file = Get-DownloadedVsix -Url $url + foreach ($platformId in $platformIds) { + Copy-Item -LiteralPath $file.FullName -Destination (Join-Path $OutputDirectory "$platformId/$($file.Name)") -Force + } + } + } + Get-ChildItem -LiteralPath $OutputDirectory -Recurse -File -Filter "*.vsix" | ForEach-Object { + Write-Host "Prepared $($_.Directory.Name): $($_.Name) ($([math]::Round($_.Length / 1MB, 1)) MB)" + } +} finally { + Remove-Item -LiteralPath $cacheDirectory -Recurse -Force +} diff --git a/.github/scripts/test-prepare-vsix.ps1 b/.github/scripts/test-prepare-vsix.ps1 new file mode 100644 index 00000000..bf3b25df --- /dev/null +++ b/.github/scripts/test-prepare-vsix.ps1 @@ -0,0 +1,172 @@ +$ErrorActionPreference = "Stop" +$scriptPath = Join-Path $PSScriptRoot "prepare-vsix.ps1" +$testDirectory = Join-Path ([System.IO.Path]::GetTempPath()) "java-pack-vsix-tests-$([guid]::NewGuid())" +$previousToken = $env:GITHUB_TOKEN +$previousArtifactToken = $env:VSCODE_JAVA_ARTIFACT_TOKEN +$platformIds = @("win32-x64", "linux-x64", "darwin-arm64") +$testState = @{ + restCalls = [System.Collections.Generic.List[object]]::new() + webCalls = [System.Collections.Generic.List[object]]::new() + restFailure = "" + webFailure = "" + caseCount = 0 +} +$releaseUrl = "https://github.com/redhat-developer/vscode-java/releases/tag/v1.57.0" +$runUrl = "https://github.com/redhat-developer/vscode-java/actions/runs/123" +$testState.archiveFiles = @($platformIds | ForEach-Object { "java-$_-1.57.0.vsix" }) +$testState.assets = @($testState.archiveFiles | ForEach-Object { + [pscustomobject]@{ name = $_; browser_download_url = "https://downloads.example/$_" } +}) +$testState.artifacts = @([pscustomobject]@{ + name = "vscode-java" + expired = $false + archive_download_url = "https://api.github.com/repos/redhat-developer/vscode-java/actions/artifacts/1/zip" +}) + +function Assert-True { + param([bool] $Condition, [string] $Message) + if (-not $Condition) { + throw $Message + } +} + +function Invoke-RestMethod { + param([string] $Uri, [hashtable] $Headers, [switch] $UseBasicParsing) + $testState.restCalls.Add([pscustomobject]@{ Uri = $Uri; Headers = $Headers }) + if ($testState.restFailure) { + throw $testState.restFailure + } + if ($Uri -match '/releases/tags/') { + Assert-True ($Headers.Authorization -eq "Bearer $env:GITHUB_TOKEN") "Release metadata must use GITHUB_TOKEN" + return [pscustomobject]@{ assets = $testState.assets } + } + if ($Uri -match '/actions/runs/') { + $token = if ($env:VSCODE_JAVA_ARTIFACT_TOKEN) { $env:VSCODE_JAVA_ARTIFACT_TOKEN } else { $env:GITHUB_TOKEN } + Assert-True ($Headers.Authorization -eq "Bearer $token") "Actions metadata must use the artifact token or GITHUB_TOKEN" + return [pscustomobject]@{ artifacts = $testState.artifacts } + } + throw "Unexpected metadata request" +} + +function Invoke-WebRequest { + param([uri] $Uri, [string] $OutFile, [hashtable] $Headers, [switch] $UseBasicParsing) + $testState.webCalls.Add([pscustomobject]@{ Uri = $Uri; Headers = $Headers }) + if ($testState.webFailure) { + throw $testState.webFailure + } + if ($Uri.Host -eq "api.github.com") { + $token = if ($env:VSCODE_JAVA_ARTIFACT_TOKEN) { $env:VSCODE_JAVA_ARTIFACT_TOKEN } else { $env:GITHUB_TOKEN } + Assert-True ($Headers.Authorization -eq "Bearer $token") "Actions archive must use the artifact token or GITHUB_TOKEN" + } else { + Assert-True (-not $Headers) "Direct/release downloads must not receive API credentials" + } + [System.IO.File]::WriteAllText($OutFile, "fixture") +} + +function Expand-Archive { + param([string] $LiteralPath, [string] $DestinationPath, [switch] $Force) + New-Item -ItemType Directory -Path $DestinationPath -Force | Out-Null + foreach ($name in $testState.archiveFiles) { + [System.IO.File]::WriteAllText((Join-Path $DestinationPath $name), "fixture") + } +} + +function Invoke-TestPreparation { + param([string] $Sources, [string] $ExpectedError = "") + + $testState.caseCount++ + $testState.restCalls.Clear() + $testState.webCalls.Clear() + $output = Join-Path $testDirectory "case-$($testState.caseCount)" + try { + & $scriptPath -Sources $Sources -OutputDirectory $output 6> $null + } catch { + if (-not $ExpectedError -or $_.Exception.Message -notlike "*$ExpectedError*") { + throw + } + return $output + } + Assert-True (-not $ExpectedError) "Expected preparation failure: $ExpectedError" + return $output +} + +function Assert-PlatformFiles { + param([string] $Directory, [switch] $Universal) + foreach ($platformId in $platformIds) { + $files = @(Get-ChildItem -LiteralPath (Join-Path $Directory $platformId) -File) + $expected = if ($Universal) { "extension-1.0.vsix" } else { "java-$platformId-1.57.0.vsix" } + Assert-True ($files.Count -eq 1 -and $files[0].Name -eq $expected) "Wrong bits for $platformId" + } +} + +try { + New-Item -ItemType Directory -Path $testDirectory | Out-Null + $env:GITHUB_TOKEN = "test-github-token" + $env:VSCODE_JAVA_ARTIFACT_TOKEN = "test-artifact-token" + + $testState.assets += [pscustomobject]@{ name = "java-linux-arm64-1.57.0.vsix"; browser_download_url = "https://downloads.example/wrong.vsix" } + $testState.assets += [pscustomobject]@{ name = "extension-1.0.vsix"; browser_download_url = "https://downloads.example/extension-1.0.vsix" } + $output = Invoke-TestPreparation "$releaseUrl, $releaseUrl" + Assert-PlatformFiles $output + Assert-True ($testState.restCalls.Count -eq 1 -and $testState.webCalls.Count -eq 3) "Release must resolve once and download only the three required platform assets" + + $testState.assets = @([pscustomobject]@{ name = "extension-1.0.vsix"; browser_download_url = "https://downloads.example/extension-1.0.vsix" }) + $output = Invoke-TestPreparation $releaseUrl + Assert-PlatformFiles $output -Universal + Assert-True ($testState.restCalls.Count -eq 1 -and $testState.webCalls.Count -eq 1) "Universal release must download once" + + $output = Invoke-TestPreparation "$runUrl,123" + Assert-PlatformFiles $output + Assert-True ($testState.restCalls.Count -eq 1 -and $testState.webCalls.Count -eq 1) "Run URL and numeric alias must share one metadata/archive download" + + $env:VSCODE_JAVA_ARTIFACT_TOKEN = $null + $output = Invoke-TestPreparation $runUrl + Assert-PlatformFiles $output + Assert-True ($testState.restCalls.Count -eq 1 -and $testState.webCalls.Count -eq 1) "Actions fallback token must retain single-download behavior" + + $testState.artifacts[0].name = "sole-artifact" + $testState.archiveFiles = @("extension-1.0.vsix") + $output = Invoke-TestPreparation $runUrl + Assert-PlatformFiles $output -Universal + + $env:GITHUB_TOKEN = $null + $output = Invoke-TestPreparation " https://downloads.example/extension-1.0.vsix?download=1,https://downloads.example/extension-1.0.vsix?download=1 " + Assert-PlatformFiles $output -Universal + Assert-True ($testState.restCalls.Count -eq 0 -and $testState.webCalls.Count -eq 1) "Direct URLs must download once without API authentication" + + Invoke-TestPreparation $releaseUrl "GitHub API requests require" | Out-Null + Assert-True ($testState.restCalls.Count -eq 0) "Missing token must fail before calling the release API" + $env:GITHUB_TOKEN = "test-github-token" + + $testState.assets = @([pscustomobject]@{ name = "java-win32-x64-1.57.0.vsix"; browser_download_url = "https://downloads.example/java-win32-x64-1.57.0.vsix" }) + Invoke-TestPreparation $releaseUrl "No matching VSIX found for platform linux-x64" | Out-Null + $testState.assets = @() + Invoke-TestPreparation $releaseUrl "No matching VSIX found" | Out-Null + + $testState.restFailure = "rate limit exceeded" + Invoke-TestPreparation $releaseUrl "rate limit exceeded" | Out-Null + Assert-True ($testState.webCalls.Count -eq 0) "Metadata failure must not fall back to other bits" + $testState.restFailure = "" + + $testState.artifacts[0].expired = $true + Invoke-TestPreparation $runUrl "No non-expired artifacts" | Out-Null + $testState.artifacts[0].expired = $false + $testState.artifacts[0].archive_download_url = "https://downloads.example/archive.zip" + Invoke-TestPreparation $runUrl "archive URL must use https://api.github.com" | Out-Null + Assert-True ($testState.webCalls.Count -eq 0) "Archive credentials must not be sent to an external host" + $testState.artifacts[0].archive_download_url = "https://api.github.com/repos/redhat-developer/vscode-java/actions/artifacts/1/zip" + + $testState.archiveFiles = @() + Invoke-TestPreparation $runUrl "does not contain VSIX files" | Out-Null + $testState.webFailure = "forbidden" + Invoke-TestPreparation $runUrl "Cross-repo downloads may require VSCODE_JAVA_ARTIFACT_TOKEN" | Out-Null + $testState.webFailure = "" + + Invoke-TestPreparation "https://downloads.example/file.zip" "must name a .vsix file" | Out-Null + Invoke-TestPreparation " , " "No VSIX sources supplied" | Out-Null + Write-Host "Passed $($testState.caseCount) VSIX preparation tests." +} finally { + $env:GITHUB_TOKEN = $previousToken + $env:VSCODE_JAVA_ARTIFACT_TOKEN = $previousArtifactToken + Remove-Item -LiteralPath $testDirectory -Recurse -Force +} diff --git a/.github/skills/uitest/SKILL.md b/.github/skills/uitest/SKILL.md index 2746e160..0f0534ef 100644 --- a/.github/skills/uitest/SKILL.md +++ b/.github/skills/uitest/SKILL.md @@ -77,3 +77,6 @@ The repository workflow `.github/workflows/e2e-autotest.yml` builds a branch VSI Use workflow dispatch when the user asks to validate in CI or across platforms. The `test_plan` input can target a single plan; leave it empty to run all plans. +For manual runs with `vsix_urls`, `prepare-bits` resolves each release/run once, downloads the Windows x64, Linux x64, and macOS ARM64 VSIX files, and uploads platform artifacts. Matrix jobs reuse those bits alongside the branch-built `pack-vsix`; they do not query release APIs. Preparation or missing-platform failures block the matrix rather than silently testing Marketplace bits. Runs without supplied URLs skip preparation and keep the existing Marketplace behavior. + +Release API requests use `GITHUB_TOKEN`. Cross-repository Actions artifacts use `VSCODE_JAVA_ARTIFACT_TOKEN` when configured, otherwise `GITHUB_TOKEN`; the token needs `actions:read` access to the source repository. Direct VSIX URLs are downloaded once and distributed unchanged, without GitHub API credentials. diff --git a/.github/workflows/e2e-autotest.yml b/.github/workflows/e2e-autotest.yml index 8380cd53..9aa2fc5c 100644 --- a/.github/workflows/e2e-autotest.yml +++ b/.github/workflows/e2e-autotest.yml @@ -102,11 +102,62 @@ jobs: echo "matrix=$matrix" >> "$GITHUB_OUTPUT" echo "Found plans: $matrix" + # Prepare supplied bits once, independently of the test matrix. + prepare-bits: + if: ${{ github.event_name == 'workflow_dispatch' && inputs.vsix_urls != '' }} + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Test VSIX preparation + shell: pwsh + run: ./.github/scripts/test-prepare-vsix.ps1 + + - name: Prepare platform VSIX files + shell: pwsh + env: + VSIX_SOURCES: ${{ inputs.vsix_urls }} + GITHUB_TOKEN: ${{ github.token }} + VSCODE_JAVA_ARTIFACT_TOKEN: ${{ secrets.VSCODE_JAVA_ARTIFACT_TOKEN }} + run: ./.github/scripts/prepare-vsix.ps1 -Sources $env:VSIX_SOURCES -OutputDirectory prepared-vsix + + - name: Upload Windows bits + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: override-vsix-Windows-X64 + path: prepared-vsix/win32-x64/*.vsix + if-no-files-found: error + compression-level: 0 + overwrite: true + retention-days: 1 + + - name: Upload Linux bits + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: override-vsix-Linux-X64 + path: prepared-vsix/linux-x64/*.vsix + if-no-files-found: error + compression-level: 0 + overwrite: true + retention-days: 1 + + - name: Upload macOS bits + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: override-vsix-macOS-ARM64 + path: prepared-vsix/darwin-arm64/*.vsix + if-no-files-found: error + compression-level: 0 + overwrite: true + retention-days: 1 + # ── Job 2: Run each test plan in parallel ─────────────── e2e-test: - needs: [discover, build-pack] + needs: [discover, build-pack, prepare-bits] # build-pack is skipped on schedule; PR/manual runs install this branch's pack VSIX first. - if: ${{ always() && needs.discover.result == 'success' && (github.event_name == 'schedule' || needs.build-pack.result == 'success') }} + # Skipped preparation is allowed only without overrides; failure must not fall back to Marketplace. + if: ${{ always() && needs.discover.result == 'success' && (github.event_name == 'schedule' || needs.build-pack.result == 'success') && (needs.prepare-bits.result == 'success' || (needs.prepare-bits.result == 'skipped' && (github.event_name != 'workflow_dispatch' || inputs.vsix_urls == ''))) }} runs-on: ${{ matrix.os }} timeout-minutes: 30 strategy: @@ -275,142 +326,12 @@ jobs: name: pack-vsix path: vsix - - name: Download VSIX files + - name: Download prepared platform VSIX files if: ${{ github.event_name == 'workflow_dispatch' && inputs.vsix_urls != '' }} - shell: pwsh - env: - GITHUB_TOKEN: ${{ github.token }} - VSCODE_JAVA_ARTIFACT_TOKEN: ${{ secrets.VSCODE_JAVA_ARTIFACT_TOKEN }} - run: | - New-Item -ItemType Directory -Path vsix -Force | Out-Null - $urls = "${{ inputs.vsix_urls }}" -split "," | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } - - # Map runner OS/arch to vscode-java platform identifiers - $platformMap = @{ "Windows" = "win32"; "Linux" = "linux"; "macOS" = "darwin" } - $archMap = @{ "X64" = "x64"; "ARM64" = "arm64" } - $platform = $platformMap["${{ runner.os }}"] - $arch = $archMap["${{ runner.arch }}"] - $platformId = "$platform-$arch" - Write-Host "Runner platform: $platformId (${{ runner.os }}/${{ runner.arch }})" - - $githubHeaders = @{ - Accept = "application/vnd.github+json" - Authorization = "Bearer $env:GITHUB_TOKEN" - "X-GitHub-Api-Version" = "2022-11-28" - "User-Agent" = "vscode-java-pack-autotest" - } - $artifactToken = if ($env:VSCODE_JAVA_ARTIFACT_TOKEN) { $env:VSCODE_JAVA_ARTIFACT_TOKEN } else { $env:GITHUB_TOKEN } - $githubArtifactHeaders = $githubHeaders.Clone() - if ($artifactToken) { - $githubArtifactHeaders.Authorization = "Bearer $artifactToken" - } - - function Select-VsixForPlatform { - param( - [Parameter(Mandatory = $true)] - [System.IO.FileInfo[]] $VsixFiles, - [Parameter(Mandatory = $true)] - [string] $PlatformId - ) - - $platformVsix = $VsixFiles | Where-Object { $_.Name -like "*-$PlatformId-*" } | Sort-Object Name | Select-Object -First 1 - if ($platformVsix) { - Write-Host " Found platform-specific VSIX: $($platformVsix.Name)" - return $platformVsix - } - - $universalVsix = $VsixFiles | Where-Object { $_.Name -notmatch '-(darwin|linux|win32)-' } | Sort-Object Name | Select-Object -First 1 - if ($universalVsix) { - Write-Host " No platform-specific VSIX found, using universal: $($universalVsix.Name)" - return $universalVsix - } - - throw "No matching VSIX found for platform $PlatformId. Available VSIX files: $($VsixFiles.Name -join ', ')" - } - - function Download-GitHubActionsRunVsix { - param( - [Parameter(Mandatory = $true)] - [string] $Owner, - [Parameter(Mandatory = $true)] - [string] $Repo, - [Parameter(Mandatory = $true)] - [string] $RunId - ) - - Write-Host "Resolving GitHub Actions run: $Owner/$Repo#$RunId for platform $platformId" - $artifactsUrl = "https://api.github.com/repos/$Owner/$Repo/actions/runs/$RunId/artifacts" - $artifacts = @((Invoke-RestMethod -Uri $artifactsUrl -Headers $githubArtifactHeaders -UseBasicParsing).artifacts | Where-Object { -not $_.expired }) - if (-not $artifacts) { - throw "No non-expired artifacts found for GitHub Actions run $Owner/$Repo#$RunId" - } - - $artifact = $artifacts | Where-Object { $_.name -eq "vscode-java" } | Select-Object -First 1 - if (-not $artifact -and $artifacts.Count -eq 1) { - $artifact = $artifacts | Select-Object -First 1 - } - if (-not $artifact) { - throw "No vscode-java artifact found for GitHub Actions run $Owner/$Repo#$RunId. Available artifacts: $($artifacts.name -join ', ')" - } - - $safeArtifactName = $artifact.name -replace '[^\w.-]', '_' - $downloadDir = Join-Path "downloaded-artifacts" "$Owner-$Repo-$RunId-$safeArtifactName" - $zipPath = "$downloadDir.zip" - New-Item -ItemType Directory -Path $downloadDir -Force | Out-Null - - Write-Host " Downloading artifact: $($artifact.name)" - try { - Invoke-WebRequest -Uri $artifact.archive_download_url -OutFile $zipPath -Headers $githubArtifactHeaders -UseBasicParsing - } catch { - throw "Failed to download artifact $($artifact.name) from $Owner/$Repo#$RunId. Cross-repo artifact downloads may require a VSCODE_JAVA_ARTIFACT_TOKEN secret with actions:read access to the source repository. $($_.Exception.Message)" - } - Expand-Archive -LiteralPath $zipPath -DestinationPath $downloadDir -Force - - $vsixFiles = @(Get-ChildItem $downloadDir -Recurse -Filter "*.vsix") - if ($vsixFiles.Count -eq 0) { - throw "Artifact $($artifact.name) from $Owner/$Repo#$RunId does not contain VSIX files" - } - - $selectedVsix = Select-VsixForPlatform -VsixFiles $vsixFiles -PlatformId $platformId - Copy-Item -LiteralPath $selectedVsix.FullName -Destination (Join-Path "vsix" $selectedVsix.Name) -Force - } - - $resolvedUrls = @() - foreach ($url in $urls) { - if ($url -match '^https://github\.com/([^/]+)/([^/]+)/releases/tag/(.+)$') { - $owner = $Matches[1]; $repo = $Matches[2]; $tag = $Matches[3] - Write-Host "Resolving GitHub release: $owner/$repo@$tag for platform $platformId" - $apiUrl = "https://api.github.com/repos/$owner/$repo/releases/tags/$tag" - $release = Invoke-RestMethod -Uri $apiUrl -Headers $githubHeaders -UseBasicParsing - $platformAsset = $release.assets | Where-Object { $_.name -like "*-$platformId-*" -and $_.name -like "*.vsix" } | Select-Object -First 1 - if ($platformAsset) { - Write-Host " Found platform-specific VSIX: $($platformAsset.name)" - $resolvedUrls += $platformAsset.browser_download_url - } else { - $universalAsset = $release.assets | Where-Object { $_.name -notmatch '-(darwin|linux|win32)-' -and $_.name -like "*.vsix" } | Select-Object -First 1 - if ($universalAsset) { - Write-Host " No platform-specific VSIX found, using universal: $($universalAsset.name)" - $resolvedUrls += $universalAsset.browser_download_url - } else { - Write-Host "::warning::No matching VSIX found in release $owner/$repo@$tag for platform $platformId" - } - } - } elseif ($url -match '^https://github\.com/([^/]+)/([^/]+)/actions/runs/(\d+)') { - Download-GitHubActionsRunVsix -Owner $Matches[1] -Repo $Matches[2] -RunId $Matches[3] - } elseif ($url -match '^\d+$') { - Download-GitHubActionsRunVsix -Owner "redhat-developer" -Repo "vscode-java" -RunId $url - } else { - $resolvedUrls += $url - } - } - - foreach ($url in $resolvedUrls) { - $fileName = [System.IO.Path]::GetFileName(($url -split '\?')[0]) - Write-Host "Downloading: $url → vsix/$fileName" - Invoke-WebRequest -Uri $url -OutFile "vsix/$fileName" -UseBasicParsing - } - Write-Host "Downloaded VSIX files:" - Get-ChildItem vsix -Filter "*.vsix" | ForEach-Object { Write-Host " $($_.Name) ($([math]::Round($_.Length/1MB, 1)) MB)" } + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: override-vsix-${{ runner.os }}-${{ runner.arch }} + path: vsix - name: Use vscode-java-pack as primary extension for VSIX override runs if: ${{ github.event_name == 'workflow_dispatch' && inputs.vsix_urls != '' }}