What is the Build Drop Location environment variable name for PowerShell in TFS 2015/2017 - powershell

In previous versions of TFS (before 2015), there was a build environment variable for PowerShell called: TF_BUILD_DROPLOCATION, which gave the The location of the drop:
https://msdn.microsoft.com/library/hh850448%28v=vs.120%29.aspx.
I can't find the equivalent variable in TFS 2017.
What is the best practice to get it?

With Build agent tasks taking over things are different. What I do to see the various build environment variables is to make a simple batch file containing this:
SET C:\temp\EnvVars.txt
That'll produce a quick list of what is available.
Here's what I see with the TFS 2017 build agent:
agent.jobstatus=Succeeded
AGENT_BUILDDIRECTORY=C:\Agent\_work\2
AGENT_HOMEDIRECTORY=C:\Agent
AGENT_ID=2 AGENT_JOBNAME=Build
AGENT_JOBSTATUS=Succeeded
AGENT_MACHINENAME=BUILDMACHINE
AGENT_NAME=BUILDMACHINE
AGENT_OS=Windows_NT
AGENT_ROOTDIRECTORY=C:\Agent\_work
AGENT_SERVEROMDIRECTORY=C:\Agent\externals\vstsom
AGENT_TEMPDIRECTORY=C:\Agent\_work\_temp
AGENT_TOOLSDIRECTORY=C:\Agent\_work\_tool
AGENT_VERSION=2.122.1
AGENT_WORKFOLDER=C:\Agent\_work
BUILD_ARTIFACTSTAGINGDIRECTORY=C:\Agent\_work\2\a
BUILD_BINARIESDIRECTORY=C:\Agent\_work\2\b
BUILD_BUILDID=2036
BUILD_BUILDNUMBER=Database Build_20190708.2
BUILD_BUILDURI=vstfs:///Build/Build/2036
BUILD_CONTAINERID=2281
BUILD_DEFINITIONNAME=Database Build
BUILD_DEFINITIONVERSION=17
BUILD_QUEUEDBY=Smith, John
BUILD_QUEUEDBYID=8c588342-b87a-40cb-9b8c-a0ed10b57a3f
BUILD_REASON=Manual
BUILD_REPOSITORY_CLEAN=false
BUILD_REPOSITORY_GIT_SUBMODULECHECKOUT=False
BUILD_REPOSITORY_ID=$/
BUILD_REPOSITORY_LOCALPATH=C:\Agent\_work\2\s
BUILD_REPOSITORY_NAME=Collection
BUILD_REPOSITORY_PROVIDER=TfsVersionControl
BUILD_REPOSITORY_TFVC_WORKSPACE=ws_2_2
BUILD_REPOSITORY_URI=http://TFSSERVER:8080/tfs/Project/
BUILD_REQUESTEDFOR=Smith, John
BUILD_REQUESTEDFOREMAIL=John.Smith#Mailinator.com
BUILD_REQUESTEDFORID=7a588222-b66a-40ee-9b2a-a0ba10b12a3f
BUILD_SOURCEBRANCH=$/Collection/Project/Code
BUILD_SOURCEBRANCHNAME=Code
BUILD_SOURCESDIRECTORY=C:\Agent\_work\2\s
BUILD_SOURCEVERSION=9811
BUILD_SOURCEVERSIONAUTHOR=Smith, John
BUILD_SOURCEVERSIONMESSAGE=Added missing permission
BUILD_STAGINGDIRECTORY=C:\Agent\_work\2\a

You can list all Environment Variables with the following command:
get-childitem ENV:\
I am assuming you could create a simple build job that executes this and then look at the console output to determine what the name is of the Environment Variable you need.

Related

Azure DevOps Pipeline not finding edmx resources

I have a .Net Core 3.1 MSTest project that calls a separate class library that connects to my database, which was done database-first so I have a .edmx file. It all runs fine locally, but when I push it out to my Azure DevOps Pipeline I start getting a Unable to load the specified metadata resource. exception. I've tested this out by putting in a bit of code to print out all the resources in the assembly
var resources = (Assembly with EDMX).Assembly.GetManifestResourceNames();
System.Console.WriteLine("There are " + resources.Length + " resources in the assembly");
foreach (var resource in resources)
{
System.Console.WriteLine(resource);
}
The result on my local computer prints out what I expect
There are 3 resources in the assembly
Model.csdl
Model.msl
Model.ssdl
However, the same run on my Pipeline shows 0 resources
There are 0 resources in the assembly
My build process locally and on my pipeline are the exact same
task: PowerShell#2
inputs:
targetType: 'inline'
script: |
& "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\msbuild.exe" $(Build.SourcesDirectory)\MyProject\MyProject.sln
dir $(Build.SourcesDirectory)\MyProject -include ('*.csdl', '*.msl', '*.ssdl') -recurse
& "C:\hostedtoolcache\windows\dotnet\dotnet.exe" test $(Build.SourcesDirectory)\MyProject\Project.Tests\Project.Tests.csproj
Just to make sure the resources actually exist on my Azure Build agent I've added that 2nd powershell command to find my .csdl, .msl, and .ssdl files, and sure enough they do exist
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 5/30/2020 3:54 PM 30772 Model.csdl
-a---- 5/30/2020 3:54 PM 10993 Model.msl
-a---- 5/30/2020 3:54 PM 23351 Model.ssdl
These files are located in $(Build.SourcesDirectory)\MyProject\Project.Models\obj\Debug\edmxResourcesToEmbed
And the property looks to be correctly set in my .csproj
<ItemGroup>
<EntityDeploy Include="ProjectModels.edmx">
<Generator>EntityModelCodeGenerator</Generator>
<LastGenOutput>ProjectModels.Designer.cs</LastGenOutput>
</EntityDeploy>
</ItemGroup>
I don't use .edmx database designs often, so I'm unfamiliar on how it is suppose to handle the resources, are they suppose to get compiled into the assembly, or are they just loaded at runtime? My build process both locally and in my Pipeline both show:
Skipping target "EntityDeployNonEmbeddedResources" because it has no outputs.
EntityDeployEmbeddedResources:
Processing 1 EDMX files.
Starting to process input file 'ProjectModels.edmx'.
Finished processing input file 'ProjectModels.edmx'.
Finished processing 1 EDMX files.
Not sure what that indicates, but since it occurs on both I'm assuming its not part of the issue. The only other difference that I can think of is my Azure Pipeline uses Visual Studio 2019 Enterprise, while my local build uses Visual Studio 2019 Community.
Any ideas on what I can do to get these resources to load on my Pipeline build?
Azure DevOps Pipeline not finding edmx resources
This issue should comes from the dotnet test.
AFAIK, dotnet cli does not support embed edmx resources. These types of files are supported in MSBuild props/targets that don't ship in the CLI and only ship in full framework VS.
You could check this ticket for some more details.
And the remaining work for this is already tracked at dotnet/ef6#231.
Hope this helps.

Change Variable Pipeline.Workspace in Azure Devops

As they are, I need to change the directory where my repository is cloned, in the documentation I saw that there is a variable Pipeline.Workspace but I can not change it, I'm working with a self-hosted agent
Change Variable Pipeline.Workspace in Azure Devops
To change the the default work folder _work that Azure Devops agents use when building a pipeline, you can open the hidden.agent file in the installation directory of the private agent and change the workFolder to the place you want:
{
"agentId": 9,
"agentName": "VsAgent1",
"poolId": 10,
"serverUrl": "https://dev.azure.com/MyXXXXOrganization/",
"workFolder": "_work"
}
As test, I use bash task to output the value of variable Pipeline.Workspace with the command line echo $(Pipeline.Workspace):
The default value is C:\VS2017Agent\_work\14:
Then I change the workFolder from _work to D:\\tfsagent\\_work in the .agent file and run the build pipeline again:
The changed value is D:\tfsagent\_work\1:
Check the document How to change the TFS Agent _work folder for some more details.
Hope this helps.

How to receive Revision in Azure Pipelines YAML build definition

I created a new build with Azure Pipelines (Azure DevOps) and it worked really well.
Usually, you use $(Rev:.r) to get the revision in the build. Unfortunately, it seems the variable isn't replaced/set in the build steps. The only place where you can use it is the name: property in the YAML document.
Now I set it in the name and extract it in some PowerShell, which isn't necessary if you can get it via an environment variable.
How do I get the Revision (like $(Rev)) in the new builds (outside of the name: property in the YAML document)?
(The Build Agents running on-premise, inside Docker - but this shouldn't affect the things above)
You can't get the revision number without parsing, it is not stored as a separate field somewhere or in an environment variable.
The $(Rev:.r) portion instructs Azure DevOps to come up with the first number that makes the build number unique (and, in that specific example, put a dot in front of it).
Like you said, the only way is to use PowerShell script to get the value:
$buildNumber = $Env:BUILD_BUILDNUMBER
$revision= $buildNumber.Substring($buildNumber.LastIndexOf('.') + 1)
Edit:
You can install the Get Revision Number extension that does it.
Another possible solution to the above problem could be to use counter expression for ex: we difine the variable and use it in a task to build nuget package.
variables:
counterVar: $[counter($(versionVariable),0)]
.......
- task: CmdLine#2
inputs:
script: >
nuget pack ClassLibrary1/ClassLibrary1.csproj
-OutputDirectory $(Build.ArtifactStagingDirectory)
-NonInteractive
-Properties Configuration=release
-Version $(versionVariable).$(counterVar)
-Verbosity Detailed
-IncludeReferencedProjects
Here versionVariable is a custome variable defined in pipelines->variables.And the seed value is 0(2nd param to counter).
It works as below
Let's assume the versionVariable is 1.19
Build Run 1 counterVar will be 0.
Build Run 2 counterVar will be 1.
Now say we change the versionVariable to 1.20
Build Run 3 counterVar will be 0.
https://learn.microsoft.com/en-us/azure/devops/pipelines/process/expressions?view=azure-devops
Check the counter expression in above link it reset its value for diff prefix.
P.S. Benefit of using counter over $(Rev:r) is that it can start from 0 unlike $(Rev:r)

How to get Resharpers InspectCode to recognize Plugins?

I am trying to run ReSharpers command line tool InspectCode.exe. It's running fine doing it's job with the expected output.
However after my earlier attempt to get plugins to work, this time with the new version it is supposed to be supported. There is a switch in the command line interface that allows to specify the extension you want to use.
/extensions (/x) – allows using ReSharper extensions that affect code analysis. To use an extension, specify its ID, which you can find by opening the extension package page in the ReSharper Gallery, and then the Package Statistics page. Multiple values are separated with the semicolon.
But I cannot get it to work properly. I cannot even provoke any reaction to the /x switch at all. No matter how or what I pass, I get no feedback from the executable and the output is identical. I don't even get an error message when passing obvious garbage.
I tried the following commandlines for the exact same result:
inspectcode.exe /o="rcli.xml" /swea /x="ReSharper.StyleCop" "my.sln"
inspectcode.exe /o="rcli.xml" /swea /x=ReSharper.StyleCop "my.sln"
inspectcode.exe /o="rcli.xml" /swea "my.sln"
inspectcode.exe /o="rcli.xml" /swea /x=ABCDEFG "my.sln"
Result
JetBrains Inspect Code 9.1.1
Running in 64-bit mode, .NET runtime 4.0.30319.18444 under Microsoft Windows NT
6.1.7601 Service Pack 1
Enabled solution-wide analysis according to Inspect Code command line Setting.
Analyzing files
[files]
Inspection report was written to rcli.xml
What am I doing wrong? How to get extensions to work?
I already tried the R# forums, but it took them more then 24h to approve my post and so far I'm not sure someone else even read it.
Unfortunately, the support for extensions was dropped in 9.0 due to the refactorings in the "ReSharper platform". I hope that JetBrains will bring it back soon.
See RSRP-436208.
This is a late answer that might help future readers (like myself). Currently inspectcode.exe will automatically look for and use any NuGet packages that are in the same folder as the executable (source).
Example for CleanCode extension:
if you have a R# instance on some machine and install the extension, it will be placed in C:\Users\{user}\AppData\Local\JetBrains\plugins\MO.CleanCode.5.6.15
copy MO.CleanCode.5.6.15.nupkg and paste it next to inspectcode.exe
when running inspectcode with verbosity = VERBOSE, the extension should appear in the Zones list:
$cmd = "..\JetBrains.ReSharper.CommandLineTools.2019.3.4\inspectcode.exe"
$outputFile = "..\Output\$($outputName).xml"
& $cmd -o="$outputFile" $sln --verbosity=VERBOSE
Zones: (52pcs)[CodeInspectionPageImplZone, DaemonEngineZone,
DaemonZone, IAmd64CpuArchitectureHostZone, IAspMvcZone,
IBatchToolEnvironmentZone, IClrImplementationHost Zone,
IClrPsiLanguageZone, ICodeEditingOptionsPageImplZone,
IConsoleEnvironmentZone, ICppProductZone, ICpuArchitectureHostZone,
IDocumentModelZone, IEnvironmentZone, IHostSolutionZone,
IInspectCodeConsoleEnvironmentZone, IInspectCodeEnvironmentZone,
IInspectCodeZone, ILanguageAspZone, ILanguageBuildScriptsZone,
ILanguageCppZone, I LanguageCSharpZone, ILanguageCssZone,
ILanguageHtmlZone, ILanguageIlZone, ILanguageJavaScriptZone,
ILanguageMsBuildZone, ILanguageNAntZone, ILanguageProtobufZone, ILa
nguageRazorZone, ILanguageRegExpZone, ILanguageResxZone,
ILanguageVBZone, ILanguageXamlZone, INetFrameworkHostZone, INuGetZone,
IOperatingSystemHostZone, IProjectMode lZone,
IPsiAssemblyFileLoaderImplZone, IPsiLanguageZone,
IPublicVisibilityZone, IRdFrameworkZone, IRiderModelZone,
ISinceClr2HostZone, ISinceClr4HostZone, ITextContro lsZone,
IToolsOptionsPageImplZone, IWebPsiLanguageZone, IWindowsNtHostZone,
PsiFeaturesImplZone, ReplaceableByIntelliJPlatformZone, SweaZone]
Packages: (23pcs)[JetBrains.ExternalAnnotations,
JetBrains.Platform.Core.Ide, JetBrains.Platform.Core.IisExpress,
JetBrains.Platform.Core.MsBuild, JetBrains.Platform. Core.Shell,
JetBrains.Platform.Core.Text, JetBrains.Platform.Interop.CommandLine,
JetBrains.Platform.Interop.dotMemoryUnit.Framework,
JetBrains.Platform.Interop.dotMe moryUnit.Interop.Console,
JetBrains.Platform.Interop.dotMemoryUnit.Interop.Ide,
JetBrains.Platform.RdProtocol, JetBrains.Psi.Features.Core,
JetBrains.Psi.Features.Cpp .Src.Core, JetBrains.Psi.Features.src,
JetBrains.Psi.Features.Tasks, JetBrains.Psi.Features.UnitTesting,
JetBrains.Psi.Features.Web.Core, JetBrains.ReSharperAutomatio
nTools.src.CleanupCode,
JetBrains.ReSharperAutomationTools.src.CommandLineCore,
JetBrains.ReSharperAutomationTools.src.CommandLineProducts,
JetBrains.ReSharperAutomat ionTools.src.DuplicatesFinder,
JetBrains.ReSharperAutomationTools.src.InspectCode, MO.CleanCode]

OctopusDeploy - Every website in the deploy has a different AppPool and Website name; how to deal; no other differences

I'm trying to setup a deploy process that targets 16 web sites each hosting an instance of the same application.
Websites and AppPools are named as such:
appServer1:
app10.site.com
app11.site.com
app12.site.com
app13.site.com
appServer2:
app20.site.com
app21.site.com
app22.site.com
app23.site.com
etc.
etc.
...with each website having a correspondingly named AppPool.
I am desperately trying to determine how to use a single Deploy NuGet Package step to target all of these websites/app pools using variables and a combination of powershell scripts if possible.
I'd like to have a single step where I can variable substitute the website and app pool names. As this is the only difference. I basically need the equivalent of being able to loop the nuget package step passing it a list of website and app pool names. I cannot simply use variables because I can only resolve to the machine level with variable scoping.
Create list of all Website and AppPool names, iterate them passing each value to a Step for execution. ForEach processing step for lack of better words.
I do have the ability to rename the AppPools if need be for a more consistent pattern, but I cannot change the website names
Any ideas would be greatly appreciated.
http://help.octopusdeploy.com/discussions/questions/3481-every-website-in-the-deploy-has-a-different-apppool-and-website-name-how-to-deal-no-other-differences
There's a lot to your question, but I'm going to take a stab at explaining our approach, in hopes of jogging your creative juices.
tl;dr
simply put, use your own powershell scripts to install the web-application. In there you can set the app pool name on a per website basis
For starters, we do do a separate deployment step for each project. The scripts we use will allow you to do all deployments from a single deploy.ps1 (including unique appPool names), but we find that it really helps keep each deployment nice and lean, and easy to manage. Each project get's it's own nupkg and therein contains the predeploy.ps1, deploy.ps1, and postdeploy.ps1 as well as a folder of build/deploy scripts that we've open sourcesd, and a folder of environment config xml files.
A sample of an environment config would be this. The name is simply [envName].xml
<!-- environments\Production.xml -->
<environmentSettings>
<webSites>
<app>
<physicalPathRoot>c:\inetpub</physicalPathRoot>
<physicalFolderPrefix>appname</physicalFolderPrefix>
<siteProtcol>https</siteProtcol>
<siteName>appname.tld</siteName>
<siteHost>appname.tld</siteHost>
<portNumber>443</portNumber>
<appPath>/</appPath>
<appPool>
<name>appname.tld</name>
<!-- valid identityTypes are: [LocalSystem, LocalService, NetworkService, SpecificUser, ApplicationPoolIdentity] -->
<identityType>NetworkService</identityType>
<!-- Set this value to the User the Service will run under in the format DOMAIN\username -->
<!-- If Running as 'NetworkService' then 'NT AUTHORITY\Network Service' is used -->
<userName>NT AUTHORITY\Network Service</userName>
<!-- Leave blank unless using SpecificUser -->
<password></password>
<maxWorkerProcesses>5</maxWorkerProcesses>
</appPool>
</app>
</webSites>
<serverDatabase>
<name>database_name</name>
<connectionString>REPLACED BY OCTOPUS</connectionString>
<providerName>System.Data.SqlClient</providerName>
</serverDatabase>
</environmentSettings>
You can see in the corresponding Get-EnvironmentSettings.ps1 where we load up the config, and then update it with any Octopus variables. This is the trickiest part, because we use dot-Notation to update the paths (case sensitive).
Our octopus variables really only contain information that is secret, as everything else lives in [environment].xml
| Name | Value | Scope
--------------------------------------------------------------------------
| webSites.app.appPool.password | supersecret | Production
So now a typical deployment script simply imports the modules, grab environmentSettings, update config, and install the web app.
# Top of the script, get Octopus environment and version
param(
[string] $version = $OctopusPackageVersion,
[string] $environment = $OctopusEnvironmentName
)
# Make sure a failed deployment actually fails
$ErrorActionPreference = "Stop"
# Import the modules
$currentDir = Split-Path $script:MyInvocation.MyCommand.Path
$moduleDir = "$currentDir\modules"
Import-Module BuildDeployModules
# Grab the environment settings
$environmentSettings = Get-EnvironmentSettings $environment "//environmentSettings"
$databaseSettings = $environmentSettings.serverDatabase
$websiteSettings = $environmentSettings.webSites.app
# update the config
Update-XmlConfigValues $currentDir\website\Web.config "//appSettings/add[#key='databaseName']" $($databaseSettings.name) "value"
Update-XmlConfigValues $currentDir\website\Web.config "//connectionStrings/add[#name='databaseConnection']" $($databaseSettings.connectionString) "connectionString"
Update-XmlConfigValues $currentDir\website\Web.config "//connectionStrings/add[#name='databaseConnection']" $($databaseSettings.providerName) "providerName"
# Install the web application
Install-WebApplication $environment $websiteSettings $version "anonymousAuthentication"
In doing all of this, the web application is installed into IIS with a specific application pool, and appropriate config transforms without relying on any unknowns.
Our nupkg structure looks something like this
appname.1.2.3.4.nupkg
environments
dev.xml
staging.xml
qual.xml
production.xml
modules
[all of our build modules]
website
[all of our website files]
This is super repeatable, easy to maintain, and easy to edit config. Hope it helps