diff --git a/TGCommandLine/RepoCommands.cs b/TGCommandLine/RepoCommands.cs index 0545b7220d..1ad2101a4e 100644 --- a/TGCommandLine/RepoCommands.cs +++ b/TGCommandLine/RepoCommands.cs @@ -9,7 +9,7 @@ namespace TGCommandLine public RepoCommand() { Keyword = "repo"; - Children = new Command[] { new RepoSetupCommand(), new RepoUpdateCommand(), new RepoGenChangelogCommand(), new RepoPushChangelogCommand(), new RepoPythonPathCommand(), new RepoSetEmailCommand(), new RepoSetNameCommand(), new RepoMergePRCommand(), new RepoListPRsCommand(), new RepoStatusCommand(), new RepoListBackupsCommand(), new RepoCheckoutCommand(), new RepoResetCommand() }; + Children = new Command[] { new RepoSetupCommand(), new RepoUpdateCommand(), new RepoGenChangelogCommand(), new RepoPushChangelogCommand(), new RepoPythonPathCommand(), new RepoSetEmailCommand(), new RepoSetNameCommand(), new RepoMergePRCommand(), new RepoListPRsCommand(), new RepoStatusCommand(), new RepoListBackupsCommand(), new RepoCheckoutCommand(), new RepoResetCommand(), new RepoUpdateJsonCommand() }; } public override string GetHelpText() { @@ -17,6 +17,30 @@ namespace TGCommandLine } } + class RepoUpdateJsonCommand : Command + { + public RepoUpdateJsonCommand() + { + Keyword = "update-json"; + } + + public override string GetHelpText() + { + return "Updates the cached TGS3.json with the one from the repo. Compilation is blocked if these two do not match."; + } + + protected override ExitCode Run(IList parameters) + { + var res = Server.GetComponent().UpdateTGS3Json(); + if (res != null) + { + OutputProc(res); + return ExitCode.ServerError; + } + return ExitCode.Normal; + } + } + class RepoSetupCommand : Command { public RepoSetupCommand() diff --git a/TGControlPanel/Main.Designer.cs b/TGControlPanel/Main.Designer.cs index 01d1580352..dd296d5ddc 100644 --- a/TGControlPanel/Main.Designer.cs +++ b/TGControlPanel/Main.Designer.cs @@ -158,6 +158,7 @@ this.StaticFileCreateButton = new System.Windows.Forms.Button(); this.label6 = new System.Windows.Forms.Label(); this.StaticFileListBox = new System.Windows.Forms.ListBox(); + this.TGSJsonUpdate = new System.Windows.Forms.Button(); this.ChatPanel.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.ChatPortSelector)).BeginInit(); this.ChatProviderSelectorPanel.SuspendLayout(); @@ -1279,6 +1280,7 @@ // RepoPanel // this.RepoPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); + this.RepoPanel.Controls.Add(this.TGSJsonUpdate); this.RepoPanel.Controls.Add(this.RepoRefreshButton); this.RepoPanel.Controls.Add(this.BackupTagsList); this.RepoPanel.Controls.Add(this.ResetRemote); @@ -1349,7 +1351,7 @@ // RecloneButton // this.RecloneButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.RecloneButton.Location = new System.Drawing.Point(722, 212); + this.RecloneButton.Location = new System.Drawing.Point(722, 247); this.RecloneButton.Name = "RecloneButton"; this.RecloneButton.Size = new System.Drawing.Size(140, 29); this.RecloneButton.TabIndex = 32; @@ -1734,6 +1736,18 @@ this.StaticFileListBox.TabIndex = 0; this.StaticFileListBox.SelectedIndexChanged += new System.EventHandler(this.StaticFileListBox_SelectedIndexChanged); // + // TGSJsonUpdate + // + this.TGSJsonUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.TGSJsonUpdate.Location = new System.Drawing.Point(722, 212); + this.TGSJsonUpdate.Name = "TGSJsonUpdate"; + this.TGSJsonUpdate.Size = new System.Drawing.Size(140, 29); + this.TGSJsonUpdate.TabIndex = 36; + this.TGSJsonUpdate.Text = "Update TGS3.json"; + this.TGSJsonUpdate.UseVisualStyleBackColor = true; + this.TGSJsonUpdate.Visible = false; + this.TGSJsonUpdate.Click += new System.EventHandler(this.TGSJsonUpdate_Click); + // // Main // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -1899,5 +1913,6 @@ private System.Windows.Forms.Button StaticFileSaveButton; private System.Windows.Forms.Button StaticFilesRefreshButton; private System.Windows.Forms.Button StaticFileDownloadButton; + private System.Windows.Forms.Button TGSJsonUpdate; } } diff --git a/TGControlPanel/Main.cs b/TGControlPanel/Main.cs index 62f2c4618a..b7db343f41 100644 --- a/TGControlPanel/Main.cs +++ b/TGControlPanel/Main.cs @@ -29,6 +29,5 @@ namespace TGControlPanel { Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex; } - } } diff --git a/TGControlPanel/RepoPage.cs b/TGControlPanel/RepoPage.cs index b24935ab0e..15983de0ec 100644 --- a/TGControlPanel/RepoPage.cs +++ b/TGControlPanel/RepoPage.cs @@ -115,6 +115,7 @@ namespace TGControlPanel RepoGenChangelogButton.Visible = true; RecloneButton.Visible = true; ResetRemote.Visible = true; + TGSJsonUpdate.Visible = true; CurrentRevisionLabel.Text = Repo.GetHead(false, out string error) ?? "Unknown"; RepoRemoteTextBox.Text = Repo.GetRemote(out error) ?? "Unknown"; @@ -262,6 +263,7 @@ namespace TGControlPanel ResetRemote.Visible = false; BackupTagsList.Visible = false; RepoRefreshButton.Visible = false; + TGSJsonUpdate.Visible = false; RepoPanel.UseWaitCursor = true; @@ -345,5 +347,14 @@ namespace TGControlPanel { DoAsyncOp(RepoAction.GenCL, "Generating changelog..."); } + + private void TGSJsonUpdate_Click(object sender, EventArgs e) + { + if (MessageBox.Show("This will update the cached TGS3.json to the current repository version, potentially redefining symlinks. Proceed?", "Json Update", MessageBoxButtons.YesNo) != DialogResult.Yes) + return; + var res = Server.GetComponent().UpdateTGS3Json(); + if (res != null) + MessageBox.Show(res); + } } } diff --git a/TGServerService/Compiler.cs b/TGServerService/Compiler.cs index 0fc81ab6c8..49ccfffa8f 100644 --- a/TGServerService/Compiler.cs +++ b/TGServerService/Compiler.cs @@ -172,6 +172,16 @@ namespace TGServerService return; } } + + if (!RepoConfigsMatch()) + { + lock (CompilerLock) + { + lastCompilerError = "Repository TGS3.json does not match cached version! Please update the config appropriately!"; + compilerCurrentStatus = IsInitialized(); + return; + } + } try { SendMessage("DM: Setting up symlinks...", ChatMessageType.DeveloperInfo); @@ -181,7 +191,7 @@ namespace TGServerService Directory.CreateDirectory(GameDirA); Directory.CreateDirectory(GameDirB); - var Config = LoadRepoConfig(); + var Config = new RepoConfig(false); if (Config != null) { foreach (var I in Config.StaticDirectoryPaths) @@ -287,52 +297,23 @@ namespace TGServerService { if (GetVersion(TGByondVersion.Installed) == null) { - lastCompilerError = "BYOND not installed!"; - compilerCurrentStatus = TGCompilerStatus.Initialized; - return; - } - bool silent; - lock (CompilerLock) - { - silent = silentCompile; - silentCompile = false; - } - - if(!silent) - SendMessage("DM: Compiling...", ChatMessageType.DeveloperInfo); - - var resurrectee = GetStagingDir(); - - var Config = LoadRepoConfig(); - var deleteExcludeList = new List(); - if (Config != null) - { - deleteExcludeList.AddRange(Config.StaticDirectoryPaths); - deleteExcludeList.AddRange(Config.DLLPaths); - Program.DeleteDirectory(resurrectee, true, deleteExcludeList); - } - - Directory.CreateDirectory(resurrectee + "/.git/logs"); - - if (Config != null) - { - foreach (var I in Config.StaticDirectoryPaths) + lock (CompilerLock) { - var the_path = Path.Combine(resurrectee, I); - if (!Directory.Exists(the_path)) - CreateSymlink(Path.Combine(resurrectee, I), Path.Combine(StaticDirs, I)); - } - foreach (var I in Config.DLLPaths) - { - var the_path = Path.Combine(resurrectee, I); - if (!File.Exists(the_path)) - CreateSymlink(the_path, Path.Combine(StaticDirs, I)); + lastCompilerError = "BYOND not installed!"; + compilerCurrentStatus = TGCompilerStatus.Initialized; + return; } } - - if (!File.Exists(Path.Combine(resurrectee, InterfaceDLLName))) - CreateSymlink(Path.Combine(resurrectee, InterfaceDLLName), InterfaceDLLName); - + if (!RepoConfigsMatch()) + { + lock (CompilerLock) + { + lastCompilerError = "Repository TGS3.json does not match cached version! Please update the config appropriately!"; + compilerCurrentStatus = IsInitialized(); + return; + } + } + string resurrectee; bool repobusy_check = false; if (!Monitor.TryEnter(RepoLock)) repobusy_check = true; @@ -352,12 +333,49 @@ namespace TGServerService lock (CompilerLock) { lastCompilerError = "The repo could not be locked for copying"; - compilerCurrentStatus = TGCompilerStatus.Initialized; //still fairly valid + compilerCurrentStatus = TGCompilerStatus.Initialized; //still fairly valid return; } } try { + bool silent; + lock (CompilerLock) + { + silent = silentCompile; + silentCompile = false; + } + + if (!silent) + SendMessage("DM: Compiling...", ChatMessageType.DeveloperInfo); + + resurrectee = GetStagingDir(); + + var Config = new RepoConfig(false); + var deleteExcludeList = new List(); + deleteExcludeList.AddRange(Config.StaticDirectoryPaths); + deleteExcludeList.AddRange(Config.DLLPaths); + Program.DeleteDirectory(resurrectee, true, deleteExcludeList); + + + Directory.CreateDirectory(resurrectee + "/.git/logs"); + + foreach (var I in Config.StaticDirectoryPaths) + { + var the_path = Path.Combine(resurrectee, I); + if (!Directory.Exists(the_path)) + CreateSymlink(Path.Combine(resurrectee, I), Path.Combine(StaticDirs, I)); + } + foreach (var I in Config.DLLPaths) + { + var the_path = Path.Combine(resurrectee, I); + if (!File.Exists(the_path)) + CreateSymlink(the_path, Path.Combine(StaticDirs, I)); + } + + if (!File.Exists(Path.Combine(resurrectee, InterfaceDLLName))) + CreateSymlink(Path.Combine(resurrectee, InterfaceDLLName), InterfaceDLLName); + deleteExcludeList.Add(".git"); Program.CopyDirectory(RepoPath, resurrectee, deleteExcludeList); //just the tip @@ -376,9 +394,9 @@ namespace TGServerService RepoBusy = false; } } - + var res = CreateBackup(); - if(res != null) + if (res != null) lock (CompilerLock) { lastCompilerError = res; @@ -387,7 +405,7 @@ namespace TGServerService } var dmeName = ProjectName() + ".dme"; - var dmePath = resurrectee + "/" + dmeName; + var dmePath = resurrectee + "/" + dmeName; if (!File.Exists(dmePath)) { var errorMsg = String.Format("Could not find {0}!", dmeName); @@ -417,7 +435,7 @@ namespace TGServerService DM.StartInfo.UseShellExecute = false; var OutputList = new StringBuilder(); DM.OutputDataReceived += new DataReceivedEventHandler( - delegate(object sender, DataReceivedEventArgs e) + delegate (object sender, DataReceivedEventArgs e) { OutputList.Append(Environment.NewLine); OutputList.Append(e.Data); @@ -431,7 +449,7 @@ namespace TGServerService return; canCancelCompilation = true; } - + DM.Start(); DM.BeginOutputReadLine(); while (!DM.HasExited) @@ -469,7 +487,7 @@ namespace TGServerService { //gotta go fast var online = currentStatus == TGDreamDaemonStatus.Online; - if(online) + if (online) Proc.Suspend(); try { @@ -480,7 +498,7 @@ namespace TGServerService } finally { - if(online && !Proc.HasExited) + if (online && !Proc.HasExited) Proc.Resume(); } } diff --git a/TGServerService/Config.cs b/TGServerService/Config.cs index 74d641dd53..188a2c838d 100644 --- a/TGServerService/Config.cs +++ b/TGServerService/Config.cs @@ -33,7 +33,7 @@ namespace TGServerService if (repo) { //ensure we aren't trying to read anything outside the static dirs - var Config = LoadRepoConfig(); + var Config = new RepoConfig(false); if (Config == null) { error = "Unable to load static directory configuration"; diff --git a/TGServerService/Repository.cs b/TGServerService/Repository.cs index ec6957fae7..c52193723b 100644 --- a/TGServerService/Repository.cs +++ b/TGServerService/Repository.cs @@ -15,6 +15,7 @@ namespace TGServerService { const string RepoPath = "Repository"; const string RepoTGS3SettingsPath = RepoPath + "/TGS3.json"; + const string CachedTGS3SettingsPath = "TGS3.json"; const string RepoErrorUpToDate = "Already up to date!"; const string SSHPushRemote = "ssh_push_target"; const string PrivateKeyPath = "RepoKey/private_key.txt"; @@ -33,20 +34,22 @@ namespace TGServerService /// Repo specific information about the installation /// Requires RepoLock and !RepoBusy to be instantiated /// - class RepoConfig + class RepoConfig : IEquatable { - IList LoadArray(object o) { - var array = (object[])o; - var res = new List(); - foreach (var I in array) - res.Add((string)I); - return res; - } - public RepoConfig() + public readonly bool ChangelogSupport; + public readonly string PathToChangelogPy; + public readonly string ChangelogPyArguments; + public readonly IList PipDependancies = new List(); + public readonly IList ChangelogPathsToStage = new List(); + public readonly IList StaticDirectoryPaths = new List(); + public readonly IList DLLPaths = new List(); + + public RepoConfig(bool FromRepository) { - if (!File.Exists(RepoTGS3SettingsPath)) + var path = FromRepository ? RepoTGS3SettingsPath : CachedTGS3SettingsPath; + if (!File.Exists(path)) return; - var rawdata = File.ReadAllText(RepoTGS3SettingsPath); + var rawdata = File.ReadAllText(path); var Deserializer = new JavaScriptSerializer(); var json = Deserializer.Deserialize>(rawdata); try @@ -80,16 +83,59 @@ namespace TGServerService } catch { } } - public readonly bool ChangelogSupport; - public readonly string PathToChangelogPy; - public readonly string ChangelogPyArguments; - public readonly IList PipDependancies = new List(); - public readonly IList ChangelogPathsToStage = new List(); - public readonly IList StaticDirectoryPaths = new List(); - public readonly IList DLLPaths = new List(); - } + private static IList LoadArray(object o) + { + var array = (object[])o; + var res = new List(); + foreach (var I in array) + res.Add((string)I); + return res; + } - RepoConfig _CurrentRepoConfig; + public override bool Equals(object obj) + { + return Equals(obj as RepoConfig); + } + + private static bool ListEquals(IList A, IList B) + { + return A.All(B.Contains) && A.Count == B.Count; + } + + public bool Equals(RepoConfig other) + { + return ChangelogSupport == other.ChangelogSupport + && PathToChangelogPy == other.PathToChangelogPy + && ChangelogPyArguments == other.ChangelogPyArguments + && ListEquals(PipDependancies, other.PipDependancies) + && ListEquals(ChangelogPathsToStage, other.ChangelogPathsToStage) + && ListEquals(StaticDirectoryPaths, other.StaticDirectoryPaths) + && ListEquals(DLLPaths, other.DLLPaths); + } + + public override int GetHashCode() + { + var hashCode = 1890628544; + hashCode = hashCode * -1521134295 + ChangelogSupport.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PathToChangelogPy); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ChangelogPyArguments); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(PipDependancies); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(ChangelogPathsToStage); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(StaticDirectoryPaths); + hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(DLLPaths); + return hashCode; + } + + public static bool operator ==(RepoConfig config1, RepoConfig config2) + { + return EqualityComparer.Default.Equals(config1, config2); + } + + public static bool operator !=(RepoConfig config1, RepoConfig config2) + { + return !(config1 == config2); + } + } void InitRepo() { @@ -97,19 +143,21 @@ namespace TGServerService UpdateInterfaceDll(false); } - RepoConfig LoadRepoConfig() + bool RepoConfigsMatch() { - if (_CurrentRepoConfig == null) - lock (RepoLock) - { - if (RepoBusy) - return null; - if (LoadRepo() == null) - _CurrentRepoConfig = new RepoConfig(); - } - return _CurrentRepoConfig; + //this should never be called while the repo is busy + RepoConfig I = null; + lock (RepoLock) + { + if (!RepoBusy && LoadRepo() == null) + I = new RepoConfig(true); + } + if (I == null) + throw new Exception("Unable to load TGS3.json from repo!"); + var J = new RepoConfig(false); + return I == J; } - + //public api public bool OperationInProgress() { @@ -261,11 +309,28 @@ namespace TGServerService } } + public string UpdateTGS3Json() + { + try + { + if (File.Exists(RepoTGS3SettingsPath)) + File.Copy(RepoTGS3SettingsPath, CachedTGS3SettingsPath, true); + else if (File.Exists(CachedTGS3SettingsPath)) + File.Delete(CachedTGS3SettingsPath); + } + catch(Exception e) + { + return e.ToString(); + } + return null; + } + void InitialConfigureRepository() { Directory.CreateDirectory(StaticDirs); UpdateInterfaceDll(false); - var Config = new RepoConfig(); //RepoBusy is set if we're here + UpdateTGS3Json(); + var Config = new RepoConfig(false); //RepoBusy is set if we're here foreach(var I in Config.StaticDirectoryPaths) { try @@ -300,7 +365,6 @@ namespace TGServerService TGServerService.WriteWarning("Could not setup static DLL: " + I, TGServerService.EventID.RepoConfigurationFail); } } - _CurrentRepoConfig = Config; } //kicks off the cloning thread @@ -442,7 +506,6 @@ namespace TGServerService Commands.Checkout(Repo, sha, Opts); var res = ResetNoLock(null); UpdateSubmodules(); - _CurrentRepoConfig = new RepoConfig(); SendMessage("REPO: Checkout complete!", ChatMessageType.DeveloperInfo); TGServerService.WriteInfo("Repo checked out " + sha, TGServerService.EventID.RepoCheckout); return res; @@ -510,7 +573,6 @@ namespace TGServerService if (res != null) throw new Exception(res); UpdateSubmodules(); - _CurrentRepoConfig = new RepoConfig(); TGServerService.WriteInfo("Repo merge updated to " + originBranch.Tip.Sha, TGServerService.EventID.RepoMergeUpdate); return null; } @@ -617,7 +679,6 @@ namespace TGServerService lock (RepoLock) { var res = LoadRepo() ?? ResetNoLock(trackedBranch ? (Repo.Head.TrackedBranch ?? Repo.Head) : Repo.Head); - _CurrentRepoConfig = new RepoConfig(); if (res == null) { SendMessage(String.Format("REPO: Hard reset to {0}branch", trackedBranch ? "tracked " : ""), ChatMessageType.DeveloperInfo); @@ -725,7 +786,6 @@ namespace TGServerService if (Result == null) try { - _CurrentRepoConfig = new RepoConfig(); UpdateSubmodules(); } catch (Exception e) @@ -849,7 +909,7 @@ namespace TGServerService public string PushChangelog() { - var Config = LoadRepoConfig(); + var Config = new RepoConfig(false); if (Config == null) return "Error reading changelog configuration"; if(!Config.ChangelogSupport || !SSHAuth()) @@ -1003,7 +1063,7 @@ namespace TGServerService //impl proc just for single level recursion public string GenerateChangelogImpl(out string error, bool recurse = false) { - var RConfig = LoadRepoConfig(); + var RConfig = new RepoConfig(false); if (RConfig == null) { error = null; diff --git a/TGServiceInterface/Repository.cs b/TGServiceInterface/Repository.cs index 82f39ee2e1..da0ef9d8c5 100644 --- a/TGServiceInterface/Repository.cs +++ b/TGServiceInterface/Repository.cs @@ -217,5 +217,13 @@ namespace TGServiceInterface /// A dictionary of tag name -> commit on success, null on failure [OperationContract] IDictionary ListBackups(out string error); + + /// + /// Updates the cached TGS3.json to the repo's version + /// Compiles will not succeed if these two to not match + /// + /// null on success, error message on failure + [OperationContract] + string UpdateTGS3Json(); } }