Reputation: 2095
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
Upvotes: 1
Views: 1266
Reputation: 927
I was trying to use your code, yet I had a problem
MESSAGE: 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:
-H "Authorization: Bearer $(System.AccessToken)"
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
- 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
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
Upvotes: 0
Reputation: 2095
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
Upvotes: 3
Reputation: 16188
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"
Upvotes: 0