Merge lp:~lifeless/bzr-builder/bug-469874 into lp:~james-w/bzr-builder/trunk-old

Proposed by Robert Collins
Status: Merged
Merge reported by: James Westby
Merged at revision: not available
Proposed branch: lp:~lifeless/bzr-builder/bug-469874
Merge into: lp:~james-w/bzr-builder/trunk-old
Diff against target: 324 lines (+132/-66)
4 files modified
__init__.py (+91/-58)
recipe.py (+6/-2)
tests/test_blackbox.py (+24/-5)
tests/test_recipe.py (+11/-1)
To merge this branch: bzr merge lp:~lifeless/bzr-builder/bug-469874
Reviewer Review Type Date Requested Status
James Westby Approve
Review via email: mp+14686@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

This permits recipes that need less gardening.

lp:~lifeless/bzr-builder/bug-469874 updated
59. By Robert Collins

Merge trunk

Revision history for this message
James Westby (james-w) wrote :

A few comments that could be fixed when merging:

  * I don't like "debupstream," as I think it's slightly ambiguous,
    e.g. is it the upstream part of the debian upstream version number?
    I would go for "dch" I think.
  * "{debupstream}" is repeated lots of times in the code. Sticking it
    in a variable somewhere would be good, probably pulling out the others
    too.
  * BaseRecipeBranch has methods for substituting time and revnos, but this
    does it externally. I think a method should be used for consistency.

18 + raise errors.BzrCommandError("No previous changelog to "
19 + "take the upstream version from - {debupstream} was "
20 + "used.")

I'd replace "-" with "as".

62 + def _substitute_stuff

I don't like the use of "stuff", and the method does more than just substitute.

176 + finally:
177 + # package_dir -> working_directory
178 + os.rename(package_dir, working_directory)

Should exceptions from the rename be caught, warned about, then swallowed
if handling an exception?
We have had issues in the past with errors in unwind code masking the real
issue.

I have some uneasiness about the whole idea, given that you have to look
at the built tree to know the version number, but I think it is worth it.
Looking in the revision trees at resolve_revisions time would be possible,
but knowing which version will actually end up on disk isn't really possible.

The only big question I have is whether it should be possible/enforced
to use "{dch:branch-id}" here. Then you could rid the concern above
if it was enforced. I'm not sure whether you would want to use a changelog
from a branch that wasn't going to put the changelog on disk though.

I think this is good to land with the tweaks above, and we can always
bump format to enforce a branch id if it is a good idea.

I'm not sure whether we should be bumping format for the addition of a
{} variable. I think we should.

Thanks,

James

Revision history for this message
James Westby (james-w) wrote :

Voting needs-fixing, but unless we bump the format version the
tweaks could be made on merging.

Thanks,

James

review: Needs Fixing
Revision history for this message
Robert Collins (lifeless) wrote :

On Mon, 2009-11-23 at 16:35 +0000, James Westby wrote:
> A few comments that could be fixed when merging:
>
> * I don't like "debupstream," as I think it's slightly ambiguous,
> e.g. is it the upstream part of the debian upstream version number?
> I would go for "dch" I think.

Do you mean 'dchupstream' ? Just dch would be very unclear I think.

> * "{debupstream}" is repeated lots of times in the code. Sticking it
> in a variable somewhere would be good, probably pulling out the others
> too.

Sure.

> * BaseRecipeBranch has methods for substituting time and revnos, but this
> does it externally. I think a method should be used for consistency.

We can do that as well; BRB doesn't really have strong encapsulation of
this though, and as this substitution happens after building it didn't
seem to fit to me, BRB seemed to want to work on the model, this is on
the concrete contents.

> 18 + raise errors.BzrCommandError("No previous changelog to "
> 19 + "take the upstream version from - {debupstream} was "
> 20 + "used.")
>
> I'd replace "-" with "as".
>
> 62 + def _substitute_stuff
>
> I don't like the use of "stuff", and the method does more than just substitute.

Yeah, we didn't know what to call it. Suggestions welcome.

> 176 + finally:
> 177 + # package_dir -> working_directory
> 178 + os.rename(package_dir, working_directory)
>
> Should exceptions from the rename be caught, warned about, then swallowed
> if handling an exception?
> We have had issues in the past with errors in unwind code masking the real
> issue.

We could, but thats really not trivial to do well. There is some
supporting code in bzr core to help with this now. OTOH if the rename
fails I think people should know about it: package_dir will be in the
way of later runs.

> I have some uneasiness about the whole idea, given that you have to look
> at the built tree to know the version number, but I think it is worth it.
> Looking in the revision trees at resolve_revisions time would be possible,
> but knowing which version will actually end up on disk isn't really possible.

You'd have to do the merge first. And with the also desired 'get version
from configure' feature you'd have to simulate m4/shell ;).

> The only big question I have is whether it should be possible/enforced
> to use "{dch:branch-id}" here. Then you could rid the concern above
> if it was enforced. I'm not sure whether you would want to use a changelog
> from a branch that wasn't going to put the changelog on disk though.

I don't know what {dch:branch-id} would mean.

> I think this is good to land with the tweaks above, and we can always
> bump format to enforce a branch id if it is a good idea.
>
> I'm not sure whether we should be bumping format for the addition of a
> {} variable. I think we should.

I don't think you need to:
Old versions will always error (they want the string fully substituted)
New versions will work.

-Rob

Revision history for this message
James Westby (james-w) wrote :

On Mon Nov 23 21:03:09 UTC 2009 Robert Collins wrote:
> On Mon, 2009-11-23 at 16:35 +0000, James Westby wrote:
> > A few comments that could be fixed when merging:
> >
> > * I don't like "debupstream," as I think it's slightly ambiguous,
> > e.g. is it the upstream part of the debian upstream version number?
> > I would go for "dch" I think.
>
> Do you mean 'dchupstream' ? Just dch would be very unclear I think.

No, I meant 'dch', being a fairly standard abbreviation of 'debian changelog.'
'debchangelog' would work for me too, it's the 'upstream' part that is
mainly confusing to me.

> > 18 + raise errors.BzrCommandError("No previous changelog to "
> > 19 + "take the upstream version from - {debupstream} was "
> > 20 + "used.")
> >
> > I'd replace "-" with "as".
> >
> > 62 + def _substitute_stuff
> >
> > I don't like the use of "stuff", and the method does more than just substitute.
>
> Yeah, we didn't know what to call it. Suggestions welcome.

Something like "get_prepared_branch_from_recipe" would seem to be closer to
the functions it performs.

> > 176 + finally:
> > 177 + # package_dir -> working_directory
> > 178 + os.rename(package_dir, working_directory)
> >
> > Should exceptions from the rename be caught, warned about, then swallowed
> > if handling an exception?
> > We have had issues in the past with errors in unwind code masking the real
> > issue.
>
> We could, but thats really not trivial to do well. There is some
> supporting code in bzr core to help with this now. OTOH if the rename
> fails I think people should know about it: package_dir will be in the
> way of later runs.

Well, the way it is currently coded, the user will only know about one
of the issues, if you want them to know about both then we should write
it such that they will know about both (as I suggested).

> > The only big question I have is whether it should be possible/enforced
> > to use "{dch:branch-id}" here. Then you could rid the concern above
> > if it was enforced. I'm not sure whether you would want to use a changelog
> > from a branch that wasn't going to put the changelog on disk though.
>
> I don't know what {dch:branch-id} would mean.

Get the tree of the desired revision of the branch known as 'branch-id' in the
recipe, read the debian/changelog in that, and extract the version from there.

Thanks,

James

Revision history for this message
Robert Collins (lifeless) wrote :

On Mon, 2009-11-23 at 21:45 +0000, James Westby wrote:
> On Mon Nov 23 21:03:09 UTC 2009 Robert Collins wrote:
> > On Mon, 2009-11-23 at 16:35 +0000, James Westby wrote:
> > > A few comments that could be fixed when merging:
> > >
> > > * I don't like "debupstream," as I think it's slightly ambiguous,
> > > e.g. is it the upstream part of the debian upstream version number?
> > > I would go for "dch" I think.
> >
> > Do you mean 'dchupstream' ? Just dch would be very unclear I think.
>
> No, I meant 'dch', being a fairly standard abbreviation of 'debian changelog.'
> 'debchangelog' would work for me too, it's the 'upstream' part that is
> mainly confusing to me.

Debian version numbers are structured and contain an upstream component
and a debian specific component. The code in this patch only uses the
upstream component. Both 'upstream' and 'debian changelog' are key
concepts to communicate in the {} variable name. Just either would be
incorrect and misleading.

> > > The only big question I have is whether it should be possible/enforced
> > > to use "{dch:branch-id}" here. Then you could rid the concern above
> > > if it was enforced. I'm not sure whether you would want to use a changelog
> > > from a branch that wasn't going to put the changelog on disk though.
> >
> > I don't know what {dch:branch-id} would mean.
>
> Get the tree of the desired revision of the branch known as 'branch-id' in the
> recipe, read the debian/changelog in that, and extract the version from there.

I think that that would be more awkward, because you'd have to know
where the final changelog was coming from.

-Rob

Revision history for this message
James Westby (james-w) wrote :

On Mon Nov 23 21:57:10 UTC 2009 Robert Collins wrote:
> Debian version numbers are structured and contain an upstream component
> and a debian specific component. The code in this patch only uses the
> upstream component. Both 'upstream' and 'debian changelog' are key
> concepts to communicate in the {} variable name. Just either would be
> incorrect and misleading.

In that case then your name makes more sense. I said it was confusing,
and gave exactly that as the possible interpretation, as I didn't think
it was that. I thought it would take the whole version number.

I think this discussion shows that the change needs some tweaks to the
docs to explain what it is and when you would use it.

Thanks,

James

Revision history for this message
James Westby (james-w) wrote :

Fixes done in a local branch and merged.

Thanks,

James

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '__init__.py'
2--- __init__.py 2009-10-23 13:14:30 +0000
3+++ __init__.py 2009-11-13 06:05:22 +0000
4@@ -272,11 +272,19 @@
5 distribution = cl._blocks[0].distributions.split()[0]
6 if package is None:
7 package = cl._blocks[0].package
8+ if "{debupstream}" in base_branch.deb_version:
9+ cl_version = cl._blocks[0].version
10+ base_branch.deb_version = base_branch.deb_version.replace(
11+ "{debupstream}", cl_version.upstream_version)
12 else:
13 if package is None:
14 raise errors.BzrCommandError("No previous changelog to "
15 "take the package name from, and --package not "
16 "specified.")
17+ if "{debupstream}" in base_branch.deb_version:
18+ raise errors.BzrCommandError("No previous changelog to "
19+ "take the upstream version from - {debupstream} was "
20+ "used.")
21 if distribution is None:
22 distribution = "jaunty"
23 # Use debian packaging environment variables
24@@ -338,33 +346,6 @@
25 "%s" % output)
26
27
28-def get_base_branch(recipe_file, if_changed_from=None):
29- base_branch = get_branch_from_recipe_file(recipe_file)
30- time = datetime.datetime.utcnow()
31- base_branch.substitute_time(time)
32- old_recipe = None
33- if if_changed_from is not None:
34- old_recipe = get_old_recipe(if_changed_from)
35- changed = resolve_revisions(base_branch, if_changed_from=old_recipe)
36- if not changed:
37- return None
38- return base_branch
39-
40-
41-def build_one_recipe(recipe_file, working_directory, manifest=None,
42- if_changed_from=None):
43- base_branch = get_base_branch(recipe_file, if_changed_from=if_changed_from)
44- if base_branch is None:
45- trace.note("Unchanged")
46- return 0
47- build_tree(base_branch, working_directory)
48- if manifest is not None:
49- write_manifest_to_path(manifest, base_branch)
50- else:
51- write_manifest_to_path(os.path.join(working_directory,
52- "bzr-builder.manifest"), base_branch)
53-
54-
55 class cmd_build(Command):
56 """Build a tree based on a 'recipe'.
57
58@@ -381,10 +362,36 @@
59 "to that specified in the specified manifest."),
60 ]
61
62+ def _substitute_stuff(self, recipe_file, if_changed_from):
63+ """Common code to substitute stuff
64+
65+ :return: A tuple with (retcode, base_branch). If retcode is None
66+ then the command execution should continue.
67+ """
68+ base_branch = get_branch_from_recipe_file(recipe_file)
69+ time = datetime.datetime.utcnow()
70+ base_branch.substitute_time(time)
71+ old_recipe = None
72+ if if_changed_from is not None:
73+ old_recipe = get_old_recipe(if_changed_from)
74+ # Save the unsubstituted version for dailydeb.
75+ self._template_version = base_branch.deb_version
76+ changed = resolve_revisions(base_branch, if_changed_from=old_recipe)
77+ if not changed:
78+ trace.note("Unchanged")
79+ return 0, base_branch
80+ return None, base_branch
81+
82 def run(self, recipe_file, working_directory, manifest=None,
83 if_changed_from=None):
84- return build_one_recipe(recipe_file, working_directory,
85- manifest=manifest, if_changed_from=if_changed_from)
86+ result, base_branch = self._substitute_stuff(recipe_file,
87+ if_changed_from)
88+ if result is not None:
89+ return result
90+ manifest_path = manifest or os.path.join(working_directory,
91+ "bzr-builder.manifest")
92+ build_tree(base_branch, working_directory)
93+ write_manifest_to_path(manifest_path, base_branch)
94
95
96 register_command(cmd_build)
97@@ -418,52 +425,78 @@
98 "must be specified if you use --dput."),
99 ]
100
101- takes_args = ["recipe_file", "working_directory?"]
102+ takes_args = ["recipe_file", "working_basedir?"]
103
104- def run(self, recipe_file, working_directory=None, manifest=None,
105+ def run(self, recipe_file, working_basedir=None, manifest=None,
106 if_changed_from=None, package=None, distribution=None,
107 dput=None, key_id=None):
108
109 if dput is not None and key_id is None:
110 raise errors.BzrCommandError("You must specify --key-id if you "
111 "specify --dput.")
112-
113- base_branch = get_base_branch(recipe_file, if_changed_from=if_changed_from)
114- if base_branch is None:
115- trace.note("Unchanged")
116- return 0
117- recipe_name = os.path.basename(recipe_file)
118- if recipe_name.endswith(".recipe"):
119- recipe_name = recipe_name[:-len(".recipe")]
120- version = base_branch.deb_version
121- if "-" in version:
122- version = version[:version.rindex("-")]
123- package_basedir = "%s-%s" % (package or recipe_name, version)
124- if working_directory is None:
125+ result, base_branch = self._substitute_stuff(recipe_file,
126+ if_changed_from)
127+ if result is not None:
128+ return result
129+ if working_basedir is None:
130 temp_dir = tempfile.mkdtemp(prefix="bzr-builder-")
131- working_directory = temp_dir
132+ working_basedir = temp_dir
133 else:
134 temp_dir = None
135- if not os.path.exists(working_directory):
136- os.makedirs(working_directory)
137+ if not os.path.exists(working_basedir):
138+ os.makedirs(working_basedir)
139+ self._calculate_package_name(recipe_file, package)
140+ working_directory = os.path.join(working_basedir,
141+ "%s-%s" % (self._package_name, self._template_version))
142 try:
143- package_dir = os.path.join(working_directory, package_basedir)
144- build_tree(base_branch, package_dir)
145- write_manifest_to_path(os.path.join(package_dir, "debian",
146- "bzr-builder.manifest"), base_branch)
147- add_changelog_entry(base_branch, package_dir,
148- distribution=distribution, package=package)
149- build_source_package(package_dir)
150- if key_id is not None:
151- sign_source_package(package_dir, key_id)
152- if dput is not None:
153- dput_source_package(package_dir, dput)
154+ # we want to use a consistent package_dir always to support
155+ # updates in place, but debuild etc want PACKAGE-UPSTREAMVERSION
156+ # on disk, so we build_tree with the unsubstituted version number
157+ # and do a final rename-to step before calling into debian build
158+ # tools. We then rename the working dir back.
159+ manifest_path = os.path.join(working_directory, "debian",
160+ "bzr-builder.manifest")
161+ build_tree(base_branch, working_directory)
162+ write_manifest_to_path(manifest_path, base_branch)
163+ # Add changelog also substitutes {debupstream}.
164+ add_changelog_entry(base_branch, working_directory,
165+ distribution=distribution, package=package)
166+ package_dir = self._calculate_package_dir(base_branch,
167+ working_basedir)
168+ # working_directory -> package_dir: after this debian stuff works.
169+ os.rename(working_directory, package_dir)
170+ try:
171+ build_source_package(package_dir)
172+ if key_id is not None:
173+ sign_source_package(package_dir, key_id)
174+ if dput is not None:
175+ dput_source_package(package_dir, dput)
176+ finally:
177+ # package_dir -> working_directory
178+ os.rename(package_dir, working_directory)
179+ # Note that this may write a second manifest.
180 if manifest is not None:
181 write_manifest_to_path(manifest, base_branch)
182 finally:
183 if temp_dir is not None:
184 shutil.rmtree(temp_dir)
185
186+ def _calculate_package_dir(self, base_branch, working_basedir):
187+ """Calculate the directory name that should be used while debuilding."""
188+ version = base_branch.deb_version
189+ if "-" in version:
190+ version = version[:version.rindex("-")]
191+ package_basedir = "%s-%s" % (self._package_name, version)
192+ package_dir = os.path.join(working_basedir, package_basedir)
193+ return package_dir
194+
195+ def _calculate_package_name(self, recipe_file, package):
196+ """Calculate the directory name that should be used while debuilding."""
197+ recipe_name = os.path.basename(recipe_file)
198+ if recipe_name.endswith(".recipe"):
199+ recipe_name = recipe_name[:-len(".recipe")]
200+ self._package_name = package or recipe_name
201+
202
203 register_command(cmd_dailydeb)
204
205
206=== modified file 'recipe.py'
207--- recipe.py 2009-08-18 21:16:17 +0000
208+++ recipe.py 2009-11-13 06:05:22 +0000
209@@ -226,7 +226,7 @@
210 def get_revno():
211 try:
212 revno = br_from.revision_id_to_revno(revision_id)
213- return "%s" % revno
214+ return str(revno)
215 except errors.NoSuchRevision:
216 # We need to load and use the full revno map after all
217 result = br_from.get_revision_id_to_revno_map().get(
218@@ -288,7 +288,11 @@
219 if_changed_from=if_changed_from_revisions)
220 if not changed:
221 changed = changed_revisions
222- if "{" in base_branch.deb_version:
223+ ok_to_preserve = ["{debupstream}"]
224+ checked_version = base_branch.deb_version
225+ for token in ok_to_preserve:
226+ checked_version = checked_version.replace(token, "")
227+ if "{" in checked_version:
228 raise errors.BzrCommandError("deb-version not fully "
229 "expanded: %s" % base_branch.deb_version)
230 if if_changed_from is not None and not changed:
231
232=== modified file 'tests/test_blackbox.py'
233--- tests/test_blackbox.py 2009-09-21 18:07:53 +0000
234+++ tests/test_blackbox.py 2009-11-13 06:05:22 +0000
235@@ -155,6 +155,7 @@
236
237 def test_cmd_dailydeb_no_work_dir(self):
238 #TODO: define a test feature for debuild and require it here.
239+ self.permit_dir('/') # Allow the made working dir to be accessed.
240 source = self.make_branch_and_tree("source")
241 self.build_tree(["source/a", "source/debian/"])
242 self.build_tree_contents([("source/debian/rules",
243@@ -170,6 +171,7 @@
244
245 def test_cmd_dailydeb_if_changed_from_non_existant(self):
246 #TODO: define a test feature for debuild and require it here.
247+ self.permit_dir('/') # Allow the made working dir to be accessed.
248 source = self.make_branch_and_tree("source")
249 self.build_tree(["source/a", "source/debian/"])
250 self.build_tree_contents([("source/debian/rules",
251@@ -183,8 +185,7 @@
252 out, err = self.run_bzr("dailydeb test.recipe "
253 "--manifest manifest --package foo --if-changed-from bar")
254
255- def test_cmd_dailydeb_with_package_from_changelog(self):
256- #TODO: define a test feature for debuild and require it here.
257+ def make_simple_package(self):
258 source = self.make_branch_and_tree("source")
259 self.build_tree(["source/a", "source/debian/"])
260 cl_contents = ("package (0.1-1) unstable; urgency=low\n * foo\n"
261@@ -197,7 +198,12 @@
262 ("source/debian/changelog", cl_contents)])
263 source.add(["a", "debian/", "debian/rules", "debian/control",
264 "debian/changelog"])
265- revid = source.commit("one")
266+ source.commit("one")
267+ return source
268+
269+ def test_cmd_dailydeb_with_package_from_changelog(self):
270+ #TODO: define a test feature for debuild and require it here.
271+ source = self.make_simple_package()
272 self.build_tree_contents([("test.recipe", "# bzr-builder format 0.1 "
273 "deb-version 1\nsource 1\n")])
274 out, err = self.run_bzr("dailydeb test.recipe "
275@@ -209,5 +215,18 @@
276 actual_cl_contents = f.read()
277 finally:
278 f.close()
279- self.assertEqual(new_cl_contents,
280- actual_cl_contents[:len(new_cl_contents)])
281+ self.assertStartsWith(actual_cl_contents, new_cl_contents)
282+
283+ def test_cmd_dailydeb_with_upstream_version_from_changelog(self):
284+ source = self.make_simple_package()
285+ self.build_tree_contents([("test.recipe", "# bzr-builder format 0.1 "
286+ "deb-version {debupstream}-2\nsource 1\n")])
287+ out, err = self.run_bzr("dailydeb test.recipe working")
288+ new_cl_contents = ("package (0.1-2) unstable; urgency=low\n\n"
289+ " * Auto build.\n\n -- M. Maintainer <maint@maint.org> ")
290+ f = open("working/test-{debupstream}-2/debian/changelog")
291+ try:
292+ actual_cl_contents = f.read()
293+ finally:
294+ f.close()
295+ self.assertStartsWith(actual_cl_contents, new_cl_contents)
296
297=== modified file 'tests/test_recipe.py'
298--- tests/test_recipe.py 2009-08-18 21:25:32 +0000
299+++ tests/test_recipe.py 2009-11-13 06:05:22 +0000
300@@ -714,7 +714,7 @@
301 def test_substitute(self):
302 source =self.make_branch_and_tree("source")
303 revid1 = source.commit("one")
304- revid2 = source.commit("two")
305+ source.commit("two")
306 branch1 = BaseRecipeBranch("source",
307 "{revno}-{revno:packaging}", 0.2, revspec="1")
308 branch2 = RecipeBranch("packaging", "source")
309@@ -725,6 +725,16 @@
310 self.assertEqual("1", branch1.revspec)
311 self.assertEqual("1-2", branch1.deb_version)
312
313+ def test_substitute_supports_debupstream(self):
314+ # resolve_revisions should leave debupstream parameters alone and not
315+ # complain.
316+ source =self.make_branch_and_tree("source")
317+ source.commit("one")
318+ source.commit("two")
319+ branch1 = BaseRecipeBranch("source", "{debupstream}-{revno}", 0.2)
320+ resolve_revisions(branch1)
321+ self.assertEqual("{debupstream}-2", branch1.deb_version)
322+
323
324 class BuildManifestTests(TestCaseInTempDir):
325

Subscribers

People subscribed via source and target branches