Skip to content

Conversation

@endorama
Copy link
Contributor

@endorama endorama commented Aug 9, 2022

elastic-package stack shellinit output shell code needed to setup the testing environment and connect to the stack instance started via elastic-package stack up.

The current implementation emits POSIX shell code, compatible with sh, zsh and bash. The shellcode breaks on other shells, like fish - that I'm using. There are workarounds (like the awesome babelfish) but I think a built in solution would be preferred.

This PR aims to address this, adding a simple template mechanism that allows to support any shell through a custom shell code template, adds supports for fish shell and a --shell flag on the shellinit command to select the desired shell output.

This change is not breaking, as by default it outputs POSIX compatible shell code.

@elasticmachine
Copy link
Collaborator

elasticmachine commented Aug 9, 2022

💚 Build Succeeded

the below badges are clickable and redirect to their specific view in the CI or DOCS
Pipeline View Test View Changes Artifacts preview preview

Expand to view the summary

Build stats

  • Start Time: 2022-10-18T10:06:39.308+0000

  • Duration: 33 min 53 sec

Test stats 🧪

Test Results
Failed 0
Passed 850
Skipped 0
Total 850

🤖 GitHub comments

Expand to view the GitHub comments

To re-run your PR in the CI, just comment with:

  • /test : Re-trigger the build.

@jsoriano jsoriano requested a review from a team August 9, 2022 16:04
Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

Nice! Maybe this is also a way to support other shells like powershell in the future 🙂

Added some comments, the most important ones would be to decide if we want separated types for the different posix shells, and to define the new flag and their description in cobraext as is done in other modules. The rest of comments are suggestions about code organization.

@jsoriano jsoriano added enhancement New feature or request Team:Ecosystem Label for the Packages Ecosystem team labels Aug 9, 2022
Copy link
Member

@andrewkroh andrewkroh left a comment

Choose a reason for hiding this comment

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

Thinking about UX and I'm wondering it it would be possible attempt determine the shell automatically such that it automatically does the correct thing for the user's environment (e.g. if we detect fish then output in the appropriate fish format).

I think the most reliable and cross-platform means (assuming we need this to work on Windows to output PowerShell format) of detecting the shell would be to get the executable name of the parent process. Like

        // Using github.com/elastic/go-sysinfo.
	parent, err := sysinfo.Process(os.Getppid())
	if err != nil {
		log.Fatal(err)
	}

	parentInfo, err := parent.Info()
	if err != nil {
		log.Fatal(err)
	}

	shell := filepath.Base(parentInfo.Exe)

Reading the SHELL env var could be a simple option, but if a user has launched a subshell explicitly then it will be wrong (e.g. opening fish from bash).

@mtojek mtojek self-requested a review August 22, 2022 12:02
mtojek
mtojek previously requested changes Aug 22, 2022
Copy link
Contributor

@mtojek mtojek left a comment

Choose a reason for hiding this comment

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

Could you please explain what's wrong with the original implementation? I might be missing something. Fish seems to be supported too.

$ elastic-package completion
Generate the autocompletion script for elastic-package for the specified shell.
See each sub-command's help for details on how to use the generated script.

Usage:
  elastic-package completion [command]

Available Commands:
  bash        Generate the autocompletion script for bash
  fish        Generate the autocompletion script for fish
  powershell  Generate the autocompletion script for powershell
  zsh         Generate the autocompletion script for zsh
$ elastic-package completion fish

@mtojek
Copy link
Contributor

mtojek commented Aug 24, 2022

I misread your intention, thanks @jsoriano for pointing it. I thought it's about completion not shellinit. In this case, I would research the solution proposed by Andrew. You could use SHELL env to detect the shell.

@endorama
Copy link
Contributor Author

endorama commented Sep 7, 2022

I updated the code with Jaime suggestion:

  • remove the shell package and put everything in stack
  • updated cobra flag to cobraext
  • simplified templates to posix and fish

Instead of exposing posix to users, I kept bash, sh, zsh as I think is more ergonomic (as a user I don't have to worry about if my shell is compatible, just which shell I'm using) and I want to dig into automating this.

I would strongly advise against using SHELL, as from the docs, it

shall represent a pathname of the user's preferred command language interpreter

This can create issues when running the command in subshell or automating it and depends on a parameter that may not be configurable (think running this in the CI), leading to "fail first, configure later" scenario that I think are

I like Andrew's approach, just want to double check if you are ok adding elastic/go-sysinfo as a dependency, as is not currently used. If you're ok, I would change default flag value to empty and use that as a signal to trigger the autodetection. I would still allow overriding this with an explicit value.

cmd/stack.go Outdated
return cobraext.FlagParsingError(err, cobraext.ProfileFlagName)
}

s, err := cmd.Flags().GetString(cobraext.ShellInitShellFlagName)
Copy link
Member

Choose a reason for hiding this comment

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

Please use a more meaningful name here.

Suggested change
s, err := cmd.Flags().GetString(cobraext.ShellInitShellFlagName)
shellName, err := cmd.Flags().GetString(cobraext.ShellInitShellFlagName)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in 18364c6 (#921)

export %s=%s
export %s=%s
export %s=%s
export %s=%s
Copy link
Member

Choose a reason for hiding this comment

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

This is adding spaces before each export right? Can you check the output of elastic-package stack shellinit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in 7a98775 (#921)

case "zsh":
return posixTemplate
default:
panic("shell type is unknown, should be one of " + strings.Join(availableShellTypes, ", "))
Copy link
Member

Choose a reason for hiding this comment

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

s comes directly from user input, we shouldn't panic in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed in 5a0adf4 (#921)

@jsoriano
Copy link
Member

jsoriano commented Sep 7, 2022

I like Andrew's approach, just want to double check if you are ok adding elastic/go-sysinfo as a dependency, as is not currently used. If you're ok, I would change default flag value to empty and use that as a signal to trigger the autodetection. I would still allow overriding this with an explicit value.

I think it would be fine to add this dependency if it helps to have a better UX.

@elasticmachine
Copy link
Collaborator

elasticmachine commented Sep 7, 2022

🌐 Coverage report

Name Metrics % (covered/total) Diff
Packages 100.0% (35/35) 💚
Files 66.667% (86/129) 👍 0.775
Classes 61.413% (113/184) 👍 0.543
Methods 47.451% (363/765) 👍 0.145
Lines 30.634% (3276/10694) 👍 0.115
Conditionals 100.0% (0/0) 💚

@endorama
Copy link
Contributor Author

Added shell detection using the script provided by Andrew in 57cf67f (#921)

@jsoriano jsoriano self-requested a review September 13, 2022 15:10
@jsoriano
Copy link
Member

CI failures seem related, go-sysinfo should be added to go mod.

`stack shellinit` command outputs shell code to setup the environment
and connect to the local stack instances created by the `stack` command.

Before this change this command was only supporting POSIX compatible
shells. After this commit the support has been extended so is possible
to specify, through a flag, the desired compatibility for it's output.
This removes the `shell` package moving the logic alongside
`ShellInit`, removes the `Shell` type and reduce template code
to only POSIX compliant and fish shell.
Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

👍

cmd/stack.go Outdated
return "", fmt.Errorf("cannot retrieve information for parent of process %d: %w", ppid, err)
}

shell := filepath.Base(parentInfo.Exe)
Copy link
Member

Choose a reason for hiding this comment

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

Found another corner case.

When running elastic-package with go run, it detects go as the shell:

detected shell: go
Error: shellinit failed: cannot get shell init template: shell type is unknown, should be one of bash, fish, sh, zsh
exit status 1

This happens when running elastic-package with go run in repositories where this package is in go.mod. This is done in CI in some cases, and for example I use it when trying to reproduce failures in the integrations repository with the same version of elastic-package that is used in CI.

This could be considered a regression, could we do something about this? Maybe it is possible to detect if elastic-package is being run with go run, or we could get the parent of the parent when it is go.

Copy link
Member

@jsoriano jsoriano Oct 5, 2022

Choose a reason for hiding this comment

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

Or maybe another option to avoid regressions, would be to directly return bash and log something to stderr instead of failing when this method cannot detect the shell.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This use case is supported by not using auto detection (passing --shell=bash). I think that is preferable than deciding to fallback to BASH because that can lead to errors that are very hard to troubleshoot and less explanatory.

We may want to make this explicit using a different error message.

Copy link
Member

Choose a reason for hiding this comment

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

Well, let's separate my two comments:

  • Not breaking the not so rare go run case. Possible workaround for this could be: if found shell is go, try to get the parent of the parent instead.
  • General solution to don't break unknown cases that work now. I propose to use bash in case of error only to keep the current behavior, where only bash is supported. I agree that it could be more discussed as I don't have more use cases beyond the mentioned ones, and it would be better to have explicit behavior.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not breaking the not so rare go run case. Possible workaround for this could be: if found shell is go, try to get the parent of the parent instead.

I added this in 12ddc3d (with tests)

General solution to don't break unknown cases that work now. I propose to use bash in case of error only to keep the current behavior, where only bash is supported.

I didn't consider this POV, but it make complete sense, as the behaviour would not change from current implementation. I will change the default case.

@endorama
Copy link
Contributor Author

endorama commented Oct 6, 2022

The CI fails on check-static, complaining that go.mod and go.sum have changes, but make check-static passes on my local machine. Any idea how to solve?

@jsoriano
Copy link
Member

jsoriano commented Oct 6, 2022

The CI fails on check-static, complaining that go.mod and go.sum have changes, but make check-static passes on my local machine. Any idea how to solve?

CI uses go 1.19, is it possible that you are using an older version?

Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

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

Apart of some last nitpicking and the issues with CI and go.mod it LGTM, thanks!

@jsoriano jsoriano dismissed mtojek’s stale review October 6, 2022 11:38

Comments addressed.

Co-authored-by: Jaime Soriano Pastor <jaime.soriano@elastic.co>
@endorama
Copy link
Contributor Author

CI uses go 1.19, is it possible that you are using an older version?

I'm using go 1.19.1 locally, which is the same as reported in .go-version

@jsoriano
Copy link
Member

CI uses go 1.19, is it possible that you are using an older version?

I'm using go 1.19.1 locally, which is the same as reported in .go-version

If I run make check-static locally with the code of this branch, with go 1.19.2, I see the same errors as in CI. If I run go mod tidy, I see that go.mod and go.sum get changes. 🤔

Shall I push the changes I see to this branch?

@endorama
Copy link
Contributor Author

@jsoriano yes please, I cannot reproduce even with go 1.19.2 :(

@jsoriano jsoriano merged commit 88c16e0 into elastic:main Oct 18, 2022
@endorama endorama deleted the shellinit-fish-support branch October 19, 2022 10:29
@endorama
Copy link
Contributor Author

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request Team:Ecosystem Label for the Packages Ecosystem team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants