blob: 89072b49c3904b788d2cc1cfcc6622a78358d5a9 [file] [log] [blame]
Hirthanan Subenderan3e884d62020-01-23 13:12:45 -08001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-"
3#
4# Copyright 2020 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Find missing stable and backported mainline fix patches in chromeos."""
9
10from __future__ import print_function
11import os
12import subprocess
13import sys
14import sqlite3
15import re
16from enum import Enum
17
18import config
19from common import stabledb, UPSTREAMDB, \
20 stable_branch, chromeosdb, chromeos_branch
21
22
23CHANGEID = re.compile(r'^( )*Change-Id: [a-zA-Z0-9]*$')
24
25
26class Path(Enum):
27 """Enum representing repo path as Stable or ChromeOS."""
28 stable = 1
29 chromeos = 2
30
31
32def get_status(sha):
33 """Check if patch needs to be applied to current branch.
34
35 The working directory and branch must be set when calling
36 this function.
37
38 Return 0 if the patch has already been applied,
39 1 if the patch is missing and applies cleanly,
40 2 if the patch is missing and fails to apply.
41 """
42 ret = 0
43
44 cmd = 'git reset --hard HEAD'
45 subprocess.run(cmd.split(' '), stdout=subprocess.DEVNULL,
46 stderr=subprocess.DEVNULL)
47
48 try:
49 # Returns 0 on success, else a non-zero status code
50 result = subprocess.call(['git', 'cherry-pick', '-n', sha],
51 stdout=subprocess.DEVNULL,
52 stderr=subprocess.DEVNULL)
53
54 if result:
55 ret = 2
56 else:
57 diff = subprocess.check_output(['git', 'diff', 'HEAD'])
58 if diff:
59 ret = 1
60 except subprocess.CalledProcessError:
61 ret = 2
62
63 cmd = 'git reset --hard HEAD'
64 subprocess.run(cmd.split(' '), stdout=subprocess.DEVNULL,
65 stderr=subprocess.DEVNULL)
66
67
68 return ret
69
70
71def usha_to_downstream_sha(sdb, usha):
72 """Retrieves chromeos/stable sha by indexing db.
73
74 Returns sha or None if upstream sha doesn't exist downstream.
75 """
76 cs = sdb.cursor()
77 cs.execute("SELECT sha from commits where usha is '%s'" % usha)
78 row = cs.fetchone()
79
80 return row[0] if row else None
81
82
83def parse_changeID(chromeos_sha):
84 """String searches for Change-Id in a chromeos git commit.
85
86 Returns Change-Id or None if commit doesn't have associated Change-Id
87 """
88 commit = subprocess.check_output(['git', 'show', \
89 chromeos_sha]).decode('utf-8', errors='ignore')
90
91 for line in commit.splitlines():
92 if CHANGEID.match(line):
93 # removes whitespace prefixing Change-Id
94 line = line.lstrip()
95 commit_changeID = line[(line.index(' ') + 1):]
96 return commit_changeID
97
98 return None
99
100
101def get_context(bname, sdb, udb, usha, recursive):
102 """Outputs dependency of patches and fixes needed in chromeOS."""
103 cs = sdb.cursor()
104 cu = udb.cursor()
105
106 cu.execute("select sha, description from commits where sha is '%s'" % usha)
107 found = False
108
109 for (sha, description) in cu.fetchall():
110 # usha -> sha maping should be 1:1
111 # If it isn't, skip duplicate entries.
112 if found:
113 print('Already found usha->sha mapping for %s , skipping row.' % sha)
114 continue
115 found = True
116 cu.execute("select fsha, patchid, ignore from fixes where sha='%s'" % usha)
117 # usha, however, may have multiple fixes
118 printed = recursive
119 for (fsha, patchid, ignore) in cu.fetchall():
120 if ignore:
121 continue
122 # Check if the fix (fsha) is in our code base or
123 # try to find it using its patch ID.
124 cs.execute("select sha, usha from commits \
125 where usha is '%s' or patchid is '%s'" % (fsha, patchid))
126 fix = cs.fetchone()
127 if not fix:
128 status = get_status(fsha)
129 if status != 0:
130 if not printed:
131 downstream_sha = usha_to_downstream_sha(sdb, usha)
132 print("\n[downstream_sha %s] [usha %s] ('%s')"
133 % (downstream_sha, usha, description))
134 printed = True
135 space_str = ' ' if recursive else ' '
136 print('%sCommit (upstream) %s fixed by commit (upstream) %s' %
137 (space_str, usha, fsha))
138 if status == 1:
139 print(' %sFix is missing from %s and applies cleanly'
140 % (space_str, bname))
141 else:
142 print(' %sFix may be missing from %s; '
143 'trying to apply it results in conflicts/errors' %
144 (space_str, bname))
145 get_context(bname, sdb, udb, fsha, True)
146
147
148def missing(version, release):
149 """Look for missing Fixup commits in provided chromeos or stable release."""
150
151 bname = stable_branch(version) if release == Path.stable \
152 else chromeos_branch(version)
153
154 print('Checking branch %s' % bname)
155
156 subprocess.check_output(['git', 'checkout', bname], stderr=subprocess.DEVNULL)
157
158 chosen_db = stabledb(version) if release == Path.stable \
159 else chromeosdb(version)
160
161 sdb = sqlite3.connect(chosen_db)
162 cs = sdb.cursor()
163 udb = sqlite3.connect(UPSTREAMDB)
164
165 cs.execute("select usha from commits where usha != ''")
166 for usha in cs.fetchall():
167 get_context(bname, sdb, udb, usha[0], False)
168
169 udb.close()
170 sdb.close()
171
172
173def findmissing_helper(release):
174 """Helper to find missing patches in the stable and chromeos releases."""
175 if len(sys.argv) > 1:
176 branches = sys.argv[1:]
177 else:
178 branches = config.STABLE_BRANCHES if release == Path.stable \
179 else config.CHROMEOS_BRANCHES
180
181 path = config.STABLE_PATH if release == Path.stable \
182 else config.CHROMEOS_PATH
183 os.chdir(path)
184
185 for b in branches:
186 missing(b, release)
187
188
189def findmissing():
190 """Finds missing patches in stable and chromeos releases."""
191 cur_wd = os.getcwd()
192
193 print('--Missing patches from baseline -> stable.--')
194 findmissing_helper(Path.stable)
195
196 os.chdir(cur_wd)
197
198 print('--Missing patches from baseline -> chromeos.--')
199 findmissing_helper(Path.chromeos)
200
201
202if __name__ == '__main__':
203 findmissing()