prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  [Bug][ci] Deploy docs is busted
  See for example https://ci.tlcpack.ai/blue/organizations/jenkins/tvm/detail/main/4756/pipeline
  
  ```
  Traceback (most recent call last):
    File "./ci/scripts/jenkins/s3.py", line 134, in <module>
      chmod(files)
    File "./ci/scripts/jenkins/s3.py", line 70, in chmod
      SH.run(f"chmod +x {' '.join(to_chmod)}")
    File "/tmp/jenkins-ba6c252c/workspace/exec_0/tvm/deploy-docs/ci/scripts/jenkins/cmd_utils.py", line 78, in run
      return subprocess.run(cmd, **defaults)
    File "/usr/lib/python3.8/subprocess.py", line 493, in run
      with Popen(*popenargs, **kwargs) as process:
    File "/usr/lib/python3.8/subprocess.py", line 858, in __init__
      self._execute_child(args, executable, preexec_fn, close_fds,
    File "/usr/lib/python3.8/subprocess.py", line 1704, in _execute_child
      raise child_exception_type(errno_num, err_msg, err_filename)
  OSError: [Errno 7] Argument list too long: '/bin/sh'
  ```
  
  cc @Mousius @driazati @gigiblender
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `ci/scripts/jenkins/s3.py`
  Content:
  ```
  1 #!/usr/bin/env python3
  2 # Licensed to the Apache Software Foundation (ASF) under one
  3 # or more contributor license agreements.  See the NOTICE file
  4 # distributed with this work for additional information
  5 # regarding copyright ownership.  The ASF licenses this file
  6 # to you under the Apache License, Version 2.0 (the
  7 # "License"); you may not use this file except in compliance
  8 # with the License.  You may obtain a copy of the License at
  9 #
  10 #   http://www.apache.org/licenses/LICENSE-2.0
  11 #
  12 # Unless required by applicable law or agreed to in writing,
  13 # software distributed under the License is distributed on an
  14 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 # KIND, either express or implied.  See the License for the
  16 # specific language governing permissions and limitations
  17 # under the License.
  18 
  19 import argparse
  20 import logging
  21 import re
  22 from pathlib import Path
  23 from typing import List
  24 from enum import Enum
  25 
  26 from cmd_utils import Sh, REPO_ROOT, init_log
  27 
  28 RETRY_SCRIPT = REPO_ROOT / "ci" / "scripts" / "jenkins" / "retry.sh"
  29 S3_DOWNLOAD_REGEX = re.compile(r"download: s3://.* to (.*)")
  30 SH = Sh()
  31 
  32 
  33 class Action(Enum):
  34     UPLOAD = 1
  35     DOWNLOAD = 2
  36 
  37 
  38 def show_md5(item: str) -> None:
  39     if not Path(item).is_dir():
  40         sh.run(f"md5sum {item}")
  41 
  42 
  43 def parse_output_files(stdout: str) -> List[str]:
  44     """
  45     Grab the list of downloaded files from the output of 'aws s3 cp'. Lines look
  46     like:
  47 
  48         download: s3://some/prefix/a_file.txt to a_file.txt
  49     """
  50     files = []
  51     for line in stdout.split("\n"):
  52         line = line.strip()
  53         if line == "":
  54             continue
  55         m = S3_DOWNLOAD_REGEX.match(line)
  56         if m:
  57             files.append(m.groups()[0])
  58 
  59     return files
  60 
  61 
  62 def chmod(files: List[str]) -> None:
  63     """
  64     S3 has no concept of file permissions so add them back in here to every file
  65     """
  66     # Add execute bit for downloads
  67     to_chmod = [str(f) for f in files]
  68     logging.info(f"Adding execute bit for files: {to_chmod}")
  69     if len(to_chmod) > 0:
  70         SH.run(f"chmod +x {' '.join(to_chmod)}")
  71 
  72 
  73 def s3(source: str, destination: str, recursive: bool) -> List[str]:
  74     """
  75     Send or download the source to the destination in S3
  76     """
  77     cmd = f". {RETRY_SCRIPT.relative_to(REPO_ROOT)} && retry 3 aws s3 cp --no-progress"
  78 
  79     if recursive:
  80         cmd += " --recursive"
  81 
  82     cmd += f" {source} {destination}"
  83     _, stdout = SH.tee(cmd)
  84     return stdout
  85 
  86 
  87 if __name__ == "__main__":
  88     init_log()
  89     help = "Uploads or downloads files from S3"
  90     parser = argparse.ArgumentParser(description=help)
  91     parser.add_argument("--action", help="either 'upload' or 'download'", required=True)
  92     parser.add_argument("--bucket", help="s3 bucket", required=True)
  93     parser.add_argument(
  94         "--prefix", help="s3 bucket + tag (e.g. s3://tvm-ci-prod/PR-1234/cpu", required=True
  95     )
  96     parser.add_argument("--items", help="files and folders to upload", nargs="+")
  97 
  98     args = parser.parse_args()
  99     logging.info(args)
  100 
  101     sh = Sh()
  102 
  103     if Path.cwd() != REPO_ROOT:
  104         logging.error(f"s3.py can only be executed from the repo root, instead was in {Path.cwd()}")
  105         exit(1)
  106 
  107     prefix = args.prefix.strip("/")
  108     s3_path = f"s3://{args.bucket}/{prefix}"
  109     logging.info(f"Using s3 path: {s3_path}")
  110 
  111     if args.action == "upload":
  112         action = Action.UPLOAD
  113     elif args.action == "download":
  114         action = Action.DOWNLOAD
  115     else:
  116         logging.error(f"Unsupported action: {args.action}")
  117         exit(1)
  118 
  119     if args.items is None:
  120         if args.action == "upload":
  121             logging.error(f"Cannot upload without --items")
  122             exit(1)
  123         else:
  124             # Download the whole prefix
  125             items = ["."]
  126 
  127     else:
  128         items = args.items
  129 
  130     for item in items:
  131         if action == Action.DOWNLOAD:
  132             stdout = s3(source=s3_path, destination=item, recursive=True)
  133             files = parse_output_files(stdout)
  134             chmod(files)
  135             for file in files:
  136                 # Show md5 after downloading
  137                 show_md5(file)
  138         elif action == Action.UPLOAD:
  139             show_md5(item)
  140             s3(item, s3_path + "/" + item, recursive=Path(item).is_dir())
  141
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/ci/scripts/jenkins/s3.py b/ci/scripts/jenkins/s3.py
  --- a/ci/scripts/jenkins/s3.py
  +++ b/ci/scripts/jenkins/s3.py
  @@ -129,7 +129,12 @@
   
       for item in items:
           if action == Action.DOWNLOAD:
  -            stdout = s3(source=s3_path, destination=item, recursive=True)
  +            source = s3_path
  +            recursive = True
  +            if item != ".":
  +                source = s3_path + "/" + item
  +                recursive = False
  +            stdout = s3(source=source, destination=item, recursive=recursive)
               files = parse_output_files(stdout)
               chmod(files)
               for file in files:
  
verification_info: {"golden_diff": "diff --git a/ci/scripts/jenkins/s3.py b/ci/scripts/jenkins/s3.py\n--- a/ci/scripts/jenkins/s3.py\n+++ b/ci/scripts/jenkins/s3.py\n@@ -129,7 +129,12 @@\n \n     for item in items:\n         if action == Action.DOWNLOAD:\n-            stdout = s3(source=s3_path, destination=item, recursive=True)\n+            source = s3_path\n+            recursive = True\n+            if item != \".\":\n+                source = s3_path + \"/\" + item\n+                recursive = False\n+            stdout = s3(source=source, destination=item, recursive=recursive)\n             files = parse_output_files(stdout)\n             chmod(files)\n             for file in files:\n", "issue": "[Bug][ci] Deploy docs is busted\nSee for example https://ci.tlcpack.ai/blue/organizations/jenkins/tvm/detail/main/4756/pipeline\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"./ci/scripts/jenkins/s3.py\", line 134, in <module>\r\n    chmod(files)\r\n  File \"./ci/scripts/jenkins/s3.py\", line 70, in chmod\r\n    SH.run(f\"chmod +x {' '.join(to_chmod)}\")\r\n  File \"/tmp/jenkins-ba6c252c/workspace/exec_0/tvm/deploy-docs/ci/scripts/jenkins/cmd_utils.py\", line 78, in run\r\n    return subprocess.run(cmd, **defaults)\r\n  File \"/usr/lib/python3.8/subprocess.py\", line 493, in run\r\n    with Popen(*popenargs, **kwargs) as process:\r\n  File \"/usr/lib/python3.8/subprocess.py\", line 858, in __init__\r\n    self._execute_child(args, executable, preexec_fn, close_fds,\r\n  File \"/usr/lib/python3.8/subprocess.py\", line 1704, in _execute_child\r\n    raise child_exception_type(errno_num, err_msg, err_filename)\r\nOSError: [Errno 7] Argument list too long: '/bin/sh'\r\n```\n\ncc @Mousius @driazati @gigiblender\n", "before_files": [{"content": "#!/usr/bin/env python3\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport argparse\nimport logging\nimport re\nfrom pathlib import Path\nfrom typing import List\nfrom enum import Enum\n\nfrom cmd_utils import Sh, REPO_ROOT, init_log\n\nRETRY_SCRIPT = REPO_ROOT / \"ci\" / \"scripts\" / \"jenkins\" / \"retry.sh\"\nS3_DOWNLOAD_REGEX = re.compile(r\"download: s3://.* to (.*)\")\nSH = Sh()\n\n\nclass Action(Enum):\n    UPLOAD = 1\n    DOWNLOAD = 2\n\n\ndef show_md5(item: str) -> None:\n    if not Path(item).is_dir():\n        sh.run(f\"md5sum {item}\")\n\n\ndef parse_output_files(stdout: str) -> List[str]:\n    \"\"\"\n    Grab the list of downloaded files from the output of 'aws s3 cp'. Lines look\n    like:\n\n        download: s3://some/prefix/a_file.txt to a_file.txt\n    \"\"\"\n    files = []\n    for line in stdout.split(\"\\n\"):\n        line = line.strip()\n        if line == \"\":\n            continue\n        m = S3_DOWNLOAD_REGEX.match(line)\n        if m:\n            files.append(m.groups()[0])\n\n    return files\n\n\ndef chmod(files: List[str]) -> None:\n    \"\"\"\n    S3 has no concept of file permissions so add them back in here to every file\n    \"\"\"\n    # Add execute bit for downloads\n    to_chmod = [str(f) for f in files]\n    logging.info(f\"Adding execute bit for files: {to_chmod}\")\n    if len(to_chmod) > 0:\n        SH.run(f\"chmod +x {' '.join(to_chmod)}\")\n\n\ndef s3(source: str, destination: str, recursive: bool) -> List[str]:\n    \"\"\"\n    Send or download the source to the destination in S3\n    \"\"\"\n    cmd = f\". {RETRY_SCRIPT.relative_to(REPO_ROOT)} && retry 3 aws s3 cp --no-progress\"\n\n    if recursive:\n        cmd += \" --recursive\"\n\n    cmd += f\" {source} {destination}\"\n    _, stdout = SH.tee(cmd)\n    return stdout\n\n\nif __name__ == \"__main__\":\n    init_log()\n    help = \"Uploads or downloads files from S3\"\n    parser = argparse.ArgumentParser(description=help)\n    parser.add_argument(\"--action\", help=\"either 'upload' or 'download'\", required=True)\n    parser.add_argument(\"--bucket\", help=\"s3 bucket\", required=True)\n    parser.add_argument(\n        \"--prefix\", help=\"s3 bucket + tag (e.g. s3://tvm-ci-prod/PR-1234/cpu\", required=True\n    )\n    parser.add_argument(\"--items\", help=\"files and folders to upload\", nargs=\"+\")\n\n    args = parser.parse_args()\n    logging.info(args)\n\n    sh = Sh()\n\n    if Path.cwd() != REPO_ROOT:\n        logging.error(f\"s3.py can only be executed from the repo root, instead was in {Path.cwd()}\")\n        exit(1)\n\n    prefix = args.prefix.strip(\"/\")\n    s3_path = f\"s3://{args.bucket}/{prefix}\"\n    logging.info(f\"Using s3 path: {s3_path}\")\n\n    if args.action == \"upload\":\n        action = Action.UPLOAD\n    elif args.action == \"download\":\n        action = Action.DOWNLOAD\n    else:\n        logging.error(f\"Unsupported action: {args.action}\")\n        exit(1)\n\n    if args.items is None:\n        if args.action == \"upload\":\n            logging.error(f\"Cannot upload without --items\")\n            exit(1)\n        else:\n            # Download the whole prefix\n            items = [\".\"]\n\n    else:\n        items = args.items\n\n    for item in items:\n        if action == Action.DOWNLOAD:\n            stdout = s3(source=s3_path, destination=item, recursive=True)\n            files = parse_output_files(stdout)\n            chmod(files)\n            for file in files:\n                # Show md5 after downloading\n                show_md5(file)\n        elif action == Action.UPLOAD:\n            show_md5(item)\n            s3(item, s3_path + \"/\" + item, recursive=Path(item).is_dir())\n", "path": "ci/scripts/jenkins/s3.py"}], "after_files": [{"content": "#!/usr/bin/env python3\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n\nimport argparse\nimport logging\nimport re\nfrom pathlib import Path\nfrom typing import List\nfrom enum import Enum\n\nfrom cmd_utils import Sh, REPO_ROOT, init_log\n\nRETRY_SCRIPT = REPO_ROOT / \"ci\" / \"scripts\" / \"jenkins\" / \"retry.sh\"\nS3_DOWNLOAD_REGEX = re.compile(r\"download: s3://.* to (.*)\")\nSH = Sh()\n\n\nclass Action(Enum):\n    UPLOAD = 1\n    DOWNLOAD = 2\n\n\ndef show_md5(item: str) -> None:\n    if not Path(item).is_dir():\n        sh.run(f\"md5sum {item}\")\n\n\ndef parse_output_files(stdout: str) -> List[str]:\n    \"\"\"\n    Grab the list of downloaded files from the output of 'aws s3 cp'. Lines look\n    like:\n\n        download: s3://some/prefix/a_file.txt to a_file.txt\n    \"\"\"\n    files = []\n    for line in stdout.split(\"\\n\"):\n        line = line.strip()\n        if line == \"\":\n            continue\n        m = S3_DOWNLOAD_REGEX.match(line)\n        if m:\n            files.append(m.groups()[0])\n\n    return files\n\n\ndef chmod(files: List[str]) -> None:\n    \"\"\"\n    S3 has no concept of file permissions so add them back in here to every file\n    \"\"\"\n    # Add execute bit for downloads\n    to_chmod = [str(f) for f in files]\n    logging.info(f\"Adding execute bit for files: {to_chmod}\")\n    if len(to_chmod) > 0:\n        SH.run(f\"chmod +x {' '.join(to_chmod)}\")\n\n\ndef s3(source: str, destination: str, recursive: bool) -> List[str]:\n    \"\"\"\n    Send or download the source to the destination in S3\n    \"\"\"\n    cmd = f\". {RETRY_SCRIPT.relative_to(REPO_ROOT)} && retry 3 aws s3 cp --no-progress\"\n\n    if recursive:\n        cmd += \" --recursive\"\n\n    cmd += f\" {source} {destination}\"\n    _, stdout = SH.tee(cmd)\n    return stdout\n\n\nif __name__ == \"__main__\":\n    init_log()\n    help = \"Uploads or downloads files from S3\"\n    parser = argparse.ArgumentParser(description=help)\n    parser.add_argument(\"--action\", help=\"either 'upload' or 'download'\", required=True)\n    parser.add_argument(\"--bucket\", help=\"s3 bucket\", required=True)\n    parser.add_argument(\n        \"--prefix\", help=\"s3 bucket + tag (e.g. s3://tvm-ci-prod/PR-1234/cpu\", required=True\n    )\n    parser.add_argument(\"--items\", help=\"files and folders to upload\", nargs=\"+\")\n\n    args = parser.parse_args()\n    logging.info(args)\n\n    sh = Sh()\n\n    if Path.cwd() != REPO_ROOT:\n        logging.error(f\"s3.py can only be executed from the repo root, instead was in {Path.cwd()}\")\n        exit(1)\n\n    prefix = args.prefix.strip(\"/\")\n    s3_path = f\"s3://{args.bucket}/{prefix}\"\n    logging.info(f\"Using s3 path: {s3_path}\")\n\n    if args.action == \"upload\":\n        action = Action.UPLOAD\n    elif args.action == \"download\":\n        action = Action.DOWNLOAD\n    else:\n        logging.error(f\"Unsupported action: {args.action}\")\n        exit(1)\n\n    if args.items is None:\n        if args.action == \"upload\":\n            logging.error(f\"Cannot upload without --items\")\n            exit(1)\n        else:\n            # Download the whole prefix\n            items = [\".\"]\n\n    else:\n        items = args.items\n\n    for item in items:\n        if action == Action.DOWNLOAD:\n            source = s3_path\n            recursive = True\n            if item != \".\":\n                source = s3_path + \"/\" + item\n                recursive = False\n            stdout = s3(source=source, destination=item, recursive=recursive)\n            files = parse_output_files(stdout)\n            chmod(files)\n            for file in files:\n                # Show md5 after downloading\n                show_md5(file)\n        elif action == Action.UPLOAD:\n            show_md5(item)\n            s3(item, s3_path + \"/\" + item, recursive=Path(item).is_dir())\n", "path": "ci/scripts/jenkins/s3.py"}]}