/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
5752.3.8 by John Arbash Meinel
Merge bzr.dev 5764 to resolve release-notes (aka NEWS) conflicts
1
# Copyright (C) 2006, 2007, 2009, 2010, 2011 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1732.2.3 by Martin Pool
sign-my-commits just signs revisions in the branch's ancestry.
16
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
17
"""Command which looks for unsigned commits by the current user, and signs them.
18
"""
19
1996.3.14 by John Arbash Meinel
lazy_import osutils and sign_my_commits
20
from bzrlib.lazy_import import lazy_import
21
lazy_import(globals(), """
22
from bzrlib import (
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
23
    bzrdir as _mod_bzrdir,
5972.3.23 by Jelmer Vernooij
Fix handling of ghosts in sign_my_commits.
24
    errors,
1996.3.14 by John Arbash Meinel
lazy_import osutils and sign_my_commits
25
    gpg,
5972.3.2 by Jelmer Vernooij
Use iter_ancestry rather than get_ancestry.
26
    revision as _mod_revision,
1996.3.14 by John Arbash Meinel
lazy_import osutils and sign_my_commits
27
    )
28
""")
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
29
from bzrlib.commands import Command
30
from bzrlib.option import Option
5971.1.1 by Jonathan Riddell
add a verify command
31
from bzrlib.trace import note
5971.1.24 by Jonathan Riddell
fix translations for plural forms
32
from bzrlib.i18n import gettext, ngettext
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
33
34
class cmd_sign_my_commits(Command):
5131.2.1 by Martin
Permit bzrlib to run under python -OO by explictly assigning to __doc__ for user-visible docstrings
35
    __doc__ = """Sign all commits by a given committer.
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
36
37
    If location is not specified the local tree is used.
38
    If committer is not specified the default committer is used.
39
40
    This does not sign commits that already have signatures.
41
    """
1732.2.3 by Martin Pool
sign-my-commits just signs revisions in the branch's ancestry.
42
    # Note that this signs everything on the branch's ancestry
43
    # (both mainline and merged), but not other revisions that may be in the
44
    # repository
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
45
2598.1.2 by Martin Pool
Also check that option help ends in a period, and fix those that don't
46
    takes_options = [
47
            Option('dry-run',
48
                   help='Don\'t actually sign anything, just print'
49
                        ' the revisions that would be signed.'),
50
            ]
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
51
    takes_args = ['location?', 'committer?']
52
53
    def run(self, location=None, committer=None, dry_run=False):
54
        if location is None:
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
55
            bzrdir = _mod_bzrdir.BzrDir.open_containing('.')[0]
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
56
        else:
57
            # Passed in locations should be exact
5753.2.2 by Jelmer Vernooij
Remove some unnecessary imports, clean up lazy imports.
58
            bzrdir = _mod_bzrdir.BzrDir.open(location)
1732.2.3 by Martin Pool
sign-my-commits just signs revisions in the branch's ancestry.
59
        branch = bzrdir.open_branch()
60
        repo = branch.repository
1770.2.12 by Aaron Bentley
Merge bzr.dev
61
        branch_config = branch.get_config()
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
62
63
        if committer is None:
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
64
            committer = branch_config.username()
65
        gpg_strategy = gpg.GPGStrategy(branch_config)
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
66
67
        count = 0
1711.2.35 by John Arbash Meinel
sign-my-commits should take out a write lock.
68
        repo.lock_write()
69
        try:
5972.3.2 by Jelmer Vernooij
Use iter_ancestry rather than get_ancestry.
70
            graph = repo.get_graph()
3010.1.15 by Robert Collins
Manage write groups in sign_my_commits, for efficiency.
71
            repo.start_write_group()
72
            try:
5972.3.2 by Jelmer Vernooij
Use iter_ancestry rather than get_ancestry.
73
                for rev_id, parents in graph.iter_ancestry(
74
                        [branch.last_revision()]):
75
                    if _mod_revision.is_null(rev_id):
76
                        continue
5972.3.24 by Jelmer Vernooij
Simplify ghost check.
77
                    if parents is None:
78
                        # Ignore ghosts
79
                        continue
80
                    if repo.has_signature_for_revision_id(rev_id):
3010.1.15 by Robert Collins
Manage write groups in sign_my_commits, for efficiency.
81
                        continue
82
                    rev = repo.get_revision(rev_id)
83
                    if rev.committer != committer:
84
                        continue
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
85
                    # We have a revision without a signature who has a
3010.1.15 by Robert Collins
Manage write groups in sign_my_commits, for efficiency.
86
                    # matching committer, start signing
87
                    print rev_id
88
                    count += 1
89
                    if not dry_run:
90
                        repo.sign_revision(rev_id, gpg_strategy)
91
            except:
92
                repo.abort_write_group()
93
                raise
94
            else:
95
                repo.commit_write_group()
1711.2.35 by John Arbash Meinel
sign-my-commits should take out a write lock.
96
        finally:
97
            repo.unlock()
1185.78.6 by John Arbash Meinel
Adding sign-my-commits as a builtin, along with some simple tests.
98
        print 'Signed %d revisions' % (count,)
99
100
5971.1.52 by Jonathan Riddell
change command name to verify-signatures
101
class cmd_verify_signatures(Command):
5971.1.3 by Jonathan Riddell
tidying up
102
    __doc__ = """Verify all commit signatures.
103
104
    Verifies that all commits in the branch are signed by known GnuPG keys.
5971.1.1 by Jonathan Riddell
add a verify command
105
    """
5971.1.3 by Jonathan Riddell
tidying up
106
5971.1.12 by Jonathan Riddell
add acceptable-keys option
107
    takes_options = [
108
            Option('acceptable-keys',
109
                   help='Comma separated list of GPG key patterns which are'
110
                        ' acceptable for verification.',
111
                   short_name='k',
112
                   type=str,),
5971.1.17 by Jonathan Riddell
add verbose option
113
            'revision', 
114
            'verbose',
5971.1.53 by Jonathan Riddell
add directory option
115
            'directory',
5971.1.15 by Jonathan Riddell
add a revision argument to bzr verify
116
          ]
5971.1.12 by Jonathan Riddell
add acceptable-keys option
117
5971.1.53 by Jonathan Riddell
add directory option
118
    def run(self, acceptable_keys=None, revision=None, verbose=None,
119
                                                            directory=u'.'):
120
        bzrdir = _mod_bzrdir.BzrDir.open_containing(directory)[0]
5971.1.1 by Jonathan Riddell
add a verify command
121
        branch = bzrdir.open_branch()
122
        repo = branch.repository
123
        branch_config = branch.get_config()
124
        gpg_strategy = gpg.GPGStrategy(branch_config)
5971.1.56 by Jonathan Riddell
add an option for acceptable_keys in config, also make config docs match reality for signature options
125
126
        acceptable_keys_config = branch_config.acceptable_keys()
127
        try:
128
            acceptable_keys_config = str(acceptable_keys_config)
129
        except UnicodeEncodeError:
130
            raise errors.BzrCommandError('Only ASCII permitted in option names')
131
132
        if acceptable_keys_config is not None:
133
            gpg_strategy.set_acceptable_keys(acceptable_keys_config)
134
        if acceptable_keys is not None: #command line overrides config
5971.1.12 by Jonathan Riddell
add acceptable-keys option
135
            gpg_strategy.set_acceptable_keys(acceptable_keys)
5971.1.1 by Jonathan Riddell
add a verify command
136
137
        count = {gpg.SIGNATURE_VALID: 0,
138
                 gpg.SIGNATURE_KEY_MISSING: 0,
139
                 gpg.SIGNATURE_NOT_VALID: 0,
140
                 gpg.SIGNATURE_NOT_SIGNED: 0}
141
        result = []
5971.1.15 by Jonathan Riddell
add a revision argument to bzr verify
142
        revisions = []
5971.1.50 by Jonathan Riddell
merge in trunk
143
5971.1.15 by Jonathan Riddell
add a revision argument to bzr verify
144
        if revision is not None:
145
            if len(revision) == 1:
146
                revno, rev_id = revision[0].in_history(branch)
147
                revisions.append(rev_id)
148
            elif len(revision) == 2:
149
                from_revno, from_revid = revision[0].in_history(branch)
150
                to_revno, to_revid = revision[1].in_history(branch)
151
                if to_revid is None:
152
                    to_revno = branch.revno()
153
                if from_revno is None or to_revno is None:
5971.1.51 by Jonathan Riddell
fix string formatting
154
                    raise errors.BzrCommandError('Cannot verify a range of '\
155
                                               'non-revision-history revisions')
5971.1.15 by Jonathan Riddell
add a revision argument to bzr verify
156
                for revno in range(from_revno, to_revno + 1):
157
                    revisions.append(branch.get_rev_id(revno))
158
        else:
5971.1.16 by Jonathan Riddell
tidying
159
            #all revisions by default including merges
5971.1.50 by Jonathan Riddell
merge in trunk
160
            graph = repo.get_graph()
161
            revisions = []
162
            repo.lock_read()
163
            for rev_id, parents in graph.iter_ancestry(
164
                    [branch.last_revision()]):
165
                if _mod_revision.is_null(rev_id):
166
                    continue
167
                if parents is None:
168
                    # Ignore ghosts
169
                    continue
170
                revisions.append(rev_id)
171
            repo.unlock()
5971.1.15 by Jonathan Riddell
add a revision argument to bzr verify
172
        for rev_id in revisions:
5971.1.22 by Jonathan Riddell
fix tests
173
            verification_result, uid = repo.verify_revision(rev_id,gpg_strategy)
5971.1.18 by Jonathan Riddell
add email to verbose output
174
            result.append([rev_id, verification_result, uid])
5971.1.3 by Jonathan Riddell
tidying up
175
            count[verification_result] += 1
5971.1.2 by Jonathan Riddell
give result to user
176
177
        if count[gpg.SIGNATURE_VALID] > 0 and \
178
           count[gpg.SIGNATURE_KEY_MISSING] == 0 and \
179
           count[gpg.SIGNATURE_NOT_VALID] == 0 and \
180
           count[gpg.SIGNATURE_NOT_SIGNED] == 0:
5971.1.19 by Jonathan Riddell
i18n
181
               note(gettext("All commits signed with verifiable keys"))
5971.1.17 by Jonathan Riddell
add verbose option
182
               if verbose:
5971.1.25 by Jonathan Riddell
Add _print_verbose_valid_message()
183
                   self._print_verbose_valid_message(result)
5971.1.2 by Jonathan Riddell
give result to user
184
               return 0
185
        else:
5971.1.19 by Jonathan Riddell
i18n
186
            note(gettext("{0} commits with valid signatures").format(
5971.1.2 by Jonathan Riddell
give result to user
187
                                        count[gpg.SIGNATURE_VALID]))
5971.1.20 by Jonathan Riddell
add verbose for bad signatures too
188
            if verbose:
5971.1.25 by Jonathan Riddell
Add _print_verbose_valid_message()
189
               self._print_verbose_valid_message(result)
5971.1.20 by Jonathan Riddell
add verbose for bad signatures too
190
            #TODO verbose for the other types too
5971.1.24 by Jonathan Riddell
fix translations for plural forms
191
            note(ngettext("{0} commit with unknown key",
192
                          "{0} commits with unknown keys",
193
                          count[gpg.SIGNATURE_KEY_MISSING]).format(
5971.1.2 by Jonathan Riddell
give result to user
194
                                        count[gpg.SIGNATURE_KEY_MISSING]))
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
195
            if verbose:
196
               self._print_verbose_missing_key_message(result)
5971.1.24 by Jonathan Riddell
fix translations for plural forms
197
            note(ngettext("{0} commit not valid",
198
                          "{0} commits not valid",
199
                          count[gpg.SIGNATURE_NOT_VALID]).format(
5971.1.2 by Jonathan Riddell
give result to user
200
                                        count[gpg.SIGNATURE_NOT_VALID]))
5971.1.29 by Jonathan Riddell
verbose for invalid signatures
201
            if verbose:
202
               self._print_verbose_not_valid(result, repo)
5971.1.24 by Jonathan Riddell
fix translations for plural forms
203
            note(ngettext("{0} commit not signed",
204
                          "{0} commits not signed",
205
                          count[gpg.SIGNATURE_NOT_SIGNED]).format(
5971.1.2 by Jonathan Riddell
give result to user
206
                                        count[gpg.SIGNATURE_NOT_SIGNED]))
5971.1.28 by Jonathan Riddell
verbose for not signed
207
            if verbose:
208
               self._print_verbose_not_signed(result, repo)
5971.1.2 by Jonathan Riddell
give result to user
209
            return 1
5971.1.25 by Jonathan Riddell
Add _print_verbose_valid_message()
210
5971.1.29 by Jonathan Riddell
verbose for invalid signatures
211
    def _print_verbose_not_valid(self, result, repo):
212
        """takes a verify result and prints out not signed commit info"""
213
        signers = {}
214
        for rev_id, validity, empty in result:
215
            if validity == gpg.SIGNATURE_NOT_VALID:
216
                revision = repo.get_revision(rev_id)
217
                authors = ', '.join(revision.get_apparent_authors())
218
                signers.setdefault(authors, 0)
219
                signers[authors] += 1
220
        for authors, number in signers.items():
221
            note(gettext(ngettext("  {0} commit by author {1}", 
222
                                "  {0} commits by author {1}",
223
                            number)).format(number, authors))
224
5971.1.28 by Jonathan Riddell
verbose for not signed
225
    def _print_verbose_not_signed(self, result, repo):
226
        """takes a verify result and prints out not signed commit info"""
227
        signers = {}
228
        for rev_id, validity, empty in result:
229
            if validity == gpg.SIGNATURE_KEY_MISSING:
230
                revision = repo.get_revision(rev_id)
231
                authors = ', '.join(revision.get_apparent_authors())
232
                signers.setdefault(authors, 0)
233
                signers[authors] += 1
234
        for authors, number in signers.items():
235
            note(gettext(ngettext("  {0} commit by author {1}", 
236
                                "  {0} commits by author {1}",
237
                            number)).format(number, authors))
238
5971.1.27 by Jonathan Riddell
verbose info for unknown keys
239
    def _print_verbose_missing_key_message(self, result):
240
        """takes a verify result and prints out missing key info"""
241
        signers = {}
242
        for rev_id, validity, fingerprint in result:
243
            if validity == gpg.SIGNATURE_KEY_MISSING:
244
                signers.setdefault(fingerprint, 0)
245
                signers[fingerprint] += 1
246
        for fingerprint, number in signers.items():
247
            note(gettext(ngettext("  Unknown key {0} signed {1} commit", 
248
                                "  Unknown key {0} signed {1} commits",
249
                            number)).format(fingerprint, number))
250
5971.1.25 by Jonathan Riddell
Add _print_verbose_valid_message()
251
    def _print_verbose_valid_message(self, result):
5971.1.26 by Jonathan Riddell
Add _print_verbose_valid_message() doc
252
        """takes a verify result and prints out number of signed commits"""
5971.1.25 by Jonathan Riddell
Add _print_verbose_valid_message()
253
        signers = {}
254
        for rev_id, validity, uid in result:
255
            if validity == gpg.SIGNATURE_VALID:
256
                signers.setdefault(uid, 0)
257
                signers[uid] += 1
258
        for uid, number in signers.items():
259
            note(gettext(ngettext("  {0} signed {1} commit", 
260
                                "  {0} signed {1} commits",
261
                            number)).format(uid, number))