Merge lp:~edwin-grubbs/launchpad/all-downloads-link-sprite into lp:launchpad
- all-downloads-link-sprite
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Edwin Grubbs |
Approved revision: | no longer in the source branch. |
Merged at revision: | not available |
Proposed branch: | lp:~edwin-grubbs/launchpad/all-downloads-link-sprite |
Merge into: | lp:launchpad |
Diff against target: |
3199 lines 58 files modified
database/sampledata/current-dev.sql (+12/-12) database/schema/Makefile (+3/-3) database/schema/comments.sql (+20/-1) database/schema/fti.py (+8/-12) database/schema/patch-2207-00-3.sql (+6/-1) database/schema/patch-2207-04-0.sql (+13/-0) database/schema/patch-2207-05-0.sql (+12/-0) database/schema/patch-2207-06-0.sql (+122/-0) database/schema/patch-2207-08-0.sql (+20/-0) database/schema/security.cfg (+9/-1) database/schema/trusted.sql (+15/-0) database/schema/unautovacuumable.py (+25/-7) lib/canonical/config/schema-lazr.conf (+0/-7) lib/canonical/ftests/pgsql.py (+11/-0) lib/canonical/launchpad/emailtemplates/branch-merge-proposal-created.txt (+1/-1) lib/canonical/launchpad/icing/style-3-0.css (+3/-0) lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+10/-0) lib/canonical/launchpad/templates/launchpad-login.pt (+7/-7) lib/lp/bugs/doc/externalbugtracker-debbugs.txt (+2/-2) lib/lp/code/browser/branch.py (+12/-2) lib/lp/code/mail/branchmergeproposal.py (+5/-0) lib/lp/code/mail/tests/test_branchmergeproposal.py (+16/-3) lib/lp/code/stories/branches/xx-branch-merge-proposals.txt (+17/-3) lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt (+4/-0) lib/lp/code/templates/branchmergeproposal-resubmit.pt (+3/-2) lib/lp/registry/browser/__init__.py (+47/-9) lib/lp/registry/browser/productseries.py (+7/-2) lib/lp/registry/browser/tests/milestone-views.txt (+6/-0) lib/lp/registry/browser/tests/productseries-views.txt (+38/-9) lib/lp/registry/doc/milestone.txt (+10/-0) lib/lp/registry/model/distribution.py (+1/-0) lib/lp/registry/model/milestone.py (+4/-0) lib/lp/registry/templates/product-index.pt (+1/-1) lib/lp/registry/templates/productseries-delete.pt (+12/-6) lib/lp/soyuz/adapters/archivedependencies.py (+3/-6) lib/lp/soyuz/browser/archive.py (+38/-2) lib/lp/soyuz/browser/configure.zcml (+1/-1) lib/lp/soyuz/browser/packageset.py (+19/-0) lib/lp/soyuz/browser/tests/archive-views.txt (+47/-0) lib/lp/soyuz/configure.zcml (+10/-2) lib/lp/soyuz/doc/archive-dependencies.txt (+14/-28) lib/lp/soyuz/doc/archive.txt (+23/-5) lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt (+1/-0) lib/lp/soyuz/doc/package-cache.txt (+4/-2) lib/lp/soyuz/interfaces/archive.py (+36/-11) lib/lp/soyuz/interfaces/archivepermission.py (+14/-1) lib/lp/soyuz/interfaces/packageset.py (+56/-6) lib/lp/soyuz/interfaces/packagesetgroup.py (+41/-0) lib/lp/soyuz/model/archive.py (+9/-2) lib/lp/soyuz/model/archivepermission.py (+21/-8) lib/lp/soyuz/model/packageset.py (+76/-7) lib/lp/soyuz/model/packagesetgroup.py (+30/-0) lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt (+25/-2) lib/lp/soyuz/stories/webservice/xx-packageset.txt (+234/-60) lib/lp/soyuz/templates/person-archive-subscriptions.pt (+1/-1) lib/lp/soyuz/tests/test_packageset.py (+187/-0) scripts/ftpmaster-tools/_syncorigins.py (+1/-1) utilities/pgmassacre.py (+19/-3) |
To merge this branch: | bzr merge lp:~edwin-grubbs/launchpad/all-downloads-link-sprite |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Nelson (community) | release-critical | Approve | |
Curtis Hovey (community) | code | Approve | |
Review via email: mp+14333@code.launchpad.net |
Commit message
Description of the change
Edwin Grubbs (edwin-grubbs) wrote : | # |
Edwin Grubbs (edwin-grubbs) wrote : | # |
Here is the real diff:
{{{
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -219,7 +219,7 @@
<p class="alternate">
- <a class="sprint info"
+ <a class="sprite info"
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -34,7 +34,7 @@
- <a tal:attributes=
+ <a tal:attributes=
}}}
Curtis Hovey (sinzui) wrote : | # |
This looks good to land. Thanks for fixing the typo and adding the missing class.
Michael Nelson (michael.nelson) wrote : | # |
Approved for the 'real' diff that you've listed. As you said, it'd be great to wait until the last devel changes hit db-devel so you can verify the real diff.
Preview Diff
1 | === modified file 'database/sampledata/current-dev.sql' |
2 | --- database/sampledata/current-dev.sql 2009-09-07 01:46:23 +0000 |
3 | +++ database/sampledata/current-dev.sql 2009-11-03 18:13:40 +0000 |
4 | @@ -969,18 +969,18 @@ |
5 | |
6 | ALTER TABLE archive DISABLE TRIGGER ALL; |
7 | |
8 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (1, 17, NULL, true, NULL, 1, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:12.241774', 15, 1, 8, 5, 1, '2008-09-23 17:29:03.442606', NULL, NULL, NULL, 'Primary Archive for Ubuntu Linux', 0); |
9 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (2, 1, NULL, true, NULL, 2, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.863812', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.445921', NULL, NULL, NULL, 'Primary Archive for Redhat Advanced Server', 0); |
10 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (3, 1, NULL, true, NULL, 3, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.864941', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.446557', NULL, NULL, NULL, 'Primary Archive for Debian GNU/Linux', 0); |
11 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (4, 1, NULL, true, NULL, 4, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.865502', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.44689', NULL, NULL, NULL, 'Primary Archive for The Gentoo Linux', 0); |
12 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (5, 1, NULL, true, NULL, 5, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.866015', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447202', NULL, NULL, NULL, 'Primary Archive for Kubuntu - Free KDE-based Linux', 0); |
13 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (7, 4, NULL, true, NULL, 7, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.866529', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447515', NULL, NULL, NULL, 'Primary Archive for GuadaLinex: Linux for Andalucia', 0); |
14 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (8, 17, NULL, true, NULL, 8, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.867154', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447851', NULL, NULL, NULL, 'Primary Archive for Ubuntu Test', 0); |
15 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (9, 28, 'packages to help my friends.', true, 1024, 1, 2, false, 3, 3, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.867684', 4, 0, 3, 1, 0, '2008-09-23 17:29:03.448178', NULL, NULL, NULL, 'PPA for Celso Providelo', 0); |
16 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (10, 1, 'packages to help the humanity (you know, ubuntu)', true, 1024, 1, 2, false, 1, 1, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.868202', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.448488', NULL, NULL, NULL, 'PPA for Mark Shuttleworth', 0); |
17 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (11, 52, 'I am not allowed to say, I have no privs.', true, 1024, 1, 2, false, 0, 0, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.868709', 1, 0, 0, 1, 0, '2008-09-23 17:29:03.448797', NULL, NULL, NULL, 'PPA for No Privileges Person', 0); |
18 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (12, 17, 'Partner archive', true, NULL, 1, 4, false, NULL, NULL, NULL, NULL, NULL, false, 'partner', true, '2008-05-27 18:15:15.869209', 1, 0, 1, 0, 0, '2008-09-23 17:29:03.449157', NULL, NULL, NULL, 'Partner Archive for Ubuntu Linux', 0); |
19 | -INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score) VALUES (13, 17, 'Partner archive', true, NULL, 8, 4, false, NULL, NULL, NULL, NULL, NULL, false, 'partner', true, '2008-05-27 18:15:15.869732', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.449471', NULL, NULL, NULL, 'Partner Archive for Ubuntu Test', 0); |
20 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (1, 17, NULL, true, NULL, 1, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:12.241774', 15, 1, 8, 5, 1, '2008-09-23 17:29:03.442606', NULL, NULL, NULL, 'Primary Archive for Ubuntu Linux', 0, NULL); |
21 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (2, 1, NULL, true, NULL, 2, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.863812', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.445921', NULL, NULL, NULL, 'Primary Archive for Redhat Advanced Server', 0, NULL); |
22 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (3, 1, NULL, true, NULL, 3, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.864941', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.446557', NULL, NULL, NULL, 'Primary Archive for Debian GNU/Linux', 0, NULL); |
23 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (4, 1, NULL, true, NULL, 4, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.865502', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.44689', NULL, NULL, NULL, 'Primary Archive for The Gentoo Linux', 0, NULL); |
24 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (5, 1, NULL, true, NULL, 5, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.866015', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447202', NULL, NULL, NULL, 'Primary Archive for Kubuntu - Free KDE-based Linux', 0, NULL); |
25 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (7, 4, NULL, true, NULL, 7, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.866529', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447515', NULL, NULL, NULL, 'Primary Archive for GuadaLinex: Linux for Andalucia', 0, NULL); |
26 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (8, 17, NULL, true, NULL, 8, 1, false, NULL, NULL, NULL, NULL, NULL, false, 'primary', true, '2008-05-27 18:15:15.867154', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.447851', NULL, NULL, NULL, 'Primary Archive for Ubuntu Test', 0, NULL); |
27 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (9, 28, 'packages to help my friends.', true, 1024, 1, 2, false, 3, 3, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.867684', 4, 0, 3, 1, 0, '2008-09-23 17:29:03.448178', NULL, NULL, NULL, 'PPA for Celso Providelo', 0, NULL); |
28 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (10, 1, 'packages to help the humanity (you know, ubuntu)', true, 1024, 1, 2, false, 1, 1, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.868202', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.448488', NULL, NULL, NULL, 'PPA for Mark Shuttleworth', 0, NULL); |
29 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (11, 52, 'I am not allowed to say, I have no privs.', true, 1024, 1, 2, false, 0, 0, NULL, NULL, NULL, true, 'ppa', true, '2008-05-27 18:15:15.868709', 1, 0, 0, 1, 0, '2008-09-23 17:29:03.448797', NULL, NULL, NULL, 'PPA for No Privileges Person', 0, NULL); |
30 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (12, 17, 'Partner archive', true, NULL, 1, 4, false, NULL, NULL, NULL, NULL, NULL, false, 'partner', true, '2008-05-27 18:15:15.869209', 1, 0, 1, 0, 0, '2008-09-23 17:29:03.449157', NULL, NULL, NULL, 'Partner Archive for Ubuntu Linux', 0, NULL); |
31 | +INSERT INTO archive (id, owner, description, enabled, authorized_size, distribution, purpose, private, sources_cached, binaries_cached, package_description_cache, fti, buildd_secret, require_virtualized, name, publish, date_updated, total_count, pending_count, succeeded_count, failed_count, building_count, date_created, signing_key, removed_binary_retention_days, num_old_versions_published, displayname, relative_build_score, external_dependencies) VALUES (13, 17, 'Partner archive', true, NULL, 8, 4, false, NULL, NULL, NULL, NULL, NULL, false, 'partner', true, '2008-05-27 18:15:15.869732', 0, 0, 0, 0, 0, '2008-09-23 17:29:03.449471', NULL, NULL, NULL, 'Partner Archive for Ubuntu Test', 0, NULL); |
32 | |
33 | |
34 | ALTER TABLE archive ENABLE TRIGGER ALL; |
35 | |
36 | === modified file 'database/schema/Makefile' |
37 | --- database/schema/Makefile 2009-08-04 22:16:48 +0000 |
38 | +++ database/schema/Makefile 2009-11-03 18:13:41 +0000 |
39 | @@ -113,7 +113,7 @@ |
40 | @ echo "* eg. sudo -u postgres make create" |
41 | @ echo |
42 | @ echo "* Creating database \"$(EMPTY_DBNAME)\"." |
43 | - @ ${CREATEDB} template1 ${EMPTY_DBNAME} |
44 | + @ ${CREATEDB} template0 ${EMPTY_DBNAME} |
45 | @ if ! `createlang -l ${EMPTY_DBNAME} | grep -qs plpythonu`; then \ |
46 | echo "* Installing PL/PythonU"; \ |
47 | createlang -d ${EMPTY_DBNAME} plpythonu; \ |
48 | @@ -142,12 +142,12 @@ |
49 | |
50 | @ echo "* Creating session databases '${SESSION_DBNAME}' (if necessary)" |
51 | @if [ "$$((`psql -l | grep -w ${SESSION_DBNAME} | wc -l`))" = '0' ]; \ |
52 | - then ${CREATEDB} template1 ${SESSION_DBNAME} ; \ |
53 | + then ${CREATEDB} template0 ${SESSION_DBNAME} ; \ |
54 | createlang plpgsql ${SESSION_DBNAME}; \ |
55 | psql -q -d ${SESSION_DBNAME} -f launchpad_session.sql ; \ |
56 | fi |
57 | @ echo "* Creating session database '${TEST_SESSION_DBNAME}'" |
58 | - @ ${CREATEDB} template1 ${TEST_SESSION_DBNAME} |
59 | + @ ${CREATEDB} template0 ${TEST_SESSION_DBNAME} |
60 | @ createlang plpgsql ${TEST_SESSION_DBNAME} |
61 | @ psql -q -d ${TEST_SESSION_DBNAME} -f launchpad_session.sql |
62 | |
63 | |
64 | === modified file 'database/schema/comments.sql' |
65 | --- database/schema/comments.sql 2009-08-30 01:41:38 +0000 |
66 | +++ database/schema/comments.sql 2009-11-03 18:13:41 +0000 |
67 | @@ -763,8 +763,11 @@ |
68 | |
69 | -- SprintAttendance |
70 | COMMENT ON TABLE SprintAttendance IS 'The record that someone will be attending a particular sprint or meeting.'; |
71 | +COMMENT ON COLUMN SprintAttendance.attendee IS 'The person attending the sprint.'; |
72 | +COMMENT ON COLUMN SprintAttendance.sprint IS 'The sprint the person is attending.'; |
73 | COMMENT ON COLUMN SprintAttendance.time_starts IS 'The time from which the person will be available to participate in meetings at the sprint.'; |
74 | COMMENT ON COLUMN SprintAttendance.time_ends IS 'The time of departure from the sprint or conference - this is the last time at which the person is available for meetings during the sprint.'; |
75 | +COMMENT ON COLUMN SprintAttendance.is_physical IS 'Is the person physically attending the sprint'; |
76 | |
77 | |
78 | -- SprintSpecification |
79 | @@ -1862,6 +1865,7 @@ |
80 | COMMENT ON COLUMN Archive.removed_binary_retention_days IS 'The number of days before superseded or deleted binary files are expired in the librarian, or zero for never.'; |
81 | COMMENT ON COLUMN Archive.num_old_versions_published IS 'The number of versions of a package to keep published before older versions are superseded.'; |
82 | COMMENT ON COLUMN Archive.relative_build_score IS 'A delta to the build score that is applied to all builds in this archive.'; |
83 | +COMMENT ON COLUMN Archive.external_dependencies IS 'Newline-separated list of repositories to be used to retrieve any external build dependencies when building packages in this archive, in the format: deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] [components] The series variable is replaced with the series name of the context build. This column is specifically and only intended for OEM migration to Launchpad and should be re-examined in October 2010 to see if it is still relevant.'; |
84 | |
85 | -- ArchiveAuthToken |
86 | |
87 | @@ -2235,11 +2239,20 @@ |
88 | |
89 | -- Packageset |
90 | |
91 | -COMMENT ON TABLE Packageset IS 'Package sets facilitate the grouping of packages for purposes like the control of upload permissions, et.'; |
92 | +COMMENT ON TABLE Packageset IS 'Package sets facilitate the grouping of packages (in a given distro series) for purposes like the control of upload permissions, etc.'; |
93 | COMMENT ON COLUMN Packageset.date_created IS 'Date and time of creation.'; |
94 | COMMENT ON COLUMN Packageset.owner IS 'The Person or team who owns the package set'; |
95 | COMMENT ON COLUMN Packageset.name IS 'The name for the package set on hand.'; |
96 | COMMENT ON COLUMN Packageset.description IS 'The description for the package set on hand.'; |
97 | +COMMENT ON COLUMN Packageset.packagesetgroup IS 'The group this package set is affiliated with.'; |
98 | +COMMENT ON COLUMN Packageset.distroseries IS 'The distro series this package set belongs to.'; |
99 | + |
100 | +-- PackagesetGroup |
101 | + |
102 | +COMMENT ON TABLE PackagesetGroup IS 'Package set groups keep track of equivalent package sets across distro series boundaries.'; |
103 | +COMMENT ON COLUMN Packageset.date_created IS 'Date and time of creation.'; |
104 | +COMMENT ON COLUMN Packageset.owner IS 'The Person or team who owns the package |
105 | +set group.'; |
106 | |
107 | -- PackagesetSources |
108 | |
109 | @@ -2256,3 +2269,9 @@ |
110 | COMMENT ON TABLE FlatPackagesetInclusion IS 'In order to facilitate the querying of set-subset relationships an expanded or flattened representation of the set-subset hierarchy is provided by this table.'; |
111 | COMMENT ON COLUMN FlatPackagesetInclusion.parent IS 'The package set that is (directly or indirectly) including a subset.'; |
112 | COMMENT ON COLUMN FlatPackagesetInclusion.child IS 'The package set that is being included as a subset.'; |
113 | + |
114 | +-- SourcePackageFormatSelection |
115 | +COMMENT ON TABLE SourcePackageFormatSelection IS 'Allowed source package formats for a given distroseries.'; |
116 | +COMMENT ON COLUMN SourcePackageFormatSelection.distroseries IS 'Refers to the distroseries in question.'; |
117 | +COMMENT ON COLUMN SourcePackageFormatSelection.format IS 'The SourcePackageFormat to allow.'; |
118 | + |
119 | |
120 | === modified file 'database/schema/fti.py' |
121 | --- database/schema/fti.py 2009-06-24 21:17:33 +0000 |
122 | +++ database/schema/fti.py 2009-11-03 18:13:42 +0000 |
123 | @@ -13,6 +13,7 @@ |
124 | |
125 | import _pythonpath |
126 | |
127 | +from distutils.version import LooseVersion |
128 | import sys |
129 | import os.path |
130 | from optparse import OptionParser |
131 | @@ -287,9 +288,9 @@ |
132 | """Setup and install tsearch2 if isn't already""" |
133 | |
134 | # tsearch2 is out-of-the-box in 8.3+ |
135 | - v83 = get_pgversion(con).startswith('8.3') |
136 | - |
137 | - assert v83, 'This script only supports PostgreSQL 8.3' |
138 | + required = LooseVersion('8.3.0') |
139 | + assert get_pgversion(con) >= required, ( |
140 | + 'This script only supports PostgreSQL 8.3+') |
141 | |
142 | schema_exists = bool(execute( |
143 | con, "SELECT COUNT(*) FROM pg_namespace WHERE nspname='ts2'", |
144 | @@ -608,18 +609,13 @@ |
145 | |
146 | def get_pgversion(con): |
147 | rows = execute(con, r"show server_version", results=True) |
148 | - return rows[0][0] |
149 | + return LooseVersion(rows[0][0]) |
150 | |
151 | |
152 | def get_tsearch2_sql_path(con): |
153 | - pgversion = get_pgversion(con) |
154 | - if pgversion.startswith('8.2.'): |
155 | - path = os.path.join(PGSQL_BASE, '8.2', 'contrib', 'tsearch2.sql') |
156 | - elif pgversion.startswith('8.3.'): |
157 | - path = os.path.join(PGSQL_BASE, '8.3', 'contrib', 'tsearch2.sql') |
158 | - else: |
159 | - raise RuntimeError('Unknown version %s' % pgversion) |
160 | - |
161 | + major, minor = get_pgversion(con).version[:2] |
162 | + path = os.path.join( |
163 | + PGSQL_BASE, '%d.%d' % (major, minor), 'contrib', 'tsearch2.sql') |
164 | assert os.path.exists(path), '%s does not exist' % path |
165 | return path |
166 | |
167 | |
168 | === modified file 'database/schema/patch-2207-00-3.sql' |
169 | --- database/schema/patch-2207-00-3.sql 2009-09-18 04:13:18 +0000 |
170 | +++ database/schema/patch-2207-00-3.sql 2009-11-03 18:13:41 +0000 |
171 | @@ -1,7 +1,12 @@ |
172 | SET client_min_messages TO ERROR; |
173 | |
174 | UPDATE BugActivity SET person=(SELECT id FROM Person WHERE name='janitor') |
175 | -WHERE person NOT IN (SELECT id FROM Person); |
176 | +FROM ( |
177 | + SELECT BugActivity.id |
178 | + FROM BugActivity LEFT OUTER JOIN Person ON BugActivity.person = Person.id |
179 | + WHERE Person.id IS NULL |
180 | + ) AS Whatever |
181 | +WHERE Whatever.id = BugActivity.id; |
182 | |
183 | ALTER TABLE BugActivity |
184 | ADD CONSTRAINT bugactivity__person__fk |
185 | |
186 | === added file 'database/schema/patch-2207-04-0.sql' |
187 | --- database/schema/patch-2207-04-0.sql 1970-01-01 00:00:00 +0000 |
188 | +++ database/schema/patch-2207-04-0.sql 2009-11-03 18:13:39 +0000 |
189 | @@ -0,0 +1,13 @@ |
190 | +-- Copyright 2009 Canonical Ltd. This software is licensed under the |
191 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
192 | + |
193 | +SET client_min_messages=ERROR; |
194 | + |
195 | + |
196 | +-- Add a column to indicate that the attendee is physically attending |
197 | +-- the sprint. |
198 | +ALTER TABLE SprintAttendance |
199 | + ADD COLUMN is_physical BOOLEAN NOT NULL DEFAULT FALSE; |
200 | + |
201 | + |
202 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 04, 0); |
203 | |
204 | === added file 'database/schema/patch-2207-05-0.sql' |
205 | --- database/schema/patch-2207-05-0.sql 1970-01-01 00:00:00 +0000 |
206 | +++ database/schema/patch-2207-05-0.sql 2009-11-03 18:13:40 +0000 |
207 | @@ -0,0 +1,12 @@ |
208 | +-- Copyright 2009 Canonical Ltd. This software is licensed under the |
209 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
210 | + |
211 | +SET client_min_messages=ERROR; |
212 | + |
213 | + |
214 | +-- Add a column for external archive dependencies. |
215 | +ALTER TABLE Archive |
216 | + ADD COLUMN external_dependencies text DEFAULT NULL; |
217 | + |
218 | + |
219 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 05, 0); |
220 | |
221 | === added file 'database/schema/patch-2207-06-0.sql' |
222 | --- database/schema/patch-2207-06-0.sql 1970-01-01 00:00:00 +0000 |
223 | +++ database/schema/patch-2207-06-0.sql 2009-11-03 18:13:43 +0000 |
224 | @@ -0,0 +1,122 @@ |
225 | +-- Copyright 2009 Canonical Ltd. This software is licensed under the |
226 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
227 | + |
228 | +SET client_min_messages=ERROR; |
229 | + |
230 | +-- ** PART 1 ** Create the 'packagesetgroup' table and the |
231 | +-- 'packageset.packagesetgroup' foreign key, |
232 | +-- populate the 'packagesetgroup' table |
233 | + |
234 | +-- This table keeps track of package sets that are equivalent across |
235 | +-- distro series boundaries. |
236 | +CREATE SEQUENCE packagesetgroup_id_seq |
237 | + START WITH 1 |
238 | + INCREMENT BY 1 |
239 | + NO MAXVALUE |
240 | + NO MINVALUE |
241 | + CACHE 1; |
242 | +CREATE TABLE packagesetgroup ( |
243 | + id integer NOT NULL DEFAULT nextval('packagesetgroup_id_seq'), |
244 | + date_created timestamp without time zone DEFAULT timezone('UTC'::text, now()) NOT NULL, |
245 | + owner integer NOT NULL, |
246 | + -- Please note: the 'name' column is only here to ease the data migration |
247 | + -- and will be dropped at the end of this patch. |
248 | + name text NOT NULL |
249 | +); |
250 | +ALTER SEQUENCE packagesetgroup_id_seq OWNED BY packagesetgroup.id; |
251 | +ALTER TABLE ONLY packagesetgroup |
252 | + ADD CONSTRAINT packagesetgroup_pkey PRIMARY KEY (id); |
253 | +ALTER TABLE ONLY packagesetgroup |
254 | + ADD CONSTRAINT packagesetgroup__owner__fk |
255 | + FOREIGN KEY (owner) REFERENCES person(id); |
256 | + |
257 | +-- Package sets and their clones belong to the same package set group. |
258 | +ALTER TABLE ONLY packageset ADD COLUMN packagesetgroup integer; |
259 | +ALTER TABLE ONLY packageset |
260 | + ADD CONSTRAINT packageset__packagesetgroup__fk |
261 | + FOREIGN KEY (packagesetgroup) REFERENCES packagesetgroup(id); |
262 | + |
263 | +-- Create a group for each of the original (karmic koala) package sets. |
264 | +INSERT INTO packagesetgroup(owner, name) |
265 | +SELECT packageset.owner, packageset.name |
266 | +FROM packageset WHERE NOT packageset.name LIKE('lucid-%'); |
267 | + |
268 | + |
269 | +-- ** PART 2 ** Associate the karmic koala package sets and their lucid lynx |
270 | +-- clones with the appropriate package set groups |
271 | + |
272 | +-- Update the karmic koala package sets so they reference their groups. |
273 | +UPDATE packageset SET packagesetgroup = packagesetgroup.id |
274 | +FROM packagesetgroup WHERE packageset.name = packagesetgroup.name; |
275 | + |
276 | +-- Update the lucid lynx package set *clones* so they reference their groups |
277 | +-- as well. |
278 | +UPDATE packageset SET packagesetgroup = packagesetgroup.id |
279 | +FROM packagesetgroup WHERE packageset.name = 'lucid-' || packagesetgroup.name; |
280 | + |
281 | +-- ** PART 3 ** Add the 'packageset.distroseries' foreign key and |
282 | +-- initialise it for the existing package sets. |
283 | + |
284 | +-- A package set lives in a distro series context. |
285 | +ALTER TABLE ONLY packageset ADD COLUMN distroseries integer; |
286 | + |
287 | +-- Define the foreign key constraint. |
288 | +ALTER TABLE ONLY packageset |
289 | + ADD CONSTRAINT packageset__distroseries__fk |
290 | + FOREIGN KEY (distroseries) REFERENCES distroseries(id); |
291 | + |
292 | +-- First migrate the original package sets created for the karmic koala. |
293 | +UPDATE packageset SET distroseries = distroseries.id FROM distroseries |
294 | +WHERE distroseries.name = 'karmic' AND NOT packageset.name LIKE('lucid-%'); |
295 | + |
296 | +-- Migrate the lucid lynx package sets next. |
297 | +UPDATE packageset SET distroseries = distroseries.id FROM distroseries |
298 | +WHERE distroseries.name = 'lucid' AND packageset.name LIKE('lucid-%'); |
299 | + |
300 | +-- Make the 'distroseries' foreign key mandatory. |
301 | +ALTER TABLE ONLY packageset ALTER COLUMN distroseries SET NOT NULL; |
302 | + |
303 | +-- The package set name is now only unique in conjunction with a distro series. |
304 | +ALTER TABLE ONLY packageset |
305 | + DROP CONSTRAINT packageset_name_key; |
306 | +ALTER TABLE ONLY packageset |
307 | + ADD CONSTRAINT packageset__name__distroseries__key UNIQUE (name, distroseries); |
308 | + |
309 | +-- ** PART 4 ** Strip off the 'lucid-' prefix of the lucid lynx |
310 | +-- package set names |
311 | +UPDATE packageset SET name = substring(name FROM length('lucid-')+1) |
312 | +WHERE name LIKE('lucid-%'); |
313 | + |
314 | +-- ** PART 5 ** Create package set groups for package sets that were added in |
315 | +-- lucid lynx but do not exist in the karmic koala, |
316 | +-- associate these package sets with their newly created groups |
317 | +INSERT INTO packagesetgroup(owner, name) |
318 | +SELECT packageset.owner, packageset.name |
319 | +FROM packageset, distroseries WHERE |
320 | + packageset.packagesetgroup IS NULL |
321 | + AND packageset.distroseries = distroseries.id |
322 | + AND distroseries.name = 'lucid'; |
323 | + |
324 | +UPDATE packageset SET packagesetgroup = packagesetgroup.id |
325 | +FROM packagesetgroup, distroseries |
326 | +WHERE |
327 | + packageset.packagesetgroup IS NULL |
328 | + AND packageset.distroseries = distroseries.id |
329 | + AND distroseries.name = 'lucid' |
330 | + AND packageset.name = packagesetgroup.name; |
331 | + |
332 | +-- ** PART 6 ** Make the 'packageset.packagesetgroup' foreign key mandatory |
333 | +ALTER TABLE ONLY packageset ALTER COLUMN packagesetgroup SET NOT NULL; |
334 | + |
335 | +-- ** PART 7 ** Drop the 'packagesetgroup.name' column that was only added |
336 | +-- for data migration purposes. |
337 | +ALTER TABLE ONLY packagesetgroup DROP COLUMN name; |
338 | + |
339 | +-- Define indices on the newly added foreign keys. |
340 | +CREATE INDEX packageset__packagesetgroup__idx |
341 | + ON packageset(packagesetgroup); |
342 | +CREATE INDEX packageset__distroseries__idx |
343 | + ON packageset(distroseries); |
344 | +CREATE INDEX packagesetgroup__owner__idx ON PackageSetGroup(owner); |
345 | + |
346 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 06, 0); |
347 | |
348 | === added file 'database/schema/patch-2207-08-0.sql' |
349 | --- database/schema/patch-2207-08-0.sql 1970-01-01 00:00:00 +0000 |
350 | +++ database/schema/patch-2207-08-0.sql 2009-11-03 18:13:40 +0000 |
351 | @@ -0,0 +1,20 @@ |
352 | +-- Copyright 2009 Canonical Ltd. This software is licensed under the |
353 | +-- GNU Affero General Public License version 3 (see the file LICENSE). |
354 | + |
355 | +SET client_min_messages=ERROR; |
356 | + |
357 | +CREATE TABLE sourcepackageformatselection ( |
358 | + id serial PRIMARY KEY, |
359 | + distroseries integer NOT NULL |
360 | + CONSTRAINT sourceformatselection__distroseries__fk |
361 | + REFERENCES distroseries, |
362 | + format integer NOT NULL, |
363 | + CONSTRAINT sourceformatselection__distroseries__format__key |
364 | + UNIQUE (distroseries, format) |
365 | +); |
366 | + |
367 | +-- Allow all series to accept format 1.0 by default. |
368 | +INSERT INTO sourcepackageformatselection (distroseries, format) |
369 | + SELECT id, 0 AS format FROM distroseries; |
370 | + |
371 | +INSERT INTO LaunchpadDatabaseRevision VALUES (2207, 08, 0); |
372 | |
373 | === modified file 'database/schema/security.cfg' |
374 | --- database/schema/security.cfg 2009-10-31 01:41:29 +0000 |
375 | +++ database/schema/security.cfg 2009-11-03 18:13:41 +0000 |
376 | @@ -227,6 +227,7 @@ |
377 | public.packagediff = SELECT, INSERT, UPDATE, DELETE |
378 | public.packagediff = SELECT, INSERT, UPDATE, DELETE |
379 | public.packageset = SELECT, INSERT, UPDATE, DELETE |
380 | +public.packagesetgroup = SELECT, INSERT, UPDATE, DELETE |
381 | public.packagesetsources = SELECT, INSERT, UPDATE, DELETE |
382 | public.packagesetinclusion = SELECT, INSERT, UPDATE, DELETE |
383 | public.flatpackagesetinclusion = SELECT, INSERT, UPDATE, DELETE |
384 | @@ -649,6 +650,7 @@ |
385 | public.messagechunk = SELECT, INSERT |
386 | # Merge notifications |
387 | public.codereviewvote = SELECT |
388 | +public.codereviewmessage = SELECT |
389 | |
390 | [branch-distro] |
391 | type=user |
392 | @@ -785,6 +787,7 @@ |
393 | public.packagecopyrequest = SELECT, INSERT, UPDATE |
394 | public.packagediff = SELECT, INSERT, UPDATE |
395 | public.packageset = SELECT |
396 | +public.packagesetgroup = SELECT |
397 | public.packagesetsources = SELECT, INSERT, UPDATE, DELETE |
398 | public.packagesetinclusion = SELECT, INSERT, UPDATE, DELETE |
399 | public.flatpackagesetinclusion = SELECT, INSERT, UPDATE, DELETE |
400 | @@ -862,6 +865,7 @@ |
401 | public.teammembership = SELECT |
402 | public.gpgkey = SELECT |
403 | public.packageset = SELECT |
404 | +public.packagesetgroup = SELECT |
405 | public.packagesetsources = SELECT |
406 | public.packagesetinclusion = SELECT |
407 | public.flatpackagesetinclusion = SELECT |
408 | @@ -917,7 +921,7 @@ |
409 | public.bugpackageinfestation = SELECT, INSERT, UPDATE |
410 | public.bugproductinfestation = SELECT, INSERT, UPDATE |
411 | public.bugsubscription = SELECT, INSERT, UPDATE, DELETE |
412 | -public.bugtask = SELECT, INSERT, UPDATE |
413 | +public.bugtask = SELECT, INSERT, UPDATE, DELETE |
414 | public.bugtracker = SELECT, INSERT, UPDATE, DELETE |
415 | public.bugtrackeralias = SELECT, INSERT, UPDATE, DELETE |
416 | public.bugwatch = SELECT, INSERT, UPDATE, DELETE |
417 | @@ -1062,6 +1066,7 @@ |
418 | public.archive = SELECT, INSERT, UPDATE |
419 | public.archivearch = SELECT, INSERT, UPDATE |
420 | public.packageset = SELECT |
421 | +public.packagesetgroup = SELECT |
422 | public.packagesetsources = SELECT |
423 | public.packagesetinclusion = SELECT |
424 | public.flatpackagesetinclusion = SELECT |
425 | @@ -1250,6 +1255,7 @@ |
426 | public.personlanguage = SELECT |
427 | public.structuralsubscription = SELECT |
428 | public.packageset = SELECT |
429 | +public.packagesetgroup = SELECT |
430 | public.packagesetsources = SELECT |
431 | public.packagesetinclusion = SELECT |
432 | public.flatpackagesetinclusion = SELECT |
433 | @@ -1702,6 +1708,7 @@ |
434 | public.productseries = SELECT |
435 | public.revision = SELECT |
436 | public.revisionauthor = SELECT, INSERT |
437 | +public.seriessourcepackagebranch = SELECT |
438 | public.sourcepackagename = SELECT |
439 | public.staticdiff = SELECT, INSERT |
440 | public.teammembership = SELECT |
441 | @@ -1807,6 +1814,7 @@ |
442 | type=user |
443 | public.libraryfilecontent = SELECT |
444 | public.openidrpconfig = SELECT |
445 | +public.branch = SELECT |
446 | |
447 | [modified-branches] |
448 | type=user |
449 | |
450 | === modified file 'database/schema/trusted.sql' |
451 | --- database/schema/trusted.sql 2009-08-19 15:35:13 +0000 |
452 | +++ database/schema/trusted.sql 2009-11-03 18:13:42 +0000 |
453 | @@ -1029,7 +1029,22 @@ |
454 | DECLARE |
455 | parent_name text; |
456 | child_name text; |
457 | + parent_distroseries text; |
458 | + child_distroseries text; |
459 | BEGIN |
460 | + -- Make sure that the package sets being associated here belong |
461 | + -- to the same distro series. |
462 | + IF (SELECT parent.distroseries != child.distroseries |
463 | + FROM packageset parent, packageset child |
464 | + WHERE parent.id = NEW.parent AND child.id = NEW.child) |
465 | + THEN |
466 | + SELECT name INTO parent_name FROM packageset WHERE id = NEW.parent; |
467 | + SELECT name INTO child_name FROM packageset WHERE id = NEW.child; |
468 | + SELECT ds.name INTO parent_distroseries FROM packageset ps, distroseries ds WHERE ps.id = NEW.parent AND ps.distroseries = ds.id; |
469 | + SELECT ds.name INTO child_distroseries FROM packageset ps, distroseries ds WHERE ps.id = NEW.child AND ps.distroseries = ds.id; |
470 | + RAISE EXCEPTION 'Package sets % and % belong to different distro series (to % and % respectively) and thus cannot be associated.', child_name, parent_name, child_distroseries, parent_distroseries; |
471 | + END IF; |
472 | + |
473 | IF EXISTS( |
474 | SELECT * FROM flatpackagesetinclusion |
475 | WHERE parent = NEW.child AND child = NEW.parent LIMIT 1) |
476 | |
477 | === modified file 'database/schema/unautovacuumable.py' |
478 | --- database/schema/unautovacuumable.py 2009-06-24 21:17:33 +0000 |
479 | +++ database/schema/unautovacuumable.py 2009-11-03 18:13:43 +0000 |
480 | @@ -19,6 +19,7 @@ |
481 | # pylint: disable-msg=W0403 |
482 | import _pythonpath |
483 | |
484 | +from distutils.version import LooseVersion |
485 | from optparse import OptionParser |
486 | import sys |
487 | import time |
488 | @@ -44,14 +45,31 @@ |
489 | con.set_isolation_level(0) # Autocommit |
490 | cur = con.cursor() |
491 | |
492 | + cur.execute('show server_version') |
493 | + pg_version = LooseVersion(cur.fetchone()[0]) |
494 | + |
495 | log.debug("Disabling autovacuum on all tables in the database.") |
496 | - cur.execute(""" |
497 | - INSERT INTO pg_autovacuum |
498 | - SELECT pg_class.oid, FALSE, -1,-1,-1,-1,-1,-1,-1,-1 |
499 | - FROM pg_class |
500 | - WHERE relkind in ('r','t') |
501 | - AND pg_class.oid NOT IN (SELECT vacrelid FROM pg_autovacuum) |
502 | - """) |
503 | + if pg_version < LooseVersion('8.4.0'): |
504 | + cur.execute(""" |
505 | + INSERT INTO pg_autovacuum |
506 | + SELECT pg_class.oid, FALSE, -1,-1,-1,-1,-1,-1,-1,-1 |
507 | + FROM pg_class |
508 | + WHERE relkind in ('r','t') |
509 | + AND pg_class.oid NOT IN (SELECT vacrelid FROM pg_autovacuum) |
510 | + """) |
511 | + else: |
512 | + cur.execute(""" |
513 | + SELECT nspname,relname |
514 | + FROM pg_namespace, pg_class |
515 | + WHERE relnamespace = pg_namespace.oid |
516 | + AND relkind = 'r' AND nspname <> 'pg_catalog' |
517 | + """) |
518 | + for namespace, table in list(cur.fetchall()): |
519 | + cur.execute(""" |
520 | + ALTER TABLE ONLY "%s"."%s" SET ( |
521 | + autovacuum_enabled=false, |
522 | + toast.autovacuum_enabled=false) |
523 | + """ % (namespace, table)) |
524 | |
525 | log.debug("Killing existing autovacuum processes") |
526 | num_autovacuums = -1 |
527 | |
528 | === modified file 'lib/canonical/config/schema-lazr.conf' |
529 | --- lib/canonical/config/schema-lazr.conf 2009-10-29 05:50:08 +0000 |
530 | +++ lib/canonical/config/schema-lazr.conf 2009-11-03 18:13:40 +0000 |
531 | @@ -1436,13 +1436,6 @@ |
532 | |
533 | |
534 | [ppa.master] |
535 | -# Line-separated repository lines to be used as build dependencies, in |
536 | -# the following format: |
537 | -# deb [user:pass@]<host>[/path] %(series)s[-pocket] [components] |
538 | -# 'series' variable is replaced with the series name of the context build. |
539 | -# datatype: string |
540 | -dependencies: none |
541 | - |
542 | |
543 | [poimport] |
544 | # The database user which will be used by this process. |
545 | |
546 | === modified file 'lib/canonical/ftests/pgsql.py' |
547 | --- lib/canonical/ftests/pgsql.py 2009-06-25 05:30:52 +0000 |
548 | +++ lib/canonical/ftests/pgsql.py 2009-11-03 18:13:40 +0000 |
549 | @@ -144,6 +144,13 @@ |
550 | host = None |
551 | port = None |
552 | |
553 | + # Class attributes. With PostgreSQL 8.4, pg_shdepend bloats |
554 | + # hugely when we drop and create databases, because this |
555 | + # operation cancels any autovacuum process maintaining it. |
556 | + # To cope, we need to manually vacuum it ourselves occasionally. |
557 | + vacuum_shdepend_every = 10 |
558 | + _vacuum_shdepend_counter = 0 |
559 | + |
560 | # (template, name) of last test. Class attribute. |
561 | _last_db = (None, None) |
562 | # Class attribute. True if we should destroy the DB because changes made. |
563 | @@ -308,6 +315,10 @@ |
564 | if 'does not exist' in str(x): |
565 | break |
566 | raise |
567 | + PgTestSetup._vacuum_shdepend_counter += 1 |
568 | + if (PgTestSetup._vacuum_shdepend_counter |
569 | + % PgTestSetup.vacuum_shdepend_every) == 0: |
570 | + cur.execute('VACUUM pg_catalog.pg_shdepend') |
571 | finally: |
572 | con.close() |
573 | |
574 | |
575 | === modified file 'lib/canonical/launchpad/emailtemplates/branch-merge-proposal-created.txt' |
576 | --- lib/canonical/launchpad/emailtemplates/branch-merge-proposal-created.txt 2009-08-05 19:27:17 +0000 |
577 | +++ lib/canonical/launchpad/emailtemplates/branch-merge-proposal-created.txt 2009-11-03 18:13:39 +0000 |
578 | @@ -1,4 +1,4 @@ |
579 | -%(proposal_registrant)s has proposed merging %(source_branch)s into %(target_branch)s. |
580 | +%(proposal_registrant)s has proposed merging %(source_branch)s into %(target_branch)s%(prerequisite)s. |
581 | |
582 | %(reviews)s%(related_bugs)s%(gap)s%(comment)s%(diff_cutoff_warning)s |
583 | -- |
584 | |
585 | === modified file 'lib/canonical/launchpad/icing/style-3-0.css' |
586 | --- lib/canonical/launchpad/icing/style-3-0.css 2009-10-29 21:39:12 +0000 |
587 | +++ lib/canonical/launchpad/icing/style-3-0.css 2009-11-03 18:13:42 +0000 |
588 | @@ -466,6 +466,9 @@ |
589 | padding: 0 1.5em 0 0; |
590 | } |
591 | .subordinate { |
592 | + margin-left: 2em; |
593 | + } |
594 | +ol.subordinate { |
595 | margin-left: 4em; |
596 | } |
597 | .two-column-list li, |
598 | |
599 | === modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py' |
600 | --- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2009-10-29 19:55:59 +0000 |
601 | +++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2009-11-03 18:13:43 +0000 |
602 | @@ -234,6 +234,14 @@ |
603 | IArchive, 'getAllPublishedBinaries', 'status', PackagePublishingStatus) |
604 | patch_choice_parameter_type( |
605 | IArchive, 'getAllPublishedBinaries', 'pocket', PackagePublishingPocket) |
606 | +patch_plain_parameter_type( |
607 | + IArchive, 'isSourceUploadAllowed', 'distroseries', IDistroSeries) |
608 | +patch_plain_parameter_type( |
609 | + IArchive, 'newPackagesetUploader', 'packageset', IPackageset) |
610 | +patch_plain_parameter_type( |
611 | + IArchive, 'getUploadersForPackageset', 'packageset', IPackageset) |
612 | +patch_plain_parameter_type( |
613 | + IArchive, 'deletePackagesetUploader', 'packageset', IPackageset) |
614 | |
615 | # IDistribution |
616 | IDistribution['serieses'].value_type.schema = IDistroSeries |
617 | @@ -283,6 +291,8 @@ |
618 | IPackageset, 'getSourcesSharedBy', 'other_package_set', IPackageset) |
619 | patch_plain_parameter_type( |
620 | IPackageset, 'getSourcesNotSharedBy', 'other_package_set', IPackageset) |
621 | +patch_collection_return_type( |
622 | + IPackageset, 'relatedSets', IPackageset) |
623 | |
624 | # IPackageUpload |
625 | IPackageUpload['pocket'].vocabulary = PackagePublishingPocket |
626 | |
627 | === modified file 'lib/canonical/launchpad/templates/launchpad-login.pt' |
628 | --- lib/canonical/launchpad/templates/launchpad-login.pt 2009-10-16 16:13:00 +0000 |
629 | +++ lib/canonical/launchpad/templates/launchpad-login.pt 2009-11-03 18:13:39 +0000 |
630 | @@ -171,13 +171,13 @@ |
631 | <p> |
632 | Creating your Launchpad account is easy. Here's what to do:</p> |
633 | |
634 | - <ol class="subordinate"> |
635 | - <li>Make sure cookies are enabled in your browser.</li> |
636 | - <li>Enter your e-mail address and answer our random question |
637 | - so we know that you're human. |
638 | - </li> |
639 | - <li>Follow the URL in the confirmation e-mail that Launchpad sends and you're done!</li> |
640 | - </ol> |
641 | + <ol class="subordinate"> |
642 | + <li>Make sure cookies are enabled in your browser.</li> |
643 | + <li>Enter your e-mail address and answer our random question |
644 | + so we know that you're human. |
645 | + </li> |
646 | + <li>Follow the URL in the confirmation e-mail that Launchpad sends and you're done!</li> |
647 | + </ol> |
648 | |
649 | |
650 | |
651 | |
652 | === modified file 'lib/lp/bugs/doc/externalbugtracker-debbugs.txt' |
653 | --- lib/lp/bugs/doc/externalbugtracker-debbugs.txt 2009-10-06 07:38:29 +0000 |
654 | +++ lib/lp/bugs/doc/externalbugtracker-debbugs.txt 2009-11-03 18:13:40 +0000 |
655 | @@ -102,7 +102,7 @@ |
656 | >>> from lp.bugs.scripts.checkwatches import BugWatchUpdater |
657 | >>> bug_watch_updater = BugWatchUpdater(txn) |
658 | >>> external_debbugs.sync_comments = False |
659 | - >>> bug_watch_ids = [bug_watch.id for bug_watch in bug_watches] |
660 | + >>> bug_watch_ids = sorted([bug_watch.id for bug_watch in bug_watches]) |
661 | >>> bug_watch_updater.updateBugWatches(external_debbugs, bug_watches) |
662 | INFO:...:Updating 5 watches for 5 bugs on http://... |
663 | |
664 | @@ -110,10 +110,10 @@ |
665 | >>> for bug_watch_id in bug_watch_ids: |
666 | ... bug_watch = getUtility(IBugWatchSet).get(bug_watch_id) |
667 | ... print "%s: %s" % (bug_watch.remotebug, bug_watch.remotestatus) |
668 | + 280883: done grave woody security |
669 | 304014: open important |
670 | 327452: done critical patch security |
671 | 327549: open important security |
672 | - 280883: done grave woody security |
673 | 308994: open important |
674 | |
675 | The lastchecked attribute got updated for each bug watch, so no more |
676 | |
677 | === modified file 'lib/lp/code/browser/branch.py' |
678 | --- lib/lp/code/browser/branch.py 2009-10-11 02:33:30 +0000 |
679 | +++ lib/lp/code/browser/branch.py 2009-11-03 18:13:40 +0000 |
680 | @@ -575,7 +575,8 @@ |
681 | BranchLifecycleStatus, |
682 | css_class_prefix='branchstatus'), |
683 | 'status_value': self.context.lifecycle_status.title, |
684 | - 'user_can_edit_status': check_permission('launchpad.Edit', self.context), |
685 | + 'user_can_edit_status': check_permission( |
686 | + 'launchpad.Edit', self.context), |
687 | 'branch_path': '/' + self.context.unique_name, |
688 | }) |
689 | |
690 | @@ -1153,6 +1154,13 @@ |
691 | description=_( |
692 | "The branch that the source branch will be merged into.")) |
693 | |
694 | + prerequisite_branch = Choice( |
695 | + title=_('Prerequisite Branch'), |
696 | + vocabulary='Branch', required=False, readonly=False, |
697 | + description=_( |
698 | + 'A branch that should be merged before this one. (Its changes' |
699 | + ' will not be shown in the diff.)')) |
700 | + |
701 | comment = Text( |
702 | title=_('Initial Comment'), required=False, |
703 | description=_('Describe your change.')) |
704 | @@ -1206,6 +1214,7 @@ |
705 | registrant = self.user |
706 | source_branch = self.context |
707 | target_branch = data['target_branch'] |
708 | + prerequisite_branch = data.get('prerequisite_branch') |
709 | |
710 | review_requests = [] |
711 | reviewer = data.get('reviewer') |
712 | @@ -1217,7 +1226,8 @@ |
713 | # and an advanced expandable section. |
714 | proposal = source_branch.addLandingTarget( |
715 | registrant=registrant, target_branch=target_branch, |
716 | - needs_review=True, initial_comment=data.get('comment'), |
717 | + prerequisite_branch=prerequisite_branch, needs_review=True, |
718 | + initial_comment=data.get('comment'), |
719 | review_requests=review_requests) |
720 | |
721 | self.next_url = canonical_url(proposal) |
722 | |
723 | === modified file 'lib/lp/code/mail/branchmergeproposal.py' |
724 | --- lib/lp/code/mail/branchmergeproposal.py 2009-09-18 13:50:52 +0000 |
725 | +++ lib/lp/code/mail/branchmergeproposal.py 2009-11-03 18:13:41 +0000 |
726 | @@ -199,6 +199,7 @@ |
727 | 'proposal_registrant': self.merge_proposal.registrant.displayname, |
728 | 'source_branch': self.merge_proposal.source_branch.bzr_identity, |
729 | 'target_branch': self.merge_proposal.target_branch.bzr_identity, |
730 | + 'prerequisite': '', |
731 | 'proposal_title': self.merge_proposal.title, |
732 | 'proposal_url': canonical_url(self.merge_proposal), |
733 | 'edit_subscription': '', |
734 | @@ -209,6 +210,10 @@ |
735 | 'diff_cutoff_warning': '', |
736 | } |
737 | |
738 | + if self.merge_proposal.prerequisite_branch is not None: |
739 | + prereq_url = self.merge_proposal.prerequisite_branch.bzr_identity |
740 | + params['prerequisite'] = ' with %s as a prerequisite' % prereq_url |
741 | + |
742 | requested_reviews = [] |
743 | for review in self.requested_reviews: |
744 | reviewer = review.reviewer |
745 | |
746 | === modified file 'lib/lp/code/mail/tests/test_branchmergeproposal.py' |
747 | --- lib/lp/code/mail/tests/test_branchmergeproposal.py 2009-09-18 13:50:52 +0000 |
748 | +++ lib/lp/code/mail/tests/test_branchmergeproposal.py 2009-11-03 18:13:42 +0000 |
749 | @@ -36,7 +36,7 @@ |
750 | super(TestMergeProposalMailing, self).setUp('admin@canonical.com') |
751 | |
752 | def makeProposalWithSubscriber(self, diff_text=None, |
753 | - initial_comment=None): |
754 | + initial_comment=None, prerequisite=False): |
755 | if diff_text is not None: |
756 | preview_diff = PreviewDiff.create( |
757 | diff_text, |
758 | @@ -49,9 +49,14 @@ |
759 | registrant = self.factory.makePerson( |
760 | name='bazqux', displayname='Baz Qux', email='baz.qux@example.com') |
761 | product = self.factory.makeProduct(name='super-product') |
762 | + if prerequisite: |
763 | + prerequisite_branch = self.factory.makeProductBranch(product) |
764 | + else: |
765 | + prerequisite_branch = None |
766 | bmp = self.factory.makeBranchMergeProposal( |
767 | - registrant=registrant, product=product, preview_diff=preview_diff, |
768 | - initial_comment=initial_comment) |
769 | + registrant=registrant, product=product, |
770 | + prerequisite_branch=prerequisite_branch, |
771 | + preview_diff=preview_diff, initial_comment=initial_comment) |
772 | subscriber = self.factory.makePerson(displayname='Baz Quxx', |
773 | email='baz.quxx@example.com') |
774 | bmp.source_branch.subscribe(subscriber, |
775 | @@ -133,6 +138,14 @@ |
776 | 'Requested reviews:\n Review-person (review-person)\n\n-- \n', |
777 | ctrl.body) |
778 | |
779 | + def test_forCreation_with_prerequisite_branch(self): |
780 | + """Correctly format list of reviewers.""" |
781 | + bmp, subscriber = self.makeProposalWithSubscriber(prerequisite=True) |
782 | + mailer = BMPMailer.forCreation(bmp, bmp.registrant) |
783 | + ctrl = mailer.generateEmail('baz.quxx@example.com', subscriber) |
784 | + prereq = bmp.prerequisite_branch.bzr_identity |
785 | + self.assertIn(' with %s as a prerequisite.' % prereq, ctrl.body) |
786 | + |
787 | def test_to_addrs_includes_reviewers(self): |
788 | """The addresses for the to header include requested reviewers""" |
789 | request, requester = self.makeReviewRequest() |
790 | |
791 | === modified file 'lib/lp/code/stories/branches/xx-branch-merge-proposals.txt' |
792 | --- lib/lp/code/stories/branches/xx-branch-merge-proposals.txt 2009-10-23 02:36:17 +0000 |
793 | +++ lib/lp/code/stories/branches/xx-branch-merge-proposals.txt 2009-11-03 18:13:42 +0000 |
794 | @@ -57,6 +57,9 @@ |
795 | >>> nopriv_browser.getControl( |
796 | ... name='field.target_branch.target_branch').value = ( |
797 | ... '~name12/gnome-terminal/main') |
798 | + >>> nopriv_browser.getControl( |
799 | + ... name='field.prerequisite_branch').value = ( |
800 | + ... '~name12/gnome-terminal/pushed') |
801 | |
802 | There is a cancel link shown with the buttons. |
803 | |
804 | @@ -68,6 +71,20 @@ |
805 | >>> print nopriv_browser.url |
806 | http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/... |
807 | |
808 | +The summary reflects the selected target and prerequisite. |
809 | + |
810 | + >>> def print_summary(browser): |
811 | + ... print extract_text(find_tag_by_id( |
812 | + ... browser.contents, 'proposal-summary')) |
813 | + >>> print_summary(nopriv_browser) |
814 | + Status:... |
815 | + Proposed branch: |
816 | + lp://dev/~name12/gnome-terminal/klingon |
817 | + Merge into: |
818 | + lp://dev/~name12/gnome-terminal/main |
819 | + Prerequisite: |
820 | + lp://dev/~name12/gnome-terminal/pushed |
821 | + |
822 | |
823 | Editing a commit message |
824 | ------------------------ |
825 | @@ -83,9 +100,6 @@ |
826 | >>> print nopriv_browser.url |
827 | http://code.launchpad.dev/~name12/gnome-terminal/klingon/+merge/1 |
828 | |
829 | - >>> def print_summary(browser): |
830 | - ... print extract_text(find_tag_by_id( |
831 | - ... browser.contents, 'proposal-summary')) |
832 | >>> print_tag_with_id(nopriv_browser.contents, 'edit-description') |
833 | Commit Message |
834 | Add more <b>mojo</b> |
835 | |
836 | === modified file 'lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt' |
837 | --- lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt 2009-10-21 23:20:59 +0000 |
838 | +++ lib/lp/code/templates/branchmergeproposal-pagelet-summary.pt 2009-11-03 18:13:43 +0000 |
839 | @@ -105,6 +105,10 @@ |
840 | <th>Merge into:</th> |
841 | <td tal:content="structure context/target_branch/fmt:bzr-link">lp:~foo/bar/baz</td> |
842 | </tr> |
843 | + <tr tal:condition="context/prerequisite_branch"> |
844 | + <th>Prerequisite:</th> |
845 | + <td tal:content="structure context/prerequisite_branch/fmt:bzr-link">lp:~foo/bar/baz</td> |
846 | + </tr> |
847 | <tr tal:condition="context/preview_diff"> |
848 | <th>Diff against target:</th> |
849 | <td> |
850 | |
851 | === modified file 'lib/lp/code/templates/branchmergeproposal-resubmit.pt' |
852 | --- lib/lp/code/templates/branchmergeproposal-resubmit.pt 2009-09-08 19:24:40 +0000 |
853 | +++ lib/lp/code/templates/branchmergeproposal-resubmit.pt 2009-11-03 18:13:39 +0000 |
854 | @@ -14,8 +14,9 @@ |
855 | <div metal:fill-slot="extra_info"> |
856 | <p> |
857 | Resubmitting this proposal to merge will cause this proposal to be |
858 | - marked as <strong>superseded</strong>. Another proposal to merge with |
859 | - the same source and target branches will be created. |
860 | + marked as <strong>superseded</strong>. Another merge proposal will |
861 | + be created, with the same source, target and prerequisite branch |
862 | + (if any). |
863 | </p> |
864 | <p> |
865 | Everyone who has reviewed the previous proposal or was requested to |
866 | |
867 | === modified file 'lib/lp/registry/browser/__init__.py' |
868 | --- lib/lp/registry/browser/__init__.py 2009-09-22 16:21:12 +0000 |
869 | +++ lib/lp/registry/browser/__init__.py 2009-11-03 18:13:42 +0000 |
870 | @@ -18,6 +18,8 @@ |
871 | |
872 | from zope.component import getUtility |
873 | |
874 | +from storm.store import Store |
875 | + |
876 | from lp.bugs.interfaces.bugtask import BugTaskSearchParams, IBugTaskSet |
877 | from lp.registry.interfaces.productseries import IProductSeries |
878 | from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities |
879 | @@ -136,15 +138,22 @@ |
880 | """The context's URL.""" |
881 | return canonical_url(self.context) |
882 | |
883 | - def _getBugtasks(self, milestone): |
884 | - """Return the list `IBugTask`s targeted to the milestone.""" |
885 | - params = BugTaskSearchParams(milestone=milestone, user=None) |
886 | + def _getBugtasks(self, target): |
887 | + """Return the list `IBugTask`s associated with the target.""" |
888 | + if IProductSeries.providedBy(target): |
889 | + params = BugTaskSearchParams(user=None) |
890 | + params.setProductSeries(target) |
891 | + else: |
892 | + params = BugTaskSearchParams(milestone=target, user=None) |
893 | bugtasks = getUtility(IBugTaskSet).search(params) |
894 | return list(bugtasks) |
895 | |
896 | - def _getSpecifications(self, milestone): |
897 | - """Return the list `ISpecification`s targeted to the milestone.""" |
898 | - return list(milestone.specifications) |
899 | + def _getSpecifications(self, target): |
900 | + """Return the list `ISpecification`s associated to the target.""" |
901 | + if IProductSeries.providedBy(target): |
902 | + return list(target.all_specifications) |
903 | + else: |
904 | + return list(target.specifications) |
905 | |
906 | def _getProductRelease(self, milestone): |
907 | """The `IProductRelease` associated with the milestone.""" |
908 | @@ -158,10 +167,37 @@ |
909 | else: |
910 | return [] |
911 | |
912 | + def _unsubscribe_structure(self, structure): |
913 | + """Removed the subscriptions from structure.""" |
914 | + for subscription in structure.getSubscriptions(): |
915 | + # The owner of the subscription or an admin are the only users |
916 | + # that can destroy a subscription, but this rule cannot prevent |
917 | + # the owner from removing the structure. |
918 | + Store.of(subscription).remove(subscription) |
919 | + |
920 | + def _remove_series_bugs_and_specifications(self, series): |
921 | + """Untarget the associated bugs and subscriptions.""" |
922 | + for spec in self._getSpecifications(series): |
923 | + spec.proposeGoal(None, self.user) |
924 | + for bugtask in self._getBugtasks(series): |
925 | + # Bugtasks cannot be deleted directly. In this case, the bugtask |
926 | + # is already reported on the product, so the series bugtask has |
927 | + # no purpose without a series. |
928 | + Store.of(bugtask).remove(bugtask) |
929 | + |
930 | def _deleteProductSeries(self, series): |
931 | - """Remove the series and delete/unlink related objects.""" |
932 | - # Delete all milestones, releases, and files. |
933 | - # Any associated bugtasks and specifications are untargeted. |
934 | + """Remove the series and delete/unlink related objects. |
935 | + |
936 | + All subordinate milestones, releases, and files will be deleted. |
937 | + Milestone bugs and blueprints will be untargeted. |
938 | + Series bugs and blueprints will be untargeted. |
939 | + Series and milestone structural subscriptions are unsubscribed. |
940 | + Series branches are unlinked. |
941 | + """ |
942 | + self._unsubscribe_structure(series) |
943 | + self._remove_series_bugs_and_specifications(series) |
944 | + series.branch = None |
945 | + |
946 | for milestone in series.all_milestones: |
947 | self._deleteMilestone(milestone) |
948 | # Series are not deleted because some objects like translations are |
949 | @@ -174,6 +210,7 @@ |
950 | |
951 | def _deleteMilestone(self, milestone): |
952 | """Delete a milestone and unlink related objects.""" |
953 | + self._unsubscribe_structure(milestone) |
954 | for bugtask in self._getBugtasks(milestone): |
955 | bugtask.milestone = None |
956 | for spec in self._getSpecifications(milestone): |
957 | @@ -191,6 +228,7 @@ |
958 | |
959 | class RegistryEditFormView(LaunchpadEditFormView): |
960 | """A base class that provides consistent edit form behaviour.""" |
961 | + |
962 | @property |
963 | def page_title(self): |
964 | """The page title.""" |
965 | |
966 | === modified file 'lib/lp/registry/browser/productseries.py' |
967 | --- lib/lp/registry/browser/productseries.py 2009-10-26 19:47:59 +0000 |
968 | +++ lib/lp/registry/browser/productseries.py 2009-11-03 18:13:42 +0000 |
969 | @@ -479,7 +479,7 @@ |
970 | @cachedproperty |
971 | def bugtasks(self): |
972 | """A list of all `IBugTask`s targeted to this series.""" |
973 | - all_bugtasks = [] |
974 | + all_bugtasks = self._getBugtasks(self.context) |
975 | for milestone in self.milestones: |
976 | all_bugtasks.extend(self._getBugtasks(milestone)) |
977 | return all_bugtasks |
978 | @@ -487,7 +487,7 @@ |
979 | @cachedproperty |
980 | def specifications(self): |
981 | """A list of all `ISpecification`s targeted to this series.""" |
982 | - all_specifications = [] |
983 | + all_specifications = self._getSpecifications(self.context) |
984 | for milestone in self.milestones: |
985 | all_specifications.extend(self._getSpecifications(milestone)) |
986 | return all_specifications |
987 | @@ -497,6 +497,11 @@ |
988 | """Does the series have any targeted bugtasks or specifications.""" |
989 | return len(self.bugtasks) > 0 or len(self.specifications) > 0 |
990 | |
991 | + @property |
992 | + def has_linked_branch(self): |
993 | + """Is the series linked to a branch.""" |
994 | + return self.context.branch is not None |
995 | + |
996 | @cachedproperty |
997 | def product_release_files(self): |
998 | """A list of all `IProductReleaseFile`s that belong to this series.""" |
999 | |
1000 | === modified file 'lib/lp/registry/browser/tests/milestone-views.txt' |
1001 | --- lib/lp/registry/browser/tests/milestone-views.txt 2009-09-22 16:21:12 +0000 |
1002 | +++ lib/lp/registry/browser/tests/milestone-views.txt 2009-11-03 18:13:41 +0000 |
1003 | @@ -654,6 +654,9 @@ |
1004 | >>> bug = factory.makeBug(product=firefox) |
1005 | >>> bugtask = bug.bugtasks[0] |
1006 | >>> bugtask.milestone = milestone |
1007 | + >>> subscription = milestone.addSubscription(owner, owner) |
1008 | + >>> [subscription for subscription in owner.structural_subscriptions] |
1009 | + [<StructuralSubscription ...>] |
1010 | |
1011 | >>> view = create_initialized_view(milestone, '+delete') |
1012 | >>> [bugtask.milestone.name for bugtask in view.bugtasks] |
1013 | @@ -685,6 +688,9 @@ |
1014 | >>> print bugtask.milestone |
1015 | None |
1016 | |
1017 | + >>> [subscription for subscription in owner.structural_subscriptions] |
1018 | + [] |
1019 | + |
1020 | No Privileges Person cannot access this view because he is neither the |
1021 | project owner or series driver.. |
1022 | |
1023 | |
1024 | === modified file 'lib/lp/registry/browser/tests/productseries-views.txt' |
1025 | --- lib/lp/registry/browser/tests/productseries-views.txt 2009-10-23 16:21:47 +0000 |
1026 | +++ lib/lp/registry/browser/tests/productseries-views.txt 2009-11-03 18:13:42 +0000 |
1027 | @@ -229,6 +229,8 @@ |
1028 | [] |
1029 | >>> view.product_release_files |
1030 | [] |
1031 | + >>> view.has_linked_branch |
1032 | + False |
1033 | |
1034 | Most series that are deleted do not have any related objects, but a small |
1035 | portion do. |
1036 | @@ -244,18 +246,38 @@ |
1037 | >>> bugtask = bug.bugtasks[0] |
1038 | >>> bugtask.milestone = milestone_two |
1039 | |
1040 | + >>> owner = product.owner |
1041 | + >>> series_specification = factory.makeSpecification(product=product) |
1042 | + >>> series_specification.proposeGoal(productseries, owner) |
1043 | + >>> series_bugtask = factory.makeBugTask(bug=bug, target=productseries) |
1044 | + >>> subscription = productseries.addSubscription(owner, owner) |
1045 | + >>> productseries.branch = factory.makeBranch() |
1046 | + |
1047 | >>> view = create_view(productseries, name='+delete') |
1048 | >>> [milestone.name for milestone in view.milestones] |
1049 | [u'0.2', u'0.1'] |
1050 | >>> view.has_bugtasks_and_specifications |
1051 | True |
1052 | - >>> [bugtask.milestone.name for bugtask in view.bugtasks] |
1053 | - [u'0.2'] |
1054 | - >>> [spec.milestone.name for spec in view.specifications] |
1055 | - [u'0.1'] |
1056 | - |
1057 | - # Listing and deleting product release files is done in the story |
1058 | - # because they require the Librarian to be running. |
1059 | + >>> for bugtask in view.bugtasks: |
1060 | + ... if bugtask.milestone is not None: |
1061 | + ... print bugtask.milestone.name |
1062 | + ... else: |
1063 | + ... print bugtask.target.name |
1064 | + rabbit |
1065 | + 0.2 |
1066 | + >>> for spec in view.specifications: |
1067 | + ... if spec.milestone is not None: |
1068 | + ... print spec.milestone.name |
1069 | + ... else: |
1070 | + ... print spec.goal.name |
1071 | + rabbit |
1072 | + 0.1 |
1073 | + |
1074 | + >>> view.has_linked_branch |
1075 | + True |
1076 | + |
1077 | + # Listing and deleting product release files is done in |
1078 | + # product-release-views because they require the Librarian to be running. |
1079 | |
1080 | Series that are the active focus of development cannot be deleted. The |
1081 | view's can_delete property checks this rule. |
1082 | @@ -291,7 +313,8 @@ |
1083 | Calling the view's delete action on a series that can be deleted will |
1084 | untarget the bugtasks and specifications that are targeted to the |
1085 | series' milestones. The milestones, releases, and release files are |
1086 | -deleted. |
1087 | +deleted. Bugs and blueprints targeted to the series are unassigned. |
1088 | +Series structural subscriptions are removed. Branch links are removed. |
1089 | |
1090 | >>> view = create_initialized_view(productseries, '+delete', form=form) |
1091 | >>> for notification in view.request.response.notifications: |
1092 | @@ -308,11 +331,17 @@ |
1093 | None |
1094 | >>> print bugtask.milestone |
1095 | None |
1096 | + >>> bugtask.related_tasks |
1097 | + [] |
1098 | + >>> print series_specification.milestone |
1099 | + None |
1100 | + >>> [subscription for subscription in owner.structural_subscriptions] |
1101 | + [] |
1102 | |
1103 | The series was not actually deleted because there are problematic objects |
1104 | like translations. The series are assigned to the Obsolete Junk project. |
1105 | The series name is changed to 'product_name-series_name-date_created' to |
1106 | -avoid conflicts. |
1107 | +avoid conflicts. The linked branch is removed. |
1108 | |
1109 | >>> from zope.component import getUtility |
1110 | >>> from canonical.launchpad.interfaces.launchpad import ( |
1111 | |
1112 | === modified file 'lib/lp/registry/doc/milestone.txt' |
1113 | --- lib/lp/registry/doc/milestone.txt 2009-08-13 19:03:36 +0000 |
1114 | +++ lib/lp/registry/doc/milestone.txt 2009-11-03 18:13:40 +0000 |
1115 | @@ -496,3 +496,13 @@ |
1116 | ... |
1117 | AssertionError: You cannot delete a milestone which has specifications |
1118 | targeted to it. |
1119 | + |
1120 | +If a milestone has a structural subscription, it cannot be deleted. |
1121 | + |
1122 | + >>> milestone = ff_onedotzero.newMilestone('1.0.14') |
1123 | + >>> subscription = milestone.addSubscription(owner, owner) |
1124 | + >>> milestone.destroySelf() |
1125 | + Traceback (most recent call last): |
1126 | + ... |
1127 | + AssertionError: You cannot delete a milestone which has structural |
1128 | + subscriptions. |
1129 | |
1130 | === modified file 'lib/lp/registry/model/distribution.py' |
1131 | --- lib/lp/registry/model/distribution.py 2009-10-23 16:20:14 +0000 |
1132 | +++ lib/lp/registry/model/distribution.py 2009-11-03 18:13:43 +0000 |
1133 | @@ -851,6 +851,7 @@ |
1134 | SourcePackagePublishingHistory.dateremoved is NULL |
1135 | """ % sqlvalues(self, archive), |
1136 | distinct=True, |
1137 | + orderBy="name", |
1138 | clauseTables=['SourcePackagePublishingHistory', 'DistroSeries', |
1139 | 'SourcePackageRelease'])) |
1140 | |
1141 | |
1142 | === modified file 'lib/lp/registry/model/milestone.py' |
1143 | --- lib/lp/registry/model/milestone.py 2009-10-22 09:43:29 +0000 |
1144 | +++ lib/lp/registry/model/milestone.py 2009-11-03 18:13:43 +0000 |
1145 | @@ -188,6 +188,9 @@ |
1146 | """See `IMilestone`.""" |
1147 | params = BugTaskSearchParams(milestone=self, user=None) |
1148 | bugtasks = getUtility(IBugTaskSet).search(params) |
1149 | + assert len(self.getSubscriptions()) == 0, ( |
1150 | + "You cannot delete a milestone which has structural " |
1151 | + "subscriptions.") |
1152 | assert bugtasks.count() == 0, ( |
1153 | "You cannot delete a milestone which has bugtasks targeted " |
1154 | "to it.") |
1155 | @@ -238,6 +241,7 @@ |
1156 | """See lp.registry.interfaces.milestone.IMilestoneSet.""" |
1157 | return Milestone.selectBy(active=True, orderBy='id') |
1158 | |
1159 | + |
1160 | class ProjectMilestone(HasBugsBase): |
1161 | """A virtual milestone implementation for project. |
1162 | |
1163 | |
1164 | === modified file 'lib/lp/registry/templates/product-index.pt' |
1165 | --- lib/lp/registry/templates/product-index.pt 2009-10-08 15:54:09 +0000 |
1166 | +++ lib/lp/registry/templates/product-index.pt 2009-11-03 18:13:40 +0000 |
1167 | @@ -219,7 +219,7 @@ |
1168 | </tal:release> |
1169 | |
1170 | <p class="alternate"> |
1171 | - <a class="sprint info" |
1172 | + <a class="sprite info" |
1173 | tal:define="link overview_menu/downloads" |
1174 | tal:condition="release" |
1175 | tal:attributes="href link/fmt:url; |
1176 | |
1177 | === modified file 'lib/lp/registry/templates/productseries-delete.pt' |
1178 | --- lib/lp/registry/templates/productseries-delete.pt 2009-08-11 21:31:51 +0000 |
1179 | +++ lib/lp/registry/templates/productseries-delete.pt 2009-11-03 18:13:43 +0000 |
1180 | @@ -30,10 +30,12 @@ |
1181 | </tal:no-files> |
1182 | </p> |
1183 | |
1184 | - <ul id="milestones" tal:condition="view/milestones"> |
1185 | + <ul id="milestones" class="subordinate" |
1186 | + tal:condition="view/milestones"> |
1187 | <li tal:repeat="milestone view/milestones"> |
1188 | <strong> |
1189 | - <a tal:attributes="href milestone/fmt:url"><tal:name |
1190 | + <a class="sprite milestone" |
1191 | + tal:attributes="href milestone/fmt:url"><tal:name |
1192 | content="milestone/name">0.9</tal:name><tal:codename |
1193 | condition="milestone/code_name"> |
1194 | "<tal:name |
1195 | @@ -42,9 +44,8 @@ |
1196 | </li> |
1197 | </ul> |
1198 | |
1199 | - |
1200 | - |
1201 | - <ul id="files" tal:condition="view/product_release_files"> |
1202 | + <ul id="files" class="subordinate" |
1203 | + tal:condition="view/product_release_files"> |
1204 | <li tal:repeat="file view/product_release_files"> |
1205 | <strong tal:content="file/libraryfile/filename">foo.tgz</strong> |
1206 | </li> |
1207 | @@ -54,7 +55,7 @@ |
1208 | The following bugs and blueprints will be <em>untargeted</em>: |
1209 | </p> |
1210 | |
1211 | - <ul id="bugtasks-and-blueprints" |
1212 | + <ul id="bugtasks-and-blueprints" class="subordinate" |
1213 | tal:condition="view/has_bugtasks_and_specifications"> |
1214 | <li tal:repeat="bugtask view/bugtasks" |
1215 | tal:content="structure bugtask/bug/fmt:link">bug 1 |
1216 | @@ -64,6 +65,11 @@ |
1217 | </li> |
1218 | </ul> |
1219 | |
1220 | + <p tal:condition="view/has_linked_branch"> |
1221 | + The associated branch will be <em>unlinked</em>: |
1222 | + <a tal:replace="structure view/context/branch/fmt:link" /> |
1223 | + </p> |
1224 | + |
1225 | <p> |
1226 | Series deletion is permanent. |
1227 | </p> |
1228 | |
1229 | === modified file 'lib/lp/soyuz/adapters/archivedependencies.py' |
1230 | --- lib/lp/soyuz/adapters/archivedependencies.py 2009-08-16 12:38:12 +0000 |
1231 | +++ lib/lp/soyuz/adapters/archivedependencies.py 2009-11-03 18:13:42 +0000 |
1232 | @@ -36,7 +36,6 @@ |
1233 | 'pocket_dependencies', |
1234 | ] |
1235 | |
1236 | -from canonical.config import config |
1237 | from lp.registry.interfaces.pocket import ( |
1238 | PackagePublishingPocket, pocketsuffix) |
1239 | from lp.soyuz.interfaces.archive import ArchivePurpose, ALLOW_RELEASE_BUILDS |
1240 | @@ -161,11 +160,9 @@ |
1241 | |
1242 | # Append external sources_list lines for this archive if it's |
1243 | # specified in the configuration. |
1244 | - archive_config_key = 'ppa.%s_%s' % ( |
1245 | - build.archive.owner.name, build.archive.name) |
1246 | - if archive_config_key in config: |
1247 | - archive_config = config[archive_config_key] |
1248 | - for archive_dep in archive_config.dependencies.splitlines(): |
1249 | + dependencies = build.archive.external_dependencies |
1250 | + if dependencies is not None: |
1251 | + for archive_dep in dependencies.splitlines(): |
1252 | line = archive_dep % ( |
1253 | {'series': build.distroarchseries.distroseries.name}) |
1254 | sources_list_lines.append(line) |
1255 | |
1256 | === modified file 'lib/lp/soyuz/browser/archive.py' |
1257 | --- lib/lp/soyuz/browser/archive.py 2009-10-30 12:29:32 +0000 |
1258 | +++ lib/lp/soyuz/browser/archive.py 2009-11-03 18:13:43 +0000 |
1259 | @@ -28,6 +28,7 @@ |
1260 | |
1261 | from datetime import datetime, timedelta |
1262 | import pytz |
1263 | +from urlparse import urlparse |
1264 | |
1265 | from zope.app.form.browser import TextAreaWidget |
1266 | from zope.component import getUtility |
1267 | @@ -1775,7 +1776,10 @@ |
1268 | class ArchiveAdminView(BaseArchiveEditView): |
1269 | |
1270 | field_names = ['enabled', 'private', 'require_virtualized', |
1271 | - 'buildd_secret', 'authorized_size', 'relative_build_score'] |
1272 | + 'buildd_secret', 'authorized_size', 'relative_build_score', |
1273 | + 'external_dependencies'] |
1274 | + |
1275 | + custom_widget('external_dependencies', TextAreaWidget, height=3) |
1276 | |
1277 | def validate_save(self, action, data): |
1278 | """Validate the save action on ArchiveAdminView. |
1279 | @@ -1794,12 +1798,44 @@ |
1280 | self.setFieldError( |
1281 | 'private', |
1282 | 'Private teams may not have public archives.') |
1283 | - |
1284 | elif data.get('buildd_secret') is not None and not data['private']: |
1285 | self.setFieldError( |
1286 | 'buildd_secret', |
1287 | 'Do not specify for non-private archives') |
1288 | |
1289 | + # Check the external_dependencies field. |
1290 | + ext_deps = data.get('external_dependencies') |
1291 | + if ext_deps is not None: |
1292 | + errors = self.validate_external_dependencies(ext_deps) |
1293 | + if len(errors) != 0: |
1294 | + error_text = "\n".join(errors) |
1295 | + self.setFieldError('external_dependencies', error_text) |
1296 | + |
1297 | + def validate_external_dependencies(self, ext_deps): |
1298 | + """Validate the external_dependencies field. |
1299 | + |
1300 | + :param ext_deps: The dependencies form field to check. |
1301 | + """ |
1302 | + errors = [] |
1303 | + # The field can consist of multiple entries separated by |
1304 | + # newlines, so process each in turn. |
1305 | + for dep in ext_deps.splitlines(): |
1306 | + try: |
1307 | + deb, url, suite, components = dep.split(" ", 3) |
1308 | + except ValueError: |
1309 | + errors.append( |
1310 | + "'%s' is not a complete and valid sources.list entry" |
1311 | + % dep) |
1312 | + continue |
1313 | + |
1314 | + if deb != "deb": |
1315 | + errors.append("%s: Must start with 'deb'" % dep) |
1316 | + url_components = urlparse(url) |
1317 | + if not url_components[0] or not url_components[1]: |
1318 | + errors.append("%s: Invalid URL" % dep) |
1319 | + |
1320 | + return errors |
1321 | + |
1322 | @property |
1323 | def owner_is_private_team(self): |
1324 | """Is the owner a private team? |
1325 | |
1326 | === modified file 'lib/lp/soyuz/browser/configure.zcml' |
1327 | --- lib/lp/soyuz/browser/configure.zcml 2009-10-22 10:33:00 +0000 |
1328 | +++ lib/lp/soyuz/browser/configure.zcml 2009-11-03 18:13:42 +0000 |
1329 | @@ -783,7 +783,7 @@ |
1330 | /> |
1331 | <browser:url |
1332 | for="lp.soyuz.interfaces.packageset.IPackageset" |
1333 | - path_expression="name" |
1334 | + path_expression="string:${distroseries/name}/${name}" |
1335 | parent_utility="lp.soyuz.interfaces.packageset.IPackagesetSet" |
1336 | /> |
1337 | <browser:url |
1338 | |
1339 | === modified file 'lib/lp/soyuz/browser/packageset.py' |
1340 | --- lib/lp/soyuz/browser/packageset.py 2009-06-30 16:56:07 +0000 |
1341 | +++ lib/lp/soyuz/browser/packageset.py 2009-11-03 18:13:42 +0000 |
1342 | @@ -17,3 +17,22 @@ |
1343 | class PackagesetSetNavigation(GetitemNavigation): |
1344 | """Navigation methods for PackagesetSet.""" |
1345 | usedfor = IPackagesetSet |
1346 | + |
1347 | + def traverse(self, distroseries): |
1348 | + """Traverse package sets in distro series context. |
1349 | + |
1350 | + The URI fragment of interest is: |
1351 | + |
1352 | + /package-sets/lucid/mozilla |
1353 | + |
1354 | + where 'lucid' is the distro series and 'mozilla' is the package set |
1355 | + *name* respectively. |
1356 | + """ |
1357 | + if self.request.stepstogo: |
1358 | + # The package set name follows after the distro series. |
1359 | + ps_name = self.request.stepstogo.consume() |
1360 | + return self.context.getByName(ps_name, distroseries=distroseries) |
1361 | + |
1362 | + # Otherwise return None (to trigger a NotFound error). |
1363 | + return None |
1364 | + |
1365 | |
1366 | === modified file 'lib/lp/soyuz/browser/tests/archive-views.txt' |
1367 | --- lib/lp/soyuz/browser/tests/archive-views.txt 2009-10-30 21:42:10 +0000 |
1368 | +++ lib/lp/soyuz/browser/tests/archive-views.txt 2009-11-03 18:13:41 +0000 |
1369 | @@ -1345,3 +1345,50 @@ |
1370 | |
1371 | >>> print copy.status.name |
1372 | ACCEPTED |
1373 | + |
1374 | + |
1375 | +== External dependencies validation == |
1376 | + |
1377 | +The ArchiveAdminView checks the external_dependencies form data to see if |
1378 | +it's a valid sources.list entry. |
1379 | + |
1380 | + >>> ppa_archive_view = create_initialized_view( |
1381 | + ... cprov.archive, name="+admin") |
1382 | + |
1383 | +The validate_external_dependencies() method is called when validating and will |
1384 | +return a list of errors if the data dis not validate. A valid entry is of the |
1385 | +form: |
1386 | + deb scheme://domain/ suite component[s] |
1387 | + |
1388 | + >>> print ppa_archive_view.validate_external_dependencies( |
1389 | + ... "deb http://example.com/ karmic main") |
1390 | + [] |
1391 | + |
1392 | +Multiple entries are valid, separated by newlines: |
1393 | + |
1394 | + >>> print ppa_archive_view.validate_external_dependencies( |
1395 | + ... "deb http://example.com/ karmic main\n" |
1396 | + ... "deb http://example.com/ karmic restricted") |
1397 | + [] |
1398 | + |
1399 | +If the line does not start with the word "deb" it fails: |
1400 | + |
1401 | + >>> print ppa_archive_view.validate_external_dependencies( |
1402 | + ... "deb http://example.com/ karmic universe\n" |
1403 | + ... "dab http://example.com/ karmic main") |
1404 | + ["dab http://example.com/ karmic main: Must start with 'deb'"] |
1405 | + |
1406 | +If the line has too few parts it fails. Here we're missing a suite: |
1407 | + |
1408 | + >>> print ppa_archive_view.validate_external_dependencies( |
1409 | + ... "deb http://example.com/ karmic universe\n" |
1410 | + ... "deb http://example.com/ main") |
1411 | + ["'deb http://example.com/ main' |
1412 | + is not a complete and valid sources.list entry"] |
1413 | + |
1414 | +If the URL looks invalid, it fails: |
1415 | + |
1416 | + >>> print ppa_archive_view.validate_external_dependencies( |
1417 | + ... "deb http://example.com/ karmic universe\n" |
1418 | + ... "deb example.com/ karmic main") |
1419 | + ['deb example.com/ karmic main: Invalid URL'] |
1420 | |
1421 | === modified file 'lib/lp/soyuz/configure.zcml' |
1422 | --- lib/lp/soyuz/configure.zcml 2009-10-28 14:42:40 +0000 |
1423 | +++ lib/lp/soyuz/configure.zcml 2009-11-03 18:13:42 +0000 |
1424 | @@ -399,8 +399,8 @@ |
1425 | <require |
1426 | permission="launchpad.Commercial" |
1427 | set_attributes="authorized_size buildd_secret |
1428 | - enabled private require_virtualized |
1429 | - relative_build_score"/> |
1430 | + enabled external_dependencies private |
1431 | + require_virtualized relative_build_score "/> |
1432 | <require |
1433 | permission="launchpad.Admin" |
1434 | set_attributes="distribution name signing_key"/> |
1435 | @@ -856,4 +856,12 @@ |
1436 | new"/> |
1437 | </securedutility> |
1438 | |
1439 | + <!-- PackagesetGroup --> |
1440 | + <class |
1441 | + class="lp.soyuz.model.packagesetgroup.PackagesetGroup"> |
1442 | + <allow |
1443 | + interface="lp.soyuz.interfaces.packagesetgroup.IPackagesetGroup"/> |
1444 | + </class> |
1445 | + |
1446 | + |
1447 | </configure> |
1448 | |
1449 | === modified file 'lib/lp/soyuz/doc/archive-dependencies.txt' |
1450 | --- lib/lp/soyuz/doc/archive-dependencies.txt 2009-08-28 07:34:44 +0000 |
1451 | +++ lib/lp/soyuz/doc/archive-dependencies.txt 2009-11-03 18:13:40 +0000 |
1452 | @@ -441,38 +441,27 @@ |
1453 | |
1454 | == External build dependencies == |
1455 | |
1456 | -Via a static configuration change, any PPA hosted in launchpad can be |
1457 | +Via an administrator change, any PPA hosted in launchpad can be |
1458 | assigned to one or more 'external' build dependencies additionally to |
1459 | the internal ones. |
1460 | |
1461 | -There is a configuration category called 'ppa' which can be extended |
1462 | -for any hosted PPA. They are named as following: |
1463 | - |
1464 | - [ppa.<owner_name>_<ppa_name>] |
1465 | - |
1466 | -The 'ppa' categories contain the 'dependencies' field, which is a |
1467 | -multi-line string listing the external dependencies in the debian |
1468 | -sources_list format. |
1469 | - |
1470 | - deb [user:pass@]<host>[/path] %(series)s[-pocket] [components] |
1471 | +There is a column on IArchive called 'external_dependencies' which can be set |
1472 | +for any hosted PPA. It is a string listing the comma-separated external |
1473 | +dependencies in the debian sources_list format. |
1474 | + |
1475 | + deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] [components] |
1476 | |
1477 | The '%(series)s' part is optional and will be replaced on-the-fly with |
1478 | the series name for the build record being dispatched. |
1479 | |
1480 | -We will create a configuration for Celso's PPA. |
1481 | - |
1482 | - >>> from canonical.config import config |
1483 | - >>> cprov_deps = """ |
1484 | - ... [ppa.cprov_ppa] |
1485 | - ... dependencies: |
1486 | - ... deb http://user:pass@repository zoing everything |
1487 | - ... deb http://user:pass@repository %(series)s public private |
1488 | - ... deb http://user:pass@repository %(series)s-extra public |
1489 | - ... """ |
1490 | - >>> config.push('test_deps', cprov_deps) |
1491 | - |
1492 | -Configuration in place, now builds in Celso's PPA will use the |
1493 | -external dependencies. |
1494 | +We will create some dependencies for Celso's PPA. |
1495 | + |
1496 | + >>> cprov.archive.external_dependencies = ( |
1497 | + ... "deb http://user:pass@repository zoing everything\n" |
1498 | + ... "deb http://user:pass@repository %(series)s public private\n" |
1499 | + ... "deb http://user:pass@repository %(series)s-extra public") |
1500 | + |
1501 | +Now builds in Celso's PPA will use the external dependencies. |
1502 | |
1503 | >>> print_building_sources_list(a_build) |
1504 | deb http://ftpmaster.internal/ubuntu hoary |
1505 | @@ -486,6 +475,3 @@ |
1506 | deb http://user:pass@repository hoary-extra public |
1507 | deb http://user:pass@repository zoing everything |
1508 | |
1509 | -Tests done, we can remove the extra configuration content we've added. |
1510 | - |
1511 | - >>> unused = config.pop('test_deps') |
1512 | |
1513 | === modified file 'lib/lp/soyuz/doc/archive.txt' |
1514 | --- lib/lp/soyuz/doc/archive.txt 2009-10-13 16:12:10 +0000 |
1515 | +++ lib/lp/soyuz/doc/archive.txt 2009-11-03 18:13:41 +0000 |
1516 | @@ -58,11 +58,11 @@ |
1517 | >>> cprov_archive.failed_count |
1518 | 1 |
1519 | |
1520 | -relative_build_score is a property that can be set only by LP admins and read |
1521 | -by anyone. It is a signed integer that represents a delta to all the build |
1522 | -scores for builds done in the archive. |
1523 | +relative_build_score and external_dependencies are properties that can be set |
1524 | +only by LP admins and read by anyone. |
1525 | |
1526 | -The default value is zero: |
1527 | +relative_build_score is a signed integer that represents a delta to all the |
1528 | +build scores for builds done in the archive. The default value is zero: |
1529 | |
1530 | >>> cprov_archive.relative_build_score |
1531 | 0 |
1532 | @@ -74,10 +74,28 @@ |
1533 | ... |
1534 | Unauthorized: (..., 'relative_build_score', 'launchpad.Commercial') |
1535 | |
1536 | -As a Launchpad admin, it will work. |
1537 | +external_dependencies is a text field that should contain a comma-separated |
1538 | +list of sources.list entries in the format: |
1539 | +deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] [components] |
1540 | +where the series variable is replaced with the series name of the context |
1541 | +build. This allows an admin to set external repositories as a source for |
1542 | +build dependencies on the context PPA. Its default value is None: |
1543 | + |
1544 | + >>> print cprov_archive.external_dependencies |
1545 | + None |
1546 | + |
1547 | +Amending it as an unprivileged user results in failure: |
1548 | + |
1549 | + >>> cprov_archive.external_dependencies = "test" |
1550 | + Traceback (most recent call last): |
1551 | + ... |
1552 | + Unauthorized: (..., 'external_dependencies', 'launchpad.Commercial') |
1553 | + |
1554 | +As a Launchpad admin, setting these properties will work. |
1555 | |
1556 | >>> login("admin@canonical.com") |
1557 | >>> cprov_archive.relative_build_score = 100 |
1558 | + >>> cprov_archive.external_dependencies = "test" |
1559 | |
1560 | The buildd_secret is used by the slave scanner when generating a |
1561 | sources.list entry for the builder to access a private archive. It is |
1562 | |
1563 | === modified file 'lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt' |
1564 | --- lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt 2009-09-04 12:17:11 +0000 |
1565 | +++ lib/lp/soyuz/doc/distroarchseriesbinarypackage.txt 2009-11-03 18:13:40 +0000 |
1566 | @@ -209,6 +209,7 @@ |
1567 | |
1568 | >>> ubuntu.updateCompleteSourcePackageCache( |
1569 | ... archive=cprov.archive, ztm=LaunchpadZopelessLayer.txn, log=TestLog()) |
1570 | + DEBUG: ... |
1571 | DEBUG: Considering source 'pmount' |
1572 | ... |
1573 | |
1574 | |
1575 | === modified file 'lib/lp/soyuz/doc/package-cache.txt' |
1576 | --- lib/lp/soyuz/doc/package-cache.txt 2009-08-13 13:09:34 +0000 |
1577 | +++ lib/lp/soyuz/doc/package-cache.txt 2009-11-03 18:13:39 +0000 |
1578 | @@ -163,11 +163,12 @@ |
1579 | |
1580 | >>> updates = ubuntu.updateCompleteSourcePackageCache( |
1581 | ... archive=ubuntu.main_archive, ztm=transaction, log=TestLog()) |
1582 | - DEBUG: Considering source 'mozilla-firefox' |
1583 | - ... |
1584 | + DEBUG: ... |
1585 | DEBUG: Considering source 'cdrkit' |
1586 | DEBUG: Creating new source cache entry. |
1587 | ... |
1588 | + DEBUG: Considering source 'mozilla-firefox' |
1589 | + ... |
1590 | |
1591 | >>> print updates |
1592 | 10 |
1593 | @@ -355,6 +356,7 @@ |
1594 | |
1595 | >>> source_updates = ubuntu.updateCompleteSourcePackageCache( |
1596 | ... archive=cprov.archive, ztm=transaction, log=TestLog()) |
1597 | + DEBUG: ... |
1598 | DEBUG: Considering source 'pmount' |
1599 | ... |
1600 | |
1601 | |
1602 | === modified file 'lib/lp/soyuz/interfaces/archive.py' |
1603 | --- lib/lp/soyuz/interfaces/archive.py 2009-10-26 09:43:56 +0000 |
1604 | +++ lib/lp/soyuz/interfaces/archive.py 2009-11-03 18:13:41 +0000 |
1605 | @@ -256,6 +256,18 @@ |
1606 | description=_( |
1607 | "A delta to apply to all build scores for this archive.")) |
1608 | |
1609 | + external_dependencies = Text( |
1610 | + title=_("External dependencies"), required=False, readonly=False, |
1611 | + description=_( |
1612 | + "Newline-separated list of repositories to be used to retrieve " |
1613 | + "any external build dependencies when building packages in this " |
1614 | + "archive, in the format:\n" |
1615 | + "deb http[s]://[user:pass@]<host>[/path] %(series)s[-pocket] " |
1616 | + "[components]\n" |
1617 | + "The series variable is replaced with the series name of the " |
1618 | + "context build.\n" |
1619 | + "NOTE: This is for migration of OEM PPAs only!")) |
1620 | + |
1621 | def getSourcesForDeletion(name=None, status=None, distroseries=None): |
1622 | """All `ISourcePackagePublishingHistory` available for deletion. |
1623 | |
1624 | @@ -433,8 +445,10 @@ |
1625 | |
1626 | @operation_parameters( |
1627 | person=Reference(schema=IPerson), |
1628 | - packageset=TextLine( |
1629 | - title=_("Package set"), required=True), |
1630 | + # Really IPackageset, corrected in _schema_circular_imports to avoid |
1631 | + # circular import. |
1632 | + packageset=Reference( |
1633 | + Interface, title=_("Package set"), required=True), |
1634 | explicit=Bool( |
1635 | title=_("Explicit"), required=False)) |
1636 | # Really IArchivePermission, set in _schema_circular_imports to avoid |
1637 | @@ -444,7 +458,7 @@ |
1638 | """Add a package set based permission for a person. |
1639 | |
1640 | :param person: An `IPerson` for whom you want to add permission. |
1641 | - :param packageset: An `IPackageset` or a string package set name. |
1642 | + :param packageset: An `IPackageset`. |
1643 | :param explicit: True if the package set in question requires |
1644 | specialist skills for proper handling. |
1645 | |
1646 | @@ -453,8 +467,10 @@ |
1647 | """ |
1648 | |
1649 | @operation_parameters( |
1650 | - packageset=TextLine( |
1651 | - title=_("Package set"), required=True), |
1652 | + # Really IPackageset, corrected in _schema_circular_imports to avoid |
1653 | + # circular import. |
1654 | + packageset=Reference( |
1655 | + Interface, title=_("Package set"), required=True), |
1656 | direct_permissions=Bool( |
1657 | title=_("Ignore package set hierarchy"), required=False)) |
1658 | # Really IArchivePermission, set in _schema_circular_imports to avoid |
1659 | @@ -464,7 +480,7 @@ |
1660 | def getUploadersForPackageset(packageset, direct_permissions=True): |
1661 | """The `ArchivePermission` records for uploaders to the package set. |
1662 | |
1663 | - :param packageset: An `IPackageset` or a string package set name. |
1664 | + :param packageset: An `IPackageset`. |
1665 | :param direct_permissions: If True, only consider permissions granted |
1666 | directly for the package set at hand. Otherwise, include any |
1667 | uploaders for package sets that include this one. |
1668 | @@ -475,8 +491,10 @@ |
1669 | |
1670 | @operation_parameters( |
1671 | person=Reference(schema=IPerson), |
1672 | - packageset=TextLine( |
1673 | - title=_("Package set"), required=True), |
1674 | + # Really IPackageset, corrected in _schema_circular_imports to avoid |
1675 | + # circular import. |
1676 | + packageset=Reference( |
1677 | + Interface, title=_("Package set"), required=True), |
1678 | explicit=Bool( |
1679 | title=_("Explicit"), required=False)) |
1680 | @export_write_operation() |
1681 | @@ -484,7 +502,7 @@ |
1682 | """Revoke upload permissions for a person. |
1683 | |
1684 | :param person: An `IPerson` for whom you want to revoke permission. |
1685 | - :param packageset: An `IPackageset` or a string package set name. |
1686 | + :param packageset: An `IPackageset`. |
1687 | :param explicit: The value of the 'explicit' flag for the permission |
1688 | to be revoked. |
1689 | """ |
1690 | @@ -567,9 +585,13 @@ |
1691 | @operation_parameters( |
1692 | sourcepackagename=TextLine( |
1693 | title=_("Source package name"), required=True), |
1694 | - person=Reference(schema=IPerson)) |
1695 | + person=Reference(schema=IPerson), |
1696 | + distroseries=Reference( |
1697 | + # Really IDistroSeries, avoiding a circular import here. |
1698 | + Interface, |
1699 | + title=_("The distro series"), required=False)) |
1700 | @export_read_operation() |
1701 | - def isSourceUploadAllowed(sourcepackagename, person): |
1702 | + def isSourceUploadAllowed(sourcepackagename, person, distroseries=None): |
1703 | """True if the person is allowed to upload the given source package. |
1704 | |
1705 | Return True if there exists a permission that combines |
1706 | @@ -585,6 +607,9 @@ |
1707 | either a string or a `ISourcePackageName`. |
1708 | :param person: An `IPerson` for whom you want to find out which |
1709 | package sets he has access to. |
1710 | + :param distroseries: The `IDistroSeries` for which to check |
1711 | + permissions. If none is supplied then `currentseries` in |
1712 | + Ubuntu is assumed. |
1713 | |
1714 | :raises NoSuchSourcePackageName: if a source package with the |
1715 | given name could not be found. |
1716 | |
1717 | === modified file 'lib/lp/soyuz/interfaces/archivepermission.py' |
1718 | --- lib/lp/soyuz/interfaces/archivepermission.py 2009-08-03 17:10:12 +0000 |
1719 | +++ lib/lp/soyuz/interfaces/archivepermission.py 2009-11-03 18:13:41 +0000 |
1720 | @@ -258,7 +258,8 @@ |
1721 | archive in question. |
1722 | """ |
1723 | |
1724 | - def isSourceUploadAllowed(archive, sourcepackagename, person): |
1725 | + def isSourceUploadAllowed( |
1726 | + archive, sourcepackagename, person, distroseries=None): |
1727 | """True if the person is allowed to upload the given source package. |
1728 | |
1729 | Return True if there exists a permission that combines |
1730 | @@ -275,6 +276,9 @@ |
1731 | either a string or a `ISourcePackageName`. |
1732 | :param person: An `IPerson` for whom you want to find out which |
1733 | package sets he has access to. |
1734 | + :param distroseries: The `IDistroSeries` for which to check |
1735 | + permissions. If none is supplied then `currentseries` in |
1736 | + Ubuntu is assumed. |
1737 | |
1738 | :raises SourceNotFound: if a source package with the given |
1739 | name could not be found. |
1740 | @@ -284,6 +288,9 @@ |
1741 | def uploadersForPackageset(archive, packageset, direct_permissions=True): |
1742 | """The `ArchivePermission` records for uploaders to the package set. |
1743 | |
1744 | + Please note: if a package set *name* is passed the respective |
1745 | + package set in the current distro series will be used. |
1746 | + |
1747 | :param archive: The archive the permission applies to. |
1748 | :param packageset: An `IPackageset` or a string package set name. |
1749 | :param direct_permissions: If True only consider permissions granted |
1750 | @@ -332,6 +339,9 @@ |
1751 | def newPackagesetUploader(archive, person, packageset, explicit=False): |
1752 | """Create and return a new `ArchivePermission` for an uploader. |
1753 | |
1754 | + Please note: if a package set *name* is passed the respective |
1755 | + package set in the current distro series will be used. |
1756 | + |
1757 | :param archive: The archive the permission applies to. |
1758 | :param person: An `IPerson` for whom you want to add permission. |
1759 | :param packageset: An `IPackageset` or a string package set name. |
1760 | @@ -379,6 +389,9 @@ |
1761 | def deletePackagesetUploader(archive, person, packageset, explicit=False): |
1762 | """Revoke upload permissions for a person. |
1763 | |
1764 | + Please note: if a package set *name* is passed the respective |
1765 | + package set in the current distro series will be used. |
1766 | + |
1767 | :param archive: The archive the permission applies to. |
1768 | :param person: An `IPerson` for whom you want to revoke permission. |
1769 | :param packageset: An `IPackageset` or a string package set name. |
1770 | |
1771 | === modified file 'lib/lp/soyuz/interfaces/packageset.py' |
1772 | --- lib/lp/soyuz/interfaces/packageset.py 2009-07-25 16:33:39 +0000 |
1773 | +++ lib/lp/soyuz/interfaces/packageset.py 2009-11-03 18:13:41 +0000 |
1774 | @@ -8,6 +8,7 @@ |
1775 | __metaclass__ = type |
1776 | |
1777 | __all__ = [ |
1778 | + 'DuplicatePackagesetName', |
1779 | 'IPackageset', |
1780 | 'IPackagesetSet', |
1781 | 'NoSuchPackageSet', |
1782 | @@ -26,14 +27,23 @@ |
1783 | operation_parameters, operation_returns_collection_of, |
1784 | operation_returns_entry, webservice_error) |
1785 | from lazr.restful.fields import Reference |
1786 | +from lp.registry.interfaces.distroseries import IDistroSeries |
1787 | from lp.registry.interfaces.person import IPerson |
1788 | from lp.registry.interfaces.role import IHasOwner |
1789 | +from lp.soyuz.interfaces.packagesetgroup import IPackagesetGroup |
1790 | |
1791 | |
1792 | class NoSuchPackageSet(NameLookupFailed): |
1793 | """Raised when we try to look up an PackageSet that doesn't exist.""" |
1794 | - webservice_error(400) #Bad request. |
1795 | - _message_prefix = "No such packageset" |
1796 | + # Bad request. |
1797 | + webservice_error(400) |
1798 | + _message_prefix = "No such package set (in the specified distro series)" |
1799 | + |
1800 | + |
1801 | +class DuplicatePackagesetName(Exception): |
1802 | + """Raised for packagesets with the same name and distroseries.""" |
1803 | + # Bad request. |
1804 | + webservice_error(400) |
1805 | |
1806 | |
1807 | class IPackagesetViewOnly(IHasOwner): |
1808 | @@ -48,7 +58,7 @@ |
1809 | |
1810 | owner = exported(Reference( |
1811 | IPerson, title=_("Person"), required=True, readonly=True, |
1812 | - description=_("The person who owns the package set at hand."))) |
1813 | + description=_("The person who owns this package set."))) |
1814 | |
1815 | name = exported(TextLine( |
1816 | title=_('Valid package set name'), |
1817 | @@ -58,6 +68,18 @@ |
1818 | title=_("Description"), required=True, readonly=True, |
1819 | description=_("The description for the package set at hand."))) |
1820 | |
1821 | + distroseries = exported(Reference( |
1822 | + IDistroSeries, title=_("Distribution series"), required=True, |
1823 | + readonly=True, |
1824 | + description=_( |
1825 | + "The distroseries to which this package set is related."))) |
1826 | + |
1827 | + packagesetgroup = Reference( |
1828 | + IPackagesetGroup, title=_('Package set group'), required=True, |
1829 | + readonly=True, |
1830 | + description=_( |
1831 | + 'Used internally to link package sets across distro series.')) |
1832 | + |
1833 | def sourcesIncluded(direct_inclusion=False): |
1834 | """Get all source names associated with this package set. |
1835 | |
1836 | @@ -195,6 +217,16 @@ |
1837 | names. |
1838 | """ |
1839 | |
1840 | + @operation_returns_collection_of(Interface) |
1841 | + @export_read_operation() |
1842 | + def relatedSets(): |
1843 | + """Get all package sets related to this one. |
1844 | + |
1845 | + Return all package sets that are related to this one. |
1846 | + |
1847 | + :return: A (potentially empty) sequence of `IPackageset` instances. |
1848 | + """ |
1849 | + |
1850 | |
1851 | class IPackagesetEdit(Interface): |
1852 | """A writeable interface for package sets.""" |
1853 | @@ -316,15 +348,31 @@ |
1854 | title=_('Package set description'), required=True), |
1855 | owner=Reference( |
1856 | IPerson, title=_("Person"), required=True, readonly=True, |
1857 | - description=_("The person who owns the package set at hand."))) |
1858 | + description=_("The person who owns this package set.")), |
1859 | + distroseries=Reference( |
1860 | + IDistroSeries, title=_("Distroseries"), required=False, |
1861 | + readonly=True, description=_( |
1862 | + "The distribution series to which the packageset " |
1863 | + "is related.")), |
1864 | + related_set=Reference( |
1865 | + IPackageset, title=_("Related package set"), required=False, |
1866 | + readonly=True, description=_( |
1867 | + "The new package set will share the package set group " |
1868 | + "with this one."))) |
1869 | @export_factory_operation(IPackageset, []) |
1870 | - def new(name, description, owner): |
1871 | + def new(name, description, owner, distroseries=None, related_set=None): |
1872 | """Create a new package set. |
1873 | |
1874 | :param name: the name of the package set to be created. |
1875 | :param description: the description for the package set to be created. |
1876 | :param owner: the owner of the package set to be created. |
1877 | + :param distroseries: the distroseries to which the new packageset |
1878 | + is related. Defaults to the current Ubuntu series. |
1879 | + :param related_set: the newly created package set is to be related to |
1880 | + `related_set` (by being placed in the same package group). |
1881 | |
1882 | + :raises DuplicatePackagesetName: if a package set with the same `name` |
1883 | + exists in `distroseries` already. |
1884 | :return: a newly created `IPackageset`. |
1885 | """ |
1886 | |
1887 | @@ -332,10 +380,12 @@ |
1888 | name=TextLine(title=_('Package set name'), required=True)) |
1889 | @operation_returns_entry(IPackageset) |
1890 | @export_read_operation() |
1891 | - def getByName(name): |
1892 | + def getByName(name, distroseries=None): |
1893 | """Return the single package set with the given name (if any). |
1894 | |
1895 | :param name: the name of the package set sought. |
1896 | + :param distroseries: the distroseries to which the new packageset |
1897 | + is related. Defaults to the current Ubuntu series. |
1898 | |
1899 | :return: An `IPackageset` instance or None. |
1900 | """ |
1901 | |
1902 | === added file 'lib/lp/soyuz/interfaces/packagesetgroup.py' |
1903 | --- lib/lp/soyuz/interfaces/packagesetgroup.py 1970-01-01 00:00:00 +0000 |
1904 | +++ lib/lp/soyuz/interfaces/packagesetgroup.py 2009-11-03 18:13:43 +0000 |
1905 | @@ -0,0 +1,41 @@ |
1906 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
1907 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
1908 | + |
1909 | +"""Packageset Group interface.""" |
1910 | + |
1911 | +__metaclass__ = type |
1912 | + |
1913 | +__all__ = [ |
1914 | + 'IPackagesetGroup', |
1915 | + ] |
1916 | + |
1917 | +from zope.schema import Datetime, Int |
1918 | + |
1919 | +from lazr.restful.fields import Reference |
1920 | + |
1921 | +from canonical.launchpad import _ |
1922 | +from lp.registry.interfaces.person import IPerson |
1923 | +from lp.registry.interfaces.role import IHasOwner |
1924 | + |
1925 | + |
1926 | +class IPackagesetGroup(IHasOwner): |
1927 | + """A group of related package sets across distroseries' |
1928 | + |
1929 | + This class is used internally to group related packagesets across |
1930 | + distroseries. For example, if in Karmic there is a 'gnome-games' |
1931 | + package set, and this package set is cloned initially for Lucid, |
1932 | + then both packagesets would refer to the same packageset-group. |
1933 | + |
1934 | + Packageset-groups are not exposed at all. The date_created and |
1935 | + owner fields are present for internal use only. |
1936 | + """ |
1937 | + id = Int(title=_('ID'), required=True, readonly=True) |
1938 | + |
1939 | + date_created = Datetime( |
1940 | + title=_("Date Created"), required=True, readonly=True, |
1941 | + description=_("The creation date/time for this packageset group.")) |
1942 | + |
1943 | + owner = Reference( |
1944 | + IPerson, title=_("Person"), required=True, readonly=True, |
1945 | + description=_("The person who created this packageset group.")) |
1946 | + |
1947 | |
1948 | === modified file 'lib/lp/soyuz/model/archive.py' |
1949 | --- lib/lp/soyuz/model/archive.py 2009-10-26 09:43:56 +0000 |
1950 | +++ lib/lp/soyuz/model/archive.py 2009-11-03 18:13:42 +0000 |
1951 | @@ -174,6 +174,12 @@ |
1952 | relative_build_score = IntCol( |
1953 | dbName='relative_build_score', notNull=True, default=0) |
1954 | |
1955 | + # This field is specifically and only intended for OEM migration to |
1956 | + # Launchpad and should be re-examined in October 2010 to see if it |
1957 | + # is still relevant. |
1958 | + external_dependencies = StringCol( |
1959 | + dbName='external_dependencies', notNull=False, default=None) |
1960 | + |
1961 | def _init(self, *args, **kw): |
1962 | """Provide the right interface for URL traversal.""" |
1963 | SQLBase._init(self, *args, **kw) |
1964 | @@ -998,11 +1004,12 @@ |
1965 | return permission_set.packagesetsForSource( |
1966 | self, sourcepackagename, direct_permissions) |
1967 | |
1968 | - def isSourceUploadAllowed(self, sourcepackagename, person): |
1969 | + def isSourceUploadAllowed( |
1970 | + self, sourcepackagename, person, distroseries=None): |
1971 | """See `IArchive`.""" |
1972 | permission_set = getUtility(IArchivePermissionSet) |
1973 | return permission_set.isSourceUploadAllowed( |
1974 | - self, sourcepackagename, person) |
1975 | + self, sourcepackagename, person, distroseries) |
1976 | |
1977 | def getFileByName(self, filename): |
1978 | """See `IArchive`.""" |
1979 | |
1980 | === modified file 'lib/lp/soyuz/model/archivepermission.py' |
1981 | --- lib/lp/soyuz/model/archivepermission.py 2009-07-28 21:52:56 +0000 |
1982 | +++ lib/lp/soyuz/model/archivepermission.py 2009-11-03 18:13:43 +0000 |
1983 | @@ -22,6 +22,7 @@ |
1984 | from canonical.database.enumcol import EnumCol |
1985 | from canonical.database.sqlbase import sqlvalues, SQLBase |
1986 | |
1987 | +from lp.registry.interfaces.distribution import IDistributionSet |
1988 | from lp.soyuz.interfaces.archive import ComponentNotFound |
1989 | from lp.soyuz.interfaces.archivepermission import ( |
1990 | ArchivePermissionType, IArchivePermission, IArchivePermissionSet, |
1991 | @@ -312,9 +313,13 @@ |
1992 | def _nameToPackageset(self, packageset): |
1993 | """Helper to convert a possible string name to IPackageset.""" |
1994 | if isinstance(packageset, basestring): |
1995 | + # A package set name was passed, assume the current distro series. |
1996 | + ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
1997 | name = packageset |
1998 | store = IStore(Packageset) |
1999 | - packageset = store.find(Packageset, name=name).one() |
2000 | + packageset = store.find( |
2001 | + Packageset, name=name, |
2002 | + distroseries=ubuntu.currentseries).one() |
2003 | if packageset is not None: |
2004 | return packageset |
2005 | else: |
2006 | @@ -475,48 +480,56 @@ |
2007 | ''', (sourcepackagename.id, archive.id))) |
2008 | return rset |
2009 | |
2010 | - def isSourceUploadAllowed(self, archive, sourcepackagename, person): |
2011 | + def isSourceUploadAllowed( |
2012 | + self, archive, sourcepackagename, person, distroseries=None): |
2013 | """See `IArchivePermissionSet`.""" |
2014 | sourcepackagename = self._nameToSourcePackageName(sourcepackagename) |
2015 | store = IStore(ArchivePermission) |
2016 | + if distroseries is None: |
2017 | + ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
2018 | + distroseries = ubuntu.currentseries |
2019 | |
2020 | # Put together the parameters for the query that follows. |
2021 | archive_params = (ArchivePermissionType.UPLOAD, archive.id) |
2022 | + permission_params = (sourcepackagename.id, person.id, distroseries.id) |
2023 | query_params = ( |
2024 | # Query parameters for the first WHERE clause. |
2025 | - (archive.id, sourcepackagename.id) + |
2026 | + (archive.id, distroseries.id, sourcepackagename.id) + |
2027 | # Query parameters for the second WHERE clause. |
2028 | - (sourcepackagename.id,) + (person.id,) + archive_params + |
2029 | + permission_params + archive_params + |
2030 | # Query parameters for the third WHERE clause. |
2031 | - (sourcepackagename.id,) + (person.id,) + archive_params) |
2032 | + permission_params + archive_params) |
2033 | |
2034 | query = ''' |
2035 | SELECT CASE |
2036 | WHEN ( |
2037 | SELECT COUNT(ap.id) |
2038 | - FROM packagesetsources pss, archivepermission ap |
2039 | + FROM packagesetsources pss, archivepermission ap, packageset ps |
2040 | WHERE |
2041 | ap.archive = %s AND ap.explicit = TRUE |
2042 | + AND ap.packageset = ps.id AND ps.distroseries = %s |
2043 | AND pss.sourcepackagename = %s |
2044 | AND pss.packageset = ap.packageset) > 0 |
2045 | THEN ( |
2046 | SELECT COUNT(ap.id) |
2047 | FROM |
2048 | - packagesetsources pss, archivepermission ap, |
2049 | + packagesetsources pss, archivepermission ap, packageset ps, |
2050 | teamparticipation tp |
2051 | WHERE |
2052 | pss.sourcepackagename = %s |
2053 | AND ap.person = tp.team AND tp.person = %s |
2054 | + AND ap.packageset = ps.id AND ps.distroseries = %s |
2055 | AND pss.packageset = ap.packageset AND ap.explicit = TRUE |
2056 | AND ap.permission = %s AND ap.archive = %s) |
2057 | ELSE ( |
2058 | SELECT COUNT(ap.id) |
2059 | FROM |
2060 | - packagesetsources pss, archivepermission ap, |
2061 | + packagesetsources pss, archivepermission ap, packageset ps, |
2062 | teamparticipation tp, flatpackagesetinclusion fpsi |
2063 | WHERE |
2064 | pss.sourcepackagename = %s |
2065 | AND ap.person = tp.team AND tp.person = %s |
2066 | + AND ap.packageset = ps.id AND ps.distroseries = %s |
2067 | AND pss.packageset = fpsi.child AND fpsi.parent = ap.packageset |
2068 | AND ap.permission = %s AND ap.archive = %s) |
2069 | END AS number_of_permitted_package_sets; |
2070 | |
2071 | === modified file 'lib/lp/soyuz/model/packageset.py' |
2072 | --- lib/lp/soyuz/model/packageset.py 2009-07-25 16:33:39 +0000 |
2073 | +++ lib/lp/soyuz/model/packageset.py 2009-11-03 18:13:42 +0000 |
2074 | @@ -6,6 +6,7 @@ |
2075 | |
2076 | import pytz |
2077 | |
2078 | +from storm.exceptions import IntegrityError |
2079 | from storm.expr import In, SQL |
2080 | from storm.locals import DateTime, Int, Reference, Storm, Unicode |
2081 | |
2082 | @@ -13,12 +14,14 @@ |
2083 | from zope.interface import implements |
2084 | |
2085 | from canonical.launchpad.interfaces.lpstorm import IMasterStore, IStore |
2086 | +from canonical.launchpad.webapp.interfaces import NotFoundError |
2087 | +from lp.registry.interfaces.distribution import IDistributionSet |
2088 | from lp.registry.interfaces.sourcepackagename import ( |
2089 | ISourcePackageName, ISourcePackageNameSet) |
2090 | from lp.registry.model.sourcepackagename import SourcePackageName |
2091 | from lp.soyuz.interfaces.packageset import ( |
2092 | - IPackageset, IPackagesetSet, NoSuchPackageSet) |
2093 | - |
2094 | + DuplicatePackagesetName, IPackageset, IPackagesetSet, NoSuchPackageSet) |
2095 | +from lp.soyuz.model.packagesetgroup import PackagesetGroup |
2096 | |
2097 | def _order_result_set(result_set): |
2098 | """Default order for package set and source package name result sets.""" |
2099 | @@ -45,6 +48,12 @@ |
2100 | name = Unicode(name='name', allow_none=False) |
2101 | description = Unicode(name='description', allow_none=False) |
2102 | |
2103 | + distroseries_id = Int(name='distroseries', allow_none=False) |
2104 | + distroseries = Reference(distroseries_id, 'DistroSeries.id') |
2105 | + |
2106 | + packagesetgroup_id = Int(name='packagesetgroup', allow_none=False) |
2107 | + packagesetgroup = Reference(packagesetgroup_id, 'PackagesetGroup.id') |
2108 | + |
2109 | def add(self, data): |
2110 | """See `IPackageset`.""" |
2111 | handlers = ( |
2112 | @@ -279,41 +288,101 @@ |
2113 | |
2114 | def addSubsets(self, names): |
2115 | """See `IPackageset`.""" |
2116 | - clauses = (Packageset, In(Packageset.name, names)) |
2117 | + clauses = ( |
2118 | + Packageset, In(Packageset.name, names), |
2119 | + Packageset.distroseries == self.distroseries) |
2120 | self._api_add_or_remove(clauses, self._addDirectSuccessors) |
2121 | |
2122 | def removeSubsets(self, names): |
2123 | """See `IPackageset`.""" |
2124 | - clauses = (Packageset, In(Packageset.name, names)) |
2125 | + clauses = ( |
2126 | + Packageset, In(Packageset.name, names), |
2127 | + Packageset.distroseries == self.distroseries) |
2128 | self._api_add_or_remove(clauses, self._removeDirectSuccessors) |
2129 | |
2130 | + def relatedSets(self): |
2131 | + """See `IPackageset`.""" |
2132 | + store = IStore(Packageset) |
2133 | + result_set = store.find( |
2134 | + Packageset, |
2135 | + Packageset.packagesetgroup == self.packagesetgroup, |
2136 | + Packageset.id != self.id) |
2137 | + return _order_result_set(result_set) |
2138 | + |
2139 | |
2140 | class PackagesetSet: |
2141 | """See `IPackagesetSet`.""" |
2142 | implements(IPackagesetSet) |
2143 | |
2144 | - def new(self, name, description, owner): |
2145 | + def new( |
2146 | + self, name, description, owner, distroseries=None, related_set=None): |
2147 | """See `IPackagesetSet`.""" |
2148 | store = IMasterStore(Packageset) |
2149 | + |
2150 | + packagesetgroup = None |
2151 | + if related_set is not None: |
2152 | + # Use the packagesetgroup of the `related_set`. |
2153 | + packagesetgroup = related_set.packagesetgroup |
2154 | + else: |
2155 | + # We create the related internal PackagesetGroup for this |
2156 | + # packageset so that we can later see related package sets across |
2157 | + # distroserieses. |
2158 | + packagesetgroup = PackagesetGroup() |
2159 | + packagesetgroup.owner = owner |
2160 | + store.add(packagesetgroup) |
2161 | + |
2162 | + if distroseries is None: |
2163 | + ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
2164 | + distroseries = ubuntu.currentseries |
2165 | + |
2166 | packageset = Packageset() |
2167 | + packageset.packagesetgroup = packagesetgroup |
2168 | packageset.name = name |
2169 | packageset.description = description |
2170 | packageset.owner = owner |
2171 | + |
2172 | + packageset.distroseries = distroseries |
2173 | + |
2174 | store.add(packageset) |
2175 | + |
2176 | + # We need to ensure that the cached statements are flushed so that |
2177 | + # the duplicate name constraint gets triggered here. |
2178 | + try: |
2179 | + store.flush() |
2180 | + except IntegrityError: |
2181 | + raise DuplicatePackagesetName() |
2182 | + |
2183 | return packageset |
2184 | |
2185 | def __getitem__(self, name): |
2186 | """See `IPackagesetSet`.""" |
2187 | return self.getByName(name) |
2188 | |
2189 | - def getByName(self, name): |
2190 | + def getByName(self, name, distroseries=None): |
2191 | """See `IPackagesetSet`.""" |
2192 | store = IStore(Packageset) |
2193 | if not isinstance(name, unicode): |
2194 | name = unicode(name, 'utf-8') |
2195 | - package_set = store.find(Packageset, Packageset.name == name).one() |
2196 | + |
2197 | + ubuntu = getUtility(IDistributionSet).getByName('ubuntu') |
2198 | + extra_args = [] |
2199 | + if distroseries is not None: |
2200 | + # If the user just passed a distro series name, look it up. |
2201 | + if isinstance(distroseries, basestring): |
2202 | + try: |
2203 | + distroseries = ubuntu[distroseries] |
2204 | + except NotFoundError: |
2205 | + raise NoSuchPackageSet(distroseries) |
2206 | + extra_args.append(Packageset.distroseries == distroseries) |
2207 | + else: |
2208 | + extra_args.append(Packageset.distroseries == ubuntu.currentseries) |
2209 | + |
2210 | + package_set = store.find( |
2211 | + Packageset, Packageset.name == name, *extra_args).one() |
2212 | + |
2213 | if package_set is None: |
2214 | raise NoSuchPackageSet(name) |
2215 | + |
2216 | return package_set |
2217 | |
2218 | def getByOwner(self, owner): |
2219 | |
2220 | === added file 'lib/lp/soyuz/model/packagesetgroup.py' |
2221 | --- lib/lp/soyuz/model/packagesetgroup.py 1970-01-01 00:00:00 +0000 |
2222 | +++ lib/lp/soyuz/model/packagesetgroup.py 2009-11-03 18:13:41 +0000 |
2223 | @@ -0,0 +1,30 @@ |
2224 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
2225 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
2226 | + |
2227 | +__metaclass__ = type |
2228 | + |
2229 | +__all__ = [ |
2230 | + 'PackagesetGroup', |
2231 | + ] |
2232 | + |
2233 | +import pytz |
2234 | + |
2235 | +from storm.locals import DateTime, Int, Reference, Storm |
2236 | + |
2237 | +from zope.interface import implements |
2238 | + |
2239 | +from lp.soyuz.interfaces.packagesetgroup import IPackagesetGroup |
2240 | + |
2241 | + |
2242 | +class PackagesetGroup(Storm): |
2243 | + """See `IPackageset`.""" |
2244 | + implements(IPackagesetGroup) |
2245 | + __storm_table__ = 'PackagesetGroup' |
2246 | + id = Int(primary=True) |
2247 | + |
2248 | + date_created = DateTime( |
2249 | + name='date_created', allow_none=False, tzinfo=pytz.UTC) |
2250 | + |
2251 | + owner_id = Int(name='owner', allow_none=False) |
2252 | + owner = Reference(owner_id, 'Person.id') |
2253 | + |
2254 | |
2255 | === modified file 'lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt' |
2256 | --- lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt 2009-10-13 10:05:58 +0000 |
2257 | +++ lib/lp/soyuz/stories/ppa/xx-ppa-workflow.txt 2009-11-03 18:13:40 +0000 |
2258 | @@ -354,6 +354,7 @@ |
2259 | * Set a maximum disk size: uploads will be rejected if the resulting |
2260 | PPA size is exceeding the authorized size. |
2261 | * Set a per-archive build score delta. |
2262 | + * Set external archive dependencies |
2263 | |
2264 | In this case, the administrator may wish to amend the PPA so that it is |
2265 | set up like the ubuntu security PPA, which is private but does not |
2266 | @@ -367,6 +368,8 @@ |
2267 | True |
2268 | >>> admin_browser.getControl(name="field.relative_build_score").value |
2269 | '0' |
2270 | + >>> admin_browser.getControl(name="field.external_dependencies").value |
2271 | + '' |
2272 | |
2273 | >>> admin_browser.getControl(name="field.enabled").value = False |
2274 | >>> admin_browser.getControl(name="field.private").value = True |
2275 | @@ -376,6 +379,9 @@ |
2276 | >>> admin_browser.getControl(name="field.authorized_size").value = '1' |
2277 | >>> admin_browser.getControl( |
2278 | ... name="field.relative_build_score").value = '199' |
2279 | + >>> admin_browser.getControl( |
2280 | + ... name="field.external_dependencies" |
2281 | + ... ).value = "deb http://my.spethial.repo.com/ %(series)s main" |
2282 | >>> admin_browser.getControl("Save").click() |
2283 | |
2284 | Once confirmed the administrator is sent to the PPA page where he can |
2285 | @@ -388,16 +394,33 @@ |
2286 | ... print msg |
2287 | This archive has been disabled. |
2288 | |
2289 | -We need go back to the "Administer archive" page to see the build score change |
2290 | -that was made: |
2291 | +We need go back to the "Administer archive" page to see the build score and |
2292 | +external dependencies changes that were made: |
2293 | |
2294 | >>> admin_browser.getLink("Administer archive").click() |
2295 | >>> admin_browser.getControl(name="field.relative_build_score").value |
2296 | '199' |
2297 | + >>> admin_browser.getControl(name="field.external_dependencies").value |
2298 | + 'deb http://my.spethial.repo.com/ %(series)s main' |
2299 | + |
2300 | +The external dependencies field is validated to make sure it looks like |
2301 | +a sources.list entry. If the field fails validation an error is displayed. |
2302 | + |
2303 | + >>> admin_browser.getControl( |
2304 | + ... name="field.external_dependencies" |
2305 | + ... ).value = "deb not_a_url" |
2306 | + >>> admin_browser.getControl("Save").click() |
2307 | + >>> for error in get_feedback_messages(admin_browser.contents): |
2308 | + ... print error |
2309 | + There is 1 error. |
2310 | + 'deb not_a_url' is not a complete and valid sources.list entry |
2311 | + |
2312 | |
2313 | When the archive is private, the buildd secret must also be set, or an |
2314 | error is issued: |
2315 | |
2316 | + >>> admin_browser.getControl( |
2317 | + ... name="field.external_dependencies").value = "" |
2318 | >>> admin_browser.getControl(name="field.private").value = True |
2319 | >>> admin_browser.getControl(name="field.buildd_secret").value = "" |
2320 | >>> admin_browser.getControl("Save").click() |
2321 | |
2322 | === modified file 'lib/lp/soyuz/stories/webservice/xx-packageset.txt' |
2323 | --- lib/lp/soyuz/stories/webservice/xx-packageset.txt 2009-08-20 04:46:48 +0000 |
2324 | +++ lib/lp/soyuz/stories/webservice/xx-packageset.txt 2009-11-03 18:13:41 +0000 |
2325 | @@ -18,6 +18,9 @@ |
2326 | Please refer to the tests contained in the file above if you are really |
2327 | interested in package sets and the complete functionality they offer. |
2328 | |
2329 | + |
2330 | +== General package set properties == |
2331 | + |
2332 | We start off by creating an 'umbrella' package set that will include all |
2333 | source packages. |
2334 | |
2335 | @@ -52,16 +55,16 @@ |
2336 | Can we access it via the webservice API as well? |
2337 | |
2338 | >>> logout() |
2339 | - >>> umbrella = webservice.get("/package-sets/umbrella").jsonBody() |
2340 | + >>> umbrella = webservice.get("/package-sets/hoary/umbrella").jsonBody() |
2341 | >>> print umbrella['self_link'] |
2342 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2343 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2344 | |
2345 | `PackageSet`s can be looked up by name. |
2346 | |
2347 | >>> response = webservice.named_get( |
2348 | ... '/package-sets', 'getByName', {}, name=u'umbrella') |
2349 | >>> print response.jsonBody()['self_link'] |
2350 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2351 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2352 | |
2353 | When a `PackageSet` cannot be found, an error is returned. |
2354 | |
2355 | @@ -70,9 +73,17 @@ |
2356 | >>> print response |
2357 | HTTP/1.1 400 Bad Request |
2358 | ... |
2359 | - NoSuchPackageSet: No such packageset: 'not-found'. |
2360 | + No such package set (in the specified distro series): 'not-found'. |
2361 | + ... |
2362 | <BLANKLINE> |
2363 | |
2364 | +Here's an example with a funny URL concoted by a "smart" user. |
2365 | + |
2366 | + >>> response = webservice.get("/package-sets/lucid-plus-1/umbrella/+pwn") |
2367 | + >>> print response |
2368 | + HTTP/1.1 404 Not Found |
2369 | + ... |
2370 | + |
2371 | Populate the 'umbrella' package set with source packages. |
2372 | |
2373 | >>> from canonical.launchpad.webapp.interfaces import ( |
2374 | @@ -81,7 +92,7 @@ |
2375 | >>> store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
2376 | >>> all_spns = store.find(SourcePackageName) |
2377 | >>> response = webservice.named_post( |
2378 | - ... '/package-sets/umbrella', 'addSources', {}, |
2379 | + ... '/package-sets/hoary/umbrella', 'addSources', {}, |
2380 | ... names=[spn.name for spn in all_spns]) |
2381 | >>> print response |
2382 | HTTP/1.1 200 Ok |
2383 | @@ -91,7 +102,7 @@ |
2384 | exist will not fail. Non-existing source package names are *ignored*. |
2385 | |
2386 | >>> response = webservice.named_post( |
2387 | - ... '/package-sets/umbrella', 'addSources', {}, |
2388 | + ... '/package-sets/hoary/umbrella', 'addSources', {}, |
2389 | ... names=[u'does-not-exist']) |
2390 | >>> print response |
2391 | HTTP/1.1 200 Ok |
2392 | @@ -99,7 +110,7 @@ |
2393 | null |
2394 | |
2395 | >>> response = webservice.named_post( |
2396 | - ... '/package-sets/umbrella', 'removeSources', {}, |
2397 | + ... '/package-sets/hoary/umbrella', 'removeSources', {}, |
2398 | ... names=[u'does-not-exist']) |
2399 | >>> print response |
2400 | HTTP/1.1 200 Ok |
2401 | @@ -109,7 +120,7 @@ |
2402 | Let's see what we got. |
2403 | |
2404 | >>> response = webservice.named_get( |
2405 | - ... '/package-sets/umbrella', 'getSourcesIncluded', {}) |
2406 | + ... '/package-sets/hoary/umbrella', 'getSourcesIncluded', {}) |
2407 | >>> print response |
2408 | HTTP/1.1 200 Ok |
2409 | ... |
2410 | @@ -136,7 +147,7 @@ |
2411 | from the 'umbrella' package set. |
2412 | |
2413 | >>> response = webservice.named_post( |
2414 | - ... '/package-sets/umbrella', 'removeSources', {}, |
2415 | + ... '/package-sets/hoary/umbrella', 'removeSources', {}, |
2416 | ... names=["foobar", "iceweasel"]) |
2417 | >>> print response |
2418 | HTTP/1.1 200 Ok |
2419 | @@ -146,7 +157,7 @@ |
2420 | from the list below. |
2421 | |
2422 | >>> response = webservice.named_get( |
2423 | - ... '/package-sets/umbrella', 'getSourcesIncluded', {}) |
2424 | + ... '/package-sets/hoary/umbrella', 'getSourcesIncluded', {}) |
2425 | >>> print response |
2426 | HTTP/1.1 200 Ok |
2427 | ... |
2428 | @@ -176,13 +187,13 @@ |
2429 | |
2430 | >>> response = webservice.get("/package-sets/") |
2431 | >>> print_payload(response) |
2432 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2433 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2434 | |
2435 | Package sets may include other package sets (as subsets). At this point, |
2436 | however, we only have the 'umbrella' package set. It hence has no subsets. |
2437 | |
2438 | >>> response = webservice.named_get( |
2439 | - ... '/package-sets/umbrella', 'setsIncluded', {}) |
2440 | + ... '/package-sets/hoary/umbrella', 'setsIncluded', {}) |
2441 | >>> print response |
2442 | HTTP/1.1 200 Ok |
2443 | ... |
2444 | @@ -206,6 +217,50 @@ |
2445 | HTTP/1.1 201 Created |
2446 | ... |
2447 | |
2448 | + |
2449 | +=== Package sets and distro series === |
2450 | + |
2451 | +Every package set is associated with a distro series. |
2452 | + |
2453 | + >>> from lazr.restful.testing.webservice import pprint_entry |
2454 | + >>> mozilla = webservice.named_get( |
2455 | + ... '/package-sets', 'getByName', {}, name=u'mozilla').jsonBody() |
2456 | + >>> print mozilla['distroseries_link'] |
2457 | + http://api.launchpad.dev/beta/ubuntu/hoary |
2458 | + |
2459 | + |
2460 | +=== Related package sets === |
2461 | + |
2462 | +When adding a package set we can specify that is to be related to another set |
2463 | +that exists already. |
2464 | + |
2465 | + >>> grumpy = webservice.get("/ubuntu/grumpy").jsonBody() |
2466 | + >>> print grumpy['self_link'] |
2467 | + http://api.launchpad.dev/beta/ubuntu/grumpy |
2468 | + |
2469 | +We are adding a new 'mozilla' package set to the 'grumpy' distro series and |
2470 | +it is related to 'mozilla' in 'hoary'. |
2471 | + |
2472 | + >>> response = webservice.named_post( |
2473 | + ... '/package-sets', 'new', {}, |
2474 | + ... name=u'mozilla', |
2475 | + ... description=u'Contains all mozilla packages in grumpy', |
2476 | + ... owner=name12['self_link'], distroseries=grumpy['self_link'], |
2477 | + ... related_set=mozilla['self_link']) |
2478 | + >>> print response |
2479 | + HTTP/1.1 201 Created |
2480 | + ... |
2481 | + |
2482 | + >>> response = webservice.named_get( |
2483 | + ... mozilla['self_link'], 'relatedSets', {}) |
2484 | + >>> print_payload(response) |
2485 | + http://api.launchpad.dev/beta/package-sets/grumpy/mozilla |
2486 | + |
2487 | + |
2488 | +== Package set hierarchy == |
2489 | + |
2490 | +More package sets are needed to set up the hierarchy described below. |
2491 | + |
2492 | >>> response = webservice.named_post( |
2493 | ... '/package-sets', 'new', {}, |
2494 | ... name=u'firefox', description=u'Contains all firefox packages', |
2495 | @@ -232,6 +287,18 @@ |
2496 | HTTP/1.1 201 Created |
2497 | ... |
2498 | |
2499 | +The 'languagepack' package set will be removed later (in hoary). Let's add a |
2500 | +set with the same name in 'grumpy' to make sure that the right one is found. |
2501 | + |
2502 | + >>> response = webservice.named_post( |
2503 | + ... '/package-sets', 'new', {}, |
2504 | + ... name=u'languagepack', |
2505 | + ... description=u'Contains all languagepack packages', |
2506 | + ... owner=name12['self_link'], distroseries=grumpy['self_link']) |
2507 | + >>> print response |
2508 | + HTTP/1.1 201 Created |
2509 | + ... |
2510 | + |
2511 | In order to test whether methods relating to package set hierarchies were |
2512 | exposed on the Launchpad API correctly we will define the following package |
2513 | set hierarchy: |
2514 | @@ -245,27 +312,27 @@ |
2515 | * languagepack |
2516 | |
2517 | >>> response = webservice.named_post( |
2518 | - ... '/package-sets/umbrella', 'addSubsets', {}, |
2519 | + ... '/package-sets/hoary/umbrella', 'addSubsets', {}, |
2520 | ... names=[u'gnome', u'mozilla']) |
2521 | >>> print response |
2522 | HTTP/1.1 200 Ok |
2523 | ... |
2524 | |
2525 | >>> response = webservice.named_post( |
2526 | - ... '/package-sets/gnome', 'addSubsets', {}, names=[u'languagepack']) |
2527 | + ... '/package-sets/hoary/gnome', 'addSubsets', {}, names=[u'languagepack']) |
2528 | >>> print response |
2529 | HTTP/1.1 200 Ok |
2530 | ... |
2531 | |
2532 | >>> response = webservice.named_post( |
2533 | - ... '/package-sets/thunderbird', 'addSubsets', {}, |
2534 | + ... '/package-sets/hoary/thunderbird', 'addSubsets', {}, |
2535 | ... names=[u'languagepack']) |
2536 | >>> print response |
2537 | HTTP/1.1 200 Ok |
2538 | ... |
2539 | |
2540 | >>> response = webservice.named_post( |
2541 | - ... '/package-sets/mozilla', 'addSubsets', {}, |
2542 | + ... '/package-sets/hoary/mozilla', 'addSubsets', {}, |
2543 | ... names=[u'firefox', u'thunderbird']) |
2544 | >>> print response |
2545 | HTTP/1.1 200 Ok |
2546 | @@ -275,7 +342,7 @@ |
2547 | non-existing package sets will not fail. |
2548 | |
2549 | >>> response = webservice.named_post( |
2550 | - ... '/package-sets/thunderbird', 'addSubsets', {}, |
2551 | + ... '/package-sets/hoary/thunderbird', 'addSubsets', {}, |
2552 | ... names=[u'does-not-exist']) |
2553 | >>> print response |
2554 | HTTP/1.1 200 Ok |
2555 | @@ -283,7 +350,7 @@ |
2556 | null |
2557 | |
2558 | >>> response = webservice.named_post( |
2559 | - ... '/package-sets/thunderbird', 'removeSubsets', {}, |
2560 | + ... '/package-sets/hoary/thunderbird', 'removeSubsets', {}, |
2561 | ... names=[u'does-not-exist']) |
2562 | >>> print response |
2563 | HTTP/1.1 200 Ok |
2564 | @@ -293,49 +360,49 @@ |
2565 | The 'umbrella' package set should have plenty of subsets now. |
2566 | |
2567 | >>> response = webservice.named_get( |
2568 | - ... '/package-sets/umbrella', 'setsIncluded', {}) |
2569 | + ... '/package-sets/hoary/umbrella', 'setsIncluded', {}) |
2570 | >>> print_payload(response) |
2571 | - http://api.launchpad.dev/beta/package-sets/firefox |
2572 | - http://api.launchpad.dev/beta/package-sets/gnome |
2573 | - http://api.launchpad.dev/beta/package-sets/languagepack |
2574 | - http://api.launchpad.dev/beta/package-sets/mozilla |
2575 | - http://api.launchpad.dev/beta/package-sets/thunderbird |
2576 | + http://api.launchpad.dev/beta/package-sets/hoary/firefox |
2577 | + http://api.launchpad.dev/beta/package-sets/hoary/gnome |
2578 | + http://api.launchpad.dev/beta/package-sets/hoary/languagepack |
2579 | + http://api.launchpad.dev/beta/package-sets/hoary/mozilla |
2580 | + http://api.launchpad.dev/beta/package-sets/hoary/thunderbird |
2581 | |
2582 | However only two of the above are direct subsets. |
2583 | |
2584 | >>> response = webservice.named_get( |
2585 | - ... '/package-sets/umbrella', 'setsIncluded', {}, |
2586 | + ... '/package-sets/hoary/umbrella', 'setsIncluded', {}, |
2587 | ... direct_inclusion=True) |
2588 | >>> print_payload(response) |
2589 | - http://api.launchpad.dev/beta/package-sets/gnome |
2590 | - http://api.launchpad.dev/beta/package-sets/mozilla |
2591 | + http://api.launchpad.dev/beta/package-sets/hoary/gnome |
2592 | + http://api.launchpad.dev/beta/package-sets/hoary/mozilla |
2593 | |
2594 | Let's ask the question the other way around what package sets are including |
2595 | a particular subset? |
2596 | |
2597 | >>> response = webservice.named_get( |
2598 | - ... '/package-sets/languagepack', 'setsIncludedBy', {}) |
2599 | + ... '/package-sets/hoary/languagepack', 'setsIncludedBy', {}) |
2600 | >>> print_payload(response) |
2601 | - http://api.launchpad.dev/beta/package-sets/gnome |
2602 | - http://api.launchpad.dev/beta/package-sets/mozilla |
2603 | - http://api.launchpad.dev/beta/package-sets/thunderbird |
2604 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2605 | + http://api.launchpad.dev/beta/package-sets/hoary/gnome |
2606 | + http://api.launchpad.dev/beta/package-sets/hoary/mozilla |
2607 | + http://api.launchpad.dev/beta/package-sets/hoary/thunderbird |
2608 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2609 | |
2610 | The list of package sets that *directly* include 'languagepack' will be |
2611 | shorter because the transitive closure is ignored. |
2612 | |
2613 | >>> response = webservice.named_get( |
2614 | - ... '/package-sets/languagepack', 'setsIncludedBy', {}, |
2615 | + ... '/package-sets/hoary/languagepack', 'setsIncludedBy', {}, |
2616 | ... direct_inclusion=True) |
2617 | >>> print_payload(response) |
2618 | - http://api.launchpad.dev/beta/package-sets/gnome |
2619 | - http://api.launchpad.dev/beta/package-sets/thunderbird |
2620 | + http://api.launchpad.dev/beta/package-sets/hoary/gnome |
2621 | + http://api.launchpad.dev/beta/package-sets/hoary/thunderbird |
2622 | |
2623 | We can remove subsets as well. In the example below 'thunderbird' will |
2624 | stop including 'languagepack'. |
2625 | |
2626 | >>> response = webservice.named_post( |
2627 | - ... '/package-sets/thunderbird', 'removeSubsets', {}, |
2628 | + ... '/package-sets/hoary/thunderbird', 'removeSubsets', {}, |
2629 | ... names=[u'languagepack']) |
2630 | >>> print response |
2631 | HTTP/1.1 200 Ok |
2632 | @@ -344,37 +411,37 @@ |
2633 | And, here we go, now 'languagepack' has only one direct predecessor: 'gnome'. |
2634 | |
2635 | >>> response = webservice.named_get( |
2636 | - ... '/package-sets/languagepack', 'setsIncludedBy', {}, |
2637 | + ... '/package-sets/hoary/languagepack', 'setsIncludedBy', {}, |
2638 | ... direct_inclusion=True) |
2639 | >>> print_payload(response) |
2640 | - http://api.launchpad.dev/beta/package-sets/gnome |
2641 | + http://api.launchpad.dev/beta/package-sets/hoary/gnome |
2642 | |
2643 | Let's add a few source packages to the 'firefox' and the 'thunderbird' |
2644 | package sets. |
2645 | |
2646 | >>> response = webservice.named_post( |
2647 | - ... '/package-sets/firefox', 'addSources', {}, |
2648 | + ... '/package-sets/hoary/firefox', 'addSources', {}, |
2649 | ... names=['at', 'mozilla-firefox', 'language-pack-de']) |
2650 | >>> print response |
2651 | HTTP/1.1 200 Ok |
2652 | ... |
2653 | |
2654 | >>> response = webservice.named_get( |
2655 | - ... '/package-sets/firefox', 'getSourcesIncluded', {}) |
2656 | + ... '/package-sets/hoary/firefox', 'getSourcesIncluded', {}) |
2657 | >>> print response |
2658 | HTTP/1.1 200 Ok |
2659 | ... |
2660 | ["at", "language-pack-de", "mozilla-firefox"] |
2661 | |
2662 | >>> response = webservice.named_post( |
2663 | - ... '/package-sets/thunderbird', 'addSources', {}, |
2664 | + ... '/package-sets/hoary/thunderbird', 'addSources', {}, |
2665 | ... names=['at', 'cnews', 'thunderbird', 'language-pack-de']) |
2666 | >>> print response |
2667 | HTTP/1.1 200 Ok |
2668 | ... |
2669 | |
2670 | >>> response = webservice.named_get( |
2671 | - ... '/package-sets/thunderbird', 'getSourcesIncluded', {}) |
2672 | + ... '/package-sets/hoary/thunderbird', 'getSourcesIncluded', {}) |
2673 | >>> print response |
2674 | HTTP/1.1 200 Ok |
2675 | ... |
2676 | @@ -386,9 +453,9 @@ |
2677 | ... '/package-sets/', 'setsIncludingSource', {}, |
2678 | ... sourcepackagename=u'mozilla-firefox') |
2679 | >>> print_payload(response) |
2680 | - http://api.launchpad.dev/beta/package-sets/firefox |
2681 | - http://api.launchpad.dev/beta/package-sets/mozilla |
2682 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2683 | + http://api.launchpad.dev/beta/package-sets/hoary/firefox |
2684 | + http://api.launchpad.dev/beta/package-sets/hoary/mozilla |
2685 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2686 | |
2687 | Which package sets include the 'mozilla-firefox' source package *directly*? |
2688 | |
2689 | @@ -397,8 +464,8 @@ |
2690 | ... sourcepackagename=u'mozilla-firefox', |
2691 | ... direct_inclusion=True) |
2692 | >>> print_payload(response) |
2693 | - http://api.launchpad.dev/beta/package-sets/firefox |
2694 | - http://api.launchpad.dev/beta/package-sets/umbrella |
2695 | + http://api.launchpad.dev/beta/package-sets/hoary/firefox |
2696 | + http://api.launchpad.dev/beta/package-sets/hoary/umbrella |
2697 | |
2698 | If a non-existing source package name is passed it returns an error. |
2699 | |
2700 | @@ -414,9 +481,9 @@ |
2701 | What source packages are shared by the 'firefox' and the 'thunderbird' |
2702 | package sets? |
2703 | |
2704 | - >>> thunderbird = webservice.get("/package-sets/thunderbird").jsonBody() |
2705 | + >>> thunderbird = webservice.get("/package-sets/hoary/thunderbird").jsonBody() |
2706 | >>> response = webservice.named_get( |
2707 | - ... '/package-sets/firefox', 'getSourcesSharedBy', {}, |
2708 | + ... '/package-sets/hoary/firefox', 'getSourcesSharedBy', {}, |
2709 | ... other_package_set=thunderbird['self_link']) |
2710 | >>> print response |
2711 | HTTP/1.1 200 Ok |
2712 | @@ -426,16 +493,16 @@ |
2713 | How about the complement set i.e. the packages not shared? |
2714 | |
2715 | >>> response = webservice.named_get( |
2716 | - ... '/package-sets/firefox', 'getSourcesNotSharedBy', {}, |
2717 | + ... '/package-sets/hoary/firefox', 'getSourcesNotSharedBy', {}, |
2718 | ... other_package_set=thunderbird['self_link']) |
2719 | >>> print response |
2720 | HTTP/1.1 200 Ok |
2721 | ... |
2722 | ["mozilla-firefox"] |
2723 | |
2724 | - >>> firefox = webservice.get("/package-sets/firefox").jsonBody() |
2725 | + >>> firefox = webservice.get("/package-sets/hoary/firefox").jsonBody() |
2726 | >>> response = webservice.named_get( |
2727 | - ... '/package-sets/thunderbird', 'getSourcesNotSharedBy', {}, |
2728 | + ... '/package-sets/hoary/thunderbird', 'getSourcesNotSharedBy', {}, |
2729 | ... other_package_set=firefox['self_link']) |
2730 | >>> print response |
2731 | HTTP/1.1 200 Ok |
2732 | @@ -461,7 +528,7 @@ |
2733 | >>> response = webservice.named_post( |
2734 | ... ubuntu['main_archive_link'], 'newPackagesetUploader', {}, |
2735 | ... person=name12['self_link'], |
2736 | - ... packageset='firefox') |
2737 | + ... packageset=firefox['self_link']) |
2738 | >>> print response |
2739 | HTTP/1.1 201 Created |
2740 | ... |
2741 | @@ -489,7 +556,7 @@ |
2742 | >>> response = webservice.named_post( |
2743 | ... ubuntu['main_archive_link'], 'newPackagesetUploader', {}, |
2744 | ... person=name12['self_link'], |
2745 | - ... packageset='mozilla') |
2746 | + ... packageset=mozilla['self_link']) |
2747 | >>> print response |
2748 | HTTP/1.1 201 Created |
2749 | ... |
2750 | @@ -499,7 +566,7 @@ |
2751 | |
2752 | >>> response = webservice.named_get( |
2753 | ... ubuntu['main_archive_link'], 'getUploadersForPackageset', {}, |
2754 | - ... packageset='firefox') |
2755 | + ... packageset=firefox['self_link']) |
2756 | >>> print_payload(response) |
2757 | http://.../+archive/primary/+upload/name12?type=packageset&item=firefox |
2758 | |
2759 | @@ -508,7 +575,7 @@ |
2760 | |
2761 | >>> response = webservice.named_get( |
2762 | ... ubuntu['main_archive_link'], 'getUploadersForPackageset', {}, |
2763 | - ... packageset='firefox', direct_permissions=False) |
2764 | + ... packageset=firefox['self_link'], direct_permissions=False) |
2765 | >>> print_payload(response) |
2766 | http://.../+archive/primary/+upload/name12?type=packageset&item=firefox |
2767 | http://.../+archive/primary/+upload/name12?type=packageset&item=mozilla |
2768 | @@ -518,7 +585,7 @@ |
2769 | >>> response = webservice.named_post( |
2770 | ... ubuntu['main_archive_link'], 'deletePackagesetUploader', {}, |
2771 | ... person=name12['self_link'], |
2772 | - ... packageset='mozilla') |
2773 | + ... packageset=mozilla['self_link']) |
2774 | >>> print response |
2775 | HTTP/1.1 200 Ok |
2776 | ... |
2777 | @@ -528,7 +595,7 @@ |
2778 | |
2779 | >>> response = webservice.named_get( |
2780 | ... ubuntu['main_archive_link'], 'getUploadersForPackageset', {}, |
2781 | - ... packageset='firefox', direct_permissions=False) |
2782 | + ... packageset=firefox['self_link'], direct_permissions=False) |
2783 | >>> print_payload(response) |
2784 | http://.../+archive/primary/+upload/name12?type=packageset&item=firefox |
2785 | |
2786 | @@ -538,7 +605,7 @@ |
2787 | >>> response = webservice.named_post( |
2788 | ... ubuntu['main_archive_link'], 'newPackagesetUploader', {}, |
2789 | ... person=cprov['self_link'], |
2790 | - ... packageset='mozilla') |
2791 | + ... packageset=mozilla['self_link']) |
2792 | >>> print response |
2793 | HTTP/1.1 201 Created |
2794 | ... |
2795 | @@ -546,7 +613,7 @@ |
2796 | >>> response = webservice.named_post( |
2797 | ... ubuntu['main_archive_link'], 'newPackagesetUploader', {}, |
2798 | ... person=cprov['self_link'], |
2799 | - ... packageset='thunderbird') |
2800 | + ... packageset=thunderbird['self_link']) |
2801 | >>> print response |
2802 | HTTP/1.1 201 Created |
2803 | ... |
2804 | @@ -599,6 +666,39 @@ |
2805 | ... |
2806 | true |
2807 | |
2808 | +Archive permissions have distro series scope. We did not specify a distro |
2809 | +series in the query above. Hence the `currentseries` in Ubuntu is assumed |
2810 | +('hoary'). |
2811 | +The following query (note the additional 'distroseries' parameter) is |
2812 | +thus equivalent: |
2813 | + |
2814 | + >>> print ubuntu['current_series_link'] |
2815 | + http://api.launchpad.dev/beta/ubuntu/hoary |
2816 | + >>> hoary = webservice.get("/ubuntu/hoary").jsonBody() |
2817 | + >>> print hoary['self_link'] |
2818 | + http://api.launchpad.dev/beta/ubuntu/hoary |
2819 | + |
2820 | + >>> response = webservice.named_get( |
2821 | + ... ubuntu['main_archive_link'], 'isSourceUploadAllowed', |
2822 | + ... {}, sourcepackagename='mozilla-firefox', |
2823 | + ... person=cprov['self_link'], distroseries=hoary['self_link']) |
2824 | + >>> print(response) |
2825 | + HTTP/1.1 200 Ok |
2826 | + ... |
2827 | + true |
2828 | + |
2829 | +Since cprov's upload permission is limited to the current distro series |
2830 | +('hoary') checking the same permission for 'grumpy' will fail. |
2831 | + |
2832 | + >>> response = webservice.named_get( |
2833 | + ... ubuntu['main_archive_link'], 'isSourceUploadAllowed', |
2834 | + ... {}, sourcepackagename='mozilla-firefox', |
2835 | + ... person=cprov['self_link'], distroseries=grumpy['self_link']) |
2836 | + >>> print(response) |
2837 | + HTTP/1.1 200 Ok |
2838 | + ... |
2839 | + false |
2840 | + |
2841 | 'name12' should not be allowed to upload the 'thunderbird' source package. |
2842 | |
2843 | >>> response = webservice.named_get( |
2844 | @@ -610,6 +710,80 @@ |
2845 | ... |
2846 | false |
2847 | |
2848 | +Let's create a (related) package set in 'grumpy' and authorize 'name12' to |
2849 | +upload to it. |
2850 | + |
2851 | +This will fail since 'name12' has no permissions applying to 'grumpy' yet. |
2852 | + |
2853 | + >>> response = webservice.named_get( |
2854 | + ... ubuntu['main_archive_link'], 'isSourceUploadAllowed', |
2855 | + ... {}, sourcepackagename='thunderbird', |
2856 | + ... person=name12['self_link'], distroseries=grumpy['self_link']) |
2857 | + >>> print(response) |
2858 | + HTTP/1.1 200 Ok |
2859 | + ... |
2860 | + false |
2861 | + |
2862 | +Create a new package set ('grouchy-thunderbird') in 'grumpy'. |
2863 | + |
2864 | + >>> response = webservice.named_post( |
2865 | + ... '/package-sets', 'new', {}, |
2866 | + ... name=u'grouchy-thunderbird', |
2867 | + ... description=u'Contains all thunderbird packages in grumpy', |
2868 | + ... owner=name12['self_link'], distroseries=grumpy['self_link'], |
2869 | + ... related_set=thunderbird['self_link']) |
2870 | + >>> print response |
2871 | + HTTP/1.1 201 Created |
2872 | + ... |
2873 | + |
2874 | + >>> response = webservice.named_get( |
2875 | + ... thunderbird['self_link'], 'relatedSets', {}) |
2876 | + >>> print_payload(response) |
2877 | + http://api.launchpad.dev/beta/package-sets/grumpy/grouchy-thunderbird |
2878 | + |
2879 | +Associate 'grouchy-thunderbird' with the appropriate source packages. |
2880 | + |
2881 | + >>> response = webservice.named_post( |
2882 | + ... '/package-sets/grumpy/grouchy-thunderbird', 'addSources', {}, |
2883 | + ... names=['thunderbird', 'language-pack-de']) |
2884 | + >>> print response |
2885 | + HTTP/1.1 200 Ok |
2886 | + ... |
2887 | + |
2888 | +Grant 'name12' upload permissions to 'grouchy-thunderbird' in 'grumpy'. |
2889 | + |
2890 | + >>> grouchy_bird = webservice.get( |
2891 | + ... "/package-sets/grumpy/grouchy-thunderbird").jsonBody() |
2892 | + |
2893 | + >>> response = webservice.named_post( |
2894 | + ... ubuntu['main_archive_link'], 'newPackagesetUploader', {}, |
2895 | + ... person=name12['self_link'], |
2896 | + ... packageset=grouchy_bird['self_link']) |
2897 | + >>> print response |
2898 | + HTTP/1.1 201 Created |
2899 | + ... |
2900 | + |
2901 | +Does the new archive permission show up? |
2902 | + |
2903 | + >>> response = webservice.named_get( |
2904 | + ... ubuntu['main_archive_link'], 'getPackagesetsForUploader', {}, |
2905 | + ... person=name12['self_link']) |
2906 | + >>> print_payload(response) |
2907 | + http://...+archive/primary/+upload/name12?type=packageset&item=firefox |
2908 | + http://...+archive/primary/+upload/name12?type=packageset&item=grouchy-thunderbird |
2909 | + |
2910 | +And now 'name12' should be authorized to upload source package |
2911 | +'thunderbird' in 'grumpy'. |
2912 | + |
2913 | + >>> response = webservice.named_get( |
2914 | + ... ubuntu['main_archive_link'], 'isSourceUploadAllowed', |
2915 | + ... {}, sourcepackagename='thunderbird', |
2916 | + ... person=name12['self_link'], distroseries=grumpy['self_link']) |
2917 | + >>> print(response) |
2918 | + HTTP/1.1 200 Ok |
2919 | + ... |
2920 | + true |
2921 | + |
2922 | Sometimes it's also interesting to see what package set based upload |
2923 | permissions apply to a source package irrespective of the principal. |
2924 | |
2925 | |
2926 | === modified file 'lib/lp/soyuz/templates/person-archive-subscriptions.pt' |
2927 | --- lib/lp/soyuz/templates/person-archive-subscriptions.pt 2009-09-18 07:46:03 +0000 |
2928 | +++ lib/lp/soyuz/templates/person-archive-subscriptions.pt 2009-11-03 18:13:39 +0000 |
2929 | @@ -34,7 +34,7 @@ |
2930 | </td> |
2931 | <td> |
2932 | <tal:active condition="token"> |
2933 | - <a tal:attributes="href subscription/fmt:url" class="info"> |
2934 | + <a tal:attributes="href subscription/fmt:url" class="sprite info"> |
2935 | View |
2936 | </a> |
2937 | </tal:active> |
2938 | |
2939 | === added file 'lib/lp/soyuz/tests/test_packageset.py' |
2940 | --- lib/lp/soyuz/tests/test_packageset.py 1970-01-01 00:00:00 +0000 |
2941 | +++ lib/lp/soyuz/tests/test_packageset.py 2009-11-03 18:13:43 +0000 |
2942 | @@ -0,0 +1,187 @@ |
2943 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
2944 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
2945 | + |
2946 | +"""Test Packageset features.""" |
2947 | + |
2948 | +from zope.component import getUtility |
2949 | + |
2950 | +from canonical.testing import LaunchpadZopelessLayer |
2951 | + |
2952 | +from lp.testing import TestCaseWithFactory |
2953 | +from lp.registry.interfaces.distribution import IDistributionSet |
2954 | +from lp.registry.interfaces.distroseries import DistroSeriesStatus |
2955 | +from lp.soyuz.interfaces.packageset import ( |
2956 | + DuplicatePackagesetName, IPackagesetSet) |
2957 | + |
2958 | + |
2959 | +class TestPackagesetSet(TestCaseWithFactory): |
2960 | + |
2961 | + layer = LaunchpadZopelessLayer |
2962 | + |
2963 | + def setUp(self): |
2964 | + """Setup a distribution with multiple distroseries.""" |
2965 | + super(TestPackagesetSet, self).setUp() |
2966 | + self.distribution = getUtility(IDistributionSet).getByName( |
2967 | + 'ubuntu') |
2968 | + self.distroseries_current = self.distribution.currentseries |
2969 | + self.distroseries_experimental = self.factory.makeDistroRelease( |
2970 | + distribution = self.distribution, name="experimental", |
2971 | + status=DistroSeriesStatus.EXPERIMENTAL) |
2972 | + |
2973 | + self.person1 = self.factory.makePerson( |
2974 | + name='hacker', displayname=u'Happy Hacker') |
2975 | + |
2976 | + self.packageset_set = getUtility(IPackagesetSet) |
2977 | + |
2978 | + def test_new_defaults_to_current_distroseries(self): |
2979 | + # If the distroseries is not provided, the current development |
2980 | + # distroseries will be assumed. |
2981 | + packageset = self.packageset_set.new( |
2982 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
2983 | + |
2984 | + self.failUnlessEqual( |
2985 | + self.distroseries_current, packageset.distroseries) |
2986 | + |
2987 | + def test_new_with_specified_distroseries(self): |
2988 | + # A distroseries can be provided when creating a package set. |
2989 | + packageset = self.packageset_set.new( |
2990 | + u'kernel', u'Contains all OS kernel packages', self.person1, |
2991 | + distroseries=self.distroseries_experimental) |
2992 | + |
2993 | + self.failUnlessEqual( |
2994 | + self.distroseries_experimental, packageset.distroseries) |
2995 | + |
2996 | + def test_new_creates_new_packageset_group(self): |
2997 | + # Creating a new packageset should also create a new packageset |
2998 | + # group with the same owner. |
2999 | + packageset = self.packageset_set.new( |
3000 | + u'kernel', u'Contains all OS kernel packages', self.person1, |
3001 | + distroseries=self.distroseries_experimental) |
3002 | + |
3003 | + self.failUnlessEqual( |
3004 | + self.person1, packageset.packagesetgroup.owner) |
3005 | + |
3006 | + def test_new_duplicate_name_for_same_distroseries(self): |
3007 | + # Creating a packageset with a duplicate name for the |
3008 | + # given distroseries will fail. |
3009 | + packageset = self.packageset_set.new( |
3010 | + u'kernel', u'Contains all OS kernel packages', self.person1, |
3011 | + distroseries=self.distroseries_experimental) |
3012 | + |
3013 | + self.failUnlessRaises( |
3014 | + DuplicatePackagesetName, self.packageset_set.new, |
3015 | + u'kernel', u'A packageset with a duplicate name', self.person1, |
3016 | + distroseries=self.distroseries_experimental) |
3017 | + |
3018 | + def test_new_duplicate_name_for_different_distroseries(self): |
3019 | + # Creating a packageset with a duplicate name but for a different |
3020 | + # series is no problem. |
3021 | + packageset = self.packageset_set.new( |
3022 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3023 | + |
3024 | + packageset2 = self.packageset_set.new( |
3025 | + u'kernel', u'A packageset with a duplicate name', self.person1, |
3026 | + distroseries=self.distroseries_experimental) |
3027 | + self.assertEqual(packageset.name, packageset2.name) |
3028 | + |
3029 | + def test_new_related_packageset(self): |
3030 | + # Creating a new package set while specifying a `related_set` should |
3031 | + # have the effect that the former ends up in the same group as the |
3032 | + # latter. |
3033 | + pset1 = self.packageset_set.new( |
3034 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3035 | + |
3036 | + pset2 = self.packageset_set.new( |
3037 | + u'kernel', u'A related package set.', self.person1, |
3038 | + distroseries=self.distroseries_experimental, related_set=pset1) |
3039 | + self.assertEqual(pset1.packagesetgroup, pset2.packagesetgroup) |
3040 | + |
3041 | + def test_get_by_name_in_current_distroseries(self): |
3042 | + # IPackagesetSet.getByName() will return the package set in the |
3043 | + # current distroseries if the optional `distroseries` parameter is |
3044 | + # omitted. |
3045 | + pset1 = self.packageset_set.new( |
3046 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3047 | + pset2 = self.packageset_set.new( |
3048 | + u'kernel', u'A related package set.', self.person1, |
3049 | + distroseries=self.distroseries_experimental, related_set=pset1) |
3050 | + pset_found = getUtility(IPackagesetSet).getByName('kernel') |
3051 | + self.assertEqual(pset1, pset_found) |
3052 | + |
3053 | + def test_get_by_name_in_specified_distroseries(self): |
3054 | + # IPackagesetSet.getByName() will return the package set in the |
3055 | + # specified distroseries. |
3056 | + pset1 = self.packageset_set.new( |
3057 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3058 | + pset2 = self.packageset_set.new( |
3059 | + u'kernel', u'A related package set.', self.person1, |
3060 | + distroseries=self.distroseries_experimental, related_set=pset1) |
3061 | + pset_found = getUtility(IPackagesetSet).getByName( |
3062 | + 'kernel', distroseries=self.distroseries_experimental) |
3063 | + self.assertEqual(pset2, pset_found) |
3064 | + |
3065 | + |
3066 | +class TestPackageset(TestCaseWithFactory): |
3067 | + |
3068 | + layer = LaunchpadZopelessLayer |
3069 | + |
3070 | + def setUp(self): |
3071 | + """Setup a distribution with multiple distroseries.""" |
3072 | + super(TestPackageset, self).setUp() |
3073 | + self.distribution = getUtility(IDistributionSet).getByName( |
3074 | + 'ubuntu') |
3075 | + self.distroseries_current = self.distribution.currentseries |
3076 | + self.distroseries_experimental = self.factory.makeDistroRelease( |
3077 | + distribution = self.distribution, name="experimental", |
3078 | + status=DistroSeriesStatus.EXPERIMENTAL) |
3079 | + self.distroseries_experimental2 = self.factory.makeDistroRelease( |
3080 | + distribution = self.distribution, name="experimental2", |
3081 | + status=DistroSeriesStatus.EXPERIMENTAL) |
3082 | + |
3083 | + self.person1 = self.factory.makePerson( |
3084 | + name='hacker', displayname=u'Happy Hacker') |
3085 | + |
3086 | + self.packageset_set = getUtility(IPackagesetSet) |
3087 | + |
3088 | + def test_no_related_sets(self): |
3089 | + # If the package set is the only one in the group the result set |
3090 | + # returned by relatedSets() is empty. |
3091 | + packageset = self.packageset_set.new( |
3092 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3093 | + |
3094 | + self.failUnlessEqual(packageset.relatedSets().count(), 0) |
3095 | + |
3096 | + def test_related_set_found(self): |
3097 | + # Creating a new package set while specifying a `related_set` should |
3098 | + # have the effect that the former ends up in the same group as the |
3099 | + # latter. |
3100 | + |
3101 | + # The original package set. |
3102 | + pset1 = self.packageset_set.new( |
3103 | + u'kernel', u'Contains all OS kernel packages', self.person1) |
3104 | + |
3105 | + # A related package set. |
3106 | + pset2 = self.packageset_set.new( |
3107 | + u'kernel', u'A related package set.', self.person1, |
3108 | + distroseries=self.distroseries_experimental, related_set=pset1) |
3109 | + self.assertEqual(pset1.packagesetgroup, pset2.packagesetgroup) |
3110 | + |
3111 | + # An unrelated package set with the same name. |
3112 | + pset3 = self.packageset_set.new( |
3113 | + u'kernel', u'Unrelated package set.', self.person1, |
3114 | + distroseries=self.distroseries_experimental2) |
3115 | + self.assertNotEqual(pset2.packagesetgroup, pset3.packagesetgroup) |
3116 | + |
3117 | + # Make sure 'pset2' is related to 'pset1'. |
3118 | + related = pset1.relatedSets() |
3119 | + self.assertEqual(related.count(), 1) |
3120 | + self.assertEqual(related[0], pset2) |
3121 | + |
3122 | + # And the other way around .. |
3123 | + related = pset2.relatedSets() |
3124 | + self.assertEqual(related.count(), 1) |
3125 | + self.assertEqual(related[0], pset1) |
3126 | + |
3127 | + # Unsurprisingly, the unrelated package set is not associated with any |
3128 | + # other package set. |
3129 | + self.failUnlessEqual(pset3.relatedSets().count(), 0) |
3130 | |
3131 | === modified file 'scripts/ftpmaster-tools/_syncorigins.py' |
3132 | --- scripts/ftpmaster-tools/_syncorigins.py 2009-07-23 02:33:14 +0000 |
3133 | +++ scripts/ftpmaster-tools/_syncorigins.py 2009-11-03 18:13:42 +0000 |
3134 | @@ -12,7 +12,7 @@ |
3135 | "debian": { |
3136 | "name": "Debian", |
3137 | "url": "http://ftp.debian.org/debian/", |
3138 | - "default suite": "unstable", |
3139 | + "default suite": "testing", |
3140 | "default component": "main", |
3141 | "dsc": "must be signed and valid" |
3142 | }, |
3143 | |
3144 | === modified file 'utilities/pgmassacre.py' |
3145 | --- utilities/pgmassacre.py 2009-06-24 20:15:50 +0000 |
3146 | +++ utilities/pgmassacre.py 2009-11-03 18:13:41 +0000 |
3147 | @@ -11,6 +11,7 @@ |
3148 | |
3149 | # Nothing but system installed libraries - this script sometimes |
3150 | # gets installed standalone with no Launchpad tree available. |
3151 | +from distutils.version import LooseVersion |
3152 | import sys |
3153 | import time |
3154 | import psycopg2 |
3155 | @@ -184,12 +185,20 @@ |
3156 | error_msg = None |
3157 | con = connect() |
3158 | con.set_isolation_level(0) # Autocommit required for CREATE DATABASE. |
3159 | + create_db_cmd = """ |
3160 | + CREATE DATABASE %s WITH ENCODING='UTF8' TEMPLATE=%s |
3161 | + """ % (database, template) |
3162 | + # 8.4 allows us to create empty databases with a different locale |
3163 | + # to template1 by using the template0 database as a template. |
3164 | + # We make use of this feature so we don't have to care what locale |
3165 | + # was used to create the database cluster rather than requiring it |
3166 | + # to be rebuilt in the C locale. |
3167 | + if pg_version >= LooseVersion("8.4.0") and template == "template0": |
3168 | + create_db_cmd += "LC_COLLATE='C' LC_CTYPE='C'" |
3169 | while now < start + 20: |
3170 | cur = con.cursor() |
3171 | try: |
3172 | - cur.execute( |
3173 | - "CREATE DATABASE %s WITH ENCODING='UTF8' TEMPLATE=%s" |
3174 | - % (database, template)) |
3175 | + cur.execute(create_db_cmd) |
3176 | con.close() |
3177 | return 0 |
3178 | except psycopg2.Error, exception: |
3179 | @@ -219,6 +228,7 @@ |
3180 | |
3181 | |
3182 | options = None |
3183 | +pg_version = None # LooseVersion - Initialized in main() |
3184 | |
3185 | |
3186 | def main(): |
3187 | @@ -243,6 +253,12 @@ |
3188 | |
3189 | con = connect() |
3190 | cur = con.cursor() |
3191 | + |
3192 | + # Store the database version for version specific code. |
3193 | + global pg_version |
3194 | + cur.execute("show server_version") |
3195 | + pg_version = LooseVersion(cur.fetchone()[0]) |
3196 | + |
3197 | # Ensure the template database exists. |
3198 | if options.template is not None: |
3199 | cur.execute( |
Summary
-------
In a previous branch, I removed the stylesheet for a.info, since it
was interfering with the "info" class for sprites. This broke a couple
of links. This was corrected by setting class="sprite info".
See the "All downloads" link on https:/ /edge.launchpad .net/bzr
BTW, I also merged devel into db-devel, so that I won't have
to worry about conflicts when pqm is finally working.
Demo and Q/A
------------
* Open https:/ /launchpad. dev/firefox
The other page where this occurs is difficult to view, since
you must be subscribed to a Private PPA. You can subscribe
the name16 (foo.bar) user on launchpad.dev with the following
sql statements.
UPDATE Archive SET private = TRUE, buildd_secret = 'foo' WHERE id = 13;
INSERT INTO ArchiveSubscriber (archive, registrant, subscriber, status)
VALUES (13, 1, 16, 1);
INSERT INTO ArchiveAuthToken (archive, person, token)
VALUES (13, 16, 'asdf');
* https:/ /launchpad. dev/~name16/ +archivesubscri ptions