Jump to content

Create a Jenkins pipeline to deploy Desktop Apps as MSIX - Part 2: Packaging a Visual Studio solutio


Recommended Posts

Guest luisdem
Posted

 

Parts:

 

  1. Setup the Jenkins environment: install Jenkins and the required tools.
  2. Packaging a Visual Studio solution: for applications that use Visual Studio IDE, like Windows Forms and WPF.
  3. Packaging a solution developed outside Visual Studio: for applications developed outside VS, i.e., in others IDEs like Eclipse or Visual Studio Code, for Java GUI application.
  4. Packaging using the VB6RegistryTool: despite of the name, the tool can be used by any technology.

[HEADING=1]1. Visual Studio Solution[/HEADING]

 

In this section it will be demonstrated how to create the Visual Studio solution with a Windows Forms application and the Windows Application Packaging Project project used to generate the MSIX file.

 

In case you prefer, you can skip this step, since the solution is already available on jenkins_msix repo.

 

[HEADING=2]Create the Windows Form application[/HEADING]

 

On Visual Studio 2019, select Create a new project, select the Windows Forms app project template and click Next:

 

largevv2px999.png.d887061cc07e987230387c764df2e84e.png

 

 

 

Provide the project name WinForms.App and click Next:

 

 

 

largevv2px999.png.4f2e3b1532e8d41cdbd9d4de26f9f35c.png

 

 

 

I am choosing the .NET 6.0 framework, but feel free to use any other version. Click on Create:

 

 

 

 

 

largevv2px999.png.f06db25fd1479301943cb9622dd1eb9a.png

 

 

 

 

 

 

 

This is the WinForms project structure. I am using only a PictureBox and a button, but it could be an empty project, as the idea is just to show how to package a WinForm application:

 

 

 

largevv2px999.thumb.png.585467b5de7e55ed62544732cc84896f.png

 

 

 

 

 

 

 

Follows the code-behind:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

namespace WinForms.App
{
   public partial class Form1 : Form
   {
       public Form1()
       {
           InitializeComponent();
       }

       private void button1_Click(object sender, EventArgs e)
       {
           Close();
       }
   }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

So far, the WinForm project is a .NET 6.0 application that depends of the .NET runtime available in the client machine. To remove this dependency, it is possible to publish the project as self-contained where the .NET runtime and runtime libraries are bundled together with the application and third-party assemblies.

 

Add the following lines to the SelfContained and RuntimeIdentifier to the WinForms.App project file:

 

 

 

 

 

 

 

 

 

 

 

 

 

<PropertyGroup>
   <OutputType>WinExe</OutputType>
   <TargetFramework>net6.0-windows</TargetFramework>
   <Nullable>enable</Nullable>
   <UseWindowsForms>true</UseWindowsForms>
   <ImplicitUsings>enable</ImplicitUsings>
   
   <SelfContained>true</SelfContained>
   <RuntimeIdentifier>win-x64</RuntimeIdentifier>
   
 </PropertyGroup>

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Build the application to ensure that no errors appear.

 

[HEADING=2]Create the the Windows Application Packaging project[/HEADING]

 

The next step is adding the Windows Application Packaging Project to the solution.

 

Right-click on the solution, select Add and click on New Project...:

 

 

 

largevv2px999.png.facaec0f9afd376e9e0d2af5250177dd.png

 

 

 

Select the Windows Application Packaging Project and click Next:

 

 

 

largevv2px999.png.6b03a96eda78bb0cf76fe905d204a68b.png

 

 

 

Provide the project name WinForms.Packaging and click Create:

 

 

 

largevv2px999.png.f431992c94959d32ca454b3f50277649.png

 

 

 

 

 

The next step is to provide the target and minimum platform version supported by your application. I am selecting both versions to 19041 to keep it simple, as I need to install the same SDKs on my Jenkins server environment:

 

 

 

largevv2px999.png.ccd9fc9db6aab827e43d11fcb0886004.png

 

 

 

In the WinForms.Packaging project, right-click on Dependencies node and click Add Project Reference...:

 

 

 

largevv2px999.png.624c7ba46acea746e17b544aad97a537.png

 

 

 

Select the Windows Application Packaging Project and click Next:

 

 

 

largevv2px999.png.a19bc5bf8a8d05edc2dc7953cf311140.png

 

 

 

Observe that the WinForms.App project was added to the WinForms.Packaging project:

 

 

 

largevv2px999.png.40d669d52530dbc771f6689bac4ba067.png

 

 

 

The Windows Application Packaging Project don't accept the target Any CPU. Therefore, we need to change the processor target to x86 or x64 for both projects.

 

 

 

Open Configuration Manager, change the Active solution platform to x86, change the WinForms.App and WinForms.Packaging projects to x86:

 

 

 

largevv2px999.png.d5b11c12ca652c79fd259e3ba981a7c0.png

 

 

 

Build the application to ensure that no errors appear.

 

[HEADING=2]Build the application using MSBuild command line[/HEADING]

 

Before creating a Jenkins pipeline, let's make sure that the MSBuild command line that will be used to build our application is working.

 

Open the Package Manager Console (you can press CTRL+Q and type package manager console):

 

 

 

 

 

largevv2px999.png.846a9965e92498c29b149959eb1b2b71.png

 

 

 

Before running the following command, make sure to provide the MSBuild.exe PATH available in your environment:

 

 

 

 

 

 

 

 

 

 

 

 

 

[iCODE]&"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe" /p:AppxBundlePlatforms=X86 /p:AppxBundle=Never /p:UapAppxPackageBuildMode=Sideloading /p:AppxPackageSigningEnabled=false[/iCODE]

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

largevv2px999.png.34c895770dea8f10d536a0476f6aa5ed.png

 

 

 

 

 

In my case, the msix package file was generated on:

 

 

 

C:\github\msixdemos\01_VisualStudio\WinForms.App\WinForms.Packaging\AppPackages\WinForms.Packaging_1.0.0.0_AnyCPU_Debug_Test

 

 

 

largevv2px999.png.47f396b84fc29580403fd4be2c933d57.png

 

 

 

In the next section, it will demonstrate how to build a Jenkins Pipeline for this project.

 

[HEADING=1]2. Jenkins Pipeline[/HEADING]

 

Pipeline is a series of tasks required to build, test, and deploy an application.

 

[HEADING=2]Create a new job[/HEADING]

 

In the Jenkins Dashboard, click on the Create a job option:

 

 

 

largevv2px999.png.370233a45e1eb78427974e5eee65ec7a.png

 

 

 

Provide a name for the job, check the Pipeline type of job and click on OK to proceed.

 

 

 

largevv2px999.png.981f64ac9475fcafc4d359ed0b74b9ff.png

 

 

 

 

 

In the pipeline configuration page, check the GitHub Project to specify that this is a GitHub project and provide a GitHub URL:

 

 

 

 

 

largevv2px999.png.e5cc269a345d8e5e807905ad6a683343.png

 

 

 

Scroll down under the Pipeline section and change the definition to Pipeline script from SCM

 

 

 

largevv2px999.png.3f982ab54fbd4222c32e167b4cf4c01a.png

 

 

 

Provide the Repository URL as well. Because this is a public project, we can skip the credentials:

 

 

 

largevv2px999.png.980b5b0d6be34af01824921ce816e1a8.png

 

 

 

 

 

Scroll-down to the Branches to build section, change the branch name to */main, the Jenkins script path to Jenkinsfile01 and click on Save:

 

 

 

largevv2px999.png.36a569c787bb0b252bb1bb558cdae268.png

 

 

 

Those actions were needed as we want to use the Jenkins pipeline file available in the main branch of the following repo:

 

 

 

largevv2px999.thumb.png.a28aca022c1a45ec43d432651cd4bcfa.png

 

 

 

[HEADING=2]Jenkins Pipeline File[/HEADING]

 

In the previous section it was demonstrated how to setup the Jenkins pipeline to use a Jenkins script file available on our GitHub repository.

 

 

 

Follows the Jenkinsfile01 content:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

pipeline {
 
 agent any

 environment {
   MSBUILD = "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Msbuild\\Current\\Bin\\MSBuild.exe"
   CONFIG = 'Release'
   PLATFORM = 'x86'
 }
 
 stages {
   
   stage('Update manifest version') {
     steps {
         powershell '''
           $manifest = "01_VisualStudio\\WinForms.App\\WinForms.Packaging\\Package.appxmanifest"     
           [xml]$xmlDoc = Get-Content $manifest
           $version = $xmlDoc.Package.Identity.Version
           $trimmedVersion = $version -replace '.[0-9]+$', '.'
           $xmlDoc.Package.Identity.Version = $trimmedVersion + ${env:BUILD_NUMBER}
           $xmlDoc.Save($manifest)
         '''
     }
   }
   
   stage('Build') {
     steps {
       bat "dotnet restore 01_VisualStudio\\WinForms.App\\WinForms.App\\WinForms.App.csproj"

       bat "\"${MSBUILD}\" 01_VisualStudio\\WinForms.App\\WinForms.app.sln /p:Configuration=${env.CONFIG} /p:AppxBundlePlatforms=${env.PLATFORM}  /p:AppxBundle=Never /p:UapAppxPackageBuildMode=Sideloading  /p:AppxPackageSigningEnabled=false"
       
     }
     post{
         always {
          archiveArtifacts artifacts: '**/*.msix', followSymlinks: false
         }
     }
   }
 }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

The pipeline directive is the complete script from beginning to end.

 

 

 

The agent directive instructs Jenkins to allocate an executor and workspace for the entire Pipeline. In our case, we are justing saying it can run on any agent. For example, it could be specified that it could run in a Docker container or run on a specific node.

 

 

 

The environment directive specifies a sequence of key-value pairs which will be defined as environment variables for all steps, or stage-specific steps, depending on where the environment directive is located within the Pipeline.

 

 

 

In our case, it is defined the variables MSBUILD that contains the MSBUILD path, the CONFIG with the value Release and PLATFORM with the value x86. Those variables will be used in the command line used to build our application.

 

 

 

The stages block contains on or more stage block, and each stage is going to have one or more steps. In our case, we have only one stage named Build, that has two steps to restore the dotnet WinForms.App project and to build the solution.

 

 

 

The post section defines the additional step needed to keep the msix file artifact available in our build, as workspace is a temporary directory.

 

 

 

 

You can find more details about the Jenkins pipeline syntax in the post Getting started with Pipeline.

 

 

 

 

In addition, there is a great post about how to Creating a Jenkins pipeline for a .NET Core application.

 

 

 

Switch back to Jenkins, click on Dashboard and click on the Visual Studio Solution pipeline:

 

 

 

largevv2px999.png.11a7814bfac1d24d671bf1197f67afac.png

 

 

 

Click on Build Now to start the build:

 

 

 

largevv2px999.png.6272407cc547f18401c4e51a5a631e46.png

 

 

 

The job starts by checking out the source code to next restore and build our solution as defined in the Jenkinsfile01.

 

 

 

largevv2px999.png.58d336b2e848a08c3e916cf73e11cc1b.png

 

 

 

After the build is done, the build icon will be green and the msix artifact will be available:

 

 

 

largevv2px999.png.052a731aea34e3391a3a6dcb6fb18ba1.png

 

 

 

The next post demonstrate how to package an application that not uses the Visual Studio IDE, but that uses the Windows Application Packaging Project to generate the MSIX file.

 

Continue reading...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...