dotnet / sdk

Core functionality needed to create .NET Core projects, that is shared between Visual Studio and CLI
https://dot.net/core
MIT License
2.66k stars 1.06k forks source link

Shell completions let users bypass the first run message #8433

Open omajid opened 7 years ago

omajid commented 7 years ago

Steps to reproduce

Using bash, load the shell completions and then try and use the before invoking a dotnet command explicitly.

$ . cli/scripts/register-completions.bash
$ rm -rf ~/.dotnet/ ~/.nuget/ ~/.local/share/NuGet
$ ls
$ dotnet new c<TAB>
classlib  console   
$ dotnet new console 
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on /home/omajid/hello-world/hello-world.csproj...
Restore succeeded.

Notice that I didn't get a welcome message. If I run the exact same command without using the completion, I do see the telemetry message:

$ rm -rf ~/.dotnet/ ~/.nuget/ ~/.local/share/NuGet
$ rm -rf *
$ dotnet new console 

Welcome to .NET Core!
---------------------
Learn more about .NET Core @ https://aka.ms/dotnet-docs. Use dotnet --help to see available commands or go to https://aka.ms/dotnet-cli-docs.

Telemetry
--------------
The .NET Core tools collect usage data in order to improve your experience. The data is anonymous and does not include command-line arguments. The data is collected by Microsoft and shared with the community.
You can opt out of telemetry by setting a DOTNET_CLI_TELEMETRY_OPTOUT environment variable to 1 using your favorite shell.
You can read more about .NET Core tools telemetry @ https://aka.ms/dotnet-cli-telemetry.
The template "Console Application" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on /home/omajid/hello-world/hello-world.csproj...
Restore succeeded.

Environment data

This is a custom build of the CLI.

dotnet --info output:

.NET Command Line Tools (2.0.0-preview2-006372)

Product Information:
 Version:            2.0.0-preview2-006372
 Commit SHA-1 hash:  a3d6c2a551

Runtime Environment:
 OS Name:     rhel
 OS Version:  7
 OS Platform: Linux
 RID:         rhel.7-x64
 Base Path:   /opt/rh/rh-dotnet20/root/usr/lib64/dotnet/sdk/2.0.0-preview2-006372/

Microsoft .NET Core Shared Framework Host

  Version  : 2.0.0-preview2-25407-01
  Build    : N/A
omajid commented 7 years ago

cc @wli3

ellismg commented 7 years ago

I'm guessing that we should just set DOTNET_SKIP_FIRST_TIME_EXPERIENCE in the local function that we register with complete?

omajid commented 7 years ago

Does dotnet complete send telemetry events? My impression was that we have show the users the first-run message if it does.

ellismg commented 7 years ago

That's a good point, @omajid. We could set DOTNET_CLI_TELEMETRY_OPTOUT as well?

omajid commented 7 years ago

Sure. Any other switches we should enable?

Also, I don't know why I am not seeing the first run message on complete. Can anyone else confirm/reproduce? FWIW, I copied the completion file to /usr/share/bash-completion/comlletions/dotnet and rely on bash automatically picking up and using the completions.

jonsequitur commented 7 years ago

@omajid Is complete working for you when the first run message has not yet been displayed for that CLI installation?

omajid commented 7 years ago

@jonsequitur I am using rm -rf ~/.dotnet/ ~/.nuget/ ~/.local/share/NuGet to simulate "fresh install on a fresh machine". Is there something else I should do to simulate such an environment?

livarcocc commented 7 years ago

That's good enough for preview2. For preview3, delete the sentinel file under dotnet/SDK/NUGetFallbackFolder, where dotnet is where you install the SDK.

I am confused here. Is the problem here that auto-complete is sending telemetry or that a command that was constructed with dotnet complete, when invoked, does not show the telemetry message?

jonsequitur commented 7 years ago

If the first run experience has not yet been triggered, then dotnet complete will trigger it (last I checked). If you run dotnet complete directly you can see the first run text followed by the completion results. But if you triggered tab completion, so that the shell completion script is the caller into dotnet complete, then what I've observed is that the shell doesn't know how to parse the result containing the first run output, and you won't see the custom completion results. You may see it fall back to file system completion or see blank output. I think this varies by shell.

mcornella commented 4 years ago

I know this is old, but I've come up with a solution that solves all use cases with just one little downside.

As you said, there's the option of calling the dotnet complete command with DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1, but that has the downside of not showing the telemetry message but collecting completion data. It also didn't have an effect when $HOME/.dotnet is empty.

The next thing was to set DOTNET_CLI_TELEMETRY_OPTOUT=1 to avoid capturing telemetry data from completion commands, which I can see setting for all completion triggers without a way to unset it might not be a good idea.

So we could set it for as long as the first time experience message hasn't been shown, but I can't figure out a way to do it programatically.

So the option I've come up with is to parse the dotnet complete message and separate the first time experience message from the completion entries using sed, though it has the downside of relying on the separator line never changing (86-hyphens line). The code could use a lower magic number, the only requirement is that the last separator line has more hyphens than the rest of the separators. The code also uses two sed calls but these are relatively inexpensive.

Here's the solution in zsh, but it can be easily ported to bash:

_dotnet() {
  local dotnet_msg="$(dotnet complete "$words")"
  local separator=$(printf '-%.0s' {1..86})

  # Print the message until completion entries begin, where an 86-character
  # hyphen separator is printed.
  if [[ "$dotnet_msg" = *$separator* ]]; then
    sed "/$separator/q" <<< "$dotnet_msg"
  fi

  # Afterwards offer the completion entries, which come after the separator.
  local completions=("$(sed -e "1,/$separator/d" <<< "$dotnet_msg")")
  if [[ -n "$completions" ]]; then
    compadd -- "${(@f)completions}"
  fi
}

compdef _dotnet dotnet