diff options
378 files changed, 10106 insertions, 1670 deletions
diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values index a68a21d..4b8a017 100644 --- a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values +++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/4be73baf-e46b-4acb-a58e-4719e57c550b/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Fri, 18 Apr 2008 11:21:03 +0000 - - - - - - -From=benf +Date: Fri, 18 Apr 2008 11:21:03 +0000 +From: benf diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body new file mode 100644 index 0000000..19b1cf5 --- /dev/null +++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/body @@ -0,0 +1 @@ +We could add this functionality to update_copyright.sh diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values new file mode 100644 index 0000000..0b164a1 --- /dev/null +++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/comments/d5ed4f87-f1a1-4138-b0ad-190e4a49d820/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 14:08:45 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 4be73baf-e46b-4acb-a58e-4719e57c550b + diff --git a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values index a2d65ff..2094f46 100644 --- a/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values +++ b/.be/bugs/00f26f04-9202-4288-8744-b29abc2342d6/values @@ -7,7 +7,7 @@ creator: benf severity: minor -status: closed +status: fixed summary: Address is outdated for FSF offices diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/body new file mode 100644 index 0000000..d3f00e7 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/body @@ -0,0 +1,25 @@ +Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: + +> the basic idea is to take a look at all public branches (for exaple +> all on lp/bitbucket/github) in order to tell the user of a +> webinterface that bug foo is fixed in branch xyz, and if its merged to +> the main branch + +I don't understand. The state of the bug in the main branch is right +there in the main branch; if it's not fixed there, it's not fixed there. +If it's merged in from a different branch, the bug state follows all the +other changes when they come in. + +Can you give an example of what would be done differently? + +-- + \ “The basic fact about human existence is not that it is a | + `\ tragedy, but that it is a bore.” —Henry L. Mencken | +_o__) | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/values new file mode 100644 index 0000000..c3d2045 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/0f60a148-7024-44bd-bbed-377cbece9d1b/values @@ -0,0 +1,14 @@ +Alt-id: <874otjmjhr.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 23:34:08 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: 88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/body new file mode 100644 index 0000000..1f6d84b --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/body @@ -0,0 +1,28 @@ +On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> 1. is there any way to aggregate over multiple public branches in order +> to get the complete bug state + +Keeping the bug data with the source helps synchronize bug state and +source code. Bug state in branch A may not apply to branch B. Some +people like to weaken this source-bug linkage by keeping their bugs in +a branch all by themselves (ditz [http://ditz.rubyforge.org/] +currently supports this workflow). It sounds like you want to move +from "bugs with code" to "bugs and code in separate branches". We +don't have an easy way to do that in BE at the moment, since +version-control systems like Git have a single working branch at a +time (I think :p). What VCS are you using as a backend? + +> 2. is there any model for storing bigger files at a central place (for +> some of my bugs i have multi-megabyte tarballs attached) + + be comment ID "See the tarball at http://yourpage/something.tar.gz" +Then to grab the tarball, you'd use: + wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +to grab it. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/values new file mode 100644 index 0000000..ed9c16f --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/13012b22-2d02-444c-87c0-8cf0f17137ae/values @@ -0,0 +1,14 @@ +Alt-id: <20090711125030.GA18185@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 08:50:30 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/body new file mode 100644 index 0000000..bd9e63a --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/body @@ -0,0 +1,25 @@ +Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: + +> 1. is there any way to aggregate over multiple public branches in +> order to get the complete bug state + +The bug state is as complete as the source code state. It's exactly as +aggregated as the rest of the source code; the “complete bug state” +would be the integration branch where you merge all the feature branches +and bug-fix branches together. + +If instead you want bugs to *not* be tightly linked with the rest of the +source code state, it seems you don't want a distributed bug tracker +like Bugs Everywhere. + +-- + \ “I cannot conceive that anybody will require multiplications at | + `\ the rate of 40,000 or even 4,000 per hour …” —F. H. Wales, 1936 | +_o__) | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/values new file mode 100644 index 0000000..6958136 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/1f9f60de-ba37-42bc-a1c0-dc062ef255e1/values @@ -0,0 +1,14 @@ +Alt-id: <878wivmjm1.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 23:31:34 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: 88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/body new file mode 100644 index 0000000..11f344c --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/body @@ -0,0 +1,93 @@ +On Mon, Jul 13, 2009 at 09:05:34AM +0200, Ronny Pfannschmidt wrote: +> On Sun, 2009-07-12 at 19:55 -0400, W. Trevor King wrote: +> > On Sun, Jul 12, 2009 at 11:20:10PM +0200, Ronny Pfannschmidt wrote: +> > > On Sat, 2009-07-11 at 11:25 -0400, W. Trevor King wrote: +> > > > On Sat, Jul 11, 2009 at 03:13:05PM +0200, Ronny Pfannschmidt wrote: +> > > > > On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> > > > > > On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > > > > > > 2. is there any model for storing bigger files at a central place (for +> > > > > > > some of my bugs i have multi-megabyte tarballs attached) +> > > > > > +> > > > > > be comment ID "See the tarball at http://yourpage/something.tar.gz" +> > > > > > Then to grab the tarball, you'd use: +> > > > > > wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> > > > > > to grab it. +> > > > > +> > > > > so the basic idea is to do it completely self-managed +> > > > > and have have heterogenous sources of extended data? +> > > > +> > > > I assume "extended data" here refers to your tarballs. What sort of +> > > > homogenous source did you have in mind? The comment body is currently +> > > > just a binary blob for non-text/* types, otherwise it's text in +> > > > whatever encoding you've configured. +> > > +> > > some kind of common upload target for a single project in order to have +> > > more reliable sources of stuff thats related to bugs but doesnt fit into +> > > the normal repository +> > +> > Sorry, I'm still having trouble with "doesn't fit into the normal +> > repository". It's going to be large wherever you keep it. You +> > worried about multiple branches all having these big tarballs in them +> > and want a "lightweight" checkout without all the big +> > tarballs/whatever? I still think having some sort of "resources" +> > directory on an http server somewhere that you link to from comments +> > is the best plan. If you use the +> > be show --xml ID | be-xml-to-mbox | catmutt +> > approach, you can even write your comments in text/html and get +> > clickable links ;). A "push big file to remote and commit comment +> > linking to it" script would be pretty simple and keep everything +> > consistent. +> +> thats probably what i want to do + +#!/bin/bash +REMOTE_DIR="you@webhost:./public_html/bigfiles" +REMOTE_LINK="http://www.webhost.com/bigfiles" +if [ $# -ne 2 ]; then + echo "usage: $0 ID BIGFILE" + exit 1 +fi +ID="$1" +BIGFILE="$2" +be comment "$ID" "Large file stored at ${REMOTE_LINK}/${BIGFILE}" && scp "$BIGFILE" "${REMOTE_DIR}" + +> > > > On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> > > > > Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> > > > > +> > > > > > i want to see the combination of the bug data of all branches +> > > > > +> > > > > How is a tool to determine the set of “all branches”? The distributed +> > > > > VCS model means that set is indeterminate. +> > > > +> > > > He could just make a list of branches he likes. +> > > > +> > > > Ronny, are you looking to check bug status across several repos on the +> > > > fly, or periodically run something (with cron, etc.) to update a +> > > > static multi-repo summary? +> > > +> > > on the fly access +> > +> > Then listing bugs in a remote repo will either involve httping tons of +> > tiny values files for each bug (slow?) or running some hypothetical +> > BE-server locally for each repo speaking some BE-specific protocol +> > (complicated?). And how would you handle e.g. headless git repos, +> > where nothing's even checked out? +> > +> > You could always run the cron job every 15 minutes, and rely on +> > whatever VCS you're using having some intelligent protocol/procedure +> > to keep bandwidth down ;). If you need faster / more-efficient +> > updates, you'll probably need to throw out polling altogether and +> > setup all involved repos with a "push to central-repo on commit" hook +> > or some such. +> +> its intended to run on the place where i publish the repositories anyway + +Oh, you mean all the repos you want to cover are all _already_ on the +same host? + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/values new file mode 100644 index 0000000..d95deb9 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/30a8b841-98ae-41b7-9ef2-6af7cffca8da/values @@ -0,0 +1,14 @@ +Alt-id: <20090713104715.GA13723@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Mon, 13 Jul 2009 06:47:15 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 6dcc910a-ce15-4eeb-b49b-4747719748ed + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/body new file mode 100644 index 0000000..cf3c990 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/body @@ -0,0 +1,32 @@ +On Sat, Jul 11, 2009 at 11:25:07AM -0400, W. Trevor King wrote: +> The easiest implementation I can think of would be to keep local +> branches (on whatever computer is hosting your web interface) +> following your favorite repos. +> proxectX/ +> |-- repoA +> |-- repoB +> `-- repoC +> You'd pull upstream changes with a cron job. +> Listing bugs would be something along the lines of +> projectX$ for repo in * +> do +> pushd $repo +> be list +> popd +> done | sort | uniq +> ... + +I've reworked option handling for be, so my branch now supports + projectX$ for repo in * + do + be --dir $repo list + done | sort | uniq +etc. This also makes it easy to use your uninstalled development +version of be on any bug directory on your local machine. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/values new file mode 100644 index 0000000..1c7b2bf --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/46937fd4-b0bc-4eed-8033-d699445441ea/values @@ -0,0 +1,14 @@ +Alt-id: <20090713115734.GA13788@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Mon, 13 Jul 2009 07:57:34 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: bd98f525-95ec-446a-84e8-34c7d6fa5b40 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/body new file mode 100644 index 0000000..c22de06 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/body @@ -0,0 +1,73 @@ +On Sun, Jul 12, 2009 at 11:20:10PM +0200, Ronny Pfannschmidt wrote: +> On Sat, 2009-07-11 at 11:25 -0400, W. Trevor King wrote: +> > On Sat, Jul 11, 2009 at 03:13:05PM +0200, Ronny Pfannschmidt wrote: +> > > On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> > > > On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > > > > 2. is there any model for storing bigger files at a central place (for +> > > > > some of my bugs i have multi-megabyte tarballs attached) +> > > > +> > > > be comment ID "See the tarball at http://yourpage/something.tar.gz" +> > > > Then to grab the tarball, you'd use: +> > > > wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> > > > to grab it. +> > > +> > > so the basic idea is to do it completely self-managed +> > > and have have heterogenous sources of extended data? +> > +> > I assume "extended data" here refers to your tarballs. What sort of +> > homogenous source did you have in mind? The comment body is currently +> > just a binary blob for non-text/* types, otherwise it's text in +> > whatever encoding you've configured. +> +> some kind of common upload target for a single project in order to have +> more reliable sources of stuff thats related to bugs but doesnt fit into +> the normal repository + +Sorry, I'm still having trouble with "doesn't fit into the normal +repository". It's going to be large wherever you keep it. You +worried about multiple branches all having these big tarballs in them +and want a "lightweight" checkout without all the big +tarballs/whatever? I still think having some sort of "resources" +directory on an http server somewhere that you link to from comments +is the best plan. If you use the + be show --xml ID | be-xml-to-mbox | catmutt +approach, you can even write your comments in text/html and get +clickable links ;). A "push big file to remote and commit comment +linking to it" script would be pretty simple and keep everything +consistent. + +> > On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> > > Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> > > +> > > > i want to see the combination of the bug data of all branches +> > > +> > > How is a tool to determine the set of “all branches”? The distributed +> > > VCS model means that set is indeterminate. +> > +> > He could just make a list of branches he likes. +> > +> > Ronny, are you looking to check bug status across several repos on the +> > fly, or periodically run something (with cron, etc.) to update a +> > static multi-repo summary? +> +> on the fly access + +Then listing bugs in a remote repo will either involve httping tons of +tiny values files for each bug (slow?) or running some hypothetical +BE-server locally for each repo speaking some BE-specific protocol +(complicated?). And how would you handle e.g. headless git repos, +where nothing's even checked out? + +You could always run the cron job every 15 minutes, and rely on +whatever VCS you're using having some intelligent protocol/procedure +to keep bandwidth down ;). If you need faster / more-efficient +updates, you'll probably need to throw out polling altogether and +setup all involved repos with a "push to central-repo on commit" hook +or some such. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/values new file mode 100644 index 0000000..89f2724 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/4d192c6c-a4a8-4844-b083-2dd5926bd2d9/values @@ -0,0 +1,14 @@ +Alt-id: <20090712235502.GA10782@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 19:55:02 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 8ffc90d7-0be7-4b00-88e6-9ae1b65f7957 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/body new file mode 100644 index 0000000..6b7d3eb --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/body @@ -0,0 +1,70 @@ +On Sun, 2009-07-12 at 19:55 -0400, W. Trevor King wrote: +> On Sun, Jul 12, 2009 at 11:20:10PM +0200, Ronny Pfannschmidt wrote: +> > On Sat, 2009-07-11 at 11:25 -0400, W. Trevor King wrote: +> > > On Sat, Jul 11, 2009 at 03:13:05PM +0200, Ronny Pfannschmidt wrote: +> > > > On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> > > > > On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > > > > > 2. is there any model for storing bigger files at a central place (for +> > > > > > some of my bugs i have multi-megabyte tarballs attached) +> > > > > +> > > > > be comment ID "See the tarball at http://yourpage/something.tar.gz" +> > > > > Then to grab the tarball, you'd use: +> > > > > wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> > > > > to grab it. +> > > > +> > > > so the basic idea is to do it completely self-managed +> > > > and have have heterogenous sources of extended data? +> > > +> > > I assume "extended data" here refers to your tarballs. What sort of +> > > homogenous source did you have in mind? The comment body is currently +> > > just a binary blob for non-text/* types, otherwise it's text in +> > > whatever encoding you've configured. +> > +> > some kind of common upload target for a single project in order to have +> > more reliable sources of stuff thats related to bugs but doesnt fit into +> > the normal repository +> +> Sorry, I'm still having trouble with "doesn't fit into the normal +> repository". It's going to be large wherever you keep it. You +> worried about multiple branches all having these big tarballs in them +> and want a "lightweight" checkout without all the big +> tarballs/whatever? I still think having some sort of "resources" +> directory on an http server somewhere that you link to from comments +> is the best plan. If you use the +> be show --xml ID | be-xml-to-mbox | catmutt +> approach, you can even write your comments in text/html and get +> clickable links ;). A "push big file to remote and commit comment +> linking to it" script would be pretty simple and keep everything +> consistent. +thats probably what i want to do + +> +> > > On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> > > > Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> > > > +> > > > > i want to see the combination of the bug data of all branches +> > > > +> > > > How is a tool to determine the set of “all branches”? The distributed +> > > > VCS model means that set is indeterminate. +> > > +> > > He could just make a list of branches he likes. +> > > +> > > Ronny, are you looking to check bug status across several repos on the +> > > fly, or periodically run something (with cron, etc.) to update a +> > > static multi-repo summary? +> > +> > on the fly access +> +> Then listing bugs in a remote repo will either involve httping tons of +> tiny values files for each bug (slow?) or running some hypothetical +> BE-server locally for each repo speaking some BE-specific protocol +> (complicated?). And how would you handle e.g. headless git repos, +> where nothing's even checked out? +> +> You could always run the cron job every 15 minutes, and rely on +> whatever VCS you're using having some intelligent protocol/procedure +> to keep bandwidth down ;). If you need faster / more-efficient +> updates, you'll probably need to throw out polling altogether and +> setup all involved repos with a "push to central-repo on commit" hook +> or some such. +its intended to run on the place where i publish the repositories anyway diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/values new file mode 100644 index 0000000..867700a --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/6dcc910a-ce15-4eeb-b49b-4747719748ed/values @@ -0,0 +1,14 @@ +Alt-id: <1247468734.7189.1.camel@localhost> + + +Content-type: text/plain + + +Date: Mon, 13 Jul 2009 09:05:34 +0200 + + +From: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + + +In-reply-to: 4d192c6c-a4a8-4844-b083-2dd5926bd2d9 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/body new file mode 100644 index 0000000..2f2c16e --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/body @@ -0,0 +1,15 @@ +Hi, + +1. is there any way to aggregate over multiple public branches in order +to get the complete bug state + +2. is there any model for storing bigger files at a central place (for +some of my bugs i have multi-megabyte tarballs attached) + +Regards Ronny + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/values new file mode 100644 index 0000000..38b8aa1 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7/values @@ -0,0 +1,11 @@ +Alt-id: <1247313294.7701.60.camel@localhost> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 13:54:54 +0200 + + +From: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/body new file mode 100644 index 0000000..debd486 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/body @@ -0,0 +1,95 @@ +On Sat, 2009-07-11 at 11:25 -0400, W. Trevor King wrote: +> On Sat, Jul 11, 2009 at 03:13:05PM +0200, Ronny Pfannschmidt wrote: +> > On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> > > On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > > > 1. is there any way to aggregate over multiple public branches in order +> > > > to get the complete bug state +> > > +> > > Keeping the bug data with the source helps synchronize bug state and +> > > source code. Bug state in branch A may not apply to branch B. Some +> > > people like to weaken this source-bug linkage by keeping their bugs in +> > > a branch all by themselves (ditz [http://ditz.rubyforge.org/] +> > > currently supports this workflow). It sounds like you want to move +> > > from "bugs with code" to "bugs and code in separate branches". We +> > > don't have an easy way to do that in BE at the moment, since +> > > version-control systems like Git have a single working branch at a +> > > time (I think :p). What VCS are you using as a backend? +> > +> > the basic idea is to take a look at all public branches (for exaple all +> > on lp/bitbucket/github) in order to tell the user of a webinterface that +> > bug foo is fixed in branch xyz, and if its merged to the main branch +> +> Hmm. +> +> > > > 2. is there any model for storing bigger files at a central place (for +> > > > some of my bugs i have multi-megabyte tarballs attached) +> > > +> > > be comment ID "See the tarball at http://yourpage/something.tar.gz" +> > > Then to grab the tarball, you'd use: +> > > wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> > > to grab it. +> > so the basic idea is to do it completely self-managed +> +> Well, it's going to be managed by somebody ;). So far I'm not +> convinced enough for the manager to be me, so I'm suggesting it be you +> :p. +> +> > and have have heterogenous sources of extended data? +> +> I assume "extended data" here refers to your tarballs. What sort of +> homogenous source did you have in mind? The comment body is currently +> just a binary blob for non-text/* types, otherwise it's text in +> whatever encoding you've configured. +some kind of common upload target for a single project in order to have +more reliable sources of stuff thats related to bugs but doesnt fit into +the normal repository + + +> +> On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> > Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> > +> > > i want to see the combination of the bug data of all branches +> > +> > How is a tool to determine the set of “all branches”? The distributed +> > VCS model means that set is indeterminate. +> +> He could just make a list of branches he likes. +> +> Ronny, are you looking to check bug status across several repos on the +> fly, or periodically run something (with cron, etc.) to update a +> static multi-repo summary? +on the fly access + +> +> The easiest implementation I can think of would be to keep local +> branches (on whatever computer is hosting your web interface) +> following your favorite repos. +> proxectX/ +> |-- repoA +> |-- repoB +> `-- repoC +> You'd pull upstream changes with a cron job. +> Listing bugs would be something along the lines of +> projectX$ for repo in * +> do +> pushd $repo +> be list +> popd +> done | sort | uniq +> Then to show bug status you would have something like +> projectX$ for repo in * +> do +> echo $repo +> pushd $repo +> be show ${BUGID} +> popd +> done +> For a web frontend, you'd want to translate that to python/libbe. +> + +yes, the idea is to get a web fontend for multiple branches +and maybe a local gtk fontend for local multi-branch setups + +for some of my projects i have n local and m remote repos, and merging +is not always intended soonish diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/values new file mode 100644 index 0000000..de585ee --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/8ffc90d7-0be7-4b00-88e6-9ae1b65f7957/values @@ -0,0 +1,14 @@ +Alt-id: <1247433610.14803.3.camel@localhost> + + +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 23:20:10 +0200 + + +From: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + + +In-reply-to: bd98f525-95ec-446a-84e8-34c7d6fa5b40 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/body new file mode 100644 index 0000000..5f55127 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/body @@ -0,0 +1,87 @@ +On Sat, Jul 11, 2009 at 03:13:05PM +0200, Ronny Pfannschmidt wrote: +> On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> > On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > > 1. is there any way to aggregate over multiple public branches in order +> > > to get the complete bug state +> > +> > Keeping the bug data with the source helps synchronize bug state and +> > source code. Bug state in branch A may not apply to branch B. Some +> > people like to weaken this source-bug linkage by keeping their bugs in +> > a branch all by themselves (ditz [http://ditz.rubyforge.org/] +> > currently supports this workflow). It sounds like you want to move +> > from "bugs with code" to "bugs and code in separate branches". We +> > don't have an easy way to do that in BE at the moment, since +> > version-control systems like Git have a single working branch at a +> > time (I think :p). What VCS are you using as a backend? +> +> the basic idea is to take a look at all public branches (for exaple all +> on lp/bitbucket/github) in order to tell the user of a webinterface that +> bug foo is fixed in branch xyz, and if its merged to the main branch + +Hmm. + +> > > 2. is there any model for storing bigger files at a central place (for +> > > some of my bugs i have multi-megabyte tarballs attached) +> > +> > be comment ID "See the tarball at http://yourpage/something.tar.gz" +> > Then to grab the tarball, you'd use: +> > wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> > to grab it. +> so the basic idea is to do it completely self-managed + +Well, it's going to be managed by somebody ;). So far I'm not +convinced enough for the manager to be me, so I'm suggesting it be you +:p. + +> and have have heterogenous sources of extended data? + +I assume "extended data" here refers to your tarballs. What sort of +homogenous source did you have in mind? The comment body is currently +just a binary blob for non-text/* types, otherwise it's text in +whatever encoding you've configured. + +On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> +> > i want to see the combination of the bug data of all branches +> +> How is a tool to determine the set of “all branches”? The distributed +> VCS model means that set is indeterminate. + +He could just make a list of branches he likes. + +Ronny, are you looking to check bug status across several repos on the +fly, or periodically run something (with cron, etc.) to update a +static multi-repo summary? + +The easiest implementation I can think of would be to keep local +branches (on whatever computer is hosting your web interface) +following your favorite repos. + proxectX/ + |-- repoA + |-- repoB + `-- repoC +You'd pull upstream changes with a cron job. +Listing bugs would be something along the lines of + projectX$ for repo in * + do + pushd $repo + be list + popd + done | sort | uniq +Then to show bug status you would have something like + projectX$ for repo in * + do + echo $repo + pushd $repo + be show ${BUGID} + popd + done +For a web frontend, you'd want to translate that to python/libbe. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/values new file mode 100644 index 0000000..2792f2b --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/bd98f525-95ec-446a-84e8-34c7d6fa5b40/values @@ -0,0 +1,14 @@ +Alt-id: <20090711152507.GA18461@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 11:25:07 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: e520239c-8d69-4ff6-b1bd-0c2f74366200 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/body new file mode 100644 index 0000000..cc3cff3 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/body @@ -0,0 +1,37 @@ +On Sun, Jul 12, 2009 at 12:57:35AM +1000, Ben Finney wrote: +> Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> +> > i want to see the combination of the bug data of all branches +> +> What is your definition of ???all branches???? When I'm working with +> distributed VCS, I create branches wherever I feel like, and the VCS +> tool doesn't have some central registry of branches to keep up to date. +> +> How is a tool to determine the set of ???all branches???? The distributed +> VCS model means that set is indeterminate. + +In the first main Ronny spoke about "public" branches. To me it means that +if a branch is public, he should like to have a status of that branch. + +We all agree (probably ;-) ) that tha main branch is the "right" branch, but +as I see it, Ronny's question has some logic. +I'd like to know that a certain bug is fixed in a certain branch, also if it +is still not merged in the main branch, for various reason (ie I am interested +in the solution since the bug stop my work) + +Imagine it like a rss feed aggregator: in one place there are all the bugs of +all the branches that the developers make avaible to the public with +a repository. This can make easier the life to who want to try a something +since he know what branch he must check out, instead of checking all the +branch he can find to test if he get what is looking for. + +Unluckyly I have no idea how to solve it. :-( + +bye +Gianluca + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/values new file mode 100644 index 0000000..5e3db52 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/c8283e08-967c-4a7b-b953-3ec62c83fb9f/values @@ -0,0 +1,14 @@ +Alt-id: <20090713085859.GA21800@grys.it> + + +Content-type: text/plain + + +Date: Mon, 13 Jul 2009 10:58:59 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: e520239c-8d69-4ff6-b1bd-0c2f74366200 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/body new file mode 100644 index 0000000..93f082b --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/body @@ -0,0 +1,33 @@ +On Sat, 2009-07-11 at 23:34 +1000, Ben Finney wrote: +> Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> +> > the basic idea is to take a look at all public branches (for exaple +> > all on lp/bitbucket/github) in order to tell the user of a +> > webinterface that bug foo is fixed in branch xyz, and if its merged to +> > the main branch +> +> I don't understand. The state of the bug in the main branch is right +> there in the main branch; if it's not fixed there, it's not fixed there. +> If it's merged in from a different branch, the bug state follows all the +> other changes when they come in. +> +> Can you give an example of what would be done differently? +> +i want to see the combination of the bug data of all branches + +for example + +i got bug +its fixed in the branch "something" +its not fixed/merged to "main" + +now something like a website should tell me, this bug has been fixed in +branch xyz and the fix is not yet merged into main + + + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/values new file mode 100644 index 0000000..789fd7f --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/d86e497d-667d-4c2b-9249-76026df56633/values @@ -0,0 +1,14 @@ +Alt-id: <1247320857.7701.67.camel@localhost> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 16:00:57 +0200 + + +From: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + + +In-reply-to: 0f60a148-7024-44bd-bbed-377cbece9d1b + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/body new file mode 100644 index 0000000..3b417be --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/body @@ -0,0 +1,27 @@ +On Sat, 2009-07-11 at 08:50 -0400, W. Trevor King wrote: +> On Sat, Jul 11, 2009 at 01:54:54PM +0200, Ronny Pfannschmidt wrote: +> > 1. is there any way to aggregate over multiple public branches in order +> > to get the complete bug state +> +> Keeping the bug data with the source helps synchronize bug state and +> source code. Bug state in branch A may not apply to branch B. Some +> people like to weaken this source-bug linkage by keeping their bugs in +> a branch all by themselves (ditz [http://ditz.rubyforge.org/] +> currently supports this workflow). It sounds like you want to move +> from "bugs with code" to "bugs and code in separate branches". We +> don't have an easy way to do that in BE at the moment, since +> version-control systems like Git have a single working branch at a +> time (I think :p). What VCS are you using as a backend? +the basic idea is to take a look at all public branches (for exaple all +on lp/bitbucket/github) in order to tell the user of a webinterface that +bug foo is fixed in branch xyz, and if its merged to the main branch +> +> > 2. is there any model for storing bigger files at a central place (for +> > some of my bugs i have multi-megabyte tarballs attached) +> +> be comment ID "See the tarball at http://yourpage/something.tar.gz" +> Then to grab the tarball, you'd use: +> wget `be show COMMENT-ID | sed -n 's/ *See the tarball at //p'` +> to grab it. +so the basic idea is to do it completely self-managed and have have +heterogenous sources of extended data? diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/values new file mode 100644 index 0000000..43173a4 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/dc32aa62-cf56-4171-84a1-8f7d02b23b6d/values @@ -0,0 +1,14 @@ +Alt-id: <1247317985.7701.63.camel@localhost> + + +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 15:13:05 +0200 + + +From: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + + +In-reply-to: 13012b22-2d02-444c-87c0-8cf0f17137ae + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/body new file mode 100644 index 0000000..0263fbb --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/body @@ -0,0 +1,22 @@ +Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: + +> i want to see the combination of the bug data of all branches + +What is your definition of “all branches”? When I'm working with +distributed VCS, I create branches wherever I feel like, and the VCS +tool doesn't have some central registry of branches to keep up to date. + +How is a tool to determine the set of “all branches”? The distributed +VCS model means that set is indeterminate. + +-- + \ “Pinky, are you pondering what I'm pondering?” “I think so, | + `\ Brain, but I find scratching just makes it worse.” —_Pinky and | +_o__) The Brain_ | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/values new file mode 100644 index 0000000..351fdb9 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/e520239c-8d69-4ff6-b1bd-0c2f74366200/values @@ -0,0 +1,14 @@ +Alt-id: <87zlbbl128.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 00:57:35 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: 88d1f2c2-e1af-4f0d-9390-e3c89ae4f7d7 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/body b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/body new file mode 100644 index 0000000..9fb10bc --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/body @@ -0,0 +1,26 @@ +On Sat, Jul 11, 2009 at 11:31:34PM +1000, Ben Finney wrote: +> Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> writes: +> +> > 1. is there any way to aggregate over multiple public branches in +> > order to get the complete bug state +> +> The bug state is as complete as the source code state. It's exactly as +> aggregated as the rest of the source code; the ???complete bug state??? +> would be the integration branch where you merge all the feature branches +> and bug-fix branches together. +> +> If instead you want bugs to *not* be tightly linked with the rest of the +> source code state, it seems you don't want a distributed bug tracker +> like Bugs Everywhere. + +"the complete bug state" probably means that he want to know (and in some way +to publish it) that the bug "xyz" is fixed and merged in main while bug "abc" +is fixed but only in branch "123" and bug "def" is still open in branch "456" + +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/values new file mode 100644 index 0000000..a7c438b --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/comments/fd6162f3-7fc1-41d1-a073-a07465802b72/values @@ -0,0 +1,14 @@ +Alt-id: <20090713090341.GB21800@grys.it> + + +Content-type: text/plain + + +Date: Mon, 13 Jul 2009 11:03:41 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: 1f9f60de-ba37-42bc-a1c0-dc062ef255e1 + diff --git a/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/values b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/values new file mode 100644 index 0000000..2a3c4f3 --- /dev/null +++ b/.be/bugs/12c986be-d19a-4b8b-b1b5-68248ff4d331/values @@ -0,0 +1,17 @@ +creator: W. Trevor King <wking@drexel.edu> + + +reporter: Ronny Pfannschmidt <Ronny.Pfannschmidt@gmx.de> + + +severity: wishlist + + +status: unconfirmed + + +summary: Bug aggregation. Multi-repo meta-BE? + + +time: Tue, 21 Jul 2009 18:32:12 +0000 + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/body new file mode 100644 index 0000000..c799630 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/body @@ -0,0 +1,47 @@ +On Sunday 19 July 2009 00:00:46 Chris Ball wrote: +> Hi, +> +> > For example, let's assume we have target a, b, c There is a way +> > to know that "a" is a past target, "b" is the current target and +> > "c" is a future target ? +> +> We could add a "date due" field for each target. + +Good idea + +> > More: there is a way to know if a target is closed or open ? +> +> We could add a "target close" operation that moves all open bugs +> assigned to one target to the next date-due target. + +Nice. But instead of moving all bugs to the next date-due target, I'd prefer +to leave the choice to the user + + +> I see problems with these ideas in general, because we're assuming +> agreement by all parties/branches on when a target's date due is. +> Maybe it's okay to demand that social conventions be used to handle +> such a disagreement, or maybe not. + +I don't see these as problems per se. We can have two cases: + +1) a personal branch (like my html output or Trevor's email interface). In +this case there is not any problem to decide the due date + +2) a branch with a group of delopers (let it be the canonical branch o an +experimental branch): in this case I suppose that working together means to be +able to agree on some things + +In any case, having the possibility to set a due date does not means that it +is obligatory to do it and should be a good idea to offer as many possibilities +as we can to the users of BE + +bye +Gianluca + + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/values new file mode 100644 index 0000000..db30358 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/4952e1c7-e035-42f1-882b-6b5264481d0a/values @@ -0,0 +1,14 @@ +Alt-id: <200907202259.11774.gian@grys.it> + + +Content-type: text/plain + + +Date: Mon, 20 Jul 2009 22:59:11 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: 6555a651-5a7f-4a8a-9793-47ad1315e9e8 + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/body new file mode 100644 index 0000000..ef09dc0 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/body @@ -0,0 +1,26 @@ +Hi, + + > For example, let's assume we have target a, b, c There is a way + > to know that "a" is a past target, "b" is the current target and + > "c" is a future target ? + +We could add a "date due" field for each target. + + > More: there is a way to know if a target is closed or open ? + +We could add a "target close" operation that moves all open bugs +assigned to one target to the next date-due target. + +I see problems with these ideas in general, because we're assuming +agreement by all parties/branches on when a target's date due is. +Maybe it's okay to demand that social conventions be used to handle +such a disagreement, or maybe not. + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/values new file mode 100644 index 0000000..cdf7754 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/6555a651-5a7f-4a8a-9793-47ad1315e9e8/values @@ -0,0 +1,14 @@ +Alt-id: <m3skgt648h.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 18:00:46 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: b9865d8b-46ae-4169-bc83-d75a98164729 + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/body new file mode 100644 index 0000000..874d906 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/body @@ -0,0 +1,20 @@ +On Monday 20 July 2009 23:03:18 Chris Ball wrote: +> Hi Gianluca, +> +> > In any case, having the possibility to set a due date does not +> > means that it is obligatory to do it and should be a good idea to +> > offer as many possibilities as we can to the users of BE +> +> Okay, sounds reasonable. Would you like to write a patch for +> associating due dates and open/closed with a target? + +Ok. As soon as I finish a basic implementation of the html export, I will be +glad to try to write a patch. + +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/values new file mode 100644 index 0000000..d830558 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/7750d77c-85d2-4810-9d41-cec62b0da885/values @@ -0,0 +1,14 @@ +Alt-id: <200907202340.39963.gian@grys.it> + + +Content-type: text/plain + + +Date: Mon, 20 Jul 2009 23:40:39 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: 777182da-a216-45c7-bf4d-42c84e511c66 + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/body new file mode 100644 index 0000000..13505c1 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/body @@ -0,0 +1,19 @@ +Hi Gianluca, + + > In any case, having the possibility to set a due date does not + > means that it is obligatory to do it and should be a good idea to + > offer as many possibilities as we can to the users of BE + +Okay, sounds reasonable. Would you like to write a patch for +associating due dates and open/closed with a target? + +Thanks, + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/values new file mode 100644 index 0000000..a14e287 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/777182da-a216-45c7-bf4d-42c84e511c66/values @@ -0,0 +1,14 @@ +Alt-id: <m3hbx72hk9.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Mon, 20 Jul 2009 17:03:18 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: 4952e1c7-e035-42f1-882b-6b5264481d0a + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/body new file mode 100644 index 0000000..a916904 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/body @@ -0,0 +1,37 @@ +On Sat, Jul 18, 2009 at 06:00:46PM -0400, Chris Ball wrote: +> > For example, let's assume we have target a, b, c There is a way +> > to know that "a" is a past target, "b" is the current target and +> > "c" is a future target ? +> +> We could add a "date due" field for each target. + +Another option would be a "blocked by" field, since you might miss +deadlines, or have parallel targeted branches. Or just pick target +names following some scheme so the alphanumeric-sort is also a +dependency-order sort ;). + +> > More: there is a way to know if a target is closed or open ? + +There's also + $ be list --target 0.1 +If there are active bugs, the target is open. Otherwise, you must have +made it ;). + +> We could add a "target close" operation that moves all open bugs +> assigned to one target to the next date-due target. + +for bug in `be list --target 0.1 --uuids`; do + be target $bug $NEXT_TARGET +done + +To avoid the loop, we could change status, severity, target, etc from + be COMMAND BUG ARG +to + be COMMAND ARG BUG [MORE BUGS ...] + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/values new file mode 100644 index 0000000..b6c0979 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/9bbe9370-99c7-4d7c-80ee-9ade6b6feb9f/values @@ -0,0 +1,14 @@ +Alt-id: <20090718222701.GA304@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 18:27:01 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 6555a651-5a7f-4a8a-9793-47ad1315e9e8 + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/body b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/body new file mode 100644 index 0000000..7382bae --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/body @@ -0,0 +1,20 @@ +Hello + +Just a question and only for curiosity: there is an easy way to determine the +target succession ? + +For example, let's assume we have target a, b, c +There is a way to know that "a" is a past target, "b" is the current target +and "c" is a future target ? More: there is a way to know if a target is +closed or open ? + +thanks + +bye +Gianluca + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/values new file mode 100644 index 0000000..4972040 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/comments/b9865d8b-46ae-4169-bc83-d75a98164729/values @@ -0,0 +1,11 @@ +Alt-id: <200907182351.03217.gian@grys.it> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 23:51:03 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + diff --git a/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/values b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/values new file mode 100644 index 0000000..1485877 --- /dev/null +++ b/.be/bugs/22b6f620-d2f7-42a5-a02e-145733a4e366/values @@ -0,0 +1,20 @@ +assigned: Gianluca Montecchi <gian@grys.it> + + +creator: W. Trevor King <wking@drexel.edu> + + +reporter: Gianluca Montecchi <gian@grys.it> + + +severity: wishlist + + +status: assigned + + +summary: Sorting targets chronologically + + +time: Tue, 21 Jul 2009 18:34:25 +0000 + diff --git a/.be/bugs/2b81b428-fc43-4970-9469-b442385b9c0d/values b/.be/bugs/2b81b428-fc43-4970-9469-b442385b9c0d/values index 0a47ab5..c2861d0 100644 --- a/.be/bugs/2b81b428-fc43-4970-9469-b442385b9c0d/values +++ b/.be/bugs/2b81b428-fc43-4970-9469-b442385b9c0d/values @@ -7,10 +7,10 @@ reporter: gianluca <gian@galactica> severity: minor -status: closed +status: fixed -summary: Use the get_parser +summary: Use the get_parser in becommands/html.py time: Wed, 08 Jul 2009 21:27:37 +0000 diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/body new file mode 100644 index 0000000..5ce4f1c --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/body @@ -0,0 +1,23 @@ +"W. Trevor King" <wking@drexel.edu> writes: + +> On Sat, Jul 04, 2009 at 10:19:35AM +1000, Ben Finney wrote: +> > Instead of a separate command for each output format, could we have +> > a single "produce a static report of the bug database" command, and +> > specify output format as an option? +> > […] +> +> Do people like this architecture better than my be-xml-to-mbox +> approach? + +I think this question is illuminated by the related question: Is mbox +output a static report, or another read-write data store? + +It can technically be both, of course, which is why the question may be +helpful: it may help show what is the *conceptual* purpose of the mbox +output format for Bugs Everywhere. + +-- + \ “Time is the great legalizer, even in the field of morals.” | + `\ —Henry L. Mencken | +_o__) | +Ben Finney diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/values new file mode 100644 index 0000000..b83f4a6 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/074ef29a-3f1d-46dc-8561-7a56af7e6d67/values @@ -0,0 +1,14 @@ +Alt-id: <87hbxqrckv.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Mon, 06 Jul 2009 08:26:24 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cb5689f4-7c36-4c44-b380-ca9e06e80bae + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/body new file mode 100644 index 0000000..dbf3b1b --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/body @@ -0,0 +1,47 @@ +Gianluca Montecchi <gian@grys.it> writes: + +> 1) is it ok to develop this command ? I know that this is not a fully +> featured web interface, but I am sure that it can be usefull. + +Yes, definitely. I can see it being a very easy way to put one's bug +database online for browsing. + +> I am open to suggestion about it of course. + +Instead of a separate command for each output format, could we have a +single “produce a static report of the bug database” command, and +specify output format as an option? + +How about: + + be report + be report --format ascii + be report --format rst + be report --format html + +Where the ‘--format’ option has a default of, e.g., “ascii”. + +This would mean that you are implementing the ‘html’ format of this +putative command. + +> 2) I see that every command is implemented with a python file in the +> becommand dir. For a better code, I'd like to split the command +> implementation into two files: a file that contain the actual code and +> a second file that have the html related part, any problem with this ? + +This sounds quite sensible to me. The existence of a command implies a +module of the same name in ‘becommand’, but there's no necessary +implication that that module can't import modules from elsewhere to do +its work. + +-- + \ “It ain't so much the things we don't know that get us in | + `\ trouble. It's the things we know that ain't so.” —Artemus Ward | +_o__) (1834–1867), U.S. journalist | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/values new file mode 100644 index 0000000..4ef9544 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/1dba8196-654b-4ca0-9a95-fb334af81863/values @@ -0,0 +1,14 @@ +Alt-id: <87y6r5qoyw.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sat, 04 Jul 2009 10:19:35 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cb5689f4-7c36-4c44-b380-ca9e06e80bae + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/body new file mode 100644 index 0000000..4276b9c --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/body @@ -0,0 +1,25 @@ +Gianluca Montecchi <gian@grys.it> writes: + +> On Monday 06 July 2009 12:48:39 W. Trevor King wrote: +> > Gianluca is clearly thinking about a static report [for a collection +> > of HTML files as output]: +> +> You are right, static, but not exactly a report as I think Ben is +> thinking + +I think it exactly is a report: multiple, static, browseable pages +reporting the state of the database at a point in time. + +What makes you think that term doesn't apply? + +-- + \ “The problem with television is that the people must sit and | + `\ keep their eyes glued on a screen: the average American family | +_o__) hasn't time for it.” —_The New York Times_, 1939 | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/values new file mode 100644 index 0000000..7dee5d6 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/3bf57ee7-710f-4a01-a8af-8bb9eb9dc937/values @@ -0,0 +1,14 @@ +Alt-id: <87skh9p8ax.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Tue, 07 Jul 2009 11:53:58 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cb5689f4-7c36-4c44-b380-ca9e06e80bae + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/body new file mode 100644 index 0000000..8451bd7 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/body @@ -0,0 +1,50 @@ +On Mon, Jul 06, 2009 at 08:26:24AM +1000, Ben Finney wrote: +> "W. Trevor King" <wking@drexel.edu> writes: +> +> > On Sat, Jul 04, 2009 at 10:19:35AM +1000, Ben Finney wrote: +> > > Instead of a separate command for each output format, could we have +> > > a single "produce a static report of the bug database" command, and +> > > specify output format as an option? +> > +> > Do people like this architecture better than my be-xml-to-mbox +> > approach? +> +> I think this question is illuminated by the related question: Is mbox +> output a static report, or another read-write data store? + +Gianluca is clearly thinking about a static report: + +On Fri, Jul 03, 2009 at 10:50:17PM +0200, Gianluca Montecchi wrote: +> The goal is to be able to do something like "be html /web/page" to have in the +> /web/page directory some static html pages that basically are the dump of the +> be repository, much like ditz have + +I think truly interactive frontends like Steve's working on need to be +build on top of libbe directly, since they'll need to make lots of +small changes to the database, and it's to slow to be reloading the +database for every change. Static dumps like my mbox or Gianluca's +html could just parse the xml output of `be list' and other be +commands. + +There should also be an xml import for `be new' and `be comment' so +you could import new bugs/comments from whatever format after writing +a whatever->xml converter. This would allow you to email new bugs and +comments to the database (e.g. via some procmail-spawned +be-parse-email script) which would give you some level of +interactivity, but you'd have to regenerate your mbox to see your new +comments in your mail reader. + +I think interactive use that gives you live-updates in your mail +reader isn't worth the trouble, since you'd need to teach BE imap or +smtp+mbox-locking. Hmm, maybe it smtp+mbox-locking wouldn't be so bad, +but that would be a distinct frontend project like Steve's, not part +of the becommands. + +Trevor + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/values new file mode 100644 index 0000000..6f9ecf7 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/55263144-9775-4b18-ab83-29d66ed91a53/values @@ -0,0 +1,14 @@ +Alt-id: <20090706104839.GA19537@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Mon, 6 Jul 2009 06:48:39 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 074ef29a-3f1d-46dc-8561-7a56af7e6d67 + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/body new file mode 100644 index 0000000..3b53533 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/body @@ -0,0 +1,23 @@ +On Sat, Jul 04, 2009 at 10:19:35AM +1000, Ben Finney wrote: +> Instead of a separate command for each output format, could we have a +> single "produce a static report of the bug database" command, and +> specify output format as an option? +> +> How about: +> +> be report +> be report --format ascii +> be report --format rst +> be report --format html + +Do people like this architecture better than my be-xml-to-mbox +approach? I think the tradeoff is easy output format implementation +vs cluttered core codebase. Should we use both, depending on how +useful people think the output format will be? + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/values new file mode 100644 index 0000000..3452022 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/68927fef-6ce1-4a1f-a414-28695d913a50/values @@ -0,0 +1,14 @@ +Alt-id: <20090705143108.GB10709@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sun, 5 Jul 2009 10:31:08 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 1dba8196-654b-4ca0-9a95-fb334af81863 + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/body new file mode 100644 index 0000000..9bf3851 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/body @@ -0,0 +1,123 @@ +On Fri, Jul 03, 2009 at 10:50:17PM +0200, Gianluca Montecchi wrote: +> +> Hello to everyone +> +> As i said in a previous mail, I am working on a "html" command for be. +> The goal is to be able to do something like "be html /web/page" to have in the +> /web/page directory some static html pages that basically are the dump of the +> be repository, much like ditz have +> This will enable a simple and fast publish of the bus list and details on the +> web, at least in read only mode. +> +> So I'd like to ask some question: +> 1) is it ok to develop this command ? I know that this is not a fully featured +> web interface, but I am sure that it can be usefull. +> +> I am open to suggestion about it of course. +> +> 2) I see that every command is implemented with a python file in the becommand +> dir. For a better code, I'd like to split the command implementation into two +> files: a file that contain the actual code and a second file that have the html +> related part, any problem with this ? I don't like to have the html part and +> the code part in one big and unreadable file. +> +> I'd like to hear other opinion about this. +> +> Thanks for now +> bye +> Gianluca +> +> +> _______________________________________________ +> Be-devel mailing list +> Be-devel@bugseverywhere.org +> http://void.printf.net/cgi-bin/mailman/listinfo/be-devel + +On Mon, Jul 06, 2009 at 10:18:33PM +0200, Gianluca Montecchi wrote: +> This sound like an interesting idea, but what i'd like to do is not, strictly +> speaking, a report. It is a full tree of html pages that are browseable, both +> on line and offline + +I'm not sure what distinction you're making about "report". You're +just producing a static snapshot of the current database status, +right? The number of pages and completeness of coverage are nice, but +it's still a static entity generated from a particular snapshot, which +is what I mean by "report" ;). + +> > > 2) I see that every command is implemented with a python file in the +> > > becommand dir. For a better code, I'd like to split the command +> > > implementation into two files: a file that contain the actual code and +> > > a second file that have the html related part, any problem with this ? +> > +> > This sounds quite sensible to me. The existence of a command implies a +> > module of the same name in ‘becommand’, but there's no necessary +> > implication that that module can't import modules from elsewhere to do +> > its work. +> +> The "elsewhere" for now is the same directory, just another module +> + +On Mon, Jul 06, 2009 at 10:38:56PM +0200, Gianluca Montecchi wrote: +> > On Fri, Jul 03, 2009 at 10:50:17PM +0200, Gianluca Montecchi wrote: +> > > The goal is to be able to do something like "be html /web/page" to have +> > > in the /web/page directory some static html pages that basically are the +> > > dump of the be repository, much like ditz have +> > +> > I think truly interactive frontends like Steve's working on need to be +> > build on top of libbe directly, since they'll need to make lots of +> > small changes to the database, and it's to slow to be reloading the +> > database for every change. Static dumps like my mbox or Gianluca's +> > html could just parse the xml output of `be list' and other be +> > commands. +> +> Ok, but if I want to have an html dump that is browseable, I need to parse the +> xml. Am I correct ? +> If yes, should not be easiear to use directly the libbe ? + +Using libbe directly is easier, but also more tightly tied to the be +internals which could weigh down future refactoring. Partly I'm +afraid of our 2.5 different html-output mechanisms. Either their +should be a single Right Way that tries to satisfy everyone, or a +smorgasbord of loosely coupled translators, so it's not so painful to +kill them if/when they go out of style :p. + +On Mon, Jul 06, 2009 at 10:46:54PM +0200, Gianluca Montecchi wrote: +> On Saturday 04 July 2009 02:31:26 Chris Ball wrote: +> > It might be a good idea for "be html" to use the CherryPy web interface +> > that Steve is working on. The command could start up the CherryPy app +> > and scrape all of the available pages to get a stand-alone dump; this +> > would avoid having to keep two (okay, more than two at this point) +> > separate sets of HTML templates in the source tree. What do you think? +> +> It can be do, but this implies that CherryPy must be installed and configured, +> a thing that I don't want to impose. My idea is to offer a simpler way to have +> some html pages, where you just need to have BE installed. + +I agree that not needing CherryPy for a static html dump is good. +Also, read-only templates will look different from the CherryPy +interactive templates. +1 for another quasi-redundant template set +;). + +> > > 2) I see that every command is implemented with a python file in +> > > the becommand dir. For a better code, I'd like to split the +> > > command implementation into two files: a file that contain the +> > > actual code and a second file that have the html related part, +> > > any problem with this ? I don't like to have the html part and +> > > the code part in one big and unreadable file. +> > +> > I agree that becommands/*.py commands should not contain any HTML +> > layout code. Putting it somewhere else instead sounds fine. +> +> I am in doubt with the "somewhere else", since for now I put the html template +> into a separate file in the same directory. Suggestion ? + +I think that only code intended only for command line use only should +go into becommands, but really, just dump it anywhere and we can shift +it around later :p. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/values new file mode 100644 index 0000000..ac3b5ab --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/83202b83-eea8-452f-8239-d468940bddba/values @@ -0,0 +1,14 @@ +Alt-id: <20090707013454.GA3721@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Mon, 6 Jul 2009 21:34:54 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: da97e18f-33d6-469e-9d93-6457b9a6bfca + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/body new file mode 100644 index 0000000..2301eba --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/body @@ -0,0 +1,47 @@ +On Saturday 04 July 2009 02:19:35 Ben Finney wrote: +> Gianluca Montecchi <gian@grys.it> writes: + +> +> > I am open to suggestion about it of course. +> +> Instead of a separate command for each output format, could we have a +> single “produce a static report of the bug database” command, and +> specify output format as an option? +> +> How about: +> +> be report +> be report --format ascii +> be report --format rst +> be report --format html +> +> Where the ‘--format’ option has a default of, e.g., “ascii”. +> +> This would mean that you are implementing the ‘html’ format of this +> putative command. +> + +This sound like an interesting idea, but what i'd like to do is not, strictly +speaking, a report. It is a full tree of html pages that are browseable, both +on line and offline + +> > 2) I see that every command is implemented with a python file in the +> > becommand dir. For a better code, I'd like to split the command +> > implementation into two files: a file that contain the actual code and +> > a second file that have the html related part, any problem with this ? +> +> This sounds quite sensible to me. The existence of a command implies a +> module of the same name in ‘becommand’, but there's no necessary +> implication that that module can't import modules from elsewhere to do +> its work. + +The "elsewhere" for now is the same directory, just another module + +bye +Gianluca + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/values new file mode 100644 index 0000000..6259717 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/8c1c4f38-a8d4-4cf9-a9f0-e9846ebbcad8/values @@ -0,0 +1,14 @@ +Alt-id: <200907062218.33895.gian@grys.it> + + +Content-type: text/plain + + +Date: Mon, 06 Jul 2009 22:18:33 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: 1dba8196-654b-4ca0-9a95-fb334af81863 + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/body new file mode 100644 index 0000000..50a30e8 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/body @@ -0,0 +1,35 @@ +Hi Gianluca, + + > As i said in a previous mail, I am working on a "html" command + > for be. The goal is to be able to do something like "be html + > /web/page" to have in the /web/page directory some static html + > pages that basically are the dump of the be repository, much like + > ditz have. This will enable a simple and fast publish of the bus + > list and details on the web, at least in read only mode. + +It might be a good idea for "be html" to use the CherryPy web interface +that Steve is working on. The command could start up the CherryPy app +and scrape all of the available pages to get a stand-alone dump; this +would avoid having to keep two (okay, more than two at this point) +separate sets of HTML templates in the source tree. What do you think? + + > 2) I see that every command is implemented with a python file in + > the becommand dir. For a better code, I'd like to split the + > command implementation into two files: a file that contain the + > actual code and a second file that have the html related part, + > any problem with this ? I don't like to have the html part and + > the code part in one big and unreadable file. + +I agree that becommands/*.py commands should not contain any HTML +layout code. Putting it somewhere else instead sounds fine. + +Thanks! + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/values new file mode 100644 index 0000000..7d09f96 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/b900f7fd-bab6-48c4-922c-a051f933da58/values @@ -0,0 +1,14 @@ +Alt-id: <m3iqi9thk1.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Fri, 03 Jul 2009 20:31:26 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: cb5689f4-7c36-4c44-b380-ca9e06e80bae + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/body new file mode 100644 index 0000000..8991cfb --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/body @@ -0,0 +1,93 @@ +> On Mon, Jul 06, 2009 at 10:18:33PM +0200, Gianluca Montecchi wrote: +>> This sound like an interesting idea, but what i'd like to do is not, +>> strictly +>> speaking, a report. It is a full tree of html pages that are browseable, +>> both +>> on line and offline +> +> I'm not sure what distinction you're making about "report". You're +> just producing a static snapshot of the current database status, +> right? The number of pages and completeness of coverage are nice, but +> it's still a static entity generated from a particular snapshot, which +> is what I mean by "report" ;). + +Mmm, my bad here. +I normally speak about "report" as something that is not browseable, like +the output of a report generator (reportlab or whatever), but I admit that +basically also the html output I am working on is a report. + + +> On Mon, Jul 06, 2009 at 10:38:56PM +0200, Gianluca Montecchi wrote: +>> +>> Ok, but if I want to have an html dump that is browseable, I need to +>> parse the +>> xml. Am I correct ? +>> If yes, should not be easiear to use directly the libbe ? +> +> Using libbe directly is easier, but also more tightly tied to the be +> internals which could weigh down future refactoring. Partly I'm +> afraid of our 2.5 different html-output mechanisms. Either their +> should be a single Right Way that tries to satisfy everyone, or a +> smorgasbord of loosely coupled translators, so it's not so painful to +> kill them if/when they go out of style :p. + +I know that using libbe I am more tightly tied to the internals, but +I am trying to keep the command code and the presentation code crearly +separated to minimize this problem. I am not sure this is a real problem +anyway. + + +> On Mon, Jul 06, 2009 at 10:46:54PM +0200, Gianluca Montecchi wrote: +>> On Saturday 04 July 2009 02:31:26 Chris Ball wrote: +>> > It might be a good idea for "be html" to use the CherryPy web +>> interface +>> > that Steve is working on. The command could start up the CherryPy app +>> > and scrape all of the available pages to get a stand-alone dump; this +>> > would avoid having to keep two (okay, more than two at this point) +>> > separate sets of HTML templates in the source tree. What do you +>> think? +>> +>> It can be do, but this implies that CherryPy must be installed and +>> configured, +>> a thing that I don't want to impose. My idea is to offer a simpler way +>> to have +>> some html pages, where you just need to have BE installed. +> +> I agree that not needing CherryPy for a static html dump is good. +> Also, read-only templates will look different from the CherryPy +> interactive templates. +1 for another quasi-redundant template set +> ;). + +The look is not a problem. I can always use the same html Steve is using. +I am also playing with the idea to have the template themeable some time +after I have a fully working version. + +> +>> > > 2) I see that every command is implemented with a python file in +>> > > the becommand dir. For a better code, I'd like to split the +>> > > command implementation into two files: a file that contain the +>> > > actual code and a second file that have the html related part, +>> > > any problem with this ? I don't like to have the html part and +>> > > the code part in one big and unreadable file. +>> > +>> > I agree that becommands/*.py commands should not contain any HTML +>> > layout code. Putting it somewhere else instead sounds fine. +>> +>> I am in doubt with the "somewhere else", since for now I put the html +>> template +>> into a separate file in the same directory. Suggestion ? +> +> I think that only code intended only for command line use only should +> go into becommands, but really, just dump it anywhere and we can shift +> it around later :p. + +Of course. + +bye +Gianluca + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/values new file mode 100644 index 0000000..e846ff5 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/c7ace551-2982-4683-bca3-b5e66056cce5/values @@ -0,0 +1,14 @@ +Alt-id: <6f719a1c43fdcba8bdbfee1130072595.squirrel@webmail.grys.it> + + +Content-type: text/plain + + +Date: Tue, 07 Jul 2009 14:15:08 +0200 + + +From: gian@grys.it + + +In-reply-to: 83202b83-eea8-452f-8239-d468940bddba + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/body new file mode 100644 index 0000000..d8f14fc --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/body @@ -0,0 +1,32 @@ +Hello to everyone + +As i said in a previous mail, I am working on a "html" command for be. +The goal is to be able to do something like "be html /web/page" to have in the +/web/page directory some static html pages that basically are the dump of the +be repository, much like ditz have +This will enable a simple and fast publish of the bus list and details on the +web, at least in read only mode. + +So I'd like to ask some question: +1) is it ok to develop this command ? I know that this is not a fully featured +web interface, but I am sure that it can be usefull. + +I am open to suggestion about it of course. + +2) I see that every command is implemented with a python file in the becommand +dir. For a better code, I'd like to split the command implementation into two +files: a file that contain the actual code and a second file that have the html +related part, any problem with this ? I don't like to have the html part and +the code part in one big and unreadable file. + +I'd like to hear other opinion about this. + +Thanks for now +bye +Gianluca + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/values new file mode 100644 index 0000000..e95ab61 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/cb5689f4-7c36-4c44-b380-ca9e06e80bae/values @@ -0,0 +1,11 @@ +Alt-id: <200907032250.17327.gian@grys.it> + + +Content-type: text/plain + + +Date: Fri, 03 Jul 2009 22:50:17 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/body new file mode 100644 index 0000000..27dca1e --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/body @@ -0,0 +1,45 @@ +On Saturday 04 July 2009 02:31:26 Chris Ball wrote: +> Hi Gianluca, +> +> > As i said in a previous mail, I am working on a "html" command +> > for be. The goal is to be able to do something like "be html +> > /web/page" to have in the /web/page directory some static html +> > pages that basically are the dump of the be repository, much like +> > ditz have. This will enable a simple and fast publish of the bus +> > list and details on the web, at least in read only mode. +> +> It might be a good idea for "be html" to use the CherryPy web interface +> that Steve is working on. The command could start up the CherryPy app +> and scrape all of the available pages to get a stand-alone dump; this +> would avoid having to keep two (okay, more than two at this point) +> separate sets of HTML templates in the source tree. What do you think? + +It can be do, but this implies that CherryPy must be installed and configured, +a thing that I don't want to impose. My idea is to offer a simpler way to have +some html pages, where you just need to have BE installed. + +My very first implementation was a script that parse directly the .be directory +to build the pages, without BE itself installed. + + +> > 2) I see that every command is implemented with a python file in +> > the becommand dir. For a better code, I'd like to split the +> > command implementation into two files: a file that contain the +> > actual code and a second file that have the html related part, +> > any problem with this ? I don't like to have the html part and +> > the code part in one big and unreadable file. +> +> I agree that becommands/*.py commands should not contain any HTML +> layout code. Putting it somewhere else instead sounds fine. + +I am in doubt with the "somewhere else", since for now I put the html template +into a separate file in the same directory. Suggestion ? + +thanks +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/values new file mode 100644 index 0000000..1bf2dc4 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/da97e18f-33d6-469e-9d93-6457b9a6bfca/values @@ -0,0 +1,14 @@ +Alt-id: <200907062246.54804.gian@grys.it> + + +Content-type: text/plain + + +Date: Mon, 06 Jul 2009 22:46:54 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: b900f7fd-bab6-48c4-922c-a051f933da58 + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/body new file mode 100644 index 0000000..1d2b619 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/body @@ -0,0 +1,43 @@ +On Thursday 25 June 2009 16:23:04 Steve Losh wrote: +> On Jun 25, 2009, at 10:02 AM, Chris Ball <cjb@laptop.org> wrote: +> >> Oh, and obviously there must still be bugs in BE. Please submit +> >> more ;). +> > +> > Perhaps it's a good time to merge Steve Losh's CherryPy web interface? +> > +> > http://void.printf.net/pipermail/be-devel/2009-February/000095.html +> > http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ +> +> Hey, I haven't touched the web interface in a while, but I should have +> some time to fix some stuff up tonight and tomorrow. Hold off on +> merging it in until then. +> +> I'm still curious as to what people think the role of a web interface +> like this should be. When I wrote it I meant it as a single-user +> interface like the command line one. It could definitely work as a +> public, read-only interface too. + +I'd really like to have some sort of web interface for BE, also in read-only +mode. + +I am thinking to write (actually I wrote some test code) a tool to parse a BE +repository to output a set of static html pages to put online, like the "ditz +html" command, but this was before I start to play with BE sourcecode, so now +I ma thinking to implement it as a BE command. + +> If the goal is to allow more than one person to add issues, how should +> commits go? One commit per change? Commit every X minutes if necessary? + +I think that a simple web interface should be read-only. + +Eventually, to allow to add issues also from the web interface, it should be +done to a specific branch, one commit per change. + +just my 2 cents... +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/values new file mode 100644 index 0000000..2285e1d --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/e5248100-ea02-4205-a4c1-ac7a577c6362/values @@ -0,0 +1,11 @@ +Alt-id: <200906252203.08535.gian@grys.it> + + +Content-type: text/plain + + +Date: Thu, 25 Jun 2009 22:03:08 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/body b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/body new file mode 100644 index 0000000..2e4f851 --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/body @@ -0,0 +1,43 @@ +On Monday 06 July 2009 12:48:39 W. Trevor King wrote: +> On Mon, Jul 06, 2009 at 08:26:24AM +1000, Ben Finney wrote: +> > "W. Trevor King" <wking@drexel.edu> writes: +> > > On Sat, Jul 04, 2009 at 10:19:35AM +1000, Ben Finney wrote: +> > > > Instead of a separate command for each output format, could we have +> > > > a single "produce a static report of the bug database" command, and +> > > > specify output format as an option? +> > > +> > > Do people like this architecture better than my be-xml-to-mbox +> > > approach? +> > +> > I think this question is illuminated by the related question: Is mbox +> > output a static report, or another read-write data store? +> +> Gianluca is clearly thinking about a static report: + +You are right, static, but not exactly a report as I think Ben is thinking + +> +> On Fri, Jul 03, 2009 at 10:50:17PM +0200, Gianluca Montecchi wrote: +> > The goal is to be able to do something like "be html /web/page" to have +> > in the /web/page directory some static html pages that basically are the +> > dump of the be repository, much like ditz have +> +> I think truly interactive frontends like Steve's working on need to be +> build on top of libbe directly, since they'll need to make lots of +> small changes to the database, and it's to slow to be reloading the +> database for every change. Static dumps like my mbox or Gianluca's +> html could just parse the xml output of `be list' and other be +> commands. + +Ok, but if I want to have an html dump that is browseable, I need to parse the +xml. Am I correct ? +If yes, should not be easiear to use directly the libbe ? + + +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/values new file mode 100644 index 0000000..b61fc2b --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/comments/fd7ab206-5937-4ede-9e78-97aff098b677/values @@ -0,0 +1,14 @@ +Alt-id: <200907062238.56930.gian@grys.it> + + +Content-type: text/plain + + +Date: Mon, 06 Jul 2009 22:38:56 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: 55263144-9775-4b18-ab83-29d66ed91a53 + diff --git a/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/values b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/values new file mode 100644 index 0000000..093611e --- /dev/null +++ b/.be/bugs/2f048ac5-5564-4b34-b7f9-605357267ed2/values @@ -0,0 +1,20 @@ +assigned: Gianluca Montecchi <gian@grys.it> + + +creator: W. Trevor King <wking@drexel.edu> + + +reporter: Gianluca Montecchi <gian@grys.it> + + +severity: wishlist + + +status: assigned + + +summary: Static html report generation + + +time: Tue, 21 Jul 2009 18:43:06 +0000 + diff --git a/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values b/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values index 9da9004..70ec5f5 100644 --- a/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values +++ b/.be/bugs/3613e6e9-db9e-4775-8914-f31f0b4b81ac/values @@ -4,7 +4,7 @@ creator: abentley severity: minor -status: closed +status: fixed summary: auto-add files to revision control diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/body new file mode 100644 index 0000000..53456f6 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/body @@ -0,0 +1,10 @@ +Hmm, perhaps my thinking has been too revision-centric. I'm not +really sure what other level of granularity is appropriate though. +Both notifications and commits should be generated on a "per-session" +level, so maybe I'll just ignore Arch and Mercurial (for whom revising +history is difficult, so per-session commits can be more work) for the +time being ;). + +In that case, _every_ commit will be a + notify-since <revision-id> +sort of change, so I'll just use libbe.diff :). diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/values new file mode 100644 index 0000000..3e89c06 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/287d3cc1-1cd0-449a-b280-87c529e33951/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Wed, 22 Jul 2009 19:07:28 +0000 + + +From: W. Trevor King <wking@drexel.edu> + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/body new file mode 100644 index 0000000..df90918 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/body @@ -0,0 +1,16 @@ +Perhaps something like + be-handle-mail --notify-since <revision-id> +to tell subscribers about changes since the specified revision. + +This would duplicate mail to P in our first example above, but that's +not too annoying, and P might _want_ to know what R had merged from Q. + +On the other hand it would be annoying if 10 other repos merged Q and +ran the notification. + +We could make the subscription something like + subscribe BUG-ID HOST-LIST +e.g. + subscribe 1234 bugseverywhere.org,fancy_branch.com + subscribe abcd * +To allow users to whitelist hosts they want updates from. diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/values new file mode 100644 index 0000000..ddb7442 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/303986f2-0b17-4589-bf76-ed1461699c3e/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Tue, 21 Jul 2009 19:52:25 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 950ac308-f3e1-4956-885a-e79ce3025fd5 + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/body new file mode 100644 index 0000000..8842587 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/body @@ -0,0 +1,5 @@ +"all" and "new" might be valid shortnames? + +Nope, UUID string representations are restricted to hex (0-9a-f) and +"-" as per RFC 4122 section 3. + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/values new file mode 100644 index 0000000..64258d9 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/478443b3-dd69-4719-b79a-b1279f75b8e4/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Tue, 21 Jul 2009 19:53:02 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 85a2d1ac-200a-4ae7-841f-9f4e87795dbf + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/body new file mode 100644 index 0000000..99d9cc3 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/body @@ -0,0 +1,8 @@ +Obviously via the control interface: + subscribe #BUG-ID + subscribe new + subscribe all + unsubscribe #BUG-ID + ... +Implemented via .extra_strings, although we'll need +BugDir.extra_strings for the repo-wide new/all. diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/values new file mode 100644 index 0000000..eaad59f --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/85a2d1ac-200a-4ae7-841f-9f4e87795dbf/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Tue, 21 Jul 2009 19:34:20 +0000 + + +From: W. Trevor King <wking@drexel.edu> + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/body new file mode 100644 index 0000000..890a4b6 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/body @@ -0,0 +1,10 @@ +This creates an interesting situation: + Person P subscribes to bug B in repo R. + Repo S merges repo R. + Person Q comments on B in S. + S notifies P :). +which is nice. However + Person P subscribes to bug B in repo R. + Person Q comments on B in repo S. + R merges S. + P never notified about Q's comment. diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/values new file mode 100644 index 0000000..710ad30 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/950ac308-f3e1-4956-885a-e79ce3025fd5/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Tue, 21 Jul 2009 19:34:32 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 85a2d1ac-200a-4ae7-841f-9f4e87795dbf + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/body b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/body new file mode 100644 index 0000000..3c95f19 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/body @@ -0,0 +1,2 @@ +The intereface changed a bit as I implemented it. See "be help +subscribe" for details. diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/values new file mode 100644 index 0000000..1c908f7 --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/comments/f72f8640-2e50-471e-aebe-0ddb8cdd5a2a/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Wed, 22 Jul 2009 18:54:06 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 85a2d1ac-200a-4ae7-841f-9f4e87795dbf + diff --git a/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/values b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/values new file mode 100644 index 0000000..aa22fab --- /dev/null +++ b/.be/bugs/3e331b72-51fd-4408-bc0d-b6c5ac3b9f3e/values @@ -0,0 +1,20 @@ +assigned: W. Trevor King <wking@drexel.edu> + + +creator: W. Trevor King <wking@drexel.edu> + + +reporter: W. Trevor King <wking@drexel.edu> + + +severity: minor + + +status: fixed + + +summary: 'subscribe/unsubscribe (bug #..., "new bugs", "all", etc.)' + + +time: Tue, 21 Jul 2009 19:27:04 +0000 + diff --git a/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/body b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/body new file mode 100644 index 0000000..e39beb0 --- /dev/null +++ b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/body @@ -0,0 +1,24 @@ +> Currently, the code and interface of Bugs Everywhere speaks loosely +> about the term “RCS”. Sometimes it means “revision control system” +> referring in general to these types of system, and sometimes it talks +> about GNU RCS, a specific system. + +I don't think we ever rever to GNU RCS. Our current libbe.rcs.RCS +default implementation is a "don't version" backend for BE, but +perhaps this is what you're refereing to. + +> I propose that “Version Control System” (“VCS”) has emerged as a +> consensus term to refer to such systems in general, with no specific +> reference to any particular system. + +Fair enough. + +> This will change some interface (e.g. the ‘rcs_name’ configuration +> setting, and some of the methods on objects), but making this change +> while Bugs Everywhere is small will be much less painful than making it +> later. + +Hmm, we really need a method for upgrading the on-disk BugDir version. +It's hard when you need to maintain backwards compatibilty with +earlier versions in the VCS history.... + diff --git a/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/values b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/values new file mode 100644 index 0000000..7eb5b45 --- /dev/null +++ b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/29ad0d9e-c05b-4793-bb8b-e8bf237f51b3/values @@ -0,0 +1,11 @@ +Author: W. Trevor King <wking@drexel.edu> + + +Content-type: text/plain + + +Date: Mon, 03 Aug 2009 23:26:22 +0000 + + +In-reply-to: a92f97a4-e9fe-43f7-bf56-5862b03a2641 + diff --git a/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/body b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/body new file mode 100644 index 0000000..f9c166b --- /dev/null +++ b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/body @@ -0,0 +1,33 @@ +Howdy all, + +Currently, the code and interface of Bugs Everywhere speaks loosely +about the term “RCS”. Sometimes it means “revision control system” +referring in general to these types of system, and sometimes it talks +about GNU RCS, a specific system. + +I propose that “Version Control System” (“VCS”) has emerged as a +consensus term to refer to such systems in general, with no specific +reference to any particular system. + +So I'd like to modify the Bugs Everywhere code to disambiguate: the term +“VCS” will be used consistently to refer to version control systems in +general, and “RCS” will only ever refer to GNU RCS. + +This will change some interface (e.g. the ‘rcs_name’ configuration +setting, and some of the methods on objects), but making this change +while Bugs Everywhere is small will be much less painful than making it +later. + +Any objections? Any alternative suggestions? + +-- + \ “I watched the Indy 500, and I was thinking that if they left | + `\ earlier they wouldn't have to go so fast.” —Steven Wright | +_o__) | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/values b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/values new file mode 100644 index 0000000..5f3cf73 --- /dev/null +++ b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/comments/a92f97a4-e9fe-43f7-bf56-5862b03a2641/values @@ -0,0 +1,11 @@ +Alt-id: <87d49879v7.fsf@benfinney.id.au> + + +Author: Ben Finney <ben@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sat, 13 Jun 2009 19:37:16 +1000 + diff --git a/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/values b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/values new file mode 100644 index 0000000..a5cc2cd --- /dev/null +++ b/.be/bugs/427e0ca7-17f5-4a5a-8c68-98cc111a2495/values @@ -0,0 +1,17 @@ +creator: W. Trevor King <wking@drexel.edu> + + +reporter: W. Trevor King <wking@drexel.edu> + + +severity: minor + + +status: open + + +summary: 'Terminology: Version control system vs. RCS' + + +time: Mon, 03 Aug 2009 23:10:02 +0000 + diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/body deleted file mode 100644 index b8a6cb9..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/body +++ /dev/null @@ -1 +0,0 @@ -Reply 1-bis diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/values deleted file mode 100644 index 062b638..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/3e83dd98-d421-43b6-a78c-5da7aac5f279/values +++ /dev/null @@ -1,11 +0,0 @@ -Content-type: text/plain - - -Date: Tue, 21 Jul 2009 21:33:37 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - - -In-reply-to: f776d667-6959-4cab-b05d-39e07702c04b - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/body deleted file mode 100644 index 1e25ab8..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/body +++ /dev/null @@ -1 +0,0 @@ -Reply 1 diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/values deleted file mode 100644 index f335ced..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/433e2090-55d6-4b13-bc6d-0b509556f21b/values +++ /dev/null @@ -1,11 +0,0 @@ -Content-type: text/plain - - -Date: Tue, 21 Jul 2009 21:33:29 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - - -In-reply-to: a0cbbd2e-a078-41ac-b583-900e9bb2abf3 - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/body deleted file mode 100644 index df57a1a..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/body +++ /dev/null @@ -1 +0,0 @@ -prova diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/values deleted file mode 100644 index b2249ff..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/9d56f097-bf5b-4d8a-a83e-7ade8afd2b4c/values +++ /dev/null @@ -1,11 +0,0 @@ -Content-type: text/plain - - -Date: Tue, 04 Aug 2009 19:48:58 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - - -In-reply-to: 433e2090-55d6-4b13-bc6d-0b509556f21b - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/body deleted file mode 100644 index c8e09f8..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/body +++ /dev/null @@ -1 +0,0 @@ -Reply diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/values deleted file mode 100644 index 06691af..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/a0cbbd2e-a078-41ac-b583-900e9bb2abf3/values +++ /dev/null @@ -1,11 +0,0 @@ -Content-type: text/plain - - -Date: Tue, 21 Jul 2009 21:31:21 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - - -In-reply-to: f776d667-6959-4cab-b05d-39e07702c04b - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/b1a772a0-241f-42fc-8209-765162485b0a/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/b1a772a0-241f-42fc-8209-765162485b0a/body deleted file mode 100644 index 2e6c9cb..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/b1a772a0-241f-42fc-8209-765162485b0a/body +++ /dev/null @@ -1 +0,0 @@ -commento \n su due righe diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/body deleted file mode 100644 index 6db3446..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/body +++ /dev/null @@ -1 +0,0 @@ -Identato diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/values deleted file mode 100644 index 2b282c0..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/c1b9bc11-71e1-473e-ad9c-cfba0a2533d5/values +++ /dev/null @@ -1,11 +0,0 @@ -Content-type: text/plain - - -Date: Mon, 27 Jul 2009 20:08:02 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - - -In-reply-to: d74a6a82-6a08-472b-86d8-b1546c4d460f - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/body deleted file mode 100644 index bea1060..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/body +++ /dev/null @@ -1 +0,0 @@ -Commento normale diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/values deleted file mode 100644 index e26b88e..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/d74a6a82-6a08-472b-86d8-b1546c4d460f/values +++ /dev/null @@ -1,8 +0,0 @@ -Content-type: text/plain - - -Date: Wed, 22 Jul 2009 20:05:15 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/body b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/body deleted file mode 100644 index 184fdad..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/body +++ /dev/null @@ -1 +0,0 @@ -Commento di test diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/values deleted file mode 100644 index dd1fd4b..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/f776d667-6959-4cab-b05d-39e07702c04b/values +++ /dev/null @@ -1,8 +0,0 @@ -Content-type: text/plain - - -Date: Mon, 20 Jul 2009 21:54:57 +0000 - - -From: Gianluca Montecchi <gian@grys.it> - diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/values b/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/values deleted file mode 100644 index ba1e385..0000000 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/values +++ /dev/null @@ -1,20 +0,0 @@ -assigned: Gianluca Montecchi <gian@grys.it> - - -creator: gianluca <gian@galactica> - - -reporter: gianluca <gian@galactica> - - -severity: minor - - -status: assigned - - -summary: Bug di test - - -time: Fri, 03 Jul 2009 20:19:36 +0000 - diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values index c8719a0..62b29d5 100644 --- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values +++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/1ba36272-7ae1-4f95-8002-7b45e62e6790/values @@ -1,28 +1,11 @@ +Content-type: text/plain +Date: Mon, 16 Jul 2007 15:23:47 +0000 -Content-type=text/plain +From: abentley - - - -Date=Mon, 16 Jul 2007 15:23:47 +0000 - - - - - - -From=abentley - - - - - - -In-reply-to=e173c09a-1b3e-4d8a-a86a-6b8c94a76247 - - +In-reply-to: e173c09a-1b3e-4d8a-a86a-6b8c94a76247 diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values index 96cc18c..e31b44b 100644 --- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values +++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/comments/e173c09a-1b3e-4d8a-a86a-6b8c94a76247/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Sun, 15 Jul 2007 13:34:52 +0000 - - - - - - -From=jelmer +Date: Sun, 15 Jul 2007 13:34:52 +0000 +From: jelmer diff --git a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values index 9b17373..c471b0f 100644 --- a/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values +++ b/.be/bugs/508ea95e-7bc6-4b9b-9e36-a3a87014423d/values @@ -4,7 +4,7 @@ creator: jelmer severity: minor -status: closed +status: fixed summary: should check not just EDITOR but also VISUAL. diff --git a/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/body b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/body new file mode 100644 index 0000000..90b386a --- /dev/null +++ b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/body @@ -0,0 +1,20 @@ +I'm all for flexibility, so long as it doesn't require too much +hackery to implement it. You'll have two problems: + + * Determining what to commit. + + You'd have to have RCS keep a log of all versioned files it + touched, and extend .commit() to accept the keyword list "files" + and commit only those files. This is doable, but maybe not worth + the trouble. + + * Generating meaningful commit messages. + + You'd have to add this functionality to each command (and future + commands). + +This would probably not be a good idea for the Arch and Mercurial +backends, since they have a limited ability to rewrite history when +you screw up your commit message (as far as I can tell). Mercurial +does have "hg rollback", but it only works once, and lots of +typo-correction commits would just make the logs awkward. diff --git a/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/values b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/values new file mode 100644 index 0000000..5823128 --- /dev/null +++ b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/4c50ca0b-a08f-4723-b00d-4bf342cf86b6/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Fri, 24 Jul 2009 12:33:58 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: b17a561a-6100-490e-84eb-d1ae4b617940 + diff --git a/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/body b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/body new file mode 100644 index 0000000..c88a838 --- /dev/null +++ b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/body @@ -0,0 +1,9 @@ +... +Also, why doesn't be commit after it takes an action? I think it's +kinda weird that I have to commit after creating a new bug. +... + +as posted in + http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=477125 + on + Fri, 12 Jun 2009 17:03:02 +0200 diff --git a/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/values b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/values new file mode 100644 index 0000000..c069931 --- /dev/null +++ b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/comments/b17a561a-6100-490e-84eb-d1ae4b617940/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Fri, 24 Jul 2009 12:09:02 +0000 + + +From: Martin F Krafft <madduck@debian.org> + diff --git a/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/values b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/values new file mode 100644 index 0000000..d060e87 --- /dev/null +++ b/.be/bugs/52034fd0-ec50-424d-b25d-2beaf2d2c317/values @@ -0,0 +1,17 @@ +creator: W. Trevor King <wking@drexel.edu> + + +reporter: Martin F Krafft <madduck@debian.org> + + +severity: wishlist + + +status: open + + +summary: Allow autocommit option for command line interface? + + +time: Fri, 24 Jul 2009 12:04:08 +0000 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/body new file mode 100644 index 0000000..fa9e963 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/body @@ -0,0 +1,36 @@ +* W. Trevor King (wking@drexel.edu) wrote: +> One problem is that we don't actually have "releases". People just +> clone a branch, install, and go. + + This is actually the main reason I've manually mirrored the tree in +the past, so that users of our projects can get BE. If tarballs were +available I probably wouldn't even bother, but bzr really isn't a nice +dependency for just submitting/commenting on bugs. + + Isn't there a bzr web interface that at least supports creating +tarballs/zips? It is pretty standard functionality for most other VCS' +web interfaces so I'm guessing there must be, but loggerhead seems not +to support it. + + If it is a case of not having the hardware to host a more featureful +web UI I may be able to offer some assistance. + +> If you're worried about stability, just clone from a more stable branch +> (i.e., Chris' trunk). I think > this is good for distributed development, +> but maybe makes it hard to package into a conventional release-based system. +> With the bzr patch number in setup.py as the patch release number, I would be +> releasing my 0.1.363 while Chris releases his 0.1.314, even though they're at +> about the same point. I would rather be releasing my +> 0.1.20090714121347 +> while Chris releases his +> 0.1.20090713154540 +> Since then the similarity is clearer. + + Both approaches seem pretty odd to me, as a user you would have no +idea if 0.1.200910302359 has the fixes you required in a release you +were using that was numbered 0.1.200907141554. Surely you'd at least be +{pre,suf}fixing a branch name to the version. + +Thanks, + +James diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/values new file mode 100644 index 0000000..c064938 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/0c40c13a-3515-4b45-a8c3-142cceab9254/values @@ -0,0 +1,14 @@ +Alt-id: <20090714142942.GA5717@ukfsn.org> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 15:29:42 +0100 + + +From: James Rowe <jnrowe@gmail.com> + + +In-reply-to: ea01c122-e629-4d5c-afa7-b180f4a8748b + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/body new file mode 100644 index 0000000..7e1434b --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/body @@ -0,0 +1,115 @@ +On Tue, Jul 14, 2009 at 03:29:42PM +0100, James Rowe wrote: +> * W. Trevor King (wking@drexel.edu) wrote: +> > One problem is that we don't actually have "releases". People just +> > clone a branch, install, and go. +> +> This is actually the main reason I've manually mirrored the tree in +> the past, so that users of our projects can get BE. If tarballs were +> available I probably wouldn't even bother, but bzr really isn't a nice +> dependency for just submitting/commenting on bugs. + +Fair enough. It will be good when we get a commit-able web interface +and/or email interface going. + +> Isn't there a bzr web interface that at least supports creating +> tarballs/zips? It is pretty standard functionality for most other VCS' +> web interfaces so I'm guessing there must be, but loggerhead seems not +> to support it. + +Unfortunately, people would still need bzr to install the versioned source: + + libbe/_version.py: + bzr version-info --format python > $@ + +So you'll need a "release" target in the makefile to build a bzr-less +install. While you're at it, you should probably compile the manpage +too to remove the docbook-to-man dependency. + +Do people want a HEAD tarball too? There must be a bzr equivalent of + .git/hooks/post-update +but I don't know what it is. + +> > If you're worried about stability, just clone from a more stable branch +> > (i.e., Chris' trunk). I think > this is good for distributed development, +> > but maybe makes it hard to package into a conventional release-based system. +> > With the bzr patch number in setup.py as the patch release number, I would be +> > releasing my 0.1.363 while Chris releases his 0.1.314, even though they're at +> > about the same point. I would rather be releasing my +> > 0.1.20090714121347 +> > while Chris releases his +> > 0.1.20090713154540 +> > Since then the similarity is clearer. +> +> Both approaches seem pretty odd to me, as a user you would have no +> idea if 0.1.200910302359 has the fixes you required in a release you +> were using that was numbered 0.1.200907141554. Surely you'd at least be +> {pre,suf}fixing a branch name to the version. + +"be --version" currently gives you the revision id: + wking@drexel.edu-20090714121347-c6rloikst1t3m5yl +which tells you exactly which commit your installed version is based on. +If we want stick with revision numbers, how about: + major.minor.revno-branch_nick +But then we'd have to pick "unique" branch nicknames... + +I'd sliced out the timestamp portion of the revision id so that the +"patch-number" would be an integer and the branch name wasn't +references, so that "upgrading" from one branch to another could be +transparent to the users (who just see an increading timestamp), but +still available to the developers (who know when commits were made to +the branches they track, and the likelyhood of to-the-second commit +collisions in official packages is small). + +On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> "W. Trevor King" <wking@drexel.edu> writes: +> +> > On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > > Please, no. Timestamps aren't version strings, that's conflating two +> > > pieces of information with very different meanings. Correlating the +> > > two is the job of a changelog. +> > +> > Which we don't bother keeping (also NEWS), since "bzr log" works so +> > nicely. +> +> That's not a changelog, that's a commit log of every source-level commit +> made. Far too much detail for a changelog of *user-visible* changes +> associated with a release. + +I need a user around to help me determine "user-visable" changes ;). +My labmates loose interest after be init/new/comment :p. None of +which has ever changed, other than set-root -> init ;). + +> > The timestamp should at least replace the patch release number, which +> > you agree is-desirable-to increase motonically ;). +> +> I still disagree that a timestamp is the right thing to use there. If +> you want a monotonically-increasing indicator of which revision we're up +> to, that's immediately available with the revision number from VCS on +> the main branch. That also has the advantage of producing consecutive +> numbers for each revision, by definition. + +But not during branch-switches, while my method skips large regions, +but probably increases during any reasonable branch-switch. For +example, when I upgraded to rich root to pull Ben's patch, I'm not +sure if Chris upgraded the trunk and merged my branch, or just ditched +the trunk and cloned my branch. Using actual bzr revision numbers +would make switching branches that either wrong (in the case of +rev-id decreases) or confusing (in the case of a single +non-consecutive increase). + +On Tue, Jul 14, 2009 at 11:11:31AM -0400, Chris Ball wrote: +> > I agree that's a problem. I think the solution is to start making +> > releases, with specific version strings, as source tarballs. +> +> I'm happy to do this if people think it would be useful, and I don't +> yet have a strong opinion on whether the releases should come with +> version numbers or timestamps. + +I imagine the news from 2006 to now will be somewhat abridged ;). + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/values new file mode 100644 index 0000000..4538a9f --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/1f40efc1-6efc-4dd8-bdd2-97907e5aa624/values @@ -0,0 +1,14 @@ +Alt-id: <20090714171725.GB10445@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 13:17:25 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 0c40c13a-3515-4b45-a8c3-142cceab9254 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/body new file mode 100644 index 0000000..a0b6a14 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/body @@ -0,0 +1,58 @@ +Chris Ball <cjb@laptop.org> writes: + +> Hi, +> +> > That's not a changelog, that's a commit log of every source-level +> > commit made. Far too much detail for a changelog of +> > *user-visible* changes associated with a release. +> +> I think I agree with both of you. :) It seems like it's both true that +> there's no point in keeping a GNU-style ChangeLog these days + +I think I have a better understanding of why this apparent disagreement +occurred, and it was due to my sloppy use of terms. + +Looking into it further, it seems there is a certain expectation (set, +in part, by the long-standing GNU coding conventions) that a “GNU-style +ChangeLog” contains not only a particular *format*, but information at +a particular level of *detail*. + +That is, a GNU ChangeLog is intended for the style of work where one +logs all the changes made to every file in the tree each working day, +and then makes a new day's entry above that, and so on. This is, of +course, entirely redundant with a VCS revision history, which makes all +the commit messages available on request. + +So to disambiguate, that's not what I meant. I'm more familiar with a +less-frequently-updated and less-fine-detail change log; perhaps more +akin to the GNU-style “NEWS” file. + +> and that if we make a release we should write an announce mail that +> directly mentions new user-visible changes as well as attaching the +> commit log. That smaller list of highly user-visible changes could +> live in NEWS, or in the announce mail, or both. + +Yes, that's mostly what I meant. + +I actually don't think the commit log needs to be part of the release at +all. It's of interest only to those who want fine-level detail about +changes to every file, and for that purpose I think read access to the +VCS is much better. Packaging a static copy of the commit log as plain +text seems pointless. + +Rather, we should treat a user-changes level “NEWS” file (or whatever +name we choose for it) as part of the documentation, and set the +expectation among the team that it will be updated for each user-visible +change being worked on, like any other documentation. + +-- + \ “… Nature … is seen to do all things Herself and through | + `\ herself of own accord, rid of all gods.” —Titus Lucretius | +_o__) Carus, c. 40 BCE | +Ben Finney + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/values new file mode 100644 index 0000000..4e3ade1 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2bb7b4d0-6290-4771-9fff-4aa2e8086b1a/values @@ -0,0 +1,14 @@ +Alt-id: <87hbxdhtkp.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Thu, 16 Jul 2009 19:21:10 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/body new file mode 100644 index 0000000..5f478b5 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/body @@ -0,0 +1,96 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA1 + +W. Trevor King wrote: +> Thinking about this some more, I think that the role of the +> main-branch is to officially sanction the current state of the code as +> "released". If a series of commits will leave a branch in a +> known-unusable form, they should be carried out in some appropriately +> named development branch. Then the log of commits to the main branch +> ("bzr log -n 1" for bzr > ) should produce a fairly respectable +> changelog. + +This is how we develop bzr itself. The mainline is controlled by PQM, +which is a tool that merges feature branches, runs the tests, and +commits only if the tests pass. + +$ bzr log --short --limit 10 + 4534 Canonical.com Patch Queue Manager 2009-07-14 [merge] + (abentley) Implement merge --interactive + + 4533 Canonical.com Patch Queue Manager 2009-07-14 [merge] + (jml) Merge in changes from 1.17 branch. + + 4532 Canonical.com Patch Queue Manager 2009-07-14 [merge] + (igc) zc.buildout Windows build support (Sidnei da Silva) + + 4531 Canonical.com Patch Queue Manager 2009-07-13 [merge] + (vila) Delete forgotten debug print + + 4530 Canonical.com Patch Queue Manager 2009-07-13 [merge] + (vila) Isolate some tests from TZ + + 4529 Canonical.com Patch Queue Manager 2009-07-13 [merge] + (igc) Bazaar 2.0 Upgrade Guide + + 4528 Canonical.com Patch Queue Manager 2009-07-13 [merge] + (mbp) correction to news + + 4527 Canonical.com Patch Queue Manager 2009-07-13 [merge] + (jml) Merge in 1.17 branch, updating version numbers and NEWS file. + + 4526 Canonical.com Patch Queue Manager 2009-07-10 [merge] + (mbp, vila) Finish the *_implementation to per_* test renaming + + 4525 Canonical.com Patch Queue Manager 2009-07-10 [merge] + (vila) Quicker check for changes in mutable trees + +You can also see all the merges as they come into the mainline: + +$ bzr log --short --limit 10 --include-merges + 4534 Canonical.com Patch Queue Manager 2009-07-14 [merge] + (abentley) Implement merge --interactive + + 4526.6.15 Aaron Bentley 2009-07-14 + Update command help + + 4526.6.14 Aaron Bentley 2009-07-14 + Use default DiffWriter. + + 4526.6.13 Aaron Bentley 2009-07-14 + Add docstring to do_interactive. + + 4526.6.12 Aaron Bentley 2009-07-14 + Updates from review. + + 4526.6.11 Aaron Bentley 2009-07-13 + Update NEWS. + + 4526.6.10 Aaron Bentley 2009-07-13 [merge] + Merged apply-vocab into merge-interactive. + + 4526.7.4 Aaron Bentley 2009-07-13 [merge] + Merged bzr.dev into apply-vocab. + + 4526.6.9 Aaron Bentley 2009-07-13 [merge] + Merged apply-vocab into merge-interactive. + + 4526.7.3 Aaron Bentley 2009-07-13 + Test shelve_change. + +> This also means that _every_commit_ to a main branch would +> be an official release. + +We don't do that. We have official releases every 4 weeks, but we do +believe that running bzr.dev is pretty safe, because it's always tested +and our test suite is quite thorough. + +Aaron +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.9 (GNU/Linux) +Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org + +iEYEARECAAYFAkpcznIACgkQ0F+nu1YWqI0yhACePTFUUp6u+Dw+8IRwWOWBQRtb +TgsAniJq4lqnDfjNACMr7IEt7xYJhx7m +=BbGG +-----END PGP SIGNATURE----- diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/values new file mode 100644 index 0000000..5134cf2 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/2c95ee07-462d-42cf-8dc3-8f5389a392cb/values @@ -0,0 +1,14 @@ +Alt-id: <4A5CCE76.9040106@aaronbentley.com> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 14:29:10 -0400 + + +From: Aaron Bentley <aaron@aaronbentley.com> + + +In-reply-to: ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/body new file mode 100644 index 0000000..b34e037 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/body @@ -0,0 +1,52 @@ +On Tue, Jul 14, 2009 at 07:34:04PM +0100, jnrowe@gmail.com wrote: +> [This time to the list] +> +> * W. Trevor King (wking@drexel.edu) wrote: +> > On Tue, Jul 14, 2009 at 03:29:42PM +0100, James Rowe wrote: +> > > Isn't there a bzr web interface that at least supports creating +> > > tarballs/zips? It is pretty standard functionality for most other VCS' +> > > web interfaces so I'm guessing there must be, but loggerhead seems not +> > > to support it. +> > +> > Unfortunately, people would still need bzr to install the versioned source: +> > +> > libbe/_version.py: +> > bzr version-info --format python > $@ +> +> I hadn't even seen that change go in. The last upstream change in the +> version I have installed locally was by you on 2008-11-24. + +It's only been in Chris' http://bzr.bugseverywhere.org/be/ branch +since revno: 321, 2009-06-25. Obviously we may have to adjust the +--verison output once we settle on a versioning scheme, but whatever +we pick, I think having the auto-generated libbe/_version.py around +for pinpointing bugs is worth the trouble of requiring bzr when +building distribution tarballs. + +> > So you'll need a "release" target in the makefile to build a bzr-less +> > install. While you're at it, you should probably compile the manpage +> > too to remove the docbook-to-man dependency. +> +> Maybe for others. Our packages just don't have the manpage as it is only +> the "be help" text reformatted, the easy option is sometimes the right +> one :) Also, I've just noticed that it has even less documentation in +> the bzr tree[1] making its installation much less compelling unless your +> packaging rules require a man page like Debians. +> +> Out of curiosity why is the Makefile being used for this stuff anyway? +> It is going to make it difficult to build locally when we finally get +> around to merging. Examples: If distutils was being used exclusively it +> would fix the #! lines in xml/*. We'd be able to point Python +> $version_of_the_day at setup.py instead of having to sed the Makefile or +> run setup and manually install other files. + +I speak Makefile better than I speak distutils ;). I'm not sure how +to translate the be.1 generation/installation or the libbe/_version.py +generation into distutils. Anyone else? + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/values new file mode 100644 index 0000000..0c1e529 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/31beb504-c72b-4304-95ba-a66d2bcbc46a/values @@ -0,0 +1,14 @@ +Alt-id: <20090714191145.GB10606@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 15:11:45 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 6e315abe-a080-4369-8729-4aea2dee8494 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/body new file mode 100644 index 0000000..7ffe231 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/body @@ -0,0 +1,38 @@ +[This time to the list] + +* W. Trevor King (wking@drexel.edu) wrote: +> On Tue, Jul 14, 2009 at 03:29:42PM +0100, James Rowe wrote: +> > Isn't there a bzr web interface that at least supports creating +> > tarballs/zips? It is pretty standard functionality for most other VCS' +> > web interfaces so I'm guessing there must be, but loggerhead seems not +> > to support it. +> +> Unfortunately, people would still need bzr to install the versioned source: +> +> libbe/_version.py: +> bzr version-info --format python > $@ + + I hadn't even seen that change go in. The last upstream change in the +version I have installed locally was by you on 2008-11-24. + +> So you'll need a "release" target in the makefile to build a bzr-less +> install. While you're at it, you should probably compile the manpage +> too to remove the docbook-to-man dependency. + + Maybe for others. Our packages just don't have the manpage as it is only +the "be help" text reformatted, the easy option is sometimes the right +one :) Also, I've just noticed that it has even less documentation in +the bzr tree[1] making its installation much less compelling unless your +packaging rules require a man page like Debians. + + Out of curiosity why is the Makefile being used for this stuff anyway? +It is going to make it difficult to build locally when we finally get +around to merging. Examples: If distutils was being used exclusively it +would fix the #! lines in xml/*. We'd be able to point Python +$version_of_the_day at setup.py instead of having to sed the Makefile or +run setup and manually install other files. + +Thanks, + +James + 1. http://pullcord.laptop.org:4000/revision/314.1.15/doc/be.1.sgml diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/values new file mode 100644 index 0000000..a4534a2 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/6e315abe-a080-4369-8729-4aea2dee8494/values @@ -0,0 +1,14 @@ +Alt-id: <20090714183404.GB26032@ukfsn.org> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 19:34:04 +0100 + + +From: jnrowe@gmail.com + + +In-reply-to: 1f40efc1-6efc-4dd8-bdd2-97907e5aa624 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/body new file mode 100644 index 0000000..24ff7b0 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/body @@ -0,0 +1,58 @@ +"W. Trevor King" <wking@drexel.edu> writes: + +> Currently setup.py sets the version number for BE to 0.0.193 and the +> url to http://panoramicfeedback.com/opensource/. These are both a bit +> outdated ;). + +Right, that should change. + +> I've switched my branch over to the current url, and moved to +> last-commit-timestamp version numbers. + +Please, no. Timestamps aren't version strings, that's conflating two +pieces of information with very different meanings. Correlating the two +is the job of a changelog. + +> This removes the "prefered branch" issues with the old scheme, and +> version numbers should increase monotonically + +The English word “should” is ambiguous in this context. Are you saying +this is desirable, or are you predicting that it will likely be the +case? + +I don't see how it's either, so am doubly confused :-) + +> but it looses any stability information suggested by the preceding +> 0.0. + +The convention for three-part version strings is often: + + * Major release number (big changes in how the program works, + increasing monotonically per major release, with “0”indicating no + major release yet) + + * Minor release number (smaller impact on how the program works, + increasing monotonically per minor release, with “0” indicating no + minor release yet since the previous major) + + * Patch release number (bug-fix and other changes that don't affect + the documented interface, increasing monotonically per patch + release, with “0” indicating no patch release since the previous + major or minor) + +Obviously there's no standard or enforcement for this, but that's the +interpretation I usually give to dotted version strings in the absence +of more formal declaration specific to the project. + +> We can add those back in if people want. Does the first 0 mean +> "interfaces in flux" and the second 0 mean "lots of bugs"? If so, I +> think we're up to 0.1, since the major features are pretty calm. + +I disagree with your interpretation and prefer mine, above; on that +basis, I agree that we're at least up to version 0.1 by now :-) + +-- + \ “A lot of water has been passed under the bridge since this | + `\ variation has been played.” chess book, Russia | +_o__) | +Ben Finney diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/values new file mode 100644 index 0000000..1b70837 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/744435b7-1521-4059-a55d-f0c403d7b4d8/values @@ -0,0 +1,14 @@ +Alt-id: <87ocrnjvat.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 22:36:26 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/body new file mode 100644 index 0000000..8b32751 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/body @@ -0,0 +1,14 @@ +Hi, + + > Which we don't bother keeping (also NEWS), since "bzr log" works + > so nicely. If you really want an standard changelog, see + > http://mail.gnome.org/archives/desktop-devel-list/2007-September/msg00186.html + +Actually, there's a `bzr log --gnu-changelog` now, and `bzr help +log-formats` offers some more styles. (None of them seem to match +my preferred style for release announcements exactly, which would +be `git shortlog`-style.) + +- Chris. +-- +Chris Ball <cjb@laptop.org> diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/values new file mode 100644 index 0000000..ea6e6aa --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a536cee5-cc8d-4b18-b491-657e0c7998b4/values @@ -0,0 +1,14 @@ +Alt-id: <m3ljmrfgot.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 11:05:38 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: ea01c122-e629-4d5c-afa7-b180f4a8748b + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/body new file mode 100644 index 0000000..33a8d66 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/body @@ -0,0 +1,51 @@ +I don't think anyone's changing their mind ;), so tallying the +comments so far: + +On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> I still disagree that a timestamp is the right thing to use there. If +> you want a monotonically-increasing indicator of which revision we're up +> to, that's immediately available with the revision number from VCS on +> the main branch. That also has the advantage of producing consecutive +> numbers for each revision, by definition. + ++1 for trunk version number. + +On Tue, Jul 14, 2009 at 05:27:52PM +0200, Elena of Valhalla wrote: +> I also have a weak preference for version numbers, as long as they +> give useful informations on the state the release. + ++1 for trunk version number. + +On Tue, Jul 14, 2009 at 02:29:10PM -0400, Aaron Bentley wrote: +> We don't do that. We have official releases every 4 weeks, but we do +> believe that running bzr.dev is pretty safe, because it's always tested +> and our test suite is quite thorough. + ++1 for by hand version bumps. + +On Fri, Jul 17, 2009 at 11:37:49PM +0200, Gianluca Montecchi wrote: +> The version number of trunk _is_ should be the official version number of the +> Bugs Everywhere releases. +> The version number in branch does not means nothing outside the branch. +> At least we can have a mechanism to build a version number scheme that is +> consistent for us to be able to merge branch easily. + ++1 for trunk version number. + +And me with my timestamps ;). + +Sounds like we should go with trunk version number, but that it should +be set by hand whenever Chris decides to release something, since the +rest of us don't know what version the trunk is on. Unless we do +something like: + bzr log -n 0 | grep -B2 'nick: be$' | head -n1 | sed 's/ *revno: \([0-9]*\).*/\1/' +to extract the last trunk commit referenced from our branch. + +Implementation preferences? (i.e. Chris vs. regexp matching :p) + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/values new file mode 100644 index 0000000..1acfd91 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/a845096e-3cdf-41ed-a0e3-283439665b92/values @@ -0,0 +1,14 @@ +Alt-id: <20090718105008.GA31639@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 06:50:08 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: c35835c0-8f9f-4090-ba92-1f616867e486 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/body new file mode 100644 index 0000000..063afcb --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/body @@ -0,0 +1,30 @@ +Hi, + + > That's not a changelog, that's a commit log of every source-level + > commit made. Far too much detail for a changelog of + > *user-visible* changes associated with a release. + +I think I agree with both of you. :) It seems like it's both true that +there's no point in keeping a GNU-style ChangeLog these days, and that +if we make a release we should write an announce mail that directly +mentions new user-visible changes as well as attaching the commit log. +That smaller list of highly user-visible changes could live in NEWS, +or in the announce mail, or both. + + > I agree that's a problem. I think the solution is to start making + > releases, with specific version strings, as source tarballs. + +I'm happy to do this if people think it would be useful, and I don't +yet have a strong opinion on whether the releases should come with +version numbers or timestamps. + +Thanks, + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/values new file mode 100644 index 0000000..761c219 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/aad59898-8949-44fb-ad0b-2acea6eb2ef8/values @@ -0,0 +1,14 @@ +Alt-id: <m3k52bfgf0.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 11:11:31 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/body new file mode 100644 index 0000000..1e2a870 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/body @@ -0,0 +1,37 @@ +On Tue, Jul 14, 2009 at 01:17:25PM -0400, W. Trevor King wrote: +> On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> > "W. Trevor King" <wking@drexel.edu> writes: +> > +> > > On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > > > Please, no. Timestamps aren't version strings, that's conflating two +> > > > pieces of information with very different meanings. Correlating the +> > > > two is the job of a changelog. +> > > +> > > Which we don't bother keeping (also NEWS), since "bzr log" works so +> > > nicely. +> > +> > That's not a changelog, that's a commit log of every source-level commit +> > made. Far too much detail for a changelog of *user-visible* changes +> > associated with a release. +> +> I need a user around to help me determine "user-visable" changes ;). +> My labmates loose interest after be init/new/comment :p. None of +> which has ever changed, other than set-root -> init ;). + +Thinking about this some more, I think that the role of the +main-branch is to officially sanction the current state of the code as +"released". If a series of commits will leave a branch in a +known-unusable form, they should be carried out in some appropriately +named development branch. Then the log of commits to the main branch +("bzr log -n 1" for bzr > ) should produce a fairly respectable +changelog. Obviously we are all quite guilty of doing most of our +development in single branches, but it may be a useful model going +forward. This also means that _every_commit_ to a main branch would +be an official release. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/values new file mode 100644 index 0000000..4439bad --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ae4f8f1e-6f86-4f81-ba9f-4042deb2ee68/values @@ -0,0 +1,14 @@ +Alt-id: <20090714182034.GA10606@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 14:20:34 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 1f40efc1-6efc-4dd8-bdd2-97907e5aa624 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/body new file mode 100644 index 0000000..e02bd38 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/body @@ -0,0 +1,25 @@ +On Tue, Jul 14, 2009 at 5:11 PM, Chris Ball<cjb@laptop.org> wrote: +> > I agree that's a problem. I think the solution is to start making +> > releases, with specific version strings, as source tarballs. +> +> I'm happy to do this if people think it would be useful, and I don't +> yet have a strong opinion on whether the releases should come with +> version numbers or timestamps. + +as an user of be that plans to try and "package" it for openembedded, +a release would be very useful: writing a recipe that downloads a +specific commit from bzr and builds it is probably feasible, but +definitely not ideal. + +I also have a weak preference for version numbers, as long as they +give useful informations on the state the release. + +-- +Elena ``of Valhalla'' + +email: elena.valhalla@gmail.com + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/values new file mode 100644 index 0000000..5d49c42 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/b19a8f6a-1d7b-4887-a9df-123d59b0cd9b/values @@ -0,0 +1,14 @@ +Alt-id: <5c5e5c350907140827u218553e8rc5773325d43c2bf2@mail.gmail.com> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 17:27:52 +0200 + + +From: Elena of Valhalla <elena.valhalla@gmail.com> + + +In-reply-to: aad59898-8949-44fb-ad0b-2acea6eb2ef8 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/body new file mode 100644 index 0000000..d8014d2 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/body @@ -0,0 +1,102 @@ +On Thursday 16 July 2009 12:38:55 W. Trevor King wrote: +> On Thu, Jul 16, 2009 at 07:32:31PM +1000, Ben Finney wrote: +> > "W. Trevor King" <wking@drexel.edu> writes: +> > > On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> > > > "W. Trevor King" <wking@drexel.edu> writes: +> > > > > On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > > > > > Please, no. Timestamps aren't version strings, that's conflating +> > > > > > two pieces of information with very different meanings. +> > > > > > Correlating the two is the job of a [NEWS file]. +> > > > +> > > > If you want a monotonically-increasing indicator of which revision +> > > > we're up to, that's immediately available with the revision number +> > > > from VCS on the main branch. That also has the advantage of +> > > > producing consecutive numbers for each revision, by definition. +> > > +> > > But not during branch-switches, while my method skips large regions, +> > > but probably increases during any reasonable branch-switch. +> > +> > I've read this several times now, and I don't see what it's saying. +> > +> > The assumption I'm making is that there is a single canonical “main +> > branch”, from which releases will be made. +> +> I don't think you need to assume this. See my "virtual branch" +> argument below. + +But if we have a canonical "main branch" that we release, and the packager +get, we can refer to it as the stable branch, that it is not a bad idea. + + + +> > The version number set in that branch is the one which determines +> > the version of Bugs Everywhere as a whole. +> +> If you are suggesting that the dev branches adjust their release +> number _by_hand_ to match the current trunk release number, that +> allows switching, but sounds like a lot of work and isn't correct +> anyway, since they are not in the same state as the trunk. + +The version number of trunk _is_ should be the official version number of the +Bugs Everywhere releases. +The version number in branch does not means nothing outside the branch. +At least we can have a mechanism to build a version number scheme that is +consistent for us to be able to merge branch easily. + +> > The revision number is only useful in the context of the branch, so it +> > only matters when comparing versions within a branch. When you switch +> > between branches, if you're interested in the revision number you'll +> > still need to know which branch you're talking about. +> +> I think this is our main disagreement. I see all the branches as part +> of the same codebase, with monotonically increasing timestamp patch +> numbers. If you were to collapse all the commit snapshots down into a +> single chronological "virtual branch", it would still make sense, it +> would just be a bit unorganized. We do all try to move in the same +> general direction ;). + +I don't think that, outside the developers, a version number like + +cjb@laptop.org-20090713154540-ve4pmydqzb1ghgvc + +is a good choice, not for the user of BE, not for the packager of BE + + +> > This, then, is an argument for not having the revision number in the +> > version string at all. The version then becomes a more traditional +> > “major.minor.patch” tuple, and is only ever updated when some release +> > manager of the canonical branch decides it's correct to do so. +> +> It is an argument for not using the revision number. You can avoid +> revision numbers by using hand-coded patch numbers, or by using +> timestamps, which is what we're trying to decide on :p. + +We can use both. +During the development we can use version number like + +x.y.z.timestamp + +As we decide to release a stable version, the release manager set the version +number to a more traditional x.y.z format, and create a branch (stable branch) + +This way we have these advantages: + +1) an user have a simple version number to use for bug report/feature +request/help request + +2) a packager have an easy life to choose to package a stable or a trunk +version, knowing what are they doing + +bonus) we can maintain a stable and a developmente source tree/branch, where +in the development tree we can make also backward incompatible modification to +the source without making any damage to the users/packagers, while in the +stable branch we can make only bugfix/security fix or port from the devel branch +some interesting features as long as they don't break compatibility. + +bye +Gianluca + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/values new file mode 100644 index 0000000..a828a3a --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/c35835c0-8f9f-4090-ba92-1f616867e486/values @@ -0,0 +1,14 @@ +Alt-id: <200907172337.49779.gian@grys.it> + + +Content-type: text/plain + + +Date: Fri, 17 Jul 2009 23:37:49 +0200 + + +From: Gianluca Montecchi <gian@grys.it> + + +In-reply-to: f925e56f-26f9-4620-82fb-a0f160f27921 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/body new file mode 100644 index 0000000..4e8445a --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/body @@ -0,0 +1,18 @@ +Currently setup.py sets the version number for BE to 0.0.193 and the +url to http://panoramicfeedback.com/opensource/. These are both a bit +outdated ;). I've switched my branch over to the current url, and +moved to last-commit-timestamp version numbers. This removes the +"prefered branch" issues with the old scheme, and version numbers +should increase monotonically, but it looses any stability information +suggested by the preceding 0.0. + +We can add those back in if people want. Does the first 0 mean +"interfaces in flux" and the second 0 mean "lots of bugs"? If so, I +think we're up to 0.1, since the major features are pretty calm. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/values new file mode 100644 index 0000000..94bb94d --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c/values @@ -0,0 +1,11 @@ +Alt-id: <20090714110543.GB4855@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 07:05:43 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/body new file mode 100644 index 0000000..fce4941 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/body @@ -0,0 +1,72 @@ +On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> "W. Trevor King" <wking@drexel.edu> writes: +> > I've switched my branch over to the current url, and moved to +> > last-commit-timestamp version numbers. +> +> Please, no. Timestamps aren't version strings, that's conflating two +> pieces of information with very different meanings. Correlating the two +> is the job of a changelog. + +Which we don't bother keeping (also NEWS), since "bzr log" works so nicely. +If you really want an standard changelog, see + http://mail.gnome.org/archives/desktop-devel-list/2007-September/msg00186.html + +> > This removes the "prefered branch" issues with the old scheme, and +> > version numbers should increase monotonically +> +> The English word “should” is ambiguous in this context. Are you saying +> this is desirable, or are you predicting that it will likely be the +> case? + +Both. + +> I don't see how it's either, so am doubly confused :-) + +The timestamp should at least replace the patch release number, which +you agree is-desirable-to increase motonically ;). I also predict +that it will increase monotonically for any given branch, since the +branch HEAD will have both the most recent commit and the highest +version number. The only problem I can think of is having your clock +_way_ off, and that is unlikely enough to ignore. If you hop between +branches, the timestamp is much more likely to increase going to the +more modern branch than the bzr revision number, which desynchronize +between branches fairly quickly. + +> The convention for three-part version strings is often: +> +> * Major release number (big changes in how the program works, +> increasing monotonically per major release, with “0”indicating no +> major release yet) +> +> * Minor release number (smaller impact on how the program works, +> increasing monotonically per minor release, with “0” indicating no +> minor release yet since the previous major) +> +> * Patch release number (bug-fix and other changes that don't affect +> the documented interface, increasing monotonically per patch +> release, with “0” indicating no patch release since the previous +> major or minor) + +One problem is that we don't actually have "releases". People just +clone a branch, install, and go. If you're worried about stability, +just clone from a more stable branch (i.e., Chris' trunk). I think +this is good for distributed development, but maybe makes it hard to +package into a conventional release-based system. With the bzr patch +number in setup.py as the patch release number, I would be releasing +my 0.1.363 while Chris releases his 0.1.314, even though they're at +about the same point. I would rather be releasing my + 0.1.20090714121347 +while Chris releases his + 0.1.20090713154540 +Since then the similarity is clearer. + +At any rate, I think the two approaches are close enough that an +auto-updating timestamp beats a manually bumped patch number, since +no-one ever actually bumps the patch number ;). + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/values new file mode 100644 index 0000000..c863757 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ea01c122-e629-4d5c-afa7-b180f4a8748b/values @@ -0,0 +1,14 @@ +Alt-id: <20090714133732.GB6160@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 14 Jul 2009 09:37:32 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 744435b7-1521-4059-a55d-f0c403d7b4d8 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/body new file mode 100644 index 0000000..5eeb353 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/body @@ -0,0 +1,88 @@ +On Thu, Jul 16, 2009 at 07:32:31PM +1000, Ben Finney wrote: +> "W. Trevor King" <wking@drexel.edu> writes: +> +> > On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> > > "W. Trevor King" <wking@drexel.edu> writes: +> > > +> > > > On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > > > > Please, no. Timestamps aren't version strings, that's conflating +> > > > > two pieces of information with very different meanings. +> > > > > Correlating the two is the job of a [NEWS file]. +> > +> > > If you want a monotonically-increasing indicator of which revision +> > > we're up to, that's immediately available with the revision number +> > > from VCS on the main branch. That also has the advantage of +> > > producing consecutive numbers for each revision, by definition. +> > +> > But not during branch-switches, while my method skips large regions, +> > but probably increases during any reasonable branch-switch. +> +> I've read this several times now, and I don't see what it's saying. +> +> The assumption I'm making is that there is a single canonical “main +> branch”, from which releases will be made. + +I don't think you need to assume this. See my "virtual branch" +argument below. + +> The version number set in that branch is the one which determines +> the version of Bugs Everywhere as a whole. + +If you are suggesting that the dev branches adjust their release +number _by_hand_ to match the current trunk release number, that +allows switching, but sounds like a lot of work and isn't correct +anyway, since they are not in the same state as the trunk. + +> The revision number is only useful in the context of the branch, so it +> only matters when comparing versions within a branch. When you switch +> between branches, if you're interested in the revision number you'll +> still need to know which branch you're talking about. + +I think this is our main disagreement. I see all the branches as part +of the same codebase, with monotonically increasing timestamp patch +numbers. If you were to collapse all the commit snapshots down into a +single chronological "virtual branch", it would still make sense, it +would just be a bit unorganized. We do all try to move in the same +general direction ;). + +> Switching between branches doesn't change the canonical version string. + +Different released code should have different version numbers. + +> > For example, when I upgraded to rich root to pull Ben's patch, I'm not +> > sure if Chris upgraded the trunk and merged my branch, or just ditched +> > the trunk and cloned my branch. Using actual bzr revision numbers +> > would make switching branches that either wrong (in the case of rev-id +> > decreases) or confusing (in the case of a single non-consecutive +> > increase). +> +> This, then, is an argument for not having the revision number in the +> version string at all. The version then becomes a more traditional +> “major.minor.patch” tuple, and is only ever updated when some release +> manager of the canonical branch decides it's correct to do so. + +It is an argument for not using the revision number. You can avoid +revision numbers by using hand-coded patch numbers, or by using +timestamps, which is what we're trying to decide on :p. + +> If we use the ‘bzr version-info --format=python > foo_version.py’ +> command in some build routine, the branch's revision number will be +> available directly within Python by importing that module. That would +> allow it to be output in some UI, if that's what you're interested in +> seeing. + +True. Which means that whichever version string wins out, the other +side will still be able to access the info we both want included ;). +We can certainly suggest that bug reporters submit their + be --verbose-version +when they submit bugs. The only role of the official "version string" +is to make life easy for packagers. If they woln't be switching +branches, then either of our proposals are fine. If they will, then +I think timestamps work better. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/values new file mode 100644 index 0000000..36f4007 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/f925e56f-26f9-4620-82fb-a0f160f27921/values @@ -0,0 +1,14 @@ +Alt-id: <20090716103855.GA8579@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Thu, 16 Jul 2009 06:38:55 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: fdb615a4-168a-467b-8090-875c998455e5 + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/body new file mode 100644 index 0000000..b36292a --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/body @@ -0,0 +1,55 @@ +"W. Trevor King" <wking@drexel.edu> writes: + +> On Wed, Jul 15, 2009 at 12:54:05AM +1000, Ben Finney wrote: +> > "W. Trevor King" <wking@drexel.edu> writes: +> > +> > > On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > > > Please, no. Timestamps aren't version strings, that's conflating +> > > > two pieces of information with very different meanings. +> > > > Correlating the two is the job of a [NEWS file]. +> +> > If you want a monotonically-increasing indicator of which revision +> > we're up to, that's immediately available with the revision number +> > from VCS on the main branch. That also has the advantage of +> > producing consecutive numbers for each revision, by definition. +> +> But not during branch-switches, while my method skips large regions, +> but probably increases during any reasonable branch-switch. + +I've read this several times now, and I don't see what it's saying. + +The assumption I'm making is that there is a single canonical “main +branch”, from which releases will be made. The version number set in +that branch is the one which determines the version of Bugs Everywhere +as a whole. + +The revision number is only useful in the context of the branch, so it +only matters when comparing versions within a branch. When you switch +between branches, if you're interested in the revision number you'll +still need to know which branch you're talking about. + +Switching between branches doesn't change the canonical version string. + +> For example, when I upgraded to rich root to pull Ben's patch, I'm not +> sure if Chris upgraded the trunk and merged my branch, or just ditched +> the trunk and cloned my branch. Using actual bzr revision numbers +> would make switching branches that either wrong (in the case of rev-id +> decreases) or confusing (in the case of a single non-consecutive +> increase). + +This, then, is an argument for not having the revision number in the +version string at all. The version then becomes a more traditional +“major.minor.patch” tuple, and is only ever updated when some release +manager of the canonical branch decides it's correct to do so. + +If we use the ‘bzr version-info --format=python > foo_version.py’ +command in some build routine, the branch's revision number will be +available directly within Python by importing that module. That would +allow it to be output in some UI, if that's what you're interested in +seeing. + +-- + \ “Never do anything against conscience even if the state demands | + `\ it.” —Albert Einstein | +_o__) | +Ben Finney diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/values new file mode 100644 index 0000000..d373a73 --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/fdb615a4-168a-467b-8090-875c998455e5/values @@ -0,0 +1,14 @@ +Alt-id: <87d481ht1s.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Thu, 16 Jul 2009 19:32:31 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/body b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/body new file mode 100644 index 0000000..30e3cbd --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/body @@ -0,0 +1,44 @@ +"W. Trevor King" <wking@drexel.edu> writes: + +> On Tue, Jul 14, 2009 at 10:36:26PM +1000, Ben Finney wrote: +> > Please, no. Timestamps aren't version strings, that's conflating two +> > pieces of information with very different meanings. Correlating the +> > two is the job of a changelog. +> +> Which we don't bother keeping (also NEWS), since "bzr log" works so +> nicely. + +That's not a changelog, that's a commit log of every source-level commit +made. Far too much detail for a changelog of *user-visible* changes +associated with a release. + +> The timestamp should at least replace the patch release number, which +> you agree is-desirable-to increase motonically ;). + +I still disagree that a timestamp is the right thing to use there. If +you want a monotonically-increasing indicator of which revision we're up +to, that's immediately available with the revision number from VCS on +the main branch. That also has the advantage of producing consecutive +numbers for each revision, by definition. + +> One problem is that we don't actually have "releases". People just +> clone a branch, install, and go. + +I agree that's a problem. I think the solution is to start making +releases, with specific version strings, as source tarballs. + +James Rowe <jnrowe@gmail.com> writes: + +> Isn't there a bzr web interface that at least supports creating +> tarballs/zips? + +Even better: ‘bzr export /tmp/foo.tar.gz’ will create a source tarball +of all the files in the branch's VCS inventory. All we need to do is +start the practice of tagging a release in the VCS, and export the +tarball at that time. + +-- + \ “Pinky, are you pondering what I'm pondering?” “Well, I think | + `\ so (hiccup), but Kevin Costner with an English accent?” —_Pinky | +_o__) and The Brain_ | +Ben Finney diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/values new file mode 100644 index 0000000..aa9b55f --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/comments/ffbf5ac9-e2f5-47ab-9c3c-33989c81ad42/values @@ -0,0 +1,14 @@ +Alt-id: <87k52bjoxe.fsf_-_@benfinney.id.au> + + +Content-type: text/plain + + +Date: Wed, 15 Jul 2009 00:54:05 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: cdf15bdd-d3fe-4251-9d0b-f1b687e9a26c + diff --git a/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/values b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/values new file mode 100644 index 0000000..b9e8dff --- /dev/null +++ b/.be/bugs/529c290e-b1cf-4800-be7e-68f1ecb9565c/values @@ -0,0 +1,17 @@ +creator: W. Trevor King <wking@drexel.edu> + + +reporter: W. Trevor King <wking@drexel.edu> + + +severity: wishlist + + +status: open + + +summary: How should we version BE? + + +time: Tue, 21 Jul 2009 17:19:22 +0000 + diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values index 4cb1f35..59a3828 100644 --- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values +++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/db2c18d9-9573-4d68-88a5-ee47ed24b813/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Thu, 24 Mar 2005 17:04:47 +0000 - - - - - - -From=abentley +Date: Thu, 24 Mar 2005 17:04:47 +0000 +From: abentley diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values index 51af41d..ddbdc15 100644 --- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values +++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/comments/ec16300f-529a-4492-8327-f9a72e4447c2/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Thu, 24 Mar 2005 13:05:13 +0000 - - - - - - -From=abentley +Date: Thu, 24 Mar 2005 13:05:13 +0000 +From: abentley diff --git a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values index 94a1f9f..e2a930c 100644 --- a/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values +++ b/.be/bugs/7ba4bc51-b251-483a-a67a-f1b89c83f6af/values @@ -4,7 +4,7 @@ creator: abentley severity: serious -status: closed +status: fixed summary: Add test cases diff --git a/.be/bugs/8385a1fb-63df-4ca6-81cd-28ede83bb0c2/values b/.be/bugs/8385a1fb-63df-4ca6-81cd-28ede83bb0c2/values index 918f6be..5d80e70 100644 --- a/.be/bugs/8385a1fb-63df-4ca6-81cd-28ede83bb0c2/values +++ b/.be/bugs/8385a1fb-63df-4ca6-81cd-28ede83bb0c2/values @@ -10,7 +10,7 @@ severity: minor status: wontfix -summary: Add the html files for the status detail +summary: Add the html files for the status detail to "be html" output time: Fri, 03 Jul 2009 22:56:09 +0000 diff --git a/.be/bugs/9b1a0e71-4f7d-40b1-ab32-18496bf19a3f/values b/.be/bugs/9b1a0e71-4f7d-40b1-ab32-18496bf19a3f/values index 600f2c3..4b6bb4f 100644 --- a/.be/bugs/9b1a0e71-4f7d-40b1-ab32-18496bf19a3f/values +++ b/.be/bugs/9b1a0e71-4f7d-40b1-ab32-18496bf19a3f/values @@ -10,7 +10,7 @@ severity: minor status: wontfix -summary: Add the html files for the severity detail +summary: Add the html files for the severity detail to "be html" output time: Fri, 03 Jul 2009 22:56:19 +0000 diff --git a/.be/bugs/c271a802-d324-48a6-b01d-63e4a72aa43e/values b/.be/bugs/c271a802-d324-48a6-b01d-63e4a72aa43e/values index 8b58566..b859364 100644 --- a/.be/bugs/c271a802-d324-48a6-b01d-63e4a72aa43e/values +++ b/.be/bugs/c271a802-d324-48a6-b01d-63e4a72aa43e/values @@ -10,7 +10,7 @@ severity: wishlist status: open -summary: Add a verbose option? +summary: Add a verbose option to "be html"? time: Fri, 03 Jul 2009 21:17:41 +0000 diff --git a/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/body b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/body new file mode 100644 index 0000000..1090ace --- /dev/null +++ b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/body @@ -0,0 +1,2 @@ +Available with + be -d DIR html diff --git a/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/values b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/values new file mode 100644 index 0000000..9ac4884 --- /dev/null +++ b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/comments/5b2e1ec8-3bb7-40cd-9f4f-74e5c59838f6/values @@ -0,0 +1,8 @@ +Author: W. Trevor King <wking@drexel.edu> + + +Content-type: text/plain + + +Date: Fri, 07 Aug 2009 17:58:58 +0000 + diff --git a/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/values b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/values index a5777b9..ce4bc92 100644 --- a/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/values +++ b/.be/bugs/d8dba78d-f82a-4674-9003-a0ec569b4a96/values @@ -7,10 +7,10 @@ reporter: gianluca <gian@galactica> severity: wishlist -status: closed +status: fixed -summary: Add the possibility to specify the repository Directory ? +summary: Add the possibility to specify the repository directory to "be html"? time: Fri, 03 Jul 2009 21:18:13 +0000 diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/body new file mode 100644 index 0000000..63b61ad --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/body @@ -0,0 +1,26 @@ +Hi, + + > http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ + +Cool! I've set up a copy here: + + http://bugsweb.bugseverywhere.org/ + +(Since we don't have any open bugs lately, click "Closed" at the top, +or create some, but don't expect them to persist if you do.) + + > anyone has any feedback (on any aspect of it) I'd appreciate it. + +I'm pretty enthusiastic about merging this and then working on it +further. + +Thanks, + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/values new file mode 100644 index 0000000..f303bf3 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/00c6f4d8-f965-4d2f-a652-17e58c20ab8c/values @@ -0,0 +1,14 @@ +Alt-id: <m3eisxtgfx.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Fri, 03 Jul 2009 20:55:30 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: 2496ccca-130b-4459-bfae-9d9ef0138177 + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/body new file mode 100644 index 0000000..3a08d71 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/body @@ -0,0 +1,35 @@ +Hi everyone. I found Bugs Everywhere and really like the idea of +distributed bug tracking. I felt like practicing building a CherryPy +site so I put together a quick web interface to BE. I know there's +already a TurboGears one in the works, but I needed an excuse to try +out CherryPy again after working with Django for a while. + +Would any of you be willing to take a look at what I've got so far and +tell me what you think I could improve? + +To install and use it: + +* Install CherryPy from http://cherrypy.org/ if you don't have it. +* Install Jinja2 from http://jinja.pocoo.org/2/ if you don't have it. +* Install BugsEverywhere - you probably know how to do this :) +* Download a zip/tar of my project (or hg clone) from http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ +* Unzip my project and put the folder in your Python site-packages +directory. +* Symlink site-packages/cherryflavoredbugseverywhere/cfbe.py to /usr/ +local/bin/cfbe +* Use the "cfbe [project_root]" command to start up the web interface +for that project. +* Visit http://localhost:8080/ in a browser. + +I know that's a lot of steps. I'd like to streamline it quite a bit, +but first I wanted to see if you have any feedback on the system +itself. Thanks! + +-- +Steve Losh +http://stevelosh.com/ + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/values new file mode 100644 index 0000000..2029281 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/16357f68-19c0-4bf9-8220-b88b52b3456d/values @@ -0,0 +1,11 @@ +Alt-id: <272FECFE-D16B-47B7-B39A-E2C8A718CCC5@stevelosh.com> + + +Content-type: text/plain + + +Date: Sat, 07 Feb 2009 16:30:33 -0500 + + +From: Steve Losh <steve@stevelosh.com> + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/body new file mode 100644 index 0000000..d2ef28c --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/body @@ -0,0 +1,77 @@ +Those are beautiful templates -- can you share those? I'd love to +study the HTML and CSS behind them. + +On Sat, Feb 7, 2009 at 5:48 PM, Steve Losh <steve@stevelosh.com> wrote: +> Hey Chris, thanks for the comments. +> +>> +>> My initial impression is that this looks good enough already to merge as +>> a replacement for the turbogears site. What does everyone else think? +>> +> +> I'm not quite sure it's there yet. There are a bunch of bugs I've got +> marked as "beta" that I'd like to see fixed before it's ready for real use. +> Hopefully they shouldn't be too tough to fix. You can point CFBE at itself +> to see them. :) +> +>> Could you explain a little about how you handle authorship of bug +>> changes at the moment, and if it looks plausible to try making it +>> multiuser? (Having it handle more than one "user" logged in at once.) +>> +> +> That's something I need advice on. Right now CFBE is pretty much only +> suitable for local use - you check out whatever you're working on and use it +> as a local interface to the bugs in the repository. Change those, check in, +> etc. It's effectively just a pretty version of the command line be tool. +> +> I haven't used CherryPy's session/authentication support before. This might +> be a good time for me to learn. One way it might be able to handle multiple +> users hitting a central server: +> +> * Each user has to register with the server and be approved by an admin. +> * Each account would be mapped to a contributor string, the same one that +> would show up if you were going to commit to the repository. +> * Once you have an account, you'd login to make any changes. +> +> +> Aside from all that, I'm a little fuzzy on how a centralized interface to a +> distributed bug tracking system should work. A read-only interface to a +> central "main" repository would be easy. Run the server in read-only mode +> pointing at the main repository. People can use it to look at the bugs in +> the tip of that repository. +> +> If it's not read-only, what happens when a user changes/adds/whatevers a +> bug? Should CFBE commit that change to the repository right then and there? +> Should it never commit, just update the bugdir and let the commits happen +> manually? +> +> What happens when you have multiple branches for a repository? Should there +> be one CFBE instance for each branch, or a single one that lets you switch +> between branches (effectively switching between revisions)? +> +> Those are the kind of things that don't really apply when CFBE is just a +> local interface to a single repository. If anyone has any advice on how a +> multi-user interface should work I'd love to hear it! +> +> -- +> Steve Losh +> http://stevelosh.com/ +> +> +> _______________________________________________ +> Be-devel mailing list +> Be-devel@bugseverywhere.org +> http://void.printf.net/cgi-bin/mailman/listinfo/be-devel +> + + + +-- +Matthew Wilson +matt@tplus1.com +http://tplus1.com + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/values new file mode 100644 index 0000000..96612e6 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/1f25cba2-03ee-43e1-a042-ef6724938ad8/values @@ -0,0 +1,14 @@ +Alt-id: <f6f643a20902071531y6aa3d7a6k7c5a4bd4aa5a04f6@mail.gmail.com> + + +Content-type: text/plain + + +Date: Sat, 07 Feb 2009 18:31:04 -0500 + + +From: Matthew Wilson <matt@tplus1.com> + + +In-reply-to: 21c90231-d7f2-49bb-97d9-99e16459d799 + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/body new file mode 100644 index 0000000..504f82b --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/body @@ -0,0 +1,62 @@ +Hey Chris, thanks for the comments. + +> +> My initial impression is that this looks good enough already to +> merge as +> a replacement for the turbogears site. What does everyone else think? +> + +I'm not quite sure it's there yet. There are a bunch of bugs I've got +marked as "beta" that I'd like to see fixed before it's ready for real +use. Hopefully they shouldn't be too tough to fix. You can point +CFBE at itself to see them. :) + +> Could you explain a little about how you handle authorship of bug +> changes at the moment, and if it looks plausible to try making it +> multiuser? (Having it handle more than one "user" logged in at once.) +> + +That's something I need advice on. Right now CFBE is pretty much only +suitable for local use - you check out whatever you're working on and +use it as a local interface to the bugs in the repository. Change +those, check in, etc. It's effectively just a pretty version of the +command line be tool. + +I haven't used CherryPy's session/authentication support before. This +might be a good time for me to learn. One way it might be able to +handle multiple users hitting a central server: + +* Each user has to register with the server and be approved by an admin. +* Each account would be mapped to a contributor string, the same one +that would show up if you were going to commit to the repository. +* Once you have an account, you'd login to make any changes. + + +Aside from all that, I'm a little fuzzy on how a centralized interface +to a distributed bug tracking system should work. A read-only +interface to a central "main" repository would be easy. Run the +server in read-only mode pointing at the main repository. People can +use it to look at the bugs in the tip of that repository. + +If it's not read-only, what happens when a user changes/adds/whatevers +a bug? Should CFBE commit that change to the repository right then +and there? Should it never commit, just update the bugdir and let the +commits happen manually? + +What happens when you have multiple branches for a repository? Should +there be one CFBE instance for each branch, or a single one that lets +you switch between branches (effectively switching between revisions)? + +Those are the kind of things that don't really apply when CFBE is just +a local interface to a single repository. If anyone has any advice on +how a multi-user interface should work I'd love to hear it! + +-- +Steve Losh +http://stevelosh.com/ + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/values new file mode 100644 index 0000000..7bdded2 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/21c90231-d7f2-49bb-97d9-99e16459d799/values @@ -0,0 +1,14 @@ +Alt-id: <D765386C-4D43-4AE0-83E3-986A1CB4008C@stevelosh.com> + + +Content-type: text/plain + + +Date: Sat, 07 Feb 2009 17:48:06 -0500 + + +From: Steve Losh <steve@stevelosh.com> + + +In-reply-to: 42d57a41-219f-46db-9fda-21b42351da63 + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/body new file mode 100644 index 0000000..e160b76 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/body @@ -0,0 +1,52 @@ +Speaking of that interface, I changed up the look and feel a bit last +weekend. It's still at http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ + -- if anyone has any feedback (on any aspect of it) I'd appreciate it. + +-- +Steve + +On Jul 3, 2009, at 8:31 PM, Chris Ball wrote: + +> Hi Gianluca, +> +>> As i said in a previous mail, I am working on a "html" command +>> for be. The goal is to be able to do something like "be html +>> /web/page" to have in the /web/page directory some static html +>> pages that basically are the dump of the be repository, much like +>> ditz have. This will enable a simple and fast publish of the bus +>> list and details on the web, at least in read only mode. +> +> It might be a good idea for "be html" to use the CherryPy web +> interface +> that Steve is working on. The command could start up the CherryPy app +> and scrape all of the available pages to get a stand-alone dump; this +> would avoid having to keep two (okay, more than two at this point) +> separate sets of HTML templates in the source tree. What do you +> think? +> +>> 2) I see that every command is implemented with a python file in +>> the becommand dir. For a better code, I'd like to split the +>> command implementation into two files: a file that contain the +>> actual code and a second file that have the html related part, +>> any problem with this ? I don't like to have the html part and +>> the code part in one big and unreadable file. +> +> I agree that becommands/*.py commands should not contain any HTML +> layout code. Putting it somewhere else instead sounds fine. +> +> Thanks! +> +> - Chris. +> -- +> Chris Ball <cjb@laptop.org> +> +> _______________________________________________ +> Be-devel mailing list +> Be-devel@bugseverywhere.org +> http://void.printf.net/cgi-bin/mailman/listinfo/be-devel + + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/values new file mode 100644 index 0000000..0c68560 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/2496ccca-130b-4459-bfae-9d9ef0138177/values @@ -0,0 +1,14 @@ +Alt-id: <4701D71B-A14D-4C63-ABCC-E7E5FFE4E4BA@stevelosh.com> + + +Content-type: text/plain + + +Date: Fri, 03 Jul 2009 20:34:51 -0400 + + +From: Steve Losh <steve@stevelosh.com> + + +In-reply-to: 16357f68-19c0-4bf9-8220-b88b52b3456d + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/body new file mode 100644 index 0000000..f43e8dd --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/body @@ -0,0 +1,32 @@ +Hi Steve, + + > Hi everyone. I found Bugs Everywhere and really like the idea of + > distributed bug tracking. I felt like practicing building a + > CherryPy site so I put together a quick web interface to BE. I + > know there's already a TurboGears one in the works, but I needed an + > excuse to try out CherryPy again after working with Django for a + > while. + +This looks awesome, thanks! I've taken some screenshots for others to +see: + +http://chris.printf.net/cfbe-main.png +http://chris.printf.net/cfbe-detail.png + +My initial impression is that this looks good enough already to merge as +a replacement for the turbogears site. What does everyone else think? + +Could you explain a little about how you handle authorship of bug +changes at the moment, and if it looks plausible to try making it +multiuser? (Having it handle more than one "user" logged in at once.) + +Great work, thanks! + +- Chris. +-- +Chris Ball <cjb@laptop.org> + +_______________________________________________ +Be-devel mailing list +Be-devel@bugseverywhere.org +http://void.printf.net/cgi-bin/mailman/listinfo/be-devel diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/values new file mode 100644 index 0000000..7c7e41a --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/42d57a41-219f-46db-9fda-21b42351da63/values @@ -0,0 +1,14 @@ +Alt-id: <m3zlgxyjo5.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Sat, 07 Feb 2009 17:19:22 -0500 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: 16357f68-19c0-4bf9-8220-b88b52b3456d + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/body new file mode 100644 index 0000000..7bea88c --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/body @@ -0,0 +1,30 @@ +Hi, + + > Works for me. I've done this now, which closes the last open bug + > in my repo :D. + +Wow. Congrats! I've merged your branch. + + > All the new functionality comes from bug.extra_strings, which + > provides a list for storing arbitrary strings in the bug's + > permanent state. + +That's going to be really useful. + + > Next up: regexp searching for list --extra-strings! ;). + +Awesome. + + > Oh, and obviously there must still be bugs in BE. Please submit + > more ;). + +Perhaps it's a good time to merge Steve Losh's CherryPy web interface? + +http://void.printf.net/pipermail/be-devel/2009-February/000095.html +http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ + +Thanks, + +- Chris. +-- +Chris Ball <cjb@laptop.org> diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/values new file mode 100644 index 0000000..9b607ef --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/5e339bac-f4f3-407b-974a-b88795d3573b/values @@ -0,0 +1,14 @@ +Alt-id: <m31vp82yyj.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Thu, 25 Jun 2009 10:02:44 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: 16357f68-19c0-4bf9-8220-b88b52b3456d + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/body new file mode 100644 index 0000000..909b989 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/body @@ -0,0 +1,25 @@ +On Jun 25, 2009, at 10:02 AM, Chris Ball <cjb@laptop.org> wrote: +> +>> Oh, and obviously there must still be bugs in BE. Please submit +>> more ;). +> +> Perhaps it's a good time to merge Steve Losh's CherryPy web interface? +> +> http://void.printf.net/pipermail/be-devel/2009-February/000095.html +> http://bitbucket.org/sjl/cherryflavoredbugseverywhere/ +> + +Hey, I haven't touched the web interface in a while, but I should have +some time to fix some stuff up tonight and tomorrow. Hold off on +merging it in until then. + +I'm still curious as to what people think the role of a web interface +like this should be. When I wrote it I meant it as a single-user +interface like the command line one. It could definitely work as a +public, read-only interface too. + +If the goal is to allow more than one person to add issues, how should +commits go? One commit per change? Commit every X minutes if necessary? + +-- +Steve diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/values new file mode 100644 index 0000000..72597bc --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/7fa903a3-f9e6-4e4d-8128-0f26e1ce664b/values @@ -0,0 +1,14 @@ +Alt-id: <26FBD153-39C5-4641-AF5E-749731964086@stevelosh.com> + + +Content-type: text/plain + + +Date: Thu, 25 Jun 2009 10:23:04 -0400 + + +From: Steve Losh <steve@stevelosh.com> + + +In-reply-to: 5e339bac-f4f3-407b-974a-b88795d3573b + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/body new file mode 100644 index 0000000..614abd3 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/body @@ -0,0 +1,33 @@ +On Sat, Feb 07, 2009 at 05:48:06PM -0500, Steve Losh wrote: +>> My initial impression is that this looks good enough already to merge as +>> a replacement for the turbogears site. What does everyone else think? +> +> I'm not quite sure it's there yet. There are a bunch of bugs I've +> got marked as "beta" that I'd like to see fixed before it's ready +> for real use. Hopefully they shouldn't be too tough to fix. You +> can point CFBE at itself to see them. :) + +Steve's also versioning it with Mercurial. Will he mind changing to +Bazaar? + +Steve, I've touched up CFBE to work with my current BE branch. Some +of the changes apply to the trunk BE, and hopefully the rest will +soon. I think the version-naming issue is what's currently blocking +their adoption ;). I've put up my CFBE branch at + static-http://www.physics.drexel.edu/~wking/code/hg/cfbe +for Mercurial. + +Everyone, should CFBE-specific discussions move off-list? More +generally, I've been sending in lots of what-I'm-working on messages, +but not hearing much back, so let me know if I'm being too obnoxious, +and I'll shut up (or at least move more off-list) ;). + +Cheers, +Trevor + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/values new file mode 100644 index 0000000..37e1899 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/e249e2aa-2029-4a96-bc84-962366e07fd6/values @@ -0,0 +1,14 @@ +Alt-id: <20090721135907.GB4469@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Tue, 21 Jul 2009 09:59:07 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 21c90231-d7f2-49bb-97d9-99e16459d799 + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/body b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/body new file mode 100644 index 0000000..ae6a5fe --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/body @@ -0,0 +1,69 @@ +On Thu, Jun 25, 2009 at 10:23:04AM -0400, Steve Losh wrote: +> I'm still curious as to what people think the role of a web interface like +> this should be. When I wrote it I meant it as a single-user interface like +> the command line one. It could definitely work as a public, read-only +> interface too. + +I think the multi-user/public is the way to go. I'd also like to see +a procmail-able script to handle multi-user/public access via email, +which would have all the same issues we're worrying about here. + +On Sat, Feb 07, 2009 at 05:48:06PM -0500, Steve Losh wrote: +> I haven't used CherryPy's session/authentication support before. This +> might be a good time for me to learn. One way it might be able to handle +> multiple users hitting a central server: +> +> * Each user has to register with the server and be approved by an admin. +> * Each account would be mapped to a contributor string, the same one that +> would show up if you were going to commit to the repository. +> * Once you have an account, you'd login to make any changes. + +This sounds good to me. Yay spam reduction ;). + +> If it's not read-only, what happens when a user changes/adds/whatevers a +> bug? Should CFBE commit that change to the repository right then and +> there? Should it never commit, just update the bugdir and let the commits +> happen manually? + +On Thu, Jun 25, 2009 at 10:23:04AM -0400, Steve Losh wrote: +> One commit per change? Commit every X minutes if necessary? + +On Sat, Feb 07, 2009 at 05:48:06PM -0500, Steve Losh wrote: +> What happens when you have multiple branches for a repository? Should +> there be one CFBE instance for each branch, or a single one that lets you +> switch between branches (effectively switching between revisions)? + +There are interesting discussions about the role of distributed +bugtracking here (I'm sure there are others): + http://lwn.net/Articles/281849/ + http://community.livejournal.com/evan_tech/248736.html + +Personally, I've never done much cherry-picking or anything that would +require me to determine exactly which parts of someone's work I want +and which I don't want. I just merge someone's head and edit out the +bits I don't like, a process that also works well for our current +"commit however you want" BE development model ;). Maybe that just +shows that I only work on minor branches of small projects :p. In the +absence of big-project advice, I think we just limit the web front end +to our current model, and let the web interface commit however it +wants as well ;). +1 for adding a <commit> button to the web +interface ;). + +One caveat about a multi-user interface would be that it would allow +the casual users to commit bugs about whatever version they had +installed onto the head of the public-bug branch. In order to figure +out what version of the project they were talking about, the project +should have a way for the user to get a unique version string, ideally +be the bzr-revision-id/git-commit/etc. of the commit for the version +they were using. This would allow developers to determine what branch +to work on with the bug fix, and what branches needed to pull the +eventual fix. If the initially reported buggy version wasn't actually +the root of the bug, oh well :p. Material for a later related bug +report or a reopen. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/values new file mode 100644 index 0000000..2fa8470 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/comments/fa60ce1f-a809-4fb3-a2cd-1a2e0bdd0e0a/values @@ -0,0 +1,14 @@ +Alt-id: <20090625154734.GA19441@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Thu, 25 Jun 2009 11:47:34 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 16357f68-19c0-4bf9-8220-b88b52b3456d + diff --git a/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/values b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/values new file mode 100644 index 0000000..8cf85c9 --- /dev/null +++ b/.be/bugs/d9959864-ea91-475a-a075-f39aa6760f98/values @@ -0,0 +1,20 @@ +assigned: Steve Losh <steve@stevelosh.com> + + +creator: W. Trevor King <wking@drexel.edu> + + +reporter: Steve Losh <steve@stevelosh.com> + + +severity: wishlist + + +status: assigned + + +summary: CherryPy interface "Cherry-flavored BE" + + +time: Tue, 21 Jul 2009 18:52:44 +0000 + diff --git a/.be/bugs/da2b09ff-af24-40f3-9b8d-6ffaa5f41164/values b/.be/bugs/da2b09ff-af24-40f3-9b8d-6ffaa5f41164/values index 4d784ad..2832bb3 100644 --- a/.be/bugs/da2b09ff-af24-40f3-9b8d-6ffaa5f41164/values +++ b/.be/bugs/da2b09ff-af24-40f3-9b8d-6ffaa5f41164/values @@ -10,7 +10,7 @@ severity: wishlist status: open -summary: Add an icon near the status string +summary: Add an icon near the status string in "be html" output time: Tue, 04 Aug 2009 21:15:52 +0000 diff --git a/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/comments/d4a87066-c5f4-49f1-9bd9-a872c8e4ffe6/body b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/comments/d4a87066-c5f4-49f1-9bd9-a872c8e4ffe6/body new file mode 100644 index 0000000..d29c749 --- /dev/null +++ b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/comments/d4a87066-c5f4-49f1-9bd9-a872c8e4ffe6/body @@ -0,0 +1,43 @@ +BE should not crash when be list|show is used on a git repository that +have not the config variables user.name and user.email defined in the +.git/config file. + +To view the bug, in my opinion shold not be mandatory to have these two options +defined + + +Traceroute: + +galactica:~/Devel/dumb> be show 996 +Traceback (most recent call last): + File "/usr/bin/be", line 62, in <module> + sys.exit(cmdutil.execute(args[0], args[1:])) + File "/usr/lib/python2.5/site-packages/libbe/cmdutil.py", line 76, in execute + ret = cmd.execute([a.decode(enc) for a in args]) + File "/usr/lib/python2.5/site-packages/becommands/show.py", line 60, in execute + bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + File "/usr/lib/python2.5/site-packages/libbe/bugdir.py", line 302, in __init__ + self.load() + File "/usr/lib/python2.5/site-packages/libbe/bugdir.py", line 382, in load + self.load_settings() + File "/usr/lib/python2.5/site-packages/libbe/bugdir.py", line 411, in load_settings + self._setup_user_id(self.user_id) + File "/usr/lib/python2.5/site-packages/libbe/properties.py", line 293, in _fget + value = generator(self) + File "/usr/lib/python2.5/site-packages/libbe/bugdir.py", line 177, in _guess_user_id + return self.rcs.get_user_id() + File "/usr/lib/python2.5/site-packages/libbe/rcs.py", line 258, in get_user_id + id = self._rcs_get_user_id() + File "/usr/lib/python2.5/site-packages/libbe/git.py", line 56, in _rcs_get_user_id + status,output,error = self._u_invoke_client("config", "user.name") + File "/usr/lib/python2.5/site-packages/libbe/rcs.py", line 458, in _u_invoke_client + return self._u_invoke(cl_args, stdin=stdin,expect=expect,cwd=directory) + File "/usr/lib/python2.5/site-packages/libbe/rcs.py", line 450, in _u_invoke + raise CommandError(args, status, error) +libbe.rcs.CommandError: Command failed (1): + + +while executing + ['git', 'config', 'user.name'] +galactica:~/Devel/dumb> + diff --git a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/b1a772a0-241f-42fc-8209-765162485b0a/values b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/comments/d4a87066-c5f4-49f1-9bd9-a872c8e4ffe6/values index b6abe1f..d6d1b61 100644 --- a/.be/bugs/4ddf1313-bb3c-45d3-8dca-79ed5830d606/comments/b1a772a0-241f-42fc-8209-765162485b0a/values +++ b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/comments/d4a87066-c5f4-49f1-9bd9-a872c8e4ffe6/values @@ -1,7 +1,7 @@ Content-type: text/plain -Date: Wed, 22 Jul 2009 21:48:23 +0000 +Date: Mon, 03 Aug 2009 20:33:30 +0000 From: Gianluca Montecchi <gian@grys.it> diff --git a/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/values b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/values new file mode 100644 index 0000000..375e44d --- /dev/null +++ b/.be/bugs/dcca51b3-bf8f-4482-8f67-662cfbcb9c6c/values @@ -0,0 +1,17 @@ +creator: Gianluca Montecchi <gian@grys.it> + + +reporter: Gianluca Montecchi <gian@grys.it> + + +severity: minor + + +status: fixed + + +summary: BE should not crash if user.email and user.name are not defined + + +time: Mon, 03 Aug 2009 20:30:43 +0000 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/body new file mode 100644 index 0000000..770af86 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/body @@ -0,0 +1,19 @@ +On Thu, Jul 16, 2009 at 09:39:30AM -0400, W. Trevor King wrote: +> Disclaimer: I imaging the current implementation will choke on +> non-text/plain content types. Also possibly on non-ascii encodings. + +Non-ascii encodings are now handled (although now the output is +base64-encoded). This is limited by poor unicode handling in the +email module for current pythons, see the log for more details. + +> I should probably allow the "help" command ... ;). + +Added, but it currently shows _all_ commands, not just allowed +commands. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/values new file mode 100644 index 0000000..d8edf61 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/09f950d4-9366-4e7b-98b3-9057999f8f38/values @@ -0,0 +1,14 @@ +Alt-id: <20090718131220.GA31832@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 09:12:20 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: f1cde826-0506-4b4a-92ab-8499e953fa49 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/body new file mode 100644 index 0000000..e008923 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/body @@ -0,0 +1,27 @@ +On Sat, Jul 18, 2009 at 06:05:51PM -0400, W. Trevor King wrote: +> My email interface now supports bug creation/comments that look +> like: +> +> $ cat | mail -s "[be-bug] new" "whatever-dev@fancyprojects.com" +> The demuxulizer is broken +> +> <describe bug> +> ^D + +The move towards the DBT interface means this example should now look +like + + $ cat | mail -s "[be-bug:submit] The demuxulizer is broken" whatever-dev@fancyprojects.com + Version: XYZ + + <describe bug> + -- + Ignored text + ^D + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/values new file mode 100644 index 0000000..c00299a --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/704b37ab-01bb-43d3-9e9f-f0d354f63c7d/values @@ -0,0 +1,14 @@ +Alt-id: <20090719130649.GA4164@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sun, 19 Jul 2009 09:06:49 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 7b904395-86e9-4eb1-8534-69cec63801d4 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/body new file mode 100644 index 0000000..800609e --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/body @@ -0,0 +1,27 @@ +Ah, it's been a good day :). My email interface now supports bug +creation/comments that look like: + + $ cat | mail -s "[be-bug] new" "whatever-dev@fancyprojects.com" + The demuxulizer is broken + + <describe bug> + ^D + +Which is probably easy enough for just about anybody ;). Also easy +for other projects to wrap into one of their tools: + + $ cat | projectAexecutable --report-bug + The demuxulizer is broken + + <describe bug> + ^D + +Which could do things like automatically add the version string, OS +name, etc. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/values new file mode 100644 index 0000000..ab160fb --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/7b904395-86e9-4eb1-8534-69cec63801d4/values @@ -0,0 +1,14 @@ +Alt-id: <20090718220551.GB32230@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 18:05:51 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: 09f950d4-9366-4e7b-98b3-9057999f8f38 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/body new file mode 100644 index 0000000..087d67a --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/body @@ -0,0 +1,58 @@ +On Sun, Jul 19, 2009 at 09:09:05AM +1000, Ben Finney wrote: +> > The interface is basically "place your be command in the subject line" +> +> I would far prefer an interface of “place as many BE commands as you +> like at the top of the message body, ending with an optional terminator +> command, and they will each be executed in turn”. +> ... + +I think the idea behind my first approach was "you guys have +experience with the command line BE interface, so it will be easier to +test if you don't have to learn the DBT interface." The Debian people +have been doing this for a while though, so I imagine their email +interface is pretty good ;). + +Here's a short primer on my take on the DBT interface. + +The DBT has three main email addresses, each with it's own parsing style. + 1) creating bugs (submit@bugs.debian.org) + 2) commenting on bugs (<bug-number>@bugs.debian.org) + 3) controlling/managing bugs (control@bugs.debian.org) +I'm trying to squeeze these down into a single email address to avoid +having to tinker with the mail delivery system, so I've got everything +at (wking at tremily dot us) with subject tags: + 1) creating bugs + Subject: [be-bug:submit] new bug summary ... + 2) commenting on bugs + Subject: [be-bug:<bug-number>] human-specific subject + 3) control + Subject: [be-bug] human-specific subject + +The parsing styles each follow their DBT counterparts, but currently +have a much restricted breadth. + +The control-style consists of a list of allowed be commands, with one +command per line. Blank lines and lines beginning with '#' are +ignored, as well anything following a line starting with '--'. All the +listed commands are executed in order and their output returned. + +The comment-style interface appends a comment to the bug specified in +the subject tag. The the first non-multipart body is attached with +the appropriate content-type. In the case of "text/plain" contents, +anything following a line starting with '--' is stripped. + +The create-style interface creates a bug whose summary is given by the +email's post-tag subject. The body of the email must begin with a +psuedo-header containing at least the "Version" field. Anything after +the pseudo-header and before a line starting with '--' is, if present, +attached as the bugs first comment. + +Take a look at my interfaces/email/interactive/examples for some +examples. + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/values new file mode 100644 index 0000000..30de513 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/a0e846ed-1549-4ec3-b94d-391e54610f61/values @@ -0,0 +1,14 @@ +Alt-id: <20090719130153.GA4036@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Sun, 19 Jul 2009 09:01:53 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + + +In-reply-to: cfd7cbc7-27ad-4618-8530-cb4d7323514a + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/body new file mode 100644 index 0000000..3db2a91 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/body @@ -0,0 +1,26 @@ +"W. Trevor King" <wking@drexel.edu> writes: + +> The interface is basically "place your be command in the subject line" + +I would far prefer an interface of “place as many BE commands as you +like at the top of the message body, ending with an optional terminator +command, and they will each be executed in turn”. + +This would allow a single message to perform a batch of BE commands that +are related, instead of requiring to send each command in a separate +message. + +It would also leave the subject field free for something more +descriptive. The subject field could also be used as the summary field +of newly-created bug reports. With a terminator command, this would +allow the message to be sent both to BE and to some other recipient +(e.g. a mailing list) explaining the change. + +Have a look at the email interface of the Debian BTS for an example +<URL:http://www.debian.org/Bugs/server-request>. + +-- + \ “Pinky, are you pondering what I'm pondering?” “Wuh, I think | + `\ so, Brain, but will they let the Cranberry Duchess stay in the | +_o__) Lincoln Bedroom?” —_Pinky and The Brain_ | +Ben Finney diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/values new file mode 100644 index 0000000..b98fbf7 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/cfd7cbc7-27ad-4618-8530-cb4d7323514a/values @@ -0,0 +1,14 @@ +Alt-id: <87fxctbnce.fsf@benfinney.id.au> + + +Content-type: text/plain + + +Date: Sun, 19 Jul 2009 09:09:05 +1000 + + +From: Ben Finney <bignose+hates-spam@benfinney.id.au> + + +In-reply-to: f1cde826-0506-4b4a-92ab-8499e953fa49 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/body new file mode 100644 index 0000000..37b9936 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/body @@ -0,0 +1,38 @@ +I finally did something towards a useful interactive email interface +;). As per our new guidelines, I'll develop this feature in it's own +branch: + http://www.physics.drexel.edu/~wking/code/bzr/be-email + +The interface is basically "place your be command in the subject line" +with a few exceptions. Some examples: + Subject: [be-bug] list --status=all + Subject: [be-bug] show --xml ID + Subject: [be-bug] new + Subject: [be-bug] comment ID +In the case of "new", the bug description is extracted from the first +non-blank body line. In the case of "comment", the email body is used +as the comment. Currently only "list", "show", "new", and "comment" +are allowed. + +You should get a reply email with exit status, stdout, and stderr from +your command. + +Send some mail to [wking (at) tremily (dot) us] to try it out! Depending +on spam attraction, this might be a limited time offer ;). + +Hopefully this lowers the entry barrier for bug reporting :). + +Disclaimer: I imaging the current implementation will choke on +non-text/plain content types. Also possibly on non-ascii encodings. +Probably lots of other bugs too... ;). For example, I should probably +allow the "help" command ... ;). + +Cheers, +Trevor + +-- +This email may be signed or encrypted with GPG (http://www.gnupg.org). +The GPG signature (if present) will be attached as 'signature.asc'. +For more information, see http://en.wikipedia.org/wiki/Pretty_Good_Privacy + +My public key is at http://www.physics.drexel.edu/~wking/pubkey.txt diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/values new file mode 100644 index 0000000..3f81305 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/f1cde826-0506-4b4a-92ab-8499e953fa49/values @@ -0,0 +1,11 @@ +Alt-id: <20090716133930.GC12213@mjolnir.home.net> + + +Content-type: text/plain + + +Date: Thu, 16 Jul 2009 09:39:30 -0400 + + +From: '"W. Trevor King" <wking@drexel.edu>' + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/body b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/body new file mode 100644 index 0000000..167cfe5 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/body @@ -0,0 +1,10 @@ +Hi Trevor, + + > I finally did something towards a useful interactive email + > interface ;). + +Wow, nice! That'll be really useful. + +- Chris. +-- +Chris Ball <cjb@laptop.org> diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/values new file mode 100644 index 0000000..f5da0c9 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/comments/fba8de97-9c61-4a08-b3e7-d8a95d6efe54/values @@ -0,0 +1,14 @@ +Alt-id: <m3fxct5vl6.fsf@pullcord.laptop.org> + + +Content-type: text/plain + + +Date: Sat, 18 Jul 2009 21:07:33 -0400 + + +From: Chris Ball <cjb@laptop.org> + + +In-reply-to: f1cde826-0506-4b4a-92ab-8499e953fa49 + diff --git a/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/values b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/values new file mode 100644 index 0000000..da43639 --- /dev/null +++ b/.be/bugs/e0858b12-0be3-49bb-ad7a-030e488bb2f1/values @@ -0,0 +1,20 @@ +assigned: W. Trevor King <wking@drexel.edu> + + +creator: W. Trevor King <wking@drexel.edu> + + +reporter: W. Trevor King <wking@drexel.edu> + + +severity: wishlist + + +status: fixed + + +summary: Interactive email interface + + +time: Tue, 21 Jul 2009 18:53:50 +0000 + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body new file mode 100644 index 0000000..0598d70 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/body @@ -0,0 +1,8 @@ +<type 'unicode'> <body>�</body> +Traceback (most recent call last): + File "<string>", line 1, in <module> + File "/usr/lib/python2.5/xml/etree/ElementTree.py", line 963, in XML + parser.feed(text) + File "/usr/lib/python2.5/xml/etree/ElementTree.py", line 1245, in feed + self._parser.Parse(data, 0) +UnicodeEncodeError: 'ascii' codec can't encode character u'\u1234' in position 6: ordinal not in range(128) diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values new file mode 100644 index 0000000..cd8d8b9 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/07fc448f-c42e-4846-929a-8924de485766/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 11:34:22 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: faa686bf-c0eb-48bf-8a0b-d9a2e02bd132 + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body new file mode 100644 index 0000000..397d4b6 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/body @@ -0,0 +1 @@ +It looks like etree wants a byte string, not unicode input diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values new file mode 100644 index 0000000..8bdaf52 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/520a9829-8d90-43ce-be64-868b8321e5b0/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 11:42:16 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: faa686bf-c0eb-48bf-8a0b-d9a2e02bd132 + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body new file mode 100644 index 0000000..ce2bb8d --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/body @@ -0,0 +1,5 @@ +For example, this works: + +python -c 'from xml.etree import ElementTree; a=u"<body>\u1234</body>"; print type(a), a; b=ElementTree.XML(a.encode("unicode_escape")); print type(b.text), unicode(b.text).decode("unicode_escape");' + +Ugly though :p. Ah well. diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values new file mode 100644 index 0000000..1784e0e --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/8b54e56e-c693-4594-998f-5bd6c1f385d7/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 11:46:57 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 520a9829-8d90-43ce-be64-868b8321e5b0 + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body new file mode 100644 index 0000000..89a8f8d --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/body @@ -0,0 +1 @@ +That's with Python 2.5.2 and ElementTree "2326 2005-03-17 07:45:21Z fredrik" diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values new file mode 100644 index 0000000..cca07c3 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/bb124fd9-08f5-4f82-a035-6355e8403075/values @@ -0,0 +1,11 @@ +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 11:37:55 +0000 + + +From: W. Trevor King <wking@drexel.edu> + + +In-reply-to: 07fc448f-c42e-4846-929a-8924de485766 + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body new file mode 100644 index 0000000..57e050d --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/body @@ -0,0 +1,5 @@ +Isolated problem to: + +python -c 'from xml.etree import ElementTree; a=u"<body>\u1234</body>"; print type(a), a; b=ElementTree.XML(a);' + +Output attached below diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values new file mode 100644 index 0000000..e430ea0 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/comments/faa686bf-c0eb-48bf-8a0b-d9a2e02bd132/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Sun, 12 Jul 2009 11:31:13 +0000 + + +From: W. Trevor King <wking@drexel.edu> + diff --git a/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values new file mode 100644 index 0000000..4bc81f5 --- /dev/null +++ b/.be/bugs/e4ed63f6-9000-4d0b-98c3-487269140141/values @@ -0,0 +1,17 @@ +creator: W. Trevor King <wking@drexel.edu> + + +reporter: W. Trevor King <wking@drexel.edu> + + +severity: minor + + +status: fixed + + +summary: utf8 problems in xml parsing + + +time: Sat, 11 Jul 2009 15:48:32 +0000 + diff --git a/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values b/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values index df99653..a82beb8 100644 --- a/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values +++ b/.be/bugs/ecc91b94-7f3f-44a7-af58-03191d327a7f/values @@ -4,7 +4,7 @@ creator: abentley severity: minor -status: closed +status: fixed summary: no tests for missing $EDITOR diff --git a/.be/bugs/f77fc673-c852-4c81-bfa2-1d59de2661c8/values b/.be/bugs/f77fc673-c852-4c81-bfa2-1d59de2661c8/values index 5a7261b..72c2839 100644 --- a/.be/bugs/f77fc673-c852-4c81-bfa2-1d59de2661c8/values +++ b/.be/bugs/f77fc673-c852-4c81-bfa2-1d59de2661c8/values @@ -10,7 +10,7 @@ severity: minor status: fixed -summary: Comment should be threaded in the html output +summary: Comment should be threaded in the "be html" output time: Tue, 21 Jul 2009 21:39:52 +0000 diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values index eb56317..d39c4a1 100644 --- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/028d2e8d-5b0f-4c43-a913-35a1709b2276/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Tue, 25 Nov 2008 19:41:02 +0000 - - - - - - -From=W. Trevor King <wking@drexel.edu> +Date: Tue, 25 Nov 2008 19:41:02 +0000 +From: W. Trevor King <wking@drexel.edu> diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values index f976972..639fd4a 100644 --- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/15602c0c-25e4-4c2c-9e24-79bdb90721b1/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Tue, 25 Nov 2008 02:36:16 +0000 - - - - - - -From=W. Trevor King <wking@drexel.edu> +Date: Tue, 25 Nov 2008 02:36:16 +0000 +From: W. Trevor King <wking@drexel.edu> diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values index bf5085b..2821b2f 100644 --- a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/3f556a48-c538-4569-8609-3e829b561d78/values @@ -1,21 +1,8 @@ +Content-type: text/plain - -Content-type=text/plain - - - - - - -Date=Tue, 25 Nov 2008 03:02:59 +0000 - - - - - - -From=W. Trevor King <wking@drexel.edu> +Date: Tue, 25 Nov 2008 03:02:59 +0000 +From: W. Trevor King <wking@drexel.edu> diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body new file mode 100644 index 0000000..b441da9 --- /dev/null +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/body @@ -0,0 +1 @@ +Test unicode �quotes� diff --git a/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values new file mode 100644 index 0000000..a67680d --- /dev/null +++ b/.be/bugs/f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a/comments/f376debf-9f7e-4347-807f-00e7263487c7/values @@ -0,0 +1,8 @@ +Content-type: text/plain + + +Date: Sat, 11 Jul 2009 18:28:57 +0000 + + +From: W. Trevor King <wking@drexel.edu> + diff --git a/.be/settings b/.be/settings index a9bd6dd..15c4553 100644 --- a/.be/settings +++ b/.be/settings @@ -1,3 +1,7 @@ +extra_strings: +- "SUBSCRIBE:W. Trevor King <wking@drexel.edu>\tall\t*" + + inactive_status: - - closed - The bug is no longer relevant. @@ -1,6 +1,7 @@ Bugs Everywhere was written by: Aaron Bentley Alexander Belchenko +Alex Miller Ben Finney Chris Ball Gianluca Montecchi diff --git a/Bugs-Everywhere-Web/libbe b/Bugs-Everywhere-Web/libbe deleted file mode 120000 index 4e5e17e..0000000 --- a/Bugs-Everywhere-Web/libbe +++ /dev/null @@ -1 +0,0 @@ -../libbe
\ No newline at end of file @@ -1,9 +1,8 @@ - GNU GENERAL PUBLIC LICENSE Version 2, June 1991 - Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -16,7 +15,7 @@ software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by -the GNU Library General Public License instead.) You can apply it to +the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not @@ -56,7 +55,7 @@ patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. - + GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -111,7 +110,7 @@ above, provided that you also meet all of these conditions: License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) - + These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in @@ -169,7 +168,7 @@ access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. - + 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is @@ -226,7 +225,7 @@ impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. - + 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License @@ -279,7 +278,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest @@ -304,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: - Gnomovision version 69, Copyright (C) year name of author + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. @@ -337,5 +335,5 @@ necessary. Here is a sample; alter the names: This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Library General +library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. @@ -7,11 +7,20 @@ # Copyright (C) 2008-2009 Ben Finney <benf@cybersource.com.au> # Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> -# This is free software; you may copy, modify and/or distribute this work -# under the terms of the GNU General Public License, version 2 or later. -# No warranty expressed or implied. See the file COPYING for details. - -# Makefile for Bugs Everywhere project +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. SHELL = /bin/bash PATH = /usr/bin:/bin @@ -29,6 +38,7 @@ MODULES += ${DOC_DIR} RM = rm +#PREFIX = /usr/local PREFIX = ${HOME} INSTALL_OPTIONS = "--prefix=${PREFIX}" @@ -47,12 +57,14 @@ build: libbe/_version.py .PHONY: install install: doc build python setup.py install ${INSTALL_OPTIONS} - cp -v xml/* ${PREFIX}/bin +#cp -v interfaces/xml/* ${PREFIX}/bin +#cp -v interfaces/email/catmutt ${PREFIX}/bin .PHONY: clean clean: $(RM) -rf ${GENERATED_FILES} +.PHONY: libbe/_version.py libbe/_version.py: bzr version-info --format python > $@ @@ -1,12 +1,14 @@ Bugs Everywhere =============== -This is Bugs Everywhere, a bugtracker built on distributed revision control. -It works with Bazaar and Arch at the moment, but is easily extensible. It -can also function with no RCS at all. +This is Bugs Everywhere, a bugtracker built on distributed revision +control. It works with Arch, Bazaar, Darcs, Git, and Mercurial at the +moment, but is easily extensible. It can also function with no RCS at +all. The idea is to package the bug information with the source code, so that bugs can be marked 'fixed' in the branches that fix them. So, instead of -numbers, bugs have ids. +numbers, bugs have globally unique ids. + Getting started =============== @@ -15,9 +17,11 @@ set the bug root to your project root, so that Bugs Everywhere works in any part of your project tree. $ be init $PROJECT_ROOT -To create bugs, use "be new $DESCRIPTION". To comment on bugs, you can can use -"be comment $BUG_ID". To close a bug, use "be close $BUG_ID". For more -commands, see "be help". You can also look at the usage in test_usage.sh. +To create bugs, use "be new $DESCRIPTION". To comment on bugs, you +can can use "be comment $BUG_ID". To close a bug, use "be close +$BUG_ID" or "be status $BUG_ID fixed". For more commands, see "be +help". You can also look at the usage examples in test_usage.sh. + Using BeWeb, the web UI ======================= @@ -31,6 +31,7 @@ consistent interface Again, you can just browse around in becommands to get a feel for things. + Testing ------- @@ -67,3 +68,12 @@ later. In recognition of this, cmdutil provides the default_complete function which ensures that if '--complete' is any one of the arguments, options, or option-arguments, GetCompletions will be raised with and empty list. + +Profiling +========= + +Find out which 20 calls take the most cumulative time (time of +execution + childrens' times). + + $ python -m cProfile -o profile be [command] [args] + $ python -c "import pstats; p=pstats.Stats('profile'); p.sort_stats('cumulative').print_stats(20)" @@ -3,54 +3,81 @@ # Chris Ball <cjb@laptop.org> # Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import os import sys + from libbe import cmdutil, _version -__doc__ == cmdutil.help() +__doc__ = cmdutil.help() + +usage = "be [options] [command] [command_options ...] [command_args ...]" -if len(sys.argv) == 1 or sys.argv[1] in ('--help', '-h'): - print cmdutil.help() -elif sys.argv[1] == '--complete': - for command, module in cmdutil.iter_commands(): - print command - print '\n'.join(["--help","--complete","--options","--version"]) -elif sys.argv[1] == '--version': +parser = cmdutil.CmdOptionParser(usage) +parser.command = "be" +parser.add_option("--version", action="store_true", dest="version", + help="Print version string and exit.") +parser.add_option("-d", "--dir", dest="dir", metavar="DIR", + help="Run this command from DIR instead of the current directory.") + +try: + options,args = parser.parse_args() + for option,value in cmdutil.option_value_pairs(options, parser): + if value == "--complete": + if option == "dir": + if len(args) == 0: + args = ["."] + paths = cmdutil.complete_path(args[0]) + raise cmdutil.GetCompletions(paths) +except cmdutil.GetHelp: + print cmdutil.help(parser=parser) + sys.exit(0) +except cmdutil.GetCompletions, e: + print '\n'.join(e.completions) + sys.exit(0) + +if options.version == True: print _version.version_info["revision_id"] -else: - try: - try: - sys.exit(cmdutil.execute(sys.argv[1], sys.argv[2:])) - except KeyError, e: - raise cmdutil.UserError("Unknown command \"%s\"" % e.args[0]) - except cmdutil.GetHelp: - print cmdutil.help(sys.argv[1]) - sys.exit(0) - except cmdutil.GetCompletions, e: - print '\n'.join(e.completions) - sys.exit(0) - except cmdutil.UsageError, e: - print "Invalid usage:", e - print "\nArgs:", sys.argv[1:] - print cmdutil.help(sys.argv[1]) - sys.exit(1) - except cmdutil.UserError, e: - print "ERROR:" - print e - sys.exit(1) + sys.exit(0) +if options.dir != None: + os.chdir(options.dir) + +try: + if len(args) == 0: + raise cmdutil.UsageError, "must supply a command" + sys.exit(cmdutil.execute(args[0], args[1:])) +except cmdutil.GetHelp: + print cmdutil.help(sys.argv[1]) + sys.exit(0) +except cmdutil.GetCompletions, e: + print '\n'.join(e.completions) + sys.exit(0) +except cmdutil.UnknownCommand, e: + print e + sys.exit(1) +except cmdutil.UsageError, e: + print "Invalid usage:", e + if len(args) == 0: + print cmdutil.help(parser=parser) + else: + print "\nArgs:", args + print cmdutil.help(sys.argv[1]) + sys.exit(1) +except cmdutil.UserError, e: + print "ERROR:" + print e + sys.exit(1) diff --git a/becommands/assign.py b/becommands/assign.py index 985cfdd..794f028 100644 --- a/becommands/assign.py +++ b/becommands/assign.py @@ -2,47 +2,47 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Assign an individual or group to fix a bug""" -from libbe import cmdutil, bugdir, settings_object +from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> bd.bug_from_shortname("a").assigned is settings_object.EMPTY + >>> bd.bug_from_shortname("a").assigned is None True - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) >>> bd._clear_bugs() >>> bd.bug_from_shortname("a").assigned == bd.user_id True - >>> execute(["a", "someone"], test=True) + >>> execute(["a", "someone"], manipulate_encodings=False) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("a").assigned someone - >>> execute(["a","none"], test=True) + >>> execute(["a","none"], manipulate_encodings=False) >>> bd._clear_bugs() - >>> bd.bug_from_shortname("a").assigned is settings_object.EMPTY + >>> bd.bug_from_shortname("a").assigned is None True + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -54,7 +54,9 @@ def execute(args, test=False): if len(args) > 2: help() raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) bug = bd.bug_from_shortname(args[0]) if len(args) == 1: bug.assigned = bd.user_id diff --git a/becommands/close.py b/becommands/close.py index deaccce..0532ed2 100644 --- a/becommands/close.py +++ b/becommands/close.py @@ -2,37 +2,37 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Close a bug""" from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> from libbe import bugdir >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("a").status open - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("a").status closed + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -42,8 +42,9 @@ def execute(args, test=False): raise cmdutil.UsageError("Please specify a bug id.") if len(args) > 1: raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) bug.status = "closed" bd.save() diff --git a/becommands/comment.py b/becommands/comment.py index 0b3a576..9a614b2 100644 --- a/becommands/comment.py +++ b/becommands/comment.py @@ -1,62 +1,66 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Add a comment to a bug""" -from libbe import cmdutil, bugdir, settings_object, editor +from libbe import cmdutil, bugdir, comment, editor import os import sys +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import time - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute(["a", "This is a comment about a"], test=True) + >>> execute(["a", "This is a comment about a"], manipulate_encodings=False) >>> bd._clear_bugs() - >>> bug = bd.bug_from_shortname("a") + >>> bug = cmdutil.bug_from_shortname(bd, "a") >>> bug.load_comments(load_full=False) >>> comment = bug.comment_root[0] >>> print comment.body This is a comment about a <BLANKLINE> - >>> comment.From == bd.user_id + >>> comment.author == bd.user_id True >>> comment.time <= int(time.time()) True - >>> comment.in_reply_to is settings_object.EMPTY + >>> comment.in_reply_to is None True >>> if 'EDITOR' in os.environ: ... del os.environ["EDITOR"] - >>> execute(["b"], test=True) + >>> execute(["b"], manipulate_encodings=False) Traceback (most recent call last): UserError: No comment supplied, and EDITOR not specified. >>> os.environ["EDITOR"] = "echo 'I like cheese' > " - >>> execute(["b"], test=True) + >>> execute(["b"], manipulate_encodings=False) >>> bd._clear_bugs() - >>> bug = bd.bug_from_shortname("b") + >>> bug = cmdutil.bug_from_shortname(bd, "b") >>> bug.load_comments(load_full=False) >>> comment = bug.comment_root[0] >>> print comment.body I like cheese <BLANKLINE> + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -65,10 +69,10 @@ def execute(args, test=False): raise cmdutil.UsageError("Please specify a bug or comment id.") if len(args) > 2: raise cmdutil.UsageError("Too many arguments.") - + shortname = args[0] if shortname.count(':') > 1: - raise cmdutil.UserError("Invalid id '%s'." % shortname) + raise cmdutil.UserError("Invalid id '%s'." % shortname) elif shortname.count(':') == 1: # Split shortname generated by Comment.comment_shortnames() bugname = shortname.split(':')[0] @@ -76,24 +80,30 @@ def execute(args, test=False): else: bugname = shortname is_reply = False - - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(bugname) + + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, bugname) bug.load_comments(load_full=False) if is_reply: parent = bug.comment_root.comment_from_shortname(shortname, bug_shortname=bugname) else: parent = bug.comment_root - + if len(args) == 1: # try to launch an editor for comment-body entry try: - body = editor.editor_string("Please enter your comment above") + if parent == bug.comment_root: + parent_body = bug.summary+"\n" + else: + parent_body = parent.body + estr = "Please enter your comment above\n\n> %s\n" \ + % ("\n> ".join(parent_body.splitlines())) + body = editor.editor_string(estr) except editor.CantFindEditor, e: raise cmdutil.UserError, "No comment supplied, and EDITOR not specified." if body is None: raise cmdutil.UserError("No comment entered.") - body = body.decode('utf-8') elif args[1] == '-': # read body from stdin binary = not (options.content_type == None or options.content_type.startswith("text/")) @@ -107,16 +117,68 @@ def execute(args, test=False): body = args[1] if not body.endswith('\n'): body+='\n' - - comment = parent.new_reply(body=body) - if options.content_type != None: - comment.content_type = options.content_type - bd.save() + + if options.XML == False: + new = parent.new_reply(body=body) + if options.author != None: + new.author = options.author + if options.alt_id != None: + new.alt_id = options.alt_id + if options.content_type != None: + new.content_type = options.content_type + else: # import XML comment [list] + # read in the comments + str_body = body.encode("unicode_escape").replace(r'\n', '\n') + comment_list = ElementTree.XML(str_body) + if comment_list.tag not in ["bug", "comment-list"]: + raise comment.InvalidXML( + comment_list, "root element must be <bug> or <comment-list>") + new_comments = [] + ids = [] + for c in bug.comment_root.traverse(): + ids.append(c.uuid) + if c.alt_id != None: + ids.append(c.alt_id) + for child in comment_list.getchildren(): + if child.tag == "comment": + new = comment.Comment(bug) + new.from_xml(unicode(ElementTree.tostring(child)).decode("unicode_escape")) + if new.alt_id in ids: + raise cmdutil.UserError( + "Clashing comment alt_id: %s" % new.alt_id) + ids.append(new.uuid) + if new.alt_id != None: + ids.append(new.alt_id) + if new.in_reply_to == None: + new.in_reply_to = parent.uuid + new_comments.append(new) + else: + print >> sys.stderr, "Ignoring unknown tag %s in %s" \ + % (child.tag, comment_list.tag) + try: + comment.list_to_root(new_comments,bug,root=parent, # link new comments + ignore_missing_references=options.ignore_missing_references) + except comment.MissingReference, e: + raise cmdutil.UserError(e) + # Protect against programmer error causing data loss: + kids = [c.uuid for c in parent.traverse()] + for nc in new_comments: + assert nc.uuid in kids, "%s wasn't added to %s" % (nc.uuid, parent.uuid) + nc.save() def get_parser(): parser = cmdutil.CmdOptionParser("be comment ID [COMMENT]") + parser.add_option("-a", "--author", metavar="AUTHOR", dest="author", + help="Set the comment author", default=None) + parser.add_option("--alt-id", metavar="ID", dest="alt_id", + help="Set an alternate comment ID", default=None) parser.add_option("-c", "--content-type", metavar="MIME", dest="content_type", help="Set comment content-type (e.g. text/plain)", default=None) + parser.add_option("-x", "--xml", action="store_true", default=False, + dest='XML', help="Use COMMENT to specify an XML comment description rather than the comment body. The root XML element should be either <bug> or <comment-list> with one or more <comment> children. The syntax for the <comment> elements should match that generated by 'be show --xml COMMENT-ID'. Unrecognized tags are ignored. Missing tags are left at the default value. The comment UUIDs are always auto-generated, so if you set a <uuid> field, but no <alt-id> field, your <uuid> will be used as the comment's <alt-id>. An exception is raised if <alt-id> conflicts with an existing comment.") + parser.add_option("-i", "--ignore-missing-references", action="store_true", + dest="ignore_missing_references", + help="For XML import, if any comment's <in-reply-to> refers to a non-existent comment, ignore it (instead of raising an exception).") return parser longhelp=""" diff --git a/becommands/commit.py b/becommands/commit.py new file mode 100644 index 0000000..fb85651 --- /dev/null +++ b/becommands/commit.py @@ -0,0 +1,78 @@ +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""Commit the currently pending changes to the repository""" +from libbe import cmdutil, bugdir, editor, rcs +import sys +__desc__ = __doc__ + +def execute(args, manipulate_encodings=True): + """ + >>> import os, time + >>> from libbe import bug + >>> bd = bugdir.SimpleBugDir() + >>> os.chdir(bd.root) + >>> full_path = "testfile" + >>> test_contents = "A test file" + >>> bd.rcs.set_file_contents(full_path, test_contents) + >>> execute(["Added %s." % (full_path)], manipulate_encodings=False) # doctest: +ELLIPSIS + Committed ... + >>> bd.cleanup() + """ + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser) + if len(args) != 1: + raise cmdutil.UsageError("Please supply a commit message") + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + if args[0] == '-': # read summary from stdin + assert options.body != "EDITOR", \ + "Cannot spawn and editor when the summary is using stdin." + summary = sys.stdin.readline() + else: + summary = args[0] + if options.body == None: + body = None + elif options.body == "EDITOR": + body = editor.editor_string("Please enter your commit message above") + else: + body = bd.rcs.get_file_contents(options.body, allow_no_rcs=True) + try: + revision = bd.rcs.commit(summary, body=body, + allow_empty=options.allow_empty) + except rcs.EmptyCommit, e: + print e + return 1 + else: + print "Committed %s" % revision + +def get_parser(): + parser = cmdutil.CmdOptionParser("be commit COMMENT") + parser.add_option("-b", "--body", metavar="FILE", dest="body", + help='Provide a detailed body for the commit message. In the special case that FILE == "EDITOR", spawn an editor to enter the body text (in which case you cannot use stdin for the summary)', default=None) + parser.add_option("-a", "--allow-empty", dest="allow_empty", + help="Allow empty commits", + default=False, action="store_true") + return parser + +longhelp=""" +Commit the current repository status. The summary specified on the +commandline is a string (only one line) that describes the commit +briefly or "-", in which case the string will be read from stdin. +""" + +def help(): + return get_parser().help_str() + longhelp diff --git a/becommands/depend.py b/becommands/depend.py index 58e4388..f72b8ba 100644 --- a/becommands/depend.py +++ b/becommands/depend.py @@ -1,39 +1,69 @@ # Copyright (C) 2009 W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Add/remove bug dependencies""" -from libbe import cmdutil, bugdir +from libbe import cmdutil, bugdir, tree import os, copy __desc__ = __doc__ -def execute(args, test=False): +BLOCKS_TAG="BLOCKS:" +BLOCKED_BY_TAG="BLOCKED-BY:" + +class BrokenLink (Exception): + def __init__(self, blocked_bug, blocking_bug, blocks=True): + if blocks == True: + msg = "Missing link: %s blocks %s" \ + % (blocking_bug.uuid, blocked_bug.uuid) + else: + msg = "Missing link: %s blocked by %s" \ + % (blocked_bug.uuid, blocking_bug.uuid) + Exception.__init__(self, msg) + self.blocked_bug = blocked_bug + self.blocking_bug = blocking_bug + + +def execute(args, manipulate_encodings=True): """ >>> from libbe import utility - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> bd.save() >>> os.chdir(bd.root) - >>> execute(["a", "b"], test=True) - Blocks on a: + >>> execute(["a", "b"], manipulate_encodings=False) + a blocked by: b - >>> execute(["a"], test=True) - Blocks on a: + >>> execute(["a"], manipulate_encodings=False) + a blocked by: b - >>> execute(["--show-status", "a"], test=True) # doctest: +NORMALIZE_WHITESPACE - Blocks on a: + >>> execute(["--show-status", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + a blocked by: + b closed + >>> execute(["b", "a"], manipulate_encodings=False) + b blocked by: + a + b blocks: + a + >>> execute(["--show-status", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + a blocked by: + b closed + a blocks: b closed - >>> execute(["-r", "a", "b"], test=True) + >>> execute(["-r", "b", "a"], manipulate_encodings=False) + b blocks: + a + >>> execute(["-r", "a", "b"], manipulate_encodings=False) + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -41,46 +71,83 @@ def execute(args, test=False): bugid_args={0: lambda bug : bug.active==True, 1: lambda bug : bug.active==True}) - if len(args) < 1: + if options.repair == True: + if len(args) > 0: + raise cmdutil.UsageError("No arguments with --repair calls.") + elif len(args) < 1: raise cmdutil.UsageError("Please a bug id.") - if len(args) > 2: + elif len(args) > 2: help() raise cmdutil.UsageError("Too many arguments.") - - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bugA = bd.bug_from_shortname(args[0]) + elif len(args) == 2 and options.tree_depth != None: + raise cmdutil.UsageError("Only one bug id used in tree mode.") + + + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + if options.repair == True: + good,fixed,broken = check_dependencies(bd, repair_broken_links=True) + assert len(broken) == 0, broken + if len(fixed) > 0: + print "Fixed the following links:" + print "\n".join(["%s |-- %s" % (blockee.uuid, blocker.uuid) + for blockee,blocker in fixed]) + return 0 + + bugA = cmdutil.bug_from_shortname(bd, args[0]) + + if options.tree_depth != None: + dtree = DependencyTree(bd, bugA, options.tree_depth) + if len(dtree.blocked_by_tree()) > 0: + print "%s blocked by:" % bugA.uuid + for depth,node in dtree.blocked_by_tree().thread(): + if depth == 0: continue + print "%s%s" % (" "*(depth), node.bug.string(shortlist=True)) + if len(dtree.blocks_tree()) > 0: + print "%s blocks:" % bugA.uuid + for depth,node in dtree.blocks_tree().thread(): + if depth == 0: continue + print "%s%s" % (" "*(depth), node.bug.string(shortlist=True)) + return 0 + if len(args) == 2: - bugB = bd.bug_from_shortname(args[1]) - estrs = bugA.extra_strings - depend_string = "BLOCKED-BY:%s" % bugB.uuid + bugB = cmdutil.bug_from_shortname(bd, args[1]) if options.remove == True: - estrs.remove(depend_string) + remove_block(bugA, bugB) else: # add the dependency - estrs.append(depend_string) - bugA.extra_strings = estrs # reassign to notice change - bugA.save() - - depends = [] - for estr in bugA.extra_strings: - if estr.startswith("BLOCKED-BY:"): - uuid = estr[11:] - if options.show_status == True: - blocker = bd.bug_from_uuid(uuid) - block_string = "%s\t%s" % (uuid, blocker.status) - else: - block_string = uuid - depends.append(block_string) - if len(depends) > 0: - print "Blocks on %s:" % bugA.uuid - print '\n'.join(depends) + add_block(bugA, bugB) + + blocked_by = get_blocked_by(bd, bugA) + if len(blocked_by) > 0: + print "%s blocked by:" % bugA.uuid + if options.show_status == True: + print '\n'.join(["%s\t%s" % (bug.uuid, bug.status) + for bug in blocked_by]) + else: + print '\n'.join([bug.uuid for bug in blocked_by]) + blocks = get_blocks(bd, bugA) + if len(blocks) > 0: + print "%s blocks:" % bugA.uuid + if options.show_status == True: + print '\n'.join(["%s\t%s" % (bug.uuid, bug.status) + for bug in blocks]) + else: + print '\n'.join([bug.uuid for bug in blocks]) def get_parser(): - parser = cmdutil.CmdOptionParser("be depend BUG-ID [BUG-ID]") - parser.add_option("-r", "--remove", action="store_true", dest="remove", + parser = cmdutil.CmdOptionParser("be depend BUG-ID [BUG-ID]\nor: be depend --repair") + parser.add_option("-r", "--remove", action="store_true", + dest="remove", default=False, help="Remove dependency (instead of adding it)") parser.add_option("-s", "--show-status", action="store_true", - dest="show_status", + dest="show_status", default=False, help="Show status of blocking bugs") + parser.add_option("-t", "--tree-depth", metavar="DEPTH", default=None, + type="int", dest="tree_depth", + help="Print dependency tree rooted at BUG-ID with DEPTH levels of both blockers and blockees. Set DEPTH <= 0 to disable the depth limit.") + parser.add_option("--repair", action="store_true", + dest="repair", default=False, + help="Check for and repair one-way links") return parser longhelp=""" @@ -89,7 +156,184 @@ If bug B is not specified, just print a list of bugs blocking (A). To search for bugs blocked by a particular bug, try $ be list --extra-strings BLOCKED-BY:<your-bug-uuid> + +In repair mode, add the missing direction to any one-way links. + +The "|--" symbol in the repair-mode output is inspired by the +"negative feedback" arrow common in biochemistry. See, for example + http://www.nature.com/nature/journal/v456/n7223/images/nature07513-f5.0.jpg """ def help(): return get_parser().help_str() + longhelp + +# internal helper functions + +def _generate_blocks_string(blocked_bug): + return "%s%s" % (BLOCKS_TAG, blocked_bug.uuid) + +def _generate_blocked_by_string(blocking_bug): + return "%s%s" % (BLOCKED_BY_TAG, blocking_bug.uuid) + +def _parse_blocks_string(string): + assert string.startswith(BLOCKS_TAG) + return string[len(BLOCKS_TAG):] + +def _parse_blocked_by_string(string): + assert string.startswith(BLOCKED_BY_TAG) + return string[len(BLOCKED_BY_TAG):] + +def _add_remove_extra_string(bug, string, add): + estrs = bug.extra_strings + if add == True: + estrs.append(string) + else: # remove the string + estrs.remove(string) + bug.extra_strings = estrs # reassign to notice change + +def _get_blocks(bug): + uuids = [] + for line in bug.extra_strings: + if line.startswith(BLOCKS_TAG): + uuids.append(_parse_blocks_string(line)) + return uuids + +def _get_blocked_by(bug): + uuids = [] + for line in bug.extra_strings: + if line.startswith(BLOCKED_BY_TAG): + uuids.append(_parse_blocked_by_string(line)) + return uuids + +def _repair_one_way_link(blocked_bug, blocking_bug, blocks=None): + if blocks == True: # add blocks link + blocks_string = _generate_blocks_string(blocked_bug) + _add_remove_extra_string(blocking_bug, blocks_string, add=True) + else: # add blocked by link + blocked_by_string = _generate_blocked_by_string(blocking_bug) + _add_remove_extra_string(blocked_bug, blocked_by_string, add=True) + +# functions exposed to other modules + +def add_block(blocked_bug, blocking_bug): + blocked_by_string = _generate_blocked_by_string(blocking_bug) + _add_remove_extra_string(blocked_bug, blocked_by_string, add=True) + blocks_string = _generate_blocks_string(blocked_bug) + _add_remove_extra_string(blocking_bug, blocks_string, add=True) + +def remove_block(blocked_bug, blocking_bug): + blocked_by_string = _generate_blocked_by_string(blocking_bug) + _add_remove_extra_string(blocked_bug, blocked_by_string, add=False) + blocks_string = _generate_blocks_string(blocked_bug) + _add_remove_extra_string(blocking_bug, blocks_string, add=False) + +def get_blocks(bugdir, bug): + """ + Return a list of bugs that the given bug blocks. + """ + blocks = [] + for uuid in _get_blocks(bug): + blocks.append(bugdir.bug_from_uuid(uuid)) + return blocks + +def get_blocked_by(bugdir, bug): + """ + Return a list of bugs blocking the given bug blocks. + """ + blocked_by = [] + for uuid in _get_blocked_by(bug): + blocked_by.append(bugdir.bug_from_uuid(uuid)) + return blocked_by + +def check_dependencies(bugdir, repair_broken_links=False): + """ + Check that links are bi-directional for all bugs in bugdir. + + >>> bd = bugdir.SimpleBugDir(sync_with_disk=False) + >>> a = bd.bug_from_uuid("a") + >>> b = bd.bug_from_uuid("b") + >>> blocked_by_string = _generate_blocked_by_string(b) + >>> _add_remove_extra_string(a, blocked_by_string, add=True) + >>> good,repaired,broken = check_dependencies(bd, repair_broken_links=False) + >>> good + [] + >>> repaired + [] + >>> broken + [(Bug(uuid='a'), Bug(uuid='b'))] + >>> _get_blocks(b) + [] + >>> good,repaired,broken = check_dependencies(bd, repair_broken_links=True) + >>> _get_blocks(b) + ['a'] + >>> good + [] + >>> repaired + [(Bug(uuid='a'), Bug(uuid='b'))] + >>> broken + [] + """ + if bugdir.sync_with_disk == True: + bugdir.load_all_bugs() + good_links = [] + fixed_links = [] + broken_links = [] + for bug in bugdir: + for blocker in get_blocked_by(bugdir, bug): + blocks = get_blocks(bugdir, blocker) + if (bug, blocks) in good_links+fixed_links+broken_links: + continue # already checked that link + if bug not in blocks: + if repair_broken_links == True: + _repair_one_way_link(bug, blocker, blocks=True) + fixed_links.append((bug, blocker)) + else: + broken_links.append((bug, blocker)) + else: + good_links.append((bug, blocker)) + for blockee in get_blocks(bugdir, bug): + blocked_by = get_blocked_by(bugdir, blockee) + if (blockee, bug) in good_links+fixed_links+broken_links: + continue # already checked that link + if bug not in blocked_by: + if repair_broken_links == True: + _repair_one_way_link(blockee, bug, blocks=False) + fixed_links.append((blockee, bug)) + else: + broken_links.append((blockee, bug)) + else: + good_links.append((blockee, bug)) + return (good_links, fixed_links, broken_links) + +class DependencyTree (object): + """ + Note: should probably be DependencyDiGraph. + """ + def __init__(self, bugdir, root_bug, depth_limit=0): + self.bugdir = bugdir + self.root_bug = root_bug + self.depth_limit = depth_limit + def _build_tree(self, child_fn): + root = tree.Tree() + root.bug = self.root_bug + root.depth = 0 + stack = [root] + while len(stack) > 0: + node = stack.pop() + if self.depth_limit > 0 and node.depth == self.depth_limit: + continue + for bug in child_fn(self.bugdir, node.bug): + child = tree.Tree() + child.bug = bug + child.depth = node.depth+1 + node.append(child) + stack.append(child) + return root + def blocks_tree(self): + if not hasattr(self, "_blocks_tree"): + self._blocks_tree = self._build_tree(get_blocks) + return self._blocks_tree + def blocked_by_tree(self): + if not hasattr(self, "_blocked_by_tree"): + self._blocked_by_tree = self._build_tree(get_blocked_by) + return self._blocked_by_tree diff --git a/becommands/diff.py b/becommands/diff.py index 2bdea93..034823d 100644 --- a/becommands/diff.py +++ b/becommands/diff.py @@ -1,44 +1,54 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Compare bug reports with older tree""" from libbe import cmdutil, bugdir, diff import os __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() + >>> bd.set_sync_with_disk(True) >>> original = bd.rcs.commit("Original status") >>> bug = bd.bug_from_uuid("a") >>> bug.status = "closed" - >>> bd.save() >>> changed = bd.rcs.commit("Closed bug a") >>> os.chdir(bd.root) >>> if bd.rcs.versioned == True: - ... execute([original], test=True) + ... execute([original], manipulate_encodings=False) ... else: - ... print "a:cm: Bug A\\nstatus: open -> closed\\n" - Modified bug reports: - a:cm: Bug A - status: open -> closed - <BLANKLINE> + ... print "Modified bugs:\\n a:cm: Bug A\\n Changed bug settings:\\n status: open -> closed" + Modified bugs: + a:cm: Bug A + Changed bug settings: + status: open -> closed + >>> if bd.rcs.versioned == True: + ... execute(["--modified", original], manipulate_encodings=False) + ... else: + ... print "a" + a + >>> if bd.rcs.versioned == False: + ... execute([original], manipulate_encodings=False) + ... else: + ... print "This directory is not revision-controlled." + This directory is not revision-controlled. + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -49,27 +59,32 @@ def execute(args, test=False): revision = args[0] if len(args) > 1: raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) if bd.rcs.versioned == False: print "This directory is not revision-controlled." else: + if revision == None: # get the most recent revision + revision = bd.rcs.revision_id(-1) old_bd = bd.duplicate_bugdir(revision) - r,m,a = diff.diff(old_bd, bd) - - optbugs = [] + d = diff.Diff(old_bd, bd) + tree = d.report_tree() + + uuids = [] if options.all == True: options.new = options.modified = options.removed = True if options.new == True: - optbugs.extend(a) + uuids.extend([c.name for c in tree.child_by_path("/bugs/new")]) if options.modified == True: - optbugs.extend([new for old,new in m]) + uuids.extend([c.name for c in tree.child_by_path("/bugs/mod")]) if options.removed == True: - optbugs.extend(r) - if len(optbugs) > 0: - for bug in optbugs: - print bug.uuid + uuids.extend([c.name for c in tree.child_by_path("/bugs/rem")]) + if (options.new or options.modified or options.removed) == True: + print "\n".join(uuids) else : - print diff.diff_report((r,m,a), bd).encode(bd.encoding) + rep = tree.report_string() + if rep != None: + print rep bd.remove_duplicate_bugdir() def get_parser(): @@ -85,13 +100,14 @@ def get_parser(): long = "--%s" % s[1] help = s[2] parser.add_option(short, long, action="store_true", - dest=attr, help=help) + default=False, dest=attr, help=help) return parser longhelp=""" -Uses the RCS to compare the current tree with a previous tree, and prints -a pretty report. If specifier is given, it is a specifier for the particular -previous tree to use. Specifiers are specific to their RCS. +Uses the RCS to compare the current tree with a previous tree, and +prints a pretty report. If REVISION is given, it is a specifier for +the particular previous tree to use. Specifiers are specific to their +RCS. For Arch your specifier must be a fully-qualified revision name. diff --git a/becommands/help.py b/becommands/help.py index b0b182d..a8f346a 100644 --- a/becommands/help.py +++ b/becommands/help.py @@ -1,28 +1,29 @@ # Copyright (C) 2006-2009 Aaron Bentley and Panometrics, Inc. # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <tgerigk@gmx.de> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Print help for given subcommand""" from libbe import cmdutil, utility __desc__ = __doc__ -def execute(args): +def execute(args, manipulate_encodings=False): """ - Print help of specified command. + Print help of specified command (the manipulate_encodings argument + is ignored). + >>> execute(["help"]) Usage: be help [COMMAND] <BLANKLINE> diff --git a/becommands/html.py b/becommands/html.py index 4bf43f4..3acf973 100644 --- a/becommands/html.py +++ b/becommands/html.py @@ -1,8 +1,4 @@ -# Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. -# Marien Zwart <marienz@gentoo.org> -# Thomas Gerigk <tgerigk@gmx.de> -# W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> +# Copyright (C) 2005-2009 # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,24 +14,33 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Generate a static HTML dump of the current repository status""" -from libbe import cmdutil, bugdir, bug, settings_object +from libbe import cmdutil, bugdir, bug #from html_data import * import codecs, os, re, string, time import xml.sax.saxutils, htmlentitydefs __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> print bd.bug_from_shortname("b").status - closed - >>> execute(["b"], test=True) - >>> bd._clear_bugs() - >>> print bd.bug_from_shortname("b").status - open + >>> execute([], manipulate_encodings=False) + Creating the html output in html_export + >>> os.path.exists("./html_export") + True + >>> os.path.exists("./html_export/index.html") + True + >>> os.path.exists("./html_export/index_inactive.html") + True + >>> os.path.exists("./html_export/bugs") + True + >>> os.path.exists("./html_export/bugs/a.html") + True + >>> os.path.exists("./html_export/bugs/b.html") + True + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -51,7 +56,8 @@ def execute(args, test=False): if len(args) > 0: raise cmdutil.UsageError, "Too many arguments." - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) bd.load_all_bugs() status_list = bug.status_values severity_list = bug.severity_values @@ -98,7 +104,7 @@ def complete(options, args, parser): def escape(string): - if string == settings_object.EMPTY: + if string == None: return "" chars = [] for char in xml.sax.saxutils.escape(string): diff --git a/becommands/init.py b/becommands/init.py index 390dd15..4156a26 100644 --- a/becommands/init.py +++ b/becommands/init.py @@ -1,26 +1,25 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Assign the root directory for bug tracking""" import os.path from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> from libbe import utility, rcs >>> import os @@ -30,7 +29,7 @@ def execute(args, test=False): ... except bugdir.NoBugDir, e: ... True True - >>> execute(['--root', dir.path], test=True) + >>> execute(['--root', dir.path], manipulate_encodings=False) No revision control detected. Directory initialized. >>> del(dir) @@ -41,17 +40,17 @@ def execute(args, test=False): >>> rcs.init('.') >>> print rcs.name Arch - >>> execute([], test=True) + >>> execute([], manipulate_encodings=False) Using Arch for revision control. Directory initialized. >>> rcs.cleanup() >>> try: - ... execute(['--root', '.'], test=True) + ... execute(['--root', '.'], manipulate_encodings=False) ... except cmdutil.UserError, e: ... str(e).startswith("Directory already initialized: ") True - >>> execute(['--root', '/highly-unlikely-to-exist'], test=True) + >>> execute(['--root', '/highly-unlikely-to-exist'], manipulate_encodings=False) Traceback (most recent call last): UserError: No such directory: /highly-unlikely-to-exist >>> os.chdir('/') @@ -65,7 +64,7 @@ def execute(args, test=False): bd = bugdir.BugDir(options.root_dir, from_disk=False, sink_to_existing_root=False, assert_new_BugDir=True, - manipulate_encodings=not test) + manipulate_encodings=manipulate_encodings) except bugdir.NoRootEntry: raise cmdutil.UserError("No such directory: %s" % options.root_dir) except bugdir.AlreadyInitialized: diff --git a/becommands/list.py b/becommands/list.py index 443704b..12e1e29 100644 --- a/becommands/list.py +++ b/becommands/list.py @@ -2,21 +2,20 @@ # Chris Ball <cjb@laptop.org> # Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """List bugs""" from libbe import cmdutil, bugdir, bug import os @@ -27,16 +26,17 @@ __desc__ = __doc__ AVAILABLE_CMPS = [fn[4:] for fn in dir(bug) if fn[:4] == 'cmp_'] AVAILABLE_CMPS.remove("attr") # a cmp_* template. -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute([], test=True) + >>> execute([], manipulate_encodings=False) a:om: Bug A - >>> execute(["--status", "all"], test=True) + >>> execute(["--status", "all"], manipulate_encodings=False) a:om: Bug A b:cm: Bug B + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -47,11 +47,13 @@ def execute(args, test=False): if options.sort_by != None: for cmp in options.sort_by.split(','): if cmp not in AVAILABLE_CMPS: - raise cmdutil.UserError("Invalid sort on '%s'.\nValid sorts:\n %s" - % (cmp, '\n '.join(AVAILABLE_CMPS))) + raise cmdutil.UserError( + "Invalid sort on '%s'.\nValid sorts:\n %s" + % (cmp, '\n '.join(AVAILABLE_CMPS))) cmp_list.append(eval('bug.cmp_%s' % cmp)) - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) bd.load_all_bugs() # select status if options.status != None: @@ -135,11 +137,12 @@ def execute(args, test=False): return True bugs = [b for b in bd if filter(b) ] - if len(bugs) == 0: + if len(bugs) == 0 and options.xml == False: print "No matching bugs found" def list_bugs(cur_bugs, title=None, just_uuids=False, xml=False): if xml == True: + print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding print "<bugs>" if len(cur_bugs) > 0: if title != None and xml == False: diff --git a/becommands/merge.py b/becommands/merge.py index 4bec6bf..f212b01 100644 --- a/becommands/merge.py +++ b/becommands/merge.py @@ -1,28 +1,28 @@ # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Merge duplicate bugs""" from libbe import cmdutil, bugdir import os, copy __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> from libbe import utility - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() + >>> bd.set_sync_with_disk(True) >>> a = bd.bug_from_shortname("a") >>> a.comment_root.time = 0 >>> dummy = a.new_comment("Testing") @@ -36,9 +36,8 @@ def execute(args, test=False): >>> dummy.time = 1 >>> dummy = dummy.new_reply("1 2 3 4") >>> dummy.time = 2 - >>> bd.save() >>> os.chdir(bd.root) - >>> execute(["a", "b"], test=True) + >>> execute(["a", "b"], manipulate_encodings=False) Merging bugs a and b >>> bd._clear_bugs() >>> a = bd.bug_from_shortname("a") @@ -121,6 +120,7 @@ def execute(args, test=False): Merged into bug a >>> print b.status closed + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -134,20 +134,21 @@ def execute(args, test=False): help() raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bugA = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bugA = cmdutil.bug_from_shortname(bd, args[0]) bugA.load_comments() - bugB = bd.bug_from_shortname(args[1]) + bugB = cmdutil.bug_from_shortname(bd, args[1]) bugB.load_comments() mergeA = bugA.new_comment("Merged from bug %s" % bugB.uuid) newCommTree = copy.deepcopy(bugB.comment_root) - for comment in newCommTree.traverse(): + for comment in newCommTree.traverse(): # all descendant comments comment.bug = bugA - for comment in newCommTree: + comment.save() # force onto disk under bugA + for comment in newCommTree: # just the child comments mergeA.add_reply(comment, allow_time_inversion=True) bugB.new_comment("Merged into bug %s" % bugA.uuid) bugB.status = "closed" - bd.save() print "Merging bugs %s and %s" % (bugA.uuid, bugB.uuid) def get_parser(): diff --git a/becommands/new.py b/becommands/new.py index 32e070a..a8ee2ec 100644 --- a/becommands/new.py +++ b/becommands/new.py @@ -1,34 +1,34 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Create a new bug""" -from libbe import cmdutil, bugdir, settings_object +from libbe import cmdutil, bugdir +import sys __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os, time >>> from libbe import bug - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) >>> bug.uuid_gen = lambda: "X" - >>> execute (["this is a test",], test=True) + >>> execute (["this is a test",], manipulate_encodings=False) Created bug with ID X - >>> bd.load() + >>> bd._clear_bugs() >>> bug = bd.bug_from_uuid("X") >>> print bug.summary this is a test @@ -36,25 +36,30 @@ def execute(args, test=False): True >>> print bug.severity minor - >>> bug.target == settings_object.EMPTY + >>> bug.target == None True + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) cmdutil.default_complete(options, args, parser) if len(args) != 1: raise cmdutil.UsageError("Please supply a summary message") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.new_bug(summary=args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + if args[0] == '-': # read summary from stdin + summary = sys.stdin.readline() + else: + summary = args[0] + bug = bd.new_bug(summary=summary.strip()) if options.reporter != None: bug.reporter = options.reporter else: bug.reporter = bug.creator if options.assigned != None: bug.assigned = options.assigned - elif bd.default_assignee != settings_object.EMPTY: + elif bd.default_assignee != None: bug.assigned = bd.default_assignee - bd.save() print "Created bug with ID %s" % bd.bug_shortname(bug) def get_parser(): @@ -66,8 +71,9 @@ def get_parser(): return parser longhelp=""" -Create a new bug, with a new ID. The summary specified on the commandline -is a string that describes the bug briefly. +Create a new bug, with a new ID. The summary specified on the +commandline is a string (only one line) that describes the bug briefly +or "-", in which case the string will be read from stdin. """ def help(): diff --git a/becommands/open.py b/becommands/open.py index f9abcbb..0c6bf05 100644 --- a/becommands/open.py +++ b/becommands/open.py @@ -2,36 +2,36 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Re-open a bug""" from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("b").status closed - >>> execute(["b"], test=True) + >>> execute(["b"], manipulate_encodings=False) >>> bd._clear_bugs() >>> print bd.bug_from_shortname("b").status open + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -41,10 +41,10 @@ def execute(args, test=False): raise cmdutil.UsageError, "Please specify a bug id." if len(args) > 1: raise cmdutil.UsageError, "Too many arguments." - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) bug.status = "open" - bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be open BUG-ID") diff --git a/becommands/remove.py b/becommands/remove.py index 213a8d9..8d85033 100644 --- a/becommands/remove.py +++ b/becommands/remove.py @@ -1,39 +1,39 @@ # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Remove (delete) a bug and its comments""" from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> from libbe import mapfile >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) >>> print bd.bug_from_shortname("b").status closed - >>> execute (["b"], test=True) + >>> execute (["b"], manipulate_encodings=False) Removed bug b >>> bd._clear_bugs() >>> try: ... bd.bug_from_shortname("b") - ... except KeyError: + ... except bugdir.NoBugMatches: ... print "Bug not found" Bug not found + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -41,10 +41,10 @@ def execute(args, test=False): bugid_args={0: lambda bug : bug.active==True}) if len(args) != 1: raise cmdutil.UsageError, "Please specify a bug id." - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) bd.remove_bug(bug) - bd.save() print "Removed bug %s" % bug.uuid def get_parser(): diff --git a/becommands/set.py b/becommands/set.py index e771018..e78a1ea 100644 --- a/becommands/set.py +++ b/becommands/set.py @@ -3,55 +3,57 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Change tree settings""" -from libbe import cmdutil, bugdir, settings_object +import textwrap +from libbe import cmdutil, bugdir, rcs, settings_object __desc__ = __doc__ def _value_string(bd, setting): val = bd.settings.get(setting, settings_object.EMPTY) if val == settings_object.EMPTY: default = getattr(bd, bd._setting_name_to_attr_name(setting)) - if default != settings_object.EMPTY: + if default not in [None, settings_object.EMPTY]: val = "None (%s)" % default else: val = None return str(val) -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute(["target"], test=True) + >>> execute(["target"], manipulate_encodings=False) None - >>> execute(["target", "tomorrow"], test=True) - >>> execute(["target"], test=True) + >>> execute(["target", "tomorrow"], manipulate_encodings=False) + >>> execute(["target"], manipulate_encodings=False) tomorrow - >>> execute(["target", "none"], test=True) - >>> execute(["target"], test=True) + >>> execute(["target", "none"], manipulate_encodings=False) + >>> execute(["target"], manipulate_encodings=False) None + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) complete(options, args, parser) if len(args) > 2: raise cmdutil.UsageError, "Too many arguments" - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) if len(args) == 0: keys = bd.settings_properties keys.sort() @@ -60,7 +62,9 @@ def execute(args, test=False): elif len(args) == 1: print _value_string(bd, args[0]) else: - if args[1] != "none": + if args[1] == "none": + setattr(bd, args[0], settings_object.EMPTY) + else: if args[0] not in bd.settings_properties: msg = "Invalid setting %s\n" % args[0] msg += 'Allowed settings:\n ' @@ -68,14 +72,35 @@ def execute(args, test=False): raise cmdutil.UserError(msg) old_setting = bd.settings.get(args[0]) setattr(bd, args[0], args[1]) - else: - del bd.settings[args[0]] - bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be set [NAME] [VALUE]") return parser +def get_bugdir_settings(): + settings = [] + for s in bugdir.BugDir.settings_properties: + settings.append(s) + settings.sort() + documented_settings = [] + for s in settings: + set = getattr(bugdir.BugDir, s) + dstr = set.__doc__.strip() + # per-setting comment adjustments + if s == "rcs_name": + lines = dstr.split('\n') + while lines[0].startswith("This property defaults to") == False: + lines.pop(0) + assert len(lines) != None, \ + "Unexpected rcs_name docstring:\n '%s'" % dstr + lines.insert( + 0, "The name of the revision control system to use.\n") + dstr = '\n'.join(lines) + doc = textwrap.wrap(dstr, width=70, initial_indent=' ', + subsequent_indent=' ') + documented_settings.append("%s\n%s" % (s, '\n'.join(doc))) + return documented_settings + longhelp=""" Show or change per-tree settings. @@ -83,14 +108,11 @@ If name and value are supplied, the name is set to a new value. If no value is specified, the current value is printed. If no arguments are provided, all names and values are listed. -Interesting settings are: -rcs_name - The name of the revision control system. "Arch" and "None" are supported. -target - The current development goal - To unset a setting, set it to "none". -""" + +Allowed settings are: + +%s""" % ('\n'.join(get_bugdir_settings()),) def help(): return get_parser().help_str() + longhelp diff --git a/becommands/severity.py b/becommands/severity.py index f8a0c02..660586e 100644 --- a/becommands/severity.py +++ b/becommands/severity.py @@ -2,46 +2,47 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Show or change a bug's severity level""" from libbe import cmdutil, bugdir, bug __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) minor - >>> execute(["a", "wishlist"], test=True) - >>> execute(["a"], test=True) + >>> execute(["a", "wishlist"], manipulate_encodings=False) + >>> execute(["a"], manipulate_encodings=False) wishlist - >>> execute(["a", "none"], test=True) + >>> execute(["a", "none"], manipulate_encodings=False) Traceback (most recent call last): UserError: Invalid severity level: none + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) complete(options, args, parser) if len(args) not in (1,2): raise cmdutil.UsageError - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) if len(args) == 1: print bug.severity elif len(args) == 2: @@ -51,7 +52,6 @@ def execute(args, test=False): if e.name != "severity": raise e raise cmdutil.UserError ("Invalid severity level: %s" % e.value) - bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be severity BUG-ID [SEVERITY]") diff --git a/becommands/show.py b/becommands/show.py index ff434ab..50bd6eb 100644 --- a/becommands/show.py +++ b/becommands/show.py @@ -3,31 +3,31 @@ # Thomas Gerigk <tgerigk@gmx.de> # Thomas Habets <thomas@habets.pp.se> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Show a particular bug""" +import sys from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute (["a",], test=True) # doctest: +ELLIPSIS + >>> execute (["a",], manipulate_encodings=False) # doctest: +ELLIPSIS ID : a Short name : a Severity : minor @@ -39,7 +39,8 @@ def execute(args, test=False): Created : ... Bug A <BLANKLINE> - >>> execute (["--xml", "a"], test=True) # doctest: +ELLIPSIS + >>> execute (["--xml", "a"], manipulate_encodings=False) # doctest: +ELLIPSIS + <?xml version="1.0" encoding="..." ?> <bug> <uuid>a</uuid> <short-name>a</short-name> @@ -49,31 +50,66 @@ def execute(args, test=False): <created>...</created> <summary>Bug A</summary> </bug> + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) cmdutil.default_complete(options, args, parser, - bugid_args={0: lambda bug : bug.active==True}) + bugid_args={-1: lambda bug : bug.active==True}) if len(args) == 0: raise cmdutil.UsageError - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - for bugid in args: - bug = bd.bug_from_shortname(bugid) - if options.dumpXML: - print bug.xml(show_comments=True) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + if options.XML: + print '<?xml version="1.0" encoding="%s" ?>' % bd.encoding + for shortname in args: + if shortname.count(':') > 1: + raise cmdutil.UserError("Invalid id '%s'." % shortname) + elif shortname.count(':') == 1: + # Split shortname generated by Comment.comment_shortnames() + bugname = shortname.split(':')[0] + is_comment = True else: - print bug.string(show_comments=True) - if bugid != args[-1]: - print "" # add a blank line between bugs + bugname = shortname + is_comment = False + if is_comment == True and options.comments == False: + continue + bug = cmdutil.bug_from_shortname(bd, bugname) + if is_comment == False: + if options.XML: + print bug.xml(show_comments=options.comments) + else: + print bug.string(show_comments=options.comments) + else: + comment = bug.comment_root.comment_from_shortname( + shortname, bug_shortname=bugname) + if options.XML: + print comment.xml(shortname=shortname) + else: + if len(args) == 1 and options.only_raw_body == True: + sys.__stdout__.write(comment.body) + else: + print comment.string(shortname=shortname) + if shortname != args[-1] and options.XML == False: + print "" # add a blank line between bugs/comments def get_parser(): - parser = cmdutil.CmdOptionParser("be show [options] BUG-ID [BUG-ID ...]") - parser.add_option("-x", "--xml", action="store_true", - dest='dumpXML', help="Dump as XML") + parser = cmdutil.CmdOptionParser("be show [options] ID [ID ...]") + parser.add_option("-x", "--xml", action="store_true", default=False, + dest='XML', help="Dump as XML") + parser.add_option("--only-raw-body", action="store_true", + dest='only_raw_body', + help="When printing only a single comment, just print it's body. This allows extraction of non-text content types.") + parser.add_option("-c", "--no-comments", dest="comments", + action="store_false", default=True, + help="Disable comment output. This is useful if you just want more details on a bug's current status.") return parser longhelp=""" -Show all information about a bug. +Show all information about the bugs or comments whose IDs are given. + +It's probably not a good idea to mix bug and comment IDs in a single +call, but you're free to do so if you like. """ def help(): diff --git a/becommands/status.py b/becommands/status.py index d8bd4c4..f315003 100644 --- a/becommands/status.py +++ b/becommands/status.py @@ -1,44 +1,45 @@ # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Show or change a bug's status""" from libbe import cmdutil, bugdir, bug __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) open - >>> execute(["a", "closed"], test=True) - >>> execute(["a"], test=True) + >>> execute(["a", "closed"], manipulate_encodings=False) + >>> execute(["a"], manipulate_encodings=False) closed - >>> execute(["a", "none"], test=True) + >>> execute(["a", "none"], manipulate_encodings=False) Traceback (most recent call last): UserError: Invalid status: none + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) complete(options, args, parser) if len(args) not in (1,2): raise cmdutil.UsageError - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) - bug = bd.bug_from_shortname(args[0]) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + bug = cmdutil.bug_from_shortname(bd, args[0]) if len(args) == 1: print bug.status else: @@ -48,7 +49,6 @@ def execute(args, test=False): if e.name != "status": raise raise cmdutil.UserError ("Invalid status: %s" % e.value) - bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be status BUG-ID [STATUS]") @@ -56,24 +56,40 @@ def get_parser(): def help(): - longhelp=[""" -Show or change a bug's status. - -If no status is specified, the current value is printed. If a status -is specified, it will be assigned to the bug. - -Status levels are: -"""] try: # See if there are any per-tree status configurations - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=False) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=False) except bugdir.NoBugDir, e: pass # No tree, just show the defaults longest_status_len = max([len(s) for s in bug.status_values]) - for status in bug.status_values : + active_statuses = [] + for status in bug.active_status_values : + description = bug.status_description[status] + s = "%*s : %s" % (longest_status_len, status, description) + active_statuses.append(s) + inactive_statuses = [] + for status in bug.inactive_status_values : description = bug.status_description[status] - s = "%*s : %s\n" % (longest_status_len, status, description) - longhelp.append(s) - longhelp = ''.join(longhelp) + s = "%*s : %s" % (longest_status_len, status, description) + inactive_statuses.append(s) + longhelp=""" +Show or change a bug's status. + +If no status is specified, the current value is printed. If a status +is specified, it will be assigned to the bug. + +There are two classes of statuses, active and inactive, which are only +important for commands like "be list" that show only active bugs by +default. + +Active status levels are: + %s +Inactive status levels are: + %s + +You can overide the list of allowed statuses on a per-repository basis. +See "be set --help" for more details. +""" % ('\n '.join(active_statuses), '\n '.join(inactive_statuses)) return get_parser().help_str() + longhelp def complete(options, args, parser): diff --git a/becommands/subscribe.py b/becommands/subscribe.py new file mode 100644 index 0000000..b754937 --- /dev/null +++ b/becommands/subscribe.py @@ -0,0 +1,370 @@ +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +"""(Un)subscribe to change notification""" +from libbe import cmdutil, bugdir, tree +import os, copy +__desc__ = __doc__ + +TAG="SUBSCRIBE:" + +class SubscriptionType (tree.Tree): + """ + Trees of subscription types to allow users to select exactly what + notifications they want to subscribe to. + """ + def __init__(self, type_name, *args, **kwargs): + tree.Tree.__init__(self, *args, **kwargs) + self.type = type_name + def __str__(self): + return self.type + def __repr__(self): + return "<SubscriptionType: %s>" % str(self) + def string_tree(self, indent=0): + lines = [] + for depth,node in self.thread(): + lines.append("%s%s" % (" "*(indent+2*depth), node)) + return "\n".join(lines) + +BUGDIR_TYPE_NEW = SubscriptionType("new") +BUGDIR_TYPE_ALL = SubscriptionType("all", [BUGDIR_TYPE_NEW]) + +# same name as BUGDIR_TYPE_ALL for consistency +BUG_TYPE_ALL = SubscriptionType(str(BUGDIR_TYPE_ALL)) + +INVALID_TYPE = SubscriptionType("INVALID") + +class InvalidType (ValueError): + def __init__(self, type_name, type_root): + msg = "Invalid type %s for tree:\n%s" \ + % (type_name, type_root.string_tree(4)) + ValueError.__init__(self, msg) + self.type_name = type_name + self.type_root = type_root + + +def execute(args, manipulate_encodings=True): + """ + >>> bd = bugdir.SimpleBugDir() + >>> bd.set_sync_with_disk(True) + >>> os.chdir(bd.root) + >>> a = bd.bug_from_shortname("a") + >>> print a.extra_strings + [] + >>> execute(["-s","John Doe <j@doe.com>", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + John Doe <j@doe.com> all * + >>> bd._clear_bugs() # resync our copy of bug + >>> a = bd.bug_from_shortname("a") + >>> print a.extra_strings + ['SUBSCRIBE:John Doe <j@doe.com>\\tall\\t*'] + >>> execute(["-s","Jane Doe <J@doe.com>", "-S", "a.com,b.net", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + Jane Doe <J@doe.com> all a.com,b.net + John Doe <j@doe.com> all * + >>> execute(["-s","Jane Doe <J@doe.com>", "-S", "a.edu", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + Jane Doe <J@doe.com> all a.com,a.edu,b.net + John Doe <j@doe.com> all * + >>> execute(["-u", "-s","Jane Doe <J@doe.com>", "-S", "a.com", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + Jane Doe <J@doe.com> all a.edu,b.net + John Doe <j@doe.com> all * + >>> execute(["-s","Jane Doe <J@doe.com>", "-S", "*", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + Jane Doe <J@doe.com> all * + John Doe <j@doe.com> all * + >>> execute(["-u", "-s","Jane Doe <J@doe.com>", "a"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for a: + John Doe <j@doe.com> all * + >>> execute(["-u", "-s","John Doe <j@doe.com>", "a"], manipulate_encodings=False) + >>> execute(["-s","Jane Doe <J@doe.com>", "-t", "new", "DIR"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for bug directory: + Jane Doe <J@doe.com> new * + >>> execute(["-s","Jane Doe <J@doe.com>", "DIR"], manipulate_encodings=False) # doctest: +NORMALIZE_WHITESPACE + Subscriptions for bug directory: + Jane Doe <J@doe.com> all * + >>> bd.cleanup() + """ + parser = get_parser() + options, args = parser.parse_args(args) + cmdutil.default_complete(options, args, parser, + bugid_args={0: lambda bug : bug.active==True}) + + if len(args) > 1: + help() + raise cmdutil.UsageError("Too many arguments.") + + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) + + subscriber = options.subscriber + if subscriber == None: + subscriber = bd.user_id + if options.unsubscribe == True: + if options.servers == None: + options.servers = "INVALID" + if options.types == None: + options.types = "INVALID" + else: + if options.servers == None: + options.servers = "*" + if options.types == None: + options.types = "all" + servers = options.servers.split(",") + types = options.types.split(",") + + if len(args) == 0 or args[0] == "DIR": # directory-wide subscriptions + type_root = BUGDIR_TYPE_ALL + entity = bd + entity_name = "bug directory" + else: # bug-specific subscriptions + type_root = BUG_TYPE_ALL + bug = bd.bug_from_shortname(args[0]) + entity = bug + entity_name = bug.uuid + + types = [type_from_name(name, type_root, default=INVALID_TYPE, + default_ok=options.unsubscribe) + for name in types] + estrs = entity.extra_strings + if options.unsubscribe == True: + estrs = unsubscribe(estrs, subscriber, types, servers, type_root) + else: # add the tag + estrs = subscribe(estrs, subscriber, types, servers, type_root) + entity.extra_strings = estrs # reassign to notice change + + subscriptions = [] + for estr in entity.extra_strings: + if estr.startswith(TAG): + subscriptions.append(estr[len(TAG):]) + + if len(subscriptions) > 0: + print "Subscriptions for %s:" % entity_name + print '\n'.join(subscriptions) + + +def get_parser(): + parser = cmdutil.CmdOptionParser("be subscribe ID") + parser.add_option("-u", "--unsubscribe", action="store_true", + dest="unsubscribe", default=False, + help="Unsubscribe instead of subscribing.") + parser.add_option("-s", "--subscriber", dest="subscriber", + metavar="SUBSCRIBER", + help="Email address of the subscriber (defaults to bugdir.user_id).") + parser.add_option("-S", "--servers", dest="servers", metavar="SERVERS", + help="Servers from which you want notification.") + parser.add_option("-t", "--type", dest="types", metavar="TYPES", + help="Types of changes you wish to be notified about.") + return parser + +longhelp=""" +ID can be either a bug id, or blank/"DIR", in which case it refers to the +whole bug directory. + +SERVERS specifies the servers from which you would like to receive +notification. Multiple severs may be specified in a comma-separated +list, or you can use "*" to match all servers (the default). If you +have not selected a server, it should politely refrain from notifying +you of changes, although there is no way to guarantee this behavior. + +Available TYPES: + For bugs: +%s + For DIR : +%s + +For unsubscription, any listed SERVERS and TYPES are removed from your +subscription. Either the catch-all server "*" or type "%s" will +remove SUBSCRIBER entirely from the specified ID. + +This command is intended for use primarily by public interfaces, since +if you're just hacking away on your private repository, you'll known +what's changed ;). This command just (un)sets the appropriate +subscriptions, and leaves it up to each interface to perform the +notification. +""" % (BUG_TYPE_ALL.string_tree(6), BUGDIR_TYPE_ALL.string_tree(6), + BUGDIR_TYPE_ALL) + +def help(): + return get_parser().help_str() + longhelp + +# internal helper functions + +def _generate_string(subscriber, types, servers): + types = sorted([str(t) for t in types]) + servers = sorted(servers) + return "%s%s\t%s\t%s" % (TAG,subscriber,",".join(types),",".join(servers)) + +def _parse_string(string, type_root): + assert string.startswith(TAG), string + string = string[len(TAG):] + subscriber,types,servers = string.split("\t") + types = [type_from_name(name, type_root) for name in types.split(",")] + return (subscriber,types,servers.split(",")) + +def _get_subscriber(extra_strings, subscriber, type_root): + for i,string in enumerate(extra_strings): + if string.startswith(TAG): + s,ts,srvs = _parse_string(string, type_root) + if s == subscriber: + return i,s,ts,srvs # match! + return None # no match + +# functions exposed to other modules + +def type_from_name(name, type_root, default=None, default_ok=False): + if name == str(type_root): + return type_root + for t in type_root.traverse(): + if name == str(t): + return t + if default_ok: + return default + raise InvalidType(name, type_root) + +def subscribe(extra_strings, subscriber, types, servers, type_root): + args = _get_subscriber(extra_strings, subscriber, type_root) + if args == None: # no match + extra_strings.append(_generate_string(subscriber, types, servers)) + return extra_strings + # Alter matched string + i,s,ts,srvs = args + for t in types: + if t not in ts: + ts.append(t) + # remove descendant types + all_ts = copy.copy(ts) + for t in all_ts: + for tt in all_ts: + if tt in ts and t.has_descendant(tt): + ts.remove(tt) + if "*" in servers+srvs: + srvs = ["*"] + else: + srvs = list(set(servers+srvs)) + extra_strings[i] = _generate_string(subscriber, ts, srvs) + return extra_strings + +def unsubscribe(extra_strings, subscriber, types, servers, type_root): + args = _get_subscriber(extra_strings, subscriber, type_root) + if args == None: # no match + return extra_strings # pass + # Remove matched string + i,s,ts,srvs = args + all_ts = copy.copy(ts) + for t in types: + for tt in all_ts: + if tt in ts and t.has_descendant(tt): + ts.remove(tt) + if "*" in servers+srvs: + srvs = [] + else: + for srv in servers: + if srv in srvs: + srvs.remove(srv) + if len(ts) == 0 or len(srvs) == 0: + extra_strings.pop(i) + else: + extra_strings[i] = _generate_string(subscriber, ts, srvs) + return extra_strings + +def get_subscribers(extra_strings, type, server, type_root, + match_ancestor_types=False, + match_descendant_types=False): + """ + Set match_ancestor_types=True if you want to find eveyone who + cares about your particular type. + + Set match_descendant_types=True if you want to find subscribers + who may only care about some subset of your type. This is useful + for generating lists of all the subscribers in a given set of + extra_strings. + + >>> def sgs(*args, **kwargs): + ... return sorted(get_subscribers(*args, **kwargs)) + >>> es = [] + >>> es = subscribe(es, "John Doe <j@doe.com>", [BUGDIR_TYPE_ALL], ["a.com"], BUGDIR_TYPE_ALL) + >>> es = subscribe(es, "Jane Doe <J@doe.com>", [BUGDIR_TYPE_NEW], ["*"], BUGDIR_TYPE_ALL) + >>> sgs(es, BUGDIR_TYPE_ALL, "a.com", BUGDIR_TYPE_ALL) + ['John Doe <j@doe.com>'] + >>> sgs(es, BUGDIR_TYPE_ALL, "a.com", BUGDIR_TYPE_ALL, match_descendant_types=True) + ['Jane Doe <J@doe.com>', 'John Doe <j@doe.com>'] + >>> sgs(es, BUGDIR_TYPE_ALL, "b.net", BUGDIR_TYPE_ALL, match_descendant_types=True) + ['Jane Doe <J@doe.com>'] + >>> sgs(es, BUGDIR_TYPE_NEW, "a.com", BUGDIR_TYPE_ALL) + ['Jane Doe <J@doe.com>'] + >>> sgs(es, BUGDIR_TYPE_NEW, "a.com", BUGDIR_TYPE_ALL, match_ancestor_types=True) + ['Jane Doe <J@doe.com>', 'John Doe <j@doe.com>'] + """ + for string in extra_strings: + subscriber,types,servers = _parse_string(string, type_root) + type_match = False + if type in types: + type_match = True + if type_match == False and match_ancestor_types == True: + for t in types: + if t.has_descendant(type): + type_match = True + break + if type_match == False and match_descendant_types == True: + for t in types: + if type.has_descendant(t): + type_match = True + break + server_match = False + if server in servers or servers == ["*"]: + server_match = True + if type_match == True and server_match == True: + yield subscriber + +def get_bugdir_subscribers(bugdir, server): + """ + I have a bugdir. Who cares about it, and what do they care about? + Returns a dict of dicts: + subscribers[user][id] = types + where id is either a bug.uuid (in the case of a bug subscription) + or "DIR" (in the case of a bugdir subscription). + + >>> bd = bugdir.SimpleBugDir(sync_with_disk=False) + >>> a = bd.bug_from_shortname("a") + >>> bd.extra_strings = subscribe(bd.extra_strings, "John Doe <j@doe.com>", [BUGDIR_TYPE_ALL], ["a.com"], BUGDIR_TYPE_ALL) + >>> bd.extra_strings = subscribe(bd.extra_strings, "Jane Doe <J@doe.com>", [BUGDIR_TYPE_NEW], ["*"], BUGDIR_TYPE_ALL) + >>> a.extra_strings = subscribe(a.extra_strings, "John Doe <j@doe.com>", [BUG_TYPE_ALL], ["a.com"], BUG_TYPE_ALL) + >>> subscribers = get_bugdir_subscribers(bd, "a.com") + >>> subscribers["Jane Doe <J@doe.com>"]["DIR"] + [<SubscriptionType: new>] + >>> subscribers["John Doe <j@doe.com>"]["DIR"] + [<SubscriptionType: all>] + >>> subscribers["John Doe <j@doe.com>"]["a"] + [<SubscriptionType: all>] + >>> get_bugdir_subscribers(bd, "b.net") + {'Jane Doe <J@doe.com>': {'DIR': [<SubscriptionType: new>]}} + >>> bd.cleanup() + """ + subscribers = {} + for sub in get_subscribers(bugdir.extra_strings, BUGDIR_TYPE_ALL, server, + BUGDIR_TYPE_ALL, match_descendant_types=True): + i,s,ts,srvs = _get_subscriber(bugdir.extra_strings,sub,BUGDIR_TYPE_ALL) + subscribers[sub] = {"DIR":ts} + for bug in bugdir: + for sub in get_subscribers(bug.extra_strings, BUG_TYPE_ALL, server, + BUG_TYPE_ALL, match_descendant_types=True): + i,s,ts,srvs = _get_subscriber(bug.extra_strings,sub,BUG_TYPE_ALL) + if sub in subscribers: + subscribers[sub][bug.uuid] = ts + else: + subscribers[sub] = {bug.uuid:ts} + return subscribers diff --git a/becommands/tag.py b/becommands/tag.py index ab0324e..ecd853f 100644 --- a/becommands/tag.py +++ b/becommands/tag.py @@ -1,50 +1,51 @@ # Copyright (C) 2009 W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Tag a bug, or search bugs for tags""" from libbe import cmdutil, bugdir import os, copy __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> from libbe import utility - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() + >>> bd.set_sync_with_disk(True) >>> os.chdir(bd.root) >>> a = bd.bug_from_shortname("a") >>> print a.extra_strings [] - >>> execute(["a", "GUI"], test=True) + >>> execute(["a", "GUI"], manipulate_encodings=False) Tags for a: GUI >>> bd._clear_bugs() # resync our copy of bug >>> a = bd.bug_from_shortname("a") >>> print a.extra_strings ['TAG:GUI'] - >>> execute(["a", "later"], test=True) + >>> execute(["a", "later"], manipulate_encodings=False) Tags for a: GUI later - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) Tags for a: GUI later - >>> execute(["--list"], test=True) + >>> execute(["--list"], manipulate_encodings=False) GUI later - >>> execute(["a", "Alphabetically first"], test=True) + >>> execute(["a", "Alphabetically first"], manipulate_encodings=False) Tags for a: Alphabetically first GUI @@ -56,16 +57,16 @@ def execute(args, test=False): >>> a.extra_strings = [] >>> print a.extra_strings [] - >>> a.save() - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) >>> bd._clear_bugs() # resync our copy of bug >>> a = bd.bug_from_shortname("a") >>> print a.extra_strings [] - >>> execute(["a", "Alphabetically first"], test=True) + >>> execute(["a", "Alphabetically first"], manipulate_encodings=False) Tags for a: Alphabetically first - >>> execute(["--remove", "a", "Alphabetically first"], test=True) + >>> execute(["--remove", "a", "Alphabetically first"], manipulate_encodings=False) + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -78,7 +79,8 @@ def execute(args, test=False): help() raise cmdutil.UsageError("Too many arguments.") - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) if options.list: bd.load_all_bugs() tags = [] @@ -92,7 +94,7 @@ def execute(args, test=False): if len(tags) > 0: print '\n'.join(tags) return - bug = bd.bug_from_shortname(args[0]) + bug = cmdutil.bug_from_shortname(bd, args[0]) if len(args) == 2: given_tag = args[1] estrs = bug.extra_strings @@ -102,7 +104,6 @@ def execute(args, test=False): else: # add the tag estrs.append(tag_string) bug.extra_strings = estrs # reassign to notice change - bug.save() tags = [] for estr in bug.extra_strings: diff --git a/becommands/target.py b/becommands/target.py index 283998a..7e41451 100644 --- a/becommands/target.py +++ b/becommands/target.py @@ -4,40 +4,40 @@ # Marien Zwart <marienz@gentoo.org> # Thomas Gerigk <tgerigk@gmx.de> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Show or change a bug's target for fixing""" -from libbe import cmdutil, bugdir, settings_object +from libbe import cmdutil, bugdir __desc__ = __doc__ -def execute(args, test=False): +def execute(args, manipulate_encodings=True): """ >>> import os - >>> bd = bugdir.simple_bug_dir() + >>> bd = bugdir.SimpleBugDir() >>> os.chdir(bd.root) - >>> execute(["a"], test=True) + >>> execute(["a"], manipulate_encodings=False) No target assigned. - >>> execute(["a", "tomorrow"], test=True) - >>> execute(["a"], test=True) + >>> execute(["a", "tomorrow"], manipulate_encodings=False) + >>> execute(["a"], manipulate_encodings=False) tomorrow - >>> execute(["--list"], test=True) + >>> execute(["--list"], manipulate_encodings=False) tomorrow - >>> execute(["a", "none"], test=True) - >>> execute(["a"], test=True) + >>> execute(["a", "none"], manipulate_encodings=False) + >>> execute(["a"], manipulate_encodings=False) No target assigned. + >>> bd.cleanup() """ parser = get_parser() options, args = parser.parse_args(args) @@ -47,16 +47,17 @@ def execute(args, test=False): if len(args) not in (1, 2): if not (options.list == True and len(args) == 0): raise cmdutil.UsageError - bd = bugdir.BugDir(from_disk=True, manipulate_encodings=not test) + bd = bugdir.BugDir(from_disk=True, + manipulate_encodings=manipulate_encodings) if options.list: ts = set([bd.bug_from_uuid(bug).target for bug in bd.list_uuids()]) for target in sorted(ts): if target and isinstance(target,str): print target return - bug = bd.bug_from_shortname(args[0]) + bug = cmdutil.bug_from_shortname(bd, args[0]) if len(args) == 1: - if bug.target is None or bug.target is settings_object.EMPTY: + if bug.target is None: print "No target assigned." else: print bug.target @@ -66,7 +67,6 @@ def execute(args, test=False): bug.target = None else: bug.target = args[1] - bd.save() def get_parser(): parser = cmdutil.CmdOptionParser("be target BUG-ID [TARGET]\nor: be target --list") diff --git a/doc/module.mk b/doc/module.mk index 828f15f..7791f48 100644 --- a/doc/module.mk +++ b/doc/module.mk @@ -4,9 +4,21 @@ # Part of Bugs Everywhere, a distributed bug tracking system. # # Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org> -# This is free software; you may copy, modify and/or distribute this work -# under the terms of the GNU General Public License, version 2 or later. -# No warranty expressed or implied. See the file COPYING for details. +# W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # Makefile module for documentation diff --git a/interfaces/README b/interfaces/README new file mode 100644 index 0000000..4d74580 --- /dev/null +++ b/interfaces/README @@ -0,0 +1,34 @@ +Removing spam commits from the history +====================================== + +arch bzr darcs git hg none + +In the case that some spam or inappropriate comment makes its way +through you interface, you can remove the offending commit XYZ with: + + If the offending commit is the last commit: + + arch: + bzr: bzr uncommit && bzr revert + darcs: darcs obliterate --last=1 + git: git reset --hard HEAD^ + hg: hg rollback && hg revert + + If the offending commit is not the last commit: + + arch: + bzr: bzr rebase -r <XYZ+1>..-1 --onto before:XYZ . + (requires bzr-rebase plugin, note, you have to increment XYZ by + hand for <XYZ+1>, because bzr does not support "after:XYZ".) + darcs: darcs obliterate --matches 'name XYZ' + git: git rebase --onto XYZ~1 XYZ + hg: -not-supported- + (From http://hgbook.red-bean.com/read/finding-and-fixing-mistakes.html#id394667 + "Mercurial also does not provide a way to make a file or + changeset completely disappear from history, because there is no + way to enforce its disappearance") + +Note that all of these _change_the_repo_history_, so only do this on +your interface-specific repo before it interacts with any other repo. +Otherwise, you'll have to survive by cherry-picking only the good +commits. diff --git a/xml/catmutt b/interfaces/email/catmutt index 601f14f..601f14f 100755 --- a/xml/catmutt +++ b/interfaces/email/catmutt diff --git a/interfaces/email/interactive/README b/interfaces/email/interactive/README new file mode 100644 index 0000000..8954383 --- /dev/null +++ b/interfaces/email/interactive/README @@ -0,0 +1,145 @@ +Overview +======== + +The interactive email interface to Bugs Everywhere (BE) attempts to +provide a Debian-bug-tracking-system-style interface to a BE +repository. Users can mail in bug reports, comments, or control +requests, which will be committed to the served repository. +Developers can then pull the changes they approve of from the served +repository into their other repositories and push updates back onto +the served repository. + +For details about the Debian bug tracking system that inspired this +interface, see http://www.debian.org/Bugs . + +Architecture +============ + +In order to reduce setup costs, the entire interface can piggyback on +an existing email address, although from a security standpoint it's +probably best to create a dedicated user. Incoming email is filtered +by procmail, with matching emails being piped into be-handle-mail for +execution. + +Once be-handle-mail receives the email, the parsing method is selected +according to the subject tag that procmail used grab the email in the +first place. There are three parsing styles: + Style Subject + creating bugs [be-bug:submit] new bug summary + commenting on bugs [be-bug:<bug-id>] human-specific subject + control [be-bug] human-specific subject +These are analogous to submit@bugs.debian.org, nnn@bugs.debian.org, +and control@bugs.debian.org respectively. + +Creating bugs +============= + +This interface creates a bug whose summary is given by the email's +post-tag subject. The body of the email must begin with a +pseudo-header containing at least the "Version" field. Anything after +the pseudo-header and before a line starting with '--' is, if present, +attached as the bug's first comment. + + From jdoe@example.com Fri Apr 18 12:00:00 2008 + From: John Doe <jdoe@example.com> + Date: Fri, 18 Apr 2008 12:00:00 +0000 + Content-Type: text/plain; charset=UTF-8 + Content-Transfer-Encoding: 8bit + Subject: [be-bug:submit] Need tests for the email interface. + + Version: XYZ + Severity: minor + + Someone should write up a series of test emails to send into + be-handle mail so we can test changes quickly without having to + use procmail. + + -- + Goofy tagline not included. + +Available pseudo-headers are Version, Reporter, Assign, Depend, +Severity, Status, Tag, and Target. + +Commenting on bugs +================== + +This interface appends a comment to the bug specified in the subject +tag. The the first non-multipart body is attached with the +appropriate content-type. In the case of "text/plain" contents, +anything following a line starting with '--' is stripped. + + From jdoe@example.com Fri Apr 18 12:00:00 2008 + From: John Doe <jdoe@example.com> + Date: Fri, 18 Apr 2008 12:00:00 +0000 + Content-Type: text/plain; charset=UTF-8 + Content-Transfer-Encoding: 8bit + Subject: [be-bug:XYZ] Isolated problem in baz() + + Finally tracked it down to the bar() call. Some sort of + string<->unicode conversion problem. Solution ideas? + + -- + Goofy tagline not included. + +Controlling bugs +================ + +This interface consists of a list of allowed be commands, with one +command per line. Blank lines and lines beginning with '#' are +ignored, as well anything following a line starting with '--'. All +the listed commands are executed in order and their output returned. +The commands are split into arguments with the POSIX-compliant +shlex.split(). + + From jdoe@example.com Fri Apr 18 12:00:00 2008 + From: John Doe <jdoe@example.com> + Date: Fri, 18 Apr 2008 12:00:00 +0000 + Content-Type: text/plain; charset=UTF-8 + Content-Transfer-Encoding: 8bit + Subject: [be-bug] I'll handle XYZ by release 1.2.3 + + assign XYZ "John Doe <jdoe@example.com>" + status XYZ assigned + severity XYZ critical + target XYZ 1.2.3 + + -- + Goofy tagline ignored. + +Example emails +============== + +Take a look at my interfaces/email/interactive/examples for some +more examples. + +Procmail rules +============== + +The file _procmailrc as it stands is fairly appropriate for as a +dedicated user's ~/.procmailrc. It forwards matching mail to +be-handle-mail, which should be installed somewhere in the user's +path. All non-matching mail is dumped into /dev/null. Everything +procmail does will be logged to ~/be-mail/procmail.log. + +If you're piggybacking the interface on top of an existing account, +you probably only need to add the be-handle-mail stanza to your +existing ~/.procmailrc, since you will still want to receive non-bug +emails. + +Note that you will probably have to add a + --be-dir /path/to/served/repository +option to the be-handle-mail invocation so it knows what repository to +serve. + +Multiple repositories may be served by the same email address by adding +multiple be-handle-mail stanzas, each matching a different tag, for +example the "[be-bug" portion of the stanza could be "[projectX-bug", +"[projectY-bug", etc. If you change the base tag, be sure to add a + --tag-base "projectX-bug" +or equivalent to your be-handle-mail invocation. + +Testing +======= + +Send test emails in to be-handle-mail with something like + cat examples/blank | ./be-handle-mail -o -l - -a diff --git a/interfaces/email/interactive/_procmailrc b/interfaces/email/interactive/_procmailrc new file mode 100644 index 0000000..d42c0cf --- /dev/null +++ b/interfaces/email/interactive/_procmailrc @@ -0,0 +1,22 @@ +# .procmailrc +# +# see man procmail, procmailrc, and procmailex +# +# If you already have a ~/.procmailrc file, you probably only need to +# insert the bug-email grabbing stanza in your ~/.procmailrc. +# +# This file is released to the Public Domain. + +MAILDIR=$HOME/be-mail +LOGFILE=$MAILDIR/procmail.log + +# Grab all incoming bug emails (but not replies). This rule eats +# matching emails (i.e. no further procmail processing). +:0 +* ^Subject: \[be-bug +* !^Subject:.*\[be-bug].*Re: +| be-handle-mail + +# Drop everything else +:0 +/dev/null diff --git a/interfaces/email/interactive/be-handle-mail b/interfaces/email/interactive/be-handle-mail new file mode 100755 index 0000000..1d02ccf --- /dev/null +++ b/interfaces/email/interactive/be-handle-mail @@ -0,0 +1,893 @@ +#!/usr/bin/env python +# +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Provide and email interface to the distributed bugtracker Bugs +Everywhere. Recieves incoming email via procmail. Provides an +interface similar to the Debian Bug Tracker. There are currently +three distinct email types: submits, comments, and controls. The +email types are differentiated by tags in the email subject. See +SUBJECT_TAG* for the current values. + +Submit emails create a bug (and optionally add some intitial +comments). The post-tag subject is used as the bug summary, and the +email body is parsed for a pseudo-header. Any text after the +psuedo-header but before a possible line starting with BREAK is added +as the initial bug comment. + +Comment emails... + +Control emails... + +Any changes made to the repository are commited after the email is +executed, with the email's post-tag subject as the commit message. +""" + +import codecs +import cStringIO as StringIO +import email +from email.mime.multipart import MIMEMultipart +import email.utils +import os +import os.path +import re +import shlex +import sys +import time +import traceback +import doctest +import unittest + +from becommands import subscribe +import libbe.cmdutil, libbe.encoding, libbe.utility, libbe.diff, \ + libbe.bugdir, libbe.bug, libbe.comment +import send_pgp_mime + +THIS_SERVER = u"thor.physics.drexel.edu" +THIS_ADDRESS = u"BE Bugs <wking@thor.physics.drexel.edu>" + +_THIS_DIR = os.path.abspath(os.path.dirname(__file__)) +BE_DIR = _THIS_DIR +LOGPATH = os.path.join(_THIS_DIR, u"be-handle-mail.log") +LOGFILE = None + +# Tag strings generated by generate_global_tags() +SUBJECT_TAG_BASE = u"be-bug" +SUBJECT_TAG_RESPONSE = None +SUBJECT_TAG_START = None +SUBJECT_TAG_NEW = None +SUBJECT_TAG_COMMENT = None +SUBJECT_TAG_CONTROL = None + +BREAK = u"--" +NEW_REQUIRED_PSEUDOHEADERS = [u"Version"] +NEW_OPTIONAL_PSEUDOHEADERS = [u"Reporter", u"Assign", u"Depend", u"Severity", + u"Status", u"Tag", u"Target"] +CONTROL_COMMENT = u"#" +ALLOWED_COMMANDS = [u"assign", u"comment", u"commit", u"depend", u"help", + u"list", u"merge", u"new", u"open", u"severity", u"show", + u"status", u"subscribe", u"tag", u"target"] + +AUTOCOMMIT = True + +libbe.encoding.ENCODING = u"utf-8" # force default encoding +ENCODING = libbe.encoding.get_encoding() + +class InvalidEmail (ValueError): + def __init__(self, msg, message): + ValueError.__init__(self, message) + self.msg = msg + def response(self): + header = self.msg.response_header + body = [u"Error processing email:\n", + self.response_body(), u""] + response_generator = \ + send_pgp_mime.PGPMimeMessageFactory(u"\n".join(body)) + response = MIMEMultipart() + response.attach(response_generator.plain()) + response.attach(self.msg.msg) + ret = send_pgp_mime.attach_root(header, response) + return ret + def response_body(self): + err_text = [unicode(self)] + return u"\n".join(err_text) + +class InvalidSubject (InvalidEmail): + def __init__(self, msg, message=None): + if message == None: + message = u"Invalid subject" + InvalidEmail.__init__(self, msg, message) + def response_body(self): + err_text = u"\n".join([unicode(self), u"", + u"full subject was:", + self.msg.subject()]) + return err_text + +class InvalidPseudoHeader (InvalidEmail): + def response_body(self): + err_text = [u"Invalid pseudo-header:\n", + unicode(self)] + return u"\n".join(err_text) + +class InvalidCommand (InvalidEmail): + def __init__(self, msg, command, message=None): + bigmessage = u"Invalid execution command '%s'" % command + if message != None: + bigmessage += u"\n%s" % message + InvalidEmail.__init__(self, msg, bigmessage) + self.command = command + +class InvalidOption (InvalidCommand): + def __init__(self, msg, option, message=None): + bigmessage = u"Invalid option '%s'" % (option) + if message != None: + bigmessage += u"\n%s" % message + InvalidCommand.__init__(self, msg, info, command, bigmessage) + self.option = option + +class NotificationFailed (Exception): + def __init__(self, msg): + bigmessage = "Notification failed: %s" % msg + Exception.__init__(self, bigmessage) + self.short_msg = msg + +class ID (object): + """ + Sometimes you want to reference the output of a command that + hasn't been executed yet. ID is there for situations like + > a = Command(msg, "new", ["create a bug"]) + > b = Command(msg, "comment", [ID(a), "and comment on it"]) + """ + def __init__(self, command): + self.command = command + def extract_id(self): + if hasattr(self, "cached_id"): + return self.cached_id + assert self.command.ret == 0, self.command.ret + if self.command.command == u"new": + regexp = re.compile(u"Created bug with ID (.*)") + else: + raise NotImplementedError, self.command.command + match = regexp.match(self.command.stdout) + assert len(match.groups()) == 1, str(match.groups()) + self.cached_id = match.group(1) + return self.cached_id + def __str__(self): + if self.command.ret != 0: + return "<id for %s>" % repr(self.command) + return "<id %s>" % self.extract_id() + +class Command (object): + """ + A becommands command wrapper. + Doesn't validate input, so do that before initializing. + + Initialize with + Command(msg, command, args=None, stdin=None) + where + msg: the Message instance prompting this command + command: name of becommand to execute, e.g. "new" + args: list of arguments to pass to the command + stdin: if non-null, a string to pipe into the command's stdin + """ + def __init__(self, msg, command, args=None, stdin=None): + self.msg = msg + self.command = command + if args == None: + self.args = [] + else: + self.args = args + self.stdin = stdin + self.ret = None + self.stdout = None + self.stderr = None + self.err = None + def __str__(self): + return "<command: %s %s>" % (self.command, " ".join([str(s) for s in self.args])) + def normalize_args(self): + """ + Expand any ID placeholders in self.args. + """ + for i,arg in enumerate(self.args): + if isinstance(arg, ID): + self.args[i] = arg.extract_id() + def run(self): + """ + Attempt to execute the command whose info is given in the dictionary + info. Returns the exit code, stdout, and stderr produced by the + command. + """ + if self.command in [None, u""]: # don't accept blank commands + raise InvalidCommand(self.msg, self, "Blank") + elif self.command not in ALLOWED_COMMANDS: + raise InvalidCommand(self.msg, self, "Not allowed") + assert self.ret == None, u"running %s twice!" % unicode(self) + self.normalize_args() + # set stdin and catch stdout and stderr + if self.stdin != None: + new_stdin = StringIO.StringIO(self.stdin) + orig___stdin = sys.__stdin__ + sys.__stdin__ = new_stdin + orig_stdin = sys.stdin + sys.stdin = new_stdin + new_stdout = codecs.getwriter(ENCODING)(StringIO.StringIO()) + new_stderr = codecs.getwriter(ENCODING)(StringIO.StringIO()) + orig_stdout = sys.stdout + orig_stderr = sys.stderr + sys.stdout = new_stdout + sys.stderr = new_stderr + # run the command + os.chdir(BE_DIR) + try: + self.ret = libbe.cmdutil.execute(self.command, self.args, + manipulate_encodings=False) + except libbe.cmdutil.GetHelp: + print libbe.cmdutil.help(command) + except libbe.cmdutil.GetCompletions: + self.err = InvalidOption(self.msg, self.command, u"--complete") + except libbe.cmdutil.UsageError, e: + self.err = InvalidCommand(self.msg, self, + "%s\n%s" % (type(e), unicode(e))) + except libbe.cmdutil.UserError, e: + self.err = InvalidCommand(self.msg, self, + "%s\n%s" % (type(e), unicode(e))) + # restore stdin, stdout, and stderr + if self.stdin != None: + sys.__stdin__ = new_stdin + sys.__stdin__ = orig___stdin + sys.stdin = orig_stdin + sys.stdout.flush() + sys.stderr.flush() + sys.stdout = orig_stdout + sys.stderr = orig_stderr + self.stdout = codecs.decode(new_stdout.getvalue(), ENCODING) + self.stderr = codecs.decode(new_stderr.getvalue(), ENCODING) + if self.err != None: + raise self.err + return (self.ret, self.stdout, self.stderr) + def response_msg(self): + response_body = [u"Results of running: (exit code %d)" % self.ret, + u" %s %s" % (self.command, u" ".join(self.args))] + if self.stdout != None and len(self.stdout) > 0: + response_body.extend([u"", u"stdout:", u"", self.stdout]) + if self.stderr != None and len(self.stderr) > 0: + response_body.extend([u"", u"stderr:", u"", self.stderr]) + response_body.append(u"") # trailing endline + response_generator = \ + send_pgp_mime.PGPMimeMessageFactory(u"\n".join(response_body)) + return response_generator.plain() + +class DiffTree (libbe.diff.DiffTree): + """ + In order to avoid tons of tiny MIMEText attachments, bug-level + nodes set .add_child_text=True (in .join()), which is propogated + on to their descendents. Instead of creating their own + attachement, each of these descendents appends his data_part to + the end of the bug-level MIMEText attachment. + + For the example tree in the libbe.diff.Diff unittests: + bugdir + bugdir/settings + bugdir/bugs + bugdir/bugs/new + bugdir/bugs/new/c <- sets .add_child_text + bugdir/bugs/rem + bugdir/bugs/rem/b <- sets .add_child_text + bugdir/bugs/mod + bugdir/bugs/mod/a <- sets .add_child_text + bugdir/bugs/mod/a/settings + bugdir/bugs/mod/a/comments + bugdir/bugs/mod/a/comments/new + bugdir/bugs/mod/a/comments/new/acom + bugdir/bugs/mod/a/comments/rem + bugdir/bugs/mod/a/comments/mod + """ + def report_or_none(self): + report = self.report() + payload = report.get_payload() + if payload == None or len(payload) == 0: + return None + return report + def report_string(self): + report = self.report_or_none() + if report == None: + return "No changes" + else: + return send_pgp_mime.flatten(self.report(), to_unicode=True) + def make_root(self): + return MIMEMultipart() + def join(self, root, parent, data_part): + if hasattr(parent, "attach_child_text"): + self.attach_child_text = True + if data_part != None: + send_pgp_mime.append_text(parent.data_mime_part, u"\n\n%s" % (data_part)) + self.data_mime_part = parent.data_mime_part + else: + self.data_mime_part = None + if data_part != None: + self.data_mime_part = send_pgp_mime.encodedMIMEText(data_part) + if parent != None and parent.name in [u"new", u"rem", u"mod"]: + self.attach_child_text = True + if data_part == None: # make blank data_mime_part for children's appends + self.data_mime_part = send_pgp_mime.encodedMIMEText(u"") + if self.data_mime_part != None: + self.data_mime_part[u"Content-Description"] = self.name + root.attach(self.data_mime_part) + def data_part(self, depth, indent=False): + return libbe.diff.DiffTree.data_part(self, depth, indent=indent) + +class Diff (libbe.diff.Diff): + def bug_add_string(self, bug): + return bug.string(show_comments=True) + def _comment_summary_string(self, comment): + return comment.string() + def comment_add_string(self, comment): + return self._comment_summary_string(comment) + def comment_rem_string(self, comment): + return self._comment_summary_string(comment) + +class Message (object): + def __init__(self, email_text=None, disable_parsing=False): + if disable_parsing == False: + self.text = email_text + p=email.Parser.Parser() + self.msg=p.parsestr(self.text) + if LOGFILE != None: + LOGFILE.write(u"handling %s\n" % self.author_addr()) + LOGFILE.write(u"\n%s\n\n" % self.text) + def author_tuple(self): + """ + Extract and normalize the sender's email address. Returns a + (name, email) tuple. + """ + if not hasattr(self, "author_tuple_cache"): + self.author_tuple_cache = \ + send_pgp_mime.source_email(self.msg, return_realname=True) + return self.author_tuple_cache + def author_addr(self): + return email.utils.formataddr(self.author_tuple()) + def author_name(self): + return self.author_tuple()[0] + def author_email(self): + return self.author_tuple()[1] + def default_msg_attribute_access(self, attr_name, default=None): + if attr_name in self.msg: + return self.msg[attr_name] + return default + def message_id(self, default=None): + return self.default_msg_attribute_access("message-id", default=default) + def subject(self): + if "subject" not in self.msg: + raise InvalidSubject(self, u"Email must contain a subject") + return self.msg["subject"] + def _split_subject(self): + """ + Returns (tag, subject), with missing values replaced by None. + """ + if hasattr(self, "_split_subject_cache"): + return self._split_subject_cache + args = self.subject().split(u"]",1) + if len(args) < 1: + self._split_subject_cache = (None, None) + elif len(args) < 2: + self._split_subject_cache = (args[0]+u"]", None) + else: + self._split_subject_cache = (args[0]+u"]", args[1].strip()) + return self._split_subject_cache + def _subject_tag_type(self): + """ + Parse subject tag, return (type, value), where type is one of + None, "new", "comment", or "control"; and value is None except + in the case of "comment", in which case it's the bug + ID/shortname. + """ + tag,subject = self._split_subject() + type = None + value = None + if tag == SUBJECT_TAG_NEW: + type = u"new" + elif tag == SUBJECT_TAG_CONTROL: + type = u"control" + else: + match = SUBJECT_TAG_COMMENT.match(tag) + if len(match.groups()) == 1: + type = u"comment" + value = match.group(1) + return (type, value) + def validate_subject(self): + """ + Validate the subject line. + """ + tag,subject = self._split_subject() + if not tag.startswith(SUBJECT_TAG_START): + raise InvalidSubject( + self, u"Subject must start with '%s'" % SUBJECT_TAG_START) + tag_type,value = self._subject_tag_type() + if tag_type == None: + raise InvalidSubject(self, u"Invalid tag '%s'" % tag) + elif tag_type == u"new" and len(subject) == 0: + raise InvalidSubject(self, u"Cannot create a bug with blank title") + elif tag_type == u"comment" and len(value) == 0: + raise InvalidSubject(self, u"Must specify a bug ID to comment") + def _get_bodies_and_mime_types(self): + """ + Traverse the email message returning (body, mime_type) for + each non-mulitpart portion of the message. + """ + for part in self.msg.walk(): + if part.is_multipart(): + continue + body,mime_type=(part.get_payload(decode=1),part.get_content_type()) + yield (body, mime_type) + def _parse_body_pseudoheaders(self, body, required, optional, + dictionary=None): + """ + Grab any pseudo-headers from the beginning of body. Raise + InvalidPseudoHeader on errors. Returns the body text after + the pseudo-header and a dictionary of set options. If you + like, you can initialize the dictionary with some defaults + and pass your initialized dict in as dictionary. + """ + if dictionary == None: + dictionary = {} + body_lines = body.splitlines() + all = required+optional + for i,line in enumerate(body_lines): + line = line.strip() + if len(line) == 0: + break + if ":" not in line: + raise InvalidPseudoheader(self, line) + key,value = line.split(":", 1) + value = value.strip() + if key not in all: + raise InvalidPseudoHeader(self, key) + if len(value) == 0: + raise InvalidEmail( + self, u"Blank value for: %s" % key) + dictionary[key] = value + missing = [] + for key in required: + if key not in dictionary: + missing.append(key) + if len(missing) > 0: + raise InvalidPseudoHeader(self, + u"Missing required pseudo-headers:\n%s" + % u", ".join(missing)) + remaining_body = u"\n".join(body_lines[i:]).strip() + return (remaining_body, dictionary) + def _strip_footer(self, body): + body_lines = body.splitlines() + for i,line in enumerate(body_lines): + if line.startswith(BREAK): + break + return u"\n".join(body_lines[:i]).strip() + def parse(self): + """ + Parse the commands given in the email. Raises assorted + subclasses of InvalidEmail in the case of invalid messages, + otherwise returns a list of suggested commands to run. + """ + self.validate_subject() + tag_type,value = self._subject_tag_type() + commands = [] + if tag_type == u"new": + command = u"new" + tag,subject = self._split_subject() + summary = subject + options = {u"Reporter": self.author_addr()} + body,mime_type = list(self._get_bodies_and_mime_types())[0] + comment_body,options = \ + self._parse_body_pseudoheaders(body, + NEW_REQUIRED_PSEUDOHEADERS, + NEW_OPTIONAL_PSEUDOHEADERS, + options) + args = [u"--reporter", options[u"Reporter"]] + args.append(summary) + commands.append(Command(self, command, args)) + comment_body = self._strip_footer(comment_body) + id = ID(commands[0]) + if len(comment_body) > 0: + command = u"comment" + comment = u"Version: %s\n\n"%options[u"Version"] + comment_body + args = [u"--author", self.author_addr(), + u"--alt-id", self.message_id(), + u"--content-type", mime_type] + args.append(id) + args.append(u"-") + commands.append(Command(self, u"comment", args, stdin=comment)) + for key,value in options.items(): + if key in [u"Version", u"Reporter"]: + continue # we've already handled this option + command = key.lower() + args = [id, value] + commands.append(Command(self, command, args)) + elif tag_type == u"comment": + command = u"comment" + bug_id = value + author = self.author_addr() + alt_id = self.message_id() + body,mime_type = list(self._get_bodies_and_mime_types())[0] + if mime_type == "text/plain": + body = self._strip_footer(body) + content_type = mime_type + args = [u"--author", author, u"--alt-id", alt_id, + u"--content-type", content_type, bug_id, u"-"] + commands.append(Command(self, command, args, stdin=body)) + elif tag_type == u"control": + body,mime_type = list(self._get_bodies_and_mime_types())[0] + for line in body.splitlines(): + line = line.strip() + if line.startswith(CONTROL_COMMENT) or len(line) == 0: + continue + if line.startswith(BREAK): + break + fields = shlex.split(line) + command,args = (fields[0], fields[1:]) + commands.append(Command(self, command, args)) + if len(commands) == 0: + raise InvalidEmail(self, u"No commands in control email.") + else: + raise Exception, u"Unrecognized tag type '%s'" % tag_type + return commands + def run(self): + self._begin_response() + commands = self.parse() + try: + for command in commands: + command.run() + self._add_response(command.response_msg()) + finally: + if AUTOCOMMIT == True: + tag,subject = self._split_subject() + self.commit_command = Command(self, "commit", [subject]) + self.commit_command.run() + if LOGFILE != None: + LOGFILE.write(u"Autocommit:\n%s\n\n" % + send_pgp_mime.flatten(self.commit_command.response_msg(), + to_unicode=True)) + def _begin_response(self): + tag,subject = self._split_subject() + response_header = [u"From: %s" % THIS_ADDRESS, + u"To: %s" % self.author_addr(), + u"Date: %s" % libbe.utility.time_to_str(time.time()), + u"Subject: %s Re: %s"%(SUBJECT_TAG_RESPONSE,subject) + ] + if self.message_id() != None: + response_header.append(u"In-reply-to: %s" % self.message_id()) + self.response_header = \ + send_pgp_mime.header_from_text(text=u"\n".join(response_header)) + self._response_messages = [] + def _add_response(self, response_message): + self._response_messages.append(response_message) + def response_email(self): + assert len(self._response_messages) > 0 + if len(self._response_messages) == 1: + response_body = self._response_messages[0] + else: + response_body = MIMEMultipart() + for message in self._response_messages: + response_body.attach(message) + return send_pgp_mime.attach_root(self.response_header, response_body) + def subscriber_emails(self, previous_revision=None): + if previous_revision == None: + if AUTOCOMMIT != True: # no way to tell what's changed + raise NotificationFailed("Autocommit dissabled") + if len(self._response_messages) == 0: + raise NotificationFailed("Initial email failed.") + if self.commit_command.ret != 0: + # commit failed. Error already logged. + raise NotificationFailed("Commit failed") + + # read only bugdir. + bd = libbe.bugdir.BugDir(from_disk=True, + manipulate_encodings=False) + if bd.rcs.versioned == False: # no way to tell what's changed + raise NotificationFailed("Not versioned") + + subscribers = subscribe.get_bugdir_subscribers(bd, THIS_SERVER) + if len(subscribers) == 0: + return [] + + before_bd, after_bd = self._get_before_and_after_bugdirs(bd, previous_revision) + diff = Diff(before_bd, after_bd) + diff_tree = diff.report_tree(diff_tree=DiffTree) + bug_index = {} + for child in diff_tree.child_by_path("/bugs/new"): + bug_index[child.name] = ("added", child) + for child in diff_tree.child_by_path("/bugs/mod"): + bug_index[child.name] = ("modified", child) + for child in diff_tree.child_by_path("/bugs/rem"): + bug_index[child.name] = ("removed", child) + header = self._subscriber_header(bd, previous_revision) + + emails = [] + for subscriber,subscriptions in subscribers.items(): + header.replace_header("to", subscriber) + parts = [] + for id,types in subscriptions.items(): + if id == "DIR": + if subscribe.BUGDIR_TYPE_ALL in types: + parts.append(diff_tree.report_or_none()) + break + if subscribe.BUGDIR_TYPE_NEW in types: + new = diff_tree.child_by_path("/bugs/new") + parts.append(new.report_or_none()) + continue # move on to next id + assert types == [subscribe.BUG_TYPE_ALL], types + type,bug_root = bug_index[id] + parts.append(bug_root.report_or_none()) + parts = [p for p in parts if p != None] + if len(parts) == 0: + continue # no email to this subscriber + elif len(parts) == 1: + root = parts[0] + else: # join subscription parts into a single body + root = MIMEMultipart() + root[u"Content-Description"] = u"Multiple subscription trees." + for part in parts: + root.attach(part) + emails.append(send_pgp_mime.attach_root(header, root)) + if LOGFILE != None: + LOGFILE.write(u"Preparing to notify %s of changes\n" % subscriber) + return emails + def _get_before_and_after_bugdirs(self, bd, previous_revision=None): + if previous_revision == None: + commit_msg = self.commit_command.stdout + assert commit_msg.startswith("Committed "), commit_msg + after_revision = commit_msg[len("Committed "):] + before_revision = bd.rcs.revision_id(-2) + else: + before_revision = previous_revision + if before_revision == None: + # this commit was the initial commit + before_bd = libbe.bugdir.BugDir(from_disk=False, + manipulate_encodings=False) + else: + before_bd = bd.duplicate_bugdir(before_revision) + #after_bd = bd.duplicate_bugdir(after_revision) + after_bd = bd # assume no changes since commit a few cycles ago + return (before_bd, after_bd) + def _subscriber_header(self, bd, previous_revision=None): + root_dir = os.path.basename(bd.root) + if previous_revision == None: + subject = "Changes to %s on %s by %s" \ + % (root_dir, THIS_SERVER, self.author_addr()) + else: + subject = "Changes to %s on %s since revision %s" \ + % (root_dir, THIS_SERVER, previous_revision) + header = [u"From: %s" % THIS_ADDRESS, + u"To: %s" % u"DUMMY-AUTHOR", + u"Date: %s" % libbe.utility.time_to_str(time.time()), + u"Subject: %s Re: %s" % (SUBJECT_TAG_RESPONSE, subject) + ] + return send_pgp_mime.header_from_text(text=u"\n".join(header)) + +def generate_global_tags(tag_base=u"be-bug"): + """ + Generate a series of tags from a base tag string. + """ + global SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, \ + SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL + SUBJECT_TAG_BASE = tag_base + SUBJECT_TAG_START = u"[%s" % tag_base + SUBJECT_TAG_RESPONSE = u"[%s]" % tag_base + SUBJECT_TAG_NEW = u"[%s:submit]" % tag_base + SUBJECT_TAG_COMMENT = re.compile(u"\[%s:([\-0-9a-z]*)]" % tag_base) + SUBJECT_TAG_CONTROL = SUBJECT_TAG_RESPONSE + +def open_logfile(logpath=None): + """ + If logpath=None, default to global LOGPATH. + Special logpath strings: + "-" set LOGFILE to sys.stderr + "none" disable logging + Relative logpaths are expanded relative to _THIS_DIR + """ + global LOGPATH, LOGFILE + if logpath != None: + if logpath == u"-": + LOGPATH = u"stderr" + LOGFILE = sys.stderr + elif logpath == u"none": + LOGPATH = u"none" + LOGFILE = None + elif os.path.isabs(logpath): + LOGPATH = logpath + else: + LOGPATH = os.path.join(_THIS_DIR, logpath) + if LOGFILE == None and LOGPATH != u"none": + LOGFILE = codecs.open(LOGPATH, u"a+", ENCODING) + LOGFILE.write(u"Default encoding: %s\n" % ENCODING) + +def close_logfile(): + if LOGFILE != None and LOGPATH not in [u"stderr", u"none"]: + LOGFILE.close() + +def test(): + unitsuite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) + suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) + result = unittest.TextTestRunner(verbosity=2).run(suite) + num_errors = len(result.errors) + num_failures = len(result.failures) + num_bad = num_errors + num_failures + return num_bad + +def main(args): + from optparse import OptionParser + global AUTOCOMMIT, BE_DIR + + usage="be-handle-mail [options]\n\n%s" % (__doc__) + parser = OptionParser(usage=usage) + parser.add_option('-b', '--be-dir', dest='be_dir', default=BE_DIR, + metavar="DIR", + help='Select the BE directory to serve (%default).') + parser.add_option('-t', '--tag-base', dest='tag_base', + default=SUBJECT_TAG_BASE, metavar="TAG", + help='Set the subject tag base (%default).') + parser.add_option('-o', '--output', dest='output', action='store_true', + help="Don't mail the generated message, print it to stdout instead. Useful for testing be-handle-mail functionality without the whole mail transfer agent and procmail setup.") + parser.add_option('-l', '--logfile', dest='logfile', metavar='LOGFILE', + help='Set the logfile to LOGFILE. Relative paths are relative to the location of this be-handle-mail file (%s). The special value of "-" directs the log output to stderr, and "none" disables logging.' % _THIS_DIR) + parser.add_option('-a', '--disable-autocommit', dest='autocommit', + default=True, action='store_false', + help='Disable the autocommit after parsing the email.') + parser.add_option('-s', '--disable-subscribers', dest='subscribers', + default=True, action='store_false', + help='Disable subscriber notification emails.') + parser.add_option('--notify-since', dest='notify_since', metavar='REVISION', + help='Notify subscribers of all changes since REVISION. When this option is set, no input email parsing is done.') + parser.add_option('--test', dest='test', action='store_true', + help='Run internal unit-tests and exit.') + + pargs = args + options,args = parser.parse_args(args[1:]) + + if options.test == True: + num_bad = test() + if num_bad > 126: + num_bad = 1 + sys.exit(num_bad) + + BE_DIR = options.be_dir + AUTOCOMMIT = options.autocommit + + if options.notify_since == None: + msg_text = sys.stdin.read() + + libbe.encoding.set_IO_stream_encodings(ENCODING) # _after_ reading message + open_logfile(options.logfile) + generate_global_tags(options.tag_base) + + if options.notify_since != None: + if options.subscribers == True: + if LOGFILE != None: + LOGFILE.write(u"Checking for subscribers to notify since revision %s\n" + % options.notify_since) + try: + m = Message(disable_parsing=True) + emails = m.subscriber_emails(options.notify_since) + except NotificationFailed, e: + if LOGFILE != None: + LOGFILE.write(unicode(e) + u"\n") + else: + for msg in emails: + if options.output == True: + print send_pgp_mime.flatten(msg, to_unicode=True) + else: + send_pgp_mime.mail(msg, send_pgp_mime.sendmail) + close_logfile() + sys.exit(0) + + if len(msg_text.strip()) == 0: # blank email!? + if LOGFILE != None: + LOGFILE.write(u"Blank email!\n") + close_logfile() + sys.exit(1) + try: + m = Message(msg_text) + m.run() + except InvalidEmail, e: + response = e.response() + except Exception, e: + if LOGFILE != None: + LOGFILE.write(u"Uncaught exception:\n%s\n" % (e,)) + traceback.print_tb(sys.exc_traceback, file=LOGFILE) + close_logfile() + sys.exit(1) + else: + response = m.response_email() + if options.output == True: + print send_pgp_mime.flatten(response, to_unicode=True) + else: + if LOGFILE != None: + LOGFILE.write(u"sending response to %s\n" % m.author_addr()) + LOGFILE.write(u"\n%s\n\n" % send_pgp_mime.flatten(response, + to_unicode=True)) + send_pgp_mime.mail(response, send_pgp_mime.sendmail) + if options.subscribers == True: + if LOGFILE != None: + LOGFILE.write(u"Checking for subscribers\n") + try: + emails = m.subscriber_emails() + except NotificationFailed, e: + if LOGFILE != None: + LOGFILE.write(unicode(e) + u"\n") + else: + for msg in emails: + if options.output == True: + print send_pgp_mime.flatten(msg, to_unicode=True) + else: + send_pgp_mime.mail(msg, send_pgp_mime.sendmail) + + close_logfile() + + +class GenerateGlobalTagsTestCase (unittest.TestCase): + def setUp(self): + super(GenerateGlobalTagsTestCase, self).setUp() + self.save_global_tags() + def tearDown(self): + self.restore_global_tags() + super(GenerateGlobalTagsTestCase, self).tearDown() + def save_global_tags(self): + self.saved_globals = [SUBJECT_TAG_BASE, SUBJECT_TAG_START, + SUBJECT_TAG_RESPONSE, SUBJECT_TAG_NEW, + SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL] + def restore_global_tags(self): + global SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, \ + SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL + SUBJECT_TAG_BASE, SUBJECT_TAG_START, SUBJECT_TAG_RESPONSE, \ + SUBJECT_TAG_NEW, SUBJECT_TAG_COMMENT, SUBJECT_TAG_CONTROL = \ + self.saved_globals + def test_restore_global_tags(self): + "Test global tag restoration by teardown function." + global SUBJECT_TAG_BASE + self.failUnlessEqual(SUBJECT_TAG_BASE, u"be-bug") + SUBJECT_TAG_BASE = "projectX-bug" + self.failUnlessEqual(SUBJECT_TAG_BASE, u"projectX-bug") + self.restore_global_tags() + self.failUnlessEqual(SUBJECT_TAG_BASE, u"be-bug") + def test_subject_tag_base(self): + "Should set SUBJECT_TAG_BASE global correctly" + generate_global_tags(u"projectX-bug") + self.failUnlessEqual(SUBJECT_TAG_BASE, u"projectX-bug") + def test_subject_tag_start(self): + "Should set SUBJECT_TAG_START global correctly" + generate_global_tags(u"projectX-bug") + self.failUnlessEqual(SUBJECT_TAG_START, u"[projectX-bug") + def test_subject_tag_response(self): + "Should set SUBJECT_TAG_RESPONSE global correctly" + generate_global_tags(u"projectX-bug") + self.failUnlessEqual(SUBJECT_TAG_RESPONSE, u"[projectX-bug]") + def test_subject_tag_new(self): + "Should set SUBJECT_TAG_NEW global correctly" + generate_global_tags(u"projectX-bug") + self.failUnlessEqual(SUBJECT_TAG_NEW, u"[projectX-bug:submit]") + def test_subject_tag_control(self): + "Should set SUBJECT_TAG_CONTROL global correctly" + generate_global_tags(u"projectX-bug") + self.failUnlessEqual(SUBJECT_TAG_CONTROL, u"[projectX-bug]") + def test_subject_tag_comment(self): + "Should set SUBJECT_TAG_COMMENT global correctly" + generate_global_tags(u"projectX-bug") + m = SUBJECT_TAG_COMMENT.match("[projectX-bug:xyz-123]") + self.failUnlessEqual(len(m.groups()), 1) + self.failUnlessEqual(m.group(1), u"xyz-123") + +if __name__ == "__main__": + main(sys.argv) diff --git a/interfaces/email/interactive/becommands b/interfaces/email/interactive/becommands new file mode 120000 index 0000000..8af773c --- /dev/null +++ b/interfaces/email/interactive/becommands @@ -0,0 +1 @@ +../../../becommands
\ No newline at end of file diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/not-zip-safe b/interfaces/email/interactive/examples/blank index e69de29..e69de29 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/not-zip-safe +++ b/interfaces/email/interactive/examples/blank diff --git a/interfaces/email/interactive/examples/comment b/interfaces/email/interactive/examples/comment new file mode 100644 index 0000000..f22e4b2 --- /dev/null +++ b/interfaces/email/interactive/examples/comment @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <xyz@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug:a1d] Subject ignored + +We sure do. +-- +Goofy tagline ignored diff --git a/interfaces/email/interactive/examples/failing_multiples b/interfaces/email/interactive/examples/failing_multiples new file mode 100644 index 0000000..cf50211 --- /dev/null +++ b/interfaces/email/interactive/examples/failing_multiples @@ -0,0 +1,16 @@ +From jdoe@example.com Fri Apr 18 12:00:00 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Commit message... + +new "test bug" +new "test bug 2" +failing-command +new "test bug 3" + +-- +This message fails partway through, but the partial changes should be +recorded in a commit... diff --git a/interfaces/email/interactive/examples/invalid_command b/interfaces/email/interactive/examples/invalid_command new file mode 100644 index 0000000..f2963c7 --- /dev/null +++ b/interfaces/email/interactive/examples/invalid_command @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] + +close +-- +Close is currently disabled for the email interface. diff --git a/interfaces/email/interactive/examples/invalid_subject b/interfaces/email/interactive/examples/invalid_subject new file mode 100644 index 0000000..1e2eb88 --- /dev/null +++ b/interfaces/email/interactive/examples/invalid_subject @@ -0,0 +1,9 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: Spam! + +This should elicit an "invalid subject" response email. diff --git a/interfaces/email/interactive/examples/list b/interfaces/email/interactive/examples/list new file mode 100644 index 0000000..acba424 --- /dev/null +++ b/interfaces/email/interactive/examples/list @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Subject ignored + +list --status all +-- +Dummy content diff --git a/interfaces/email/interactive/examples/missing_command b/interfaces/email/interactive/examples/missing_command new file mode 100644 index 0000000..bb390fc --- /dev/null +++ b/interfaces/email/interactive/examples/missing_command @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Subject ignored + +abcde +-- +This should elicit a "invalid command 'abcde'" response email. diff --git a/interfaces/email/interactive/examples/multiple_commands b/interfaces/email/interactive/examples/multiple_commands new file mode 100644 index 0000000..41ef730 --- /dev/null +++ b/interfaces/email/interactive/examples/multiple_commands @@ -0,0 +1,14 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Subject ignored + +help +list --status=all +list --status=fixed +show --xml 361 +-- +Goofy tagline ignored. diff --git a/interfaces/email/interactive/examples/new b/interfaces/email/interactive/examples/new new file mode 100644 index 0000000..c64db93 --- /dev/null +++ b/interfaces/email/interactive/examples/new @@ -0,0 +1,19 @@ +From jdoe@example.com Fri Apr 18 12:00:00 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug:submit] Need tests for the email interface. + +Version: XYZ +Reporter: Jane Doe +Assign: Dick Tracy +Depend: 00f +Severity: critical +Status: assigned +Tag: topsecret +Target: Law&Order + +-- +Goofy tagline not included, and no comment added. diff --git a/interfaces/email/interactive/examples/new_with_comment b/interfaces/email/interactive/examples/new_with_comment new file mode 100644 index 0000000..1077f0f --- /dev/null +++ b/interfaces/email/interactive/examples/new_with_comment @@ -0,0 +1,13 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug:submit] Need tests for the email interface. + +Version: XYZ + +I think so anyway. +-- +Goofy tagline not included. diff --git a/interfaces/email/interactive/examples/show b/interfaces/email/interactive/examples/show new file mode 100644 index 0000000..c5f8a4d --- /dev/null +++ b/interfaces/email/interactive/examples/show @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Subject ignored + +show --xml 361 +-- +Can we show a bug? diff --git a/interfaces/email/interactive/examples/unicode b/interfaces/email/interactive/examples/unicode new file mode 100644 index 0000000..f0e8001 --- /dev/null +++ b/interfaces/email/interactive/examples/unicode @@ -0,0 +1,11 @@ +From jdoe@example.com Fri Apr 18 11:18:58 2008 +Message-ID: <abcd@example.com> +Date: Fri, 18 Apr 2008 12:00:00 +0000 +From: John Doe <jdoe@example.com> +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +Subject: [be-bug] Subject ignored + +show --xml f7ccd916-b5c7-4890-a2e3-8c8ace17ae3a +-- +Can we handle unicode output? diff --git a/interfaces/email/interactive/libbe b/interfaces/email/interactive/libbe new file mode 120000 index 0000000..7d18612 --- /dev/null +++ b/interfaces/email/interactive/libbe @@ -0,0 +1 @@ +../../../libbe
\ No newline at end of file diff --git a/interfaces/email/interactive/send_pgp_mime.py b/interfaces/email/interactive/send_pgp_mime.py new file mode 100644 index 0000000..55767b3 --- /dev/null +++ b/interfaces/email/interactive/send_pgp_mime.py @@ -0,0 +1,611 @@ +#!/usr/bin/python +# +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Python module and command line tool for sending pgp/mime email. + +Mostly uses subprocess to call gpg and a sendmail-compatible mailer. +If you lack gpg, either don't use the encryption functions or adjust +the pgp_* commands. You may need to adjust the sendmail command to +point to whichever sendmail-compatible mailer you have on your system. +""" + +from cStringIO import StringIO +import os +import re +#import GnuPGInterface # Maybe should use this instead of subprocess +import smtplib +import subprocess +import sys +import tempfile +import types + +try: + from email import Message + from email.mime.text import MIMEText + from email.mime.multipart import MIMEMultipart + from email.mime.application import MIMEApplication + from email.encoders import encode_7or8bit + from email.generator import Generator + from email.parser import Parser + from email.utils import getaddress +except ImportError: + # adjust to old python 2.4 + from email import Message + from email.MIMEText import MIMEText + from email.MIMEMultipart import MIMEMultipart + from email.MIMENonMultipart import MIMENonMultipart + from email.Encoders import encode_7or8bit + from email.Generator import Generator + from email.parser import Parser + from email.Utils import getaddresses + + getaddress = getaddresses + class MIMEApplication (MIMENonMultipart): + def __init__(self, _data, _subtype, _encoder, **params): + MIMENonMultipart.__init__(self, 'application', _subtype, **params) + self.set_payload(_data) + _encoder(self) + +usage="""usage: %prog [options] + +Scriptable PGP MIME email using gpg. + +You can use gpg-agent for passphrase caching if your key requires a +passphrase (it better!). Example usage would be to install gpg-agent, +and then run + export GPG_TTY=`tty` + eval $(gpg-agent --daemon) +in your shell before invoking this script. See gpg-agent(1) for more +details. Alternatively, you can send your passphrase in on stdin + echo 'passphrase' | %prog [options] +or use the --passphrase-file option + %prog [options] --passphrase-file FILE [more options] +Both of these alternatives are much less secure than gpg-agent. You +have been warned. +""" + +verboseInvoke = False +PGP_SIGN_AS = None +PASSPHRASE = None + +# The following commands are adapted from my .mutt/pgp configuration +# +# Printf-like sequences: +# %a The value of PGP_SIGN_AS. +# %f Expands to the name of a file with text to be signed/encrypted. +# %p Expands to the passphrase argument. +# %R A string with some number (0 on up) of pgp_reciepient_arg +# strings. +# %r One key ID (e.g. recipient email address) to build a +# pgp_reciepient_arg string. +# +# The above sequences can be used to optionally print a string if +# their length is nonzero. For example, you may only want to pass the +# -u/--local-user argument to gpg if PGP_SIGN_AS is defined. To +# optionally print a string based upon one of the above sequences, the +# following construct is used +# %?<sequence_char>?<optional_string>? +# where sequence_char is a character from the table above, and +# optional_string is the string you would like printed if status_char +# is nonzero. optional_string may contain other sequence as well as +# normal text, but it may not contain any question marks. +# +# see http://codesorcery.net/old/mutt/mutt-gnupg-howto +# http://www.mutt.org/doc/manual/manual-6.html#pgp_autosign +# http://tldp.org/HOWTO/Mutt-GnuPG-PGP-HOWTO-8.html +# for more details + +pgp_recipient_arg='-r "%r"' +pgp_stdin_passphrase_arg='--passphrase-fd 0' +pgp_sign_command='/usr/bin/gpg --no-verbose --quiet --batch %p --output - --detach-sign --armor --textmode %?a?-u "%a"? %f' +pgp_encrypt_only_command='/usr/bin/gpg --no-verbose --quiet --batch --output - --encrypt --armor --textmode --always-trust --encrypt-to "%a" %R -- %f' +pgp_encrypt_sign_command='/usr/bin/gpg --no-verbose --quiet --batch %p --output - --encrypt --sign %?a?-u "%a"? --armor --textmode --always-trust --encrypt-to "%a" %R -- %f' +sendmail='/usr/sbin/sendmail -t' + +def mail(msg, sendmail=None): + """ + Send an email Message instance on its merry way. + + We can shell out to the user specified sendmail in case + the local host doesn't have an SMTP server set up + for easy smtplib usage. + """ + if sendmail != None: + execute(sendmail, stdin=flatten(msg)) + return None + s = smtplib.SMTP() + s.connect() + s.sendmail(from_addr=source_email(msg), + to_addrs=target_emails(msg), + msg=flatten(msg)) + s.close() + +def header_from_text(text, encoding="us-ascii"): + """ + Simple wrapper for instantiating an email.Message from text. + >>> header = header_from_text('\\n'.join(['From: me@big.edu','To: you@big.edu','Subject: testing'])) + >>> print flatten(header) + From: me@big.edu + To: you@big.edu + Subject: testing + <BLANKLINE> + <BLANKLINE> + """ + text = text.strip() + if type(text) == types.UnicodeType: + text = text.encode(encoding) + # assume StringType arguments are already encoded + p = Parser() + return p.parsestr(text, headersonly=True) + +def guess_encoding(text): + if type(text) == types.StringType: + encoding = "us-ascii" + elif type(text) == types.UnicodeType: + for encoding in ["us-ascii", "iso-8859-1", "utf-8"]: + try: + text.encode(encoding) + except UnicodeError: + pass + else: + break + assert encoding != None + return encoding + +def encodedMIMEText(body, encoding=None): + if encoding == None: + encoding = guess_encoding(body) + if encoding == "us-ascii": + return MIMEText(body) + else: + # Create the message ('plain' stands for Content-Type: text/plain) + return MIMEText(body.encode(encoding), 'plain', encoding) + +def append_text(text_part, new_text): + original_payload = text_part.get_payload(decode=True) + new_payload = u"%s%s" % (original_payload, new_text) + new_encoding = guess_encoding(new_payload) + text_part.set_payload(new_payload.encode(new_encoding), new_encoding) + +def attach_root(header, root_part): + """ + Attach the email.Message root_part to the email.Message header + without generating a multi-part message. + """ + for k,v in header.items(): + root_part[k] = v + return root_part + +def execute(args, stdin=None, expect=(0,)): + """ + Execute a command (allows us to drive gpg). + """ + if verboseInvoke == True: + print >> sys.stderr, '$ '+args + try: + p = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, close_fds=True) + except OSError, e: + strerror = '%s\nwhile executing %s' % (e.args[1], args) + raise Exception, strerror + output, error = p.communicate(input=stdin) + status = p.wait() + if verboseInvoke == True: + print >> sys.stderr, '(status: %d)\n%s%s' % (status, output, error) + if status not in expect: + strerror = '%s\nwhile executing %s\n%s\n%d' % (args[1], args, error, status) + raise Exception, strerror + return status, output, error + +def replace(template, format_char, replacement_text): + """ + >>> replace('--textmode %?a?-u %a? %f', 'f', 'file.in') + '--textmode %?a?-u %a? file.in' + >>> replace('--textmode %?a?-u %a? %f', 'a', '0xHEXKEY') + '--textmode -u 0xHEXKEY %f' + >>> replace('--textmode %?a?-u %a? %f', 'a', '') + '--textmode %f' + """ + if replacement_text == None: + replacement_text = "" + regexp = re.compile('%[?]'+format_char+'[?]([^?]*)[?]') + if len(replacement_text) > 0: + str = regexp.sub('\g<1>', template) + else: + str = regexp.sub('', template) + regexp = re.compile('%'+format_char) + str = regexp.sub(replacement_text, str) + return str + +def flatten(msg, to_unicode=False): + """ + Produce flat text output from an email Message instance. + """ + assert msg != None + fp = StringIO() + g = Generator(fp, mangle_from_=False) + g.flatten(msg) + text = fp.getvalue() + if to_unicode == True: + encoding = msg.get_content_charset() or "utf-8" + text = unicode(text, encoding=encoding) + return text + +def source_email(msg, return_realname=False): + """ + Search the header of an email Message instance to find the + sender's email address. + """ + froms = msg.get_all('from', []) + from_tuples = getaddresses(froms) # [(realname, email_address), ...] + assert len(from_tuples) == 1 + if return_realname == True: + return from_tuples[0] # (realname, email_address) + return from_tuples[0][1] # email_address + +def target_emails(msg): + """ + Search the header of an email Message instance to find a + list of recipient's email addresses. + """ + tos = msg.get_all('to', []) + ccs = msg.get_all('cc', []) + bccs = msg.get_all('bcc', []) + resent_tos = msg.get_all('resent-to', []) + resent_ccs = msg.get_all('resent-cc', []) + resent_bccs = msg.get_all('resent-bcc', []) + all_recipients = getaddresses(tos + ccs + bccs + resent_tos + + resent_ccs + resent_bccs) + return [addr[1] for addr in all_recipients] + +class PGPMimeMessageFactory (object): + """ + See http://www.ietf.org/rfc/rfc3156.txt for specification details. + >>> from_addr = "me@big.edu" + >>> to_addr = "you@you.edu" + >>> header = header_from_text('\\n'.join(['From: %s'%from_addr,'To: %s'%to_addr,'Subject: testing'])) + >>> source_email(header) == from_addr + True + >>> target_emails(header) == [to_addr] + True + >>> m = EncryptedMessageFactory('check 1 2\\ncheck 1 2\\n') + >>> print flatten(m.clearBodyPart()) + Content-Type: text/plain; charset="us-ascii" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Disposition: inline + <BLANKLINE> + check 1 2 + check 1 2 + <BLANKLINE> + >>> print flatten(m.plain()) + Content-Type: text/plain; charset="us-ascii" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + <BLANKLINE> + check 1 2 + check 1 2 + <BLANKLINE> + >>> signed = m.sign(header) + >>> signed.set_boundary('boundsep') + >>> print flatten(signed).replace('\\t', ' '*4) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + Content-Type: multipart/signed; protocol="application/pgp-signature"; + micalg="pgp-sha1"; boundary="boundsep" + MIME-Version: 1.0 + Content-Disposition: inline + <BLANKLINE> + --boundsep + Content-Type: text/plain; charset="us-ascii" + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Disposition: inline + <BLANKLINE> + check 1 2 + check 1 2 + <BLANKLINE> + --boundsep + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Description: signature + Content-Type: application/pgp-signature; name="signature.asc"; + charset="us-ascii" + <BLANKLINE> + -----BEGIN PGP SIGNATURE----- + ... + -----END PGP SIGNATURE----- + <BLANKLINE> + --boundsep-- + >>> encrypted = m.encrypt(header) + >>> encrypted.set_boundary('boundsep') + >>> print flatten(encrypted).replace('\\t', ' '*4) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + Content-Type: multipart/encrypted; + protocol="application/pgp-encrypted"; + micalg="pgp-sha1"; boundary="boundsep" + MIME-Version: 1.0 + Content-Disposition: inline + <BLANKLINE> + --boundsep + Content-Type: application/pgp-encrypted + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + <BLANKLINE> + Version: 1 + <BLANKLINE> + --boundsep + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Type: application/octet-stream; charset="us-ascii" + <BLANKLINE> + -----BEGIN PGP MESSAGE----- + ... + -----END PGP MESSAGE----- + <BLANKLINE> + --boundsep-- + >>> signedAndEncrypted = m.signAndEncrypt(header) + >>> signedAndEncrypted.set_boundary('boundsep') + >>> print flatten(signedAndEncrypted).replace('\\t', ' '*4) # doctest: +ELLIPSIS, +NORMALIZE_WHITESPACE + Content-Type: multipart/encrypted; + protocol="application/pgp-encrypted"; + micalg="pgp-sha1"; boundary="boundsep" + MIME-Version: 1.0 + Content-Disposition: inline + <BLANKLINE> + --boundsep + Content-Type: application/pgp-encrypted + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + <BLANKLINE> + Version: 1 + <BLANKLINE> + --boundsep + MIME-Version: 1.0 + Content-Transfer-Encoding: 7bit + Content-Type: application/octet-stream; charset="us-ascii" + <BLANKLINE> + -----BEGIN PGP MESSAGE----- + ... + -----END PGP MESSAGE----- + <BLANKLINE> + --boundsep-- + """ + def __init__(self, body): + self.body = body + def clearBodyPart(self): + body = encodedMIMEText(self.body) + body.add_header('Content-Disposition', 'inline') + return body + def passphrase_arg(self, passphrase=None): + if passphrase == None and PASSPHRASE != None: + passphrase = PASSPHRASE + if passphrase == None: + return (None,'') + return (passphrase, pgp_stdin_passphrase_arg) + def plain(self): + """ + text/plain + """ + return encodedMIMEText(self.body) + def sign(self, header, passphrase=None): + """ + multipart/signed + +-> text/plain (body) + +-> application/pgp-signature (signature) + """ + passphrase,pass_arg = self.passphrase_arg(passphrase) + body = self.clearBodyPart() + bfile = tempfile.NamedTemporaryFile() + bfile.write(flatten(body)) + bfile.flush() + + args = replace(pgp_sign_command, 'f', bfile.name) + if PGP_SIGN_AS == None: + pgp_sign_as = '<%s>' % source_email(header) + else: + pgp_sign_as = PGP_SIGN_AS + args = replace(args, 'a', pgp_sign_as) + args = replace(args, 'p', pass_arg) + status,output,error = execute(args, stdin=passphrase) + signature = output + + sig = MIMEApplication(_data=signature, + _subtype='pgp-signature; name="signature.asc"', + _encoder=encode_7or8bit) + sig['Content-Description'] = 'signature' + sig.set_charset('us-ascii') + + msg = MIMEMultipart('signed', micalg='pgp-sha1', + protocol='application/pgp-signature') + msg.attach(body) + msg.attach(sig) + + msg['Content-Disposition'] = 'inline' + return msg + def encrypt(self, header, passphrase=None): + """ + multipart/encrypted + +-> application/pgp-encrypted (control information) + +-> application/octet-stream (body) + """ + body = self.clearBodyPart() + bfile = tempfile.NamedTemporaryFile() + bfile.write(flatten(body)) + bfile.flush() + + recipients = [replace(pgp_recipient_arg, 'r', recipient) + for recipient in target_emails(header)] + recipient_string = ' '.join(recipients) + args = replace(pgp_encrypt_only_command, 'R', recipient_string) + args = replace(args, 'f', bfile.name) + if PGP_SIGN_AS == None: + pgp_sign_as = '<%s>' % source_email(header) + else: + pgp_sign_as = PGP_SIGN_AS + args = replace(args, 'a', pgp_sign_as) + status,output,error = execute(args) + encrypted = output + + enc = MIMEApplication(_data=encrypted, _subtype='octet-stream', + _encoder=encode_7or8bit) + enc.set_charset('us-ascii') + + control = MIMEApplication(_data='Version: 1\n', _subtype='pgp-encrypted', + _encoder=encode_7or8bit) + + msg = MIMEMultipart('encrypted', micalg='pgp-sha1', + protocol='application/pgp-encrypted') + msg.attach(control) + msg.attach(enc) + + msg['Content-Disposition'] = 'inline' + return msg + def signAndEncrypt(self, header, passphrase=None): + """ + multipart/encrypted + +-> application/pgp-encrypted (control information) + +-> application/octet-stream (body) + """ + passphrase,pass_arg = self.passphrase_arg(passphrase) + body = self.sign(header, passphrase) + body.__delitem__('Bcc') + bfile = tempfile.NamedTemporaryFile() + bfile.write(flatten(body)) + bfile.flush() + + recipients = [replace(pgp_recipient_arg, 'r', recipient) + for recipient in target_emails(header)] + recipient_string = ' '.join(recipients) + args = replace(pgp_encrypt_only_command, 'R', recipient_string) + args = replace(args, 'f', bfile.name) + if PGP_SIGN_AS == None: + pgp_sign_as = '<%s>' % source_email(header) + else: + pgp_sign_as = PGP_SIGN_AS + args = replace(args, 'a', pgp_sign_as) + args = replace(args, 'p', pass_arg) + status,output,error = execute(args, stdin=passphrase) + encrypted = output + + enc = MIMEApplication(_data=encrypted, _subtype='octet-stream', + _encoder=encode_7or8bit) + enc.set_charset('us-ascii') + + control = MIMEApplication(_data='Version: 1\n', + _subtype='pgp-encrypted', + _encoder=encode_7or8bit) + + msg = MIMEMultipart('encrypted', micalg='pgp-sha1', + protocol='application/pgp-encrypted') + msg.attach(control) + msg.attach(enc) + + msg['Content-Disposition'] = 'inline' + return msg + +def test(): + import doctest + doctest.testmod() + + +if __name__ == '__main__': + from optparse import OptionParser + + parser = OptionParser(usage=usage) + parser.add_option('-t', '--test', dest='test', action='store_true', + help='Run doctests and exit') + + parser.add_option('-H', '--header-file', dest='header_filename', + help='file containing email header', metavar='FILE') + parser.add_option('-B', '--body-file', dest='body_filename', + help='file containing email body', metavar='FILE') + + parser.add_option('-P', '--passphrase-file', dest='passphrase_file', + help='file containing gpg passphrase', metavar='FILE') + parser.add_option('-p', '--passphrase-fd', dest='passphrase_fd', + help='file descriptor from which to read gpg passphrase (0 for stdin)', + type="int", metavar='DESCRIPTOR') + + parser.add_option('--mode', dest='mode', default='sign', + help="One of 'sign', 'encrypt', 'sign-encrypt', or 'plain'. Defaults to %default.", + metavar='MODE') + + parser.add_option('-a', '--sign-as', dest='sign_as', + help="The gpg key to sign with (gpg's -u/--local-user)", + metavar='KEY') + + parser.add_option('--output', dest='output', action='store_true', + help="Don't mail the generated message, print it to stdout instead.") + + (options, args) = parser.parse_args() + + stdin_used = False + + if options.passphrase_file != None: + PASSPHRASE = file(options.passphrase_file, 'r').read() + elif options.passphrase_fd != None: + if options.passphrase_fd == 0: + stdin_used = True + PASSPHRASE = sys.stdin.read() + else: + PASSPHRASE = os.read(options.passphrase_fd) + + if options.sign_as: + PGP_SIGN_AS = options.sign_as + + if options.test == True: + test() + sys.exit(0) + + header = None + if options.header_filename != None: + if options.header_filename == '-': + assert stdin_used == False + stdin_used = True + header = sys.stdin.read() + else: + header = file(options.header_filename, 'r').read() + if header == None: + raise Exception, "missing header" + headermsg = header_from_text(header) + body = None + if options.body_filename != None: + if options.body_filename == '-': + assert stdin_used == False + stdin_used = True + body = sys.stdin.read() + else: + body = file(options.body_filename, 'r').read() + if body == None: + raise Exception, "missing body" + + m = EncryptedMessageFactory(body) + if options.mode == "sign": + bodymsg = m.sign(header) + elif options.mode == "encrypt": + bodymsg = m.encrypt(header) + elif options.mode == "sign-encrypt": + bodymsg = m.signAndEncrypt(header) + elif options.mode == "plain": + bodymsg = m.plain() + else: + print "Unrecognized mode '%s'" % options.mode + + message = attach_root(headermsg, bodymsg) + if options.output == True: + message = flatten(message) + print message + else: + mail(message, sendmail) diff --git a/misc/gui/beg b/interfaces/gui/beg/beg index 55e537d..55e537d 100755 --- a/misc/gui/beg +++ b/interfaces/gui/beg/beg diff --git a/misc/gui/table.py b/interfaces/gui/beg/table.py index 2865f28..2865f28 100644 --- a/misc/gui/table.py +++ b/interfaces/gui/beg/table.py diff --git a/misc/gui/wxbe b/interfaces/gui/wxbe/wxbe index e71ae0c..e71ae0c 100755 --- a/misc/gui/wxbe +++ b/interfaces/gui/wxbe/wxbe diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/SOURCES.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/SOURCES.txt index def18b1..def18b1 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/SOURCES.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/SOURCES.txt diff --git a/Bugs-Everywhere-Web/beweb/__init__.py b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/not-zip-safe index e69de29..e69de29 100644 --- a/Bugs-Everywhere-Web/beweb/__init__.py +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/not-zip-safe diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/requires.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/requires.txt index 88b15cb..88b15cb 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/requires.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/requires.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/sqlobject.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/sqlobject.txt index 7f7cbad..7f7cbad 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/sqlobject.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/sqlobject.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/top_level.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/top_level.txt index 6455be9..6455be9 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/top_level.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/Bugs-Everywhere-Web.egg-info/top_level.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/PKG-INFO b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/PKG-INFO index 6cb6ad2..6cb6ad2 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/PKG-INFO +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/PKG-INFO diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt index ab62ee4..ab62ee4 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/SOURCES.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt index 8b13789..8b13789 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/dependency_links.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe index 8b13789..8b13789 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/not-zip-safe diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/paster_plugins.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/paster_plugins.txt index 14fec70..14fec70 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/paster_plugins.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/paster_plugins.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt index 5fd6f71..5fd6f71 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/requires.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/sqlobject.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/sqlobject.txt index 7f7cbad..7f7cbad 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/sqlobject.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/sqlobject.txt diff --git a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/top_level.txt b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/top_level.txt index 74a8358..74a8358 100644 --- a/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/top_level.txt +++ b/interfaces/web/Bugs-Everywhere-Web/Bugs_Everywhere_Web.egg-info/top_level.txt diff --git a/Bugs-Everywhere-Web/README.txt b/interfaces/web/Bugs-Everywhere-Web/README.txt index 10774df..10774df 100644 --- a/Bugs-Everywhere-Web/README.txt +++ b/interfaces/web/Bugs-Everywhere-Web/README.txt diff --git a/Bugs-Everywhere-Web/beweb/templates/__init__.py b/interfaces/web/Bugs-Everywhere-Web/beweb/__init__.py index e69de29..e69de29 100644 --- a/Bugs-Everywhere-Web/beweb/templates/__init__.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/__init__.py diff --git a/Bugs-Everywhere-Web/beweb/app.cfg b/interfaces/web/Bugs-Everywhere-Web/beweb/app.cfg index 024fa8a..024fa8a 100644 --- a/Bugs-Everywhere-Web/beweb/app.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/app.cfg diff --git a/Bugs-Everywhere-Web/beweb/config.py.example b/interfaces/web/Bugs-Everywhere-Web/beweb/config.py.example index 8745c6d..8745c6d 100644 --- a/Bugs-Everywhere-Web/beweb/config.py.example +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/config.py.example diff --git a/Bugs-Everywhere-Web/beweb/config/app.cfg b/interfaces/web/Bugs-Everywhere-Web/beweb/config/app.cfg index 15555b7..15555b7 100644 --- a/Bugs-Everywhere-Web/beweb/config/app.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/config/app.cfg diff --git a/Bugs-Everywhere-Web/beweb/config/log.cfg b/interfaces/web/Bugs-Everywhere-Web/beweb/config/log.cfg index ce776f8..ce776f8 100644 --- a/Bugs-Everywhere-Web/beweb/config/log.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/config/log.cfg diff --git a/Bugs-Everywhere-Web/beweb/controllers.py b/interfaces/web/Bugs-Everywhere-Web/beweb/controllers.py index a0d0ff9..a0d0ff9 100644 --- a/Bugs-Everywhere-Web/beweb/controllers.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/controllers.py diff --git a/Bugs-Everywhere-Web/beweb/formatting.py b/interfaces/web/Bugs-Everywhere-Web/beweb/formatting.py index 1278414..1278414 100644 --- a/Bugs-Everywhere-Web/beweb/formatting.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/formatting.py diff --git a/Bugs-Everywhere-Web/beweb/json.py b/interfaces/web/Bugs-Everywhere-Web/beweb/json.py index 6e100c3..6e100c3 100644 --- a/Bugs-Everywhere-Web/beweb/json.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/json.py diff --git a/Bugs-Everywhere-Web/beweb/model.py b/interfaces/web/Bugs-Everywhere-Web/beweb/model.py index aa4b6b6..aa4b6b6 100644 --- a/Bugs-Everywhere-Web/beweb/model.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/model.py diff --git a/Bugs-Everywhere-Web/beweb/prest.py b/interfaces/web/Bugs-Everywhere-Web/beweb/prest.py index 9a6505d..9a6505d 100644 --- a/Bugs-Everywhere-Web/beweb/prest.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/prest.py diff --git a/Bugs-Everywhere-Web/beweb/release.py b/interfaces/web/Bugs-Everywhere-Web/beweb/release.py index 9d64bf7..9d64bf7 100644 --- a/Bugs-Everywhere-Web/beweb/release.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/release.py diff --git a/Bugs-Everywhere-Web/beweb/static/css/style.css b/interfaces/web/Bugs-Everywhere-Web/beweb/static/css/style.css index 6fe197f..6fe197f 100644 --- a/Bugs-Everywhere-Web/beweb/static/css/style.css +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/css/style.css diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-b.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-b.png Binary files differindex 790e438..790e438 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-b.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-b.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-bl.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-bl.png Binary files differindex 5b43259..5b43259 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-bl.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-bl.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-br.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-br.png Binary files differindex 6cfd62c..6cfd62c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-br.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-br.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-l.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-l.png Binary files differindex a6ce3ce..a6ce3ce 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-l.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-l.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-r.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-r.png Binary files differindex 1ffd6f8..1ffd6f8 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-r.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-r.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-t.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-t.png Binary files differindex 0129b0c..0129b0c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-t.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-t.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-tl.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-tl.png Binary files differindex d616b77..d616b77 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-tl.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-tl.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds-tr.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-tr.png Binary files differindex 18e542e..18e542e 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds-tr.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds-tr.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds2-b.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds2-b.png Binary files differindex 05a190e..05a190e 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds2-b.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds2-b.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ds2-r.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds2-r.png Binary files differindex 0c3ea4c..0c3ea4c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ds2-r.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ds2-r.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/favicon.ico b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/favicon.ico Binary files differindex 339d09c..339d09c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/favicon.ico +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/favicon.ico diff --git a/Bugs-Everywhere-Web/beweb/static/images/favicon.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/favicon.png Binary files differindex 6dc53ee..6dc53ee 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/favicon.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/favicon.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/half-spiral.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/half-spiral.png Binary files differindex cb4b56c..cb4b56c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/half-spiral.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/half-spiral.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/header_inner.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/header_inner.png Binary files differindex 2b2d87d..2b2d87d 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/header_inner.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/header_inner.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/info.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/info.png Binary files differindex 329c523..329c523 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/info.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/info.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-b.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-b.png Binary files differindex 25d3cfa..25d3cfa 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-b.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-b.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-bl.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-bl.png Binary files differindex f496223..f496223 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-bl.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-bl.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-br.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-br.png Binary files differindex 74cbd91..74cbd91 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-br.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-br.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-l.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-l.png Binary files differindex dd567fa..dd567fa 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-l.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-l.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-r.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-r.png Binary files differindex 9ac4486..9ac4486 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-r.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-r.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-t.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-t.png Binary files differindex fbb06c8..fbb06c8 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-t.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-t.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-tl.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-tl.png Binary files differindex 9336290..9336290 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-tl.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-tl.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/is-tr.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-tr.png Binary files differindex de74808..de74808 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/is-tr.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/is-tr.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/ok.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ok.png Binary files differindex fee6751..fee6751 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/ok.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/ok.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/shadows.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/shadows.png Binary files differindex 9ddc676..9ddc676 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/shadows.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/shadows.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/spiral.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/spiral.png Binary files differindex b4bcb1e..b4bcb1e 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/spiral.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/spiral.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png Binary files differindex bc9c79c..bc9c79c 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/tg_under_the_hood.png diff --git a/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png Binary files differindex 90e84b7..90e84b7 100644 --- a/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/static/images/under_the_hood_blue.png diff --git a/Bugs-Everywhere-Web/beweb/tests/__init__.py b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/__init__.py index e69de29..e69de29 100644 --- a/Bugs-Everywhere-Web/beweb/tests/__init__.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/__init__.py diff --git a/Bugs-Everywhere-Web/beweb/templates/about.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/about.kid index fa3548a..fa3548a 100644 --- a/Bugs-Everywhere-Web/beweb/templates/about.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/about.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/bugs.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/bugs.kid index 198aa94..198aa94 100644 --- a/Bugs-Everywhere-Web/beweb/templates/bugs.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/bugs.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/edit_bug.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/edit_bug.kid index 276f610..276f610 100644 --- a/Bugs-Everywhere-Web/beweb/templates/edit_bug.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/edit_bug.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/edit_comment.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/edit_comment.kid index 2b522d4..2b522d4 100644 --- a/Bugs-Everywhere-Web/beweb/templates/edit_comment.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/edit_comment.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/error.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/error.kid index bc55615..bc55615 100644 --- a/Bugs-Everywhere-Web/beweb/templates/error.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/error.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/login.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/login.kid index e7ad852..e7ad852 100644 --- a/Bugs-Everywhere-Web/beweb/templates/login.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/login.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/master.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/master.kid index 0772524..0772524 100644 --- a/Bugs-Everywhere-Web/beweb/templates/master.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/master.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/projects.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/projects.kid index d5f9fd3..d5f9fd3 100644 --- a/Bugs-Everywhere-Web/beweb/templates/projects.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/projects.kid diff --git a/Bugs-Everywhere-Web/beweb/templates/welcome.kid b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/welcome.kid index 08abd21..08abd21 100644 --- a/Bugs-Everywhere-Web/beweb/templates/welcome.kid +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/templates/welcome.kid diff --git a/interfaces/web/Bugs-Everywhere-Web/beweb/tests/__init__.py b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/__init__.py diff --git a/Bugs-Everywhere-Web/beweb/tests/test_controllers.py b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/test_controllers.py index 0c77afe..0c77afe 100644 --- a/Bugs-Everywhere-Web/beweb/tests/test_controllers.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/test_controllers.py diff --git a/Bugs-Everywhere-Web/beweb/tests/test_model.py b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/test_model.py index 74c4e83..74c4e83 100644 --- a/Bugs-Everywhere-Web/beweb/tests/test_model.py +++ b/interfaces/web/Bugs-Everywhere-Web/beweb/tests/test_model.py diff --git a/Bugs-Everywhere-Web/dev.cfg b/interfaces/web/Bugs-Everywhere-Web/dev.cfg index eda9e6c..eda9e6c 100644 --- a/Bugs-Everywhere-Web/dev.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/dev.cfg diff --git a/interfaces/web/Bugs-Everywhere-Web/libbe b/interfaces/web/Bugs-Everywhere-Web/libbe new file mode 120000 index 0000000..7d18612 --- /dev/null +++ b/interfaces/web/Bugs-Everywhere-Web/libbe @@ -0,0 +1 @@ +../../../libbe
\ No newline at end of file diff --git a/Bugs-Everywhere-Web/prod.cfg b/interfaces/web/Bugs-Everywhere-Web/prod.cfg index c0d4aca..c0d4aca 100644 --- a/Bugs-Everywhere-Web/prod.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/prod.cfg diff --git a/Bugs-Everywhere-Web/sample-prod.cfg b/interfaces/web/Bugs-Everywhere-Web/sample-prod.cfg index d1052f8..d1052f8 100644 --- a/Bugs-Everywhere-Web/sample-prod.cfg +++ b/interfaces/web/Bugs-Everywhere-Web/sample-prod.cfg diff --git a/Bugs-Everywhere-Web/server.log b/interfaces/web/Bugs-Everywhere-Web/server.log index fe02ade..fe02ade 100644 --- a/Bugs-Everywhere-Web/server.log +++ b/interfaces/web/Bugs-Everywhere-Web/server.log diff --git a/Bugs-Everywhere-Web/setup-tables.py b/interfaces/web/Bugs-Everywhere-Web/setup-tables.py index 161d7c7..161d7c7 100644 --- a/Bugs-Everywhere-Web/setup-tables.py +++ b/interfaces/web/Bugs-Everywhere-Web/setup-tables.py diff --git a/Bugs-Everywhere-Web/setup.py b/interfaces/web/Bugs-Everywhere-Web/setup.py index 8ba3da2..8ba3da2 100644 --- a/Bugs-Everywhere-Web/setup.py +++ b/interfaces/web/Bugs-Everywhere-Web/setup.py diff --git a/Bugs-Everywhere-Web/start-beweb.py b/interfaces/web/Bugs-Everywhere-Web/start-beweb.py index 4070abd..4070abd 100755 --- a/Bugs-Everywhere-Web/start-beweb.py +++ b/interfaces/web/Bugs-Everywhere-Web/start-beweb.py diff --git a/interfaces/xml/be-mbox-to-xml b/interfaces/xml/be-mbox-to-xml new file mode 100755 index 0000000..dc6a1c5 --- /dev/null +++ b/interfaces/xml/be-mbox-to-xml @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +""" +Convert an mbox into xml suitable for imput into be. + $ cat mbox | be-mbox-to-xml | be comment --xml <ID> - +mbox is a flat-file format, consisting of a series of messages. +Messages begin with a a From_ line, followed by RFC 822 email, +followed by a blank line. +""" + +import base64 +import email.utils +from libbe.encoding import get_encoding, set_IO_stream_encodings +from mailbox import mbox, Message # the mailbox people really want an on-disk copy +from time import asctime, gmtime +import types +from xml.sax.saxutils import escape + +DEFAULT_ENCODING = get_encoding() +set_IO_stream_encodings(DEFAULT_ENCODING) + +KNOWN_IDS = [] + +def comment_message_to_xml(message, fields=None): + if fields == None: + fields = {} + new_fields = {} + new_fields[u'alt-id'] = message[u'message-id'] + new_fields[u'in-reply-to'] = message[u'in-reply-to'] + new_fields[u'author'] = message[u'from'] + new_fields[u'date'] = message[u'date'] + new_fields[u'content-type'] = message.get_content_type() + for k,v in new_fields.items(): + if v != None and type(v) != types.UnicodeType: + fields[k] = unicode(v, encoding=DEFAULT_ENCODING) + elif v == None and k in fields: + new_fields[k] = fields[k] + for k,v in fields.items(): + if k not in new_fields: + new_fields.k = fields[k] + fields = new_fields + + if fields[u'in-reply-to'] == None: + if message[u'references'] != None: + refs = message[u'references'].split() + for ref in refs: # search for a known reference id. + if ref in KNOWN_IDS: + fields[u'in-reply-to'] = ref + break + if fields[u'in-reply-to'] == None and len(refs) > 0: + fields[u'in-reply-to'] = refs[0] # default to the first + else: # check for mutliple in-reply-to references. + refs = fields[u'in-reply-to'].split() + for ref in refs: # search for a known reference id. + if ref in KNOWN_IDS: + fields[u'in-reply-to'] = ref + break + if fields[u'in-reply-to'] == None and len(refs) > 0: + fields[u'in-reply-to'] = refs[0] # default to the first + + if fields['alt-id'] != None: + KNOWN_IDS.append(fields['alt-id']) + + if message.is_multipart(): + ret = [] + alt_id = fields[u'alt-id'] + from_str = fields[u'author'] + date = fields[u'date'] + for m in message.walk(): + if m == message: + continue + fields[u'author'] = from_str + fields[u'date'] = date + if len(ret) > 0: # we've added one part already + fields.pop(u'alt-id') # don't pass alt-id to other parts + fields[u'in-reply-to'] = alt_id # others respond to first + ret.append(comment_message_to_xml(m, fields)) + return u'\n'.join(ret) + + charset = message.get_content_charset(DEFAULT_ENCODING).lower() + #assert charset == DEFAULT_ENCODING.lower(), \ + # u"Unknown charset: %s" % charset + + if message[u'content-transfer-encoding'] == None: + encoding = DEFAULT_ENCODING + else: + encoding = message[u'content-transfer-encoding'].lower() + body = message.get_payload(decode=True) # attempt to decode + assert body != None, "Unable to decode?" + if fields[u'content-type'].startswith(u"text/"): + body = unicode(body, encoding=charset).rstrip(u'\n') + else: + body = base64.encode(body) + fields[u'body'] = body + lines = [u"<comment>"] + for tag,body in fields.items(): + if body != None: + ebody = escape(body) + lines.append(u" <%s>%s</%s>" % (tag, ebody, tag)) + lines.append(u"</comment>") + return u'\n'.join(lines) + +def main(mbox_filename): + mb = mbox(mbox_filename) + print u'<?xml version="1.0" encoding="%s" ?>' % DEFAULT_ENCODING + print u"<comment-list>" + for message in mb: + print comment_message_to_xml(message) + print u"</comment-list>" + + +if __name__ == "__main__": + import sys + main(sys.argv[1]) diff --git a/xml/be-xml-to-mbox b/interfaces/xml/be-xml-to-mbox index 80db634..c630447 100755 --- a/xml/be-xml-to-mbox +++ b/interfaces/xml/be-xml-to-mbox @@ -2,19 +2,19 @@ # Copyright (C) 2009 Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Convert xml output of `be list --xml` into mbox format for browsing with a mail reader. For example @@ -26,14 +26,16 @@ followed by a blank line. """ #from mailbox import mbox, Message # the mailbox people really want an on-disk copy +import codecs import email.utils -import types - from libbe.encoding import get_encoding, set_IO_stream_encodings from libbe.utility import str_to_time as rfc2822_to_gmtime_integer from time import asctime, gmtime -from xml.sax import make_parser -from xml.sax.handler import ContentHandler +import types +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree from xml.sax.saxutils import unescape @@ -83,7 +85,7 @@ class Bug (LimitedAttrDict): u"created", u"summary", u"comments", - u"extra_strings"] + u"extra-strings"] def print_to_mbox(self): name,addr = email.utils.parseaddr(self["creator"]) print "From %s %s" % (addr, rfc2822_to_asctime(self["created"])) @@ -96,28 +98,62 @@ class Bug (LimitedAttrDict): print "" print self["summary"] print "" - if len(self["extra_strings"]) > 0: + if "extra-strings" in self: print "extra strings:\n ", print '\n '.join(self["extra_strings"]) print "" - for comment in self["comments"]: - comment.print_to_mbox(self) + if "comments" in self: + for comment in self["comments"]: + comment.print_to_mbox(self) + def init_from_etree(self, element): + assert element.tag == "bug", element.tag + for field in element.getchildren(): + text = unescape(unicode(field.text).decode("unicode_escape").strip()) + if field.tag == "comment": + comm = Comment() + comm.init_from_etree(field) + if "comments" in self: + self["comments"].append(comm) + else: + self["comments"] = [comm] + elif field.tag == "extra-string": + if "extra-strings" in self: + self["extra-strings"].append(text) + else: + self["extra-strings"] = [text] + else: + self[field.tag] = text class Comment (LimitedAttrDict): _attrs = [u"uuid", + u"alt-id", u"short-name", u"in-reply-to", - u"from", + u"author", u"date", u"content-type", u"body"] - def print_to_mbox(self, bug): - name,addr = email.utils.parseaddr(self["from"]) + def print_to_mbox(self, bug=None): + if bug == None: + bug = Bug() + bug[u"uuid"] = u"no-uuid" + name,addr = email.utils.parseaddr(self["author"]) print "From %s %s" % (addr, rfc2822_to_asctime(self["date"])) - print "Message-ID: <%s@%s>" % (self["uuid"], DEFAULT_DOMAIN) + if "uuid" in self: id = self["uuid"] + elif "alt-id" in self: id = self["alt-id"] + else: id = None + if id != None: + print "Message-ID: <%s@%s>" % (id, DEFAULT_DOMAIN) print "Date: %s" % self["date"] - print "From: %s" % self["from"] - print "Subject: %s: %s" % (self["short-name"], bug["summary"]) + print "From: %s" % self["author"] + subject = "" + if "short-name" in self: + subject += self["short-name"]+u": " + if "summary" in bug: + subject += bug["summary"] + else: + subject += u"no-subject" + print "Subject: %s" % subject if "in-reply-to" not in self.keys(): self["in-reply-to"] = bug["uuid"] print "In-Reply-To: <%s@%s>" % (self["in-reply-to"], DEFAULT_DOMAIN) @@ -129,72 +165,41 @@ class Comment (LimitedAttrDict): else: # content type and transfer encoding already in XML MIME output print self["body"] print "" + def init_from_etree(self, element): + assert element.tag == "comment", element.tag + for field in element.getchildren(): + text = unescape(unicode(field.text).decode("unicode_escape").strip()) + if field.tag == "body": + text+="\n" + self[field.tag] = text -class BE_list_handler (ContentHandler): - def __init__(self): - self.reset() - - def reset(self): - self.bug = None - self.comment = None - self.extra_strings = None - self.text_field = None - - def startElement(self, name, attributes): - if name == "bug": - assert self.bug == None, "Nested bugs?!" - assert self.comment == None - assert self.text_field == None - self.bug = Bug(comments=[], extra_strings=[]) - elif name == "comment": - assert self.bug != None, "<comment> not in <bug>?" - assert self.comment == None, "Nested comments?!" - assert self.text_field == None, "<comment> in text field %s?" % self.text_field - self.comment = Comment() - elif self.bug != None and self.comment == None: - # parse bug text field - self.text_field = name - self.text_data = "" - elif self.bug != None and self.comment != None: - # parse comment text field - self.text_field = name - self.text_data = "" - - def endElement(self, name): - if name == "bug": - assert self.bug != None, "Invalid XML?" - assert self.comment == None, "Invalid XML?" - assert self.text_field == None, "Invalid XML?" - self.bug.print_to_mbox() - self.bug = None - elif name == "comment": - assert self.bug != None, "<comment> not in <bug>?" - assert self.comment != None, "Invalid XML?" - assert self.text_field == None, "<comment> in text field %s?" % self.text_field - self.bug["comments"].append(self.comment) - # comments printed by bug.print_to_mbox() - self.comment = None - elif self.bug != None and self.comment == None: - # parse bug text field - if self.text_field == "extra-string": - self.bug["extra_strings"].append(unescape(self.text_data.strip())) - else: - self.bug[self.text_field] = unescape(self.text_data.strip()) - self.text_field = None - self.text_data = None - elif self.bug != None and self.comment != None: - # parse comment text field - self.comment[self.text_field] = unescape(self.text_data.strip()) - self.text_field = None - self.text_data = None - - def characters(self, data): - if self.text_field != None: - self.text_data += data +def print_to_mbox(element): + if element.tag == "bug": + b = Bug() + b.init_from_etree(element) + b.print_to_mbox() + elif element.tag == "comment": + c = Comment() + c.init_from_etree(element) + c.print_to_mbox() + elif element.tag in ["bugs", "bug-list"]: + for b_elt in element.getchildren(): + b = Bug() + b.init_from_etree(b_elt) + b.print_to_mbox() + elif element.tag in ["comments", "comment-list"]: + for c_elt in element.getchildren(): + c = Comment() + c.init_from_etree(c_elt) + c.print_to_mbox() if __name__ == "__main__": import sys - - parser = make_parser() - parser.setContentHandler(BE_list_handler()) - parser.parse(sys.stdin) + + if len(sys.argv) == 1: # no filename given, use stdin + xml_unicode = sys.stdin.read() + else: + xml_unicode = codecs.open(sys.argv[1], "r", DEFAULT_ENCODING).read() + xml_str = xml_unicode.encode("unicode_escape").replace(r"\n", "\n") + elist = ElementTree.XML(xml_str) + print_to_mbox(elist) diff --git a/libbe/arch.py b/libbe/arch.py index 3051b34..63e39e3 100644 --- a/libbe/arch.py +++ b/libbe/arch.py @@ -2,21 +2,20 @@ # Ben Finney <ben+python@benfinney.id.au> # James Rowe <jnrowe@ukfsn.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import codecs import os @@ -63,7 +62,11 @@ class Arch(RCS): self._create_project(path) self._add_project_code(path) def _create_archive(self, path): - # Create a new archive + """ + Create a temporary Arch archive in the directory PATH. This + archive will be removed by + __del__->cleanup->_rcs_cleanup->_remove_archive + """ # http://regexps.srparish.net/tutorial-tla/new-archive.html#Creating_a_New_Archive assert self._archive_name == None id = self.get_user_id() @@ -177,7 +180,6 @@ class Arch(RCS): self._get_archive_project_name(root) return root - def _get_archive_name(self, root): status,output,error = self._u_invoke_client("archives") lines = output.split('\n') @@ -189,7 +191,6 @@ class Arch(RCS): if os.path.realpath(location) == os.path.realpath(root): self._archive_name = archive assert self._archive_name != None - def _get_archive_project_name(self, root): # get project names status,output,error = self._u_invoke_client("tree-version", directory=root) @@ -261,16 +262,17 @@ class Arch(RCS): else: status,output,error = \ self._u_invoke_client("get", revision,directory) - def _rcs_commit(self, commitfile): + def _rcs_commit(self, commitfile, allow_empty=False): + if allow_empty == False: + # arch applies empty commits without complaining, so check first + status,output,error = self._u_invoke_client("changes",expect=(0,1)) + if status == 0: + raise rcs.EmptyCommit() summary,body = self._u_parse_commitfile(commitfile) - #status,output,error = self._invoke_client("make-log") - if body == None: - status,output,error \ - = self._u_invoke_client("commit","--summary",summary) - else: - status,output,error \ - = self._u_invoke_client("commit","--summary",summary, - "--log-message",body) + args = ["commit", "--summary", summary] + if body != None: + args.extend(["--log-message",body]) + status,output,error = self._u_invoke_client(*args) revision = None revline = re.compile("[*] committed (.*)") match = revline.search(output) @@ -281,6 +283,16 @@ class Arch(RCS): assert revpath.startswith(self._archive_project_name()+'--') revision = revpath[len(self._archive_project_name()+'--'):] return revpath + def _rcs_revision_id(self, index): + status,output,error = self._u_invoke_client("logs") + logs = output.splitlines() + first_log = logs.pop(0) + assert first_log == "base-0", first_log + try: + log = logs[index] + except IndexError: + return None + return "%s--%s" % (self._archive_project_name(), log) class CantAddFile(Exception): def __init__(self, file): diff --git a/libbe/beuuid.py b/libbe/beuuid.py index de67cb7..bc47208 100644 --- a/libbe/beuuid.py +++ b/libbe/beuuid.py @@ -1,19 +1,18 @@ # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ Backwards compatibility support for Python 2.4. Once people give up on 2.4 ;), the uuid call should be merged into bugdir.py diff --git a/libbe/bug.py b/libbe/bug.py index dfa49f2..7554318 100644 --- a/libbe/bug.py +++ b/libbe/bug.py @@ -1,21 +1,20 @@ # Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org> # Thomas Habets <thomas@habets.pp.se> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import os.path import errno @@ -34,6 +33,11 @@ import comment import utility +class DiskAccessRequired (Exception): + def __init__(self, goal): + msg = "Cannot %s without accessing the disk" % goal + Exception.__init__(self, msg) + ### Define and describe valid bug categories # Use a tuple of (category, description) tuples since we don't have # ordered dicts in Python yet http://www.python.org/dev/peps/pep-0372/ @@ -68,7 +72,7 @@ def load_severities(severity_def): global severity_values global severity_description global severity_index - if severity_def == settings_object.EMPTY: + if severity_def == None: return severity_values = tuple([val for val,description in severity_def]) severity_description = dict(severity_def) @@ -88,9 +92,9 @@ def load_status(active_status_def, inactive_status_def): global status_values global status_description global status_index - if active_status_def == settings_object.EMPTY: + if active_status_def == None: active_status_def = globals()["active_status_def"] - if inactive_status_def == settings_object.EMPTY: + if inactive_status_def == None: inactive_status_def = globals()["inactive_status_def"] active_status_values = tuple([val for val,description in active_status_def]) inactive_status_values = tuple([val for val,description in inactive_status_def]) @@ -178,7 +182,7 @@ class Bug(settings_object.SavedSettingsObject): def time_string(): return {} def _get_time(self): - if self.time_string == None or self.time_string == settings_object.EMPTY: + if self.time_string == None: return None return utility.str_to_time(self.time_string) def _set_time(self, value): @@ -188,15 +192,8 @@ class Bug(settings_object.SavedSettingsObject): doc="An integer version of .time_string") def _extra_strings_check_fn(value): - "Require an iterable full of strings" - if value == settings_object.EMPTY: - return True - elif not hasattr(value, "__iter__"): - return False - for x in value: - if type(x) not in types.StringTypes: - return False - return True + return utility.iterable_full_of_strings(value, \ + alternative=settings_object.EMPTY) def _extra_strings_change_hook(self, old, new): self.extra_strings.sort() # to make merging easier self._prop_save_settings(old, new) @@ -253,12 +250,19 @@ class Bug(settings_object.SavedSettingsObject): def __repr__(self): return "Bug(uuid=%r)" % self.uuid + def __str__(self): + return self.string(shortlist=True) + + def __cmp__(self, other): + return cmp_full(self, other) + + # serializing methods + def _setting_attr_string(self, setting): value = getattr(self, setting) - if value == settings_object.EMPTY: + if value == None: return "" - else: - return str(value) + return str(value) def xml(self, show_comments=False): if self.bugdir == None: @@ -283,7 +287,7 @@ class Bug(settings_object.SavedSettingsObject): ("summary", self.summary)] ret = '<bug>\n' for (k,v) in info: - if v is not settings_object.EMPTY: + if v is not None: ret += ' <%s>%s</%s>\n' % (k,xml.sax.saxutils.escape(v),k) for estr in self.extra_strings: ret += ' <extra-string>%s</extra-string>\n' % estr @@ -335,11 +339,7 @@ class Bug(settings_object.SavedSettingsObject): output = bugout return output - def __str__(self): - return self.string(shortlist=True) - - def __cmp__(self, other): - return cmp_full(self, other) + # methods for saving/loading/acessing settings and properties. def get_path(self, name=None): my_dir = os.path.join(self.bugdir.get_path("bugs"), self.uuid) @@ -348,11 +348,47 @@ class Bug(settings_object.SavedSettingsObject): assert name in ["values", "comments"] return os.path.join(my_dir, name) + def set_sync_with_disk(self, value): + self.sync_with_disk = value + for comment in self.comments(): + comment.set_sync_with_disk(value) + def load_settings(self): + if self.sync_with_disk == False: + raise DiskAccessRequired("load settings") self.settings = mapfile.map_load(self.rcs, self.get_path("values")) self._setup_saved_settings() + def save_settings(self): + if self.sync_with_disk == False: + raise DiskAccessRequired("save settings") + assert self.summary != None, "Can't save blank bug" + self.rcs.mkdir(self.get_path()) + path = self.get_path("values") + mapfile.map_save(self.rcs, path, self._get_saved_settings()) + + def save(self): + """ + Save any loaded contents to disk. Because of lazy loading of + comments, this is actually not too inefficient. + + However, if self.sync_with_disk = True, then any changes are + automatically written to disk as soon as they happen, so + calling this method will just waste time (unless something + else has been messing with your on-disk files). + """ + sync_with_disk = self.sync_with_disk + if sync_with_disk == False: + self.set_sync_with_disk(True) + self.save_settings() + if len(self.comment_root) > 0: + comment.saveComments(self) + if sync_with_disk == False: + self.set_sync_with_disk(False) + def load_comments(self, load_full=True): + if self.sync_with_disk == False: + raise DiskAccessRequired("load comments") if load_full == True: # Force a complete load of the whole comment tree self.comment_root = self._get_comment_root(load_full=True) @@ -365,25 +401,15 @@ class Bug(settings_object.SavedSettingsObject): self.comment_root = None self.sync_with_disk = True - def save_settings(self): - assert self.summary != None, "Can't save blank bug" - - self.rcs.mkdir(self.get_path()) - path = self.get_path("values") - mapfile.map_save(self.rcs, path, self._get_saved_settings()) - - def save(self): - self.save_settings() - - if len(self.comment_root) > 0: - self.rcs.mkdir(self.get_path("comments")) - comment.saveComments(self) - def remove(self): + if self.sync_with_disk == False: + raise DiskAccessRequired("remove") self.comment_root.remove() path = self.get_path() self.rcs.recursive_remove(path) + # methods for managing comments + def comments(self): for comment in self.comment_root.traverse(): yield comment @@ -477,8 +503,8 @@ def cmp_attr(bug_1, bug_2, attr, invert=False): return 1 val_1 = getattr(bug_1, attr) val_2 = getattr(bug_2, attr) - if val_1 == settings_object.EMPTY: val_1 = None - if val_2 == settings_object.EMPTY: val_2 = None + if val_1 == None: val_1 = None + if val_2 == None: val_2 = None if invert == True : return -cmp(val_1, val_2) @@ -486,13 +512,35 @@ def cmp_attr(bug_1, bug_2, attr, invert=False): return cmp(val_1, val_2) # alphabetical rankings (a < z) +cmp_uuid = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "uuid") cmp_creator = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "creator") cmp_assigned = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "assigned") +cmp_target = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "target") +cmp_reporter = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "reporter") +cmp_summary = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "summary") # chronological rankings (newer < older) cmp_time = lambda bug_1, bug_2 : cmp_attr(bug_1, bug_2, "time", invert=True) +def cmp_comments(bug_1, bug_2): + """ + Compare two bugs' comments lists. Doesn't load any new comments, + so you should call each bug's .load_comments() first if you want a + full comparison. + """ + comms_1 = sorted(bug_1.comments(), key = lambda comm : comm.uuid) + comms_2 = sorted(bug_2.comments(), key = lambda comm : comm.uuid) + result = cmp(len(comms_1), len(comms_2)) + if result != 0: + return result + for c_1,c_2 in zip(comms_1, comms_2): + result = cmp(c_1, c_2) + if result != 0: + return result + return 0 + DEFAULT_CMP_FULL_CMP_LIST = \ - (cmp_status,cmp_severity,cmp_assigned,cmp_time,cmp_creator) + (cmp_status, cmp_severity, cmp_assigned, cmp_time, cmp_creator, + cmp_reporter, cmp_target, cmp_comments, cmp_summary, cmp_uuid) class BugCompoundComparator (object): def __init__(self, cmp_list=DEFAULT_CMP_FULL_CMP_LIST): diff --git a/libbe/bugdir.py b/libbe/bugdir.py index d4a39cb..0eb4a6c 100644 --- a/libbe/bugdir.py +++ b/libbe/bugdir.py @@ -3,26 +3,26 @@ # Chris Ball <cjb@laptop.org> # Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import copy +import errno import os import os.path -import errno +import sys import time -import copy import unittest import doctest @@ -52,7 +52,7 @@ class NoRootEntry(Exception): class AlreadyInitialized(Exception): def __init__(self, path): self.path = path - Exception.__init__(self, + Exception.__init__(self, "Specified root is already initialized: %s" % path) class MultipleBugMatches(ValueError): @@ -63,6 +63,17 @@ class MultipleBugMatches(ValueError): self.shortname = shortname self.matches = matches +class NoBugMatches(KeyError): + def __init__(self, shortname): + msg = "No bug matches %s" % shortname + KeyError.__init__(self, msg) + self.shortname = shortname + +class DiskAccessRequired (Exception): + def __init__(self, goal): + msg = "Cannot %s without accessing the disk" % goal + Exception.__init__(self, msg) + TREE_VERSION_STRING = "Bugs Everywhere Tree 1 0\n" @@ -71,7 +82,7 @@ class BugDir (list, settings_object.SavedSettingsObject): """ Sink to existing root ====================== - + Consider the following usage case: You have a bug directory rooted in /path/to/source @@ -86,23 +97,35 @@ class BugDir (list, settings_object.SavedSettingsObject): /path/to/source/GUI/.be miss /path/to/source/.be hit! So it still roots itself appropriately without much work for you. - + File-system access ================== - - When rooted in non-bugdir directory, BugDirs live completely in - memory until the first call to .save(). This creates a '.be' - sub-directory containing configurations options, bugs, comments, - etc. Once this sub-directory has been created (possibly by - another BugDir instance) any changes to the BugDir in memory will - be flushed to the file system automatically. However, the BugDir - will only load information from the file system when it loads new - bugs/comments that it doesn't already have in memory, or when it - explicitly asked to do so (e.g. .load() or __init__(from_disk=True)). - + + BugDirs live completely in memory when .sync_with_disk is False. + This is the default configuration setup by BugDir(from_disk=False). + If .sync_with_disk == True (e.g. BugDir(from_disk=True)), then + any changes to the BugDir will be immediately written to disk. + + If you want to change .sync_with_disk, we suggest you use + .set_sync_with_disk(), which propogates the new setting through to + all bugs/comments/etc. that have been loaded into memory. If + you've been living in memory and want to move to + .sync_with_disk==True, but you're not sure if anything has been + changed in memory, a call to save() immediately before the + .set_sync_with_disk(True) call is a safe move. + + Regardless of .sync_with_disk, a call to .save() will write out + all the contents that the BugDir instance has loaded into memory. + If sync_with_disk has been True over the course of all interesting + changes, this .save() call will be a waste of time. + + The BugDir will only load information from the file system when it + loads new settings/bugs/comments that it doesn't already have in + memory and .sync_with_disk == True. + Allow RCS initialization ======================== - + This one is for testing purposes. Setting it to True allows the BugDir to search for an installed RCS backend and initialize it in the root directory. This is a convenience option for supporting @@ -110,7 +133,7 @@ class BugDir (list, settings_object.SavedSettingsObject): Disable encoding manipulation ============================= - + This one is for testing purposed. You might have non-ASCII Unicode in your bugs, comments, files, etc. BugDir instances try and support your preferred encoding scheme (e.g. "utf-8") when @@ -136,16 +159,17 @@ class BugDir (list, settings_object.SavedSettingsObject): return settings_object.versioned_property(**kwargs) @_versioned_property(name="target", - doc="The current project development target") + doc="The current project development target.") def target(): return {} def _guess_encoding(self): return encoding.get_encoding() def _check_encoding(value): - if value != None and value != settings_object.EMPTY: + if value != None: return encoding.known_encoding(value) def _setup_encoding(self, new_encoding): - if new_encoding != None and new_encoding != settings_object.EMPTY: + # change hook called before generator. + if new_encoding not in [None, settings_object.EMPTY]: if self._manipulate_encodings == True: encoding.set_IO_stream_encodings(new_encoding) def _set_encoding(self, old_encoding, new_encoding): @@ -160,7 +184,7 @@ class BugDir (list, settings_object.SavedSettingsObject): def encoding(): return {} def _setup_user_id(self, user_id): - self.rcs.user_id = user_id + self.rcs.user_id = user_id def _guess_user_id(self): return self.rcs.get_user_id() def _set_user_id(self, old_user_id, new_user_id): @@ -215,7 +239,21 @@ settings easy. Don't set this attribute. Set .rcs instead, and if uuid not in map: map[uuid] = None self._bug_map_value = map # ._bug_map_value used by @local_property - + + def _extra_strings_check_fn(value): + return utility.iterable_full_of_strings(value, \ + alternative=settings_object.EMPTY) + def _extra_strings_change_hook(self, old, new): + self.extra_strings.sort() # to make merging easier + self._prop_save_settings(old, new) + @_versioned_property(name="extra_strings", + doc="Space for an array of extra strings. Useful for storing state for functionality implemented purely in becommands/<some_function>.py.", + default=[], + check_fn=_extra_strings_check_fn, + change_hook=_extra_strings_change_hook, + mutable=True) + def extra_strings(): return {} + @Property @primed_property(primer=_bug_map_gen) @local_property("bug_map") @@ -223,7 +261,7 @@ settings easy. Don't set this attribute. Set .rcs instead, and def _bug_map(): return {} def _setup_severities(self, severities): - if severities != None and severities != settings_object.EMPTY: + if severities not in [None, settings_object.EMPTY]: bug.load_severities(severities) def _set_severities(self, old_severities, new_severities): self._setup_severities(new_severities) @@ -254,8 +292,7 @@ settings easy. Don't set this attribute. Set .rcs instead, and def __init__(self, root=None, sink_to_existing_root=True, assert_new_BugDir=False, allow_rcs_init=False, - manipulate_encodings=True, - from_disk=False, rcs=None): + manipulate_encodings=True, from_disk=False, rcs=None): list.__init__(self) settings_object.SavedSettingsObject.__init__(self) self._manipulate_encodings = manipulate_encodings @@ -270,7 +307,7 @@ settings easy. Don't set this attribute. Set .rcs instead, and # get a temporary rcs until we've loaded settings self.sync_with_disk = False self.rcs = self._guess_rcs() - + if from_disk == True: self.sync_with_disk = True self.load() @@ -284,10 +321,19 @@ settings easy. Don't set this attribute. Set .rcs instead, and self.rcs = rcs self._setup_user_id(self.user_id) + def __del__(self): + self.cleanup() + + def cleanup(self): + self.rcs.cleanup() + + # methods for getting the BugDir situated in the filesystem + def _find_root(self, path): """ Search for an existing bug database dir and it's ancestors and - return a BugDir rooted there. + return a BugDir rooted there. Only called by __init__, and + then only if sink_to_existing_root == True. """ if not os.path.exists(path): raise NoRootEntry(path) @@ -302,8 +348,78 @@ settings easy. Don't set this attribute. Set .rcs instead, and if beroot == None: raise NoBugDir(path) return beroot - + + def _guess_rcs(self, allow_rcs_init=False): + """ + Only called by __init__. + """ + deepdir = self.get_path() + if not os.path.exists(deepdir): + deepdir = os.path.dirname(deepdir) + new_rcs = rcs.detect_rcs(deepdir) + install = False + if new_rcs.name == "None": + if allow_rcs_init == True: + new_rcs = rcs.installed_rcs() + new_rcs.init(self.root) + return new_rcs + + # methods for saving/loading/accessing settings and properties. + + def get_path(self, *args): + """ + Return a path relative to .root. + """ + my_dir = os.path.join(self.root, ".be") + if len(args) == 0: + return my_dir + assert args[0] in ["version", "settings", "bugs"], str(args) + return os.path.join(my_dir, *args) + + def _get_settings(self, settings_path, for_duplicate_bugdir=False): + allow_no_rcs = not self.rcs.path_in_root(settings_path) + if allow_no_rcs == True: + assert for_duplicate_bugdir == True + if self.sync_with_disk == False and for_duplicate_bugdir == False: + # duplicates can ignore this bugdir's .sync_with_disk status + raise DiskAccessRequired("_get settings") + try: + settings = mapfile.map_load(self.rcs, settings_path, allow_no_rcs) + except rcs.NoSuchFile: + settings = {"rcs_name": "None"} + return settings + + def _save_settings(self, settings_path, settings, + for_duplicate_bugdir=False): + allow_no_rcs = not self.rcs.path_in_root(settings_path) + if allow_no_rcs == True: + assert for_duplicate_bugdir == True + if self.sync_with_disk == False and for_duplicate_bugdir == False: + # duplicates can ignore this bugdir's .sync_with_disk status + raise DiskAccessRequired("_save settings") + self.rcs.mkdir(self.get_path(), allow_no_rcs) + mapfile.map_save(self.rcs, settings_path, settings, allow_no_rcs) + + def load_settings(self): + self.settings = self._get_settings(self.get_path("settings")) + self._setup_saved_settings() + self._setup_user_id(self.user_id) + self._setup_encoding(self.encoding) + self._setup_severities(self.severities) + self._setup_status(self.active_status, self.inactive_status) + self.rcs = rcs.rcs_by_name(self.rcs_name) + self._setup_user_id(self.user_id) + + def save_settings(self): + settings = self._get_saved_settings() + self._save_settings(self.get_path("settings"), settings) + def get_version(self, path=None, use_none_rcs=False): + """ + Requires disk access. + """ + if self.sync_with_disk == False: + raise DiskAccessRequired("get version") if use_none_rcs == True: RCS = rcs.rcs_by_name("None") RCS.root(self.root) @@ -317,29 +433,31 @@ settings easy. Don't set this attribute. Set .rcs instead, and return tree_version def set_version(self): + """ + Requires disk access. + """ + if self.sync_with_disk == False: + raise DiskAccessRequired("set version") + self.rcs.mkdir(self.get_path()) self.rcs.set_file_contents(self.get_path("version"), TREE_VERSION_STRING) - def get_path(self, *args): - my_dir = os.path.join(self.root, ".be") - if len(args) == 0: - return my_dir - assert args[0] in ["version", "settings", "bugs"], str(args) - return os.path.join(my_dir, *args) + # methods controlling disk access - def _guess_rcs(self, allow_rcs_init=False): - deepdir = self.get_path() - if not os.path.exists(deepdir): - deepdir = os.path.dirname(deepdir) - new_rcs = rcs.detect_rcs(deepdir) - install = False - if new_rcs.name == "None": - if allow_rcs_init == True: - new_rcs = rcs.installed_rcs() - new_rcs.init(self.root) - return new_rcs + def set_sync_with_disk(self, value): + """ + Adjust .sync_with_disk for the BugDir and all it's children. + See the BugDir docstring for a description of the role of + .sync_with_disk. + """ + self.sync_with_disk = value + for bug in self: + bug.set_sync_with_disk(value) def load(self): + """ + Reqires disk access + """ version = self.get_version(use_none_rcs=True) if version != TREE_VERSION_STRING: raise NotImplementedError, \ @@ -348,52 +466,44 @@ settings easy. Don't set this attribute. Set .rcs instead, and if not os.path.exists(self.get_path()): raise NoBugDir(self.get_path()) self.load_settings() - - self.rcs = rcs.rcs_by_name(self.rcs_name) - self._setup_user_id(self.user_id) def load_all_bugs(self): - "Warning: this could take a while." + """ + Requires disk access. + Warning: this could take a while. + """ + if self.sync_with_disk == False: + raise DiskAccessRequired("load all bugs") self._clear_bugs() for uuid in self.list_uuids(): self._load_bug(uuid) def save(self): - self.rcs.mkdir(self.get_path()) + """ + Note that this command writes to disk _regardless_ of the + status of .sync_with_disk. + + Save any loaded contents to disk. Because of lazy loading of + bugs and comments, this is actually not too inefficient. + + However, if .sync_with_disk = True, then any changes are + automatically written to disk as soon as they happen, so + calling this method will just waste time (unless something + else has been messing with your on-disk files). + + Requires disk access. + """ + sync_with_disk = self.sync_with_disk + if sync_with_disk == False: + self.set_sync_with_disk(True) self.set_version() self.save_settings() - self.rcs.mkdir(self.get_path("bugs")) for bug in self: bug.save() + if sync_with_disk == False: + self.set_sync_with_disk(sync_with_disk) - def load_settings(self): - self.settings = self._get_settings(self.get_path("settings")) - self._setup_saved_settings() - self._setup_user_id(self.user_id) - self._setup_encoding(self.encoding) - self._setup_severities(self.severities) - self._setup_status(self.active_status, self.inactive_status) - - def _get_settings(self, settings_path): - allow_no_rcs = not self.rcs.path_in_root(settings_path) - # allow_no_rcs=True should only be for the special case of - # configuring duplicate bugdir settings - - try: - settings = mapfile.map_load(self.rcs, settings_path, allow_no_rcs) - except rcs.NoSuchFile: - settings = {"rcs_name": "None"} - return settings - - def save_settings(self): - settings = self._get_saved_settings() - self._save_settings(self.get_path("settings"), settings) - - def _save_settings(self, settings_path, settings): - allow_no_rcs = not self.rcs.path_in_root(settings_path) - # allow_no_rcs=True should only be for the special case of - # configuring duplicate bugdir settings - mapfile.map_save(self.rcs, settings_path, settings, allow_no_rcs) + # methods for managing duplicate BugDirs def duplicate_bugdir(self, revision): duplicate_path = self.rcs.duplicate_repo(revision) @@ -402,23 +512,27 @@ settings easy. Don't set this attribute. Set .rcs instead, and # initialized for versioning duplicate_settings_path = os.path.join(duplicate_path, ".be", "settings") - duplicate_settings = self._get_settings(duplicate_settings_path) + duplicate_settings = self._get_settings(duplicate_settings_path, + for_duplicate_bugdir=True) if "rcs_name" in duplicate_settings: duplicate_settings["rcs_name"] = "None" duplicate_settings["user_id"] = self.user_id if "disabled" in bug.status_values: # Hack to support old versions of BE bugs duplicate_settings["inactive_status"] = self.inactive_status - self._save_settings(duplicate_settings_path, duplicate_settings) + self._save_settings(duplicate_settings_path, duplicate_settings, + for_duplicate_bugdir=True) return BugDir(duplicate_path, from_disk=True, manipulate_encodings=self._manipulate_encodings) def remove_duplicate_bugdir(self): self.rcs.remove_duplicate_repo() + # methods for managing bugs + def list_uuids(self): uuids = [] - if os.path.exists(self.get_path()): + if self.sync_with_disk == True and os.path.exists(self.get_path()): # list the uuids on disk for uuid in os.listdir(self.get_path("bugs")): if not (uuid.startswith('.')): @@ -436,6 +550,8 @@ settings easy. Don't set this attribute. Set .rcs instead, and self._bug_map_gen() def _load_bug(self, uuid): + if self.sync_with_disk == False: + raise DiskAccessRequired("_load bug") bg = bug.Bug(bugdir=self, uuid=uuid, from_disk=True) self.append(bg) self._bug_map_gen() @@ -443,20 +559,24 @@ settings easy. Don't set this attribute. Set .rcs instead, and def new_bug(self, uuid=None, summary=None): bg = bug.Bug(bugdir=self, uuid=uuid, summary=summary) + bg.set_sync_with_disk(self.sync_with_disk) + if bg.sync_with_disk == True: + bg.save() self.append(bg) self._bug_map_gen() return bg def remove_bug(self, bug): self.remove(bug) - bug.remove() + if bug.sync_with_disk == True: + bug.remove() def bug_shortname(self, bug): """ Generate short names from uuids. Picks the minimum number of characters (>=3) from the beginning of the uuid such that the short names are unique. - + Obviously, as the number of bugs in the database grows, these short names will cease to be unique. The complete uuid should be used for long term reference. @@ -471,12 +591,13 @@ settings easy. Don't set this attribute. Set .rcs instead, and def bug_from_shortname(self, shortname): """ - >>> bd = simple_bug_dir() + >>> bd = SimpleBugDir(sync_with_disk=False) >>> bug_a = bd.bug_from_shortname('a') >>> print type(bug_a) <class 'libbe.bug.Bug'> >>> print bug_a a:om: Bug A + >>> bd.cleanup() """ matches = [] self._bug_map_gen() @@ -487,7 +608,7 @@ settings easy. Don't set this attribute. Set .rcs instead, and raise MultipleBugMatches(shortname, matches) if len(matches) == 1: return self.bug_from_uuid(matches[0]) - raise KeyError("No bug matches %s" % shortname) + raise NoBugMatches(shortname) def bug_from_uuid(self, uuid): if not self.has_bug(uuid): @@ -503,43 +624,58 @@ settings easy. Don't set this attribute. Set .rcs instead, and if bug_uuid not in self._bug_map: return False return True - -def simple_bug_dir(): + +class SimpleBugDir (BugDir): """ - For testing - >>> bugdir = simple_bug_dir() - >>> ls = list(bugdir.list_uuids()) - >>> ls.sort() - >>> print ls + For testing. Set sync_with_disk==False for a memory-only bugdir. + >>> bugdir = SimpleBugDir() + >>> uuids = list(bugdir.list_uuids()) + >>> uuids.sort() + >>> print uuids ['a', 'b'] + >>> bugdir.cleanup() """ - dir = utility.Dir() - assert os.path.exists(dir.path) - bugdir = BugDir(dir.path, sink_to_existing_root=False, allow_rcs_init=True, + def __init__(self, sync_with_disk=True): + if sync_with_disk == True: + dir = utility.Dir() + assert os.path.exists(dir.path) + root = dir.path + assert_new_BugDir = True + rcs_init = True + else: + root = "/" + assert_new_BugDir = False + rcs_init = False + BugDir.__init__(self, root, sink_to_existing_root=False, + assert_new_BugDir=assert_new_BugDir, + allow_rcs_init=rcs_init, manipulate_encodings=False) - bugdir._dir_ref = dir # postpone cleanup since dir.__del__() removes dir. - bug_a = bugdir.new_bug("a", summary="Bug A") - bug_a.creator = "John Doe <jdoe@example.com>" - bug_a.time = 0 - bug_b = bugdir.new_bug("b", summary="Bug B") - bug_b.creator = "Jane Doe <jdoe@example.com>" - bug_b.time = 0 - bug_b.status = "closed" - bugdir.save() - return bugdir - + if sync_with_disk == True: # postpone cleanup since dir.__del__() removes dir. + self._dir_ref = dir + bug_a = self.new_bug("a", summary="Bug A") + bug_a.creator = "John Doe <jdoe@example.com>" + bug_a.time = 0 + bug_b = self.new_bug("b", summary="Bug B") + bug_b.creator = "Jane Doe <jdoe@example.com>" + bug_b.time = 0 + bug_b.status = "closed" + if sync_with_disk == True: + self.save() + self.set_sync_with_disk(True) + def cleanup(self): + if hasattr(self, "_dir_ref"): + self._dir_ref.cleanup() + BugDir.cleanup(self) class BugDirTestCase(unittest.TestCase): - def __init__(self, *args, **kwargs): - unittest.TestCase.__init__(self, *args, **kwargs) def setUp(self): self.dir = utility.Dir() self.bugdir = BugDir(self.dir.path, sink_to_existing_root=False, allow_rcs_init=True) self.rcs = self.bugdir.rcs def tearDown(self): - self.rcs.cleanup() + self.bugdir.cleanup() self.dir.cleanup() def fullPath(self, path): return os.path.join(self.dir.path, path) @@ -592,24 +728,29 @@ class BugDirTestCase(unittest.TestCase): self.failUnless(bugA == bugAprime, "%s != %s" % (bugA, bugAprime)) self.bugdir.save() self.versionTest() - def testComments(self): + def testComments(self, sync_with_disk=False): + if sync_with_disk == True: + self.bugdir.set_sync_with_disk(True) self.bugdir.new_bug(uuid="a", summary="Ant") bug = self.bugdir.bug_from_uuid("a") comm = bug.comment_root rep = comm.new_reply("Ants are small.") rep.new_reply("And they have six legs.") - self.bugdir.save() - self.bugdir._clear_bugs() + if sync_with_disk == False: + self.bugdir.save() + self.bugdir.set_sync_with_disk(True) + self.bugdir._clear_bugs() bug = self.bugdir.bug_from_uuid("a") bug.load_comments() + if sync_with_disk == False: + self.bugdir.set_sync_with_disk(False) self.failUnless(len(bug.comment_root)==1, len(bug.comment_root)) for index,comment in enumerate(bug.comments()): if index == 0: repLoaded = comment self.failUnless(repLoaded.uuid == rep.uuid, repLoaded.uuid) - self.failUnless(comment.sync_with_disk == True, + self.failUnless(comment.sync_with_disk == sync_with_disk, comment.sync_with_disk) - #load_settings() self.failUnless(comment.content_type == "text/plain", comment.content_type) self.failUnless(repLoaded.settings["Content-type"]=="text/plain", @@ -623,6 +764,49 @@ class BugDirTestCase(unittest.TestCase): comment.body) else: self.failIf(True, "Invalid comment: %d\n%s" % (index, comment)) + def testSyncedComments(self): + self.testComments(sync_with_disk=True) -unitsuite = unittest.TestLoader().loadTestsFromTestCase(BugDirTestCase) -suite = unittest.TestSuite([unitsuite])#, doctest.DocTestSuite()]) +class SimpleBugDirTestCase (unittest.TestCase): + def setUp(self): + # create a pre-existing bugdir in a temporary directory + self.dir = utility.Dir() + self.original_working_dir = os.getcwd() + os.chdir(self.dir.path) + self.bugdir = BugDir(self.dir.path, sink_to_existing_root=False, + allow_rcs_init=True) + self.bugdir.new_bug("preexisting", summary="Hopefully not imported") + self.bugdir.save() + def tearDown(self): + os.chdir(self.original_working_dir) + self.bugdir.cleanup() + self.dir.cleanup() + def testOnDiskCleanLoad(self): + """SimpleBugDir(sync_with_disk==True) should not import preexisting bugs.""" + bugdir = SimpleBugDir(sync_with_disk=True) + self.failUnless(bugdir.sync_with_disk==True, bugdir.sync_with_disk) + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == ['a', 'b'], uuids) + bugdir._clear_bugs() + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == [], uuids) + bugdir.load_all_bugs() + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == ['a', 'b'], uuids) + bugdir.cleanup() + def testInMemoryCleanLoad(self): + """SimpleBugDir(sync_with_disk==False) should not import preexisting bugs.""" + bugdir = SimpleBugDir(sync_with_disk=False) + self.failUnless(bugdir.sync_with_disk==False, bugdir.sync_with_disk) + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == ['a', 'b'], uuids) + self.failUnlessRaises(DiskAccessRequired, bugdir.load_all_bugs) + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == ['a', 'b'], uuids) + bugdir._clear_bugs() + uuids = sorted([bug.uuid for bug in bugdir]) + self.failUnless(uuids == [], uuids) + bugdir.cleanup() + +unitsuite = unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]) +suite = unittest.TestSuite([unitsuite, doctest.DocTestSuite()]) diff --git a/libbe/bzr.py b/libbe/bzr.py index d73392a..9eb7de6 100644 --- a/libbe/bzr.py +++ b/libbe/bzr.py @@ -2,21 +2,20 @@ # Ben Finney <ben+python@benfinney.id.au> # Marien Zwart <marienz@gentoo.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import re @@ -72,9 +71,21 @@ class Bzr(RCS): else: self._u_invoke_client("branch", "--revision", revision, ".", directory) - def _rcs_commit(self, commitfile): - status,output,error = self._u_invoke_client("commit", "--unchanged", - "--file", commitfile) + def _rcs_commit(self, commitfile, allow_empty=False): + args = ["commit", "--file", commitfile] + if allow_empty == True: + args.append("--unchanged") + status,output,error = self._u_invoke_client(*args) + else: + kwargs = {"expect":(0,3)} + status,output,error = self._u_invoke_client(*args, **kwargs) + if status != 0: + strings = ["ERROR: no changes to commit.", # bzr 1.3.1 + "ERROR: No changes to commit."] # bzr 1.15.1 + if self._u_any_in_string(strings, error) == True: + raise rcs.EmptyCommit() + else: + raise rcs.CommandError(args, status, error) revision = None revline = re.compile("Committed revision (.*)[.]") match = revline.search(error) @@ -82,20 +93,14 @@ class Bzr(RCS): assert len(match.groups()) == 1 revision = match.groups()[0] return revision - def postcommit(self): - try: - self._u_invoke_client('merge') - except rcs.CommandError, e: - if ('No merge branch known or specified' in e.err_str or - 'No merge location known or specified' in e.err_str): - pass - else: - self._u_invoke_client('revert', '--no-backup', - directory=directory) - self._u_invoke_client('resolve', '--all', directory=directory) - raise - if len(self._u_invoke_client('status', directory=directory)[1]) > 0: - self.commit('Merge from upstream') + def _rcs_revision_id(self, index): + status,output,error = self._u_invoke_client("revno") + current_revision = int(output) + if index >= current_revision or index < -current_revision: + return None + if index >= 0: + return str(index+1) # bzr commit 0 is the empty tree. + return str(current_revision+index+1) rcs.make_rcs_testcase_subclasses(Bzr, sys.modules[__name__]) diff --git a/libbe/cmdutil.py b/libbe/cmdutil.py index edc6442..94a6856 100644 --- a/libbe/cmdutil.py +++ b/libbe/cmdutil.py @@ -1,21 +1,21 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # Oleg Romanyshyn <oromanyshyn@panoramicfeedback.com> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import glob import optparse import os from textwrap import TextWrapper @@ -32,10 +32,10 @@ class UserError(Exception): def __init__(self, msg): Exception.__init__(self, msg) -class UserErrorWrap(UserError): - def __init__(self, exception): - UserError.__init__(self, str(exception)) - self.exception = exception +class UnknownCommand(UserError): + def __init__(self, cmd): + Exception.__init__(self, "Unknown command '%s'" % cmd) + self.cmd = cmd class UsageError(Exception): pass @@ -56,24 +56,30 @@ def iter_commands(): def get_command(command_name): """Retrieves the module for a user command - >>> get_command("asdf") - Traceback (most recent call last): - UserError: Unknown command asdf + >>> try: + ... get_command("asdf") + ... except UnknownCommand, e: + ... print e + Unknown command 'asdf' >>> repr(get_command("list")).startswith("<module 'becommands.list' from ") True """ cmd = plugin.get_plugin("becommands", command_name.replace("-", "_")) if cmd is None: - raise UserError("Unknown command %s" % command_name) + raise UnknownCommand(command_name) return cmd -def execute(cmd, args): +def execute(cmd, args, manipulate_encodings=True): enc = encoding.get_encoding() - get_command(cmd).execute([a.decode(enc) for a in args]) - return 0 - -def help(cmd=None): + cmd = get_command(cmd) + ret = cmd.execute([a.decode(enc) for a in args], + manipulate_encodings=manipulate_encodings) + if ret == None: + ret = 0 + return ret + +def help(cmd=None, parser=None): if cmd != None: return get_command(cmd).help() else: @@ -82,17 +88,15 @@ def help(cmd=None): cmdlist.append((name, module.__desc__)) longest_cmd_len = max([len(name) for name,desc in cmdlist]) ret = ["Bugs Everywhere - Distributed bug tracking", - "", - "usage: be [command] [command_options ...] [command_args ...]", - "or: be help", - "or: be help [command]", - "", - "Supported commands"] + "", "Supported commands"] for name, desc in cmdlist: numExtraSpaces = longest_cmd_len-len(name) ret.append("be %s%*s %s" % (name, numExtraSpaces, "", desc)) - - return "\n".join(ret) + ret.extend(["", "Run", " be help [command]", "for more information."]) + longhelp = "\n".join(ret) + if parser == None: + return longhelp + return parser.help_str() + "\n" + longhelp def completions(cmd): parser = get_command(cmd).get_parser() @@ -106,6 +110,13 @@ def raise_get_help(option, opt, value, parser): def raise_get_completions(option, opt, value, parser): print "got completion arg" + if hasattr(parser, "command") and parser.command == "be": + comps = [] + for command, module in iter_commands(): + comps.append(command) + for opt in parser.option_list: + comps.append(opt.get_opt_string()) + raise GetCompletions(comps) raise GetCompletions(completions(sys.argv[1])) class CmdOptionParser(optparse.OptionParser): @@ -141,7 +152,7 @@ def option_value_pairs(options, parser): def default_complete(options, args, parser, bugid_args={}): """ - A dud complete implementation for becommands to that the + A dud complete implementation for becommands so that the --complete argument doesn't cause any problems. Use this until you've set up a command-specific complete function. @@ -149,15 +160,25 @@ def default_complete(options, args, parser, bugid_args={}): arguments taking bug shortnames and the values are functions for filtering, since that's a common enough operation. e.g. for "be open [options] BUGID" - bugid_args = {0: lambda bug : bug.active == False} + bugid_args = {0: lambda bug : bug.active == False} + A positional argument of -1 specifies all remaining arguments + (e.g in the case of "be show BUGID BUGID ..."). """ for option,value in option_value_pairs(options, parser): if value == "--complete": - raise cmdutil.GetCompletions() + raise GetCompletions() + if len(bugid_args.keys()) > 0: + max_pos_arg = max(bugid_args.keys()) + else: + max_pos_arg = -1 for pos,value in enumerate(args): if value == "--complete": + filter = None if pos in bugid_args: filter = bugid_args[pos] + if pos > max_pos_arg and -1 in bugid_args: + filter = bugid_args[-1] + if filter != None: bugshortnames = [] try: bd = bugdir.BugDir(from_disk=True, @@ -170,6 +191,13 @@ def default_complete(options, args, parser, bugid_args={}): raise GetCompletions(bugshortnames) raise GetCompletions() +def complete_path(path): + """List possible path completions for path.""" + comps = glob.glob(path+"*") + glob.glob(path+"/*") + if len(comps) == 1 and os.path.isdir(comps[0]): + comps.extend(glob.glob(comps[0]+"/*")) + return comps + def underlined(instring): """Produces a version of a string that is underlined with '=' @@ -179,6 +207,15 @@ def underlined(instring): return "%s\n%s" % (instring, "="*len(instring)) +def bug_from_shortname(bugdir, shortname): + """ + Exception translation for the command-line interface. + """ + try: + bug = bugdir.bug_from_shortname(shortname) + except (bugdir.MultipleBugMatches, bugdir.NoBugMatches), e: + raise UserError(e.message) + return bug def _test(): import doctest diff --git a/libbe/comment.py b/libbe/comment.py index 9d4f744..b2fc556 100644 --- a/libbe/comment.py +++ b/libbe/comment.py @@ -2,28 +2,31 @@ # Copyright (C) 2008-2009 Chris Ball <cjb@laptop.org> # Thomas Habets <thomas@habets.pp.se> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA -import email.mime.base, email.encoders +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +import base64 import os import os.path +import sys import time +import types +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree import xml.sax.saxutils -import textwrap import doctest from beuuid import uuid_gen @@ -43,16 +46,38 @@ class InvalidShortname(KeyError): self.shortname = shortname self.shortnames = shortnames +class InvalidXML(ValueError): + def __init__(self, element, comment): + msg = "Invalid comment xml: %s\n %s\n" \ + % (comment, ElementTree.tostring(element)) + ValueError.__init__(self, msg) + self.element = element + self.comment = comment + +class MissingReference(ValueError): + def __init__(self, comment): + msg = "Missing reference to %s" % (comment.in_reply_to) + ValueError.__init__(self, msg) + self.reference = comment.in_reply_to + self.comment = comment + +class DiskAccessRequired (Exception): + def __init__(self, goal): + msg = "Cannot %s without accessing the disk" % goal + Exception.__init__(self, msg) INVALID_UUID = "!!~~\n INVALID-UUID \n~~!!" -def _list_to_root(comments, bug): +def list_to_root(comments, bug, root=None, + ignore_missing_references=False): """ - Convert a raw list of comments to single (dummy) root comment. We - use a dummy root comment, because there can be several comment - threads rooted on the same parent bug. To simplify comment - interaction, we condense these threads into a single thread with a - Comment dummy root. + Convert a raw list of comments to single root comment. We use a + dummy root comment by default, because there can be several + comment threads rooted on the same parent bug. To simplify + comment interaction, we condense these threads into a single + thread with a Comment dummy root. Can also be used to append + a list of subcomments to a non-dummy root comment, so long as + all the new comments are descendants of the root comment. No Comment method should use the dummy comment. """ @@ -61,23 +86,42 @@ def _list_to_root(comments, bug): for comment in comments: assert comment.uuid != None uuid_map[comment.uuid] = comment + for comment in comments: + if comment.alt_id != None and comment.alt_id not in uuid_map: + uuid_map[comment.alt_id] = comment + if root == None: + root = Comment(bug, uuid=INVALID_UUID) + else: + uuid_map[root.uuid] = root for comm in comments: + if comm.in_reply_to == INVALID_UUID: + comm.in_reply_to = None rep = comm.in_reply_to - if rep == None or rep == settings_object.EMPTY or rep == bug.uuid: + if rep == None or rep == bug.uuid: root_comments.append(comm) else: parentUUID = comm.in_reply_to - parent = uuid_map[parentUUID] - parent.add_reply(comm) - dummy_root = Comment(bug, uuid=INVALID_UUID) - dummy_root.extend(root_comments) - return dummy_root + try: + parent = uuid_map[parentUUID] + parent.add_reply(comm) + except KeyError, e: + if ignore_missing_references == True: + print >> sys.stderr, \ + "Ignoring missing reference to %s" % parentUUID + comm.in_reply_to = None + root_comments.append(comm) + else: + raise MissingReference(comm) + root.extend(root_comments) + return root def loadComments(bug, load_full=False): """ Set load_full=True when you want to load the comment completely from disk *now*, rather than waiting and lazy loading as required. """ + if bug.sync_with_disk == False: + raise DiskAccessRequired("load comments") path = bug.get_path("comments") if not os.path.isdir(path): return Comment(bug, uuid=INVALID_UUID) @@ -86,15 +130,16 @@ def loadComments(bug, load_full=False): if uuid.startswith('.'): continue comm = Comment(bug, uuid, from_disk=True) + comm.set_sync_with_disk(bug.sync_with_disk) if load_full == True: comm.load_settings() dummy = comm.body # force the body to load comments.append(comm) - return _list_to_root(comments, bug) + return list_to_root(comments, bug) def saveComments(bug): - path = bug.get_path("comments") - bug.rcs.mkdir(path) + if bug.sync_with_disk == False: + raise DiskAccessRequired("save comments") for comment in bug.comment_root.traverse(): comment.save() @@ -122,9 +167,13 @@ class Comment(Tree, settings_object.SavedSettingsObject): kwargs["required_saved_properties"]=required_saved_properties return settings_object.versioned_property(**kwargs) - @_versioned_property(name="From", + @_versioned_property(name="Alt-id", + doc="Alternate ID for linking imported comments. Internally comments are linked (via In-reply-to) to the parent's UUID. However, these UUIDs are generated internally, so Alt-id is provided as a user-controlled linking target.") + def alt_id(): return {} + + @_versioned_property(name="Author", doc="The author of the comment") - def From(): return {} + def author(): return {} @_versioned_property(name="In-reply-to", doc="UUID for parent comment or bug") @@ -138,17 +187,17 @@ class Comment(Tree, settings_object.SavedSettingsObject): @_versioned_property(name="Date", doc="An RFC 2822 timestamp for comment creation") - def time_string(): return {} + def date(): return {} def _get_time(self): - if self.time_string == None: + if self.date == None: return None - return utility.str_to_time(self.time_string) + return utility.str_to_time(self.date) def _set_time(self, value): - self.time_string = utility.time_to_str(value) + self.date = utility.time_to_str(value) time = property(fget=_get_time, fset=_set_time, - doc="An integer version of .time_string") + doc="An integer version of .date") def _get_comment_body(self): if self.rcs != None and self.sync_with_disk == True: @@ -178,6 +227,20 @@ class Comment(Tree, settings_object.SavedSettingsObject): @doc_property(doc="A revision control system instance.") def rcs(): return {} + def _extra_strings_check_fn(value): + return utility.iterable_full_of_strings(value, \ + alternative=settings_object.EMPTY) + def _extra_strings_change_hook(self, old, new): + self.extra_strings.sort() # to make merging easier + self._prop_save_settings(old, new) + @_versioned_property(name="extra_strings", + doc="Space for an array of extra strings. Useful for storing state for functionality implemented purely in becommands/<some_function>.py.", + default=[], + check_fn=_extra_strings_check_fn, + change_hook=_extra_strings_change_hook, + mutable=True) + def extra_strings(): return {} + def __init__(self, bug=None, uuid=None, from_disk=False, in_reply_to=None, body=None): """ @@ -204,10 +267,29 @@ class Comment(Tree, settings_object.SavedSettingsObject): self.uuid = uuid_gen() self.time = int(time.time()) # only save to second precision if self.rcs != None: - self.From = self.rcs.get_user_id() + self.author = self.rcs.get_user_id() self.in_reply_to = in_reply_to self.body = body + def __cmp__(self, other): + return cmp_full(self, other) + + def __str__(self): + """ + >>> comm = Comment(bug=None, body="Some insightful remarks") + >>> comm.uuid = "com-1" + >>> comm.date = "Thu, 20 Nov 2008 15:55:11 +0000" + >>> comm.author = "Jane Doe <jdoe@example.com>" + >>> print comm + --------- Comment --------- + Name: com-1 + From: Jane Doe <jdoe@example.com> + Date: Thu, 20 Nov 2008 15:55:11 +0000 + <BLANKLINE> + Some insightful remarks + """ + return self.string() + def traverse(self, *args, **kwargs): """Avoid working with the possible dummy root comment""" for comment in Tree.traverse(self, *args, **kwargs): @@ -215,23 +297,24 @@ class Comment(Tree, settings_object.SavedSettingsObject): continue yield comment + # serializing methods + def _setting_attr_string(self, setting): value = getattr(self, setting) - if value == settings_object.EMPTY: + if value == None: return "" - else: - return str(value) + return str(value) def xml(self, indent=0, shortname=None): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") >>> comm.uuid = "0123" - >>> comm.time_string = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> comm.date = "Thu, 01 Jan 1970 00:00:00 +0000" >>> print comm.xml(indent=2, shortname="com-1") <comment> <uuid>0123</uuid> <short-name>com-1</short-name> - <from></from> + <author></author> <date>Thu, 01 Jan 1970 00:00:00 +0000</date> <content-type>text/plain</content-type> <body>Some @@ -248,27 +331,88 @@ class Comment(Tree, settings_object.SavedSettingsObject): msg = email.mime.base.MIMEBase(maintype, subtype) msg.set_payload(self.body or "") email.encoders.encode_base64(msg) - body = msg.as_string() + body = base64.encodestring(self.body or "") info = [("uuid", self.uuid), + ("alt-id", self.alt_id), ("short-name", shortname), ("in-reply-to", self.in_reply_to), - ("from", self._setting_attr_string("From")), - ("date", self.time_string), + ("author", self._setting_attr_string("author")), + ("date", self.date), ("content-type", self.content_type), ("body", body)] lines = ["<comment>"] for (k,v) in info: - if v not in [settings_object.EMPTY, None]: + if v != None: lines.append(' <%s>%s</%s>' % (k,xml.sax.saxutils.escape(v),k)) lines.append("</comment>") istring = ' '*indent sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') + def from_xml(self, xml_string, verbose=True): + """ + Note: If alt-id is not given, translates any <uuid> fields to + <alt-id> fields. + >>> commA = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") + >>> commA.uuid = "0123" + >>> commA.date = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> xml = commA.xml(shortname="com-1") + >>> commB = Comment() + >>> commB.from_xml(xml) + >>> attrs=['uuid','alt_id','in_reply_to','author','date','content_type','body'] + >>> for attr in attrs: # doctest: +ELLIPSIS + ... if getattr(commB, attr) != getattr(commA, attr): + ... estr = "Mismatch on %s: '%s' should be '%s'" + ... args = (attr, getattr(commB, attr), getattr(commA, attr)) + ... print estr % args + Mismatch on uuid: '...' should be '0123' + Mismatch on alt_id: '0123' should be 'None' + >>> print commB.alt_id + 0123 + >>> commA.author + >>> commB.author + """ + if type(xml_string) == types.UnicodeType: + xml_string = xml_string.strip().encode("unicode_escape") + comment = ElementTree.XML(xml_string) + if comment.tag != "comment": + raise InvalidXML(comment, "root element must be <comment>") + tags=['uuid','alt-id','in-reply-to','author','date','content-type','body'] + uuid = None + body = None + for child in comment.getchildren(): + if child.tag == "short-name": + pass + elif child.tag in tags: + if child.text == None or len(child.text) == 0: + text = settings_object.EMPTY + else: + text = xml.sax.saxutils.unescape(child.text) + text = unicode(text).decode("unicode_escape").strip() + if child.tag == "uuid": + uuid = text + continue # don't set the bug's uuid tag. + if child.tag == "body": + body = text + continue # don't set the bug's body yet. + else: + attr_name = child.tag.replace('-','_') + setattr(self, attr_name, text) + elif verbose == True: + print >> sys.stderr, "Ignoring unknown tag %s in %s" \ + % (child.tag, comment.tag) + if self.alt_id == None and uuid not in [None, self.uuid]: + self.alt_id = uuid + if body != None: + if self.content_type.startswith("text/"): + self.body = body+"\n" # restore trailing newline + else: + self.body = base64.decodestring(body) + def string(self, indent=0, shortname=None): """ >>> comm = Comment(bug=None, body="Some\\ninsightful\\nremarks\\n") - >>> comm.time_string = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> comm.date = "Thu, 01 Jan 1970 00:00:00 +0000" >>> print comm.string(indent=2, shortname="com-1") --------- Comment --------- Name: com-1 @@ -284,87 +428,18 @@ class Comment(Tree, settings_object.SavedSettingsObject): lines = [] lines.append("--------- Comment ---------") lines.append("Name: %s" % shortname) - lines.append("From: %s" % (self._setting_attr_string("From"))) - lines.append("Date: %s" % self.time_string) + lines.append("From: %s" % (self._setting_attr_string("author"))) + lines.append("Date: %s" % self.date) lines.append("") - #lines.append(textwrap.fill(self.body or "", - # width=(79-indent))) if self.content_type.startswith("text/"): lines.extend((self.body or "").splitlines()) else: lines.append("Content type %s not printable. Try XML output instead" % self.content_type) - # some comments shouldn't be wrapped... istring = ' '*indent sep = '\n' + istring return istring + sep.join(lines).rstrip('\n') - def __str__(self): - """ - >>> comm = Comment(bug=None, body="Some insightful remarks") - >>> comm.uuid = "com-1" - >>> comm.time_string = "Thu, 20 Nov 2008 15:55:11 +0000" - >>> comm.From = "Jane Doe <jdoe@example.com>" - >>> print comm - --------- Comment --------- - Name: com-1 - From: Jane Doe <jdoe@example.com> - Date: Thu, 20 Nov 2008 15:55:11 +0000 - <BLANKLINE> - Some insightful remarks - """ - return self.string() - - def get_path(self, name=None): - my_dir = os.path.join(self.bug.get_path("comments"), self.uuid) - if name is None: - return my_dir - assert name in ["values", "body"] - return os.path.join(my_dir, name) - - def load_settings(self): - self.settings = mapfile.map_load(self.rcs, self.get_path("values")) - self._setup_saved_settings() - - def save_settings(self): - parent_dir = os.path.dirname(self.get_path()) - self.rcs.mkdir(parent_dir) - self.rcs.mkdir(self.get_path()) - path = self.get_path("values") - mapfile.map_save(self.rcs, path, self._get_saved_settings()) - - def save(self): - assert self.body != None, "Can't save blank comment" - #if self.in_reply_to == None: - # raise Exception, str(self)+'\n'+str(self.settings)+'\n'+str(self._settings_loaded) - #assert self.in_reply_to != None, "Comment must be a reply to something" - self.save_settings() - self._set_comment_body(new=self.body, force=True) - - def remove(self): - for comment in self.traverse(): - path = comment.get_path() - self.rcs.recursive_remove(path) - - def add_reply(self, reply, allow_time_inversion=False): - if self.uuid != INVALID_UUID: - reply.in_reply_to = self.uuid - self.append(reply) - #raise Exception, "adding reply \n%s\n%s" % (self, reply) - - def new_reply(self, body=None): - """ - >>> comm = Comment(bug=None, body="Some insightful remarks") - >>> repA = comm.new_reply("Critique original comment") - >>> repB = repA.new_reply("Begin flamewar :p") - >>> repB.in_reply_to == repA.uuid - True - """ - reply = Comment(self.bug, body=body) - self.add_reply(reply) - #raise Exception, "new reply added (%s),\n%s\n%s\n\t--%s--" % (body, self, reply, reply.in_reply_to) - return reply - def string_thread(self, string_method_name="string", name_map={}, indent=0, flatten=True, auto_name_map=False, bug_shortname=None): @@ -382,7 +457,7 @@ class Comment(Tree, settings_object.SavedSettingsObject): name_map = {} for shortname,comment in comm.comment_shortnames(bug_shortname): name_map[comment.uuid] = shortname - comm.sort(key=lambda c : c.From) # your sort + comm.sort(key=lambda c : c.author) # your sort comm.string_thread(name_map=name_map) >>> a = Comment(bug=None, uuid="a", body="Insightful remarks") @@ -469,6 +544,80 @@ class Comment(Tree, settings_object.SavedSettingsObject): indent=indent, auto_name_map=auto_name_map, bug_shortname=bug_shortname) + # methods for saving/loading/acessing settings and properties. + + def get_path(self, name=None): + my_dir = os.path.join(self.bug.get_path("comments"), self.uuid) + if name is None: + return my_dir + assert name in ["values", "body"] + return os.path.join(my_dir, name) + + def set_sync_with_disk(self, value): + self.sync_with_disk = value + + def load_settings(self): + if self.sync_with_disk == False: + raise DiskAccessRequired("load settings") + self.settings = mapfile.map_load(self.rcs, self.get_path("values")) + # hack to deal with old BE comments: + if "From" in self.settings: + self.settings["Author"] = self.settings.pop("From") + self._setup_saved_settings() + + def save_settings(self): + if self.sync_with_disk == False: + raise DiskAccessRequired("save settings") + self.rcs.mkdir(self.get_path()) + path = self.get_path("values") + mapfile.map_save(self.rcs, path, self._get_saved_settings()) + + def save(self): + """ + Save any loaded contents to disk. + + However, if self.sync_with_disk = True, then any changes are + automatically written to disk as soon as they happen, so + calling this method will just waste time (unless something + else has been messing with your on-disk files). + """ + sync_with_disk = self.sync_with_disk + if sync_with_disk == False: + self.set_sync_with_disk(True) + assert self.body != None, "Can't save blank comment" + self.save_settings() + self._set_comment_body(new=self.body, force=True) + if sync_with_disk == False: + self.set_sync_with_disk(False) + + def remove(self): + if self.sync_with_disk == False and self.uuid != INVALID_UUID: + raise DiskAccessRequired("remove") + for comment in self.traverse(): + path = comment.get_path() + self.rcs.recursive_remove(path) + + def add_reply(self, reply, allow_time_inversion=False): + if self.uuid != INVALID_UUID: + reply.in_reply_to = self.uuid + self.append(reply) + + def new_reply(self, body=None): + """ + >>> comm = Comment(bug=None, body="Some insightful remarks") + >>> repA = comm.new_reply("Critique original comment") + >>> repB = repA.new_reply("Begin flamewar :p") + >>> repB.in_reply_to == repA.uuid + True + """ + reply = Comment(self.bug, body=body) + if self.bug != None: + reply.set_sync_with_disk(self.bug.sync_with_disk) + if reply.sync_with_disk == True: + reply.save() + self.add_reply(reply) + return reply + def comment_shortnames(self, bug_shortname=None): """ Iterate through (id, comment) pairs, in time order. @@ -535,4 +684,59 @@ class Comment(Tree, settings_object.SavedSettingsObject): return comment raise KeyError(uuid) +def cmp_attr(comment_1, comment_2, attr, invert=False): + """ + Compare a general attribute between two comments using the conventional + comparison rule for that attribute type. If invert == True, sort + *against* that convention. + >>> attr="author" + >>> commentA = Comment() + >>> commentB = Comment() + >>> commentA.author = "John Doe" + >>> commentB.author = "Jane Doe" + >>> cmp_attr(commentA, commentB, attr) > 0 + True + >>> cmp_attr(commentA, commentB, attr, invert=True) < 0 + True + >>> commentB.author = "John Doe" + >>> cmp_attr(commentA, commentB, attr) == 0 + True + """ + if not hasattr(comment_2, attr) : + return 1 + val_1 = getattr(comment_1, attr) + val_2 = getattr(comment_2, attr) + if val_1 == None: val_1 = None + if val_2 == None: val_2 = None + + if invert == True : + return -cmp(val_1, val_2) + else : + return cmp(val_1, val_2) + +# alphabetical rankings (a < z) +cmp_uuid = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "uuid") +cmp_author = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "author") +cmp_in_reply_to = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "in_reply_to") +cmp_content_type = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "content_type") +cmp_body = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "body") +# chronological rankings (newer < older) +cmp_time = lambda comment_1, comment_2 : cmp_attr(comment_1, comment_2, "time", invert=True) + +DEFAULT_CMP_FULL_CMP_LIST = \ + (cmp_time, cmp_author, cmp_content_type, cmp_body, cmp_in_reply_to, + cmp_uuid) + +class CommentCompoundComparator (object): + def __init__(self, cmp_list=DEFAULT_CMP_FULL_CMP_LIST): + self.cmp_list = cmp_list + def __call__(self, comment_1, comment_2): + for comparison in self.cmp_list : + val = comparison(comment_1, comment_2) + if val != 0 : + return val + return 0 + +cmp_full = CommentCompoundComparator() + suite = doctest.DocTestSuite() diff --git a/libbe/config.py b/libbe/config.py index 7f600a5..5e343b9 100644 --- a/libbe/config.py +++ b/libbe/config.py @@ -1,20 +1,19 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import ConfigParser import codecs import locale diff --git a/libbe/darcs.py b/libbe/darcs.py index 43af99a..0720ed9 100644 --- a/libbe/darcs.py +++ b/libbe/darcs.py @@ -1,25 +1,30 @@ # Copyright (C) 2009 W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import codecs import os import re import sys -import unittest +try: # import core module, Python >= 2.5 + from xml.etree import ElementTree +except ImportError: # look for non-core module + from elementtree import ElementTree +from xml.sax.saxutils import unescape import doctest +import unittest import rcs from rcs import RCS @@ -131,30 +136,43 @@ class Darcs(RCS): RCS._rcs_duplicate_repo(self, directory, revision) else: self._u_invoke_client("put", "--to-patch", revision, directory) - def _rcs_commit(self, commitfile): + def _rcs_commit(self, commitfile, allow_empty=False): id = self.get_user_id() if '@' not in id: id = "%s <%s@invalid.com>" % (id, id) - # Darcs doesn't like commitfiles without trailing endlines. - f = codecs.open(commitfile, 'r', self.encoding) - contents = f.read() - f.close() - if contents[-1] != '\n': - f = codecs.open(commitfile, 'a', self.encoding) - f.write('\n') - f.close() - status,output,error = self._u_invoke_client('record', '--all', - '--author', id, - '--logfile', commitfile) - revision = None - - revline = re.compile("Finished recording patch '(.*)'") - match = revline.search(output) - assert match != None, output+error - assert len(match.groups()) == 1 - revision = match.groups()[0] + args = ['record', '--all', '--author', id, '--logfile', commitfile] + status,output,error = self._u_invoke_client(*args) + empty_strings = ["No changes!"] + if self._u_any_in_string(empty_strings, output) == True: + if allow_empty == False: + raise rcs.EmptyCommit() + # note that darcs does _not_ make an empty revision. + # this returns the last non-empty revision id... + revision = self._rcs_revision_id(-1) + else: + revline = re.compile("Finished recording patch '(.*)'") + match = revline.search(output) + assert match != None, output+error + assert len(match.groups()) == 1 + revision = match.groups()[0] return revision - + def _rcs_revision_id(self, index): + status,output,error = self._u_invoke_client("changes", "--xml") + revisions = [] + xml_str = output.encode("unicode_escape").replace(r"\n", "\n") + element = ElementTree.XML(xml_str) + assert element.tag == "changelog", element.tag + for patch in element.getchildren(): + assert patch.tag == "patch", patch.tag + for child in patch.getchildren(): + if child.tag == "name": + text = unescape(unicode(child.text).decode("unicode_escape").strip()) + revisions.append(text) + revisions.reverse() + try: + return revisions[index] + except IndexError: + return None rcs.make_rcs_testcase_subclasses(Darcs, sys.modules[__name__]) diff --git a/libbe/diff.py b/libbe/diff.py index a349e14..1b52620 100644 --- a/libbe/diff.py +++ b/libbe/diff.py @@ -1,117 +1,415 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """Compare two bug trees""" -from libbe import cmdutil, bugdir, bug +from libbe import bugdir, bug, settings_object, tree from libbe.utility import time_to_str +import difflib import doctest -def diff(old_bugdir, new_bugdir): - added = [] - removed = [] - modified = [] - for uuid in old_bugdir.list_uuids(): - old_bug = old_bugdir.bug_from_uuid(uuid) - try: - new_bug = new_bugdir.bug_from_uuid(uuid) - if old_bug != new_bug: - modified.append((old_bug, new_bug)) - except KeyError: - removed.append(old_bug) - for uuid in new_bugdir.list_uuids(): - if not old_bugdir.has_bug(uuid): - new_bug = new_bugdir.bug_from_uuid(uuid) - added.append(new_bug) - return (removed, modified, added) +class DiffTree (tree.Tree): + """ + A tree holding difference data for easy report generation. + >>> bugdir = DiffTree("bugdir") + >>> bdsettings = DiffTree("settings", data="target: None -> 1.0") + >>> bugdir.append(bdsettings) + >>> bugs = DiffTree("bugs", "bug-count: 5 -> 6") + >>> bugdir.append(bugs) + >>> new = DiffTree("new", "new bugs: ABC, DEF") + >>> bugs.append(new) + >>> rem = DiffTree("rem", "removed bugs: RST, UVW") + >>> bugs.append(rem) + >>> print bugdir.report_string() + target: None -> 1.0 + bug-count: 5 -> 6 + new bugs: ABC, DEF + removed bugs: RST, UVW + >>> print "\\n".join(bugdir.paths()) + bugdir + bugdir/settings + bugdir/bugs + bugdir/bugs/new + bugdir/bugs/rem + >>> bugdir.child_by_path("/") == bugdir + True + >>> bugdir.child_by_path("/bugs") == bugs + True + >>> bugdir.child_by_path("/bugs/rem") == rem + True + >>> bugdir.child_by_path("bugdir") == bugdir + True + >>> bugdir.child_by_path("bugdir/") == bugdir + True + >>> bugdir.child_by_path("bugdir/bugs") == bugs + True + >>> bugdir.child_by_path("/bugs").masked = True + >>> print bugdir.report_string() + target: None -> 1.0 + """ + def __init__(self, name, data=None, data_part_fn=str, + requires_children=False, masked=False): + tree.Tree.__init__(self) + self.name = name + self.data = data + self.data_part_fn = data_part_fn + self.requires_children = requires_children + self.masked = masked + def paths(self, parent_path=None): + paths = [] + if parent_path == None: + path = self.name + else: + path = "%s/%s" % (parent_path, self.name) + paths.append(path) + for child in self: + paths.extend(child.paths(path)) + return paths + def child_by_path(self, path): + if hasattr(path, "split"): # convert string path to a list of names + names = path.split("/") + if names[0] == "": + names[0] = self.name # replace root with self + if len(names) > 1 and names[-1] == "": + names = names[:-1] # strip empty tail + else: # it was already an array + names = path + assert len(names) > 0, path + if names[0] == self.name: + if len(names) == 1: + return self + for child in self: + if names[1] == child.name: + return child.child_by_path(names[1:]) + if len(names) == 1: + raise KeyError, "%s doesn't match '%s'" % (names, self.name) + raise KeyError, "%s points to child not in %s" % (names, [c.name for c in self]) + def report_string(self): + return "\n".join(self.report()) + def report(self, root=None, parent=None, depth=0): + if root == None: + root = self.make_root() + if self.masked == True: + return None + data_part = self.data_part(depth) + if self.requires_children == True and len(self) == 0: + pass + else: + self.join(root, parent, data_part) + if data_part != None: + depth += 1 + for child in self: + child.report(root, self, depth) + return root + def make_root(self): + return [] + def join(self, root, parent, data_part): + if data_part != None: + root.append(data_part) + def data_part(self, depth, indent=True): + if self.data == None: + return None + if hasattr(self, "_cached_data_part"): + return self._cached_data_part + data_part = self.data_part_fn(self.data) + if indent == True: + data_part_lines = data_part.splitlines() + indent = " "*(depth) + line_sep = "\n"+indent + data_part = indent+line_sep.join(data_part_lines) + self._cached_data_part = data_part + return data_part -def diff_report(diff_data, bug_dir): - (removed, modified, added) = diff_data - def modified_cmp(left, right): - return bug.cmp_severity(left[1], right[1]) +class Diff (object): + """ + Difference tree generator for BugDirs. + >>> import copy + >>> bd = bugdir.SimpleBugDir(sync_with_disk=False) + >>> bd.user_id = "John Doe <j@doe.com>" + >>> bd_new = copy.deepcopy(bd) + >>> bd_new.target = "1.0" + >>> a = bd_new.bug_from_uuid("a") + >>> rep = a.comment_root.new_reply("I'm closing this bug") + >>> rep.uuid = "acom" + >>> rep.date = "Thu, 01 Jan 1970 00:00:00 +0000" + >>> a.status = "closed" + >>> b = bd_new.bug_from_uuid("b") + >>> bd_new.remove_bug(b) + >>> c = bd_new.new_bug("c", "Bug C") + >>> d = Diff(bd, bd_new) + >>> r = d.report_tree() + >>> print "\\n".join(r.paths()) + bugdir + bugdir/settings + bugdir/bugs + bugdir/bugs/new + bugdir/bugs/new/c + bugdir/bugs/rem + bugdir/bugs/rem/b + bugdir/bugs/mod + bugdir/bugs/mod/a + bugdir/bugs/mod/a/settings + bugdir/bugs/mod/a/comments + bugdir/bugs/mod/a/comments/new + bugdir/bugs/mod/a/comments/new/acom + bugdir/bugs/mod/a/comments/rem + bugdir/bugs/mod/a/comments/mod + >>> print r.report_string() + Changed bug directory settings: + target: None -> 1.0 + New bugs: + c:om: Bug C + Removed bugs: + b:cm: Bug B + Modified bugs: + a:cm: Bug A + Changed bug settings: + status: open -> closed + New comments: + from John Doe <j@doe.com> on Thu, 01 Jan 1970 00:00:00 +0000 + I'm closing this bug... + >>> bd.cleanup() + """ + def __init__(self, old_bugdir, new_bugdir): + self.old_bugdir = old_bugdir + self.new_bugdir = new_bugdir - added.sort(bug.cmp_severity) - removed.sort(bug.cmp_severity) - modified.sort(modified_cmp) - lines = [] - - if len(added) > 0: - lines.append("New bug reports:") - for bg in added: - lines.extend(bg.string(shortlist=True).splitlines()) - lines.append("") + # data assembly methods - if len(modified) > 0: - printed = False - for old_bug, new_bug in modified: - change_str = bug_changes(old_bug, new_bug, bug_dir) - if change_str is None: - continue - if not printed: - printed = True - lines.append("Modified bug reports:") - lines.extend(change_str.splitlines()) - if printed == True: - lines.append("") - - if len(removed) > 0: - lines.append("Removed bug reports:") - for bg in removed: - lines.extend(bg.string(shortlist=True).splitlines()) - lines.append("") - - return '\n'.join(lines) - -def change_lines(old, new, attributes): - change_list = [] - for attr in attributes: - old_attr = getattr(old, attr) - new_attr = getattr(new, attr) - if old_attr != new_attr: - change_list.append((attr, old_attr, new_attr)) - if len(change_list) >= 0: - return change_list - else: + def _changed_bugs(self): + """ + Search for differences in all bugs between .old_bugdir and + .new_bugdir. Returns + (added_bugs, modified_bugs, removed_bugs) + where added_bugs and removed_bugs are lists of added and + removed bugs respectively. modified_bugs is a list of + (old_bug,new_bug) pairs. + """ + if hasattr(self, "__changed_bugs"): + return self.__changed_bugs + added = [] + removed = [] + modified = [] + for uuid in self.new_bugdir.list_uuids(): + new_bug = self.new_bugdir.bug_from_uuid(uuid) + try: + old_bug = self.old_bugdir.bug_from_uuid(uuid) + except KeyError: + added.append(new_bug) + else: + if old_bug.sync_with_disk == True: + old_bug.load_comments() + if new_bug.sync_with_disk == True: + new_bug.load_comments() + if old_bug != new_bug: + modified.append((old_bug, new_bug)) + for uuid in self.old_bugdir.list_uuids(): + if not self.new_bugdir.has_bug(uuid): + old_bug = self.old_bugdir.bug_from_uuid(uuid) + removed.append(old_bug) + added.sort() + removed.sort() + modified.sort(self._bug_modified_cmp) + self.__changed_bugs = (added, modified, removed) + return self.__changed_bugs + def _bug_modified_cmp(self, left, right): + return cmp(left[1], right[1]) + def _changed_comments(self, old, new): + """ + Search for differences in all loaded comments between the bugs + old and new. Returns + (added_comments, modified_comments, removed_comments) + analogous to ._changed_bugs. + """ + if hasattr(self, "__changed_comments"): + if new.uuid in self.__changed_comments: + return self.__changed_comments[new.uuid] + else: + self.__changed_comments = {} + added = [] + removed = [] + modified = [] + old.comment_root.sort(key=lambda comm : comm.time) + new.comment_root.sort(key=lambda comm : comm.time) + old_comment_ids = [c.uuid for c in old.comments()] + new_comment_ids = [c.uuid for c in new.comments()] + for uuid in new_comment_ids: + new_comment = new.comment_from_uuid(uuid) + try: + old_comment = old.comment_from_uuid(uuid) + except KeyError: + added.append(new_comment) + else: + if old_comment != new_comment: + modified.append((old_comment, new_comment)) + for uuid in old_comment_ids: + if uuid not in new_comment_ids: + new_comment = new.comment_from_uuid(uuid) + removed.append(new_comment) + self.__changed_comments[new.uuid] = (added, modified, removed) + return self.__changed_comments[new.uuid] + def _attribute_changes(self, old, new, attributes): + """ + Take two objects old and new, and compare the value of *.attr + for attr in the list attribute names. Returns a list of + (attr_name, old_value, new_value) + tuples. + """ + change_list = [] + for attr in attributes: + old_value = getattr(old, attr) + new_value = getattr(new, attr) + if old_value != new_value: + change_list.append((attr, old_value, new_value)) + if len(change_list) >= 0: + return change_list return None + def _settings_properties_attribute_changes(self, old, new, + hidden_properties=[]): + properties = sorted(new.settings_properties) + for p in hidden_properties: + properties.remove(p) + attributes = [settings_object.setting_name_to_attr_name(None, p) + for p in properties] + return self._attribute_changes(old, new, attributes) + def _bugdir_attribute_changes(self): + return self._settings_properties_attribute_changes( \ + self.old_bugdir, self.new_bugdir, + ["rcs_name"]) # tweaked by bugdir.duplicate_bugdir + def _bug_attribute_changes(self, old, new): + return self._settings_properties_attribute_changes(old, new) + def _comment_attribute_changes(self, old, new): + return self._settings_properties_attribute_changes(old, new) -def bug_changes(old, new, bugs): - change_list = change_lines(old, new, ("time", "creator", "severity", - "target", "summary", "status", "assigned")) + # report generation methods - old_comment_ids = [c.uuid for c in old.comments()] - new_comment_ids = [c.uuid for c in new.comments()] - change_strings = ["%s: %s -> %s" % f for f in change_list] - for comment_id in new_comment_ids: - if comment_id not in old_comment_ids: - summary = comment_summary(new.comment_from_uuid(comment_id), "new") - change_strings.append(summary) - for comment_id in old_comment_ids: - if comment_id not in new_comment_ids: - summary = comment_summary(new.comment_from_uuid(comment_id), - "removed") - change_strings.append(summary) + def report_tree(self, diff_tree=DiffTree): + """ + Pretty bare to make it easy to adjust to specific cases. You + can pass in a DiffTree subclass via diff_tree to override the + default report assembly process. + """ + if hasattr(self, "__report_tree"): + return self.__report_tree + bugdir_settings = sorted(self.new_bugdir.settings_properties) + bugdir_settings.remove("rcs_name") # tweaked by bugdir.duplicate_bugdir + root = diff_tree("bugdir") + bugdir_attribute_changes = self._bugdir_attribute_changes() + if len(bugdir_attribute_changes) > 0: + bugdir = diff_tree("settings", bugdir_attribute_changes, + self.bugdir_attribute_change_string) + root.append(bugdir) + bug_root = diff_tree("bugs") + root.append(bug_root) + add,mod,rem = self._changed_bugs() + bnew = diff_tree("new", "New bugs:", requires_children=True) + bug_root.append(bnew) + for bug in add: + b = diff_tree(bug.uuid, bug, self.bug_add_string) + bnew.append(b) + brem = diff_tree("rem", "Removed bugs:", requires_children=True) + bug_root.append(brem) + for bug in rem: + b = diff_tree(bug.uuid, bug, self.bug_rem_string) + brem.append(b) + bmod = diff_tree("mod", "Modified bugs:", requires_children=True) + bug_root.append(bmod) + for old,new in mod: + b = diff_tree(new.uuid, (old,new), self.bug_mod_string) + bmod.append(b) + bug_attribute_changes = self._bug_attribute_changes(old, new) + if len(bug_attribute_changes) > 0: + bset = diff_tree("settings", bug_attribute_changes, + self.bug_attribute_change_string) + b.append(bset) + if old.summary != new.summary: + data = (old.summary, new.summary) + bsum = diff_tree("summary", data, self.bug_summary_change_string) + b.append(bsum) + cr = diff_tree("comments") + b.append(cr) + a,m,d = self._changed_comments(old, new) + cnew = diff_tree("new", "New comments:", requires_children=True) + for comment in a: + c = diff_tree(comment.uuid, comment, self.comment_add_string) + cnew.append(c) + crem = diff_tree("rem", "Removed comments:",requires_children=True) + for comment in d: + c = diff_tree(comment.uuid, comment, self.comment_rem_string) + crem.append(c) + cmod = diff_tree("mod","Modified comments:",requires_children=True) + for o,n in m: + c = diff_tree(n.uuid, (o,n), self.comment_mod_string) + cmod.append(c) + comm_attribute_changes = self._comment_attribute_changes(o, n) + if len(comm_attribute_changes) > 0: + cset = diff_tree("settings", comm_attribute_changes, + self.comment_attribute_change_string) + if o.body != n.body: + data = (o.body, n.body) + cbody = diff_tree("cbody", data, + self.comment_body_change_string) + c.append(cbody) + cr.extend([cnew, crem, cmod]) + self.__report_tree = root + return self.__report_tree - if len(change_strings) == 0: - return None - return "%s\n %s" % (new.string(shortlist=True), - " \n".join(change_strings)) + # change data -> string methods. + # Feel free to play with these in subclasses. + def attribute_change_string(self, attribute_changes, indent=0): + indent_string = " "*indent + change_strings = [u"%s: %s -> %s" % f for f in attribute_changes] + for i,change_string in enumerate(change_strings): + change_strings[i] = indent_string+change_string + return u"\n".join(change_strings) + def bugdir_attribute_change_string(self, attribute_changes): + return "Changed bug directory settings:\n%s" % \ + self.attribute_change_string(attribute_changes, indent=1) + def bug_attribute_change_string(self, attribute_changes): + return "Changed bug settings:\n%s" % \ + self.attribute_change_string(attribute_changes, indent=1) + def comment_attribute_change_string(self, attribute_changes): + return "Changed comment settings:\n%s" % \ + self.attribute_change_string(attribute_changes, indent=1) + def bug_add_string(self, bug): + return bug.string(shortlist=True) + def bug_rem_string(self, bug): + return bug.string(shortlist=True) + def bug_mod_string(self, bugs): + old_bug,new_bug = bugs + return new_bug.string(shortlist=True) + def bug_summary_change_string(self, summaries): + old_summary,new_summary = summaries + return "summary changed:\n %s\n %s" % (old_summary, new_summary) + def _comment_summary_string(self, comment): + return "from %s on %s" % (comment.author, time_to_str(comment.time)) + def comment_add_string(self, comment): + summary = self._comment_summary_string(comment) + first_line = comment.body.splitlines()[0] + return "%s\n %s..." % (summary, first_line) + def comment_rem_string(self, comment): + summary = self._comment_summary_string(comment) + first_line = comment.body.splitlines()[0] + return "%s\n %s..." % (summary, first_line) + def comment_mod_string(self, comments): + old_comment,new_comment = comments + return self._comment_summary_string(new_comment) + def comment_body_change_string(self, bodies): + old_body,new_body = bodies + return difflib.unified_diff(old_body, new_body) -def comment_summary(comment, status): - return "%8s comment from %s on %s" % (status, comment.From, - time_to_str(comment.time)) suite = doctest.DocTestSuite() diff --git a/libbe/editor.py b/libbe/editor.py index 6638ed9..8f2bdbf 100644 --- a/libbe/editor.py +++ b/libbe/editor.py @@ -1,21 +1,19 @@ # Bugs Everywhere, a distributed bugtracker # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import codecs import locale @@ -33,7 +31,7 @@ class CantFindEditor(Exception): Exception.__init__(self, "Can't find editor to get string from") def editor_string(comment=None, encoding=None): - """Invokes the editor, and returns the user_produced text as a string + """Invokes the editor, and returns the user-produced text as a string >>> if "EDITOR" in os.environ: ... del os.environ["EDITOR"] @@ -64,7 +62,8 @@ def editor_string(comment=None, encoding=None): fhandle, fname = tempfile.mkstemp() try: if comment is not None: - os.write(fhandle, '\n'+comment_string(comment)) + cstring = u'\n'+comment_string(comment) + os.write(fhandle, cstring.encode(encoding)) os.close(fhandle) oldmtime = os.path.getmtime(fname) os.system("%s %s" % (editor, fname)) diff --git a/libbe/encoding.py b/libbe/encoding.py index bdac98e..4af864e 100644 --- a/libbe/encoding.py +++ b/libbe/encoding.py @@ -1,31 +1,33 @@ # Bugs Everywhere, a distributed bugtracker # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, -# MA 02110-1301, USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import codecs import locale import sys import doctest +ENCODING = None # override get_encoding() output by setting this + def get_encoding(): """ Guess a useful input/output/filesystem encoding... Maybe we need seperate encodings for input/output and filesystem? Hmm... """ + if ENCODING != None: + return ENCODING encoding = locale.getpreferredencoding() or sys.getdefaultencoding() if sys.platform != 'win32' or sys.version_info[:2] > (2, 3): encoding = locale.getlocale(locale.LC_TIME)[1] or encoding diff --git a/libbe/git.py b/libbe/git.py index 31bbe32..53bc030 100644 --- a/libbe/git.py +++ b/libbe/git.py @@ -2,19 +2,19 @@ # Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import re @@ -53,10 +53,18 @@ class Git(RCS): def _rcs_init(self, path): self._u_invoke_client("init", directory=path) def _rcs_get_user_id(self): - status,output,error = self._u_invoke_client("config", "user.name") - name = output.rstrip('\n') - status,output,error = self._u_invoke_client("config", "user.email") - email = output.rstrip('\n') + status,output,error = \ + self._u_invoke_client("config", "user.name", expect=(0,1)) + if status == 0: + name = output.rstrip('\n') + else: + name = "" + status,output,error = \ + self._u_invoke_client("config", "user.email", expect=(0,1)) + if status == 0: + email = output.rstrip('\n') + else: + email = "" if name != "" or email != "": # got something! # guess missing info, if necessary if name == "": @@ -93,16 +101,41 @@ class Git(RCS): #self._u_invoke_client("archive", revision, directory) # makes tarball self._u_invoke_client("clone", "--no-checkout",".",directory) self._u_invoke_client("checkout", revision, directory=directory) - def _rcs_commit(self, commitfile): - status,output,error = self._u_invoke_client('commit', '-a', - '-F', commitfile) + def _rcs_commit(self, commitfile, allow_empty=False): + args = ['commit', '--all', '--file', commitfile] + if allow_empty == True: + args.append("--allow-empty") + status,output,error = self._u_invoke_client(*args) + else: + kwargs = {"expect":(0,1)} + status,output,error = self._u_invoke_client(*args, **kwargs) + strings = ["nothing to commit", + "nothing added to commit"] + if self._u_any_in_string(strings, output) == True: + raise rcs.EmptyCommit() revision = None revline = re.compile("(.*) (.*)[:\]] (.*)") match = revline.search(output) assert match != None, output+error assert len(match.groups()) == 3 revision = match.groups()[1] - return revision + full_revision = self._rcs_revision_id(-1) + assert full_revision.startswith(revision), \ + "Mismatched revisions:\n%s\n%s" % (revision, full_revision) + return full_revision + def _rcs_revision_id(self, index): + args = ["rev-list", "--first-parent", "--reverse", "HEAD"] + kwargs = {"expect":(0,128)} + status,output,error = self._u_invoke_client(*args, **kwargs) + if status == 128: + if error.startswith("fatal: ambiguous argument 'HEAD': unknown "): + return None + raise rcs.CommandError(args, status, error) + commits = output.splitlines() + try: + return commits[index] + except IndexError: + return None rcs.make_rcs_testcase_subclasses(Git, sys.modules[__name__]) diff --git a/libbe/hg.py b/libbe/hg.py index 30b0470..31df1d0 100644 --- a/libbe/hg.py +++ b/libbe/hg.py @@ -2,19 +2,19 @@ # Ben Finney <ben+python@benfinney.id.au> # W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import re @@ -58,7 +58,7 @@ class Hg(RCS): def _rcs_add(self, path): self._u_invoke_client("add", path) def _rcs_remove(self, path): - self._u_invoke_client("rm", path) + self._u_invoke_client("rm", "--force", path) def _rcs_update(self, path): pass def _rcs_get_file_contents(self, path, revision=None, binary=False): @@ -73,16 +73,24 @@ class Hg(RCS): return RCS._rcs_duplicate_repo(self, directory, revision) else: self._u_invoke_client("archive", "--rev", revision, directory) - def _rcs_commit(self, commitfile): - self._u_invoke_client('commit', '--logfile', commitfile) - status,output,error = self._u_invoke_client('identify') - revision = None - revline = re.compile("(.*) tip") - match = revline.search(output) - assert match != None, output+error - assert len(match.groups()) == 1 - revision = match.groups()[0] - return revision + def _rcs_commit(self, commitfile, allow_empty=False): + args = ['commit', '--logfile', commitfile] + status,output,error = self._u_invoke_client(*args) + if allow_empty == False: + strings = ["nothing changed"] + if self._u_any_in_string(strings, output) == True: + raise rcs.EmptyCommit() + return self._rcs_revision_id(-1) + def _rcs_revision_id(self, index, style="id"): + args = ["identify", "--rev", str(int(index)), "--%s" % style] + kwargs = {"expect": (0,255)} + status,output,error = self._u_invoke_client(*args, **kwargs) + if status == 0: + id = output.strip() + if id == '000000000000': + return None # before initial commit. + return id + return None rcs.make_rcs_testcase_subclasses(Hg, sys.modules[__name__]) diff --git a/libbe/mapfile.py b/libbe/mapfile.py index 9ff2215..b959d76 100644 --- a/libbe/mapfile.py +++ b/libbe/mapfile.py @@ -1,20 +1,19 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import yaml import os.path import errno @@ -68,9 +67,9 @@ def generate(map): assert(':' not in key) assert(len(key) > 0) except AssertionError: - raise IllegalKey(key.encode('string_escape')) + raise IllegalKey(unicode(key).encode('unicode_escape')) if "\n" in map[key]: - raise IllegalValue(map[key].encode('string_escape')) + raise IllegalValue(unicode(map[key]).encode('unicode_escape')) lines = [] for key in keys: diff --git a/libbe/plugin.py b/libbe/plugin.py index 3c7c753..0545fd7 100644 --- a/libbe/plugin.py +++ b/libbe/plugin.py @@ -1,21 +1,20 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # Marien Zwart <marienz@gentoo.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import os.path import sys diff --git a/libbe/properties.py b/libbe/properties.py index 37204d6..09dd20e 100644 --- a/libbe/properties.py +++ b/libbe/properties.py @@ -1,18 +1,19 @@ # Bugs Everywhere - a distributed bugtracker # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ This module provides a series of useful decorators for defining @@ -51,7 +52,7 @@ def Property(funcs): args["fset"] = funcs.get("fset", None) args["fdel"] = funcs.get("fdel", None) args["doc"] = funcs.get("doc", None) - + #print "Creating a property with" #for key, val in args.items(): print key, value return property(**args) @@ -76,6 +77,9 @@ def local_property(name, null=None, mutable_null=False): Define get/set access to per-parent-instance local storage. Uses ._<name>_value to store the value for a particular owner instance. If the ._<name>_value attribute does not exist, returns null. + + If mutable_null == True, we only release deepcopies of the null to + the outside world. """ def decorator(funcs): if hasattr(funcs, "__call__"): @@ -156,20 +160,25 @@ def _get_cached_mutable_property(self, cacher_name, property_name, default=None) if (cacher_name, property_name) not in self._mutable_property_cache_copy: return default return self._mutable_property_cache_copy[(cacher_name, property_name)] -def _cmp_cached_mutable_property(self, cacher_name, property_name, value): +def _cmp_cached_mutable_property(self, cacher_name, property_name, value, default=None): _init_mutable_property_cache(self) if (cacher_name, property_name) not in self._mutable_property_cache_hash: - return 1 # any value > non-existant old hash + _set_cached_mutable_property(self, cacher_name, property_name, default) old_hash = self._mutable_property_cache_hash[(cacher_name, property_name)] return cmp(_hash_mutable_value(value), old_hash) def defaulting_property(default=None, null=None, - default_mutable=False, - null_mutable=False): + mutable_default=False): """ Define a default value for get access to a property. If the stored value is null, then default is returned. + + If mutable_default == True, we only release deepcopies of the + default to the outside world. + + null should never escape to the outside world, so don't worry + about it being a mutable. """ def decorator(funcs): if hasattr(funcs, "__call__"): @@ -180,17 +189,14 @@ def defaulting_property(default=None, null=None, def _fget(self): value = fget(self) if value == null: - if default_mutable == True: + if mutable_default == True: return copy.deepcopy(default) else: return default return value def _fset(self, value): if value == default: - if null_mutable == True: - value = copy.deepcopy(null) - else: - value = null + value = null fset(self, value) funcs["fget"] = _fget funcs["fset"] = _fset @@ -260,7 +266,7 @@ def cached_property(generator, initVal=None, mutable=False): If the input value is no longer initVal (e.g. a value has been loaded from disk or set with fset), that value overrides any cached value, and this property has no effect. - + When the cache flag is False and the stored value is initVal, the generator is not cached, but is called on every fget. @@ -269,7 +275,7 @@ def cached_property(generator, initVal=None, mutable=False): In the case that mutable == True, all caching is disabled and the generator is called whenever the cached value would otherwise be - used. This avoids uncertainties in the value of stored mutables. + used. """ def decorator(funcs): if hasattr(funcs, "__call__"): @@ -295,7 +301,7 @@ def cached_property(generator, initVal=None, mutable=False): def primed_property(primer, initVal=None): """ - Just like a generator_property, except that instead of returning a + Just like a cached_property, except that instead of returning a new value and running fset to cache it, the primer performs some background manipulation (e.g. loads data into instance.settings) such that a _second_ pass through fget succeeds. @@ -321,7 +327,7 @@ def primed_property(primer, initVal=None): return funcs return decorator -def change_hook_property(hook, mutable=False): +def change_hook_property(hook, mutable=False, default=None): """ Call the function hook(instance, old_value, new_value) whenever a value different from the current value is set (instance is a a @@ -330,6 +336,17 @@ def change_hook_property(hook, mutable=False): called _after_ the new value has been stored, allowing you to change the stored value if you want. + In the case of mutables, things are slightly trickier. Because + the property-owning class has no way of knowing when the value + changes. We work around this by caching a private deepcopy of the + mutable value, and checking for changes whenever the property is + set (obviously) or retrieved (to check for external changes). So + long as you're conscientious about accessing the property after + making external modifications, mutability woln't be a problem. + t.x.append(5) # external modification + t.x # dummy access notices change and triggers hook + See testChangeHookMutableProperty for an example of the expected + behavior. """ def decorator(funcs): if hasattr(funcs, "__call__"): @@ -338,10 +355,13 @@ def change_hook_property(hook, mutable=False): fset = funcs.get("fset") name = funcs.get("name", "<unknown>") def _fget(self, new_value=None, from_fset=False): # only used if mutable == True - value = fget(self) - if _cmp_cached_mutable_property(self, "change hook property", name, value) != 0: + if from_fset == True: + value = new_value # compare new value with cached + else: + value = fget(self) # compare current value with cached + if _cmp_cached_mutable_property(self, "change hook property", name, value, default) != 0: # there has been a change, cache new value - old_value = _get_cached_mutable_property(self, "change hook property", name) + old_value = _get_cached_mutable_property(self, "change hook property", name, default) _set_cached_mutable_property(self, "change hook property", name, value) if from_fset == True: # return previously cached value value = old_value @@ -361,7 +381,7 @@ def change_hook_property(hook, mutable=False): funcs["fset"] = _fset return funcs return decorator - + class DecoratorTests(unittest.TestCase): def testLocalDoc(self): @@ -405,7 +425,7 @@ class DecoratorTests(unittest.TestCase): @local_property(name="DEFAULT", null=5) def x(): return {} t = Test() - self.failUnless(t.x == 5, str(t.x)) + self.failUnless(t.x == 5, str(t.x)) t.x = 'x' self.failUnless(t.x == 'y', str(t.x)) t.x = 'y' @@ -574,14 +594,17 @@ class DecoratorTests(unittest.TestCase): t.x = [] self.failUnless(t.old == None, t.old) self.failUnless(t.new == [], t.new) + self.failUnless(t.hook_calls == 1, t.hook_calls) a = t.x a.append(5) t.x = a self.failUnless(t.old == [], t.old) self.failUnless(t.new == [5], t.new) + self.failUnless(t.hook_calls == 2, t.hook_calls) t.x = [] self.failUnless(t.old == [5], t.old) self.failUnless(t.new == [], t.new) + self.failUnless(t.hook_calls == 3, t.hook_calls) # now append without reassigning. this doesn't trigger the # change, since we don't ever set t.x, only get it and mess # with it. It does, however, update our t.new, since t.new = @@ -589,25 +612,26 @@ class DecoratorTests(unittest.TestCase): t.x.append(5) self.failUnless(t.old == [5], t.old) self.failUnless(t.new == [5], t.new) + self.failUnless(t.hook_calls == 3, t.hook_calls) # however, the next t.x get _will_ notice the change... a = t.x self.failUnless(t.old == [], t.old) self.failUnless(t.new == [5], t.new) - self.failUnless(t.hook_calls == 6, t.hook_calls) + self.failUnless(t.hook_calls == 4, t.hook_calls) t.x.append(6) # this append(6) is not noticed yet self.failUnless(t.old == [], t.old) self.failUnless(t.new == [5,6], t.new) - self.failUnless(t.hook_calls == 6, t.hook_calls) + self.failUnless(t.hook_calls == 4, t.hook_calls) # this append(7) is not noticed, but the t.x get causes the # append(6) to be noticed t.x.append(7) self.failUnless(t.old == [5], t.old) self.failUnless(t.new == [5,6,7], t.new) - self.failUnless(t.hook_calls == 7, t.hook_calls) + self.failUnless(t.hook_calls == 5, t.hook_calls) a = t.x # now the append(7) is noticed self.failUnless(t.old == [5,6], t.old) self.failUnless(t.new == [5,6,7], t.new) - self.failUnless(t.hook_calls == 8, t.hook_calls) + self.failUnless(t.hook_calls == 6, t.hook_calls) suite = unittest.TestLoader().loadTestsFromTestCase(DecoratorTests) diff --git a/libbe/rcs.py b/libbe/rcs.py index 2c416f4..0206bf6 100644 --- a/libbe/rcs.py +++ b/libbe/rcs.py @@ -3,21 +3,20 @@ # Ben Finney <ben+python@benfinney.id.au> # Chris Ball <cjb@laptop.org> # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. from subprocess import Popen, PIPE import codecs @@ -62,10 +61,13 @@ def installed_rcs(): class CommandError(Exception): - def __init__(self, err_str, status): - Exception.__init__(self, "Command failed (%d): %s" % (status, err_str)) - self.err_str = err_str + def __init__(self, command, status, err_str): + strerror = ["Command failed (%d):\n %s\n" % (status, err_str), + "while executing\n %s" % command] + Exception.__init__(self, "\n".join(strerror)) + self.command = command self.status = status + self.err_str = err_str class SettingIDnotSupported(NotImplementedError): pass @@ -87,6 +89,10 @@ class NoSuchFile(Exception): path = os.path.abspath(os.path.join(root, pathname)) Exception.__init__(self, "No such file: %s" % path) +class EmptyCommit(Exception): + def __init__(self): + Exception.__init__(self, "No changes to commit") + def new(): return RCS() @@ -187,7 +193,7 @@ class RCS(object): if binary == False: f = codecs.open(os.path.join(self.rootdir, path), "r", self.encoding) else: - f = open(path, "rb") + f = open(os.path.join(self.rootdir, path), "rb") contents = f.read() f.close() return contents @@ -198,11 +204,24 @@ class RCS(object): dir specifies a directory to create the duplicate in. """ shutil.copytree(self.rootdir, directory, True) - def _rcs_commit(self, commitfile): + def _rcs_commit(self, commitfile, allow_empty=False): """ Commit the current working directory, using the contents of commitfile as the comment. Return the name of the old - revision. + revision (or None if commits are not supported). + + If allow_empty == False, raise EmptyCommit if there are no + changes to commit. + """ + return None + def _rcs_revision_id(self, index): + """ + Return the name of the <index>th revision. Index will be an + integer (possibly <= 0). The choice of which branch to follow + when crossing branches/merges is not defined. + + Return None if revision IDs are not supported, or if the + specified revision does not exist. """ return None def installed(self): @@ -330,11 +349,15 @@ class RCS(object): self.add(path) else: self.update(path) - def mkdir(self, path, allow_no_rcs=False): + def mkdir(self, path, allow_no_rcs=False, check_parents=True): """ Create (if neccessary) a directory at path under version control. """ + if check_parents == True: + parent = os.path.dirname(path) + if not os.path.exists(parent): # recurse through parents + self.mkdir(parent, allow_no_rcs, check_parents) if not os.path.exists(path): os.mkdir(path) if self._use_rcs(path, allow_no_rcs): @@ -342,7 +365,9 @@ class RCS(object): else: assert os.path.isdir(path) if self._use_rcs(path, allow_no_rcs): - self.update(path) + #self.update(path)# Don't update directories. Changing files + pass # underneath them should be sufficient. + def duplicate_repo(self, revision=None): """ Get the repository as it was in a given revision. @@ -365,30 +390,67 @@ class RCS(object): shutil.rmtree(self._duplicateBasedir) self._duplicateBasedir = None self._duplicateDirname = None - def commit(self, summary, body=None): + def commit(self, summary, body=None, allow_empty=False): """ Commit the current working directory, with a commit message string summary and body. Return the name of the old revision (or None if versioning is not supported). + + If allow_empty == False (the default), raise EmptyCommit if + there are no changes to commit. """ + summary = summary.strip()+'\n' if body is not None: - summary += '\n' + body + summary += '\n' + body.strip() + '\n' descriptor, filename = tempfile.mkstemp() revision = None try: temp_file = os.fdopen(descriptor, 'wb') temp_file.write(summary) temp_file.flush() - revision = self._rcs_commit(filename) + self.precommit() + revision = self._rcs_commit(filename, allow_empty=allow_empty) temp_file.close() + self.postcommit() finally: os.remove(filename) return revision - def precommit(self, directory): + def precommit(self): + """ + Executed before all attempted commits. + """ pass - def postcommit(self, directory): + def postcommit(self): + """ + Only executed after successful commits. + """ pass + def revision_id(self, index=None): + """ + Return the name of the <index>th revision. The choice of + which branch to follow when crossing branches/merges is not + defined. + + Return None if index==None, revision IDs are not supported, or + if the specified revision does not exist. + """ + if index == None: + return None + return self._rcs_revision_id(index) + def _u_any_in_string(self, list, string): + """ + Return True if any of the strings in list are in string. + Otherwise return False. + """ + for list_string in list: + if list_string in string: + return True + return False def _u_invoke(self, args, stdin=None, expect=(0,), cwd=None): + """ + expect should be a tuple of allowed exit codes. cwd should be + the directory from which the command will be executed. + """ if cwd == None: cwd = self.rootdir if self.verboseInvoke == True: @@ -401,15 +463,13 @@ class RCS(object): q = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE, shell=True, cwd=cwd) except OSError, e : - strerror = "%s\nwhile executing %s" % (e.args[1], args) - raise CommandError(strerror, e.args[0]) + raise CommandError(args, e.args[0], e) output, error = q.communicate(input=stdin) status = q.wait() if self.verboseInvoke == True: print >> sys.stderr, "%d\n%s%s" % (status, output, error) if status not in expect: - strerror = "%s\nwhile executing %s\n%s" % (args[1], args, error) - raise CommandError(strerror, status) + raise CommandError(args, status, error) return status, output, error def _u_invoke_client(self, *args, **kwargs): directory = kwargs.get('directory',None) @@ -784,6 +844,39 @@ class RCS_commit_TestCase(RCSTestCase): self.failUnlessEqual( self.test_contents['rev_1'], committed_contents) + def test_revision_id_as_committed(self): + """Check for compatibility between .commit() and .revision_id()""" + if not self.rcs.versioned: + self.failUnlessEqual(self.rcs.revision_id(5), None) + return + committed_revisions = [] + for path in self.test_files: + full_path = self.full_path(path) + self.rcs.set_file_contents( + full_path, self.test_contents['rev_1']) + revision = self.rcs.commit("Initial %s contents." % path) + committed_revisions.append(revision) + self.rcs.set_file_contents( + full_path, self.test_contents['uncommitted']) + revision = self.rcs.commit("Altered %s contents." % path) + committed_revisions.append(revision) + for i,revision in enumerate(committed_revisions): + self.failUnlessEqual(self.rcs.revision_id(i), revision) + i += -len(committed_revisions) # check negative indices + self.failUnlessEqual(self.rcs.revision_id(i), revision) + i = len(committed_revisions) + self.failUnlessEqual(self.rcs.revision_id(i), None) + self.failUnlessEqual(self.rcs.revision_id(-i-1), None) + + def test_revision_id_as_committed(self): + """Check revision id before first commit""" + if not self.rcs.versioned: + self.failUnlessEqual(self.rcs.revision_id(5), None) + return + committed_revisions = [] + for path in self.test_files: + self.failUnlessEqual(self.rcs.revision_id(0), None) + class RCS_duplicate_repo_TestCase(RCSTestCase): """Test cases for RCS.duplicate_repo method.""" diff --git a/libbe/settings_object.py b/libbe/settings_object.py index 7326d1b..ceea9d5 100644 --- a/libbe/settings_object.py +++ b/libbe/settings_object.py @@ -1,18 +1,19 @@ # Bugs Everywhere - a distributed bugtracker # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. """ This module provides a base class implementing settings-dict based @@ -95,7 +96,7 @@ def versioned_property(name, doc, require_save=False): """ Combine the common decorators in a single function. - + Use zero or one (but not both) of default or generator, since a working default will keep the generator from functioning. Use the default if you know what you want the default value to be at @@ -103,49 +104,57 @@ def versioned_property(name, doc, determine a valid default at run time. If both default and generator are None, then the property will be a defaulting property which defaults to None. - + allowed and check_fn have a similar relationship, although you can use both of these if you want. allowed compares the proposed value against a list determined at 'coding time' and check_fn allows more flexible comparisons to take place at run time. - + Set require_save to True if you want to save the default/generated value for a property, to protect against future changes. E.g., we currently expect all comments to be 'text/plain' but in the future we may want to default to 'text/html'. If we don't want the old comments to be interpreted as 'text/html', we would require that the content type be saved. - + change_hook, primer, settings_properties, and required_saved_properties are only options to get their defaults into our local scope. Don't mess with them. + + Set mutable=True if: + * default is a mutable + * your generator function may return mutables + * you set change_hook and might have mutable property values + See the docstrings in libbe.properties for details on how each of + these cases are handled. """ settings_properties.append(name) if require_save == True: required_saved_properties.append(name) def decorator(funcs): fulldoc = doc - if default != None: + if default != None or generator == None: defaulting = defaulting_property(default=default, null=EMPTY, - default_mutable=mutable) - fulldoc += "\n\nThis property defaults to %s" % default + mutable_default=mutable) + fulldoc += "\n\nThis property defaults to %s." % default if generator != None: cached = cached_property(generator=generator, initVal=EMPTY, mutable=mutable) - fulldoc += "\n\nThis property is generated with %s" % generator + fulldoc += "\n\nThis property is generated with %s." % generator if check_fn != None: fn_checked = fn_checked_property(value_allowed_fn=check_fn) - fulldoc += "\n\nThis property is checked with %s" % check_fn + fulldoc += "\n\nThis property is checked with %s." % check_fn if allowed != None: checked = checked_property(allowed=allowed) fulldoc += "\n\nThe allowed values for this property are: %s." \ % (', '.join(allowed)) - hooked = change_hook_property(hook=change_hook, mutable=mutable) + hooked = change_hook_property(hook=change_hook, mutable=mutable, + default=EMPTY) primed = primed_property(primer=primer, initVal=UNPRIMED) settings = settings_property(name=name, null=UNPRIMED) docp = doc_property(doc=fulldoc) deco = hooked(primed(settings(docp(funcs)))) - if default != None: + if default != None or generator == None: deco = defaulting(deco) if generator != None: deco = cached(deco) @@ -179,7 +188,7 @@ class SavedSettingsObject(object): # Override. Must call ._setup_saved_settings() after loading. self.settings = {} self._setup_saved_settings() - + def _setup_saved_settings(self, flag_as_loaded=True): """ To be run after setting self.settings up from disk. Marks all @@ -207,7 +216,7 @@ class SavedSettingsObject(object): for k in self.required_saved_properties: settings[k] = getattr(self, self._setting_name_to_attr_name(k)) return settings - + def clear_cached_setting(self, setting=None): "If setting=None, clear *all* cached settings" if setting != None: @@ -235,7 +244,7 @@ class SavedSettingsObjectTests(unittest.TestCase): # access missing setting self.failUnless(t._settings_loaded == False, t._settings_loaded) self.failUnless(len(t.settings) == 0, len(t.settings)) - self.failUnless(t.content_type == EMPTY, t.content_type) + self.failUnless(t.content_type == None, t.content_type) # accessing t.content_type triggers the priming, which runs # t._setup_saved_settings, which fills out t.settings with # EMPTY data. t._settings_loaded is still false though, since @@ -250,16 +259,16 @@ class SavedSettingsObjectTests(unittest.TestCase): self.failUnless(t._settings_loaded == True, t._settings_loaded) self.failUnless(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) - self.failUnless(t.content_type == EMPTY, t.content_type) + self.failUnless(t.content_type == None, t.content_type) self.failUnless(len(t.settings) == 1, len(t.settings)) self.failUnless(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) # now we set a value - t.content_type = None - self.failUnless(t.settings["Content-type"] == None, + t.content_type = 5 + self.failUnless(t.settings["Content-type"] == 5, t.settings["Content-type"]) - self.failUnless(t.content_type == None, t.content_type) - self.failUnless(t.settings["Content-type"] == None, + self.failUnless(t.content_type == 5, t.content_type) + self.failUnless(t.settings["Content-type"] == 5, t.settings["Content-type"]) # now we set another value t.content_type = "text/plain" @@ -273,7 +282,7 @@ class SavedSettingsObjectTests(unittest.TestCase): self.failUnless(t._settings_loaded == True, t._settings_loaded) self.failUnless(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) - self.failUnless(t.content_type == EMPTY, t.content_type) + self.failUnless(t.content_type == None, t.content_type) self.failUnless(len(t.settings) == 1, len(t.settings)) self.failUnless(t.settings["Content-type"] == EMPTY, t.settings["Content-type"]) @@ -376,33 +385,25 @@ class SavedSettingsObjectTests(unittest.TestCase): t.load_settings() self.failUnless(SAVES == [], SAVES) self.failUnless(t._settings_loaded == True, t._settings_loaded) - self.failUnless(t.list_type == EMPTY, t.list_type) - self.failUnless(SAVES == [ - "'None' -> '<class 'libbe.settings_object.EMPTY'>'" - ], SAVES) + self.failUnless(t.list_type == None, t.list_type) + self.failUnless(SAVES == [], SAVES) self.failUnless(t.settings["List-type"]==EMPTY,t.settings["List-type"]) t.list_type = [] self.failUnless(t.settings["List-type"] == [], t.settings["List-type"]) self.failUnless(SAVES == [ - "'None' -> '<class 'libbe.settings_object.EMPTY'>'", "'<class 'libbe.settings_object.EMPTY'>' -> '[]'" ], SAVES) t.list_type.append(5) self.failUnless(SAVES == [ - "'None' -> '<class 'libbe.settings_object.EMPTY'>'", "'<class 'libbe.settings_object.EMPTY'>' -> '[]'", - "'<class 'libbe.settings_object.EMPTY'>' -> '[]'" # <- TODO. Where did this come from? ], SAVES) self.failUnless(t.settings["List-type"] == [5],t.settings["List-type"]) self.failUnless(SAVES == [ # the append(5) has not yet been saved - "'None' -> '<class 'libbe.settings_object.EMPTY'>'", - "'<class 'libbe.settings_object.EMPTY'>' -> '[]'", "'<class 'libbe.settings_object.EMPTY'>' -> '[]'", ], SAVES) self.failUnless(t.list_type == [5], t.list_type) # <-get triggers saved + self.failUnless(SAVES == [ # now the append(5) has been saved. - "'None' -> '<class 'libbe.settings_object.EMPTY'>'", - "'<class 'libbe.settings_object.EMPTY'>' -> '[]'", "'<class 'libbe.settings_object.EMPTY'>' -> '[]'", "'[]' -> '[5]'" ], SAVES) diff --git a/libbe/tree.py b/libbe/tree.py index 54b927e..45ae085 100644 --- a/libbe/tree.py +++ b/libbe/tree.py @@ -1,20 +1,19 @@ # Bugs Everywhere, a distributed bugtracker # Copyright (C) 2008-2009 W. Trevor King <wking@drexel.edu> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA -# 02110-1301, USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import doctest @@ -36,7 +35,7 @@ class Tree(list): >>> a = Tree(); a.n = "a" >>> a.append(c) >>> a.append(b) - + >>> a.branch_len() 5 >>> a.sort(key=lambda node : -node.branch_len()) @@ -45,7 +44,7 @@ class Tree(list): >>> a.sort(key=lambda node : node.branch_len()) >>> "".join([node.n for node in a.traverse()]) 'abdgcefhi' - >>> "".join([node.n for node in a.traverse(depthFirst=False)]) + >>> "".join([node.n for node in a.traverse(depth_first=False)]) 'abcdefghi' >>> for depth,node in a.thread(): ... print "%*s" % (2*depth+1, node.n) @@ -69,7 +68,18 @@ class Tree(list): f h i + >>> a.has_descendant(g) + True + >>> c.has_descendant(g) + False + >>> a.has_descendant(a) + False + >>> a.has_descendant(a, match_self=True) + True """ + def __eq__(self, other): + return id(self) == id(other) + def branch_len(self): """ Exhaustive search every time == SLOW. @@ -98,11 +108,11 @@ class Tree(list): for child in self: child.sort(*args, **kwargs) - def traverse(self, depthFirst=True): + def traverse(self, depth_first=True): """ Note: you might want to sort() your tree first. """ - if depthFirst == True: + if depth_first == True: yield self for child in self: for descendant in child.traverse(): @@ -120,7 +130,7 @@ class Tree(list): When flatten==False, the depth of any node is one greater than the depth of its parent. That way the inheritance is explicit, but you can end up with highly indented threads. - + When flatten==True, the depth of any node is only greater than the depth of its parent when there is a branch, and the node is not the last child. This can lead to ancestry ambiguity, @@ -139,8 +149,8 @@ class Tree(list): stack = [] # ancestry of the current node if flatten == True: depthDict = {} - - for node in self.traverse(depthFirst=True): + + for node in self.traverse(depth_first=True): while len(stack) > 0 \ and id(node) not in [id(c) for c in stack[-1]]: stack.pop(-1) @@ -158,4 +168,12 @@ class Tree(list): yield (depth,node) stack.append(node) + def has_descendant(self, descendant, depth_first=True, match_self=False): + if descendant == self: + return match_self + for d in self.traverse(depth_first): + if descendant == d: + return True + return False + suite = doctest.DocTestSuite() diff --git a/libbe/utility.py b/libbe/utility.py index 8a0f318..3df06b4 100644 --- a/libbe/utility.py +++ b/libbe/utility.py @@ -1,29 +1,28 @@ # Copyright (C) 2005-2009 Aaron Bentley and Panometrics, Inc. # W. Trevor King <wking@drexel.edu> -# <abentley@panoramicfeedback.com> # -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. # -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. # -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import calendar import codecs import os import shutil import tempfile import time +import types import doctest - def search_parent_directories(path, filename): """ Find the file (or directory) named filename in path or in any @@ -77,17 +76,54 @@ def time_to_str(time_val): return time.strftime(RFC_2822_TIME_FMT, time.gmtime(time_val)) def str_to_time(str_time): - """Convert an RFC 2822-fomatted string into a time falue. + """Convert an RFC 2822-fomatted string into a time value. >>> str_to_time("Thu, 01 Jan 1970 00:00:00 +0000") 0 >>> q = time.time() >>> str_to_time(time_to_str(q)) == int(q) True + >>> str_to_time("Thu, 01 Jan 1970 00:00:00 -1000") + 36000 """ - return calendar.timegm(time.strptime(str_time, RFC_2822_TIME_FMT)) + timezone_str = str_time[-5:] + if timezone_str != "+0000": + str_time = str_time.replace(timezone_str, "+0000") + time_val = calendar.timegm(time.strptime(str_time, RFC_2822_TIME_FMT)) + timesign = -int(timezone_str[0]+"1") # "+" -> time_val ahead of GMT + timezone_tuple = time.strptime(timezone_str[1:], "%H%M") + timezone = timezone_tuple.tm_hour*3600 + timezone_tuple.tm_min*60 + return time_val + timesign*timezone def handy_time(time_val): return time.strftime("%a, %d %b %Y %H:%M", time.localtime(time_val)) +def time_to_gmtime(str_time): + """Convert an RFC 2822-fomatted string to a GMT string. + >>> time_to_gmtime("Thu, 01 Jan 1970 00:00:00 -1000") + 'Thu, 01 Jan 1970 10:00:00 +0000' + """ + time_val = str_to_time(str_time) + return time_to_str(time_val) + +def iterable_full_of_strings(value, alternative=None): + """ + Require an iterable full of strings. + >>> iterable_full_of_strings([]) + True + >>> iterable_full_of_strings(["abc", "def", u"hij"]) + True + >>> iterable_full_of_strings(["abc", None, u"hij"]) + False + >>> iterable_full_of_strings(None, alternative=None) + True + """ + if value == alternative: + return True + elif not hasattr(value, "__iter__"): + return False + for x in value: + if type(x) not in types.StringTypes: + return False + return True suite = doctest.DocTestSuite() diff --git a/completion/be.bash b/misc/completion/be.bash index 834bf25..834bf25 100644 --- a/completion/be.bash +++ b/misc/completion/be.bash @@ -1,12 +1,16 @@ #!/usr/bin/env python from distutils.core import setup +from libbe import _version + +rev_id = _version.version_info["revision_id"] +rev_date = rev_id.split("-")[1] setup( name='Bugs Everywhere', - version='0.0.193', + version=rev_date, description='Bugtracker built on distributed revision control', - url='http://panoramicfeedback.com/opensource/', + url='http://bugseverywhere.org/', packages=['becommands', 'libbe'], scripts=['be'], data_files=[ diff --git a/test_usage.sh b/test_usage.sh index b2e2cab..13be2ff 100755 --- a/test_usage.sh +++ b/test_usage.sh @@ -18,6 +18,8 @@ set -v # verbose, echo commands to stdout exec 6>&2 # save stderr to file descriptor 6 exec 2>&1 # fd 2 now writes to stdout +ONLY_TEST_COMMIT="true" + if [ $# -gt 1 ] then echo "usage: test_usage.sh [RCS]" @@ -75,8 +77,8 @@ then darcs init elif [ "$RCS" == "git" ] then - NAME=`git-config user.name` - EMAIL=`git-config user.email` + NAME=`git config user.name` + EMAIL=`git config user.email` ID="$NAME <$EMAIL>" git init elif [ "$RCS" == "hg" ] @@ -124,7 +126,16 @@ BUGB=`echo "$OUT" | sed -n 's/Created bug with ID //p'` be comment $BUGB "Blissfully unaware of a similar bug" be merge $BUG $BUGB # join BUGB to BUG be show $BUG # show bug details & comments +# you can also export/import XML bugs/comments +OUT=`be new 'yet more fun'` +BUGC=`echo "$OUT" | sed -n 's/Created bug with ID //p'` +be comment $BUGC "The ants go marching..." +be show --xml $BUGC | be comment --xml ${BUG}:2 - be remove $BUG # decide that you don't like that bug after all +be commit "You can even commit using BE" +be commit --allow-empty "And you can add empty commits if you like" +be commit "But this will fail" || echo "Failed" + cd / rm -rf $TESTDIR diff --git a/update_copyright.sh b/update_copyright.sh index 260cb3b..28eb0e0 100755 --- a/update_copyright.sh +++ b/update_copyright.sh @@ -1,10 +1,62 @@ #!/bin/bash # +# Copyright (C) 2009 W. Trevor King <wking@drexel.edu> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + # Update copyright information in source code with information from # the bzr repository. Run from the BE repository root. +# +# Replaces everything starting with '^# Copyright' and continuing with +# '^#' with an auto-generated copyright blurb. If you want to add +# #-commented material after a copyright blurb, please insert a blank +# line between the blurb and your comment (as in this file), so the +# next run of update_copyright.sh doesn't clobber your comment. +# +# usage: update_copyright.sh [files ...] +# +# If no files are given, a list of files to update is generated +# automatically. set -o pipefail +if [ $# -gt 0 ]; then + FILES="$*" +else + FILES=`grep -rc "# Copyright" . | grep -v ':0$' | cut -d: -f1` +fi + +COPYRIGHT_TEXT="# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA." +# escape newlines and special characters +SED_RM_TRAIL_END='s/[\]n$//' # strip trailing newline escape +SED_ESC_SPECIAL='s/\([()/]\)/\\\1/g' # escape special characters +ESCAPED_TEXT=`echo "$COPYRIGHT_TEXT" | awk '{printf("%s\\\\n", $0)}' | sed "$SED_RM_TRAIL_END" | sed "$SED_ESC_SPECIAL"` + # adjust the AUTHORS file AUTHORS=`bzr log | grep '^ *committer\|^ *author' | cut -d: -f2 | sed 's/ <.*//;s/^ *//' | sort | uniq` AUTHORS=`echo "$AUTHORS" | grep -v 'j\^\|wking\|John Doe'` # remove non-names @@ -12,7 +64,6 @@ echo "Bugs Everywhere was written by:" > AUTHORS echo "$AUTHORS" >> AUTHORS CURRENT_YEAR=`date +"%Y"` -FILES=`grep -rc "# Copyright" . | grep -v ':0$' | cut -d: -f1` TMP=`mktemp BE_update_copyright.XXXXXXX` for file in $FILES @@ -91,10 +142,10 @@ do COPYRIGHT=`echo "$COPYRIGHT\\n# $DATE_SPACE $AUTHOR"` done < <(echo "$OTHER_AUTHORS") fi - echo -e "$COPYRIGHT" + COPYRIGHT=`echo "$COPYRIGHT\\n$ESCAPED_TEXT"` # Strip old copyright info and replace with tag - awk 'BEGIN{incopy=0}{if(match($0, "^# Copyright")>0){incopy=1; print "-xyz-COPYRIGHT-zyx-"}else{if(incopy=0){print $0}else{if(match($0, "^# ")==0){incopy=0; print $0}}}}' "$file" > "$TMP" + awk 'BEGIN{incopy==0}{if(match($0, "^# Copyright")>0){incopy=1; print "-xyz-COPYRIGHT-zyx-"}else{if(incopy==0){print $0}else{if(match($0, "^#")==0){incopy=0; print $0}}}}' "$file" > "$TMP" # Replace tag in with new string sed -i "s/^-xyz-COPYRIGHT-zyx-$/$COPYRIGHT/" "$TMP" |