~racb/apport/877852

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
#!/usr/bin/python
# Author: Brian Murray <brian@canonical.com>
# Copyright (C) 2009 Canonical, Ltd.
# License: GPLv3
#
# Given a package name and some criteria query Launchpad for a bug list
# then check each bug against the package's bug pattern
#
# missing the ability to search for no tags

from apport.crashdb import get_crashdb
from datetime import datetime
from datetime import timedelta
from launchpadlib.launchpad import Launchpad
from launchpadlib.credentials import Credentials
from launchpadlib.errors import HTTPError

import apport
import optparse
import os
import re
import sys
import tempfile

def connect():
    cachedir = os.path.expanduser('~/.launchpadlib/cache')
    if not os.path.exists(cachedir):
        os.makedirs(cachedir,0700)

    root = 'production'
    credfile = os.path.expanduser('~/.cache/apport/launchpad.credentials')
    launchpad = Launchpad.login_with(sys.argv[0], credentials_file=credfile,
            service_root=root, launchpadlib_dir=cachedir, version="devel")
    return launchpad

def mark_as_duplicate(master_number, bug):
    comment="Thank you for taking the time to report this bug and helping to make Ubuntu better. This particular bug has already been reported and is a duplicate of bug %s, so it is being marked as such. Please look at the other bug report to see if there is any missing information that you can provide, or to see if there is a workaround for the bug.  Additionally, any further discussion regarding the bug should occur in the other report.  Please continue to report any other bugs you may find." % (master_number)
    master = lp.bugs[master_number]
    # 2011-02-17 this is redundant as it is built into LP (I think)
    #if bug.duplicates:
    #    for duplicate in bug.duplicates:
    #        mark_as_duplicate(master_number, duplicate)
    #        print 'Taking care of a bug with duplicates'
    #    mark_as_duplicate(master_number, bug)
    #else:
    for task in bug.bug_tasks:
        task.status = 'Confirmed'
    bug.newMessage(content=comment)
    bug.duplicate_of = master
    bug.lp_save()
    print 'Modified LP: #%s' % ( task.bug.id )


def trim_dpkg_log(report):
    '''Trim DpkgTerminalLog to the most recent installation session.
       Taken from apport data/general-hook/ubuntu.py'''

    if 'DpkgTerminalLog' not in report:
        return
    lines = []
    trim_re = re.compile('^\(.* ... \d+ .*\)$')
    for line in report['DpkgTerminalLog'].splitlines():
        if line.startswith('Log started: ') or trim_re.match(line):
            lines = []
            continue
        lines.append(line)
    report['DpkgTerminalLog'] = '\n'.join(lines)


parser = optparse.OptionParser(usage="usage: %prog -p PACKAGE -t TAG(s) "\
    "[options]")
parser.add_option("-p", "--package", help="Filter on PACKAGE", dest="package",
    metavar="PACKAGE")
parser.add_option("-s", "--status", help="Filter on STATUS", dest="status",
    metavar="STATUS")
parser.add_option("-t", "--tags", help="Filter on TAG,TAG", dest="tags",
    metavar="TAGS")
parser.add_option("-d", "--dupes", help="Include duplicates in search",
    dest="dupes", action="store_true")
parser.add_option("-q", "--quiet", help="Only print bug numbers",
    dest="quiet", action="store_true")
parser.add_option("-a", "--all", help="Check all package bugs", dest="all",
    action="store_true")
parser.add_option("-k", "--keywords", help="Search for KEYWORDS",
    dest="keywords", metavar="KEYWORDS")
parser.add_option("-w", "--within", help="Search for bugs reported within the past X days",
    dest="within", metavar="WITHIN")
parser.add_option("-C", "--consolidate", dest="consolidate",
    help="Mark bugs as duplicate of master bug in pattern",
    action="store_true")

(opt, args) = parser.parse_args()

# Connect to Launchpad
lp = connect()

status_list = []
tag_list = []
ubuntu = lp.distributions['ubuntu']

valid_status = ['New', 'Incomplete', 'Invalid', "Won't Fix", 'Confirmed',
                'Triaged', 'In Progress', 'Fix Committed', 'Fix Released',
                'Unknown']

if not opt.status:
    status_list = ['New', 'Incomplete', 'Confirmed', 'Triaged',
                   'In Progress', 'Fix Committed' ]
elif opt.status:
    if opt.status not in valid_status:
        print >> sys.stderr, ("Invalid status '%s'. Aborting") % (opt.status)
        sys.exit(1)
    else:
        status_list.append(opt.status)

if opt.package == 'ubuntu':
    package = ubuntu
if opt.package != 'ubuntu':
    package = ubuntu.getSourcePackage(name='%s' % opt.package)
    if package is None:
        print 'Package %s not found in Ubuntu' % opt.package
        sys.exit(1)
elif not opt.package:
    print >> sys.stderr, ("A package is required.")
    sys.exit(1)

if opt.dupes:
    dupes = False
elif not opt.dupes:
    dupes = True

if opt.tags:
    for tag in opt.tags.split(','):
        tag_list.append(tag)

if opt.within:
    period = int(opt.within)
    today = datetime.utcnow()
    search_start = today - timedelta(period)
elif not opt.within:
    search_start = None


search_args = {'tags': tag_list,
               'tags_combinator': 'All',
               'order_by': '-datecreated',
               'status': opt.status,
               'search_text': opt.keywords,
               'omit_duplicates': dupes,
               'created_since': search_start}

tasks = package.searchTasks(**search_args)
db = get_crashdb(None)
for task in tasks:
    # they should be retraced first
    if 'need-i386-retrace' in task.bug.tags or 'need-amd64-retrace' in task.bug.tags:
        continue
    try:
        report = db.download(task.bug.id)
    except AssertionError, error:
        print "LP: #%s: %s" % (task.bug.id, error)
        continue
    except Exception, error:
        print "LP: #%s: %s" % (task.bug.id, error)
        continue
    except AttributeError, error:
        print "LP: #%s: %s" % (task.bug.id, error)
        continue

    # trim the dpkg log file
    trim_dpkg_log(report)

    try:
        match = report.search_bug_patterns('bugpatterns.xml')
    except AssertionError, error:
        print "%s" % error
        continue

    if match and not opt.quiet:
        # this should handle wiki urls somehow
        master_number = match.split('/')[-1]
        if str(master_number) == str(task.bug.id) and not opt.all:
            print "Reached master bug LP: #%s" % master_number
            break
        print 'LP: #%s (%s, %s): Matched bug pattern: %s' % (task.bug.id,
            task.status, task.importance, match)
        if opt.consolidate:
            if task.bug.duplicate_of:
                print 'LP: #%s is already a duplicate of another bug' % (task.bug.id)
                break
            elif int(task.bug.id) == int(master_number):
                continue
            mark_as_duplicate(master_number, task.bug)
    elif match and opt.quiet:
        print '%s' % (task.bug.id)