Azure Pipeline - Build one project from solution with project dependencies - azure-devops

I have a solution that has a few projects.
App.Client.Website
App.Client.WindowsService
App.Client.Database
App.Client.Connectors
App.Shared
The App.Client.Database and App.Client.Connectors are both dependencies of App.Client.Website and App.Client.WindowsService, and App.Shared is a dependency of all the projects.
I am wanting to create 2 pipelines: One for the website and one for the windows service.
The issue that I have is that when I set my solution to be App.Client.Website/*.csproj, it can't find any of my project references to build - which makes a lot of sense to me, but I would obviously like to have it build my project references as well.
I tried solving this by creating pipelines for each of the dependency projects that would pack a nuget package and then I would reference that nuget packages from the feed in my website and window service projects, but this stopped me from being able to debug the code and make changes in those project on the fly.
DevOps is something that I am only just starting to pick up. I have gotten to the point where I am needing to add build numbers to my website application and wanting to stop manually incrementing patch numbers. I am using YAML instead of the Classic pipeline builder.
Any and all help is appreciated.
YAML
Here is the YAML that I am using. It is just the basic YAML with the solution targeting my Website project.
# ASP.NET
# Build and test ASP.NET projects.
# Add steps that publish symbols, save build artifacts, deploy, and more:
# https://learn.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4
trigger:
- master
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.Client.Website/*.csproj'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
steps:
- task: NuGetToolInstaller#1
- task: NuGetCommand#2
inputs:
command: 'restore'
restoreSolution: '$(solution)'
feedsToUse: 'config'
nugetConfigPath: 'Pipelines/nuget.config'
externalFeedCredentials: 'External Feed'
- task: VSBuild#1
inputs:
solution: '$(solution)'
msbuildArgs: '/p:DeployOnBuild=true /p:WebPublishMethod=Package /p:PackageAsSingleFile=true /p:SkipInvalidConfigurations=true /p:PackageLocation="$(build.artifactStagingDirectory)"'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'Container'
The repo folder structure has the project folders at the root of repo.
My project references in my Website cs project file are as follows:
<ItemGroup>
<ProjectReference Include="..\App.Client.Database\App.Client.Database.csproj" />
<ProjectReference Include="..\App.Client.Connectors\App.Client.Connectors.csproj" />
<ProjectReference Include="..\App.Shared\App.Shared.csproj" />
</ItemGroup>
Here is an error from the build task:
##[error]C:\Program Files\dotnet\sdk\3.1.403\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(241,5): Error NETSDK1004: Assets file 'D:\a\1\s\App.Shared\obj\project.assets.json' not found. Run a NuGet package restore to generate this file.

The issue is related to the NuGet Restore task, We could try to restore the .sln file to restore all NuGet packages, and then build the .csproj file. It should work.

Related

Azure SQL - DacPac Deployment

I am currently trying to test my DacPac deplyoment pipelines. However, I get error message all the time:
##[error]No files were found to deploy with search pattern C:\agent\_work\5\a\sqlproj_artifacts_1\DWH\DWH\bin\Release\database.dacpacCheck out how to troubleshoot failures at
I can't find what the problem is. Here the code, idea why it does not work? Have done before and worked perfectly....
steps:
- task: VSBuild#1
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.SourcesDirectory)'
ArtifactName: 'sqlproj_artifacts_$(System.JobAttempt)'
publishLocation: 'Container'
steps:
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
downloadType: 'single'
artifactName: 'sqlproj_artifacts_$(System.JobAttempt)'
downloadPath: '$(System.ArtifactsDirectory)'
deployType: 'DacpacTask'
DeploymentAction: 'Publish'
DacpacFile: '$(System.ArtifactsDirectory)/sqlproj_artifacts_$(System.JobAttempt)/DWH/DWH/bin/Release/$(azureSqlDBName).dacpac'
I'm using AuthenticationType: 'server'
And this is the local path where project is:
source\repos\DataWarehouse\DWH\DWH
Tried to adjust the path, but it still doesn't work.
Hope you can help me, thanks!
You can check the path of the artifact you are looking for on the build screen when you click on artifacts:
Here the $(azureSqlDBName).dacpac file name looks suspicious to me, as in if the dacpac is produced from a sql project, then probably the file will have the project name, not the azure sql db name.
In addition to this, I also would try to publish separate build artifacts for separate deployment, so instead of creating an artifact for the whole source artifact directory, I would copy the dacpac from the source to staging directory and then publish the dacpac artifact:
- task: CopyFiles#2
displayName: 'Copy DacPac Files'
inputs:
SourceFolder: '$(Build.SourcesDirectory)'
Contents: '**/*.dacpac'
TargetFolder: '$(Build.ArtifactStagingDirectory)/db'
CleanTargetFolder: true
OverWrite: true
flattenFolders: true
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)/db'
ArtifactName: 'sqlproj_artifacts_$(System.JobAttempt)'
publishLocation: 'Container'
Then it is easier to find what you are looking for when you need to use the artifact. Please note the flattenFolders flag which comes in handy when you are dealing with a single package within the artifact.
One more thing is the use of pipeline artifacts instead of build artifacts, since you are already working with yaml pipelines, and possibly multi-stage yamls, you can utilize pipeline artifacts which are automatically downloaded to the default workspace folder.
Hope it helps,

VSBuild Fails with Error MSB4057 When Building with Azure Pipelines ('The target "<project_name>" does not exist in the project')

I am trying to run the VSBuild#1 task in an Azure Pipeline and every time it fails with the same error - "The target "<project_name>" does not exist in the project". I've searched for every answer to this question and none have worked for me. The exact yaml I'm running is
- task: VSBuild#1
displayName: 'Build Project1 and Project2'
inputs:
solution: '$(solution)'
msbuildArgs: '/t:Folder\Project1:Rebuild;Folder\Project2:Rebuild'
clean: true
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
The directory looks like 'Repo\Folder\Project1\Project1.csproj' and 'Repo\Folder\Project2\Project2.csproj' and the VSBuild version is 1.199.
You can run
set MSBuildEmitSolution=1
msbuild All.sln /t:<project_name>
Then search in the generated All.sln.metaproj file the exact target names () of all projects you want to build. After that you can build projects:
msbuild All.sln /t:"<project_name>" /p:Configuration=Release /p:Platform=x64

Can I use VSBuild#1 yaml task to restore multiple projects?

I need to restore 3 projects (csproj) using VSBuild yaml task. I know that I can do it if I add 3 yaml tasks, but I wonder if is there a way to restore them all using just one task.
I already tried like this:
- task: VSBuild#1
displayName: 'Restore projects'
inputs:
solution: "'**/Project1.csproj';'**/Project2.csproj'"
msbuildArgs: '/t:Restore'
vsVersion: $(VSVersion)
platform: '$(BuildPlatform)'
configuration: '$(Configuration)'
,and I don't have syntax errors but the pipeline fails with this error: "Solution not found using search pattern..."
Usually, you specify solution file(s) to build (e.g. *.sln) rather than individual projects. So if it's possible to provide a solution file name that includes those 3 projects you need, that would be the best approach.
If that doesn't fit your case, you can use wildcards to provide a mask that fits all 3 projects, for example, **/Project*.csproj.
As the last option, you can try to switch to the MSBuild task instead. In fact, the docs say that:
In some cases you might need to use the MSBuild task. For example, you should use it if you are building code projects apart from a solution.
VSBuild task cannot recognize the multiple projects in this format "'**/Project1.csproj';'**/Project2.csproj'". You can use the wildcards **/Project*.csproj to map your projects.
- task: VSBuild#1
displayName: 'Restore projects'
inputs:
solution: '**/Project*.csproj'
msbuildArgs: '/t:Restore'
If your projects are dotnet core projects. You can use the DotNetCoreCLI task, which support multiple lines input:
steps:
- task: DotNetCoreCLI#2
displayName: 'dotnet restore'
inputs:
command: restore
projects: |
**/Project1.csproj
**/Project2.csproj
**/Project3.csproj

Combine results of several pipeline jobs into azure web app package

We're in the process of moving our product to an azure web app. We have an extensive existing pipeline containing multiple parallel jobs. One of these jobs compiles a asp.net web application. Some others compile a vue.js website. Currently, the results of the web application and the vue projects are combined in a separate stage, using a powershell script.
Now I can convert the publish step of the web application to generate a deployment package for azure. But what is the best way of also adding the vue outputs into this package so I can deploy it to azure correctly, without losing the parallel jobs? I cannot include the is output files in my project, because they don't exist within the web application build job
You can use publish build artifact task to upload the build results of the web application and vue projects to azure devops server as #Krzysztof mentioned. And you can add a new job to download the artifacts.
Please check below simple example in yaml.
In order to combine the build results, you can use extract file task to extract the zipped artifacts and published the unpacked artifacts in Build_web job. And in the Combine job you can use copy file task to copy the results of vue artifacts to the web artifacts folder. And then you can use archive file task to pack the artifacts which now contains the results of web and vue application.
Combine job should dependsOn Build_web and Build_vue jobs
jobs:
- job: Build_Web
pool:
vmImage: "windows-latest"
steps:
- task: ExtractFiles#1
inputs:
archiveFilePatterns: '*.zip'
destinationFolder: '$(Build.ArtifactStagingDirectory)\unzip'
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: '$(Build.ArtifactStagingDirectory)\unzip'
artifactName: webapp
- job: Build_Vue
pool:
vmImage: "windows-latest"
steps:
- task: PublishBuildArtifacts#1
inputs:
PathtoPublish: 'path to the build results'
artifactName: vueapp
- job: Combine
dependsOn:
- Build_Web
- Build_Vue
pool:
vmImage: "windows-latest"
steps:
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
artifactName: webapp
downloadPath: "$(System.ArtifactsDirectory)"
- task: DownloadBuildArtifacts#0
inputs:
buildType: 'current'
artifactName: vueapp
downloadPath: "$(System.ArtifactsDirectory)"
- task: CopyFiles#2
inputs:
SourceFolder: '$(System.ArtifactsDirectory)\vueapp'
TargetFolder: 'path to web application result folder' #eg. $(System.ArtifactsDirectory)\webapp\Content\d_C\a\1\s\AboutSite\AboutSite\obj\Release\netcoreapp2.0\PubTmp\Out\
- task: ArchiveFiles#2
inputs:
rootFolderOrFile: $(System.ArtifactsDirectory)\webapp
archiveType: 'zip'
archiveFile: '$(Build.ArtifactStagingDirectory)/webapplication.zip'
Above example only shows a general idea. You can aslo move the ExtractFile task to Combine job. In either way, you will have to use extract file, copy file and archive file task.
For TargetFolder parameter in copy file task, you can check the download build artifacts log for webapp artifact to get the full path. For example as below screenshot shows.
You can use PublishPipelineArtifact#1 to create artifacts for your projects and then in a separate job DownloadPipelineArtifact#2. By defining path you may compose your final artifact (if this mixing many projects is not more complicated than putting one inside another). And publish your artifact as build or pipeline artifact depending how you have roganized your release.
# Download an artifact named 'WebApp' to 'bin' in $(Build.SourcesDirectory)
- task: DownloadPipelineArtifact#2
inputs:
artifact: 'WebApp'
path: $(Build.SourcesDirectory)/bin
Here you have more info about publishing and downloding artifacts.

Specify framework/platform in Azure pipeline yml

I want to tell Azure to run tests against the framework and platform actually used by my test project. According to Microsoft,
Tests that target the .NET core framework can be executed by specifying the appropriate target framework value.
But how do I do that?
Additionally, the log output says there is an issue of different platforms. I'm not sure how to address that either. I've tried putting a platform into my yml, but it doesn't help.
Here is my current yml:
- job: Test
dependsOn: SetBuildName
pool:
vmImage: 'windows-2019'
variables:
solution: '**/MyTestSolution.sln'
buildPlatform: 'x86|x64|ARM'
buildConfiguration: 'Release'
appxStagingDir: '$(build.artifactStagingDirectory)\AppxPackages\\'
steps:
- task: NuGetToolInstaller#1
inputs:
versionSpec: '5.4.0'
- task: NuGetCommand#2
inputs:
restoreSolution: '$(solution)'
- task: VersionAPPX#2
inputs:
Path: '$(Build.SourcesDirectory)'
VersionNumber: '$(versionNumber)'
InjectVersion: true
OutputVersion: 'OutputedVersion'
- task: VSBuild#1
inputs:
platform: 'x86' #Changing this to AnyCPU had no effect.
solution: '$(solution)'
configuration: '$(buildConfiguration)'
msbuildArgs: '/t:Restore'
- task: VSBuild#1
inputs:
platform: 'x86'
solution: '$(solution)'
configuration: '$(buildConfiguration)'
msbuildArgs: '/p:AppxBundlePlatforms="$(buildPlatform)"
/p:AppxPackageDir="$(appxStagingDir)"
/p:AppxBundle=Always
/p:UapAppxPackageBuildMode=Sideload
/p:AppxPackageSigningEnabled=true
/p:VersionPrefix="$(versionNumber)"
/p:VersionSuffix="$(version.SpecialBuild)"
/p:SourceRevisionId="$(Build.SourceVersion)"'
And here is an excerpt from the log:
2020-03-19T12:28:57.5842598Z Test run will use DLL(s) built for framework .NETFramework,Version=v4.0 and platform X86. Following DLL(s) do not match framework/platform settings.
2020-03-19T12:28:57.5843802Z MyProject.Test.dll is built for Framework .NETCoreApp,Version=v3.1 and Platform AnyCPU.
The best solution would be to tell it to use whatever the project built against. But if that isn't possible, I'd settle for a way to specify NETCoreApp 3.1 and AnyCPU.
Tests that target the .NET core framework can be executed by
specifying the appropriate target framework value.
In VSTest#2 task, there has one argument names otherConsoleOptions. It can pass some additional options to the tool vstest.console.exe, including platform, framwork and etc.
Note: The platfrom argument of task just purely for reporting purposes.
Here is what I am using on my YAML:
- task: VSTest#2
inputs:
testSelector: 'testAssemblies'
testAssemblyVer2: |
**\Release\UnitTestProject1.build.appxrecipe
!**\*TestAdapter.dll
!**\obj\**
searchFolder: '$(System.DefaultWorkingDirectory)'
otherConsoleOptions: '/Platform:x86 /Framework:Framework45'
platform: 'x86|x64'
Just replace the otherConsoleOptions value based on your actual demand like this:
otherConsoleOptions: '/Platform:{platform type: x86/x64/ARM} /Framework:{Framwork version}'
Above method is used for making platform/framwork configuration in yml file.
But there has another way you can use to achieve it: specify platform type and framwork version in your runsetting file.
<RunSettings>
<!-- Configurations that affect the Test Framework -->
<RunConfiguration>
...
...
<TargetPlatform>x86</TargetPlatform>
<TargetFrameworkVersion>Framework40</TargetFrameworkVersion>
....
....
</RunConfiguration>
...
...
</RunSettings>