From be7fdcaf334b87406c28147ae3bcca6c07a29bdf Mon Sep 17 00:00:00 2001 From: Azure SDK Bot <53356347+azure-sdk@users.noreply.github.com> Date: Wed, 13 May 2020 10:05:03 -0700 Subject: [PATCH] Sync eng/common directory with azure-sdk-tools repository (#97) --- .../templates/steps/create-pull-request.yml | 4 +- .../templates/steps/docs-metadata-release.yml | 61 +++ .../templates/steps/verify-agent-os.yml | 38 ++ eng/common/scripts/SemVer.ps1 | 8 +- .../scripts/artifact-metadata-parsing.ps1 | 472 ++++++++++++++++++ eng/common/scripts/copy-readmes-to-docs.ps1 | 87 ---- .../scripts/create-tags-and-git-release.ps1 | 442 +--------------- eng/common/scripts/git-branch-push.ps1 | 1 - eng/common/scripts/update-docs-metadata.ps1 | 125 +++++ 9 files changed, 702 insertions(+), 536 deletions(-) create mode 100644 eng/common/pipelines/templates/steps/docs-metadata-release.yml create mode 100644 eng/common/pipelines/templates/steps/verify-agent-os.yml create mode 100644 eng/common/scripts/artifact-metadata-parsing.ps1 delete mode 100644 eng/common/scripts/copy-readmes-to-docs.ps1 create mode 100644 eng/common/scripts/update-docs-metadata.ps1 diff --git a/eng/common/pipelines/templates/steps/create-pull-request.yml b/eng/common/pipelines/templates/steps/create-pull-request.yml index 1f8d72cc8..46ff5a8f6 100644 --- a/eng/common/pipelines/templates/steps/create-pull-request.yml +++ b/eng/common/pipelines/templates/steps/create-pull-request.yml @@ -16,8 +16,8 @@ parameters: steps: - pwsh: | - echo "git add ." - git add . + echo "git add -A" + git add -A echo "git diff --name-status --cached --exit-code" git diff --name-status --cached --exit-code diff --git a/eng/common/pipelines/templates/steps/docs-metadata-release.yml b/eng/common/pipelines/templates/steps/docs-metadata-release.yml new file mode 100644 index 000000000..3ffdd5ec6 --- /dev/null +++ b/eng/common/pipelines/templates/steps/docs-metadata-release.yml @@ -0,0 +1,61 @@ +# intended to be used as part of a release process +parameters: + ArtifactLocation: 'not-specified' + PackageRepository: 'not-specified' + ReleaseSha: 'not-specified' + RepoId: 'not-specified' + WorkingDirectory: '' + ScriptDirectory: eng/common/scripts + TargetDocRepoName: '' + TargetDocRepoOwner: '' + PRBranchName: 'smoke-test-rdme' + ArtifactName: '' + Language: '' + DocRepoDestinationPath: '' #usually docs-ref-services/ + +steps: +- pwsh: | + git clone https://github.com/${{ parameters.TargetDocRepoOwner }}/${{ parameters.TargetDocRepoName }} ${{ parameters.WorkingDirectory }}/repo + + try { + Push-Location ${{ parameters.WorkingDirectory }}/repo + + Write-Host "git checkout smoke-test" + git checkout smoke-test + } finally { + Pop-Location + } + displayName: Clone Documentation Repository + ignoreLASTEXITCODE: false + +- task: PowerShell@2 + displayName: 'Apply Documentation Updates From Artifact' + inputs: + targetType: filePath + filePath: ${{ parameters.ScriptDirectory }}/update-docs-metadata.ps1 + arguments: > + -ArtifactLocation ${{parameters.ArtifactLocation}} + -Repository ${{parameters.PackageRepository}} + -ReleaseSHA ${{parameters.ReleaseSha}} + -RepoId ${{parameters.RepoId}} + -WorkDirectory '${{parameters.WorkingDirectory}}' + -DocRepoLocation "${{parameters.WorkingDirectory}}/repo" + -Language "${{parameters.Language}}" + -DocRepoContentLocation ${{ parameters.DocRepoDestinationPath }} + pwsh: true + env: + GH_TOKEN: $(azuresdk-github-pat) + +- template: /eng/common/pipelines/templates/steps/create-pull-request.yml + parameters: + RepoName: ${{ parameters.TargetDocRepoName }} + RepoOwner: ${{ parameters.TargetDocRepoOwner }} + PRBranchName: ${{ parameters.PRBranchName }} + CommitMsg: "Update readme content for ${{ parameters.ArtifactName }}" + PRTitle: "Docs.MS Readme Update." + BaseBranchName: smoke-test + WorkingDirectory: ${{parameters.WorkingDirectory}}/repo + ScriptDirectory: ${{parameters.WorkingDirectory}}/${{parameters.ScriptDirectory}} + + + diff --git a/eng/common/pipelines/templates/steps/verify-agent-os.yml b/eng/common/pipelines/templates/steps/verify-agent-os.yml new file mode 100644 index 000000000..1786612ba --- /dev/null +++ b/eng/common/pipelines/templates/steps/verify-agent-os.yml @@ -0,0 +1,38 @@ +# Template for all Python Scripts in this repository +parameters: + OSVmImage: $(OSVmImage) + +steps: + - task: PythonScript@0 + displayName: Verify Agent OS + inputs: + scriptSource: inline + script: | + # Script verifies the operating system for the platform on which it is being run + # Used in build pipelines to verify the build agent os + # Variable: The friendly name or image name of the os to verfy against + from __future__ import print_function + import sys + import platform + + os_parameter = "${{ parameters.OSVmImage }}".lower() + if os_parameter.startswith('mac') or os_parameter.startswith('darwin'): + os_parameter = 'macOS' + elif os_parameter.startswith('ubuntu') or os_parameter.startswith('linux'): + os_parameter = 'Linux' + elif os_parameter.startswith('vs') or os_parameter.startswith('win'): + os_parameter = 'Windows' + else: + raise Exception('Variable OSVmImage is empty or has an unexpected value [${{ parameters.OSVmImage }}]') + + + print("Job requested to run on OS: %s" % (os_parameter)) + + agent_os = platform.system() + agent_os = 'macOS' if agent_os == 'Darwin' else agent_os + + if (agent_os.lower() == os_parameter.lower()): + print('Job ran on OS: %s' % (agent_os)) + print('##vso[task.setvariable variable=OSName]%s' % (agent_os)) + else: + raise Exception('Job ran on the wrong OS: %s' % (agent_os)) diff --git a/eng/common/scripts/SemVer.ps1 b/eng/common/scripts/SemVer.ps1 index c0a700f2c..402c9fb1f 100644 --- a/eng/common/scripts/SemVer.ps1 +++ b/eng/common/scripts/SemVer.ps1 @@ -18,8 +18,9 @@ class AzureEngSemanticVersion { [int] $PrereleaseNumber [bool] $IsPrerelease [string] $RawVersion + # Regex inspired but simplified from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string + static [string] $SEMVER_REGEX = "(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-?(?[a-zA-Z-]*)(?:\.?(?0|[1-9]\d*)))?" - static [AzureEngSemanticVersion] ParseVersionString([string] $versionString) { try { @@ -31,10 +32,7 @@ class AzureEngSemanticVersion { } AzureEngSemanticVersion([string] $versionString){ - # Regex inspired but simplifie from https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string - $SEMVER_REGEX = "^(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-?(?[a-zA-Z-]*)(?:\.?(?0|[1-9]\d*)))?$" - - if ($versionString -match $SEMVER_REGEX) { + if ($versionString -match "^$([AzureEngSemanticVersion]::SEMVER_REGEX)$") { if ($null -eq $matches['prelabel']) { # artifically provide these values for non-prereleases to enable easy sorting of them later than prereleases. $prelabel = "zzz" diff --git a/eng/common/scripts/artifact-metadata-parsing.ps1 b/eng/common/scripts/artifact-metadata-parsing.ps1 new file mode 100644 index 000000000..51ee8eb56 --- /dev/null +++ b/eng/common/scripts/artifact-metadata-parsing.ps1 @@ -0,0 +1,472 @@ +. (Join-Path $PSScriptRoot SemVer.ps1) + +$SDIST_PACKAGE_REGEX = "^(?.*)\-(?$([AzureEngSemanticVersion]::SEMVER_REGEX))" + +# Posts a github release for each item of the pkgList variable. SilentlyContinue +function CreateReleases($pkgList, $releaseApiUrl, $releaseSha) { + foreach ($pkgInfo in $pkgList) { + Write-Host "Creating release $($pkgInfo.Tag)" + + $releaseNotes = "" + if ($pkgInfo.ReleaseNotes[$pkgInfo.PackageVersion].ReleaseContent -ne $null) { + $releaseNotes = $pkgInfo.ReleaseNotes[$pkgInfo.PackageVersion].ReleaseContent + } + + $isPrerelease = $False + + $parsedSemver = [AzureEngSemanticVersion]::ParseVersionString($pkgInfo.PackageVersion) + + if ($parsedSemver) { + $isPrerelease = $parsedSemver.IsPrerelease + } + + $url = $releaseApiUrl + $body = ConvertTo-Json @{ + tag_name = $pkgInfo.Tag + target_commitish = $releaseSha + name = $pkgInfo.Tag + draft = $False + prerelease = $isPrerelease + body = $releaseNotes + } + + $headers = @{ + "Content-Type" = "application/json" + "Authorization" = "token $($env:GH_TOKEN)" + } + + Invoke-WebRequest-WithHandling -url $url -body $body -headers $headers -method "Post" + } +} + +function Invoke-WebRequest-WithHandling($url, $method, $body = $null, $headers = $null) { + $attempts = 1 + + while ($attempts -le 3) { + try { + return Invoke-RestMethod -Method $method -Uri $url -Body $body -Headers $headers + } + catch { + $response = $_.Exception.Response + + $statusCode = $response.StatusCode.value__ + $statusDescription = $response.StatusDescription + + if ($statusCode) { + Write-Host "API request attempt number $attempts to $url failed with statuscode $statusCode" + Write-Host $statusDescription + + Write-Host "Rate Limit Details:" + Write-Host "Total: $($response.Headers.GetValues("X-RateLimit-Limit"))" + Write-Host "Remaining: $($response.Headers.GetValues("X-RateLimit-Remaining"))" + Write-Host "Reset Epoch: $($response.Headers.GetValues("X-RateLimit-Reset"))" + } + else { + Write-Host "API request attempt number $attempts to $url failed with no statuscode present, exception follows:" + Write-Host $_.Exception.Response + Write-Host $_.Exception + } + + if ($attempts -ge 3) { + Write-Host "Abandoning Request $url after 3 attempts." + exit(1) + } + + Start-Sleep -s 10 + } + + $attempts += 1 + } +} + +# Parse out package publishing information given a maven POM file +function ParseMavenPackage($pkg, $workingDirectory) { + [xml]$contentXML = Get-Content $pkg + + $pkgId = $contentXML.project.artifactId + $pkgVersion = $contentXML.project.version + $groupId = if ($contentXML.project.groupId -eq $null) { $contentXML.project.parent.groupId } else { $contentXML.project.groupId } + + # if it's a snapshot. return $null (as we don't want to create tags for this, but we also don't want to fail) + if ($pkgVersion.Contains("SNAPSHOT")) { + return $null + } + + $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $pkg.DirectoryName -Recurse -Include "$($pkg.Basename)-changelog.md")[0] + + $readmeContentLoc = @(Get-ChildItem -Path $pkg.DirectoryName -Recurse -Include "$($pkg.Basename)-readme.md")[0] + if (Test-Path -Path $readmeContentLoc) { + $readmeContent = Get-Content -Raw $readmeContentLoc + } + + return New-Object PSObject -Property @{ + PackageId = $pkgId + PackageVersion = $pkgVersion + Deployable = $forceCreate -or !(IsMavenPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion -groupId $groupId.Replace(".", "/")) + ReleaseNotes = $releaseNotes + ReadmeContent = $readmeContent + } +} + +# Returns the maven (really sonatype) publish status of a package id and version. +function IsMavenPackageVersionPublished($pkgId, $pkgVersion, $groupId) { + try { + + $uri = "https://oss.sonatype.org/content/repositories/releases/$groupId/$pkgId/$pkgVersion/$pkgId-$pkgVersion.pom" + $pomContent = Invoke-RestMethod -MaximumRetryCount 3 -Method "GET" -uri $uri + + if ($pomContent -ne $null -or $pomContent.Length -eq 0) { + return $true + } + else { + return $false + } + } + catch { + $statusCode = $_.Exception.Response.StatusCode.value__ + $statusDescription = $_.Exception.Response.StatusDescription + + # if this is 404ing, then this pkg has never been published before + if ($statusCode -eq 404) { + return $false + } + + Write-Host "VersionCheck to maven for packageId $pkgId failed with statuscode $statusCode" + Write-Host $statusDescription + exit(1) + } +} + +# make certain to always take the package json closest to the top +function ResolvePkgJson($workFolder) { + $pathsWithComplexity = @() + foreach ($file in (Get-ChildItem -Path $workFolder -Recurse -Include "package.json")) { + $complexity = ($file.FullName -Split { $_ -eq "/" -or $_ -eq "\" }).Length + $pathsWithComplexity += New-Object PSObject -Property @{ + Path = $file + Complexity = $complexity + } + } + + return ($pathsWithComplexity | Sort-Object -Property Complexity)[0].Path +} + +# Parse out package publishing information given a .tgz npm artifact +function ParseNPMPackage($pkg, $workingDirectory) { + $workFolder = "$workingDirectory$($pkg.Basename)" + $origFolder = Get-Location + New-Item -ItemType Directory -Force -Path $workFolder + cd $workFolder + + tar -xzf $pkg + + $packageJSON = ResolvePkgJson -workFolder $workFolder | Get-Content | ConvertFrom-Json + $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0] + $readmeContentLoc = @(Get-ChildItem -Path $workFolder -Recurse -Include "README.md")[0] + if (Test-Path -Path $readmeContentLoc) { + $readmeContent = Get-Content -Raw $readmeContentLoc + } + + cd $origFolder + Remove-Item $workFolder -Force -Recurse -ErrorAction SilentlyContinue + + $pkgId = $packageJSON.name + $pkgVersion = $packageJSON.version + + $resultObj = New-Object PSObject -Property @{ + PackageId = $pkgId + PackageVersion = $pkgVersion + Deployable = $forceCreate -or !(IsNPMPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion) + ReleaseNotes = $releaseNotes + ReadmeContent = $readmeContent + } + + return $resultObj +} + +# Returns the npm publish status of a package id and version. +function IsNPMPackageVersionPublished($pkgId, $pkgVersion) { + $npmVersions = (npm show $pkgId versions) + + if ($LastExitCode -ne 0) { + npm ping + + if ($LastExitCode -eq 0) { + return $False + } + + Write-Host "Could not find a deployed version of $pkgId, and NPM connectivity check failed." + exit(1) + } + + $npmVersionList = $npmVersions.split(",") | % { return $_.replace("[", "").replace("]", "").Trim() } + return $npmVersionList.Contains($pkgVersion) +} + +# Parse out package publishing information given a nupkg ZIP format. +function ParseNugetPackage($pkg, $workingDirectory) { + $workFolder = "$workingDirectory$($pkg.Basename)" + $origFolder = Get-Location + $zipFileLocation = "$workFolder/$($pkg.Basename).zip" + New-Item -ItemType Directory -Force -Path $workFolder + + Copy-Item -Path $pkg -Destination $zipFileLocation + Expand-Archive -Path $zipFileLocation -DestinationPath $workFolder + [xml] $packageXML = Get-ChildItem -Path "$workFolder/*.nuspec" | Get-Content + $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0] + + $readmeContentLoc = @(Get-ChildItem -Path $workFolder -Recurse -Include "README.md")[0] + if (Test-Path -Path $readmeContentLoc) { + $readmeContent = Get-Content -Raw $readmeContentLoc + } + + Remove-Item $workFolder -Force -Recurse -ErrorAction SilentlyContinue + $pkgId = $packageXML.package.metadata.id + $pkgVersion = $packageXML.package.metadata.version + + return New-Object PSObject -Property @{ + PackageId = $pkgId + PackageVersion = $pkgVersion + Deployable = $forceCreate -or !(IsNugetPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion) + ReleaseNotes = $releaseNotes + ReadmeContent = $readmeContent + } +} + +# Returns the nuget publish status of a package id and version. +function IsNugetPackageVersionPublished($pkgId, $pkgVersion) { + + $nugetUri = "https://api.nuget.org/v3-flatcontainer/$($pkgId.ToLowerInvariant())/index.json" + + try { + $nugetVersions = Invoke-RestMethod -MaximumRetryCount 3 -uri $nugetUri -Method "GET" + + return $nugetVersions.versions.Contains($pkgVersion) + } + catch { + $statusCode = $_.Exception.Response.StatusCode.value__ + $statusDescription = $_.Exception.Response.StatusDescription + + # if this is 404ing, then this pkg has never been published before + if ($statusCode -eq 404) { + return $False + } + + Write-Host "Nuget Invocation failed:" + Write-Host "StatusCode:" $statusCode + Write-Host "StatusDescription:" $statusDescription + exit(1) + } + +} + +# Parse out package publishing information given a python sdist of ZIP format. +function ParsePyPIPackage($pkg, $workingDirectory) { + $pkg.Basename -match $SDIST_PACKAGE_REGEX | Out-Null + + $pkgId = $matches["package"] + $pkgVersion = $matches["versionstring"] + + $workFolder = "$workingDirectory$($pkg.Basename)" + $origFolder = Get-Location + New-Item -ItemType Directory -Force -Path $workFolder + + Expand-Archive -Path $pkg -DestinationPath $workFolder + $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0] + $readmeContentLoc = @(Get-ChildItem -Path $workFolder -Recurse -Include "README.md")[0] + if (Test-Path -Path $readmeContentLoc) { + $readmeContent = Get-Content -Raw $readmeContentLoc + } + Remove-Item $workFolder -Force -Recurse -ErrorAction SilentlyContinue + + return New-Object PSObject -Property @{ + PackageId = $pkgId + PackageVersion = $pkgVersion + Deployable = $forceCreate -or !(IsPythonPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion) + ReleaseNotes = $releaseNotes + ReadmeContent = $readmeContent + } +} + +function ParseCArtifact($pkg, $workingDirectory) { + $packageInfo = Get-Content -Raw -Path $pkg | ConvertFrom-JSON + $packageArtifactLocation = (Get-ItemProperty $pkg).Directory.FullName + + $releaseNotes = ExtractReleaseNotes -changeLogLocation @(Get-ChildItem -Path $packageArtifactLocation -Recurse -Include "CHANGELOG.md")[0] + + $readmeContentLoc = @(Get-ChildItem -Path $packageArtifactLocation -Recurse -Include "README.md")[0] + if (Test-Path -Path $readmeContentLoc) { + $readmeContent = Get-Content -Raw $readmeContentLoc + } + + return New-Object PSObject -Property @{ + PackageId = $packageInfo.name + PackageVersion = $packageInfo.version + # Artifact info is always considered deployable for C becasue it is not + # deployed anywhere. Dealing with duplicate tags happens downstream in + # CheckArtifactShaAgainstTagsList + Deployable = $true + ReleaseNotes = $releaseNotes + ReadmeContent = $readmeContent + } +} + +# Returns the pypi publish status of a package id and version. +function IsPythonPackageVersionPublished($pkgId, $pkgVersion) { + try { + $existingVersion = (Invoke-RestMethod -MaximumRetryCount 3 -Method "Get" -uri "https://pypi.org/pypi/$pkgId/$pkgVersion/json").info.version + + # if existingVersion exists, then it's already been published + return $True + } + catch { + $statusCode = $_.Exception.Response.StatusCode.value__ + $statusDescription = $_.Exception.Response.StatusDescription + + # if this is 404ing, then this pkg has never been published before + if ($statusCode -eq 404) { + return $False + } + + Write-Host "PyPI Invocation failed:" + Write-Host "StatusCode:" $statusCode + Write-Host "StatusDescription:" $statusDescription + exit(1) + } +} + +# Retrieves the list of all tags that exist on the target repository +function GetExistingTags($apiUrl) { + try { + return (Invoke-WebRequest-WithHandling -Method "GET" -url "$apiUrl/git/refs/tags" ) | % { $_.ref.Replace("refs/tags/", "") } + } + catch { + $statusCode = $_.Exception.Response.StatusCode.value__ + $statusDescription = $_.Exception.Response.StatusDescription + + Write-Host "Failed to retrieve tags from repository." + Write-Host "StatusCode:" $statusCode + Write-Host "StatusDescription:" $statusDescription + + # Return an empty list if there are no tags in the repo + if ($statusCode -eq 404) { + return @() + } + + exit(1) + } +} + +# Walk across all build artifacts, check them against the appropriate repository, return a list of tags/releases +function VerifyPackages($pkgRepository, $artifactLocation, $workingDirectory, $apiUrl, $releaseSha, $exitOnError = $True) { + $pkgList = [array]@() + $ParsePkgInfoFn = "" + $packagePattern = "" + + switch ($pkgRepository) { + "Maven" { + $ParsePkgInfoFn = "ParseMavenPackage" + $packagePattern = "*.pom" + break + } + "Nuget" { + $ParsePkgInfoFn = "ParseNugetPackage" + $packagePattern = "*.nupkg" + break + } + "NPM" { + $ParsePkgInfoFn = "ParseNPMPackage" + $packagePattern = "*.tgz" + break + } + "PyPI" { + $ParsePkgInfoFn = "ParsePyPIPackage" + $packagePattern = "*.zip" + break + } + "C" { + $ParsePkgInfoFn = "ParseCArtifact" + $packagePattern = "*.json" + } + default { + Write-Host "Unrecognized Language: $language" + exit(1) + } + } + + $pkgs = (Get-ChildItem -Path $artifactLocation -Include $packagePattern -Recurse -File) + + foreach ($pkg in $pkgs) { + try { + $parsedPackage = &$ParsePkgInfoFn -pkg $pkg -workingDirectory $workingDirectory + + if ($parsedPackage -eq $null) { + continue + } + + if ($parsedPackage.Deployable -ne $True -and $exitOnError) { + Write-Host "Package $($parsedPackage.PackageId) is marked with version $($parsedPackage.PackageVersion), the version $($parsedPackage.PackageVersion) has already been deployed to the target repository." + Write-Host "Maybe a pkg version wasn't updated properly?" + exit(1) + } + + $pkgList += New-Object PSObject -Property @{ + PackageId = $parsedPackage.PackageId + PackageVersion = $parsedPackage.PackageVersion + Tag = ($parsedPackage.PackageId + "_" + $parsedPackage.PackageVersion) + ReleaseNotes = $parsedPackage.ReleaseNotes + ReadmeContent = $parsedPackage.ReadmeContent + } + } + catch { + Write-Host $_.Exception.Message + exit(1) + } + } + + $results = @([array]$pkgList | Sort-Object -Property Tag -uniq) + + $existingTags = GetExistingTags($apiUrl) + + $intersect = $results | % { $_.Tag } | ? { $existingTags -contains $_ } + + if ($intersect.Length -gt 0 -and $exitOnError) { + CheckArtifactShaAgainstTagsList -priorExistingTagList $intersect -releaseSha $releaseSha -apiUrl $apiUrl -exitOnError $exitOnError + + # all the tags are clean. remove them from the list of releases we will publish. + $results = $results | ? { -not ($intersect -contains $_.Tag ) } + } + + return $results +} + +# given a set of tags that we want to release, we need to ensure that if they already DO exist. +# if they DO exist, quietly exit if the commit sha of the artifact matches that of the tag +# if the commit sha does not match, exit with error and report both problem shas +function CheckArtifactShaAgainstTagsList($priorExistingTagList, $releaseSha, $apiUrl, $exitOnError) { + $headers = @{ + "Content-Type" = "application/json" + "Authorization" = "token $($env:GH_TOKEN)" + } + + $unmatchedTags = @() + + foreach ($tag in $priorExistingTagList) { + $tagSha = (Invoke-WebRequest-WithHandling -Method "Get" -Url "$apiUrl/git/refs/tags/$tag" -Headers $headers)."object".sha + + if ($tagSha -eq $releaseSha) { + Write-Host "This package has already been released. The existing tag commit SHA $releaseSha matches the artifact SHA being processed. Skipping release step for this tag." + } + else { + Write-Host "The artifact SHA $releaseSha does not match that of the currently existing tag." + Write-Host "Tag with issues is $tag with commit SHA $tagSha" + + $unmatchedTags += $tag + } + } + + if ($unmatchedTags.Length -gt 0 -and $exitOnError) { + Write-Host "Tags already existing with different SHA versions. Exiting." + exit(1) + } +} \ No newline at end of file diff --git a/eng/common/scripts/copy-readmes-to-docs.ps1 b/eng/common/scripts/copy-readmes-to-docs.ps1 deleted file mode 100644 index 837e52f36..000000000 --- a/eng/common/scripts/copy-readmes-to-docs.ps1 +++ /dev/null @@ -1,87 +0,0 @@ -# Example Usage: ./copy-readmes-to-docs.ps1 -CodeRepo C:/repo/sdk-for-python -DocRepo C:/repo/azure-docs-sdk-python -# run the link updating script before this to get links fixed to the release -# Highly recommended that you use Powershell Core. -# git reset --hard origin/smoke-test on the doc repo prior to running this - -param ( - [String]$CodeRepo, - [String]$DocRepo, - [String]$TargetServices -) - -Write-Host "> $PSCommandPath $args" - -$PACKAGE_README_REGEX = ".*[\/\\]sdk[\\\/][^\/\\]+[\\\/][^\/\\]+[\/\\]README\.md" - -Write-Host "repo is $CodeRepo" - -$date = Get-Date -Format "MM/dd/yyyy" - -if ($CodeRepo -Match "net") -{ - $lang = ".NET" - $TARGET_FOLDER = Join-Path -Path $DocRepo -ChildPath "api/overview/azure" - $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/dotnet-packages.csv" -} -if ($CodeRepo -Match "python"){ - $lang = "Python" - $TARGET_FOLDER = Join-Path -Path $DocRepo -ChildPath "docs-ref-services" - $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/python-packages.csv" -} -if ($CodeRepo -Match "java"){ - $lang = "Java" - $TARGET_FOLDER = Join-Path -Path $DocRepo -ChildPath "docs-ref-services" - $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/java-packages.csv" -} -if ($CodeRepo -Match "js"){ - $lang = "JavaScript" - $TARGET_FOLDER = Join-Path -Path $DocRepo -ChildPath "docs-ref-services" - $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/js-packages.csv" -} - - - -$metadataResponse = Invoke-WebRequest -Uri $metadataUri | ConvertFrom-Csv - -if ([string]::IsNullOrWhiteSpace($TargetServices)) -{ - $selectedServices = $metadataResponse | ForEach-Object -Process {$_.RepoPath} | Get-Unique -} -else { - $selectedServices = $TargetServices -Split "," | % { return $_.Trim() } -} - - -foreach($service in $selectedServices){ - $readmePath = Join-Path -Path $CodeRepo -ChildPath "sdk/$service" - Write-Host "Examining: $readmePath" - - $libraries = $metadataResponse | Where-Object { $_.RepoPath -eq $service } - - foreach($library in $libraries){ - - $package = $library.Package - $version = If ([string]::IsNullOrWhiteSpace($library.VersionGA)) {$library.VersionPreview} Else {$library.VersionGA} - - $file = Join-Path -Path $readmePath -ChildPath "/$package/README.md" | Get-Item - Write-Host "`tOutputting $($file.FullName)" - - $fileContent = Get-Content $file - - $fileContent = $fileContent -Join "`n" - - $fileMatch = (Select-String -InputObject $fileContent -Pattern 'Azure .+? (client|plugin|shared) library for (JavaScript|Java|Python|\.NET)').Matches[0] - - $header = "---`r`ntitle: $fileMatch`r`nkeywords: Azure, $lang, SDK, API, $service, $package`r`nauthor: maggiepint`r`nms.author: magpint`r`nms.date: $date`r`nms.topic: article`r`nms.prod: azure`r`nms.technology: azure`r`nms.devlang: $lang`r`nms.service: $service`r`n---`r`n" - - $fileContent = $fileContent -replace $fileMatch, "$fileMatch - Version $version `r`n" - - $fileContent = "$header $fileContent" - - $readmeName = "$($file.Directory.Name.Replace('azure-','').Replace('Azure.', '').ToLower())-readme.md" - - $readmeOutputLocation = Join-Path $TARGET_FOLDER -ChildPath $readmeName - - Set-Content -Path $readmeOutputLocation -Value $fileContent - } -} \ No newline at end of file diff --git a/eng/common/scripts/create-tags-and-git-release.ps1 b/eng/common/scripts/create-tags-and-git-release.ps1 index f2f4c7738..56e3f22a4 100644 --- a/eng/common/scripts/create-tags-and-git-release.ps1 +++ b/eng/common/scripts/create-tags-and-git-release.ps1 @@ -20,447 +20,7 @@ param ( Write-Host "> $PSCommandPath $args" -$VERSION_REGEX = "(?\d+)(\.(?\d+))?(\.(?\d+))?((?
[^0-9][^\s]+))?"
-$SDIST_PACKAGE_REGEX = "^(?.*)\-(?$VERSION_REGEX$)"
-
-# Posts a github release for each item of the pkgList variable. SilentlyContinue
-function CreateReleases($pkgList, $releaseApiUrl, $releaseSha) {
-  foreach ($pkgInfo in $pkgList) {
-    Write-Host "Creating release $($pkgInfo.Tag)"
-
-    $releaseNotes = ""
-    if ($pkgInfo.ReleaseNotes[$pkgInfo.PackageVersion].ReleaseContent -ne $null) {
-      $releaseNotes = $pkgInfo.ReleaseNotes[$pkgInfo.PackageVersion].ReleaseContent
-    }
-
-    $isPrerelease = $False
-    if ($pkgInfo.PackageVersion -match $VERSION_REGEX) {
-      $preReleaseLabel = $matches["pre"]
-      $isPrerelease = ![string]::IsNullOrEmpty($preReleaseLabel)
-    }
-
-    $url = $releaseApiUrl
-    $body = ConvertTo-Json @{
-      tag_name         = $pkgInfo.Tag
-      target_commitish = $releaseSha
-      name             = $pkgInfo.Tag
-      draft            = $False
-      prerelease       = $isPrerelease
-      body             = $releaseNotes
-    }
-
-    $headers = @{
-      "Content-Type"  = "application/json"
-      "Authorization" = "token $($env:GH_TOKEN)"
-    }
-
-    FireAPIRequest -url $url -body $body -headers $headers -method "Post"
-  }
-}
-
-function FireAPIRequest($url, $method, $body = $null, $headers = $null) {
-  $attempts = 1
-
-  while ($attempts -le 3) {
-    try {
-      return Invoke-RestMethod -Method $method -Uri $url -Body $body -Headers $headers
-    }
-    catch {
-      $response = $_.Exception.Response
-
-      $statusCode = $response.StatusCode.value__
-      $statusDescription = $response.StatusDescription
-
-      if ($statusCode) {
-        Write-Host "API request attempt number $attempts to $url failed with statuscode $statusCode"
-        Write-Host $statusDescription
-
-        Write-Host "Rate Limit Details:"
-        Write-Host "Total: $($response.Headers.GetValues("X-RateLimit-Limit"))"
-        Write-Host "Remaining: $($response.Headers.GetValues("X-RateLimit-Remaining"))"
-        Write-Host "Reset Epoch: $($response.Headers.GetValues("X-RateLimit-Reset"))"
-      }
-      else {
-        Write-Host "API request attempt number $attempts to $url failed with no statuscode present, exception follows:"
-        Write-Host $_.Exception.Response
-        Write-Host $_.Exception
-      }
-
-      if ($attempts -ge 3) {
-        Write-Host "Abandoning Request $url after 3 attempts."
-        exit(1)
-      }
-
-      Start-Sleep -s 10
-    }
-
-    $attempts += 1
-  }
-}
-
-# Parse out package publishing information given a maven POM file
-function ParseMavenPackage($pkg, $workingDirectory) {
-  [xml]$contentXML = Get-Content $pkg
-
-  $pkgId = $contentXML.project.artifactId
-  $pkgVersion = $contentXML.project.version
-  $groupId = if ($contentXML.project.groupId -eq $null) { $contentXML.project.parent.groupId } else { $contentXML.project.groupId }
-
-  # if it's a snapshot. return $null (as we don't want to create tags for this, but we also don't want to fail)
-  if ($pkgVersion.Contains("SNAPSHOT")) {
-    return $null
-  }
-
-  $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $pkg.DirectoryName -Recurse -Include "$($pkg.Basename)-changelog.md")[0]
-
-  return New-Object PSObject -Property @{
-    PackageId      = $pkgId
-    PackageVersion = $pkgVersion
-    Deployable     = $forceCreate -or !(IsMavenPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion -groupId $groupId.Replace(".", "/"))
-    ReleaseNotes   = $releaseNotes
-  }
-}
-
-# Returns the maven (really sonatype) publish status of a package id and version.
-function IsMavenPackageVersionPublished($pkgId, $pkgVersion, $groupId) {
-  try {
-
-    $uri = "https://oss.sonatype.org/content/repositories/releases/$groupId/$pkgId/$pkgVersion/$pkgId-$pkgVersion.pom"
-    $pomContent = Invoke-RestMethod -Method "GET" -Uri $uri
-
-    if ($pomContent -ne $null -or $pomContent.Length -eq 0) {
-      return $true
-    }
-    else {
-      return $false
-    }
-  }
-  catch {
-    $statusCode = $_.Exception.Response.StatusCode.value__
-    $statusDescription = $_.Exception.Response.StatusDescription
-
-    # if this is 404ing, then this pkg has never been published before
-    if ($statusCode -eq 404) {
-      return $false
-    }
-
-    Write-Host "VersionCheck to maven for packageId $pkgId failed with statuscode $statusCode"
-    Write-Host $statusDescription
-    exit(1)
-  }
-}
-
-# make certain to always take the package json closest to the top
-function ResolvePkgJson($workFolder) {
-  $pathsWithComplexity = @()
-  foreach ($file in (Get-ChildItem -Path $workFolder -Recurse -Include "package.json")) {
-    $complexity = ($file.FullName -Split { $_ -eq "/" -or $_ -eq "\" }).Length
-    $pathsWithComplexity += New-Object PSObject -Property @{
-      Path       = $file
-      Complexity = $complexity
-    }
-  }
-
-  return ($pathsWithComplexity | Sort-Object -Property Complexity)[0].Path
-}
-
-# Parse out package publishing information given a .tgz npm artifact
-function ParseNPMPackage($pkg, $workingDirectory) {
-  $workFolder = "$workingDirectory$($pkg.Basename)"
-  $origFolder = Get-Location
-  mkdir $workFolder
-  cd $workFolder
-
-  tar -xzf $pkg
-
-  $packageJSON = ResolvePkgJson -workFolder $workFolder | Get-Content | ConvertFrom-Json
-  $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0]
-
-  cd $origFolder
-  Remove-Item $workFolder -Force  -Recurse -ErrorAction SilentlyContinue
-
-  $pkgId = $packageJSON.name
-  $pkgVersion = $packageJSON.version
-
-  $resultObj = New-Object PSObject -Property @{
-    PackageId      = $pkgId
-    PackageVersion = $pkgVersion
-    Deployable     = $forceCreate -or !(IsNPMPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion)
-    ReleaseNotes   = $releaseNotes
-  }
-
-  return $resultObj
-}
-
-# Returns the npm publish status of a package id and version.
-function IsNPMPackageVersionPublished($pkgId, $pkgVersion) {
-  $npmVersions = (npm show $pkgId versions)
-
-  if ($LastExitCode -ne 0) {
-    npm ping
-
-    if ($LastExitCode -eq 0) {
-      return $False
-    }
-
-    Write-Host "Could not find a deployed version of $pkgId, and NPM connectivity check failed."
-    exit(1)
-  }
-
-  $npmVersionList = $npmVersions.split(",") | % { return $_.replace("[", "").replace("]", "").Trim() }
-  return $npmVersionList.Contains($pkgVersion)
-}
-
-# Parse out package publishing information given a nupkg ZIP format.
-function ParseNugetPackage($pkg, $workingDirectory) {
-  $workFolder = "$workingDirectory$($pkg.Basename)"
-  $origFolder = Get-Location
-  $zipFileLocation = "$workFolder/$($pkg.Basename).zip"
-  mkdir $workFolder
-
-  Copy-Item -Path $pkg -Destination $zipFileLocation
-  Expand-Archive -Path $zipFileLocation -DestinationPath $workFolder
-  [xml] $packageXML = Get-ChildItem -Path "$workFolder/*.nuspec" | Get-Content
-  $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0]
-
-  Remove-Item $workFolder -Force  -Recurse -ErrorAction SilentlyContinue
-  $pkgId = $packageXML.package.metadata.id
-  $pkgVersion = $packageXML.package.metadata.version
-
-  return New-Object PSObject -Property @{
-    PackageId      = $pkgId
-    PackageVersion = $pkgVersion
-    Deployable     = $forceCreate -or !(IsNugetPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion)
-    ReleaseNotes   = $releaseNotes
-  }
-}
-
-# Returns the nuget publish status of a package id and version.
-function IsNugetPackageVersionPublished($pkgId, $pkgVersion) {
-
-  $nugetUri = "https://api.nuget.org/v3-flatcontainer/$($pkgId.ToLowerInvariant())/index.json"
-
-  try {
-    $nugetVersions = Invoke-RestMethod -Method "GET" -Uri $nugetUri
-
-    return $nugetVersions.versions.Contains($pkgVersion)
-  }
-  catch {
-    $statusCode = $_.Exception.Response.StatusCode.value__
-    $statusDescription = $_.Exception.Response.StatusDescription
-
-    # if this is 404ing, then this pkg has never been published before
-    if ($statusCode -eq 404) {
-      return $False
-    }
-
-    Write-Host "Nuget Invocation failed:"
-    Write-Host "StatusCode:" $statusCode
-    Write-Host "StatusDescription:" $statusDescription
-    exit(1)
-  }
-
-}
-
-# Parse out package publishing information given a python sdist of ZIP format.
-function ParsePyPIPackage($pkg, $workingDirectory) {
-  $pkg.Basename -match $SDIST_PACKAGE_REGEX | Out-Null
-
-  $pkgId = $matches["package"]
-  $pkgVersion = $matches["versionstring"]
-
-  $workFolder = "$workingDirectory$($pkg.Basename)"
-  $origFolder = Get-Location
-  mkdir $workFolder
-
-  Expand-Archive -Path $pkg -DestinationPath $workFolder
-  $releaseNotes = &"${PSScriptRoot}/../Extract-ReleaseNotes.ps1" -ChangeLogLocation @(Get-ChildItem -Path $workFolder -Recurse -Include "CHANGELOG.md")[0]
-  Remove-Item $workFolder -Force  -Recurse -ErrorAction SilentlyContinue
-
-  return New-Object PSObject -Property @{
-    PackageId      = $pkgId
-    PackageVersion = $pkgVersion
-    Deployable     = $forceCreate -or !(IsPythonPackageVersionPublished -pkgId $pkgId -pkgVersion $pkgVersion)
-    ReleaseNotes   = $releaseNotes
-  }
-}
-
-function ParseCArtifact($pkg, $workingDirectory) {
-  $packageInfo = Get-Content -Raw -Path $pkg | ConvertFrom-JSON
-  $packageArtifactLocation = (Get-ItemProperty $pkg).Directory.FullName
-
-  $releaseNotes = ExtractReleaseNotes -changeLogLocation @(Get-ChildItem -Path $packageArtifactLocation -Recurse -Include "CHANGELOG.md")[0]
-
-  return New-Object PSObject -Property @{
-    PackageId      = $packageInfo.name
-    PackageVersion = $packageInfo.version
-    # Artifact info is always considered deployable for C becasue it is not
-    # deployed anywhere. Dealing with duplicate tags happens downstream in
-    # CheckArtifactShaAgainstTagsList
-    Deployable     = $true
-    ReleaseNotes   = $releaseNotes
-  }
-}
-
-# Returns the pypi publish status of a package id and version.
-function IsPythonPackageVersionPublished($pkgId, $pkgVersion) {
-  try {
-    $existingVersion = (Invoke-RestMethod -Method "Get" -Uri "https://pypi.org/pypi/$pkgId/$pkgVersion/json").info.version
-
-    # if existingVersion exists, then it's already been published
-    return $True
-  }
-  catch {
-    $statusCode = $_.Exception.Response.StatusCode.value__
-    $statusDescription = $_.Exception.Response.StatusDescription
-
-    # if this is 404ing, then this pkg has never been published before
-    if ($statusCode -eq 404) {
-      return $False
-    }
-
-    Write-Host "PyPI Invocation failed:"
-    Write-Host "StatusCode:" $statusCode
-    Write-Host "StatusDescription:" $statusDescription
-    exit(1)
-  }
-}
-
-# Retrieves the list of all tags that exist on the target repository
-function GetExistingTags($apiUrl) {
-  try {
-    return (Invoke-RestMethod -Method "GET" -Uri "$apiUrl/git/refs/tags"  ) | % { $_.ref.Replace("refs/tags/", "") }
-  }
-  catch {
-    $statusCode = $_.Exception.Response.StatusCode.value__
-    $statusDescription = $_.Exception.Response.StatusDescription
-
-    Write-Host "Failed to retrieve tags from repository."
-    Write-Host "StatusCode:" $statusCode
-    Write-Host "StatusDescription:" $statusDescription
-
-    # Return an empty list if there are no tags in the repo
-    if ($statusCode -eq 404) {
-      return @()
-    }
-
-    exit(1)
-  }
-}
-
-# Walk across all build artifacts, check them against the appropriate repository, return a list of tags/releases
-function VerifyPackages($pkgRepository, $artifactLocation, $workingDirectory, $apiUrl, $releaseSha) {
-  $pkgList = [array]@()
-  $ParsePkgInfoFn = ""
-  $packagePattern = ""
-
-  switch ($pkgRepository) {
-    "Maven" {
-      $ParsePkgInfoFn = "ParseMavenPackage"
-      $packagePattern = "*.pom"
-      break
-    }
-    "Nuget" {
-      $ParsePkgInfoFn = "ParseNugetPackage"
-      $packagePattern = "*.nupkg"
-      break
-    }
-    "NPM" {
-      $ParsePkgInfoFn = "ParseNPMPackage"
-      $packagePattern = "*.tgz"
-      break
-    }
-    "PyPI" {
-      $ParsePkgInfoFn = "ParsePyPIPackage"
-      $packagePattern = "*.zip"
-      break
-    }
-    "C" {
-      $ParsePkgInfoFn = "ParseCArtifact"
-      $packagePattern = "*.json"
-    }
-    default {
-      Write-Host "Unrecognized Language: $language"
-      exit(1)
-    }
-  }
-
-  $pkgs = (Get-ChildItem -Path $artifactLocation -Include $packagePattern -Recurse -File)
-
-  Write-Host $pkgs
-
-  foreach ($pkg in $pkgs) {
-    try {
-      $parsedPackage = &$ParsePkgInfoFn -pkg $pkg -workingDirectory $workingDirectory
-
-      if ($parsedPackage -eq $null) {
-        continue
-      }
-
-      if ($parsedPackage.Deployable -ne $True) {
-        Write-Host "Package $($parsedPackage.PackageId) is marked with version $($parsedPackage.PackageVersion), the version $($parsedPackage.PackageVersion) has already been deployed to the target repository."
-        Write-Host "Maybe a pkg version wasn't updated properly?"
-        exit(1)
-      }
-
-      $pkgList += New-Object PSObject -Property @{
-        PackageId      = $parsedPackage.PackageId
-        PackageVersion = $parsedPackage.PackageVersion
-        Tag            = ($parsedPackage.PackageId + "_" + $parsedPackage.PackageVersion)
-        ReleaseNotes   = $parsedPackage.ReleaseNotes
-      }
-    }
-    catch {
-      Write-Host $_.Exception.Message
-      exit(1)
-    }
-  }
-
-  $results = ([array]$pkgList | Sort-Object -Property Tag -uniq)
-
-  $existingTags = GetExistingTags($apiUrl)
-  $intersect = $results | % { $_.Tag } | ? { $existingTags -contains $_ }
-
-  if ($intersect.Length -gt 0) {
-    CheckArtifactShaAgainstTagsList -priorExistingTagList $intersect -releaseSha $releaseSha -apiUrl $apiUrl
-
-    # all the tags are clean. remove them from the list of releases we will publish.
-    $results = $results | ? { -not ($intersect -contains $_.Tag ) }
-  }
-
-  return $results
-}
-
-# given a set of tags that we want to release, we need to ensure that if they already DO exist.
-# if they DO exist, quietly exit if the commit sha of the artifact matches that of the tag
-# if the commit sha does not match, exit with error and report both problem shas
-function CheckArtifactShaAgainstTagsList($priorExistingTagList, $releaseSha, $apiUrl) {
-  $headers = @{
-    "Content-Type"  = "application/json"
-    "Authorization" = "token $($env:GH_TOKEN)"
-  }
-
-  $unmatchedTags = @()
-
-  foreach ($tag in $priorExistingTagList) {
-    $tagSha = (FireAPIRequest -Method "Get" -Url "$apiUrl/git/refs/tags/$tag" -Headers $headers)."object".sha
-
-    if ($tagSha -eq $releaseSha) {
-      Write-Host "This package has already been released. The existing tag commit SHA $releaseSha matches the artifact SHA being processed. Skipping release step for this tag."
-    }
-    else {
-      Write-Host "The artifact SHA $releaseSha does not match that of the currently existing tag."
-      Write-Host "Tag with issues is $tag with commit SHA $tagSha"
-
-      $unmatchedTags += $tag
-    }
-  }
-
-  if ($unmatchedTags.Length -gt 0) {
-    Write-Host "Tags already existing with different SHA versions. Exiting."
-    exit(1)
-  }
-}
+. (Join-Path $PSScriptRoot artifact-metadata-parsing.ps1)
 
 $apiUrl = "https://api.github.com/repos/$repoId"
 Write-Host "Using API URL $apiUrl"
diff --git a/eng/common/scripts/git-branch-push.ps1 b/eng/common/scripts/git-branch-push.ps1
index 4f2b5d771..b5a7ec8ba 100644
--- a/eng/common/scripts/git-branch-push.ps1
+++ b/eng/common/scripts/git-branch-push.ps1
@@ -137,7 +137,6 @@ do
             }
         }
     }
-
 } while($needsRetry -and $tryNumber -le $numberOfRetries)
 
 if ($LASTEXITCODE -ne 0)
diff --git a/eng/common/scripts/update-docs-metadata.ps1 b/eng/common/scripts/update-docs-metadata.ps1
new file mode 100644
index 000000000..a162cbf85
--- /dev/null
+++ b/eng/common/scripts/update-docs-metadata.ps1
@@ -0,0 +1,125 @@
+# Note, due to how `Expand-Archive` is leveraged in this script,
+# powershell core is a requirement for successful execution.
+param (
+  # arguments leveraged to parse and identify artifacts
+  $ArtifactLocation, # the root of the artifact folder. DevOps $(System.ArtifactsDirectory)
+  $WorkDirectory, # a clean folder that we can work in
+  $ReleaseSHA, # the SHA for the artifacts. DevOps: $(Release.Artifacts..SourceVersion) or $(Build.SourceVersion)
+  $RepoId, # full repo id. EG azure/azure-sdk-for-net  DevOps: $(Build.Repository.Id). Used as a part of VerifyPackages
+  $Repository, # EG: "Maven", "PyPI", "NPM"
+
+  # arguments necessary to power the docs release
+  $DocRepoLocation, # the location on disk where we have cloned the documentation repository
+  $Language, # EG: js, java, dotnet. Used in language for the embedded readme.
+  $DocRepoContentLocation = "docs-ref-services/" # within the doc repo, where does our readme go?
+)
+
+Write-Host "> $PSCommandPath $args"
+
+
+# import artifact parsing and semver handling
+. (Join-Path $PSScriptRoot artifact-metadata-parsing.ps1)
+. (Join-Path $PSScriptRoot SemVer.ps1)
+
+function GetMetaData($lang){
+  switch ($lang) {
+    "java" {
+      $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/java-packages.csv"
+      break
+    }
+    ".net" {
+      $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/dotnet-packages.csv"
+      break
+    }
+    "python" {
+      $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/python-packages.csv"
+      break
+    }
+    "javascript" {
+      $metadataUri = "https://raw.githubusercontent.com/Azure/azure-sdk/master/_data/releases/latest/js-packages.csv"
+      break
+    }
+    default {
+      Write-Host "Unrecognized Language: $language"
+      exit(1)
+    }
+  }
+
+  $metadataResponse = Invoke-WebRequest-WithHandling -url $metadataUri -method "GET" | ConvertFrom-Csv
+}
+
+function GetAdjustedReadmeContent($pkgInfo, $lang){
+    $date = Get-Date -Format "MM/dd/yyyy"
+    $service = ""
+
+    # the namespace is not expected to be present for js.
+    $pkgId = $pkgInfo.PackageId.Replace("@azure/", "")
+
+    try {
+      $metadata = GetMetaData -lang $lang 
+      $service = $metadata | ? { $_.Package -eq $pkgId }
+
+      if ($service) {
+        $service = "$service,"
+      }
+    }
+    catch {
+      Write-Host $_
+      Write-Host "Unable to retrieve service metadata for packageId $($pkgInfo.PackageId)"
+    }
+
+    $headerContentMatch = (Select-String -InputObject $pkgInfo.ReadmeContent -Pattern 'Azure .+? (client|plugin|shared) library for (JavaScript|Java|Python|\.NET|C)').Matches[0]
+
+    if ($headerContentMatch){
+      $header = "---`r`ntitle: $headerContentMatch`r`nkeywords: Azure, $lang, SDK, API, $service $($pkgInfo.PackageId)`r`nauthor: maggiepint`r`nms.author: magpint`r`nms.date: $date`r`nms.topic: article`r`nms.prod: azure`r`nms.technology: azure`r`nms.devlang: $lang`r`nms.service: $service`r`n---`r`n"
+      $fileContent = $pkgInfo.ReadmeContent -replace $headerContentMatch, "$headerContentMatch - Version $($pkgInfo.PackageVersion) `r`n"
+      return "$header $fileContent"
+    }
+    else {
+      return ""
+    }
+}
+
+$apiUrl = "https://api.github.com/repos/$repoId"
+$pkgs = VerifyPackages -pkgRepository $Repository `
+  -artifactLocation $ArtifactLocation `
+  -workingDirectory $WorkDirectory `
+  -apiUrl $apiUrl `
+  -releaseSha $ReleaseSHA `
+  -exitOnError $False
+
+if ($pkgs) {
+  Write-Host "Given the visible artifacts, readmes will be copied for the following packages"
+  Write-Host ($pkgs | % { $_.PackageId }) 
+
+  foreach ($packageInfo in $pkgs) {
+    # sync the doc repo
+    $semVer = [AzureEngSemanticVersion]::ParseVersionString($packageInfo.PackageVersion)
+    $rdSuffix = ""
+    if ($semVer.IsPreRelease) {
+      $rdSuffix = "-pre"
+    }
+
+    $readmeName = "$($packageInfo.PackageId.Replace('azure-','').Replace('Azure.', '').Replace('@azure/', '').ToLower())-readme$rdSuffix.md"
+    $readmeLocation = Join-Path $DocRepoLocation $DocRepoContentLocation $readmeName
+    $adjustedContent = GetAdjustedReadmeContent -pkgInfo $packageInfo -lang $Language
+
+    if ($adjustedContent) {
+      try {
+        Push-Location $DocRepoLocation
+        Set-Content -Path $readmeLocation -Value $adjustedContent -Force
+
+        Write-Host "Updated readme for $readmeName."
+      } catch {
+        Write-Host $_
+      } finally {
+        Pop-Location
+      }
+    } else {
+      Write-Host "Unable to parse a header out of the readmecontent for PackageId $($packageInfo.PackageId)"
+    }
+  }
+}
+else {
+  Write-Host "No readmes discovered for doc release under folder $ArtifactLocation."
+}