MicrosoftPremier / VstsExtensions

Documentation and issue tracking for Microsoft Premier Services Visual Studio Team Services Extensions
MIT License
59 stars 14 forks source link

Create Work Item fails if build trigger is PR #91

Closed Andrii-Papezhuk closed 4 years ago

Andrii-Papezhuk commented 4 years ago

I'm getting an issue with creating a New Work Item for my builds if the pipeline was triggered by Pull Request. It fails with the next error:

Cannot read property 'fields' of null

However, the same build is passing fine and creates a new item correctly if I trigger it manually with the same configuration.

Detailed build log:

##[debug]Evaluating condition for step: 'Create a new work item if build fails'
##[debug]Evaluating: failed()
##[debug]Evaluating failed:
##[debug]=> True
##[debug]Result: True
Starting: Create a new work item if build fails
==============================================================================
Task         : Create Work Item
Description  : Creates a new work item from a build or release.
Version      : 1.6.0
Author       : Microsoft Premier Services
Help         : [[Docs]](https://github.com/MicrosoftPremier/VstsExtensions/blob/master/CreateWorkItem/en-US/overview.md)
==============================================================================
##[debug]agent.TempDirectory=/home/vsts/work/_temp
##[debug]loading inputs and endpoints
##[debug]loading INPUT_TEAMPROJECT
##[debug]loading INPUT_WORKITEMTYPE
##[debug]loading INPUT_TITLE
##[debug]loading INPUT_ASSIGNEDTO
##[debug]loading INPUT_AREAPATH
##[debug]loading INPUT_ITERATIONPATH
##[debug]loading INPUT_FIELDMAPPINGS
##[debug]loading INPUT_ASSOCIATE
##[debug]loading INPUT_LINKWORKITEMS
##[debug]loading INPUT_LINKTYPE
##[debug]loading INPUT_LINKTARGET
##[debug]loading INPUT_TARGETID
##[debug]loading INPUT_LINKPR
##[debug]loading INPUT_ADDATTACHMENTS
##[debug]loading INPUT_PREVENTDUPLICATES
##[debug]loading INPUT_UPDATEDUPLICATES
##[debug]loading INPUT_CREATEOUTPUTS
##[debug]loading ENDPOINT_AUTH_SYSTEMVSSCONNECTION
##[debug]loading ENDPOINT_AUTH_SCHEME_SYSTEMVSSCONNECTION
##[debug]loading ENDPOINT_AUTH_PARAMETER_SYSTEMVSSCONNECTION_ACCESSTOKEN
##[debug]loading SECRET_KNOWN_HOSTS
##[debug]loading SECRET_PUBLIC_SSH_KEY
##[debug]loading SECRET_SYSTEM_ACCESSTOKEN
##[debug]loading SECRET_KNOWN_HOSTS_XXXXXX
##[debug]loading SECRET_PUBLIC_SSH_KEY_XXXXXX
##[debug]loaded 25
##[debug]Agent.ProxyUrl=undefined
##[debug]Agent.CAInfo=undefined
##[debug]Agent.ClientCert=undefined
##[debug]Agent.SkipCertValidation=undefined
##[debug]prepareRun
##[debug]check path : /home/vsts/work/_tasks/CreateWorkItem_92979e78-16ed-4e27-b008-001d2d58bf74/1.6.0/task.json
##[debug]adding resource file: /home/vsts/work/_tasks/CreateWorkItem_92979e78-16ed-4e27-b008-001d2d58bf74/1.6.0/task.json
##[debug]system.culture=en-US
SystemVssConnection exists true
##[debug]SystemVssConnection exists true
##[debug]System.TeamFoundationCollectionUri=https://marsdevteam.visualstudio.com/
##[debug]teamProject=XXXXXXXXXXXXXXXXXX
##[debug]workItemType=Task
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3237) Warning: Use Cipheriv for counter mode of aes-256-ctr
Cannot read property 'fields' of null
##[debug]task result: Failed
##[error]Cannot read property 'fields' of null
##[debug]Processed: ##vso[task.issue type=error;]Cannot read property 'fields' of null
##[debug]Processed: ##vso[task.complete result=Failed;]Cannot read property 'fields' of null
##[debug]task result: Succeeded
##[debug]Processed: ##vso[task.complete result=Succeeded;]
Finishing: Create a new work item if build fails
ReneSchumacher commented 4 years ago

Hi @seylas,

can you post the configuration of your task? I inspected the code and your log and it seems that the task fails VERY early when it tries to read all fields for the selected work item type. As long as the work item type you configured exists, this should never be the case. However, we don't handle the case of using an invalid work item type correctly (I'll fix that).

Are you sure that the Task work item type does exist in the project you're running in?

Andrii-Papezhuk commented 4 years ago

Thanks for reply @ReneSchumacher

Yes, I'm pretty sure Please find a task configuration below:

  - task: CreateWorkItem@1
    displayName: 'Create a new work item if build fails'
    condition: failed()
    inputs:
      teamProject: 'XXXXXXXXXXXXXX'
      workItemType: 'Task'
      title: 'Platform Build Failure New Stack'
      assignedTo: 'XXXXXX YYYYYYYY <xxxxxx.yyyyyyy@zzzzz.com>'
      areaPath: 'XXXXXXXXXXXXXX\DevOps'
      iterationPath: 'XXXXXXXXXXXXXX'
      fieldMappings: |
        Description=This item was automatically created by Azure Pipeline. Failed build is linked to this task.
      associate: true
      linkWorkItems: true
      linkType: 'System.LinkTypes.Hierarchy-Reverse'
      linkTarget: 'id'
      targetId: '142432'

Also, this configuration is working fine once I trigger the same build manually:

(node:3346) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3346) Warning: Use Cipheriv for counter mode of aes-256-ctr
(node:3346) Warning: Use Cipheriv for counter mode of aes-256-ctr
##[debug]teamProject=XXXXXXXXXXXXXX
##[debug]workItemType=Task
##[debug]title=Platform Build Failure New Stack
##[debug]assignedTo=XXXXXX YYYYYYYY <xxxxxx.yyyyyyy@zzzzz.com>
##[debug]areaPath=XXXXXXXXXXXXXX\DevOps
##[debug]iterationPath=XXXXXXXXXXXXXX
##[debug]fieldMappings=Description=This item was automatically created by Azure Pipeline. Failed build is linked to this task.
##[debug]associate=true
##[debug]linkWorkItems=true
##[debug]linkType=System.LinkTypes.Hierarchy-Reverse
##[debug]linkTarget=id
##[debug]targetId=142432
##[debug]linkPR=false
##[debug]addAttachments=false
##[debug]preventDuplicates=false
##[debug]createOutputs=false
##[debug]createWorkItem
##[debug]System.HostType=build
##[debug]Build.BuildUri=vstfs:///Build/Build/47222
##[debug]getDuplicateWorkItem
##[debug]createWorkItem
##[debug]createWorkItemCreationData
##[debug]resolveIterationPath
##[debug]createAssociateBuildData
Successfully created new work item with ID 163345.
##[debug]associateBuild
##[debug]System.HostType=build
##[debug]Build.BuildUri=vstfs:///Build/Build/47222
##[debug]associateBuild
##[debug]buildAlreadyAssociated
##[debug]linkWorkItems
##[debug]getWorkItemById
##[debug]linkWorkItems
##[debug]createWorkItemLinkingData
##[debug]workItemAlreadyLinked
Successfully linked work item with ID 163345 to work item(s) 142432
##[debug]linkPullRequest
##[debug]attachFiles
##[debug]createOutputVariables
##[debug]task result: Succeeded
##[debug]Processed: ##vso[task.complete result=Succeeded;]
ReneSchumacher commented 4 years ago

Hm, the configuration looks good and it doesn't make much sense that the same config is working in one situation but not in the other. Are you executing both builds on the same agent or agent type (i.e., private or hosted)?

I'll have to dig into this later today. As soon as I have more to share, I'm getting back to you.

Andrii-Papezhuk commented 4 years ago

Thanks for digging into it both builds were executed on Microsoft-hosted agents

pool:
  vmImage: 'ubuntu-16.04'

Please let me know if I can provide any other details that would be helpful for you.

ReneSchumacher commented 4 years ago

Hi again!

I just prepared a (hopefully good enough) repro for your issue. My YAML pipeline looks like this:

trigger: none

pool:
  vmImage: 'ubuntu-16.04'

steps:
- checkout: self

- pwsh: if ((Get-Content ./file.txt) -contains "fail") { Write-Host "##vso[task.complete result=Failed;]Fail build" } else { Write-Host "Build will succeed" }
  displayName: Fail build if needed
  workingDirectory: $(build.sourcesDirectory)

- task: CreateWorkItem@1
  displayName: 'Create a new work item if build fails'
  condition: failed()
  inputs:
    workItemType: 'Task'
    title: 'Platform Build Failure New Stack'
    fieldMappings: |
      Description=This item was automatically created by Azure Pipeline. Failed build is linked to this task.
    associate: true
    linkWorkItems: true
    linkType: 'System.LinkTypes.Hierarchy-Reverse'
    linkTarget: 'id'
    targetId: '1735'

That is very much simplified but it should come close to your scenario while providing a simple way to trigger the task condition. Unfortunately, this pipeline works fine no matter if I run it through a branch policy in a PR or manually. This matched my expectation as it shouldn't make any difference if the task runs in the context of a PR or not (unless you configure it to link the work item to the PR of course).

This leads me to the assumption that there has to be some kind of configuration issue in your case that only applies when the build is run from a PR. Are you using variables for task inputs that might change between manually triggered and PR builds?

I again looked at the code and the execution seems to take a fallback path for TFS 2017:

public static async create(webApi: WebApi, teamProject: string, workItemType: string): Promise<WorkItemFieldResolver> {
    let resolver = new WorkItemFieldResolver();

    let witClient = await webApi.getWorkItemTrackingApi();
    resolver.workItemFields = await witClient.getWorkItemTypeFieldsWithReferences(teamProject, workItemType);
    if (!resolver.workItemFields) {
        resolver.workItemFields = await this.getWorkItemTypeFieldsForTfs2017(witClient, teamProject, workItemType);
    }
    resolver.allFields = await witClient.getFields(teamProject);
    resolver.project = teamProject;
    resolver.workItemType = workItemType;

    return resolver;
}

private static async getWorkItemTypeFieldsForTfs2017(witClient: IWorkItemTrackingApi, teamProject: string, workItemType: string): Promise<WorkItemTypeFieldInstance[]> {
    let witDefinition = await witClient.getWorkItemType(teamProject, workItemType);
    return witDefinition.fields;
}

The only access to the property fields in this case is the second last line. However, on Azure DevOps the code should never even call getWorkItemTypeFieldsForTfs2017. As I mentioned earlier, this will also happen if you use an invalid work item type and or team project, which is clearly a bug that I'm going to fix. Yet, I don't see any other reason why the code should fail.

Andrii-Papezhuk commented 4 years ago

Hi René! Thanks again for your efforts, my pipeline is almost the same:

trigger:
  branches:
    include:
    - 'master'
  tags:
    include:
    - '*'

pool:
  vmImage: 'ubuntu-16.04'

variables:
  System.Debug: true

steps:
  - checkout: self
    persistCredentials: true
  - task: Bash@3
    displayName: 'Install Dependencies'
    inputs:
      targetType: 'inline'
      script: |
        echo "Failing build"
        exit 1
  - task: CreateWorkItem@1
    displayName: 'Create a new work item if build fails'
    condition: failed()
    inputs:
      teamProject: 'XXXXXXXXXXXXXX'
      workItemType: 'Task'
      title: 'Platform Build Failure New Stack'
      assignedTo: 'XXXXXX YYYYYYYY <xxxxxx.yyyyyyy@zzzzz.com>'
      areaPath: 'XXXXXXXXXXXXXX\DevOps'
      iterationPath: 'XXXXXXXXXXXXXX'
      fieldMappings: |
        Description=This item was automatically created by Azure Pipeline. Failed build is linked to this task.
      associate: true
      linkWorkItems: true
      linkType: 'System.LinkTypes.Hierarchy-Reverse'
      linkTarget: 'id'
      targetId: '142432'

As a trigger, I'm using a GitHub webhook.

In my flow, I've created a PR from the FORKed repository to the original. In this case, I was able to reproduce this issue in 100% cases. I've also tried to create the same PR in the original repository from branch, and everything works fine for any scenario. I've also tried to compare webhook payloads for those scenarios but didn't find any suspicious discrepancies.

Such behavior doesn't make any sense for me and makes this issue weirder...

Could you please try to reproduce it once again by creating a PR from the fork?

ReneSchumacher commented 4 years ago

Hm, just to be sure because you mentioned GitHub:

My repro so far has been fully in Azure DevOps and GitHub might change the situation.

If you're uncomfortable sharing some of the information here, you can also send me an email to PSGerExtSupport@microsoft.com so we can take this offline. I'll report back the actual root cause and solution here when we're done. Of course, we can keep this in the open as well.

Andrii-Papezhuk commented 4 years ago

Sorry, I didn't mention this. As a source code repository, I'm using a GitHub Enterprise. I'll share webhook payloads for both scenarios by email shortly, they might be useful for the investigation.

ReneSchumacher commented 4 years ago

Thanks for clarifying. I now have a repro! 👍

Even though I still don't understand why the task fails. At least I can start debugging this now.

ReneSchumacher commented 4 years ago

Quick update: It currently looks like the task loses access to Azure DevOps when it is triggered from a GitHub PR. I'm working with the Azure DevOps teams to figure out what exactly is going on and how to fix it.

Andrii-Papezhuk commented 4 years ago

Thanks a lot for the update I've opened a support case regarding this issue for Azure DevOps team 120042822002281 Feel free to refer or link my case

ReneSchumacher commented 4 years ago

Hi again,

I received feedback from team responsible for pipelines. The behavior is by design because the security context for PR builds from forked repos is very limited. Thus, it doesn't have access to the work item definition and wouldn't have permissions to create or update work items as well.

If you think about it, it really makes sense. Since forks are basically not very secure (anyone can fork your repo and create a PR) we want to make sure that the attack surface is very limited for someone trying to inject something malicious into your pipeline.

I have asked if there's a way to explicitly grant additional permissions to the security context, but I don't believe there is. If I am right, I'll update our task so that it issues a warning and skips all actions when it runs on a PR build from a forked repo.

ReneSchumacher commented 4 years ago

I updated the extension as described above as we cannot change the security scope. If you really need to have this functionality, we could probably add a special option that lets you configure a personal access token for authentication. You would have to put this into a secret variable in Azure DevOps to protect it. I'm not sure if this would work under all circumstances, but it might be a solution.

Andrii-Papezhuk commented 4 years ago

Thanks for update I've noticed your changes. I'm getting a warning instead of a failed build, thanks:

##[warning]NotSupportedInForkPRBuild

I don't see any security risks with using forks in private GitHub Enterprise repository, but yes, for a public GitHub repository, it's a valid concern.

I would appreciate having the possibility to pass a private token for this job to overcome my case if it's not a complicated task for you. I guess other possible solution for me would be creating a new task for failed build through REST API.

ReneSchumacher commented 4 years ago

Thanks for the feedback. This should be a quick addition to the task since security is set up only once. I'll try to squeeze this in today as I'm busy for the rest of the week.

ReneSchumacher commented 4 years ago

Hi @seylas,

I just release v1.7.0 of the task that supports using a PAT for authentication to Azure DevOps. I confirmed through my tests that this also works properly for forked repos, but you have to set the security in the pipeline correctly. Please look at the task docs for more information. If you encounter any issues with the new version, please let me know.

Happy building, René

Andrii-Papezhuk commented 4 years ago

Hi @ReneSchumacher

Yep, everything works fine so far for me. Thank you very much for your help and a new feature!

Thanks & Regards, Andriy