= Branch Visibility Policy = The branch visibility policy is used to define the default visibility of branches when they are created. Both projects and products can define a branch visibility policy. If a policy does not exist for a product, it uses the policy defined by its project (if it exists). The information is saved in the database table BranchVisibilityPolicy. This table effectively creates tuples of (ProjectGroup or Product, Team, Policy). Since there is no team that is used to define everyone, the non-specifying of a team means *everyone*. The Policy is defined by the enumerated type BranchVisibilityRule. >>> from lp.code.enums import BranchVisibilityRule There are currently four visibility policies: * Public - branches are public by default for the specified team. * Private - branches are private by default for the specified team. * Private only - branches are private by default, and the owner of the branch is not able to make them public. * Forbidden - members of the specified team are not able to create branches in the context. This is only valid as a base policy that applies to everyone, and cannot be used as a selective discriminator against certain teams. >>> for item in BranchVisibilityRule.items: ... print item.title Public Private Private only Forbidden == IHasBranchVisibilityPolicy == The interface IHasBranchVisibilityPolicy is used to define the public interface for modifying the branch visibility policy items. Both IProjectGroup and IProduct inherit from this interface as both projects and products can set branch visibility policies. >>> from zope.component import getUtility >>> from canonical.launchpad.webapp.testing import verifyObject >>> from lp.registry.interfaces.projectgroup import IProjectGroupSet >>> from lp.code.interfaces.branchvisibilitypolicy import ( ... IHasBranchVisibilityPolicy) >>> project_set = getUtility(IProjectGroupSet) >>> mozilla = project_set.getByName('mozilla') >>> IHasBranchVisibilityPolicy.providedBy(mozilla) True >>> verifyObject(IHasBranchVisibilityPolicy, mozilla) True >>> firefox, thunderbird = list(mozilla.products) >>> IHasBranchVisibilityPolicy.providedBy(firefox) True >>> verifyObject(IHasBranchVisibilityPolicy, firefox) True == Teams to work with == >>> from lp.registry.interfaces.person import IPersonSet >>> person_set = getUtility(IPersonSet) >>> launchpad_team = person_set.getByName('launchpad') >>> ubuntu_gnome_team = person_set.getByName('name18') >>> from lp.registry.interfaces.product import IProductSet >>> launchpad = getUtility(IProductSet).getByName('launchpad') >>> evolution = getUtility(IProductSet).getByName('evolution') >>> def teamname(team): ... if team is None: ... return "*everyone*" ... else: ... return team.displayname >>> def print_team_policies(context): ... for item in context.getBranchVisibilityTeamPolicies(): ... print "%s: %s" % (teamname(item.team), item.rule.title) == Default policy == There is an implicit policy defined for all contexts. The implicit policy is that everyone can create public branches. The implicit policy is not returned with the items attribute. In order to override this implicit policy you need to explicitly define a policy for everyone, and this is done by specifying None for the team. >>> print_team_policies(evolution) >>> print evolution.getBaseBranchVisibilityRule().title Public >>> evolution.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.PUBLIC) >>> print_team_policies(evolution) *everyone*: Public >>> print evolution.getBaseBranchVisibilityRule().title Public == Setting the policy for teams == Setting the policy for a team overrides any existing policy that may exist for that team. >>> evolution.setBranchVisibilityTeamPolicy( ... launchpad_team, BranchVisibilityRule.PUBLIC) >>> print_team_policies(evolution) *everyone*: Public Launchpad Developers: Public Setting again replaces the policy. >>> evolution.setBranchVisibilityTeamPolicy( ... launchpad_team, BranchVisibilityRule.PRIVATE) >>> print_team_policies(evolution) *everyone*: Public Launchpad Developers: Private Setting the policy for *everyone* is done by passing None for the team. >>> evolution.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.FORBIDDEN) >>> print_team_policies(evolution) *everyone*: Forbidden Launchpad Developers: Private >>> print evolution.getBaseBranchVisibilityRule().title Forbidden == Removing teams from the policy object == >>> evolution.removeTeamFromBranchVisibilityPolicy(launchpad_team) >>> print_team_policies(evolution) *everyone*: Forbidden Attempting to remove a team that doesn't exist is allowed, but does not change the state of the policy object. >>> evolution.removeTeamFromBranchVisibilityPolicy(launchpad_team) >>> print_team_policies(evolution) *everyone*: Forbidden In order to remove a policy assigned to everyone you pass None in for the team name. >>> evolution.removeTeamFromBranchVisibilityPolicy(None) >>> print_team_policies(evolution) == Listing the team policies == Team policies are listed in alphabetical order of the team display name with the policy for *everybody* being first. >>> evolution.setBranchVisibilityTeamPolicy( ... ubuntu_gnome_team, BranchVisibilityRule.PRIVATE) >>> evolution.setBranchVisibilityTeamPolicy( ... launchpad_team, BranchVisibilityRule.PRIVATE) >>> print_team_policies(evolution) Launchpad Developers: Private Ubuntu Gnome Team: Private >>> evolution.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.PUBLIC) >>> print_team_policies(evolution) *everyone*: Public Launchpad Developers: Private Ubuntu Gnome Team: Private == Inherited team policies == Products can inherit the branch visibility team policies of their projects. If a product has no project, then there are no inherited team policies to inherit. >>> print mozilla.isUsingInheritedBranchVisibilityPolicy() False The inherited behaviour is only used if there are no explicit team policies set. >>> print thunderbird.isUsingInheritedBranchVisibilityPolicy() True Since the Launchpad product doesn't have a project associated with it, there is no inherited policy. >>> print launchpad.isUsingInheritedBranchVisibilityPolicy() False Adding a team to the mozilla project will also affect products associated with the mozilla project that are inheriting the project's branch policy. >>> mozilla.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.FORBIDDEN) >>> mozilla.setBranchVisibilityTeamPolicy( ... ubuntu_gnome_team, BranchVisibilityRule.PRIVATE) >>> print_team_policies(mozilla) *everyone*: Forbidden Ubuntu Gnome Team: Private >>> print_team_policies(thunderbird) *everyone*: Forbidden Ubuntu Gnome Team: Private In order to avoid inheriting the policy of the project, there just needs to be an explicit team policy for the product. This explicit team policy could even be the same as the default policy. >>> thunderbird.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.PUBLIC) >>> print thunderbird.isUsingInheritedBranchVisibilityPolicy() False >>> print_team_policies(thunderbird) *everyone*: Public == Determining the defined policy for a team == You can get the defined policy for any given team using the method getBranchVisibilityRuleForTeam. If there is not a policy explicity defined, then None is returned. >>> print mozilla.getBranchVisibilityRuleForTeam(ubuntu_gnome_team).title Private >>> print mozilla.getBranchVisibilityRuleForTeam(None).title Forbidden >>> print mozilla.getBranchVisibilityRuleForTeam(launchpad_team) None == Policy checks during branch creation == The branch visibility policy is checked during branch creation through the relevant IBranchNamespace implementation. >>> from canonical.launchpad.ftests import login >>> login('foo.bar@canonical.com') >>> evolution.setBranchVisibilityTeamPolicy( ... None, BranchVisibilityRule.FORBIDDEN) >>> vcs_imports_team = person_set.getByName('vcs-imports') >>> evolution.setBranchVisibilityTeamPolicy( ... vcs_imports_team, BranchVisibilityRule.PUBLIC) >>> print_team_policies(evolution) *everyone*: Forbidden Launchpad Developers: Private Ubuntu Gnome Team: Private VCS imports: Public >>> foo_bar = person_set.getByEmail("foo.bar@canonical.com") >>> stevea = person_set.getByName("stevea") >>> ddaa = person_set.getByName("ddaa") When creating a branch, both the creator and owner need to be specified. The creator must be in the team of the owner in order to create a branch in the owner's namespace. If there is a policy that applies specificly to the owner of the branch, then that policy is applied for the branch. This is to handle the situation where TeamA is a member of TeamB, TeamA has a PUBLIC policy, and TeamB has a PRIVATE policy. By pushing to TeamA's branch area, the PUBLIC policy is used. If a owner is a member of more than one team that has a specified policy the PRIVATE and PRIVATE_ONLY override PUBLIC policies. If the owner is a member of more than one team that has PRIVATE or PRIVATE_ONLY set as the policy, then the branch is created private, and no team is subscribed to it as we can't guess which team the user means to have the visibility. Extensive behavioural tests for the determination of branch visibility can be found in the file: lib/canonical/launchpad/database/tests/test_branchset.py Foo Bar is a member of all the teams. >>> foo_bar.inTeam(launchpad_team) True >>> foo_bar.inTeam(ubuntu_gnome_team) True >>> foo_bar.inTeam(vcs_imports_team) True If Foo Bar is creating a branch for evolution, the branch would be created as a private branch, but since there are two teams that specify privacy, the branch is created so only Foo Bar can see it. It is up to Foo Bar to subscribe the appropriate team at a later date. >>> branch = factory.makeProductBranch( ... name="testing-1", owner=foo_bar, product=evolution) >>> branch.private True >>> branch.subscribers.count() 1 Steve is a member of the Ubuntu Gnome Team, but not the Launchpad Developers team, so branches that are created by Steve are private and are subscribed to by the Ubuntu Gnome Team. >>> stevea.inTeam(launchpad_team) False >>> stevea.inTeam(ubuntu_gnome_team) True >>> stevea.inTeam(vcs_imports_team) False >>> branch = factory.makeProductBranch( ... name="testing-2", owner=stevea, product=evolution) >>> branch.private True >>> for name in sorted([subscriber.displayname for subscriber ... in branch.subscribers]): ... print name Steve Alexander Ubuntu Gnome Team Since David is a member of VCS Imports but not a member of the other two teams that have private branches, David's branch will be public. >>> ddaa.inTeam(launchpad_team) False >>> ddaa.inTeam(ubuntu_gnome_team) False >>> ddaa.inTeam(vcs_imports_team) True >>> branch = factory.makeProductBranch( ... name="testing-3", owner=ddaa, product=evolution) >>> branch.private False >>> branch.subscribers.count() 1 People that are not members of any of the specified teams get the base visibility policy, which for this product is Forbidden. >>> no_priv = person_set.getByEmail("no-priv@canonical.com") >>> no_priv.inTeam(launchpad_team) False >>> no_priv.inTeam(ubuntu_gnome_team) False >>> no_priv.inTeam(vcs_imports_team) False >>> branch = factory.makeProductBranch( ... name="testing-4", owner=no_priv, product=evolution) Traceback (most recent call last): ... BranchCreationForbidden: You cannot create branches ...