Copy one file in target directory on deploy from visual studio team services

I'm using VSTS as a build server, and while building I want to copy the bin folder contents to the root of the target, and also custom files from another folder to this target. MSDN suggests I use a minimatch pattern, but it's copying files with the subdirectory structure. I'm not interested in restoring the structure.

For example, I am getting this folder structure:

Project
    MyProjectFiles
    bin
        x86 (it's build configuration)
            Project.exe
    Other project files
    Project.sln
SomeScrips
    script1.ps1

But I want to receive this folder structure:

Project.exe
SomeScripts
    script.ps1

Which minimatch pattern can I use for my requirements?


Solution 1:

You need to specify the copy root if you want to copy files only without folder structure. Since the project.exe is in a different path with script.ps1 file, you need to copy them in different copy task.

Following the steps below:

  1. Add a "Copy Files" step to copy "project.exe". Settings like following: enter image description here
  2. Add a "Copy Files" step to copy "SomeScripts" folder. Settings like following: enter image description here
  3. Add a "Copy and Publish Build Artifacts" step to copy these files to "drop". Settings like following: enter image description here

Now you should get the things like following in drop folder:

Project.exe
SomeScripts
    script.ps1

Solution 2:

"Flatten Folders" option in "Advanced" section of "Copy Files" step.

If you are using TFS Online (Visual Studio Online) and don't need to copy the folder structure use "Flatten Folders" option in "Advanced" section of "Copy Files" step in your build definition

Solution 3:

With the new web based build system you can use multiple patterns in a single step. Therefore you can do something like this for your case:

Project\bin\x86\Release\project.exe
SomeScripts\**\*

Or if you have the build platform and configuration in a variable (eg. BuildPlatform / BuildConfiguration) which you also use in the build step you could use them in the pattern:

Project\bin\$(BuildPlatform)\$(BuildConfiguration)\project.exe
SomeScripts\**\*

If you want the project.exe to be in the root instead of the structure you need to use a Copy Task to stage your files in the desired structure first. You can use $(Build.StagingDirectory) as a target for this. Afterwards use the Publish task with $(Build.StagingDirectory) as copy root and publish everything from this root to the drop.

Solution 4:

For those who would like to have a PowerShell script to use in your build server, here is a working (at least, on my build server ;)) sample:

param
(
    [string] $buildConfiguration = "Debug",
    [string] $outputFolder = $PSScriptRoot + "\[BuildOutput]\"
)

Write-Output "Copying all build output to folder '$outputFolder'..."

$includeWildcards = @("*.dll","*.exe","*.pdb","*.sql")
$excludeWildcards = @("*.vshost.*")

# create target folder if not existing, or, delete all files if existing
if(-not (Test-Path -LiteralPath $outputFolder)) {
    New-Item -ItemType Directory -Force -Path $outputFolder | Out-Null

    # exit if target folder (still) does not exist
    if(-not (Test-Path -LiteralPath $outputFolder)) {
        Write-Error "Output folder '$outputFolder' could not be created."
        Exit 1
    }
} else {
    Get-ChildItem -LiteralPath $outputFolder -Include * -Recurse -File | foreach {
        $_.Delete()
    }
    Get-ChildItem -LiteralPath $outputFolder -Include * -Recurse -Directory | foreach {
        $_.Delete()
    }
}

# find all output files (only when in their own project directory)
$files = @(Get-ChildItem ".\" -Include $includeWildcards -Recurse -File |
    Where-Object {(
        $_.DirectoryName -inotmatch '\\obj\\' -and
        $_.DirectoryName -inotmatch '\\*Test*\\' -and
        $_.DirectoryName -ilike "*\" + $_.BaseName + "\*" -and
        $_.DirectoryName -ilike "*\" + $buildConfiguration
    )}
)

# copy output files (overwrite if destination already exists)
foreach ($file in $files) {
    Write-Output ("Copying: " + $file.FullName)
    Copy-Item $file.FullName $outputFolder -Force

    # copy all dependencies from folder (also in subfolders) to output folder as well (if not existing already)
    $dependencies = Get-ChildItem $file.DirectoryName -Include $includeWildcards -Exclude $excludeWildcards -Recurse -File
    foreach ($dependency in $dependencies) {
        $dependencyRelativePathAndFilename = $dependency.FullName.Replace($file.DirectoryName, "")
        $destinationFileName = Join-Path -Path $outputFolder -ChildPath $dependencyRelativePathAndFilename
        if (-not(Test-Path -LiteralPath $destinationFileName)) {
            Write-Output ("Copying: " + $dependencyRelativePathAndFilename + " => " + $destinationFileName)

            # create sub directory if not exists
            $destinationDirectory = Split-Path $destinationFileName -Parent
            if (-not(Test-Path -LiteralPath $destinationDirectory)) {
                New-Item -Type Directory $destinationDirectory
            }
            Copy-Item $dependency.FullName $destinationDirectory
        } else {
            Write-Debug ("Ignoring (existing destination): " + $dependency.FullName)
        }
    }
}

Here is the script being used in a PowerShell build step:

TFS 2015 Build - Output to single folder step

Solution 5:

The "flattenFolders" option is also available as a YAML task parameter. The following code excerpt shows a CopyFiles@2 task which copyies the build output to the $(Build.ArtifactStagingDirectory). When I specify the option flattenFolders: true the nested folder structure bin\release\...\My.exe is flattened, means the exe files is copied to the root of $(Build.ArtifactStagingDirectory).

- task: CopyFiles@2
  displayName: 'Copy Files to: $(Build.ArtifactStagingDirectory)'
  inputs:
    SourceFolder: '$(system.defaultworkingdirectory)'
    Contents: |
     **\bin\$(BuildConfiguration)\**\*.exe
    TargetFolder: '$(Build.ArtifactStagingDirectory)'
    flattenFolders: true

Further documentation concerning the CopyFiles task can be found here: https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/copy-files?view=vsts&tabs=yaml