Skip to content

Commit 7dc05a9

Browse files
ramsessanchezCopilotCopilot
authored
Ramsess/fix release pipeline (#3593)
* add explicit working dir to intall tools step * add registry flag * derive nuget feed uri in script * remove use of powershell-yaml * replace rush update purge * set npm userconfig for all npm subprocessess * use npmrc for subsequent npm auth * ensure deps downloaded * add logging to determine failure * resolve autorest modelerfour * ensure autorest doesnt reach for npm registry * ensure autorest is electing cached files * fix args * retrieve nuget without blocked call * pick up auth from npmrc file * move from global to local build * Address PR review: harden network isolation in release pipeline - Add --no-install to all npx calls (rush, autorest) to prevent silent registry downloads when tools are already installed locally - Add --no-package-lock to npm install commands to avoid polluting the working tree with package-lock.json that could be accidentally committed - Gate autorest/npm diagnostics behind ENABLE_AUTOREST_DIAGNOSTICS env var to avoid npm calls in network-isolated release pipelines - Harden NuGet feed URL derivation with input normalization and validation instead of fragile regex replacement Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * consolidate scripts and function --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 9f0998f commit 7dc05a9

9 files changed

Lines changed: 191 additions & 21 deletions

.azure-pipelines/common-templates/install-tools.yml

Lines changed: 33 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,46 @@ steps:
4848
inputs:
4949
workingFile: $(Build.SourcesDirectory)/autorest.powershell/common/config/rush/.npmrc
5050

51+
- task: PowerShell@2
52+
displayName: Apply npm auth config to user profile
53+
inputs:
54+
targetType: inline
55+
pwsh: true
56+
script: |
57+
# Copy authenticated .npmrc to the user home dir so ALL npm subprocesses
58+
# (including autorest's internal npm calls) use the private feed + auth tokens.
59+
$src = "$(Build.SourcesDirectory)/.npmrc"
60+
$dst = Join-Path $env:USERPROFILE ".npmrc"
61+
Copy-Item -Path $src -Destination $dst -Force
62+
Write-Host "Copied npm config to $dst"
63+
5164
- task: Npm@1
5265
displayName: Install AutoRest
5366
inputs:
5467
command: custom
55-
customCommand: install -g autorest@3.7.2
56-
68+
workingDir: $(Build.SourcesDirectory)
69+
customCommand: install --no-package-lock autorest@3.7.2
70+
5771
- task: Npm@1
5872
displayName: Install AutorestCore
5973
inputs:
6074
command: custom
61-
customCommand: install -g @autorest/core@3.10.4
75+
workingDir: $(Build.SourcesDirectory)
76+
customCommand: install --no-package-lock @autorest/core@3.10.4
77+
78+
- task: PowerShell@2
79+
displayName: Pre-populate autorest extension cache
80+
inputs:
81+
targetType: filePath
82+
pwsh: true
83+
filePath: $(Build.SourcesDirectory)/tools/utilities/PrePopulateAutorestCache.ps1
6284

6385
- task: Npm@1
6486
displayName: Install Rush
6587
inputs:
6688
command: custom
67-
customCommand: install -g @microsoft/rush
89+
workingDir: $(Build.SourcesDirectory)
90+
customCommand: install --no-package-lock @microsoft/rush
6891

6992
- task: PowerShell@2
7093
displayName: Rush Build
@@ -73,6 +96,9 @@ steps:
7396
pwsh: true
7497
workingDirectory: "autorest.powershell"
7598
script: |
76-
rush install
77-
rush link
78-
rush rebuild
99+
npx --no-install rush install
100+
if ($LASTEXITCODE -ne 0) { throw "rush install failed with exit code $LASTEXITCODE" }
101+
npx --no-install rush link
102+
if ($LASTEXITCODE -ne 0) { throw "rush link failed with exit code $LASTEXITCODE" }
103+
npx --no-install rush rebuild
104+
if ($LASTEXITCODE -ne 0) { throw "rush rebuild failed with exit code $LASTEXITCODE" }

tools/Configure-PrivateNpmFeed.ps1

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,24 @@ Write-Host "Created $rootNpmrc"
3535
$rushNpmrc = Join-Path $SourcesDirectory "autorest.powershell/common/config/rush/.npmrc"
3636
Set-Content -Path $rushNpmrc -Value $npmrcContent -NoNewline
3737
Write-Host "Updated $rushNpmrc"
38+
39+
# Create NuGet.config to redirect dotnet restore to the private feed
40+
# Normalize the registry URL and validate the expected suffix before transforming
41+
$normalizedRegistry = $Registry.TrimEnd('/')
42+
$npmSuffix = "/npm/registry"
43+
if (-not $normalizedRegistry.EndsWith($npmSuffix)) {
44+
throw "Cannot derive NuGet feed URL: Registry '$Registry' does not end with '$npmSuffix'. Pass an explicit Azure Artifacts npm registry URL using the -Registry parameter."
45+
}
46+
$nugetFeed = $normalizedRegistry.Substring(0, $normalizedRegistry.Length - $npmSuffix.Length) + "/nuget/v3/index.json"
47+
$nugetConfig = @"
48+
<?xml version="1.0" encoding="utf-8"?>
49+
<configuration>
50+
<packageSources>
51+
<clear />
52+
<add key="PowerShell_V2_Build" value="$nugetFeed" />
53+
</packageSources>
54+
</configuration>
55+
"@
56+
$nugetConfigPath = Join-Path $SourcesDirectory "NuGet.config"
57+
Set-Content -Path $nugetConfigPath -Value $nugetConfig -NoNewline
58+
Write-Host "Created $nugetConfigPath"

tools/GenerateModules.ps1

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,6 @@ if (-not $Isolated) {
3232
# Module import.
3333
Import-Module PowerShellGet
3434

35-
# Install Powershell-yaml
36-
if (!(Get-Module -Name powershell-yaml -ListAvailable)) {
37-
Install-Module powershell-yaml -Repository PSGallery -Scope CurrentUser -Force
38-
}
3935

4036
$ScriptRoot = $PSScriptRoot
4137
$ModulesSrc = Join-Path $ScriptRoot "..\src\"
@@ -52,8 +48,50 @@ if (-not (Test-Path $ModuleMappingPath)) {
5248

5349
# Build AutoREST.PowerShell submodule.
5450
Set-Location (Join-Path $ScriptRoot "../autorest.powershell")
55-
rush update --purge
56-
rush build
51+
npx --no-install rush install
52+
if ($LASTEXITCODE -ne 0) {
53+
throw "Command 'npx --no-install rush install' failed with exit code $LASTEXITCODE."
54+
}
55+
56+
npx --no-install rush build
57+
if ($LASTEXITCODE -ne 0) {
58+
throw "Command 'npx --no-install rush build' failed with exit code $LASTEXITCODE."
59+
}
60+
61+
# Diagnostic: show autorest cache state and npm registry config before generation.
62+
# Gated behind ENABLE_AUTOREST_DIAGNOSTICS to avoid npm calls in network-isolated release pipelines.
63+
if ($env:ENABLE_AUTOREST_DIAGNOSTICS) {
64+
Write-Host "--- Autorest/npm diagnostics ---"
65+
$autorestHome = if ($env:AUTOREST_HOME) { $env:AUTOREST_HOME } else { Join-Path $env:USERPROFILE ".autorest" }
66+
Write-Host "AUTOREST_HOME: $autorestHome"
67+
$coreCacheDir = Join-Path $autorestHome "@autorest\core"
68+
if (Test-Path $coreCacheDir) {
69+
Write-Host "Autorest core cache versions: $(Get-ChildItem $coreCacheDir -Directory | Select-Object -ExpandProperty Name)"
70+
$nodeModulesDir = Join-Path $coreCacheDir "3.10.4\node_modules\@autorest\core"
71+
Write-Host "Cache 3.10.4 node_modules present: $(Test-Path $nodeModulesDir)"
72+
} else {
73+
Write-Host "WARNING: Autorest core cache directory not found at $coreCacheDir"
74+
}
75+
$modelerfourCacheDir = Join-Path $autorestHome "@autorest\modelerfour"
76+
if (Test-Path $modelerfourCacheDir) {
77+
Write-Host "Autorest modelerfour cache versions: $(Get-ChildItem $modelerfourCacheDir -Directory | Select-Object -ExpandProperty Name)"
78+
$modelerfourNodeModules = Join-Path $modelerfourCacheDir "4.24.3\node_modules\@autorest\modelerfour"
79+
Write-Host "Cache modelerfour 4.24.3 node_modules present: $(Test-Path $modelerfourNodeModules)"
80+
} else {
81+
Write-Host "WARNING: Autorest modelerfour cache directory not found at $modelerfourCacheDir"
82+
}
83+
Write-Host "npm registry: $(npm config get registry 2>&1)"
84+
Write-Host "npm_config_registry env: $env:npm_config_registry"
85+
Write-Host "NPM_CONFIG_USERCONFIG: $env:NPM_CONFIG_USERCONFIG"
86+
$userNpmrc = Join-Path $env:USERPROFILE ".npmrc"
87+
Write-Host "~/.npmrc exists: $(Test-Path $userNpmrc)"
88+
if (Test-Path $userNpmrc) {
89+
Write-Host "~/.npmrc registry line: $(Select-String -Path $userNpmrc -Pattern '^registry=' | Select-Object -First 1 -ExpandProperty Line)"
90+
}
91+
Write-Host "--- End diagnostics ---"
92+
} else {
93+
Write-Host "Autorest diagnostics skipped (set ENABLE_AUTOREST_DIAGNOSTICS=1 to enable)"
94+
}
5795

5896
$RequiredGraphModules = @()
5997
$AuthModuleManifest = Join-Path $ModulesSrc "Authentication" "Authentication" "artifacts" "Microsoft.Graph.Authentication.psd1"

tools/GenerateServiceModule.ps1

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,33 @@ $ApiVersion | ForEach-Object {
6969
else {
7070
$FullModuleVersion = $ModuleMetadata.versions[$CurrentApiVersion].version
7171
}
72-
npx autorest --max-memory-size=$MaxMemorySize --module-version:$FullModuleVersion --module-name:$ModuleFullName --service-name:$Module --input-file:$OpenApiFile $AutoRestModuleConfig --max-cpu=2 --network-calls=2
73-
if ($LastExitCode -ne 0) {
74-
Write-Host -ForegroundColor Red "AutoREST failed to generate '$ModuleFullName' module."
75-
return $LastExitCode
72+
# Pass @autorest/modelerfour as a local --use:<path> argument so autorest loads it
73+
# directly from the pre-populated cache without calling fetchPackageMetadata.
74+
# fetchPackageMetadata is called unconditionally before the cache check inside
75+
# ExtensionManager.findPackage, and it hits the npm registry which is DNS-blocked
76+
# by 1ES supply-chain security on release pipelines. A local --use path bypasses
77+
# findPackage entirely: the extension goes straight into localExtensions, and when
78+
# use-extension in autorest-configuration.md is processed, resolveExtension finds
79+
# it there without any registry call.
80+
$autorestHome = if ($env:AUTOREST_HOME) { $env:AUTOREST_HOME } else { Join-Path $env:USERPROFILE ".autorest" }
81+
$modelerFourPath = Join-Path $autorestHome "@autorest" "modelerfour" "4.24.3" "node_modules" "@autorest" "modelerfour"
82+
# @(if ...) always produces [object[]], preventing PowerShell from unwrapping the
83+
# single-element array to a [string] scalar. A scalar string splatted with @
84+
# enumerates IEnumerable<char>, passing each character as a separate argument.
85+
$modelerFourUseFlag = @(if (Test-Path $modelerFourPath) { "--use:$modelerFourPath" })
86+
if ($modelerFourUseFlag.Count -eq 0) {
87+
Write-Host -ForegroundColor Yellow "WARNING: @autorest/modelerfour local cache not found at $modelerFourPath — autorest will attempt npm registry lookup (may fail in network-isolated environment)"
88+
}
89+
90+
$autorestLog = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), "autorest-$($ModuleFullName -replace '[^a-zA-Z0-9-]', '-').log")
91+
npx --no-install autorest @modelerFourUseFlag --verbose --max-memory-size=$MaxMemorySize --module-version:$FullModuleVersion --module-name:$ModuleFullName --service-name:$Module --input-file:$OpenApiFile $AutoRestModuleConfig --max-cpu=2 --network-calls=2 2>&1 | Out-File -FilePath $autorestLog -Encoding utf8
92+
$autorestExitCode = $LASTEXITCODE
93+
if ($autorestExitCode -ne 0) {
94+
Write-Host -ForegroundColor Red "AutoREST failed (exit $autorestExitCode) generating '$ModuleFullName'."
95+
Write-Host -ForegroundColor Yellow "=== AutoREST log: $autorestLog ==="
96+
if (Test-Path $autorestLog) { Get-Content $autorestLog | ForEach-Object { Write-Host $_ } }
97+
Write-Host -ForegroundColor Yellow "=== End AutoREST log ==="
98+
return $autorestExitCode
7699
}
77100
Write-Debug "AutoRest generated '$ModuleFullName' successfully."
78101

tools/ManageGeneratedModule.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ foreach ($Package in $NugetPackagesToRemove) {
4747
# Add nuget packages from generate modules.
4848
foreach ($Package in $NugetPackagesToAdd) {
4949
Write-Debug "Executing: dotnet add $ModuleCsProj package $Package"
50-
dotnet add $ModuleCsProj package $Package -s https://api.nuget.org/v3/index.json | Out-Null
50+
dotnet add $ModuleCsProj package $Package | Out-Null
5151
if ($LastExitCode) {
5252
Write-Error "Failed to execute: dotnet add $ModuleCsProj package $Package"
5353
}

tools/ReadModuleReadMe.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ param(
66
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string] $FieldToRead
77
)
88
$ErrorActionPreference = "Stop"
9+
. "$PSScriptRoot\utilities\utils.ps1"
10+
911
$FieldValue = $null
1012
# Read readme.md.
1113
$ReadMeContent = Get-Content $ReadMePath -Delimiter "### Versioning"
1214
if ($ReadMeContent.Length -eq 2) {
1315
# Convert versioning section to yaml.
14-
$VersioningSection = $ReadMeContent[1].Replace("``", "").Replace("yaml", "") | ConvertFrom-Yaml
16+
$VersioningSection = $ReadMeContent[1].Replace("``", "").Replace("yaml", "") | ConvertFrom-SimpleYaml
1517
$FieldValue = $VersioningSection[$FieldToRead]
1618
}
1719
return $FieldValue
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
<#
5+
.SYNOPSIS
6+
Pre-populates the autorest extension cache so that autorest makes zero npm
7+
network calls during module generation.
8+
9+
.DESCRIPTION
10+
Autorest resolves extensions from ~/.autorest/<pkg>/<version>/node_modules/.
11+
This script pre-installs every extension referenced by
12+
autorest-configuration.md into that cache layout.
13+
Registry and auth are read from ~/.npmrc (copied from the authenticated
14+
.npmrc earlier in the pipeline).
15+
#>
16+
17+
[CmdletBinding()]
18+
param()
19+
20+
$extensions = @(
21+
"@autorest/core@3.10.4",
22+
"@autorest/modelerfour@4.24.3"
23+
)
24+
25+
foreach ($ext in $extensions) {
26+
$parts = $ext -split '@(?=[^@]+$)' # split on last @
27+
$pkg = $parts[0]
28+
$ver = $parts[1]
29+
$cacheDir = Join-Path $env:USERPROFILE ".autorest\$($pkg.Replace('/','\'))\$ver"
30+
$nodeModules = Join-Path $cacheDir "node_modules\$($pkg.Replace('/','\'))"
31+
32+
if (Test-Path $nodeModules) {
33+
Write-Host "Cache already present: $ext"
34+
continue
35+
}
36+
37+
New-Item -ItemType Directory -Force -Path $cacheDir | Out-Null
38+
Write-Host "Pre-installing $ext into $cacheDir"
39+
npm install $ext --prefix $cacheDir
40+
if ($LASTEXITCODE -ne 0) { throw "Failed to pre-install $ext (exit $LASTEXITCODE)" }
41+
Write-Host "Done: $ext"
42+
}

tools/Utilities/utils.ps1

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,18 @@ Function Get-LocalCertificate {
2424
$global:DefaultCertificate = $pfxCertificate
2525
}
2626
return $global:DefaultCertificate
27+
}
28+
29+
<#
30+
Converts a simple single-level YAML string into a hashtable.
31+
#>
32+
Function ConvertFrom-SimpleYaml {
33+
param([string]$Yaml)
34+
$result = @{}
35+
$Yaml -split "`n" | ForEach-Object {
36+
if ($_.Trim() -match '^([^:]+):\s*(.*)$') {
37+
$result[$Matches[1].Trim()] = $Matches[2].Trim()
38+
}
39+
}
40+
return $result
2741
}

tools/WriteToModuleReadMe.ps1

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,18 @@ param(
77
[Parameter(Mandatory = $true)][ValidateNotNullOrEmpty()][string] $NewFieldValue
88
)
99
$ErrorActionPreference = "Stop"
10+
. "$PSScriptRoot\utilities\utils.ps1"
11+
1012
# Read readme.md.
1113
$ReadMeContent = Get-Content $ReadMePath -Delimiter "### Versioning"
1214
if ($ReadMeContent.Length -eq 2) {
1315
# Convert versioning section to yaml.
1416
$UpdatedVersionSection = "### Versioning" + $ReadMeContent[1]
15-
$VersioningSection = $ReadMeContent[1].Replace("``", "").Replace("yaml", "") | ConvertFrom-Yaml
17+
$VersioningSection = $ReadMeContent[1].Replace("``", "").Replace("yaml", "") | ConvertFrom-SimpleYaml
1618
$FieldValue = $VersioningSection[$FieldName]
17-
$RegexPattern = "$FieldName`:\s*$FieldValue"
19+
$EscapedFieldName = [regex]::Escape($FieldName)
20+
$EscapedFieldValue = [regex]::Escape($FieldValue)
21+
$RegexPattern = "$EscapedFieldName`:\s*$EscapedFieldValue"
1822
$UpdatedVersionSection = $UpdatedVersionSection -replace $RegexPattern, "$FieldName`: $NewFieldValue"
1923

2024
$ReadMeContent[0] = $ReadMeContent[0].Trim()

0 commit comments

Comments
 (0)