launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #18025
[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