Max Cascone
Max Cascone

Reputation: 833

Using msbuild, how can I build once and then publish with multiple transform profiles?

I have a .net solution that I can build with msbuild and successfully generate a deploy package with a PublishProfile.pubxml transform for deploying to a single web server.

I need to take that build and generate deploy packages for different environments, which are set up as transforms using various .pubxml profile files.

I know I could build each profile separately, and it'd be negligible risk of injecting a change into a build, but it's time and space that aren't necessary to consume. I would only end up keeping one of them anyway, and just copying the unique web.configs from each transform into their own deploy package's folder (sorry if this isn't clear, happy to clarify).

I'm looking for something like this pseudocode, which I know is syntactically incorrect but should get the point across:

// step 1
msbuild myapp.csproj target=Build

// step 2
foreach ($profile in $profileList) {
  msbuild myapp.csproj outputdir="releaseDir/$profile" target=Publish publishProfile=$profile
}

I am working in a Jenkins context and can't use Visual Studio functions unless they're available as command-line options.

I've also tried desperately to get msdeploy to work, which I think might simplify some of this, but for the life of me I can't get it to work with the tempAgent option, which is really the only way I want to go in my environment. Details on that particular struggle here.

Thanks in advance!

Update 1: This post works well for me, but as I say in a comment, it's still creating multiple deploy packages that take up a ton of space and aren't necessary. What I'd really like is a way to use a single build artifact and create multiple transforms of the Web.config files. Then on deploy, copy that build artifact and the appropriate config file. That would really be build-once/deploy-many!

FWIW, this is the code i came up with using the link above:

function Initialize-Build {
  $ErrorActionPreference = 'Stop'
  $appName = 'myApp'
  $projsToBuild = 'Ui', 'DataService'
  $projPath = "$appName/$appName"
  $buildPath = 'obj/Release/Package/'
  $releasePath = 'Release-Packages'

  $commonArgs = '/p:Configuration=Release',
    '/nologo',
    '/verbosity:quiet'
}

function Invoke-Build {
  foreach ($proj in $projsToBuild) {
    $projName = $proj -eq 'Ui' ? 'WebUi' : $proj
    $p = "$projPath.$proj/$appName.$projName.csproj"

    $buildArgs = '/t:Clean,Build',
      '/p:AutoParameterizationWebConfigConnectionStrings=False'

    Write-Output "Building $p"
    msbuild $p @commonArgs @buildArgs
    if ($LASTEXITCODE) { exit 1 }
  }
}

function Invoke-Transform {
  $xformArgs = "/p:deployOnBuild=True", "/p:Targets=Publish"

  foreach ($proj in $projsToBuild) {
    $p = "$projPath.$proj/$appName.$projName.csproj"

    $pubProfiles = 'Dev', 'QA', 'UAT', 'Prod'
    foreach ($prof in $pubProfiles) {  
      Write-Output "Building $p ($prof)"
      $pubProfileArg = "/p:PublishProfile=$prof"

      msbuild $p @commonArgs @xformArgs $pubProfileArg
      if ($LASTEXITCODE) { exit 1 }
    }  
  }
}

. Initialize-PrivLogBuild

Invoke-Build

Invoke-Transform

Upvotes: 3

Views: 1057

Answers (1)

Max Cascone
Max Cascone

Reputation: 833

I realized I wasn't really asking the right question. When I started searching for msbuild transform posts, I found a way to do what I need.

I landed on updating the .csproj files of the apps I'm building with an AfterBuild target.

There are 4 transforms required, each with their own .config file as the transform source. I was fortunate that these files had already been created by the application developer.

This is the code I ended up with, placed at the end of the .csproj file, inside the </project> tag. To reduce repetition of paths and filenames, I created configDir and xformFile properties. I like this pattern because it's easily scalable and generic!

<!-- all the rest of the .csproj above this -->
  <UsingTask TaskName="TransformXml"
    AssemblyFile="$(MSBuildExtensionsPath32)/Microsoft/VisualStudio/v16.0/Web/Microsoft.Web.Publishing.Tasks.dll" />
  <Target Name="AfterBuild">
    <PropertyGroup>
      <configDir>../../Release-Packages/configs</configDir>
      <xformFile>Web.config</xformFile>
    </PropertyGroup>
    <MakeDir Directories="$(configDir)" />
    <TransformXml Source="$(xformFile)"
                  Transform="Web.Dev.config"
                  Destination="$(configDir)/$(AssemblyName).$(xformFile).DEV" />
    <TransformXml Source="$(xformFile)"
                  Transform="Web.QA.config"
                  Destination="$(configDir)/$(AssemblyName).$(xformFile).QA" />
    <TransformXml Source="$(xformFile)"
                  Transform="Web.Prod.config"
                  Destination="$(configDir)/$(AssemblyName).$(xformFile).Prod" />
    <TransformXml Source="$(xformFile)"
                  Transform="Web.UAT.config"
                  Destination="$(configDir)/$(AssemblyName).$(xformFile).UAT" />                                 
  </Target>
</Project>

many thanks to these posts for lighting the way to this solution

Upvotes: 2

Related Questions