-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make Avalonia UI the default on all platforms and move in-process #1207
Conversation
eb36ba7
to
cc6d0c3
Compare
c436534
to
4230893
Compare
Rename the WPF implementation of DialogWindow to prevent name collisions that would otherwise occur in upcoming commits where we merge the Avalonia implementation of DialogWindow into the Core project.
Explicitly target the WPF Dispatcher type using the fully qualified type name to prevent an ambiguous reference that would otherwise occur in upcomming commits where the Avalonia Dispatcher, and the GCM Dispatcher are merged in to the Core project.
Add the .Windows suffix to the WPF project namespaces to prevent name collisions that would otherwise occur in future commits where the Avalonia UI types are to be merged in to the core projects. Also clean up some unused XML namespace imports.
Set up a message pump on the main thread in the primary GCM executable in preparation to adding in-process, AvaloniaUI-based UI. Also move to explicitly shutdown the dispatcher correctly rather than just killing the process.
Default to using the in-proc Avalonia GUI on all platforms, with a fallback flag that can be set on Windows to continue to use the legacy WPF, out-of-proc UI helpers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is so exciting! Great work, Matthew! 🚀
@@ -81,15 +80,6 @@ $DOTNET_ROOT/dotnet publish "$GCM_SRC" \ | |||
-p:PublishSingleFile=true \ | |||
--output="$(make_absolute "$PAYLOAD")" || exit 1 | |||
|
|||
echo "Publishing core UI helper..." |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay! So happy these are going away.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Builds are much faster and will be easier to maintain or modify going forwards (at least once we can drop the WPF helpers entirely).
// Default to the 'first' choice in the menu | ||
TerminalMenuItem choice = menu.Show(0); | ||
|
||
if (choice == browserItem) goto case OAuthAuthenticationModes.Browser; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know this was already here, but just wanted to call out that I think it's elegant - one of the cases where a goto
does make sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, the C# goto case
keywords used together is a neat way to achieve this sort of control flow, without allowing for spaghetti jumps (can only jump to another case
).
var promptCts = new CancellationTokenSource(); | ||
var tokenCts = new CancellationTokenSource(); | ||
|
||
// Show the dialog with the device code but don't await its closure | ||
Task promptTask = InvokeHelperAsync(command, promptArgs.ToString(), null, promptCts.Token); | ||
Task promptTask = ShowDeviceCodeViaHelperAsync(dcr, command, args, promptCts.Token); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice - the refactors you've done here make the sequence of events a lot clearer imo.
@@ -102,6 +102,7 @@ public static class EnvironmentVariables | |||
public const string OAuthDeviceEndpoint = "GCM_OAUTH_DEVICE_ENDPOINT"; | |||
public const string OAuthClientAuthHeader = "GCM_OAUTH_USE_CLIENT_AUTH_HEADER"; | |||
public const string OAuthDefaultUserName = "GCM_OAUTH_DEFAULT_USERNAME"; | |||
public const string GcmDevUseLegacyUiHelpers = "GCM_DEV_USELEGACYUIHELPERS"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the use of DEV
in the name here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't something we want to necessarily advertise or be something that users can rely on being there for the long term. It's a 'hidden' setting – we don't document it.
@@ -21,12 +21,13 @@ | |||
<PackageReference Include="Microsoft.Identity.Client" Version="4.52.0" /> | |||
<PackageReference Include="Microsoft.Identity.Client.Extensions.Msal" Version="2.28.0" /> | |||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.21216.1" /> | |||
<PackageReference Include="Avalonia" Version="0.10.18" /> | |||
<PackageReference Include="Avalonia.Desktop" Version="0.10.18" /> | |||
<PackageReference Include="Avalonia" Version="11.0.0-preview6" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! Big jump.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah they moved from 0.10
to dropping the 0.
prefix with v11
public bool ShowCustomWindowBorder | ||
{ | ||
// Draw the window border explicitly on Windows | ||
get => ShowCustomChrome && PlatformUtils.IsWindows(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite understanding the Custom Chrome concept. Any docs I can take a look at to learn more?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "client area" is the part of a window that we can draw into. Typically this is the box 'inside' of the window titlebar, border and controls that the OS provides.
When we "extend" the client area, we can now draw over the entire window surface, including the title bar and window controls. Note that on macOS the traffic-light controls are still always drawn, but on Windows we are left without any "X" control.
The last column shows the custom window chrome and controls we draw. Here we actually explicitly disable the traffic-light controls on macOS.
The preference on macOS is the middle option, and on Windows it's the right-most one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simplify where we check for user interaction and/or terminal prompts during authentication, to only need to check at the start of each `GetAuthenticationAsync` call. Note that is is a behaviour change - we now require user interaction to be allowed to perform authentication even with only 1 mode available. This is technically more 'correct' because when the user disables interactivity, we should never require interaction to continue. Although we are not asking the user to select a mode of authentication, we still either need them to enter a username/password, or respond to a browser OAuth flow.
Support dark theme resource dictionaries.
To keep the size of the Windows distribution small, directly target the Win32 Avalonia packages rather than the Desktop package that includes macOS and Linux dependencies.
**Changes:** - Support ports in URL-scoped config (#825) - Support URL-scoped enterprise default settings (#1149) - Add support for client TLS certificates (#1152) - Add TRACE2 support(#1131, #1151, #1156, #1162) - Better browser detection inside of WSL (#1148) - Handle expired OAuth refresh token for generic auth (#1196) - Target *-latest runner images in CI workflow (#1178) - Various bug fixes: - Ensure we create a WindowsProcessManager on Windows (#1146) - Ensure we start child processes created with ProcessManager (#1177) - Fix app path name of Windows dropping file extension (#1181) - Ensure we init IEnvironment before SessionManager (#1167) - git: consistently read from stdout before exit wait (#1136) - trace2: guard against null pipe client in dispose (#1135) - Make Avalonia UI the default Windows and move to in-process (#1207) - Add Git configuration options for trace & debug (#1228) - Transition from Nerdbank.GitVersioning to a version file (#1231) - Add support for using the current Windows user for WAM on DevBox (#1197) - Various documentation updates: - org-rename: update references to GitCredentialManager (#1141) - issue templates: remove core suffix (#1180) - readme: add link to project roadmap (#1204) - docs: add bitbucket app password requirements (#1213) - .net tool: clarify install instructions (#1126) - docs: call out different GCM install paths in WSL docs (#1168) - docs: add trace2 to config/env documentation (#1230)
Today GCM has an architecture that delegates all GUI prompting to (bundled) external executables, using WPF-based helpers on Windows and Avalonia UI helpers on Mac and Linux. Each host provider has its own GUI helper executable, meaning we currently bundle four (GitHub.UI, GitLab.UI, Atlassian.Bitbucket.UI, git-credential-manager.ui [for generic auth]) helpers in addition to the core git-credential-manager CLI executable.
On Linux we use the "publish as a single-file" model for each of these executables, meaning we have a copy of the CLR included for each executable! That means five CLRs!
Now that the Avalonia UI-based GUI helpers have matured, we can look at dropping the WPF helpers and reduce the effort required to ship new GUI by 50% (we no longer need to do everything twice: once with WPF and once with Avalonia UI).
In addition, in a recent spike, I found that it may be possible to bring the Avalonia UI helpers in-process, with nearly zero net-new code. This would help reduce our installation footprint on Linux down to approximately 1/4 of the current size. The single executable model would also help decrease build times, and increase end-user performance since we no longer pay the process startup costs using external processes!
We need to be mindful that this change may result in a small increase in the Windows installation as we now bring in extra libraries used to support Avalonia UI, whereas previously we relied on WPF libraries as included with Windows 'for free'. This is expected to be minimal.
As for the current GUI extensibility model, we are not aware of any consumers, but it would not be much effort to keep the existing external process model, but now just default to using our in-process GUI options in the absence of external configuration. This still gives tools like VS or VSCode options to integrate native UI for GCM in the future.
Initially, we are still keeping the WPF helpers around on Windows but disabled by default.
We can drop these after at least one release using the in-proc Avalonia helpers.
To switch back to the WPF helpers, use
credential.devUseLegacyUIHelpers
orGCM_DEV_USELEGACYUIHELPERS
.