-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create pull requests for solution merges (#61)
BREAKING CHANGE: This commit include multiple breaking changes: - Extract build YAML files must be updated to match the sample in this commit - Merge-SolutionVersion.ps1 scripts must be replaced by the Merge-SolutionMerge.ps1 script in the sample in this commit - All field values in the Azure DevOps tab of the Solution table must be moved to Project and Repository table rows - Azure DevOps build service users must be granted permissions to contribute to pull requests
- Loading branch information
Showing
138 changed files
with
5,620 additions
and
1,489 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
param ( | ||
[Parameter()] | ||
[String] | ||
$ClientId, | ||
[Parameter()] | ||
[String] | ||
$TenantId, | ||
[Parameter()] | ||
[SecureString] | ||
$ClientSecret, | ||
[Parameter()] | ||
[String] | ||
$SolutionMergeId, | ||
[Parameter()] | ||
[String] | ||
$DevEnvironmentUrl | ||
) | ||
|
||
Install-Module ADAL.PS -Scope CurrentUser -Force | ||
|
||
Write-Host "Installing solution packager." | ||
|
||
$coreToolsPath = nuget install Microsoft.CrmSdk.CoreTools -o (Join-Path $env:TEMP -ChildPath packages) | Where-Object { $_ -like "*Installing package *'Microsoft.CrmSdk.CoreTools' to '*'." } | Select-String -Pattern "to '(.*)'" | ForEach-Object { $_.Matches[0].Groups[1].Value } | ||
$solutionPackager = Get-ChildItem -Filter "SolutionPackager.exe" -Path $coreToolsPath -Recurse | ||
|
||
function Get-WebApiHeaders ($url, $clientId, $tenantId, $clientSecret) { | ||
Write-Host "Getting access token for $url for client ID $clientId." | ||
$tokenResponse = Get-AdalToken -Resource $url -ClientId $clientId -Authority "https://login.microsoftonline.com/$tenantId" -ClientSecret $clientSecret -Verbose | ||
$token = $tokenResponse.AccessToken | ||
|
||
return @{ | ||
"method" = "GET" | ||
"authorization" = "Bearer $token" | ||
"content-type" = "application/json" | ||
} | ||
} | ||
|
||
Write-Host "Authenticating to development environment." | ||
$devWebApiHeaders = Get-WebApiHeaders -url $DevEnvironmentUrl -clientId $ClientId -tenantId $TenantId -clientSecret $ClientSecret | ||
$devWebApiUrl = "$DevEnvironmentUrl/api/data/v9.1" | ||
|
||
Write-Host "Getting solution merge $SolutionMergeId." | ||
$select = 'devhub_sourcebranch,statuscode' | ||
$expand = 'createdby($select=fullname,internalemailaddress),devhub_TargetSolution($select=devhub_uniquename;$expand=devhub_StagingEnvironment($select=devhub_url),devhub_Repository($select=devhub_sourcecontrolstrategy,devhub_extractbuilddefinitionid,devhub_targetbranch)),devhub_Issue($select=devhub_type,devhub_name,devhub_azuredevopsworkitemid,devhub_developmentsolution)' | ||
$solutionMerge = Invoke-RestMethod -Uri "$devWebApiUrl/devhub_solutionmerges($SolutionMergeId)?`$select=$select&`$expand=$expand" -Headers $devWebApiHeaders | ||
|
||
Write-Host "Setting git user configuration to solution merge creator: $($solutionMerge.createdby.internalemailaddress)." | ||
git config --global user.email $solutionMerge.createdby.internalemailaddress | ||
git config --global user.name $solutionMerge.createdby.fullname | ||
|
||
Write-Host "Checking source control strategy." | ||
if ($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_sourcecontrolstrategy -eq 353400000) { | ||
Write-Host "Source control strategy is pull request." | ||
if ($solutionMerge.devhub_sourcebranch) { | ||
Write-Host "Source branch provided. Checking out $($solutionMerge.devhub_sourcebranch)." | ||
$updateExistingBranch = $true | ||
git checkout "$($solutionMerge.devhub_sourcebranch)" | ||
} | ||
else { | ||
Write-Host "No source branch provided. Calculating branch name from solution merge issue." | ||
$branchPrefix = if ($solutionMerge.devhub_Issue.devhub_type -eq 353400001) { "feature/" } else { "bugfix/" } | ||
$branchName = $solutionMerge.devhub_Issue.devhub_name.ToLower().Replace(' ', '-') -replace "[^a-zA-Z0-9\s-]" | ||
$calculatedBranch = "$branchPrefix$branchName" | ||
|
||
Write-Host "Checking if $calculatedBranch exists." | ||
$updateExistingBranch = $null -ne (git rev-parse --verify --quiet "origin/$calculatedBranch") | ||
if ($updateExistingBranch) { | ||
Write-Host "Branch already exists. Updating existing branch at $calculatedBranch." | ||
git checkout "$calculatedBranch" | ||
} | ||
else { | ||
Write-Host "Branch not found. Creating branch $calculatedBranch." | ||
git checkout -b "$calculatedBranch" | ||
} | ||
} | ||
} | ||
else { | ||
Write-Host "Source control strategy is push. Checking out $($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_targetbranch)" | ||
git checkout $solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_targetbranch | ||
if ($SourceBranch) { | ||
Write-Host "Source branch provided. Squashing $SourceBranch." | ||
git merge origin/$SourceBranch --squash --no-commit; | ||
} | ||
$result = git merge HEAD | ||
if ($result[0] -like "*error*") { | ||
Write-Error "Unable to automatically merge the source branch due to a conflict." | ||
} | ||
} | ||
|
||
Write-Host "Authenticating to extract environment." | ||
$extractUrl = $solutionMerge.devhub_TargetSolution.devhub_StagingEnvironment.devhub_url | ||
$extractWebApiHeaders = Get-WebApiHeaders -url $extractUrl -clientId $ClientId -tenantId $TenantId -clientSecret $ClientSecret | ||
$extractWebApiUrl = "$($extractUrl)/api/data/v9.1" | ||
|
||
Write-Host "Exporting $($solutionMerge.devhub_TargetSolution.devhub_uniquename) as unmanaged." | ||
$unmanagedZipResponse = Invoke-RestMethod -Uri "$($extractWebApiUrl)/ExportSolution" ` | ||
-Method POST ` | ||
-Headers $extractWebApiHeaders ` | ||
-UseBasicParsing ` | ||
-Body (ConvertTo-Json @{ SolutionName = $solutionMerge.devhub_TargetSolution.devhub_uniquename; Managed = $false }) | ||
|
||
$unmanagedZipFilePath = Join-Path -Path $env:TEMP -ChildPath "$($solutionMerge.devhub_TargetSolution.devhub_uniquename).zip" | ||
Write-Host "Writing unmanaged solution to $unmanagedZipFilePath." | ||
[IO.File]::WriteAllBytes($unmanagedZipFilePath, [Convert]::FromBase64String($unmanagedZipResponse.ExportSolutionFile)); | ||
|
||
Write-Host "Exporting $($solutionMerge.devhub_TargetSolution.devhub_uniquename) as managed." | ||
$managedZipResponse = Invoke-RestMethod ` | ||
-Uri "$extractWebApiUrl/ExportSolution" ` | ||
-Method POST ` | ||
-Headers $extractWebApiHeaders ` | ||
-UseBasicParsing ` | ||
-Body (ConvertTo-Json @{ SolutionName = $solutionMerge.devhub_TargetSolution.devhub_uniquename; Managed = $true }) | ||
|
||
$managedZipFilePath = Join-Path -Path $env:TEMP -ChildPath "$($solutionMerge.devhub_TargetSolution.devhub_uniquename)_managed.zip" | ||
Write-Host "Writing managed solution to $managedZipFilePath." | ||
[IO.File]::WriteAllBytes($managedZipFilePath, [Convert]::FromBase64String($managedZipResponse.ExportSolutionFile)); | ||
|
||
$solutionFolder = Get-ChildItem -Filter $solutionMerge.devhub_TargetSolution.devhub_uniquename -Path "./src/solutions" -Directory | ||
$extractFolder = Join-Path -Path $solutionFolder.FullName -ChildPath "extract" | ||
Write-Host "Extracting solutions with the Solution Packager to $extractFolder." | ||
$solutionPackagerPath = $solutionPackager.FullName | ||
& $solutionPackagerPath /action:Extract /zipfile:$unmanagedZipFilePath /folder:$extractFolder /packagetype:Both /allowWrite:Yes /allowDelete:Yes | ||
|
||
git add . | ||
git reset -- NuGet.config | ||
|
||
Write-Host "Calculating commit message from solution merge issue." | ||
$commitPrefix = if ($solutionMerge.devhub_Issue.devhub_type -eq 353400001) { "feat: " } else { "fix: " } | ||
$commitMessage = $solutionMerge.devhub_Issue.devhub_name | ||
$buildNumber = $commitMessage -replace "[^a-zA-Z0-9\s]" | ||
Write-Host "##vso[build.updatebuildnumber]$buildNumber" | ||
|
||
$commitTrailers = @" | ||
Solution-merge-id: $SolutionMergeId | ||
Solution-merge-creator: $($solutionMerge.createdby.fullname) <$($solutionMerge.createdby.internalemailaddress)> | ||
"@ | ||
|
||
if ($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid) { | ||
Write-Host "Committing '$commitPrefix$commitMessage' with work item $($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid)." | ||
git commit -m "$commitPrefix$commitMessage" -m "#$($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid)" -m "$commitTrailers"; | ||
} | ||
else { | ||
Write-Host "Committing '$commitPrefix$commitMessage'." | ||
git commit -m "$commitPrefix$commitMessage" -m "$commitTrailers" | ||
} | ||
|
||
if ($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_sourcecontrolstrategy -eq 353400000) { | ||
$remoteOrigin = [Uri]::new((git config --get remote.origin.url)) | ||
if ($remoteOrigin.Host -eq 'dev.azure.com') { | ||
$org = $remoteOrigin.UserInfo | ||
$project = $remoteOrigin.Segments[2].Replace('/', '') | ||
$repository = $remoteOrigin.Segments[4] | ||
} | ||
else { | ||
$org = $remoteOrigin.Host.Split('.')[0] | ||
$project = $remoteOrigin.Segments[1].Replace('/', '') | ||
$repository = $remoteOrigin.Segments[3] | ||
} | ||
|
||
$sourceBranch = if ($solutionMerge.devhub_sourcebranch) { $solutionMerge.devhub_sourcebranch } else { $calculatedBranch } | ||
Write-Host "Checking for existing pull request" | ||
$result = Invoke-RestMethod ` | ||
-Uri "https://dev.azure.com/$org/$project/_apis/git/repositories/$repository/pullRequests?searchCriteria.sourceRefName=refs/heads/$sourceBranch&searchCriteria.targetRefName=refs/heads/$($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_targetbranch)&api-version=6.0" ` | ||
-Headers @{ 'authorization' = "Bearer $env:SYSTEM_ACCESSTOKEN"; 'content-type' = 'application/json' } | ||
|
||
if ($result.value.Count -eq 0) { | ||
Write-Host "Publishing pull request branch." | ||
git push -u origin HEAD | ||
|
||
Write-Host "Creating pull request from refs/heads/$sourceBranch into refs/heads/$($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_targetbranch)." | ||
$result = Invoke-RestMethod ` | ||
-Uri "https://dev.azure.com/$org/$project/_apis/git/repositories/$repository/pullRequests?api-version=6.0" ` | ||
-Method POST ` | ||
-Headers @{ 'authorization' = "Bearer $env:SYSTEM_ACCESSTOKEN"; 'content-type' = 'application/json' } ` | ||
-UseBasicParsing ` | ||
-Body (ConvertTo-Json ` | ||
@{ | ||
title = "$commitPrefix$commitMessage"; | ||
sourceRefName = "refs/heads/$sourceBranch"; | ||
targetRefName = "refs/heads/$($solutionMerge.devhub_TargetSolution.devhub_Repository.devhub_targetbranch)"; | ||
description = @" | ||
$commitTrailers | ||
"@ | ||
}) | ||
|
||
if ($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid) { | ||
Write-Host "Linking pull request to work item $($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid)" | ||
$result = Invoke-RestMethod ` | ||
-Uri "https://dev.azure.com/$org/$project/_apis/wit/workItems/$($solutionMerge.devhub_Issue.devhub_azuredevopsworkitemid)?api-version=4.0-preview" ` | ||
-Headers @{ 'authorization' = "Bearer $env:SYSTEM_ACCESSTOKEN"; 'content-type' = 'application/json-patch+json' } ` | ||
-Method PATCH ` | ||
-Body (ConvertTo-Json -Depth 100 @( | ||
@{ | ||
op = 'add'; | ||
path = '/relations/-'; | ||
value = | ||
@{ | ||
rel = "ArtifactLink"; | ||
url = $($result.artifactId) | ||
attributes = @{ | ||
name = "Pull Request" | ||
} | ||
} | ||
} | ||
) | ||
) | ||
} | ||
} | ||
|
||
Write-Host "Updating solution merge status to 'Awaiting PR Approval'." | ||
$solutionMerge = Invoke-RestMethod ` | ||
-Method PATCH ` | ||
-Uri "$devWebApiUrl/devhub_solutionmerges($SolutionMergeId)" ` | ||
-Headers $devWebApiHeaders ` | ||
-Body (ConvertTo-Json @{ | ||
statuscode = 353400007 | ||
}) | ||
} | ||
|
||
Write-Host "Pushing new commit." | ||
git push origin |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
44 changes: 44 additions & 0 deletions
44
...pmentHub_AzureDevOps/Extract/AppModuleSiteMaps/devhub_DevelopmentHub/AppModuleSiteMap.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<AppModuleSiteMap xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||
<SiteMapUniqueName>devhub_DevelopmentHub</SiteMapUniqueName> | ||
<SiteMap IntroducedVersion="7.0.0.0"> | ||
<Area Id="devhub_DevelopmentHub" ResourceId="SitemapDesigner.NewArea" DescriptionResourceId="SitemapDesigner.NewArea" VectorIcon="/WebResources/devhub_/Images/devhub_Issue.svg" Icon="/WebResources/devhub_/Images/devhub_Issue.svg" ShowGroups="true" IntroducedVersion="7.0.0.0"> | ||
<Titles> | ||
<Title LCID="1033" Title="Development Hub" /> | ||
</Titles> | ||
<Group Id="devhub_Issues" ResourceId="SitemapDesigner.NewGroup" DescriptionResourceId="SitemapDesigner.NewGroup" IntroducedVersion="7.0.0.0" IsProfile="false" ToolTipResourseId="SitemapDesigner.Unknown"> | ||
<Titles> | ||
<Title LCID="1033" Title="Issues" /> | ||
</Titles> | ||
<SubArea Id="devhub_Dashboards" ResourceId="Homepage_Dashboards" DescriptionResourceId="Dashboards_Description" Icon="/_imgs/imagestrips/transparent_spacer.gif" Url="/workplace/home_dashboards.aspx" DefaultDashboard="899883f9-9876-e911-a819-002248008902" IntroducedVersion="7.0.0.0" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA"> | ||
<Titles> | ||
<Title LCID="1033" Title="Dashboard" /> | ||
</Titles> | ||
</SubArea> | ||
<SubArea Id="devhub_Issues" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_issue" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA"> | ||
<Titles> | ||
<Title LCID="1033" Title="Issues" /> | ||
</Titles> | ||
</SubArea> | ||
</Group> | ||
<Group Id="NewGroup_4aa6eeb6" ResourceId="SitemapDesigner.NewGroup" IsProfile="false" ToolTipResourseId="SitemapDesigner.Unknown"> | ||
<Titles> | ||
<Title LCID="1033" Title="Develop" /> | ||
</Titles> | ||
<SubArea Id="Environments" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_environment" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA" /> | ||
<SubArea Id="Solutions" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_solution" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA" /> | ||
<SubArea Id="SolutionMerges" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_solutionmerge" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA" /> | ||
</Group> | ||
<Group Id="devhub_AzureDevOps" ResourceId="SitemapDesigner.NewGroup" IsProfile="false" ToolTipResourseId="SitemapDesigner.Unknown"> | ||
<Titles> | ||
<Title LCID="1033" Title="Azure DevOps" /> | ||
</Titles> | ||
<SubArea Id="devhub_Projects" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_project" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA" /> | ||
<SubArea Id="devhub_Repositories" Icon="/_imgs/imagestrips/transparent_spacer.gif" Entity="devhub_repository" Client="All,Outlook,OutlookLaptopClient,OutlookWorkstationClient,Web" AvailableOffline="true" PassParams="false" Sku="All,OnPremise,Live,SPLA" /> | ||
</Group> | ||
</Area> | ||
</SiteMap> | ||
<LocalizedNames> | ||
<LocalizedName description="Development Hub" languagecode="1033" /> | ||
</LocalizedNames> | ||
</AppModuleSiteMap> |
Oops, something went wrong.