Inject EmbeddedResource created by MSBuild Task - msbuild

I have a custom MSBuild Task that generates a file based on files that have no build action. The generated files need to be embedded into the final assembly. The task looks something like this:
public class MyTask : Task
{
public string OutputDirectory { get; set; }
public string[] NoneIncluded { get; set; }
private IEnumerable<ITaskItem> _generatedFiles;
[Output]
public ITaskItem[] GeneratedFiles => _generatedFiles.ToArray();
public override bool Execute()
{
_generatedCssFiles = new List<ITaskItem>();
foreach(var item in NoneIncluded)
{
if(someCondition)
{
var contents = DoFoo(item);
var outputPath = Path.Combine(OutputDirectory, $"{item}.txt");
File.WriteAllText(outputPath, contents);
_generatedFiles.Add(new TaskItem(ProjectCollection.Escape(outputFile)));
}
}
}
}
In my targets file, I then have a target defined like the following:
<PropertyGroup>
<CoreCompileDependsOn>MyTarget;$(CoreCompileDependsOn);</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="MyTarget"
BeforeTargets="CoreCompile;Build">
<MyTask OutputDirectory="$(IntermediateOutputPath)"
NoneIncluded="#(None)">
<Output ItemName="FilesWrite"
TaskParameter="GeneratedFiles"/>
<Output ItemName="EmbeddedResource"
TaskParameter="GeneratedFiles" />
</MyTask>
</Target>
I ultimately end up with two issues that I can't seem to resolve:
Although the files are generated prior to the compile task, it isn't embedded into the assembly, unless I rebuild the project without cleaning the outputs. On the subsequent build the file is embedded.
If I generate the files in the IntermediateOutputPath, the embedded resource id includes that path. So instead of MyProject.SomeResource.txt I get MyProject.obj.netstandard2._0.SomeResource.txt
Note:
- If I replace the Path.Combine and simply generate the output file in the project, it fixes the issue of the resource id, but not the first issue with it not being embedded on the first compile.
How can I ensure the my generated files are embedded on the first compilation, and that I can generate them in the IntermediateOutputPath rather than the Project directory with a resource id as if it were in the project.

You need to run your target earlier in the pipeline. CoreCompile is too late.
You can add the following to your .csproj file. This target will run before ResolveReferences and add Image.png as an embedded resource.
<Target Name="IncludeDynamicResources" BeforeTargets="ResolveReferences">
<ItemGroup>
<EmbeddedResource Include="Image.png">
<Type>Non-Resx</Type>
</EmbeddedResource>
</ItemGroup>
</Target>

Your second issue can be solved by simply setting LogicalName metadata to the created task item.
TaskItem taskItem = new TaskItem(ProjectCollection.Escape(outputFile));
taskItem.SetMetadata("LogicalName", $"{AssemblyName}.{item}.txt");
_generatedFiles.Add(taskItem);
Now to access the assembly name inside your task just add AssemblyName string property to MyTask and AssemblyName="$(AssemblyName)" to your targets. This should produce the desired result.

Related

.NetCore: How to include folder in production?

So I have this code to send email. The code tries to find the .cshtml for the email template.
This is my folder hierarchy:
Project
-- Email
---- EmailConfirmation.cshtml
---- ResetPassword.cshtml
This is the code to find the template:
public static string GetEmailTemplate(string templateName)
{
string path = Path.Combine(Config.Env.ContentRootPath, "Email", templateName);
string content = "";
// This text is added only once to the file.
if (File.Exists(path))
{
// Create a file to write to.
content = File.ReadAllText(path);
}
else
{
throw new Exception("The email template could not be found");
}
return content;
}
In Debug, this runs perfectly, but in production, the code cannot find the template.
How can I include the template in publish package?
I tried this in my .csproj:
<ItemGroup>
<Folder Include="ClientApp\" />
<Folder Include="Migrations\" />
<Views Include="Email\**" />
</ItemGroup>
It is not working.
Edit
I tried to set this:
But still not working.
So my problem was the Email folder was not copied to the published package. Add this code to .csproj:
<ItemGroup>
<None Include="Email\EmailConfirmation.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Include="Email\ResetPassword.cshtml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Or you can set it in the project explorer like this:
For some reason, the build action has to be set to None, because .NetCore will treat Content differently and the folder will not be copied to the publishing package (maybe the content will be merge into .dll, I do not know).
Could you try this ?
string workingDirectory = Environment.CurrentDirectory;
string projectDirectory=Directory.GetParent(workingDirectory).Parent.Parent.FullName;
It will give you the Path to the project directory. Then you complete with the subdirectory which leads to your file.

How to specify the path to a VS extension DLL from an MSBuild <UsingTask> tag

I'm developing a Visual Studio extension. The extension's associated VS project template includes a call to a custom task in the extension's DLL:
<UsingTask TaskName="MyTask" AssemblyFile="path to MyDLL.dll" />
The extension will be installed in the usual place, through use of the VSIX installer.
My question is: Is there a good MSBuild property or macro that I can use to construct the path to the extension's DLL (i.e., MyDLL.dll)? I'm aware of $(DevEnvDir) and could extend that path when using the project and extension in Visual Studio 2015 (append \VendorName\ProductName\Version), but that doesn't seem to work in VS 2017, where the appended path uses a mangled name that can't be predicted ahead of time (or can it?). There's also the issue that the project/extension should work in the VS experimental instance, which does not appear to reflect $(DevEnvDir).
Is there any good way to do this with MSBuild properties, or will I need to look at alternatives like environment variables or the registry?
Is there any good way to do this with MSBuild properties, or will I need to look at alternatives like environment variables or the registry?
You can use environment variables or the registry to achieve it.
environment variables
you could use environment variables like this:
<UsingTask TaskName="MyTask" AssemblyFile="$(yourenvironmentvariablesname)MyDLL.dll" />
For more information, please refer to:
https://learn.microsoft.com/en-us/visualstudio/msbuild/how-to-use-environment-variables-in-a-build
registry
You could use registry like this:
<UsingTask TaskName="MyTask" AssemblyFile="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework#DbgManagedDebugger)MyDLL.dll" />
Note: please change related registry path as you want.
https://blogs.msdn.microsoft.com/msbuild/2007/05/04/new-registry-syntax-in-msbuild-v3-5/
The solution that made sense for me was to create a wizard and to set the path to the extension install location in the replacements dictionary and use the replacement in the template with the UsingTask.
public class ProjectLocationWizard : IWizard
{
public void BeforeOpeningFile(ProjectItem projectItem)
{
}
public void ProjectFinishedGenerating(Project project)
{
}
public void ProjectItemFinishedGenerating(ProjectItem projectItem)
{
}
public void RunFinished()
{
}
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
var wizardDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
replacementsDictionary.Add("$installlocation$", wizardDirectory);
}
public bool ShouldAddProjectItem(string filePath)
{
return true;
}
}
<UsingTask AssemblyFile="$installlocation$\MyTask.dll" TaskName="MyTask" />
Although the wizard docs say to sign the wizard assembly, I did not, and it works fine without.

in asp.net 5 is it possible to store and read custom files in approot instead of wwwroot?

when you deploy an asp.net5/mvc6 app there is the wwwroot folder where web assets like css, js, images belong, and there is approot folder where packages and source code belong.
It seems that classes in the Microsoft.Framework.Configuration namespace for example must be able to read files from below approot since that is where config.json files would live.
What I want to know is, is it possible to store and read custom files of my own in approot? and if so how?
For example I'm not using Entity Framework so I need a place to put sql install and upgrade scripts and would prefer not to put them beneath wwwroot. I also have custom configuration files for things like navigation sitemap that I would rather not put below wwwroot if it is possible to put them elsewhere such as approot.
I know I can access files below wwwroot using IHostingEnvironment env.MapPath("~/somefileinwwwrootfoilder.json")
Is there a similar way to access files under approot?
The accepted answer is correct, but since a ASP.NET Core 1.0 release a few things have changed so I thought I'd write a new clear things up a bit.
What I did was create a folder in my project called AppData. You can call it anything you like.
Note: it's not in wwwroot because we want to keep this data private.
Next, you can use IHostingEnvironment to get access to the folder path. This interface can be injected as a dependency into some kind of helper service and what you end up with is something like this:
public class AppDataHelper
{
private readonly IHostingEnvironment _hostingEnvironment;
private const string _appDataFolder = "AppData";
public AppDataHelper(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public async Task<string> ReadAllTextAsync(string relativePath)
{
var path = Path.Combine(_hostingEnvironment.ContentRootPath, _appDataFolder, relativePath);
using (var reader = File.OpenText(path))
{
return await reader.ReadToEndAsync();
}
}
}
Additionally, to get things to deploy correctly I had to add the AppData folder to the publishOptions include list in project.json.
As mentioned in the comments, to deploy AppData folder correctly in ASP.NET MVC Core 2 (using *.csproj file, instead of project.json), syntax is as follows:
<ItemGroup>
<None Include="AppData\*" CopyToPublishDirectory="PreserveNewest" />
</ItemGroup>
Yes, it is possible. Just get the path to your app folder and the pass it to configuration or whoever else needs it:
public class Startup
{
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var wwwrootRoot = env.WebRootPath;
var appRoot = appEnv.ApplicationBasePath;

MSBuild exec task without blocking

Would anyone happen to know a trick that will keep this MSBuild task from blocking? I really just want the explorer to open and the build script to keep on going. Currently it blocks at the Exec task until the explorer window is closed.
<Target Name="OpenExplorer">
<Exec Command='explorer.exe "$(DestinationDir)"' IgnoreExitCode="true" />
</Target>
Thanks!
Edit: I was hoping to avoid creating a custom task for this. Perhaps some command line magic exists that could be placed inline for the Command?
Here is an easy way to execute processes asynchronously by only using msbuild and inline tasks. This is only for MSBuild V4.0 and up (God bless the MSBuild guys for adding this feature!). You don't need the any external extension packs.
Effectively, we are taking the code suggested above and putting it in an inline task. Feel free to fiddle with the code to suite your needs.
The point of this solution is that it lets you achieve the result without the headache of creating a separate dll for the custom task. The implementation in the extension pack is definitely more solid but this works as a quick 'n dirty way of solving this issue. You can also customise exactly how you want it to run.
<!--Launch a Process in Parallel-->
<UsingTask TaskName="ExecAsync" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<!--The file path is the full path to the executable file to run-->
<FilePath ParameterType="System.String" Required="true" />
<!--The arguments should contain all the command line arguments that need to be sent to the application-->
<Arguments ParameterType="System.String" Required="true" />
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<![CDATA[
string name = System.IO.Path.GetFileNameWithoutExtension(FilePath);
Log.LogMessage("Starting {0}...", name);
System.Diagnostics.ProcessStartInfo processStartInfo = new System.Diagnostics.ProcessStartInfo(FilePath, Arguments);
processStartInfo.UseShellExecute = true;
System.Diagnostics.Process.Start(processStartInfo);
Log.LogMessage("Finished running process {0}.", name);
]]>
</Code>
</Task>
</UsingTask>
You can then call the ExecAsync task from within your normal script in the following fashion. Note: My script below is used to gather code coverage for an application.
<!--Start listening for coverage data:-->
<Message Text="Starting to listen for coverage..."/>
<ExecAsync FilePath='$(VSPerfCmdExePath)' Arguments='/start:coverage /output:"$(CoverageFilePath)"' ContinueOnError='true'/>
<Message Text="Listening for coverage..."/>
<!--Start App with Coverage:-->
<Message Text="Starting App..."/>
<Exec Command='"$(AppCoverageLatestExePath)"' ContinueOnError='true' WorkingDirectory='$(AppCoverageLatestFolder)'/>
<Message Text="App shut down."/>
<!--Stop gathering coverage results:-->
<Message Text="Stopping listening for coverage..."/>
<Exec Command='"$(VSPerfCmdExePath)" /shutdown'/>
<Message Text="Coverage shut down."/>
Here is a description of what is happening there:
First I kick the performance tool off so that it listens for
coverage. I do this using our AsyncExec task because normally the
tool blocks when running in MSBuild (see here).
Next, we start our program that we want to gather coverage on.
Then we shut down the coverage tool once we are done.
You can't do it with the native Exec. But you can write your own that fires asynchronously, as in this example:
public class AsyncExec : Exec {
protected override int ExecuteTool(string pathToTool,
string responseFileCommands,
string commandLineCommands) {
Process process = new Process();
process.StartInfo = GetProcessStartInfo(pathToTool, commandLineCommands);
process.Start();
return 0;
}
protected virtual ProcessStartInfo GetProcessStartInfo(string executable,
string arguments) {
if (arguments.Length > 0x7d00) {
this.Log.LogWarningWithCodeFromResources("ToolTask.CommandTooLong", new object[] { base.GetType().Name });
}
ProcessStartInfo startInfo = new ProcessStartInfo(executable, arguments);
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = true;
string workingDirectory = this.GetWorkingDirectory();
if (workingDirectory != null) {
startInfo.WorkingDirectory = workingDirectory;
}
StringDictionary environmentOverride = this.EnvironmentOverride;
if (environmentOverride != null) {
foreach (DictionaryEntry entry in environmentOverride) {
startInfo.EnvironmentVariables.Remove(entry.Key.ToString());
startInfo.EnvironmentVariables.Add(entry.Key.ToString(), entry.Value.ToString());
}
}
return startInfo;
}
}
which you can then run with:
<AsyncExec Command="..." />
Answered at Starting a program with MSBuild/Web Deployment Project and not waiting for it
<Exec Command="..." Timeout="2000"></Exec>
Try AsyncExec in MSBuild Extension Pack.
The Command in Exec is placed in a batch file and executed. So you can use the "start" keyword in the Command just the same as in a console window. That will do the trick.

Automatically increment "minimum required version" in a ClickOnce deployment?

Is there a way to automatically increment the "minimum required version" fields in a ClickOnce deployment to always equal the current build number? Basically, I always want my deployment to be automatically updated at launch.
I suspect I'm going to need a some pre-/post-build events, but I hope there's an easier way.
I may be a little late with answering this one but I found it difficult to find the solution on google but eventually figured it out so thought I would share.
With MSBuild version 4 (VS2010 and VS2012) this can be achieved by inserting the following target:
<Target Name="AutoSetMinimumRequiredVersion" BeforeTargets="GenerateDeploymentManifest">
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="MinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
<FormatVersion Version="$(ApplicationVersion)" Revision="$(ApplicationRevision)">
<Output PropertyName="_DeploymentBuiltMinimumRequiredVersion" TaskParameter="OutputVersion" />
</FormatVersion>
</Target>
The $(ApplicationVersion) is the same setting that you can set manually in the project's Publish window in the VS IDE, with the revision part set to an asterisk. The $(ApplicationRevision) is the actual revision being used for the published version. The FormatVersion task is a built-in MSBuild task that formats the two into a single full version number.
This will set the 'Minimum Required Version' to be the same as the 'Publish Version' therefore ensuring that the new deployment will always be installed by users, ie no option to Skip the update.
Of course, if you don't want to set the minimum required version to the publish version and want to use a different source property then it is straight-forward to amend the target, but the principle is the same.
I ended up actually rolling an AddIn to VS that synchronizes all the version numbers, and then builds and publishes with a single click. It was pretty easy.
Public Sub Publish()
Try
Dim startProjName As String = Nothing
Dim targetProj As Project = Nothing
Dim soln As Solution2 = TryCast(Me._applicationObject.DTE.Solution, Solution2)
If soln IsNot Nothing Then
For Each prop As [Property] In soln.Properties
If prop.Name = "StartupProject" Then
startProjName = prop.Value.ToString()
Exit For
End If
Next
If startProjName IsNot Nothing Then
For Each proj As Project In soln.Projects
If proj.Name = startProjName Then
targetProj = proj
Exit For
End If
Next
If targetProj IsNot Nothing Then
Dim currAssemVersionString As String = targetProj.Properties.Item("AssemblyVersion").Value.ToString
Dim currAssemVer As New Version(currAssemVersionString)
Dim newAssemVer As New Version(currAssemVer.Major, currAssemVer.Minor, currAssemVer.Build, currAssemVer.Revision + 1)
targetProj.Properties.Item("AssemblyVersion").Value = newAssemVer.ToString()
targetProj.Properties.Item("AssemblyFileVersion").Value = newAssemVer.ToString()
Dim publishProps As Properties = TryCast(targetProj.Properties.Item("Publish").Value, Properties)
Dim shouldPublish As Boolean = False
If publishProps IsNot Nothing Then
shouldPublish = CBool(publishProps.Item("Install").Value)
If shouldPublish Then
targetProj.Properties.Item("GenerateManifests").Value = "true"
publishProps.Item("ApplicationVersion").Value = newAssemVer.ToString()
publishProps.Item("MinimumRequiredVersion").Value = newAssemVer.ToString()
publishProps.Item("ApplicationRevision").Value = newAssemVer.Revision.ToString()
End If
End If
targetProj.Save()
Dim build As SolutionBuild2 = TryCast(soln.SolutionBuild, SolutionBuild2)
If build IsNot Nothing Then
build.Clean(True)
build.Build(True)
If shouldPublish Then
If build.LastBuildInfo = 0 Then
build.Publish(True)
End If
End If
End If
End If
End If
End If
Catch ex As Exception
MsgBox(ex.ToString)
End Try
End Sub
Out of the box, I don't belive there is a way. It's not too much effort to spin your own however.
The approach I use is as follows:
1) create a Version.Properties file
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Util-VersionMajor>1</Util-VersionMajor>
<Util-VersionMinor>11</Util-VersionMinor>
<Util-VersionBuild>25</Util-VersionBuild>
<Util-VersionRevision>0</Util-VersionRevision>
<Util-VersionDots>$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)</Util-VersionDots>
<Util-VersionUnders>$(Util-VersionMajor)_$(Util-VersionMinor)_$(Util-VersionBuild)_$(Util-VersionRevision)</Util-VersionUnders>
<MinimumRequiredVersion>$(Util-VersionDots)</MinimumRequiredVersion>
<ApplicationVersion>$(Util-VersionDots)</ApplicationVersion>
<ApplicationRevision>$(Util-VersionRevision)</ApplicationRevision>
</PropertyGroup>
</Project>
2) Import the Version.Properties file into your project files
3) Create a task to increment the version on Build. Here's mine
<Target Name="IncrementVersion" DependsOnTargets="Build" Condition="'$(BuildingInsideVisualStudio)'==''">
<ItemGroup>
<Util-VersionProjectFileItem Include="$(Util-VersionProjectFile)" />
</ItemGroup>
<PropertyGroup>
<Util-VersionProjectFileFullPath>#(Util-VersionProjectFileItem->'%(FullPath)')</Util-VersionProjectFileFullPath>
</PropertyGroup>
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-VersionProjectFileFullPath)"" Outputs="" />
<Version Major="$(Util-VersionMajor)" Minor="$(Util-VersionMinor)" Build="$(Util-VersionBuild)" Revision="$(Util-VersionRevision)" RevisionType="None" BuildType="Increment">
<Output TaskParameter="Major" PropertyName="Util-VersionMajor" />
<Output TaskParameter="Minor" PropertyName="Util-VersionMinor" />
<Output TaskParameter="Build" PropertyName="Util-VersionBuild" />
<Output TaskParameter="Revision" PropertyName="Util-VersionRevision" />
</Version>
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMajor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMajor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionMinor" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionMinor)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionBuild" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionBuild)" />
<XmlUpdate Prefix="msb" Namespace="http://schemas.microsoft.com/developer/msbuild/2003" XPath="/msb:Project/msb:PropertyGroup/msb:Util-VersionRevision" XmlFileName="$(Util-VersionProjectFile)" Value="$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-VersionProjectFileFullPath)"" />
<Exec Command=""$(TfCommand)" get /overwrite /force /noprompt "$(Util-AssemblyInfoFile)"" Outputs="" />
<Exec Command=""$(TfCommand)" checkout /lock:checkout "$(Util-AssemblyInfoFile)"" Outputs="" />
<AssemblyInfo CodeLanguage="CS" OutputFile="$(Util-AssemblyInfoFile)" AssemblyConfiguration="$(Configuration)" AssemblyVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" AssemblyFileVersion="$(Util-VersionMajor).$(Util-VersionMinor).$(Util-VersionBuild).$(Util-VersionRevision)" />
<Exec Command=""$(TfCommand)" checkin /override:AutoBuildIncrement /comment:***NO_CI*** "$(Util-AssemblyInfoFile)"" />
</Target>
Some additional clickonce tricks here http://weblogs.asp.net/sweinstein/archive/2008/08/24/top-5-secrets-of-net-desktop-deployment-wizards.aspx
Here's how I handled this one. First I created a custom task that wraps string replacement:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
namespace SynchBuild
{
public class RemoveAsterisk : Task
{
private string myVersion;
[Required]
public string Version
{
set{myVersion = value;}
}
[Output]
public string ReturnValue
{
get { return myVersion.Replace("*", ""); }
}
public override bool Execute()
{
return true;
}
}
}
So that gets built into SynchBuild.dll which you see referenced in the UsingTask below. Now I tried just overwritting the MinimumRequiredVersion property, but it didn't seem to get picked up, so I just overwrote the GenerateApplicationManifest target by adding the following lines to the end of my csproj file:
<UsingTask AssemblyFile="$(MSBuildExtensionsPath)\WegmansBuildTasks\SynchBuild.dll" TaskName="SynchBuild.RemoveAsterisk" />
<Target Name="GenerateDeploymentManifest" DependsOnTargets="GenerateApplicationManifest" Inputs="
$(MSBuildAllProjects);
#(ApplicationManifest)
" Outputs="#(DeployManifest)">
<RemoveAsterisk Version="$(ApplicationVersion)$(ApplicationRevision)">
<Output TaskParameter="ReturnValue" PropertyName="MinimumRequiredVersion" />
</RemoveAsterisk>
<GenerateDeploymentManifest MinimumRequiredVersion="$(MinimumRequiredVersion)" AssemblyName="$(_DeploymentDeployManifestIdentity)" AssemblyVersion="$(_DeploymentManifestVersion)" CreateDesktopShortcut="$(CreateDesktopShortcut)" DeploymentUrl="$(_DeploymentFormattedDeploymentUrl)" Description="$(Description)" DisallowUrlActivation="$(DisallowUrlActivation)" EntryPoint="#(_DeploymentResolvedDeploymentManifestEntryPoint)" ErrorReportUrl="$(_DeploymentFormattedErrorReportUrl)" Install="$(Install)" MapFileExtensions="$(MapFileExtensions)" MaxTargetPath="$(MaxTargetPath)" OutputManifest="#(DeployManifest)" Platform="$(PlatformTarget)" Product="$(ProductName)" Publisher="$(PublisherName)" SuiteName="$(SuiteName)" SupportUrl="$(_DeploymentFormattedSupportUrl)" TargetCulture="$(TargetCulture)" TargetFrameworkVersion="$(TargetFrameworkVersion)" TrustUrlParameters="$(TrustUrlParameters)" UpdateEnabled="$(UpdateEnabled)" UpdateInterval="$(_DeploymentBuiltUpdateInterval)" UpdateMode="$(UpdateMode)" UpdateUnit="$(_DeploymentBuiltUpdateIntervalUnits)" Condition="'$(GenerateClickOnceManifests)'=='true'">
<Output TaskParameter="OutputManifest" ItemName="FileWrites" />
</GenerateDeploymentManifest>
</Target>
The end result is we take the app version and revision, combine them, remove the asterisk, then set the minimum required version. I have the auto increment app version in my publish properties set so that's how incrementing takes place, then I'm just setting the minimumrequiredversion to always match.I don't use team build, this is just designed so that a developer using visual studio can make all clickonce deployments required. Hope this helps.
If you are publishing your ClickOnce application from Visual Studio then just install the AutoUpdateProjectsMinimumRequiredClickOnceVersion NuGet Package in your project and you're good to go.
If you are publishing from a build server or other script, then you can use the Set-ProjectFilesClickOnceVersion PowerShell script. My blog describes in more detail how to setup your build server to accommodate publishing ClickOnce applications.
Are you looking for Application Updates?
Right clicking on the project in the Solution Explorer and then clicking Publish... is the wrong way to get Application Updates. You have to right-click your project and the click Properties, then click the Publish tab. Click the Updates... button and then check the "The application should check for updates" check box. There you can also specify a minimum required version for the application. (I haven't used that functionality but the Updates functionality is the core reason I use ClickOnce and it works great.)