Repository: ansible/ansible-navigator (language: python)

Issue:
ansible-navigator artifacts don't contain EE image, args or runtime information

For debugging and other purposes the log artifact created per run contains no information about the EE, name, tags, runtime, etc. So logs passed, for example, back to an SME, 3rd level support etc., lack encapsulated context. 

##### ISSUE TYPE

 - Feature Idea

##### SUMMARY

Provide an extensive `execution-environment:` dictionary within the log so the exact context is captured. Useful attributes include:

- host OS
- container runtime (including version)
- EE name and tag
- CLI arguments.
- volume mounts
- extra vars

Goal: Anyone with the log should be able to exactly recreate the environment and test without additional files or context being supplied. 



Discussion / hints:
Hello @tonykay 

This is a great idea.

We can add a `meta` section to the artifact file with some information about the current settings for the run, we have access to every setting the user has and dump it already in a debug log, but no reason not to add it to the artifact file for informational purposes.

We won't be able to show it in the UI or use the information in navigator, but having it in the artifact file would be useful.

If we're really slick about it, we might be able to add it in ansible-navigator settings format so it could be cut and pasted into a settings file :)

thanks again great idea.
This should follow #577, as it should be able to reuse the generation of structured data from the configuration subsystem 

Fix patch:
diff --git a/docs/changelog-fragments.d/1152.feature.md b/docs/changelog-fragments.d/1152.feature.md
new file mode 100644
index 000000000..86eb32224
--- /dev/null
+++ b/docs/changelog-fragments.d/1152.feature.md
@@ -0,0 +1,3 @@
+Added settings information to playbook artifact.
+
+-- by {user}`cidrblock`
diff --git a/src/ansible_navigator/actions/run.py b/src/ansible_navigator/actions/run.py
index 66b0a7eaf..2d2f53c54 100644
--- a/src/ansible_navigator/actions/run.py
+++ b/src/ansible_navigator/actions/run.py
@@ -23,6 +23,8 @@
 from ..action_defs import RunStdoutReturn
 from ..app_public import AppPublic
 from ..configuration_subsystem import ApplicationConfiguration
+from ..configuration_subsystem import to_effective
+from ..configuration_subsystem import to_sources
 from ..content_defs import ContentView
 from ..content_defs import SerializationFormat
 from ..runner import CommandAsync
@@ -418,7 +420,7 @@ def _init_replay(self) -> bool:
             return False
 
         version = data.get("version", "")
-        if version.startswith("1."):
+        if version.startswith("1.") or version.startswith("2."):
             try:
                 stdout = data["stdout"]
                 if self.mode == "interactive":
@@ -851,11 +853,13 @@ def write_artifact(self, filename: Optional[str] = None) -> None:
             try:
                 os.makedirs(os.path.dirname(filename), exist_ok=True)
                 artifact = {
-                    "version": "1.0.0",
+                    "version": "2.0.0",
                     "plays": self._plays.value,
                     "stdout": self.stdout,
                     "status": status,
                     "status_color": status_color,
+                    "settings_entries": to_effective(self._args),
+                    "settings_sources": to_sources(self._args),
                 }
                 serialize_write_file(
                     content=artifact,


Test patch:
diff --git a/tests/unit/actions/run/test_artifact.py b/tests/unit/actions/run/test_artifact.py
index adddbe5c8..03acc2591 100644
--- a/tests/unit/actions/run/test_artifact.py
+++ b/tests/unit/actions/run/test_artifact.py
@@ -14,6 +14,28 @@
 
 from ansible_navigator.actions.run import Action as action
 from ansible_navigator.configuration_subsystem import NavigatorConfiguration
+from ansible_navigator.configuration_subsystem.definitions import Constants
+from ansible_navigator.initialization import parse_and_update
+
+
+def make_dirs(*_args, **_kwargs):
+    """Mock make_dirs.
+
+    :param _args: The positional arguments
+    :param _kwargs: The keyword arguments
+    :returns: Indication of directory creation success
+    """
+    return True
+
+
+def get_status(*_args, **_kwargs):
+    """Mock run.get_status.
+
+    :param _args: The positional arguments
+    :param _kwargs: The keyword arguments
+    :returns: The runner status
+    """
+    return (0, 0)
 
 
 @dataclass
@@ -126,17 +148,8 @@ def test_artifact_path(
     """
     caplog.set_level(logging.DEBUG)
     monkeypatch.setenv("HOME", "/home/test_user")
-
-    def make_dirs(*_args, **_kwargs):
-        return True
-
     monkeypatch.setattr(os, "makedirs", make_dirs)
-
-    def get_status(*_args, **_kwargs):
-        return (0, 0)
-
     monkeypatch.setattr(action, "_get_status", get_status)
-
     mocked_write = mocker.patch(
         "ansible_navigator.actions.run.serialize_write_file",
         return_value=None,
@@ -175,3 +188,31 @@ def get_status(*_args, **_kwargs):
             assert data.re_match.match(opened_filename), caplog.text
     else:
         mocked_write.assert_not_called()
+
+
+def test_artifact_contents(monkeypatch: pytest.MonkeyPatch, mocker: MockerFixture):
+    """Test the artifact contents for settings information.
+
+    :param monkeypatch: The monkeypatch fixture
+    :param mocker: The mocker fixture
+    """
+    monkeypatch.setattr(os, "makedirs", make_dirs)
+    monkeypatch.setattr(action, "_get_status", get_status)
+    mocked_write = mocker.patch(
+        "ansible_navigator.actions.run.serialize_write_file",
+        return_value=None,
+    )
+
+    settings = deepcopy(NavigatorConfiguration)
+    settings.internals.initializing = True
+    _messages, exit_messages = parse_and_update(params=["run", "site.yaml"], args=settings)
+    assert not exit_messages
+
+    run_action = action(args=settings)
+    run_action.write_artifact(filename="artifact.json")
+
+    settings_entries = mocked_write.call_args[1]["content"]["settings_entries"]
+    assert settings_entries["ansible-navigator"]["app"] == "run"
+
+    settings_sources = mocked_write.call_args[1]["content"]["settings_sources"]
+    assert settings_sources["ansible-navigator.app"] == Constants.USER_CLI.value