Snap for 9587611 from a56945eb52ea48e7a58f036f10b732459941de9b to tm-qpr3-release

Change-Id: Ic17827c974b7db412d6b7713c0e5c63f8bad786e
diff --git a/prepare_upgrade.py b/prepare_upgrade.py
new file mode 100755
index 0000000..cccd909
--- /dev/null
+++ b/prepare_upgrade.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python3
+
+#
+# Copyright 2023, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Script to prepare an update to a new version of ktfmt."""
+
+import subprocess
+import os
+import sys
+import re
+import shutil
+import argparse
+import textwrap
+
+tmp_dir = "/tmp/ktfmt"
+zip_path = os.path.join(tmp_dir, "common.zip")
+jar_path = os.path.join(tmp_dir, "framework/ktfmt.jar")
+copy_path = os.path.join(tmp_dir, "copy.jar")
+
+
+def main():
+  parser = argparse.ArgumentParser(
+      description="Prepare a repository for the upgrade of ktfmt to a new version."
+  )
+  parser.add_argument(
+      "--build_id",
+      required=True,
+      help="The build ID of aosp-build-tools-release with the new version of ktfmt"
+  )
+  parser.add_argument(
+      "--bug_id",
+      required=True,
+      help="The bug ID associated to each CL generated by this tool")
+  parser.add_argument(
+      "--repo",
+      required=True,
+      help="The relative path of the repository to upgrade, e.g. 'frameworks/base/'"
+  )
+  args = parser.parse_args()
+
+  build_id = args.build_id
+  bug_id = args.bug_id
+  repo_relative_path = args.repo
+
+  build_top = os.environ["ANDROID_BUILD_TOP"]
+  repo_absolute_path = os.path.join(build_top, repo_relative_path)
+
+  print("Preparing upgrade of ktfmt from build", build_id)
+  os.chdir(repo_absolute_path)
+  check_workspace_clean()
+  check_branches()
+
+  print("Downloading ktfmt.jar from aosp-build-tools-release")
+  download_jar(build_id)
+
+  print(f"Creating local branch ktfmt_update1")
+  run_cmd(["repo", "start", "ktfmt_update1"])
+
+  includes_file = find_includes_file(repo_relative_path)
+  if includes_file:
+    update_includes_file(build_top, includes_file, bug_id)
+  else:
+    print("No includes file found, skipping first CL")
+
+  print(f"Creating local branch ktfmt_update2")
+  run_cmd(["repo", "start", "--head", "ktfmt_update2"])
+  format_files(build_top, includes_file, repo_absolute_path, bug_id)
+
+  print("Done. You can now submit the generated CL(s), if any.")
+
+
+def run_cmd(cmd):
+  result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  if result.returncode != 0:
+    print("Error running command: {}".format(" ".join(cmd)))
+    print("Output: {}".format(result.stderr.decode()))
+    sys.exit(1)
+  return result.stdout.decode("utf-8")
+
+
+def is_workspace_clean():
+  return run_cmd(["git", "status", "--porcelain"]) == ""
+
+
+def check_workspace_clean():
+  if not is_workspace_clean():
+    print(
+        "The current repository contains uncommitted changes, please run this script in a clean workspace"
+    )
+    sys.exit(1)
+
+
+def check_branches():
+  result = run_cmd(["git", "branch"])
+  if "ktfmt_update1" in result or "ktfmt_update2" in result:
+    print(
+        "Branches ktfmt_update1 or ktfmt_update2 already exist, you should delete them before running this script"
+    )
+    sys.exit(1)
+
+
+def download_jar(build_id):
+  cmd = [
+      "/google/data/ro/projects/android/fetch_artifact", "--branch",
+      "aosp-build-tools-release", "--bid", build_id, "--target", "linux",
+      "build-common-prebuilts.zip", zip_path
+  ]
+  run_cmd(cmd)
+  cmd = ["unzip", "-q", "-o", "-d", tmp_dir, zip_path]
+  run_cmd(cmd)
+
+  if not os.path.isfile(jar_path):
+    print("Error: {} is not readable".format(jar_path))
+    sys.exit(1)
+
+
+def find_includes_file(repo_relative_path):
+  with open("PREUPLOAD.cfg") as f:
+    includes_line = [line for line in f if "ktfmt.py" in line][0].split(" ")
+    if "-i" not in includes_line:
+      return None
+
+    index = includes_line.index("-i") + 1
+    includes_file = includes_line[index][len("${REPO_ROOT}/") +
+                                         len(repo_relative_path):]
+    if not os.path.isfile(includes_file):
+      print("Error: {} does not exist or is not a file".format(includes_file))
+      sys.exit(1)
+    return includes_file
+
+
+def get_included_folders(includes_file):
+  with open(includes_file) as f:
+    return [line[1:] for line in f.read().splitlines() if line.startswith("+")]
+
+
+def update_includes_file(build_top, includes_file, bug_id):
+  included_folders = get_included_folders(includes_file)
+  cmd = [
+      f"{build_top}/external/ktfmt/generate_includes_file.py",
+      f"--output={includes_file}"
+  ] + included_folders
+  print(f"Updating {includes_file} with the command: {cmd}")
+  run_cmd(cmd)
+
+  if is_workspace_clean():
+    print(f"No change were made to {includes_file}, skipping first CL")
+  else:
+    print(f"Creating first CL with update of {includes_file}")
+    create_first_cl(bug_id)
+
+
+def create_first_cl(bug_id):
+  sha1sum = get_sha1sum(jar_path)
+  change_id = f"I{sha1sum}"
+  command = " ".join(sys.argv)
+  cl_message = textwrap.dedent(f"""
+  Regenerate include file for ktfmt upgrade
+
+  This CL was generated automatically from the following command:
+
+  $ {command}
+
+  This CL regenerates the inclusion file with the current version of ktfmt
+  so that it is up-to-date with files currently formatted or ignored by
+  ktfmt.
+
+  Bug: {bug_id}
+  Test: Presubmits
+  Change-Id: {change_id}
+  Merged-In: {change_id}
+  """)
+
+  run_cmd(["git", "add", "--all"])
+  run_cmd(["git", "commit", "-m", cl_message])
+
+
+def get_sha1sum(file):
+  output = run_cmd(["sha1sum", file])
+  regex = re.compile(r"[a-f0-9]{40}")
+  match = regex.search(output)
+  if not match:
+    print(f"sha1sum not found in output: {output}")
+    sys.exit(1)
+  return match.group()
+
+
+def format_files(build_top, includes_file, repo_absolute_path, bug_id):
+  if (includes_file):
+    included_folders = get_included_folders(includes_file)
+    cmd = [
+        f"{build_top}/external/ktfmt/ktfmt.py", "-i", includes_file, "--jar",
+        jar_path
+    ] + included_folders
+  else:
+    cmd = [
+        f"{build_top}/external/ktfmt/ktfmt.py", "--jar", jar_path,
+        repo_absolute_path
+    ]
+
+  print(
+      f"Formatting the files that are already formatted with the command: {cmd}"
+  )
+  run_cmd(cmd)
+
+  if is_workspace_clean():
+    print("All files were already properly formatted, skipping second CL")
+  else:
+    print("Creating second CL that formats all files")
+    create_second_cl(bug_id)
+
+
+def create_second_cl(bug_id):
+  # Append 'ktfmt_update' at the end of a copy of the jar file to get
+  # a different sha1sum.
+  shutil.copyfile(jar_path, copy_path)
+  with open(copy_path, "a") as file_object:
+    file_object.write("ktfmt_update")
+
+  sha1sum = get_sha1sum(copy_path)
+  change_id = f"I{sha1sum}"
+  command = " ".join(sys.argv)
+  cl_message = textwrap.dedent(f"""
+  Format files with the upcoming version of ktfmt
+
+  This CL was generated automatically from the following command:
+
+  $ {command}
+
+  This CL formats all files already correctly formatted with the upcoming
+  version of ktfmt.
+
+  Bug: {bug_id}
+  Test: Presubmits
+  Change-Id: {change_id}
+  Merged-In: {change_id}
+  """)
+
+  run_cmd(["git", "add", "--all"])
+  run_cmd(["git", "commit", "-m", cl_message])
+
+
+if __name__ == "__main__":
+  main()
diff --git a/prepare_upgrade.sh b/prepare_upgrade.sh
deleted file mode 100755
index 5ee6f7f..0000000
--- a/prepare_upgrade.sh
+++ /dev/null
@@ -1,151 +0,0 @@
-# Copyright (C) 2023 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# This script prepares the update to a new ktfmt version in a single git
-# repository. This will create max 2 CLs:
-#   1. One that regenerates the ktfmt_includes.txt with the current version
-#      of ktfmt, so that all files already properly formatted are checked
-#      and files that are not already formatted are ignored. If the file is
-#      already up-to-date, then this CL won't be created.
-#   2. One that reformats all files with the new version of ktfmt. This CL
-#      won't be created if all files are already formatted.
-
-# Stop the script at the first error.
-set -e
-
-# Stop the script if we try to access a variable that does not exist.
-set -u
-
-# Fail if we didn't pass the correct args.
-[ $# -ne 3 ] && {
-    echo "Usage: $0 build_tools_id relative/repo/path/ bug_id"
-    echo "Example: prepare_upgrade.sh 8932651 frameworks/base/ 266197805"
-    exit 1
-}
-
-build_id=$1
-repo_relative_path=$2
-bug_id=$3
-repo_absolute_path="${ANDROID_BUILD_TOP}/$repo_relative_path"
-
-echo "Preparing upgrade of ktfmt from build $build_id"
-
-cd $repo_absolute_path
-
-# Check that the ktfmt_update1 and ktfmt_update2 local branches don't exist yet.
-existing_branches=$(git branch | grep 'ktfmt_update1\|ktfmt_update2' || echo "")
-[[ ! -z "$existing_branches" ]] && {
-    echo "Branches ktfmt_update1 or ktfmt_update2 already exist, you should delete them before running this script"
-    exit 1
-}
-
-# Fail if the workspace is not clean.
-git_status=$(git status --porcelain)
-[[ ! -z "$git_status" ]] && {
-    echo "The current repository contains uncommitted changes, please run this script in a clean workspace"
-    exit 1
-}
-
-echo "Downloading ktfmt.jar from aosp-build-tools-release"
-tmp=/tmp/ktfmt
-mkdir -p $tmp
-zip_path="$tmp/common.zip"
-/google/data/ro/projects/android/fetch_artifact --branch aosp-build-tools-release --bid $build_id --target linux build-common-prebuilts.zip $zip_path
-unzip -q -o -d $tmp $zip_path
-new_jar="$tmp/framework/ktfmt.jar"
-[ -r "$new_jar" ] || (echo "Error: $new_jar is not readable" && exit 1)
-echo "Extracted jar in $new_jar"
-
-# Find the ktfmt_includes.txt and the folders/files that were included file
-includes_file=$(cat PREUPLOAD.cfg | grep ktfmt.py | sed 's/.*-i \(.*\) .*/\1/' | cut -c 14-)
-includes_file=${includes_file#"$repo_relative_path"}
-[ -r "$includes_file" ] || (echo "Error: $includes_file is not readable" && exit 1)
-included_folders=$(cat ktfmt_includes.txt | grep + | cut -c 2- | tr '\n' ' ')
-
-echo "Creating local branch ktfmt_update1"
-repo start ktfmt_update1
-
-echo "Updating $includes_file with the command: \$ANDROID_BUILD_TOP/external/ktfmt/generate_includes_file.py --output=$includes_file $included_folders"
-$ANDROID_BUILD_TOP/external/ktfmt/generate_includes_file.py --output=$includes_file $included_folders
-
-git_status=$(git status --porcelain)
-if [[ ! -z "$git_status" ]]
-then
-    echo "Creating first CL with update of $includes_file"
-
-    # Use the sha1 of the jar file for change ID.
-    change_id="I$(sha1sum $new_jar | sed 's/\([a-z0-9]\{40\}\) .*/\1/')"
-    cl_message=$(cat << EOF
-Regenerate include file for ktfmt upgrade
-
-This CL was generated automatically from the following command:
-
-$ $0 $@
-
-This CL regenerates the inclusion file with the current version of ktfmt
-so that it is up-to-date with files currently formatted or ignored by
-ktfmt.
-
-Bug: $bug_id
-Test: Presubmits
-Change-Id: $change_id
-Merged-In: $change_id
-EOF
-)
-    git add --all
-    git commit -m "$cl_message"
-else
-    echo "No change were made to $includes_file, skipping first CL"
-fi
-
-echo "Creating local branch ktfmt_update2"
-repo start --head ktfmt_update2
-
-echo "Formatting the files that are already formatted with the command: \${ANDROID_BUILD_TOP}/external/ktfmt/ktfmt.py -i $includes_file --jar $new_jar $included_folders"
-${ANDROID_BUILD_TOP}/external/ktfmt/ktfmt.py -i $includes_file --jar $new_jar $included_folders 2> /dev/null
-
-git_status=$(git status --porcelain)
-if [[ ! -z "$git_status" ]]
-then
-    echo "Creating second CL that format all files"
-
-    # Use the sha1 of the jar file + some random salt for change ID.
-    jar_copy=/tmp/ktfmt/copy.jar
-    cp $new_jar $jar_copy
-    echo "ktfmt_update" >> $jar_copy
-    change_id="I$(sha1sum $jar_copy | sed 's/\([a-z0-9]\{40\}\) .*/\1/')"
-    cl_message=$(cat << EOF
-Format files with the upcoming version of ktfmt
-
-This CL was generated automatically from the following command:
-
-$ $0 $@
-
-This CL formats all files already correctly formatted with the upcoming
-version of ktfmt.
-
-Bug: $bug_id
-Test: Presubmits
-Change-Id: $change_id
-Merged-In: $change_id
-EOF
-)
-    git add --all
-    git commit -m "$cl_message"
-else
-    echo "All files were already properly formatted, skipping second CL"
-fi
-
-echo
-echo "Done. You can now submit the generated CL(s), if any."