We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Don't activate zulip_py3_venv at the launch of shell on direct system dev setups.
As a part of `./tools/provision` we add `"source " + os.path.join(VENV_PATH, "bin", "activate")` to the bash profile of the user which is responsible for automatic activation of the zulip's virtualenv when a new shell is launched.

While this is handy for Vagrant/Docker/Droplet setups, which are dedicated machines for running Zulip, this is not a desired behaviour in other setups like [Direct Installation on systems](https://zulip.readthedocs.io/en/latest/development/setup-advanced.html#installing-directly-on-ubuntu-debian-centos-or-fedora).

Solution would be to extract the script which writes the venv activation to bash profile and call it via Vagrantfile/Dockerfile/Droplet setup script.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tools/lib/provision_inner.py`
Content:
```
1 #!/usr/bin/env python3
2 import argparse
3 import glob
4 import os
5 import shutil
6 import sys
7 from typing import List
8 
9 ZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
10 
11 sys.path.append(ZULIP_PATH)
12 import pygments
13 
14 from scripts.lib import clean_unused_caches
15 from scripts.lib.zulip_tools import (
16     ENDC,
17     OKBLUE,
18     get_dev_uuid_var_path,
19     get_tzdata_zi,
20     is_digest_obsolete,
21     run,
22     run_as_root,
23     write_new_digest,
24 )
25 from tools.setup.generate_zulip_bots_static_files import generate_zulip_bots_static_files
26 from version import PROVISION_VERSION
27 
28 VENV_PATH = "/srv/zulip-py3-venv"
29 UUID_VAR_PATH = get_dev_uuid_var_path()
30 
31 with get_tzdata_zi() as f:
32     line = f.readline()
33     assert line.startswith("# version ")
34     timezones_version = line[len("# version ") :]
35 
36 
37 def create_var_directories() -> None:
38     # create var/coverage, var/log, etc.
39     var_dir = os.path.join(ZULIP_PATH, "var")
40     sub_dirs = [
41         "coverage",
42         "log",
43         "node-coverage",
44         "test_uploads",
45         "uploads",
46         "xunit-test-results",
47     ]
48     for sub_dir in sub_dirs:
49         path = os.path.join(var_dir, sub_dir)
50         os.makedirs(path, exist_ok=True)
51 
52 
53 def build_pygments_data_paths() -> List[str]:
54     paths = [
55         "tools/setup/build_pygments_data",
56         "tools/setup/lang.json",
57     ]
58     return paths
59 
60 
61 def build_timezones_data_paths() -> List[str]:
62     paths = [
63         "tools/setup/build_timezone_values",
64     ]
65     return paths
66 
67 
68 def compilemessages_paths() -> List[str]:
69     paths = ["zerver/management/commands/compilemessages.py"]
70     paths += glob.glob("locale/*/LC_MESSAGES/*.po")
71     paths += glob.glob("locale/*/translations.json")
72     return paths
73 
74 
75 def configure_rabbitmq_paths() -> List[str]:
76     paths = [
77         "scripts/setup/configure-rabbitmq",
78     ]
79     return paths
80 
81 
82 def setup_shell_profile(shell_profile: str) -> None:
83     shell_profile_path = os.path.expanduser(shell_profile)
84 
85     def write_command(command: str) -> None:
86         if os.path.exists(shell_profile_path):
87             with open(shell_profile_path) as shell_profile_file:
88                 lines = [line.strip() for line in shell_profile_file.readlines()]
89             if command not in lines:
90                 with open(shell_profile_path, "a+") as shell_profile_file:
91                     shell_profile_file.writelines(command + "\n")
92         else:
93             with open(shell_profile_path, "w") as shell_profile_file:
94                 shell_profile_file.writelines(command + "\n")
95 
96     source_activate_command = "source " + os.path.join(VENV_PATH, "bin", "activate")
97     write_command(source_activate_command)
98     if os.path.exists("/srv/zulip"):
99         write_command("cd /srv/zulip")
100 
101 
102 def setup_bash_profile() -> None:
103     """Select a bash profile file to add setup code to."""
104 
105     BASH_PROFILES = [
106         os.path.expanduser(p) for p in ("~/.bash_profile", "~/.bash_login", "~/.profile")
107     ]
108 
109     def clear_old_profile() -> None:
110         # An earlier version of this script would output a fresh .bash_profile
111         # even though a .profile existed in the image used. As a convenience to
112         # existing developers (and, perhaps, future developers git-bisecting the
113         # provisioning scripts), check for this situation, and blow away the
114         # created .bash_profile if one is found.
115 
116         BASH_PROFILE = BASH_PROFILES[0]
117         DOT_PROFILE = BASH_PROFILES[2]
118         OLD_PROFILE_TEXT = "source /srv/zulip-py3-venv/bin/activate\ncd /srv/zulip\n"
119 
120         if os.path.exists(DOT_PROFILE):
121             try:
122                 with open(BASH_PROFILE) as f:
123                     profile_contents = f.read()
124                 if profile_contents == OLD_PROFILE_TEXT:
125                     os.unlink(BASH_PROFILE)
126             except FileNotFoundError:
127                 pass
128 
129     clear_old_profile()
130 
131     for candidate_profile in BASH_PROFILES:
132         if os.path.exists(candidate_profile):
133             setup_shell_profile(candidate_profile)
134             break
135     else:
136         # no existing bash profile found; claim .bash_profile
137         setup_shell_profile(BASH_PROFILES[0])
138 
139 
140 def need_to_run_build_pygments_data() -> bool:
141     if not os.path.exists("web/generated/pygments_data.json"):
142         return True
143 
144     return is_digest_obsolete(
145         "build_pygments_data_hash",
146         build_pygments_data_paths(),
147         [pygments.__version__],
148     )
149 
150 
151 def need_to_run_build_timezone_data() -> bool:
152     if not os.path.exists("web/generated/timezones.json"):
153         return True
154 
155     return is_digest_obsolete(
156         "build_timezones_data_hash",
157         build_timezones_data_paths(),
158         [timezones_version],
159     )
160 
161 
162 def need_to_run_compilemessages() -> bool:
163     if not os.path.exists("locale/language_name_map.json"):
164         # User may have cleaned their Git checkout.
165         print("Need to run compilemessages due to missing language_name_map.json")
166         return True
167 
168     return is_digest_obsolete(
169         "last_compilemessages_hash",
170         compilemessages_paths(),
171     )
172 
173 
174 def need_to_run_configure_rabbitmq(settings_list: List[str]) -> bool:
175     obsolete = is_digest_obsolete(
176         "last_configure_rabbitmq_hash",
177         configure_rabbitmq_paths(),
178         settings_list,
179     )
180 
181     if obsolete:
182         return True
183 
184     try:
185         from zerver.lib.queue import SimpleQueueClient
186 
187         SimpleQueueClient()
188         return False
189     except Exception:
190         return True
191 
192 
193 def main(options: argparse.Namespace) -> int:
194     setup_bash_profile()
195     setup_shell_profile("~/.zprofile")
196 
197     # This needs to happen before anything that imports zproject.settings.
198     run(["scripts/setup/generate_secrets.py", "--development"])
199 
200     create_var_directories()
201 
202     # The `build_emoji` script requires `emoji-datasource` package
203     # which we install via npm; thus this step is after installing npm
204     # packages.
205     run(["tools/setup/emoji/build_emoji"])
206 
207     # copy over static files from the zulip_bots package
208     generate_zulip_bots_static_files()
209 
210     if options.is_force or need_to_run_build_pygments_data():
211         run(["tools/setup/build_pygments_data"])
212         write_new_digest(
213             "build_pygments_data_hash",
214             build_pygments_data_paths(),
215             [pygments.__version__],
216         )
217     else:
218         print("No need to run `tools/setup/build_pygments_data`.")
219 
220     if options.is_force or need_to_run_build_timezone_data():
221         run(["tools/setup/build_timezone_values"])
222         write_new_digest(
223             "build_timezones_data_hash",
224             build_timezones_data_paths(),
225             [timezones_version],
226         )
227     else:
228         print("No need to run `tools/setup/build_timezone_values`.")
229 
230     if not options.is_build_release_tarball_only:
231         # The following block is skipped when we just need the development
232         # environment to build a release tarball.
233 
234         # Need to set up Django before using template_status
235         os.environ.setdefault("DJANGO_SETTINGS_MODULE", "zproject.settings")
236         import django
237 
238         django.setup()
239 
240         from django.conf import settings
241 
242         from zerver.lib.test_fixtures import (
243             DEV_DATABASE,
244             TEST_DATABASE,
245             destroy_leaked_test_databases,
246         )
247 
248         assert settings.RABBITMQ_PASSWORD is not None
249         if options.is_force or need_to_run_configure_rabbitmq([settings.RABBITMQ_PASSWORD]):
250             run_as_root(["scripts/setup/configure-rabbitmq"])
251             write_new_digest(
252                 "last_configure_rabbitmq_hash",
253                 configure_rabbitmq_paths(),
254                 [settings.RABBITMQ_PASSWORD],
255             )
256         else:
257             print("No need to run `scripts/setup/configure-rabbitmq.")
258 
259         dev_template_db_status = DEV_DATABASE.template_status()
260         if options.is_force or dev_template_db_status == "needs_rebuild":
261             run(["tools/setup/postgresql-init-dev-db"])
262             if options.skip_dev_db_build:
263                 # We don't need to build the manual development
264                 # database on continuous integration for running tests, so we can
265                 # just leave it as a template db and save a minute.
266                 #
267                 # Important: We don't write a digest as that would
268                 # incorrectly claim that we ran migrations.
269                 pass
270             else:
271                 run(["tools/rebuild-dev-database"])
272                 DEV_DATABASE.write_new_db_digest()
273         elif dev_template_db_status == "run_migrations":
274             DEV_DATABASE.run_db_migrations()
275         elif dev_template_db_status == "current":
276             print("No need to regenerate the dev DB.")
277 
278         test_template_db_status = TEST_DATABASE.template_status()
279         if options.is_force or test_template_db_status == "needs_rebuild":
280             run(["tools/setup/postgresql-init-test-db"])
281             run(["tools/rebuild-test-database"])
282             TEST_DATABASE.write_new_db_digest()
283         elif test_template_db_status == "run_migrations":
284             TEST_DATABASE.run_db_migrations()
285         elif test_template_db_status == "current":
286             print("No need to regenerate the test DB.")
287 
288         if options.is_force or need_to_run_compilemessages():
289             run(["./manage.py", "compilemessages", "--ignore=*"])
290             write_new_digest(
291                 "last_compilemessages_hash",
292                 compilemessages_paths(),
293             )
294         else:
295             print("No need to run `manage.py compilemessages`.")
296 
297         destroyed = destroy_leaked_test_databases()
298         if destroyed:
299             print(f"Dropped {destroyed} stale test databases!")
300 
301     clean_unused_caches.main(
302         argparse.Namespace(
303             threshold_days=6,
304             # The defaults here should match parse_cache_script_args in zulip_tools.py
305             dry_run=False,
306             verbose=False,
307             no_headings=True,
308         )
309     )
310 
311     # Keeping this cache file around can cause eslint to throw
312     # random TypeErrors when new/updated dependencies are added
313     if os.path.isfile(".eslintcache"):
314         # Remove this block when
315         # https://github.com/eslint/eslint/issues/11639 is fixed
316         # upstream.
317         os.remove(".eslintcache")
318 
319     # Clean up the root of the `var/` directory for various
320     # testing-related files that we have migrated to
321     # `var/<uuid>/test-backend`.
322     print("Cleaning var/ directory files...")
323     var_paths = glob.glob("var/test*")
324     var_paths.append("var/bot_avatar")
325     for path in var_paths:
326         try:
327             if os.path.isdir(path):
328                 shutil.rmtree(path)
329             else:
330                 os.remove(path)
331         except FileNotFoundError:
332             pass
333 
334     version_file = os.path.join(UUID_VAR_PATH, "provision_version")
335     print(f"writing to {version_file}\n")
336     with open(version_file, "w") as f:
337         f.write(".".join(map(str, PROVISION_VERSION)) + "\n")
338 
339     print()
340     print(OKBLUE + "Zulip development environment setup succeeded!" + ENDC)
341     return 0
342 
343 
344 if __name__ == "__main__":
345     parser = argparse.ArgumentParser()
346     parser.add_argument(
347         "--force",
348         action="store_true",
349         dest="is_force",
350         help="Ignore all provisioning optimizations.",
351     )
352 
353     parser.add_argument(
354         "--build-release-tarball-only",
355         action="store_true",
356         dest="is_build_release_tarball_only",
357         help="Provision for test suite with production settings.",
358     )
359 
360     parser.add_argument(
361         "--skip-dev-db-build", action="store_true", help="Don't run migrations on dev database."
362     )
363 
364     options = parser.parse_args()
365     sys.exit(main(options))
366
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tools/lib/provision_inner.py b/tools/lib/provision_inner.py
--- a/tools/lib/provision_inner.py
+++ b/tools/lib/provision_inner.py
@@ -93,8 +93,16 @@
             with open(shell_profile_path, "w") as shell_profile_file:
                 shell_profile_file.writelines(command + "\n")
 
-    source_activate_command = "source " + os.path.join(VENV_PATH, "bin", "activate")
-    write_command(source_activate_command)
+    # Check if the environment is one of the dedicated containers: Vagrant, Docker, Droplet or WSL2
+    WSL = os.path.exists("/proc/sys/fs/binfmt_misc/WSLInterop")
+    vagrant = os.path.exists("/vagrant")
+    docker = os.path.exists("/var/run/docker.sock")
+    droplet = os.path.exists("/etc/digitalocean")
+    # If the environment matches one of the containers, write the activation command to the user's bash profile
+    if WSL or vagrant or docker or droplet:
+        source_activate_command = "source " + os.path.join(VENV_PATH, "bin", "activate")
+        write_command(source_activate_command)
+
     if os.path.exists("/srv/zulip"):
         write_command("cd /srv/zulip")

{"golden_diff": "diff --git a/tools/lib/provision_inner.py b/tools/lib/provision_inner.py\n--- a/tools/lib/provision_inner.py\n+++ b/tools/lib/provision_inner.py\n@@ -93,8 +93,16 @@\n             with open(shell_profile_path, \"w\") as shell_profile_file:\n                 shell_profile_file.writelines(command + \"\\n\")\n \n-    source_activate_command = \"source \" + os.path.join(VENV_PATH, \"bin\", \"activate\")\n-    write_command(source_activate_command)\n+    # Check if the environment is one of the dedicated containers: Vagrant, Docker, Droplet or WSL2\n+    WSL = os.path.exists(\"/proc/sys/fs/binfmt_misc/WSLInterop\")\n+    vagrant = os.path.exists(\"/vagrant\")\n+    docker = os.path.exists(\"/var/run/docker.sock\")\n+    droplet = os.path.exists(\"/etc/digitalocean\")\n+    # If the environment matches one of the containers, write the activation command to the user's bash profile\n+    if WSL or vagrant or docker or droplet:\n+        source_activate_command = \"source \" + os.path.join(VENV_PATH, \"bin\", \"activate\")\n+        write_command(source_activate_command)\n+\n     if os.path.exists(\"/srv/zulip\"):\n         write_command(\"cd /srv/zulip\")\n", "issue": "Don't activate zulip_py3_venv at the launch of shell on direct system dev setups.\nAs a part of `./tools/provision` we add `\"source \" + os.path.join(VENV_PATH, \"bin\", \"activate\")` to the bash profile of the user which is responsible for automatic activation of the zulip's virtualenv when a new shell is launched.\r\n\r\nWhile this is handy for Vagrant/Docker/Droplet setups, which are dedicated machines for running Zulip, this is not a desired behaviour in other setups like [Direct Installation on systems](https://zulip.readthedocs.io/en/latest/development/setup-advanced.html#installing-directly-on-ubuntu-debian-centos-or-fedora).\r\n\r\nSolution would be to extract the script which writes the venv activation to bash profile and call it via Vagrantfile/Dockerfile/Droplet setup script.\n", "before_files": [{"content": "#!/usr/bin/env python3\nimport argparse\nimport glob\nimport os\nimport shutil\nimport sys\nfrom typing import List\n\nZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nsys.path.append(ZULIP_PATH)\nimport pygments\n\nfrom scripts.lib import clean_unused_caches\nfrom scripts.lib.zulip_tools import (\n    ENDC,\n    OKBLUE,\n    get_dev_uuid_var_path,\n    get_tzdata_zi,\n    is_digest_obsolete,\n    run,\n    run_as_root,\n    write_new_digest,\n)\nfrom tools.setup.generate_zulip_bots_static_files import generate_zulip_bots_static_files\nfrom version import PROVISION_VERSION\n\nVENV_PATH = \"/srv/zulip-py3-venv\"\nUUID_VAR_PATH = get_dev_uuid_var_path()\n\nwith get_tzdata_zi() as f:\n    line = f.readline()\n    assert line.startswith(\"# version \")\n    timezones_version = line[len(\"# version \") :]\n\n\ndef create_var_directories() -> None:\n    # create var/coverage, var/log, etc.\n    var_dir = os.path.join(ZULIP_PATH, \"var\")\n    sub_dirs = [\n        \"coverage\",\n        \"log\",\n        \"node-coverage\",\n        \"test_uploads\",\n        \"uploads\",\n        \"xunit-test-results\",\n    ]\n    for sub_dir in sub_dirs:\n        path = os.path.join(var_dir, sub_dir)\n        os.makedirs(path, exist_ok=True)\n\n\ndef build_pygments_data_paths() -> List[str]:\n    paths = [\n        \"tools/setup/build_pygments_data\",\n        \"tools/setup/lang.json\",\n    ]\n    return paths\n\n\ndef build_timezones_data_paths() -> List[str]:\n    paths = [\n        \"tools/setup/build_timezone_values\",\n    ]\n    return paths\n\n\ndef compilemessages_paths() -> List[str]:\n    paths = [\"zerver/management/commands/compilemessages.py\"]\n    paths += glob.glob(\"locale/*/LC_MESSAGES/*.po\")\n    paths += glob.glob(\"locale/*/translations.json\")\n    return paths\n\n\ndef configure_rabbitmq_paths() -> List[str]:\n    paths = [\n        \"scripts/setup/configure-rabbitmq\",\n    ]\n    return paths\n\n\ndef setup_shell_profile(shell_profile: str) -> None:\n    shell_profile_path = os.path.expanduser(shell_profile)\n\n    def write_command(command: str) -> None:\n        if os.path.exists(shell_profile_path):\n            with open(shell_profile_path) as shell_profile_file:\n                lines = [line.strip() for line in shell_profile_file.readlines()]\n            if command not in lines:\n                with open(shell_profile_path, \"a+\") as shell_profile_file:\n                    shell_profile_file.writelines(command + \"\\n\")\n        else:\n            with open(shell_profile_path, \"w\") as shell_profile_file:\n                shell_profile_file.writelines(command + \"\\n\")\n\n    source_activate_command = \"source \" + os.path.join(VENV_PATH, \"bin\", \"activate\")\n    write_command(source_activate_command)\n    if os.path.exists(\"/srv/zulip\"):\n        write_command(\"cd /srv/zulip\")\n\n\ndef setup_bash_profile() -> None:\n    \"\"\"Select a bash profile file to add setup code to.\"\"\"\n\n    BASH_PROFILES = [\n        os.path.expanduser(p) for p in (\"~/.bash_profile\", \"~/.bash_login\", \"~/.profile\")\n    ]\n\n    def clear_old_profile() -> None:\n        # An earlier version of this script would output a fresh .bash_profile\n        # even though a .profile existed in the image used. As a convenience to\n        # existing developers (and, perhaps, future developers git-bisecting the\n        # provisioning scripts), check for this situation, and blow away the\n        # created .bash_profile if one is found.\n\n        BASH_PROFILE = BASH_PROFILES[0]\n        DOT_PROFILE = BASH_PROFILES[2]\n        OLD_PROFILE_TEXT = \"source /srv/zulip-py3-venv/bin/activate\\ncd /srv/zulip\\n\"\n\n        if os.path.exists(DOT_PROFILE):\n            try:\n                with open(BASH_PROFILE) as f:\n                    profile_contents = f.read()\n                if profile_contents == OLD_PROFILE_TEXT:\n                    os.unlink(BASH_PROFILE)\n            except FileNotFoundError:\n                pass\n\n    clear_old_profile()\n\n    for candidate_profile in BASH_PROFILES:\n        if os.path.exists(candidate_profile):\n            setup_shell_profile(candidate_profile)\n            break\n    else:\n        # no existing bash profile found; claim .bash_profile\n        setup_shell_profile(BASH_PROFILES[0])\n\n\ndef need_to_run_build_pygments_data() -> bool:\n    if not os.path.exists(\"web/generated/pygments_data.json\"):\n        return True\n\n    return is_digest_obsolete(\n        \"build_pygments_data_hash\",\n        build_pygments_data_paths(),\n        [pygments.__version__],\n    )\n\n\ndef need_to_run_build_timezone_data() -> bool:\n    if not os.path.exists(\"web/generated/timezones.json\"):\n        return True\n\n    return is_digest_obsolete(\n        \"build_timezones_data_hash\",\n        build_timezones_data_paths(),\n        [timezones_version],\n    )\n\n\ndef need_to_run_compilemessages() -> bool:\n    if not os.path.exists(\"locale/language_name_map.json\"):\n        # User may have cleaned their Git checkout.\n        print(\"Need to run compilemessages due to missing language_name_map.json\")\n        return True\n\n    return is_digest_obsolete(\n        \"last_compilemessages_hash\",\n        compilemessages_paths(),\n    )\n\n\ndef need_to_run_configure_rabbitmq(settings_list: List[str]) -> bool:\n    obsolete = is_digest_obsolete(\n        \"last_configure_rabbitmq_hash\",\n        configure_rabbitmq_paths(),\n        settings_list,\n    )\n\n    if obsolete:\n        return True\n\n    try:\n        from zerver.lib.queue import SimpleQueueClient\n\n        SimpleQueueClient()\n        return False\n    except Exception:\n        return True\n\n\ndef main(options: argparse.Namespace) -> int:\n    setup_bash_profile()\n    setup_shell_profile(\"~/.zprofile\")\n\n    # This needs to happen before anything that imports zproject.settings.\n    run([\"scripts/setup/generate_secrets.py\", \"--development\"])\n\n    create_var_directories()\n\n    # The `build_emoji` script requires `emoji-datasource` package\n    # which we install via npm; thus this step is after installing npm\n    # packages.\n    run([\"tools/setup/emoji/build_emoji\"])\n\n    # copy over static files from the zulip_bots package\n    generate_zulip_bots_static_files()\n\n    if options.is_force or need_to_run_build_pygments_data():\n        run([\"tools/setup/build_pygments_data\"])\n        write_new_digest(\n            \"build_pygments_data_hash\",\n            build_pygments_data_paths(),\n            [pygments.__version__],\n        )\n    else:\n        print(\"No need to run `tools/setup/build_pygments_data`.\")\n\n    if options.is_force or need_to_run_build_timezone_data():\n        run([\"tools/setup/build_timezone_values\"])\n        write_new_digest(\n            \"build_timezones_data_hash\",\n            build_timezones_data_paths(),\n            [timezones_version],\n        )\n    else:\n        print(\"No need to run `tools/setup/build_timezone_values`.\")\n\n    if not options.is_build_release_tarball_only:\n        # The following block is skipped when we just need the development\n        # environment to build a release tarball.\n\n        # Need to set up Django before using template_status\n        os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"zproject.settings\")\n        import django\n\n        django.setup()\n\n        from django.conf import settings\n\n        from zerver.lib.test_fixtures import (\n            DEV_DATABASE,\n            TEST_DATABASE,\n            destroy_leaked_test_databases,\n        )\n\n        assert settings.RABBITMQ_PASSWORD is not None\n        if options.is_force or need_to_run_configure_rabbitmq([settings.RABBITMQ_PASSWORD]):\n            run_as_root([\"scripts/setup/configure-rabbitmq\"])\n            write_new_digest(\n                \"last_configure_rabbitmq_hash\",\n                configure_rabbitmq_paths(),\n                [settings.RABBITMQ_PASSWORD],\n            )\n        else:\n            print(\"No need to run `scripts/setup/configure-rabbitmq.\")\n\n        dev_template_db_status = DEV_DATABASE.template_status()\n        if options.is_force or dev_template_db_status == \"needs_rebuild\":\n            run([\"tools/setup/postgresql-init-dev-db\"])\n            if options.skip_dev_db_build:\n                # We don't need to build the manual development\n                # database on continuous integration for running tests, so we can\n                # just leave it as a template db and save a minute.\n                #\n                # Important: We don't write a digest as that would\n                # incorrectly claim that we ran migrations.\n                pass\n            else:\n                run([\"tools/rebuild-dev-database\"])\n                DEV_DATABASE.write_new_db_digest()\n        elif dev_template_db_status == \"run_migrations\":\n            DEV_DATABASE.run_db_migrations()\n        elif dev_template_db_status == \"current\":\n            print(\"No need to regenerate the dev DB.\")\n\n        test_template_db_status = TEST_DATABASE.template_status()\n        if options.is_force or test_template_db_status == \"needs_rebuild\":\n            run([\"tools/setup/postgresql-init-test-db\"])\n            run([\"tools/rebuild-test-database\"])\n            TEST_DATABASE.write_new_db_digest()\n        elif test_template_db_status == \"run_migrations\":\n            TEST_DATABASE.run_db_migrations()\n        elif test_template_db_status == \"current\":\n            print(\"No need to regenerate the test DB.\")\n\n        if options.is_force or need_to_run_compilemessages():\n            run([\"./manage.py\", \"compilemessages\", \"--ignore=*\"])\n            write_new_digest(\n                \"last_compilemessages_hash\",\n                compilemessages_paths(),\n            )\n        else:\n            print(\"No need to run `manage.py compilemessages`.\")\n\n        destroyed = destroy_leaked_test_databases()\n        if destroyed:\n            print(f\"Dropped {destroyed} stale test databases!\")\n\n    clean_unused_caches.main(\n        argparse.Namespace(\n            threshold_days=6,\n            # The defaults here should match parse_cache_script_args in zulip_tools.py\n            dry_run=False,\n            verbose=False,\n            no_headings=True,\n        )\n    )\n\n    # Keeping this cache file around can cause eslint to throw\n    # random TypeErrors when new/updated dependencies are added\n    if os.path.isfile(\".eslintcache\"):\n        # Remove this block when\n        # https://github.com/eslint/eslint/issues/11639 is fixed\n        # upstream.\n        os.remove(\".eslintcache\")\n\n    # Clean up the root of the `var/` directory for various\n    # testing-related files that we have migrated to\n    # `var/<uuid>/test-backend`.\n    print(\"Cleaning var/ directory files...\")\n    var_paths = glob.glob(\"var/test*\")\n    var_paths.append(\"var/bot_avatar\")\n    for path in var_paths:\n        try:\n            if os.path.isdir(path):\n                shutil.rmtree(path)\n            else:\n                os.remove(path)\n        except FileNotFoundError:\n            pass\n\n    version_file = os.path.join(UUID_VAR_PATH, \"provision_version\")\n    print(f\"writing to {version_file}\\n\")\n    with open(version_file, \"w\") as f:\n        f.write(\".\".join(map(str, PROVISION_VERSION)) + \"\\n\")\n\n    print()\n    print(OKBLUE + \"Zulip development environment setup succeeded!\" + ENDC)\n    return 0\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--force\",\n        action=\"store_true\",\n        dest=\"is_force\",\n        help=\"Ignore all provisioning optimizations.\",\n    )\n\n    parser.add_argument(\n        \"--build-release-tarball-only\",\n        action=\"store_true\",\n        dest=\"is_build_release_tarball_only\",\n        help=\"Provision for test suite with production settings.\",\n    )\n\n    parser.add_argument(\n        \"--skip-dev-db-build\", action=\"store_true\", help=\"Don't run migrations on dev database.\"\n    )\n\n    options = parser.parse_args()\n    sys.exit(main(options))\n", "path": "tools/lib/provision_inner.py"}], "after_files": [{"content": "#!/usr/bin/env python3\nimport argparse\nimport glob\nimport os\nimport shutil\nimport sys\nfrom typing import List\n\nZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))\n\nsys.path.append(ZULIP_PATH)\nimport pygments\n\nfrom scripts.lib import clean_unused_caches\nfrom scripts.lib.zulip_tools import (\n    ENDC,\n    OKBLUE,\n    get_dev_uuid_var_path,\n    get_tzdata_zi,\n    is_digest_obsolete,\n    run,\n    run_as_root,\n    write_new_digest,\n)\nfrom tools.setup.generate_zulip_bots_static_files import generate_zulip_bots_static_files\nfrom version import PROVISION_VERSION\n\nVENV_PATH = \"/srv/zulip-py3-venv\"\nUUID_VAR_PATH = get_dev_uuid_var_path()\n\nwith get_tzdata_zi() as f:\n    line = f.readline()\n    assert line.startswith(\"# version \")\n    timezones_version = line[len(\"# version \") :]\n\n\ndef create_var_directories() -> None:\n    # create var/coverage, var/log, etc.\n    var_dir = os.path.join(ZULIP_PATH, \"var\")\n    sub_dirs = [\n        \"coverage\",\n        \"log\",\n        \"node-coverage\",\n        \"test_uploads\",\n        \"uploads\",\n        \"xunit-test-results\",\n    ]\n    for sub_dir in sub_dirs:\n        path = os.path.join(var_dir, sub_dir)\n        os.makedirs(path, exist_ok=True)\n\n\ndef build_pygments_data_paths() -> List[str]:\n    paths = [\n        \"tools/setup/build_pygments_data\",\n        \"tools/setup/lang.json\",\n    ]\n    return paths\n\n\ndef build_timezones_data_paths() -> List[str]:\n    paths = [\n        \"tools/setup/build_timezone_values\",\n    ]\n    return paths\n\n\ndef compilemessages_paths() -> List[str]:\n    paths = [\"zerver/management/commands/compilemessages.py\"]\n    paths += glob.glob(\"locale/*/LC_MESSAGES/*.po\")\n    paths += glob.glob(\"locale/*/translations.json\")\n    return paths\n\n\ndef configure_rabbitmq_paths() -> List[str]:\n    paths = [\n        \"scripts/setup/configure-rabbitmq\",\n    ]\n    return paths\n\n\ndef setup_shell_profile(shell_profile: str) -> None:\n    shell_profile_path = os.path.expanduser(shell_profile)\n\n    def write_command(command: str) -> None:\n        if os.path.exists(shell_profile_path):\n            with open(shell_profile_path) as shell_profile_file:\n                lines = [line.strip() for line in shell_profile_file.readlines()]\n            if command not in lines:\n                with open(shell_profile_path, \"a+\") as shell_profile_file:\n                    shell_profile_file.writelines(command + \"\\n\")\n        else:\n            with open(shell_profile_path, \"w\") as shell_profile_file:\n                shell_profile_file.writelines(command + \"\\n\")\n\n    # Check if the environment is one of the dedicated containers: Vagrant, Docker, Droplet or WSL2\n    WSL = os.path.exists(\"/proc/sys/fs/binfmt_misc/WSLInterop\")\n    vagrant = os.path.exists(\"/vagrant\")\n    docker = os.path.exists(\"/var/run/docker.sock\")\n    droplet = os.path.exists(\"/etc/digitalocean\")\n    # If the environment matches one of the containers, write the activation command to the user's bash profile\n    if WSL or vagrant or docker or droplet:\n        source_activate_command = \"source \" + os.path.join(VENV_PATH, \"bin\", \"activate\")\n        write_command(source_activate_command)\n\n    if os.path.exists(\"/srv/zulip\"):\n        write_command(\"cd /srv/zulip\")\n\n\ndef setup_bash_profile() -> None:\n    \"\"\"Select a bash profile file to add setup code to.\"\"\"\n\n    BASH_PROFILES = [\n        os.path.expanduser(p) for p in (\"~/.bash_profile\", \"~/.bash_login\", \"~/.profile\")\n    ]\n\n    def clear_old_profile() -> None:\n        # An earlier version of this script would output a fresh .bash_profile\n        # even though a .profile existed in the image used. As a convenience to\n        # existing developers (and, perhaps, future developers git-bisecting the\n        # provisioning scripts), check for this situation, and blow away the\n        # created .bash_profile if one is found.\n\n        BASH_PROFILE = BASH_PROFILES[0]\n        DOT_PROFILE = BASH_PROFILES[2]\n        OLD_PROFILE_TEXT = \"source /srv/zulip-py3-venv/bin/activate\\ncd /srv/zulip\\n\"\n\n        if os.path.exists(DOT_PROFILE):\n            try:\n                with open(BASH_PROFILE) as f:\n                    profile_contents = f.read()\n                if profile_contents == OLD_PROFILE_TEXT:\n                    os.unlink(BASH_PROFILE)\n            except FileNotFoundError:\n                pass\n\n    clear_old_profile()\n\n    for candidate_profile in BASH_PROFILES:\n        if os.path.exists(candidate_profile):\n            setup_shell_profile(candidate_profile)\n            break\n    else:\n        # no existing bash profile found; claim .bash_profile\n        setup_shell_profile(BASH_PROFILES[0])\n\n\ndef need_to_run_build_pygments_data() -> bool:\n    if not os.path.exists(\"web/generated/pygments_data.json\"):\n        return True\n\n    return is_digest_obsolete(\n        \"build_pygments_data_hash\",\n        build_pygments_data_paths(),\n        [pygments.__version__],\n    )\n\n\ndef need_to_run_build_timezone_data() -> bool:\n    if not os.path.exists(\"web/generated/timezones.json\"):\n        return True\n\n    return is_digest_obsolete(\n        \"build_timezones_data_hash\",\n        build_timezones_data_paths(),\n        [timezones_version],\n    )\n\n\ndef need_to_run_compilemessages() -> bool:\n    if not os.path.exists(\"locale/language_name_map.json\"):\n        # User may have cleaned their Git checkout.\n        print(\"Need to run compilemessages due to missing language_name_map.json\")\n        return True\n\n    return is_digest_obsolete(\n        \"last_compilemessages_hash\",\n        compilemessages_paths(),\n    )\n\n\ndef need_to_run_configure_rabbitmq(settings_list: List[str]) -> bool:\n    obsolete = is_digest_obsolete(\n        \"last_configure_rabbitmq_hash\",\n        configure_rabbitmq_paths(),\n        settings_list,\n    )\n\n    if obsolete:\n        return True\n\n    try:\n        from zerver.lib.queue import SimpleQueueClient\n\n        SimpleQueueClient()\n        return False\n    except Exception:\n        return True\n\n\ndef main(options: argparse.Namespace) -> int:\n    setup_bash_profile()\n    setup_shell_profile(\"~/.zprofile\")\n\n    # This needs to happen before anything that imports zproject.settings.\n    run([\"scripts/setup/generate_secrets.py\", \"--development\"])\n\n    create_var_directories()\n\n    # The `build_emoji` script requires `emoji-datasource` package\n    # which we install via npm; thus this step is after installing npm\n    # packages.\n    run([\"tools/setup/emoji/build_emoji\"])\n\n    # copy over static files from the zulip_bots package\n    generate_zulip_bots_static_files()\n\n    if options.is_force or need_to_run_build_pygments_data():\n        run([\"tools/setup/build_pygments_data\"])\n        write_new_digest(\n            \"build_pygments_data_hash\",\n            build_pygments_data_paths(),\n            [pygments.__version__],\n        )\n    else:\n        print(\"No need to run `tools/setup/build_pygments_data`.\")\n\n    if options.is_force or need_to_run_build_timezone_data():\n        run([\"tools/setup/build_timezone_values\"])\n        write_new_digest(\n            \"build_timezones_data_hash\",\n            build_timezones_data_paths(),\n            [timezones_version],\n        )\n    else:\n        print(\"No need to run `tools/setup/build_timezone_values`.\")\n\n    if not options.is_build_release_tarball_only:\n        # The following block is skipped when we just need the development\n        # environment to build a release tarball.\n\n        # Need to set up Django before using template_status\n        os.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"zproject.settings\")\n        import django\n\n        django.setup()\n\n        from django.conf import settings\n\n        from zerver.lib.test_fixtures import (\n            DEV_DATABASE,\n            TEST_DATABASE,\n            destroy_leaked_test_databases,\n        )\n\n        assert settings.RABBITMQ_PASSWORD is not None\n        if options.is_force or need_to_run_configure_rabbitmq([settings.RABBITMQ_PASSWORD]):\n            run_as_root([\"scripts/setup/configure-rabbitmq\"])\n            write_new_digest(\n                \"last_configure_rabbitmq_hash\",\n                configure_rabbitmq_paths(),\n                [settings.RABBITMQ_PASSWORD],\n            )\n        else:\n            print(\"No need to run `scripts/setup/configure-rabbitmq.\")\n\n        dev_template_db_status = DEV_DATABASE.template_status()\n        if options.is_force or dev_template_db_status == \"needs_rebuild\":\n            run([\"tools/setup/postgresql-init-dev-db\"])\n            if options.skip_dev_db_build:\n                # We don't need to build the manual development\n                # database on continuous integration for running tests, so we can\n                # just leave it as a template db and save a minute.\n                #\n                # Important: We don't write a digest as that would\n                # incorrectly claim that we ran migrations.\n                pass\n            else:\n                run([\"tools/rebuild-dev-database\"])\n                DEV_DATABASE.write_new_db_digest()\n        elif dev_template_db_status == \"run_migrations\":\n            DEV_DATABASE.run_db_migrations()\n        elif dev_template_db_status == \"current\":\n            print(\"No need to regenerate the dev DB.\")\n\n        test_template_db_status = TEST_DATABASE.template_status()\n        if options.is_force or test_template_db_status == \"needs_rebuild\":\n            run([\"tools/setup/postgresql-init-test-db\"])\n            run([\"tools/rebuild-test-database\"])\n            TEST_DATABASE.write_new_db_digest()\n        elif test_template_db_status == \"run_migrations\":\n            TEST_DATABASE.run_db_migrations()\n        elif test_template_db_status == \"current\":\n            print(\"No need to regenerate the test DB.\")\n\n        if options.is_force or need_to_run_compilemessages():\n            run([\"./manage.py\", \"compilemessages\", \"--ignore=*\"])\n            write_new_digest(\n                \"last_compilemessages_hash\",\n                compilemessages_paths(),\n            )\n        else:\n            print(\"No need to run `manage.py compilemessages`.\")\n\n        destroyed = destroy_leaked_test_databases()\n        if destroyed:\n            print(f\"Dropped {destroyed} stale test databases!\")\n\n    clean_unused_caches.main(\n        argparse.Namespace(\n            threshold_days=6,\n            # The defaults here should match parse_cache_script_args in zulip_tools.py\n            dry_run=False,\n            verbose=False,\n            no_headings=True,\n        )\n    )\n\n    # Keeping this cache file around can cause eslint to throw\n    # random TypeErrors when new/updated dependencies are added\n    if os.path.isfile(\".eslintcache\"):\n        # Remove this block when\n        # https://github.com/eslint/eslint/issues/11639 is fixed\n        # upstream.\n        os.remove(\".eslintcache\")\n\n    # Clean up the root of the `var/` directory for various\n    # testing-related files that we have migrated to\n    # `var/<uuid>/test-backend`.\n    print(\"Cleaning var/ directory files...\")\n    var_paths = glob.glob(\"var/test*\")\n    var_paths.append(\"var/bot_avatar\")\n    for path in var_paths:\n        try:\n            if os.path.isdir(path):\n                shutil.rmtree(path)\n            else:\n                os.remove(path)\n        except FileNotFoundError:\n            pass\n\n    version_file = os.path.join(UUID_VAR_PATH, \"provision_version\")\n    print(f\"writing to {version_file}\\n\")\n    with open(version_file, \"w\") as f:\n        f.write(\".\".join(map(str, PROVISION_VERSION)) + \"\\n\")\n\n    print()\n    print(OKBLUE + \"Zulip development environment setup succeeded!\" + ENDC)\n    return 0\n\n\nif __name__ == \"__main__\":\n    parser = argparse.ArgumentParser()\n    parser.add_argument(\n        \"--force\",\n        action=\"store_true\",\n        dest=\"is_force\",\n        help=\"Ignore all provisioning optimizations.\",\n    )\n\n    parser.add_argument(\n        \"--build-release-tarball-only\",\n        action=\"store_true\",\n        dest=\"is_build_release_tarball_only\",\n        help=\"Provision for test suite with production settings.\",\n    )\n\n    parser.add_argument(\n        \"--skip-dev-db-build\", action=\"store_true\", help=\"Don't run migrations on dev database.\"\n    )\n\n    options = parser.parse_args()\n    sys.exit(main(options))\n", "path": "tools/lib/provision_inner.py"}]}