1
0

setup_versions.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. # Copyright 2021-2025 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. # --------------------------------------------------------------------------------------------------
  12. # Checks that build version matches package(s) version.
  13. #
  14. # Invoked from the workflow in build-and-release.yml.
  15. #
  16. # Outputs a line for each package (all packages if 'all'):
  17. # <package_short_name>_VERSION=<release_version>
  18. # the release version of the package that gets built.
  19. # - if 'dev' release mode, that would be M.m.p.dev<x>
  20. # where dev<x> is the first available available release version number that has no release yet.
  21. # - if 'production' release mode, that would be M.m.p, as read in the packages's version.json
  22. # file.
  23. # If a 'production' release mode is requested, a similar line is issued indicating the next patch
  24. # version number:
  25. # NEXT_<package_short_name>_VERSION=<next_release_version>
  26. # --------------------------------------------------------------------------------------------------
  27. import argparse
  28. import os
  29. from common import Git, Package, Version, fetch_github_releases, fetch_latest_github_taipy_releases
  30. def __setup_dev_version(
  31. package: Package, version: Version, released_versions: list[Version], target_version: dict[str, Version]
  32. ) -> None:
  33. # Find latest dev release for that version
  34. ext_index = 0
  35. latest_version = (
  36. max([v for v in released_versions if v.matches(version) and v.validate_extension()])
  37. if released_versions
  38. else None
  39. )
  40. ext, ext_index = ("dev", 0)
  41. if latest_version:
  42. ext, ext_index = latest_version.split_ext()
  43. ext_index += 1
  44. target_version[package.short_name] = Version(version.major, version.minor, version.patch, f"{ext}{ext_index}")
  45. def __setup_prod_version(
  46. package: Package,
  47. version: Version,
  48. branch_name: str,
  49. target_versions: dict[str, Version],
  50. next_versions: dict[str, Version],
  51. ) -> None:
  52. # Production releases can only be performed from a release branch
  53. if (os.environ.get("GITHUB_ACTIONS") == "true") and (
  54. target_branch_name := f"release/{version.major}.{version.minor}"
  55. ) != branch_name:
  56. raise ValueError(f"Current branch '{branch_name}' does not match expected '{target_branch_name}'")
  57. target_versions[package.short_name] = version
  58. # Compute next patch version
  59. next_versions[package.short_name] = Version(version.major, version.minor, version.patch + 1)
  60. def main():
  61. parser = argparse.ArgumentParser(
  62. description="Computes the Taipy package versions to be build.", formatter_class=argparse.RawTextHelpFormatter
  63. )
  64. # <package> argument
  65. def _check_package(value: str) -> str:
  66. n_value = value.lower()
  67. if n_value in Package.names(True) or value == "all":
  68. return n_value
  69. raise argparse.ArgumentTypeError(f"'{value}' is not a valid Taipy package name.")
  70. parser.add_argument(
  71. "package",
  72. type=_check_package,
  73. action="store",
  74. help="""The name of the package to setup the build version for.
  75. This should be the short name of a Taipy package (common, core...) or 'taipy'.
  76. If can also be set to 'ALL' then all versions for all packages are computed.
  77. """,
  78. )
  79. # <version> argument
  80. parser.add_argument(
  81. "-v",
  82. "--version",
  83. type=Version.check_argument,
  84. required=True,
  85. help="""Full name of the target version (M.m.p).
  86. This version must match the one in the package's 'version.json' file.
  87. """,
  88. )
  89. # <release_type> argument
  90. parser.add_argument(
  91. "-t",
  92. "--release_type",
  93. choices=["dev", "production"],
  94. default="dev",
  95. type=str.lower,
  96. help="""Type of release to build (default: dev).
  97. If 'dev', the release version is computed from the existing released packages versions
  98. in the repository:
  99. - If there is no release with version <version>, the release will have the version set
  100. to <version>.dev0.
  101. - If there is a <version>.dev<n> release, the release will have the version <version>.dev<n+1>.
  102. - If there is a <version> release (with no 'dev' part), the script fails.
  103. If 'production', the package version is computed from for existing released packages versions
  104. """,
  105. )
  106. # <repository_name> argument
  107. def _check_repository_name(value: str) -> str:
  108. if len(value.split("/")) != 2:
  109. raise argparse.ArgumentTypeError(f"'{value}' is not a valid '<owner>/<repo>' pair.")
  110. return value
  111. parser.add_argument(
  112. "-r",
  113. "--repository_name",
  114. type=_check_repository_name,
  115. help="""The '<owner>/<repo>' string that identifies the repository where releases are fetched.
  116. The default is the current repository.""",
  117. )
  118. # <branch_name> argument
  119. parser.add_argument(
  120. "-b",
  121. "--branch_name",
  122. help="""The name of the branch to check package versions from."
  123. If <release_type> is 'production', this branch has to be a release branch ('release/*').
  124. This value is extracted from the current branch by default.
  125. """,
  126. )
  127. args = parser.parse_args()
  128. all_releases = fetch_github_releases(args.repository_name)
  129. target_versions = {}
  130. next_versions = {}
  131. for package_name in Package.names(True):
  132. package_releases = all_releases.get(Package(package_name))
  133. released_versions = [release["version"] for release in package_releases] if package_releases else []
  134. if args.release_type == "production":
  135. released_versions = list(filter(lambda v: v.ext is None, released_versions))
  136. else:
  137. released_versions = list(filter(lambda v: v.ext is not None, released_versions))
  138. # Matching versions
  139. released_versions = [v for v in released_versions if v.matches(args.version, Version.MINOR)]
  140. target_version = max(released_versions) if released_versions else None
  141. target_versions[package_name] = target_version if target_version else Version.UNKNOWN
  142. packages: list[str] = [args.package] if args.package != "all" else Package.names(True)
  143. branch_name = args.branch_name if args.branch_name else Git.get_current_branch()
  144. for package_name in packages:
  145. package = Package(package_name)
  146. version = package.load_version()
  147. if version.ext:
  148. raise ValueError(f"Package version for '{package.name}' has an extension ({version.full_name}).")
  149. if version != args.version:
  150. raise ValueError(
  151. f"Target version ({args.version.full_name}) does not match version"
  152. + f" {version.full_name} in package {package.name}."
  153. )
  154. package_releases = all_releases.get(package)
  155. released_versions = [release["version"] for release in package_releases] if package_releases else []
  156. if version in released_versions:
  157. raise ValueError(f"{version} is already released for package {package.name}.")
  158. if args.release_type == "dev":
  159. __setup_dev_version(package, version, released_versions, target_versions)
  160. else:
  161. __setup_prod_version(package, version, branch_name, target_versions, next_versions)
  162. for p, v in target_versions.items():
  163. print(f"{p}_VERSION={v}") # noqa: T201
  164. if next_versions:
  165. for p, v in next_versions.items():
  166. print(f"NEXT_{p}_VERSION={v}") # noqa: T201
  167. # Print out the latest 'taipy' version that has no extension
  168. print(f"LATEST_TAIPY_VERSION={fetch_latest_github_taipy_releases(all_releases)}") # noqa: T201
  169. if __name__ == "__main__":
  170. main()