Skip to content

Commit 535693d

Browse files
committed
add support for creating repo into github orgs (that you are member of), closes #218
1 parent 5334578 commit 535693d

5 files changed

Lines changed: 187 additions & 17 deletions

File tree

UnityLauncherPro/Helpers/Github.cs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
using System.Threading.Tasks;
88
using System.Security.Cryptography;
99
using System.Diagnostics;
10+
using System.Collections.Generic;
11+
using System.Text.RegularExpressions;
1012

1113
namespace UnityLauncherPro.Helpers
1214
{
@@ -34,7 +36,7 @@ public class GitHubCreateRepoResult
3436
public static class GithubActions
3537
{
3638

37-
public static async Task<GitHubCreateRepoResult> CreateRepositoryAsync(string token, string repoName, string description = "", bool isPrivate = true, bool autoInit = false)
39+
public static async Task<GitHubCreateRepoResult> CreateRepositoryAsync(string token, string repoName, string description = "", bool isPrivate = true, bool autoInit = false, string organization = null)
3840
{
3941
if (string.IsNullOrWhiteSpace(token))
4042
{
@@ -75,12 +77,13 @@ public static async Task<GitHubCreateRepoResult> CreateRepositoryAsync(string to
7577
using (var content = new StringContent(json, Encoding.UTF8, "application/json"))
7678
{
7779
HttpResponseMessage response;
80+
var endpoint = string.IsNullOrWhiteSpace(organization)
81+
? "https://api.github.com/user/repos"
82+
: "https://api.github.com/orgs/" + Uri.EscapeDataString(organization.Trim()) + "/repos";
7883

7984
try
8085
{
81-
response = await client.PostAsync(
82-
"https://api.github.com/user/repos",
83-
content);
86+
response = await client.PostAsync(endpoint, content);
8487
}
8588
catch (Exception ex)
8689
{
@@ -120,12 +123,47 @@ public static async Task<GitHubCreateRepoResult> CreateRepositoryAsync(string to
120123
}
121124
}
122125

126+
public static async Task<List<string>> GetUserOrganizationsAsync(string token)
127+
{
128+
var organizations = new List<string>();
123129

130+
if (string.IsNullOrWhiteSpace(token)) return organizations;
124131

132+
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
125133

134+
using (var client = new HttpClient())
135+
{
136+
client.DefaultRequestHeaders.UserAgent.ParseAdd(MainWindow.appName);
137+
client.DefaultRequestHeaders.Authorization =
138+
new AuthenticationHeaderValue("Bearer", token.Trim());
126139

140+
client.DefaultRequestHeaders.Accept.ParseAdd("application/vnd.github+json");
141+
client.DefaultRequestHeaders.Add("X-GitHub-Api-Version", "2022-11-28");
127142

143+
HttpResponseMessage response;
128144

145+
try
146+
{
147+
response = await client.GetAsync("https://api.github.com/user/orgs");
148+
}
149+
catch
150+
{
151+
return organizations;
152+
}
153+
154+
if (!response.IsSuccessStatusCode) return organizations;
155+
156+
string responseText = await response.Content.ReadAsStringAsync();
157+
var matches = Regex.Matches(responseText, "\"login\"\\s*:\\s*\"([^\"]+)\"");
158+
159+
for (int i = 0; i < matches.Count; i++)
160+
{
161+
organizations.Add(matches[i].Groups[1].Value);
162+
}
163+
}
164+
165+
return organizations;
166+
}
129167

130168
public static async Task<string> InitRepositoryAsync(string baseDir, string projectName, bool initGitLfs = false, string defaultBranch = "main")
131169
{
@@ -323,10 +361,7 @@ public static async Task<GitHubTokenValidationResult> ValidateTokenAsync(string
323361
{
324362
IsValid = false,
325363
StatusCode = response.StatusCode,
326-
Error = "Unexpected GitHub response: " +
327-
(int)response.StatusCode + " " +
328-
response.ReasonPhrase + "\n" +
329-
responseText
364+
Error = "Unexpected GitHub response: " + (int)response.StatusCode + " " + response.ReasonPhrase + "\n" + responseText
330365
};
331366
}
332367
}

UnityLauncherPro/NewProject.xaml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
xmlns:data="clr-namespace:UnityLauncherPro.Data"
88
d:DataContext="{d:DesignInstance Type=local:NewProject}"
99
mc:Ignorable="d"
10-
Title="Create New Project" Height="660" Width="670" Background="{DynamicResource ThemeDarkestBackground}" PreviewKeyDown="Window_PreviewKeyDown" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" ShowInTaskbar="True">
10+
Title="Create New Project" Height="680" Width="670" Background="{DynamicResource ThemeDarkestBackground}" PreviewKeyDown="Window_PreviewKeyDown" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" ShowInTaskbar="True">
1111

1212
<Grid>
1313
<Grid.ColumnDefinitions>
@@ -89,7 +89,9 @@
8989
<Button x:Name="btnAuthorizeToken" Style="{StaticResource CustomButton}" Content="Authorize" ToolTip="Open Github to create a token" Height="22" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="6,4,0,0" IsEnabled="False" Width="60" Click="btnAuthorizeToken_Click" />
9090
<Button x:Name="btnDisconnectToken" Style="{StaticResource CustomButton}" Content="Disconnect" ToolTip="Disconnect from Github AND delete saved token" Height="22" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="6,4,0,0" IsEnabled="False" Width="64" Visibility="Collapsed" Click="btnDisconnectToken_Click" />
9191
</StackPanel>
92+
9293
<CheckBox x:Name="chkEnableVersionControl" Content="Enable Version Control" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,6,0,6" VerticalAlignment="Center" Checked="chkEnableVersionControl_Checked" Unchecked="chkEnableVersionControl_Checked"/>
94+
9395
<Grid Margin="0,0,0,0">
9496
<Grid.ColumnDefinitions>
9597
<ColumnDefinition Width="120"/>
@@ -103,8 +105,14 @@
103105
<Label Content="🌐" Foreground="#FF009CFF"/>
104106
</StackPanel>
105107
</Grid>
108+
109+
<StackPanel Orientation="Horizontal" Margin="0,4,0,0">
110+
<CheckBox x:Name="chkEnableOrgs" Content="Organization:" ToolTip="Enable Large File Storage and add .gitattributes" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,8,0,0" VerticalAlignment="Center" IsChecked="True" Checked="chkEnableOrgs_Checked" Unchecked="chkEnableOrgs_Checked"/>
111+
<ComboBox x:Name="cmbGithubOrgs" Margin="6,0,0,0" TabIndex="2" VerticalAlignment="Top" HorizontalAlignment="Left" SelectionChanged="cmbGithubOrgs_SelectionChanged" />
112+
</StackPanel>
113+
106114

107-
<Grid Margin="0,0,0,0" HorizontalAlignment="Left">
115+
<Grid Margin="0,8,0,0" HorizontalAlignment="Left">
108116
<Grid.ColumnDefinitions>
109117
<ColumnDefinition Width="120"/>
110118
<ColumnDefinition Width="*"/>
@@ -129,13 +137,16 @@
129137
<CheckBox x:Name="chkEnableLfs" Content="Enable Git LFS " ToolTip="Enable Large File Storage and add .gitattributes" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,8,0,0" VerticalAlignment="Center" Checked="chkEnableLfs_Checked" Unchecked="chkEnableLfs_Checked" IsChecked="True"/>
130138
<CheckBox x:Name="chkInitialCommit" Content="Create initial commit" Foreground="{DynamicResource ThemeButtonForeground}" Margin="16,5,0,0" VerticalAlignment="Center" Checked="chkInitialCommit_Checked" Unchecked="chkInitialCommit_Checked" IsChecked="True"/>
131139
<CheckBox x:Name="chkAddUnityGitIgnore" Content="Add Unity .gitignore" Foreground="{DynamicResource ThemeButtonForeground}" Margin="16,5,0,0" VerticalAlignment="Center" IsChecked="True" Checked="chkAddUnityGitIgnore_Checked"/>
140+
<CheckBox x:Name="chkAddReadme" Content="Add README" ToolTip="Add a README file" Foreground="{DynamicResource ThemeButtonForeground}" Margin="16,8,0,0" VerticalAlignment="Center" IsChecked="True" Checked="chkAddReadme_Checked" Unchecked="chkAddReadme_Checked"/>
132141
</StackPanel>
133142
<StackPanel Orientation="Horizontal" Margin="0,0,0,0">
134-
<CheckBox x:Name="chkAddReadme" Content="Add README" ToolTip="Add a README file" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,8,0,0" VerticalAlignment="Center" IsChecked="True" Checked="chkAddReadme_Checked" Unchecked="chkAddReadme_Checked"/>
135143
</StackPanel>
144+
136145
</StackPanel>
137146

138147
</Grid>
148+
149+
139150
</Expander>
140151

141152
<Grid UseLayoutRounding="False" Margin="0,2,0,0">

UnityLauncherPro/NewProject.xaml.cs

Lines changed: 100 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ public partial class NewProject : Window
3636

3737
const string githubTokenCreationURL = "https://github.com/settings/tokens/new?description=UnityLauncherPro+Setup+Project+Access&default_expires_at=90&scopes=repo#Remember_to_Copy_Token!";
3838

39+
void UpdateOrgUiState()
40+
{
41+
bool orgsEnabled = chkEnableOrgs.IsChecked == true;
42+
btnCreateNewProjectAndRepo.Content = orgsEnabled ? "New Project+Repo at Org" : "New Project+Repo";
43+
}
44+
3945

4046
public NewProject(string unityVersion, string suggestedName, string targetFolder, bool nameIsLocked = false, bool fetchOnlineTemplates = false)
4147
{
@@ -140,6 +146,8 @@ private async void LoadSettings()
140146
chkInitialCommit.IsEnabled = chkEnableVersionControl.IsChecked == true;
141147
chkAddReadme.IsChecked = Settings.Default.gitAddReadme;
142148
chkAddUnityGitIgnore.IsChecked = Settings.Default.gitAddIgnore;
149+
chkEnableOrgs.IsChecked = Settings.Default.gitEnableOrgs;
150+
UpdateOrgUiState();
143151

144152
expVersionControl.IsExpanded = Settings.Default.gitPanelExpanded || chkEnableVersionControl.IsChecked == true;
145153

@@ -159,6 +167,7 @@ private async void LoadSettings()
159167
if (result.IsValid)
160168
{
161169
ShowGitAuthorizedUI(true);
170+
await LoadGithubOrgsAsync(token);
162171
}
163172
else
164173
{
@@ -294,8 +303,11 @@ private async Task CreateNewProject(bool withRepo)
294303
Settings.Default.Save();
295304
}
296305

306+
var repoOwner = GitHubTokenStore.LoadUsername();
307+
297308
if (withRepo && chkEnableVersionControl.IsChecked == true)
298309
{
310+
299311
// setup local git
300312
try
301313
{
@@ -319,19 +331,28 @@ private async Task CreateNewProject(bool withRepo)
319331
txtRepoName.Text += "_" + DateTime.Now.ToString("ddMMyyyy_HHmmss");
320332
}
321333

322-
GitHubCreateRepoResult result = await GithubActions.CreateRepositoryAsync(token: token, repoName: txtRepoName.Text, description: txtRepoDescription.Text, isPrivate: rbPrivate.IsChecked == true, autoInit: false);
334+
string selectedOrg = null;
335+
if (chkEnableOrgs.IsChecked == true)
336+
{
337+
selectedOrg = cmbGithubOrgs.SelectedItem as string;
338+
if (string.IsNullOrWhiteSpace(selectedOrg)) selectedOrg = null;
339+
}
340+
341+
repoOwner = selectedOrg ?? repoOwner;
342+
343+
GitHubCreateRepoResult result = await GithubActions.CreateRepositoryAsync(token: token, repoName: txtRepoName.Text, description: txtRepoDescription.Text, isPrivate: rbPrivate.IsChecked == true, autoInit: false, organization: selectedOrg);
323344

324345
if (result.Success)
325346
{
326347
Console.WriteLine("Created repo successfully.");
327348

328-
string username = GitHubTokenStore.LoadUsername();
329-
string remoteUrl = $"https://github.com/{username}/{txtRepoName.Text}.git";
349+
string remoteUrl = $"https://github.com/{repoOwner}/{txtRepoName.Text}.git";
330350
await GithubActions.RunGitAsync(Path.Combine(txtNewProjectFolder.Text, txtNewProjectName.Text), $"remote add origin {remoteUrl}");
331351
}
332352
else
333353
{
334354
Console.WriteLine("Failed to create repo..");
355+
txtNewProjectStatus.Text = "GitHub repo creation failed: " + (string.IsNullOrWhiteSpace(result.Error) ? "Unknown GitHub error." : result.Error);
335356
}
336357
}
337358
catch (Exception ex)
@@ -428,8 +449,7 @@ private async Task CreateNewProject(bool withRepo)
428449
}
429450
}
430451

431-
string username = GitHubTokenStore.LoadUsername();
432-
Tools.OpenURL("https://github.com/" + username + "/" + txtRepoName.Text);
452+
Tools.OpenURL("https://github.com/" + repoOwner + "/" + txtRepoName.Text);
433453

434454
} // if version control enabled
435455

@@ -748,6 +768,40 @@ private void btnCreateMissingFolder_Click(object sender, RoutedEventArgs e)
748768

749769
private static readonly HttpClient _httpClient = new HttpClient();
750770

771+
private async Task LoadGithubOrgsAsync(string token)
772+
{
773+
cmbGithubOrgs.IsEnabled = chkEnableOrgs.IsChecked == true;
774+
if (chkEnableOrgs.IsChecked != true) return;
775+
776+
var orgs = await GithubActions.GetUserOrganizationsAsync(token);
777+
cmbGithubOrgs.ItemsSource = orgs;
778+
779+
var lastOrg = Settings.Default.gitLastOrg;
780+
if (!string.IsNullOrWhiteSpace(lastOrg) && orgs.Contains(lastOrg))
781+
{
782+
cmbGithubOrgs.SelectedItem = lastOrg;
783+
}
784+
else if (orgs.Count > 0)
785+
{
786+
cmbGithubOrgs.SelectedIndex = 0;
787+
}
788+
else
789+
{
790+
cmbGithubOrgs.SelectedIndex = -1;
791+
}
792+
}
793+
794+
private void cmbGithubOrgs_SelectionChanged(object sender, SelectionChangedEventArgs e)
795+
{
796+
if (isInitializing) return;
797+
798+
var selectedOrg = cmbGithubOrgs.SelectedItem as string;
799+
if (string.IsNullOrWhiteSpace(selectedOrg)) return;
800+
801+
Settings.Default.gitLastOrg = selectedOrg;
802+
Settings.Default.Save();
803+
}
804+
751805
private async Task LoadOnlineTemplatesAsync(string baseVersion, CancellationToken cancellationToken = default)
752806
{
753807
try
@@ -1061,7 +1115,12 @@ private void btnFetchTemplates_Click(object sender, RoutedEventArgs e)
10611115

10621116
private void btnCreateToken_Click(object sender, RoutedEventArgs e)
10631117
{
1064-
Tools.OpenURL(githubTokenCreationURL);
1118+
string tokenUrl = githubTokenCreationURL;
1119+
if (chkEnableOrgs.IsChecked == true)
1120+
{
1121+
tokenUrl = tokenUrl.Replace("scopes=repo", "scopes=repo,read:org");
1122+
}
1123+
Tools.OpenURL(tokenUrl);
10651124
}
10661125

10671126

@@ -1132,6 +1191,7 @@ private async void btnAuthorizeToken_Click(object sender, RoutedEventArgs e)
11321191
txtNewProjectStatus.Text = "Token valid. Logged in as " + result.Login + ".";
11331192
lblGithubUsername.Content = result.Login;
11341193
ShowGitAuthorizedUI(true);
1194+
await LoadGithubOrgsAsync(token);
11351195
}
11361196
else
11371197
{
@@ -1179,6 +1239,37 @@ private void chkAddUnityGitIgnore_Checked(object sender, RoutedEventArgs e)
11791239
Settings.Default.Save();
11801240
}
11811241

1242+
private async void chkEnableOrgs_Checked(object sender, RoutedEventArgs e)
1243+
{
1244+
if (isInitializing) return;
1245+
1246+
Settings.Default.gitEnableOrgs = chkEnableOrgs.IsChecked == true;
1247+
Settings.Default.Save();
1248+
UpdateOrgUiState();
1249+
1250+
var enabled = chkEnableOrgs.IsChecked == true;
1251+
cmbGithubOrgs.IsEnabled = enabled;
1252+
1253+
if (!enabled)
1254+
{
1255+
var selectedOrg = cmbGithubOrgs.SelectedItem as string;
1256+
if (!string.IsNullOrWhiteSpace(selectedOrg))
1257+
{
1258+
Settings.Default.gitLastOrg = selectedOrg;
1259+
Settings.Default.Save();
1260+
}
1261+
1262+
cmbGithubOrgs.ItemsSource = null;
1263+
cmbGithubOrgs.SelectedIndex = -1;
1264+
return;
1265+
}
1266+
1267+
var token = GitHubTokenStore.LoadToken();
1268+
if (string.IsNullOrWhiteSpace(token)) return;
1269+
1270+
await LoadGithubOrgsAsync(token);
1271+
}
1272+
11821273
private CancellationTokenSource _repoNameCts;
11831274

11841275
private async void txtRepoName_TextChanged(object sender, TextChangedEventArgs e)
@@ -1224,6 +1315,8 @@ private void expVersionControl_Collapsed(object sender, RoutedEventArgs e)
12241315
private void btnDisconnectToken_Click(object sender, RoutedEventArgs e)
12251316
{
12261317
GitHubTokenStore.DeleteToken();
1318+
cmbGithubOrgs.ItemsSource = null;
1319+
cmbGithubOrgs.SelectedIndex = -1;
12271320
Settings.Default.Save();
12281321
ShowGitAuthorizedUI(false);
12291322
}
@@ -1249,5 +1342,6 @@ private void txtTokenInput_PasswordChanged(object sender, RoutedEventArgs e)
12491342
btnAuthorizeToken.IsEnabled = tokenSeemsOK;
12501343
}
12511344

1345+
12521346
} // class NewProject
12531347
} // namespace UnityLauncherPro

UnityLauncherPro/Properties/Settings.Designer.cs

Lines changed: 24 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

UnityLauncherPro/Properties/Settings.settings

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,11 @@
187187
<Setting Name="gitAddIgnore" Type="System.Boolean" Scope="User">
188188
<Value Profile="(Default)">True</Value>
189189
</Setting>
190+
<Setting Name="gitEnableOrgs" Type="System.Boolean" Scope="User">
191+
<Value Profile="(Default)">False</Value>
192+
</Setting>
193+
<Setting Name="gitLastOrg" Type="System.String" Scope="User">
194+
<Value Profile="(Default)" />
195+
</Setting>
190196
</Settings>
191197
</SettingsFile>

0 commit comments

Comments
 (0)