/ scripts / fix_version.py
fix_version.py
  1  # SPDX-License-Identifier: MIT
  2  #
  3  # Copyright (c) 2021 The Anvil Extras project team members listed at
  4  # https://github.com/anvilistas/anvil-extras/graphs/contributors
  5  #
  6  # This software is published at https://github.com/anvilistas/anvil-extras
  7  
  8  import argparse
  9  import logging
 10  import pathlib
 11  import re
 12  from configparser import ConfigParser
 13  
 14  logging.basicConfig(
 15      level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
 16  )
 17  
 18  
 19  def read_bumpversion_config(config_file):
 20      config = ConfigParser()
 21      config.read(config_file)
 22      current_version = config.get("bumpversion", "current_version")
 23  
 24      patterns = []
 25      for section in config.sections():
 26          if section.startswith("bumpversion:glob:"):
 27              pattern = section[len("bumpversion:glob:") :]
 28              patterns.append(pattern)
 29  
 30      glob_patterns = patterns or ["**/*.py"]
 31      return current_version, glob_patterns
 32  
 33  
 34  def update_version_in_file(file_path, current_version):
 35      with open(file_path, "r") as file:
 36          content = file.read()
 37  
 38      logging.info(f"  Updating version in file: {file_path}")
 39  
 40      version_line = f'__version__ = "{current_version}"'
 41  
 42      if re.search(r'^__version__\s*=\s*"[^"]+"', content, flags=re.MULTILINE):
 43          logging.info("    Version found")
 44          updated_content = re.sub(
 45              r'^__version__\s*=\s*"[^"]+"', version_line, content, flags=re.MULTILINE
 46          )
 47  
 48      else:
 49          logging.info("    No version found")
 50          lines = content.splitlines()
 51          insert_position = 0
 52  
 53          for i, line in enumerate(lines):
 54              if (
 55                  not line
 56                  or line.startswith("#")
 57                  or line.startswith("import")
 58                  or line.startswith("from")
 59              ):
 60                  insert_position = i + 1
 61              else:
 62                  break
 63  
 64          lines.insert(insert_position, version_line + "\n")
 65          updated_content = "\n".join(lines)
 66  
 67      if content != updated_content:
 68          with open(file_path, "w") as file:
 69              file.write(updated_content)
 70              logging.info(f"Updated version in {file_path}")
 71  
 72  
 73  def process_files_or_directory(path, current_version, all_files):
 74      path = pathlib.Path(path).resolve()
 75  
 76      if path.is_file():
 77          logging.info(f"Processing file: {path}")
 78          if str(path) in all_files:
 79              update_version_in_file(str(path), current_version)
 80      elif path.is_dir():
 81          logging.info(f"Processing dir: {path}")
 82          for file_path in path.glob("**/*.py"):
 83              if str(file_path) not in all_files:
 84                  continue
 85              update_version_in_file(str(file_path), current_version)
 86      else:
 87          logging.info(f"Error: {path} is not a file or directory")
 88  
 89  
 90  if __name__ == "__main__":
 91      parser = argparse.ArgumentParser()
 92      parser.add_argument("paths", nargs="*", help="Files or directories to process")
 93      parser.add_argument(
 94          "--config",
 95          default=".bumpversion.cfg",
 96          help="Path to the bumpversion configuration file",
 97      )
 98      args = parser.parse_args()
 99  
100      config_file = args.config
101      current_version, glob_patterns = read_bumpversion_config(config_file)
102      # Use the current working directory as the root directory
103      root_dir = pathlib.Path.cwd()
104  
105      all_files = [str(p) for pattern in glob_patterns for p in root_dir.glob(pattern)]
106  
107      for path in args.paths:
108          process_files_or_directory(
109              path, current_version=current_version, all_files=all_files
110          )