Merge lp:~cjwatson/launchpad/codeimport-git-configure-code into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18260
Proposed branch: lp:~cjwatson/launchpad/codeimport-git-configure-code
Merge into: lp:launchpad
Prerequisite: lp:~cjwatson/launchpad/codeimport-git-refactor-name-validation
Diff against target: 745 lines (+335/-120)
6 files modified
lib/lp/code/javascript/productseries-setbranch.js (+26/-16)
lib/lp/code/javascript/tests/test_productseries-setbranch.html (+1/-1)
lib/lp/code/templates/configure-code.pt (+41/-14)
lib/lp/registry/browser/product.py (+197/-83)
lib/lp/registry/browser/tests/productseries-setbranch-view.txt (+3/-3)
lib/lp/registry/browser/tests/test_product.py (+67/-3)
To merge this branch: bzr merge lp:~cjwatson/launchpad/codeimport-git-configure-code
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+308577@code.launchpad.net

Commit message

Extend Product:+configure-code to be able to create Git-to-Git code imports.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/code/javascript/productseries-setbranch.js'
--- lib/lp/code/javascript/productseries-setbranch.js 2015-06-25 07:39:40 +0000
+++ lib/lp/code/javascript/productseries-setbranch.js 2016-11-11 14:59:36 +0000
@@ -87,32 +87,39 @@
87 module.onclick_branch_type = function(e) {87 module.onclick_branch_type = function(e) {
88 /* Which branch type radio button was selected? */88 /* Which branch type radio button was selected? */
89 var selectedRCS = module._get_selected_rcs();89 var selectedRCS = module._get_selected_rcs();
90 var types = document.getElementsByName('field.branch_type');90 var type_node = Y.one('input[name="field.branch_type"]:checked');
91 var type = 'None';91 var type = (type_node === null) ? 'None' : type_node.get('value');
92 var i;
93 for (i = 0; i < types.length; i++) {
94 if (types[i].checked) {
95 type = types[i].value;
96 break;
97 }
98 }
99 // Linked92 // Linked
100 module.set_enabled('field.branch_location', type === 'link-lp-bzr');93 module.set_enabled('field.branch_location', type === 'link-lp');
101 module.set_enabled('field.branch_name', type !== 'link-lp-bzr');94 module.set_enabled('field.branch_name', type !== 'link-lp');
102 module.set_enabled('field.branch_owner', type !== 'link-lp-bzr');95 module.set_enabled('field.branch_owner', type !== 'link-lp');
103 // New, empty branch.
104 // Import96 // Import
105 var is_external = (type === 'import-external');97 var is_external = (type === 'import-external');
106 module.set_enabled('field.repo_url', is_external);98 module.set_enabled('field.repo_url', is_external);
107 module.set_enabled('field.cvs_module',99 module.set_enabled('field.cvs_module',
108 (is_external & selectedRCS === 'CVS'));100 (is_external & selectedRCS === 'CVS'));
109 var rcs_types = module._rcs_types();101 var rcs_types = module._rcs_types();
110 var j;102 var i;
111 for (j = 0; j < rcs_types.length; j++) {103 for (i = 0; i < rcs_types.length; i++) {
112 rcs_types[j].disabled = !is_external;104 rcs_types[i].disabled = !is_external;
113 }105 }
114 };106 };
115107
108 module.onclick_git_repository_type = function(e) {
109 /* Which Git repository type radio button was selected? */
110 var type_node = Y.one(
111 'input[name="field.git_repository_type"]:checked');
112 var type = (type_node === null) ? 'None' : type_node.get('value');
113 // Linked
114 module.set_enabled(
115 'field.git_repository_location', type === 'link-lp');
116 module.set_enabled('field.git_repository_name', type !== 'link-lp');
117 module.set_enabled('field.git_repository_owner', type !== 'link-lp');
118 // Import
119 var is_external = (type === 'import-external');
120 module.set_enabled('field.git_repository_url', is_external);
121 };
122
116 module.setup = function() {123 module.setup = function() {
117 Y.all('input[name="field.rcs_type"]').on(124 Y.all('input[name="field.rcs_type"]').on(
118 'click', module.onclick_rcs_type);125 'click', module.onclick_rcs_type);
@@ -120,6 +127,8 @@
120 'click', module.onclick_branch_type);127 'click', module.onclick_branch_type);
121 Y.all('input[name="field.default_vcs"]').on(128 Y.all('input[name="field.default_vcs"]').on(
122 'click', module.onclick_default_vcs);129 'click', module.onclick_default_vcs);
130 Y.all('input[name="field.git_repository_type"]').on(
131 'click', module.onclick_git_repository_type);
123132
124 // Set the initial state.133 // Set the initial state.
125 module.onclick_rcs_type();134 module.onclick_rcs_type();
@@ -128,6 +137,7 @@
128 if (document.getElementById('default_vcs')) {137 if (document.getElementById('default_vcs')) {
129 module.setup_expanders();138 module.setup_expanders();
130 module.onclick_default_vcs();139 module.onclick_default_vcs();
140 module.onclick_git_repository_type();
131 }141 }
132 };142 };
133143
134144
=== modified file 'lib/lp/code/javascript/tests/test_productseries-setbranch.html'
--- lib/lp/code/javascript/tests/test_productseries-setbranch.html 2012-10-26 09:54:28 +0000
+++ lib/lp/code/javascript/tests/test_productseries-setbranch.html 2016-11-11 14:59:36 +0000
@@ -61,7 +61,7 @@
61 <label>61 <label>
62 <input class="radioType" checked="checked"62 <input class="radioType" checked="checked"
63 id="field.branch_type.0"63 id="field.branch_type.0"
64 name="field.branch_type" type="radio" value="link-lp-bzr" />64 name="field.branch_type" type="radio" value="link-lp" />
65 Link to a Bazaar branch already in Launchpad65 Link to a Bazaar branch already in Launchpad
66 </label>66 </label>
67 <table>67 <table>
6868
=== modified file 'lib/lp/code/templates/configure-code.pt'
--- lib/lp/code/templates/configure-code.pt 2016-04-28 02:09:21 +0000
+++ lib/lp/code/templates/configure-code.pt 2016-11-11 14:59:36 +0000
@@ -51,10 +51,10 @@
51 <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>51 <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
52 </div>52 </div>
5353
54 <h3>Link or import an existing branch</h3>
54 <table id="form_bzr" class="form">55 <table id="form_bzr" class="form">
55 <tr>56 <tr>
56 <td>57 <td>
57 <h3>Link or import an existing branch</h3>
58 <label tal:replace="structure view/branch_type_link">58 <label tal:replace="structure view/branch_type_link">
59 Link to a Bazaar branch already on Launchpad59 Link to a Bazaar branch already on Launchpad
60 </label>60 </label>
@@ -133,20 +133,47 @@
133 <span class="sprite gitbranch">Git settings</span>133 <span class="sprite gitbranch">Git settings</span>
134 </a>134 </a>
135 <div id="git-expander-content">135 <div id="git-expander-content">
136 <div id="form_git" class="form">136 <div class="push-instructions">
137 <div class="push-instructions">137 <div metal:use-macro="context/@@+configure-code-macros/push-instructions-git"></div>
138 <div metal:use-macro="context/@@+configure-code-macros/push-instructions-git"></div>138 <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
139 <div metal:use-macro="context/@@+configure-code-macros/no-keys"></div>
140 </div>
141
142 <h3>Link an existing repository</h3>
143 <p>Link to an existing Git repository already on Launchpad</p>
144 <table>
145 <tal:widget define="widget nocall:view/widgets/git_repository_location">
146 <metal:block use-macro="context/@@launchpad_form/widget_row" />
147 </tal:widget>
148 </table>
149 </div>139 </div>
140
141 <h3>Link or import an existing repository</h3>
142 <table id="form_git" class="form">
143 <tr>
144 <td>
145 <label tal:replace="structure view/git_repository_type_link">
146 Link to a Git repository already on Launchpad
147 </label>
148 <table class="subordinate">
149 <tal:widget define="widget nocall:view/widgets/git_repository_location">
150 <metal:block use-macro="context/@@launchpad_form/widget_row" />
151 </tal:widget>
152 </table>
153 </td>
154 </tr>
155
156 <tr id="git_mirror"
157 tal:condition="request/features/code.import.git_target">
158 <td>
159 <label tal:replace="structure view/git_repository_type_import">
160 Import a repository hosted somewhere else
161 </label>
162 <table class="subordinate">
163 <tal:widget define="widget nocall:view/widgets/git_repository_name">
164 <metal:block use-macro="context/@@launchpad_form/widget_row" />
165 </tal:widget>
166 <tal:widget define="widget nocall:view/widgets/git_repository_owner">
167 <metal:block use-macro="context/@@launchpad_form/widget_row" />
168 </tal:widget>
169
170 <tal:widget define="widget nocall:view/widgets/git_repository_url">
171 <metal:block use-macro="context/@@launchpad_form/widget_row" />
172 </tal:widget>
173 </table>
174 </td>
175 </tr>
176 </table>
150 </div>177 </div>
151 </div>178 </div>
152 </tal:block>179 </tal:block>
153180
=== modified file 'lib/lp/registry/browser/product.py'
--- lib/lp/registry/browser/product.py 2016-10-15 13:41:19 +0000
+++ lib/lp/registry/browser/product.py 2016-11-11 14:59:36 +0000
@@ -151,14 +151,20 @@
151 RevisionControlSystems,151 RevisionControlSystems,
152 TargetRevisionControlSystems,152 TargetRevisionControlSystems,
153 )153 )
154from lp.code.errors import BranchExists154from lp.code.errors import (
155 BranchExists,
156 GitRepositoryExists,
157 )
155from lp.code.interfaces.branch import IBranch158from lp.code.interfaces.branch import IBranch
156from lp.code.interfaces.branchjob import IRosettaUploadJobSource159from lp.code.interfaces.branchjob import IRosettaUploadJobSource
157from lp.code.interfaces.codeimport import (160from lp.code.interfaces.codeimport import (
158 ICodeImport,161 ICodeImport,
159 ICodeImportSet,162 ICodeImportSet,
160 )163 )
161from lp.code.interfaces.gitrepository import IGitRepositorySet164from lp.code.interfaces.gitrepository import (
165 IGitRepository,
166 IGitRepositorySet,
167 )
162from lp.registry.browser import (168from lp.registry.browser import (
163 add_subscribe_link,169 add_subscribe_link,
164 BaseRdfView,170 BaseRdfView,
@@ -1658,18 +1664,26 @@
1658 return BatchNavigator(decorated_result, self.request)1664 return BatchNavigator(decorated_result, self.request)
16591665
16601666
1661LINK_LP_BZR = 'link-lp-bzr'1667LINK_LP = 'link-lp'
1662IMPORT_EXTERNAL = 'import-external'1668IMPORT_EXTERNAL = 'import-external'
16631669
16641670
1665BRANCH_TYPE_VOCABULARY = SimpleVocabulary((1671BRANCH_TYPE_VOCABULARY = SimpleVocabulary((
1666 SimpleTerm(LINK_LP_BZR, LINK_LP_BZR,1672 SimpleTerm(LINK_LP, LINK_LP,
1667 _("Link to a Bazaar branch already on Launchpad")),1673 _("Link to a Bazaar branch already on Launchpad")),
1668 SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,1674 SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,
1669 _("Import a branch hosted somewhere else")),1675 _("Import a branch hosted somewhere else")),
1670 ))1676 ))
16711677
16721678
1679GIT_REPOSITORY_TYPE_VOCABULARY = SimpleVocabulary((
1680 SimpleTerm(LINK_LP, LINK_LP,
1681 _("Link to a Git repository already on Launchpad")),
1682 SimpleTerm(IMPORT_EXTERNAL, IMPORT_EXTERNAL,
1683 _("Import a Git repository hosted somewhere else")),
1684 ))
1685
1686
1673class SetBranchForm(Interface):1687class SetBranchForm(Interface):
1674 """The fields presented on the form for setting a branch."""1688 """The fields presented on the form for setting a branch."""
16751689
@@ -1696,7 +1710,7 @@
16961710
1697 branch_type = Choice(1711 branch_type = Choice(
1698 title=_('Import type'), vocabulary=BRANCH_TYPE_VOCABULARY,1712 title=_('Import type'), vocabulary=BRANCH_TYPE_VOCABULARY,
1699 description=_("The type of import"), required=True)1713 description=_("Whether the branch is imported"), required=True)
17001714
1701 branch_name = copy_field(1715 branch_name = copy_field(
1702 IBranch['name'], __name__='branch_name', title=_('Branch name'),1716 IBranch['name'], __name__='branch_name', title=_('Branch name'),
@@ -1706,23 +1720,37 @@
1706 IBranch['owner'], __name__='branch_owner', title=_('Branch owner'),1720 IBranch['owner'], __name__='branch_owner', title=_('Branch owner'),
1707 description=_(''), required=True)1721 description=_(''), required=True)
17081722
17091723 default_vcs = Choice(
1710def create_git_fields():1724 title=_("Project VCS"), required=True, vocabulary=VCSType,
1711 return form.Fields(1725 description=_("The version control system for this project."))
1712 Choice(__name__='default_vcs',1726
1713 title=_("Project VCS"),1727 git_repository_location = Choice(
1714 required=True, vocabulary=VCSType,1728 title=_('Git repository'), required=False,
1715 description=_("The version control system for "1729 vocabulary='GitRepositoryRestrictedOnProduct',
1716 "this project.")),1730 description=_(
1717 Choice(__name__='git_repository_location',1731 "The Git repository for this project in Launchpad, "
1718 title=_('Git repository'),1732 "if one exists, in the form: "
1719 required=False,1733 "~user/project-name/+git/repo-name"))
1720 vocabulary='GitRepositoryRestrictedOnProduct',1734
1721 description=_(1735 git_repository_type = Choice(
1722 "The Git repository for this project in Launchpad, "1736 title=_('Import type'), required=True,
1723 "if one exists, in the form: "1737 vocabulary=GIT_REPOSITORY_TYPE_VOCABULARY,
1724 "~user/project-name/+git/repo-name"))1738 description=_('Whether the repository is imported'))
1725 )1739
1740 git_repository_name = copy_field(
1741 IGitRepository['name'], __name__='git_repository_name',
1742 title=_('Git repository name'), description=_(''), required=True)
1743
1744 git_repository_owner = copy_field(
1745 IGitRepository['owner'], __name__='git_repository_owner',
1746 title=_('Git repository owner'), description=_(''), required=True)
1747
1748 git_repository_url = URIField(
1749 title=_('Git repository URL'), required=True,
1750 description=_('The URL of the Git repository.'),
1751 allowed_schemes=["git", "http", "https"],
1752 allow_userinfo=True, allow_port=True, allow_query=False,
1753 allow_fragment=False, trailing_slash=False)
17261754
17271755
1728class ProductSetBranchView(ReturnToReferrerMixin, LaunchpadFormView,1756class ProductSetBranchView(ReturnToReferrerMixin, LaunchpadFormView,
@@ -1740,6 +1768,7 @@
1740 custom_widget('rcs_type', LaunchpadRadioWidget)1768 custom_widget('rcs_type', LaunchpadRadioWidget)
1741 custom_widget('branch_type', LaunchpadRadioWidget)1769 custom_widget('branch_type', LaunchpadRadioWidget)
1742 custom_widget('default_vcs', LaunchpadRadioWidget)1770 custom_widget('default_vcs', LaunchpadRadioWidget)
1771 custom_widget('git_repository_type', LaunchpadRadioWidget)
17431772
1744 errors_in_action = False1773 errors_in_action = False
1745 is_series = False1774 is_series = False
@@ -1754,8 +1783,9 @@
1754 return dict(1783 return dict(
1755 rcs_type=RevisionControlSystems.BZR,1784 rcs_type=RevisionControlSystems.BZR,
1756 default_vcs=(self.context.pillar.inferred_vcs or VCSType.BZR),1785 default_vcs=(self.context.pillar.inferred_vcs or VCSType.BZR),
1757 branch_type=LINK_LP_BZR,1786 branch_type=LINK_LP,
1758 branch_location=self.series.branch,1787 branch_location=self.series.branch,
1788 git_repository_type=LINK_LP,
1759 git_repository_location=repository_set.getDefaultRepository(1789 git_repository_location=repository_set.getDefaultRepository(
1760 self.context.pillar))1790 self.context.pillar))
17611791
@@ -1781,8 +1811,11 @@
1781 def setUpFields(self):1811 def setUpFields(self):
1782 """See `LaunchpadFormView`."""1812 """See `LaunchpadFormView`."""
1783 super(ProductSetBranchView, self).setUpFields()1813 super(ProductSetBranchView, self).setUpFields()
1784 if not self.is_series:1814 if self.is_series:
1785 self.form_fields = (self.form_fields + create_git_fields())1815 self.form_fields = self.form_fields.omit(
1816 'default_vcs', 'git_repository_location',
1817 'git_repository_type', 'git_repository_name',
1818 'git_repository_owner', 'git_repository_url')
17861819
1787 def setUpWidgets(self):1820 def setUpWidgets(self):
1788 """See `LaunchpadFormView`."""1821 """See `LaunchpadFormView`."""
@@ -1803,11 +1836,9 @@
1803 widget = self.widgets['branch_type']1836 widget = self.widgets['branch_type']
1804 current_value = widget._getFormValue()1837 current_value = widget._getFormValue()
1805 vocab = widget.vocabulary1838 vocab = widget.vocabulary
18061839 self.branch_type_link, self.branch_type_import = [
1807 (self.branch_type_link,
1808 self.branch_type_import) = [
1809 render_radio_widget_part(widget, value, current_value)1840 render_radio_widget_part(widget, value, current_value)
1810 for value in (LINK_LP_BZR, IMPORT_EXTERNAL)]1841 for value in (LINK_LP, IMPORT_EXTERNAL)]
18111842
1812 if not self.is_series:1843 if not self.is_series:
1813 widget = self.widgets['default_vcs']1844 widget = self.widgets['default_vcs']
@@ -1818,14 +1849,21 @@
1818 self.default_vcs_bzr = render_radio_widget_part(1849 self.default_vcs_bzr = render_radio_widget_part(
1819 widget, vocab.BZR, current_value, 'Bazaar')1850 widget, vocab.BZR, current_value, 'Bazaar')
18201851
1852 widget = self.widgets['git_repository_type']
1853 current_value = widget._getFormValue()
1854 vocab = widget.vocabulary
1855 self.git_repository_type_link, self.git_repository_type_import = [
1856 render_radio_widget_part(widget, value, current_value)
1857 for value in (LINK_LP, IMPORT_EXTERNAL)]
1858
1821 def _validateLinkLpBzr(self, data):1859 def _validateLinkLpBzr(self, data):
1822 """Validate data for link-lp-bzr case."""1860 """Validate data for link-lp bzr case."""
1823 if 'branch_location' not in data:1861 if 'branch_location' not in data:
1824 self.setFieldError(1862 self.setFieldError(
1825 'branch_location', 'The branch location must be set.')1863 'branch_location', 'The branch location must be set.')
18261864
1827 def _validateLinkLpGit(self, data):1865 def _validateLinkLpGit(self, data):
1828 """Validate data for link-lp-git case."""1866 """Validate data for link-lp git case."""
1829 if data.get('git_repository_location'):1867 if data.get('git_repository_location'):
1830 repo = data.get('git_repository_location')1868 repo = data.get('git_repository_location')
1831 if not repo:1869 if not repo:
@@ -1833,8 +1871,8 @@
1833 'git_repository_location',1871 'git_repository_location',
1834 'The repository does not exist.')1872 'The repository does not exist.')
18351873
1836 def _validateImportExternal(self, data):1874 def _validateImportExternalBzr(self, data):
1837 """Validate data for import external case."""1875 """Validate data for import-external bzr case."""
1838 rcs_type = data.get('rcs_type')1876 rcs_type = data.get('rcs_type')
1839 repo_url = data.get('repo_url')1877 repo_url = data.get('repo_url')
18401878
@@ -1864,15 +1902,41 @@
1864 elif rcs_type == RevisionControlSystems.CVS:1902 elif rcs_type == RevisionControlSystems.CVS:
1865 if 'cvs_module' not in data:1903 if 'cvs_module' not in data:
1866 self.setFieldError('cvs_module', 'The CVS module must be set.')1904 self.setFieldError('cvs_module', 'The CVS module must be set.')
1867 self._validateBranch(data)
18681905
1869 def _validateBranch(self, data):
1870 """Validate that branch name and owner are set."""
1871 if 'branch_name' not in data:1906 if 'branch_name' not in data:
1872 self.setFieldError('branch_name', 'The branch name must be set.')1907 self.setFieldError('branch_name', 'The branch name must be set.')
1873 if 'branch_owner' not in data:1908 if 'branch_owner' not in data:
1874 self.setFieldError('branch_owner', 'The branch owner must be set.')1909 self.setFieldError('branch_owner', 'The branch owner must be set.')
18751910
1911 def _validateImportExternalGit(self, data):
1912 """Validate data for import-external git case."""
1913 git_repository_url = data.get('git_repository_url')
1914
1915 # Private teams are forbidden from owning code imports.
1916 git_repository_owner = data.get('git_repository_owner')
1917 if git_repository_owner is not None and git_repository_owner.private:
1918 self.setFieldError(
1919 'git_repository_owner',
1920 'Private teams are forbidden from owning external imports.')
1921
1922 if git_repository_url is None:
1923 self.setFieldError(
1924 'git_repository_url',
1925 'You must set the external repository URL.')
1926 else:
1927 reason = validate_import_url(
1928 git_repository_url, RevisionControlSystems.GIT,
1929 TargetRevisionControlSystems.GIT)
1930 if reason:
1931 self.setFieldError('git_repository_url', reason)
1932
1933 if 'git_repository_name' not in data:
1934 self.setFieldError(
1935 'git_repository_name', 'The repository name must be set.')
1936 if 'git_repository_owner' not in data:
1937 self.setFieldError(
1938 'git_repository_owner', 'The repository owner must be set.')
1939
1876 def _setRequired(self, names, value):1940 def _setRequired(self, names, value):
1877 """Mark the widget field as optional."""1941 """Mark the widget field as optional."""
1878 for name in names:1942 for name in names:
@@ -1897,11 +1961,25 @@
18971961
1898 def validate_widgets(self, data, names=None):1962 def validate_widgets(self, data, names=None):
1899 """See `LaunchpadFormView`."""1963 """See `LaunchpadFormView`."""
1900 names = ['branch_type', 'rcs_type', 'default_vcs']1964 names = [
1965 'branch_type', 'rcs_type', 'default_vcs', 'git_repository_type']
1901 super(ProductSetBranchView, self).validate_widgets(data, names)1966 super(ProductSetBranchView, self).validate_widgets(data, names)
1967
1968 if not self.is_series:
1969 git_repository_type = data.get('git_repository_type')
1970 if git_repository_type == LINK_LP:
1971 # Mark other widgets as non-required.
1972 self._setRequired(['git_repository_url', 'git_repository_name',
1973 'git_repository_owner'], False)
1974 elif git_repository_type == IMPORT_EXTERNAL:
1975 # The repository location is not required for validation.
1976 self._setRequired(['git_repository_location'], False)
1977 else:
1978 raise AssertionError(
1979 "Unknown Git repository type %s" % git_repository_type)
1980
1902 branch_type = data.get('branch_type')1981 branch_type = data.get('branch_type')
19031982 if branch_type == LINK_LP:
1904 if branch_type == LINK_LP_BZR:
1905 # Mark other widgets as non-required.1983 # Mark other widgets as non-required.
1906 self._setRequired(['rcs_type', 'repo_url', 'cvs_module',1984 self._setRequired(['rcs_type', 'repo_url', 'cvs_module',
1907 'branch_name', 'branch_owner'], False)1985 'branch_name', 'branch_owner'], False)
@@ -1918,6 +1996,7 @@
1918 self._setRequired(['cvs_module'], True)1996 self._setRequired(['cvs_module'], True)
1919 else:1997 else:
1920 raise AssertionError("Unknown branch type %s" % branch_type)1998 raise AssertionError("Unknown branch type %s" % branch_type)
1999
1921 # Perform full validation now.2000 # Perform full validation now.
1922 super(ProductSetBranchView, self).validate_widgets(data)2001 super(ProductSetBranchView, self).validate_widgets(data)
19232002
@@ -1927,13 +2006,22 @@
1927 # continue as we'd likely just override the errors reported there.2006 # continue as we'd likely just override the errors reported there.
1928 if len(self.errors) > 0:2007 if len(self.errors) > 0:
1929 return2008 return
2009
2010 if not self.is_series:
2011 git_repository_type = data.get('git_repository_type')
2012 if git_repository_type == LINK_LP:
2013 self._validateLinkLpGit(data)
2014 elif git_repository_type == IMPORT_EXTERNAL:
2015 self._validateImportExternalGit(data)
2016 else:
2017 raise AssertionError(
2018 "Unknown Git repository type %s" % git_repository_type)
2019
1930 branch_type = data.get('branch_type')2020 branch_type = data.get('branch_type')
1931 if not self.is_series:2021 if branch_type == LINK_LP:
1932 self._validateLinkLpGit(data)
1933 if branch_type == IMPORT_EXTERNAL:
1934 self._validateImportExternal(data)
1935 elif branch_type == LINK_LP_BZR:
1936 self._validateLinkLpBzr(data)2022 self._validateLinkLpBzr(data)
2023 elif branch_type == IMPORT_EXTERNAL:
2024 self._validateImportExternalBzr(data)
1937 else:2025 else:
1938 raise AssertionError("Unknown branch type %s" % branch_type)2026 raise AssertionError("Unknown branch type %s" % branch_type)
19392027
@@ -1959,54 +2047,80 @@
1959 if default_vcs:2047 if default_vcs:
1960 self.context.vcs = default_vcs2048 self.context.vcs = default_vcs
19612049
1962 repo = data.get('git_repository_location')2050 git_repository_type = data.get('git_repository_type')
1963 getUtility(IGitRepositorySet).setDefaultRepository(2051
1964 self.context, repo)2052 if git_repository_type == LINK_LP:
1965 if branch_type == LINK_LP_BZR:2053 repo = data.get('git_repository_location')
2054 repository_set = getUtility(IGitRepositorySet)
2055 if repository_set.getDefaultRepository(self.context) != repo:
2056 repository_set.setDefaultRepository(self.context, repo)
2057 self.add_update_notification()
2058 elif git_repository_type == IMPORT_EXTERNAL:
2059 name = data.get('git_repository_name')
2060 owner = data.get('git_repository_owner')
2061 url = data.get('git_repository_url')
2062 try:
2063 code_import = getUtility(ICodeImportSet).new(
2064 owner=owner,
2065 registrant=self.user,
2066 context=self.context,
2067 branch_name=name,
2068 rcs_type=RevisionControlSystems.GIT,
2069 target_rcs_type=TargetRevisionControlSystems.GIT,
2070 url=url)
2071 except GitRepositoryExists as e:
2072 self._setBranchExists(
2073 e.existing_repository, 'git_repository_name')
2074 self.abort_update()
2075 return
2076 getUtility(IGitRepositorySet).setDefaultRepository(
2077 self.context, code_import.git_repository)
2078 self.request.response.addInfoNotification(
2079 'Code import created and repository set as default.')
2080 else:
2081 raise UnexpectedFormData(git_repository_type)
2082
2083 if branch_type == LINK_LP:
1966 branch_location = data.get('branch_location')2084 branch_location = data.get('branch_location')
1967 if branch_location != self.series.branch:2085 if branch_location != self.series.branch:
1968 self.series.branch = branch_location2086 self.series.branch = branch_location
1969 # Request an initial upload of translation files.2087 # Request an initial upload of translation files.
1970 getUtility(IRosettaUploadJobSource).create(2088 getUtility(IRosettaUploadJobSource).create(
1971 self.series.branch, NULL_REVISION)2089 self.series.branch, NULL_REVISION)
1972 else:2090 self.add_update_notification()
1973 self.series.branch = branch_location2091 elif branch_type == IMPORT_EXTERNAL:
1974 self.add_update_notification()
1975 else:
1976 branch_name = data.get('branch_name')2092 branch_name = data.get('branch_name')
1977 branch_owner = data.get('branch_owner')2093 branch_owner = data.get('branch_owner')
19782094 rcs_type = data.get('rcs_type')
1979 if branch_type == IMPORT_EXTERNAL:2095 if rcs_type == RevisionControlSystems.CVS:
1980 rcs_type = data.get('rcs_type')2096 cvs_root = data.get('repo_url')
1981 if rcs_type == RevisionControlSystems.CVS:2097 cvs_module = data.get('cvs_module')
1982 cvs_root = data.get('repo_url')2098 url = None
1983 cvs_module = data.get('cvs_module')
1984 url = None
1985 else:
1986 cvs_root = None
1987 cvs_module = None
1988 url = data.get('repo_url')
1989 rcs_item = RevisionControlSystems.items[rcs_type.name]
1990 try:
1991 code_import = getUtility(ICodeImportSet).new(
1992 owner=branch_owner,
1993 registrant=self.user,
1994 context=self.context,
1995 branch_name=branch_name,
1996 rcs_type=rcs_item,
1997 target_rcs_type=TargetRevisionControlSystems.BZR,
1998 url=url,
1999 cvs_root=cvs_root,
2000 cvs_module=cvs_module)
2001 except BranchExists as e:
2002 self._setBranchExists(e.existing_branch, 'branch_name')
2003 self.abort_update()
2004 return
2005 self.series.branch = code_import.branch
2006 self.request.response.addInfoNotification(
2007 'Code import created and branch linked to the series.')
2008 else:2099 else:
2009 raise UnexpectedFormData(branch_type)2100 cvs_root = None
2101 cvs_module = None
2102 url = data.get('repo_url')
2103 rcs_item = RevisionControlSystems.items[rcs_type.name]
2104 try:
2105 code_import = getUtility(ICodeImportSet).new(
2106 owner=branch_owner,
2107 registrant=self.user,
2108 context=self.context,
2109 branch_name=branch_name,
2110 rcs_type=rcs_item,
2111 target_rcs_type=TargetRevisionControlSystems.BZR,
2112 url=url,
2113 cvs_root=cvs_root,
2114 cvs_module=cvs_module)
2115 except BranchExists as e:
2116 self._setBranchExists(e.existing_branch, 'branch_name')
2117 self.abort_update()
2118 return
2119 self.series.branch = code_import.branch
2120 self.request.response.addInfoNotification(
2121 'Code import created and branch linked to the series.')
2122 else:
2123 raise UnexpectedFormData(branch_type)
20102124
20112125
2012class ProductRdfView(BaseRdfView):2126class ProductRdfView(BaseRdfView):
20132127
=== modified file 'lib/lp/registry/browser/tests/productseries-setbranch-view.txt'
--- lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2016-10-15 13:41:19 +0000
+++ lib/lp/registry/browser/tests/productseries-setbranch-view.txt 2016-11-11 14:59:36 +0000
@@ -39,7 +39,7 @@
39must be provided.39must be provided.
4040
41 >>> form = {41 >>> form = {
42 ... 'field.branch_type': 'link-lp-bzr',42 ... 'field.branch_type': 'link-lp',
43 ... 'field.actions.update': 'Update',43 ... 'field.actions.update': 'Update',
44 ... }44 ... }
45 >>> view = create_initialized_view(45 >>> view = create_initialized_view(
@@ -52,7 +52,7 @@
52validation error.52validation error.
5353
54 >>> form = {54 >>> form = {
55 ... 'field.branch_type': 'link-lp-bzr',55 ... 'field.branch_type': 'link-lp',
56 ... 'field.branch_location': 'foo',56 ... 'field.branch_location': 'foo',
57 ... 'field.actions.update': 'Update',57 ... 'field.actions.update': 'Update',
58 ... }58 ... }
@@ -69,7 +69,7 @@
69 >>> branch = factory.makeBranch(69 >>> branch = factory.makeBranch(
70 ... name='impala-branch', owner=driver, product=product)70 ... name='impala-branch', owner=driver, product=product)
71 >>> form = {71 >>> form = {
72 ... 'field.branch_type': 'link-lp-bzr',72 ... 'field.branch_type': 'link-lp',
73 ... 'field.branch_location': branch.unique_name,73 ... 'field.branch_location': branch.unique_name,
74 ... 'field.actions.update': 'Update',74 ... 'field.actions.update': 'Update',
75 ... }75 ... }
7676
=== modified file 'lib/lp/registry/browser/tests/test_product.py'
--- lib/lp/registry/browser/tests/test_product.py 2016-09-19 11:47:33 +0000
+++ lib/lp/registry/browser/tests/test_product.py 2016-11-11 14:59:36 +0000
@@ -30,7 +30,10 @@
30 InformationType,30 InformationType,
31 ServiceUsage,31 ServiceUsage,
32 )32 )
33from lp.code.enums import RevisionControlSystems
34from lp.code.interfaces.codeimport import CODE_IMPORT_GIT_TARGET_FEATURE_FLAG
33from lp.code.interfaces.gitrepository import IGitRepositorySet35from lp.code.interfaces.gitrepository import IGitRepositorySet
36from lp.code.tests.helpers import GitHostingFixture
34from lp.registry.browser.product import (37from lp.registry.browser.product import (
35 ProjectAddStepOne,38 ProjectAddStepOne,
36 ProjectAddStepTwo,39 ProjectAddStepTwo,
@@ -47,6 +50,7 @@
47from lp.registry.model.product import Product50from lp.registry.model.product import Product
48from lp.services.config import config51from lp.services.config import config
49from lp.services.database.interfaces import IStore52from lp.services.database.interfaces import IStore
53from lp.services.features.testing import FeatureFixture
50from lp.services.webapp.publisher import canonical_url54from lp.services.webapp.publisher import canonical_url
51from lp.services.webapp.vhosts import allvhosts55from lp.services.webapp.vhosts import allvhosts
52from lp.testing import (56from lp.testing import (
@@ -914,7 +918,7 @@
914 # control defaults to empty.918 # control defaults to empty.
915 project = self.factory.makeProduct()919 project = self.factory.makeProduct()
916 browser = self.getBrowser(project, '+configure-code')920 browser = self.getBrowser(project, '+configure-code')
917 self.assertEqual('', browser.getControl('Git repository').value)921 self.assertEqual('', browser.getControl('Git repository:').value)
918922
919 def test_initial_git_repository(self):923 def test_initial_git_repository(self):
920 # If a project has a default Git repository, its "Git repository"924 # If a project has a default Git repository, its "Git repository"
@@ -926,14 +930,16 @@
926 unique_name = repo.unique_name930 unique_name = repo.unique_name
927 browser = self.getBrowser(project, '+configure-code')931 browser = self.getBrowser(project, '+configure-code')
928 self.assertEqual(932 self.assertEqual(
929 unique_name, browser.getControl('Git repository').value)933 unique_name, browser.getControl('Git repository:').value)
930934
931 def test_link_existing_git_repository(self):935 def test_link_existing_git_repository(self):
932 repo = removeSecurityProxy(self.factory.makeGitRepository(936 repo = removeSecurityProxy(self.factory.makeGitRepository(
933 target=self.factory.makeProduct()))937 target=self.factory.makeProduct()))
934 browser = self.getBrowser(repo.project, '+configure-code')938 browser = self.getBrowser(repo.project, '+configure-code')
935 browser.getControl('Git', index=0).click()939 browser.getControl('Git', index=0).click()
936 browser.getControl('Git repository').value = repo.shortened_path940 self.assertTrue(browser.getControl(
941 'Link to a Git repository already on Launchpad').selected)
942 browser.getControl('Git repository:').value = repo.shortened_path
937 browser.getControl('Update').click()943 browser.getControl('Update').click()
938944
939 tag = Tag(945 tag = Tag(
@@ -941,6 +947,64 @@
941 text='Project settings updated.')947 text='Project settings updated.')
942 self.assertThat(browser.contents, HTMLContains(tag))948 self.assertThat(browser.contents, HTMLContains(tag))
943949
950 def test_import_git_repository_requires_feature_flag(self):
951 project = self.factory.makeProduct()
952 browser = self.getBrowser(project, '+configure-code')
953 self.assertRaises(
954 LookupError, browser.getControl,
955 'Import a Git repository hosted somewhere else')
956
957 def test_import_git_repository(self):
958 self.useFixture(
959 FeatureFixture({CODE_IMPORT_GIT_TARGET_FEATURE_FLAG: u'on'}))
960 self.useFixture(GitHostingFixture())
961 owner = self.factory.makePerson()
962 project = self.factory.makeProduct(owner=owner)
963 browser = self.getBrowser(project, '+configure-code')
964 browser.getControl('Git', index=0).click()
965 browser.getControl(
966 'Import a Git repository hosted somewhere else').click()
967 browser.getControl('Git repository name').value = 'imported'
968 browser.getControl('Git repository URL').value = (
969 'https://git.example.org/imported')
970 browser.getControl('Update').click()
971
972 tag = Tag(
973 'success-div', 'div', attrs={'class': 'informational message'},
974 text='Code import created and repository set as default.')
975 self.assertThat(browser.contents, HTMLContains(tag))
976 login_person(owner)
977 repo = getUtility(IGitRepositorySet).getDefaultRepository(project)
978 self.assertIsNotNone(repo.code_import)
979 self.assertEqual(RevisionControlSystems.GIT, repo.code_import.rcs_type)
980 self.assertEqual(
981 'https://git.example.org/imported', repo.code_import.url)
982
983 def test_import_git_repository_bad_scheme(self):
984 self.useFixture(
985 FeatureFixture({CODE_IMPORT_GIT_TARGET_FEATURE_FLAG: u'on'}))
986 owner = self.factory.makePerson()
987 project = self.factory.makeProduct(owner=owner)
988 browser = self.getBrowser(project, '+configure-code')
989 browser.getControl('Git', index=0).click()
990 browser.getControl(
991 'Import a Git repository hosted somewhere else').click()
992 browser.getControl('Git repository name').value = 'imported'
993 browser.getControl('Git repository URL').value = (
994 'svn://svn.example.org/imported')
995 browser.getControl('Update').click()
996
997 tag = Tag(
998 'error', 'div', attrs={'class': 'message'},
999 text=(
1000 'The URI scheme &quot;svn&quot; is not allowed. '
1001 'Only URIs with the following schemes may be used: '
1002 'git, http, https'))
1003 self.assertThat(browser.contents, HTMLContains(tag))
1004 login_person(owner)
1005 self.assertIsNone(
1006 getUtility(IGitRepositorySet).getDefaultRepository(project))
1007
944 def test_editsshkeys_link_if_no_keys_registered(self):1008 def test_editsshkeys_link_if_no_keys_registered(self):
945 project = self.factory.makeProduct()1009 project = self.factory.makeProduct()
946 browser = self.getBrowser(project, '+configure-code')1010 browser = self.getBrowser(project, '+configure-code')