// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; namespace Spludlow { public class Release { public static string UpdateMagic = "d6b2db15-78fd-47bf-9e60-e27bfcbafe7e"; // Used to uniquly identify the update process so it dont get killed when stopping service private static string MessageQueue = "Sys"; public static void MakeAllNew() // dup(ish) code below { DataTable devSourceTable = Spludlow.Config.DevelopSources().Tables[0]; string newDirectory = Spludlow.Config.ProgramData + @"\Release\New"; Directory.Delete(newDirectory, true); Directory.CreateDirectory(newDirectory); foreach (DataRow devSource in devSourceTable.Rows) { string package = (string)devSource["DevelopSourceId"]; string devSourceDir = (string)devSource["Directory"]; if (Directory.Exists(devSourceDir) == false) continue; string packageDirectory = newDirectory + @"\" + package; if (Directory.Exists(packageDirectory) == true) Directory.Delete(packageDirectory, true); CleanCopy(devSourceDir, packageDirectory); } List list = new List(); foreach (string directory in Directory.GetDirectories(newDirectory)) list.Add(Path.GetFileName(directory)); Spludlow.Log.Report("MakeAllNew", list.ToArray()); } // HostPackageSets each sub array 0=host then each package public static void Send(string sourceHost, string[][] targetHostPackageSets) { Spludlow.Call.Queue(sourceHost + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "Send", new object[] { targetHostPackageSets }); } public static void Send(string[][] targetHostPackageSets) { DateTime startTime = DateTime.Now; // Crate lists of what's going where List allPackages = new List(); Dictionary> networkPackages = new Dictionary>(); Dictionary> hostPackages = new Dictionary>(); foreach (string[] hostSet in targetHostPackageSets) { if (hostSet.Length < 2) throw new ApplicationException("Release Send; Bad targetHostPackageSet to short"); string host = hostSet[0]; string network = Spludlow.Config.NetworkId(host); if (networkPackages.ContainsKey(network) == false) networkPackages.Add(network, new List()); if (hostPackages.ContainsKey(host) == false) hostPackages.Add(host, new List()); for (int index = 1; index < hostSet.Length; ++index) { string package = hostSet[index]; if (allPackages.Contains(package) == false) allPackages.Add(package); if (networkPackages[network].Contains(package) == false) networkPackages[network].Add(package); if (hostPackages[host].Contains(package) == false) hostPackages[host].Add(package); } } DataTable devSourceTable = Spludlow.Config.DevelopSources().Tables[0]; using (Spludlow.TempDirectory tempDir = new TempDirectory()) { // Make clean copy of all the packages string sourceDirectory = tempDir + @"\source"; Directory.CreateDirectory(sourceDirectory); foreach (string package in allPackages) { DataRow devSource = devSourceTable.Rows.Find(package); if (devSource == null) throw new ApplicationException("DevelopementSource Row not found for applicationId:\t" + package); string devSourceDir = (string)devSource["Directory"]; if (Directory.Exists(devSourceDir) == false) throw new ApplicationException("DevelopementSource Directory not found:\t" + package + "\t" + devSourceDir); string packageDirectory = sourceDirectory + @"\" + package; CleanCopy(devSourceDir, packageDirectory); } string networkDirectoryRoot = tempDir + @"\network"; Directory.CreateDirectory(networkDirectoryRoot); // Prepare each network payload then upload on seperate thread Dictionary networkRemoteFilenames = new Dictionary(); List tasks = new List(); foreach (string network in networkPackages.Keys) { string networkDirectory = networkDirectoryRoot + @"\" + network; Directory.CreateDirectory(networkDirectoryRoot); foreach (string package in networkPackages[network]) Spludlow.Io.Dirs.Copy(sourceDirectory + @"\" + package, networkDirectory + @"\" + package); string archiveFilename = networkDirectory + ".7z"; Spludlow.Archive.Create(archiveFilename, networkDirectory); tasks.Add(new Task(() => UploadNetwork(network, archiveFilename, networkRemoteFilenames))); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks.ToArray()); // ? need to wait // Call ReceiveNetwork() on each network router host with host/package lists foreach (string network in networkPackages.Keys) { List hostPayloads = new List(); foreach (string host in hostPackages.Keys) { if (Spludlow.Config.NetworkId(host) != network) continue; List list = new List(); list.Add(host); foreach (string package in hostPackages[host]) list.Add(package); hostPayloads.Add(list.ToArray()); } string networkRouterHost = Spludlow.Config.Router(network); string archiveFilename = networkRemoteFilenames[network]; Spludlow.Call.Queue(networkRouterHost + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "ReceiveNetwork", new object[] { archiveFilename, hostPayloads.ToArray() }); } Spludlow.Io.Dirs.DeletePersistently(tempDir.Path); } Spludlow.Log.Info("Release Send; Finished, Took: " + Spludlow.Text.TimeTook(startTime)); } private static void UploadNetwork(string network, string archiveFilename, Dictionary networkRemoteFilenames) { string networkRouterHost = Spludlow.Config.Router(network); long archiveSize = Spludlow.Io.Files.FileLength(archiveFilename); string remoteFilename = Spludlow.Call.Upload(networkRouterHost, archiveFilename); lock (networkRemoteFilenames) { networkRemoteFilenames.Add(network, remoteFilename); } } public static void CleanCopy(string sourceDirectory, string targetDirectory) { if (Directory.Exists(sourceDirectory + @"\bin\Release") == true) sourceDirectory += @"\bin\Release"; Spludlow.Io.Dirs.Copy(sourceDirectory, targetDirectory); foreach (string filename in Spludlow.Io.Dirs.GetFiles(targetDirectory)) { string name = filename.ToLower(); if (name.EndsWith(".pdb") == true || name.EndsWith(".xml") == true || name.Contains(".vshost.exe") == true) File.Delete(filename); if (name.EndsWith(".config") == true) { if (name.EndsWith(@"\web.config") == false && name.EndsWith(".exe.config") == false) File.Delete(filename); } } } public static void ReceiveNetwork(string networkArchiveFilename, string[][] hostPackages) { using (Spludlow.TempDirectory tempDir = new TempDirectory()) { // Extract network archive Spludlow.Archive.Extract(networkArchiveFilename, tempDir.Path); string[] dirs = Directory.GetDirectories(tempDir.Path); if (dirs.Length != 1) throw new ApplicationException("Did not find one directory in netwok archive." + networkArchiveFilename); string sourceDirectory = dirs[0]; // Crate host archives, upload payload, and call ReceiveHost() foreach (string[] set in hostPackages) { string host = set[0]; string hostDirectory = tempDir.Path + @"\" + host; Directory.CreateDirectory(hostDirectory); for (int index = 1; index < set.Length; ++index) { string package = set[index]; Spludlow.Io.Dirs.Copy(sourceDirectory + @"\" + package, hostDirectory + @"\" + package); } string archiveFilename = hostDirectory + ".7z"; Spludlow.Archive.Create(archiveFilename, hostDirectory); long archiveSize = Spludlow.Io.Files.FileLength(archiveFilename); string hostArchiveFilename = Spludlow.Call.Upload(host, archiveFilename); Spludlow.Log.Info("Uploaded Host Payload:\t" + Spludlow.Text.DataSize(archiveSize)); Spludlow.Call.Queue(host + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "ReceiveHost", new object[] { hostArchiveFilename }); } } } public static void ReceiveHost(string hostArchiveFilename) { List applicationKeys = new List(); DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "ApplicationKey NewDirectory", "String String", }); string newDirectory = Spludlow.Config.ProgramData + @"\Release\New"; using (Spludlow.TempDirectory tempDir = new TempDirectory()) { Spludlow.Archive.Extract(hostArchiveFilename, tempDir.Path); string[] dirs = Directory.GetDirectories(tempDir.Path); if (dirs.Length != 1) throw new ApplicationException("Did not find one directory in host archive." + hostArchiveFilename); string sourceDirectory = dirs[0]; foreach (string sourcePackageDirectory in Directory.GetDirectories(sourceDirectory)) { string applicationKey = Path.GetFileName(sourcePackageDirectory); applicationKeys.Add(applicationKey); string newPackageDirectory = newDirectory + @"\" + applicationKey; if (Directory.Exists(newPackageDirectory) == true) Directory.Delete(newPackageDirectory, true); Spludlow.Io.Dirs.Copy(sourcePackageDirectory, newPackageDirectory); table.Rows.Add(applicationKey, newPackageDirectory); } } Spludlow.Log.Report("Release, ReceiveHost; Finished, calling Update", new object[] { table }); Spludlow.Call.Queue(Environment.MachineName + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "Update", new object[] { applicationKeys }); } public static void Update(List packages) { DataTable applicationsTable = Spludlow.Config.Applications().Tables[0]; // Update Spludlow-Process seperatly before anything else string updatePackage = "Spludlow-Process"; if (packages.Contains(updatePackage) == true) { UpdateFiles(updatePackage, applicationsTable); packages.Remove(updatePackage); } // Update Spludlow-WebService-Admin seperatly second updatePackage = "Spludlow-WebService-Admin"; if (packages.Contains(updatePackage) == true) { Spludlow.Admin.UpdateWebServiceAdmin(); // this method blocks until proccess dies !!!!! packages.Remove(updatePackage); } if (packages.Count == 0) return; // Spawn the Update Process Spludlow.SpawnProcess.RunMethodNoWait("Spludlow", "Spludlow.Release", "UpdateProcess", new object[] { packages.ToArray(), UpdateMagic }); Spludlow.Log.Info("Spawned Process Release.UpdateProcess"); } public static int UpdateWebServiceAdmin() // Called from Admin web servoce to update self { return Spludlow.SpawnProcess.RunMethodNoWait("Spludlow", "Spludlow.Release", "UpdateWebServiceAdminProcess"); } public static void UpdateWebServiceAdminProcess() { string applicationId = "Spludlow-WebService-Admin"; DataTable applicationsTable = Spludlow.Config.Applications().Tables[0]; DataRow row = applicationsTable.Rows.Find(applicationId); if (row == null) throw new ApplicationException("Release UpdateWebServiceAdminProcess; Application Row not found: " + applicationId); string programDirectory = (string)row["Directory"]; string applicationPool = (string)row["Hosted"]; if (programDirectory != "@") { Spludlow.WebServer.Recycle(applicationPool); UpdateFiles(applicationId, applicationsTable); Spludlow.WebServer.Recycle(applicationPool); Spludlow.Log.Info("Release UpdateWebServiceAdminProcess; Updated."); } else { Spludlow.WebServer.Recycle(applicationPool); Spludlow.Log.Info("Release UpdateWebServiceAdminProcess; Skipped file update, host is development system."); } } public static void UpdateProcess(string[] updatePackages, string updateMagic) { List packages = new List(updatePackages); DataTable appsTable = Spludlow.Config.Applications().Tables[0]; // Determine if any library updates, if they are restarts will take place later bool library = false; foreach (string package in packages) { DataRow row = appsTable.Rows.Find(package); if (row == null) throw new ApplicationException("UpdateProcess Application Row not found:\t" + package); if ((string)row["ApplicationType"] == "Lib") { library = true; break; } } // Get lists of service processes and web services that will want stopping and starting !!!! assuming all will if library in package list !!!! List services = new List(); List webAppPools = new List(); foreach (DataRow row in appsTable.Rows) { if (row.IsNull("Hosted") == true) continue; string applicationId = (string)row["ApplicationId"]; if (applicationId == "Spludlow-WebService-Admin") continue; if (library == false && packages.Contains(applicationId) == false) continue; string type = (string)row["ApplicationType"]; string hosted = (string)row["Hosted"]; if (type == "Service") services.Add(hosted); if (type == "Web" && webAppPools.Contains(hosted) == false) webAppPools.Add(hosted); } Spludlow.Log.Info("Release UpdateProcess; Starting"); Spludlow.Admin.ServiceStops(services.ToArray()); Spludlow.Log.Info("Release UpdateProcess; Services stopped"); Spludlow.Admin.WebStops(webAppPools.ToArray()); Spludlow.Log.Info("Release UpdateProcess; Webs Stopped"); foreach (string package in packages) { UpdateFiles(package, appsTable); } Spludlow.Log.Info("Release UpdateProcess; Updated Files"); Spludlow.Admin.WebStarts(webAppPools.ToArray()); Spludlow.Log.Info("Release UpdateProcess; Webs Started"); Spludlow.Admin.ServiceStarts(services.ToArray()); Spludlow.Log.Info("Release UpdateProcess; Services started"); Spludlow.Log.Finish("Release UpdateProcess Finished"); } public static void UpdateFiles(string package, DataTable appsTable) // tidy { if (appsTable == null) appsTable = Spludlow.Config.Applications().Tables[0]; DataRow row = appsTable.Rows.Find(package); if (row == null) throw new ApplicationException("UpdateFiles Application Row not found:\t" + package); string sourceDirectory = Spludlow.Config.ProgramData + @"\Release\New\" + package; string programDirectory = (string)row["Directory"]; DateTime startTime = DateTime.Now; if (programDirectory != "@") // @ as path for development machine webs that are already in place anyway { if (Directory.Exists(programDirectory) == true) { Backup(package, programDirectory); Spludlow.Io.Dirs.DeletePersistently(programDirectory); } Spludlow.Io.Dirs.Copy(sourceDirectory, programDirectory); } Spludlow.Log.Info("Release UpdateFiles; Updated Program Files: " + package + ", took: " + Spludlow.Text.TimeTook(startTime)); } public static DataSet Matrix(string sourceHost) { DataTable devSources = Spludlow.Config.DevelopSources(sourceHost); string[] hosts = Spludlow.Config.Hosts(); Dictionary> hostPackages = new Dictionary>(); Task[] tasks = new Task[hosts.Length]; for (int index = 0; index < hosts.Length; ++index) { string host = hosts[index]; tasks[index] = new Task(() => MatrixWorker(hostPackages, host)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); List applicationIds = new List(); DataTable table = new DataTable(); table.Columns.Add("Application", typeof(string)); table.PrimaryKey = new DataColumn[] { table.Columns["Application"] }; foreach (string host in hosts) { table.Columns.Add(host, typeof(bool)); foreach (string applicationId in hostPackages[host]) { if (applicationIds.Contains(applicationId) == false) applicationIds.Add(applicationId); } } foreach (DataRow devSource in devSources.Rows) { string applicationId = (string)devSource["DevelopSourceId"]; if (applicationIds.Contains(applicationId) == false) applicationIds.Add(applicationId); } foreach (string appicationId in applicationIds) { DataRow row = table.NewRow(); row["Application"] = appicationId; string developSourceId = null; DataRow devSourceRow = devSources.Rows.Find(appicationId); if (devSourceRow != null) developSourceId = (string)devSourceRow["DevelopSourceId"]; foreach (string host in hosts) { row[host] = false; if (developSourceId != null && hostPackages.ContainsKey(host) == true && hostPackages[host].Contains(developSourceId) == true) row[host] = true; } table.Rows.Add(row); } DataSet dataSet = new DataSet(); dataSet.Tables.Add(table); return dataSet; } private static void Backup(string package, string programDirectory) { int keepHistory = 8; string backupDirectory = Spludlow.Config.ProgramData + @"\Release\Backup"; string backupArchiveFilenameStart = backupDirectory + @"\" + package + "_"; string backupArchiveFilename = backupArchiveFilenameStart + Spludlow.Text.TimeStamp() + ".7z"; Spludlow.Archive.Create(backupArchiveFilename, programDirectory); List existingFilenames = BackupFilenames(backupDirectory, backupArchiveFilenameStart, backupArchiveFilename); while (existingFilenames.Count > keepHistory) { File.Delete(existingFilenames[0]); existingFilenames.RemoveAt(0); } } private static List BackupFilenames(string backupDirectory, string backupArchiveFilenameStart, string backupArchiveFilename) { List filenames = new List(); foreach (string filename in Directory.GetFiles(backupDirectory, "*.7z")) { if (filename.Length != backupArchiveFilename.Length) continue; if (filename.StartsWith(backupArchiveFilenameStart) == false) continue; filenames.Add(filename); } filenames.Sort(); return filenames; } public static void Rollback(string host, string package) { Rollback(new string[] { package }, 0); } public static void Rollback(string host, string package, int revisionsBack) { Rollback(new string[] { package }, revisionsBack); } public static void Rollback(string host, string[] packages, int revisionsBack) { Spludlow.Call.Queue(host + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "Rollback", new object[] { packages, revisionsBack }); } public static void Rollback(string[] packages, int revisionsBack) { string backupDirectory = Spludlow.Config.ProgramData + @"\Release\Backup"; string newDirectory = Spludlow.Config.ProgramData + @"\Release\New"; DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "ApplicationKey BackupArchive NewDirectory", "String String String", }); List newPackages = new List(); using (Spludlow.TempDirectory tempDir = new TempDirectory()) { foreach (string package in packages) { string backupArchiveFilenameStart = backupDirectory + @"\" + package + "_"; string backupArchiveFilename = backupArchiveFilenameStart + Spludlow.Text.TimeStamp() + ".7z"; List existingFilenames = BackupFilenames(backupDirectory, backupArchiveFilenameStart, backupArchiveFilename); if (existingFilenames.Count == 0) { Spludlow.Log.Warning("Release Rollback, Package has no backups: " + package); continue; } int index = existingFilenames.Count - (1 + revisionsBack); if (index < 0) { Spludlow.Log.Warning("Release Rollback, Package revisions Back goes past start: " + package); index = 0; } backupArchiveFilename = existingFilenames[index]; Spludlow.Archive.Extract(backupArchiveFilename, tempDir.Path); string[] dirs = Directory.GetDirectories(tempDir.Path); if (dirs.Length != 1) throw new ApplicationException("Release Rollback, Did not find one directory in backup archive." + backupArchiveFilename); string sourceDirectory = dirs[0]; string newPackageDirectory = newDirectory + @"\" + package; if (Directory.Exists(newPackageDirectory) == true) Directory.Delete(newPackageDirectory, true); Spludlow.Io.Dirs.Copy(sourceDirectory, newPackageDirectory); Directory.Delete(sourceDirectory, true); table.Rows.Add(package, backupArchiveFilename, newPackageDirectory); newPackages.Add(package); } } Spludlow.Log.Report("Release Rollback, Calling Update", table); Spludlow.Call.Queue(Environment.MachineName + ":" + MessageQueue, "Spludlow", "Spludlow.Release", "Update", new object[] { newPackages }); } private static void MatrixWorker(Dictionary> hostPackages, string host) { List applications = new List(); try { foreach (DataRow row in Spludlow.Config.Applications(host).Rows) applications.Add((string)row["ApplicationId"]); } catch (Exception ee) { Spludlow.Log.Warning("Release, Martix getting applications on host:\t" + host, ee); } lock (hostPackages) hostPackages.Add(host, new List(applications)); } public static void ReleaseDataFilesReceive(string subFilename, string tempFilename) { string filename = Spludlow.Config.ProgramData + @"\" + subFilename; string directory = Path.GetDirectoryName(filename); if (Directory.Exists(directory) == false) Directory.CreateDirectory(directory); if (File.Exists(filename) == true) File.Delete(filename); File.Copy(tempFilename, filename); Spludlow.Log.Info("Release DataFiles Received File: " + filename); } public static string[] GetAllHostsApplicationIds() { List applications = new List(); string[] hosts = Spludlow.Config.Hosts(); Task[] tasks = new Task[hosts.Length]; for (int index = 0; index < hosts.Length; ++index) { string host = hosts[index]; tasks[index] = new Task(() => GetAllHostsApplicationsWork(host, applications)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); applications.Sort(); return applications.ToArray(); } private static void GetAllHostsApplicationsWork(string host, List applications) { DataTable table = Spludlow.Config.Applications(host); lock (applications) { foreach (DataRow row in table.Rows) { string applicationId = (string)row["ApplicationId"]; if (applications.Contains(applicationId) == false) applications.Add(applicationId); } } } public static DataTable VersionNumbers(string rootDirectory) { DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "Filename ProductVersion FileVersion FileDescription", "String String String String", }); foreach (string filename in Directory.GetFiles(rootDirectory, "*", SearchOption.AllDirectories)) { string extention = Path.GetExtension(filename).ToLower(); if (extention != ".dll" && extention != ".exe") continue; System.Diagnostics.FileVersionInfo info = System.Diagnostics.FileVersionInfo.GetVersionInfo(filename); table.Rows.Add(filename, info.ProductVersion, info.FileVersion, info.FileDescription); } return table; } private static string GetApplicationVersion(string directory) { System.Diagnostics.FileVersionInfo info = null; string versionFilename; versionFilename = directory + @"\Spludlow.dll"; if (File.Exists(versionFilename) == true) info = System.Diagnostics.FileVersionInfo.GetVersionInfo(versionFilename); if (info == null) { versionFilename = directory + @"\bin\Spludlow.dll"; if (File.Exists(versionFilename) == true) info = System.Diagnostics.FileVersionInfo.GetVersionInfo(versionFilename); } if (info == null) return ""; return info.FileVersion; } /// /// Only call with VS closed /// public static void ObtainBinaries() { string indexUrl = "https://www.spludlow.co.uk/Install/Downloads/Index.txt"; ObtainBinaries(indexUrl); } public static void ObtainBinaries(string indexUrl) { string binariesDirectory = Spludlow.Config.ProgramData + @"\Release\Binaries"; if (Directory.Exists(binariesDirectory) == false) Directory.CreateDirectory(binariesDirectory); DataTable indexTable = Spludlow.Data.TextTable.ReadText(Spludlow.Net.Http.GetText(indexUrl)); DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "ApplicationId Update ExistingVersion RemoteVersion", "String Boolean String String", }); List applicationIds = new List(GetAllHostsApplicationIds()); if (applicationIds.Contains("Spludlow") == false) applicationIds.Add("Spludlow"); applicationIds.Sort(); int count = 0; using (Spludlow.TempDirectory tempDir = new TempDirectory()) { foreach (string applicationId in applicationIds) { DataRow indexRow = indexTable.Rows.Find(new object[] { applicationId, "BIN" }); if (indexRow == null) continue; string targetDirectory = binariesDirectory + @"\" + applicationId; string existingVersion = ""; if (Directory.Exists(targetDirectory) == true) existingVersion = GetApplicationVersion(targetDirectory); string releaseVersion = (string)indexRow["Version"]; bool update = (existingVersion == "" || VersionNeedUpdate(existingVersion, releaseVersion) == true); table.Rows.Add(applicationId, update, existingVersion, releaseVersion); if (update == false) continue; if (Directory.Exists(targetDirectory) == true) { try { Directory.Delete(targetDirectory, true); System.Threading.Thread.Sleep(0); if (Directory.Exists(targetDirectory) == true) throw new ApplicationException("Directory not deleted"); } catch (Exception ee) // Havn't been able to reproduce since first seeing? { throw new ApplicationException("ObtainBinaries; Have you got Visual Studio open? Problem deleting old binary directory, please close VS and try again", ee); } } string url = indexUrl.Substring(0, indexUrl.Length - 9) + ((string)indexRow["DownloadLink"]).Substring(2); // 9:"Index.txt" 2:"./" string archiveFilename = tempDir.Path + @"\" + (string)indexRow["Filename"]; string extractDirectory = tempDir.Path + @"\" + Path.GetFileNameWithoutExtension(archiveFilename); Spludlow.Net.Http.GetDataFile(url, archiveFilename); Directory.CreateDirectory(extractDirectory); Spludlow.Archive.Extract(archiveFilename, extractDirectory); string sourceDirectory = extractDirectory + @"\" + applicationId; if (Directory.Exists(sourceDirectory) == false) throw new ApplicationException("ObtainBinaries; Downloaded archive did not contain expected ApplicationId: " + applicationId + ", " + url); Spludlow.Io.Dirs.Copy(sourceDirectory, targetDirectory); ++count; } } Spludlow.Log.Report("Release ObtainBinaries; Downloaded: " + count, indexUrl, table); } public static bool VersionNeedUpdate(string existingVersion, string releaseVersion) { int[] existing = VersionParts(existingVersion); int[] release = VersionParts(releaseVersion); int range = Math.Min(existing.Length, release.Length); for (int index = 0; index < range; ++index) { if (existing[index] < release[index]) return true; } return false; } private static int[] VersionParts(string version) { string[] parts = version.Split(new char[] { '.' }); int[] numbers = new int[parts.Length]; for (int index = 0; index < numbers.Length; ++index) { if (parts[index].Length > 0) numbers[index] = Int32.Parse(parts[index]); else numbers[index] = 0; } return numbers; } public static void DevelopSourcesFromBinaries() { string binariesDirectory = Spludlow.Config.ProgramData + @"\Release\Binaries"; if (Directory.Exists(binariesDirectory) == false) throw new ApplicationException("Release, DevelopSourcesFromBinaries; binaries directory does not exist: " + binariesDirectory); DataTable table = Spludlow.Config.DevelopSourcesEmpty().Tables[0]; foreach (string directory in Directory.GetDirectories(binariesDirectory)) table.Rows.Add(Path.GetFileName(directory), directory); Spludlow.Config.MergeDevelopSources(table); } } }