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

Update shell rcfiles to indicate the user is already in an activated state. #2710

Merged
merged 12 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions internal/assets/contents/shells/bashrc_append.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ export {{$K}}="{{$V}}:$PATH"
export {{$K}}="{{$V}}"
{{- end}}
{{- end}}
if [[ ! -z "${{.ActivatedEnv}}" && -f "${{.ActivatedEnv}}/{{.ConfigFile}}" ]]; then
echo "State Tool is operating on project ${{.ActivatedNamespaceEnv}}, located at ${{.ActivatedEnv}}"
fi
# {{.Stop}}
6 changes: 5 additions & 1 deletion internal/assets/contents/shells/fishrc_append.fish
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ set -xg {{$K}} "{{$V}}:$PATH"
set -xg {{$K}} "{{$V}}"
{{- end}}
{{- end}}
# {{.Stop}}
if test ! -z "${{.ActivatedEnv}}"
and test -f "${{.ActivatedEnv}}/{{.ConfigFile}}"
mitchell-as marked this conversation as resolved.
Show resolved Hide resolved
echo "State Tool is operating on project ${{.ActivatedNamespaceEnv}}, located at ${{.ActivatedEnv}}"
end
# {{.Stop}}
3 changes: 3 additions & 0 deletions internal/assets/contents/shells/tcshrc_append.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ setenv {{$K}} "{{$V}}:$PATH"
{{- else}}
{{- end}}
{{- end}}
if ( "${{.ActivatedEnv}}" != "" && -f "${{.ActivatedEnv}}/{{.ConfigFile}}" ) then
echo "State Tool is operating on project ${{.ActivatedNamespaceEnv}}, located at ${{.ActivatedEnv}}"
endif
# {{.Stop}}
3 changes: 3 additions & 0 deletions internal/assets/contents/shells/zshrc_append.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ export {{$K}}="{{$V}}:$PATH"
export {{$K}}="{{$V}}"
{{- end}}
{{- end}}
if [[ ! -z "${{.ActivatedEnv}}" && -f "${{.ActivatedEnv}}/{{.ConfigFile}}" ]]; then
echo "State Tool is operating on project ${{.ActivatedNamespaceEnv}}, located at ${{.ActivatedEnv}}"
fi
# {{.Stop}}
3 changes: 3 additions & 0 deletions internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ const ActivatedStateEnvVarName = "ACTIVESTATE_ACTIVATED"
// ActivatedStateIDEnvVarName is the name of the environment variable that is set when in an activated state, its value will be a unique id identifying a specific instance of an activated state
const ActivatedStateIDEnvVarName = "ACTIVESTATE_ACTIVATED_ID"

// ActivatedStateProjectEnvVarName is the name of the environment variable that specifies the activated state's org/project namespace.
const ActivatedStateNamespaceEnvVarName = "ACTIVESTATE_ACTIVATED_NAMESPACE"

// ForwardedStateEnvVarName is the name of the environment variable that is set when in an activated state, its value will be the path of the project
const ForwardedStateEnvVarName = "ACTIVESTATE_FORWARDED"

Expand Down
2 changes: 1 addition & 1 deletion internal/runbits/activation/activation.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func ActivateAndWait(
}
}

ve, err := venv.GetEnv(false, true, projectDir)
ve, err := venv.GetEnv(false, true, projectDir, proj.Namespace().String())
if err != nil {
return locale.WrapError(err, "error_could_not_activate_venv", "Could not retrieve environment information.")
}
Expand Down
2 changes: 1 addition & 1 deletion internal/runners/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func (s *Exec) Run(params *Params, args ...string) (rerr error) {
}
venv := virtualenvironment.New(rt)

env, err := venv.GetEnv(true, false, projectDir)
env, err := venv.GetEnv(true, false, projectDir, projectNamespace)
if err != nil {
return locale.WrapError(err, "err_exec_env", "Could not retrieve environment information for your runtime")
}
Expand Down
4 changes: 2 additions & 2 deletions internal/scriptrun/scriptrun.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func (s *ScriptRun) PrepareVirtualEnv() (rerr error) {
venv := virtualenvironment.New(rt)

projDir := filepath.Dir(s.project.Source().Path())
env, err := venv.GetEnv(true, true, projDir)
env, err := venv.GetEnv(true, true, projDir, s.project.Namespace().String())
if err != nil {
return errs.Wrap(err, "Could not get venv environment")
}
Expand All @@ -91,7 +91,7 @@ func (s *ScriptRun) PrepareVirtualEnv() (rerr error) {
}

// search the "clean" path first (PATHS that are set by venv)
env, err = venv.GetEnv(false, true, "")
env, err = venv.GetEnv(false, true, "", "")
if err != nil {
return errs.Wrap(err, "Could not get venv environment")
}
Expand Down
9 changes: 6 additions & 3 deletions internal/subshell/sscommon/rcfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,12 @@ func WriteRcFile(rcTemplateName string, path string, data RcIdentification, env
}

rcData := map[string]interface{}{
"Start": data.Start,
"Stop": data.Stop,
"Env": env,
"Start": data.Start,
"Stop": data.Stop,
"Env": env,
"ActivatedEnv": constants.ActivatedStateEnvVarName,
"ConfigFile": constants.ConfigFileName,
"ActivatedNamespaceEnv": constants.ActivatedStateNamespaceEnvVarName,
}

if err := CleanRcFile(path, data); err != nil {
Expand Down
26 changes: 21 additions & 5 deletions internal/subshell/sscommon/rcfile_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sscommon
import (
"fmt"
"reflect"
"runtime"
"strings"
"testing"

Expand Down Expand Up @@ -43,35 +44,50 @@ func TestWriteRcFile(t *testing.T) {
path string
env map[string]string
}

zsh := fmt.Sprintf(
`export PATH="foo:$PATH"
if [[ ! -z "$%s" && -f "$%s/%s" ]]; then
echo "State Tool is operating on project $%s, located at $%s"
fi`,
constants.ActivatedStateEnvVarName,
constants.ActivatedStateEnvVarName,
constants.ConfigFileName,
constants.ActivatedStateNamespaceEnvVarName,
constants.ActivatedStateEnvVarName)
if runtime.GOOS == "windows" {
zsh = strings.ReplaceAll(zsh, "\n", "\r\n")
}

tests := []struct {
name string
args args
want error
want error
wantContents string
}{
{
"Write RC to empty file",
args{
"fishrc_append.fish",
"zshrc_append.sh",
fakeFileWithContents("", "", ""),
map[string]string{
"PATH": "foo",
},
},
nil,
fakeContents("", `set -xg PATH "foo:$PATH"`, ""),
fakeContents("", zsh, ""),
},
{
"Write RC update",
args{
"fishrc_append.fish",
"zshrc_append.sh",
fakeFileWithContents("before", "SOMETHING ELSE", "after"),
map[string]string{
"PATH": "foo",
},
},
nil,
fakeContents(strings.Join([]string{"before", "after"}, fileutils.LineEnd), `set -xg PATH "foo:$PATH"`, ""),
fakeContents(strings.Join([]string{"before", "after"}, fileutils.LineEnd), zsh, ""),
mitchell-as marked this conversation as resolved.
Show resolved Hide resolved
},
}
for _, tt := range tests {
Expand Down
3 changes: 2 additions & 1 deletion internal/virtualenvironment/virtualenvironment.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func New(runtime *runtime.Runtime) *VirtualEnvironment {
}

// GetEnv returns a map of the cumulative environment variables for all active virtual environments
func (v *VirtualEnvironment) GetEnv(inherit bool, useExecutors bool, projectDir string) (map[string]string, error) {
func (v *VirtualEnvironment) GetEnv(inherit bool, useExecutors bool, projectDir, namespace string) (map[string]string, error) {
envMap := make(map[string]string)

// Source runtime environment information
Expand All @@ -44,6 +44,7 @@ func (v *VirtualEnvironment) GetEnv(inherit bool, useExecutors bool, projectDir
if projectDir != "" {
envMap[constants.ActivatedStateEnvVarName] = projectDir
envMap[constants.ActivatedStateIDEnvVarName] = v.activationID
envMap[constants.ActivatedStateNamespaceEnvVarName] = namespace

// Get project from explicitly defined configuration file
configFile := filepath.Join(projectDir, constants.ConfigFileName)
Expand Down
49 changes: 49 additions & 0 deletions test/integration/shell_int_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/ActiveState/cli/internal/config"
"github.com/ActiveState/cli/internal/fileutils"
"github.com/ActiveState/cli/internal/subshell"
"github.com/ActiveState/cli/internal/subshell/bash"
"github.com/ActiveState/cli/internal/subshell/sscommon"
"github.com/ActiveState/cli/internal/subshell/zsh"
"github.com/ActiveState/cli/internal/testhelpers/e2e"
"github.com/ActiveState/cli/internal/testhelpers/tagsuite"
Expand Down Expand Up @@ -276,6 +278,53 @@ func (suite *ShellIntegrationTestSuite) SetupRCFile(ts *e2e.Session) {
suite.Require().NoError(err)
}

func (suite *ShellIntegrationTestSuite) TestNestedShellNotification() {
if runtime.GOOS == "windows" {
return // cmd.exe does not have an RC file to check for nested shells in
}
suite.OnlyRunForTags(tagsuite.Shell)
ts := e2e.New(suite.T(), false)
defer ts.Close()

cfg, err := config.New()
suite.Require().NoError(err)

ss := subshell.New(cfg)
err = subshell.ConfigureAvailableShells(ss, cfg, nil, sscommon.InstallID, true) // mimic installer
suite.Require().NoError(err)

rcFile, err := ss.RcFile()
suite.Require().NoError(err)
suite.Require().FileExists(rcFile)
suite.Require().Contains(string(fileutils.ReadFileUnsafe(rcFile)), "State Tool is operating on project")

cp := ts.Spawn("checkout", "ActiveState-CLI/small-python")
cp.Expect("Checked out project")
cp.ExpectExitCode(0)

cp = ts.SpawnWithOpts(
e2e.WithArgs("shell", "small-python"),
e2e.AppendEnv("ACTIVESTATE_CLI_DISABLE_RUNTIME=false"),
)
cp.Expect("Activated")
suite.Assert().NotContains(cp.TrimmedSnapshot(), "State Tool is operating on project")

binary := ss.Binary() // platform-specific shell (zsh on macOS, bash on Linux, etc.)
// Cannot run bare binary because it will not load the rcFile in ts.Dirs.HomeDir.
// Instead, tell the shell to load rcFile. This is not needed in a non-test environment.
switch ss.Shell() {
case bash.Name:
binary = fmt.Sprintf("%s --rcfile %s", binary, rcFile)
case zsh.Name:
binary = fmt.Sprintf("ZDOTDIR=%s %s", filepath.Dir(rcFile), binary)
mitchell-as marked this conversation as resolved.
Show resolved Hide resolved
}
cp.SendLine(binary)
cp.ExpectLongString("State Tool is operating on project ActiveState-CLI/small-python")
cp.SendLine("exit") // subshell within a subshell
cp.SendLine("exit")
cp.ExpectExitCode(0)
}

func TestShellIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(ShellIntegrationTestSuite))
}