Bob King
Bob King

Reputation: 25856

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.

Upvotes: 19

Views: 10262

Answers (6)

deadlydog
deadlydog

Reputation: 24394

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 for .NET Framework projects. For .NET Core projects, there's an additional step required. See the project docs for more info.

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.

Upvotes: 0

Kev
Kev

Reputation: 1892

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.

Upvotes: 25

Justin Holbrook
Justin Holbrook

Reputation:

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="&#xD;&#xA;            $(MSBuildAllProjects);&#xD;&#xA;            @(ApplicationManifest)&#xD;&#xA;            " 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.

Upvotes: 0

Bob King
Bob King

Reputation: 25856

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

Upvotes: 7

Scott Weinstein
Scott Weinstein

Reputation: 19117

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="&quot;$(TfCommand)&quot; get /overwrite /force /noprompt &quot;$(Util-VersionProjectFileFullPath)&quot;" Outputs="" />
    <Exec Command="&quot;$(TfCommand)&quot; checkout /lock:checkout &quot;$(Util-VersionProjectFileFullPath)&quot;" 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="&quot;$(TfCommand)&quot; checkin /override:AutoBuildIncrement /comment:***NO_CI***  &quot;$(Util-VersionProjectFileFullPath)&quot;" />
    <Exec Command="&quot;$(TfCommand)&quot; get /overwrite /force /noprompt &quot;$(Util-AssemblyInfoFile)&quot;" Outputs="" />
    <Exec Command="&quot;$(TfCommand)&quot; checkout /lock:checkout &quot;$(Util-AssemblyInfoFile)&quot;" 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="&quot;$(TfCommand)&quot; checkin /override:AutoBuildIncrement /comment:***NO_CI***  &quot;$(Util-AssemblyInfoFile)&quot;" />
  </Target>

Some additional clickonce tricks here http://weblogs.asp.net/sweinstein/archive/2008/08/24/top-5-secrets-of-net-desktop-deployment-wizards.aspx

Upvotes: 5

Jared Updike
Jared Updike

Reputation: 7277

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.)

Upvotes: -1

Related Questions