'AzureDevops Api call for pull-request doesn't work
I'm referring to this page, to create a pull request during the build process in Azure DevOps via API call. I'm using authorization as Bearer
using $(System.AccessToken)
instead of PAT credentials. In my previous steps, I used Bearer
with $(System.AccessToken)
to get the buildNumber
via API call which worked seamlessly. However, if I use the same in my below task for the POST
mechanism to create a pull request it doesn't work and gives me 400 error code.
Can anyone suggest to me, how can I make this work?
- task: Bash@3
inputs:
targetType: 'inline'
script: |
echo 'Started createing pull-request'
url="https://dev.azure.com/{Organization name}/{Project name}/_apis/git/repositories/$(Build.Repository.ID)/pullrequests?api-version=6.0"
echo $url
ret=$(curl -d '{"sourceRefName": $(BUILD.SOURCEBRANCH), "targetRefName": "refs/heads/devlopment", "title": "test", "description": "test" }' -X POST -H "Authorization:Bearer $(System.AccessToken)" -H "Content-Type:application/json" ${url} --write-out "%{http_code}" --output response.json)
echo $ret
cat response.json
Solution 1:[1]
The error was in JSON. Fixed and reformatted JSON all working now. This is the task now looks like with some extra addition: Title and Description are parameterized.
- task: Bash@3
displayName: Creating Pull request
inputs:
targetType: 'inline'
script: |
url="$(System.TeamFoundationCollectionUri)/$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.ID)/pullrequests?api-version=6.0"
ret=$(curl -X POST \
--silent \
-H "Authorization:Bearer $(System.AccessToken)" \
-H "Content-Type:application/json" \
${url} \
--write-out "%{http_code}" \
--output response.json \
-d'{
"sourceRefName":"$(BUILD.SOURCEBRANCH)",
"targetRefName":"refs/heads/<branch-name>",
"title":"$(Title)",
"description":"$(Description)"
}' )
if [[ $ret -ne 201 ]];
then
message=$(cat response.json | jq '.message' --raw-output)
echo "##vso[task.logissue type=error;] ERROR MESSAGE: ${message}"
exit 1
fi
continueOnError: false
Solution 2:[2]
Did you check your URL?
In your example:
url="https://dev.azure.com/{Organization name}/{Project name}/_apis/git/REPOSITORIE/$(Build.Repository.ID)/pullrequests?api-version=6.0"
should be
url="https://dev.azure.com/{Organization name}/{Project name}/_apis/git/REPOSITORIES/$(Build.Repository.ID)/pullrequests?api-version=6.0"
Solution 3:[3]
GIT Pullrequest
I was trying to use your code, yet I had a problemMESSAGE: TF401027: You need the Git 'PullRequestContribute' permission to perform this action. Details: identity 'Build\c5f6a8a4-de0a-4081-bff4-928a38d232c4', scope 'repository'.
The solution comes from https://developercommunity.visualstudio.com/t/tf401027-you-need-the-git-pullrequestcontribute-pe/1441618
Navigate to Project Settings >> Repositories >> select Security tab >> Type Project Collection Build Service (organizationName) in the search box >> check if the Contribute to pull requests permission is set to Allow.
You need to search for this 'Project Collection Build Service' and only then it would appear on your list. There is no add
button, only 'search'.
https://dev.azure.com/{owner}/{project}/_settings/repositories?_a=permissions
(example) https://dev.azure.com/jmusz/iospoc/_settings/repositories?_a=permissions
My code was based on yours, an example of a plain curl without an error handling is
- task: Bash@3
displayName: Creating Pull request
inputs:
targetType: 'inline'
script: |
url="$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.ID)/pullrequests?api-version=6.0"
echo $url
curl -X POST \
-v \
-H "Authorization: Bearer $(System.AccessToken)" \
-H "Content-Type: application/json" \
${url} \
--write-out "%{http_code}" \
--output response.json \
-d'{
"sourceRefName": "$(BUILD.SOURCEBRANCH)",
"targetRefName": "refs/heads/dest",
"title": "Title",
"description": "Description"
}'
cat response.json
As you might guess, when I could not find PullRequestContribute (took me a while), I had a 'Plan B' that was to use a Personal Access Token... Turns out, there is a difference with Authorization header:
- $(System.AccessToken) - you can use it directly, with
-H "Authorization: Bearer $(System.AccessToken)"
- while PAT must be extended with a (username:PAT) that is encoded with BASE64, and
Basic
not Bearer so you use-H "Authorization: Basic ${b64pat}" \
The code for PAT is:
- task: Bash@3
displayName: Creating Pull request
inputs:
targetType: 'inline'
script: |
url="$(System.TeamFoundationCollectionUri)$(System.TeamProject)/_apis/git/repositories/$(Build.Repository.ID)/pullrequests?api-version=6.0"
echo $url
#works
b64pat=`echo -n 'jmusz:ljfcuww24sa7ywmgvlvuwrn3v7znmuvbnfymfh4xtnt3tbo6bk' | base64`
#works (empty username)
b64pat=`echo -n ':ljfcuww24sa7ywmgvlvuwrn3v7znmuvbnfymfh4xtnt3tbo6bk' | base64`
#not working (missing `:` )
#b64pat=`echo -n 'ljfcuww24sa7ywmgvlvuwrn3v7znmuvbnfymfh4xtnt3tbo6bk' | base64`
#echo $b64pat
#-H "Authorization: Bearer $(System.AccessToken)" \ #require PullRequestContribute
#-H "Authorization: Basic ${b64pat}" \ # user:PAT | base64
ret=$(curl -X POST \
-v \
-H "Authorization: Basic ${b64pat}" \
-H "Content-Type: application/json" \
${url} \
--write-out "%{http_code}" \
--output response.json \
-d'{
"sourceRefName": "$(BUILD.SOURCEBRANCH)",
"targetRefName": "refs/heads/dest",
"title": "Title",
"description": "Description"
}' )
if [[ $ret -ne 201 ]];
then
message=$(cat response.json | jq '.message' --raw-output)
echo "##vso[task.logissue type=error;] ERROR MESSAGE: ${message}"
#cat response.json
exit 1
fi
PAT with powershell
- task: PowerShell@2
displayName: Creating Pull request - PS
inputs:
targetType: 'inline'
script: |
$url = "https://dev.azure.com/jmusz/iospoc/_apis/git/repositories/iospoc/pullrequests?api-version=6.0"
$sourceBranch = "refs/heads/source"
$targetBranch = "refs/heads/dest"
$title = "title"
$description = "description"
$MyPat = 'ljfcuww24sa7ywmgvlvuwrn3v7znmuvbnfymfh4xtnt3tbo6bk'
$B64Pat = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes(":$MyPat")) #Note : !!!!
$B64Pat
$header = @{ Authorization = "Basic $B64Pat" }
$body = @{
sourceRefName = "$sourceBranch"
targetRefName = "$targetBranch"
title = "$title"
description = "$description"
}
$jsonBody = ConvertTo-Json $body
try {
$response = Invoke-RestMethod -Uri $url -Method Post -Headers $header -Body $jsonBody -ContentType "application/json;charset=UTF-8"
}
catch {
Write-Error $_
Write-Error $_.Exception.Message
}
$response
GIT repo clone with Token
I had to pass credentials to repo to a task in pipeline, I decided the best would be to use $(System.AccessToken) (plan B was with PAT).
I faced the same problem as above - Bearer $(System.AccessToken)
vs Basic ${b64pat}
Please note that username is optional when used with PAT (for git clone) while it is not optional when used for API interaction
- stage: Lint
jobs:
- job: SwiftLint
steps:
- bash: |
b64pat=`echo -n jmusz:ljfcuww24sa7ywmgvlvuwrn3v7znmuvbnfymfh4xtnt3tbo6bk |base64`
echo 1 not working
git clone $(System.TeamFoundationCollectionUri)$(System.TeamProject)/_git/$(Build.Repository.ID) ||true
#rm -rf libraries ||true
echo 2 not working
#not working
git -c http.extraHeader="Authorization: Basic $(System.AccessToken)" clone $(System.TeamFoundationCollectionUri)$(System.TeamProject)/_git/$(Build.Repository.Name) ||true
rm -rf iospoc ||true
echo 2.1 working
#working
git -c http.extraHeader="Authorization: Bearer $(System.AccessToken)" clone $(System.TeamFoundationCollectionUri)$(System.TeamProject)/_git/$(Build.Repository.Name) ||true
rm -rf iospoc ||true
echo 3 working
git -c http.extraHeader="Authorization: Basic ${b64pat}" clone $(System.TeamFoundationCollectionUri)$(System.TeamProject)/_git/$(Build.Repository.Name) ||true
#git -c http.extraHeader="Authorization: Basic $(System.AccessToken)" clone https://dev.azure.com/jmusz/iospoc/_git/libraries/ ||true
rm -rf libraries ||true
echo 4 not working
git clone "https://jmusz:$(System.AccessToken)@dev.azure.com/jmusz/iospoc/_git/libraries" ||true
rm -rf libraries ||true
echo 4.01 not working
git clone "https://$(System.AccessToken)@dev.azure.com/jmusz/iospoc/_git/libraries" ||true
rm -rf libraries ||true
echo 4.1 working
git clone "https://jmusz:[email protected]/jmusz/iospoc/_git/libraries" ||true
rm -rf libraries ||true
echo 4.2 working
git clone "https://[email protected]/jmusz/iospoc/_git/libraries" ||true
rm -rf libraries ||true
#not working
echo 5 not working
git clone https://${b64pat}@dev.azure.com/jmusz/iospoc/_git/libraries ||true
rm -rf libraries ||true
#not working
echo 6 not working
git clone https://$(System.AccessToken)@dev.azure.com/jmusz/iospoc/_git/libraries ||true
rm -rf libraries ||true
#working
echo 7 working
git config --global --add http.https://dev.azure.com/jmusz/iospoc/_git/libraries.extraHeader "AUTHORIZATION: Basic ${b64pat}"
git clone https://dev.azure.com/jmusz/iospoc/_git/libraries/ ||true
rm -rf libraries ||true
#not working?
echo 8 not working
git config --global --add http.https://dev.azure.com/jmusz/iospoc/_git/libraries.extraHeader "AUTHORIZATION: Bearer $(System.AccessToken)"
git clone https://dev.azure.com/jmusz/iospoc/_git/libraries/ ||true
rm -rf libraries ||true
Interesting that 2.1 works while 8 does not
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Shamrai Aleksander |
Solution 3 | sirkubax |