From fa2e50749368b24d8b5655dbd84d3dce020af5f4 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 10 Apr 2026 17:32:45 -0400 Subject: [PATCH 1/4] Rename --validate-urls to --skip-url-validation with three-way hash resolution Rename the flag to --skip-url-validation (inverted semantics) in both generate-cask.sh and generate-manifests.ps1. Add three-way hash resolution: use local archives (--archive-root), placeholder hashes (--skip-url-validation), or download from URLs (default). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/homebrew/generate-cask.sh | 43 ++++++++++++---------- eng/winget/generate-manifests.ps1 | 60 ++++++++++++++++++++----------- 2 files changed, 64 insertions(+), 39 deletions(-) diff --git a/eng/homebrew/generate-cask.sh b/eng/homebrew/generate-cask.sh index 53727c781f3..e7545943571 100755 --- a/eng/homebrew/generate-cask.sh +++ b/eng/homebrew/generate-cask.sh @@ -16,7 +16,7 @@ Optional: --artifact-version VER Version segment used in the ci.dot.net artifact path (defaults to --version) --output PATH Output file path (default: ./aspire.rb) --archive-root PATH Root directory containing locally built CLI archives to hash - --validate-urls Verify all tarball URLs are accessible before downloading + --skip-url-validation Skip URL validation and downloading; use placeholder SHA256 hashes --help Show this help message EOF exit 1 @@ -26,17 +26,17 @@ VERSION="" ARTIFACT_VERSION="" OUTPUT="" ARCHIVE_ROOT="" -VALIDATE_URLS=false +SKIP_URL_VALIDATION=false while [[ $# -gt 0 ]]; do case "$1" in - --version) VERSION="$2"; shift 2 ;; - --artifact-version) ARTIFACT_VERSION="$2"; shift 2 ;; - --output) OUTPUT="$2"; shift 2 ;; - --archive-root) ARCHIVE_ROOT="$2"; shift 2 ;; - --validate-urls) VALIDATE_URLS=true; shift ;; - --help) usage ;; - *) echo "Unknown option: $1"; usage ;; + --version) VERSION="$2"; shift 2 ;; + --artifact-version) ARTIFACT_VERSION="$2"; shift 2 ;; + --output) OUTPUT="$2"; shift 2 ;; + --archive-root) ARCHIVE_ROOT="$2"; shift 2 ;; + --skip-url-validation) SKIP_URL_VALIDATION=true; shift ;; + --help) usage ;; + *) echo "Unknown option: $1"; usage ;; esac done @@ -134,8 +134,21 @@ if [[ -n "$ARCHIVE_ROOT" && ! -d "$ARCHIVE_ROOT" ]]; then exit 1 fi -# Validate URLs are accessible before downloading (fast-fail) -if [[ "$VALIDATE_URLS" == true ]]; then +if [[ -n "$ARCHIVE_ROOT" ]]; then + echo "Computing SHA256 hashes from local archives in $ARCHIVE_ROOT..." + OSX_ARM64_ARCHIVE="$(find_local_archive "aspire-cli-osx-arm64-$VERSION.tar.gz")" + OSX_X64_ARCHIVE="$(find_local_archive "aspire-cli-osx-x64-$VERSION.tar.gz")" + + SHA256_OSX_ARM64="$(compute_sha256_from_file "$OSX_ARM64_ARCHIVE" "macOS ARM64 tarball")" + SHA256_OSX_X64="$(compute_sha256_from_file "$OSX_X64_ARCHIVE" "macOS x64 tarball")" +elif [[ "$SKIP_URL_VALIDATION" == true ]]; then + echo "SkipUrlValidation specified — using placeholder SHA256 hashes (tarball URLs will not be validated or downloaded)" + SHA256_OSX_ARM64="$(printf '0%.0s' {1..64})" + SHA256_OSX_X64="$(printf '0%.0s' {1..64})" + echo " osx-arm64: URL=$OSX_ARM64_URL, SHA256=$SHA256_OSX_ARM64 (placeholder)" + echo " osx-x64: URL=$OSX_X64_URL, SHA256=$SHA256_OSX_X64 (placeholder)" +else + # Validate URLs are accessible before downloading (fast-fail) echo "Validating tarball URLs..." failed=false for url in "$OSX_ARM64_URL" "$OSX_X64_URL"; do @@ -153,15 +166,7 @@ if [[ "$VALIDATE_URLS" == true ]]; then exit 1 fi echo "" -fi -if [[ -n "$ARCHIVE_ROOT" ]]; then - OSX_ARM64_ARCHIVE="$(find_local_archive "aspire-cli-osx-arm64-$VERSION.tar.gz")" - OSX_X64_ARCHIVE="$(find_local_archive "aspire-cli-osx-x64-$VERSION.tar.gz")" - - SHA256_OSX_ARM64="$(compute_sha256_from_file "$OSX_ARM64_ARCHIVE" "macOS ARM64 tarball")" - SHA256_OSX_X64="$(compute_sha256_from_file "$OSX_X64_ARCHIVE" "macOS x64 tarball")" -else SHA256_OSX_ARM64="$(compute_sha256 "$OSX_ARM64_URL" "macOS ARM64 tarball")" SHA256_OSX_X64="$(compute_sha256 "$OSX_X64_URL" "macOS x64 tarball")" fi diff --git a/eng/winget/generate-manifests.ps1 b/eng/winget/generate-manifests.ps1 index c97ea2d5ae9..ca72d128f92 100644 --- a/eng/winget/generate-manifests.ps1 +++ b/eng/winget/generate-manifests.ps1 @@ -35,9 +35,10 @@ URL to the release notes page. If not specified, derived from the version (e.g., "13.2.0" -> "https://aspire.dev/whats-new/aspire-13-2/"). -.PARAMETER ValidateUrls - When specified, verifies that all installer URLs are accessible (HTTP HEAD request) - before downloading them to compute SHA256 hashes. +.PARAMETER SkipUrlValidation + When specified, skips all URL operations: both the HEAD-request validation and downloading + installer files to compute SHA256 hashes. Placeholder hashes are used instead. + This is useful for PR validation where the installer URLs have not been published yet. .EXAMPLE ./generate-manifests.ps1 -Version "13.3.0-preview.1.26111.5" ` @@ -47,7 +48,7 @@ ./generate-manifests.ps1 -Version "13.2.0" ` -ArtifactVersion "13.2.0-preview.1.26111.5" ` -TemplateDir "./eng/winget/microsoft.aspire" ` - -Rids "win-x64,win-arm64" -ValidateUrls + -Rids "win-x64,win-arm64" #> [CmdletBinding()] @@ -74,7 +75,7 @@ param( [string]$ReleaseNotesUrl, [Parameter(Mandatory = $false)] - [switch]$ValidateUrls + [switch]$SkipUrlValidation ) $ErrorActionPreference = 'Stop' @@ -264,8 +265,33 @@ foreach ($rid in $ridList) { $installerEntries += @{ Rid = $rid; Architecture = $arch; Url = $url } } -# Validate URLs are accessible before downloading (fast-fail) -if ($ValidateUrls) { +if ($ArchiveRoot) { + Write-Host "Computing SHA256 hashes from local archives in $ArchiveRoot..." + + $installersYaml = "Installers:" + foreach ($entry in $installerEntries) { + $archivePath = Get-LocalArchivePath -ArchiveRoot $ArchiveRoot -Rid $entry.Rid -Version $Version + $sha256 = Get-LocalFileSha256 -Path $archivePath -Description "$($entry.Rid) installer" + + $installersYaml += "`n- Architecture: $($entry.Architecture)" + $installersYaml += "`n InstallerUrl: $($entry.Url)" + $installersYaml += "`n InstallerSha256: $sha256" + } +} elseif ($SkipUrlValidation) { + Write-Host "SkipUrlValidation specified — using placeholder SHA256 hashes (installer URLs will not be validated or downloaded)" + Write-Host "" + + $installersYaml = "Installers:" + foreach ($entry in $installerEntries) { + $placeholderHash = "0" * 64 + Write-Host " $($entry.Rid): URL=$($entry.Url), SHA256=$placeholderHash (placeholder)" + + $installersYaml += "`n- Architecture: $($entry.Architecture)" + $installersYaml += "`n InstallerUrl: $($entry.Url)" + $installersYaml += "`n InstallerSha256: $placeholderHash" + } +} else { + # Validate URLs are accessible before downloading (fast-fail) Write-Host "Validating installer URLs..." $failed = $false foreach ($entry in $installerEntries) { @@ -285,23 +311,17 @@ if ($ValidateUrls) { exit 1 } Write-Host "" -} -Write-Host "Computing SHA256 hashes..." + Write-Host "Computing SHA256 hashes by downloading from URLs..." -$installersYaml = "Installers:" -foreach ($entry in $installerEntries) { - if ($ArchiveRoot) { - $archivePath = Get-LocalArchivePath -ArchiveRoot $ArchiveRoot -Rid $entry.Rid -Version $Version - $sha256 = Get-LocalFileSha256 -Path $archivePath -Description "$($entry.Rid) installer" - } - else { + $installersYaml = "Installers:" + foreach ($entry in $installerEntries) { $sha256 = Get-RemoteFileSha256 -Url $entry.Url -Description "$($entry.Rid) installer" - } - $installersYaml += "`n- Architecture: $($entry.Architecture)" - $installersYaml += "`n InstallerUrl: $($entry.Url)" - $installersYaml += "`n InstallerSha256: $sha256" + $installersYaml += "`n- Architecture: $($entry.Architecture)" + $installersYaml += "`n InstallerUrl: $($entry.Url)" + $installersYaml += "`n InstallerSha256: $sha256" + } } # Define substitutions From 0bf7d4a5a8a084c2f31b16e00b5919268a43ce66 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 10 Apr 2026 17:32:51 -0400 Subject: [PATCH 2/4] Add skipUrlValidation and archiveRoot params to installer templates Add skipUrlValidation and archiveRoot parameters to prepare-winget-manifest.yml and prepare-homebrew-cask.yml. Derive install-test gating from skipUrlValidation (no separate runInstallTest parameter needed). Skip the upstream GitHub API cask check entirely when skipUrlValidation is true to avoid rate-limit flakes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../templates/prepare-homebrew-cask.yml | 57 +++++++++++-------- .../templates/prepare-winget-manifest.yml | 15 +++-- 2 files changed, 41 insertions(+), 31 deletions(-) diff --git a/eng/pipelines/templates/prepare-homebrew-cask.yml b/eng/pipelines/templates/prepare-homebrew-cask.yml index 8273527099a..74749047f37 100644 --- a/eng/pipelines/templates/prepare-homebrew-cask.yml +++ b/eng/pipelines/templates/prepare-homebrew-cask.yml @@ -8,12 +8,11 @@ parameters: - name: archiveRoot type: string default: '' - - name: validateUrls + - name: skipUrlValidation type: boolean - default: true - - name: runInstallTest - type: boolean - default: true + default: false + # runInstallTest is derived from skipUrlValidation (they are logical inverses). + # Callers should only set skipUrlValidation; install tests run when URLs are valid. steps: - bash: | @@ -53,9 +52,9 @@ steps: args+=(--archive-root "$ARCHIVE_ROOT") fi - validateUrlsNormalized="$(printf '%s' "${VALIDATE_URLS:-false}" | tr '[:upper:]' '[:lower:]')" - if [ "$validateUrlsNormalized" = "true" ]; then - args+=(--validate-urls) + skipUrlValidation="$(printf '%s' "${SKIP_URL_VALIDATION:-false}" | tr '[:upper:]' '[:lower:]')" + if [ "$skipUrlValidation" = "true" ]; then + args+=(--skip-url-validation) fi "$(Build.SourcesDirectory)/eng/homebrew/generate-cask.sh" "${args[@]}" @@ -66,7 +65,7 @@ steps: displayName: 🟣Generate Homebrew cask env: ARCHIVE_ROOT: '${{ parameters.archiveRoot }}' - VALIDATE_URLS: '${{ parameters.validateUrls }}' + SKIP_URL_VALIDATION: '${{ parameters.skipUrlValidation }}' - bash: | set -euo pipefail @@ -102,22 +101,32 @@ steps: echo "" echo "Auditing cask via local tap..." - upstreamStatusCode="$(curl -sS -o /dev/null -w "%{http_code}" "https://api.github.com/repos/Homebrew/homebrew-cask/contents/$targetPath")" - auditArgs=(--cask --online "$tapName/$caskName") - auditCommand="brew audit --cask --online $tapName/$caskName" + skipUrlValidation="$(printf '%s' "${SKIP_URL_VALIDATION:-false}" | tr '[:upper:]' '[:lower:]')" + + auditArgs=(--cask) isNewCask=false - if [ "$upstreamStatusCode" = "404" ]; then - echo "Detected new upstream cask; running new-cask audit." - auditArgs=(--cask --new --online "$tapName/$caskName") - auditCommand="brew audit --cask --new --online $tapName/$caskName" - isNewCask=true - elif [ "$upstreamStatusCode" = "200" ]; then - echo "Detected existing upstream cask; running standard online audit." + + if [ "$skipUrlValidation" = "true" ]; then + echo "Skipping upstream cask check and online audit (URLs are not expected to be valid for this build)." else - echo "##[error]Could not determine whether $targetPath exists upstream (HTTP $upstreamStatusCode)" - exit 1 + upstreamStatusCode="$(curl -sS -o /dev/null -w "%{http_code}" "https://api.github.com/repos/Homebrew/homebrew-cask/contents/$targetPath")" + if [ "$upstreamStatusCode" = "404" ]; then + isNewCask=true + echo "Detected new upstream cask; running new-cask audit." + auditArgs+=(--new) + elif [ "$upstreamStatusCode" = "200" ]; then + echo "Detected existing upstream cask; running standard audit." + else + echo "##[error]Could not determine whether $targetPath exists upstream (HTTP $upstreamStatusCode)" + exit 1 + fi + echo "Including --online audit (URLs are expected to be valid)." + auditArgs+=(--online) fi + auditArgs+=("$tapName/$caskName") + auditCommand="brew audit ${auditArgs[*]}" + auditCode=0 brew audit "${auditArgs[@]}" || auditCode=$? @@ -131,6 +140,8 @@ steps: trap - EXIT cleanup displayName: 🟣Validate cask syntax and audit + env: + SKIP_URL_VALIDATION: '${{ parameters.skipUrlValidation }}' - bash: | set -euo pipefail @@ -203,7 +214,7 @@ steps: echo " Confirmed: aspire is no longer in PATH" fi displayName: 🟣Test cask install/uninstall - condition: and(succeeded(), eq('${{ parameters.runInstallTest }}', 'true')) + condition: and(succeeded(), eq(${{ parameters.skipUrlValidation }}, false)) - bash: | set -euo pipefail @@ -242,7 +253,7 @@ steps: echo "Wrote Homebrew validation summary to $outputPath" cat "$outputPath" displayName: 🟣Write Homebrew validation summary - condition: and(succeeded(), eq('${{ parameters.runInstallTest }}', 'true')) + condition: and(succeeded(), eq(${{ parameters.skipUrlValidation }}, false)) - bash: | set -euo pipefail diff --git a/eng/pipelines/templates/prepare-winget-manifest.yml b/eng/pipelines/templates/prepare-winget-manifest.yml index 26231ea2da3..1bf6a6b818e 100644 --- a/eng/pipelines/templates/prepare-winget-manifest.yml +++ b/eng/pipelines/templates/prepare-winget-manifest.yml @@ -10,12 +10,11 @@ parameters: - name: archiveRoot type: string default: '' - - name: validateUrls + - name: skipUrlValidation type: boolean - default: true - - name: runInstallTest - type: boolean - default: true + default: false + # runInstallTest is derived from skipUrlValidation (they are logical inverses). + # Callers should only set skipUrlValidation; install tests run when URLs are valid. steps: - pwsh: | @@ -101,8 +100,8 @@ steps: $args.ArchiveRoot = '${{ parameters.archiveRoot }}' } - if ('${{ parameters.validateUrls }}' -eq 'true') { - $args.ValidateUrls = $true + if ('${{ parameters.skipUrlValidation }}' -eq 'true') { + $args.SkipUrlValidation = $true } & "$(Build.SourcesDirectory)/eng/winget/generate-manifests.ps1" @args @@ -226,7 +225,7 @@ steps: exit 1 } displayName: 🟣Test WinGet manifest install/uninstall - condition: and(succeeded(), eq('${{ parameters.runInstallTest }}', 'true')) + condition: and(succeeded(), eq(${{ parameters.skipUrlValidation }}, false)) - pwsh: | Copy-Item "$(Build.SourcesDirectory)/eng/winget/dogfood.ps1" "$(Build.StagingDirectory)/winget-manifests/dogfood.ps1" From 384179ed30c84eac1099958f81b16b06671b8d60 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 10 Apr 2026 17:32:57 -0400 Subject: [PATCH 3/4] Publish Windows CLI archives as named pipeline artifacts Publish native_archives_win_x64 and native_archives_win_arm64 as named pipeline artifacts, matching the native_archives_ pattern used by build_sign_native for macOS/Linux. This allows the Prepare Installers stage to download them for WinGet manifest generation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/templates/BuildAndTest.yml | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/eng/pipelines/templates/BuildAndTest.yml b/eng/pipelines/templates/BuildAndTest.yml index ff7be8d0c9c..bd5fecce909 100644 --- a/eng/pipelines/templates/BuildAndTest.yml +++ b/eng/pipelines/templates/BuildAndTest.yml @@ -229,6 +229,36 @@ steps: PathtoPublish: '${{ parameters.repoArtifactsPath }}/packages/Release/vscode' ArtifactName: aspire-vscode-extension + # Publish Windows CLI archives as separate artifacts so the Prepare Installers + # stage can download them by name (matching the native_archives_ pattern + # used by build_sign_native for macOS/Linux). + - ${{ if eq(parameters.isWindows, 'true') }}: + - ${{ each targetRid in parameters.targetRids }}: + - pwsh: | + $ErrorActionPreference = 'Stop' + $sourceDir = '${{ parameters.repoArtifactsPath }}/packages' + $pattern = 'aspire-cli-${{ targetRid }}-*' + $stagingDir = '$(Build.StagingDirectory)/native_archives_${{ replace(targetRid, '-', '_') }}' + + New-Item -ItemType Directory -Force -Path $stagingDir | Out-Null + + $files = Get-ChildItem -Path $sourceDir -Recurse -Filter $pattern + if ($files.Count -eq 0) { + Write-Error "No CLI archives matching '$pattern' found under '$sourceDir'" + exit 1 + } + + foreach ($f in $files) { + Copy-Item $f.FullName $stagingDir + Write-Host "Staged: $($f.Name)" + } + displayName: 🟣Stage CLI archives (${{ targetRid }}) + - task: 1ES.PublishBuildArtifacts@1 + displayName: 🟣Publish native_archives_${{ replace(targetRid, '-', '_') }} + inputs: + PathtoPublish: $(Build.StagingDirectory)/native_archives_${{ replace(targetRid, '-', '_') }} + ArtifactName: native_archives_${{ replace(targetRid, '-', '_') }} + - script: ${{ parameters.dotnetScript }} build tests/workloads.proj From 7cf5821c50a5bf36a98ad822284be665b9ea0981 Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Fri, 10 Apr 2026 17:33:03 -0400 Subject: [PATCH 4/4] Enable Prepare Installers stage for PR and feature branch builds Widen the Prepare Installers stage condition to run on PR and feature branch builds with skipUrlValidation derived from a new _PackagesPublished pipeline variable. This deduplicates the long compile-time expression that was previously repeated in both the WinGet and Homebrew jobs. Align the unofficial pipeline with the renamed parameters. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- eng/pipelines/azure-pipelines-unofficial.yml | 7 +--- eng/pipelines/azure-pipelines.yml | 41 +++++++++++++++++--- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/eng/pipelines/azure-pipelines-unofficial.yml b/eng/pipelines/azure-pipelines-unofficial.yml index 44a7668ba1e..b76b2ac764d 100644 --- a/eng/pipelines/azure-pipelines-unofficial.yml +++ b/eng/pipelines/azure-pipelines-unofficial.yml @@ -318,13 +318,11 @@ extends: artifactVersion: $(aspireArtifactVersion) channel: $(installerChannel) archiveRoot: $(Pipeline.Workspace)/native-archives - validateUrls: false - runInstallTest: false + skipUrlValidation: true - job: Homebrew displayName: Homebrew Cask timeoutInMinutes: 30 - condition: and(succeeded(), eq(variables['installerChannel'], 'stable')) pool: name: Azure Pipelines vmImage: macOS-latest-internal @@ -343,5 +341,4 @@ extends: version: $(aspireVersion) artifactVersion: $(aspireArtifactVersion) archiveRoot: $(Pipeline.Workspace)/native-archives - validateUrls: false - runInstallTest: false + skipUrlValidation: true diff --git a/eng/pipelines/azure-pipelines.yml b/eng/pipelines/azure-pipelines.yml index ca7f1de9845..fb685bbf498 100644 --- a/eng/pipelines/azure-pipelines.yml +++ b/eng/pipelines/azure-pipelines.yml @@ -95,6 +95,12 @@ variables: - name: _Sign value: true + # Packages are published only on internal, non-PR builds on publishing branches + # (main, release/*, internal/release/*). Feature branches and PR builds don't + # have Maestro default channels so archive URLs won't be valid. + - name: _PackagesPublished + value: ${{ and(ne(variables['_RunAsPublic'], 'true'), ne(variables['Build.Reason'], 'PullRequest'), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/internal/release/'))) }} + resources: repositories: - repository: 1ESPipelineTemplates @@ -383,12 +389,14 @@ extends: condition: | and( succeeded(), - ne(variables['Build.Reason'], 'PullRequest'), or( - ne(dependencies.build.outputs['Windows.computeChannel.installerChannel'], 'stable'), + eq(variables['Build.Reason'], 'PullRequest'), or( - eq(variables['Build.SourceBranch'], 'refs/heads/main'), - startsWith(variables['Build.SourceBranch'], 'refs/heads/release/') + ne(dependencies.build.outputs['Windows.computeChannel.installerChannel'], 'stable'), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/main'), + startsWith(variables['Build.SourceBranch'], 'refs/heads/release/') + ) ) ) ) @@ -413,16 +421,27 @@ extends: steps: - checkout: self fetchDepth: 1 + - task: DownloadPipelineArtifact@2 + displayName: 🟣Download CLI archives (win-x64) + inputs: + artifact: native_archives_win_x64 + targetPath: '$(Pipeline.Workspace)/native-archives/native_archives_win_x64' + - task: DownloadPipelineArtifact@2 + displayName: 🟣Download CLI archives (win-arm64) + inputs: + artifact: native_archives_win_arm64 + targetPath: '$(Pipeline.Workspace)/native-archives/native_archives_win_arm64' - template: /eng/pipelines/templates/prepare-winget-manifest.yml@self parameters: version: $(aspireVersion) artifactVersion: $(aspireArtifactVersion) channel: $(installerChannel) + archiveRoot: $(Pipeline.Workspace)/native-archives + skipUrlValidation: ${{ ne(variables['_PackagesPublished'], 'true') }} - job: Homebrew displayName: Homebrew Cask timeoutInMinutes: 30 - condition: and(succeeded(), eq(variables['installerChannel'], 'stable')) pool: name: Azure Pipelines vmImage: macOS-latest-internal @@ -430,7 +449,19 @@ extends: steps: - checkout: self fetchDepth: 1 + - task: DownloadPipelineArtifact@2 + displayName: 🟣Download CLI archives (osx-x64) + inputs: + artifact: native_archives_osx_x64 + targetPath: '$(Pipeline.Workspace)/native-archives/native_archives_osx_x64' + - task: DownloadPipelineArtifact@2 + displayName: 🟣Download CLI archives (osx-arm64) + inputs: + artifact: native_archives_osx_arm64 + targetPath: '$(Pipeline.Workspace)/native-archives/native_archives_osx_arm64' - template: /eng/pipelines/templates/prepare-homebrew-cask.yml@self parameters: version: $(aspireVersion) artifactVersion: $(aspireArtifactVersion) + archiveRoot: $(Pipeline.Workspace)/native-archives + skipUrlValidation: ${{ ne(variables['_PackagesPublished'], 'true') }}