Skip to content

Commit

Permalink
Merge pull request #7 from Microsoft/develop
Browse files Browse the repository at this point in the history
Merge develop into master to release new version
  • Loading branch information
heaths committed Jan 29, 2017
2 parents 18665a4 + 7ea6a75 commit e114d3d
Show file tree
Hide file tree
Showing 35 changed files with 1,439 additions and 26 deletions.
23 changes: 22 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Some projects require optional software to open or otherwise use in Visual Studi

## Coding

This project uses a Git flow model releasing from the `master` branch with development based on and stabilize in the `develop` branch. You can view current build status in the [README](README.md) document.

These cmdlets use the setup configuration API. [Documentation][docs] is available on MSDN, as well as [samples][samples] of how the APIs can be used.

Code analysis and style cop rules are defined for this solution, but are currently not enforced during build for performance reasons or treated as errors. This may change in the future. Please resolve any build warnings in the code editor or **Error List** window.
Expand All @@ -30,17 +32,35 @@ Before you can build this project from the command line with MSBuild or within V

Note again that to build the full solution in Visual Studio some optional software may be required.

### Updating Packages

Please note that the types in _Microsoft.VisualStudio.Setup.Configuration.Interop_ are embeddable and Visual Studio will use this setting by default for both the source and test projects. However, after updating the package reference in the test project you must disable "Embed Interop Types" and enable "Copy Local" for the _Microsoft.VisualStudio.Setup.Configuration.Interop_ assembly or any tests that mock the interfaces will fail.

This is because only types and methods referenced are included, and any methods prior to referenced methods in VTBL order are stubbed and will not match the proper method names, thus failing any tests with mocks of embedded interfaces. By referencing the assembly locally instead of embedding, all types are left intact and mocks will correctly match all names despite those types being embedded in the source project, since COM-imported type names are equivalent based on their GUIDs.

## Testing

All available tests are discovered after a complete build in Test Explorer within Visual Studio.

On the command line, you can run the following commands from the solution directory. Replace `<version>` with whatever version was downloaded.

```
```batch
nuget install xunit.runner.console -outputdirectory packages
packages\xunit.runner.console.<version>\tools\xunit.runner.console test\VSSetup.PowerShell.Test\bin\Debug\Microsoft.VisualStudio.Setup.PowerShell.Test.dll
```

It's also recommended that, if your machine supports it, you install [Docker for Windows][docker], switch to Windows containers, and test in isolated containers for runtime behavior.

```batch
REM You only need to build once unless updating the Dockerfile or files it copies.
test\docker\build
REM This will automatically map build output. Defaults to Debug configuration. Pass -? for options.
test\docker\test
```

You can also run `test\docker\run.cmd` to start an interactive shell for exploratory testing.

## Pull Requests

We welcome pull requests for both bug fixes and new features that solve a common enough problem to benefit the community. Please note the following requirements.
Expand All @@ -50,6 +70,7 @@ We welcome pull requests for both bug fixes and new features that solve a common

Thank you for your contributions!

[docker]: https://www.docker.com/products/overview
[samples]: https://aka.ms/setup/configuration/samples
[docs]: https://aka.ms/setup/configuration/docs
[interop]: https://aka.ms/setup/configuration/interop
24 changes: 22 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,20 @@ This PowerShell module contains cmdlets to query instances of Visual Studio 2017

## Installing

You can download packages from the Releases page for this project on GitHub, but with Windows Management Framework 5.0 or newer (which installs PowerShell and comes with Windows 10), you can download and install this module even easier.
With Windows Management Framework 5.0 or newer (which installs PowerShell and comes with Windows 10), or [PowerShellGet][psget] for PowerShell 3.0 or 4.0, you can download and install this module easily.

```powershell
Install-Module VSSetup -Scope CurrentUser
```

To install for all users, pass `AllUsers` instead of `CurrentUser`, or just leave the `-Scope` parameter out of the command entirely.

You can also download the ZIP package from the [Releases][releases] page on this project site and extract to a directory named _VSSetup_ under a directory in your `$env:PSMODULEPATH`.

```powershell
Expand-Archive VSSetup.zip "${env:USERPROFILE}\Documents\WindowsPowerShell\Modules\VSSetup"
```

## Using

You can query all usable instances of Visual Studio and other products installed by the Visual Studio installer.
Expand All @@ -33,6 +41,18 @@ Get-VSSetupInstance -All | Select-VSSetupInstance -Require 'Microsoft.VisualStud

## Feedback

To file issues or suggestions, please use the Issues page for this project on GitHub.
To file issues or suggestions, please use the [Issues][issues] page for this project on GitHub.

## Status

This project uses a Git flow model releasing from the `master` branch with development based on and stabilize in the `develop` branch.

Branch | Status
------ | ------
master | [![build status: master](https://ci.appveyor.com/api/projects/status/4c1feyut6rvmw1dk/branch/master?svg=true)](https://ci.appveyor.com/project/heaths/vssetup-powershell/branch/master)
develop | [![build status: develop](https://ci.appveyor.com/api/projects/status/4c1feyut6rvmw1dk/branch/develop?svg=true)](https://ci.appveyor.com/project/heaths/vssetup-powershell/branch/develop)

[issues]: https://github.com/Microsoft/vssetup.powershell/issues
[psget]: http://go.microsoft.com/fwlink/?LinkID=746217
[releases]: https://github.com/Microsoft/vssetup.powershell/releases
[samples]: https://aka.ms/setup/configuration/samples
1 change: 1 addition & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ configuration:

environment:
RunCodeAnalysis: true
TreatWarningsAsErrors: true

branches:
only:
Expand Down
49 changes: 49 additions & 0 deletions src/VSSetup.PowerShell/Extensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// <copyright file="Extensions.cs" company="Microsoft Corporation">
// Copyright (C) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt in the project root for license information.
// </copyright>

namespace Microsoft.VisualStudio.Setup
{
using System;
using System.Text;

/// <summary>
/// Extension methods.
/// </summary>
internal static class Extensions
{
/// <summary>
/// Returns a <see cref="Version"/> without negative fields.
/// </summary>
/// <param name="version">The <see cref="Version"/> to normalize.</param>
/// <returns>A <see cref="Version"/> without negative fields.</returns>
public static Version Normalize(this Version version)
{
Validate.NotNull(version, nameof(version));

int build = version.Build > 0 ? version.Build : 0;
int revision = version.Revision > 0 ? version.Revision : 0;

return new Version(version.Major, version.Minor, build, revision);
}

/// <summary>
/// Converts a string to PascalCase.
/// </summary>
/// <param name="value">The string value to convert.</param>
/// <returns>If the string is not null or empty, a string converted to PascalCase; otherwise, the original string value.</returns>
public static string ToPascalCase(this string value)
{
if (!string.IsNullOrEmpty(value))
{
var sb = new StringBuilder(value);
sb[0] = char.ToUpperInvariant(sb[0]);

return sb.ToString();
}

return value;
}
}
}
70 changes: 69 additions & 1 deletion src/VSSetup.PowerShell/Instance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Microsoft.VisualStudio.Setup
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Configuration;

Expand All @@ -19,6 +20,8 @@ namespace Microsoft.VisualStudio.Setup
/// </summary>
public class Instance
{
private static readonly ISet<string> DeclaredProperties;

private readonly string installationName;
private readonly string installationPath;
private readonly Version installationVersion;
Expand All @@ -29,6 +32,19 @@ public class Instance
private readonly string productPath;
private readonly PackageReference product;
private readonly IList<PackageReference> packages;
private readonly IDictionary<string, object> properties;
private readonly string enginePath;
private readonly bool isComplete;
private readonly bool isLaunchable;

static Instance()
{
var properties = typeof(Instance)
.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public)
.Select(property => property.Name);

DeclaredProperties = new HashSet<string>(properties, StringComparer.OrdinalIgnoreCase);
}

/// <summary>
/// Initializes a new instance of the <see cref="Instance"/> class.
Expand All @@ -51,7 +67,7 @@ internal Instance(ISetupInstance2 instance)
var versionString = instance.GetInstallationVersion();
if (Version.TryParse(versionString, out version))
{
return version;
return version.Normalize();
}
return null;
Expand Down Expand Up @@ -104,6 +120,33 @@ internal Instance(ISetupInstance2 instance)
{
Packages = new ReadOnlyCollection<PackageReference>(packages);
}

TrySet(ref properties, nameof(Properties), () =>
{
var properties = instance.GetProperties();
return properties?.GetNames()
.ToDictionary(name => name.ToPascalCase(), name => properties.GetValue(name), StringComparer.OrdinalIgnoreCase);
});

if (properties != null)
{
Properties = new ReadOnlyDictionary<string, object>(properties);
}
else
{
// While accessing properties on a null object succeeds in PowerShell, accessing the indexer does not.
Properties = ReadOnlyDictionary<string, object>.Empty;
}

TrySet(ref enginePath, nameof(EnginePath), instance.GetEnginePath);
TrySet(ref isComplete, nameof(IsComplete), instance.IsComplete);
TrySet(ref isLaunchable, nameof(IsLaunchable), instance.IsLaunchable);

// Get all properties of the instance not explicitly declared.
var store = (ISetupPropertyStore)instance;
AdditionalProperties = store.GetNames()
.Where(name => !DeclaredProperties.Contains(name))
.ToDictionary(name => name.ToPascalCase(), name => store.GetValue(name), StringComparer.OrdinalIgnoreCase);
}

/// <summary>
Expand Down Expand Up @@ -161,6 +204,31 @@ internal Instance(ISetupInstance2 instance)
/// </summary>
public ReadOnlyCollection<PackageReference> Packages { get; }

/// <summary>
/// Gets custom properties defined for the instance.
/// </summary>
public IDictionary<string, object> Properties { get; }

/// <summary>
/// Gets the path to the engine that installed the instance.
/// </summary>
public string EnginePath => enginePath;

/// <summary>
/// Gets a value indicating whether the instance is complete.
/// </summary>
public bool IsComplete => isComplete;

/// <summary>
/// Gets a value indicating whether the instance is launchable (e.g. may have errors but other features work).
/// </summary>
public bool IsLaunchable => isLaunchable;

/// <summary>
/// Gets additional properties not explicitly defined on this class.
/// </summary>
internal IDictionary<string, object> AdditionalProperties { get; }

private static IEnumerable<PackageReference> GetPackages(ISetupInstance2 instance)
{
var references = instance.GetPackages();
Expand Down
5 changes: 5 additions & 0 deletions src/VSSetup.PowerShell/NativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,10 @@ internal static class NativeMethods
/// Element not found (as HRESULT).
/// </summary>
public const int E_NOTFOUND = unchecked((int)0x80070490);

/// <summary>
/// Class not registered.
/// </summary>
public const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
}
}
12 changes: 11 additions & 1 deletion src/VSSetup.PowerShell/PowerShell/GetInstanceCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,22 @@ private bool IsLiteralPath
/// <inheritdoc/>
protected override void BeginProcessing()
{
query = new SetupConfiguration();
query = QueryFactory.Create();
if (query == null)
{
WriteWarning(Resources.NotRegistered);
}
}

/// <inheritdoc/>
protected override void ProcessRecord()
{
if (query == null)
{
StopProcessing();
return;
}

if (AllParameterSet.Equals(ParameterSetName, StringComparison.OrdinalIgnoreCase))
{
foreach (var instance in GetInstances())
Expand Down

0 comments on commit e114d3d

Please sign in to comment.