Rev 1: Create the BranchFeed plugin, which creates ATOM feeds during commit push in file:///home/robertc/source/baz/plugins/branchrss/trunk/
Robert Collins
robertc at robertcollins.net
Mon Jan 29 17:50:01 GMT 2007
------------------------------------------------------------
revno: 1
revision-id: robertc at robertcollins.net-20070129175001-422ce4405f446d40
committer: Robert Collins <robertc at robertcollins.net>
branch nick: trunk
timestamp: Tue 2007-01-30 04:50:01 +1100
message:
Create the BranchFeed plugin, which creates ATOM feeds during commit push
and pull for bzr 0.15 or greater.
added:
README readme-20070121145705-sx0o8hd51l0r67r1-1
TODO todo-20070121145705-sx0o8hd51l0r67r1-2
__init__.py __init__.py-20070121145705-sx0o8hd51l0r67r1-3
branch_feed.py branch_feed.py-20070121145705-sx0o8hd51l0r67r1-4
tests/ tests-20070121145705-sx0o8hd51l0r67r1-5
tests/__init__.py __init__.py-20070121145705-sx0o8hd51l0r67r1-6
tests/test_branch_feed.py test_branch_feed.py-20070121145705-sx0o8hd51l0r67r1-7
tests/test_hook.py test_hook.py-20070129172729-withaw4ffooledsr-1
=== added file 'README'
--- a/README 1970-01-01 00:00:00 +0000
+++ b/README 2007-01-29 17:50:01 +0000
@@ -0,0 +1,9 @@
+Branch Feed generator
+_____________________
+
+This plugin creates a branch.atom file after every commit, push and pull. The
+amount of detail contained in the branch.atom file currently is always 20
+items, but it is planned to be made configurable.
+
+There is a bugtracker, specification tracker etc at
+https://launchpad.net/bzr-branchfeed/.
=== added file 'TODO'
--- a/TODO 1970-01-01 00:00:00 +0000
+++ b/TODO 2007-01-29 17:50:01 +0000
@@ -0,0 +1,3 @@
+ * proper escaping of the commit message
+ * Allow the filename and number of items to be configured via
+ branch.conf/branches.conf etc.
=== added file '__init__.py'
--- a/__init__.py 1970-01-01 00:00:00 +0000
+++ b/__init__.py 2007-01-29 17:50:01 +0000
@@ -0,0 +1,35 @@
+# BranchFeed: A plugin for bzr to generate rss feeds for branches.
+# Copyright (C) 2007 Canonical Limited.
+# Author: Robert Collins.
+#
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""BranchFeed is a plugin for bzr to generate rss feeds for branches.
+
+This plugin creates a branch.atom file after every commit, push and pull. The
+amount of detail contained in the branch.atom file currently is always 20
+items, but it is planned to be made configurable.
+"""
+
+from branch_feed import install_hooks
+
+
+def test_suite():
+ import bzrlib.plugins.branchfeed.tests
+ return bzrlib.plugins.branchfeed.tests.test_suite()
+
+
+install_hooks()
=== added file 'branch_feed.py'
--- a/branch_feed.py 1970-01-01 00:00:00 +0000
+++ b/branch_feed.py 2007-01-29 17:50:01 +0000
@@ -0,0 +1,123 @@
+# BranchFeed: A plugin for bzr to generate rss feeds for branches.
+# Copyright (C) 2007 Canonical Limited.
+# Author: Robert Collins.
+#
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""Branch feed generator."""
+
+import itertools
+from string import Template
+import time
+
+from bzrlib.branch import Branch
+from bzrlib.osutils import format_date
+
+
+def install_hooks():
+ """Install the BranchFeed hooks into bzrlib."""
+ Branch.hooks['set_rh'].append(set_rh_hook)
+
+
+def set_rh_hook(branch, rev_history):
+ """Update branch's atom feed from the set_rh hook."""
+ BranchFeed(branch).update()
+
+
+class BranchFeed(object):
+ """A Branch Feed.
+
+ BranchFeeds can create RSS content from a branch.
+
+ Public attributes:
+ now: a time tuple for the time that the feed is executing.
+ item_limit: The maximum number of commit items to include. Set to -1 to
+ disable.
+ """
+
+ ATOM_HEAD_TEMPLATE = u"""<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title>$title</title>
+ <id>$id</id>
+ <updated>$updated</updated>
+ <generator uri="http://atonie.org/code/atomlog" />
+"""
+ ATOM_ITEM_TEMPLATE = u"""
+<entry>
+ <title>$title</title>
+ <id>$id</id>
+ <author>
+ <name>$author</name>
+ </author>
+ <updated>$updated</updated>
+ <content type="xhtml">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>$content</p>
+ </div>
+ </content>
+</entry>
+"""
+
+ def __init__(self, branch, now=None):
+ """Create a BranchFeed on branch.
+
+ :param branch: The branch to generate a feed for.
+ :param now: The current time since epochs in seconds.
+ If not supplied, time.time is used.
+ """
+ self.branch = branch
+ self.item_limit = 20
+ self.now = now or time.time()
+
+ def iter_revisions(self):
+ """Walk the revisions to be included."""
+ history = self.branch.revision_history()
+ tip = len(history)
+ if self.item_limit != -1:
+ history = history[-self.item_limit:]
+ return itertools.izip(xrange(tip, 0, -1),
+ reversed(self.branch.repository.get_revisions(history)))
+
+ def generate(self):
+ """Generate the feed content.
+
+ The title of the feed is pulled from the branch nick, and the
+ id from the branch URL.
+
+ :return: a bytestring containing the feed.
+ """
+ template = Template(self.ATOM_HEAD_TEMPLATE)
+ feed = template.substitute({
+ 'title':self.branch.nick,
+ 'id':str(self.branch.base),
+ 'updated':format_date(self.now, 0, 'utc', '%Y-%m-%dT%H:%M:%SZ', show_offset=False)
+ })
+ template = Template(self.ATOM_ITEM_TEMPLATE)
+ for revno, revision in self.iter_revisions():
+ feed += template.substitute({
+ 'title':'revision %s' % revno,
+ 'id':revision.revision_id,
+ 'author':revision.committer,
+ 'updated':format_date(revision.timestamp, revision.timezone or 0, 'utc', '%Y-%m-%dT%H:%M:%SZ', show_offset=False),
+ 'content':revision.message,
+ })
+ return feed.encode('utf8')
+
+ def update(self):
+ """Update the feed in the branch directory."""
+ # This is a little ugly: poking under the hood.
+ self.branch.control_files._transport.put_bytes(
+ 'branch.atom', self.generate())
=== added directory 'tests'
=== added file 'tests/__init__.py'
--- a/tests/__init__.py 1970-01-01 00:00:00 +0000
+++ b/tests/__init__.py 2007-01-29 17:50:01 +0000
@@ -0,0 +1,33 @@
+# BranchFeed: A plugin for bzr to generate rss feeds for branches.
+# Copyright (C) 2007 Canonical Limited.
+# Author: Robert Collins.
+#
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+
+"""Tests for the branchfeed plugin."""
+
+
+from bzrlib.tests.TestUtil import TestLoader, TestSuite
+
+
+def test_suite():
+ module_names = [
+ 'bzrlib.plugins.branchfeed.tests.test_branch_feed',
+ 'bzrlib.plugins.branchfeed.tests.test_hook',
+ ]
+ loader = TestLoader()
+ return loader.loadTestsFromModuleNames(module_names)
=== added file 'tests/test_branch_feed.py'
--- a/tests/test_branch_feed.py 1970-01-01 00:00:00 +0000
+++ b/tests/test_branch_feed.py 2007-01-29 17:50:01 +0000
@@ -0,0 +1,156 @@
+# BranchFeed: A plugin for bzr to generate rss feeds for branches.
+# Copyright (C) 2007 Canonical Limited.
+# Author: Robert Collins.
+#
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""Tests for the creation of the atom content."""
+
+import time
+
+from bzrlib.osutils import format_date
+from bzrlib.tests import TestCaseWithMemoryTransport
+
+from bzrlib.plugins.branchfeed.branch_feed import BranchFeed
+
+class TestGenerateAtom(TestCaseWithMemoryTransport):
+
+ def test_construct(self):
+ branch = self.make_branch(".")
+ branch.nick = "a nick"
+ feed = BranchFeed(branch)
+ self.assertNotEqual(feed.now, None)
+ feed = BranchFeed(branch, "now")
+ self.assertEqual(feed.now, "now")
+ self.assertEqual(feed.item_limit, 20)
+
+ def test_empty_branch(self):
+ branch = self.make_branch(".")
+ branch.nick = "a nick"
+ currenttime = time.time()
+ feed = BranchFeed(branch, now=currenttime)
+ content = feed.generate()
+ self.assertEqual("""<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title>a nick</title>
+ <id>%s</id>
+ <updated>%s</updated>
+ <generator uri="http://atonie.org/code/atomlog" />
+""" % (branch.base, format_date(currenttime, 0, 'utc', '%Y-%m-%dT%H:%M:%SZ', False)),
+ content)
+
+ def test_generate(self):
+ """Generate a feed."""
+ commitnow = time.time()
+ rev1time = commitnow - 60*60*24 - 1
+ rev2time = commitnow - 60*60*24 + 1
+ tree = self.make_branch_and_memory_tree('.')
+ tree.branch.nick = 'a nick'
+ tree.lock_write()
+ tree.add('')
+ # two commits to check outputting two records
+ tree.commit('1', rev_id="revid1", committer="author1 at localhost",
+ timestamp=rev1time, timezone = 0)
+ tree.commit('2', rev_id="revid2", committer="author2 at localhost",
+ timestamp=rev2time, timezone = 0)
+ tree.unlock()
+ branch = tree.branch
+ feed = BranchFeed(branch, now=commitnow)
+ content = feed.generate()
+ self.assertEqualDiff("""<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+ <title>a nick</title>
+ <id>%s</id>
+ <updated>%s</updated>
+ <generator uri="http://atonie.org/code/atomlog" />
+
+<entry>
+ <title>revision 2</title>
+ <id>revid2</id>
+ <author>
+ <name>author2 at localhost</name>
+ </author>
+ <updated>%s</updated>
+ <content type="xhtml">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>2</p>
+ </div>
+ </content>
+</entry>
+
+<entry>
+ <title>revision 1</title>
+ <id>revid1</id>
+ <author>
+ <name>author1 at localhost</name>
+ </author>
+ <updated>%s</updated>
+ <content type="xhtml">
+ <div xmlns="http://www.w3.org/1999/xhtml">
+ <p>1</p>
+ </div>
+ </content>
+</entry>
+""" % (branch.base,
+ format_date(commitnow, 0, 'utc', '%Y-%m-%dT%H:%M:%SZ', False),
+ format_date(rev2time, 0, 'utc', "%Y-%m-%dT%H:%M:%SZ", False),
+ format_date(rev1time, 0, 'utc', "%Y-%m-%dT%H:%M:%SZ", False),
+ ),
+ content)
+
+ def test_number_limited_branch(self):
+ commitnow = time.time()
+ tree = self.make_branch_and_memory_tree('.')
+ tree.lock_write()
+ tree.add('')
+ # two commits at the same time, which we then limit to select only
+ # the topologically latest by the item_limit.
+ tree.commit('rev one', rev_id="revid1", committer="author1 at localhost",
+ timestamp=commitnow, timezone = 0)
+ tree.commit('rev two', rev_id="revid2", committer="author2 at localhost",
+ timestamp=commitnow, timezone = 0)
+ tree.unlock()
+ branch = tree.branch
+ currenttime = time.time()
+ feed = BranchFeed(branch, now=currenttime)
+ feed.item_limit = 1
+ self.assertEqual(
+ zip([2], list(branch.repository.get_revisions(['revid2']))),
+ list(feed.iter_revisions()))
+
+ def test_ordering(self):
+ """iter_revisions should return the newest first."""
+ tree = self.make_branch_and_memory_tree('.')
+ tree.lock_write()
+ tree.add('')
+ tree.commit('1', rev_id="1")
+ tree.commit('2', rev_id="2")
+ tree.unlock()
+ branch = tree.branch
+ feed = BranchFeed(branch)
+ self.assertEqual(
+ zip([2, 1], list(branch.repository.get_revisions(['2', '1']))),
+ list(feed.iter_revisions()))
+
+ def test_update_branch(self):
+ """update() writes the generated content to the branch."""
+ branch = self.make_branch(".")
+ branch.nick = "a nick"
+ feed = BranchFeed(branch)
+ feed.update()
+ content = feed.generate()
+ self.assertEqualDiff(content,
+ branch.control_files._transport.get('branch.atom').read())
=== added file 'tests/test_hook.py'
--- a/tests/test_hook.py 1970-01-01 00:00:00 +0000
+++ b/tests/test_hook.py 2007-01-29 17:50:01 +0000
@@ -0,0 +1,59 @@
+# BranchFeed: A plugin for bzr to generate rss feeds for branches.
+# Copyright (C) 2007 Canonical Limited.
+# Author: Robert Collins.
+#
+# 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""Tests for the hook installation."""
+
+from bzrlib.branch import Branch
+from bzrlib.tests import TestCaseWithMemoryTransport
+
+from bzrlib.plugins.branchfeed import branch_feed
+
+
+class TestHooksAreMade(TestCaseWithMemoryTransport):
+
+ def test_set_rh_installed(self):
+ """The _preserved hooks should have set_rh in them."""
+ self.assertTrue(branch_feed.set_rh_hook in
+ self._preserved_hooks['set_rh'])
+
+ def test_install_hooks(self):
+ """branch_feed.set_rh_hook should be in Branch.hooks['set_rh']."""
+ branch_feed.install_hooks()
+ self.assertTrue(branch_feed.set_rh_hook in Branch.hooks['set_rh'])
+
+ def test_set_rh_hook(self):
+ """The set_rh_hook should create a BranchFeed and call update."""
+ # change the BranchFeed object in the branch_feed module to test
+ # the api use.
+ calls = []
+ class SampleFeed(object):
+
+ def __init__(self, branch):
+ calls.append(('init', branch))
+
+ def update(self):
+ calls.append(('update', ))
+
+ original_class = branch_feed.BranchFeed
+ try:
+ branch_feed.BranchFeed = SampleFeed
+ branch_feed.set_rh_hook('branch', 'history')
+ finally:
+ branch_feed.BranchFeed = original_class
+ self.assertEqual([('init', 'branch'), ('update', )], calls)
More information about the bazaar-commits
mailing list