Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 324c534

Browse files
committed
Making sure lfs and git merge configs are set up for everyone
Fixes #732 If the project isn't initialized by the extension on the local machine, the lfs hooks and mergecmd configurations won't be set properly. These things are as important as the credential helper configuration steps, so do them at startup. To make play mode switches faster, cache the git installation state in the settings and only run the installers once a day, and validate the existing git/lfs versions once at Unity startup (if we know where they are from the cached settings)
1 parent e29863d commit 324c534

File tree

10 files changed

+193
-133
lines changed

10 files changed

+193
-133
lines changed

src/GitHub.Api/Application/ApplicationManagerBase.cs

Lines changed: 138 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Threading.Tasks;
44
using System.Collections.Generic;
55
using GitHub.Logging;
6+
using static GitHub.Unity.GitInstaller;
67

78
namespace GitHub.Unity
89
{
@@ -13,6 +14,9 @@ abstract class ApplicationManagerBase : IApplicationManager
1314
private RepositoryManager repositoryManager;
1415
private Progress progressReporter;
1516
protected bool isBusy;
17+
protected bool firstRun;
18+
protected Guid instanceId;
19+
1620
public event Action<IProgress> OnProgress
1721
{
1822
add { progressReporter.OnProgress += value; }
@@ -41,94 +45,168 @@ protected void Initialize()
4145
GitClient = new GitClient(Environment, ProcessManager, TaskManager.Token);
4246
}
4347

44-
public void Run(bool firstRun)
48+
public void Run()
4549
{
4650
isBusy = true;
4751

4852
var thread = new Thread(obj =>
4953
{
5054
CancellationToken token = (CancellationToken)obj;
51-
var endTask = new ActionTask<GitInstaller.GitInstallationState>(token, (_, s) => InitializeEnvironment(s)) { Affinity = TaskAffinity.UI };
52-
string path = null;
55+
SetupMetrics(Environment.UnityVersion, firstRun, instanceId);
5356

5457
if (Environment.IsMac)
5558
{
5659
var getEnvPath = new SimpleProcessTask(token, "bash".ToNPath(), "-c \"/usr/libexec/path_helper\"")
5760
.Configure(ProcessManager, dontSetupGit: true)
5861
.Catch(e => true); // make sure this doesn't throw if the task fails
59-
path = getEnvPath.RunWithReturn(true);
62+
var path = getEnvPath.RunWithReturn(true);
6063
if (getEnvPath.Successful)
6164
{
6265
Logger.Trace("Existing Environment Path Original:{0} Updated:{1}", Environment.Path, path);
6366
Environment.Path = path?.Split(new[] { "\"" }, StringSplitOptions.None)[1];
6467
}
6568
}
6669

67-
Environment.OctorunScriptPath = new OctorunInstaller(Environment, TaskManager).SetupOctorunIfNeeded();
70+
GitInstallationState state = new GitInstallationState();
71+
var runInstallers = firstRun;
72+
73+
if (!runInstallers)
74+
{
75+
state = SystemSettings.Get<GitInstallationState>(Constants.GitInstallationState) ?? state;
76+
var now = DateTimeOffset.Now;
77+
runInstallers = now.Date != state.GitLastCheckTime.Date || (!(state.GitIsValid && state.GitLfsIsValid));
78+
}
79+
80+
if (runInstallers)
81+
{
82+
Environment.OctorunScriptPath = new OctorunInstaller(Environment, TaskManager)
83+
.SetupOctorunIfNeeded();
84+
85+
state = new GitInstaller(Environment, ProcessManager, TaskManager, SystemSettings)
86+
{ Progress = progressReporter }
87+
.SetupGitIfNeeded();
88+
}
89+
90+
SetupGit(state);
6891

69-
var state = new GitInstaller(Environment, ProcessManager, TaskManager, SystemSettings)
70-
{ Progress = progressReporter }
71-
.SetupGitIfNeeded();
72-
endTask.PreviousResult = state;
73-
endTask.Start();
92+
if (state.GitIsValid && state.GitLfsIsValid)
93+
{
94+
RestartRepository();
95+
}
96+
97+
new ActionTask<bool>(token, (s, gitIsValid) =>
98+
{
99+
InitializationComplete();
100+
if (gitIsValid)
101+
{
102+
InitializeUI();
103+
}
104+
},
105+
() => state.GitIsValid && state.GitLfsIsValid)
106+
{ Affinity = TaskAffinity.UI }
107+
.Start();
74108
});
75109
thread.Start(CancellationToken);
76110
}
77111

78-
public ITask InitializeRepository()
112+
private void SetupGit(GitInstaller.GitInstallationState state)
79113
{
80-
var targetPath = NPath.CurrentDirectory;
81-
82-
var unityYamlMergeExec = Environment.UnityApplicationContents.Combine("Tools", "UnityYAMLMerge" + Environment.ExecutableExtension);
83-
84-
var yamlMergeCommand = Environment.IsWindows
85-
? $@"'{unityYamlMergeExec}' merge -p ""$BASE"" ""$REMOTE"" ""$LOCAL"" ""$MERGED"""
86-
: $@"'{unityYamlMergeExec}' merge -p '$BASE' '$REMOTE' '$LOCAL' '$MERGED'";
87-
88-
var gitignore = targetPath.Combine(".gitignore");
89-
var gitAttrs = targetPath.Combine(".gitattributes");
90-
var assetsGitignore = targetPath.Combine("Assets", ".gitignore");
91-
92-
var filesForInitialCommit = new List<string> { gitignore, gitAttrs, assetsGitignore };
93-
94-
var task =
95-
GitClient.Init()
96-
.Then(GitClient.SetConfig("merge.unityyamlmerge.cmd", yamlMergeCommand, GitConfigSource.Local))
97-
.Then(GitClient.SetConfig("merge.unityyamlmerge.trustExitCode", "false", GitConfigSource.Local))
98-
.Then(GitClient.LfsInstall())
99-
.ThenInUI(SetProjectToTextSerialization)
100-
.Then(new ActionTask(CancellationToken, _ => {
101-
AssemblyResources.ToFile(ResourceType.Generic, ".gitignore", targetPath, Environment);
102-
AssemblyResources.ToFile(ResourceType.Generic, ".gitattributes", targetPath, Environment);
103-
104-
assetsGitignore.CreateFile();
105-
}))
106-
.Then(GitClient.Add(filesForInitialCommit))
107-
.Then(GitClient.Commit("Initial commit", null))
108-
.Then(_ =>
109-
{
110-
Environment.InitializeRepository();
111-
RestartRepository();
112-
})
113-
.ThenInUI(() =>
114+
if (!(state.GitIsValid && state.GitLfsIsValid))
115+
return;
116+
117+
Environment.GitExecutablePath = state.GitExecutablePath;
118+
Environment.GitLfsExecutablePath = state.GitLfsExecutablePath;
119+
Environment.IsCustomGitExecutable = state.IsCustomGitPath;
120+
Environment.User.Initialize(GitClient);
121+
122+
if (firstRun)
123+
{
124+
var unityYamlMergeExec = Environment.UnityApplicationContents.Combine("Tools", "UnityYAMLMerge" + Environment.ExecutableExtension);
125+
126+
var yamlMergeCommand = Environment.IsWindows
127+
? $@"'{unityYamlMergeExec}' merge -p ""$BASE"" ""$REMOTE"" ""$LOCAL"" ""$MERGED"""
128+
: $@"'{unityYamlMergeExec}' merge -p '$BASE' '$REMOTE' '$LOCAL' '$MERGED'";
129+
130+
GitClient.SetConfig("merge.unityyamlmerge.cmd", yamlMergeCommand, GitConfigSource.Local)
131+
.Catch(e =>
132+
{
133+
Logger.Error(e, "Error setting merge.unityyamlmerge.cmd");
134+
return true;
135+
})
136+
.RunWithReturn(true);
137+
GitClient.SetConfig("merge.unityyamlmerge.trustExitCode", "false", GitConfigSource.Local)
138+
.Catch(e =>
139+
{
140+
Logger.Error(e, "Error setting merge.unityyamlmerge.trustExitCode");
141+
return true;
142+
})
143+
.RunWithReturn(true);
144+
145+
GitClient.LfsInstall().RunWithReturn(true);
146+
147+
if (Environment.IsWindows)
114148
{
115-
TaskManager.Run(UsageTracker.IncrementProjectsInitialized);
116-
InitializeUI();
117-
});
118-
return task;
149+
var credentialHelper = GitClient.GetConfig("credential.helper", GitConfigSource.Global)
150+
.Catch(e =>
151+
{
152+
Logger.Error(e, "Error getting the credential helper");
153+
return true;
154+
}).RunWithReturn(true);
155+
156+
if (string.IsNullOrEmpty(credentialHelper))
157+
{
158+
Logger.Warning("No Windows CredentialHelper found: Setting to wincred");
159+
GitClient.SetConfig("credential.helper", "wincred", GitConfigSource.Global)
160+
.Catch(e =>
161+
{
162+
Logger.Error(e, "Error setting the credential helper");
163+
return true;
164+
})
165+
.RunWithReturn(true);
166+
}
167+
}
168+
}
119169
}
120170

121-
public void RestartRepository()
171+
public void InitializeRepository()
122172
{
123-
if (Environment.RepositoryPath.IsInitialized)
173+
var thread = new Thread(obj =>
124174
{
125-
repositoryManager = Unity.RepositoryManager.CreateInstance(Platform, TaskManager, GitClient, Environment.FileSystem, Environment.RepositoryPath);
126-
repositoryManager.Initialize();
127-
Environment.Repository.Initialize(repositoryManager, TaskManager);
128-
repositoryManager.Start();
129-
Environment.Repository.Start();
130-
Logger.Trace($"Got a repository? {(Environment.Repository != null ? Environment.Repository.LocalPath : "null")}");
131-
}
175+
CancellationToken token = (CancellationToken)obj;
176+
var targetPath = NPath.CurrentDirectory;
177+
178+
var gitignore = targetPath.Combine(".gitignore");
179+
var gitAttrs = targetPath.Combine(".gitattributes");
180+
var assetsGitignore = targetPath.Combine("Assets", ".gitignore");
181+
182+
var filesForInitialCommit = new List<string> { gitignore, gitAttrs, assetsGitignore };
183+
184+
GitClient.Init().RunWithReturn(true);
185+
GitClient.LfsInstall().RunWithReturn(true);
186+
AssemblyResources.ToFile(ResourceType.Generic, ".gitignore", targetPath, Environment);
187+
AssemblyResources.ToFile(ResourceType.Generic, ".gitattributes", targetPath, Environment);
188+
assetsGitignore.CreateFile();
189+
GitClient.Add(filesForInitialCommit).RunWithReturn(true);
190+
GitClient.Commit("Initial commit", null).RunWithReturn(true);
191+
Environment.InitializeRepository();
192+
RestartRepository();
193+
UsageTracker.IncrementProjectsInitialized();
194+
TaskManager.RunInUI(InitializeUI);
195+
});
196+
thread.Start(CancellationToken);
197+
}
198+
199+
public void RestartRepository()
200+
{
201+
if (!Environment.RepositoryPath.IsInitialized)
202+
return;
203+
204+
repositoryManager = Unity.RepositoryManager.CreateInstance(Platform, TaskManager, GitClient, Environment.FileSystem, Environment.RepositoryPath);
205+
repositoryManager.Initialize();
206+
Environment.Repository.Initialize(repositoryManager, TaskManager);
207+
repositoryManager.Start();
208+
Environment.Repository.Start();
209+
Logger.Trace($"Got a repository? {(Environment.Repository != null ? Environment.Repository.LocalPath : "null")}");
132210
}
133211

134212
protected void SetupMetrics(string unityVersion, bool firstRun, Guid instanceId)
@@ -156,62 +234,12 @@ protected void SetupMetrics(string unityVersion, bool firstRun, Guid instanceId)
156234

157235
if (firstRun)
158236
{
159-
TaskManager.Run(UsageTracker.IncrementNumberOfStartups);
237+
UsageTracker.IncrementNumberOfStartups();
160238
}
161239
#endif
162240
}
163-
protected abstract void SetupMetrics();
164241
protected abstract void InitializeUI();
165-
protected abstract void SetProjectToTextSerialization();
166-
167-
/// <summary>
168-
/// Initialize environment after finding where git is. This needs to run on the main thread
169-
/// </summary>
170-
/// <param name="gitExecutablePath"></param>
171-
/// <param name="octorunScriptPath"></param>
172-
private void InitializeEnvironment(GitInstaller.GitInstallationState installationState)
173-
{
174-
isBusy = false;
175-
SetupMetrics();
176-
177-
if (!installationState.GitIsValid)
178-
{
179-
return;
180-
}
181-
182-
var gitInstallDetails = new GitInstaller.GitInstallDetails(Environment.UserCachePath, Environment.IsWindows);
183-
var isCustomGitExec = installationState.GitExecutablePath != gitInstallDetails.GitExecutablePath;
184-
185-
Environment.GitExecutablePath = installationState.GitExecutablePath;
186-
Environment.GitLfsExecutablePath = installationState.GitLfsExecutablePath;
187-
188-
Environment.IsCustomGitExecutable = isCustomGitExec;
189-
Environment.User.Initialize(GitClient);
190-
191-
var afterGitSetup = new ActionTask(CancellationToken, RestartRepository)
192-
.ThenInUI(InitializeUI);
193-
194-
ITask task = afterGitSetup;
195-
if (Environment.IsWindows)
196-
{
197-
var credHelperTask = GitClient.GetConfig("credential.helper", GitConfigSource.Global);
198-
credHelperTask.OnEnd += (thisTask, credentialHelper, success, exception) =>
199-
{
200-
if (!success || string.IsNullOrEmpty(credentialHelper))
201-
{
202-
Logger.Warning("No Windows CredentialHelper found: Setting to wincred");
203-
thisTask
204-
.Then(GitClient.SetConfig("credential.helper", "wincred", GitConfigSource.Global))
205-
.Then(afterGitSetup);
206-
}
207-
else
208-
thisTask.Then(afterGitSetup);
209-
};
210-
task = credHelperTask;
211-
}
212-
task.Start();
213-
}
214-
242+
protected abstract void InitializationComplete();
215243

216244
private bool disposed = false;
217245
protected virtual void Dispose(bool disposing)

src/GitHub.Api/Application/IApplicationManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ public interface IApplicationManager : IDisposable
1818
IGitClient GitClient { get; }
1919
IUsageTracker UsageTracker { get; }
2020
bool IsBusy { get; }
21-
void Run(bool firstRun);
21+
void Run();
2222
void RestartRepository();
23-
ITask InitializeRepository();
23+
void InitializeRepository();
2424
event Action<IProgress> OnProgress;
2525
}
2626
}

src/GitHub.Api/Git/GitClient.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ namespace GitHub.Unity
1010
public interface IGitClient
1111
{
1212
ITask<ValidateGitInstallResult> ValidateGitInstall(NPath path, bool isCustomGit);
13-
ITask Init(IOutputProcessor<string> processor = null);
14-
ITask LfsInstall(IOutputProcessor<string> processor = null);
13+
ITask<string> Init(IOutputProcessor<string> processor = null);
14+
ITask<string> LfsInstall(IOutputProcessor<string> processor = null);
1515
ITask<GitAheadBehindStatus> AheadBehindStatus(string gitRef, string otherRef, IOutputProcessor<GitAheadBehindStatus> processor = null);
1616
ITask<GitStatus> Status(IOutputProcessor<GitStatus> processor = null);
1717
ITask<string> GetConfig(string key, GitConfigSource configSource, IOutputProcessor<string> processor = null);
@@ -85,13 +85,13 @@ public ITask<ValidateGitInstallResult> ValidateGitInstall(NPath path, bool isCus
8585
return endTask;
8686
}
8787

88-
public ITask Init(IOutputProcessor<string> processor = null)
88+
public ITask<string> Init(IOutputProcessor<string> processor = null)
8989
{
9090
return new GitInitTask(cancellationToken, processor)
9191
.Configure(processManager);
9292
}
9393

94-
public ITask LfsInstall(IOutputProcessor<string> processor = null)
94+
public ITask<string> LfsInstall(IOutputProcessor<string> processor = null)
9595
{
9696
return new GitLfsInstallTask(cancellationToken, processor)
9797
.Configure(processManager);

src/GitHub.Api/Helpers/Constants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ static class Constants
1919
@"yyyy-MM-dd\THH\:mm\:sszzz",
2020
};
2121
public const string SkipVersionKey = "SkipVersion";
22+
public const string GitInstallationState = "GitInstallationState";
2223

2324
public static readonly Version MinimumGitVersion = new Version(2, 11, 0);
2425
public static readonly Version MinimumGitLfsVersion = new Version(2, 4, 0);

0 commit comments

Comments
 (0)