← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~cjwatson/launchpad/git-feature-flag into lp:launchpad

 

Colin Watson has proposed merging lp:~cjwatson/launchpad/git-feature-flag into lp:launchpad.

Commit message:
Add a code.git.enabled feature flag controlling the ability to create new Git repositories.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)
Related bugs:
  Bug #1032731 in Launchpad itself: "Support for Launchpad-hosted Git repositories"
  https://bugs.launchpad.net/launchpad/+bug/1032731

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/git-feature-flag/+merge/251798

Add a code.git.enabled feature flag controlling the ability to create new Git repositories.

I think this is strict enough: it will let us leave the flag off on production and switch it on for ~launchpad on staging, letting us experiment without committing to URLs.  Restricting read access would get in the way of things like making sure that anonymous git:// works, so I'd rather not.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/git-feature-flag into lp:launchpad.
=== modified file 'lib/lp/code/errors.py'
--- lib/lp/code/errors.py	2015-03-03 16:35:49 +0000
+++ lib/lp/code/errors.py	2015-03-04 18:25:59 +0000
@@ -29,6 +29,7 @@
     'ClaimReviewFailed',
     'DiffNotFound',
     'GitDefaultConflict',
+    'GitFeatureDisabled',
     'GitRepositoryCreationException',
     'GitRepositoryCreationFault',
     'GitRepositoryCreationForbidden',
@@ -325,6 +326,15 @@
     """Base class for Git repository creation exceptions."""
 
 
+@error_status(httplib.UNAUTHORIZED)
+class GitFeatureDisabled(GitRepositoryCreationException):
+    """Only certain users can create new Git repositories."""
+
+    def __init__(self):
+        message = "You do not have permission to create new Git repositories."
+        GitRepositoryCreationException.__init__(self, message)
+
+
 @error_status(httplib.CONFLICT)
 class GitRepositoryExists(GitRepositoryCreationException):
     """Raised when creating a Git repository that already exists."""

=== modified file 'lib/lp/code/interfaces/gitrepository.py'
--- lib/lp/code/interfaces/gitrepository.py	2015-03-03 15:05:59 +0000
+++ lib/lp/code/interfaces/gitrepository.py	2015-03-04 18:25:59 +0000
@@ -6,6 +6,7 @@
 __metaclass__ = type
 
 __all__ = [
+    'GIT_FEATURE_FLAG',
     'GitIdentityMixin',
     'GIT_REPOSITORY_NAME_VALIDATION_ERROR_MESSAGE',
     'git_repository_name_validator',
@@ -51,6 +52,9 @@
     )
 
 
+GIT_FEATURE_FLAG = u"code.git.enabled"
+
+
 GIT_REPOSITORY_NAME_VALIDATION_ERROR_MESSAGE = _(
     "Git repository names must start with a number or letter.  The characters "
     "+, -, _, . and @ are also allowed after the first character.  Repository "

=== modified file 'lib/lp/code/model/gitrepository.py'
--- lib/lp/code/model/gitrepository.py	2015-03-03 15:05:59 +0000
+++ lib/lp/code/model/gitrepository.py	2015-03-04 18:25:59 +0000
@@ -38,6 +38,7 @@
 from lp.app.interfaces.services import IService
 from lp.code.errors import (
     GitDefaultConflict,
+    GitFeatureDisabled,
     GitTargetError,
     )
 from lp.code.interfaces.gitcollection import IAllGitRepositories
@@ -47,6 +48,7 @@
     IGitNamespacePolicy,
     )
 from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
     GitIdentityMixin,
     IGitRepository,
     IGitRepositorySet,
@@ -85,6 +87,7 @@
     ArrayAgg,
     ArrayIntersects,
     )
+from lp.services.features import getFeatureFlag
 from lp.services.propertycache import cachedproperty
 from lp.services.webapp.authorization import available_with_permission
 
@@ -135,6 +138,8 @@
 
     def __init__(self, registrant, owner, target, name, information_type,
                  date_created):
+        if not getFeatureFlag(GIT_FEATURE_FLAG):
+            raise GitFeatureDisabled
         super(GitRepository, self).__init__()
         self.registrant = registrant
         self.owner = owner

=== modified file 'lib/lp/code/model/tests/test_gitcollection.py'
--- lib/lp/code/model/tests/test_gitcollection.py	2015-02-26 14:46:35 +0000
+++ lib/lp/code/model/tests/test_gitcollection.py	2015-03-04 18:25:59 +0000
@@ -17,7 +17,10 @@
     IAllGitRepositories,
     IGitCollection,
     )
-from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
+    IGitRepositorySet,
+    )
 from lp.code.model.gitcollection import GenericGitCollection
 from lp.code.model.gitrepository import GitRepository
 from lp.registry.enums import PersonVisibility
@@ -27,6 +30,7 @@
     )
 from lp.registry.model.personproduct import PersonProduct
 from lp.services.database.interfaces import IStore
+from lp.services.features.testing import FeatureFixture
 from lp.testing import (
     person_logged_in,
     StormStatementRecorder,
@@ -85,6 +89,7 @@
 
     def setUp(self):
         super(TestGenericGitCollection, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.store = IStore(GitRepository)
 
     def test_provides_gitcollection(self):
@@ -168,6 +173,7 @@
 
     def setUp(self):
         TestCaseWithFactory.setUp(self)
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.all_repositories = getUtility(IAllGitRepositories)
 
     def test_order_by_repository_name(self):
@@ -388,6 +394,7 @@
 
     def setUp(self):
         TestCaseWithFactory.setUp(self)
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.public_repository = self.factory.makeGitRepository(name=u'public')
         self.private_repository = self.factory.makeGitRepository(
             name=u'private', information_type=InformationType.USERDATA)
@@ -486,6 +493,7 @@
 
     def setUp(self):
         TestCaseWithFactory.setUp(self)
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.collection = getUtility(IAllGitRepositories)
 
     def test_exact_match_unique_name(self):
@@ -623,6 +631,7 @@
 
     def setUp(self):
         TestCaseWithFactory.setUp(self)
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.all_repositories = getUtility(IAllGitRepositories)
 
     def test_no_teams(self):
@@ -668,6 +677,7 @@
 
     def setUp(self):
         TestCaseWithFactory.setUp(self)
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.all_repositories = getUtility(IAllGitRepositories)
 
     def test_no_repositories(self):

=== modified file 'lib/lp/code/model/tests/test_gitlookup.py'
--- lib/lp/code/model/tests/test_gitlookup.py	2015-03-03 01:36:13 +0000
+++ lib/lp/code/model/tests/test_gitlookup.py	2015-03-04 18:25:59 +0000
@@ -16,7 +16,10 @@
     IGitLookup,
     IGitTraverser,
     )
-from lp.code.interfaces.gitrepository import IGitRepositorySet
+from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
+    IGitRepositorySet,
+    )
 from lp.registry.errors import NoSuchSourcePackageName
 from lp.registry.interfaces.person import NoSuchPerson
 from lp.registry.interfaces.product import (
@@ -24,6 +27,7 @@
     NoSuchProduct,
     )
 from lp.services.config import config
+from lp.services.features.testing import FeatureFixture
 from lp.testing import (
     person_logged_in,
     TestCaseWithFactory,
@@ -38,6 +42,7 @@
 
     def setUp(self):
         super(TestGetByUniqueName, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.lookup = getUtility(IGitLookup)
 
     def test_not_found(self):
@@ -69,6 +74,7 @@
 
     def setUp(self):
         super(TestGetByPath, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.lookup = getUtility(IGitLookup)
 
     def test_project(self):
@@ -131,6 +137,7 @@
 
     def setUp(self):
         super(TestGetByUrl, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.lookup = getUtility(IGitLookup)
 
     def makeProjectRepository(self):
@@ -221,6 +228,7 @@
 
     def setUp(self):
         super(TestGitTraverser, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.traverser = getUtility(IGitTraverser)
 
     def assertTraverses(self, path, owner, target, repository=None):

=== modified file 'lib/lp/code/model/tests/test_gitrepository.py'
--- lib/lp/code/model/tests/test_gitrepository.py	2015-02-26 17:32:56 +0000
+++ lib/lp/code/model/tests/test_gitrepository.py	2015-03-04 18:25:59 +0000
@@ -21,6 +21,7 @@
     )
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
 from lp.code.errors import (
+    GitFeatureDisabled,
     GitRepositoryCreatorNotMemberOfOwnerTeam,
     GitRepositoryCreatorNotOwner,
     GitTargetError,
@@ -31,6 +32,7 @@
     IGitNamespaceSet,
     )
 from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
     IGitRepository,
     IGitRepositorySet,
     )
@@ -50,6 +52,7 @@
 from lp.registry.interfaces.personproduct import IPersonProductFactory
 from lp.registry.tests.test_accesspolicy import get_policies_for_artifact
 from lp.services.database.constants import UTC_NOW
+from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.authorization import check_permission
 from lp.testing import (
     admin_logged_in,
@@ -58,7 +61,19 @@
     TestCaseWithFactory,
     verifyObject,
     )
-from lp.testing.layers import DatabaseFunctionalLayer
+from lp.testing.layers import (
+    DatabaseFunctionalLayer,
+    ZopelessDatabaseLayer,
+    )
+
+
+class TestGitRepositoryFeatureFlag(TestCaseWithFactory):
+
+    layer = ZopelessDatabaseLayer
+
+    def test_feature_flag_disabled(self):
+        # Without a feature flag, we will not create new Git repositories.
+        self.assertRaises(GitFeatureDisabled, self.factory.makeGitRepository)
 
 
 class TestGitRepository(TestCaseWithFactory):
@@ -66,6 +81,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepository, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_implements_IGitRepository(self):
         repository = self.factory.makeGitRepository()
         verifyObject(IGitRepository, repository)
@@ -117,6 +136,7 @@
 
     def setUp(self):
         super(TestGitIdentityMixin, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.repository_set = getUtility(IGitRepositorySet)
 
     def assertGitIdentity(self, repository, identity_path):
@@ -253,6 +273,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositoryDateLastModified, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_initial_value(self):
         # The initial value of date_last_modified is date_created.
         repository = self.factory.makeGitRepository()
@@ -279,6 +303,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestCodebrowse, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_simple(self):
         # The basic codebrowse URL for a repository is an 'https' URL.
         repository = self.factory.makeGitRepository()
@@ -292,6 +320,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositoryNamespace, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_namespace_personal(self):
         # The namespace attribute of a personal repository points to the
         # namespace that corresponds to ~owner.
@@ -329,6 +361,7 @@
     def setUp(self):
         # Use an admin user as we aren't checking edit permissions here.
         super(TestGitRepositoryPrivacy, self).setUp("admin@xxxxxxxxxxxxx")
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
 
     def test_personal_repositories_for_private_teams_are_private(self):
         team = self.factory.makeTeam(
@@ -377,6 +410,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositoryGetAllowedInformationTypes, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_normal_user_sees_namespace_types(self):
         # An unprivileged user sees the types allowed by the namespace.
         repository = self.factory.makeGitRepository()
@@ -412,6 +449,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositoryModerate, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_moderate_permission(self):
         # Test the ModerateGitRepository security checker.
         project = self.factory.makeProduct()
@@ -449,6 +490,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositorySetOwner, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_owner_sets_team(self):
         # The owner of the repository can set the owner of the repository to
         # be a team they are a member of.
@@ -498,6 +543,10 @@
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestGitRepositorySetTarget, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def test_personal_to_project(self):
         # A personal repository can be moved to a project.
         owner = self.factory.makePerson()
@@ -628,6 +677,7 @@
 
     def setUp(self):
         super(TestGitRepositorySet, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.repository_set = getUtility(IGitRepositorySet)
 
     def test_provides_IGitRepositorySet(self):
@@ -686,6 +736,7 @@
 
     def setUp(self):
         super(TestGitRepositorySetDefaultsMixin, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.repository_set = getUtility(IGitRepositorySet)
         self.get_method = self.repository_set.getDefaultRepository
         self.set_method = self.repository_set.setDefaultRepository

=== modified file 'lib/lp/code/xmlrpc/tests/test_git.py'
--- lib/lp/code/xmlrpc/tests/test_git.py	2015-03-03 17:09:33 +0000
+++ lib/lp/code/xmlrpc/tests/test_git.py	2015-03-04 18:25:59 +0000
@@ -16,10 +16,12 @@
     )
 from lp.code.interfaces.gitcollection import IAllGitRepositories
 from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
     GIT_REPOSITORY_NAME_VALIDATION_ERROR_MESSAGE,
     IGitRepositorySet,
     )
 from lp.code.xmlrpc.git import GitAPI
+from lp.services.features.testing import FeatureFixture
 from lp.services.webapp.escaping import html_escape
 from lp.testing import (
     ANONYMOUS,
@@ -51,11 +53,57 @@
         raise GitRepositoryCreationFault("nothing here")
 
 
+class TestGitAPIFeatureFlag(TestCaseWithFactory):
+
+    layer = LaunchpadFunctionalLayer
+
+    def setUp(self):
+        super(TestGitAPIFeatureFlag, self).setUp()
+        self.git_api = GitAPI(None, None)
+        self.git_api.hosting_client = FakeGitHostingClient()
+
+    def test_feature_flag_disabled(self):
+        # Without a feature flag, attempts to create a new Git repository fail.
+        requester = self.factory.makePerson()
+        message = "You do not have permission to create new Git repositories."
+        fault = self.git_api.translatePath(
+            u"/~%s/+git/random" % requester.name, "write", requester.id, True)
+        self.assertEqual(faults.PermissionDenied(message), fault)
+
+    def test_feature_flag_disabled_existing(self):
+        # Even without a feature flag, it is possible to operate on Git
+        # repositories that already exist.
+        requester = self.factory.makePerson()
+        path = u"/~%s/+git/random" % requester.name
+        with FeatureFixture({GIT_FEATURE_FLAG: u"on"}):
+            translation = self.git_api.translatePath(
+                path, "write", requester.id, True)
+        login(ANONYMOUS)
+        repository = getUtility(IGitRepositorySet).getByPath(
+            requester, path.lstrip("/"))
+        self.assertIsNotNone(repository)
+        self.assertEqual(
+            {"path": repository.getInternalPath(), "writable": True},
+            translation)
+        translation = self.git_api.translatePath(
+            path, "write", requester.id, True)
+        login(ANONYMOUS)
+        self.assertEqual(
+            {"path": repository.getInternalPath(), "writable": True},
+            translation)
+        # But we cannot create another one without the feature flag.
+        message = "You do not have permission to create new Git repositories."
+        fault = self.git_api.translatePath(
+            u"/~%s/+git/another" % requester.name, "write", requester.id, True)
+        self.assertEqual(faults.PermissionDenied(message), fault)
+
+
 class TestGitAPIMixin:
     """Helper methods for `IGitAPI` tests, and security-relevant tests."""
 
     def setUp(self):
         super(TestGitAPIMixin, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.git_api = GitAPI(None, None)
         self.git_api.hosting_client = FakeGitHostingClient()
 

=== modified file 'lib/lp/registry/browser/tests/test_peoplemerge.py'
--- lib/lp/registry/browser/tests/test_peoplemerge.py	2015-02-23 19:47:01 +0000
+++ lib/lp/registry/browser/tests/test_peoplemerge.py	2015-03-04 18:25:59 +0000
@@ -8,12 +8,14 @@
 from zope.security.proxy import removeSecurityProxy
 
 from lp.app.enums import InformationType
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
 from lp.registry.enums import TeamMembershipPolicy
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.persontransferjob import IPersonMergeJobSource
 from lp.services.identity.interfaces.emailaddress import EmailAddressStatus
 from lp.services.identity.model.emailaddress import EmailAddressSet
 from lp.services.mail import stub
+from lp.services.features.testing import FeatureFixture
 from lp.services.verification.tests.logintoken import get_token_url_from_email
 from lp.services.webapp import canonical_url
 from lp.services.webapp.escaping import html_escape
@@ -299,6 +301,7 @@
 
     def test_cannot_merge_person_with_private_git_repositories(self):
         # A team or user with a private Git repository cannot be merged.
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         self.factory.makeGitRepository(
             owner=self.dupe, information_type=InformationType.USERDATA)
         login_celebrity('registry_experts')

=== modified file 'lib/lp/registry/services/tests/test_sharingservice.py'
--- lib/lp/registry/services/tests/test_sharingservice.py	2015-02-23 17:59:37 +0000
+++ lib/lp/registry/services/tests/test_sharingservice.py	2015-03-04 18:25:59 +0000
@@ -23,7 +23,10 @@
     CodeReviewNotificationLevel,
     )
 from lp.code.interfaces.branch import IBranch
-from lp.code.interfaces.gitrepository import IGitRepository
+from lp.code.interfaces.gitrepository import (
+    GIT_FEATURE_FLAG,
+    IGitRepository,
+    )
 from lp.registry.enums import (
     BranchSharingPolicy,
     BugSharingPolicy,
@@ -73,6 +76,7 @@
         self.service = getUtility(IService, 'sharing')
         self.useFixture(FeatureFixture({
             'jobs.celery.enabled_classes': 'RemoveArtifactSubscriptionsJob',
+            GIT_FEATURE_FLAG: 'on',
         }))
 
     def _makeGranteeData(self, grantee, policy_permissions,

=== modified file 'lib/lp/registry/tests/test_accesspolicy.py'
--- lib/lp/registry/tests/test_accesspolicy.py	2015-02-23 17:59:37 +0000
+++ lib/lp/registry/tests/test_accesspolicy.py	2015-03-04 18:25:59 +0000
@@ -8,6 +8,7 @@
 from zope.component import getUtility
 
 from lp.app.enums import InformationType
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
 from lp.registry.enums import SharingPermission
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifact,
@@ -25,6 +26,7 @@
 from lp.registry.model.accesspolicy import reconcile_access_for_artifact
 from lp.registry.model.person import Person
 from lp.services.database.interfaces import IStore
+from lp.services.features.testing import FeatureFixture
 from lp.testing import TestCaseWithFactory
 from lp.testing.layers import DatabaseFunctionalLayer
 from lp.testing.matchers import Provides
@@ -283,6 +285,10 @@
 class TestAccessArtifactGitRepository(BaseAccessArtifactTests,
                                       TestCaseWithFactory):
 
+    def setUp(self):
+        super(TestAccessArtifactGitRepository, self).setUp()
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
+
     def getConcreteArtifact(self):
         return self.factory.makeGitRepository()
 

=== modified file 'lib/lp/registry/tests/test_personmerge.py'
--- lib/lp/registry/tests/test_personmerge.py	2015-02-23 19:47:01 +0000
+++ lib/lp/registry/tests/test_personmerge.py	2015-03-04 18:25:59 +0000
@@ -13,6 +13,7 @@
 
 from lp.app.enums import InformationType
 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
     IAccessPolicyGrantSource,
@@ -273,6 +274,7 @@
     def test_merge_moves_git_repositories(self):
         # When person/teams are merged, Git repositories owned by the from
         # person are moved.
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         person = self.factory.makePerson()
         repository = self.factory.makeGitRepository()
         duplicate = repository.owner
@@ -285,6 +287,7 @@
     def test_merge_with_duplicated_git_repositories(self):
         # If both the from and to people have Git repositories with the same
         # name, merging renames the duplicate from the from person's side.
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         project = self.factory.makeProduct()
         from_repository = self.factory.makeGitRepository(
             target=project, name=u'foo')

=== modified file 'lib/lp/registry/tests/test_sharingjob.py'
--- lib/lp/registry/tests/test_sharingjob.py	2015-02-23 17:59:37 +0000
+++ lib/lp/registry/tests/test_sharingjob.py	2015-03-04 18:25:59 +0000
@@ -17,6 +17,7 @@
     BranchSubscriptionNotificationLevel,
     CodeReviewNotificationLevel,
     )
+from lp.code.interfaces.gitrepository import GIT_FEATURE_FLAG
 from lp.registry.enums import SpecificationSharingPolicy
 from lp.registry.interfaces.accesspolicy import (
     IAccessArtifactGrantSource,
@@ -119,6 +120,7 @@
             % (branch.id, requestor.name), repr(job))
 
     def test_repr_gitrepositories(self):
+        self.useFixture(FeatureFixture({GIT_FEATURE_FLAG: u"on"}))
         requestor = self.factory.makePerson()
         gitrepository = self.factory.makeGitRepository()
         job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
@@ -244,6 +246,7 @@
     def setUp(self):
         self.useFixture(FeatureFixture({
             'jobs.celery.enabled_classes': 'RemoveArtifactSubscriptionsJob',
+            GIT_FEATURE_FLAG: 'on',
         }))
         super(RemoveArtifactSubscriptionsJobTestCase, self).setUp()
 

=== modified file 'lib/lp/services/features/flags.py'
--- lib/lp/services/features/flags.py	2014-08-19 04:56:39 +0000
+++ lib/lp/services/features/flags.py	2015-03-04 18:25:59 +0000
@@ -227,6 +227,12 @@
      'disabled',
      'PPA Separate Long Descriptions',
      ''),
+    ('code.git.enabled',
+     'boolean',
+     'If true, Git repository support is enabled.',
+     'disabled',
+     '',
+     ''),
     ])
 
 # The set of all flag names that are documented.


Follow ups