зеркало из https://github.com/microsoft/VFSForGit.git
Delete custom upgrader
This leaves 'gvfs upgrade' non-functional Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
This commit is contained in:
Родитель
704411b3d3
Коммит
a17c95a9e2
6
GVFS.sln
6
GVFS.sln
|
@ -39,8 +39,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Tests", "GVFS\GVFS.Tes
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.UnitTests", "GVFS\GVFS.UnitTests\GVFS.UnitTests.csproj", "{1A46C414-7F39-4EF0-B216-A88033D18678}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Upgrader", "GVFS\GVFS.Upgrader\GVFS.Upgrader.csproj", "{F8D96536-0F2C-4B40-8516-D2C0516C66EC}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GVFS.VirtualFileSystemHook", "GVFS\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.vcxproj", "{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Virtualization", "GVFS\GVFS.Virtualization\GVFS.Virtualization.csproj", "{EC90AF5D-E018-4248-85D6-9DB1898D710E}"
|
||||
|
@ -129,10 +127,6 @@ Global
|
|||
{1A46C414-7F39-4EF0-B216-A88033D18678}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1A46C414-7F39-4EF0-B216-A88033D18678}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1A46C414-7F39-4EF0-B216-A88033D18678}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F8D96536-0F2C-4B40-8516-D2C0516C66EC}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F8D96536-0F2C-4B40-8516-D2C0516C66EC}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F8D96536-0F2C-4B40-8516-D2C0516C66EC}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F8D96536-0F2C-4B40-8516-D2C0516C66EC}.Release|x64.Build.0 = Release|Any CPU
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Debug|x64.Build.0 = Debug|x64
|
||||
{2D23AB54-541F-4ABC-8DCA-08C199E97ABB}.Release|x64.ActiveCfg = Release|x64
|
||||
|
|
|
@ -78,28 +78,8 @@ namespace GVFS.Common
|
|||
public abstract bool IsElevated();
|
||||
public abstract string GetCurrentUser();
|
||||
public abstract string GetUserIdFromLoginSessionId(int sessionId, ITracer tracer);
|
||||
|
||||
/// <summary>
|
||||
/// Get the directory for upgrades that is permissioned to
|
||||
/// require elevated privileges to modify. This can be used for
|
||||
/// data that we don't want normal user accounts to modify.
|
||||
/// </summary>
|
||||
public abstract string GetUpgradeProtectedDataDirectory();
|
||||
|
||||
/// <summary>
|
||||
/// Directory that upgrader log directory should be placed
|
||||
/// in. There can be multiple log directories, so this is the
|
||||
/// containing directory to place them in.
|
||||
/// </summary>
|
||||
public abstract string GetUpgradeLogDirectoryParentDirectory();
|
||||
public abstract string GetSystemInstallerLogPath();
|
||||
|
||||
/// <summary>
|
||||
/// Directory that contains the file indicating that a new
|
||||
/// version is available.
|
||||
/// </summary>
|
||||
public abstract string GetUpgradeHighestAvailableVersionDirectory();
|
||||
|
||||
public abstract void ConfigureVisualStudio(string gitBinPath, ITracer tracer);
|
||||
|
||||
public abstract bool TryCopyPanicLogs(string copyToDir, out string error);
|
||||
|
@ -125,10 +105,6 @@ namespace GVFS.Common
|
|||
ITracer tracer,
|
||||
string lockPath);
|
||||
|
||||
public abstract ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions(
|
||||
PhysicalFileSystem fileSystem,
|
||||
ITracer tracer);
|
||||
|
||||
public bool TryGetNormalizedPathRoot(string path, out string pathRoot, out string errorMessage)
|
||||
{
|
||||
pathRoot = null;
|
||||
|
@ -229,11 +205,6 @@ namespace GVFS.Common
|
|||
{
|
||||
get { return "GVFS.Mount" + this.ExecutableExtension; }
|
||||
}
|
||||
|
||||
public string GVFSUpgraderExecutableName
|
||||
{
|
||||
get { return "GVFS.Upgrader" + this.ExecutableExtension; }
|
||||
}
|
||||
}
|
||||
|
||||
public class UnderConstructionFlags
|
||||
|
|
|
@ -1,687 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Runtime.Serialization.Json;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public class GitHubUpgrader : ProductUpgrader
|
||||
{
|
||||
private const string GitHubReleaseURL = @"https://api.github.com/repos/microsoft/vfsforgit/releases";
|
||||
private const string JSONMediaType = @"application/vnd.github.v3+json";
|
||||
private const string UserAgent = @"GVFS_Auto_Upgrader";
|
||||
private const string CommonInstallerArgs = "/VERYSILENT /CLOSEAPPLICATIONS /SUPPRESSMSGBOXES /NORESTART";
|
||||
private const string GVFSInstallerArgs = CommonInstallerArgs + " /REMOUNTREPOS=false";
|
||||
private const string GitInstallerArgs = CommonInstallerArgs + " /ALLOWDOWNGRADE=1";
|
||||
private const string GitAssetId = "Git";
|
||||
private const string GVFSAssetId = "GVFS";
|
||||
private const string GitInstallerFileNamePrefix = "Git-";
|
||||
private const string GVFSSigner = "Microsoft Corporation";
|
||||
private const string GVFSCertIssuer = "Microsoft Code Signing PCA";
|
||||
private const string GitSigner = "Johannes Schindelin";
|
||||
private const string GitCertIssuer = "COMODO RSA Code Signing CA";
|
||||
|
||||
private static readonly HashSet<string> GVFSInstallerFileNamePrefixCandidates = new HashSet<string>(GVFSPlatform.Instance.Constants.PathComparer)
|
||||
{
|
||||
"SetupGVFS",
|
||||
"VFSForGit"
|
||||
};
|
||||
|
||||
private Version newestVersion;
|
||||
private Release newestRelease;
|
||||
|
||||
public GitHubUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
GitHubUpgraderConfig upgraderConfig,
|
||||
bool dryRun = false,
|
||||
bool noVerify = false)
|
||||
: base(currentVersion, tracer, dryRun, noVerify, fileSystem)
|
||||
{
|
||||
this.Config = upgraderConfig;
|
||||
}
|
||||
|
||||
public GitHubUpgraderConfig Config { get; private set; }
|
||||
|
||||
public override bool SupportsAnonymousVersionQuery { get => true; }
|
||||
|
||||
public static GitHubUpgrader Create(
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
LocalGVFSConfig gvfsConfig,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
out string error)
|
||||
{
|
||||
return Create(tracer, fileSystem, dryRun, noVerify, gvfsConfig, out error);
|
||||
}
|
||||
|
||||
public static GitHubUpgrader Create(
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
LocalGVFSConfig localConfig,
|
||||
out string error)
|
||||
{
|
||||
GitHubUpgrader upgrader = null;
|
||||
GitHubUpgraderConfig gitHubUpgraderConfig = new GitHubUpgraderConfig(tracer, localConfig);
|
||||
|
||||
if (!gitHubUpgraderConfig.TryLoad(out error))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (gitHubUpgraderConfig.ConfigError())
|
||||
{
|
||||
gitHubUpgraderConfig.ConfigAlertMessage(out error);
|
||||
return null;
|
||||
}
|
||||
|
||||
upgrader = new GitHubUpgrader(
|
||||
ProcessHelper.GetCurrentProcessVersion(),
|
||||
tracer,
|
||||
fileSystem,
|
||||
gitHubUpgraderConfig,
|
||||
dryRun,
|
||||
noVerify);
|
||||
|
||||
return upgrader;
|
||||
}
|
||||
|
||||
public override bool UpgradeAllowed(out string message)
|
||||
{
|
||||
return this.Config.UpgradeAllowed(out message);
|
||||
}
|
||||
|
||||
public override bool TryQueryNewestVersion(
|
||||
out Version newVersion,
|
||||
out string message)
|
||||
{
|
||||
List<Release> releases;
|
||||
|
||||
newVersion = null;
|
||||
if (this.TryFetchReleases(out releases, out message))
|
||||
{
|
||||
foreach (Release nextRelease in releases)
|
||||
{
|
||||
Version releaseVersion = null;
|
||||
|
||||
if (nextRelease.Ring <= this.Config.UpgradeRing &&
|
||||
nextRelease.TryParseVersion(out releaseVersion) &&
|
||||
releaseVersion > this.installedVersion)
|
||||
{
|
||||
newVersion = releaseVersion;
|
||||
this.newestVersion = releaseVersion;
|
||||
this.newestRelease = nextRelease;
|
||||
message = $"New GVFS version {newVersion.ToString()} available in ring {this.Config.UpgradeRing}.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newVersion == null)
|
||||
{
|
||||
message = $"Great news, you're all caught up on upgrades in the {this.Config.UpgradeRing} ring!";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TryDownloadNewestVersion(out string errorMessage)
|
||||
{
|
||||
if (!this.TryCreateAndConfigureDownloadDirectory(this.tracer, out errorMessage))
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(GitHubUpgrader)}.{nameof(this.TryCreateAndConfigureDownloadDirectory)} failed. {errorMessage}");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool downloadedGit = false;
|
||||
bool downloadedGVFS = false;
|
||||
|
||||
foreach (Asset asset in this.newestRelease.Assets)
|
||||
{
|
||||
bool targetOSMatch = string.Equals(Path.GetExtension(asset.Name), GVFSPlatform.Instance.Constants.InstallerExtension, GVFSPlatform.Instance.Constants.PathComparison);
|
||||
bool isGitAsset = this.IsGitAsset(asset);
|
||||
bool isGVFSAsset = isGitAsset ? false : this.IsGVFSAsset(asset);
|
||||
if (!targetOSMatch || (!isGVFSAsset && !isGitAsset))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!this.TryDownloadAsset(asset, out errorMessage))
|
||||
{
|
||||
errorMessage = $"Could not download {(isGVFSAsset ? GVFSAssetId : GitAssetId)} installer. {errorMessage}";
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
downloadedGit = isGitAsset ? true : downloadedGit;
|
||||
downloadedGVFS = isGVFSAsset ? true : downloadedGVFS;
|
||||
}
|
||||
}
|
||||
|
||||
if (!downloadedGit || !downloadedGVFS)
|
||||
{
|
||||
errorMessage = $"Could not find {(!downloadedGit ? GitAssetId : GVFSAssetId)} installer in the latest release.";
|
||||
return false;
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryRunInstaller(InstallActionWrapper installActionWrapper, out string error)
|
||||
{
|
||||
string localError;
|
||||
|
||||
this.TryGetGitVersion(out GitVersion newGitVersion, out localError);
|
||||
|
||||
if (!installActionWrapper(
|
||||
() =>
|
||||
{
|
||||
if (!this.TryInstallUpgrade(GitAssetId, newGitVersion.ToString(), out localError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
$"Installing Git version: {newGitVersion}"))
|
||||
{
|
||||
error = localError;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!installActionWrapper(
|
||||
() =>
|
||||
{
|
||||
if (!this.TryInstallUpgrade(GVFSAssetId, this.newestVersion.ToString(), out localError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
$"Installing GVFS version: {this.newestVersion}"))
|
||||
{
|
||||
error = localError;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.LogVersionInfo(this.newestVersion, newGitVersion, "Newly Installed Version");
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryCleanup(out string error)
|
||||
{
|
||||
error = string.Empty;
|
||||
if (this.newestRelease == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (Asset asset in this.newestRelease.Assets)
|
||||
{
|
||||
Exception exception;
|
||||
if (!this.TryDeleteDownloadedAsset(asset, out exception))
|
||||
{
|
||||
error += $"Could not delete {asset.LocalPath}. {exception.ToString()}." + Environment.NewLine;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
error.TrimEnd(Environment.NewLine.ToCharArray());
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool TryDeleteDownloadedAsset(Asset asset, out Exception exception)
|
||||
{
|
||||
return this.fileSystem.TryDeleteFile(asset.LocalPath, out exception);
|
||||
}
|
||||
|
||||
protected virtual bool TryDownloadAsset(Asset asset, out string errorMessage)
|
||||
{
|
||||
errorMessage = null;
|
||||
|
||||
string downloadPath = ProductUpgraderInfo.GetAssetDownloadsPath();
|
||||
string localPath = Path.Combine(downloadPath, asset.Name);
|
||||
WebClient webClient = new WebClient();
|
||||
|
||||
try
|
||||
{
|
||||
webClient.DownloadFile(asset.DownloadURL, localPath);
|
||||
asset.LocalPath = localPath;
|
||||
}
|
||||
catch (WebException webException)
|
||||
{
|
||||
errorMessage = "Download error: " + webException.Message;
|
||||
this.TraceException(webException, nameof(this.TryDownloadAsset), $"Error downloading asset {asset.Name}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool TryFetchReleases(out List<Release> releases, out string errorMessage)
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
|
||||
client.DefaultRequestHeaders.Accept.Clear();
|
||||
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(JSONMediaType));
|
||||
client.DefaultRequestHeaders.Add("User-Agent", UserAgent);
|
||||
|
||||
releases = null;
|
||||
errorMessage = null;
|
||||
|
||||
try
|
||||
{
|
||||
Stream result = client.GetStreamAsync(GitHubReleaseURL).GetAwaiter().GetResult();
|
||||
|
||||
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(List<Release>));
|
||||
releases = serializer.ReadObject(result) as List<Release>;
|
||||
return true;
|
||||
}
|
||||
catch (HttpRequestException exception)
|
||||
{
|
||||
errorMessage = string.Format("Network error: could not connect to GitHub({0}). {1}", GitHubReleaseURL, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryFetchReleases), $"Error fetching release info.");
|
||||
}
|
||||
catch (TaskCanceledException exception)
|
||||
{
|
||||
// GetStreamAsync can also throw a TaskCanceledException to indicate a timeout
|
||||
// https://github.com/dotnet/corefx/issues/20296
|
||||
errorMessage = string.Format("Network error: could not connect to GitHub({0}). {1}", GitHubReleaseURL, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryFetchReleases), $"Error fetching release info.");
|
||||
}
|
||||
catch (SerializationException exception)
|
||||
{
|
||||
errorMessage = string.Format("Parse error: could not parse releases info from GitHub({0}). {1}", GitHubReleaseURL, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryFetchReleases), $"Error parsing release info.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void RunInstaller(string path, string args, string certCN, string issuerCN, out int exitCode, out string error)
|
||||
{
|
||||
using (Stream stream = this.fileSystem.OpenFileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, false))
|
||||
{
|
||||
string expectedCNPrefix = $"CN={certCN}, ";
|
||||
string expectecIssuerCNPrefix = $"CN={issuerCN}";
|
||||
string subject;
|
||||
string issuer;
|
||||
if (!GVFSPlatform.Instance.TryVerifyAuthenticodeSignature(path, out subject, out issuer, out error))
|
||||
{
|
||||
exitCode = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!subject.StartsWith(expectedCNPrefix) || !issuer.StartsWith(expectecIssuerCNPrefix))
|
||||
{
|
||||
exitCode = -1;
|
||||
error = $"Installer {path} is signed by unknown signer.";
|
||||
this.tracer.RelatedError($"Installer {path} is signed by unknown signer. Signed by {subject}, issued by {issuer} expected signer is {certCN}, issuer {issuerCN}.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.RunInstaller(path, args, out exitCode, out error);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryGetGitVersion(out GitVersion gitVersion, out string error)
|
||||
{
|
||||
error = null;
|
||||
|
||||
foreach (Asset asset in this.newestRelease.Assets)
|
||||
{
|
||||
if (asset.Name.StartsWith(GitInstallerFileNamePrefix) &&
|
||||
GitVersion.TryParseInstallerName(asset.Name, GVFSPlatform.Instance.Constants.InstallerExtension, out gitVersion))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
error = "Could not find Git version info in newest release";
|
||||
gitVersion = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryInstallUpgrade(string assetId, string version, out string consoleError)
|
||||
{
|
||||
bool installSuccess = false;
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Upgrade Step", nameof(this.TryInstallUpgrade));
|
||||
metadata.Add("AssetId", assetId);
|
||||
metadata.Add("Version", version);
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity($"{nameof(this.TryInstallUpgrade)}", EventLevel.Informational, metadata))
|
||||
{
|
||||
if (!this.TryRunInstaller(assetId, out installSuccess, out consoleError) ||
|
||||
!installSuccess)
|
||||
{
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.TryInstallUpgrade)} failed. {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("Successfully installed GVFS version: " + version);
|
||||
}
|
||||
|
||||
return installSuccess;
|
||||
}
|
||||
|
||||
private bool TryRunInstaller(string assetId, out bool installationSucceeded, out string error)
|
||||
{
|
||||
error = null;
|
||||
installationSucceeded = false;
|
||||
|
||||
int exitCode = 0;
|
||||
bool launched = this.TryRunInstallerForAsset(assetId, out exitCode, out error);
|
||||
installationSucceeded = exitCode == 0;
|
||||
|
||||
return launched;
|
||||
}
|
||||
|
||||
private bool TryRunInstallerForAsset(string assetId, out int installerExitCode, out string error)
|
||||
{
|
||||
error = null;
|
||||
installerExitCode = 0;
|
||||
|
||||
bool installerIsRun = false;
|
||||
string path;
|
||||
string installerArgs;
|
||||
if (this.TryGetLocalInstallerPath(assetId, out path, out installerArgs))
|
||||
{
|
||||
if (!this.dryRun)
|
||||
{
|
||||
string logFilePath = GVFSEnlistment.GetNewLogFileName(
|
||||
ProductUpgraderInfo.GetLogDirectoryPath(),
|
||||
Path.GetFileNameWithoutExtension(path),
|
||||
this.UpgradeInstanceId,
|
||||
this.fileSystem);
|
||||
|
||||
string args = installerArgs + " /Log=" + logFilePath;
|
||||
string certCN = null;
|
||||
string issuerCN = null;
|
||||
switch (assetId)
|
||||
{
|
||||
case GVFSAssetId:
|
||||
{
|
||||
certCN = GVFSSigner;
|
||||
issuerCN = GVFSCertIssuer;
|
||||
break;
|
||||
}
|
||||
|
||||
case GitAssetId:
|
||||
{
|
||||
certCN = GitSigner;
|
||||
issuerCN = GitCertIssuer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.RunInstaller(path, args, certCN, issuerCN, out installerExitCode, out error);
|
||||
|
||||
if (installerExitCode != 0 && string.IsNullOrEmpty(error))
|
||||
{
|
||||
error = assetId + " installer failed. Error log: " + logFilePath;
|
||||
}
|
||||
}
|
||||
|
||||
installerIsRun = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = "Could not find downloaded installer for " + assetId;
|
||||
}
|
||||
|
||||
return installerIsRun;
|
||||
}
|
||||
|
||||
private bool TryGetLocalInstallerPath(string assetId, out string path, out string args)
|
||||
{
|
||||
foreach (Asset asset in this.newestRelease.Assets)
|
||||
{
|
||||
if (string.Equals(Path.GetExtension(asset.Name), GVFSPlatform.Instance.Constants.InstallerExtension, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
path = asset.LocalPath;
|
||||
if (assetId == GitAssetId && this.IsGitAsset(asset))
|
||||
{
|
||||
args = GitInstallerArgs;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (assetId == GVFSAssetId && this.IsGVFSAsset(asset))
|
||||
{
|
||||
args = GVFSInstallerArgs;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
path = null;
|
||||
args = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsGVFSAsset(Asset asset)
|
||||
{
|
||||
return this.AssetInstallerNameCompare(asset, GVFSInstallerFileNamePrefixCandidates);
|
||||
}
|
||||
|
||||
private bool IsGitAsset(Asset asset)
|
||||
{
|
||||
return this.AssetInstallerNameCompare(asset, new string[] { GitInstallerFileNamePrefix });
|
||||
}
|
||||
|
||||
private bool AssetInstallerNameCompare(Asset asset, IEnumerable<string> expectedFileNamePrefixes)
|
||||
{
|
||||
foreach (string fileNamePrefix in expectedFileNamePrefixes)
|
||||
{
|
||||
if (asset.Name.StartsWith(fileNamePrefix, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void LogVersionInfo(
|
||||
Version gvfsVersion,
|
||||
GitVersion gitVersion,
|
||||
string message)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add(nameof(gvfsVersion), gvfsVersion.ToString());
|
||||
metadata.Add(nameof(gitVersion), gitVersion.ToString());
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, message, metadata);
|
||||
}
|
||||
|
||||
public class GitHubUpgraderConfig
|
||||
{
|
||||
public GitHubUpgraderConfig(ITracer tracer, LocalGVFSConfig localGVFSConfig)
|
||||
{
|
||||
this.Tracer = tracer;
|
||||
this.LocalConfig = localGVFSConfig;
|
||||
}
|
||||
|
||||
public enum RingType
|
||||
{
|
||||
// The values here should be ascending.
|
||||
// Invalid - User has set an incorrect ring
|
||||
// NoConfig - User has Not set any ring yet
|
||||
// None - User has set a valid "None" ring
|
||||
// (Fast should be greater than Slow,
|
||||
// Slow should be greater than None, None greater than Invalid.)
|
||||
// This is required for the correct implementation of Ring based
|
||||
// upgrade logic.
|
||||
Invalid = 0,
|
||||
NoConfig = None - 1,
|
||||
None = 10,
|
||||
Slow = None + 1,
|
||||
Fast = Slow + 1,
|
||||
}
|
||||
|
||||
public RingType UpgradeRing { get; private set; }
|
||||
public LocalGVFSConfig LocalConfig { get; private set; }
|
||||
private ITracer Tracer { get; set; }
|
||||
|
||||
public bool TryLoad(out string error)
|
||||
{
|
||||
this.UpgradeRing = RingType.NoConfig;
|
||||
|
||||
string ringConfig = null;
|
||||
string loadError = "Could not read GVFS Config." + Environment.NewLine + GVFSConstants.UpgradeVerbMessages.SetUpgradeRingCommand;
|
||||
|
||||
if (!this.LocalConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.UpgradeRing, out ringConfig, out error))
|
||||
{
|
||||
error = loadError;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.ParseUpgradeRing(ringConfig);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ParseUpgradeRing(string ringConfig)
|
||||
{
|
||||
if (string.IsNullOrEmpty(ringConfig))
|
||||
{
|
||||
this.UpgradeRing = RingType.None;
|
||||
return;
|
||||
}
|
||||
|
||||
RingType ringType;
|
||||
if (Enum.TryParse(ringConfig, ignoreCase: true, result: out ringType) &&
|
||||
Enum.IsDefined(typeof(RingType), ringType) &&
|
||||
ringType != RingType.Invalid)
|
||||
{
|
||||
this.UpgradeRing = ringType;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.UpgradeRing = RingType.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
public bool ConfigError()
|
||||
{
|
||||
return this.UpgradeRing == RingType.Invalid;
|
||||
}
|
||||
|
||||
public bool UpgradeAllowed(out string message)
|
||||
{
|
||||
if (this.UpgradeRing == RingType.Slow || this.UpgradeRing == RingType.Fast)
|
||||
{
|
||||
message = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
this.ConfigAlertMessage(out message);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ConfigAlertMessage(out string message)
|
||||
{
|
||||
message = null;
|
||||
|
||||
if (this.UpgradeRing == GitHubUpgraderConfig.RingType.None)
|
||||
{
|
||||
message = GVFSConstants.UpgradeVerbMessages.NoneRingConsoleAlert + Environment.NewLine + GVFSConstants.UpgradeVerbMessages.SetUpgradeRingCommand;
|
||||
}
|
||||
|
||||
if (this.UpgradeRing == GitHubUpgraderConfig.RingType.NoConfig)
|
||||
{
|
||||
message = GVFSConstants.UpgradeVerbMessages.NoRingConfigConsoleAlert + Environment.NewLine + GVFSConstants.UpgradeVerbMessages.SetUpgradeRingCommand;
|
||||
}
|
||||
|
||||
if (this.UpgradeRing == GitHubUpgraderConfig.RingType.Invalid)
|
||||
{
|
||||
string ring;
|
||||
string error;
|
||||
string prefix = string.Empty;
|
||||
if (this.LocalConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.UpgradeRing, out ring, out error))
|
||||
{
|
||||
prefix = $"Invalid upgrade ring `{ring}` specified in gvfs config. ";
|
||||
}
|
||||
|
||||
message = prefix + Environment.NewLine + GVFSConstants.UpgradeVerbMessages.SetUpgradeRingCommand;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DataContract(Name = "asset")]
|
||||
protected class Asset
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "size")]
|
||||
public long Size { get; set; }
|
||||
|
||||
[DataMember(Name = "browser_download_url")]
|
||||
public Uri DownloadURL { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public string LocalPath { get; set; }
|
||||
}
|
||||
|
||||
[DataContract(Name = "release")]
|
||||
protected class Release
|
||||
{
|
||||
[DataMember(Name = "name")]
|
||||
public string Name { get; set; }
|
||||
|
||||
[DataMember(Name = "tag_name")]
|
||||
public string Tag { get; set; }
|
||||
|
||||
[DataMember(Name = "prerelease")]
|
||||
public bool PreRelease { get; set; }
|
||||
|
||||
[DataMember(Name = "assets")]
|
||||
public List<Asset> Assets { get; set; }
|
||||
|
||||
[IgnoreDataMember]
|
||||
public GitHubUpgraderConfig.RingType Ring
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.PreRelease == true ? GitHubUpgraderConfig.RingType.Fast : GitHubUpgraderConfig.RingType.Slow;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryParseVersion(out Version version)
|
||||
{
|
||||
version = null;
|
||||
|
||||
if (this.Tag.StartsWith("v", StringComparison.CurrentCultureIgnoreCase))
|
||||
{
|
||||
return Version.TryParse(this.Tag.Substring(1), out version);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.Upgrader
|
||||
{
|
||||
public class InstallerPreRunChecker
|
||||
{
|
||||
private ITracer tracer;
|
||||
|
||||
public InstallerPreRunChecker(ITracer tracer, string commandToRerun)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.CommandToRerun = commandToRerun;
|
||||
}
|
||||
|
||||
protected string CommandToRerun { private get; set; }
|
||||
|
||||
public virtual bool TryRunPreUpgradeChecks(out string consoleError)
|
||||
{
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryRunPreUpgradeChecks), EventLevel.Informational))
|
||||
{
|
||||
if (this.IsUnattended())
|
||||
{
|
||||
consoleError = $"{GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported in unattended mode";
|
||||
this.tracer.RelatedWarning($"{nameof(this.TryRunPreUpgradeChecks)}: {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.IsGVFSUpgradeAllowed(out consoleError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo($"Successfully finished pre upgrade checks. Okay to run {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade}.");
|
||||
}
|
||||
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: Move repo mount calls to GVFS.Upgrader project.
|
||||
// https://github.com/Microsoft/VFSForGit/issues/293
|
||||
public virtual bool TryMountAllGVFSRepos(out string consoleError)
|
||||
{
|
||||
return this.TryRunGVFSWithArgs("service --mount-all", out consoleError);
|
||||
}
|
||||
|
||||
public virtual bool TryUnmountAllGVFSRepos(out string consoleError)
|
||||
{
|
||||
consoleError = null;
|
||||
this.tracer.RelatedInfo("Unmounting any mounted GVFS repositories.");
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryUnmountAllGVFSRepos), EventLevel.Informational))
|
||||
{
|
||||
if (!this.TryRunGVFSWithArgs("service --unmount-all", out consoleError))
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.TryUnmountAllGVFSRepos)}: {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("Successfully unmounted repositories.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual bool IsInstallationBlockedByRunningProcess(out string consoleError)
|
||||
{
|
||||
consoleError = null;
|
||||
|
||||
// While checking for blocking processes like GVFS.Mount immediately after un-mounting,
|
||||
// then sometimes GVFS.Mount shows up as running. But if the check is done after waiting
|
||||
// for some time, then eventually GVFS.Mount goes away. The retry loop below is to help
|
||||
// account for this delay between the time un-mount call returns and when GVFS.Mount
|
||||
// actually quits.
|
||||
this.tracer.RelatedInfo("Checking if GVFS or dependent processes are running.");
|
||||
int retryCount = 10;
|
||||
HashSet<string> processList = null;
|
||||
while (retryCount > 0)
|
||||
{
|
||||
if (!this.IsBlockingProcessRunning(out processList))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Thread.Sleep(TimeSpan.FromMilliseconds(250));
|
||||
retryCount--;
|
||||
}
|
||||
|
||||
if (processList.Count > 0)
|
||||
{
|
||||
consoleError = string.Join(
|
||||
Environment.NewLine,
|
||||
"Blocking processes are running.",
|
||||
$"Run {this.CommandToRerun} again after quitting these processes - " + string.Join(", ", processList.ToArray()));
|
||||
this.tracer.RelatedWarning($"{nameof(this.IsInstallationBlockedByRunningProcess)}: {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool IsElevated()
|
||||
{
|
||||
return GVFSPlatform.Instance.IsElevated();
|
||||
}
|
||||
|
||||
protected virtual bool IsGVFSUpgradeSupported()
|
||||
{
|
||||
return GVFSPlatform.Instance.KernelDriver.IsGVFSUpgradeSupported();
|
||||
}
|
||||
|
||||
protected virtual bool IsServiceInstalledAndNotRunning()
|
||||
{
|
||||
GVFSPlatform.Instance.IsServiceInstalledAndRunning(GVFSConstants.Service.ServiceName, out bool isInstalled, out bool isRunning);
|
||||
|
||||
return isInstalled && !isRunning;
|
||||
}
|
||||
|
||||
protected virtual bool IsUnattended()
|
||||
{
|
||||
return GVFSEnlistment.IsUnattended(this.tracer);
|
||||
}
|
||||
|
||||
protected virtual bool IsBlockingProcessRunning(out HashSet<string> processes)
|
||||
{
|
||||
int currentProcessId = Process.GetCurrentProcess().Id;
|
||||
Process[] allProcesses = Process.GetProcesses();
|
||||
HashSet<string> matchingNames = new HashSet<string>();
|
||||
|
||||
foreach (Process process in allProcesses)
|
||||
{
|
||||
if (process.Id == currentProcessId || !GVFSPlatform.Instance.Constants.UpgradeBlockingProcesses.Contains(process.ProcessName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
matchingNames.Add(process.ProcessName + " pid:" + process.Id);
|
||||
}
|
||||
|
||||
processes = matchingNames;
|
||||
return processes.Count > 0;
|
||||
}
|
||||
|
||||
protected virtual bool TryRunGVFSWithArgs(string args, out string consoleError)
|
||||
{
|
||||
string gvfsDirectory = ProcessHelper.GetProgramLocation(GVFSPlatform.Instance.Constants.ProgramLocaterCommand, GVFSPlatform.Instance.Constants.GVFSExecutableName);
|
||||
if (!string.IsNullOrEmpty(gvfsDirectory))
|
||||
{
|
||||
string gvfsPath = Path.Combine(gvfsDirectory, GVFSPlatform.Instance.Constants.GVFSExecutableName);
|
||||
|
||||
ProcessResult processResult = ProcessHelper.Run(gvfsPath, args);
|
||||
if (processResult.ExitCode == 0)
|
||||
{
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
consoleError = string.IsNullOrEmpty(processResult.Errors) ? $"`gvfs {args}` failed." : processResult.Errors;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
consoleError = $"Could not locate {GVFSPlatform.Instance.Constants.GVFSExecutableName}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsGVFSUpgradeAllowed(out string consoleError)
|
||||
{
|
||||
bool isConfirmed = string.Equals(this.CommandToRerun, GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage, StringComparison.OrdinalIgnoreCase);
|
||||
string adviceText = null;
|
||||
if (!this.IsElevated())
|
||||
{
|
||||
adviceText = GVFSPlatform.Instance.Constants.RunUpdateMessage;
|
||||
consoleError = string.Join(
|
||||
Environment.NewLine,
|
||||
"The installer needs to be run with elevated permissions.",
|
||||
adviceText);
|
||||
this.tracer.RelatedWarning($"{nameof(this.IsGVFSUpgradeAllowed)}: Upgrade is not installable. {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.IsGVFSUpgradeSupported())
|
||||
{
|
||||
consoleError = string.Join(
|
||||
Environment.NewLine,
|
||||
$"{GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is only supported after the \"Windows Projected File System\" optional feature has been enabled by a manual installation of VFS for Git, and only on versions of Windows that support this feature.",
|
||||
"Check your team's documentation for how to upgrade.");
|
||||
this.tracer.RelatedWarning(metadata: null, message:$"{nameof(this.IsGVFSUpgradeAllowed)}: Upgrade is not installable. {consoleError}", keywords: Keywords.Telemetry);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.IsServiceInstalledAndNotRunning())
|
||||
{
|
||||
adviceText = $"To install, run {GVFSPlatform.Instance.Constants.StartServiceCommandMessage} and run {GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage}.";
|
||||
consoleError = string.Join(
|
||||
Environment.NewLine,
|
||||
"GVFS Service is not running.",
|
||||
adviceText);
|
||||
this.tracer.RelatedWarning($"{nameof(this.IsGVFSUpgradeAllowed)}: Upgrade is not installable. {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
public class InstallActionInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Well known tokens that will be replaced when encountered in an Arguments field.
|
||||
/// </summary>
|
||||
public const string ManifestEntryInstallationIdToken = "installation_id";
|
||||
public const string ManifestEntryLogDirectoryToken = "log_directory";
|
||||
public const string ManifestEntryInstallerBaseDirectoryToken = "installer_base_path";
|
||||
|
||||
public InstallActionInfo(string name, string version, string args, string installerRelativePath, string command)
|
||||
{
|
||||
this.Name = name;
|
||||
this.Version = version;
|
||||
this.Args = args;
|
||||
this.InstallerRelativePath = installerRelativePath;
|
||||
this.Command = command;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The arguments that should be passed to the install command
|
||||
/// </summary>
|
||||
public string Args { get; }
|
||||
|
||||
/// <summary>
|
||||
/// User friendly name for the install action
|
||||
/// </summary>
|
||||
public string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The path to the application or document to start. The path
|
||||
/// is relative to the content directory of the NuGet package.
|
||||
/// </summary>
|
||||
public string InstallerRelativePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of the component that this entry installs
|
||||
/// </summary>
|
||||
public string Version { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The command to run. If this is present, the command is run
|
||||
/// directly (with the processed args), and the
|
||||
/// InstallerRelativePath property is ignored.
|
||||
/// </summary>
|
||||
public string Command { get; }
|
||||
}
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
/// <summary>
|
||||
/// Details on the upgrade included in this package, including information
|
||||
/// on what packages are included and how to install them.
|
||||
/// </summary>
|
||||
public class InstallManifest
|
||||
{
|
||||
public const string WindowsPlatformKey = "Windows";
|
||||
|
||||
public InstallManifest()
|
||||
{
|
||||
this.PlatformInstallManifests = new Dictionary<string, InstallManifestPlatform>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Install manifests for different platforms.
|
||||
/// </summary>
|
||||
public Dictionary<string, InstallManifestPlatform> PlatformInstallManifests { get; private set; }
|
||||
|
||||
public static InstallManifest FromJsonFile(string path)
|
||||
{
|
||||
using (StreamReader streamReader = File.OpenText(path))
|
||||
{
|
||||
return InstallManifest.FromJson(streamReader);
|
||||
}
|
||||
}
|
||||
|
||||
public static InstallManifest FromJsonString(string json)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<InstallManifest>(json);
|
||||
}
|
||||
|
||||
public static InstallManifest FromJson(StreamReader stream)
|
||||
{
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
return (InstallManifest)serializer.Deserialize(stream, typeof(InstallManifest));
|
||||
}
|
||||
|
||||
public void AddPlatformInstallManifest(string platform, IEnumerable<InstallActionInfo> entries)
|
||||
{
|
||||
InstallManifestPlatform platformManifest = new InstallManifestPlatform(entries);
|
||||
this.PlatformInstallManifests.Add(platform, platformManifest);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
public class InstallManifestPlatform
|
||||
{
|
||||
public InstallManifestPlatform()
|
||||
{
|
||||
this.InstallActions = new List<InstallActionInfo>();
|
||||
}
|
||||
|
||||
public InstallManifestPlatform(IEnumerable<InstallActionInfo> entries)
|
||||
{
|
||||
this.InstallActions = entries?.ToList() ?? new List<InstallActionInfo>();
|
||||
}
|
||||
|
||||
public List<InstallActionInfo> InstallActions { get; }
|
||||
}
|
||||
}
|
|
@ -1,278 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using NuGet.Commands;
|
||||
using NuGet.Common;
|
||||
using NuGet.Configuration;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Protocol;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles interactions with a NuGet Feed.
|
||||
/// </summary>
|
||||
public class NuGetFeed : IDisposable
|
||||
{
|
||||
// This is the SHA256 Certificate Thumbrint we expect packages from Microsoft to be signed with
|
||||
private const string TrustedMicrosoftCertFingerprintOld = "3F9001EA83C560D712C24CF213C3D312CB3BFF51EE89435D3430BD06B5D0EECE";
|
||||
private const string TrustedMicrosoftCertFingerprintNew = "AA12DA22A49BCE7D5C1AE64CC1F3D892F150DA76140F210ABD2CBFFCA2C18A27";
|
||||
|
||||
private readonly ITracer tracer;
|
||||
private readonly string feedUrl;
|
||||
private readonly string feedName;
|
||||
private readonly string downloadFolder;
|
||||
private readonly bool platformSupportsEncryption;
|
||||
|
||||
private SourceRepository sourceRepository;
|
||||
private string personalAccessToken;
|
||||
private SourceCacheContext sourceCacheContext;
|
||||
private ILogger nuGetLogger;
|
||||
|
||||
public NuGetFeed(
|
||||
string feedUrl,
|
||||
string feedName,
|
||||
string downloadFolder,
|
||||
string personalAccessToken,
|
||||
bool platformSupportsEncryption,
|
||||
ITracer tracer)
|
||||
{
|
||||
this.feedUrl = feedUrl;
|
||||
this.feedName = feedName;
|
||||
this.downloadFolder = downloadFolder;
|
||||
this.personalAccessToken = personalAccessToken;
|
||||
this.tracer = tracer;
|
||||
|
||||
// Configure the NuGet SourceCacheContext -
|
||||
// - Direct download packages - do not download to global
|
||||
// NuGet cache. This is set in NullSourceCacheContext.Instance
|
||||
// - NoCache - Do not cache package version lists
|
||||
this.sourceCacheContext = NullSourceCacheContext.Instance.Clone();
|
||||
this.sourceCacheContext.NoCache = true;
|
||||
this.platformSupportsEncryption = platformSupportsEncryption;
|
||||
|
||||
this.nuGetLogger = new Logger(this.tracer);
|
||||
this.SetSourceRepository();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
this.sourceRepository = null;
|
||||
this.sourceCacheContext?.Dispose();
|
||||
this.sourceCacheContext = null;
|
||||
}
|
||||
|
||||
public virtual void SetCredentials(string credential)
|
||||
{
|
||||
this.personalAccessToken = credential;
|
||||
|
||||
this.SetSourceRepository();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Query a NuGet feed for list of packages that match the packageId.
|
||||
/// </summary>
|
||||
/// <param name="packageId"></param>
|
||||
/// <returns>List of packages that match query parameters</returns>
|
||||
public virtual async Task<IList<IPackageSearchMetadata>> QueryFeedAsync(string packageId)
|
||||
{
|
||||
PackageMetadataResource packageMetadataResource = await this.sourceRepository.GetResourceAsync<PackageMetadataResource>();
|
||||
IEnumerable<IPackageSearchMetadata> queryResults = await packageMetadataResource.GetMetadataAsync(
|
||||
packageId,
|
||||
includePrerelease: false,
|
||||
includeUnlisted: false,
|
||||
sourceCacheContext: this.sourceCacheContext,
|
||||
log: this.nuGetLogger,
|
||||
token: CancellationToken.None);
|
||||
|
||||
return queryResults.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Download the specified packageId from the NuGet feed.
|
||||
/// </summary>
|
||||
/// <param name="packageId">PackageIdentity to download.</param>
|
||||
/// <returns>Path to the downloaded package.</returns>
|
||||
public virtual async Task<string> DownloadPackageAsync(PackageIdentity packageId)
|
||||
{
|
||||
string downloadPath = Path.Combine(this.downloadFolder, $"{this.feedName}.zip");
|
||||
PackageDownloadContext packageDownloadContext = new PackageDownloadContext(
|
||||
this.sourceCacheContext,
|
||||
this.downloadFolder,
|
||||
true);
|
||||
|
||||
DownloadResource downloadResource = await this.sourceRepository.GetResourceAsync<DownloadResource>();
|
||||
|
||||
using (DownloadResourceResult downloadResourceResult = await downloadResource.GetDownloadResourceResultAsync(
|
||||
packageId,
|
||||
packageDownloadContext,
|
||||
globalPackagesFolder: string.Empty,
|
||||
logger : this.nuGetLogger,
|
||||
token: CancellationToken.None))
|
||||
{
|
||||
if (downloadResourceResult.Status != DownloadResourceResultStatus.Available)
|
||||
{
|
||||
throw new Exception($"Download of NuGet package failed. DownloadResult Status: {downloadResourceResult.Status}");
|
||||
}
|
||||
|
||||
using (FileStream fileStream = File.Create(downloadPath))
|
||||
{
|
||||
downloadResourceResult.PackageStream.CopyTo(fileStream);
|
||||
}
|
||||
}
|
||||
|
||||
return downloadPath;
|
||||
}
|
||||
|
||||
public virtual bool VerifyPackage(string packagePath)
|
||||
{
|
||||
VerifyArgs verifyArgs = new VerifyArgs()
|
||||
{
|
||||
Verifications = new VerifyArgs.Verification[] { VerifyArgs.Verification.All },
|
||||
PackagePath = packagePath,
|
||||
CertificateFingerprint = new List<string>
|
||||
{
|
||||
TrustedMicrosoftCertFingerprintOld,
|
||||
TrustedMicrosoftCertFingerprintNew,
|
||||
},
|
||||
Logger = this.nuGetLogger
|
||||
};
|
||||
|
||||
VerifyCommandRunner verifyCommandRunner = new VerifyCommandRunner();
|
||||
int result = verifyCommandRunner.ExecuteCommandAsync(verifyArgs).Result;
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
protected static EventMetadata CreateEventMetadata(Exception e = null)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Area", nameof(NuGetFeed));
|
||||
if (e != null)
|
||||
{
|
||||
metadata.Add("Exception", e.ToString());
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static PackageSourceCredential BuildCredentialsFromPAT(string personalAccessToken, bool storePasswordInClearText)
|
||||
{
|
||||
// The storePasswordInClearText property is used to control whether the password
|
||||
// is written to NuGet config files in clear text or not. It also controls whether the
|
||||
// password is stored encrypted in memory or not. The ability to encrypt / decrypt the password
|
||||
// is not supported in non-windows platforms at this point.
|
||||
// We do not actually write out config files or store the password (except in memory). As in our
|
||||
// usage of NuGet functionality we do not write out config files, it is OK to not set this property
|
||||
// (with the tradeoff being the password is not encrypted in memory, and we need to make sure that new code
|
||||
// does not start to write out config files).
|
||||
return PackageSourceCredential.FromUserInput(
|
||||
"VfsForGitNugetUpgrader",
|
||||
"PersonalAccessToken",
|
||||
personalAccessToken,
|
||||
storePasswordInClearText: storePasswordInClearText);
|
||||
}
|
||||
|
||||
private void SetSourceRepository()
|
||||
{
|
||||
this.sourceRepository = Repository.Factory.GetCoreV3(this.feedUrl);
|
||||
if (!string.IsNullOrEmpty(this.personalAccessToken))
|
||||
{
|
||||
this.sourceRepository.PackageSource.Credentials = BuildCredentialsFromPAT(this.personalAccessToken, !this.platformSupportsEncryption);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation of logger used by NuGet library. It takes all output
|
||||
/// and redirects it to the GVFS logger.
|
||||
/// </summary>
|
||||
private class Logger : ILogger
|
||||
{
|
||||
private ITracer tracer;
|
||||
|
||||
public Logger(ITracer tracer)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
public void Log(LogLevel level, string data)
|
||||
{
|
||||
string message = $"NuGet Logger: ({level}): {data}";
|
||||
switch (level)
|
||||
{
|
||||
case LogLevel.Debug:
|
||||
case LogLevel.Verbose:
|
||||
case LogLevel.Minimal:
|
||||
case LogLevel.Information:
|
||||
this.tracer.RelatedInfo(message);
|
||||
break;
|
||||
case LogLevel.Warning:
|
||||
this.tracer.RelatedWarning(message);
|
||||
break;
|
||||
case LogLevel.Error:
|
||||
this.tracer.RelatedWarning(message);
|
||||
break;
|
||||
default:
|
||||
this.tracer.RelatedWarning(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Log(ILogMessage message)
|
||||
{
|
||||
this.Log(message.Level, message.Message);
|
||||
}
|
||||
|
||||
public Task LogAsync(LogLevel level, string data)
|
||||
{
|
||||
this.Log(level, data);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task LogAsync(ILogMessage message)
|
||||
{
|
||||
this.Log(message);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public void LogDebug(string data)
|
||||
{
|
||||
this.Log(LogLevel.Debug, data);
|
||||
}
|
||||
|
||||
public void LogError(string data)
|
||||
{
|
||||
this.Log(LogLevel.Error, data);
|
||||
}
|
||||
|
||||
public void LogInformation(string data)
|
||||
{
|
||||
this.Log(LogLevel.Information, data);
|
||||
}
|
||||
|
||||
public void LogInformationSummary(string data)
|
||||
{
|
||||
this.Log(LogLevel.Information, data);
|
||||
}
|
||||
|
||||
public void LogMinimal(string data)
|
||||
{
|
||||
this.Log(LogLevel.Minimal, data);
|
||||
}
|
||||
|
||||
public void LogVerbose(string data)
|
||||
{
|
||||
this.Log(LogLevel.Verbose, data);
|
||||
}
|
||||
|
||||
public void LogWarning(string data)
|
||||
{
|
||||
this.Log(LogLevel.Warning, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,799 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Threading;
|
||||
|
||||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
public class NuGetUpgrader : ProductUpgrader
|
||||
{
|
||||
protected readonly NuGetUpgraderConfig nuGetUpgraderConfig;
|
||||
protected Version highestVersionAvailable;
|
||||
|
||||
private const string ContentDirectoryName = "content";
|
||||
private const string InstallManifestFileName = "install-manifest.json";
|
||||
private const string ExtractedInstallerDirectoryName = "InstallerTemp";
|
||||
|
||||
private InstallManifest installManifest;
|
||||
private NuGetFeed nuGetFeed;
|
||||
private ICredentialStore credentialStore;
|
||||
private bool isNuGetFeedInitialized;
|
||||
|
||||
public NuGetUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
NuGetUpgraderConfig config,
|
||||
string downloadFolder,
|
||||
ICredentialStore credentialStore)
|
||||
: this(
|
||||
currentVersion,
|
||||
tracer,
|
||||
dryRun,
|
||||
noVerify,
|
||||
fileSystem,
|
||||
config,
|
||||
new NuGetFeed(
|
||||
config.FeedUrl,
|
||||
config.PackageFeedName,
|
||||
downloadFolder,
|
||||
null,
|
||||
GVFSPlatform.Instance.UnderConstruction.SupportsNuGetEncryption,
|
||||
tracer),
|
||||
credentialStore,
|
||||
GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(fileSystem, tracer))
|
||||
{
|
||||
}
|
||||
|
||||
internal NuGetUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
PhysicalFileSystem fileSystem,
|
||||
NuGetUpgraderConfig config,
|
||||
NuGetFeed nuGetFeed,
|
||||
ICredentialStore credentialStore,
|
||||
ProductUpgraderPlatformStrategy productUpgraderPlatformStrategy)
|
||||
: base(
|
||||
currentVersion,
|
||||
tracer,
|
||||
dryRun,
|
||||
noVerify,
|
||||
fileSystem,
|
||||
productUpgraderPlatformStrategy)
|
||||
{
|
||||
this.nuGetUpgraderConfig = config;
|
||||
|
||||
this.nuGetFeed = nuGetFeed;
|
||||
this.credentialStore = credentialStore;
|
||||
|
||||
// Extract the folder inside ProductUpgraderInfo.GetAssetDownloadsPath to ensure the
|
||||
// correct ACLs are in place
|
||||
this.ExtractedInstallerPath = Path.Combine(
|
||||
ProductUpgraderInfo.GetAssetDownloadsPath(),
|
||||
ExtractedInstallerDirectoryName);
|
||||
}
|
||||
|
||||
public string DownloadedPackagePath { get; private set; }
|
||||
|
||||
public override bool SupportsAnonymousVersionQuery { get => false; }
|
||||
|
||||
/// <summary>
|
||||
/// Path to unzip the downloaded upgrade package
|
||||
/// </summary>
|
||||
private string ExtractedInstallerPath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Try to load a NuGetUpgrader from config settings.
|
||||
/// <isConfigured>Flag to indicate whether the system is configured to use a NuGetUpgrader.
|
||||
/// A NuGetUpgrader can be set as the Upgrader to use, but it might not be properly configured.
|
||||
/// </isConfigured>
|
||||
/// <Returns>True if able to load a properly configured NuGetUpgrader<Returns>
|
||||
/// </summary>
|
||||
public static bool TryCreate(
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
LocalGVFSConfig gvfsConfig,
|
||||
ICredentialStore credentialStore,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
out NuGetUpgrader nuGetUpgrader,
|
||||
out bool isConfigured,
|
||||
out string error)
|
||||
{
|
||||
NuGetUpgraderConfig upgraderConfig = new NuGetUpgraderConfig(tracer, gvfsConfig);
|
||||
nuGetUpgrader = null;
|
||||
isConfigured = false;
|
||||
|
||||
if (!upgraderConfig.TryLoad(out error))
|
||||
{
|
||||
nuGetUpgrader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(isConfigured = upgraderConfig.IsConfigured(out error)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// At this point, we have determined that the system is set up to use
|
||||
// the NuGetUpgrader
|
||||
|
||||
if (!upgraderConfig.IsReady(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nuGetUpgrader = new NuGetUpgrader(
|
||||
ProcessHelper.GetCurrentProcessVersion(),
|
||||
tracer,
|
||||
fileSystem,
|
||||
dryRun,
|
||||
noVerify,
|
||||
upgraderConfig,
|
||||
ProductUpgraderInfo.GetAssetDownloadsPath(),
|
||||
credentialStore);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a replacement on well known strings in the arguments field of a manifest entry.
|
||||
/// </summary>
|
||||
/// <param name="src">The unprocessed string to use as arguments to an install command</param>
|
||||
/// <param name="installationId">A unique installer ID to replace the installer_id token with.</param>
|
||||
/// <returns>The argument string with tokens replaced.</returns>
|
||||
public static string ReplaceArgTokens(string src, string installationId, string logsDirectory, string installerBaseDirectory)
|
||||
{
|
||||
string dst = src
|
||||
.Replace(NuGetUpgrader.ReplacementToken(InstallActionInfo.ManifestEntryLogDirectoryToken), logsDirectory)
|
||||
.Replace(NuGetUpgrader.ReplacementToken(InstallActionInfo.ManifestEntryInstallationIdToken), installationId)
|
||||
.Replace(NuGetUpgrader.ReplacementToken(InstallActionInfo.ManifestEntryInstallerBaseDirectoryToken), installerBaseDirectory);
|
||||
return dst;
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
this.nuGetFeed?.Dispose();
|
||||
this.nuGetFeed = null;
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
public override bool UpgradeAllowed(out string message)
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.nuGetUpgraderConfig.FeedUrl))
|
||||
{
|
||||
message = "Nuget Feed URL has not been configured";
|
||||
return false;
|
||||
}
|
||||
else if (string.IsNullOrEmpty(this.nuGetUpgraderConfig.PackageFeedName))
|
||||
{
|
||||
message = "NuGet package feed has not been configured";
|
||||
return false;
|
||||
}
|
||||
|
||||
message = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryQueryNewestVersion(out Version newVersion, out string message)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.EnsureNuGetFeedInitialized(out message))
|
||||
{
|
||||
newVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
IList<IPackageSearchMetadata> queryResults = this.QueryFeed(firstAttempt: true);
|
||||
|
||||
// Find the package with the highest version
|
||||
IPackageSearchMetadata newestPackage = null;
|
||||
foreach (IPackageSearchMetadata result in queryResults)
|
||||
{
|
||||
if (newestPackage == null || result.Identity.Version > newestPackage.Identity.Version)
|
||||
{
|
||||
newestPackage = result;
|
||||
}
|
||||
}
|
||||
|
||||
if (newestPackage != null &&
|
||||
newestPackage.Identity.Version.Version > this.installedVersion)
|
||||
{
|
||||
this.highestVersionAvailable = newestPackage.Identity.Version.Version;
|
||||
}
|
||||
|
||||
newVersion = this.highestVersionAvailable;
|
||||
|
||||
if (newVersion != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - new version available: installedVersion: {this.installedVersion}, highestVersionAvailable: {newVersion}");
|
||||
message = $"New version {newestPackage.Identity.Version} is available.";
|
||||
return true;
|
||||
}
|
||||
else if (newestPackage != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - up-to-date");
|
||||
message = $"highest version available is {newestPackage.Identity.Version}, you are up-to-date";
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - no versions available from feed.");
|
||||
message = $"No versions available via feed.";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.TraceException(
|
||||
ex,
|
||||
nameof(this.TryQueryNewestVersion),
|
||||
"Exception encountered querying for newest version of upgrade package.");
|
||||
message = ex.Message;
|
||||
newVersion = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TryDownloadNewestVersion(out string errorMessage)
|
||||
{
|
||||
if (this.highestVersionAvailable == null)
|
||||
{
|
||||
// If we hit this code path, it indicates there was a
|
||||
// programmer error. The expectation is that this
|
||||
// method will only be called after
|
||||
// TryQueryNewestVersion has been called, and
|
||||
// indicates that a newer version is available.
|
||||
errorMessage = "No new version to download. Query for newest version to ensure a new version is available before downloading.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.EnsureNuGetFeedInitialized(out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryCreateAndConfigureDownloadDirectory(this.tracer, out errorMessage))
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(NuGetUpgrader)}.{nameof(this.TryCreateAndConfigureDownloadDirectory)} failed. {errorMessage}");
|
||||
return false;
|
||||
}
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryDownloadNewestVersion), EventLevel.Informational))
|
||||
{
|
||||
try
|
||||
{
|
||||
PackageIdentity packageId = this.GetPackageForVersion(this.highestVersionAvailable);
|
||||
|
||||
if (packageId == null)
|
||||
{
|
||||
errorMessage = $"The specified version {this.highestVersionAvailable} was not found in the NuGet feed. Please check with your administrator to make sure the feed is set up correctly.";
|
||||
return false;
|
||||
}
|
||||
|
||||
this.DownloadedPackagePath = this.nuGetFeed.DownloadPackageAsync(packageId).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.TraceException(
|
||||
activity,
|
||||
ex,
|
||||
nameof(this.TryDownloadNewestVersion),
|
||||
"Exception encountered downloading newest version of upgrade package.");
|
||||
errorMessage = ex.Message;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.noVerify)
|
||||
{
|
||||
if (!this.nuGetFeed.VerifyPackage(this.DownloadedPackagePath))
|
||||
{
|
||||
errorMessage = "Package signature validation failed. Check the upgrade logs for more details.";
|
||||
this.tracer.RelatedError(errorMessage);
|
||||
this.fileSystem.DeleteFile(this.DownloadedPackagePath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryCleanup(out string error)
|
||||
{
|
||||
return this.TryRecursivelyDeleteInstallerDirectory(out error);
|
||||
}
|
||||
|
||||
public override bool TryRunInstaller(InstallActionWrapper installActionWrapper, out string error)
|
||||
{
|
||||
string localError = null;
|
||||
int installerExitCode;
|
||||
bool installSuccessful = true;
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryRunInstaller), EventLevel.Informational))
|
||||
{
|
||||
InstallActionInfo currentInstallAction = null;
|
||||
try
|
||||
{
|
||||
string platformKey = GVFSPlatform.Instance.Name;
|
||||
|
||||
if (!this.TryRecursivelyDeleteInstallerDirectory(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.noVerify)
|
||||
{
|
||||
if (!this.nuGetFeed.VerifyPackage(this.DownloadedPackagePath))
|
||||
{
|
||||
error = "Package signature validation failed. Check the upgrade logs for more details.";
|
||||
activity.RelatedError(error);
|
||||
this.fileSystem.DeleteFile(this.DownloadedPackagePath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.UnzipPackage();
|
||||
this.installManifest = InstallManifest.FromJsonFile(Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName, InstallManifestFileName));
|
||||
if (!this.installManifest.PlatformInstallManifests.TryGetValue(platformKey, out InstallManifestPlatform platformInstallManifest) ||
|
||||
platformInstallManifest == null)
|
||||
{
|
||||
activity.RelatedError($"Extracted InstallManifest from JSON, but there was no entry for {platformKey}.");
|
||||
error = $"No entry in the manifest for the current platform ({platformKey}). Please verify the upgrade package.";
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo($"Extracted InstallManifest from JSON. InstallActions: {platformInstallManifest.InstallActions.Count}");
|
||||
|
||||
foreach (InstallActionInfo entry in platformInstallManifest.InstallActions)
|
||||
{
|
||||
currentInstallAction = entry;
|
||||
string installerBasePath = Path.Combine(this.ExtractedInstallerPath, ContentDirectoryName);
|
||||
|
||||
string args = entry.Args ?? string.Empty;
|
||||
|
||||
// Replace tokens on args
|
||||
string processedArgs = NuGetUpgrader.ReplaceArgTokens(args, this.UpgradeInstanceId, ProductUpgraderInfo.GetLogDirectoryPath(), $"\"{installerBasePath}\"");
|
||||
|
||||
activity.RelatedInfo(
|
||||
"Running install action: Name: {0}, Version: {1}, InstallerPath: {2}, Command: {3}, RawArgs: {4}, ProcessedArgs: {5}",
|
||||
entry.Name,
|
||||
entry.Version,
|
||||
entry.InstallerRelativePath ?? string.Empty,
|
||||
entry.Command ?? string.Empty,
|
||||
args,
|
||||
processedArgs);
|
||||
|
||||
string progressMessage = string.IsNullOrWhiteSpace(entry.Version) ?
|
||||
$"Running {entry.Name}" :
|
||||
$"Running {entry.Name} (version {entry.Version})";
|
||||
|
||||
installActionWrapper(
|
||||
() =>
|
||||
{
|
||||
if (!this.dryRun)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(entry.Command))
|
||||
{
|
||||
this.RunInstaller(entry.Command, processedArgs, out installerExitCode, out localError);
|
||||
}
|
||||
else
|
||||
{
|
||||
string installerPath = Path.Combine(installerBasePath, entry.InstallerRelativePath);
|
||||
this.RunInstaller(installerPath, processedArgs, out installerExitCode, out localError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We add a sleep here to ensure
|
||||
// the message for this install
|
||||
// action is written to the
|
||||
// console. Even though the
|
||||
// message is written with a delay
|
||||
// of 0, the messages are not
|
||||
// always written out. If / when
|
||||
// we can ensure that the message
|
||||
// is written out to console, then
|
||||
// we can remove this sleep.
|
||||
Thread.Sleep(1500);
|
||||
installerExitCode = 0;
|
||||
}
|
||||
|
||||
installSuccessful = installerExitCode == 0;
|
||||
|
||||
return installSuccessful;
|
||||
},
|
||||
progressMessage);
|
||||
|
||||
if (!installSuccessful)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
localError = ex.Message;
|
||||
installSuccessful = false;
|
||||
}
|
||||
|
||||
if (!installSuccessful)
|
||||
{
|
||||
this.SaveSystemInstallerLogs();
|
||||
|
||||
string installActionName = string.IsNullOrEmpty(currentInstallAction?.Name) ?
|
||||
"installer" :
|
||||
currentInstallAction.Name;
|
||||
|
||||
error = string.IsNullOrEmpty(localError) ?
|
||||
$"The {installActionName} failed, but no error message was provided by the failing command." :
|
||||
$"The {installActionName} failed with the following error: {localError}";
|
||||
|
||||
activity.RelatedError($"Could not complete all install actions. The following error was encountered: {error}");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
activity.RelatedInfo($"Install actions completed successfully.");
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected static EventMetadata CreateEventMetadata(Exception e = null)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Area", nameof(NuGetFeed));
|
||||
if (e != null)
|
||||
{
|
||||
metadata.Add("Exception", e.ToString());
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private static string ReplacementToken(string tokenString)
|
||||
{
|
||||
return "{" + tokenString + "}";
|
||||
}
|
||||
|
||||
private PackageIdentity GetPackageForVersion(Version version)
|
||||
{
|
||||
IList<IPackageSearchMetadata> queryResults = this.QueryFeed(firstAttempt: true);
|
||||
|
||||
IPackageSearchMetadata packageForVersion = null;
|
||||
foreach (IPackageSearchMetadata result in queryResults)
|
||||
{
|
||||
if (result.Identity.Version.Version == version)
|
||||
{
|
||||
packageForVersion = result;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return packageForVersion?.Identity;
|
||||
}
|
||||
|
||||
private bool TryGetPersonalAccessToken(string credentialUrl, ITracer tracer, out string token, out string error)
|
||||
{
|
||||
return this.credentialStore.TryGetCredential(this.tracer, credentialUrl, out string username, out token, out error);
|
||||
}
|
||||
|
||||
private bool TryReacquirePersonalAccessToken(string credentialUrl, ITracer tracer, out string token, out string error)
|
||||
{
|
||||
if (!this.credentialStore.TryDeleteCredential(this.tracer, credentialUrl, username: null, password: null, error: out error))
|
||||
{
|
||||
token = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.TryGetPersonalAccessToken(credentialUrl, tracer, out token, out error);
|
||||
}
|
||||
|
||||
private void UnzipPackage()
|
||||
{
|
||||
ZipFile.ExtractToDirectory(this.DownloadedPackagePath, this.ExtractedInstallerPath);
|
||||
}
|
||||
|
||||
private bool TryRecursivelyDeleteInstallerDirectory(out string error)
|
||||
{
|
||||
error = null;
|
||||
Exception e;
|
||||
if (!this.fileSystem.TryDeleteDirectory(this.ExtractedInstallerPath, out e))
|
||||
{
|
||||
if (e != null)
|
||||
{
|
||||
this.TraceException(
|
||||
e,
|
||||
nameof(this.TryRecursivelyDeleteInstallerDirectory),
|
||||
$"Exception encountered while deleting {this.ExtractedInstallerPath}.");
|
||||
}
|
||||
|
||||
error = e?.Message ?? "Failed to delete directory, but no error was specified.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private IList<IPackageSearchMetadata> QueryFeed(bool firstAttempt)
|
||||
{
|
||||
try
|
||||
{
|
||||
return this.nuGetFeed.QueryFeedAsync(this.nuGetUpgraderConfig.PackageFeedName).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception ex) when (firstAttempt &&
|
||||
this.IsAuthRelatedException(ex))
|
||||
{
|
||||
// If we fail to query the feed due to an authorization error, then it is possible we have stale
|
||||
// credentials, or credentials without the correct scope. Re-aquire fresh credentials and try again.
|
||||
EventMetadata data = CreateEventMetadata(ex);
|
||||
this.tracer.RelatedWarning(data, "Failed to query feed due to unauthorized error. Re-acquiring new credentials and trying again.");
|
||||
|
||||
if (!this.TryRefreshCredentials(out string error))
|
||||
{
|
||||
// If we were unable to re-acquire credentials, throw a new exception indicating that we tried to handle this, but were unable to.
|
||||
throw new Exception($"Failed to query the feed for upgrade packages due to: {ex.Message}, and was not able to re-acquire new credentials due to: {error}", ex);
|
||||
}
|
||||
|
||||
// Now that we have re-acquired credentials, try again - but with the retry flag set to false.
|
||||
return this.QueryFeed(firstAttempt: false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata data = CreateEventMetadata(ex);
|
||||
string message = $"Error encountered when querying NuGet feed. Is first attempt: {firstAttempt}.";
|
||||
this.tracer.RelatedWarning(data, message);
|
||||
throw new Exception($"Failed to query the NuGet package feed due to error: {ex.Message}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAuthRelatedException(Exception ex)
|
||||
{
|
||||
// In observation, we have seen either an HttpRequestException directly, or
|
||||
// a FatalProtocolException wrapping an HttpRequestException when we are not able
|
||||
// to auth against the NuGet feed.
|
||||
System.Net.Http.HttpRequestException httpRequestException = null;
|
||||
if (ex is System.Net.Http.HttpRequestException)
|
||||
{
|
||||
httpRequestException = ex as System.Net.Http.HttpRequestException;
|
||||
}
|
||||
else if (ex is FatalProtocolException &&
|
||||
ex.InnerException is System.Net.Http.HttpRequestException)
|
||||
{
|
||||
httpRequestException = ex.InnerException as System.Net.Http.HttpRequestException;
|
||||
}
|
||||
|
||||
if (httpRequestException != null &&
|
||||
(httpRequestException.Message.Contains("401") || httpRequestException.Message.Contains("403")))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TryRefreshCredentials(out string error)
|
||||
{
|
||||
try
|
||||
{
|
||||
string authUrl;
|
||||
if (!AzDevOpsOrgFromNuGetFeed.TryCreateCredentialQueryUrl(this.nuGetUpgraderConfig.FeedUrl, out authUrl, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryReacquirePersonalAccessToken(authUrl, this.tracer, out string token, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.nuGetFeed.SetCredentials(token);
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
error = ex.Message;
|
||||
this.TraceException(ex, nameof(this.TryRefreshCredentials), "Failed to refresh credentials.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool EnsureNuGetFeedInitialized(out string error)
|
||||
{
|
||||
if (!this.isNuGetFeedInitialized)
|
||||
{
|
||||
if (this.credentialStore == null)
|
||||
{
|
||||
throw new InvalidOperationException("Attempted to call method that requires authentication but no CredentialStore is configured.");
|
||||
}
|
||||
|
||||
string authUrl;
|
||||
if (!AzDevOpsOrgFromNuGetFeed.TryCreateCredentialQueryUrl(this.nuGetUpgraderConfig.FeedUrl, out authUrl, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryGetPersonalAccessToken(authUrl, this.tracer, out string token, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.nuGetFeed.SetCredentials(token);
|
||||
this.isNuGetFeedInitialized = true;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves a copy of installer log from platform native installer in the
|
||||
/// upgrader diagnose logs directory. On the Mac, it is not possible to
|
||||
/// specify custom log file path as command line arg to the installer.
|
||||
/// </summary>
|
||||
private void SaveSystemInstallerLogs()
|
||||
{
|
||||
if (GVFSPlatform.Instance.SupportsSystemInstallLog)
|
||||
{
|
||||
string systemInstallerLog = GVFSPlatform.Instance.GetSystemInstallerLogPath();
|
||||
if (!string.IsNullOrEmpty(systemInstallerLog))
|
||||
{
|
||||
string destinationPath = GVFSEnlistment.GetNewGVFSLogFileName(
|
||||
ProductUpgraderInfo.GetLogDirectoryPath(),
|
||||
GVFSConstants.LogFileTypes.UpgradeSystemInstaller,
|
||||
this.UpgradeInstanceId);
|
||||
|
||||
try
|
||||
{
|
||||
using (Stream sourceStream = this.fileSystem.OpenFileStream(
|
||||
systemInstallerLog,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.ReadWrite,
|
||||
callFlushFileBuffers: false))
|
||||
using (Stream destStream = this.fileSystem.OpenFileStream(
|
||||
destinationPath,
|
||||
FileMode.Create,
|
||||
FileAccess.Write,
|
||||
FileShare.None,
|
||||
callFlushFileBuffers: false))
|
||||
{
|
||||
// Copy the last 100K from the system wide installer log.
|
||||
// System wide installer log (/var/log/install.log) holds
|
||||
// all messages from installd, including the ones that
|
||||
// it generated while installing VFSForGit. We don't
|
||||
// need to capture the whole file, which can be lengthy.
|
||||
// From my testing, the last 100K captured immediately
|
||||
// after upgrade, is found to be lengthy enough to
|
||||
// contain all of Git + GCM + Service + VFSForGit
|
||||
// installer log messages.
|
||||
long hundredKB = 100 * 1024;
|
||||
long copyFromOffset = sourceStream.Length > hundredKB ? sourceStream.Length - hundredKB : 0;
|
||||
|
||||
sourceStream.Seek(copyFromOffset, SeekOrigin.Begin);
|
||||
sourceStream.CopyTo(destStream);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Exception", ex.ToString());
|
||||
this.tracer.RelatedError(
|
||||
metadata,
|
||||
$"{nameof(this.SaveSystemInstallerLogs)} - Error saving native installer log file.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.SaveSystemInstallerLogs)} - Error getting native installer log file path.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class NuGetUpgraderConfig
|
||||
{
|
||||
protected readonly ITracer tracer;
|
||||
protected readonly LocalGVFSConfig localConfig;
|
||||
|
||||
public NuGetUpgraderConfig(ITracer tracer, LocalGVFSConfig localGVFSConfig)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.localConfig = localGVFSConfig;
|
||||
}
|
||||
|
||||
public NuGetUpgraderConfig(
|
||||
ITracer tracer,
|
||||
LocalGVFSConfig localGVFSConfig,
|
||||
string feedUrl,
|
||||
string packageFeedName)
|
||||
: this(tracer, localGVFSConfig)
|
||||
{
|
||||
this.FeedUrl = feedUrl;
|
||||
this.PackageFeedName = packageFeedName;
|
||||
}
|
||||
|
||||
public string FeedUrl { get; private set; }
|
||||
public string PackageFeedName { get; private set; }
|
||||
public string CertificateFingerprint { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Check if the NuGetUpgrader is ready for use. A
|
||||
/// NuGetUpgrader is considered ready if all required
|
||||
/// config settings are present.
|
||||
/// </summary>
|
||||
public virtual bool IsReady(out string error)
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.FeedUrl) ||
|
||||
string.IsNullOrEmpty(this.PackageFeedName))
|
||||
{
|
||||
error = string.Join(
|
||||
Environment.NewLine,
|
||||
"One or more required settings for NuGetUpgrader are missing.",
|
||||
$"Use `gvfs config [{GVFSConstants.LocalGVFSConfig.UpgradeFeedUrl} | {GVFSConstants.LocalGVFSConfig.UpgradeFeedPackageName}] <value>` to set the config.");
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the NuGetUpgrader is configured.
|
||||
/// </summary>
|
||||
public virtual bool IsConfigured(out string error)
|
||||
{
|
||||
if (string.IsNullOrEmpty(this.FeedUrl) &&
|
||||
string.IsNullOrEmpty(this.PackageFeedName))
|
||||
{
|
||||
error = string.Join(
|
||||
Environment.NewLine,
|
||||
"NuGet upgrade server is not configured.",
|
||||
$"Use `gvfs config [ {GVFSConstants.LocalGVFSConfig.UpgradeFeedUrl} | {GVFSConstants.LocalGVFSConfig.UpgradeFeedPackageName}] <value>` to set the config.");
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to load the config for a NuGet upgrader. Returns false if there was an error reading the config.
|
||||
/// </summary>
|
||||
public virtual bool TryLoad(out string error)
|
||||
{
|
||||
string configValue;
|
||||
if (!this.localConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.UpgradeFeedUrl, out configValue, out error))
|
||||
{
|
||||
this.tracer.RelatedError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.FeedUrl = configValue;
|
||||
|
||||
if (!this.localConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.UpgradeFeedPackageName, out configValue, out error))
|
||||
{
|
||||
this.tracer.RelatedError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.PackageFeedName = configValue;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,244 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GVFS.Common.NuGetUpgrade
|
||||
{
|
||||
public class OrgNuGetUpgrader : NuGetUpgrader
|
||||
{
|
||||
private HttpClient httpClient;
|
||||
private string platform;
|
||||
|
||||
public OrgNuGetUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
HttpClient httpClient,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
OrgNuGetUpgraderConfig config,
|
||||
string downloadFolder,
|
||||
string platform,
|
||||
ICredentialStore credentialStore)
|
||||
: base(
|
||||
currentVersion,
|
||||
tracer,
|
||||
fileSystem,
|
||||
dryRun,
|
||||
noVerify,
|
||||
config,
|
||||
downloadFolder,
|
||||
credentialStore)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public OrgNuGetUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
HttpClient httpClient,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
OrgNuGetUpgraderConfig config,
|
||||
string platform,
|
||||
NuGetFeed nuGetFeed,
|
||||
ICredentialStore credentialStore)
|
||||
: base(
|
||||
currentVersion,
|
||||
tracer,
|
||||
dryRun,
|
||||
noVerify,
|
||||
fileSystem,
|
||||
config,
|
||||
nuGetFeed,
|
||||
credentialStore,
|
||||
GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(fileSystem, tracer))
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
public override bool SupportsAnonymousVersionQuery { get => true; }
|
||||
|
||||
private OrgNuGetUpgraderConfig Config { get => this.nuGetUpgraderConfig as OrgNuGetUpgraderConfig; }
|
||||
private string OrgInfoServerUrl { get => this.Config.OrgInfoServer; }
|
||||
private string Ring { get => this.Config.UpgradeRing; }
|
||||
|
||||
public static bool TryCreate(
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
LocalGVFSConfig gvfsConfig,
|
||||
HttpClient httpClient,
|
||||
ICredentialStore credentialStore,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
out OrgNuGetUpgrader upgrader,
|
||||
out string error)
|
||||
{
|
||||
OrgNuGetUpgraderConfig upgraderConfig = new OrgNuGetUpgraderConfig(tracer, gvfsConfig);
|
||||
upgrader = null;
|
||||
|
||||
if (!upgraderConfig.TryLoad(out error))
|
||||
{
|
||||
upgrader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!upgraderConfig.IsConfigured(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!upgraderConfig.IsReady(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string platform = GVFSPlatform.Instance.Name;
|
||||
|
||||
upgrader = new OrgNuGetUpgrader(
|
||||
ProcessHelper.GetCurrentProcessVersion(),
|
||||
tracer,
|
||||
fileSystem,
|
||||
httpClient,
|
||||
dryRun,
|
||||
noVerify,
|
||||
upgraderConfig,
|
||||
ProductUpgraderInfo.GetAssetDownloadsPath(),
|
||||
platform,
|
||||
credentialStore);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryQueryNewestVersion(out Version newVersion, out string message)
|
||||
{
|
||||
newVersion = null;
|
||||
|
||||
if (!AzDevOpsOrgFromNuGetFeed.TryParseOrg(this.Config.FeedUrl, out string orgName))
|
||||
{
|
||||
message = "OrgNuGetUpgrader is not able to parse org name from NuGet Package Feed URL";
|
||||
return false;
|
||||
}
|
||||
|
||||
OrgInfoApiClient infoServer = new OrgInfoApiClient(this.httpClient, this.OrgInfoServerUrl);
|
||||
|
||||
try
|
||||
{
|
||||
this.highestVersionAvailable = infoServer.QueryNewestVersion(orgName, this.platform, this.Ring);
|
||||
}
|
||||
catch (Exception exception) when (exception is HttpRequestException ||
|
||||
exception is TaskCanceledException)
|
||||
{
|
||||
// GetStringAsync can also throw a TaskCanceledException to indicate a timeout
|
||||
// https://github.com/dotnet/corefx/issues/20296
|
||||
message = string.Format("Network error: could not connect to server ({0}). {1}", this.OrgInfoServerUrl, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryQueryNewestVersion), "Error connecting to server.");
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (SerializationException exception)
|
||||
{
|
||||
message = string.Format("Parse error: could not parse response from server({0}). {1}", this.OrgInfoServerUrl, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryQueryNewestVersion), "Error parsing response from server.");
|
||||
|
||||
return false;
|
||||
}
|
||||
catch (Exception exception) when (exception is ArgumentException ||
|
||||
exception is FormatException ||
|
||||
exception is OverflowException)
|
||||
{
|
||||
message = string.Format("Unexpected response from server: could nor parse version({0}). {1}", this.OrgInfoServerUrl, exception.Message);
|
||||
this.TraceException(exception, nameof(this.TryQueryNewestVersion), "Error parsing response from server.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.highestVersionAvailable != null &&
|
||||
this.highestVersionAvailable > this.installedVersion)
|
||||
{
|
||||
newVersion = this.highestVersionAvailable;
|
||||
}
|
||||
|
||||
if (newVersion != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - new version available: installedVersion: {this.installedVersion}, highestVersionAvailable: {newVersion}");
|
||||
message = $"New version {newVersion} is available.";
|
||||
return true;
|
||||
}
|
||||
else if (this.highestVersionAvailable != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - up-to-date");
|
||||
message = $"Highest version available is {this.highestVersionAvailable}, you are up-to-date";
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.TryQueryNewestVersion)} - no versions available from feed.");
|
||||
message = "No versions available via endpoint.";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class OrgNuGetUpgraderConfig : NuGetUpgraderConfig
|
||||
{
|
||||
public OrgNuGetUpgraderConfig(ITracer tracer, LocalGVFSConfig localGVFSConfig)
|
||||
: base(tracer, localGVFSConfig)
|
||||
{
|
||||
}
|
||||
|
||||
public string OrgInfoServer { get; set; }
|
||||
|
||||
public string UpgradeRing { get; set; }
|
||||
|
||||
public override bool TryLoad(out string error)
|
||||
{
|
||||
if (!base.TryLoad(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.localConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.OrgInfoServerUrl, out string orgInfoServerUrl, out error))
|
||||
{
|
||||
this.tracer.RelatedError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.OrgInfoServer = orgInfoServerUrl;
|
||||
|
||||
if (!this.localConfig.TryGetConfig(GVFSConstants.LocalGVFSConfig.UpgradeRing, out string upgradeRing, out error))
|
||||
{
|
||||
this.tracer.RelatedError(error);
|
||||
return false;
|
||||
}
|
||||
|
||||
this.UpgradeRing = upgradeRing;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool IsReady(out string error)
|
||||
{
|
||||
if (!base.IsReady(out error) ||
|
||||
string.IsNullOrEmpty(this.UpgradeRing) ||
|
||||
string.IsNullOrEmpty(this.OrgInfoServer))
|
||||
{
|
||||
error = string.Join(
|
||||
Environment.NewLine,
|
||||
"One or more required settings for OrgNuGetUpgrader are missing.",
|
||||
"Use `gvfs config [{GVFSConstants.LocalGVFSConfig.UpgradeFeedUrl} | {GVFSConstants.LocalGVFSConfig.UpgradeFeedPackageName} | {GVFSConstants.LocalGVFSConfig.UpgradeRing} | {GVFSConstants.LocalGVFSConfig.OrgInfoServerUrl}] <value>` to set the config.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,258 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NuGetUpgrade;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Delegate to wrap install action steps in.
|
||||
/// This can be used to report the beginning / end of each install step.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to run inside wrapper</param>
|
||||
/// <param name="message">The message to display</param>
|
||||
/// <returns>success or failure return from the method run.</returns>
|
||||
public delegate bool InstallActionWrapper(Func<bool> method, string message);
|
||||
|
||||
public abstract class ProductUpgrader : IDisposable
|
||||
{
|
||||
protected readonly Version installedVersion;
|
||||
protected readonly ITracer tracer;
|
||||
protected readonly PhysicalFileSystem fileSystem;
|
||||
|
||||
protected bool noVerify;
|
||||
protected bool dryRun;
|
||||
protected ProductUpgraderPlatformStrategy productUpgraderPlatformStrategy;
|
||||
|
||||
protected ProductUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
PhysicalFileSystem fileSystem)
|
||||
: this(
|
||||
currentVersion,
|
||||
tracer,
|
||||
dryRun,
|
||||
noVerify,
|
||||
fileSystem,
|
||||
GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(fileSystem, tracer))
|
||||
{
|
||||
}
|
||||
|
||||
protected ProductUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
PhysicalFileSystem fileSystem,
|
||||
ProductUpgraderPlatformStrategy productUpgraderPlatformStrategy)
|
||||
{
|
||||
this.installedVersion = new Version(currentVersion);
|
||||
this.dryRun = dryRun;
|
||||
this.noVerify = noVerify;
|
||||
this.tracer = tracer;
|
||||
this.fileSystem = fileSystem;
|
||||
this.productUpgraderPlatformStrategy = productUpgraderPlatformStrategy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For mocking purposes only
|
||||
/// </summary>
|
||||
protected ProductUpgrader()
|
||||
{
|
||||
}
|
||||
|
||||
public abstract bool SupportsAnonymousVersionQuery { get; }
|
||||
|
||||
public string UpgradeInstanceId { get; set; } = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
|
||||
public static bool TryCreateUpgrader(
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
LocalGVFSConfig gvfsConfig,
|
||||
ICredentialStore credentialStore,
|
||||
bool dryRun,
|
||||
bool noVerify,
|
||||
out ProductUpgrader newUpgrader,
|
||||
out string error)
|
||||
{
|
||||
Dictionary<string, string> entries;
|
||||
if (!gvfsConfig.TryGetAllConfig(out entries, out error))
|
||||
{
|
||||
newUpgrader = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool containsUpgradeFeedUrl = entries.ContainsKey(GVFSConstants.LocalGVFSConfig.UpgradeFeedUrl);
|
||||
bool containsUpgradePackageName = entries.ContainsKey(GVFSConstants.LocalGVFSConfig.UpgradeFeedPackageName);
|
||||
bool containsOrgInfoServerUrl = entries.ContainsKey(GVFSConstants.LocalGVFSConfig.OrgInfoServerUrl);
|
||||
|
||||
if (containsUpgradeFeedUrl || containsUpgradePackageName)
|
||||
{
|
||||
// We are configured for NuGet - determine if we are using OrgNuGetUpgrader or not
|
||||
if (containsOrgInfoServerUrl)
|
||||
{
|
||||
if (OrgNuGetUpgrader.TryCreate(
|
||||
tracer,
|
||||
fileSystem,
|
||||
gvfsConfig,
|
||||
new HttpClient(),
|
||||
credentialStore,
|
||||
dryRun,
|
||||
noVerify,
|
||||
out OrgNuGetUpgrader orgNuGetUpgrader,
|
||||
out error))
|
||||
{
|
||||
// We were successfully able to load a NuGetUpgrader - use that.
|
||||
newUpgrader = orgNuGetUpgrader;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tracer.RelatedError($"{nameof(TryCreateUpgrader)}: Could not create organization based upgrader. {error}");
|
||||
newUpgrader = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NuGetUpgrader.TryCreate(
|
||||
tracer,
|
||||
fileSystem,
|
||||
gvfsConfig,
|
||||
credentialStore,
|
||||
dryRun,
|
||||
noVerify,
|
||||
out NuGetUpgrader nuGetUpgrader,
|
||||
out bool isConfigured,
|
||||
out error))
|
||||
{
|
||||
// We were successfully able to load a NuGetUpgrader - use that.
|
||||
newUpgrader = nuGetUpgrader;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tracer.RelatedError($"{nameof(TryCreateUpgrader)}: Could not create NuGet based upgrader. {error}");
|
||||
newUpgrader = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
newUpgrader = GitHubUpgrader.Create(tracer, fileSystem, gvfsConfig, dryRun, noVerify, out error);
|
||||
if (newUpgrader == null)
|
||||
{
|
||||
tracer.RelatedError($"{nameof(TryCreateUpgrader)}: Could not create GitHub based upgrader. {error}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool UpgradeAllowed(out string message);
|
||||
|
||||
public abstract bool TryQueryNewestVersion(out Version newVersion, out string message);
|
||||
|
||||
public abstract bool TryDownloadNewestVersion(out string errorMessage);
|
||||
|
||||
public abstract bool TryRunInstaller(InstallActionWrapper installActionWrapper, out string error);
|
||||
|
||||
public virtual bool TrySetupUpgradeApplicationDirectory(out string upgradeApplicationPath, out string error)
|
||||
{
|
||||
string upgradeApplicationDirectory = ProductUpgraderInfo.GetUpgradeApplicationDirectory();
|
||||
|
||||
if (!this.productUpgraderPlatformStrategy.TryPrepareApplicationDirectory(out error))
|
||||
{
|
||||
upgradeApplicationPath = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
string currentPath = ProcessHelper.GetCurrentProcessLocation();
|
||||
error = null;
|
||||
try
|
||||
{
|
||||
// Copying C:\Program Files\GVFS to inside of C:\Program Files\GVFS\ProgramData\GVFS.Upgrade\Tools
|
||||
// directory causes a cycle(at some point we start copying C:\Program Files\GVFS\ProgramData\GVFS.Upgrade
|
||||
// and its contents into C:\Program Files\GVFS\ProgramData\GVFS.Upgrade\Tools). The exclusion below is
|
||||
// added to avoid this loop.
|
||||
HashSet<string> directoriesToExclude = new HashSet<string>(GVFSPlatform.Instance.Constants.PathComparer);
|
||||
|
||||
string secureDataRoot = GVFSPlatform.Instance.GetSecureDataRootForGVFS();
|
||||
directoriesToExclude.Add(secureDataRoot);
|
||||
directoriesToExclude.Add(upgradeApplicationDirectory);
|
||||
|
||||
this.tracer.RelatedInfo($"Copying contents of '{currentPath}' to '{upgradeApplicationDirectory}',"
|
||||
+ $"excluding '{upgradeApplicationDirectory}' and '{secureDataRoot}'");
|
||||
|
||||
this.fileSystem.CopyDirectoryRecursive(currentPath, upgradeApplicationDirectory, directoriesToExclude);
|
||||
}
|
||||
catch (UnauthorizedAccessException e)
|
||||
{
|
||||
error = string.Join(
|
||||
Environment.NewLine,
|
||||
"File copy error - " + e.Message,
|
||||
$"Make sure you have write permissions to directory {upgradeApplicationDirectory} and run {GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage} again.");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
error = "File copy error - " + e.Message;
|
||||
this.TraceException(e, nameof(this.TrySetupUpgradeApplicationDirectory), $"Error copying {currentPath} to {upgradeApplicationDirectory}.");
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(error))
|
||||
{
|
||||
// There was no error - set upgradeToolPath and return success.
|
||||
upgradeApplicationPath = Path.Combine(
|
||||
upgradeApplicationDirectory,
|
||||
GVFSPlatform.Instance.Constants.GVFSUpgraderExecutableName);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Encountered error - do not set upgrade tool path and return failure.
|
||||
upgradeApplicationPath = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract bool TryCleanup(out string error);
|
||||
|
||||
public void TraceException(Exception exception, string method, string message)
|
||||
{
|
||||
this.TraceException(this.tracer, exception, method, message);
|
||||
}
|
||||
|
||||
public void TraceException(ITracer tracer, Exception exception, string method, string message)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Method", method);
|
||||
metadata.Add("Exception", exception.ToString());
|
||||
tracer.RelatedError(metadata, message);
|
||||
}
|
||||
|
||||
public virtual void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual bool TryCreateAndConfigureDownloadDirectory(ITracer tracer, out string error)
|
||||
{
|
||||
return this.productUpgraderPlatformStrategy.TryPrepareDownloadDirectory(out error);
|
||||
}
|
||||
|
||||
protected virtual void RunInstaller(string path, string args, out int exitCode, out string error)
|
||||
{
|
||||
ProcessResult processResult = ProcessHelper.Run(path, args);
|
||||
|
||||
exitCode = processResult.ExitCode;
|
||||
error = processResult.Errors;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,59 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public partial class ProductUpgraderInfo
|
||||
{
|
||||
public const string UpgradeDirectoryName = "GVFS.Upgrade";
|
||||
public const string LogDirectory = "UpgraderLogs";
|
||||
public const string DownloadDirectory = "Downloads";
|
||||
|
||||
/// <summary>
|
||||
/// This is the name of the directory that the Upgrader Application is copied to
|
||||
/// when running upgrade. It is called "Tools", as this is what the directory was
|
||||
/// originally named, but has since been renamed in code to be more descriptive.
|
||||
/// </summary>
|
||||
public const string ApplicationDirectory = "Tools";
|
||||
public const string HighestAvailableVersionFileName = "HighestAvailableVersion";
|
||||
|
||||
public static bool IsLocalUpgradeAvailable(ITracer tracer, string highestAvailableVersionDirectory)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.Exists(GetHighestAvailableVersionFilePath(highestAvailableVersionDirectory));
|
||||
}
|
||||
catch (Exception ex) when (
|
||||
ex is IOException ||
|
||||
ex is UnauthorizedAccessException ||
|
||||
ex is NotSupportedException)
|
||||
{
|
||||
if (tracer != null)
|
||||
{
|
||||
tracer.RelatedError(
|
||||
CreateEventMetadata(ex),
|
||||
"Exception encountered when determining if an upgrade is available.");
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static string GetHighestAvailableVersionFilePath(string highestAvailableVersionDirectory)
|
||||
{
|
||||
return Path.Combine(highestAvailableVersionDirectory, HighestAvailableVersionFileName);
|
||||
}
|
||||
|
||||
private static EventMetadata CreateEventMetadata(Exception e)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
if (e != null)
|
||||
{
|
||||
metadata.Add("Exception", e.ToString());
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public partial class ProductUpgraderInfo
|
||||
{
|
||||
private ITracer tracer;
|
||||
private PhysicalFileSystem fileSystem;
|
||||
|
||||
public ProductUpgraderInfo(ITracer tracer, PhysicalFileSystem fileSystem)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.fileSystem = fileSystem;
|
||||
}
|
||||
|
||||
public static string CurrentGVFSVersion()
|
||||
{
|
||||
return ProcessHelper.GetCurrentProcessVersion();
|
||||
}
|
||||
|
||||
public static string GetUpgradeProtectedDataDirectory()
|
||||
{
|
||||
return GVFSPlatform.Instance.GetUpgradeProtectedDataDirectory();
|
||||
}
|
||||
|
||||
public static string GetUpgradeApplicationDirectory()
|
||||
{
|
||||
return Path.Combine(
|
||||
GetUpgradeProtectedDataDirectory(),
|
||||
ProductUpgraderInfo.ApplicationDirectory);
|
||||
}
|
||||
|
||||
public static string GetParentLogDirectoryPath()
|
||||
{
|
||||
return GVFSPlatform.Instance.GetUpgradeLogDirectoryParentDirectory();
|
||||
}
|
||||
|
||||
public static string GetLogDirectoryPath()
|
||||
{
|
||||
return Path.Combine(
|
||||
GVFSPlatform.Instance.GetUpgradeLogDirectoryParentDirectory(),
|
||||
ProductUpgraderInfo.LogDirectory);
|
||||
}
|
||||
|
||||
public static string GetAssetDownloadsPath()
|
||||
{
|
||||
return Path.Combine(
|
||||
GVFSPlatform.Instance.GetUpgradeProtectedDataDirectory(),
|
||||
ProductUpgraderInfo.DownloadDirectory);
|
||||
}
|
||||
|
||||
public static string GetHighestAvailableVersionDirectory()
|
||||
{
|
||||
return GVFSPlatform.Instance.GetUpgradeHighestAvailableVersionDirectory();
|
||||
}
|
||||
|
||||
public void DeleteAllInstallerDownloads()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.fileSystem.DeleteDirectory(GetAssetDownloadsPath());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (this.tracer != null)
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.DeleteAllInstallerDownloads)}: Could not remove directory: {ProductUpgraderInfo.GetAssetDownloadsPath()}.{ex.ToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void RecordHighestAvailableVersion(Version highestAvailableVersion)
|
||||
{
|
||||
string highestAvailableVersionFile = GetHighestAvailableVersionFilePath(GetHighestAvailableVersionDirectory());
|
||||
|
||||
if (highestAvailableVersion == null)
|
||||
{
|
||||
if (this.fileSystem.FileExists(highestAvailableVersionFile))
|
||||
{
|
||||
this.fileSystem.DeleteFile(highestAvailableVersionFile);
|
||||
|
||||
if (this.tracer != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.RecordHighestAvailableVersion)}: Deleted upgrade reminder marker file");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.fileSystem.WriteAllText(highestAvailableVersionFile, highestAvailableVersion.ToString());
|
||||
|
||||
if (this.tracer != null)
|
||||
{
|
||||
this.tracer.RelatedInfo($"{nameof(this.RecordHighestAvailableVersion)}: Created upgrade reminder marker file");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Common
|
||||
{
|
||||
public abstract class ProductUpgraderPlatformStrategy
|
||||
{
|
||||
public ProductUpgraderPlatformStrategy(PhysicalFileSystem fileSystem, ITracer tracer)
|
||||
{
|
||||
this.FileSystem = fileSystem;
|
||||
this.Tracer = tracer;
|
||||
}
|
||||
|
||||
protected PhysicalFileSystem FileSystem { get; }
|
||||
protected ITracer Tracer { get; }
|
||||
|
||||
public abstract bool TryPrepareLogDirectory(out string error);
|
||||
|
||||
public abstract bool TryPrepareApplicationDirectory(out string error);
|
||||
|
||||
public abstract bool TryPrepareDownloadDirectory(out string error);
|
||||
|
||||
protected void TraceException(Exception exception, string method, string message)
|
||||
{
|
||||
this.TraceException(this.Tracer, exception, method, message);
|
||||
}
|
||||
|
||||
protected void TraceException(ITracer tracer, Exception exception, string method, string message)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Method", method);
|
||||
metadata.Add("Exception", exception.ToString());
|
||||
tracer.RelatedError(metadata, message);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -59,9 +59,6 @@
|
|||
<Compile Include="..\GVFS.Common\ProcessResult.cs">
|
||||
<Link>Common\ProcessResult.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\ProductUpgraderInfo.Shared.cs">
|
||||
<Link>Common\ProductUpgraderInfo.Shared.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="..\GVFS.Common\Tracing\EventLevel.cs">
|
||||
<Link>Common\Tracing\EventLevel.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -37,19 +37,9 @@ namespace GVFS.Hooks.HooksPlatform
|
|||
return WindowsFileSystem.TryGetNormalizedPathImplementation(path, out normalizedPath, out errorMessage);
|
||||
}
|
||||
|
||||
public static string GetUpgradeHighestAvailableVersionDirectory()
|
||||
{
|
||||
return WindowsPlatform.GetUpgradeHighestAvailableVersionDirectoryImplementation();
|
||||
}
|
||||
|
||||
public static string GetGitGuiBlockedMessage()
|
||||
{
|
||||
return "To access the 'git gui' in a GVFS repo, please invoke 'git-gui.exe' instead.";
|
||||
}
|
||||
|
||||
public static string GetUpgradeReminderNotification()
|
||||
{
|
||||
return WindowsPlatform.GetUpgradeReminderNotificationImplementation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,8 +65,6 @@ namespace GVFS.Hooks
|
|||
{
|
||||
RunLockRequest(args, unattended, ReleaseGVFSLock);
|
||||
}
|
||||
|
||||
RunPostCommands(args, unattended);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -92,30 +90,6 @@ namespace GVFS.Hooks
|
|||
}
|
||||
}
|
||||
|
||||
private static void RunPostCommands(string[] args, bool unattended)
|
||||
{
|
||||
if (!unattended)
|
||||
{
|
||||
RemindUpgradeAvailable();
|
||||
}
|
||||
}
|
||||
|
||||
private static void RemindUpgradeAvailable()
|
||||
{
|
||||
// The idea is to generate a random number between 0 and 100. To make
|
||||
// sure that the reminder is displayed only 10% of the times a git
|
||||
// command is run, check that the random number is between 0 and 10,
|
||||
// which will have a probability of 10/100 == 10%.
|
||||
int reminderFrequency = 10;
|
||||
int randomValue = random.Next(0, 100);
|
||||
|
||||
if ((IsUpgradeMessageDeterministic() || randomValue <= reminderFrequency) &&
|
||||
ProductUpgraderInfo.IsLocalUpgradeAvailable(tracer: null, highestAvailableVersionDirectory: GVFSHooksPlatform.GetUpgradeHighestAvailableVersionDirectory()))
|
||||
{
|
||||
Console.WriteLine(Environment.NewLine + GVFSHooksPlatform.GetUpgradeReminderNotification());
|
||||
}
|
||||
}
|
||||
|
||||
private static void ExitWithError(params string[] messages)
|
||||
{
|
||||
foreach (string message in messages)
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
<ProjectReference Include="..\GVFS.ReadObjectHook\GVFS.ReadObjectHook.vcxproj" />
|
||||
<ProjectReference Include="..\GVFS.Service.UI\GVFS.Service.UI.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Service\GVFS.Service.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Upgrader\GVFS.Upgrader.csproj" />
|
||||
<ProjectReference Include="..\GVFS.VirtualFileSystemHook\GVFS.VirtualFileSystemHook.vcxproj" />
|
||||
<ProjectReference Include="..\GVFS\GVFS.csproj" />
|
||||
</ItemGroup>
|
||||
|
@ -50,7 +49,6 @@
|
|||
$(OutputPath)\GVFS.ReadObjectHook.exe;
|
||||
$(OutputPath)\GVFS.Service.exe;
|
||||
$(OutputPath)\GVFS.Service.UI.exe;
|
||||
$(OutputPath)\GVFS.Upgrader.exe;
|
||||
$(OutputPath)\GVFS.VirtualFileSystemHook.exe;
|
||||
$(OutputPath)\GVFS.Virtualization.dll;">
|
||||
<Authenticode>Microsoft400</Authenticode>
|
||||
|
|
|
@ -55,7 +55,6 @@ xcopy /Y /S %BUILD_OUT%\GVFS.Hooks\%MANAGED_OUT_FRAGMENT%\* %OUTPUT%
|
|||
xcopy /Y /S %BUILD_OUT%\GVFS.Mount\%MANAGED_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GVFS.Service\%MANAGED_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GVFS.Service.UI\%MANAGED_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GVFS.Upgrader\%MANAGED_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GitHooksLoader\%NATIVE_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GVFS.PostIndexChangedHook\%NATIVE_OUT_FRAGMENT%\* %OUTPUT%
|
||||
xcopy /Y /S %BUILD_OUT%\GVFS.ReadObjectHook\%NATIVE_OUT_FRAGMENT%\* %OUTPUT%
|
||||
|
|
|
@ -128,21 +128,6 @@ namespace GVFS.Platform.Windows
|
|||
return true;
|
||||
}
|
||||
|
||||
public static string GetUpgradeProtectedDataDirectoryImplementation()
|
||||
{
|
||||
return Path.Combine(GetSecureDataRootForGVFSImplementation(), ProductUpgraderInfo.UpgradeDirectoryName);
|
||||
}
|
||||
|
||||
public static string GetUpgradeHighestAvailableVersionDirectoryImplementation()
|
||||
{
|
||||
return GetUpgradeProtectedDataDirectoryImplementation();
|
||||
}
|
||||
|
||||
public static string GetUpgradeReminderNotificationImplementation()
|
||||
{
|
||||
return $"A new version of VFS for Git is available. Run {UpgradeConfirmMessage} from an elevated command prompt to upgrade.";
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
private static extern IntPtr GetStdHandle(StdHandle std);
|
||||
|
||||
|
|
|
@ -333,28 +333,11 @@ namespace GVFS.Platform.Windows
|
|||
}
|
||||
}
|
||||
|
||||
public override string GetUpgradeLogDirectoryParentDirectory()
|
||||
{
|
||||
return Path.Combine(
|
||||
this.GetCommonAppDataRootForGVFS(),
|
||||
ProductUpgraderInfo.UpgradeDirectoryName);
|
||||
}
|
||||
|
||||
public override string GetSystemInstallerLogPath()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public override string GetUpgradeHighestAvailableVersionDirectory()
|
||||
{
|
||||
return this.GetUpgradeProtectedDataDirectory();
|
||||
}
|
||||
|
||||
public override string GetUpgradeProtectedDataDirectory()
|
||||
{
|
||||
return GetUpgradeProtectedDataDirectoryImplementation();
|
||||
}
|
||||
|
||||
public override Dictionary<string, string> GetPhysicalDiskInfo(string path, bool sizeStatsOnly) => WindowsPhysicalDiskInfo.GetPhysicalDiskInfo(path, sizeStatsOnly);
|
||||
|
||||
public override bool IsConsoleOutputRedirectedToFile()
|
||||
|
@ -375,13 +358,6 @@ namespace GVFS.Platform.Windows
|
|||
return new WindowsFileBasedLock(fileSystem, tracer, lockPath);
|
||||
}
|
||||
|
||||
public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions(
|
||||
PhysicalFileSystem fileSystem,
|
||||
ITracer tracer)
|
||||
{
|
||||
return new WindowsProductUpgraderPlatformStrategy(fileSystem, tracer);
|
||||
}
|
||||
|
||||
public override bool TryGetGVFSEnlistmentRoot(string directory, out string enlistmentRoot, out string errorMessage)
|
||||
{
|
||||
return WindowsPlatform.TryGetGVFSEnlistmentRootImplementation(directory, out enlistmentRoot, out errorMessage);
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Platform.Windows
|
||||
{
|
||||
public class WindowsProductUpgraderPlatformStrategy : ProductUpgraderPlatformStrategy
|
||||
{
|
||||
public WindowsProductUpgraderPlatformStrategy(PhysicalFileSystem fileSystem, ITracer tracer)
|
||||
: base(fileSystem, tracer)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool TryPrepareLogDirectory(out string error)
|
||||
{
|
||||
// Under normal circumstances
|
||||
// ProductUpgraderInfo.GetLogDirectoryPath will have
|
||||
// already been created by GVFS.Service. If for some
|
||||
// reason it does not (e.g. the service failed to start),
|
||||
// we need to create
|
||||
// ProductUpgraderInfo.GetLogDirectoryPath() explicity to
|
||||
// ensure that it has the correct ACLs (so that both admin
|
||||
// and non-admin users can create log files). If the logs
|
||||
// directory does not already exist, this call could fail
|
||||
// when running as a non-elevated user.
|
||||
string createDirectoryError;
|
||||
if (!this.FileSystem.TryCreateDirectoryWithAdminAndUserModifyPermissions(ProductUpgraderInfo.GetLogDirectoryPath(), out createDirectoryError))
|
||||
{
|
||||
error = $"ERROR: Unable to create directory `{ProductUpgraderInfo.GetLogDirectoryPath()}`";
|
||||
error += $"\n{createDirectoryError}";
|
||||
error += $"\n\nTry running {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} from an elevated command prompt.";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryPrepareApplicationDirectory(out string error)
|
||||
{
|
||||
string upgradeApplicationDirectory = ProductUpgraderInfo.GetUpgradeApplicationDirectory();
|
||||
|
||||
Exception deleteDirectoryException;
|
||||
if (this.FileSystem.DirectoryExists(upgradeApplicationDirectory) &&
|
||||
!this.FileSystem.TryDeleteDirectory(upgradeApplicationDirectory, out deleteDirectoryException))
|
||||
{
|
||||
error = $"Failed to delete {upgradeApplicationDirectory} - {deleteDirectoryException.Message}";
|
||||
|
||||
this.TraceException(deleteDirectoryException, nameof(this.TryPrepareApplicationDirectory), $"Error deleting {upgradeApplicationDirectory}.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.FileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissions(
|
||||
this.Tracer,
|
||||
upgradeApplicationDirectory,
|
||||
out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryPrepareDownloadDirectory(out string error)
|
||||
{
|
||||
return this.FileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissions(
|
||||
this.Tracer,
|
||||
ProductUpgraderInfo.GetAssetDownloadsPath(),
|
||||
out error);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -305,16 +305,14 @@ namespace GVFS.Service
|
|||
|
||||
DirectorySecurity serviceDataRootSecurity = this.GetServiceDirectorySecurity(serviceDataRootPath);
|
||||
|
||||
// Create GVFS.Service and GVFS.Upgrade related directories (if they don't already exist)
|
||||
// Create GVFS.Service related directories (if they don't already exist)
|
||||
Directory.CreateDirectory(serviceDataRootPath, serviceDataRootSecurity);
|
||||
Directory.CreateDirectory(this.serviceDataLocation, serviceDataRootSecurity);
|
||||
Directory.CreateDirectory(ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(), serviceDataRootSecurity);
|
||||
|
||||
// Ensure the ACLs are set correctly on any files or directories that were already created (e.g. after upgrading VFS4G)
|
||||
Directory.SetAccessControl(serviceDataRootPath, serviceDataRootSecurity);
|
||||
|
||||
// Special rules for the upgrader logs, as non-elevated users need to be be able to write
|
||||
this.CreateAndConfigureLogDirectory(ProductUpgraderInfo.GetLogDirectoryPath());
|
||||
// Special rules for the Service.UI logs, as non-elevated users need to be be able to write
|
||||
this.CreateAndConfigureLogDirectory(GVFSPlatform.Instance.GetLogsDirectoryForGVFSComponent(GVFSConstants.Service.UIName));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,140 +0,0 @@
|
|||
using GVFS.Common.NuGetUpgrade;
|
||||
using GVFS.Tests.Should;
|
||||
using Newtonsoft.Json;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.UnitTests.Common
|
||||
{
|
||||
[TestFixture]
|
||||
public class JsonInstallManifestTests
|
||||
{
|
||||
private static int manifestEntryCount = 0;
|
||||
|
||||
[TestCase]
|
||||
public void CanReadExpectedJsonString()
|
||||
{
|
||||
string installManifestJsonString =
|
||||
@"
|
||||
{
|
||||
""Version"" : ""1"",
|
||||
""PlatformInstallManifests"" : {
|
||||
""Windows"": {
|
||||
""InstallActions"": [
|
||||
{
|
||||
""Name"" : ""Git"",
|
||||
""Version"" : ""2.19.0.1.34"",
|
||||
""InstallerRelativePath"" : ""Installers\\Windows\\G4W\\Git-2.19.0.gvfs.1.34.gc7fb556-64-bit.exe"",
|
||||
""Args"" : ""/VERYSILENT /CLOSEAPPLICATIONS""
|
||||
},
|
||||
{
|
||||
""Name"" : ""PostGitInstall script"",
|
||||
""InstallerRelativePath"" : ""Installers\\Windows\\GSD\\postinstall.ps1""
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
";
|
||||
InstallManifest installManifest = InstallManifest.FromJsonString(installManifestJsonString);
|
||||
|
||||
installManifest.ShouldNotBeNull();
|
||||
InstallManifestPlatform platformInstallManifest = installManifest.PlatformInstallManifests[InstallManifest.WindowsPlatformKey];
|
||||
platformInstallManifest.ShouldNotBeNull();
|
||||
platformInstallManifest.InstallActions.Count.ShouldEqual(2);
|
||||
|
||||
this.VerifyInstallActionInfo(
|
||||
platformInstallManifest.InstallActions[0],
|
||||
"Git",
|
||||
"2.19.0.1.34",
|
||||
"/VERYSILENT /CLOSEAPPLICATIONS",
|
||||
"Installers\\Windows\\G4W\\Git-2.19.0.gvfs.1.34.gc7fb556-64-bit.exe");
|
||||
|
||||
this.VerifyInstallActionInfo(
|
||||
platformInstallManifest.InstallActions[1],
|
||||
"PostGitInstall script",
|
||||
null,
|
||||
null,
|
||||
"Installers\\Windows\\GSD\\postinstall.ps1");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanDeserializeAndSerializeInstallManifest()
|
||||
{
|
||||
List<InstallActionInfo> entries = new List<InstallActionInfo>()
|
||||
{
|
||||
this.CreateInstallActionInfo(),
|
||||
this.CreateInstallActionInfo()
|
||||
};
|
||||
|
||||
InstallManifest installManifest = new InstallManifest();
|
||||
installManifest.AddPlatformInstallManifest(InstallManifest.WindowsPlatformKey, entries);
|
||||
|
||||
JsonSerializer serializer = new JsonSerializer();
|
||||
|
||||
using (MemoryStream ms = new MemoryStream())
|
||||
using (StreamWriter streamWriter = new StreamWriter(ms))
|
||||
using (JsonWriter jsWriter = new JsonTextWriter(streamWriter))
|
||||
{
|
||||
string output = JsonConvert.SerializeObject(installManifest);
|
||||
serializer.Serialize(jsWriter, installManifest);
|
||||
jsWriter.Flush();
|
||||
|
||||
ms.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
StreamReader streamReader = new StreamReader(ms);
|
||||
InstallManifest deserializedInstallManifest = InstallManifest.FromJson(streamReader);
|
||||
|
||||
this.VerifyInstallManifestsAreEqual(installManifest, deserializedInstallManifest);
|
||||
}
|
||||
}
|
||||
|
||||
private InstallActionInfo CreateInstallActionInfo()
|
||||
{
|
||||
int entrySuffix = manifestEntryCount++;
|
||||
return new InstallActionInfo(
|
||||
name: $"Installer{entrySuffix}",
|
||||
version: $"1.{entrySuffix}.1.2",
|
||||
args: $"/nodowngrade{entrySuffix}",
|
||||
installerRelativePath: $"installers/installer1{entrySuffix}",
|
||||
command: string.Empty);
|
||||
}
|
||||
|
||||
private void VerifyInstallManifestsAreEqual(InstallManifest expected, InstallManifest actual)
|
||||
{
|
||||
actual.PlatformInstallManifests.Count.ShouldEqual(expected.PlatformInstallManifests.Count, $"The number of platforms ({actual.PlatformInstallManifests.Count}) do not match the expected number of platforms ({expected.PlatformInstallManifests.Count}).");
|
||||
|
||||
foreach (KeyValuePair<string, InstallManifestPlatform> kvp in expected.PlatformInstallManifests)
|
||||
{
|
||||
this.VerifyPlatformManifestsAreEqual(kvp.Value, actual.PlatformInstallManifests[kvp.Key]);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyInstallActionInfo(
|
||||
InstallActionInfo actualEntry,
|
||||
string expectedName,
|
||||
string expectedVersion,
|
||||
string expectedArgs,
|
||||
string expectedInstallerRelativePath)
|
||||
{
|
||||
actualEntry.Name.ShouldEqual(expectedName, "InstallActionInfo name does not match expected value");
|
||||
actualEntry.Version.ShouldEqual(expectedVersion, "InstallActionInfo version does not match expected value");
|
||||
actualEntry.Args.ShouldEqual(expectedArgs, "InstallActionInfo Args does not match expected value");
|
||||
actualEntry.InstallerRelativePath.ShouldEqual(expectedInstallerRelativePath, "InstallActionInfo InstallerRelativePath does not match expected value");
|
||||
}
|
||||
|
||||
private void VerifyPlatformManifestsAreEqual(InstallManifestPlatform expected, InstallManifestPlatform actual)
|
||||
{
|
||||
actual.InstallActions.Count.ShouldEqual(expected.InstallActions.Count, $"The number of platforms ({actual.InstallActions.Count}) do not match the expected number of platforms ({expected.InstallActions.Count}).");
|
||||
|
||||
for (int i = 0; i < actual.InstallActions.Count; i++)
|
||||
{
|
||||
actual.InstallActions[i].Version.ShouldEqual(expected.InstallActions[i].Version);
|
||||
actual.InstallActions[i].Args.ShouldEqual(expected.InstallActions[i].Args);
|
||||
actual.InstallActions[i].Name.ShouldEqual(expected.InstallActions[i].Name);
|
||||
actual.InstallActions[i].InstallerRelativePath.ShouldEqual(expected.InstallActions[i].InstallerRelativePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,455 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NuGetUpgrade;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Category;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using GVFS.UnitTests.Mock.FileSystem;
|
||||
using Moq;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.UnitTests.Common.NuGetUpgrade
|
||||
{
|
||||
[TestFixture]
|
||||
public class NuGetUpgraderTests
|
||||
{
|
||||
protected const string OlderVersion = "1.0.1185.0";
|
||||
protected const string CurrentVersion = "1.5.1185.0";
|
||||
protected const string NewerVersion = "1.6.1185.0";
|
||||
protected const string NewerVersion2 = "1.7.1185.0";
|
||||
|
||||
protected const string NuGetFeedUrl = "https://pkgs.dev.azure.com/contoso/packages";
|
||||
protected const string NuGetFeedName = "feedNameValue";
|
||||
|
||||
protected static Exception httpRequestAuthException = new System.Net.Http.HttpRequestException("Response status code does not indicate success: 401 (Unauthorized).");
|
||||
protected static Exception fatalProtocolAuthException = new FatalProtocolException("Unable to load the service index for source.", httpRequestAuthException);
|
||||
|
||||
protected static Exception[] networkAuthFailures =
|
||||
{
|
||||
httpRequestAuthException,
|
||||
fatalProtocolAuthException
|
||||
};
|
||||
|
||||
protected NuGetUpgrader upgrader;
|
||||
protected MockTracer tracer;
|
||||
|
||||
protected NuGetUpgrader.NuGetUpgraderConfig upgraderConfig;
|
||||
|
||||
protected Mock<NuGetFeed> mockNuGetFeed;
|
||||
protected MockFileSystem mockFileSystem;
|
||||
protected Mock<ICredentialStore> mockCredentialManager;
|
||||
protected ProductUpgraderPlatformStrategy productUpgraderPlatformStrategy;
|
||||
|
||||
protected string downloadDirectoryPath = Path.Combine(
|
||||
$"mock:{Path.DirectorySeparatorChar}",
|
||||
ProductUpgraderInfo.UpgradeDirectoryName,
|
||||
ProductUpgraderInfo.DownloadDirectory);
|
||||
|
||||
protected delegate void DownloadPackageAsyncCallback(PackageIdentity packageIdentity);
|
||||
|
||||
public virtual ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformStrategy()
|
||||
{
|
||||
return new MockProductUpgraderPlatformStrategy(this.mockFileSystem, this.tracer);
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
this.upgraderConfig = new NuGetUpgrader.NuGetUpgraderConfig(this.tracer, null, NuGetFeedUrl, NuGetFeedName);
|
||||
|
||||
this.tracer = new MockTracer();
|
||||
|
||||
this.mockNuGetFeed = new Mock<NuGetFeed>(
|
||||
NuGetFeedUrl,
|
||||
NuGetFeedName,
|
||||
this.downloadDirectoryPath,
|
||||
null,
|
||||
GVFSPlatform.Instance.UnderConstruction.SupportsNuGetEncryption,
|
||||
this.tracer);
|
||||
this.mockNuGetFeed.Setup(feed => feed.SetCredentials(It.IsAny<string>()));
|
||||
|
||||
this.mockFileSystem = new MockFileSystem(
|
||||
new MockDirectory(
|
||||
Path.GetDirectoryName(this.downloadDirectoryPath),
|
||||
new[] { new MockDirectory(this.downloadDirectoryPath, null, null) },
|
||||
null));
|
||||
|
||||
this.mockCredentialManager = new Mock<ICredentialStore>();
|
||||
string credentialManagerString = "value";
|
||||
string emptyString = string.Empty;
|
||||
this.mockCredentialManager.Setup(foo => foo.TryGetCredential(It.IsAny<ITracer>(), It.IsAny<string>(), out credentialManagerString, out credentialManagerString, out credentialManagerString)).Returns(true);
|
||||
|
||||
this.productUpgraderPlatformStrategy = this.CreateProductUpgraderPlatformStrategy();
|
||||
|
||||
this.upgrader = new NuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
false,
|
||||
false,
|
||||
this.mockFileSystem,
|
||||
this.upgraderConfig,
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object,
|
||||
this.productUpgraderPlatformStrategy);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
this.mockNuGetFeed.Object.Dispose();
|
||||
this.tracer.Dispose();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TryQueryNewestVersion_NewVersionAvailable()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(It.IsAny<string>())).ReturnsAsync(availablePackages);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
// Assert that we found the newer version
|
||||
success.ShouldBeTrue();
|
||||
newVersion.ShouldNotBeNull();
|
||||
newVersion.ShouldEqual<Version>(new Version(NewerVersion));
|
||||
message.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TryQueryNewestVersion_MultipleNewVersionsAvailable()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion2)),
|
||||
};
|
||||
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(It.IsAny<string>())).ReturnsAsync(availablePackages);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
// Assert that we found the newest version
|
||||
success.ShouldBeTrue();
|
||||
newVersion.ShouldNotBeNull();
|
||||
newVersion.ShouldEqual<Version>(new Version(NewerVersion2));
|
||||
message.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TryQueryNewestVersion_NoNewerVersionsAvailable()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(OlderVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
};
|
||||
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(It.IsAny<string>())).ReturnsAsync(availablePackages);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
// Assert that no new version was returned
|
||||
success.ShouldBeTrue();
|
||||
newVersion.ShouldBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public void TryQueryNewestVersion_Exception()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(OlderVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
};
|
||||
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(It.IsAny<string>())).Throws(new Exception("Network Error"));
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
// Assert that no new version was returned
|
||||
success.ShouldBeFalse();
|
||||
newVersion.ShouldBeNull();
|
||||
message.ShouldNotBeNull();
|
||||
message.Any().ShouldBeTrue();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanDownloadNewestVersion()
|
||||
{
|
||||
Version actualNewestVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
string testDownloadPath = Path.Combine(this.downloadDirectoryPath, "testNuget.zip");
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(NuGetFeedName)).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.Is<PackageIdentity>(packageIdentity => packageIdentity == newestAvailableVersion.Identity))).ReturnsAsync(testDownloadPath);
|
||||
this.mockNuGetFeed.Setup(foo => foo.VerifyPackage(It.IsAny<string>())).Returns(true);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out actualNewestVersion, out message);
|
||||
|
||||
// Assert that no new version was returned
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
actualNewestVersion.ShouldEqual(newestAvailableVersion.Identity.Version.Version, "Actual new version does not match expected new version.");
|
||||
|
||||
bool downloadSuccessful = this.upgrader.TryDownloadNewestVersion(out message);
|
||||
downloadSuccessful.ShouldBeTrue();
|
||||
this.upgrader.DownloadedPackagePath.ShouldEqual(testDownloadPath);
|
||||
this.mockNuGetFeed.Verify(nuGetFeed => nuGetFeed.VerifyPackage(It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public void DownloadNewestVersion_HandleException()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(It.IsAny<string>())).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.IsAny<PackageIdentity>())).Throws(new Exception("Network Error"));
|
||||
this.mockNuGetFeed.Setup(foo => foo.VerifyPackage(It.IsAny<string>())).Returns(true);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
newVersion.ShouldNotBeNull();
|
||||
|
||||
bool downloadSuccessful = this.upgrader.TryDownloadNewestVersion(out message);
|
||||
downloadSuccessful.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void AttemptingToDownloadBeforeQueryingFails()
|
||||
{
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
|
||||
string downloadPath = "c:\\test_download_path";
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(NuGetFeedName)).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.Is<PackageIdentity>(packageIdentity => packageIdentity == newestAvailableVersion.Identity))).ReturnsAsync(downloadPath);
|
||||
|
||||
bool downloadSuccessful = this.upgrader.TryDownloadNewestVersion(out message);
|
||||
downloadSuccessful.ShouldBeFalse();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TestUpgradeAllowed()
|
||||
{
|
||||
// Properly Configured NuGet config
|
||||
NuGetUpgrader.NuGetUpgraderConfig nuGetUpgraderConfig =
|
||||
new NuGetUpgrader.NuGetUpgraderConfig(this.tracer, null, NuGetFeedUrl, NuGetFeedName);
|
||||
|
||||
NuGetUpgrader nuGetUpgrader = new NuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
false,
|
||||
false,
|
||||
this.mockFileSystem,
|
||||
nuGetUpgraderConfig,
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object,
|
||||
this.productUpgraderPlatformStrategy);
|
||||
|
||||
nuGetUpgrader.UpgradeAllowed(out _).ShouldBeTrue("NuGetUpgrader config is complete: upgrade should be allowed.");
|
||||
|
||||
// Empty FeedURL
|
||||
nuGetUpgraderConfig =
|
||||
new NuGetUpgrader.NuGetUpgraderConfig(this.tracer, null, string.Empty, NuGetFeedName);
|
||||
|
||||
nuGetUpgrader = new NuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
false,
|
||||
false,
|
||||
this.mockFileSystem,
|
||||
nuGetUpgraderConfig,
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object,
|
||||
this.productUpgraderPlatformStrategy);
|
||||
|
||||
nuGetUpgrader.UpgradeAllowed(out string _).ShouldBeFalse("Upgrade without FeedURL configured should not be allowed.");
|
||||
|
||||
// Empty packageFeedName
|
||||
nuGetUpgraderConfig =
|
||||
new NuGetUpgrader.NuGetUpgraderConfig(this.tracer, null, NuGetFeedUrl, string.Empty);
|
||||
|
||||
// Empty packageFeedName
|
||||
nuGetUpgrader = new NuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
false,
|
||||
false,
|
||||
this.mockFileSystem,
|
||||
nuGetUpgraderConfig,
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object,
|
||||
this.productUpgraderPlatformStrategy);
|
||||
|
||||
nuGetUpgrader.UpgradeAllowed(out string _).ShouldBeFalse("Upgrade without FeedName configured should not be allowed.");
|
||||
}
|
||||
|
||||
[TestCaseSource("networkAuthFailures")]
|
||||
public void QueryNewestVersionReacquiresCredentialsOnAuthFailure(Exception exception)
|
||||
{
|
||||
Version actualNewestVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
string testDownloadPath = Path.Combine(this.downloadDirectoryPath, "testNuget.zip");
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
this.mockNuGetFeed.SetupSequence(foo => foo.QueryFeedAsync(It.IsAny<string>()))
|
||||
.Throws(exception)
|
||||
.ReturnsAsync(availablePackages);
|
||||
|
||||
// Setup the credential manager
|
||||
string emptyString = string.Empty;
|
||||
this.mockCredentialManager.Setup(foo => foo.TryDeleteCredential(It.IsAny<ITracer>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), out emptyString)).Returns(true);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out actualNewestVersion, out message);
|
||||
|
||||
// Verify expectations
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
actualNewestVersion.ShouldEqual(newestAvailableVersion.Identity.Version.Version, "Actual new version does not match expected new version.");
|
||||
|
||||
this.mockNuGetFeed.Verify(nuGetFeed => nuGetFeed.QueryFeedAsync(It.IsAny<string>()), Times.Exactly(2));
|
||||
|
||||
string outString = string.Empty;
|
||||
this.mockCredentialManager.Verify(credentialManager => credentialManager.TryGetCredential(It.IsAny<ITracer>(), It.IsAny<string>(), out outString, out outString, out outString), Times.Exactly(2));
|
||||
this.mockCredentialManager.Verify(credentialManager => credentialManager.TryDeleteCredential(It.IsAny<ITracer>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), out outString), Times.Exactly(1));
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void WellKnownArgumentTokensReplaced()
|
||||
{
|
||||
string logDirectory = "mock:\\test_log_directory";
|
||||
string noTokenSourceString = "/arg no_token log_directory installation_id";
|
||||
NuGetUpgrader.ReplaceArgTokens(noTokenSourceString, "unique_id", logDirectory, "installerBase").ShouldEqual(noTokenSourceString, "String with no tokens should not be modifed");
|
||||
|
||||
string sourceStringWithTokens = "/arg /log {log_directory}_{installation_id}_{installer_base_path}";
|
||||
string expectedProcessedString = "/arg /log " + logDirectory + "_unique_id_installerBase";
|
||||
NuGetUpgrader.ReplaceArgTokens(sourceStringWithTokens, "unique_id", logDirectory, "installerBase").ShouldEqual(expectedProcessedString, "expected tokens have not been replaced");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DownloadFailsOnNuGetPackageVerificationFailure()
|
||||
{
|
||||
Version actualNewestVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
|
||||
string testDownloadPath = Path.Combine(this.downloadDirectoryPath, "testNuget.zip");
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(NuGetFeedName)).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.Is<PackageIdentity>(packageIdentity => packageIdentity == newestAvailableVersion.Identity)))
|
||||
.Callback(new DownloadPackageAsyncCallback(
|
||||
(packageIdentity) => this.mockFileSystem.WriteAllText(testDownloadPath, "Package contents that will fail validation")))
|
||||
.ReturnsAsync(testDownloadPath);
|
||||
this.mockNuGetFeed.Setup(foo => foo.VerifyPackage(It.IsAny<string>())).Returns(false);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out actualNewestVersion, out message);
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
actualNewestVersion.ShouldEqual(newestAvailableVersion.Identity.Version.Version, "Actual new version does not match expected new version.");
|
||||
|
||||
bool downloadSuccessful = this.upgrader.TryDownloadNewestVersion(out message);
|
||||
this.mockNuGetFeed.Verify(nuGetFeed => nuGetFeed.VerifyPackage(this.upgrader.DownloadedPackagePath), Times.Once());
|
||||
downloadSuccessful.ShouldBeFalse("Failure to verify NuGet package should cause download to fail.");
|
||||
this.mockFileSystem.FileExists(testDownloadPath).ShouldBeFalse("VerifyPackage should delete invalid packages");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DoNotVerifyNuGetPackageWhenNoVerifyIsSpecified()
|
||||
{
|
||||
NuGetUpgrader.NuGetUpgraderConfig nuGetUpgraderConfig =
|
||||
new NuGetUpgrader.NuGetUpgraderConfig(this.tracer, null, NuGetFeedUrl, NuGetFeedName);
|
||||
|
||||
NuGetUpgrader nuGetUpgrader = new NuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
false,
|
||||
true,
|
||||
this.mockFileSystem,
|
||||
nuGetUpgraderConfig,
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object,
|
||||
this.productUpgraderPlatformStrategy);
|
||||
|
||||
Version actualNewestVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
|
||||
string testDownloadPath = Path.Combine(this.downloadDirectoryPath, "testNuget.zip");
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(NuGetFeedName)).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.Is<PackageIdentity>(packageIdentity => packageIdentity == newestAvailableVersion.Identity))).ReturnsAsync(testDownloadPath);
|
||||
this.mockNuGetFeed.Setup(foo => foo.VerifyPackage(It.IsAny<string>())).Returns(false);
|
||||
|
||||
bool success = nuGetUpgrader.TryQueryNewestVersion(out actualNewestVersion, out message);
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
actualNewestVersion.ShouldEqual(newestAvailableVersion.Identity.Version.Version, "Actual new version does not match expected new version.");
|
||||
|
||||
bool downloadSuccessful = nuGetUpgrader.TryDownloadNewestVersion(out message);
|
||||
this.mockNuGetFeed.Verify(nuGetFeed => nuGetFeed.VerifyPackage(It.IsAny<string>()), Times.Never());
|
||||
downloadSuccessful.ShouldBeTrue("Should be able to download package with verification issues when noVerify is specified");
|
||||
}
|
||||
|
||||
protected IPackageSearchMetadata GeneratePackageSeachMetadata(Version version)
|
||||
{
|
||||
Mock<IPackageSearchMetadata> mockPackageSearchMetaData = new Mock<IPackageSearchMetadata>();
|
||||
NuGet.Versioning.NuGetVersion nuGetVersion = new NuGet.Versioning.NuGetVersion(version);
|
||||
mockPackageSearchMetaData.Setup(foo => foo.Identity).Returns(new NuGet.Packaging.Core.PackageIdentity("generatedPackedId", nuGetVersion));
|
||||
|
||||
return mockPackageSearchMetaData.Object;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,192 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NuGetUpgrade;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Category;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using GVFS.UnitTests.Mock.FileSystem;
|
||||
using Moq;
|
||||
using Moq.Protected;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GVFS.UnitTests.Common.NuGetUpgrade
|
||||
{
|
||||
[TestFixture]
|
||||
public class OrgNuGetUpgraderTests
|
||||
{
|
||||
private const string CurrentVersion = "1.5.1185.0";
|
||||
private const string NewerVersion = "1.6.1185.0";
|
||||
|
||||
private const string DefaultUpgradeFeedPackageName = "package";
|
||||
private const string DefaultUpgradeFeedUrl = "https://pkgs.dev.azure.com/contoso/";
|
||||
private const string DefaultOrgInfoServerUrl = "https://www.contoso.com";
|
||||
private const string DefaultRing = "slow";
|
||||
|
||||
private OrgNuGetUpgrader upgrader;
|
||||
|
||||
private MockTracer tracer;
|
||||
|
||||
private OrgNuGetUpgrader.OrgNuGetUpgraderConfig upgraderConfig;
|
||||
|
||||
private Mock<NuGetFeed> mockNuGetFeed;
|
||||
private MockFileSystem mockFileSystem;
|
||||
private Mock<ICredentialStore> mockCredentialManager;
|
||||
private Mock<HttpMessageHandler> httpMessageHandlerMock;
|
||||
|
||||
private string downloadDirectoryPath = Path.Combine(
|
||||
$"mock:{Path.DirectorySeparatorChar}",
|
||||
ProductUpgraderInfo.UpgradeDirectoryName,
|
||||
ProductUpgraderInfo.DownloadDirectory);
|
||||
|
||||
private interface IHttpMessageHandlerProtectedMembers
|
||||
{
|
||||
Task<HttpResponseMessage> SendAsync(HttpRequestMessage message, CancellationToken token);
|
||||
}
|
||||
|
||||
public static IEnumerable<Exception> NetworkFailureCases()
|
||||
{
|
||||
yield return new HttpRequestException("Response status code does not indicate success: 401: (Unauthorized)");
|
||||
yield return new TaskCanceledException("Task canceled");
|
||||
}
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
MockLocalGVFSConfig mockGvfsConfig = new MockLocalGVFSConfigBuilder(
|
||||
DefaultRing,
|
||||
DefaultUpgradeFeedUrl,
|
||||
DefaultUpgradeFeedPackageName,
|
||||
DefaultOrgInfoServerUrl)
|
||||
.WithUpgradeRing()
|
||||
.WithUpgradeFeedPackageName()
|
||||
.WithUpgradeFeedUrl()
|
||||
.WithOrgInfoServerUrl()
|
||||
.Build();
|
||||
|
||||
this.upgraderConfig = new OrgNuGetUpgrader.OrgNuGetUpgraderConfig(this.tracer, mockGvfsConfig);
|
||||
this.upgraderConfig.TryLoad(out _);
|
||||
|
||||
this.tracer = new MockTracer();
|
||||
|
||||
this.mockNuGetFeed = new Mock<NuGetFeed>(
|
||||
DefaultUpgradeFeedUrl,
|
||||
DefaultUpgradeFeedPackageName,
|
||||
this.downloadDirectoryPath,
|
||||
null,
|
||||
GVFSPlatform.Instance.UnderConstruction.SupportsNuGetEncryption,
|
||||
this.tracer);
|
||||
|
||||
this.mockFileSystem = new MockFileSystem(
|
||||
new MockDirectory(
|
||||
Path.GetDirectoryName(this.downloadDirectoryPath),
|
||||
new[] { new MockDirectory(this.downloadDirectoryPath, null, null) },
|
||||
null));
|
||||
|
||||
this.mockCredentialManager = new Mock<ICredentialStore>();
|
||||
string credentialManagerString = "value";
|
||||
string emptyString = string.Empty;
|
||||
this.mockCredentialManager.Setup(foo => foo.TryGetCredential(It.IsAny<ITracer>(), It.IsAny<string>(), out credentialManagerString, out credentialManagerString, out credentialManagerString)).Returns(true);
|
||||
|
||||
this.httpMessageHandlerMock = new Mock<HttpMessageHandler>();
|
||||
|
||||
this.httpMessageHandlerMock.Protected().As<IHttpMessageHandlerProtectedMembers>()
|
||||
.Setup(m => m.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new HttpResponseMessage()
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(this.ConstructResponseContent(NewerVersion))
|
||||
});
|
||||
|
||||
HttpClient httpClient = new HttpClient(this.httpMessageHandlerMock.Object);
|
||||
|
||||
this.upgrader = new OrgNuGetUpgrader(
|
||||
CurrentVersion,
|
||||
this.tracer,
|
||||
this.mockFileSystem,
|
||||
httpClient,
|
||||
false,
|
||||
false,
|
||||
this.upgraderConfig,
|
||||
"windows",
|
||||
this.mockNuGetFeed.Object,
|
||||
this.mockCredentialManager.Object);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void SupportsAnonymousQuery()
|
||||
{
|
||||
this.upgrader.SupportsAnonymousVersionQuery.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TryQueryNewestVersion()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
newVersion.ShouldNotBeNull();
|
||||
newVersion.ShouldEqual<Version>(new Version(NewerVersion));
|
||||
message.ShouldNotBeNull();
|
||||
message.ShouldEqual($"New version {OrgNuGetUpgraderTests.NewerVersion} is available.");
|
||||
}
|
||||
|
||||
[TestCaseSource("NetworkFailureCases")]
|
||||
public void HandlesNetworkErrors(Exception ex)
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
|
||||
this.httpMessageHandlerMock.Protected().As<IHttpMessageHandlerProtectedMembers>()
|
||||
.Setup(m => m.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
|
||||
.ThrowsAsync(ex);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
success.ShouldBeFalse();
|
||||
newVersion.ShouldBeNull();
|
||||
message.ShouldNotBeNull();
|
||||
message.ShouldContain("Network error");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void HandlesEmptyVersion()
|
||||
{
|
||||
Version newVersion;
|
||||
string message;
|
||||
|
||||
this.httpMessageHandlerMock.Protected().As<IHttpMessageHandlerProtectedMembers>()
|
||||
.Setup(m => m.SendAsync(It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>()))
|
||||
.ReturnsAsync(new HttpResponseMessage()
|
||||
{
|
||||
StatusCode = HttpStatusCode.OK,
|
||||
Content = new StringContent(this.ConstructResponseContent(string.Empty))
|
||||
});
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out newVersion, out message);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
newVersion.ShouldBeNull();
|
||||
message.ShouldNotBeNull();
|
||||
message.ShouldContain("No versions available");
|
||||
}
|
||||
|
||||
private string ConstructResponseContent(string version)
|
||||
{
|
||||
return $"{{\"version\" : \"{version}\"}} ";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.UnitTests.Common
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProductUpgraderInfoTests
|
||||
{
|
||||
private Mock<PhysicalFileSystem> mockFileSystem;
|
||||
private ProductUpgraderInfo productUpgraderInfo;
|
||||
|
||||
private string upgradeDirectory;
|
||||
|
||||
private string expectedNewVersionExistsFileName = "HighestAvailableVersion";
|
||||
private string expectedNewVersionExistsFilePath;
|
||||
private MockTracer tracer;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
this.upgradeDirectory = ProductUpgraderInfo.GetHighestAvailableVersionDirectory();
|
||||
this.expectedNewVersionExistsFilePath = Path.Combine(this.upgradeDirectory, this.expectedNewVersionExistsFileName);
|
||||
this.mockFileSystem = new Mock<PhysicalFileSystem>();
|
||||
|
||||
this.mockFileSystem.Setup(fileSystem => fileSystem.WriteAllText(this.expectedNewVersionExistsFilePath, It.IsAny<string>()));
|
||||
|
||||
this.tracer = new MockTracer();
|
||||
|
||||
this.productUpgraderInfo = new ProductUpgraderInfo(
|
||||
this.tracer,
|
||||
this.mockFileSystem.Object);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
this.mockFileSystem = null;
|
||||
this.productUpgraderInfo = null;
|
||||
this.tracer = null;
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RecordHighestVersion()
|
||||
{
|
||||
this.productUpgraderInfo.RecordHighestAvailableVersion(new Version("1.0.0.0"));
|
||||
this.mockFileSystem.Verify(fileSystem => fileSystem.WriteAllText(this.expectedNewVersionExistsFilePath, It.IsAny<string>()), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RecordingEmptyVersionDeletesExistingHighestVersionFile()
|
||||
{
|
||||
this.mockFileSystem.Setup(fileSystem => fileSystem.FileExists(this.expectedNewVersionExistsFilePath)).Returns(true);
|
||||
|
||||
this.productUpgraderInfo.RecordHighestAvailableVersion(null);
|
||||
|
||||
this.mockFileSystem.Verify(fileSystem => fileSystem.FileExists(this.expectedNewVersionExistsFilePath), Times.Once());
|
||||
this.mockFileSystem.Verify(fileSystem => fileSystem.DeleteFile(this.expectedNewVersionExistsFilePath), Times.Once());
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RecordingEmptyVersionDoesNotDeleteNonExistingHighestVersionFile()
|
||||
{
|
||||
this.mockFileSystem.Setup(fileSystem => fileSystem.FileExists(this.expectedNewVersionExistsFilePath)).Returns(false);
|
||||
|
||||
this.productUpgraderInfo.RecordHighestAvailableVersion(null);
|
||||
|
||||
this.mockFileSystem.Verify(fileSystem => fileSystem.FileExists(this.expectedNewVersionExistsFilePath), Times.Once());
|
||||
this.mockFileSystem.Verify(fileSystem => fileSystem.DeleteFile(this.expectedNewVersionExistsFilePath), Times.Never());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.NuGetUpgrade;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace GVFS.UnitTests.Common
|
||||
{
|
||||
public class TryCreateProductUpgradeTests
|
||||
{
|
||||
private static string defaultUpgradeFeedPackageName = "package";
|
||||
private static string defaultUpgradeFeedUrl = "https://pkgs.dev.azure.com/contoso/";
|
||||
private static string defaultOrgInfoServerUrl = "https://www.contoso.com";
|
||||
private static string defaultRing = "slow";
|
||||
|
||||
private MockTracer tracer;
|
||||
private Mock<PhysicalFileSystem> fileSystemMock;
|
||||
private Mock<ICredentialStore> credentialStoreMock;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
this.tracer = new MockTracer();
|
||||
|
||||
// It is important that creating a new Upgrader does not
|
||||
// require credentials. We must be able to create an
|
||||
// upgrader to query / check upgrade preconditions without
|
||||
// requiring authorization. We create these mocks with
|
||||
// strict behavior to validate methods on them are called
|
||||
// unnecessarily.
|
||||
this.credentialStoreMock = new Mock<ICredentialStore>(MockBehavior.Strict);
|
||||
this.fileSystemMock = new Mock<PhysicalFileSystem>(MockBehavior.Strict);
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
this.credentialStoreMock.VerifyAll();
|
||||
this.fileSystemMock.VerifyAll();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CreatesNuGetUpgraderWhenConfigured()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockNuGetConfigBuilder()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
productUpgrader.ShouldNotBeNull();
|
||||
productUpgrader.ShouldBeOfType<NuGetUpgrader>();
|
||||
error.ShouldBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CreatesNuGetUpgraderWhenConfiguredWithNoRing()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockNuGetConfigBuilder()
|
||||
.WithNoUpgradeRing()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
productUpgrader.ShouldNotBeNull();
|
||||
productUpgrader.ShouldBeOfType<NuGetUpgrader>();
|
||||
error.ShouldBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CreatesGitHubUpgraderWhenConfigured()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultGitHubConfigBuilder()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
productUpgrader.ShouldNotBeNull();
|
||||
productUpgrader.ShouldBeOfType<GitHubUpgrader>();
|
||||
error.ShouldBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CreatesOrgNuGetUpgrader()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockOrgNuGetConfigBuilder()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeTrue();
|
||||
productUpgrader.ShouldNotBeNull();
|
||||
productUpgrader.ShouldBeOfType<OrgNuGetUpgrader>();
|
||||
error.ShouldBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void NoUpgraderWhenNuGetFeedMissing()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockNuGetConfigBuilder()
|
||||
.WithNoUpgradeFeedUrl()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeFalse();
|
||||
productUpgrader.ShouldBeNull();
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void NoOrgUpgraderWhenNuGetPackNameMissing()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockOrgNuGetConfigBuilder()
|
||||
.WithNoUpgradeFeedPackageName()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeFalse();
|
||||
productUpgrader.ShouldBeNull();
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void NoOrgUpgraderWhenNuGetFeedMissing()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockOrgNuGetConfigBuilder()
|
||||
.WithNoUpgradeFeedUrl()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeFalse();
|
||||
productUpgrader.ShouldBeNull();
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void NoUpgraderWhenNuGetPackNameMissing()
|
||||
{
|
||||
MockLocalGVFSConfig gvfsConfig = this.ConstructDefaultMockNuGetConfigBuilder()
|
||||
.WithNoUpgradeFeedPackageName()
|
||||
.Build();
|
||||
|
||||
bool success = ProductUpgrader.TryCreateUpgrader(
|
||||
this.tracer,
|
||||
this.fileSystemMock.Object,
|
||||
gvfsConfig,
|
||||
this.credentialStoreMock.Object,
|
||||
false,
|
||||
false,
|
||||
out ProductUpgrader productUpgrader,
|
||||
out string error);
|
||||
|
||||
success.ShouldBeFalse();
|
||||
productUpgrader.ShouldBeNull();
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
|
||||
private MockLocalGVFSConfigBuilder ConstructDefaultMockNuGetConfigBuilder()
|
||||
{
|
||||
MockLocalGVFSConfigBuilder configBuilder = this.ConstructMockLocalGVFSConfigBuilder()
|
||||
.WithUpgradeRing()
|
||||
.WithUpgradeFeedPackageName()
|
||||
.WithUpgradeFeedUrl();
|
||||
|
||||
return configBuilder;
|
||||
}
|
||||
|
||||
private MockLocalGVFSConfigBuilder ConstructDefaultMockOrgNuGetConfigBuilder()
|
||||
{
|
||||
MockLocalGVFSConfigBuilder configBuilder = this.ConstructMockLocalGVFSConfigBuilder()
|
||||
.WithUpgradeRing()
|
||||
.WithUpgradeFeedPackageName()
|
||||
.WithUpgradeFeedUrl()
|
||||
.WithOrgInfoServerUrl();
|
||||
|
||||
return configBuilder;
|
||||
}
|
||||
|
||||
private MockLocalGVFSConfigBuilder ConstructDefaultGitHubConfigBuilder()
|
||||
{
|
||||
MockLocalGVFSConfigBuilder configBuilder = this.ConstructMockLocalGVFSConfigBuilder()
|
||||
.WithUpgradeRing();
|
||||
|
||||
return configBuilder;
|
||||
}
|
||||
|
||||
private MockLocalGVFSConfigBuilder ConstructMockLocalGVFSConfigBuilder()
|
||||
{
|
||||
return new MockLocalGVFSConfigBuilder(
|
||||
defaultRing,
|
||||
defaultUpgradeFeedUrl,
|
||||
defaultUpgradeFeedPackageName,
|
||||
defaultOrgInfoServerUrl);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,6 @@
|
|||
<ProjectReference Include="..\GVFS.Service.UI\GVFS.Service.UI.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Service\GVFS.Service.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Tests\GVFS.Tests.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Upgrader\GVFS.Upgrader.csproj" />
|
||||
<ProjectReference Include="..\GVFS.Virtualization\GVFS.Virtualization.csproj" />
|
||||
<ProjectReference Include="..\GVFS\GVFS.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -120,27 +120,12 @@ namespace GVFS.UnitTests.Mock.Common
|
|||
public override Dictionary<string, string> GetPhysicalDiskInfo(string path, bool sizeStatsOnly)
|
||||
{
|
||||
return new Dictionary<string, string>();
|
||||
}
|
||||
|
||||
public override string GetUpgradeProtectedDataDirectory()
|
||||
{
|
||||
return this.GetSecureDataRootForGVFSComponent(ProductUpgraderInfo.UpgradeDirectoryName);
|
||||
}
|
||||
|
||||
public override string GetUpgradeLogDirectoryParentDirectory()
|
||||
{
|
||||
return this.GetUpgradeProtectedDataDirectory();
|
||||
}
|
||||
|
||||
public override string GetSystemInstallerLogPath()
|
||||
{
|
||||
return "MockPath";
|
||||
}
|
||||
|
||||
public override string GetUpgradeHighestAvailableVersionDirectory()
|
||||
{
|
||||
return this.GetUpgradeProtectedDataDirectory();
|
||||
}
|
||||
|
||||
public override bool IsConsoleOutputRedirectedToFile()
|
||||
{
|
||||
|
@ -191,13 +176,6 @@ namespace GVFS.UnitTests.Mock.Common
|
|||
{
|
||||
return new MockFileBasedLock(fileSystem, tracer, lockPath);
|
||||
}
|
||||
|
||||
public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformInteractions(
|
||||
PhysicalFileSystem fileSystem,
|
||||
ITracer tracer)
|
||||
{
|
||||
return new MockProductUpgraderPlatformStrategy(fileSystem, tracer);
|
||||
}
|
||||
|
||||
public override bool TryKillProcessTree(int processId, out int exitCode, out string error)
|
||||
{
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
|
||||
namespace GVFS.UnitTests.Mock.Common
|
||||
{
|
||||
public class MockProductUpgraderPlatformStrategy : ProductUpgraderPlatformStrategy
|
||||
{
|
||||
public MockProductUpgraderPlatformStrategy(PhysicalFileSystem fileSystem, ITracer tracer)
|
||||
: base(fileSystem, tracer)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool TryPrepareLogDirectory(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryPrepareApplicationDirectory(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool TryPrepareDownloadDirectory(out string error)
|
||||
{
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,261 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.UnitTests.Mock.Upgrader
|
||||
{
|
||||
public class MockGitHubUpgrader : GitHubUpgrader
|
||||
{
|
||||
private string expectedGVFSAssetName;
|
||||
private string expectedGitAssetName;
|
||||
private ActionType failActionTypes;
|
||||
|
||||
public MockGitHubUpgrader(
|
||||
string currentVersion,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
GitHubUpgraderConfig config) : base(currentVersion, tracer, fileSystem, config)
|
||||
{
|
||||
this.DownloadedFiles = new List<string>();
|
||||
this.InstallerArgs = new Dictionary<string, Dictionary<string, string>>();
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ActionType
|
||||
{
|
||||
Invalid = 0,
|
||||
FetchReleaseInfo = 0x1,
|
||||
CopyTools = 0x2,
|
||||
GitDownload = 0x4,
|
||||
GVFSDownload = 0x8,
|
||||
GitInstall = 0x10,
|
||||
GVFSInstall = 0x20,
|
||||
GVFSCleanup = 0x40,
|
||||
GitCleanup = 0x80,
|
||||
GitAuthenticodeCheck = 0x100,
|
||||
GVFSAuthenticodeCheck = 0x200,
|
||||
CreateDownloadDirectory = 0x400,
|
||||
}
|
||||
|
||||
public List<string> DownloadedFiles { get; private set; }
|
||||
public Dictionary<string, Dictionary<string, string>> InstallerArgs { get; private set; }
|
||||
public bool InstallerExeLaunched { get; set; }
|
||||
private Release FakeUpgradeRelease { get; set; }
|
||||
|
||||
public void SetDryRun(bool dryRun)
|
||||
{
|
||||
this.dryRun = dryRun;
|
||||
}
|
||||
|
||||
public void SetFailOnAction(ActionType failureType)
|
||||
{
|
||||
this.failActionTypes |= failureType;
|
||||
}
|
||||
|
||||
public void SetSucceedOnAction(ActionType failureType)
|
||||
{
|
||||
this.failActionTypes &= ~failureType;
|
||||
}
|
||||
|
||||
public void ResetFailedAction()
|
||||
{
|
||||
this.failActionTypes = ActionType.Invalid;
|
||||
}
|
||||
|
||||
public void PretendNewReleaseAvailableAtRemote(string upgradeVersion, GitHubUpgraderConfig.RingType remoteRing)
|
||||
{
|
||||
string assetDownloadURLPrefix = "https://github.com/Microsoft/VFSForGit/releases/download/v" + upgradeVersion;
|
||||
Release release = new Release();
|
||||
|
||||
release.Name = "GVFS " + upgradeVersion;
|
||||
release.Tag = "v" + upgradeVersion;
|
||||
release.PreRelease = remoteRing == GitHubUpgraderConfig.RingType.Fast;
|
||||
release.Assets = new List<Asset>();
|
||||
|
||||
Random random = new Random();
|
||||
Asset gvfsAsset = new Asset();
|
||||
gvfsAsset.Name = "VFSForGit." + upgradeVersion + GVFSPlatform.Instance.Constants.InstallerExtension;
|
||||
|
||||
// This is not cross-checked anywhere, random value is good.
|
||||
gvfsAsset.Size = random.Next(int.MaxValue / 10, int.MaxValue / 2);
|
||||
gvfsAsset.DownloadURL = new Uri(assetDownloadURLPrefix + "/VFSForGit." + upgradeVersion + GVFSPlatform.Instance.Constants.InstallerExtension);
|
||||
release.Assets.Add(gvfsAsset);
|
||||
|
||||
Asset gitAsset = new Asset();
|
||||
gitAsset.Name = "Git-2.17.1.gvfs.2.1.4.g4385455-64-bit" + GVFSPlatform.Instance.Constants.InstallerExtension;
|
||||
gitAsset.Size = random.Next(int.MaxValue / 10, int.MaxValue / 2);
|
||||
gitAsset.DownloadURL = new Uri(assetDownloadURLPrefix + "/Git-2.17.1.gvfs.2.1.4.g4385455-64-bit" + GVFSPlatform.Instance.Constants.InstallerExtension);
|
||||
release.Assets.Add(gitAsset);
|
||||
|
||||
this.expectedGVFSAssetName = gvfsAsset.Name;
|
||||
this.expectedGitAssetName = gitAsset.Name;
|
||||
this.FakeUpgradeRelease = release;
|
||||
}
|
||||
|
||||
public override bool TrySetupUpgradeApplicationDirectory(out string upgradeApplicationPath, out string error)
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.CopyTools))
|
||||
{
|
||||
upgradeApplicationPath = null;
|
||||
error = "Unable to copy upgrader tools";
|
||||
return false;
|
||||
}
|
||||
|
||||
upgradeApplicationPath = @"mock:\ProgramData\GVFS\GVFS.Upgrade\Tools\GVFS.Upgrader.exe";
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryCreateAndConfigureDownloadDirectory(ITracer tracer, out string error)
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.CreateDownloadDirectory))
|
||||
{
|
||||
error = "Error creating download directory";
|
||||
return false;
|
||||
}
|
||||
|
||||
error = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override bool TryDownloadAsset(Asset asset, out string errorMessage)
|
||||
{
|
||||
bool validAsset = true;
|
||||
if (this.expectedGVFSAssetName.Equals(asset.Name, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.GVFSDownload))
|
||||
{
|
||||
errorMessage = "Error downloading GVFS from GitHub";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (this.expectedGitAssetName.Equals(asset.Name, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.GitDownload))
|
||||
{
|
||||
errorMessage = "Error downloading Git from GitHub";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
validAsset = false;
|
||||
}
|
||||
|
||||
if (validAsset)
|
||||
{
|
||||
string fakeDownloadDirectory = @"mock:\ProgramData\GVFS\GVFS.Upgrade\Downloads";
|
||||
asset.LocalPath = Path.Combine(fakeDownloadDirectory, asset.Name);
|
||||
this.DownloadedFiles.Add(asset.LocalPath);
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
errorMessage = "Cannot download unknown asset.";
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override bool TryDeleteDownloadedAsset(Asset asset, out Exception exception)
|
||||
{
|
||||
if (this.expectedGVFSAssetName.Equals(asset.Name, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.GVFSCleanup))
|
||||
{
|
||||
exception = new Exception("Error deleting downloaded GVFS installer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
else if (this.expectedGitAssetName.Equals(asset.Name, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.GitCleanup))
|
||||
{
|
||||
exception = new Exception("Error deleting downloaded Git installer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
exception = null;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exception = new Exception("Unknown asset.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected override bool TryFetchReleases(out List<Release> releases, out string errorMessage)
|
||||
{
|
||||
if (this.failActionTypes.HasFlag(ActionType.FetchReleaseInfo))
|
||||
{
|
||||
releases = null;
|
||||
errorMessage = "Error fetching upgrade release info.";
|
||||
return false;
|
||||
}
|
||||
|
||||
releases = new List<Release> { this.FakeUpgradeRelease };
|
||||
errorMessage = null;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected override void RunInstaller(string path, string args, string certCN, string issuerCN, out int exitCode, out string error)
|
||||
{
|
||||
string fileName = Path.GetFileName(path);
|
||||
Dictionary<string, string> installationInfo = new Dictionary<string, string>();
|
||||
installationInfo.Add("Installer", fileName);
|
||||
installationInfo.Add("Args", args);
|
||||
|
||||
exitCode = 0;
|
||||
error = null;
|
||||
|
||||
if (fileName.Equals(this.expectedGitAssetName, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
this.InstallerArgs.Add("Git", installationInfo);
|
||||
this.InstallerExeLaunched = true;
|
||||
if (this.failActionTypes.HasFlag(ActionType.GitInstall))
|
||||
{
|
||||
exitCode = -1;
|
||||
error = "Git installation failed";
|
||||
}
|
||||
|
||||
if (this.failActionTypes.HasFlag(ActionType.GitAuthenticodeCheck))
|
||||
{
|
||||
exitCode = -1;
|
||||
error = "The contents of file C:\\ProgramData\\GVFS\\GVFS.Upgrade\\Tools\\Git-2.17.1.gvfs.2.1.4.g4385455-64-bit might have been changed by an unauthorized user or process, because the hash of the file does not match the hash stored in the digital signature. The script cannot run on the specified system. For more information, run Get-Help about_Signing.";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (fileName.Equals(this.expectedGVFSAssetName, GVFSPlatform.Instance.Constants.PathComparison))
|
||||
{
|
||||
this.InstallerArgs.Add("GVFS", installationInfo);
|
||||
this.InstallerExeLaunched = true;
|
||||
if (this.failActionTypes.HasFlag(ActionType.GVFSInstall))
|
||||
{
|
||||
exitCode = -1;
|
||||
error = "GVFS installation failed";
|
||||
}
|
||||
|
||||
if (this.failActionTypes.HasFlag(ActionType.GVFSAuthenticodeCheck))
|
||||
{
|
||||
exitCode = -1;
|
||||
error = "The contents of file C:\\ProgramData\\GVFS\\GVFS.Upgrade\\Tools\\SetupGVFS.1.0.18297.1.exe might have been changed by an unauthorized user or process, because the hash of the file does not match the hash stored in the digital signature. The script cannot run on the specified system. For more information, run Get-Help about_Signing.";
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
exitCode = -1;
|
||||
error = "Cannot launch unknown installer";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
using GVFS.Common.Tracing;
|
||||
using GVFS.Upgrader;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.UnitTests.Mock.Upgrader
|
||||
{
|
||||
public class MockInstallerPrerunChecker : InstallerPreRunChecker
|
||||
{
|
||||
public const string GitUpgradeCheckError = "Unable to upgrade Git";
|
||||
|
||||
private FailOnCheckType failOnCheck;
|
||||
|
||||
public MockInstallerPrerunChecker(ITracer tracer) : base(tracer, string.Empty)
|
||||
{
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum FailOnCheckType
|
||||
{
|
||||
Invalid = 0,
|
||||
ProjFSEnabled = 0x1,
|
||||
IsElevated = 0x2,
|
||||
BlockingProcessesRunning = 0x4,
|
||||
UnattendedMode = 0x8,
|
||||
UnMountRepos = 0x10,
|
||||
RemountRepos = 0x20,
|
||||
IsServiceInstalledAndNotRunning = 0x40,
|
||||
}
|
||||
|
||||
public void SetReturnFalseOnCheck(FailOnCheckType prerunCheck)
|
||||
{
|
||||
this.failOnCheck |= prerunCheck;
|
||||
}
|
||||
|
||||
public void SetReturnTrueOnCheck(FailOnCheckType prerunCheck)
|
||||
{
|
||||
this.failOnCheck &= ~prerunCheck;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.failOnCheck = FailOnCheckType.Invalid;
|
||||
|
||||
this.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.UnattendedMode);
|
||||
this.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.BlockingProcessesRunning);
|
||||
this.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.IsServiceInstalledAndNotRunning);
|
||||
}
|
||||
|
||||
public void SetCommandToRerun(string command)
|
||||
{
|
||||
this.CommandToRerun = command;
|
||||
}
|
||||
|
||||
protected override bool IsServiceInstalledAndNotRunning()
|
||||
{
|
||||
return this.FakedResultOfCheck(FailOnCheckType.IsServiceInstalledAndNotRunning);
|
||||
}
|
||||
|
||||
protected override bool IsElevated()
|
||||
{
|
||||
return this.FakedResultOfCheck(FailOnCheckType.IsElevated);
|
||||
}
|
||||
|
||||
protected override bool IsGVFSUpgradeSupported()
|
||||
{
|
||||
return this.FakedResultOfCheck(FailOnCheckType.ProjFSEnabled);
|
||||
}
|
||||
|
||||
protected override bool IsUnattended()
|
||||
{
|
||||
return this.FakedResultOfCheck(FailOnCheckType.UnattendedMode);
|
||||
}
|
||||
|
||||
protected override bool IsBlockingProcessRunning(out HashSet<string> processes)
|
||||
{
|
||||
processes = new HashSet<string>();
|
||||
|
||||
bool isRunning = this.FakedResultOfCheck(FailOnCheckType.BlockingProcessesRunning);
|
||||
if (isRunning)
|
||||
{
|
||||
processes.Add("GVFS.Mount");
|
||||
processes.Add("git");
|
||||
}
|
||||
|
||||
return isRunning;
|
||||
}
|
||||
|
||||
protected override bool TryRunGVFSWithArgs(string args, out string error)
|
||||
{
|
||||
if (string.CompareOrdinal(args, "service --unmount-all") == 0)
|
||||
{
|
||||
bool result = this.FakedResultOfCheck(FailOnCheckType.UnMountRepos);
|
||||
error = result == false ? "Unmount of some of the repositories failed." : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (string.CompareOrdinal(args, "service --mount-all") == 0)
|
||||
{
|
||||
bool result = this.FakedResultOfCheck(FailOnCheckType.RemountRepos);
|
||||
error = result == false ? "Auto remount failed." : null;
|
||||
return result;
|
||||
}
|
||||
|
||||
error = "Unknown GVFS command";
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool FakedResultOfCheck(FailOnCheckType checkType)
|
||||
{
|
||||
return !this.failOnCheck.HasFlag(checkType);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,130 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Tests.Should;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
|
||||
namespace GVFS.UnitTests.Upgrader
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProductUpgraderTests : UpgradeTests
|
||||
{
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnFastWhileOnLocalNoneRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.None,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnSlowWhileOnLocalNoneRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.None,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnFastWhileOnLocalSlowRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnSlowWhileOnLocalSlowRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion: UpgradeTests.NewerThanLocalVersion);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnFastWhileOnLocalFastRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion: UpgradeTests.NewerThanLocalVersion);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailableOnSlowWhileOnLocalFastRing()
|
||||
{
|
||||
this.SimulateUpgradeAvailable(
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow,
|
||||
remoteVersion: UpgradeTests.NewerThanLocalVersion,
|
||||
localRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast,
|
||||
expectedReturn: true,
|
||||
expectedUpgradeVersion:UpgradeTests.NewerThanLocalVersion);
|
||||
}
|
||||
|
||||
public override void NoneLocalRing()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void InvalidUpgradeRing()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void FetchReleaseInfo()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override ReturnCode RunUpgrade()
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private void SimulateUpgradeAvailable(
|
||||
GitHubUpgrader.GitHubUpgraderConfig.RingType remoteRing,
|
||||
string remoteVersion,
|
||||
GitHubUpgrader.GitHubUpgraderConfig.RingType localRing,
|
||||
bool expectedReturn,
|
||||
string expectedUpgradeVersion)
|
||||
{
|
||||
this.SetUpgradeRing(localRing.ToString());
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
remoteVersion,
|
||||
remoteRing);
|
||||
|
||||
Version newVersion;
|
||||
string message;
|
||||
this.Upgrader.TryQueryNewestVersion(out newVersion, out message).ShouldEqual(expectedReturn);
|
||||
|
||||
if (string.IsNullOrEmpty(expectedUpgradeVersion))
|
||||
{
|
||||
newVersion.ShouldBeNull();
|
||||
}
|
||||
else
|
||||
{
|
||||
newVersion.ShouldNotBeNull();
|
||||
newVersion.ShouldEqual(new Version(expectedUpgradeVersion));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,250 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using GVFS.UnitTests.Mock.FileSystem;
|
||||
using GVFS.UnitTests.Mock.Upgrader;
|
||||
using GVFS.Upgrader;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.UnitTests.Upgrader
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpgradeOrchestratorTests
|
||||
{
|
||||
private const string GVFSVersion = "1.1.18115.1";
|
||||
|
||||
private delegate void TryGetNewerVersionCallback(out System.Version version, out string message);
|
||||
private delegate void UpgradeAllowedCallback(out string message);
|
||||
private delegate void TryRunPreUpgradeChecksCallback(out string delegateMessage);
|
||||
private delegate void TryDownloadNewestVersionCallback(out string message);
|
||||
private delegate void TryCreateAndConfigureDownloadDirectoryCallback(ITracer tracer, out string message);
|
||||
private delegate void TryRunInstallerCallback(InstallActionWrapper installActionWrapper, out string error);
|
||||
|
||||
private MockTracer Tracer { get; set; }
|
||||
private MockFileSystem FileSystem { get; set; }
|
||||
private MockTextWriter Output { get; set; }
|
||||
private MockInstallerPrerunChecker PreRunChecker { get; set; }
|
||||
private Mock<LocalGVFSConfig> MoqLocalConfig { get; set; }
|
||||
private Mock<ProductUpgrader> MoqUpgrader { get; set; }
|
||||
|
||||
private UpgradeOrchestrator orchestrator { get; set; }
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
this.Tracer = new MockTracer();
|
||||
this.FileSystem = new MockFileSystem(new MockDirectory(@"mock:\GVFS.Upgrades\Download", null, null));
|
||||
this.Output = new MockTextWriter();
|
||||
this.PreRunChecker = new MockInstallerPrerunChecker(this.Tracer);
|
||||
this.PreRunChecker.Reset();
|
||||
this.MoqUpgrader = this.DefaultUpgrader();
|
||||
this.orchestrator = new WindowsUpgradeOrchestrator(
|
||||
this.MoqUpgrader.Object,
|
||||
this.Tracer,
|
||||
this.FileSystem,
|
||||
this.PreRunChecker,
|
||||
input: null,
|
||||
output: this.Output);
|
||||
|
||||
this.SetUpgradeAvailable(new Version(GVFSVersion), error: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteSucceedsWhenUpgradeAvailable()
|
||||
{
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: true,
|
||||
downloadNewestVersion: true,
|
||||
installNewestVersion: true,
|
||||
cleanup: true);
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.Success);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteSucceedsWhenOnLatestVersion()
|
||||
{
|
||||
this.SetUpgradeAvailable(newVersion: null, error: null);
|
||||
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: true,
|
||||
downloadNewestVersion: false,
|
||||
installNewestVersion: false,
|
||||
cleanup: true);
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.Success);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteFailsWhenGetNewVersionFails()
|
||||
{
|
||||
string errorMessage = "Authentication error.";
|
||||
|
||||
this.SetUpgradeAvailable(newVersion: null, error: errorMessage);
|
||||
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: true,
|
||||
downloadNewestVersion: false,
|
||||
installNewestVersion: false,
|
||||
cleanup: true);
|
||||
|
||||
this.VerifyOutput("ERROR: Authentication error.");
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.GenericError);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteFailsWhenPrecheckFails()
|
||||
{
|
||||
this.PreRunChecker.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.IsElevated);
|
||||
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: false,
|
||||
downloadNewestVersion: false,
|
||||
installNewestVersion: false,
|
||||
cleanup: true);
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.GenericError);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteFailsWhenDownloadFails()
|
||||
{
|
||||
this.MoqUpgrader.Setup(upgrader => upgrader.TryDownloadNewestVersion(out It.Ref<string>.IsAny))
|
||||
.Callback(new TryDownloadNewestVersionCallback(
|
||||
(out string delegateMessage) =>
|
||||
{
|
||||
delegateMessage = "Download error.";
|
||||
}))
|
||||
.Returns(false);
|
||||
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: true,
|
||||
downloadNewestVersion: true,
|
||||
installNewestVersion: false,
|
||||
cleanup: true);
|
||||
|
||||
this.VerifyOutput("ERROR: Download error.");
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.GenericError);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ExecuteFailsWhenRunInstallerFails()
|
||||
{
|
||||
this.MoqUpgrader.Setup(upgrader => upgrader.TryRunInstaller(It.IsAny<InstallActionWrapper>(), out It.Ref<string>.IsAny))
|
||||
.Callback(new TryRunInstallerCallback((InstallActionWrapper installActionWrapper, out string delegateMessage) =>
|
||||
{
|
||||
delegateMessage = "Installer error.";
|
||||
}))
|
||||
.Returns(false);
|
||||
|
||||
this.orchestrator.Execute();
|
||||
|
||||
this.VerifyOrchestratorInvokes(
|
||||
upgradeAllowed: true,
|
||||
queryNewestVersion: true,
|
||||
downloadNewestVersion: true,
|
||||
installNewestVersion: true,
|
||||
cleanup: true);
|
||||
|
||||
this.VerifyOutput("ERROR: Installer error.");
|
||||
|
||||
this.orchestrator.ExitCode.ShouldEqual(ReturnCode.GenericError);
|
||||
}
|
||||
|
||||
public Mock<ProductUpgrader> DefaultUpgrader()
|
||||
{
|
||||
Mock<ProductUpgrader> mockUpgrader = new Mock<ProductUpgrader>();
|
||||
|
||||
mockUpgrader.Setup(upgrader => upgrader.UpgradeAllowed(out It.Ref<string>.IsAny))
|
||||
.Callback(new UpgradeAllowedCallback((out string delegateMessage) =>
|
||||
{
|
||||
delegateMessage = string.Empty;
|
||||
}))
|
||||
.Returns(true);
|
||||
|
||||
string message = string.Empty;
|
||||
mockUpgrader.Setup(upgrader => upgrader.TryDownloadNewestVersion(out It.Ref<string>.IsAny)).Returns(true);
|
||||
mockUpgrader.Setup(upgrader => upgrader.TryRunInstaller(It.IsAny<InstallActionWrapper>(), out message)).Returns(true);
|
||||
mockUpgrader.Setup(upgrader => upgrader.TryCleanup(out It.Ref<string>.IsAny)).Returns(true);
|
||||
|
||||
return mockUpgrader;
|
||||
}
|
||||
|
||||
public void SetUpgradeAvailable(Version newVersion, string error)
|
||||
{
|
||||
bool upgradeResult = string.IsNullOrEmpty(error);
|
||||
|
||||
this.MoqUpgrader.Setup(upgrader => upgrader.TryQueryNewestVersion(out It.Ref<System.Version>.IsAny, out It.Ref<string>.IsAny))
|
||||
.Callback(new TryGetNewerVersionCallback((out System.Version delegateVersion, out string delegateMessage) =>
|
||||
{
|
||||
delegateVersion = newVersion;
|
||||
delegateMessage = error;
|
||||
}))
|
||||
.Returns(upgradeResult);
|
||||
}
|
||||
|
||||
public void VerifyOrchestratorInvokes(
|
||||
bool upgradeAllowed,
|
||||
bool queryNewestVersion,
|
||||
bool downloadNewestVersion,
|
||||
bool installNewestVersion,
|
||||
bool cleanup)
|
||||
{
|
||||
this.MoqUpgrader.Verify(
|
||||
upgrader => upgrader.UpgradeAllowed(
|
||||
out It.Ref<string>.IsAny),
|
||||
upgradeAllowed ? Times.Once() : Times.Never());
|
||||
|
||||
this.MoqUpgrader.Verify(
|
||||
upgrader => upgrader.TryQueryNewestVersion(
|
||||
out It.Ref<System.Version>.IsAny,
|
||||
out It.Ref<string>.IsAny),
|
||||
queryNewestVersion ? Times.Once() : Times.Never());
|
||||
|
||||
this.MoqUpgrader.Verify(
|
||||
upgrader => upgrader.TryDownloadNewestVersion(
|
||||
out It.Ref<string>.IsAny),
|
||||
downloadNewestVersion ? Times.Once() : Times.Never());
|
||||
|
||||
this.MoqUpgrader.Verify(
|
||||
upgrader => upgrader.TryRunInstaller(
|
||||
It.IsAny<InstallActionWrapper>(),
|
||||
out It.Ref<string>.IsAny),
|
||||
installNewestVersion ? Times.Once() : Times.Never());
|
||||
|
||||
this.MoqUpgrader.Verify(
|
||||
upgrader => upgrader.TryCleanup(
|
||||
out It.Ref<string>.IsAny),
|
||||
cleanup ? Times.Once() : Times.Never());
|
||||
}
|
||||
|
||||
public void VerifyOutput(string expectedMessage)
|
||||
{
|
||||
this.Output.AllLines.ShouldContain(
|
||||
new List<string>() { expectedMessage },
|
||||
(line, expectedLine) => { return line.Contains(expectedLine); });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,324 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Mock.Upgrader;
|
||||
using GVFS.Upgrader;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.UnitTests.Upgrader
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpgradeOrchestratorWithGitHubUpgraderTests : UpgradeTests
|
||||
{
|
||||
private UpgradeOrchestrator orchestrator;
|
||||
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
|
||||
this.orchestrator = new WindowsUpgradeOrchestrator(
|
||||
this.Upgrader,
|
||||
this.Tracer,
|
||||
this.FileSystem,
|
||||
this.PrerunChecker,
|
||||
input: null,
|
||||
output: this.Output);
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade --confirm`");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeNoError()
|
||||
{
|
||||
this.RunUpgrade().ShouldEqual(ReturnCode.Success);
|
||||
this.Tracer.RelatedErrorEvents.ShouldBeEmpty();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void AutoUnmountError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.PrerunChecker.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.UnMountRepos);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Unmount of some of the repositories failed."
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Unmount of some of the repositories failed."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void AbortOnBlockingProcess()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.PrerunChecker.SetReturnTrueOnCheck(MockInstallerPrerunChecker.FailOnCheckType.BlockingProcessesRunning);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"ERROR: Blocking processes are running.",
|
||||
$"Run `gvfs upgrade --confirm` again after quitting these processes - GVFS.Mount, git"
|
||||
},
|
||||
expectedErrors: null,
|
||||
expectedWarnings: new List<string>
|
||||
{
|
||||
$"Run `gvfs upgrade --confirm` again after quitting these processes - GVFS.Mount, git"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DownloadDirectoryCreationError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.CreateDownloadDirectory);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Error creating download directory"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Error creating download directory"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSDownloadError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GVFSDownload);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Error downloading GVFS from GitHub"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Error downloading GVFS from GitHub"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitDownloadError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GitDownload);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Error downloading Git from GitHub"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Error downloading Git from GitHub"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitInstallationArgs()
|
||||
{
|
||||
this.RunUpgrade().ShouldEqual(ReturnCode.Success);
|
||||
|
||||
Dictionary<string, string> gitInstallerInfo;
|
||||
this.Upgrader.InstallerArgs.ShouldBeNonEmpty();
|
||||
this.Upgrader.InstallerArgs.TryGetValue("Git", out gitInstallerInfo).ShouldBeTrue();
|
||||
|
||||
string args;
|
||||
gitInstallerInfo.TryGetValue("Args", out args).ShouldBeTrue();
|
||||
args.ShouldContain(new string[] { "/VERYSILENT", "/CLOSEAPPLICATIONS", "/SUPPRESSMSGBOXES", "/NORESTART", "/Log" });
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitInstallError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GitInstall);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Git installation failed"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Git installation failed"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitInstallerAuthenticodeError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GitAuthenticodeCheck);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"hash of the file does not match the hash stored in the digital signature"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"hash of the file does not match the hash stored in the digital signature"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSInstallationArgs()
|
||||
{
|
||||
this.RunUpgrade().ShouldEqual(ReturnCode.Success);
|
||||
|
||||
Dictionary<string, string> gitInstallerInfo;
|
||||
this.Upgrader.InstallerArgs.ShouldBeNonEmpty();
|
||||
this.Upgrader.InstallerArgs.TryGetValue("GVFS", out gitInstallerInfo).ShouldBeTrue();
|
||||
|
||||
string args;
|
||||
gitInstallerInfo.TryGetValue("Args", out args).ShouldBeTrue();
|
||||
args.ShouldContain(new string[] { "/VERYSILENT", "/CLOSEAPPLICATIONS", "/SUPPRESSMSGBOXES", "/NORESTART", "/Log", "/REMOUNTREPOS=false" });
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSInstallError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GVFSInstall);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"GVFS installation failed"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"GVFS installation failed"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSInstallerAuthenticodeError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GVFSAuthenticodeCheck);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"hash of the file does not match the hash stored in the digital signature"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"hash of the file does not match the hash stored in the digital signature"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GVFSCleanupError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GVFSCleanup);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Error deleting downloaded GVFS installer."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void GitCleanupError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.GitCleanup);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Error deleting downloaded Git installer."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void RemountReposError()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.PrerunChecker.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.RemountRepos);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Auto remount failed."
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Auto remount failed."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DryRunDoesNotRunInstallerExes()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.Upgrader.SetDryRun(true);
|
||||
this.Upgrader.InstallerExeLaunched = false;
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
upgradeVersion: NewerThanLocalVersion,
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Installing Git",
|
||||
"Installing GVFS",
|
||||
"Upgrade completed successfully."
|
||||
},
|
||||
expectedErrors: null);
|
||||
|
||||
this.Upgrader.InstallerExeLaunched.ShouldBeFalse();
|
||||
}
|
||||
|
||||
protected override ReturnCode RunUpgrade()
|
||||
{
|
||||
this.orchestrator.Execute();
|
||||
return this.orchestrator.ExitCode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,191 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Category;
|
||||
using GVFS.UnitTests.Mock.Common;
|
||||
using GVFS.UnitTests.Mock.FileSystem;
|
||||
using GVFS.UnitTests.Mock.Upgrader;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.UnitTests.Upgrader
|
||||
{
|
||||
public abstract class UpgradeTests
|
||||
{
|
||||
protected const string OlderThanLocalVersion = "1.0.17000.1";
|
||||
protected const string LocalGVFSVersion = "1.0.18115.1";
|
||||
protected const string NewerThanLocalVersion = "1.1.18115.1";
|
||||
|
||||
protected MockTracer Tracer { get; private set; }
|
||||
protected MockFileSystem FileSystem { get; private set; }
|
||||
protected MockTextWriter Output { get; private set; }
|
||||
protected MockInstallerPrerunChecker PrerunChecker { get; private set; }
|
||||
protected MockGitHubUpgrader Upgrader { get; private set; }
|
||||
protected MockLocalGVFSConfig LocalConfig { get; private set; }
|
||||
|
||||
public virtual void Setup()
|
||||
{
|
||||
this.Tracer = new MockTracer();
|
||||
this.FileSystem = new MockFileSystem(new MockDirectory(@"mock:\GVFS.Upgrades\Download", null, null));
|
||||
this.Output = new MockTextWriter();
|
||||
this.PrerunChecker = new MockInstallerPrerunChecker(this.Tracer);
|
||||
this.LocalConfig = new MockLocalGVFSConfig();
|
||||
|
||||
this.Upgrader = new MockGitHubUpgrader(
|
||||
LocalGVFSVersion,
|
||||
this.Tracer,
|
||||
this.FileSystem,
|
||||
new GitHubUpgrader.GitHubUpgraderConfig(this.Tracer, this.LocalConfig));
|
||||
|
||||
this.PrerunChecker.Reset();
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
upgradeVersion: NewerThanLocalVersion,
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow);
|
||||
this.SetUpgradeRing("Slow");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public virtual void NoneLocalRing()
|
||||
{
|
||||
string message = "Upgrade ring set to \"None\". No upgrade check was performed.";
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("None");
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
message
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public virtual void InvalidUpgradeRing()
|
||||
{
|
||||
this.SetUpgradeRing("Invalid");
|
||||
|
||||
string expectedError = "Invalid upgrade ring `Invalid` specified in gvfs config.";
|
||||
string errorString;
|
||||
GitHubUpgrader.Create(
|
||||
this.Tracer,
|
||||
this.FileSystem,
|
||||
dryRun: false,
|
||||
noVerify: false,
|
||||
localConfig: this.LocalConfig,
|
||||
error: out errorString).ShouldBeNull();
|
||||
|
||||
errorString.ShouldContain(expectedError);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public virtual void FetchReleaseInfo()
|
||||
{
|
||||
string errorString = "Error fetching upgrade release info.";
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("Fast");
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.FetchReleaseInfo);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
errorString
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
errorString
|
||||
});
|
||||
}
|
||||
|
||||
protected abstract ReturnCode RunUpgrade();
|
||||
|
||||
protected void ConfigureRunAndVerify(
|
||||
Action configure,
|
||||
ReturnCode expectedReturn,
|
||||
List<string> expectedOutput,
|
||||
List<string> expectedErrors,
|
||||
List<string> expectedWarnings = null)
|
||||
{
|
||||
configure();
|
||||
|
||||
this.RunUpgrade().ShouldEqual(expectedReturn);
|
||||
|
||||
if (expectedOutput != null)
|
||||
{
|
||||
this.Output.AllLines.ShouldContain(
|
||||
expectedOutput,
|
||||
(line, expectedLine) => { return line.Contains(expectedLine); });
|
||||
}
|
||||
|
||||
if (expectedErrors != null)
|
||||
{
|
||||
this.Tracer.RelatedErrorEvents.ShouldContain(
|
||||
expectedErrors,
|
||||
(error, expectedError) => { return error.Contains(expectedError); });
|
||||
}
|
||||
|
||||
if (expectedWarnings != null)
|
||||
{
|
||||
this.Tracer.RelatedWarningEvents.ShouldContain(
|
||||
expectedWarnings,
|
||||
(warning, expectedWarning) => { return warning.Contains(expectedWarning); });
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetUpgradeRing(string ringName)
|
||||
{
|
||||
GitHubUpgrader.GitHubUpgraderConfig.RingType ring;
|
||||
if (!Enum.TryParse<GitHubUpgrader.GitHubUpgraderConfig.RingType>(ringName, ignoreCase: true, result: out ring))
|
||||
{
|
||||
ring = GitHubUpgrader.GitHubUpgraderConfig.RingType.Invalid;
|
||||
}
|
||||
|
||||
string error;
|
||||
if (ring == GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow ||
|
||||
ring == GitHubUpgrader.GitHubUpgraderConfig.RingType.Fast)
|
||||
{
|
||||
this.LocalConfig.TrySetConfig("upgrade.ring", ringName, out error);
|
||||
this.VerifyConfig(ring, isUpgradeAllowed: true, isConfigError: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ring == GitHubUpgrader.GitHubUpgraderConfig.RingType.None)
|
||||
{
|
||||
this.LocalConfig.TrySetConfig("upgrade.ring", ringName, out error);
|
||||
this.VerifyConfig(ring, isUpgradeAllowed: false, isConfigError: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ring == GitHubUpgrader.GitHubUpgraderConfig.RingType.Invalid)
|
||||
{
|
||||
this.LocalConfig.TrySetConfig("upgrade.ring", ringName, out error);
|
||||
this.VerifyConfig(ring, isUpgradeAllowed: false, isConfigError: true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected void VerifyConfig(
|
||||
GVFS.Common.GitHubUpgrader.GitHubUpgraderConfig.RingType ring,
|
||||
bool isUpgradeAllowed,
|
||||
bool isConfigError)
|
||||
{
|
||||
string error;
|
||||
this.Upgrader.Config.TryLoad(out error).ShouldBeTrue();
|
||||
|
||||
Assert.AreEqual(ring, this.Upgrader.Config.UpgradeRing);
|
||||
error.ShouldBeNull();
|
||||
|
||||
bool upgradeAllowed = this.Upgrader.UpgradeAllowed(out _);
|
||||
bool configError = this.Upgrader.Config.ConfigError();
|
||||
|
||||
upgradeAllowed.ShouldEqual(isUpgradeAllowed);
|
||||
configError.ShouldEqual(isConfigError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,256 +0,0 @@
|
|||
using GVFS.CommandLine;
|
||||
using GVFS.Common;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Category;
|
||||
using GVFS.UnitTests.Mock.Upgrader;
|
||||
using GVFS.UnitTests.Upgrader;
|
||||
using NUnit.Framework;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GVFS.UnitTests.Windows.Upgrader
|
||||
{
|
||||
[TestFixture]
|
||||
public class UpgradeVerbTests : UpgradeTests
|
||||
{
|
||||
private MockProcessLauncher processLauncher;
|
||||
private UpgradeVerb upgradeVerb;
|
||||
|
||||
[SetUp]
|
||||
public override void Setup()
|
||||
{
|
||||
base.Setup();
|
||||
|
||||
this.processLauncher = new MockProcessLauncher(exitCode: 0, hasExited: true, startResult: true);
|
||||
this.upgradeVerb = new UpgradeVerb(
|
||||
this.Upgrader,
|
||||
this.Tracer,
|
||||
this.FileSystem,
|
||||
this.PrerunChecker,
|
||||
this.processLauncher,
|
||||
this.Output);
|
||||
this.upgradeVerb.Confirmed = false;
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade`");
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UpgradeAvailabilityReporting()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
upgradeVersion: NewerThanLocalVersion,
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"New GVFS version " + NewerThanLocalVersion + " available in ring Slow",
|
||||
"MockUpgradeInstallAdvice"
|
||||
},
|
||||
expectedErrors: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DowngradePrevention()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
upgradeVersion: OlderThanLocalVersion,
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Checking for GVFS upgrades...Succeeded",
|
||||
"Great news, you're all caught up on upgrades in the Slow ring!"
|
||||
},
|
||||
expectedErrors: null);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void LaunchInstaller()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade --confirm`");
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"New GVFS version " + NewerThanLocalVersion + " available in ring Slow",
|
||||
"Launching upgrade tool..."
|
||||
},
|
||||
expectedErrors:null);
|
||||
|
||||
this.processLauncher.IsLaunched.ShouldBeTrue();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public override void NoneLocalRing()
|
||||
{
|
||||
base.NoneLocalRing();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public override void InvalidUpgradeRing()
|
||||
{
|
||||
base.InvalidUpgradeRing();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
[Category(CategoryConstants.ExceptionExpected)]
|
||||
public void CopyTools()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.Upgrader.SetFailOnAction(MockGitHubUpgrader.ActionType.CopyTools);
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade --confirm`");
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Could not launch upgrade tool. Unable to copy upgrader tools"
|
||||
},
|
||||
expectedErrors: new List<string>
|
||||
{
|
||||
"Could not launch upgrade tool. Unable to copy upgrader tools"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ProjFSPreCheck()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.ProjFSEnabled);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"ERROR: `gvfs upgrade` is only supported after the \"Windows Projected File System\" optional feature has been enabled by a manual installation of VFS for Git, and only on versions of Windows that support this feature.",
|
||||
"Check your team's documentation for how to upgrade."
|
||||
},
|
||||
expectedErrors: null,
|
||||
expectedWarnings: new List<string>
|
||||
{
|
||||
"`gvfs upgrade` is only supported after the \"Windows Projected File System\" optional feature has been enabled by a manual installation of VFS for Git, and only on versions of Windows that support this feature."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void IsGVFSServiceRunningPreCheck()
|
||||
{
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade --confirm`");
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetReturnTrueOnCheck(MockInstallerPrerunChecker.FailOnCheckType.IsServiceInstalledAndNotRunning);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"GVFS Service is not running.",
|
||||
"To install, run MockStartServiceCommand and run MockUpgradeConfirmCommand."
|
||||
},
|
||||
expectedErrors: null,
|
||||
expectedWarnings: new List<string>
|
||||
{
|
||||
"GVFS Service is not running."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void ElevatedRunPreCheck()
|
||||
{
|
||||
this.PrerunChecker.SetCommandToRerun("`gvfs upgrade --confirm`");
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetReturnFalseOnCheck(MockInstallerPrerunChecker.FailOnCheckType.IsElevated);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"The installer needs to be run with elevated permissions.",
|
||||
"MockRunUpdateMessage"
|
||||
},
|
||||
expectedErrors: null,
|
||||
expectedWarnings: new List<string>
|
||||
{
|
||||
"The installer needs to be run with elevated permissions."
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void UnAttendedModePreCheck()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.upgradeVerb.Confirmed = true;
|
||||
this.PrerunChecker.SetReturnTrueOnCheck(MockInstallerPrerunChecker.FailOnCheckType.UnattendedMode);
|
||||
},
|
||||
expectedReturn: ReturnCode.GenericError,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"`gvfs upgrade` is not supported in unattended mode"
|
||||
},
|
||||
expectedErrors: null,
|
||||
expectedWarnings: new List<string>
|
||||
{
|
||||
"`gvfs upgrade` is not supported in unattended mode"
|
||||
});
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void DryRunLaunchesUpgradeTool()
|
||||
{
|
||||
this.ConfigureRunAndVerify(
|
||||
configure: () =>
|
||||
{
|
||||
this.upgradeVerb.DryRun = true;
|
||||
this.SetUpgradeRing("Slow");
|
||||
this.Upgrader.PretendNewReleaseAvailableAtRemote(
|
||||
upgradeVersion: NewerThanLocalVersion,
|
||||
remoteRing: GitHubUpgrader.GitHubUpgraderConfig.RingType.Slow);
|
||||
},
|
||||
expectedReturn: ReturnCode.Success,
|
||||
expectedOutput: new List<string>
|
||||
{
|
||||
"Installer launched in a new window."
|
||||
},
|
||||
expectedErrors: null);
|
||||
}
|
||||
|
||||
protected override ReturnCode RunUpgrade()
|
||||
{
|
||||
try
|
||||
{
|
||||
this.upgradeVerb.Execute();
|
||||
}
|
||||
catch (GVFSVerb.VerbAbortedException)
|
||||
{
|
||||
// ignore. exceptions are expected while simulating some failures.
|
||||
}
|
||||
|
||||
return this.upgradeVerb.ReturnCode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Platform.Windows;
|
||||
using GVFS.Tests.Should;
|
||||
using GVFS.UnitTests.Common.NuGetUpgrade;
|
||||
using Moq;
|
||||
using NuGet.Packaging.Core;
|
||||
using NuGet.Protocol.Core.Types;
|
||||
using NUnit.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GVFS.UnitTests.Windows.Common.Upgrader
|
||||
{
|
||||
[TestFixture]
|
||||
public class WindowsNuGetUpgraderTests : NuGetUpgraderTests
|
||||
{
|
||||
public override ProductUpgraderPlatformStrategy CreateProductUpgraderPlatformStrategy()
|
||||
{
|
||||
return new WindowsProductUpgraderPlatformStrategy(this.mockFileSystem, this.tracer);
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void TrySetupUpgradeApplicationDirectoryFailsIfCreateToolsDirectoryFails()
|
||||
{
|
||||
this.mockFileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissionsShouldSucceed = false;
|
||||
this.upgrader.TrySetupUpgradeApplicationDirectory(out string _, out string _).ShouldBeFalse();
|
||||
this.mockFileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissionsShouldSucceed = true;
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CanDownloadNewestVersionFailsIfDownloadDirectoryCreationFails()
|
||||
{
|
||||
Version actualNewestVersion;
|
||||
string message;
|
||||
List<IPackageSearchMetadata> availablePackages = new List<IPackageSearchMetadata>()
|
||||
{
|
||||
this.GeneratePackageSeachMetadata(new Version(CurrentVersion)),
|
||||
this.GeneratePackageSeachMetadata(new Version(NewerVersion)),
|
||||
};
|
||||
|
||||
string testDownloadPath = Path.Combine(this.downloadDirectoryPath, "testNuget.zip");
|
||||
IPackageSearchMetadata newestAvailableVersion = availablePackages.Last();
|
||||
this.mockNuGetFeed.Setup(foo => foo.QueryFeedAsync(NuGetFeedName)).ReturnsAsync(availablePackages);
|
||||
this.mockNuGetFeed.Setup(foo => foo.DownloadPackageAsync(It.Is<PackageIdentity>(packageIdentity => packageIdentity == newestAvailableVersion.Identity))).ReturnsAsync(testDownloadPath);
|
||||
|
||||
bool success = this.upgrader.TryQueryNewestVersion(out actualNewestVersion, out message);
|
||||
|
||||
// Assert that no new version was returned
|
||||
success.ShouldBeTrue($"Expecting TryQueryNewestVersion to have completed sucessfully. Error: {message}");
|
||||
actualNewestVersion.ShouldEqual(newestAvailableVersion.Identity.Version.Version, "Actual new version does not match expected new version.");
|
||||
|
||||
this.mockFileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissionsShouldSucceed = false;
|
||||
bool downloadSuccessful = this.upgrader.TryDownloadNewestVersion(out message);
|
||||
this.mockFileSystem.TryCreateOrUpdateDirectoryToAdminModifyPermissionsShouldSucceed = true;
|
||||
downloadSuccessful.ShouldBeFalse();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net461</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CommandLineParser" Version="2.1.1-beta" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\GVFS.Platform.Windows\GVFS.Platform.Windows.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
|
@ -1,16 +0,0 @@
|
|||
using CommandLine;
|
||||
using GVFS.PlatformLoader;
|
||||
|
||||
namespace GVFS.Upgrader
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
GVFSPlatformLoader.Initialize();
|
||||
|
||||
Parser.Default.ParseArguments<UpgradeOptions>(args)
|
||||
.WithParsed(options => UpgradeOrchestratorFactory.Create(options).Execute());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
using CommandLine;
|
||||
|
||||
namespace GVFS.Upgrader
|
||||
{
|
||||
[Verb("UpgradeOrchestrator", HelpText = "Upgrade VFS for Git.")]
|
||||
public class UpgradeOptions
|
||||
{
|
||||
[Option(
|
||||
"dry-run",
|
||||
Default = false,
|
||||
Required = false,
|
||||
HelpText = "Display progress and errors, but don't install GVFS")]
|
||||
public bool DryRun { get; set; }
|
||||
|
||||
[Option(
|
||||
"no-verify",
|
||||
Default = false,
|
||||
Required = false,
|
||||
HelpText = "Don't verify authenticode signature of installers")]
|
||||
public bool NoVerify { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,389 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GVFS.Upgrader
|
||||
{
|
||||
public abstract class UpgradeOrchestrator
|
||||
{
|
||||
protected InstallerPreRunChecker preRunChecker;
|
||||
protected bool mount;
|
||||
protected ITracer tracer;
|
||||
|
||||
private const EventLevel DefaultEventLevel = EventLevel.Informational;
|
||||
|
||||
private ProductUpgrader upgrader;
|
||||
private string logDirectory = ProductUpgraderInfo.GetLogDirectoryPath();
|
||||
private string installationId;
|
||||
private PhysicalFileSystem fileSystem;
|
||||
private TextWriter output;
|
||||
private TextReader input;
|
||||
|
||||
public UpgradeOrchestrator(
|
||||
ProductUpgrader upgrader,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
InstallerPreRunChecker preRunChecker,
|
||||
TextReader input,
|
||||
TextWriter output)
|
||||
{
|
||||
this.upgrader = upgrader;
|
||||
this.tracer = tracer;
|
||||
this.fileSystem = fileSystem;
|
||||
this.preRunChecker = preRunChecker;
|
||||
this.output = output;
|
||||
this.input = input;
|
||||
this.mount = false;
|
||||
this.ExitCode = ReturnCode.Success;
|
||||
this.installationId = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
}
|
||||
|
||||
public UpgradeOrchestrator(UpgradeOptions options)
|
||||
: this()
|
||||
{
|
||||
this.DryRun = options.DryRun;
|
||||
this.NoVerify = options.NoVerify;
|
||||
}
|
||||
|
||||
public UpgradeOrchestrator()
|
||||
{
|
||||
// CommandLine's Parser will create multiple instances of UpgradeOrchestrator, and we don't want
|
||||
// multiple log files to get created. Defer tracer (and preRunChecker) creation until Execute()
|
||||
this.tracer = null;
|
||||
this.preRunChecker = null;
|
||||
|
||||
this.fileSystem = new PhysicalFileSystem();
|
||||
this.output = Console.Out;
|
||||
this.input = Console.In;
|
||||
this.mount = false;
|
||||
this.ExitCode = ReturnCode.Success;
|
||||
this.installationId = DateTime.Now.ToString("yyyyMMdd_HHmmss");
|
||||
}
|
||||
|
||||
public ReturnCode ExitCode { get; private set; }
|
||||
|
||||
public bool DryRun { get; }
|
||||
|
||||
public bool NoVerify { get; }
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
string error = null;
|
||||
string mountError = null;
|
||||
Version newVersion = null;
|
||||
|
||||
if (this.tracer == null)
|
||||
{
|
||||
this.tracer = this.CreateTracer();
|
||||
}
|
||||
|
||||
if (this.preRunChecker == null)
|
||||
{
|
||||
this.preRunChecker = new InstallerPreRunChecker(this.tracer, GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (this.TryInitialize(out error))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.TryRunUpgrade(out newVersion, out error))
|
||||
{
|
||||
this.ExitCode = ReturnCode.GenericError;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (!this.TryMountRepositories(out mountError))
|
||||
{
|
||||
mountError = Environment.NewLine + "WARNING: " + mountError;
|
||||
this.output.WriteLine(mountError);
|
||||
}
|
||||
|
||||
this.DeletedDownloadedAssets();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ExitCode = ReturnCode.GenericError;
|
||||
}
|
||||
|
||||
if (this.ExitCode == ReturnCode.GenericError)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.AppendLine();
|
||||
sb.Append("ERROR: " + error);
|
||||
|
||||
sb.AppendLine();
|
||||
sb.AppendLine();
|
||||
|
||||
sb.AppendLine($"Upgrade logs can be found at: {this.logDirectory} with file names that end with the installation ID: {this.installationId}.");
|
||||
|
||||
this.output.WriteLine(sb.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newVersion != null)
|
||||
{
|
||||
this.output.WriteLine($"{Environment.NewLine}Upgrade completed successfully{(string.IsNullOrEmpty(mountError) ? "." : ", but one or more repositories will need to be mounted manually.")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.upgrader?.Dispose();
|
||||
}
|
||||
|
||||
if (this.input == Console.In)
|
||||
{
|
||||
this.output.WriteLine("Press Enter to exit.");
|
||||
this.input.ReadLine();
|
||||
}
|
||||
|
||||
Environment.ExitCode = (int)this.ExitCode;
|
||||
}
|
||||
|
||||
protected bool LaunchInsideSpinner(Func<bool> method, string message)
|
||||
{
|
||||
return ConsoleHelper.ShowStatusWhileRunning(
|
||||
method,
|
||||
message,
|
||||
this.output,
|
||||
this.output == Console.Out && !GVFSPlatform.Instance.IsConsoleOutputRedirectedToFile(),
|
||||
null);
|
||||
}
|
||||
|
||||
protected abstract bool TryMountRepositories(out string consoleError);
|
||||
|
||||
private JsonTracer CreateTracer()
|
||||
{
|
||||
string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
|
||||
this.logDirectory,
|
||||
GVFSConstants.LogFileTypes.UpgradeProcess,
|
||||
logId: null,
|
||||
fileSystem: this.fileSystem);
|
||||
|
||||
JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeProcess");
|
||||
|
||||
jsonTracer.AddLogFileEventListener(
|
||||
logFilePath,
|
||||
DefaultEventLevel,
|
||||
Keywords.Any);
|
||||
|
||||
return jsonTracer;
|
||||
}
|
||||
|
||||
private bool TryInitialize(out string errorMessage)
|
||||
{
|
||||
if (this.upgrader == null)
|
||||
{
|
||||
string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
|
||||
if (string.IsNullOrEmpty(gitBinPath))
|
||||
{
|
||||
errorMessage = $"nameof(this.TryInitialize): Unable to locate git installation. Ensure git is installed and try again.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ICredentialStore credentialStore = new GitProcess(gitBinPath, workingDirectoryRoot: null);
|
||||
|
||||
ProductUpgrader upgrader;
|
||||
if (!ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, new LocalGVFSConfig(), credentialStore, this.DryRun, this.NoVerify, out upgrader, out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Configure the upgrader to have installer logs written to the same directory
|
||||
// as the upgrader.
|
||||
upgrader.UpgradeInstanceId = this.installationId;
|
||||
this.upgrader = upgrader;
|
||||
}
|
||||
|
||||
errorMessage = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryRunUpgrade(out Version newVersion, out string consoleError)
|
||||
{
|
||||
Version newGVFSVersion = null;
|
||||
string error = null;
|
||||
|
||||
if (!this.upgrader.UpgradeAllowed(out error))
|
||||
{
|
||||
ProductUpgraderInfo productUpgraderInfo = new ProductUpgraderInfo(
|
||||
this.tracer,
|
||||
this.fileSystem);
|
||||
productUpgraderInfo.DeleteAllInstallerDownloads();
|
||||
this.output.WriteLine(error);
|
||||
consoleError = null;
|
||||
newVersion = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.LaunchInsideSpinner(
|
||||
() =>
|
||||
{
|
||||
if (!this.preRunChecker.TryRunPreUpgradeChecks(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryCheckIfUpgradeAvailable(out newGVFSVersion, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.LogInstalledVersionInfo();
|
||||
|
||||
if (newGVFSVersion != null && !this.TryDownloadUpgrade(newGVFSVersion, out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"Downloading"))
|
||||
{
|
||||
newVersion = null;
|
||||
consoleError = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newGVFSVersion == null)
|
||||
{
|
||||
newVersion = null;
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.LaunchInsideSpinner(
|
||||
() =>
|
||||
{
|
||||
if (!this.preRunChecker.TryUnmountAllGVFSRepos(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.mount = true;
|
||||
|
||||
return true;
|
||||
},
|
||||
"Unmounting repositories"))
|
||||
{
|
||||
newVersion = null;
|
||||
consoleError = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.LaunchInsideSpinner(
|
||||
() =>
|
||||
{
|
||||
if (!this.preRunChecker.IsInstallationBlockedByRunningProcess(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"Checking for blocking processes."))
|
||||
{
|
||||
newVersion = null;
|
||||
consoleError = error;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.upgrader.TryRunInstaller(this.LaunchInsideSpinner, out consoleError))
|
||||
{
|
||||
newVersion = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
newVersion = newGVFSVersion;
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DeletedDownloadedAssets()
|
||||
{
|
||||
string downloadsCleanupError;
|
||||
if (!this.upgrader.TryCleanup(out downloadsCleanupError))
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Upgrade Step", nameof(this.DeletedDownloadedAssets));
|
||||
metadata.Add("Download cleanup error", downloadsCleanupError);
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.DeletedDownloadedAssets)} failed.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryCheckIfUpgradeAvailable(out Version newestVersion, out string consoleError)
|
||||
{
|
||||
newestVersion = null;
|
||||
consoleError = null;
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryCheckIfUpgradeAvailable), EventLevel.Informational))
|
||||
{
|
||||
string message;
|
||||
if (!this.upgrader.TryQueryNewestVersion(out newestVersion, out message))
|
||||
{
|
||||
consoleError = message;
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Upgrade Step", nameof(this.TryCheckIfUpgradeAvailable));
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.upgrader.TryQueryNewestVersion)} failed. {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newestVersion == null)
|
||||
{
|
||||
this.output.WriteLine(message);
|
||||
this.tracer.RelatedInfo($"No new upgrade releases available. {message}");
|
||||
return true;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("New release found - latest available version: {0}", newestVersion);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryDownloadUpgrade(Version version, out string consoleError)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Upgrade Step", nameof(this.TryDownloadUpgrade));
|
||||
metadata.Add("Version", version.ToString());
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity($"{nameof(this.TryDownloadUpgrade)}", EventLevel.Informational, metadata))
|
||||
{
|
||||
if (!this.upgrader.TryDownloadNewestVersion(out consoleError))
|
||||
{
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.upgrader.TryDownloadNewestVersion)} failed. {consoleError}");
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("Successfully downloaded version: " + version.ToString());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void LogInstalledVersionInfo()
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
string installedGVFSVersion = ProcessHelper.GetCurrentProcessVersion();
|
||||
metadata.Add(nameof(installedGVFSVersion), installedGVFSVersion);
|
||||
|
||||
GitVersion installedGitVersion = null;
|
||||
string error = null;
|
||||
string gitPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
|
||||
if (!string.IsNullOrEmpty(gitPath) && GitProcess.TryGetVersion(gitPath, out installedGitVersion, out error))
|
||||
{
|
||||
metadata.Add(nameof(installedGitVersion), installedGitVersion.ToString());
|
||||
}
|
||||
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, "Installed Version", metadata);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
namespace GVFS.Upgrader
|
||||
{
|
||||
public static class UpgradeOrchestratorFactory
|
||||
{
|
||||
public static UpgradeOrchestrator Create(UpgradeOptions options)
|
||||
{
|
||||
return new WindowsUpgradeOrchestrator(options);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Tracing;
|
||||
using System.IO;
|
||||
|
||||
namespace GVFS.Upgrader
|
||||
{
|
||||
public class WindowsUpgradeOrchestrator : UpgradeOrchestrator
|
||||
{
|
||||
public WindowsUpgradeOrchestrator(
|
||||
ProductUpgrader upgrader,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
InstallerPreRunChecker preRunChecker,
|
||||
TextReader input,
|
||||
TextWriter output)
|
||||
: base(upgrader, tracer, fileSystem, preRunChecker, input, output)
|
||||
{
|
||||
}
|
||||
|
||||
public WindowsUpgradeOrchestrator(UpgradeOptions options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
|
||||
protected override bool TryMountRepositories(out string consoleError)
|
||||
{
|
||||
string errorMessage = string.Empty;
|
||||
if (this.mount && !this.LaunchInsideSpinner(
|
||||
() =>
|
||||
{
|
||||
string mountError;
|
||||
if (!this.preRunChecker.TryMountAllGVFSRepos(out mountError))
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Upgrade Step", nameof(this.TryMountRepositories));
|
||||
metadata.Add("Mount Error", mountError);
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.preRunChecker.TryMountAllGVFSRepos)} failed.");
|
||||
errorMessage += mountError;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
"Mounting repositories"))
|
||||
{
|
||||
consoleError = errorMessage;
|
||||
return false;
|
||||
}
|
||||
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -140,30 +140,6 @@ namespace GVFS.CommandLine
|
|||
GVFSConstants.Service.UIName,
|
||||
copySubFolders: true);
|
||||
|
||||
if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
|
||||
{
|
||||
// upgrader
|
||||
this.CopyAllFiles(
|
||||
ProductUpgraderInfo.GetParentLogDirectoryPath(),
|
||||
archiveFolderPath,
|
||||
DeprecatedUpgradeLogsDirectory,
|
||||
copySubFolders: true,
|
||||
targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, DeprecatedUpgradeLogsDirectory));
|
||||
|
||||
this.CopyAllFiles(
|
||||
ProductUpgraderInfo.GetParentLogDirectoryPath(),
|
||||
archiveFolderPath,
|
||||
ProductUpgraderInfo.LogDirectory,
|
||||
copySubFolders: true,
|
||||
targetFolderName: Path.Combine(ProductUpgraderInfo.UpgradeDirectoryName, ProductUpgraderInfo.LogDirectory));
|
||||
|
||||
this.LogDirectoryEnumeration(
|
||||
ProductUpgraderInfo.GetUpgradeProtectedDataDirectory(),
|
||||
Path.Combine(archiveFolderPath, ProductUpgraderInfo.UpgradeDirectoryName),
|
||||
ProductUpgraderInfo.DownloadDirectory,
|
||||
"downloaded-assets.txt");
|
||||
}
|
||||
|
||||
if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSConfig)
|
||||
{
|
||||
this.CopyFile(GVFSPlatform.Instance.GetSecureDataRootForGVFS(), archiveFolderPath, LocalGVFSConfig.FileName);
|
||||
|
|
|
@ -64,8 +64,6 @@ namespace GVFS.CommandLine
|
|||
|
||||
string serviceLogsRoot = GVFSPlatform.Instance.GetLogsDirectoryForGVFSComponent(GVFSConstants.Service.ServiceName);
|
||||
this.DisplayMostRecent(serviceLogsRoot, GVFSConstants.LogFileTypes.Service);
|
||||
|
||||
this.DisplayMostRecent(ProductUpgraderInfo.GetLogDirectoryPath(), GVFSConstants.LogFileTypes.UpgradePrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
using CommandLine;
|
||||
using GVFS.Common;
|
||||
using GVFS.Common.FileSystem;
|
||||
using GVFS.Common.Git;
|
||||
using GVFS.Common.Tracing;
|
||||
using GVFS.Upgrader;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
@ -20,27 +18,18 @@ namespace GVFS.CommandLine
|
|||
|
||||
private ITracer tracer;
|
||||
private PhysicalFileSystem fileSystem;
|
||||
private ProductUpgrader upgrader;
|
||||
private InstallerPreRunChecker prerunChecker;
|
||||
private ProcessLauncher processLauncher;
|
||||
|
||||
private ProductUpgraderPlatformStrategy productUpgraderPlatformStrategy;
|
||||
|
||||
public UpgradeVerb(
|
||||
ProductUpgrader upgrader,
|
||||
ITracer tracer,
|
||||
PhysicalFileSystem fileSystem,
|
||||
InstallerPreRunChecker prerunChecker,
|
||||
ProcessLauncher processWrapper,
|
||||
TextWriter output)
|
||||
{
|
||||
this.upgrader = upgrader;
|
||||
this.tracer = tracer;
|
||||
this.fileSystem = fileSystem;
|
||||
this.prerunChecker = prerunChecker;
|
||||
this.processLauncher = processWrapper;
|
||||
this.Output = output;
|
||||
this.productUpgraderPlatformStrategy = GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(fileSystem, tracer);
|
||||
}
|
||||
|
||||
public UpgradeVerb()
|
||||
|
@ -78,320 +67,7 @@ namespace GVFS.CommandLine
|
|||
|
||||
public override void Execute()
|
||||
{
|
||||
string error;
|
||||
if (!this.TryInitializeUpgrader(out error) || !this.TryRunProductUpgrade())
|
||||
{
|
||||
this.ReportErrorAndExit(this.tracer, ReturnCode.GenericError, error);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryInitializeUpgrader(out string error)
|
||||
{
|
||||
if (this.DryRun && this.Confirmed)
|
||||
{
|
||||
error = $"{DryRunOption} and {ConfirmOption} arguments are not compatible.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GVFSPlatform.Instance.UnderConstruction.SupportsGVFSUpgrade)
|
||||
{
|
||||
error = null;
|
||||
if (this.upgrader == null)
|
||||
{
|
||||
this.productUpgraderPlatformStrategy = GVFSPlatform.Instance.CreateProductUpgraderPlatformInteractions(this.fileSystem, tracer: null);
|
||||
if (!this.productUpgraderPlatformStrategy.TryPrepareLogDirectory(out error))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JsonTracer jsonTracer = new JsonTracer(GVFSConstants.GVFSEtwProviderName, "UpgradeVerb");
|
||||
string logFilePath = GVFSEnlistment.GetNewGVFSLogFileName(
|
||||
ProductUpgraderInfo.GetLogDirectoryPath(),
|
||||
GVFSConstants.LogFileTypes.UpgradeVerb);
|
||||
jsonTracer.AddLogFileEventListener(logFilePath, EventLevel.Informational, Keywords.Any);
|
||||
|
||||
this.tracer = jsonTracer;
|
||||
this.prerunChecker = new InstallerPreRunChecker(this.tracer, this.Confirmed ? GVFSPlatform.Instance.Constants.UpgradeConfirmCommandMessage : GVFSConstants.UpgradeVerbMessages.GVFSUpgrade);
|
||||
|
||||
string gitBinPath = GVFSPlatform.Instance.GitInstallation.GetInstalledGitBinPath();
|
||||
if (string.IsNullOrEmpty(gitBinPath))
|
||||
{
|
||||
error = $"nameof(this.TryInitializeUpgrader): Unable to locate git installation. Ensure git is installed and try again.";
|
||||
return false;
|
||||
}
|
||||
|
||||
ICredentialStore credentialStore = new GitProcess(gitBinPath, workingDirectoryRoot: null);
|
||||
|
||||
ProductUpgrader upgrader;
|
||||
if (ProductUpgrader.TryCreateUpgrader(this.tracer, this.fileSystem, new LocalGVFSConfig(), credentialStore, this.DryRun, this.NoVerify, out upgrader, out error))
|
||||
{
|
||||
this.upgrader = upgrader;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = $"ERROR: {error}";
|
||||
}
|
||||
}
|
||||
|
||||
return this.upgrader != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
error = $"ERROR: {GVFSConstants.UpgradeVerbMessages.GVFSUpgrade} is not supported on this operating system.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryRunProductUpgrade()
|
||||
{
|
||||
string errorOutputFormat = Environment.NewLine + "ERROR: {0}";
|
||||
string message = null;
|
||||
string cannotInstallReason = null;
|
||||
Version newestVersion = null;
|
||||
|
||||
bool isInstallable = this.TryCheckUpgradeInstallable(out cannotInstallReason);
|
||||
if (this.ShouldRunUpgraderTool() && !isInstallable)
|
||||
{
|
||||
this.ReportInfoToConsole($"Cannot upgrade GVFS on this machine.");
|
||||
this.Output.WriteLine(errorOutputFormat, cannotInstallReason);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.upgrader.UpgradeAllowed(out message))
|
||||
{
|
||||
ProductUpgraderInfo productUpgraderInfo = new ProductUpgraderInfo(
|
||||
this.tracer,
|
||||
this.fileSystem);
|
||||
productUpgraderInfo.DeleteAllInstallerDownloads();
|
||||
productUpgraderInfo.RecordHighestAvailableVersion(highestAvailableVersion: null);
|
||||
this.ReportInfoToConsole(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!this.TryRunUpgradeChecks(out newestVersion, out message))
|
||||
{
|
||||
this.Output.WriteLine(errorOutputFormat, message);
|
||||
this.tracer.RelatedError($"{nameof(this.TryRunProductUpgrade)}: Upgrade checks failed. {message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newestVersion == null)
|
||||
{
|
||||
// Make sure there a no asset installers remaining in the Downloads directory. This can happen if user
|
||||
// upgraded by manually downloading and running asset installers.
|
||||
ProductUpgraderInfo productUpgraderInfo = new ProductUpgraderInfo(
|
||||
this.tracer,
|
||||
this.fileSystem);
|
||||
productUpgraderInfo.DeleteAllInstallerDownloads();
|
||||
this.ReportInfoToConsole(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.ShouldRunUpgraderTool())
|
||||
{
|
||||
this.ReportInfoToConsole(message);
|
||||
|
||||
if (!isInstallable)
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.TryRunProductUpgrade)}: {message}");
|
||||
this.Output.WriteLine(errorOutputFormat, message);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we are on a platform that does not support nuget verification, and user has not
|
||||
// specified the --no-verify flag, then print a helpful message here.
|
||||
if (!GVFSPlatform.Instance.UnderConstruction.SupportsNuGetVerification && !this.NoVerify)
|
||||
{
|
||||
string packageVerificationNotSupportedMessage = @"
|
||||
NuGet package verification is not supported on this platform. In order to run upgrade, you must run upgrade without NuGet package verification.
|
||||
To run upgrade without NuGet verification include the --no-verify option.
|
||||
";
|
||||
this.ReportInfoToConsole(packageVerificationNotSupportedMessage);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryRunInstaller(out message))
|
||||
{
|
||||
this.tracer.RelatedError($"{nameof(this.TryRunProductUpgrade)}: Could not launch upgrade tool. {message}");
|
||||
this.Output.WriteLine(errorOutputFormat, "Could not launch upgrade tool. " + message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string advisoryMessage = string.Join(
|
||||
Environment.NewLine,
|
||||
GVFSConstants.UpgradeVerbMessages.UnmountRepoWarning,
|
||||
GVFSPlatform.Instance.Constants.UpgradeInstallAdviceMessage);
|
||||
this.ReportInfoToConsole(message + Environment.NewLine + Environment.NewLine + advisoryMessage + Environment.NewLine);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryRunUpgradeChecks(
|
||||
out Version latestVersion,
|
||||
out string error)
|
||||
{
|
||||
bool upgradeCheckSuccess = false;
|
||||
string errorMessage = null;
|
||||
Version version = null;
|
||||
|
||||
this.ShowStatusWhileRunning(
|
||||
() =>
|
||||
{
|
||||
upgradeCheckSuccess = this.TryCheckUpgradeAvailable(out version, out errorMessage);
|
||||
return upgradeCheckSuccess;
|
||||
},
|
||||
"Checking for GVFS upgrades",
|
||||
suppressGvfsLogMessage: true);
|
||||
|
||||
latestVersion = version;
|
||||
error = errorMessage;
|
||||
|
||||
return upgradeCheckSuccess;
|
||||
}
|
||||
|
||||
private bool TryRunInstaller(out string consoleError)
|
||||
{
|
||||
string upgraderPath = null;
|
||||
string errorMessage = null;
|
||||
bool supportsInlineUpgrade = GVFSPlatform.Instance.Constants.SupportsUpgradeWhileRunning;
|
||||
|
||||
this.ReportInfoToConsole("Launching upgrade tool...");
|
||||
|
||||
if (!this.TryCopyUpgradeTool(out upgraderPath, out consoleError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.TryLaunchUpgradeTool(
|
||||
upgraderPath,
|
||||
runUpgradeInline: supportsInlineUpgrade,
|
||||
consoleError: out errorMessage))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supportsInlineUpgrade)
|
||||
{
|
||||
this.processLauncher.WaitForExit();
|
||||
this.ReportInfoToConsole($"{Environment.NewLine}Upgrade completed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ReportInfoToConsole($"{Environment.NewLine}Installer launched in a new window. Do not run any git or gvfs commands until the installer has completed.");
|
||||
}
|
||||
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryCopyUpgradeTool(out string upgraderExePath, out string consoleError)
|
||||
{
|
||||
upgraderExePath = null;
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryCopyUpgradeTool), EventLevel.Informational))
|
||||
{
|
||||
if (!this.upgrader.TrySetupUpgradeApplicationDirectory(out upgraderExePath, out consoleError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo($"Successfully Copied upgrade tool to {upgraderExePath}");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryLaunchUpgradeTool(string path, bool runUpgradeInline, out string consoleError)
|
||||
{
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryLaunchUpgradeTool), EventLevel.Informational))
|
||||
{
|
||||
Exception exception;
|
||||
string args = string.Empty + (this.DryRun ? $" {DryRunOption}" : string.Empty) + (this.NoVerify ? $" {NoVerifyOption}" : string.Empty);
|
||||
|
||||
// If the upgrade application is being run "inline" with the current process, then do not run the installer via the
|
||||
// shell - we want the upgrade process to inherit the current terminal's stdin / stdout / sterr
|
||||
if (!this.processLauncher.TryStart(path, args, !runUpgradeInline, out exception))
|
||||
{
|
||||
if (exception != null)
|
||||
{
|
||||
consoleError = exception.Message;
|
||||
this.tracer.RelatedError($"Error launching upgrade tool. {exception.ToString()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
consoleError = "Error launching upgrade tool";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("Successfully launched upgrade tool.");
|
||||
}
|
||||
|
||||
consoleError = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryCheckUpgradeAvailable(
|
||||
out Version latestVersion,
|
||||
out string error)
|
||||
{
|
||||
latestVersion = null;
|
||||
error = null;
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryCheckUpgradeAvailable), EventLevel.Informational))
|
||||
{
|
||||
bool checkSucceeded = false;
|
||||
Version version = null;
|
||||
|
||||
checkSucceeded = this.upgrader.TryQueryNewestVersion(out version, out error);
|
||||
if (!checkSucceeded)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string currentVersion = ProcessHelper.GetCurrentProcessVersion();
|
||||
latestVersion = version;
|
||||
|
||||
string message = latestVersion == null ?
|
||||
$"Successfully checked for VFSForGit upgrades. Local version ({currentVersion}) is up-to-date." :
|
||||
$"Successfully checked for VFSForGit upgrades. A new version is available: {latestVersion}, local version is: {currentVersion}.";
|
||||
|
||||
activity.RelatedInfo(message);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryCheckUpgradeInstallable(out string consoleError)
|
||||
{
|
||||
consoleError = null;
|
||||
|
||||
using (ITracer activity = this.tracer.StartActivity(nameof(this.TryCheckUpgradeInstallable), EventLevel.Informational))
|
||||
{
|
||||
if (!this.prerunChecker.TryRunPreUpgradeChecks(
|
||||
out consoleError))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
activity.RelatedInfo("Upgrade is installable.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ShouldRunUpgraderTool()
|
||||
{
|
||||
return this.Confirmed || this.DryRun;
|
||||
}
|
||||
|
||||
private void ReportInfoToConsole(string message, params object[] args)
|
||||
{
|
||||
this.Output.WriteLine(message, args);
|
||||
this.ReportErrorAndExit(this.tracer, ReturnCode.GenericError, "failed to upgrade");
|
||||
}
|
||||
|
||||
public class ProcessLauncher
|
||||
|
|
Загрузка…
Ссылка в новой задаче