Skip to content

Commit a5e7a4b

Browse files
authored
Automate Python docs.ms onboarding (Azure#20061)
Automates onboarding of packages for docs.ms CI. Includes validation to ensure that new and updated packages install properly. Does not re-validate packages that have not changed. docindex successful run (pushing against a test branch, not main): https://dev.azure.com/azure-sdk/internal/_build/results?buildId=1030266&view=logs&j=293d82b9-e70f-547c-66fb-8f46778b3a20 docs CI running against test branch: https://apidrop.visualstudio.com/Content%20CI/_build/results?buildId=244126&view=logs&j=fd490c07-0b22-5182-fac9-6d67fe1e939b&t=e69637ba-916f-5e3a-caa0-a58126252db6
1 parent 10867f9 commit a5e7a4b

File tree

2 files changed

+230
-44
lines changed

2 files changed

+230
-44
lines changed

eng/pipelines/docindex.yml

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,55 @@ trigger: none
22

33
jobs:
44
- template: /eng/common/pipelines/templates/jobs/docindex.yml
5+
6+
- job: UpdateDocsMsBuildConfig
7+
pool:
8+
vmImage: ubuntu-20.04
9+
variables:
10+
DocRepoLocation: $(Pipeline.Workspace)/docs
11+
DocRepoOwner: MicrosoftDocs
12+
DocRepoName: azure-docs-sdk-python
13+
steps:
14+
# Docs CI uses Python 3.6.8 but that is not available on this image
15+
- task: UsePythonVersion@0
16+
displayName: 'Use Python 3.6'
17+
inputs:
18+
versionSpec: '3.6'
19+
20+
# Docs CI upgrades pip, wheel, and setuptools
21+
- pwsh: pip install --upgrade pip wheel setuptools
22+
displayName: Update python tools for package verification
23+
24+
# Sync docs repo onboarding files/folders
25+
- template: /eng/common/pipelines/templates/steps/sparse-checkout.yml
26+
parameters:
27+
SkipDefaultCheckout: true
28+
Paths:
29+
- ci-configs/packages-latest.json
30+
- ci-configs/packages-preview.json
31+
- metadata/
32+
Repositories:
33+
- Name: $(DocRepoOwner)/$(DocRepoName)
34+
WorkingDirectory: $(DocRepoLocation)
35+
36+
# Call update docs ci script to onboard packages
37+
- task: Powershell@2
38+
inputs:
39+
pwsh: true
40+
filePath: eng/common/scripts/Update-DocsMsPackages.ps1
41+
arguments: -DocRepoLocation $(DocRepoLocation)
42+
displayName: Update Docs Onboarding
43+
44+
# Push changes to docs repo
45+
- template: /eng/common/pipelines/templates/steps/set-default-branch.yml
46+
parameters:
47+
WorkingDirectory: $(DocRepoLocation)
48+
49+
- template: /eng/common/pipelines/templates/steps/git-push-changes.yml
50+
parameters:
51+
BaseRepoBranch: $(DefaultBranch)
52+
BaseRepoOwner: $(DocRepoOwner)
53+
CommitMsg: "Update docs CI configuration"
54+
TargetRepoName: $(DocRepoName)
55+
TargetRepoOwner: $(DocRepoOwner)
56+
WorkingDirectory: $(DocRepoLocation)

eng/scripts/Language-Settings.ps1

Lines changed: 178 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -161,68 +161,202 @@ function Get-python-GithubIoDocIndex()
161161
GenerateDocfxTocContent -tocContent $tocContent -lang "Python" -campaignId "UA-62780441-36"
162162
}
163163

164-
# Updates a python CI configuration json.
165-
# For "latest", the version attribute is cleared, as default behavior is to pull latest "non-preview".
166-
# For "preview", we update to >= the target releasing package version.
167-
function Update-python-CIConfig($pkgs, $ciRepo, $locationInDocRepo, $monikerId=$null)
168-
{
169-
$pkgJsonLoc = (Join-Path -Path $ciRepo -ChildPath $locationInDocRepo)
164+
function ValidatePackage($packageName, $packageVersion, $workingDirectory) {
165+
$packageExpression = "$packageName$packageVersion"
166+
Write-Host "Validating $packageExpression"
167+
168+
$installTargetFolder = Join-Path $workingDirectory $packageName
169+
New-Item -ItemType Directory -Force -Path $installTargetFolder | Out-Null
170+
171+
# Add more validation by replicating as much of the docs CI process as
172+
# possible
173+
# https://github.com/Azure/azure-sdk-for-python/issues/20109
174+
try {
175+
Write-Host "pip install $packageExpression --no-cache-dir --target $installTargetFolder"
176+
$pipInstallOutput = pip `
177+
install `
178+
$packageExpression `
179+
--no-cache-dir `
180+
--target $installTargetFolder 2>&1
181+
if ($LASTEXITCODE -ne 0) {
182+
LogWarning "pip install failed for $packageExpression"
183+
Write-Host $pipInstallOutput
184+
return $false
185+
}
186+
} catch {
187+
LogWarning "pip install failed for $packageExpression with exception"
188+
LogWarning $_.Exception
189+
LogWarning $_.Exception.StackTrace
190+
return $false
191+
}
170192

171-
if (-not (Test-Path $pkgJsonLoc)) {
172-
Write-Error "Unable to locate package json at location $pkgJsonLoc, exiting."
173-
exit(1)
193+
return $true
194+
}
195+
196+
$PackageExclusions = @{
197+
'azure-mgmt-apimanagement' = 'Unsupported doc directives https://github.com/Azure/azure-sdk-for-python/issues/18084';
198+
'azure-mgmt-reservations' = 'Unsupported doc directives https://github.com/Azure/azure-sdk-for-python/issues/18077';
199+
'azure-mgmt-signalr' = 'Unsupported doc directives https://github.com/Azure/azure-sdk-for-python/issues/18085';
200+
'azure-mgmt-mixedreality' = 'Missing version info https://github.com/Azure/azure-sdk-for-python/issues/18457';
201+
'azure-monitor-query' = 'Unsupported doc directives https://github.com/Azure/azure-sdk-for-python/issues/19417';
202+
'azure-mgmt-network' = 'Manual process used to build';
203+
}
204+
function Update-python-DocsMsPackages($DocsRepoLocation, $DocsMetadata) {
205+
Write-Host "Excluded packages:"
206+
foreach ($excludedPackage in $PackageExclusions.Keys) {
207+
Write-Host " $excludedPackage - $($PackageExclusions[$excludedPackage])"
174208
}
175209

176-
$allJson = Get-Content $pkgJsonLoc | ConvertFrom-Json
177-
$visibleInCI = @{}
210+
$FilteredMetadata = $DocsMetadata.Where({ !($PackageExclusions.ContainsKey($_.Package)) })
211+
212+
UpdateDocsMsPackages `
213+
(Join-Path $DocsRepoLocation 'ci-configs/packages-preview.json') `
214+
'preview' `
215+
$FilteredMetadata
216+
217+
UpdateDocsMsPackages `
218+
(Join-Path $DocsRepoLocation 'ci-configs/packages-latest.json') `
219+
'latest' `
220+
$FilteredMetadata
221+
}
178222

179-
for ($i=0; $i -lt $allJson.packages.Length; $i++) {
180-
$pkgDef = $allJson.packages[$i]
223+
function UpdateDocsMsPackages($DocConfigFile, $Mode, $DocsMetadata) {
224+
Write-Host "Updating configuration: $DocConfigFile with mode: $Mode"
225+
$packageConfig = Get-Content $DocConfigFile -Raw | ConvertFrom-Json
181226

182-
if ($pkgDef.package_info.name) {
183-
$visibleInCI[$pkgDef.package_info.name] = $i
227+
$installValidationFolder = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
228+
New-Item -ItemType Directory -Force -Path $installValidationFolder | Out-Null
229+
230+
231+
$outputPackages = @()
232+
foreach ($package in $packageConfig.packages) {
233+
$packageName = $package.package_info.name
234+
235+
if (!$packageName) {
236+
Write-Host "Keeping package with no name: $($package.package_info)"
237+
$outputPackages += $package
238+
continue
184239
}
185-
}
186240

187-
foreach ($releasingPkg in $pkgs) {
188-
if ($visibleInCI.ContainsKey($releasingPkg.PackageId)) {
189-
$packagesIndex = $visibleInCI[$releasingPkg.PackageId]
190-
$existingPackageDef = $allJson.packages[$packagesIndex]
241+
if ($package.package_info.install_type -ne 'pypi') {
242+
Write-Host "Keeping package with install_type not 'pypi': $($package.package_info.name)"
243+
$outputPackages += $package
244+
continue
245+
}
191246

192-
if ($releasingPkg.IsPrerelease) {
193-
if (-not $existingPackageDef.package_info.version) {
194-
$existingPackageDef.package_info | Add-Member -NotePropertyName version -NotePropertyValue ""
195-
}
247+
# Do not filter by GA/Preview status because we want differentiate between
248+
# tracked and non-tracked packages
249+
$matchingPublishedPackageArray = $DocsMetadata.Where( { $_.Package -eq $packageName })
250+
251+
# If this package does not match any published packages keep it in the list.
252+
# This handles packages which are not tracked in metadata but still need to
253+
# be built in Docs CI.
254+
if ($matchingPublishedPackageArray.Count -eq 0) {
255+
Write-Host "Keep non-tracked package: $packageName"
256+
$outputPackages += $package
257+
continue
258+
}
196259

197-
$existingPackageDef.package_info.version = ">=$($releasingPkg.PackageVersion)"
198-
}
199-
else {
200-
if ($existingPackageDef.package_info.version) {
201-
$existingPackageDef.package_info.PSObject.Properties.Remove('version')
202-
}
203-
}
260+
if ($matchingPublishedPackageArray.Count -gt 1) {
261+
LogWarning "Found more than one matching published package in metadata for $packageName; only updating first entry"
204262
}
205-
else {
206-
$newItem = New-Object PSObject -Property @{
207-
package_info = New-Object PSObject -Property @{
208-
prefer_source_distribution = "true"
209-
install_type = "pypi"
210-
name=$releasingPkg.PackageId
211-
}
212-
exclude_path = @("test*","example*","sample*","doc*")
263+
$matchingPublishedPackage = $matchingPublishedPackageArray[0]
264+
265+
if ($Mode -eq 'preview' -and !$matchingPublishedPackage.VersionPreview.Trim()) {
266+
# If we are in preview mode and the package does not have a superseding
267+
# preview version, remove the package from the list.
268+
Write-Host "Remove superseded preview package: $packageName"
269+
continue
270+
}
271+
272+
if ($Mode -eq 'latest' -and !$matchingPublishedPackage.VersionGA.Trim()) {
273+
LogWarning "Metadata is missing GA version for GA package $packageName. Keeping existing package."
274+
$outputPackages += $package
275+
continue
276+
}
277+
278+
$packageVersion = "==$($matchingPublishedPackage.VersionGA)"
279+
if ($Mode -eq 'preview') {
280+
if (!$matchingPublishedPackage.VersionPreview.Trim()) {
281+
LogWarning "Metadata is missing preview version for preview package $packageName. Keeping existing package."
282+
$outputPackages += $package
283+
continue
213284
}
285+
$packageVersion = "==$($matchingPublishedPackage.VersionPreview)"
286+
}
214287

215-
if ($releasingPkg.IsPrerelease) {
216-
$newItem.package_info | Add-Member -NotePropertyName version -NotePropertyValue ">=$($releasingPkg.PackageVersion)"
288+
# If upgrading the package, run basic sanity checks against the package
289+
if ($package.package_info.version -ne $packageVersion) {
290+
Write-Host "New version detected for $packageName ($packageVersion)"
291+
if (!(ValidatePackage -packageName $packageName -packageVersion $packageVersion -workingDirectory $installValidationFolder)) {
292+
LogWarning "Package is not valid: $packageName. Keeping old version."
293+
$outputPackages += $package
294+
continue
217295
}
218296

219-
$allJson.packages += $newItem
297+
$package.package_info = Add-Member `
298+
-InputObject $package.package_info `
299+
-MemberType NoteProperty `
300+
-Name 'version' `
301+
-Value $packageVersion `
302+
-PassThru `
303+
-Force
304+
}
305+
306+
Write-Host "Keeping tracked package: $packageName."
307+
$outputPackages += $package
308+
}
309+
310+
$outputPackagesHash = @{}
311+
foreach ($package in $outputPackages) {
312+
# In some cases there is no $package.package_info.name, only hash if the
313+
# name is set.
314+
if ($package.package_info.name) {
315+
$outputPackagesHash[$package.package_info.name] = $true
220316
}
221317
}
222318

223-
$jsonContent = $allJson | ConvertTo-Json -Depth 10 | % {$_ -replace "(?m) (?<=^(?: )*)", " " }
319+
$remainingPackages = @()
320+
if ($Mode -eq 'preview') {
321+
$remainingPackages = $DocsMetadata.Where({
322+
$_.VersionPreview.Trim() -and !$outputPackagesHash.ContainsKey($_.Package)
323+
})
324+
} else {
325+
$remainingPackages = $DocsMetadata.Where({
326+
$_.VersionGA.Trim() -and !$outputPackagesHash.ContainsKey($_.Package)
327+
})
328+
}
329+
330+
# Add packages that exist in the metadata but are not onboarded in docs config
331+
foreach ($package in $remainingPackages) {
332+
$packageName = $package.Package
333+
$packageVersion = "==$($package.VersionGA)"
334+
if ($Mode -eq 'preview') {
335+
$packageVersion = "==$($package.VersionPreview)"
336+
}
337+
338+
if (!(ValidatePackage -packageName $packageName -packageVersion $packageVersion -workingDirectory $installValidationFolder)) {
339+
LogWarning "Package is not valid: $packageName. Cannot onboard."
340+
continue
341+
}
342+
343+
Write-Host "Add new package from metadata: $packageName"
344+
$package = [ordered]@{
345+
package_info = [ordered]@{
346+
name = $packageName;
347+
install_type = 'pypi';
348+
prefer_source_distribution = 'true';
349+
version = $packageVersion;
350+
};
351+
exclude_path = @("test*","example*","sample*","doc*");
352+
}
353+
354+
$outputPackages += $package
355+
}
224356

225-
Set-Content -Path $pkgJsonLoc -Value $jsonContent
357+
$packageConfig.packages = $outputPackages
358+
$packageConfig | ConvertTo-Json -Depth 100 | Set-Content $DocConfigFile
359+
Write-Host "Onboarding configuration written to: $DocConfigFile"
226360
}
227361

228362
# function is used to auto generate API View

0 commit comments

Comments
 (0)