Repository navigation
Fixing get-help <cmdletName> -online for .NET Core on Desktop. - #2174
Dongbo Wang (daxian-dbw) merged 6 commits into
Conversation
|
Hi Francisco Gamino (@Francisco-Gamino), I'm your friendly neighborhood Microsoft Pull Request Bot (You can call me MSBOT). Thanks for your contribution!
TTYL, MSBOT; |
|
I think we can avoid copy-pasting. |
|
Francisco Gamino (@Francisco-Gamino) your email that you used in a commit for this PR is not linked to your account |
There was a problem hiding this comment.
We don't throw or add ErrorRecord in FullCLR case, why here?
There was a problem hiding this comment.
sergei (@vors) in Full CLR, if there is an error when trying to start a process, an exception will be thrown. However, in CoreCLR, no exception will be thrown (since we are wrapping the call in cmd), so we have to check the last exit code, and extract the error from the call.
There was a problem hiding this comment.
I guess it's reasonable for the help/browser case.
There was a problem hiding this comment.
Meanwhile, the semantic would be different, but I think it's an acceptable deviation.
|
Can we add a negative tests (i.e. trying to open misformatted help URL) to verify that we behave consistently across platforms? |
|
What is going to happen when you run this on macOS or Linux? |
|
Jeffrey Snover (@jpsnover) it's handled by |
|
Thanks! |
|
Travis Plunk (@TravisEz13) looks like Review label automation doesn't handle this correctly: The second label change to |
There was a problem hiding this comment.
What about error in this case? invokeProcess.Start(); (FullCLR) doesn't add anything to the ps errors.
There was a problem hiding this comment.
I assume this exists on Linux
There was a problem hiding this comment.
Actually, the Registry is not available on Linux, so I am updating this part of the code.
There was a problem hiding this comment.
in the case that this does not throw, you should add the following:
throw "Execution should not have succeeded"
``` #ClosedThere was a problem hiding this comment.
since you're now sure that the try will throw, this can become:
catch
{
$_.FullyQualifiedErrorId | Should be "InvalidOperation,Microsoft.PowerShell.Commands.GetHelpCommand"
}#Closed
There was a problem hiding this comment.
James Truher (@JamesWTruher) thanks for your comments. I like this approach better. I will update the test code as you suggested.
. #Closed
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
foreach [](start = 17, length = 7)
please use foreach-object and avoid aliases #Closed
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
$actualURI = $actualURI.Replace("Help URI:","").Trim() [](start = 20, length = 54)
if you think it's needed, the testhook change can simply return the uri and you can eliminate this bit of stuff #Closed
There was a problem hiding this comment.
Thanks James Truher (@JamesWTruher). I like the current implementation of the testhook, because it tells you that the fwlink is pointing to a Help URI. #Closed
There was a problem hiding this comment.
This error should have more information to enable trouble-shooting
There was a problem hiding this comment.
The default exception will be wrapped, and the user will get the following error:
"Starting a browser to display online Help failed. No program or browser is associated to open the URI {0}."
There was a problem hiding this comment.
these browserProcess.Start(); line is duplicated
There was a problem hiding this comment.
There were three different calls: Unix, coreclr, and fullclr. This has been fixed in the latest iteration.
There was a problem hiding this comment.
You can replace this by using (RegistryKey regKey = ...) { ... } syntax.
It's more modern way of doing the same.
There was a problem hiding this comment.
I agree. Fixed.
There was a problem hiding this comment.
4 is a magic number here.
It's better to re-write it as
const string exeExtention = ".exe";
browserPath = browserPath.Substring(0, browserPath.LastIndexOf(exeExtension) + exeExtension.Length);
Same goes about the "34".
It' good that there is a comment above, but if it's a comment, it should be very close to the number, i.e.
.Repalce(/* " */ (char)34 + ...)
There was a problem hiding this comment.
Good suggestions sergei (@vors) . Fixed.
There was a problem hiding this comment.
That all looks hacky. Isn't there a nicer API?
There was a problem hiding this comment.
why not Replace("\"", "")?
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
If you are able to get the default browser executable path, then maybe it's better to run that executable directly with the url, so that we can avoid this obscure cmd syntax.
If you decide to go with using the browser executable path directly, maybe you can do this for both CoreCLR win platform and FullCLR win platform. But it's up to you to decide if you want it this way :)
There was a problem hiding this comment.
Actually, this is a very good idea. We should keep parity between FullCLR and CoreCRL. So, I've updated the code to launch the default browser with the HelpURI as an argument.
There was a problem hiding this comment.
Can you please fix the indentation here?
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
there are a few problems with this method:
WebBrowserAvailableshould be renamed toIsWebBrowserAvailable. If you want to use the default browser executable path, then it should be renamed toGetDefaultWebBrowserregKeycould benull, on Nano or IoT. So your current code might raiseNullReferenceException- The registry path
HKCR\\HTTP\\shell\\open\\commandis not pointing to the default browser. It's actually always pointing to IE. The default web browser executable path can be found with 2 steps:
(1) HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice -- get the value forProgIdof this key, say the value isChromeHTML
(2) HKEY_CLASSES_ROOT\ChromeHTML\shell\open\command -- get the value of the default name. The value would be the path to chrome.exe
If we want to run the web browser executable directly, then we need to make sure we really get the default browser.
There was a problem hiding this comment.
Thank you for your comments Dongbo Wang (@daxian-dbw) . I've updated the code based on these guidance.
There was a problem hiding this comment.
why not Replace("\"", "")?
There was a problem hiding this comment.
The current implementation doesn't get the path to default browser.
There was a problem hiding this comment.
Yes, this is fixed now.
There was a problem hiding this comment.
Can you update the .csv to put 'https' there? It's probably more efficient.
There was a problem hiding this comment.
Will do, even though the actual values in the Microsoft fwlink database is http.
There was a problem hiding this comment.
-not ([System.Management.Automation.Platform]::IsIoT -or [System.Management.Automation.Platform]::IsNanoServer
This will skip NanoServer
There was a problem hiding this comment.
Actually, this will only skip on when the current system is Nano Server or IoT. The concept of -skip can be a bit confusing. For coreclr on desktop, the evaluation is as follows
$shouldSkip = -not ([System.Management.Automation.Platform]::IsIoT -or [System.Management.Automation.Platform]::IsNanoServer)
$shouldSkip = $true # this means it should be skip on desktop.
This is the output from running the tests on CoreCLR on Desktop.
Describing Get-Help -Online is not supported on Nano Server and IoT
[!] Get-help -online throws InvalidOperation 27ms
There was a problem hiding this comment.
is -ErrorAction stop necessary here? The code path should already throw and InvalidOperationException.
There was a problem hiding this comment.
And, when the test hook property is set to true, I don't think the code path will ever reach the point to throw exception.
There was a problem hiding this comment.
Good point. The code has been updated. Thanks.
There was a problem hiding this comment.
why are you skipping IoT, NanoServer and Linux? when BypassOnlineHelpRetrieval is set, the code won't reach the Process.Start part anyway ...
There was a problem hiding this comment.
Good point. I am enabling the test across all platforms.
There was a problem hiding this comment.
The tests don't test the process launch at all. Shall we at least have 1 test exercise the new code?
There was a problem hiding this comment.
Dongbo Wang (@daxian-dbw) I've added a tests, but it will leave the default browser opened. The issue here is that once the process is started, I no longer have access to its reference.
There was a problem hiding this comment.
we don't use ProcessStartInfo.UseShellExecute directly near this code. Not very clear from the comment what's the need of UseShellExecute.
There was a problem hiding this comment.
sergei (@vors) I have updated the comment. On Full CLR, ProcessStartInfo.UseShellExecute is true by default.
sergei (vors)
left a comment
There was a problem hiding this comment.
LGTM for me, I'm going to assign the PR to Dongbo Wang (@daxian-dbw) since he still has unanswered questions.
|
Also we should fix the travis build |
Dongbo Wang (daxian-dbw)
left a comment
There was a problem hiding this comment.
Please take a look and address new PR comments.
There was a problem hiding this comment.
How about:
object progId = Registry.GetValue(httpRegkey, "ProgId", null);
if (progId != null)
There was a problem hiding this comment.
Thank you for your suggestion. Fixed.
There was a problem hiding this comment.
there are 2 issues in line 687 and 788:
- Possible
NullReferenceExceptionatbrowserRegKey.GetValue(null).ToString()whenGetValue(null)returnsnull. - If line 687 runs fine, then
browserPathmust not be null, becauseReplace(...)never returns null.
This is a chance to use the new C# 6.0 feature null-conditional operator '?.' to make the code concise, like:
using (RegistryKey browserRegKey = Registry.ClassesRoot.OpenSubKey(progId + @"\shell\open\command", false))
{
const string exeExtension = ".exe";
string browserPath = browserRegKey?.GetValue(null)?.ToString().Replace(/* remove the quotes */ "\"", "");
if (!string.IsNullOrEmpty(browserPath))
{ ...
This could reduce the brackets needed for this method.
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
I know it would be super rare, but what if LastIndexOf(exeExtension) returns -1? Maybe change it to something like:
int extIndex = browserPath.LastIndexOf(exeExtension);
browserPath = extIndex > 0 ? browserPath.Substring(0, extIndex + exeExtension.Length) : string.Emtpy;
There was a problem hiding this comment.
Nitpick: for LastIndexOf, it's better to always use the overload that takes a culture parameter int LastIndexOf(string value, System.StringComparison comparisonType)
There was a problem hiding this comment.
Thanks for the suggestion. Fixed.
There was a problem hiding this comment.
Maybe we should have a message for this InvalidOperationException
There was a problem hiding this comment.
No need to have an error message. We can use the existing HelpErrors.CannotLaunchURI.
There was a problem hiding this comment.
This skipTest setting will make this test run on OSX.
There was a problem hiding this comment.
Why are we skipping linux?
And with $skipTest, the test will run on OSX. Are you intend to run this test on OSX platform?
|
Francisco Gamino (@Francisco-Gamino) we are currently waiting on you to address the review comments. Please let us know if you have any concerns on the comments. |
There was a problem hiding this comment.
are these Trim() really necessary here? If not, maybe we should avoid them.
There was a problem hiding this comment.
The one for test hook is needed, but the other one is not. Test code updated.
Dongbo Wang (daxian-dbw)
left a comment
There was a problem hiding this comment.
Please respond to the review comments.
There was a problem hiding this comment.
On CoreCLR, this property doesn't exist.
This comment is not accurate. This property does exist in CoreCLR, but it always has the value false.
There was a problem hiding this comment.
Why are we skipping Linux and OSX? If there is something blocking this from working in Linux and OSX, then we should open an issue and investigate.
There was a problem hiding this comment.
Dongbo Wang (@daxian-dbw) - this test case compares the the HelpURI from the csv files (this list came from the publishing team), and it gets compare to the cmdlet metadata. Get-help -online give precedence to helpURI in the help file, so if the fwlink has been update in the help file, it will not longer match the one in the csv file. So, for this test case to pass, we need to delete all the help content on the box, so that all HelpURI match the ones in the csv files. Unfortunately, update-help does not work on Linux (I am working on a fix), so once I delete the help content on Linux or OSX, I cannot longer install it again, and therefore, the subsequent test cases will fail. Once update-help is fixed on Linux, I will update these test to run everywhere.
There was a problem hiding this comment.
Is this Trim() really necessary?
There was a problem hiding this comment.
Updated test code, so we no longer need Trim().
There was a problem hiding this comment.
Same here, why are we skipping linux and OSx?
There was a problem hiding this comment.
Fixed, so now we are just skipping on IoT and Nano Server.
|
Francisco Gamino (@Francisco-Gamino) Thanks for the updates. We are now waiting on you to address review comments and fix the CI failure. |
Francisco Gamino (Francisco-Gamino)
left a comment
There was a problem hiding this comment.
Dongbo Wang (@daxian-dbw), I addressed all your comments. Could you please take another look? Thanks.
There was a problem hiding this comment.
Actually, the Registry is not available on Linux, so I am updating this part of the code.
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
I will fix the indentation in my next iteration.
There was a problem hiding this comment.
The default exception will be wrapped, and the user will get the following error:
"Starting a browser to display online Help failed. No program or browser is associated to open the URI {0}."
There was a problem hiding this comment.
No need to have an error message. We can use the existing HelpErrors.CannotLaunchURI.
There was a problem hiding this comment.
Fixed.
There was a problem hiding this comment.
Updated test code, so we no longer need Trim().
There was a problem hiding this comment.
Dongbo Wang (@daxian-dbw) - this test case compares the the HelpURI from the csv files (this list came from the publishing team), and it gets compare to the cmdlet metadata. Get-help -online give precedence to helpURI in the help file, so if the fwlink has been update in the help file, it will not longer match the one in the csv file. So, for this test case to pass, we need to delete all the help content on the box, so that all HelpURI match the ones in the csv files. Unfortunately, update-help does not work on Linux (I am working on a fix), so once I delete the help content on Linux or OSX, I cannot longer install it again, and therefore, the subsequent test cases will fail. Once update-help is fixed on Linux, I will update these test to run everywhere.
There was a problem hiding this comment.
Fixed, so now we are just skipping on IoT and Nano Server.
| @@ -0,0 +1,94 @@ | |||
| Describe 'Online help tests for PowerShell Core Cmdlets' -Tags "CI" { | |||
There was a problem hiding this comment.
Francisco Gamino (@Francisco-Gamino) is almost ready on the fix of #951. This test will be updated in the PR for that issue.
There was a problem hiding this comment.
Issue #2465 is opened to track the update to the test files here.
…Shell#2174) In PS Core on windows, we try to locate the default web browser and use it for online help. If we cannot find the default web browser (in NanoServer/IoT), we error out.


Resolves issue #2271 - Implement Get-Help -Online for .NET Core on Windows, Nano Server, and IoT