Repository: conan-io/conan

Issue:
[bug] conan profile update not working for env section
When trying to update a value for an existing key in a profile's `env` section, the command returns without an error but the key still has its old value. Updating values for existing keys in other sections, e.g. `options`, `scopes` and `settings`, works as expected.

Example:

1. `conan profile show default` -> `env.foo` does not exist
2. `conan profile update env.foo=bar default`
3. `conan profile show default` -> `env.foo` with value `bar` exists
5. `conan profile update env.foo=baz default`
6. `conan profile show default` -> `env.foo` still has old value `bar` instead of expected value `baz`

Conan 1.0.0 (bug persists at least since version 0.28.1)
Python 2.7.6 64-bit
Linux Mint 17.3 64-bit

and

Conan 0.30.3
Python 3.6.4 64-bit
Windows 10 64-bit


Discussion:
Thanks for the report, I have been able to reproduce it.

The issue is that for this update, this code is being used:

https://github.com/conan-io/conan/blob/f4d49d5ec4c2b05ab7fe8f16633c52bd02066e9b/conans/model/env_info.py#L112

This code is shared with the update that is done from dependencies, in which the existing value should not be replaced, because it has higher priority. That makes sense in the propagation stage, but for this command line functionality I agree it can be unexpected. What do yo think @lasote?
In the meantime @verbatom there could be some workarounds: It seems that you might be generating profiles on-the-fly for CI? Is this the case? Then you can consider maybe using the ``conan config install`` feauture, and install profiles instead of generating them? Thanks!

In my case this problem is not a big issue. I wanted to use the `conan profile update` functionality for making the build instructions for the cross-compilation of our software cleaner. The current workaround is to manually edit the corresponding profile file. Nevertheless, I wanted to report this issue.
Ok, good, I have scheduled it for 1.2, many thanks!

Resolving patch:
diff --git a/conans/client/cmd/profile.py b/conans/client/cmd/profile.py
--- a/conans/client/cmd/profile.py
+++ b/conans/client/cmd/profile.py
@@ -62,7 +62,7 @@ def cmd_profile_update(profile_name, key, value, cache_profiles_path):
         tmp = OptionsValues([(rest_key, value)])
         profile.options.update(tmp)
     elif first_key == "env":
-        profile.env_values.update(EnvValues.loads("%s=%s" % (rest_key, value)))
+        profile.env_values.update_replace(rest_key, value)
     elif first_key == "build_requires":
         raise ConanException("Edit the profile manually to change the build_requires")
 
diff --git a/conans/model/env_info.py b/conans/model/env_info.py
--- a/conans/model/env_info.py
+++ b/conans/model/env_info.py
@@ -118,6 +118,16 @@ def add(self, name, value, package=None):
     def remove(self, name, package=None):
         del self._data[package][name]
 
+    def update_replace(self, key, value):
+        """ method useful for command "conan profile update"
+        to execute real update instead of soft update
+        """
+        if ":" in key:
+            package_name, key = key.split(":", 1)
+        else:
+            package_name, key = None, key
+        self._data[package_name][key] = value
+
     def update(self, env_obj):
         """accepts other EnvValues object or DepsEnvInfo
            it prioritize the values that are already at self._data


Test patch:
diff --git a/conans/test/command/profile_test.py b/conans/test/command/profile_test.py
--- a/conans/test/command/profile_test.py
+++ b/conans/test/command/profile_test.py
@@ -125,6 +125,21 @@ def profile_update_and_get_test(self):
         client.run("profile remove env.foo ./MyProfile", ignore_error=True)
         self.assertIn("Profile key 'env.foo' doesn't exist", client.user_io.out)
 
+    def profile_update_env_test(self):
+        client = TestClient()
+        client.run("profile new ./MyProfile")
+        pr_path = os.path.join(client.current_folder, "MyProfile")
+
+        client.run("profile update env.foo=bar ./MyProfile")
+        self.assertEqual(["[env]", "foo=bar"], load(pr_path).splitlines()[-2:])
+        client.run("profile update env.foo=BAZ ./MyProfile")
+        self.assertEqual(["[env]", "foo=BAZ"], load(pr_path).splitlines()[-2:])
+        client.run("profile update env.MyPkg:foo=FOO ./MyProfile")
+        self.assertEqual(["[env]", "foo=BAZ", "MyPkg:foo=FOO"], load(pr_path).splitlines()[-3:])
+        client.run("profile update env.MyPkg:foo=FOO,BAZ,BAR ./MyProfile")
+        self.assertEqual(["[env]", "foo=BAZ", "MyPkg:foo=FOO,BAZ,BAR"],
+                         load(pr_path).splitlines()[-3:])
+
     def profile_new_test(self):
         client = TestClient()
         client.run("profile new ./MyProfile")