1
0
mirror of https://github.com/lvgl/lvgl.git synced 2025-01-28 07:03:00 +08:00

ci: add port release updater (#7590)

This commit is contained in:
Liam 2025-01-15 02:50:52 -05:00 committed by GitHub
parent 5f5c74c088
commit 124b2e268d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 242 additions and 0 deletions

View File

@ -0,0 +1,27 @@
name: Port repo release update
on:
push:
branches:
- 'release/v*' # on release branches
workflow_dispatch: # allow manual triggering
jobs:
run-release-branch-updater:
if: github.repository == 'lvgl/lvgl'
runs-on: ubuntu-24.04
steps:
- name: Checkout LVGL
uses: actions/checkout@v4
with:
path: lvgl
fetch-depth: 0 # fetch all
- name: set git credentials
run: |
git config user.name 'lvgl-bot'
git config user.email 'lvgl-bot@users.noreply.github.com'
- name: run the script
run: python3 lvgl/scripts/release_branch_updater.py --oldest-major 9

View File

@ -0,0 +1,214 @@
import subprocess
import re
import argparse
import os
import shutil
import sys
# v10.0.0 -> fail if release/v10.0 is not there
# v10.0.1 -> update release/v10.0
# v10.1.0 -> create release/v10.1 from release/v10.0 and update it
# v10.1.1 -> update release/v10.1
LOG = "[release_branch_updater.py]"
def main():
arg_parser = argparse.ArgumentParser()
arg_parser.add_argument("--port-clone-tmpdir", default="port_tmpdir")
arg_parser.add_argument("--port-urls-path", default=os.path.join(os.path.dirname(__file__), "release_branch_updater_port_urls.txt"))
arg_parser.add_argument("--lvgl-path", default=os.path.join(os.path.dirname(__file__), ".."))
arg_parser.add_argument("--dry-run", action="store_true")
arg_parser.add_argument("--oldest-major", type=int)
args = arg_parser.parse_args()
port_clone_tmpdir = args.port_clone_tmpdir
port_urls_path = args.port_urls_path
lvgl_path = args.lvgl_path
dry_run = args.dry_run
oldest_major = args.oldest_major
lvgl_release_branches = get_release_branches(lvgl_path)
print(LOG, "LVGL release branches:", ", ".join(fmt_release(br) for br in lvgl_release_branches) or "(none)")
if oldest_major is not None:
lvgl_release_branches = [br for br in lvgl_release_branches if br[0] >= oldest_major]
print(LOG, 'LVGL release branches after "oldest-major" filter:',
", ".join(fmt_release(br) for br in lvgl_release_branches) or "(none)")
with open(port_urls_path) as f:
urls = f.read()
urls = [url for url in map(str.strip, urls.splitlines()) if url]
# ensure this script creates the directory i.e. it doesn't belong to the user since it will rm -rf at the end
assert not os.path.exists(port_clone_tmpdir), "the port clone tmpdir should not exist yet"
for url in urls:
print(LOG, "working with port:", url)
subprocess.check_call(("git", "clone", url, port_clone_tmpdir))
port_release_branches = get_release_branches(port_clone_tmpdir)
print(LOG, "port release branches:", ", ".join(fmt_release(br) for br in port_release_branches) or "(none)")
# we want to
# 1. create (if necessary) the port's release branch
# 2. update the LVGL submodule to match the LVGL's release branch version
# 3. update the lv_conf.h based on the lv_conf.defaults
# from oldest to newest release...
for lvgl_branch in lvgl_release_branches:
print(LOG, f"attempting to update release branch {fmt_release(lvgl_branch)} ...")
port_does_not_have_the_branch = False
port_submodule_was_updated = False
port_lv_conf_h_was_updated = False
# if the branch does not exist in the port, create it from
# the closest minor of the same major.
if lvgl_branch in port_release_branches:
print(LOG, "... this port has a matching release branch.")
subprocess.check_call(("git", "-C", port_clone_tmpdir, "branch", "--track",
fmt_release(lvgl_branch),
f"origin/{fmt_release(lvgl_branch)}"))
else:
print(LOG, "... this port does not have this release branch minor ...")
port_does_not_have_the_branch = True
# get the port branch with this major and the next smallest minor
create_from = next((
br
for br in reversed(port_release_branches) # reverse it to get the newest (largest) minor
if br[0] == lvgl_branch[0] # same major
and br[1] < lvgl_branch[1] # smaller minor because exact minor does not exist
), None)
if create_from is None:
# there are no branches in the port that are this major
# version. One must be created manually.
print(LOG, "... this port has no major from which to create the minor. one must be created manually. continuing to next.")
continue
print(LOG, f"... creating the new branch {fmt_release(lvgl_branch)} "
f"from {fmt_release(create_from)}")
subprocess.check_call(("git", "-C", port_clone_tmpdir, "branch",
fmt_release(lvgl_branch), # new branch name
fmt_release(create_from))) # start point
port_release_branches.append(lvgl_branch)
port_release_branches.sort()
# checkout the same release in both LVGL and the port
subprocess.check_call(("git", "-C", lvgl_path, "checkout", f"origin/{fmt_release(lvgl_branch)}"))
subprocess.check_call(("git", "-C", port_clone_tmpdir, "checkout", fmt_release(lvgl_branch)))
# update the submodule in the port if it exists
out = subprocess.check_output(("git", "-C", port_clone_tmpdir, "config", "--file",
".gitmodules", "--get-regexp", "path"))
port_lvgl_submodule_path = next((
line.partition("lvgl.path ")[2]
for line
in out.decode().strip().splitlines()
if "lvgl.path " in line
), None)
if port_lvgl_submodule_path is None:
print(LOG, "this port has no LVGL submodule")
else:
print(LOG, "lvgl submodule found in port at:", port_lvgl_submodule_path)
# get the SHA of LVGL in this release of LVGL
out = subprocess.check_output(("git", "-C", lvgl_path, "rev-parse", "--verify", "--quiet", "HEAD"))
lvgl_sha = out.decode().strip()
print(LOG, "the SHA of LVGL in this release should be:", lvgl_sha)
# get the SHA of LVGL this port wants to use in this release
out = subprocess.check_output(("git", "-C", port_clone_tmpdir, "rev-parse",
"--verify", "--quiet", f"HEAD:{port_lvgl_submodule_path}"))
port_lvgl_submodule_sha = out.decode().strip()
print(LOG, "the SHA of LVGL in the submodule of this port is:", port_lvgl_submodule_sha)
if lvgl_sha == port_lvgl_submodule_sha:
print(LOG, "the submodule's version of LVGL is already up to date")
else:
print(LOG, "the submodule's version of LVGL is NOT up to date")
port_submodule_was_updated = True
# update the version of the submodule in the index. no need to `git submodule update --init` it.
# also no need to `git add .` afterwards because it stages the change.
# 160000 is a git file mode which means submodule.
subprocess.check_call(("git", "-C", port_clone_tmpdir, "update-index", "--cacheinfo",
f"160000,{lvgl_sha},{port_lvgl_submodule_path}"))
# update the lv_conf.h if there's an lv_conf.defaults
out = subprocess.check_output(("find", ".", "-name", "lv_conf.defaults", "-print", "-quit"), cwd=port_clone_tmpdir)
port_lv_conf_defaults = next(iter(out.decode().strip().splitlines()), None)
if port_lv_conf_defaults is None:
print(LOG, "this port has no lv_conf.defaults")
else:
out = subprocess.check_output(("find", ".", "-name", "lv_conf.h", "-print", "-quit"), cwd=port_clone_tmpdir)
port_lv_conf_h = next(iter(out.decode().strip().splitlines()), None)
if port_lv_conf_h is None:
print(LOG, "this port has an lv_conf.defaults but no lv_conf.h")
else:
subprocess.check_call((sys.executable, os.path.join(lvgl_path, "scripts/generate_lv_conf.py"),
"--defaults", os.path.abspath(os.path.join(port_clone_tmpdir, port_lv_conf_defaults)),
"--config", os.path.abspath(os.path.join(port_clone_tmpdir, port_lv_conf_h)), ))
# check if lv_conf.h actually changed. it will not detect the submodule change as a false positive.
out = subprocess.check_output(("git", "-C", port_clone_tmpdir, "diff"))
diff = out.decode().strip()
if not diff:
print(LOG, "this port's lv_conf.h did NOT change")
else:
print(LOG, "this port's lv_conf.h changed")
port_lv_conf_h_was_updated = True
subprocess.check_call(("git", "-C", port_clone_tmpdir, "add", port_lv_conf_h))
out = subprocess.check_output(("git", "-C", port_clone_tmpdir, "diff"))
diff = out.decode().strip()
assert not diff
if port_does_not_have_the_branch or port_submodule_was_updated or port_lv_conf_h_was_updated:
print(LOG, "changes were made. ready to push.")
# keep it brief for commit message 50 character limit suggestion.
# max length will be 50 characters in this case: "CI release edit: new branch. submodule. lv_conf.h."
commit_msg = ("CI release edit:"
+ (" new branch." if port_does_not_have_the_branch else "")
+ (" submodule." if port_submodule_was_updated else "")
+ (" lv_conf.h." if port_lv_conf_h_was_updated else "")
)
print(LOG, f"commit message: '{commit_msg}'")
subprocess.check_call(("git", "-C", port_clone_tmpdir, "commit", "-m", commit_msg))
if dry_run:
print(LOG, "this is a dry run so nothing will be pushed")
else:
subprocess.check_call(("git", "-C", port_clone_tmpdir, "push",
*(("-u", "origin") if port_does_not_have_the_branch else ()),
fmt_release(lvgl_branch),
))
print(LOG, "the changes were pushed.")
else:
print(LOG, "nothing to push for this release. it is up to date.")
shutil.rmtree(port_clone_tmpdir)
print(LOG, "port update complete:", url)
def get_release_branches(working_dir):
out = subprocess.check_output(("git", "-C", working_dir, "branch", "--quiet", "--format", "%(refname)", "--all"))
branches = out.decode().strip().splitlines()
release_versions = []
for branch_name in branches:
release_branch = re.fullmatch(r"refs/remotes/origin/release/v([0-9]+)\.([0-9]+)", branch_name)
if release_branch is None:
continue
release_versions.append((int(release_branch[1]), int(release_branch[2])))
release_versions.sort()
return release_versions
def fmt_release(release_tuple):
return f"release/v{release_tuple[0]}.{release_tuple[1]}"
if __name__ == "__main__":
main()

View File

@ -0,0 +1 @@
https://github.com/lvgl/lv_port_linux