Documents Repository.cs

This commit is contained in:
Cyberboss
2017-10-21 20:57:42 -04:00
parent e5fa4c4e65
commit 5860ec9c4c
7 changed files with 374 additions and 158 deletions
+152
View File
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web.Script.Serialization;
namespace TGServerService
{
/// <summary>
/// Repository specific information for a <see cref="ServerInstance"/>
/// </summary>
sealed class RepoConfig : IEquatable<RepoConfig>
{
/// <summary>
/// If this json is setup to support <see cref="TGServiceInterface.Components.ITGRepository.GenerateChangelog(out string)"/>
/// </summary>
public readonly bool ChangelogSupport;
/// <summary>
/// Path to the repository's changelog generator script
/// </summary>
public readonly string PathToChangelogPy;
/// <summary>
/// Arguments for the changelog gennerator script
/// </summary>
public readonly string ChangelogPyArguments;
/// <summary>
/// List of python pip dependencies for the changelog generator script
/// </summary>
public readonly IList<string> PipDependancies = new List<string>();
/// <summary>
/// Paths to commit and push to the remote repository
/// </summary>
public readonly IList<string> PathsToStage = new List<string>();
/// <summary>
/// Directory's whose contents should not be touched when the <see cref="ServerInstance"/> updates
/// </summary>
public readonly IList<string> StaticDirectoryPaths = new List<string>();
/// <summary>
/// DLL's used by DreamDaemon call()() operations. Must be handled as symlinks to avoid lockups during update operations
/// </summary>
public readonly IList<string> DLLPaths = new List<string>();
/// <summary>
/// Construct a RepoConfig
/// </summary>
/// <param name="path">Path to the config JSON to use</param>
public RepoConfig(string path)
{
if (!File.Exists(path))
return;
var rawdata = File.ReadAllText(path);
var Deserializer = new JavaScriptSerializer();
var json = Deserializer.Deserialize<IDictionary<string, object>>(rawdata);
try
{
var details = (IDictionary<string, object>)json["changelog"];
PathToChangelogPy = (string)details["script"];
ChangelogPyArguments = (string)details["arguments"];
ChangelogSupport = true;
try
{
PipDependancies = LoadArray(details["pip_dependancies"]);
}
catch { }
}
catch
{
ChangelogSupport = false;
}
try
{
PathsToStage = LoadArray(json["synchronize_paths"]);
}
catch { }
try
{
StaticDirectoryPaths = LoadArray(json["static_directories"]);
}
catch { }
try
{
DLLPaths = LoadArray(json["dlls"]);
}
catch { }
}
/// <summary>
/// Convert an array of <see cref="string"/>s to a <see cref="IList{T}"/> of <see cref="string"/>s
/// </summary>
/// <param name="o">The <see cref="string"/> array to convert</param>
/// <returns></returns>
private static IList<string> LoadArray(object o)
{
var array = (object[])o;
var res = new List<string>();
foreach (var I in array)
res.Add((string)I);
return res;
}
/// <inheritdoc />
public override bool Equals(object obj)
{
return Equals(obj as RepoConfig);
}
/// <summary>
/// Check if two <see cref="string"/> <see cref="IList{T}"/>s have the same contents
/// </summary>
/// <param name="A">The first <see cref="string"/> <see cref="IList{T}"/></param>
/// <param name="B">The second <see cref="string"/> <see cref="IList{T}"/></param>
/// <returns><see langword="true"/> if the <see cref="string"/> <see cref="IList{T}"/>s match, <see langword="false"/> otherwise</returns>
private static bool ListEquals(IList<string> A, IList<string> 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(PathsToStage, other.PathsToStage)
&& ListEquals(StaticDirectoryPaths, other.StaticDirectoryPaths)
&& ListEquals(DLLPaths, other.DLLPaths);
}
public override int GetHashCode()
{
var hashCode = 1890628544;
hashCode = hashCode * -1521134295 + ChangelogSupport.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(PathToChangelogPy);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(ChangelogPyArguments);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(PipDependancies);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(PathsToStage);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(StaticDirectoryPaths);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(DLLPaths);
return hashCode;
}
public static bool operator ==(RepoConfig config1, RepoConfig config2)
{
return EqualityComparer<RepoConfig>.Default.Equals(config1, config2);
}
public static bool operator !=(RepoConfig config1, RepoConfig config2)
{
return !(config1 == config2);
}
}
}
+1 -1
View File
@@ -43,7 +43,7 @@ namespace TGServerService
/// <summary>
/// The instance directory to modify the BYOND cfg before installation
/// </summary>
const string ByondConfigDir = "";
const string ByondConfigDir = StagingDirectory + "/BYOND/cfg";
/// <summary>
/// BYOND's DreamDaemon config file in the cfg modification directory
/// </summary>
+2 -2
View File
@@ -192,7 +192,7 @@ namespace TGServerService
Directory.CreateDirectory(GameDirA);
Directory.CreateDirectory(GameDirB);
var Config = new RepoConfig(false);
var Config = GetCachedRepoConfig();
if (Config != null) {
foreach (var I in Config.StaticDirectoryPaths)
@@ -354,7 +354,7 @@ namespace TGServerService
resurrectee = GetStagingDir();
var Config = new RepoConfig(false);
var Config = GetCachedRepoConfig();
var deleteExcludeList = new List<string> { InterfaceDLLName };
deleteExcludeList.AddRange(Config.StaticDirectoryPaths);
deleteExcludeList.AddRange(Config.DLLPaths);
+1 -1
View File
@@ -35,7 +35,7 @@ namespace TGServerService
if (repo)
{
//ensure we aren't trying to read anything outside the static dirs
var Config = new RepoConfig(false);
var Config = GetCachedRepoConfig();
if (Config == null)
{
error = "Unable to load static directory configuration";
+214 -151
View File
@@ -14,137 +14,84 @@ namespace TGServerService
{
sealed partial class ServerInstance : ITGRepository, IDisposable
{
/// <summary>
/// The <see cref="ServerInstance"/> directory for the repository
/// </summary>
const string RepoPath = "Repository";
/// <summary>
/// The path to the Repository's <see cref="RepoConfig"/> json
/// </summary>
const string RepoTGS3SettingsPath = RepoPath + "/TGS3.json";
/// <summary>
/// Path to the <see cref="ServerInstance"/>'s <see cref="RepoConfig"/> json
/// </summary>
const string CachedTGS3SettingsPath = "TGS3.json";
/// <summary>
/// Error message for when a merge operation fails due to the target branch already having the source branch's commits
/// </summary>
const string RepoErrorUpToDate = "Already up to date!";
/// <summary>
/// Git remote for push operations
/// </summary>
const string SSHPushRemote = "ssh_push_target";
/// <summary>
/// The <see cref="ServerInstance"/> directory for the repository SSH keys
/// </summary>
const string RepoKeyDir = "RepoKey/";
/// <summary>
/// The path to the private ssh-rsa key file
/// </summary>
const string PrivateKeyPath = RepoKeyDir + "private_key.txt";
/// <summary>
/// The path to the public ssh-rsa key file
/// </summary>
const string PublicKeyPath = RepoKeyDir + "public_key.txt";
/// <summary>
/// File name for monitoring which github pull requests are currently test merged
/// </summary>
const string PRJobFile = "prtestjob.json";
/// <summary>
/// The branch that points to the current commit that is live or staged to be live in DreamDaemon
/// </summary>
const string LiveTrackingBranch = "___TGSLiveCommitTrackingBranch";
/// <summary>
/// Commit message for <see cref="RepoConfig.PathsToStage"/>
/// </summary>
const string CommitMessage = "Automatic changelog compile, [ci skip]";
/// <summary>
/// Used in conjunction with <see cref="RepoBusy"/> for multithreading safety
/// </summary>
object RepoLock = new object();
/// <summary>
/// Used in conjunction with <see cref="RepoLock"/> for multithreading safety
/// </summary>
bool RepoBusy = false;
/// <summary>
/// Whether or not a git clone operation is in progress
/// </summary>
bool Cloning = false;
/// <summary>
/// The repository object
/// </summary>
Repository Repo;
/// <summary>
/// Used for reporting operation progress to the <see cref="TGServiceInterface"/>
/// </summary>
int currentProgress = -1;
/// <summary>
/// Used for automatically updating the <see cref="ServerInstance"/>
/// </summary>
System.Timers.Timer autoUpdateTimer = new System.Timers.Timer()
{
AutoReset = true
};
/// <summary>
/// Repo specific information about the installation
/// Requires RepoLock and !RepoBusy to be instantiated
/// Initializes the repository
/// </summary>
class RepoConfig : IEquatable<RepoConfig>
{
public readonly bool ChangelogSupport;
public readonly string PathToChangelogPy;
public readonly string ChangelogPyArguments;
public readonly IList<string> PipDependancies = new List<string>();
public readonly IList<string> PathsToStage = new List<string>();
public readonly IList<string> StaticDirectoryPaths = new List<string>();
public readonly IList<string> DLLPaths = new List<string>();
public RepoConfig(bool FromRepository)
{
var path = FromRepository ? RepoTGS3SettingsPath : CachedTGS3SettingsPath;
if (!File.Exists(path))
return;
var rawdata = File.ReadAllText(path);
var Deserializer = new JavaScriptSerializer();
var json = Deserializer.Deserialize<IDictionary<string, object>>(rawdata);
try
{
var details = (IDictionary<string, object>)json["changelog"];
PathToChangelogPy = (string)details["script"];
ChangelogPyArguments = (string)details["arguments"];
ChangelogSupport = true;
try
{
PipDependancies = LoadArray(details["pip_dependancies"]);
}
catch { }
}
catch {
ChangelogSupport = false;
}
try
{
PathsToStage = LoadArray(json["synchronize_paths"]);
}
catch { }
try
{
StaticDirectoryPaths = LoadArray(json["static_directories"]);
}
catch { }
try
{
DLLPaths = LoadArray(json["dlls"]);
}
catch { }
}
private static IList<string> LoadArray(object o)
{
var array = (object[])o;
var res = new List<string>();
foreach (var I in array)
res.Add((string)I);
return res;
}
public override bool Equals(object obj)
{
return Equals(obj as RepoConfig);
}
private static bool ListEquals(IList<string> A, IList<string> 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(PathsToStage, other.PathsToStage)
&& ListEquals(StaticDirectoryPaths, other.StaticDirectoryPaths)
&& ListEquals(DLLPaths, other.DLLPaths);
}
public override int GetHashCode()
{
var hashCode = 1890628544;
hashCode = hashCode * -1521134295 + ChangelogSupport.GetHashCode();
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(PathToChangelogPy);
hashCode = hashCode * -1521134295 + EqualityComparer<string>.Default.GetHashCode(ChangelogPyArguments);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(PipDependancies);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(PathsToStage);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(StaticDirectoryPaths);
hashCode = hashCode * -1521134295 + EqualityComparer<IList<string>>.Default.GetHashCode(DLLPaths);
return hashCode;
}
public static bool operator ==(RepoConfig config1, RepoConfig config2)
{
return EqualityComparer<RepoConfig>.Default.Equals(config1, config2);
}
public static bool operator !=(RepoConfig config1, RepoConfig config2)
{
return !(config1 == config2);
}
}
void InitRepo()
{
Directory.CreateDirectory(RepoKeyDir);
@@ -157,6 +104,10 @@ namespace TGServerService
SetAutoUpdateInterval(Properties.Settings.Default.AutoUpdateInterval);
}
/// <summary>
/// Checks if the <see cref="CachedTGS3SettingsPath"/> and <see cref="RepoTGS3SettingsPath"/> json files match.
/// </summary>
/// <returns><see langword="true"/> if the jsons match, <see langword="false"/> otherwise</returns>
bool RepoConfigsMatch()
{
//this should never be called while the repo is busy
@@ -164,14 +115,23 @@ namespace TGServerService
lock (RepoLock)
{
if (!RepoBusy && LoadRepo() == null)
I = new RepoConfig(true);
I = new RepoConfig(RepoTGS3SettingsPath);
}
if (I == null)
throw new Exception("Unable to load TGS3.json from repo!");
var J = new RepoConfig(false);
var J = GetCachedRepoConfig();
return I == J;
}
/// <summary>
/// Gets the <see cref="RepoConfig"/> for <see cref="CachedTGS3SettingsPath"/>
/// </summary>
/// <returns>The <see cref="RepoConfig"/> for <see cref="CachedTGS3SettingsPath"/></returns>
RepoConfig GetCachedRepoConfig()
{
return new RepoConfig(CachedTGS3SettingsPath);
}
/// <inheritdoc />
public bool OperationInProgress()
{
@@ -187,7 +147,10 @@ namespace TGServerService
return currentProgress;
}
//Sets up the repo object
/// <summary>
/// Initializes <see cref="Repo"/>
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string LoadRepo()
{
if (Repo != null)
@@ -205,7 +168,9 @@ namespace TGServerService
return null;
}
//Cleans up the repo object
/// <summary>
/// Cleans up <see cref="Repo"/>
/// </summary>
void DisposeRepo()
{
if (Repo != null)
@@ -224,35 +189,38 @@ namespace TGServerService
}
}
//Updates the currentProgress var
//no locks required because who gives a shit, it's a fucking 32-bit integer
/// <summary>
/// Updates <see cref="currentProgress"/> with the progess of the current <see cref="Repo"/> transfer operation
/// </summary>
/// <param name="progress">The <see cref="TransferProgress"/> of the current <see cref="Repo"/> transfer operation</param>
/// <returns><see langword="true"/></returns>
bool HandleTransferProgress(TransferProgress progress)
{
currentProgress = (int)(((float)progress.ReceivedObjects / progress.TotalObjects) * 100) / 2;
currentProgress += (int)(((float)progress.IndexedObjects / progress.TotalObjects) * 100) / 2;
currentProgress = ((int)(((float)progress.ReceivedObjects / progress.TotalObjects) * 100) / 2) +( (int)(((float)progress.IndexedObjects / progress.TotalObjects) * 100) / 2);
return true;
}
//see above
/// <summary>
/// Updates <see cref="currentProgress"/> with the progess of the current <see cref="Repo"/> checkout operation
/// </summary>
/// <param name="path">Ignored</param>
/// <param name="completedSteps">Dividend for progress calculation</param>
/// <param name="totalSteps">Divisor for progress calculation</param>
void HandleCheckoutProgress(string path, int completedSteps, int totalSteps)
{
currentProgress = (int)(((float)completedSteps / totalSteps) * 100);
}
//For the thread parameter
private class TwoStrings
{
public string a, b;
}
//This is the thread that resets za warldo
//clones, checksout, sets up static dir
/// <summary>
/// Backups up the <see cref="StaticDirs"/> and deletes the current <see cref="RepoPath"/> if they exist. Clones the given branch of the given remote. Sets up new <see cref="StaticDirs"/>
/// </summary>
/// <param name="twostrings">Remote and branch name, seperated by a ' '</param>
void Clone(object twostrings)
{
//busy flag set by caller
var ts = (TwoStrings)twostrings;
var RepoURL = ts.a;
var BranchName = ts.b;
var ts = ((string)twostrings).Split(' ');
var RepoURL = ts[0];
var BranchName = ts[1];
try
{
SendMessage(String.Format("REPO: {2} started: Cloning {0} branch of {1} ...", BranchName, RepoURL, Repository.IsValid(RepoPath) ? "Full reset" : "Setup"), MessageType.DeveloperInfo);
@@ -309,12 +277,19 @@ namespace TGServerService
}
}
}
/// <summary>
/// Turns off the gc.auto git config setting
/// </summary>
void DisableGarbageCollectionNoLock()
{
Repo.Config.Set("gc.auto", false);
}
/// <summary>
/// Copies the Static directory to the first available Static_BACKUP path in the <see cref="ServerInstance"/> then deleted the old directory
/// </summary>
void BackupAndDeleteStaticDirectory()
{
if (Directory.Exists(StaticDirs))
@@ -335,6 +310,10 @@ namespace TGServerService
Program.DeleteDirectory(StaticDirs);
}
/// <summary>
/// Updates the <see cref="CachedTGS3SettingsPath"/> with the <see cref="RepoTGS3SettingsPath"/>
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
public string UpdateTGS3Json()
{
try
@@ -351,12 +330,15 @@ namespace TGServerService
return null;
}
/// <summary>
/// Initial setup for the <see cref="StaticDirs"/> and <see cref="CachedTGS3SettingsPath"/>
/// </summary>
void InitialConfigureRepository()
{
Directory.CreateDirectory(StaticDirs);
UpdateInterfaceDll(false);
UpdateTGS3Json();
var Config = new RepoConfig(false); //RepoBusy is set if we're here
var Config = GetCachedRepoConfig(); //RepoBusy is set if we're here
foreach(var I in Config.StaticDirectoryPaths)
{
try
@@ -392,8 +374,7 @@ namespace TGServerService
}
}
}
//kicks off the cloning thread
/// <inheritdoc />
public string Setup(string RepoURL, string BranchName)
{
@@ -415,12 +396,18 @@ namespace TGServerService
new Thread(new ParameterizedThreadStart(Clone))
{
IsBackground = true //make sure we don't hold up shutdown
}.Start(new TwoStrings { a = RepoURL, b = BranchName });
}.Start(RepoURL + ' ' + BranchName);
return null;
}
}
//Gets what HEAD is pointing to
/// <summary>
/// Gets the SHA or branch name of the <see cref="Repo"/>'s HEAD
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <param name="branch">If <see langword="true"/>, returns the branch name instead of the SHA</param>
/// <param name="tracked">If <see langword="true"/>, returns the tracked branch SHA and ignores <paramref name="branch"/></param>
/// <returns>The SHA or branch name of the <see cref="Repo"/>'s HEAD</returns>
string GetShaOrBranch(out string error, bool branch, bool tracked)
{
lock (RepoLock)
@@ -446,8 +433,7 @@ namespace TGServerService
}
}
}
//moist shleppy noises
/// <inheritdoc />
public string GetHead(bool useTracked, out string error)
{
@@ -481,8 +467,11 @@ namespace TGServerService
}
}
//calls git reset --hard on HEAD
//requires RepoLock
/// <summary>
/// Equivalent of running `git reset --hard` on the repository. Requires <see cref="RepoLock"/>
/// </summary>
/// <param name="targetBranch">If not <see langword="null"/>, reset to this branch instead of HEAD</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string ResetNoLock(Branch targetBranch)
{
try
@@ -547,14 +536,18 @@ namespace TGServerService
}
}
//Merges a thing into HEAD, not even necessarily a branch
string MergeBranch(string branchname)
/// <summary>
/// Merges given <paramref name="committish"/> into the current branch
/// </summary>
/// <param name="committish">The sha/branch/tag to merge</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string MergeBranch(string committish)
{
var mo = new MergeOptions()
{
OnCheckoutProgress = HandleCheckoutProgress
};
var Result = Repo.Merge(branchname, MakeSig());
var Result = Repo.Merge(committish, MakeSig());
currentProgress = -1;
switch (Result.Status)
{
@@ -574,6 +567,12 @@ namespace TGServerService
return UpdateImpl(reset, true);
}
/// <summary>
/// Fetches the origin and merges it into the current branch
/// </summary>
/// <param name="reset">If <see langword="true"/>, the operation will perform a hard reset instead of a merge</param>
/// <param name="successOnUpToDate">If <see langword="true"/>, a return value of <see cref="RepoErrorUpToDate"/> will be changed to <see langword="null"/></param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string UpdateImpl(bool reset, bool successOnUpToDate)
{
lock (RepoLock)
@@ -622,6 +621,9 @@ namespace TGServerService
}
}
/// <summary>
/// Properly updates any git submodules in the repository
/// </summary>
private void UpdateSubmodules()
{
var suo = new SubmoduleUpdateOptions
@@ -652,6 +654,10 @@ namespace TGServerService
}
}
/// <summary>
/// Creates a date and timestamped tag of the current HEAD
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string CreateBackup()
{
try
@@ -686,6 +692,11 @@ namespace TGServerService
}
}
/// <summary>
/// Lists tags in the repository created by the <see cref="ServerInstance"/>
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>A dictionary of tag title -> commit SHA on success, <see langword="null"/> on failure</returns>
public IDictionary<string, string> ListBackups(out string error)
{
try
@@ -728,15 +739,20 @@ namespace TGServerService
return res;
}
}
//Makes the LibGit2Sharp sig we'll use for committing based on the configured stuff
/// <summary>
/// Creates a commit <see cref="LibGit2Sharp.Signature"/> based off of the configured name and e-mail
/// </summary>
/// <returns>The created <see cref="LibGit2Sharp.Signature"/></returns>
Signature MakeSig()
{
var Config = Properties.Settings.Default;
return new Signature(new Identity(Config.CommitterName, Config.CommitterEmail), DateTimeOffset.Now);
}
//I wonder...
/// <summary>
/// Deletes the <see cref="ServerInstance"/>'s <see cref="PRJobFile"/>
/// </summary>
void DeletePRList()
{
if (File.Exists(PRJobFile))
@@ -750,7 +766,10 @@ namespace TGServerService
}
}
//json_decode(file2text())
/// <summary>
/// Deserializes the <see cref="PRJobFile"/>
/// </summary>
/// <returns>A <see cref="IDictionary{TKey, TValue}"/> of <see cref="IDictionary{TKey, TValue}"/>. The outer one is keyed by PR# the inner one is keyed by internal <see cref="string"/>s</returns>
IDictionary<string, IDictionary<string, string>> GetCurrentPRList()
{
if (!File.Exists(PRJobFile))
@@ -760,7 +779,10 @@ namespace TGServerService
return Deserializer.Deserialize<IDictionary<string, IDictionary<string, string>>>(rawdata);
}
//text2file(json_encode())
/// <summary>
/// Serializes pull request info into <see cref="PRJobFile"/>
/// </summary>
/// <param name="list"></param>
void SetCurrentPRList(IDictionary<string, IDictionary<string, string>> list)
{
var Serializer = new JavaScriptSerializer();
@@ -770,17 +792,13 @@ namespace TGServerService
/// <inheritdoc />
public string MergePullRequest(int PRNumber)
{
return MergePullRequestImpl(PRNumber, false);
}
string MergePullRequestImpl(int PRNumber, bool impliedUpdate)
{
lock (RepoLock)
{
var result = LoadRepo();
if (result != null)
return result;
SendMessage(String.Format("REPO: {1}erging PR #{0}...", PRNumber, impliedUpdate ? "Test m" : "M"), MessageType.DeveloperInfo);
SendMessage(String.Format("REPO: Merging PR #{0}...", PRNumber), MessageType.DeveloperInfo);
result = ResetNoLock(null);
if (result != null)
return result;
@@ -944,9 +962,10 @@ namespace TGServerService
}
}
/// <inheritdoc />
public string SynchronizePush()
{
var Config = new RepoConfig(false);
var Config = GetCachedRepoConfig();
if (Config == null)
return "Error reading changelog configuration";
if(!Config.ChangelogSupport || !SSHAuth())
@@ -954,6 +973,10 @@ namespace TGServerService
return LocalIsRemote() ? Commit(Config) ?? Push() : "Can't push changelog: HEAD does not match tracked remote branch";
}
/// <summary>
/// Create <see cref="FetchOptions"/> that Prune and have the appropriate credentials and progress handler
/// </summary>
/// <returns>Properly configured <see cref="FetchOptions"/></returns>
FetchOptions GenerateFetchOptions()
{
return new FetchOptions()
@@ -984,6 +1007,10 @@ namespace TGServerService
}
}
/// <summary>
/// Check if the current HEAD matches the tracked remote branch HEAD
/// </summary>
/// <returns><see langword="true"/> if the current HEAD matches the tracked remote branch HEAD, <see langword="false"/> otherwise</returns>
bool LocalIsRemote()
{
lock (RepoLock)
@@ -1003,6 +1030,11 @@ namespace TGServerService
}
}
/// <summary>
/// Create a commit based on a <paramref name="Config"/>
/// </summary>
/// <param name="Config">A <see cref="RepoConfig"/> with <see cref="RepoConfig.PathsToStage"/></param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
string Commit(RepoConfig Config)
{
lock (RepoLock)
@@ -1066,11 +1098,22 @@ namespace TGServerService
}
}
/// <summary>
/// Check if the <see cref="ServerInstance"/> is configured for SSH pushing
/// </summary>
/// <returns><see langword="true"/> if the see cref="ServerInstance"/> is configured for SSH pushing, <see langword="false"/> otherwise</returns>
bool SSHAuth()
{
return File.Exists(PrivateKeyPath) && File.Exists(PublicKeyPath);
}
/// <summary>
/// SSH credentials callback. Properly sets up SSH keys for an authorization operation
/// </summary>
/// <param name="url">Ignored</param>
/// <param name="usernameFromUrl">The username to use for the operation</param>
/// <param name="types">The <see cref="SupportedCredentialTypes"/> for the operation</param>
/// <returns>The proper <see cref="Credentials"/> for the operation</returns>
Credentials GenerateGitCredentials(string url, string usernameFromUrl, SupportedCredentialTypes types)
{
var user = usernameFromUrl ?? "git";
@@ -1094,10 +1137,15 @@ namespace TGServerService
return GenerateChangelogImpl(out error);
}
//impl proc just for single level recursion
/// <summary>
/// Updates the html changelog
/// </summary>
/// <param name="error"><see langword="null"/> on success, error on failure</param>
/// <param name="recurse">If <see langword="true"/>, prevents a recursive call to this function after updating pip dependencies</param>
/// <returns>The output of the python script</returns>
public string GenerateChangelogImpl(out string error, bool recurse = false)
{
var RConfig = new RepoConfig(false);
var RConfig = GetCachedRepoConfig();
if (RConfig == null)
{
error = null;
@@ -1216,11 +1264,17 @@ namespace TGServerService
Properties.Settings.Default.AutoUpdateInterval = newInterval;
}
/// <inheritdoc />
public ulong AutoUpdateInterval()
{
return Properties.Settings.Default.AutoUpdateInterval;
}
/// <summary>
/// Runs on the configured <see cref="Properties.Settings.AutoUpdateInterval"/> and tries to <see cref="Update(bool)"/> and <see cref="Compile(bool)"/> the <see cref="ServerInstance"/>
/// </summary>
/// <param name="sender">A <see cref="System.Timers.Timer"/></param>
/// <param name="e">The event arguments</param>
private void AutoUpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
if (UpdateImpl(true, false) == null)
@@ -1238,11 +1292,16 @@ namespace TGServerService
return true;
}
/// <inheritdoc />
public string PythonPath()
{
return Properties.Settings.Default.PythonPath;
}
/// <summary>
/// Updates <see cref="LiveTrackingBranch"/> with the staged <see cref="Compile(bool)"/>'s SHA
/// </summary>
/// <param name="newSha">The commit SHA that was staged</param>
void UpdateLiveSha(string newSha)
{
if (LoadRepo() != null)
@@ -1253,6 +1312,10 @@ namespace TGServerService
Repo.CreateBranch(LiveTrackingBranch, newSha);
}
/// <summary>
/// Gets the current staged or live commit SHA
/// </summary>
/// <returns>The current staged or live commit SHA</returns>
public string LiveSha()
{
var B = Repo.Branches[LiveTrackingBranch];
+1
View File
@@ -83,6 +83,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="MessageType.cs" />
<Compile Include="RepoConfig.cs" />
<Compile Include="ServerInstance\Administration.cs" />
<Compile Include="ServerInstance\Byond.cs" />
<Compile Include="ServerInstance\Chat.cs" />
+3 -3
View File
@@ -24,18 +24,18 @@ namespace TGServiceInterface
/// <summary>
/// The maximum message size to and from a local server
/// </summary>
public static readonly long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher
public const long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher
/// <summary>
/// The maximum message size to and from a remote server
/// </summary>
public static readonly long TransferLimitRemote = 10485760; //10 MB
public const long TransferLimitRemote = 10485760; //10 MB
/// <summary>
/// Base name of the communication pipe
/// they are formatted as MasterPipeName/ComponentName
/// </summary>
public static string MasterInterfaceName = "TGStationServerService";
public const string MasterInterfaceName = "TGStationServerService";
/// <summary>
/// If this is set, we will try and connect to an HTTPS server running at this address