Explorar o código

app.py: use ap_git module for git operations

Shiv Tyagi hai 1 ano
pai
achega
801b6235c2
Modificáronse 1 ficheiros con 67 adicións e 122 borrados
  1. 67 122
      app.py

+ 67 - 122
app.py

@@ -36,6 +36,9 @@ dictConfig({
         'handlers': ['wsgi']
         'handlers': ['wsgi']
     }
     }
 })
 })
+
+import ap_git
+
 # run at lower priority
 # run at lower priority
 os.nice(20)
 os.nice(20)
 
 
@@ -61,7 +64,6 @@ REMOTES = None
 
 
 # LOCKS
 # LOCKS
 queue_lock = Lock()
 queue_lock = Lock()
-head_lock = Lock()  # lock git HEAD, i.e., no branch change until this lock is released
 remotes_lock = Lock()  # lock for accessing and updating REMOTES list
 remotes_lock = Lock()  # lock for accessing and updating REMOTES list
 
 
 def get_remotes():
 def get_remotes():
@@ -73,35 +75,14 @@ def set_remotes(remotes):
         global REMOTES
         global REMOTES
         REMOTES = remotes
         REMOTES = remotes
 
 
-def find_hash_for_ref(remote_name, ref):
-    result = subprocess.run(['git', 'ls-remote', remote_name], cwd=sourcedir, encoding='utf-8', capture_output=True, shell=False)
-
-    for line in result.stdout.split('\n')[:-1]:
-        (git_hash, r) = line.split('\t')
-        if r == ref:
-            return git_hash
-
-def fetch_remote(remote_name):
-    app.logger.info("Fetching remote %s" % remote_name)
-    run_git(['git', 'fetch', remote_name, '--tags', '--force'], sourcedir)
-
-def get_git_hash(remote, commit_reference, fetch=False):
-    if remote is None or commit_reference  is None:
-        return ''
-
-    # fetch remote
-    if fetch:
-        fetch_remote(remote)
-
-    raise Exception('Branch ref not found on remote')
-
-def ref_is_branch(commit_reference):
-    prefix = 'refs/heads'
-    return commit_reference[:len(prefix)] == prefix
-
-def ref_is_tag(commit_reference):
-    prefix = 'refs/tags'
-    return commit_reference[:len(prefix)] == prefix
+try:
+    repo = ap_git.GitRepo(sourcedir)
+except FileNotFoundError:
+    repo = ap_git.GitRepo.clone(
+        source="https://github.com/ardupilot/ardupilot.git",
+        dest=sourcedir,
+        recurse_submodules=True,
+    )
 
 
 def load_remotes():
 def load_remotes():
     # load file contianing vehicles listed to be built for each remote along with the braches/tags/commits on which the firmware can be built
     # load file contianing vehicles listed to be built for each remote along with the braches/tags/commits on which the firmware can be built
@@ -134,54 +115,6 @@ def find_version_info(vehicle_name, remote_name, commit_reference):
     release = next((r for r in vehicle['releases'] if r['commit_reference'] == commit_reference), None)
     release = next((r for r in vehicle['releases'] if r['commit_reference'] == commit_reference), None)
     return release
     return release
 
 
-
-def run_git(cmd, cwd):
-    app.logger.info("Running git: %s" % ' '.join(cmd))
-    return subprocess.run(cmd, cwd=cwd, shell=False)
-
-def delete_branch(branch_name, s_dir):
-    run_git(['git', 'checkout', 'master'], cwd=s_dir) # to make sure we are not already on branch to be deleted
-    run_git(['git', 'branch', '-D', branch_name], cwd=s_dir)    # delete branch
-
-def do_checkout(remote, commit_reference, s_dir, force_fetch=False, temp_branch_name=None):
-    '''checkout to given commit/branch and return the git hash'''
-    # Note: remember to acquire head_lock before calling this method
-    if force_fetch:
-        fetch_remote(remote)
-
-    git_hash_target = commit_reference
-    if ref_is_branch(commit_reference) or ref_is_tag(commit_reference):
-        git_hash_target = find_hash_for_ref(remote, commit_reference)
-
-    app.logger.info("Checking out to %s (%s/%s)" % (git_hash_target, remote, commit_reference))
-
-    result = run_git(['git', 'checkout', '-f', git_hash_target], cwd=s_dir)
-    if result.returncode != 0:
-        # commit with the given hash isn't fetched? fetch and try again
-        fetch_remote(remote)
-        result = run_git(['git', 'checkout', '-f', git_hash_target], cwd=s_dir)
-        if result.returncode != 0:
-            raise Exception("Could not checkout to the requested commit")
-
-    # we have successfully checked out to requested commit
-    # do git reset and git clean to make sure the tree remains clean
-    run_git(['git', 'reset', '--hard', 'HEAD'], cwd=s_dir)
-    run_git(['git', 'clean', '-dxff', git_hash_target], cwd=s_dir)
-
-    if temp_branch_name is not None:
-        delete_branch(temp_branch_name, s_dir=s_dir) # delete temp branch if it already exists
-        run_git(['git', 'checkout', '-b', temp_branch_name, git_hash_target], cwd=s_dir)    # creates new temp branch
-    return git_hash_target
-
-def branch_and_clone(remote, commit_reference, sourcedir, out_dir, temp_branch_name):
-    remove_directory_recursive(out_dir)
-    head_lock.acquire()
-    do_checkout(remote, commit_reference, s_dir=sourcedir, force_fetch=True, temp_branch_name=temp_branch_name)
-    output = run_git(['git', 'clone', '--single-branch', '--branch='+temp_branch_name, sourcedir, out_dir], cwd=sourcedir)
-    delete_branch(temp_branch_name, sourcedir) # delete temp branch
-    head_lock.release()
-    return output.returncode == 0
-
 def get_boards_from_ardupilot_tree(s_dir):
 def get_boards_from_ardupilot_tree(s_dir):
     '''return a list of boards to build'''
     '''return a list of boards to build'''
     tstart = time.time()
     tstart = time.time()
@@ -223,24 +156,21 @@ def get_build_options_from_ardupilot_tree(s_dir):
 def setup_remotes_urls(remotes):
 def setup_remotes_urls(remotes):
     added_remotes = 0
     added_remotes = 0
     for remote in remotes:
     for remote in remotes:
-        # add new remote
-        result = subprocess.run(['git', 'remote', 'add', remote['name'], remote['url']], cwd=sourcedir, encoding='utf-8', capture_output=True, shell=False)
-
-        # non-zero returncode? the remote already exists probably
-        # To-do
-        # change this condition to be specific for returncode 3
-        # which is returned when remote already exists, this change came after git version 2.30
-        if result.returncode != 0:
-            app.logger.info("Remote %s already exists, updating url" % remote['name'])
-            result = subprocess.run(['git', 'remote', 'set-url', remote['name'], remote['url']], cwd=sourcedir, encoding='utf-8', capture_output=True, shell=False)
-
-        # did we succeed?
-        if result.returncode != 0:
-            app.logger.error("Failed to add/update remote %s %s %s" % (remote['name'], remote['url'], result.stderr.rstrip()))
-        else:
-            app.logger.info("Initial fetch")
-            fetch_remote(remote['name'])
-            added_remotes += 1
+        try:
+            repo.remote_add(
+                remote=remote['name'],
+                url=remote['url'],
+            )
+        except ap_git.DuplicateRemoteError:
+            app.logger.info(
+                f"remote '{remote['name']}' already exists. "
+                f"Updating url to '{remote['url']}'."
+            )
+            repo.remote_set_url(
+                remote=remote['name'],
+                url=remote['url'],
+            )
+        added_remotes += 1
 
 
     app.logger.info("%d/%d remotes added to base repo" % (added_remotes, len(remotes)))
     app.logger.info("%d/%d remotes added to base repo" % (added_remotes, len(remotes)))
 
 
@@ -266,13 +196,23 @@ def run_build(task, tmpdir, outdir, logpath):
     '''run a build with parameters from task'''
     '''run a build with parameters from task'''
     remove_directory_recursive(tmpdir_parent)
     remove_directory_recursive(tmpdir_parent)
     create_directory(tmpdir)
     create_directory(tmpdir)
-    # creates a branch from the commit reference and clones it into a new repository
     tmp_src_dir = os.path.join(tmpdir, 'build_src')
     tmp_src_dir = os.path.join(tmpdir, 'build_src')
-    branch_and_clone(task['remote'], task['git_hash_short'], sourcedir, tmp_src_dir, task['git_hash_short']+'_clone')
+    source_repo = ap_git.GitRepo.shallow_clone_at_commit_from_local(
+        source=sourcedir,
+        remote=task['remote'],
+        commit_ref=task['git_hash_short'],
+        dest=tmp_src_dir
+    )
     # update submodules in temporary source directory
     # update submodules in temporary source directory
-    update_submodules(tmp_src_dir)
+    source_repo.submodule_update(init=True, recursive=True, force=True)
     # checkout to the commit pointing to the requested commit
     # checkout to the commit pointing to the requested commit
-    do_checkout(task['remote'], task['git_hash_short'], tmp_src_dir)
+    source_repo.checkout_remote_commit_ref(
+        remote=task['remote'],
+        commit_ref=task['git_hash_short'],
+        force=True,
+        hard_reset=True,
+        clean_working_tree=True
+    )
     if not os.path.isfile(os.path.join(outdir, 'extra_hwdef.dat')):
     if not os.path.isfile(os.path.join(outdir, 'extra_hwdef.dat')):
         app.logger.error('Build aborted, missing extra_hwdef.dat')
         app.logger.error('Build aborted, missing extra_hwdef.dat')
     app.logger.info('Appending to build.log')
     app.logger.info('Appending to build.log')
@@ -523,12 +463,6 @@ def status_thread():
             pass
             pass
         time.sleep(3)
         time.sleep(3)
 
 
-def update_submodules(s_dir):
-    if not os.path.exists(s_dir):
-        return
-    app.logger.info('Updating submodules')
-    run_git(['git', 'submodule', 'update', '--recursive', '--force', '--init'], cwd=s_dir)
-
 app = Flask(__name__, template_folder='templates')
 app = Flask(__name__, template_folder='templates')
 
 
 if not os.path.isdir(outdir_parent):
 if not os.path.isdir(outdir_parent):
@@ -591,14 +525,19 @@ def generate():
             raise Exception("Commit reference invalid or not listed to be built for given vehicle for remote")
             raise Exception("Commit reference invalid or not listed to be built for given vehicle for remote")
 
 
         chosen_board = request.form['board']
         chosen_board = request.form['board']
-        head_lock.acquire()
-        do_checkout(chosen_remote, chosen_commit_reference, s_dir=sourcedir)
-        if chosen_board not in get_boards_from_ardupilot_tree(s_dir=sourcedir)[0]:
-            raise Exception("bad board")
-
-        #ToDo - maybe have the if-statement to check if it's changed.
-        build_options = get_build_options_from_ardupilot_tree(s_dir=sourcedir)
-        head_lock.release()
+        with repo.get_checkout_lock():
+            repo.checkout_remote_commit_ref(
+                remote=chosen_remote,
+                commit_ref=chosen_commit_reference,
+                force=True,
+                hard_reset=True,
+                clean_working_tree=True
+            )
+            if chosen_board not in get_boards_from_ardupilot_tree(s_dir=sourcedir)[0]:
+                raise Exception("bad board")
+
+            #ToDo - maybe have the if-statement to check if it's changed.
+            build_options = get_build_options_from_ardupilot_tree(s_dir=sourcedir)
 
 
         # fetch features from user input
         # fetch features from user input
         extra_hwdef = []
         extra_hwdef = []
@@ -639,9 +578,10 @@ def generate():
                         os.path.join(outdir_parent, 'extra_hwdef.dat'))
                         os.path.join(outdir_parent, 'extra_hwdef.dat'))
         os.remove(os.path.join(outdir_parent, 'extra_hwdef.dat'))
         os.remove(os.path.join(outdir_parent, 'extra_hwdef.dat'))
 
 
-        new_git_hash = chosen_commit_reference
-        if ref_is_branch(chosen_commit_reference) or ref_is_tag(chosen_commit_reference):
-            new_git_hash = find_hash_for_ref(chosen_remote, chosen_commit_reference)
+        new_git_hash = repo.commit_id_for_remote_ref(
+            remote=chosen_remote,
+            commit_ref=chosen_commit_reference
+        )
         git_hash_short = new_git_hash[:10]
         git_hash_short = new_git_hash[:10]
         app.logger.info('Git hash = ' + new_git_hash)
         app.logger.info('Git hash = ' + new_git_hash)
         selected_features_dict['git_hash_short'] = git_hash_short
         selected_features_dict['git_hash_short'] = git_hash_short
@@ -742,11 +682,16 @@ def boards_and_features(vehicle_name, remote_name, commit_reference):
 
 
     app.logger.info('Board list and build options requested for %s %s %s' % (vehicle_name, remote_name, commit_reference))
     app.logger.info('Board list and build options requested for %s %s %s' % (vehicle_name, remote_name, commit_reference))
     # getting board list for the branch
     # getting board list for the branch
-    head_lock.acquire()
-    do_checkout(remote_name, commit_reference, s_dir=sourcedir)
-    (boards, default_board) = get_boards_from_ardupilot_tree(s_dir=sourcedir)
-    options = get_build_options_from_ardupilot_tree(s_dir=sourcedir)   # this is a list of Feature() objects defined in build_options.py
-    head_lock.release()
+    with repo.get_checkout_lock():
+        repo.checkout_remote_commit_ref(
+            remote=remote_name,
+            commit_ref=commit_reference,
+            force=True,
+            hard_reset=True,
+            clean_working_tree=True,
+        )
+        (boards, default_board) = get_boards_from_ardupilot_tree(s_dir=sourcedir)
+        options = get_build_options_from_ardupilot_tree(s_dir=sourcedir)   # this is a list of Feature() objects defined in build_options.py
     # parse the set of categories from these objects
     # parse the set of categories from these objects
     categories = parse_build_categories(options)
     categories = parse_build_categories(options)
     features = []
     features = []