Skip to content
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

Release v2.2.0 #1306

Merged
merged 95 commits into from
Jun 26, 2023
Merged

Release v2.2.0 #1306

merged 95 commits into from
Jun 26, 2023

Conversation

mjcheetham
Copy link
Collaborator

Changes:

mjcheetham and others added 30 commits May 3, 2023 16:09
Since .NET 5 we can now use the `Environment.OSVersion` property to
lookup the real OS version for macOS and Windows[1].

For .NET Framework (still used for our releases on Windows) we must
continue to use the Win32 API `RtlGetVersion` to avoid any Windows
compatibility nonsense.

We continue to use `uname` on Linux.

[1] https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/5.0/environment-osversion-returns-correct-version
Try using the /etc/os-release file, for systemd distros, in favour of
calling out to `uname` which can add extra overhead in the form of
process startup. Direct file I/O and parsing should be faster.
removed unnecessary tabs
removed unnecessary tabs
We are spending about 45ms on macOS to read version information by
shelling out to `sw_vers` on each run of GCM.

Let's try and be smarter and use BCL APIs, system APIs or file-based
sources for the OS and distribution information before resorting to
external processes.

In my testing, on macOS, we went from 45ms to 4.5ms (10x speedup).

Note that we are now also getting something a little bit more useful for
Linux:

Before:
```
$ GCM_TRACE=1 ./git-credential-manager --version
20:55:36.776385 ...re/Application.cs:95 trace: [RunInternalAsync] Version: 2.1.1.0
20:55:36.787669 ...re/Application.cs:96 trace: [RunInternalAsync] Runtime: .NET 6.0.16
20:55:36.787711 ...re/Application.cs:97 trace: [RunInternalAsync] Platform: Linux (x86-64)
20:55:36.787749 ...re/Application.cs:98 trace: [RunInternalAsync] OSVersion: Linux ubuntu2204-vm1 5.15.0-1037-azure #44-Ubuntu SMP Thu Apr 20 13:19:31 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux
..
```

After:
```
$ GCM_TRACE=1 ./git-credential-manager --version
20:52:12.325628 ...re/Application.cs:95 trace: [RunInternalAsync] Version: 2.1.1.0
20:52:12.337234 ...re/Application.cs:96 trace: [RunInternalAsync] Runtime: .NET 6.0.16
20:52:12.337283 ...re/Application.cs:97 trace: [RunInternalAsync] Platform: Linux (x86-64)
20:52:12.337301 ...re/Application.cs:98 trace: [RunInternalAsync] OSVersion: Ubuntu 22.04.1 LTS
..
```
Bumps [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/lycheeverse/lychee-action/releases)
- [Commits](lycheeverse/lychee-action@97189f2...ec3ed11)

---
updated-dependencies:
- dependency-name: lycheeverse/lychee-action
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Migrate applicable secrets to a new 'release' workflow environment. This
is a security measure to help ensure secrets cannot be accessed by those
without proper permissions.
Migrate applicable secrets to the new 'release' workflow environment and
update appropriate workflows to use these secrets. This is a security
measure to help ensure secrets cannot be accessed by those without
proper permissions.

An example of a passing workflow with these changes can be found
[here](https://github.com/ldennington/git-credential-manager/actions/runs/5007711493).
Move deployment of .NET tool to nuget.org out of release workflow. With
these changes, the .NET tool is published only after a release has been
published (rather than with the creation of the release). This emulates
what we currently do with Homebrew and ensures we only release the .NET
tool when we are confident enough in a release's stability to publish it.
Move deployment of .NET tool to nuget.org out of release workflow. With
these changes, the .NET tool is published only after a release has been
published (rather than with the creation of the release). This emulates
what we currently do with Homebrew and ensures we only release the .NET
tool when we are confident enough in a release's stability to publish
it.
Use Environment.OSVersion.Version.ToString() over .VersionString to get
a numercal version only on Windows and macOS.

Using .VersionString on macOS you get values like "Unix 13.3.1" rather
than "13.3.1", and on Windows you get "Microsoft Windows NT 10.x.y.z"
rather than "10.x.y.z".
Use `Environment.OSVersion.Version.ToString()` over `.VersionString` to
get a numerical version only on Windows and macOS.

Using `.VersionString` on macOS you get values like "Unix 13.3.1" rather
than "13.3.1", and on Windows you get "Microsoft Windows NT 10.x.y.z"
rather than "10.x.y.z".
Update `System.CommandLine` to the latest version. There have been some
changes to the API since the last version around binding a command to a
handler, and the removal of the model binding.

We now explicitly specify the options when setting the handlers. This
means we should be able to avoid the use of reflection inside of the
package, making it trimable in the future!
Add ability to suppress the GUI from the command-line argument
`--no-ui`. We achieve this by setting the process enviroment variable
that already exists and is used to control GUI prompts.
Add an extension method for adding multiple options to a `Command`,
optionally setting a validator to enforce the arity of their usage.
This allows us to create mutually exclusive option sets.
Add the ability to pull all the accounts from a credential store.
Change GitHubHostProvider to implement IHostProvider directly rather
than derive from the abstract HostProvider class. Inline the previously
super-class implementations to preserve behaviour.
Refactor the `GenerateCredentialAsync` method to take a remote URI and
optional user name hint, rather than requiring an instance of
`InputArguments` from Git.
Add a set of basic provider commands to add, remove and list GitHub
accounts. Users can explicitly login to GitHub.com or any GHES instance,
or remove an account.
Add a few options to the `login` command to customise how auth happens,
without needing to respond to an interactive prompt.

 --username <username>
    Specify the user name to use; useful for login hints in the web

 --browser/--web
    Select the web browser OAuth flow

 --device
    Select the device code flow

 --pat/--token <token>
    Use the specified personal access token. The user name is resolved
    if not provided with the --username option.
If we find a credential already exists for the same user prior to
authenticating, require the user explicitly pass --force to make us
re-authenticate the same account.
Add a new prompt to allow the user to select between GitHub accounts, or
using a new account.
Check for multiple existing accounts, and if there is more than one
account with credentials in the store AND no user name hint provided in
the remote URL, then show the new account selection prompt.
Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3.0.3 to 3.1.0.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](actions/setup-dotnet@v3.0.3...v3.1.0)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
mjcheetham and others added 27 commits June 9, 2023 13:21
Add the new `wwwauth[]` credential property to the input arguments
surfaced to providers and commands.
Use an IPv4 loopback redirect URL instead of the `localhost` name. This
is in accordance with the recommendation in the OAuth spec[^1][^2] and
also GitHub's documentation[^3].

Note that this change depends on an update to the Git Credential Manager
OAuth application on GitHub to add the "http://127.0.0.1/" redirect
(with a trailing slash!). We will be strictly adding the new URL, and
keep the older localhost-based redirect URL untouched for older clients.

The change to the OAuth app registration can occur before this is
merged.

Fixes #594 

[^1]: https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
[^2]: https://datatracker.ietf.org/doc/html/rfc8252#section-8.3
[^3]:
https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps#loopback-redirect-urls
Use the new `wwwauth[]` header information from Git to determine the
Azure authority for Azure Repos, before needing to resort to a cached
value, or making a HEAD call.
Bumps [DavidAnson/markdownlint-cli2-action](https://github.com/DavidAnson/markdownlint-cli2-action) from 10.0.1 to 11.0.0.
- [Release notes](https://github.com/DavidAnson/markdownlint-cli2-action/releases)
- [Commits](DavidAnson/markdownlint-cli2-action@bb4bb94...8f35160)

---
updated-dependencies:
- dependency-name: DavidAnson/markdownlint-cli2-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Arch Linux was incorrectly added to the install-from-source script and
accompanying validation as a .NET-supported distribution, when this is not
actually the case. A list of supported distributions can be found here:

https://learn.microsoft.com/en-us/dotnet/core/install/linux

Instead of removing unsupported distros, we plan to print out a message
notifying users that, while they can be installed, they are not officially
validated or supported by the GCM project.
Arch Linux was incorrectly added to the install-from-source script and
accompanying validation as a .NET-supported distribution, when this is
not actually the case. A list of supported distributions can be found here:
  
https://learn.microsoft.com/en-us/dotnet/core/install/linux
  
Instead of removing unsupported distros, we plan to print out a message
notifying users that, while they can be installed, they are not
officially validated or supported by the GCM project.
Now that Git can forward on the `WWW-Authenticate` headers from the
remote, we no longer need to make a second, redundant, `HEAD` query to
the organisation URL to rediscover the header values.

If we have been provided such a header from Git, use it in the first
instance! Otherwise continue to make a `HEAD` query (and cache it).

Note this only affects the OAuth mode, just as the authority caching
only works here.
Previously it wasn't clear whether to install GCM for Linux or Windows when using WSL until later in the document.

Now the first step explicitly says GCM for Windows.
Based on [1], it appears that .NET is not yet supported on Debian Bookworm
(12). As a result, attempting to install .NET in a container running Bookworm led to failures [2]. This change locks the validation workflow into using
Bullseye to work around this issue.

1: https://learn.microsoft.com/en-us/dotnet/core/install/linux-debian#supported-distributions
2: https://github.com/ldennington/git-credential-manager/actions/runs/5326250435/jobs/9648032190
Add workflow dispatch trigger to more easily run install-from-source
validation workflow.
Previously it wasn't clear whether to install GCM for Linux or Windows
when using WSL until later in the document.

Now the first step explicitly says GCM for Windows.
Based on [this documentation](https://learn.microsoft.com/en-us/dotnet/core/install/linux-debian#supported-distributions), it appears that .NET is not yet supported on Debian Bookworm (12). As a result, attempting to install .NET in a container running Bookworm [led to failures](https://github.com/ldennington/git-credential-manager/actions/runs/5326250435/jobs/9648032190). This change locks the validation workflow into using Bullseye to work around this issue. It also adds a `workflow_dispatch` trigger to make running `validate-install-from-source` easier in the future.

A successful workflow run with these changes can be found [here](https://github.com/ldennington/git-credential-manager/actions/runs/5326464820).
…e GCM project since Mariner is non-.NET supported distro.
Add Microsoft Mariner to install from source script.
Add parsing of GitHub.com's WWW-Authenticate header, with the upcoming
enterprise_hint and domain_hint properties that can be used to indicate
when a resource (repository) requires a specific EMU account.
If we have been given a domain_hint in the WWW-Authenticate headers we
should use that value to filter any existing accounts we have stored.

The header format is:

WWW-Authenticate: Basic realm="GitHub" [enterprise_hint="X"] [domain_hint="Y"]

..where X is the enterprise slug/name, and Y is the enterprise 'shortcode'.

The shortcode is the suffix applied to GitHub.com accounts that are
EMUs (Enterprise Managed Users). That is to say they are backed by an
external IdP (Identity Provider).

If we have not been given any WWW-Authenticate header (such as with
older versions of Git), do not do any filtering. Likewise, if the remote
is not GitHub.com (the only place EMUs mingle with other account types)
then do no filtering.
Add support for GitHub [enterprise-manage users
(EMU)](https://docs.github.com/en/enterprise-cloud@latest/admin/identity-and-access-management/using-enterprise-managed-users-for-iam/about-enterprise-managed-users)
to the GitHub host provider.

Accounts in an 'EMU' enterprise/business are siloed from the regular,
public GitHub.com accounts. EMU accounts are identified by the
`_shortcode` suffix, where the `shortcode` is a moniker for the
enterprise/business, for example `alice_contoso`.

When asked to recall credentials for the GitHub.com host we now attempt
to filter stored accounts by the `shortcode`, given information provided
in `WWW-Authenticate` headers from upcoming versions of Git that support
these headers (as of
gitgitgadget/git@92c56da).

The format of the header is:

```
WWW-Authenticate: Basic realm="GitHub" [domain_hint="X"] [enterprise_hint="Y"]
```

..where `X` is the shortcode, and `Y` is the enterprise name.

If multiple accounts are available for the given 'domain' then we
present an account selection prompt. Users can avoid this prompt in the
case of multiple user accounts by specifying the desired account in the
remote URL (e.g. `https://alice@github.com/mona/test` to always use the
`alice` account).

Note that GitHub.com does not yet return such `WWW-Authenticate`
headers, except always `Basic realm="GitHub"`, so this may be subject to
fixes later. In the case of `realm="GitHub"`, i.e., public accounts,
there is no change.

### Testing

To test the new behaviour before GitHub.com returns such headers, it's
possible to fake the server response using
[`mitmproxy`](https://mitmproxy.org) and the following script:

```python
"""Add an HTTP header to each response."""

class AddHeader:
    # initialize a dict with shortcodes and paths
    def __init__(self):
        org1 = ("domain1", "enterprise1")
        org2 = ("domain2", "enterprise2")

        self.orgMap = {
            "org1" : enterprise1,
            "org2" : enterprise1,
            "org3" : enterprise2,
        }

    def response(self, flow):
        if flow.response.status_code == 401:
            # lookup the correct shortcode based on the org
            org = flow.request.path.split("/")[1]
            if org not in self.orgMap:
                return
            domain_hint = self.orgMap[org][0]
            enterprise_hint = self.orgMap[org][1]
            # build the header
            header = "Basic realm=\"GitHub\" enterprise_hint=\"" + enterprise_hint + "\" domain_hint=\"" + domain_hint + "\""
            # set the header
            flow.response.headers["WWW-Authenticate"] = header

addons = [
    AddHeader()
]
```

Replace `orgN` with the org names that are backed by an EMU Enterprise,
and fill `domainN` for the shortcode, and `enterpriseN` for the
enterprise slug/name.

Configure Git to use the proxy and run `mitmproxy` with the `--scripts`
argument:

```shell
git config --global http.proxy 'http://127.0.0.1:8080'
mitmproxy --scripts <SCRIPT>
```

Now all Git interactions that touch `orgN` will include the
`domain_hint` and `enterprise_hint`s as defined.

I use these two helpful aliases to quickly add and remove the local
proxy from Git's config:

```shell
[alias]
	mitm = "!f(){ git config --global http.proxy 'http://127.0.0.1:8080'; }; f"
	unmitm = "!f(){ git config --global --unset http.proxy; }; f"
```
@mjcheetham mjcheetham requested a review from ldennington June 26, 2023 19:43
Copy link
Contributor

@ldennington ldennington left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉

@mjcheetham mjcheetham merged commit de28bd9 into release Jun 26, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants