summaryrefslogtreecommitdiffstats
path: root/meta/lib/patchtest/repo.py
diff options
context:
space:
mode:
authorTrevor Gamblin <tgamblin@baylibre.com>2023-10-16 15:44:56 -0400
committerRichard Purdie <richard.purdie@linuxfoundation.org>2023-10-17 11:41:34 +0100
commit9d137188ad03c111ff8df7396b6b3dfd59307ac0 (patch)
tree6dd7227dec950282973d21e9cfd20b5e505f6bb1 /meta/lib/patchtest/repo.py
parent790aa2096f7c6be92f994a1f8ec22d3bef91f337 (diff)
downloadpoky-9d137188ad03c111ff8df7396b6b3dfd59307ac0.tar.gz
patchtest: add supporting modules
Add modules that support core patchtest functionality to meta/lib/patchtest. These include classes and functions for handling repository and patch objects, parsing the patchtest CLI arguments, and other utilities. (From OE-Core rev: 499cdad7a16f6cc256837069c7add294132127a4) Signed-off-by: Trevor Gamblin <tgamblin@baylibre.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
Diffstat (limited to 'meta/lib/patchtest/repo.py')
-rw-r--r--meta/lib/patchtest/repo.py185
1 files changed, 185 insertions, 0 deletions
diff --git a/meta/lib/patchtest/repo.py b/meta/lib/patchtest/repo.py
new file mode 100644
index 0000000000..5c85c65ffb
--- /dev/null
+++ b/meta/lib/patchtest/repo.py
@@ -0,0 +1,185 @@
1# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3#
4# patchtestrepo: PatchTestRepo class used mainly to control a git repo from patchtest
5#
6# Copyright (C) 2016 Intel Corporation
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License version 2 as
10# published by the Free Software Foundation.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License along
18# with this program; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
21import os
22import utils
23import logging
24import json
25from patch import PatchTestPatch
26
27logger = logging.getLogger('patchtest')
28info=logger.info
29
30class PatchTestRepo(object):
31
32 # prefixes used for temporal branches/stashes
33 prefix = 'patchtest'
34
35 def __init__(self, patch, repodir, commit=None, branch=None):
36 self._repodir = repodir
37 self._patch = PatchTestPatch(patch)
38 self._current_branch = self._get_current_branch()
39
40 # targeted branch defined on the patch may be invalid, so make sure there
41 # is a corresponding remote branch
42 valid_patch_branch = None
43 if self._patch.branch in self.upstream_branches():
44 valid_patch_branch = self._patch.branch
45
46 # Target Branch
47 # Priority (top has highest priority):
48 # 1. branch given at cmd line
49 # 2. branch given at the patch
50 # 3. current branch
51 self._branch = branch or valid_patch_branch or self._current_branch
52
53 # Target Commit
54 # Priority (top has highest priority):
55 # 1. commit given at cmd line
56 # 2. branch given at cmd line
57 # 3. branch given at the patch
58 # 3. current HEAD
59 self._commit = self._get_commitid(commit) or \
60 self._get_commitid(branch) or \
61 self._get_commitid(valid_patch_branch) or \
62 self._get_commitid('HEAD')
63
64 self._workingbranch = "%s_%s" % (PatchTestRepo.prefix, os.getpid())
65
66 # create working branch
67 self._exec({'cmd': ['git', 'checkout', '-b', self._workingbranch, self._commit]})
68
69 self._patchmerged = False
70
71 # Check if patch can be merged using git-am
72 self._patchcanbemerged = True
73 try:
74 self._exec({'cmd': ['git', 'am', '--keep-cr'], 'input': self._patch.contents})
75 except utils.CmdException as ce:
76 self._exec({'cmd': ['git', 'am', '--abort']})
77 self._patchcanbemerged = False
78 finally:
79 # if patch was applied, remove it
80 if self._patchcanbemerged:
81 self._exec({'cmd':['git', 'reset', '--hard', self._commit]})
82
83 # for debugging purposes, print all repo parameters
84 logger.debug("Parameters")
85 logger.debug("\tRepository : %s" % self._repodir)
86 logger.debug("\tTarget Commit : %s" % self._commit)
87 logger.debug("\tTarget Branch : %s" % self._branch)
88 logger.debug("\tWorking branch : %s" % self._workingbranch)
89 logger.debug("\tPatch : %s" % self._patch)
90
91 @property
92 def patch(self):
93 return self._patch.path
94
95 @property
96 def branch(self):
97 return self._branch
98
99 @property
100 def commit(self):
101 return self._commit
102
103 @property
104 def ismerged(self):
105 return self._patchmerged
106
107 @property
108 def canbemerged(self):
109 return self._patchcanbemerged
110
111 def _exec(self, cmds):
112 _cmds = []
113 if isinstance(cmds, dict):
114 _cmds.append(cmds)
115 elif isinstance(cmds, list):
116 _cmds = cmds
117 else:
118 raise utils.CmdException({'cmd':str(cmds)})
119
120 results = []
121 cmdfailure = False
122 try:
123 results = utils.exec_cmds(_cmds, self._repodir)
124 except utils.CmdException as ce:
125 cmdfailure = True
126 raise ce
127 finally:
128 if cmdfailure:
129 for cmd in _cmds:
130 logger.debug("CMD: %s" % ' '.join(cmd['cmd']))
131 else:
132 for result in results:
133 cmd, rc, stdout, stderr = ' '.join(result['cmd']), result['returncode'], result['stdout'], result['stderr']
134 logger.debug("CMD: %s RCODE: %s STDOUT: %s STDERR: %s" % (cmd, rc, stdout, stderr))
135
136 return results
137
138 def _get_current_branch(self, commit='HEAD'):
139 cmd = {'cmd':['git', 'rev-parse', '--abbrev-ref', commit]}
140 cb = self._exec(cmd)[0]['stdout']
141 if cb == commit:
142 logger.warning('You may be detached so patchtest will checkout to master after execution')
143 cb = 'master'
144 return cb
145
146 def _get_commitid(self, commit):
147
148 if not commit:
149 return None
150
151 try:
152 cmd = {'cmd':['git', 'rev-parse', '--short', commit]}
153 return self._exec(cmd)[0]['stdout']
154 except utils.CmdException as ce:
155 # try getting the commit under any remotes
156 cmd = {'cmd':['git', 'remote']}
157 remotes = self._exec(cmd)[0]['stdout']
158 for remote in remotes.splitlines():
159 cmd = {'cmd':['git', 'rev-parse', '--short', '%s/%s' % (remote, commit)]}
160 try:
161 return self._exec(cmd)[0]['stdout']
162 except utils.CmdException:
163 pass
164
165 return None
166
167 def upstream_branches(self):
168 cmd = {'cmd':['git', 'branch', '--remotes']}
169 remote_branches = self._exec(cmd)[0]['stdout']
170
171 # just get the names, without the remote name
172 branches = set(branch.split('/')[-1] for branch in remote_branches.splitlines())
173 return branches
174
175 def merge(self):
176 if self._patchcanbemerged:
177 self._exec({'cmd': ['git', 'am', '--keep-cr'],
178 'input': self._patch.contents,
179 'updateenv': {'PTRESOURCE':self._patch.path}})
180 self._patchmerged = True
181
182 def clean(self):
183 self._exec({'cmd':['git', 'checkout', '%s' % self._current_branch]})
184 self._exec({'cmd':['git', 'branch', '-D', self._workingbranch]})
185 self._patchmerged = False