microsoft / powerplatform-build-tools

Power Platform Build Tools automate common build and deployment tasks related to Power Platform. This includes synchronization of solution metadata (a.k.a. solutions) between development environments and source control, generating build artifacts, deploying to downstream environments, provisioning/de-provisioning of environments, and the ability to perform static analysis checks against your solution using the PowerApps checker service.
MIT License
192 stars 39 forks source link

Push error message in a variable to be able to process it in a following task #514

Open michael-heidweiller opened 1 year ago

michael-heidweiller commented 1 year ago

Whenever a task fails it would be nice to be able to process in further step. Example when a Solution Imports fails due to missing dependencies the failed conditional step could send a message to the Pipeline run requestor that it failed because of missing dependency X. Or when exporting a solution that the solution doesn't exist.

A snippet on how to currently resolve it using a PowerShell task instead of the Build Tool Tasks

function Invoke-pac {
    $result = pac $args 2>&1
    if(!$?) { 
        $errorFound = $result -match "^Error:.*$"
        $errorMessage = $errorFound[0]
        Write-Error -Message "pac $args returned $errorMessage" -ErrorAction Stop 
    }
    return $result
}

try {
    Invoke-pac solution export -n NotExisting
    exit 0
}
catch {
    Write-Host "##vso[task.setvariable variable=pacErrorMessage]$_"
    exit 1
}

Could probably be lot more efficient as this would kill the intermediate info outputs of the pac process.

snizar007 commented 1 year ago

Can you please provide additional details at https://aka.ms/ProDevOfficeHours form? It also depends on the next task if it depends on the task that failed. You can enable diagnostics in your AzDO pipeline to review additional messages for a given task.

michael-heidweiller commented 1 year ago

I created the following logic in our pipeline that grabs the solution changes from our development environment to our staging environment:

The problem trying to resolve is that Exporting and Importing of solutions can have a spectrum of errors which requires the customizer to make decisions on. Currently I can state to my team that they need to go into the pipeline run and scroll through the messages and to search for the text printed in Red. I would like to write back to the triggering Task what went wrong. Error types I know:

  1. Server Occupied, due to someone else publishing a component or Microsoft importing update solutions.
  2. Solution not existing, due to the customizer creating the Task with a typo.
  3. Solution import failing on missing dependency, development conflict or human error.

As we know 99% of computer errors happen between chair and keyboard, there is a use case to help the non-Tech Saffy users and parse the error messages that are expected by a common issue or an error by them.

Currently the PAC doesn't have anything by itself to get a better grip on the error handling, the code solution I wrote also isn't correct. But I think especially because customizations on PowerApps platform happen at lot of different skill levels and not everyone is a code developer. For those colleagues it is helpful to empower them with more feedback on what went wrong.

Currently thinking of replacing all build tool tasks with snippets using the following wrapper function. The RegEx can be stronger, but at least this will keep the pipeline log active and remove the least interesting part of the PAC error output (the help text).

function Invoke-Pac {
    Write-Host "##[command]pac $args"
    pac $args 2>&1 | Tee-Object -Variable result
    # pipe > error stream 2 in to the output stream &1
    if(!$?) { # last command not successful
        $result = $result -join "`r`n"
        $textOfInterest = $($result | Select-String -Pattern "[\s\S]*?(?=Usage:)").Matches[0].Value
        $errorMessage = "pac $args returned`r`n$textOfInterest"
        Write-Host "##vso[task.setvariable variable=pacErrorMessage]$($errorMessage | ConvertTo-Json)"
        Write-Host "##[error]$errorMessage"
        Write-Error -Message $errorMessage -ErrorAction Stop 
    }
    return $result
}

Also here are the 4 tasks:

- task: PowerPlatformExportSolution@2
  inputs:
    authenticationType: PowerPlatformSPN
    PowerPlatformSPN: ${{ parameters.SourceEnvironmentURL }}
    Environment: ${{ parameters.SourceEnvironmentURL }}
    SolutionName: ${{ parameters.SourceSolutionName }}
    SolutionOutputFile: $(Build.SourcesDirectory)/Work/${{ parameters.SourceSolutionName }}.zip
    AsyncOperation: true
    MaxAsyncWaitTime: 60
    ExportAutoNumberingSettings: true
  displayName: Export Solution ${{ parameters.SourceSolutionName }}

- ${{ if ne(parameters.TriggerWorkItem, '') }}:
  - powershell: |
      $Error[0] | Format-Table
      $user = az devops user show --user ${{ parameters.RequestForEmail }} | ConvertFrom-Json
      $comment = '<div><a href=\"#\" data-vss-mention=\"version:2.0,' + $user.id + '\">@${{ parameters.RequestedFor }}</a> Could not find ${{ parameters.SourceSolutionName }}</div>'
      az boards work-item update --id ${{ parameters.TriggerWorkItem }}  --state "Failed" --discussion "$($comment)"
      Write-Host "##vso[task.setvariable variable=stepFailed;]true"
    env:
      AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)
    displayName: Could not find Solution
    condition: and(failed(), ne(variables.stepFailed, 'true'))

- task: PowerPlatformImportSolution@2
  inputs:
    authenticationType: PowerPlatformSPN
    PowerPlatformSPN: ${{ parameters.DestEnvironmentURL }}
    Environment: ${{ parameters.DestEnvironmentURL }}
    SolutionInputFile: $(Build.SourcesDirectory)/Work/${{ parameters.SourceSolutionName }}.zip
    UseDeploymentSettingsFile: false
    AsyncOperation: true
    MaxAsyncWaitTime: 60
    ActivatePlugins: true
    ConvertToManaged: false
    PublishCustomizationChanges: false
    PublishWorkflows: false
  displayName: Import Solution ${{ parameters.SourceSolutionName }}

- ${{ if gt(length(parameters.TriggerWorkItem), 0) }}:
  - powershell: |
      $Error[0] | Format-Table
      $user = az devops user show --user ${{ parameters.RequestForEmail }} | ConvertFrom-Json
      $comment = '<div><a href=\"#\" data-vss-mention=\"version:2.0,' + $user.id + '\">@${{ parameters.RequestedFor }}</a> Error importing ${{ parameters.SourceSolutionName }}</div>'
      az boards work-item update --id ${{ parameters.TriggerWorkItem }}  --state "Failed" --discussion "$($comment)"
      Write-Host "##vso[task.setvariable variable=stepFailed;]true"
    env:
      AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)
    displayName: Error Importing Solution
    condition: and(failed(), ne(variables.stepFailed, 'true'))
snizar007 commented 1 year ago

We can certainly look into this.