From e790f19eaa2b83ca0be560a5d9ef3af8f76a5684 Mon Sep 17 00:00:00 2001 From: Nathan Rijksen Date: Tue, 22 Dec 2020 15:06:34 -0800 Subject: [PATCH] Fix activation walking up the project path too aggressively --- internal/runners/activate/activate.go | 6 ++-- pkg/project/project.go | 14 ++++++++ pkg/projectfile/projectfile.go | 22 +++++++++++++ test/integration/activate_int_test.go | 47 +++++++++++++++++++++++++++ 4 files changed, 86 insertions(+), 3 deletions(-) diff --git a/internal/runners/activate/activate.go b/internal/runners/activate/activate.go index 7e737ecb93..be941c8d08 100644 --- a/internal/runners/activate/activate.go +++ b/internal/runners/activate/activate.go @@ -100,7 +100,7 @@ func (r *Activate) run(params *ActivateParams) error { } // Detect target project - proj, err := r.projectToUse(pathToUse) + proj, err := r.pathToProject(pathToUse) if err != nil { return locale.WrapError(err, "err_activate_projecttouse", "Could not figure out what project to use.") } @@ -242,8 +242,8 @@ func (r *Activate) pathToUse(namespace string, preferredPath string) (string, er } } -func (r *Activate) projectToUse(path string) (*project.Project, error) { - projectToUse, err := project.FromPath(path) +func (r *Activate) pathToProject(path string) (*project.Project, error) { + projectToUse, err := project.FromExactPath(path) if err != nil && !errs.Matches(err, &projectfile.ErrorNoProject{}) { return nil, locale.WrapError(err, "err_activate_projectpath", "Could not find a valid project path.") } diff --git a/pkg/project/project.go b/pkg/project/project.go index a4b47d00e1..cbe50c7713 100644 --- a/pkg/project/project.go +++ b/pkg/project/project.go @@ -314,6 +314,20 @@ func FromPath(path string) (*Project, error) { return project, nil } +// FromExactPath will return the project that's located at the given path without walking up the directory tree +func FromExactPath(path string) (*Project, error) { + pjFile, err := projectfile.FromExactPath(path) + if err != nil { + return nil, err + } + project, err := New(pjFile, output.Get()) + if err != nil { + return nil, err + } + + return project, nil +} + // Platform covers the platform structure type Platform struct { platform *projectfile.Platform diff --git a/pkg/projectfile/projectfile.go b/pkg/projectfile/projectfile.go index ee2fbc06ca..36fd09937b 100644 --- a/pkg/projectfile/projectfile.go +++ b/pkg/projectfile/projectfile.go @@ -936,6 +936,28 @@ func FromPath(path string) (*Project, error) { return project, nil } +// FromExactPath will return the projectfile that's located at the given path without walking up the directory tree +func FromExactPath(path string) (*Project, error) { + // we do not want to use a path provided by state if we're running tests + projectFilePath := filepath.Join(path, constants.ConfigFileName) + + if !fileutils.FileExists(projectFilePath) { + return nil, &ErrorNoProject{locale.NewInputError("err_no_projectfile")} + } + + _, err := ioutil.ReadFile(projectFilePath) + if err != nil { + logging.Warning("Cannot load config file: %v", err) + return nil, &ErrorNoProject{locale.WrapInputError(err, "err_no_projectfile")} + } + project, err := Parse(projectFilePath) + if err != nil { + return nil, errs.Wrap(err, "Parse %s failed", projectFilePath) + } + + return project, nil +} + // CreateParams are parameters that we create a custom activestate.yaml file from type CreateParams struct { Owner string diff --git a/test/integration/activate_int_test.go b/test/integration/activate_int_test.go index 9467a2c30a..31f5e1808c 100644 --- a/test/integration/activate_int_test.go +++ b/test/integration/activate_int_test.go @@ -378,7 +378,54 @@ version: %s c2.WaitForInput(20 * time.Second) c2.SendLine("exit") c2.ExpectExitCode(0) +} + +func (suite *ActivateIntegrationTestSuite) TestActivate_NamespaceWins() { + suite.OnlyRunForTags(tagsuite.Activate) + ts := e2e.New(suite.T(), false) + identifyPath := "identifyable-path" + targetPath := filepath.Join(ts.Dirs.Work, "foo", "bar", identifyPath) + defer ts.Close() + err := fileutils.Mkdir(targetPath) + suite.Require().NoError(err) + + // Create the project file at the root of the temp dir + content := strings.TrimSpace(fmt.Sprintf(` +project: "https://platform.activestate.com/ActiveState-CLI/Python3" +`)) + + ts.PrepareActiveStateYAML(content) + + // Pull to ensure we have an up to date config file + cp := ts.Spawn("pull") + cp.Expect("Your activestate.yaml has been updated to the latest version available") + cp.ExpectExitCode(0) + // Activate in the subdirectory + c2 := ts.SpawnWithOpts( + e2e.WithArgs("activate", "ActiveState-CLI/Python2"), // activate a different namespace + e2e.WithWorkDirectory(targetPath), + ) + c2.ExpectLongString("Where would you like") + c2.SendUnterminated(string([]byte{0033, '[', 'B'})) // move cursor down, and then press enter + c2.Expect(">") + c2.Send("") + c2.Expect(">") + c2.SendLine(targetPath) + c2.ExpectLongString("ActiveState-CLI/Python2") + c2.ExpectLongString("default project?") + c2.Send("n") + c2.Expect("You're Activated") + + c2.WaitForInput(20 * time.Second) + if runtime.GOOS == "windows" { + c2.SendLine("@echo %cd%") + } else { + c2.SendLine("pwd") + } + c2.Expect(identifyPath) + c2.SendLine("exit") + c2.ExpectExitCode(0) } func (suite *ActivateIntegrationTestSuite) TestInit_Activation_NoCommitID() {