1#!/usr/bin/env python
2
3# Release lvgl, lv_examples, lv_drivers. docs, blog and prepare the development of the next major, minoror bugfix release
4# Usage: ./release,py bugfix | minor | major
5# The option means what type of versin to prepare for development after release
6#
7# STEPS:
8#  - clone all 5 repos
9#  - get the version numnber from lvgl.h
10#  - set release branch (e.g. "release/v7")
11#  - prepare lvgl
12#    - run lv_conf_internal.py
13#    - run code formatter
14#    - clear LVGL_VERSION_INFO (set to "")
15#    - run Doxygen
16#    - update the version in lvgl's library.json, library.properties, lv_conf_template.h
17#    - update CHANGELOG.md
18#    - commit changes
19#  - prepare lv_examples
20#    - upadte the required LVGL version in lv_examples.h (LV_VERSION_CHECK)
21#    - update the version in lv_ex_conf_template.h
22#  - prepare lv_drivers
23#    - update the version in library.json, lv_drv_conf_template.h
24#  - prepare docs
25#    - update API XML
26#    - clear the versiopn info (should be plain vx.y.z)
27#  - tag all repos with the new version
28#  - merge to release branches
29#  - blog: add release post
30#  - push tags and commits
31#  - docs: run ./updade.py release/vX
32#
33# If --patch
34#  - merge master to dev branches
35#  - increment patch version by 1 and append "-dev". E.g. "vX.Y.(Z+1)-dev"
36#  - update version numbers in lvgl and docs
37#  - commit and push
38#  - docs: run ./updade.py latest dev
39#
40# Else (not --patch)
41#  - merge master to dev
42#  - merge the dev to master
43#  - increment version number like "vX.(Y+1).0-dev"
44#  - apply the new version in dev branches of lvgl, lv_examples, lv_drivers, docs
45#  - commit and push to dev branches
46#  - docs: run ./updade.py latest dev
47
48import re
49import os, fnmatch
50import os.path
51from os import path
52from datetime import date
53import sys
54
55upstream_org_url = "https://github.com/lvgl/"
56workdir = "./release_tmp"
57proj_list = [ "lv_sim_eclipse_sdl"]
58
59ver_major = -1
60ver_minor = -1
61ver_patch = -1
62
63dev_ver_major = -1
64dev_ver_minor = -1
65dev_ver_patch = -1
66
67ver_str = ""
68dev_ver_str = ""
69release_br = ""
70release_note = ""
71
72prepare_type = ['major', 'minor', 'bugfix']
73
74dev_prepare = 'minor'
75
76def upstream(repo):
77    return upstream_org_url + repo + ".git"
78
79def cmd(c, exit_on_err = True):
80  print("\n" + c)
81  r = os.system(c)
82  if r:
83    print("### Error: " + str(r))
84    if exit_on_err: exit(int(r))
85
86def define_set(fn, name, value):
87    print("In " + fn + " set " + name + " to " + value)
88
89    new_content = ""
90    s = r'^ *# *define +' + str(name).rstrip()
91
92    f = open(fn, "r")
93    for i in f.read().splitlines():
94        r = re.search(s, i)
95        if r:
96            d = i.split("define")
97            i = d[0] + "define " + name + " " + value
98        new_content += i + '\n'
99
100    f.close()
101
102    f = open(fn, "w")
103    f.write(new_content)
104    f.close()
105
106def clone_repos():
107    cmd("rm -fr " + workdir)
108    cmd("mkdir " + workdir)
109    os.chdir(workdir)
110
111    #For debuging just copy the repos
112    #cmd("cp -a ../repos/. .")
113    #return
114
115    cmd("git clone " + upstream("lvgl") + "; cd lvgl; git checkout master; git remote update origin --prune; ")
116    cmd("git clone " + upstream("lv_examples") + "; cd lv_examples; git checkout master; git remote update origin --prune; ")
117    cmd("git clone " + upstream("lv_drivers") + "; cd lv_drivers; git checkout master; git remote update origin --prune; ")
118    cmd("git clone --recurse-submodules " + upstream("docs") + "; cd docs; git checkout master; git remote update origin --prune; ")
119    cmd("git clone " + upstream("blog") + "; cd blog; git checkout master; git remote update origin --prune; ")
120
121    for p in proj_list:
122        cmd("git clone " + upstream(p) + " --recurse-submodules ; cd " + p + "; git checkout master; git remote update origin --prune; ")
123
124
125def get_lvgl_version(br):
126    print("Get LVGL's version")
127
128    global ver_str, ver_major, ver_minor, ver_patch, release_br
129
130    os.chdir("./lvgl")
131
132    cmd("git checkout " + br)
133
134    f = open("./lvgl.h", "r")
135
136    lastNum = re.compile(r'(?:[^\d]*(\d+)[^\d]*)+')
137    for i in f.read().splitlines():
138        r = re.search(r'^#define LVGL_VERSION_MAJOR ', i)
139        if r:
140            m = lastNum.search(i)
141            if m: ver_major = m.group(1)
142
143        r = re.search(r'^#define LVGL_VERSION_MINOR ', i)
144        if r:
145            m = lastNum.search(i)
146            if m: ver_minor = m.group(1)
147
148        r = re.search(r'^#define LVGL_VERSION_PATCH ', i)
149        if r:
150            m = lastNum.search(i)
151            if m: ver_patch = m.group(1)
152
153    f.close()
154
155    cmd("git checkout master")
156
157    ver_str =  "v" + str(ver_major) + "." + str(ver_minor) + "." + str(ver_patch)
158    print("New version:" + ver_str)
159
160    release_br = "release/v" + ver_major
161
162    os.chdir("../")
163
164def update_version():
165    templ = fnmatch.filter(os.listdir('.'), '*templ*')
166
167    if templ[0]:
168        print("Updating version in " + templ[0])
169        cmd("sed -i -r 's/v[0-9]+\.[0-9]+\.[0-9]+/"+ ver_str +"/' " + templ[0])
170
171    if os.path.exists("library.json"):
172        print("Updating version in library.json")
173        cmd("sed -i -r 's/[0-9]+\.[0-9]+\.[0-9]+/"+ ver_str[1:] +"/' library.json")
174
175    if path.exists("library.properties"):
176        print("Updating version in library.properties")
177        cmd("sed -i -r 's/version=[0-9]+\.[0-9]+\.[0-9]+/"+ "version=" + ver_str[1:] + "/' library.properties")
178
179def lvgl_prepare():
180    print("Prepare lvgl")
181
182    global ver_str, ver_major, ver_minor, ver_patch
183
184    os.chdir("./lvgl")
185    define_set("./lvgl.h", "LVGL_VERSION_INFO", '\"\"')
186
187    # Run some scripts
188    os.chdir("./scripts")
189    cmd("./code-format.sh")
190    cmd("./lv_conf_checker.py")
191    cmd("doxygen")
192    os.chdir("../")
193
194    update_version()
195
196    #update CHANGLELOG
197    new_content = ""
198    f = open("./CHANGELOG.md", "r")
199
200    global release_note
201    release_note = ""
202    note_state = 0
203    for i in f.read().splitlines():
204        if note_state == 0:
205            r = re.search(r'^## ' + ver_str, i)
206            if r:
207                i = i.replace("planned on ", "")
208                note_state+=1
209
210        elif note_state == 1:
211            r = re.search(r'^## ', i)
212            if r:
213                note_state+=1
214            else:
215                release_note += i + '\n'
216
217        new_content += i + '\n'
218
219    f.close()
220
221    f = open("./CHANGELOG.md", "w")
222    f.write(new_content)
223    f.close()
224
225    cmd('git commit -am "prepare to release ' + ver_str + '"')
226
227    os.chdir("../")
228
229
230def lv_examples_prepare():
231    print("Prepare lv_examples")
232    global ver_str, ver_major, ver_minor, ver_patch
233
234    os.chdir("./lv_examples")
235
236    update_version()
237
238    cmd("sed -i -r 's/LV_VERSION_CHECK\([0-9]+, *[0-9]+, *[0-9]+\)/"+ "LV_VERSION_CHECK(" + ver_major + ", " + ver_minor + ", " + ver_patch + ")/' lv_examples.h")
239
240    cmd('git commit -am "prepare to release ' + ver_str + '"')
241
242    os.chdir("../")
243
244def lv_drivers_prepare():
245    print("Prepare lv_drivers")
246    global ver_str, ver_major, ver_minor, ver_patch
247
248    os.chdir("./lv_drivers")
249
250    update_version()
251
252    cmd('git commit -am "prepare to release ' + ver_str + '"')
253
254    os.chdir("../")
255
256def docs_prepare():
257    print("Prepare docs")
258    global ver_str, ver_major, ver_minor, ver_patch
259
260    os.chdir("./docs")
261
262    cmd("git co latest --")
263    cmd("rm -rf xml");
264    cmd("cp -r ../lvgl/docs/api_doc/xml .");
265    cmd("git add xml");
266
267    cmd("sed -i -r \"s/'v[0-9]+\.[0-9]+\.[0-9]+.*'/\'" + ver_str + "'/\" conf.py")
268
269    cmd('git commit -am "prepare to release ' + ver_str + '"')
270
271    os.chdir("../")
272
273def blog_add_post():
274    global ver_str, release_note
275
276    os.chdir("./blog/_posts")
277
278    post = "---\nlayout: post\ntitle: " + ver_str + " is released\nauthor: \"kisvegabor\"\ncover: /assets/release_cover.png\n---\n\n"
279    post += release_note
280
281    today = date.today()
282    d = today.strftime("%Y-%m-%d")
283
284    f = open(d + "-release_" + ver_str + ".md", "w")
285    f.write(post)
286    f.close()
287
288    cmd("git add .")
289    cmd("git commit -am 'Add " + ver_str + " release post'")
290
291    os.chdir("../../")
292
293def add_tags():
294    global ver_str
295    tag_cmd = " git tag -a " + ver_str + " -m 'Release " + ver_str + "' "
296    cmd("cd lvgl; " + tag_cmd)
297    cmd("cd lv_examples; " + tag_cmd)
298    cmd("cd lv_drivers; " + tag_cmd)
299    cmd("cd docs; " + tag_cmd)
300
301def update_release_branches():
302    global release_br
303    merge_cmd = " git checkout " + release_br + "; git pull origin " + release_br + "; git merge master  -X ours; git push origin " + release_br + "; git checkout master"
304    cmd("cd lvgl; " + merge_cmd)
305    cmd("cd lv_examples; " + merge_cmd)
306    cmd("cd lv_drivers; " + merge_cmd)
307
308    merge_cmd = " git checkout " + release_br + "; git pull origin " + release_br  + "; git merge latest  -X ours; git push origin " + release_br + "; git checkout latest"
309    cmd("cd docs; " + merge_cmd)
310
311def publish_master():
312
313    #Merge LVGL master to dev first to avoid "merge-to-dev.yml" running asynchronous
314    os.chdir("./lvgl")
315    cmd("git checkout dev")
316    cmd("git merge master -X ours")
317    cmd("git add .")
318    cmd("git commit -am 'Merge master'", False)
319    cmd("git push origin dev")
320    cmd("git checkout master")
321    os.chdir("../")
322
323    pub_cmd = "git push origin master; git push origin " + ver_str
324    cmd("cd lvgl; " + pub_cmd)
325    cmd("cd lv_examples; " + pub_cmd)
326    cmd("cd lv_drivers; " + pub_cmd)
327
328    pub_cmd = "git push origin master"
329    cmd("cd blog; " + pub_cmd)
330
331def merge_from_dev():
332    merge_cmd = "git checkout master; git merge dev;"
333    cmd("cd lvgl; " + merge_cmd)
334
335    merge_cmd = "git checkout latest -- ; git merge dev -X theirs --no-edit;"
336    cmd("cd docs; " + merge_cmd)
337
338
339def lvgl_update_master_version():
340    global ver_major, ver_minor, ver_patch, ver_str
341
342    os.chdir("./lvgl")
343
344    cmd("git checkout master")
345    define_set("./lvgl.h", "LVGL_VERSION_MAJOR", ver_major)
346    define_set("./lvgl.h", "LVGL_VERSION_MINOR", ver_minor)
347    define_set("./lvgl.h", "LVGL_VERSION_PATCH", ver_patch)
348    define_set("./lvgl.h", "LVGL_VERSION_INFO", "dev")
349
350    templ = fnmatch.filter(os.listdir('.'), '*templ*')
351    if templ[0]:
352        print("Updating version in " + templ[0])
353        cmd("sed -i -r 's/v[0-9]+\.[0-9]+\.[0-9]+.*/"+ ver_str +"/' " + templ[0])
354
355
356    cmd("git commit -am 'Update version'")
357
358    os.chdir("../")
359
360def docs_update_latest_version():
361    global ver_str
362
363    os.chdir("./docs")
364    cmd("git checkout latest --")
365    cmd("sed -i -r \"s/'v[0-9]+\.[0-9]+\.[0-9]+.*'/\'" + ver_str + "'/\" conf.py")
366    cmd("git commit -am 'Update version'")
367    cmd("git checkout master --")
368
369    os.chdir("../")
370
371
372def lvgl_update_dev_version():
373    global ver_major, ver_minor, ver_patch, dev_ver_str
374
375    os.chdir("./lvgl")
376
377    cmd("git checkout dev")
378    define_set("./lvgl.h", "LVGL_VERSION_MAJOR", str(ver_major))
379    define_set("./lvgl.h", "LVGL_VERSION_MINOR", str(ver_minor))
380    define_set("./lvgl.h", "LVGL_VERSION_PATCH", str(ver_patch))
381    define_set("./lvgl.h", "LVGL_VERSION_INFO", "\"dev\"")
382
383    templ = fnmatch.filter(os.listdir('.'), '*templ*')
384    if templ[0]:
385        print("Updating version in " + templ[0])
386        cmd("sed -i -r 's/v[0-9]+\.[0-9]+\.[0-9]+.*/"+ dev_ver_str +"/' " + templ[0])
387
388
389    cmd("git commit -am 'Update dev version'")
390    cmd("git checkout master")
391
392    os.chdir("../")
393
394def docs_update_dev_version():
395    global dev_ver_str
396
397    os.chdir("./docs")
398    cmd("git checkout dev --")
399    cmd("sed -i -r \"s/'v[0-9]+\.[0-9]+\.[0-9]+.*'/\'" + dev_ver_str + "'/\" conf.py")
400    cmd("git commit -am 'Update dev version'")
401    cmd("git checkout master --")
402
403    os.chdir("../")
404
405
406def publish_dev_and_master():
407    pub_cmd = "git checkout dev; git push origin dev"
408    cmd("cd lvgl; " + pub_cmd)
409    pub_cmd = "git checkout master; git push origin master"
410    cmd("cd lvgl; " + pub_cmd)
411
412def projs_update():
413    global proj_list, release_br, ver_str
414    for p in proj_list:
415        os.chdir("./" + p)
416        cmd('git checkout master')
417        print(p + ": upadte lvgl");
418        cmd("cd lvgl; git co " + release_br + "; git pull origin " + release_br)
419        cmd("cp -f lvgl/lv_conf_template.h lv_conf.h")
420        cmd("sed -i -r 's/#if 0/#if 1/' lv_conf.h")  # Enable lv_conf.h
421        d = {}
422        with open("confdef.txt") as f:
423            for line in f:
424                (key, val) = line.rstrip().split('\t')
425                d[key] = val
426
427        for k,v in d.items():
428            define_set("lv_conf.h", str(k), str(v))
429
430        if os.path.exists("lv_examples"):
431            print(p + ": upadte lv_examples");
432            cmd("cd lv_examples; git co " + release_br + "; git pull origin " + release_br)
433
434        if os.path.exists("lv_drivers"):
435            print(p + ": upadte lv_drivers");
436            cmd("cd lv_drivers " + release_br + "; git pull origin " + release_br)
437
438        msg = 'Update to ' + ver_str
439        cmd("git add .")
440        cmd('git commit -am "' + msg +  '"')
441        cmd('git push origin master')
442        cmd("git tag -a " + ver_str + " -m '" + msg + "' " )
443        cmd('git push origin ' + ver_str)
444
445        os.chdir("../")
446
447def docs_update_all():
448    cmd("cd docs; git checkout master; python 2.7 ./update.py master dev " + release_br)
449
450def cleanup():
451    os.chdir("../")
452    cmd("rm -fr " + workdir)
453
454if __name__ == '__main__':
455    dev_prepare = 'bugfix'
456    if(len(sys.argv) != 2):
457        print("Missing argument. Usage ./release.py bugfix | minor | major")
458        print("Use minor by deafult")
459    else:
460        dev_prepare = sys.argv[1]
461
462    if not (dev_prepare in prepare_type):
463        print("Invalid argument. Usage ./release.py bugfix | minor | major")
464        exit(1)
465
466    clone_repos()
467    get_lvgl_version("dev")
468    dev_ver_major = ver_major
469    dev_ver_minor = ver_minor
470    dev_ver_patch = ver_patch
471    dev_ver_str = ver_str
472
473    get_lvgl_version("master")
474
475    lvgl_prepare()
476    lv_examples_prepare()
477    lv_drivers_prepare()
478    docs_prepare()
479    blog_add_post()
480    add_tags()
481    update_release_branches()
482    publish_master()
483
484    projs_update()
485
486    if dev_prepare == 'bugfix':
487        ver_patch = str(int(ver_patch) + 1)
488        ver_str = "v" + ver_major + "." + ver_minor + "." + ver_patch + "-dev"
489
490        print("Prepare bugfix version " + ver_str)
491
492        lvgl_update_master_version()
493        docs_update_latest_version()
494
495    else:
496        #merge_from_dev()
497
498        get_lvgl_version("master")
499
500        if dev_prepare == 'minor':
501            ver_minor = str(int(ver_minor) + 1)
502            ver_patch = "0"
503        else:
504            ver_major = str(int(ver_major) + 1)
505            ver_minor = "0"
506            ver_patch = "0"
507
508        dev_ver_str = "v" + str(ver_major) + "." + str(ver_minor) + "." + str(ver_patch) + "-dev"
509
510        print("Prepare minor version " + dev_ver_str)
511
512        lvgl_update_dev_version()
513        docs_update_dev_version()
514        publish_dev_and_master()
515
516    docs_update_all();
517    cleanup()
518
519