If you have a need to copy code between repos I found it a bit fiddlier than I expected so for my own notes this blog is how you can set this up.

Why might you want to do this

In most cases you probably wont want to do this as you could just create a branch and merge in changes if needed, however there are a couple of scenarios you might want to do this such as:

  • You are working on a project where you have a private and public version of your code such as an open source project
  • You are getting a code review or doing a proof of concept and you want to give the reviewer a separate disconnected copy of the code for the review without the overhead of your existing processes
  • You want to run some tools on a copy of your code on a periodic basis
  • You need to give a customer access to a copy of the code as part of a contract

Requirements for the pipeline

  • Trigger the pipeline manually or on the 1st friday of the month at 9pm
  • Get the code from a repo in project A on the main branch
  • Copy it to a repo in project B
  • Check in the code that has changed

Prerequisites

  1. Check Build Service has permission to read the source project

2. Give permissions for the Build Service on the target DevOps Project so it can contribute and add the code to the repo

3. Allow the Pipeline to access other Projects

YAML for Pipeline

Below is the YAML I used in the pipeline.

Points to Note:

  • I have split the git commands into separate tasks so its a bit easier to troubleshoot if any of them have an issue.
  • I am putting the yaml file in the repo in the destination devops project and pulling the code over to it from the source to the destination keeping everything away from the source project. You could probably do it the other way around just as easily
# Pipeline to copy code from Project A to Project B

# Manual trigger to run on demand
trigger: 
- none
pr: none

# Scheduled Trigger to run pipeline first friday of each month
schedules:
  - cron: "0 21 1-7 * 5"  # Runs on the first Friday of each month at 9:00 PM UTC
    displayName: "Monthly Schedule - First Friday of the Month at 9:00 PM UTC"
    branches:
      include:
        - main  # Adjust this to the branch you want the schedule to run on
    always: true  # Ensures the pipeline runs even if there are no code changes

resources:
  repositories:
  - repository: Source
    type: git
    name: Mikes.DevOps.Project.A/Mikes.DevOps.Repo.A
    ref: main    

pool:
  vmImage: ubuntu-latest

steps:
# Step 1: Clone repo's
- checkout: self
  persistCredentials: true
- checkout: Source

# Listing variables in the pipeline
- task: PowerShell@2
  displayName: List Variables
  inputs:
    targetType: 'inline'
    script: |
      #Displays all of the environment variables for troubleshooting
      
      gci env:* | sort-object name

 # List the files in the directories for diagnostics
- task: PowerShell@2
  displayName: List Files in working directory
  inputs:
    targetType: 'inline'
    script: 'Get-ChildItem -Path $(System.DefaultWorkingDirectory) -Recurse'

# Copy files from the source directory to the destination directory so we can copy code over to the destination project folder copy
- task: CopyFiles@2
  displayName: Copy files from Source to Destination
  inputs:
    SourceFolder: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.A'
    Contents: '**'
    TargetFolder: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'

- task: PowerShell@2
  displayName: Git Configure
  inputs:
    targetType: 'inline'
    script: |
      git config --global user.email "[email protected]"
      git config --global user.name "DevOps Pipeline"
    workingDirectory: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'


- task: PowerShell@2
  displayName: Git Add
  inputs:
    targetType: 'inline'
    script: |
      git add .
    workingDirectory: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'

- task: PowerShell@2
  displayName: Git Status
  inputs:
    targetType: 'inline'
    script: |
      git status
    workingDirectory: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'

- task: PowerShell@2
  displayName: Git Commit
  inputs:
    targetType: 'inline'
    script: |
      # Check for changes and commit if needed
        if ((git diff-index --cached HEAD --quiet) -eq $false) {
            Write-Output "Changes detected. Committing..."
            git commit -m "$(BUILD.BUILDNUMBER) - Code updated by pipeline"
        } else {
            Write-Output "No changes to commit."
        }
      
    workingDirectory: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'

- task: PowerShell@2
  displayName: Git Push
  inputs:
    targetType: 'inline'
    script: |
        $lastCommitMessage = git log -1 --pretty=%B

        if ($lastCommitMessage -eq "$(BUILD.BUILDNUMBER) - Code updated by pipeline") {
            Write-Output "Pushing commit to remote..."
            git -c http.extraheader="AUTHORIZATION: bearer $(System.AccessToken)" push origin HEAD:$(Build.SourceBranch)
        } else {
            Write-Output "No commit to push."
        }
      
    workingDirectory: '$(System.DefaultWorkingDirectory)/Mikes.DevOps.Repo.B'
  env:
    SYSTEM_ACCESSTOKEN: $(System.AccessToken)

 

Buy Me A Coffee