Nexus Repository Manager OSS as Nuget server

Introduction

In the past years I heard a lot of bad excuses when it came to NuGet package management. Mentioning Nexus in many .Net shops I had a chance to work in, put a strange ‘fear feeling’ in the air. None of those two chaps are to be in fear off and, surprise surprise, they work exceptionally well together. In this post I would like to tell you how to set up a NuGet proxy repository and a local, private, NuGet repository on Nexus Repository Manager OSS. I will guide you through the installation of Nexus Repository Manager OSS and all of the necessary setup tasks in order to create your own NuGet server that will proxy the calls to NuGet.org and store your own packages. I will mentions also some details about the configuration and maintenance and show you how to set up your tooling in order to work with it. It is going to be a pretty long post, so if interested, keep on reading. I hope that I can convince you to give a change to Nexus and get the best out NuGet.

Prerequisites

In order ton install Nexus you need to make sure that a couple of things are in place.
First thing, make sure JRE is installed. In the command prompt run java -version. If command executed successfully you are ready to go. If not, download the right version for your OS at http://www.java.com/en/download/manual.jsp. Still make sure that you are running at least JRE version 8u31+, as it is strongly recommended by SonaType.

Operating system requirements are specified at the following link https://support.sonatype.com/hc/en-us/articles/213464208-Sonatype-Nexus-System-Requirements.

Download the latest version of SonaType Nexus at http://www.sonatype.org/nexus/go/. At the moment of writing the current version is 2.12.0.

Installing Nexus

In order to install Nexus Repository Manager OSS, you need to extract the content of the downloaded file inside a directory. In my case this will be D:\Nexus. Once done get inside a folder called nexus-2.x.x-xx\bin and execute the following commands, nexus install, and once done, nexus start. You will see something similar to this:

D:\Nexus\nexus-2.12.0-01\bin>nexus install
wrapper  | nexus installed.

D:\Nexus\nexus-2.12.0-01\bin>nexus start
wrapper  | Starting the nexus service...
wrapper  | Waiting to start...
wrapper  | Waiting to start...
wrapper  | Waiting to start...
wrapper  | Waiting to start...
wrapper  | Waiting to start...
wrapper  | nexus started.

D:\Nexus\nexus-2.12.0-01\bin>

Now let’s get on the default address http://localhost:8081/nexus.

If all went well you should see the following:

welcome_screen

Login by following the link in the up-right corner. Default administrative account is admin with a password equal to admin123. Once logged in you will see in the menu on the left side additional options. We are now ready to start configuring our repositories.

I also advise you to check the list of recommended post installation configuration steps in the Post-Install Checklist.

Nuget proxy repository

By default, Nexus Repository Manager OSS ships pre-configured with several repositories. If you intend to used it only as a Nuget repository/proxy you can remove all of them.

repositories

This happens to be my case, so I will remove all of the pre-configured repositories. Once you delete all of them, you should see a message saying, No repositories defined.

I will now add a repository that will proxy calls towards the Nuget.org public repository. Click on add a choose Proxy repository.

add_proxy_repo

At the bottom of the screen you will be asked to fill in all the necessary details. Let’s analyse the important ones.

Repository ID: I am going to choose a vary concise name here, as this will be a part of our feed url. I will set it to proxy.

Repository Name: This is just a name that will be assigned to this repository and it will only be visible from the Nexus repositories management interface. I will set it to Nuget.org proxy.

Provider: It needs to be set to NuGet.

Remote Storage Location: Here we will set the link to the NuGet.org feed. Currently it is https://www.nuget.org/api/v2/

This are all of the necessary parameters. Once filled in, you can click on Save.

new_proxy_repo

If you now select the just created repository and then open the NuGet tab, you will find the link to your feed. In my case it is http://localhost:8081/nexus/service/local/nuget/proxy/.
First part of the link is obviously wrong. You will not be able to use this feed from any other machine than the server itself. It is displayed as local host as that is the url that we used to access the administration interface. It needs to be http://yournexusservername:8081/nexus/service/local/nuget/proxy/ where yournexusservername is the name of the machine. We will see later how to influence this url.

Nuget private repository

In case you have your own, private NuGet packages, that you would like to publish and retrieve, Nexus Repository Manager OSS can help you with that too. You need to create a hosted repository. For this repository you need to set the same settings as for the proxy, except the Remote Storage Location. I’ve set Repository ID to ‘private’ and Repository Name to ‘Nuget local repository’.
Once created, under the NuGet tab, you will find the personal API key that you need to use via NuGet.exe in order to push a package to this repository.

api_key

One last step is necessary before you get to be able to upload your packages. We need to enable the authentication and to do so, you will need to set NuGet API-Key Realm.

security_settings_realms

Under the Server settings, make sure that NuGet API-Key Realm is under the Selected Realms, which by default is NOT.

Now you can set your Api Key and push your package.

Api keys are on per user basis generated. Each user has it’s own unique key, and if you intend to upload the packages under a specific user profile, make sure you get the right key. Log in as that user and check the above mentioned property.

Living under the same roof

Having multiple repositories and feeds can be unhandy to setup and maintain. Lucky Nexus Repository Manager OSS offers a solution that can consolidate multiple repositories under a single feed. A repository group hides multiple repositories behind a single address/feed and allows us to modify the underlying repositories, add new ones or make other changes, without the need to change the configuration in any of your projects or in the tooling. Also it gives a lot of flexibility in setting up different policies and strategies in managing your repositories.
In order to create a Repository Group, under the repository management, choose Add then Repository Group option.

new_repository_group

Same settings are mandatory as for the private repository with one addition. You need to choose which repositories will be part of this group.

Tidying up

Link to our feed as it is right now is quite verbose and hard to recall, http://yournexusservername:8081/nexus/service/local/nuget/proxy/. Instead of pointing to yournexusservername we may consider adding a CNAME into our DNS and then point it to that machine. This will also ease the maintenance later on, if we do decide to move the service to another machine. In that case it will be sufficient to re-point DNS to the new machine and no changes to the feed URL will be necessary.

Another thing that stands out is that Nexus Repository Manager OSS web server as it’s feeds do run on port 8081. If this is an option, it will be easier to run it on port 80 (or 443 in case you plan to use SSL). you can also notice that ‘nexus’ part of URL (context root) just after the port number, it also may not be necessary and removing it will shorten all of the url’s.

I registered a CNAME in my domain and called it nexus. Now by calling http://nexus.maio.local:8081/nexus I’m able to reach my server. In order to get server running on port 80 and remove that nexus suffix, we need to get to the conf folder (in my case D:\Nexus\nexus-2.12.0-01\conf) and edit the file called nexus.properties. We are interested in changing two properties under # Jetty section. First one is application-port which by default is set to 8081. You need to set this to 80. Second property we are going to change is nexus-webapp-context-path and we need to change if from /nexus to just a /. This will set the context root with no prefix and make our URL shorter.
At the end your configuration file should look like following:

...
# Jetty section
application-port=80
application-host=0.0.0.0
nexus-webapp=${bundleBasedir}/nexus
nexus-webapp-context-path=/
...

Now it will be sufficient to restart the nexus windows service in order the changes to take the effect. In case no other application is using the port 80, restart should succeed and we are now able to get to the administrative interface by just calling http://nexus.maio.local/.
Also my feed now changes to http://nexus.maio.local/service/local/nuget/proxy/ which is shorter and easier to remember.

To make sure that the URL’s are not based on the upcoming request, you will need to set the base URL in the server settings.

application_server_settings

Set the base URL accordingly to your CNAME and remove the nexus from context root. You would like also to check the Force Base URL option as then the current settings will be considered for all of the links and Nexus Repository Manager will not base any on the upcoming request URL.

Other considerations

Proxy

In case your Nexus Repository Manager is behind a proxy you will need to set the relevant settings inside the Server panel. Make sure that you set both HTTP and HTTPS settings as NuGet.org feed uses HTTPS.

http_proxy_settings

It is sufficient to indicate the proxy host, port and the Authentication credentials (Username and password).

License and Vulnerability Tracking

This is a very neat feature that is offered by Nexus Repository Manager OSS. For our proxied repository we are able to activate Repository Health Check. If active Nexus Repository Manager OSS will return actionable quality, security, and licensing information about the open source components in the repository. This will give us an overview of the mentioned metrics for all of the packages that were served by our proxy feed. To enable it, click on the analyse button and after some time you will see the results.

activate_rhc

On the SonaType blog you can find a video interview made to Marcel de Vries that talks this argument in the detail.

Maintenance

In case you are planning to use your proxy intensively you may be concerned about the disk space it may require to support all of the packages that are cached by the proxy. In that case you could opt for an out of the box task. If you open the Scheduled Tasks panel and Add a new scheduled task, there is a predefined task that will do just that.

scheduled_tasks

Create a Scheduled task of type ‘Evict Unused Proxied Items From Repository Caches’. On a given schedule it will remove all of the items for the chosen repository that are older than a number of days you set.

Support and documentation

Nexus is a well documented project. You can find more information about all of the arguments treated in this blog post and much more at http://books.sonatype.com/nexus-book/reference/index.html. Also in case you encounter a problem and can’t find an answer about, there is a support even for a free OSS version at https://support.sonatype.com/hc/en-us/categories/201980798.

Visual Studio feed

Adding a new feed in Visual Studio is an ordinary task. Select the following menu Tools > NuGet Package Manager > Package Manager Setting. Then in the Options window, select Package Sources and add a new one by clicking on the plus button. You can name the new feed to Nexus and set the source to http://nexus.maio.local/service/local/nuget/feed/. Press Update, then OK.

package_sources_options

This will allow you to access NuGet.org through Nexus Repository Manager OSS in Visual Studio.

Now in the NuGet Package Manager select the newly added Package Source and you are ready to browse and install the available packages.

nuget_package_manager

If you added a group feed, you will be able to retrive both NuGet.org and your private packages through the same feed.

Build agent package restore

Before we enable the NuGet package restore in our build task we need to make sure that we have the necessary inside our nuget.config file. If you do not have a nuget.config file, you can place it in one of the paths that are listed in NuGet documentation.
In the package sources you need to add the new feed. Consider the following example.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
	<add key="Nexus" value="http://nexus.maio.local/service/local/nuget/feed/" />
  </packageSources>
</configuration>

Now you need to check in this file and enable the package restore on your build task.

build_task

This should be now sufficient for the build agent to retrieve all of the necessary packages via Nexus Repository Manager OSS.
You can now remove the packages folder from the source code and request a clean build. In the build console then you will find references about packages being restored.

Manually uploading a package

Our custom NuGet packages can be uploaded via the Nexus Repository Manager OSS web interface. Select your repository and go to the NuPkg Upload tab.

nupkg_upload

In that tab, select Browse and choose your .nupkg file. Then click on Add package and if that is the only one you would like to upload, choose Upload Package(s) button. If all goes well you will see the upload confirmation message. If you get to browse storage tab and refresh it, you should see your package uploaded.
To be sure that we can retrieve our custom package (also via the group repository) let’s get in Visual Studio and search for it.

search_hosted_package

As you can see, we are able to find our package both via our hosted repository feed as also via the group repository. This shows all of the potential of the group repositories and the way it eases the maintenance.

I came in an situation in which I was not able to find via group repository the package I uploaded. In case you upload a package that is present (matches the name and version of the package) in your proxy repository (NuGet.org), your package will not be shown. This is however an edge case and in real life situation you should never encounter such a situation. Still I think it is worth mentioning as during a trial it can happen and I do not want you to freak out about it.

Uploading the package via TFS 2015 build

With TFS 2015 Update 1 a new build task called NuGet Publisher is made available. It can help you with uploading your package to any NuGet repository. After adding the above mentioned build task to your build definition you will be presented with the following options:

nuget_publisher

It is sufficient to define a new NuGet Server Endpoint only once. If you choose manage and then add new generic endpoint you will need to specify your feed as server URL, assign an arbitrary name to this endpoint and set you ApiKey as Password/Token Key value.

In our example name is Nexus NuGet, Server URL is http://nexus.maio.local/service/local/nuget/private/ and Password/Token Key is 626d4343-965f-30bc-858e-a322672acf54.

nexus_endpoint

Now that the endpoint is set, we need to specify a minimatch pattern that will point to our NuGet package. In the picture above you can see that I’m pointing my search for all the packages in the the staging folder, where my package is created. This will depend largely on your build definition and what you are trying to achieve.

This is sufficient to upload automatically from your build a new version of your package to our local NuGet server.

In case you are interested in building the package based on your sources and your nuspec file directly by your build definition, you can use NuGet Packager build step to achieve that. This is however out of scope for this blog post.

Uninstall Nexus

Last but not least, removing Nexus. In case you are not satisfied with Nexus you should remove it correctly. Luckily it is trivial.
As for the installation, let’s get into nexus-2.x.x-xx\bin folder. Following are the commands to execute and the relative output.

D:\Nexus\nexus-2.12.0-01\bin>nexus stop
wrapper  | Stopping the nexus service...
wrapper  | nexus stopped.

D:\Nexus\nexus-2.12.0-01\bin>nexus uninstall
wrapper  | nexus removed.

After both of these commands succeeded, you can safely remove the folder for your disk. In case you set a non default path for the repository storage, you can remove that one also.

Conclusion

That’s all folks! I hope I covered all of the arguments you may be interested in when it comes to Nexus Repository Manager OSS and NuGet. I do hope that this will encourage you in using or improve your current NuGet services setup.

Utility for quick IE settings change

Introduction

We all agree that repetitive tasks are boring and do take time. Sometimes considerable amount of time. Not all of our tasks are covered by a freeware available on internet. It is often the case we could use a small application that will make some of the tasks we encounter, simpler and quicker to perform. I will show you how I found myself in this type of situation and how did I solved my problem. It will not be the best code I wrote nor I am trying to show any advanced development technique, it is just the quickest way to get to the result.

A nice introduction tutorial for neophyte developers!

My problem

I found myself in need to test one of the web applications I am working on in different environments with different users. You could guess that I am using Windows Authentication on this application. As I need to change the user often, with a different profile, on different environment, I need to make sure that my IE will not automatically pass my own credentials to the server, instead he should ask about it. Still I need to pass my credentials to all other apps on intranet. In order to accomplish this, we need to open IE, go to Internet Options, Security tab…

Internet_Options_No_Security_Tab

…wait a moment, there is no Security tab… …damn you security administrator and your group polices! Still, I’m a local admin on my PC and these settings should be changeable. And they are, we will see later how it’s done.
In case your have no particular GP’s applied you should see the following screen in which you should be able to change the necessary settings:

Internet Options

and once there in the Local Intranet custom level, you can tweak it at your pleasure.

Security Settings - Local Internet Zone

No matter if you are able to change these settings through IE or not, this are still a lot of steps to perform frequently in order to have this settings changed. This is when I got the idea to create a small utility app that will help me with this task.

My solution

I already knew a couple of tricks in what concerns changing this setting directly in the registry. I blogged and described this in the following post Manage with CodedUI sites that are using Windows authentication. You will read about the details on how to change the values in the registry and the meaning of each one.
What I was in need is a small and simple interface that will let me quickly alter this setting. This is when I created “Login Settings Changer”, a half hour project that makes me save plenty of time and clicks every day.

For simplicity I have chosen a Windows Forms application and after setting a simple UI that you can see here

LogonSettingsChanger

I added the necessary logic of which we will check the most important steps (for the less experienced readers) further in this post.
One of these steps are the binded values to the state selection drop down. Different setting options are represented with an enumeration and in order to attach a more friendly description to every state I decorated my LogonSetting enum with the Description attribute. This is what it looks like:

public enum LogonSetting
{
    [Description("Not set")]
    NotSet = -1,
    [Description("Automatically logon with current user name and password")]
    AutomaticallyLogonWithCurrentUsernameAndPassword = 0x00000,
    [Description("Prompt for user name and password")]
    PromptForUserNameAndPassword = 0x10000,
    [Description("Automatic logon only in the Intranet Zone")]
    AutomaticLogonOnlyInTheIntranetZone = 0x20000,
    [Description("Anonymous logon")]
    AnonymousLogon = 0x30000
}

Later on, the necessary logic to retrieve this description will be part of the class that I used for representation, which again is extracted through reflection.
In retrieving our settings from registry we need to consider the case in which this value is not specified (usually this is the case when this IE feature is disabled by the group policy), so we need to perform the necessary check for null value and set it to our enum value of Not Set.

/// 
/// Retrieves the current IE Logon setting.
/// 
private DisplaySetting GetLogonSettings()
{
    object logonSettingValue = LocalIntranetZone.GetValue(LogonSettingValueName);

    if (logonSettingValue == null)
    {
        return DisplaySetting.NotSet;
    }

    return new DisplaySetting(logonSettingValue);
}

The same consideration needs to follow once we do try to persist the Not Set state (remove the value).

/// 
/// Sets the IE Logon setting to the desired value.
/// 
/// The desired value to assign to the Logon Setting.
private void SetLogonSettings(DisplaySetting logonSetting)
{
    if (logonSetting.Setting == LogonSetting.NotSet)
    {
        LocalIntranetZone.DeleteValue(LogonSettingValueName);
    }
    else
    {
        LocalIntranetZone.SetValue(LogonSettingValueName, (int)logonSetting.Setting);
    }
}

As you can see, in both of the examples I’m working with an instance of a DisplaySetting class. It wraps around our enumeration and exposes it’s properties in a more structured way.

public class DisplaySetting
{
    public DisplaySetting(object setting)
    {
        if (setting == null)
        {
            throw new ArgumentNullException("setting");
        }

        Setting = (LogonSetting)setting;
    }

    public DisplaySetting(LogonSetting setting)
    {
        Setting = setting;
    }

    public string Description
    {
        get { return GetDescription(Setting); }
    }

    public static DisplaySetting NotSet
    {
        get { return new DisplaySetting(LogonSetting.NotSet); }
    }

    public LogonSetting Setting { get; private set; }

    public static IEnumerable GetAllSettings()
    {
        foreach (LogonSetting item in Enum.GetValues(typeof (LogonSetting)))
        {
            yield return new DisplaySetting(item);
        }
    }

    protected string GetDescription(LogonSetting value)
    {
        Type type = typeof (LogonSetting);
        string name = Enum.GetName(type, value);

        if (name != null)
        {
            FieldInfo field = type.GetField(name);
            if (field != null)
            {
                DescriptionAttribute attr = Attribute.GetCustomAttribute(
                    field,
                    typeof (DescriptionAttribute)) as DescriptionAttribute;

                if (attr != null)
                {
                    return attr.Description;
                }
            }
        }

        return null;
    }
}

DisplaySetting contains only two properties. The enumerator itself and the description of the value, retrieved, as earlier mentioned, through the reflection. It also, for simplicity, offers a static method which returns an instance of the class itself set to a Not Set state and some handy constructor overloads.

What is left there are only some details in wiring down the UI interaction. You can check them in the attached source code project.

Conclusion

This is a simple example that will hopefully encourage less experienced developers to start filling the gaps in the software with their own hands. It’s simple and easy and can save a ton of time.
You can find here under the link to the source code and compiled version of this little application.

Happy coding!

Download Logon Settings Changer Source Code

Download Logon Settings Changer App

Installing VSIX package via WiX installer

Introduction

WiX Toolset is a powerful set of tools used to create Windows installations packages. Unfortunately it’s learning curve is quite steep and the documentation is not it’s strong point. That’s why I decided to show you, step by step, how to create an installer for your VSIX packages. I will not cover all of the WiX features as this is not intended to be an exhaustive WiX guide but I will describe in detail what is necessary to deploy a VSIX package. Also I will add some interesting details that can be useful more in general when creating WiX installers. The end result should be a MSI installation file that will correctly install and uninstall your VSIX package. Let’s start.

It’s true that VSIX leverages an installer on it’s own. Still you should consider this approach if your installation is more complex and/or requires other components to be installed.

Prerequisites

In order to follow this example you will need any version of Visual Studio starting from 2010 till the most recent 2015. Also any sku except the Express is fine. You will need a Visual Studio SDK and WiX Toolset installed. The examples you will find are made w Visual Studio 2013 and WiX 3.9 R2. You can download them from here (SDK and WiX).

Creating the necessary projects

In order to start our practical example, let’s first create the necessary project that we are going to use by our installer later. Create a new project in Visual Studio and choose a “VSIX Project”.
I will name it MyAwesomeExtension.

MyAwesomeExtension

Once the project is created you will be presented by the vsixmanifest designer. You can leave all the defaults. Make sure only that the Product ID is set in the unique way and that, for commodity, it doesn’t contain spaces.

EmptyVSIXProjectWiX

This should be it for what concerns our extension. I will not focus on creating Visual Studio extensions and this is just enough to have an extension registered and working. If you hit F5 you should start another instance of Visual Studio, by default in Experimental Mode, and if you check inside Tools > Extensions and Updates menu you should find your newly created extension.

The next thing is adding a WiX Setup Project into your solution. I will simply call it Installer.

AddingInstaller

This was about it. In the next paragraph I will show you how to install your VSIX package using WiX.

WiX Toolset installer

First thing first. We are going to take advantage of a standard custom action offered by WixVSExtension. In order to do so, we need to reference the WixVSExtension.dll. You can find it inside the WiX installation folder which in my case is C:\Program Files (x86)WiX Toolset v3.9bin. Be aware that the Add reference panel for the WiX setup projects is custom and it slightly differs from the default one. Aside of having the necessary to help us installing (and uninstalling) VSIX packages, it also contains many other useful properties and custom actions that will ease interacting with Visual Studio. You can find a list of the properties and custom actions here.
After the reference to the project is added we need to include the WixVSExtension namespace in our main wxs file (by default Product.wxs). In the Wix element next to the default namespace add the following xmlns:VSExtension="http://schemas.microsoft.com/wix/VSExtension". The end result should look like this:



...

Now we are going to add another reference to our Wix project and it will point to our VSIX project. This is done for commodity so that we can point Wix to the output of our VSIX project. Once this is done we need to define a component that will leverage the custom action defined in WixVSExtension that will actually install our VSIX. Inside the fragment where an example of ComponentGroup is defined, we are going to remove all of the code inside it, comprehensive of ComponentGroup itself. Define the following:


  
    
    
  

Let’s analyze what we added.
First element we added is a Component, that we called for convenience C_MyAwesomeExtension, we assigned a unique identifier to our component (so that MSI can track this element) and we specified the folder in which this component should be deployed. I will not get into details of what a component is and how it works, I do suppose you already know that and if not you can find more info here.

Inside our component we specified two elements. The simpler one to understand is for sure the File element. We gave an id to the file that we do need in order to deploy our VSIX and named it by using the name of the compiled assembly. In order to retrieve the name I used a variable that was generated by Wix because of the reference to the project we added, appending to the result the .vsix string. Now, each time you reference a project Wix will create several variables dynamically and they will be at your service. A full list of them you can find at this link. In case we were referencing a project that will output an executable or a library, we could use the TargetFileName variable and omit the .visx. However as by default Wix doesn’t know nothing about VSIX project types and outputs, we need to use TargetName property and concatenate .vsix to it in order to get a file name with the correct extension.

Same thing we did with the source attribute. With it we do specify where this file should be copied from in order to be placed inside the MSI installation file. As for the Name attribute, we are going to use some of auto generated variables to get to the correct path of our compiled VSIX.

VSExtension:VsixPackage element

Let’s focus on the main character of this article, the VSExtension:VsixPackage element. I will describe all of the attributes I used and also the one I haven’t and describe the behavior that they do produce.

File
File, as the name suggest, requires to be set to the file name that we used in our MSI tables and points to our VSIX installer. Simply said it needs to be the same as the Id attribute we used for the File element that we discussed earlier. In this way the custom action will know where is the VSIX file that we are planning to install.

PackageId
This is a very important value. Earlier I mentioned the Product ID, when we were creating our VSIX extension. Well that’s it, the unique product ID that is set inside vsixmanifest file in your extension. Make sure that it does not contain any spaces and if so add quote marks and escape them properly.

Target
You can specify one of the following values: integratedShell, professional, premium, ultimate, vbExpress, vcExpress, vcsExpress, vwdExpress. It will indicate which edition (SKU) of Visual Studio is required in order to register this extension.

TargetVersion
Specifies the version of Visual Studio for which to register the extension. Should contain a version number as at example 11.0 in case of Visual Studio 2012 or 14.0 in case of Visual Studio 2015.

Vital
In indicates if the failure of the installation of the VSIX extension will cause a rollback of the whole installation.

Permanent
Often underestimated and forgotten parameter. This is the reason of many complains about the extension not uninstalling on msi uninstall. And that’s all about it. If set to no, your extension will be uninstalled once you try to uninstall your product. If set to yes, your extension will not be removed in case the user uninstalls the product.

VsixInstallerPathProperty
As this custom action relies on VsixInstaller.exe to trigger the installation of the VSIX, it needs to know where the VsixInstaller.exe is located in order to use it. It is recommended not to set this attribute and let it be populated by the custom action itself. By default, the latest VsixInstaller.exe on the machine will be used to install the VSIX package.

Believe it or not this is sufficient to let the Wix install your VSIX extension. In our case we do target the Visual Studio 2013 and accepting any edition from professional above.

Behind the scene

So what’s the magic behind this custom action? The simplest way to verify what is happening is to compile our project and launch the installation. Before we are able to this there are two minor things we need to take care of. Inside our Feature element we need to change the ComponentGroupRef to ComponentRef and point the id to our C_MyAwesomeExtension component. Also to make the output a single file, we will tell Wix to integrate the cabinet inside the msi itself. To do so under the MediaTemplate element we are going to add the EmbedCab attribute and set it’s value to yes.




  

We can compile now our project and position our self in the build output directory. A file called Installer.msi should be there.
In order to see what is happening we can’t just double click our newly created msi. We need to launch the installation from the command prompt and request the verbose logging to be activated. To request the log file being emitted we need to launch the following command: msiexec /i Installer.msi /l*v install.log. This will tell the msi installer to install our application and output a verbose log inside the file named install.log.
If you execute this command, you will briefly see installation starting and that’s it. This is because we haven’t chosen any UI for this installation. It’s not in our scope showing how to create/choose and UI for the installer.
Let’s check our default install folder at C:\Program Files (x86)Installer. Bingo! Our extension is there. Now open your Visual Studio 2013 instance and check the Extensions and updates window. Again, MyAwesomeExtension is there!

MyAwesomeExtensionInstalled

Open the newly created log file to see what was done in order to achieve this. Open the install.log file and search for “/skuName:Pro /skuVersion:12.0”. The line you landed on is the command that was executed by our VSExtension:VsixPackage custom action. The full command is following:

C:\Program Files (x86)Microsoft Visual Studio 12.0Common7IDEVSIXInstaller.exe /q /skuName:Pro /skuVersion:12.0 “C:\Program Files (x86)InstallerMyAwesomeExtension.vsix” /admin

What happened here? Well nothing special. When you double click your vsix file, the vsix extension is associated with VSIXInstaller.exe and it will show you the following window:

MyAwesomeExtensionVSIXInstaller

Same thing with our custom action, it calls directly VSIXInstaller.exe and triggers the quite mode (so no UI is shown) and passes the necessary parameters in order for VSIXInstaller.exe to preform the installation. Do you recognize the other parameters? That’s right, the do match what we have specified with the attributes on our VSExtension:VsixPackage element. Execept the last /admin parameter. It implies that the extension will be installed to the admin extension location. If you prefer it to be installed on per user basis, you will need to create a perUser installation, thus, set the Package InstallScope to perUser.

You can get through the log file and probably you will find plenty of interesting information, like the search for the VSIXInstaller.exe and the values that are set in order to accommodate it, etc.

The same thing is done for what concerns the uninstall. We can launch the uninstall with the following command: msiexec /x Installer.msi /l*v unistall.log. As you can see it is similar to the install except that instead of /i we used a /x parameter. Once you execute it you will be prompted for the confirmation:

MyAwesomeExtensionUninstall

By confirming that, the uninstallation will be preformed.

Get inside the log file and search again for /skuName:Pro /skuVersion:12.0. You will land on line that triggers the following command:

C:\Program Files (x86)Microsoft Visual Studio 12.0Common7IDEVSIXInstaller.exe /q /skuName:Pro /skuVersion:12.0 /u:”MyAwesomeExtension.majcica.com.420d4ff9-340c-4867-9247-c85ab370ec7b” /admin

Please notice that the /u parameter is passed which will indicate to VSIXInstaller.exe to remove the extension with product Id (and not the vsix file anymore) that matches the unique ID of our extension.

Tuning the installer

There are a couple of things we could take care of. One of them is to check if the requested Visual Studio is installed. This in order to prevent an eventual exception and a consequent installation failure. As this is fairly simple I will add the necessary info to this post.

Everything we need to know about the Visual Studio been installed. WixVSExtension puts on our disposition this info in form of a property. In case Visual Studio is installed, the property will be populated, otherwise will not. Only thing we need to do is to reference the property, which in our case is called VS2013DEVENV, and create a condition that verifies if it is set.




  

After adding this code inside the product element, compile, and launch your installation, you will get the following message, which eventually will interrupt and prevent the installation.

NoVS2013

Another thing I can think of is installing the extension to multiple versions of Visual Studio. You could declare several components, each one targeting the same file but different TargetVersion, and add a condition to each of them. Example:


  VS2012DEVENV
  



  VS2013DEVENV
  

Conclusion

I hope I answered all of the questions you may have regarding installing VSIX packages via Wix. If there is anything I omitted and you would like to know more about, please do not hesitate to ask in the comments.
For completeness I will now list the complete content on the Product.wxs file.



  
    

    

    
      

    
    

    
      
    
  

  
    
      
        
      
    
  

  
    
      
      
    
  

Happy coding!