Search code examples
azure-devopsazure-pipelinespipeline

How to get only changed files using Azure devops pipelines


I have folder structure this way in source code. f1 f2 f3 f4

I have added gitcopy diff task in my pipeline which lists and copies files which are modified to a destination folder. Now, I want to have a conditional loop as powershell script to only zip those folders which have modified files with a particular names for example if files from f1 are modified..I want particular steps to be performed and so on.. How can I do it as a loop? edit: I have written my pipeline in this way. But its failing in publish step with errors as listed.

yaml: trigger:

none

pool:
  vmImage: 'windows-latest'

variables:
  FR1PHPPRDAPP1VFlag: false
  FR1PHPPRDAPP4VFlag: false
  FR1PHPPRDAPP5VFlag: false
  FR1PHPPRDSRE1VFlag: false
  FR1PHPPRDAPP7VFlag: false
  stages:
  -stage: Zipping modified folders
steps:

- powershell: |

      ## get the changed files
      $files=$(git diff HEAD HEAD~ --name-only)
      $temp=$files -split ' '
      $count=$temp.Length
      echo "Total changed $count files"
      For ($i=0; $i -lt $temp.Length; $i++)
        {
          
          $name=$temp[$i]
          echo "this is $name file"
          if ($name -like 'FR1PHPPRDAPP1V/*') 
          {
            cd $(Build.ArtifactStagingDirectory)
            mkdir Output -force
           
          Compress-Archive -Path $(system.defaultworkingdirectory)/FR1PHPPRDAPP1V -DestinationPath $(Build.ArtifactStagingDirectory)/Output/APP1V.zip
          
          ##set the flag variable FR1PHPPRDAPP1VFlag to true
          Write-Host "##vso[task.setvariable variable=FR1PHPPRDAPP1VFlag]true"
          }
          if ($name -like 'FR1PHPPRDAPP4V/*')
          {
            cd $(Build.ArtifactStagingDirectory)
            mkdir Output -force
            ##achive folder FR1PHPPRDAPP4V if it is changed.
          Compress-Archive -Path $(system.defaultworkingdirectory)/FR1PHPPRDAPP4V -DestinationPath $(Build.ArtifactStagingDirectory)/Output/APP4V.zip
          ##set the flag variable FR1PHPPRDAPP4VFlag to true
          Write-Host "##vso[task.setvariable variable=FR1PHPPRDAPP4VFlag]True"
          }
           if ($name -like 'FR1PHPPRDAPP5V/*')
          {
            cd $(Build.ArtifactStagingDirectory)
            mkdir Output -force
            ##achive folder FR1PHPPRDAPP5V if it is changed.
          Compress-Archive -Path $(system.defaultworkingdirectory)/FR1PHPPRDAPP5V -DestinationPath $(Build.ArtifactStagingDirectory)/Output/APP5V.zip
          ##set the flag variable FR1PHPPRDAPP5VFlag to true
          Write-Host "##vso[task.setvariable variable=FR1PHPPRDAPP5VFlag]True"
          }
            if ($name -like 'FR1PHPPRDSRE1V/*')
          {
            cd $(Build.ArtifactStagingDirectory)
            mkdir Output -force
            ##achive folder FR1PHPPRDSRE1V if it is changed.
          Compress-Archive -Path $(system.defaultworkingdirectory)/FR1PHPPRDSRE1V -DestinationPath $(Build.ArtifactStagingDirectory)/Output/SRE1V.zip
          ##set the flag variable FR1PHPPRDSRE1VFlag to true
          Write-Host "##vso[task.setvariable variable=FR1PHPPRDSRE1VFlag]True"
          }
            if ($name -like 'FR1PHPPRDAPP7V/*')
          {
            cd $(Build.ArtifactStagingDirectory)
            mkdir Output -force
            ##achive folder FR1PHPPRDAPP7V if it is changed.
          Compress-Archive -Path $(system.defaultworkingdirectory)/FR1PHPPRDAPP7V -DestinationPath $(Build.ArtifactStagingDirectory)/Output/APP7V.zip
          ##set the flag variable FR1PHPPRDAPP7VFlag to true
          Write-Host "##vso[task.setvariable variable=FR1PHPPRDAPP7VFlag]True"
          }
        }
- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)/Output'
    ArtifactName: 'scripts-f2p'
    publishLocation: 'Container'
  condition: and(succeeded(), or(eq(variables.FR1PHPPRDAPP1VFlag, true),eq(variables.FR1PHPPRDAPP4VFlag, true),eq(variables.FR1PHPPRDAPP5VFlag, true),eq(variables.FR1PHPPRDSRE1VFlag, true),eq(variables.FR1PHPPRDAPP7VFlag, true)))

Solution

  • You can directly run below git commands in the powershell task to check the changed files. It is much easier than Rest api.

    git diff-tree --no-commit-id --name-only -r $(Build.SourceVersion)

    When you get the changed files, you can use the zip the changed folders directly in the powershell task using Compress-Archive command: See below example:

    Compress-Archive -Path C:\f1 -DestinationPath f1.zip

    If you want some particular steps to be performed based on the the changed folders. You can define the flag variables and using the logging commands in the powershell scripts to set the flags to true. And then use the condtions for the following steps.

    See below full example scripts:

    ##set flag variables to indicate if the folder is changed.
    
    variables:
      f1Flag: false
      f2Flag: false
      f3Flag: false
      
    steps:
    
    - powershell: |
          ## get the changed files
          $files=$(git diff-tree --no-commit-id --name-only -r $(Build.SourceVersion))
          $temp=$files -split ' '
          $count=$temp.Length
          echo "Total changed $count files"
         
          For ($i=0; $i -lt $temp.Length; $i++)
          {
            $name=$temp[$i]
            echo "this is $name file"
            if ($name -like 'f1/*')  #if f1 is a subfolder under a folder use "- like '*/f1/*'"
            { 
              ##achive folder f1 if it is changed.
              ##Compress-Archive -Path $(system.defaultworkingdirectory)/f1 -DestinationPath $(Build.ArtifactStagingDirectory)/f1.zip
              
              ##set the flag variable f1Flag to true
              Write-Host "##vso[task.setvariable variable=f2Flag]true"
            }
            if ($name -like 'f2/*')
            {
              ##achive folder f2 if it is changed.
              ##Compress-Archive -Path $(system.defaultworkingdirectory)/f2 -DestinationPath $(Build.ArtifactStagingDirectory)/f2.zip
              ##set the flag variable f2Flag to true
              Write-Host "##vso[task.setvariable variable=f2Flag]True"
            }
          }
          ## create a temp folder to hold the changed files
          New-Item -ItemType directory -Path $(system.defaultworkingdirectory)\temp
    
          foreach($file in $temp){
            if(Test-Path -path $file){
            Copy-Item -Path $file -Destination $(system.defaultworkingdirectory)\temp
            }
          }
          ## zip the temp folder which only have the changed files
          Compress-Archive -Path $(system.defaultworkingdirectory)\temp\* -DestinationPath $(Build.ArtifactStagingDirectory)\changedfiles.zip
    

    Then you can use the condition for some particular steps just as Krzysztof mentioned

    condition: and(succeeded(), or(eq(variables.f1Flag, true),eq(variables.f2Flag, true),eq(variables.f3Flag, true)))

    See the answer to this thread for more information.

    Update:

    steps:
    
    - powershell: |
         #get the changed files
         ....
    
            
    - task: PublishBuildArtifacts@1
      inputs:
         PathtoPublish: '$(Build.ArtifactStagingDirectory)/Output'
         ArtifactName: 'drop'
         publishLocation: 'Container'
      condtion: and(succeeded(), or(eq(variables.f1Flag, true),eq(variables.f2Flag, true),eq(variables.f3Flag, true)))
       
    

    Update 2:

    The default checkout is now a shallow clone of depth 1 preventing the git diff to work. Checking out with fetchDepth: 0 solves that problem as mentioned in the comments. This may increase the time required for the checkout step - especially on larger repos.