11# DevDiv pipeline setup steps
2+ #
3+ # Notes on custom registry support:
4+ # - Custom npm registry setup is implemented inline below (no external scripts).
5+ # - The template creates a temp .npmrc, sets NPM_CONFIG_USERCONFIG/REGISTRY, and
6+ # rewrites lockfiles via a transient JS helper emitted into Agent.TempDirectory.
27parameters :
38 - name : installNode
49 type : boolean
@@ -30,17 +35,47 @@ steps:
3035 - ${{ if ne(parameters.customNPMRegistry, '') }} :
3136 # When using a private/custom registry, configure npm to read auth/config from a temp user config
3237 # instead of relying on a checked-in project .npmrc.
33- - pwsh : >
34- $(Build.SourcesDirectory)/build/scripts/ensure-npm-userconfig.ps1
35- -Path "${{ parameters.npmrcPath }}"
36- -Registry "${{ parameters.customNPMRegistry }}"
38+ - pwsh : |
39+ $path = "${{ parameters.npmrcPath }}"
40+
41+ if (Test-Path -LiteralPath $path -PathType Container) {
42+ throw "npmrcPath points to a directory (expected a file): $path"
43+ }
44+
45+ $parent = Split-Path -Parent $path
46+ if ($parent -and -not (Test-Path -LiteralPath $parent)) {
47+ New-Item -ItemType Directory -Path $parent -Force | Out-Null
48+ }
49+
50+ if (-not (Test-Path -LiteralPath $path -PathType Leaf)) {
51+ New-Item -ItemType File -Path $path -Force | Out-Null
52+ }
53+
54+ Write-Host "##vso[task.setvariable variable=NPM_CONFIG_USERCONFIG]$path"
55+ Write-Host "##vso[task.setvariable variable=NPM_CONFIG_REGISTRY]${{ parameters.customNPMRegistry }}"
3756 displayName: 📦 Setup NPM User Config
3857
3958 # Configure npm/yarn to use the custom registry and ensure auth headers are sent.
40- - pwsh : >
41- $(Build.SourcesDirectory)/build/scripts/setup-npm-and-yarn.ps1
42- -NpmrcPath "${{ parameters.npmrcPath }}"
43- -Registry "${{ parameters.customNPMRegistry }}"
59+ - pwsh : |
60+ $path = "${{ parameters.npmrcPath }}"
61+ $registry = "${{ parameters.customNPMRegistry }}"
62+
63+ $env:NPM_CONFIG_USERCONFIG = $path
64+ $env:NPM_CONFIG_REGISTRY = $registry
65+
66+ npm config set registry "$registry"
67+
68+ # npm >v7 deprecated the `always-auth` config option, refs npm/cli@72a7eeb
69+ # following is a workaround for yarn-like clients to send authorization header for GET
70+ # requests to the registry.
71+ "always-auth=true" | Out-File -FilePath $path -Append -Encoding utf8
72+
73+ $yarn = Get-Command yarn -ErrorAction SilentlyContinue
74+ if ($null -ne $yarn) {
75+ yarn config set registry "$registry"
76+ } else {
77+ Write-Host "yarn not found; skipping yarn registry configuration"
78+ }
4479 displayName: 📦 Setup NPM & Yarn
4580
4681 # Populate the temp .npmrc with auth for the configured registry.
@@ -49,17 +84,57 @@ steps:
4984 workingFile : ${{ parameters.npmrcPath }}
5085 displayName : 📦 Setup NPM Authentication
5186
52- # Ensure the registry always sends auth headers (npmAuthenticate may overwrite the file).
53- - pwsh : >
54- $(Build.SourcesDirectory)/build/scripts/finalize-npm-config.ps1
55- -Path "${{ parameters.npmrcPath }}"
56- displayName: 📦 Finalize NPM config
57-
5887 # Some lockfiles contain hardcoded references to public registries. Rewrite them so installs
5988 # and `npx` resolve from the custom registry consistently.
60- - pwsh : >
61- $(Build.SourcesDirectory)/build/scripts/setup-npm-registry.ps1
62- -Registry "${{ parameters.customNPMRegistry }}"
89+ - pwsh : |
90+ $registry = "${{ parameters.customNPMRegistry }}"
91+ $env:NPM_CONFIG_REGISTRY = $registry
92+ $scriptPath = Join-Path "$(Agent.TempDirectory)" 'setup-npm-registry.js'
93+ $lines = @(
94+ "const fs = require('fs').promises;",
95+ "const path = require('path');",
96+ "",
97+ "async function* getLockFiles(dir) {",
98+ " const files = await fs.readdir(dir);",
99+ "",
100+ " for (const file of files) {",
101+ " const fullPath = path.join(dir, file);",
102+ " const stat = await fs.stat(fullPath);",
103+ "",
104+ " if (stat.isDirectory()) {",
105+ " if (file === 'node_modules' || file === '.git') {",
106+ " continue;",
107+ " }",
108+ " yield* getLockFiles(fullPath);",
109+ " } else if (file === 'yarn.lock' || file === 'package-lock.json') {",
110+ " yield fullPath;",
111+ " }",
112+ " }",
113+ "}",
114+ "",
115+ "async function rewrite(file, registry) {",
116+ " let contents = await fs.readFile(file, 'utf8');",
117+ " const re = new RegExp('https://registry\\.[^.]+\\.(com|org)/', 'g');",
118+ " contents = contents.replace(re, registry);",
119+ " await fs.writeFile(file, contents);",
120+ "}",
121+ "",
122+ "async function main() {",
123+ " const root = process.cwd();",
124+ " const registry = process.env.NPM_CONFIG_REGISTRY;",
125+ " if (!registry) { throw new Error('NPM_CONFIG_REGISTRY is not set'); }",
126+ "",
127+ " for await (const file of getLockFiles(root)) {",
128+ " await rewrite(file, registry);",
129+ " console.log('Updated node registry:', file);",
130+ " }",
131+ "}",
132+ "",
133+ "main();"
134+ )
135+
136+ Set-Content -LiteralPath $scriptPath -Value ($lines -join "`n") -Encoding utf8
137+ node $scriptPath
63138 displayName: 📦 Setup NPM Registry
64139
65140 - script : npm config get registry
0 commit comments