From b97cba0f880a0e5338293ea35365ffa0be643869 Mon Sep 17 00:00:00 2001 From: Daniel Snider Date: Fri, 2 Oct 2026 15:04:04 -0500 Subject: [PATCH] fix(variable:create): forward --level when handing off to variable:update With --update, an existing variable is handed to variable:update, but --level was dropped from the forwarded options, so update looked the variable up with no level. A name present at both project and environment level then failed with "Variable found at both project and environment levels", and --level project without --environment failed with "No environment specified", although create had already resolved the variable. The exclusion was introduced in c0c67746 (#1247) next to --prefix and carried through f5b7f32b (#1426), with no rationale given in either. variable:update has accepted --level throughout. --- integration-tests/variable_write_test.go | 43 +++++++++++++++++++ .../Variable/VariableCreateCommand.php | 2 +- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/integration-tests/variable_write_test.go b/integration-tests/variable_write_test.go index faf469d1..f6bffb07 100644 --- a/integration-tests/variable_write_test.go +++ b/integration-tests/variable_write_test.go @@ -115,6 +115,49 @@ func TestVariableCreateDefaultEnvironment(t *testing.T) { assertTrimmed(t, "bar foo", f.Run("var:get", "-p", p, "-e", "main", "env:FOOBAR", "-P", "value")) } +func TestVariableCreateUpdateWithLevel(t *testing.T) { + // Regression test for CLI-194: "var:create --update" must forward --level to var:update. + s := setupVariableTest(t) + s.apiHandler.SetEnvironments([]*mockapi.Environment{s.mainEnv}) + + f, p := s.factory, s.projectID + + f.Run("var:create", "-p", p, "-l", "e", "-e", "main", "env:TEST", "--value", "e0") + f.Run("var:create", "-p", p, "-l", "p", "env:TEST", "--value", "p0") + + //nolint:lll + _, stdErr, err := f.RunCombinedOutput("var:create", "-p", p, "-u", "-e", "main", "-l", "e", "env:TEST", "--value", "e1") + assert.NoError(t, err) + assert.NotContains(t, stdErr, "found at both") + assertTrimmed(t, "e1", f.Run("var:get", "-p", p, "-e", "main", "env:TEST", "-l", "e", "-P", "value")) + assertTrimmed(t, "p0", f.Run("var:get", "-p", p, "-e", "main", "env:TEST", "-l", "p", "-P", "value")) + + _, _, err = f.RunCombinedOutput("var:create", "-p", p, "-u", "-e", "main", "-l", "p", "env:TEST", "--value", "p1") + assert.NoError(t, err) + assertTrimmed(t, "p1", f.Run("var:get", "-p", p, "-e", "main", "env:TEST", "-l", "p", "-P", "value")) + assertTrimmed(t, "e1", f.Run("var:get", "-p", p, "-e", "main", "env:TEST", "-l", "e", "-P", "value")) + + // Project level, without an environment. + s = setupVariableTest(t) + s.apiHandler.SetEnvironments([]*mockapi.Environment{s.mainEnv}) + f, p = s.factory, s.projectID + + f.Run("var:create", "-p", p, "-l", "p", "env:P", "--value", "p0") + _, _, err = f.RunCombinedOutput("var:create", "-p", p, "-u", "-l", "p", "env:P", "--value", "p1") + assert.NoError(t, err) + assertTrimmed(t, "p1", f.Run("var:get", "-p", p, "env:P", "-l", "p", "-P", "value")) + + // Without --level. + s = setupVariableTest(t) + s.apiHandler.SetEnvironments([]*mockapi.Environment{s.mainEnv}) + f, p = s.factory, s.projectID + + f.Run("var:create", "-p", p, "-l", "e", "-e", "main", "env:E", "--value", "e0") + _, _, err = f.RunCombinedOutput("var:create", "-p", p, "-u", "-e", "main", "env:E", "--value", "e1") + assert.NoError(t, err) + assertTrimmed(t, "e1", f.Run("var:get", "-p", p, "-e", "main", "env:E", "-l", "e", "-P", "value")) +} + func TestVariableCreateWithAppScope(t *testing.T) { s := setupVariableTest(t) diff --git a/legacy/src/Command/Variable/VariableCreateCommand.php b/legacy/src/Command/Variable/VariableCreateCommand.php index 7be070ca..f47a48f6 100644 --- a/legacy/src/Command/Variable/VariableCreateCommand.php +++ b/legacy/src/Command/Variable/VariableCreateCommand.php @@ -118,7 +118,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int foreach ($this->form->getFields() as $field) { $argName = '--' . $field->getOptionName(); $value = $field->getValueFromInput($input, false); - if ($value !== null && !in_array($argName, ['--name', '--level', '--prefix'])) { + if ($value !== null && !in_array($argName, ['--name', '--prefix'])) { $arguments[$argName] = $value; } }