fix diff logic, and entry detection logic
[meego-infrastructure-tools:boss-participant-getchangelog.git] / get_relevant_changelog.py
1 #!/usr/bin/python
2 """ Quality check participant """
3
4 import difflib
5 import re
6 from buildservice import BuildService
7 from urllib2 import HTTPError
8
9 _blankre = re.compile(r"^\W*$")
10
11 def get_relevant_changelog(src_chlog, dst_chlog):
12     """ Diff two changelogs and return the list of lines that are only in
13         the source changelog """
14
15     relchlog = []
16     # compare source changelog to dest changelog
17     diff_txt = difflib.unified_diff(dst_chlog.splitlines(),
18                                     src_chlog.splitlines())
19     # Convert the diff text to a list of lines discarding the diff header
20     diff_list = list(diff_txt)[3:]
21     # Logic to compare changelogs and extract relevant entries 
22     for line in diff_list:
23         if line.startswith("+"):
24             entry = line.replace("+", "", 1)
25             relchlog.append(entry)
26         elif line.startswith("-"):
27             # As soon as we hit a removed line we skip out
28             break
29         else:
30             continue
31
32     # Now take the list of lines and create a list of changelog
33     # entries by splitting on blanks
34     ces = []
35     ce = ""
36     for line in relchlog:
37         if _blankre.match(line):
38             ces.append(ce)
39             ce = ""
40             continue # without adding the blank to the ce
41         ce += line + "\n"
42     # If we have any lines left they're a ce
43     if ce:
44         ces.append(ce)
45
46     return ces
47
48 class ParticipantHandler(object):
49
50     """ Participant class as defined by the SkyNET API """
51
52     def __init__(self):
53         self.obs = None
54         self.oscrc = None
55
56     def handle_wi_control(self, ctrl):
57         """ job control thread """
58         pass
59     
60     def handle_lifecycle_control(self, ctrl):
61         """ participant control thread """
62         if ctrl.message == "start":
63             if ctrl.config.has_option("obs", "oscrc"):
64                 self.oscrc = ctrl.config.get("obs", "oscrc")
65     
66     def setup_obs(self, namespace):
67         """ setup the Buildservice instance using the namespace as an alias
68             to the apiurl """
69
70         self.obs = BuildService(oscrc=self.oscrc, apiurl=namespace)
71
72     def get_changes_file(self, prj, pkg, rev=None):
73
74
75         """ Get a package's changes file """
76
77         changelog = ""
78         try:
79             file_list = self.obs.getPackageFileList(prj, pkg, revision=rev)
80             for fil in file_list:
81                 if fil.endswith(".changes"):
82                     changelog = self.obs.getFile(prj, pkg, fil, revision=rev)
83         except HTTPError, e:
84             if e.code == 404:
85                 pass
86
87         return changelog
88
89     def get_relevant_changelogs(self, wid):
90
91         """ Get relevant changelog entries for the actions of an OBS request
92             and enrich each action's data structure with them """
93
94         wid.result = False
95         actions = wid.fields.ev.actions
96
97         if not actions:
98             wid.__error__ = "A needed field does not exist."
99             return
100
101         use_rev = False
102         if wid.params.compare and wid.params.compare == "last_revision":
103             use_rev = True
104
105         for i in xrange(len(actions)):
106             src_chlog = ""
107             if use_rev:
108                 # get commit history
109                 commit_log = self.obs.getCommitLog(actions[i]['targetproject'],
110                                                    actions[i]['targetpackage'])
111                 # use the second last commit revision if available
112                 if len(commit_log) > 1 :
113                     src_chlog = self.get_changes_file(actions[i]['targetproject'],
114                                                       actions[i]['targetpackage'],
115                                                       str( commit_log[1][0] ))
116             else:
117                 src_chlog = self.get_changes_file(actions[i]['sourceproject'],
118                                                   actions[i]['sourcepackage'],
119                                                   actions[i]['sourcerevision'])
120
121             dst_chlog = self.get_changes_file(actions[i]['targetproject'],
122                                               actions[i]['targetpackage'])
123
124             rel_chlog = get_relevant_changelog(src_chlog, dst_chlog)
125
126             if rel_chlog:
127                 actions[i]["relevant_changelog"] = rel_chlog
128
129
130         wid.fields.ev.actions = actions
131         wid.result = True
132
133     def handle_wi(self, wid):
134
135         """ actual job thread """
136
137         # We may want to examine the fields structure
138         if wid.fields.debug_dump or wid.params.debug_dump:
139             print wid.dump() 
140
141         self.setup_obs(wid.fields.ev.namespace)
142         self.get_relevant_changelogs(wid)