set_versions.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. #!/usr/bin/env python3
  2. #
  3. # Add version information to poky.yaml based upon current git branch/tags
  4. # Also generate the list of available manuals (releases.rst file)
  5. #
  6. # Copyright Linux Foundation
  7. # Author: Richard Purdie <richard.purdie@linuxfoundation.org>
  8. # Author: Quentin Schulz <foss@0leil.net>
  9. #
  10. # SPDX-License-Identifier: MIT
  11. #
  12. import subprocess
  13. import collections
  14. import sys
  15. import os
  16. import itertools
  17. import re
  18. ourversion = None
  19. if len(sys.argv) == 2:
  20. ourversion = sys.argv[1]
  21. ourversion = None
  22. if len(sys.argv) == 2:
  23. ourversion = sys.argv[1]
  24. activereleases = ["walnascar", "scarthgap", "kirkstone"]
  25. devbranch = "whinlatter"
  26. ltsseries = ["scarthgap", "kirkstone"]
  27. # used by run-docs-builds to get the default page
  28. if ourversion == "getlatest":
  29. print(activereleases[0])
  30. sys.exit(0)
  31. release_series = collections.OrderedDict()
  32. release_series["whinlatter"] = "5.3"
  33. release_series["walnascar"] = "5.2"
  34. release_series["styhead"] = "5.1"
  35. release_series["scarthgap"] = "5.0"
  36. release_series["nanbield"] = "4.3"
  37. release_series["mickledore"] = "4.2"
  38. release_series["langdale"] = "4.1"
  39. release_series["kirkstone"] = "4.0"
  40. release_series["honister"] = "3.4"
  41. release_series["hardknott"] = "3.3"
  42. release_series["gatesgarth"] = "3.2"
  43. release_series["dunfell"] = "3.1"
  44. release_series["zeus"] = "3.0"
  45. release_series["warrior"] = "2.7"
  46. release_series["thud"] = "2.6"
  47. release_series["sumo"] = "2.5"
  48. release_series["rocko"] = "2.4"
  49. release_series["pyro"] = "2.3"
  50. release_series["morty"] = "2.2"
  51. release_series["krogoth"] = "2.1"
  52. release_series["jethro"] = "2.0"
  53. release_series["jethro-pre"] = "1.9"
  54. release_series["fido"] = "1.8"
  55. release_series["dizzy"] = "1.7"
  56. release_series["daisy"] = "1.6"
  57. release_series["dora"] = "1.5"
  58. release_series["dylan"] = "1.4"
  59. release_series["danny"] = "1.3"
  60. release_series["denzil"] = "1.2"
  61. release_series["edison"] = "1.1"
  62. release_series["bernard"] = "1.0"
  63. release_series["laverne"] = "0.9"
  64. bitbake_mapping = {
  65. "whinlatter" : "2.14",
  66. "walnascar" : "2.12",
  67. "styhead" : "2.10",
  68. "scarthgap" : "2.8",
  69. "nanbield" : "2.6",
  70. "mickledore" : "2.4",
  71. "langdale" : "2.2",
  72. "kirkstone" : "2.0",
  73. "honister" : "1.52",
  74. "hardknott" : "1.50",
  75. "gatesgarth" : "1.48",
  76. "dunfell" : "1.46",
  77. }
  78. # 3.4 onwards doesn't have poky version
  79. # Early 3.4 release docs do reference it though
  80. poky_mapping = {
  81. "3.4" : "26.0",
  82. "3.3" : "25.0",
  83. "3.2" : "24.0",
  84. "3.1" : "23.0",
  85. }
  86. ourseries = None
  87. ourbranch = None
  88. bitbakeversion = None
  89. docconfver = None
  90. # Test tags exist and inform the user to fetch if not
  91. try:
  92. subprocess.run(["git", "show", "yocto-%s" % release_series[activereleases[0]]], stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
  93. except subprocess.CalledProcessError:
  94. sys.exit("Please run 'git fetch --tags' before building the documentation")
  95. # Try and figure out what we are
  96. tags = subprocess.run(["git", "tag", "--points-at", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout
  97. for t in tags.split():
  98. if t.startswith("yocto-"):
  99. ourversion = t[6:]
  100. if ourversion:
  101. # We're a tagged release
  102. components = ourversion.split(".")
  103. baseversion = components[0] + "." + components[1]
  104. docconfver = ourversion
  105. for i in release_series:
  106. if release_series[i] == baseversion:
  107. ourseries = i
  108. ourbranch = i
  109. if i in bitbake_mapping:
  110. bitbakeversion = bitbake_mapping[i]
  111. else:
  112. # We're floating on a branch
  113. branch = subprocess.run(["git", "branch", "--show-current"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout.strip()
  114. ourbranch = branch
  115. if branch != "master" and branch not in release_series:
  116. # We're not on a known release branch so we have to guess. Compare the numbers of commits
  117. # from each release branch and assume the smallest number of commits is the one we're based off
  118. possible_branch = None
  119. branch_count = 0
  120. for b in itertools.chain(release_series.keys(), ["master"]):
  121. result = subprocess.run(["git", "log", "--format=oneline", "HEAD..origin/" + b], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
  122. if result.returncode == 0:
  123. count = result.stdout.count('\n')
  124. if not possible_branch or count < branch_count:
  125. print("Branch %s has count %s" % (b, count))
  126. possible_branch = b
  127. branch_count = count
  128. if possible_branch:
  129. branch = possible_branch
  130. else:
  131. branch = "master"
  132. print("Nearest release branch estimated to be %s" % branch)
  133. if branch == "master":
  134. ourseries = devbranch
  135. docconfver = "dev"
  136. bitbakeversion = "dev"
  137. elif branch in release_series:
  138. ourseries = branch
  139. if branch in bitbake_mapping:
  140. bitbakeversion = bitbake_mapping[branch]
  141. else:
  142. sys.exit("Unknown series for branch %s" % branch)
  143. previoustags = subprocess.run(["git", "tag", "--merged", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout
  144. previoustags = [t[6:] for t in previoustags.split() if t.startswith("yocto-" + release_series[ourseries])]
  145. futuretags = subprocess.run(["git", "tag", "--merged", ourbranch], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout
  146. futuretags = [t[6:] for t in futuretags.split() if t.startswith("yocto-" + release_series[ourseries])]
  147. # Append .999 against the last known version
  148. if len(previoustags) != len(futuretags):
  149. ourversion = previoustags[-1] + ".999"
  150. else:
  151. ourversion = release_series[ourseries] + ".999"
  152. if not docconfver:
  153. docconfver = ourversion
  154. series = [k for k in release_series]
  155. previousseries = series[series.index(ourseries)+1:] or [""]
  156. lastlts = [k for k in previousseries if k in ltsseries] or "dunfell"
  157. latestreltag = subprocess.run(["git", "describe", "--abbrev=0", "--tags", "--match", "yocto-*"], capture_output=True, text=True).stdout
  158. latestreltag = latestreltag.strip()
  159. if latestreltag:
  160. if latestreltag.startswith("yocto-"):
  161. latesttag = latestreltag[6:]
  162. else:
  163. # fallback on the calculated version
  164. print("Did not find a tag with 'git describe', falling back to %s" % ourversion)
  165. latestreltag = "yocto-" + ourversion
  166. latesttag = ourversion
  167. print("Version calculated to be %s" % ourversion)
  168. print("Latest release tag found is %s" % latestreltag)
  169. print("Release series calculated to be %s" % ourseries)
  170. replacements = {
  171. "DISTRO" : ourversion,
  172. "DISTRO_LATEST_TAG": latesttag,
  173. "DISTRO_NAME_NO_CAP" : ourseries,
  174. "DISTRO_NAME" : ourseries.capitalize(),
  175. "DISTRO_NAME_NO_CAP_MINUS_ONE" : previousseries[0],
  176. "DISTRO_NAME_NO_CAP_LTS" : lastlts[0],
  177. "YOCTO_DOC_VERSION" : ourversion,
  178. "DOCCONF_VERSION" : docconfver,
  179. "BITBAKE_SERIES" : bitbakeversion,
  180. }
  181. if release_series[ourseries] in poky_mapping:
  182. pokyversion = poky_mapping[release_series[ourseries]]
  183. if ourversion != release_series[ourseries]:
  184. pokyversion = pokyversion + "." + ourversion.rsplit(".", 1)[1]
  185. else:
  186. pokyversion = pokyversion + ".0"
  187. replacements["POKYVERSION"] = pokyversion
  188. if os.path.exists("poky.yaml.in"):
  189. with open("poky.yaml.in", "r") as r, open("poky.yaml", "w") as w:
  190. lines = r.readlines()
  191. for line in lines:
  192. data = line.split(":")
  193. k = data[0].strip()
  194. if k in replacements:
  195. w.write("%s : \"%s\"\n" % (k, replacements[k]))
  196. else:
  197. w.write(line)
  198. print("poky.yaml generated from poky.yaml.in")
  199. # In the switcher list of versions we display:
  200. # - latest dev
  201. # - latest stable release
  202. # - latest LTS
  203. # - latest for each releases listed as active
  204. # - latest doc version in current series
  205. # - current doc version
  206. # (with duplicates removed)
  207. versions = []
  208. with open("sphinx-static/switchers.js.in", "r") as r, open("sphinx-static/switchers.js", "w") as w:
  209. lines = r.readlines()
  210. for line in lines:
  211. if "ALL_RELEASES_PLACEHOLDER" in line:
  212. w.write(str(list(release_series.keys())))
  213. continue
  214. if "VERSIONS_PLACEHOLDER" in line:
  215. w.write(" 'dev': { 'title': 'Unstable (dev)', 'obsolete': false,},\n")
  216. for branch in activereleases + ([ourseries] if ourseries not in activereleases else []):
  217. if branch == devbranch:
  218. continue
  219. branch_versions = subprocess.run('git tag --list yocto-%s*' % (release_series[branch]), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout.split()
  220. branch_versions = sorted([v.replace("yocto-" + release_series[branch] + ".", "").replace("yocto-" + release_series[branch], "0") for v in branch_versions], key=int)
  221. if not branch_versions:
  222. continue
  223. version = release_series[branch]
  224. if branch_versions[-1] != "0":
  225. version = version + "." + branch_versions[-1]
  226. versions.append(version)
  227. w.write(" '%s': {'title': '%s (%s)', 'obsolete': %s,},\n" % (version, branch.capitalize(), version, str(branch not in activereleases).lower()))
  228. if ourversion not in versions and ourseries != devbranch:
  229. w.write(" '%s': {'title': '%s (%s)', 'obsolete': %s,},\n" % (ourversion, ourseries.capitalize(), ourversion, str(ourseries not in activereleases).lower()))
  230. else:
  231. w.write(line)
  232. print("switchers.js generated from switchers.js.in")
  233. # generate releases.rst
  234. # list missing tags in yocto-docs
  235. missing_tags = [
  236. 'yocto-0.9',
  237. 'yocto-1.0', 'yocto-1.0.1',
  238. 'yocto-1.1', 'yocto-1.1.1',
  239. 'yocto-1.2',
  240. 'yocto-1.4.4', 'yocto-1.4.5',
  241. 'yocto-1.5', 'yocto-1.5.2', 'yocto-1.5.3', 'yocto-1.5.4',
  242. 'yocto-1.6', 'yocto-1.6.1', 'yocto-1.6.2',
  243. 'yocto-1.7', 'yocto-1.7.1',
  244. 'yocto-1.9',
  245. 'yocto-2.5.3',
  246. 'yocto-3.1', 'yocto-3.1.1', 'yocto-3.1.2', 'yocto-3.1.3',
  247. ]
  248. semver = re.compile(r'yocto-(\d+)\.(\d+)(?:\.)?(\d*)')
  249. # git is able to properly order semver versions but not python
  250. # instead of adding a dependency on semver module, let's convert the version
  251. # into a decimal number, e.g. 11.23.1 will be 112301 and 1.5 will be 010500 so
  252. # it can be used as a key for the sorting algorithm.
  253. # This can be removed once all the old tags are re-created.
  254. def tag_to_semver_like(v):
  255. v_semver = semver.search(v)
  256. v_maj, v_min, v_patch = v_semver.groups('0')
  257. return int("{:0>2}{:0>2}{:0>2}".format(v_maj, v_min, v_patch), 10)
  258. yocto_tags = subprocess.run(["git", "tag", "--list", "--sort=version:refname", "yocto-*"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True).stdout
  259. yocto_tags = sorted(yocto_tags.split() + missing_tags, key=tag_to_semver_like)
  260. tags = [tag[6:] for tag in yocto_tags]
  261. with open('releases.rst', 'w') as f:
  262. f.write('===========================\n')
  263. f.write(' Supported Release Manuals\n')
  264. f.write('===========================\n')
  265. f.write('\n')
  266. for activerelease in activereleases:
  267. title = "Release Series %s (%s)" % (release_series[activerelease], activerelease)
  268. f.write('*' * len(title) + '\n')
  269. f.write(title + '\n')
  270. f.write('*' * len(title) + '\n')
  271. f.write('\n')
  272. for tag in tags:
  273. if tag == release_series[activerelease] or tag.startswith('%s.' % release_series[activerelease]):
  274. f.write('- :yocto_docs:`%s Documentation </%s>`\n' % (tag, tag))
  275. f.write('\n')
  276. f.write('==========================\n')
  277. f.write(' Outdated Release Manuals\n')
  278. f.write('==========================\n')
  279. f.write('\n')
  280. for series in release_series:
  281. if series == devbranch or series in activereleases:
  282. continue
  283. if series == "jethro-pre":
  284. continue
  285. title = "Release Series %s (%s)" % (release_series[series], series)
  286. f.write('*' * len(title) + '\n')
  287. f.write(title + '\n')
  288. f.write('*' * len(title) + '\n')
  289. f.write('\n')
  290. if series == "jethro":
  291. f.write('- :yocto_docs:`1.9 Documentation </1.9>`\n')
  292. for tag in tags:
  293. if tag == release_series[series] or tag.startswith('%s.' % release_series[series]):
  294. f.write('- :yocto_docs:`%s Documentation </%s>`\n' % (tag, tag))
  295. f.write('\n')