Skip to content

Commit 714c6ad

Browse files
committed
feat: use LibGit2Sharp instead of git commands
1 parent 671b8a9 commit 714c6ad

File tree

5 files changed

+120
-36
lines changed

5 files changed

+120
-36
lines changed

App.axaml.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ namespace Installer;
99
public partial class App : Application
1010
{
1111
public override void Initialize()
12-
=> AvaloniaXamlLoader.Load(this);
12+
=> AvaloniaXamlLoader.Load(this);
1313

1414
public override void OnFrameworkInitializationCompleted()
1515
{

InstallRbxcs.cs

Lines changed: 104 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
using Installer.ViewModels;
1010
using Avalonia.Controls;
1111
using System.Collections.Generic;
12+
using LibGit2Sharp;
13+
using System.Net;
14+
using System.Linq;
1215

1316
namespace Installer;
1417

@@ -25,7 +28,7 @@ public static class Installation
2528
private static Action? _markErrored;
2629
private static bool _errored = false;
2730
private static string _path = "";
28-
private static string _latestTag = "";
31+
private static Tag? _latestTag;
2932

3033
public static async Task InstallRbxcs(
3134
Action<int> updateProgress,
@@ -69,37 +72,102 @@ string path
6972
ShowErrorMessageBox($"Failed to change directory (run as administrator?): {err.Message}");
7073
}
7174

75+
const string repoURL = "https://github.com/roblox-csharp/roblox-cs.git";
76+
const string remoteName = "origin";
77+
var clonePath = Path.Combine(path, ".");
78+
var gitFolder = Path.Combine(clonePath, ".git");
79+
80+
Repository? repo = null;
81+
if (Directory.Exists(gitFolder))
82+
{
83+
try
84+
{
85+
repo = new Repository(gitFolder);
86+
}
87+
catch (Exception err)
88+
{
89+
ShowErrorMessageBox($"Failed to create repository: {err.Message}\n{string.Join('\n', err.StackTrace)}");
90+
return;
91+
}
92+
}
7293
StepProgress();
94+
95+
Remote origin = null!;
96+
IEnumerable<string> refspecs = null!;
7397
Display("Pulling repository...");
7498
try
7599
{
76-
var directoryEntries = Directory.GetFileSystemEntries(".");
77-
if (directoryEntries.Length != 0)
100+
var directoryEntries = Directory.GetFileSystemEntries(clonePath);
101+
if (directoryEntries.Length == 0)
78102
{
79-
ExecuteGitCommand("-v", "Failed to run 'git' command (is git installed?)");
80-
ExecuteGitCommand("pull origin master --allow-unrelated-histories", "Failed to pull from the compiler repository");
103+
try
104+
{
105+
Repository.Clone(repoURL, clonePath);
106+
}
107+
catch (Exception err)
108+
{
109+
ShowErrorMessageBox($"Failed to clone the compiler repository: {err.Message}");
110+
return;
111+
}
81112
}
82-
else
113+
if (repo == null)
83114
{
84-
ExecuteGitCommand("clone https://github.com/roblox-csharp/roblox-cs.git .", "Failed to clone the compiler repository");
115+
try
116+
{
117+
repo = new Repository(gitFolder);
118+
}
119+
catch (Exception err)
120+
{
121+
ShowErrorMessageBox($"Failed to create repository: {err.Message}\n{string.Join('\n', err.StackTrace)}");
122+
return;
123+
}
85124
}
125+
origin = repo.Network.Remotes[remoteName];
126+
refspecs = origin.FetchRefSpecs.Select(refspec => refspec.ToString()).OfType<string>();
127+
GitPull(repo, origin, refspecs);
86128
}
87129
catch (Exception err)
88130
{
89131
ShowErrorMessageBox($"Failed to read the compiler repository directory (run as administrator?): {err.Message}");
132+
return;
90133
}
91134

92135
StepProgress();
93136
Display("Fetching tags...");
94-
ExecuteGitCommand("fetch --tags", "Failed to fetch release tags");
137+
if (repo == null) return;
138+
try
139+
{
140+
repo.Network.Fetch(origin.Name, refspecs, new FetchOptions()
141+
{
142+
TagFetchMode = TagFetchMode.All
143+
});
144+
}
145+
catch (Exception err)
146+
{
147+
ShowErrorMessageBox($"Failed to fetch release tags from repository: {err.Message}");
148+
}
95149
StepProgress();
96150

97151
Display("Fetching latest release...");
98-
_latestTag = ExecuteGitCommand("describe --tags --abbrev=0", "Failed to get the latest release tag");
152+
var tags = repo.Tags;
153+
_latestTag = tags.OrderByDescending(tag => tag.FriendlyName).FirstOrDefault()!;
154+
if (_latestTag == null)
155+
{
156+
ShowErrorMessageBox($"Failed to get the latest release tag, tags found: {repo.Tags.Count()}");
157+
return;
158+
}
99159
StepProgress();
100160

101161
Display("Checking out latest release...");
102-
ExecuteGitCommand($"checkout {_latestTag}", "Failed to checkout the latest release");
162+
try
163+
{
164+
var commit = repo.Commits.FirstOrDefault(commit => commit == (Commit)_latestTag.Target);
165+
Commands.Checkout(repo, commit);
166+
}
167+
catch (Exception err)
168+
{
169+
ShowErrorMessageBox($"Failed to checkout the latest release: {err.Message}");
170+
}
103171
StepProgress();
104172

105173
Display("Building roblox-cs...");
@@ -143,7 +211,32 @@ public static void OnCredentialsAcquired(SourceCredentialsWindow credentialsWind
143211
Display("Successfully added roblox-cs to your PATH.");
144212

145213
StepProgress();
146-
Display($"Successfully installed roblox-cs ({_latestTag}).");
214+
Display($"Successfully installed roblox-cs ({_latestTag?.FriendlyName ?? "???"}).");
215+
}
216+
217+
private static void GitPull(Repository repo, Remote remote, IEnumerable<string> refspecs)
218+
{
219+
try
220+
{
221+
repo.Network.Fetch(remote.Name, refspecs);
222+
}
223+
catch (Exception err)
224+
{
225+
ShowErrorMessageBox($"Failed to fetch origin/master from repository: {err.Message}");
226+
}
227+
try
228+
{
229+
var branchToMerge = repo.Branches[$"{remote.Name}/master"];
230+
var signature = new Signature("rbxcs-installer", "[email protected]", DateTimeOffset.Now);
231+
var mergeResult = repo.Merge(branchToMerge, signature, new MergeOptions
232+
{
233+
FileConflictStrategy = CheckoutFileConflictStrategy.Theirs
234+
});
235+
}
236+
catch (Exception err)
237+
{
238+
ShowErrorMessageBox($"Failed to merge: {err.Message}");
239+
}
147240
}
148241

149242
private static void UpdateEnvironmentPath(string path)
@@ -182,12 +275,6 @@ private static void UpdateShellProfilePath(string path)
182275
WriteProfile(profilePath, pathUpdateCmd);
183276
}
184277

185-
private static string ExecuteGitCommand(string arguments, string errorMessage)
186-
{
187-
var result = ExecuteCommand(errorMessage, "git", arguments.Split(' '));
188-
return result.StandardOutput.Trim();
189-
}
190-
191278
private static ProcessResult ExecuteCommand(string? errorMessage, string command, params string[] arguments)
192279
{
193280
var startInfo = new ProcessStartInfo

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
This is a simple installer GUI for [roblox-cs](https://github.com/roblox-csharp/roblox-cs) written in C# using the Avalonia framework.
44

55
# Requirements
6-
- Git
76
- .NET 8.0
87

98
# Contribute

ViewModels/MainWindowViewModel.cs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using System;
22
using System.IO;
33
using System.Reactive;
4-
using System.Diagnostics;
54
using ReactiveUI;
65
using Avalonia.Threading;
76
using MessageBox.Avalonia;
8-
using System.Threading.Tasks;
97

108
namespace Installer.ViewModels;
119

@@ -73,6 +71,9 @@ public MainWindowViewModel()
7371
Console.WriteLine("Initialized app.");
7472
}
7573

74+
public void UpdateTitle(string title)
75+
=> TitleText = title;
76+
7677
private string GetDefaultInstallationDirectory()
7778
{
7879
string defaultDirectory = string.Empty;
@@ -92,10 +93,7 @@ private void InstallRbxcs()
9293
IsNotInstalling = false;
9394
TitleText = "Installing...";
9495

95-
string fullCurrentDir = Path.GetFullPath(Path.GetDirectoryName(Path.GetDirectoryName(new StackTrace(true).GetFrame(0)!.GetFileName())!)!);
96-
string fullSelectedDir = Path.GetFullPath(_selectedDirectory);
97-
string absolutePath = Path.Combine(fullSelectedDir, "roblox-cs");
98-
96+
var absolutePath = Path.Combine(Path.GetFullPath(_selectedDirectory), "roblox-cs");
9997
Dispatcher.UIThread.InvokeAsync(async () =>
10098
{
10199
try
@@ -128,17 +126,14 @@ await Dispatcher.UIThread.InvokeAsync(() =>
128126
}
129127

130128
private async void SuccessfulExit()
131-
=> await Dispatcher.UIThread.InvokeAsync(() => Environment.Exit(0));
129+
=> await Dispatcher.UIThread.InvokeAsync(() => Environment.Exit(0));
132130

133131
private void MarkErrored()
134132
{
135133
_errored = true;
136134
ProgressBarVisible = false;
137135
}
138136

139-
private void UpdateTitle(string title)
140-
=> TitleText = title;
141-
142137
private void UpdateProgress(int progress)
143-
=> ProgressBarValue = progress;
138+
=> ProgressBarValue = progress;
144139
}

Views/MainWindow.axaml.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Avalonia.Controls;
1+
using System;
2+
using Avalonia.Controls;
23
using Avalonia.Interactivity;
34
using Avalonia.Markup.Xaml;
45
using Installer.ViewModels;
@@ -11,21 +12,23 @@ public partial class MainWindow : Window
1112

1213
public MainWindow()
1314
{
14-
DataContext = new MainWindowViewModel();
1515
InitializeComponent();
1616
}
1717

1818
private void InitializeComponent()
19-
=> AvaloniaXamlLoader.Load(this);
19+
=> AvaloniaXamlLoader.Load(this);
2020

21-
[System.Obsolete]
21+
[Obsolete]
2222
private async void SelectDirectoryButton_Click(object sender, RoutedEventArgs e)
2323
{
2424
var dialog = new OpenFolderDialog();
2525
dialog.Title = "Select installation directory";
2626

2727
var selectedDirectory = await dialog.ShowAsync(this);
28-
if (!string.IsNullOrEmpty(selectedDirectory) && DataContext != null)
29-
DataContext.SelectedDirectory = selectedDirectory;
28+
Console.WriteLine("selected dir: " + selectedDirectory);
29+
if (!string.IsNullOrEmpty(selectedDirectory))
30+
{
31+
((MainWindowViewModel)base.DataContext!).SelectedDirectory = selectedDirectory;
32+
}
3033
}
3134
}

0 commit comments

Comments
 (0)