Merge lp:~robru/cupstream2distro/no-source-build-deps into lp:cupstream2distro

Proposed by Robert Bruce Park on 2016-03-18
Status: Merged
Approved by: Robert Bruce Park on 2016-03-23
Approved revision: 1459
Merged at revision: 1384
Proposed branch: lp:~robru/cupstream2distro/no-source-build-deps
Merge into: lp:cupstream2distro
Diff against target: 758 lines (+146/-371)
20 files modified
Makefile (+2/-1)
chroot-tools/.pbuilderrc (+0/-41)
chroot-tools/buildsource-chroot (+0/-117)
chroot-tools/create-chroot (+0/-42)
chroot-tools/pbuilder-clean (+0/-8)
citrain/jenkins-templates/upgrade-chroot.xml.tmpl (+0/-58)
citrain/recipes/base.py (+1/-2)
citrain/recipes/merge.py (+1/-0)
citrain/recipes/secondary.py (+1/-0)
citrain/setup_citrain.py (+0/-1)
cupstream2distro/packagemanager.py (+13/-17)
cupstream2distro/settings.py (+1/-1)
files/sudoers.conf (+0/-3)
tests/unit/test_packagemanager.py (+43/-75)
tests/unit/test_recipe_base.py (+1/-3)
tests/unit/test_recipe_merge.py (+2/-0)
tests/unit/test_recipe_secondary.py (+2/-0)
tests/unit/test_script_setup_citrain.py (+0/-2)
tools/buildpackage.sh (+38/-0)
tools/run_hook.sh (+41/-0)
To merge this branch: bzr merge lp:~robru/cupstream2distro/no-source-build-deps
Reviewer Review Type Date Requested Status
Martin Pitt Approve on 2016-03-22
Robert Bruce Park (community) Approve on 2016-03-21
Review via email: mp+289446@code.launchpad.net

Commit Message

Stop installing build deps at source package build time.

To post a comment you must log in.
1385. By Robert Bruce Park on 2016-03-18

Drop unused var.

1386. By Robert Bruce Park on 2016-03-18

Add new tools.

1387. By Robert Bruce Park on 2016-03-18

Call aa-exec directly.

1388. By Robert Bruce Park on 2016-03-18

Run package hook for secondary builds too.

1389. By Robert Bruce Park on 2016-03-18

Fix docstring.

1390. By Robert Bruce Park on 2016-03-18

Fix tests.

1391. By Robert Bruce Park on 2016-03-18

100% tests.

1392. By Robert Bruce Park on 2016-03-18

Fix some docstrings.

1393. By Robert Bruce Park on 2016-03-18

Exclude debian dir from orig.tar.

1394. By Robert Bruce Park on 2016-03-18

Fix tools path.

1395. By Robert Bruce Park on 2016-03-18

Fix permissions.

1396. By Robert Bruce Park on 2016-03-18

Fix tar.

1397. By Robert Bruce Park on 2016-03-18

Iterate.

1398. By Robert Bruce Park on 2016-03-18

Log to stderr.

1399. By Robert Bruce Park on 2016-03-18

Bah

1400. By Robert Bruce Park on 2016-03-18

Lowercase non-env var.

1401. By Robert Bruce Park on 2016-03-18

--human-readable.

1402. By Robert Bruce Park on 2016-03-18

Try quilt format.

1403. By Robert Bruce Park on 2016-03-18

Fix upstream tarball name.

1404. By Robert Bruce Park on 2016-03-18

Include .bzr-builddeb in upstream tarball.

1405. By Robert Bruce Park on 2016-03-18

orig.tar.gz for now.

1406. By Robert Bruce Park on 2016-03-18

What happens when debian/source is missing?

1407. By Robert Bruce Park on 2016-03-18

dpkg-buildpackage -i

1408. By Robert Bruce Park on 2016-03-18

Ignore bzr-builddeb.

1409. By Robert Bruce Park on 2016-03-18

Ignore a lot more.

1410. By Robert Bruce Park on 2016-03-18

Add comments.

1411. By Robert Bruce Park on 2016-03-19

Add new deps.

1412. By Robert Bruce Park on 2016-03-19

Full path to aa-exec because $PATH is wrong.

1413. By Robert Bruce Park on 2016-03-19

Force apparmor_parser.

1414. By Robert Bruce Park on 2016-03-19

Fix path.

1415. By Robert Bruce Park on 2016-03-19

Allow find and xgettext.

1416. By Robert Bruce Park on 2016-03-19

Allow sort and uniq.

1417. By Robert Bruce Park on 2016-03-19

Allow readlink.

1418. By Robert Bruce Park on 2016-03-19

Define $SERIES env var.

Martin Pitt (pitti) wrote :

What's the purpose of these hooks? If they are meant to be able to install additional build dependencies to be able to build a source package, then this won't work. It seems they are meant to be limited to reading/writing files from their own source tree, but most of that could already be done in the bzr branch? Do you have an example what such a hook should do? Why is that coupled to not building the source in pbuilder?

I'm doubtful about the apparmor stuff and I found some smaller issues, see the inline comments.

review: Needs Fixing
Barry Warsaw (barry) :
Robert Bruce Park (robru) wrote :
Download full text (5.3 KiB)

On Mon, Mar 21, 2016 at 4:20 AM, Martin Pitt <email address hidden> wrote:
> What's the purpose of these hooks? If they are meant to be able to install additional build dependencies to be able to build a source package, then this won't work. It seems they are meant to be limited to reading/writing files from their own source tree, but most of that could already be done in the bzr branch? Do you have an example what such a hook should do? Why is that coupled to not building the source in pbuilder?

The purpose of these hooks is to allow people to munge debian/control
prior to source-build-time, eg, if you want to build vivid & xenial
builds out of the same source tree, but they build different package
names due to SONAME changes due to gcc abi breakage between vivid &
xenial. Here is one such example:

http://bazaar.launchpad.net/~robru/unity-scopes-api/vivid-tweaks/view/head:/debian/bileto_pre_release_hook

Another usage is to update translation templates automatically prior
to building:

http://bazaar.launchpad.net/~robru/unity8/pre_release_hook/view/head:/debian/bileto_pre_release_hook

> You don't use patchutils and diffstat anywhere. But you do need to add dpkg-dev.

Ah, sorry. This work was actually backported from a much larger branch
where I do use patchutils and diffstat, I wasn't paying attention when
I copied over the updated deps.

> It seems to me that creating and maintaining this list is quite some busy-work -- if the package hooks will actually get used for anything, then they will hit a wall fairly quickly. What's the point of restricting package hooks to only run some of these commands, but not others? sed and dash by themselves are powerful enough to emulate pretty much everything else. This hook runs as some "ci train" user, right?
>
> But consider your trust model: If you cannot trust the contents of branches, then with sed, sh, and shipping/reading/writing arbitrary files into the silos/** dir you can already do pretty much anything as that "CI train" user -- including messing up files from different silos!
>
> I think it would be both safer (and also avoid maintaining the above list) to create a temporary "silo-XXXXX" unix user, do the "unpack source/run hook" and the dpkg-buildpackage -S as that user, chown the resulting .dsc back to the CI train user and deluser the temporary silo-XXXX one again.

Hmmm, not sure. The silo dirs are short-lived (eg they are empty while
not actively being built, so the idea of a hook script being malicious
and attacking other silo dirs doesn't bother me that much. The main
reason for the apparmor profile is to prevent the script from being
able to read/write sensitive files in ~ and /tmp, eg to avoid leaking
GPG keys and other sensitive state, which the apparmor profile
successfully prevents.

What would a chown solution look like? Note that we explicitly do not
install build deps or run debian/rules from dpkg-buildpackage so
isolating that is not necessary, just the hook script. But we'd need
to chown the source tree to the temp user and then chown it back
after. Doesn't it require root to change the ownership of something
you don't own back to your own user? I guess we could make t...

Read more...

Robert Bruce Park (robru) wrote :
Download full text (4.1 KiB)

On Mon, Mar 21, 2016 at 7:45 AM, Barry Warsaw <email address hidden> wrote:
>> + rm -rf /etc/sudoers.d/citrain # TODO drop this transition
>
> Maybe prepend the line with - to ignore the error if the file doesn't exist, during the transition?
>
> https://ftp.gnu.org/old-gnu/Manuals/make-3.79.1/html_chapter/make_5.html#SEC48

'rm -rf does_not_exist' exits with 0 though?

> Is that safer and more robust in the long run than building the source package in a container, e.g. an schroot, lxc or other container? I agree with Martin about the current model, so his suggestion does improve things, and there may be problems with getting the artifacts of the source build out of whatever container you create, but it feels cleaner to use a container than to do the temporary user creation. But anyway if you do that, be sure to handle any errors and clear out any allocated resources (e.g. the temp silo-XXXX user) when things fail.

No, containers/chroots is what we already were doing, and it didn't
make any sense. We spent a lot of time debootstrapping whole systems
and keeping them updated and installing build dependencies that were
entirely unused. The apparmor profile is actually significantly more
secure than the chroot it's replacing because if you look at the
chroot it just bindmounts ~ which means any malicious `debian/rules
clean` could actually leak our GPG keys, but the apparmor profile does
not allow access to ~.

> The other thing to consider is local reproducibility. With all automated systems (e.g. autopkgtest, source->binary builds), things *will* fail and they will fail in mysterious ways. IMHO it's always better to have a system where the environment is both well-documented and locally reproducible so developers can try to debug the problems on their own. Otherwise, mysterious failures will fall on you to debug and that doesn't scale.

I believe this approach is more reproducible because it is simpler and
more direct than the previous approach. Somebody trying to reproduce
the old way would need a chroot, but reproducing the new way just
requires dpkg-buildpackage. And the code is open of course so they can
view it and run what it does with significantly less setup.

But dpkg-buildpackage isn't really something that can fail because it
doesn't invoke any custom code in debian/rules (at least not the way
I'm invoking it). So making the source package is essentially just the
tar, then dpkg-genchanges, then signing it, then uploading. The only
real failure modes that people are going to encounter are a) merge
conflicts prior to the source build happening, and b) binary package
build failures in the PPA.

>
> I understand that a container-based approach may be slower, but I still would trade speed for robustness and reproducibility.

Everybody has been clamoring for speed improvements in the train as in
some cases it takes nearly 2 hours to build source packages, which is
really pathetic when you consider that a source package isn't much
more than a tarball. I took an example silo from production and build
it with this branch and the *source* package build time (not counting
binary builds in PPA) decreased from 100 minutes to 35 mi...

Read more...

1419. By Robert Bruce Park on 2016-03-21

Fix deps as per pitti.

Martin Pitt (pitti) wrote :
Download full text (3.3 KiB)

Robert Bruce Park [2016-03-21 19:03 -0000]:
> The purpose of these hooks is to allow people to munge debian/control
> prior to source-build-time, eg, if you want to build vivid & xenial
> builds out of the same source tree, but they build different package
> names due to SONAME changes due to gcc abi breakage between vivid &
> xenial. Here is one such example:
>
> http://bazaar.launchpad.net/~robru/unity-scopes-api/vivid-tweaks/view/head:/debian/bileto_pre_release_hook

Ah, thanks. This particular case could also be done with substvars.

> Another usage is to update translation templates automatically prior
> to building:
>
> http://bazaar.launchpad.net/~robru/unity8/pre_release_hook/view/head:/debian/bileto_pre_release_hook

That's a better use case. Normally these should be done during binary
package build (as that's also the time when Launchpad will import
them), but for upstream translations it's important to have them up to
date in the source tree indeed. But this is also a rather complex use
case which is prone to use /tmp, as well as tools like intltool-update
(which isn't in your whitelist)?

> Hmmm, not sure. The silo dirs are short-lived (eg they are empty while
> not actively being built, so the idea of a hook script being malicious
> and attacking other silo dirs doesn't bother me that much. The main
> reason for the apparmor profile is to prevent the script from being
> able to read/write sensitive files in ~ and /tmp, eg to avoid leaking
> GPG keys and other sensitive state, which the apparmor profile
> successfully prevents.

Running it as a different user will also do all that, but in a much
more robust manner (IMHO).

> What would a chown solution look like?

A sketch:

U=citrain-$somerandomstring
sudo adduser $U
cat <<EOF | su - $U
bzr branch proj...
cd proj*
dpkg-buildpackage -S -us -uc ...
EOF
cp ~$U/proj_* some/ci-train-work/dir
sudo deluser --remove-home $U
debsign -k... some/ci-train-work/dir/*.dsc

> But we'd need to chown the source tree to the temp user and then
> chown it back after.

Not really, you just need to chown (or copy) the generated .dsc/tar.

> Doesn't it require root to change the ownership of something you
> don't own back to your own user?

Yes, it does. More importantly, you require root to create/remove/run
something as temporary user.

> I guess we could make the directory group-writable and also create a
> temporary group for the temporary user. The idea of allowing jenkins
> user to 'sudo chown' without restrictions sounds somewhat
> terrifying.

You could restrict the sudo capabilities to alling adduser and deluser
if you want (even with adding the "citrain-" user name prefix).

> Nah, at any given time, there will only be one or two silo dirs
> present, so a malicious script trying to trample on other people's
> silos would be limited by timing.

Well, bugs happen, not necessarily out of malice. Gems like
"rm -rf $TMPDIR/" have destroyed the world more than once already :-)

> Thanks for the tip about --with-colons, but the purpose of this code
> is to sort lexically and then choose the newest key

That should still be easier and more reliable using --with-colons;
that is meant to be a machin...

Read more...

1420. By Robert Bruce Park on 2016-03-21

gpg --with-colons thanks to pitti.

1421. By Robert Bruce Park on 2016-03-21

Creation date, not expiry date.

1422. By Robert Bruce Park on 2016-03-21

Run hooks as temp user.

1423. By Robert Bruce Park on 2016-03-21

Resurrect sudoers.

1424. By Robert Bruce Park on 2016-03-21

Drop apparmor dep.

1425. By Robert Bruce Park on 2016-03-21

Fix perms.

1426. By Robert Bruce Park on 2016-03-21

Absolute paths in sudoers.

1427. By Robert Bruce Park on 2016-03-21

Run run_hook.sh as root.

1428. By Robert Bruce Park on 2016-03-21

Change ownership back.

1429. By Robert Bruce Park on 2016-03-21

Use absolute paths.

1430. By Robert Bruce Park on 2016-03-21

Stop hard-coding jenkins username.

1431. By Robert Bruce Park on 2016-03-21

Abort earlier.

1432. By Robert Bruce Park on 2016-03-21

Fix traps.

1433. By Robert Bruce Park on 2016-03-21

Drop unused job.

Robert Bruce Park (robru) wrote :

Hi Martin, Barry, please re-review. I've switched to a temporary-user based security approach, which you can see working here:

https://ci-train.staging.ubuntu.com/job/ubuntu-landing-002-1-build/6/console

+ hook=./debian/bileto_pre_release_hook
+ [ -s ./debian/bileto_pre_release_hook ]
+ trap userdel --force "$tmpuser"; rm --force "$tmpfile" EXIT
+ pwd
+ source_tree=/var/lib/jenkins/silos/ubuntu/landing-002/vivid/unity-scopes-api/unity-scopes-api
+ stat -c %U /var/lib/jenkins/silos/ubuntu/landing-002/vivid/unity-scopes-api/unity-scopes-api
+ orig_owner=jenkins
+ mktemp --tmpdir citrain-XXXXXXXXXXXXXXXXXXXXXXXX
+ tmpfile=/tmp/citrain-qR5kHMqYd2R8nT49XT4VLKRm
+ basename /tmp/citrain-qR5kHMqYd2R8nT49XT4VLKRm
+ tmpuser=citrain-qR5kHMqYd2R8nT49XT4VLKRm
+ useradd citrain-qR5kHMqYd2R8nT49XT4VLKRm
+ chown --recursive citrain-qR5kHMqYd2R8nT49XT4VLKRm /var/lib/jenkins/silos/ubuntu/landing-002/vivid/unity-scopes-api/unity-scopes-api
+ su --preserve-environment citrain-qR5kHMqYd2R8nT49XT4VLKRm --command ./debian/bileto_pre_release_hook
+ chown --recursive jenkins /var/lib/jenkins/silos/ubuntu/landing-002/vivid/unity-scopes-api/unity-scopes-api
+ userdel --force citrain-qR5kHMqYd2R8nT49XT4VLKRm
+ rm --force /tmp/citrain-qR5kHMqYd2R8nT49XT4VLKRm

review: Approve
1434. By Robert Bruce Park on 2016-03-22

Fix permissions on lp creds.

1435. By Robert Bruce Park on 2016-03-22

Fix permissions on openstack creds.

1436. By Robert Bruce Park on 2016-03-22

Moar permissions.

1437. By Robert Bruce Park on 2016-03-22

EVEN MOAR permissions.

Robert Bruce Park (robru) wrote :

Here's an example malicious script:

http://bazaar.launchpad.net/~robru/unity8/testing-train-please-ignore/view/2294/debian/bileto_pre_release_hook

And here's the output:

https://pastebin.canonical.com/152455/

It gets a directory listing for ~, which the apparmor profile wouldn't have allowed, but all the creds are only readable by owner so none of the attempts at leaking creds are successful.

let me know if there's other nefarious things to try in a malicious script. I want to make sure this is really secure.

Thanks!

1438. By Robert Bruce Park on 2016-03-22

Fix docstring.

1439. By Robert Bruce Park on 2016-03-22

More comments.

1440. By Robert Bruce Park on 2016-03-22

Tweak environment.

1441. By Robert Bruce Park on 2016-03-22

Tweak environment again.

1442. By Robert Bruce Park on 2016-03-22

Apparently unnecessary.

1443. By Robert Bruce Park on 2016-03-22

Fix comment.

1444. By Robert Bruce Park on 2016-03-22

Fix tests.

Martin Pitt (pitti) wrote :

Chowning the entire source tree twice is a bit more work than I had in mind, but it doesn't take that much time. This should work fine now. I just found one nitpick in the shell trap, otherwise LGTM, thanks!

review: Approve
1445. By Robert Bruce Park on 2016-03-22

Trap more signals.

1446. By Robert Bruce Park on 2016-03-22

Decrease log noise.

1447. By Robert Bruce Park on 2016-03-22

Simplify

Robert Bruce Park (robru) wrote :

On Tue, Mar 22, 2016 at 1:13 AM, Martin Pitt <email address hidden> wrote:
> Review: Approve
>
> Chowning the entire source tree twice is a bit more work than I had in mind, but it doesn't take that much time.

Due to the architecture of the train it's easier to just chown it and
chown it back within one little hook script rather than trying to keep
it as a unique owner all the time as we do a lot of transformations on
the source tree (lots of merges, variable substitutions in debian/
files, commits, etc). Would have taken lots of big changes to make it
always sudo to the owner of the files every time we want to touch it.

>> +trap 'userdel --force "$tmpuser"; rm --force "$tmpfile"' EXIT
>
> Please add INT QUIT PIPE too, as these are also common modes how a shell script can abort.

I played with this a little bit and found that when I trap INT QUIT
and PIPE, the trap runs twice when those signals are sent, but the
trap isn't run on just EXIT (I read some documentation that claimed
that EXIT fires on any exit, whether there was a signal or not, but
that doesn't seem to be the case according to my experiments). Still,
deleting the user twice is better than not at all.

Of course trapping INT QUIT and PIPE stops the script from actually
exiting when those are trapped so I had to add an exit statement, fun
times.

--
robru

1448. By Robert Bruce Park on 2016-03-22

Ensure we always chown source tree back to $orig_owner.

1449. By Robert Bruce Park on 2016-03-22

Add a warning for packages that may require transitioning.

1450. By Robert Bruce Park on 2016-03-22

Duh

1451. By Robert Bruce Park on 2016-03-22

debug

1452. By Robert Bruce Park on 2016-03-22

debug ls

1453. By Robert Bruce Park on 2016-03-22

Why isn't this working?

1454. By Robert Bruce Park on 2016-03-22

Sigh

1455. By Robert Bruce Park on 2016-03-22

Simplify.

1456. By Robert Bruce Park on 2016-03-22

All output to STDERR for logging.

1457. By Robert Bruce Park on 2016-03-23

Fix GLES builds.

1458. By Robert Bruce Park on 2016-03-23

Clean up logging a bit.

1459. By Robert Bruce Park on 2016-03-23

Stop deleting debian/watch

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Makefile'
2--- Makefile 2016-02-05 00:23:41 +0000
3+++ Makefile 2016-03-23 08:43:27 +0000
4@@ -28,7 +28,7 @@
5
6 # Charm runs this as root.
7 install: install-common-deps
8- $(apt) sudo rsync cowbuilder eatmydata bzr-builddeb dput amqp-tools python-swiftclient
9+ $(apt) sudo rsync dpkg-dev dput amqp-tools python-swiftclient
10 mkdir -p /etc/rsync-juju.d/
11 cp files/rsyncd.conf /etc/rsync-juju.d/020-ci-train.conf
12 service rsync restart
13@@ -39,6 +39,7 @@
14
15 # Charm runs this as unprivileged jenkins user.
16 install-user:
17+ chmod 0600 $(HOME)/.launchpad.credentials $(HOME)/os.* $(HOME)/*.token $(HOME)/*.key $(HOME)/cred*
18 bzr whoami "CI Train Bot <$(LAUNCHPAD_USER)@canonical.com>"
19 bzr launchpad-login $(LAUNCHPAD_USER)
20 $(envsubst) <files/dput.cf >$(HOME)/.dput.cf
21
22=== removed file 'chroot-tools/.pbuilderrc'
23--- chroot-tools/.pbuilderrc 2014-10-28 02:10:49 +0000
24+++ chroot-tools/.pbuilderrc 1970-01-01 00:00:00 +0000
25@@ -1,41 +0,0 @@
26-# The file is automatically picked up by the internal pbuilder call
27-# Just use it to create the cowbuilder with: sudo HOME=. DIST=<wanted_dist> cowbuilder --create --debootstrapopts --variant=buildd
28-# If you need to tweak the base.cow, use: sudo HOME=. DIST=<wanted_dist> cowbuilder --login --save
29-
30-# Optionally set the series to the host series and
31-# architecture to the host architecture if none set.
32-: ${DIST:="$(lsb_release -sc)"}
33-: ${ARCH:="$(dpkg --print-architecture)"}
34-NAME="$DIST-$ARCH"
35-
36-BASETGZ="/var/cache/pbuilder/$NAME-base.tgz"
37-BASEPATH="/var/cache/pbuilder/$NAME/base.cow" # for cowbuilder
38-DISTRIBUTION="$DIST"
39-BUILDRESULT="/var/cache/pbuilder/$NAME/result/"
40-APTCACHE="/var/cache/pbuilder/$NAME/aptcache/"
41-BUILDPLACE="/var/cache/pbuilder/build/"
42-
43-# create the directories if doesn't exist
44-mkdir -p $BUILDRESULT
45-mkdir -p $APTCACHE
46-mkdir -p $BUILDPLACE
47-
48-# use ubuntu main mirror only
49-# Ubuntu configuration
50-MIRRORSITE="http://archive.ubuntu.com/ubuntu/"
51-COMPONENTS="main restricted universe multiverse"
52-OTHERMIRROR=""
53-
54-# Enable eatmydata
55-# EXTRAPACKAGES is not obeyed by 'cowbuilder --create' but running
56-# 'cowbuilder--update' will happily fix that.
57-EXTRAPACKAGES="eatmydata pbuilder bzr bzr-builddeb"
58-case $DIST in
59-precise|trusty|utopic)
60- export LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}/usr/lib/libeatmydata/libeatmydata.so"
61- ;;
62-*)
63- # FIXME: This rubbish is totally broken in Vivid.
64- #export LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}/usr/lib/$(dpkg-architecture -a$ARCH -qDEB_HOST_MULTIARCH)/libeatmydata.so"
65- ;;
66-esac
67
68=== removed file 'chroot-tools/buildsource-chroot'
69--- chroot-tools/buildsource-chroot 2015-12-07 20:44:00 +0000
70+++ chroot-tools/buildsource-chroot 1970-01-01 00:00:00 +0000
71@@ -1,117 +0,0 @@
72-#!/bin/bash
73-# FIXME: This script should really use /bin/sh but it breaks pbuilder.
74-# Copyright (C) 2012-2013 Canonical
75-#
76-# This program is free software; you can redistribute it and/or modify it under
77-# the terms of the GNU General Public License as published by the Free Software
78-# Foundation; version 3.
79-#
80-# This program is distributed in the hope that it will be useful, but WITHOUT
81-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
82-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
83-# details.
84-#
85-# You should have received a copy of the GNU General Public License along with
86-# this program; if not, write to the Free Software Foundation, Inc.,
87-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
88-#
89-
90-set -e
91-
92-# go to the working directory
93-cd "$1"
94-shift
95-
96-while [ -n "$1" ]; do
97- case "$1" in
98- --debug)
99- set -x
100- shift;
101- ;;
102- --distro-version)
103- DISTRO_VERSION=$2
104- shift; shift;
105- ;;
106- --gnupg-parentdir)
107- # set home directory to real HOME for signing the source packaging
108- CHROOT_HOME="${2}";
109- shift; shift;
110- ;;
111- --help)
112- echo "$0: Enable building a source package inside a chroot
113-
114- Usage: $0 WORKDIR --gnupg-parentdir=DIR --distro-version=version_from_distro
115- All parameters below are mandatory:
116- - WORKDIR is the current package source directory (in tree)
117- - gnupg-parentdir is the directory containing the .gnupg/ directory. Certainly $HOME for you.
118- - distro-version is the latest version published to the distro
119-
120- Note that WORKDIR and gnupg-keyid should be bindmounted into the chroot.
121- This will create a signed source package using bzr bd -S, after installing all build-deps, ran into the WORKDIR, with the correct permission.
122-
123-
124- Example on how to run it with pbuilder (cowbuilder can be used with exactly the same parameters, replace pbuilder with cowbuilder):"'
125-
126- $ pbuilder --execute --distribution TARGET_DIST --bindmounts ${PWD} --bindmounts ${HOME} -- '$0' ${PWD} --gnupg-parentdir ${HOME}'
127- shift;
128- exit 0
129- ;;
130- --*)
131- echo "E: Unknown option [$1] was specified "
132- exit 1;
133- ;;
134- *)
135- break;
136- ;;
137- esac
138-done
139-
140-if [ -z "$CHROOT_HOME" ] || [ -z "DISTRO_VERSION" ] ; then
141- echo "Every parameters is mandatory to run $0. Use --help for more info."
142- exit 1
143-fi
144-
145-SU="su -p pbuser -c"
146-HOME="$CHROOT_HOME"
147-export HOME;
148-
149-apt-get update
150-apt-get -yq --force-yes dist-upgrade
151-
152-# this isn't needed (you should install them in the chroot), but easier for debugging
153-apt-get install pbuilder bzr-builddeb libwww-perl -yq --force-yes
154-
155-# install build-deps
156-. /usr/lib/pbuilder/pdebuild-checkparams
157-export PBCURRENTCOMMANDLINEOPERATION="pdebuild"
158-"$PBUILDERSATISFYDEPENDSCMD"-classic --force-version --continue-fail
159-
160-# create the user similar to that used outside.
161-cmd_retry_exec () {
162- # Tries to execute $@ $loop times with a delay of 1s between retries
163- # before aborting
164- loop=10
165- set +e
166- while [ $loop -gt 0 ]; do
167- $@ && break
168- loop=$(expr $loop - 1)
169- sleep 1
170- done
171- set -e
172-}
173-
174-# Workaround a bug where /etc/group or /etc/passwd are locked when several
175-# jobs run in parallel (LP: #1155510)
176-cmd_retry_exec groupadd -g "$(stat -c '%g' .)" -o pbgroup
177-cmd_retry_exec useradd -g pbgroup -u "$(stat -c '%u' .)" -d "${HOME}" -o pbuser
178-
179-# Determine the most-recently-created GPG secret key we have available.
180-# 'awk' converts "sec 2048R/9D68D3D4 2014-12-11" into "2014-12-112048R/9D68D3D4".
181-# 'sort | tail -1' chooses the lexically highest (aka chronologically newest) one.
182-# 'cut -f2 -d/' removes the date and key size, leaving only the key id.
183-KEY=$($SU "gpg --list-secret-keys" | egrep ^sec | awk '{ print $3$2 }' | sort | tail -1 | cut -f2 -d/)
184-
185-set -x
186-
187-$SU "make -f ./debian/rules clean"
188-$SU "bzr bd -S -- -sa -k$KEY -d -v$DISTRO_VERSION"
189
190=== removed file 'chroot-tools/create-chroot'
191--- chroot-tools/create-chroot 2015-06-29 22:52:03 +0000
192+++ chroot-tools/create-chroot 1970-01-01 00:00:00 +0000
193@@ -1,42 +0,0 @@
194-#!/bin/sh
195-export LANG=en_US.UTF-8
196-
197-if echo "$DIST" | egrep -q '[0-9]'; then
198- export ARCHIVE="http://derived.archive.canonical.com/ubuntu-rtm"
199-fi
200-
201-OPTS=""
202-if [ ! -z "$ARCHIVE" ]; then
203- OPTS="$OPTS --mirror=$ARCHIVE"
204-fi
205-
206-ARCH=$(dpkg --print-architecture)
207-
208-# Use our custom .pbuilderrc
209-export HOME=$(cd $(dirname "$0"); pwd)
210-
211-if [ ! -e "/var/cache/pbuilder/${DIST}-${ARCH}/base.cow" ]; then
212- echo "Preparing pbuilder for $DIST"
213-
214- if [ ! -e "/usr/share/debootstrap/scripts/$DIST" ]; then
215- # Temporary solution
216- echo "Can't find any support for $DIST yet, improvising..."
217- cp -a /usr/share/debootstrap /tmp/debootstrap-$DIST
218-
219- echo "Preparing a temporary symlink for the new distribution"
220- ln -s /usr/share/debootstrap/scripts/trusty /tmp/debootstrap-$DIST/scripts/$DIST
221- export DEBOOTSTRAP_DIR=/tmp/debootstrap-$DIST
222- fi
223-
224- sudo -E cowbuilder --create --debootstrapopts --variant=buildd $OPTS
225- sudo -E cowbuilder --save --execute -- /usr/bin/apt-get install \
226- eatmydata bzr-builddeb software-properties-common -yq --force-yes
227-
228- unset DEBOOTSTRAP_DIR
229- if [ -d "/tmp/debootstrap-$DIST" ]; then
230- echo "Cleaning up after setting up a temporary fix for debootstrap"
231- rm -rf /tmp/debootstrap-$DIST
232- fi
233-
234- echo "$DIST pbuilder prepared"
235-fi
236
237=== removed file 'chroot-tools/pbuilder-clean'
238--- chroot-tools/pbuilder-clean 2015-12-07 20:51:16 +0000
239+++ chroot-tools/pbuilder-clean 1970-01-01 00:00:00 +0000
240@@ -1,8 +0,0 @@
241-#!/bin/sh
242-
243-set -x
244-
245-# Don't clean while builds are happening.
246-if ! pgrep -c "^pbuilder$"; then
247- sudo pbuilder --clean
248-fi
249
250=== removed file 'citrain/jenkins-templates/upgrade-chroot.xml.tmpl'
251--- citrain/jenkins-templates/upgrade-chroot.xml.tmpl 2015-12-16 21:53:13 +0000
252+++ citrain/jenkins-templates/upgrade-chroot.xml.tmpl 1970-01-01 00:00:00 +0000
253@@ -1,58 +0,0 @@
254-<?xml version='1.0' encoding='UTF-8'?>
255-<project>
256- <actions/>
257- <description>Upgrades the cowbuilder chroots.</description>
258- <logRotator class="hudson.tasks.LogRotator">
259- <daysToKeep>15</daysToKeep>
260- <numToKeep>-1</numToKeep>
261- <artifactDaysToKeep>-1</artifactDaysToKeep>
262- <artifactNumToKeep>-1</artifactNumToKeep>
263- </logRotator>
264- <keepDependencies>false</keepDependencies>
265- <properties/>
266- <scm class="hudson.scm.NullSCM"/>
267- <canRoam>true</canRoam>
268- <disabled>false</disabled>
269- <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
270- <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
271- <triggers>
272- <hudson.triggers.TimerTrigger>
273- <spec>@daily</spec>
274- </hudson.triggers.TimerTrigger>
275- </triggers>
276- <concurrentBuild>false</concurrentBuild>
277- <builders>
278- <hudson.tasks.Shell>
279- <command>#/bin/sh
280-set -ex
281-
282-export HOME=/var/lib/citrain/chroot-tools/
283-export ARCH=$(dpkg-architecture -qDEB_HOST_ARCH)
284-
285-# Create the script to be executed inside the chroot
286-cat &gt; build.sh &lt;&lt; EOF
287-#!/bin/sh
288-set -x
289-
290-# apt-get update can encounter transient failures due to archive updates, etc.
291-apt-get update || apt-get update || apt-get update
292-
293-# The following were used to fix a broken package upgrade and should be safe to use on a regular basis
294-apt-get dist-upgrade -y --force-yes
295-apt-get -f install -y --force-yes
296-apt-get dist-upgrade -y --force-yes
297-apt-get autoremove -y
298-EOF
299-
300-for chroot in /var/cache/pbuilder/*-$ARCH; do
301- [ -d "$chroot" ] || exit 0 # Don't try to update literal "*-amd64"
302- export DIST=$(basename -s -$ARCH $chroot)
303- # If you want to do a &apos;dry-run&apos; and not commit any changes, just omit the --save option as shown:
304- # sudo -E cowbuilder --execute -- build.sh
305- sudo -E cowbuilder --execute --save -- build.sh
306-done</command>
307- </hudson.tasks.Shell>
308- </builders>
309- <publishers/>
310- <buildWrappers/>
311-</project>
312
313=== modified file 'citrain/recipes/base.py'
314--- citrain/recipes/base.py 2016-02-15 07:31:50 +0000
315+++ citrain/recipes/base.py 2016-03-23 08:43:27 +0000
316@@ -293,8 +293,7 @@
317 def build_phase(self):
318 """Build the source package in a chroot."""
319 logging.info('Building in %s', self.path)
320- prev_version = self.get_archive_version(self.dest)
321- self.build_package(self.series.name, prev_version)
322+ self.build_package()
323
324 @staticmethod
325 def pre_upload_phase(silo_state):
326
327=== modified file 'citrain/recipes/merge.py'
328--- citrain/recipes/merge.py 2016-01-26 23:16:49 +0000
329+++ citrain/recipes/merge.py 2016-03-23 08:43:27 +0000
330@@ -128,6 +128,7 @@
331 revno = merge.source_branch.revision_count
332 logging.info('Merging %s at r%s.', branch, revno)
333 self.merge_branch(branch, message, authors)
334+ self.package_hook()
335 self.verify_changelog_versions()
336 self.identify_versions()
337
338
339=== modified file 'citrain/recipes/secondary.py'
340--- citrain/recipes/secondary.py 2015-12-08 06:04:44 +0000
341+++ citrain/recipes/secondary.py 2016-03-23 08:43:27 +0000
342@@ -40,6 +40,7 @@
343 shutil.copytree(self.primary.path, self.path, symlinks=True)
344 ver = V(self.get_package_version()).change_series(self.series.version)
345 self.correct_package_changelog(ver, self.series.name)
346+ self.package_hook()
347
348 def checkupload_phase(self):
349 """The primary checkupload phase already tracks upload rights."""
350
351=== modified file 'citrain/setup_citrain.py'
352--- citrain/setup_citrain.py 2016-02-10 17:26:23 +0000
353+++ citrain/setup_citrain.py 2016-03-23 08:43:27 +0000
354@@ -116,7 +116,6 @@
355 setup_silo(siloname)
356 setup_job('cyphermox-test')
357 setup_job('prepare-silo')
358- setup_job('upgrade-chroot')
359 setup_job('apt-get-clean')
360 setup_job('revert')
361 setup_job('abandon')
362
363=== modified file 'cupstream2distro/packagemanager.py'
364--- cupstream2distro/packagemanager.py 2016-02-10 16:29:30 +0000
365+++ cupstream2distro/packagemanager.py 2016-03-23 08:43:27 +0000
366@@ -29,7 +29,6 @@
367 from cupstream2distro.settings import (
368 BOT_DEBEMAIL,
369 BOT_DEBFULLNAME,
370- GNUPG_DIR,
371 TOOLS,
372 )
373 from cupstream2distro.utils import (
374@@ -69,8 +68,6 @@
375
376 NOCHANGE = {BOT_DEBFULLNAME: ['No-change rebuild.']}
377
378-BUILDSOURCE = join(TOOLS, 'buildsource-chroot')
379-
380
381 class Package(object):
382 """Define methods for interacting with debian packages."""
383@@ -191,20 +188,19 @@
384 'debian/{}: update to released version.'.format(filename)],
385 cwd=self.path, env=dch_env)
386
387- def build_package(self, series, distro_version):
388- """Build the source package in a chroot."""
389- distro_version = distro_version or '0'
390- environ = os.environ.copy()
391- environ.update(HOME=TOOLS, DIST=series)
392- log_call(['./create-chroot'], cwd=TOOLS, env=environ)
393- cmd = [
394- 'sudo', '-E', 'cowbuilder', '--execute',
395- '--bindmounts', dirname(self.path),
396- '--bindmounts', GNUPG_DIR, '--', BUILDSOURCE, self.path,
397- '--gnupg-parentdir', GNUPG_DIR, '--distro-version', distro_version,
398- ]
399- instance = log_call(cmd, cwd=self.path, env=environ)[0]
400- log_call('./pbuilder-clean', cwd=TOOLS)
401+ def package_hook(self):
402+ """Run the pre_release_hook."""
403+ hook_env = os.environ.copy()
404+ hook_env.update(SERIES=self.series.name)
405+ instance = log_call(
406+ 'sudo --preserve-env /bin/sh ' + join(TOOLS, 'run_hook.sh'),
407+ cwd=self.path, env=hook_env)[0]
408+ if instance.returncode != 0:
409+ raise PackageError('{} pre release hook.'.format(self.name))
410+
411+ def build_package(self):
412+ """Build the source package without installing build deps."""
413+ instance = log_call(join(TOOLS, 'buildpackage.sh'), cwd=self.path)[0]
414 if instance.returncode != 0:
415 raise PackageError('{} source package.'.format(self.name))
416
417
418=== modified file 'cupstream2distro/settings.py'
419--- cupstream2distro/settings.py 2016-02-05 23:58:24 +0000
420+++ cupstream2distro/settings.py 2016-03-23 08:43:27 +0000
421@@ -43,7 +43,7 @@
422 OLD_STACK_DIR = 'old'
423
424 ROOT_CU2D = dirname(dirname(realpath(__file__)))
425-TOOLS = join(ROOT_CU2D, 'chroot-tools')
426+TOOLS = join(ROOT_CU2D, 'tools')
427
428 SILO_NAMES_FILE = expanduser('~/.ci-train-silo-names')
429
430
431=== added file 'files/sudoers.conf'
432--- files/sudoers.conf 1970-01-01 00:00:00 +0000
433+++ files/sudoers.conf 2016-03-23 08:43:27 +0000
434@@ -0,0 +1,1 @@
435+jenkins ALL=(ALL) NOPASSWD:SETENV: /bin/sh /var/lib/citrain/tools/run_hook.sh
436
437=== removed file 'files/sudoers.conf'
438--- files/sudoers.conf 2015-08-31 04:06:23 +0000
439+++ files/sudoers.conf 1970-01-01 00:00:00 +0000
440@@ -1,3 +0,0 @@
441-jenkins ALL=(ALL) NOPASSWD:SETENV: /usr/sbin/cowbuilder
442-jenkins ALL=(ALL) NOPASSWD:SETENV: /usr/bin/apt-get clean
443-jenkins ALL=(ALL) NOPASSWD:SETENV: /usr/sbin/pbuilder --clean
444
445=== modified file 'tests/unit/test_packagemanager.py'
446--- tests/unit/test_packagemanager.py 2016-02-10 16:29:30 +0000
447+++ tests/unit/test_packagemanager.py 2016-03-23 08:43:27 +0000
448@@ -17,9 +17,8 @@
449 """Tests for debian package related functions."""
450
451
452-from pprint import pprint
453-from os import stat, makedirs, readlink, symlink
454-from os.path import dirname, exists, expanduser, isdir, islink, join
455+from os import environ, stat, makedirs, readlink, symlink
456+from os.path import dirname, exists, isdir, islink, join
457
458 from mock import Mock, patch, call
459
460@@ -30,6 +29,7 @@
461 from cupstream2distro.packagemanager import Package
462 from cupstream2distro.utils import env, utf8_open
463 from cupstream2distro.errors import PackageError
464+from cupstream2distro.settings import TOOLS
465
466
467 class PackageManagerTests(BaseTestCase):
468@@ -88,7 +88,7 @@
469
470 @patch('cupstream2distro.packagemanager.call')
471 def test_get_package_version_failed(self, call_mock):
472- """Raise PackageError when dpkg-parsechangelog errors out."""
473+ """Return None when dpkg-parsechangelog errors out."""
474 self.assertIsNone(self.package.get_package_version())
475
476 def test_preserve_package_version(self):
477@@ -104,80 +104,48 @@
478 self.prep_debian_file('control', control)
479 self.assertEqual(self.package.preserve_package_version(), result)
480
481- @patch('cupstream2distro.packagemanager.os.getuid', lambda: '1000')
482- @patch('cupstream2distro.packagemanager.os.getgid', lambda: '1000')
483- @patch('cupstream2distro.packagemanager.os.environ.copy')
484- @patch('cupstream2distro.packagemanager.log_call')
485- def test_build_package(self, call_mock, copy_mock):
486- """call cowbuilder and build a source package."""
487- home = expanduser('~')
488- copy_mock.return_value = {}
489- call_mock.return_value = (Mock(returncode=0), '', '')
490- self.package.build_package('raring', '1.1-0ubuntu1')
491- copy_mock.assert_called_once_with()
492- self.assertEqual(
493- copy_mock(), dict(DIST='raring', HOME=dirname(self.bsc)))
494- pprint(call_mock.mock_calls)
495- self.assertEqual(call_mock.mock_calls, [
496- call(['./create-chroot'],
497- env=copy_mock(), cwd=dirname(self.bsc)),
498- call(['sudo', '-E', 'cowbuilder', '--execute', '--bindmounts',
499- dirname(self.tempdir), '--bindmounts', home, '--',
500- self.bsc, self.tempdir, '--gnupg-parentdir', home,
501- '--distro-version', '1.1-0ubuntu1'],
502- env=copy_mock(), cwd=self.tempdir),
503- call('./pbuilder-clean', cwd=dirname(self.bsc)),
504- ])
505-
506- @patch('cupstream2distro.packagemanager.os.getuid', lambda: '1000')
507- @patch('cupstream2distro.packagemanager.os.getgid', lambda: '1000')
508- @patch('cupstream2distro.packagemanager.os.environ.copy')
509- @patch('cupstream2distro.packagemanager.log_call')
510- def test_build_with_other_distro_version(self, call_mock, copy_mock):
511- """Build a source package (unsigned for tests) for an older distro."""
512- home = expanduser('~')
513- copy_mock.return_value = {}
514- call_mock.return_value = (Mock(returncode=0), '', '')
515- self.package.build_package('precise', '1.1-0ubuntu1')
516- copy_mock.assert_called_once_with()
517- self.assertEqual(
518- copy_mock(), dict(DIST='precise', HOME=dirname(self.bsc)))
519- pprint(call_mock.mock_calls)
520- self.assertEqual(call_mock.mock_calls, [
521- call(['./create-chroot'],
522- env=copy_mock(), cwd=dirname(self.bsc)),
523- call(['sudo', '-E', 'cowbuilder', '--execute', '--bindmounts',
524- dirname(self.tempdir), '--bindmounts', home, '--',
525- self.bsc, self.tempdir, '--gnupg-parentdir', home,
526- '--distro-version', '1.1-0ubuntu1'],
527- env=copy_mock(), cwd=self.tempdir),
528- call('./pbuilder-clean', cwd=dirname(self.bsc)),
529- ])
530-
531- @patch('cupstream2distro.packagemanager.os.getuid', lambda: '1000')
532- @patch('cupstream2distro.packagemanager.os.getgid', lambda: '1000')
533- @patch('cupstream2distro.packagemanager.os.environ.copy')
534- @patch('cupstream2distro.packagemanager.log_call')
535- def test_build_package_raises_exception(self, call_mock, copy_mock):
536+ @patch('cupstream2distro.packagemanager.log_call')
537+ def test_build_package(self, call_mock):
538+ """Build a source package."""
539+ call_mock.return_value = (Mock(returncode=0), '', '')
540+ self.package.build_package()
541+ self.assertEqual(call_mock.mock_calls, [
542+ call(join(TOOLS, 'buildpackage.sh'), cwd=self.package.path),
543+ ])
544+
545+ @patch('cupstream2distro.packagemanager.log_call')
546+ def test_build_package_raises_exception(self, call_mock):
547 """Raise exception if subprocess returns error."""
548- home = expanduser('~')
549- copy_mock.return_value = {}
550 call_mock.return_value = (Mock(returncode=30), '', '')
551 with self.assertRaisesRegexp(PackageError, 'wheee-doggy source'):
552- self.package.build_package('raring', '1.1-0ubuntu1')
553- copy_mock.assert_called_once_with()
554- self.assertEqual(
555- copy_mock(), dict(DIST='raring', HOME=dirname(self.bsc)))
556- pprint(call_mock.mock_calls)
557- self.assertEqual(call_mock.mock_calls, [
558- call(['./create-chroot'],
559- env=copy_mock(), cwd=dirname(self.bsc)),
560- call(['sudo', '-E', 'cowbuilder', '--execute', '--bindmounts',
561- dirname(self.tempdir), '--bindmounts', home, '--',
562- self.bsc, self.tempdir, '--gnupg-parentdir', home,
563- '--distro-version', '1.1-0ubuntu1'],
564- env=copy_mock(), cwd=self.tempdir),
565- call('./pbuilder-clean', cwd=dirname(self.bsc)),
566+ self.package.build_package()
567+ self.assertEqual(call_mock.mock_calls, [
568+ call(join(TOOLS, 'buildpackage.sh'), cwd=self.package.path),
569+ ])
570+
571+ @patch('cupstream2distro.packagemanager.log_call')
572+ def test_package_hook(self, call_mock):
573+ """Call hook script."""
574+ hook_env = environ.copy()
575+ hook_env.update(SERIES=self.package.series.name)
576+ call_mock.return_value = (Mock(returncode=0), '', '')
577+ self.package.package_hook()
578+ self.assertEqual(call_mock.mock_calls, [
579+ call('sudo --preserve-env /bin/sh ' + join(TOOLS, 'run_hook.sh'),
580+ cwd=self.package.path, env=hook_env),
581+ ])
582+
583+ @patch('cupstream2distro.packagemanager.log_call')
584+ def test_package_hook_fail(self, call_mock):
585+ """Call failing hook script."""
586+ hook_env = environ.copy()
587+ hook_env.update(SERIES=self.package.series.name)
588+ call_mock.return_value = (Mock(returncode=99), '', '')
589+ with self.assertRaisesRegexp(PackageError, 'pre release hook'):
590+ self.package.package_hook()
591+ self.assertEqual(call_mock.mock_calls, [
592+ call('sudo --preserve-env /bin/sh ' + join(TOOLS, 'run_hook.sh'),
593+ cwd=self.package.path, env=hook_env),
594 ])
595
596 @patch('cupstream2distro.packagemanager.glob')
597
598=== modified file 'tests/unit/test_recipe_base.py'
599--- tests/unit/test_recipe_base.py 2016-02-15 07:31:50 +0000
600+++ tests/unit/test_recipe_base.py 2016-03-23 08:43:27 +0000
601@@ -311,9 +311,7 @@
602 build.get_archive_version = Mock()
603 build.build_package = Mock()
604 build.build_phase()
605- build.get_archive_version.assert_called_once_with(self.dest)
606- build.build_package.assert_called_once_with(
607- self.series.name, build.get_archive_version.return_value)
608+ build.build_package.assert_called_once_with()
609
610 @patch('citrain.recipes.base.name_ppa')
611 def test_buildbase_upload_phase(self, name_mock):
612
613=== modified file 'tests/unit/test_recipe_merge.py'
614--- tests/unit/test_recipe_merge.py 2016-01-26 23:11:55 +0000
615+++ tests/unit/test_recipe_merge.py 2016-03-23 08:43:27 +0000
616@@ -214,6 +214,7 @@
617
618 @patch('citrain.recipes.merge.reorder_branches')
619 @patch('citrain.recipes.merge.Merge.merge_branch', Mock())
620+ @patch('citrain.recipes.merge.Merge.package_hook', Mock())
621 @patch('citrain.recipes.merge.Merge.identify_versions', Mock())
622 @patch('citrain.recipes.merge.Merge.get_archive_version', Mock())
623 @patch('citrain.recipes.merge.Merge.get_package_version', Mock())
624@@ -252,6 +253,7 @@
625 ])
626 merge.verify_changelog_versions.assert_called_once_with()
627 merge.identify_versions.assert_called_once_with()
628+ merge.package_hook.assert_called_once_with()
629
630 @patch('citrain.recipes.merge.Merge.release_branch', Mock())
631 @patch('citrain.recipes.merge.Merge.get_package_version', Mock())
632
633=== modified file 'tests/unit/test_recipe_secondary.py'
634--- tests/unit/test_recipe_secondary.py 2015-11-24 20:03:28 +0000
635+++ tests/unit/test_recipe_secondary.py 2016-03-23 08:43:27 +0000
636@@ -53,6 +53,7 @@
637 self.assertEqual(sec.get_archive_version.mock_calls, [])
638
639 @patch(MOD + 'shutil')
640+ @patch(MOD + 'Secondary.package_hook', Mock())
641 @patch(MOD + 'Secondary.get_package_version', Mock())
642 @patch(MOD + 'Secondary.correct_package_changelog', Mock())
643 def test_secondary_collect_phase(self, sh_mock):
644@@ -68,3 +69,4 @@
645 symlinks=True)
646 sec.correct_package_changelog.assert_called_once_with(
647 '0+15.04.20150525-0ubuntu1', 'vivid')
648+ sec.package_hook.assert_called_once_with()
649
650=== modified file 'tests/unit/test_script_setup_citrain.py'
651--- tests/unit/test_script_setup_citrain.py 2016-02-10 17:26:23 +0000
652+++ tests/unit/test_script_setup_citrain.py 2016-03-23 08:43:27 +0000
653@@ -120,7 +120,6 @@
654 self.assertEqual(self.script.setup_job.mock_calls, [
655 call('cyphermox-test'),
656 call('prepare-silo'),
657- call('upgrade-chroot'),
658 call('apt-get-clean'),
659 call('revert'),
660 call('abandon'),
661@@ -138,7 +137,6 @@
662 'ubuntu-landing-{:03d}-1-build/config.xml',
663 'ubuntu-landing-{:03d}-2-publish/config.xml',
664 'ubuntu-landing-{:03d}-3-merge-clean/config.xml',
665- 'upgrade-chroot/config.xml',
666 ]
667
668 def gen_expected():
669
670=== renamed directory 'chroot-tools' => 'tools'
671=== added file 'tools/buildpackage.sh'
672--- tools/buildpackage.sh 1970-01-01 00:00:00 +0000
673+++ tools/buildpackage.sh 2016-03-23 08:43:27 +0000
674@@ -0,0 +1,38 @@
675+#!/bin/sh
676+
677+[ "$DEBUG" = "true" ] && verbose="--verbose" || verbose=""
678+
679+set -eu
680+
681+# Converts "sec::4096:1:359834FAB93307E4:2013-12-06:..." into
682+# "2015-01-12\tDEADBEEF", sorts lexically (ie, chronologically), then chooses
683+# the last (ie, newest) one.
684+gpg_key=$(gpg --list-secret-keys --with-colons | awk -F: '/^sec/ {print $6 "\t" $5}' | sort | tail -1 | cut --field 2)
685+
686+source_name="$(dpkg-parsechangelog --show-field Source)"
687+upstream="$(dpkg-parsechangelog --show-field Version | sed -e 's/^[^:]\+://' -e 's/-[^-]\+$//')"
688+orig_tar="../${source_name}_${upstream}.orig.tar.gz"
689+
690+nongles="$(echo $source_name | sed -n 's/-gles$//p')"
691+
692+if [ -n "$nongles" ]; then
693+ grep --quiet get-orig-source ./debian/rules && echo "WARNING! Ignoring obsolete get-orig-source target in debian/rules." 1>&2 || true
694+ set -x
695+ cp --verbose ../../${nongles}/${nongles}_${upstream}.orig.tar.gz "$orig_tar" 1>&2
696+else
697+ set -x
698+ tar --create --auto-compress $verbose --file "$orig_tar" \
699+ --exclude='.bzr*' --exclude='.git*' --exclude="debian" . 1>&2
700+fi
701+
702+# -sa: Force inclusion of original source.
703+# -d: Do not check build dependencies and conflicts.
704+# -S: Only build source package, no binaries.
705+# -i: Ignore VCS files from generated diff.
706+# -I: Ignore VCS files from generated tarball.
707+# -nc: Do not run `./debian/rules clean`
708+# -k: Sign .changes with this key.
709+# -i'^.bzr' and -I'.bzr*': Ignore .bzr-builddeb which default -i and -I miss.
710+dpkg-buildpackage -sa -d -S -i -i'^.bzr' -I -I'.bzr*' -nc -k"$gpg_key" 1>&2
711+
712+ls -l --all --human-readable .. 1>&2
713
714=== added file 'tools/run_hook.sh'
715--- tools/run_hook.sh 1970-01-01 00:00:00 +0000
716+++ tools/run_hook.sh 2016-03-23 08:43:27 +0000
717@@ -0,0 +1,41 @@
718+#!/bin/sh
719+
720+#
721+# WARNING: This script runs as root, don't do anything stupid!
722+#
723+
724+cleanup() {
725+ chown --recursive "$orig_owner" "$source_tree" 1>&2
726+ userdel --force "$tmpuser" 1>&2
727+ rm --force "$tmpfile" 1>&2
728+}
729+
730+hook="./debian/bileto_pre_release_hook"
731+
732+# Check for packages which might need the hook but don't have it.
733+if ! [ -s "$hook" ]; then
734+ grep --quiet override_dh_auto_clean ./debian/rules && cat 1>&2 <<EOF
735+WARNING! Found override_dh_auto_clean but not bileto_pre_release_hook defined
736+in this package. "./debian/rules clean" is no longer called during source
737+package builds! If you have code you need to be run prior to the source package
738+build, you must create a new script: $hook
739+EOF
740+ exit 0
741+fi
742+
743+trap 'cleanup' EXIT
744+trap 'cleanup; exit 1' INT QUIT TERM
745+
746+source_tree="$(pwd)"
747+orig_owner="$(stat -c '%U' "$source_tree")"
748+
749+set -eux
750+
751+tmpfile="$(mktemp --tmpdir citrain-XXXXXXXXXXXXXXXXXXXXXXXX)"
752+tmpuser="$(basename "$tmpfile")"
753+
754+useradd "$tmpuser" 1>&2
755+
756+chown --recursive "$tmpuser" "$source_tree" 1>&2
757+
758+su "$tmpuser" --command "$hook" 1>&2

Subscribers

People subscribed via source and target branches