Skip to content

Commit

Permalink
Rewrite logic for generating list with packages to upgrade (#121)
Browse files Browse the repository at this point in the history
* Remove double slash from nuget package API calls

* Allow for other projects to be upgraded if one or more are with old Sitefinity version

* Improve logic for SyncReferencesWithPackages;

* Add logic to remove references to non-existing files in nuget packages

* Use Path.Combine where approriate

* Improve logic for updating hint path; Update variable names;

* Fix bug with parsing assembly version

* Replaced string.format with string interpolation

* Bump version to 1.1.0.20

* Rewrite logic for building list of packages to upgrade to simplify and resolve package dependency issues;

* Version bump to 1.1.0.21

* Minor changes to logging when generating upgrade config

Co-authored-by: PROGRESS\sruzmano <[email protected]>
  • Loading branch information
yasen-yankov and RuzmanovDev authored Apr 15, 2021
1 parent 809251e commit f38975e
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 80 deletions.
186 changes: 110 additions & 76 deletions Sitefinity CLI/Commands/UpgradeCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ protected virtual async Task ExecuteUpgrade()
}
}

await this.GeneratePowershellConfig(sitefinityProjectFilePaths, newSitefinityPackage);
await this.GenerateUpgradeConfig(sitefinityProjectFilePaths, newSitefinityPackage);

var updaterPath = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, Constants.SitefinityUpgradePowershellFolderName, "Updater.ps1");
this.visualStudioWorker.Initialize(this.SolutionPath);
Expand Down Expand Up @@ -192,7 +192,7 @@ private async Task<string> GetLicenseContent(NuGetPackage newSitefinityPackage)
return licenseContent;
}

private string DetectSitefinityVersion(string sitefinityProjectPath)
private Version DetectSitefinityVersion(string sitefinityProjectPath)
{
CsProjectFileReference sitefinityReference = this.csProjectFileEditor.GetReferences(sitefinityProjectPath)
.FirstOrDefault(r => this.IsSitefinityReference(r));
Expand All @@ -206,7 +206,8 @@ private string DetectSitefinityVersion(string sitefinityProjectPath)
{
// 13.2.7500.76032 .ToString(3) will return 13.2.7500 - we need the version without the revision
var sitefinityVersionWithoutRevision = System.Version.Parse(versionMatch.Groups[1].Value).ToString(3);
return sitefinityVersionWithoutRevision;

return System.Version.Parse(sitefinityVersionWithoutRevision);
}
}

Expand Down Expand Up @@ -277,103 +278,137 @@ private string ReadAllTextFromFile(string path)
}
}

private async Task GeneratePowershellConfig(IEnumerable<string> projectFilePaths, NuGetPackage newSitefinityPackage)
private async Task GenerateUpgradeConfig(IEnumerable<string> projectFilePaths, NuGetPackage newSitefinityVersionPackageTree)
{
this.logger.LogInformation("Exporting powershell config...");
this.logger.LogInformation("Exporting upgrade config...");

var powerShellXmlConfig = new XmlDocument();
var powerShellXmlConfigNode = powerShellXmlConfig.CreateElement("config");
XmlDocument powerShellXmlConfig = new XmlDocument();
XmlElement powerShellXmlConfigNode = powerShellXmlConfig.CreateElement("config");
powerShellXmlConfig.AppendChild(powerShellXmlConfigNode);

foreach (string projectFilePath in projectFilePaths)
{
var projectNode = powerShellXmlConfig.CreateElement("project");
var projectNameAttr = powerShellXmlConfig.CreateAttribute("name");
projectNameAttr.Value = projectFilePath.Split(new string[] { "\\", Constants.CsprojFileExtension, Constants.VBProjFileExtension }, StringSplitOptions.RemoveEmptyEntries).Last();
projectNode.Attributes.Append(projectNameAttr);
powerShellXmlConfigNode.AppendChild(projectNode);
await this.GenerateProjectUpgradeConfigSection(powerShellXmlConfig, powerShellXmlConfigNode, projectFilePath, newSitefinityVersionPackageTree);
}

powerShellXmlConfig.Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constants.SitefinityUpgradePowershellFolderName, "config.xml"));

this.logger.LogInformation("Successfully exported upgrade config!");
}

private async Task GenerateProjectUpgradeConfigSection(XmlDocument powerShellXmlConfig, XmlElement powerShellXmlConfigNode, string projectFilePath, NuGetPackage newSitefinityVersionPackageTree)
{
XmlElement projectNode = powerShellXmlConfig.CreateElement("project");
XmlAttribute projectNameAttribute = powerShellXmlConfig.CreateAttribute("name");
projectNameAttribute.Value = projectFilePath.Split(new string[] { "\\", Constants.CsprojFileExtension, Constants.VBProjFileExtension }, StringSplitOptions.RemoveEmptyEntries).Last();
projectNode.Attributes.Append(projectNameAttribute);
powerShellXmlConfigNode.AppendChild(projectNode);

Version currentSitefinityVersion = this.DetectSitefinityVersion(projectFilePath);
if (currentSitefinityVersion == null)
{
this.logger.LogInformation($"Skip upgrade for project: '{projectFilePath}'. Current Sitefinity version was not detected.");

return;
}

var currentSitefinityVersion = this.DetectSitefinityVersion(projectFilePath);
this.logger.LogInformation($"Detected sitefinity version for '{projectFilePath}' - '{currentSitefinityVersion}'.");

this.logger.LogInformation($"Collecting Sitefinity NuGet package tree for '{projectFilePath}'...");
NuGetPackage currentSitefinityVersionPackageTree = await this.sitefinityPackageManager.GetSitefinityPackageTree(currentSitefinityVersion.ToString());

this.processedPackagesPerProjectCache[projectFilePath] = new HashSet<string>();
if (!this.TryAddPackageTreeToProjectUpgradeConfigSection(powerShellXmlConfig, projectNode, projectFilePath, currentSitefinityVersionPackageTree, newSitefinityVersionPackageTree))
{
await this.ProcessPackagesForProjectUpgradeConfigSection(powerShellXmlConfig, projectNode, projectFilePath, currentSitefinityVersionPackageTree.Dependencies, newSitefinityVersionPackageTree);
}
}

if (string.IsNullOrEmpty(currentSitefinityVersion))
private async Task ProcessPackagesForProjectUpgradeConfigSection(XmlDocument powerShellXmlConfig, XmlElement projectNode, string projectFilePath, IEnumerable<NuGetPackage> currentSitefinityVersionPackages, NuGetPackage newSitefinityVersionPackageTree)
{
IList<NuGetPackage> packageTreesToProcessFurther = new List<NuGetPackage>();
foreach (NuGetPackage currentSitefinityVersionPackage in currentSitefinityVersionPackages)
{
bool isPackageAlreadyProcessed = this.processedPackagesPerProjectCache[projectFilePath].Contains(currentSitefinityVersionPackage.Id);
if (!isPackageAlreadyProcessed &&
!this.TryAddPackageTreeToProjectUpgradeConfigSection(powerShellXmlConfig, projectNode, projectFilePath, currentSitefinityVersionPackage, newSitefinityVersionPackageTree))
{
this.logger.LogInformation(string.Format("Skip upgrade for project: \"{0}\". Current Sitefinity version was not detected.", projectFilePath));
continue;
packageTreesToProcessFurther.Add(currentSitefinityVersionPackage);
}
}

// todo add validation if from version is greater than the to version
foreach (NuGetPackage packageTree in packageTreesToProcessFurther)
{
await this.ProcessPackagesForProjectUpgradeConfigSection(powerShellXmlConfig, projectNode, projectFilePath, packageTree.Dependencies, newSitefinityVersionPackageTree);
}
}

this.logger.LogInformation(string.Format("Detected sitefinity version for \"{0}\" - \"{1}\"", projectFilePath, currentSitefinityVersion));
private bool TryAddPackageTreeToProjectUpgradeConfigSection(XmlDocument powerShellXmlConfig, XmlElement projectNode, string projectFilePath, NuGetPackage currentSitefinityVersionPackage, NuGetPackage newSitefinityVersionPackageTree)
{
bool packageExists = this.sitefinityPackageManager.PackageExists(currentSitefinityVersionPackage.Id, projectFilePath);
if (!packageExists)
{
return false;
}

this.logger.LogInformation(string.Format("Collecting Sitefinity NuGet package tree for \"{0}\"...", projectFilePath));
NuGetPackage currentSitefinityPackage = await this.sitefinityPackageManager.GetSitefinityPackageTree(currentSitefinityVersion);
NuGetPackage newSitefinityVersionPackage = this.FindNuGetPackageByIdInDependencyTree(newSitefinityVersionPackageTree, currentSitefinityVersionPackage.Id);
if (newSitefinityVersionPackage == null)
{
this.logger.LogWarning($"New version for package '{currentSitefinityVersionPackage.Id}' was not found. Package will not be upgraded.");

this.IteratePackages(projectFilePath, currentSitefinityPackage, newSitefinityPackage, (package) =>
{
var packageNode = powerShellXmlConfig.CreateElement("package");
var nameAttr = powerShellXmlConfig.CreateAttribute("name");
nameAttr.Value = package.Id;
var versionAttr = powerShellXmlConfig.CreateAttribute("version");
versionAttr.Value = package.Version;
packageNode.Attributes.Append(nameAttr);
packageNode.Attributes.Append(versionAttr);
projectNode.AppendChild(packageNode);
});
return false;
}

powerShellXmlConfig.Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constants.SitefinityUpgradePowershellFolderName, "config.xml"));
this.AddPackageNodeToProjectUpgradeConfigSection(powerShellXmlConfig, projectNode, newSitefinityVersionPackage);

// Add the NuGet package and all of its dependencies to the cache, because those packages will be upgraded as dependencies of the root package
this.AddNuGetPackageTreeToCache(projectFilePath, newSitefinityVersionPackage);

this.logger.LogInformation("Successfully exported powershell config!");
return true;
}

private void IteratePackages(string projectFilePath, NuGetPackage currentSitefinityPackage, NuGetPackage newSitefinityPackage, Action<NuGetPackage> action)
private void AddPackageNodeToProjectUpgradeConfigSection(XmlDocument powerShellXmlConfig, XmlElement projectNode, NuGetPackage nuGetPackage)
{
this.processedPackagesPerProjectCache[projectFilePath] = new HashSet<string>();
var packagesQueue = new Queue<NuGetPackage>();
packagesQueue.Enqueue(newSitefinityPackage);
XmlElement packageNode = powerShellXmlConfig.CreateElement("package");
XmlAttribute nameAttribute = powerShellXmlConfig.CreateAttribute("name");
nameAttribute.Value = nuGetPackage.Id;
XmlAttribute versionAttribute = powerShellXmlConfig.CreateAttribute("version");
versionAttribute.Value = nuGetPackage.Version;
packageNode.Attributes.Append(nameAttribute);
packageNode.Attributes.Append(versionAttribute);
projectNode.AppendChild(packageNode);
}

while (packagesQueue.Any())
private void AddNuGetPackageTreeToCache(string projectFilePath, NuGetPackage nuGetPackage)
{
if (!this.processedPackagesPerProjectCache[projectFilePath].Contains(nuGetPackage.Id))
{
var package = packagesQueue.Dequeue();

bool isPackageAlreadyProcessed = this.processedPackagesPerProjectCache[projectFilePath].Contains(package.Id);
if (isPackageAlreadyProcessed)
{
continue;
}
this.processedPackagesPerProjectCache[projectFilePath].Add(nuGetPackage.Id);
}

this.processedPackagesPerProjectCache[projectFilePath].Add(package.Id);
foreach (NuGetPackage nuGetPackageDependency in nuGetPackage.Dependencies)
{
this.AddNuGetPackageTreeToCache(projectFilePath, nuGetPackageDependency);
}
}

bool packageExists = this.sitefinityPackageManager.PackageExists(package.Id, projectFilePath);
if (packageExists)
{
action(package);
}
private NuGetPackage FindNuGetPackageByIdInDependencyTree(NuGetPackage nuGetPackageTree, string id)
{
if (nuGetPackageTree.Id.Equals(id, StringComparison.OrdinalIgnoreCase))
{
return nuGetPackageTree;
}

if (package.Dependencies != null)
foreach (NuGetPackage nuGetPackageTreeDependency in nuGetPackageTree.Dependencies)
{
NuGetPackage nuGetPackage = this.FindNuGetPackageByIdInDependencyTree(nuGetPackageTreeDependency, id);
if (nuGetPackage != null)
{
var dependenciesOfDependency = new List<NuGetPackage>();
foreach (NuGetPackage nuGetPackageDependency in package.Dependencies)
{
// Package manager can't update packages that are dependencies of other packages.
var parentDependencies = currentSitefinityPackage.Dependencies.Where(directDependency => directDependency.Dependencies.Any(d => d.Id == nuGetPackageDependency.Id));
if (!parentDependencies.Any())
{
packagesQueue.Enqueue(nuGetPackageDependency);
}
else
{
dependenciesOfDependency.Add(nuGetPackageDependency);
}
}

// Finally include the dependencies that are also dependencies in other packages
foreach (var dependencyOfDependency in dependenciesOfDependency)
{
packagesQueue.Enqueue(dependencyOfDependency);
}
return nuGetPackage;
}
}

return null;
}

private void SyncProjectReferencesWithPackages(IEnumerable<string> projectFilePaths, string solutionFolder)
Expand Down Expand Up @@ -406,18 +441,17 @@ private IList<string> GetProjectsPathsFromSolution(string solutionPath, bool onl

private bool HasValidSitefinityVersion(string projectFilePath)
{
var currentSfVersionString = this.DetectSitefinityVersion(projectFilePath);
var currentVersion = System.Version.Parse(currentSfVersionString);
Version currentVersion = this.DetectSitefinityVersion(projectFilePath);

// The new version can be preview one, or similar. That's why we split by '-' and get the first part for validation.
System.Version versionToUpgrade;
if (string.IsNullOrEmpty(this.Version) || !System.Version.TryParse(this.Version.Split('-').First(), out versionToUpgrade))
if (string.IsNullOrWhiteSpace(this.Version) || !System.Version.TryParse(this.Version.Split('-').First(), out versionToUpgrade))
throw new UpgradeException(string.Format("The version '{0}' you are trying to upgrade to is not valid.", this.Version));

var projectName = Path.GetFileName(projectFilePath);
if (versionToUpgrade <= currentVersion)
{
this.logger.LogWarning(string.Format(Constants.VersionIsGreaterThanOrEqual, projectName, currentSfVersionString, versionToUpgrade));
this.logger.LogWarning(string.Format(Constants.VersionIsGreaterThanOrEqual, projectName, currentVersion, versionToUpgrade));

return false;
}
Expand Down
3 changes: 1 addition & 2 deletions Sitefinity CLI/PackageManagement/NuGetApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public async Task<NuGetPackage> GetPackageWithFullDependencyTree(string id, stri
nuGetPackage.Id = nuGetPackageXmlDoc
.Element(this.xmlns + Constants.EntryElem)
.Element(this.xmlns + Constants.TitleElem).Value;

nuGetPackage.Version = propertiesElement.Element(this.xmlnsd + Constants.VersionElem).Value;

string dependenciesString = propertiesElement.Element(this.xmlnsd + Constants.DependenciesElem).Value;
Expand Down Expand Up @@ -83,8 +84,6 @@ public async Task<NuGetPackage> GetPackageWithFullDependencyTree(string id, stri
}
}
}

nuGetPackage.Dependencies = nuGetPackage.Dependencies.OrderByDescending(d => d.Id).ToList();
}
}

Expand Down
4 changes: 2 additions & 2 deletions Sitefinity CLI/Sitefinity CLI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
<TargetFramework>netcoreapp3.0</TargetFramework>
<AssemblyName>sf</AssemblyName>
<RootNamespace>Sitefinity_CLI</RootNamespace>
<AssemblyVersion>1.1.0.20</AssemblyVersion>
<AssemblyVersion>1.1.0.21</AssemblyVersion>
<Version>1.1.0</Version>
<FileVersion>1.1.0.20</FileVersion>
<FileVersion>1.1.0.21</FileVersion>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>
</PropertyGroup>
Expand Down

0 comments on commit f38975e

Please sign in to comment.