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