Veiko Aasa c136f27707
gitweb: Add ability to change default branch
Now it is possible to change default branch when editing a repository.

Gitweb site shows default branch as a main branch and the 'git clone'
command checks out to default branch.

Added unit and functional tests. Splitted one large 'test_actions'
into multiple tests.

Tests performed:
- All gitweb unit and functional tests pass.
- Created a repository from a remote repository which has default
  branch other than master. Confirmed that the 'Edit repository'
  page shows correct branch and gitweb site shows this branch as
  a default branch

Closes #1925

Signed-off-by: Veiko Aasa <veiko17@disroot.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
2020-09-09 14:37:17 -07:00

158 lines
4.9 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
Django form for configuring Gitweb.
"""
import json
import re
from urllib.parse import urlparse
from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import URLValidator
from django.utils.translation import ugettext_lazy as _
from plinth import actions
from plinth.modules import gitweb
def _get_branches(repo):
"""Get all the branches in the repository."""
branch_data = json.loads(
actions.run('gitweb', ['get-branches', '--name', repo]))
default_branch = branch_data['default_branch']
branches = branch_data['branches']
if default_branch not in branches:
branches.insert(0, default_branch)
return [(branch, branch) for branch in branches]
def get_name_from_url(url):
"""Get a repository name from URL"""
return urlparse(url).path.split('/')[-1]
def is_repo_url(url):
"""Check if URL is valid."""
try:
RepositoryValidator(input_should_be='url')(url)
except ValidationError:
return False
return True
class RepositoryValidator:
input_should_be = 'name'
def __init__(self, input_should_be=None):
if input_should_be is not None:
self.input_should_be = input_should_be
def __call__(self, value):
"""Validate that the input is a correct repository name or URL"""
if self.input_should_be in ('url', 'url_or_name'):
try:
URLValidator(schemes=['http', 'https'],
message=_('Invalid repository URL.'))(value)
except ValidationError:
if self.input_should_be == 'url':
raise
else:
value = get_name_from_url(value)
if (not re.match(r'^[a-zA-Z0-9-._]+$', value)) \
or value.startswith(('-', '.')) \
or value.endswith('.git.git'):
raise ValidationError(_('Invalid repository name.'), 'invalid')
class CreateRepoForm(forms.Form):
"""Form to create and edit a new repository."""
name = forms.CharField(
label=_(
'Name of a new repository or URL to import an existing repository.'
), strip=True,
validators=[RepositoryValidator(input_should_be='url_or_name')],
widget=forms.TextInput(attrs={'autocomplete': 'off'}))
description = forms.CharField(
label=_('Description of the repository'), strip=True, required=False,
help_text=_('Optional, for displaying on Gitweb.'))
owner = forms.CharField(label=_('Repository\'s owner name'), strip=True,
required=False,
help_text=_('Optional, for displaying on Gitweb.'))
is_private = forms.BooleanField(
label=_('Private repository'), required=False,
help_text=_('Allow only authorized users to access this repository.'))
def __init__(self, *args, **kwargs):
"""Initialize the form with extra request argument."""
super().__init__(*args, **kwargs)
self.fields['name'].widget.attrs.update({'autofocus': 'autofocus'})
def clean_name(self):
"""Check if the name is valid."""
name = self.cleaned_data['name']
repo_name = name
if is_repo_url(name):
repo_name = get_name_from_url(name)
if repo_name.endswith('.git'):
repo_name = repo_name[:-4]
for repo in gitweb.get_repo_list():
if repo_name == repo['name']:
raise ValidationError(
_('A repository with this name already exists.'))
if is_repo_url(name):
if not gitweb.repo_exists(name):
raise ValidationError('Remote repository is not available.')
return name
class EditRepoForm(CreateRepoForm):
"""Form to create and edit a new repository."""
name = forms.CharField(
label=_('Name of the repository'),
strip=True,
validators=[RepositoryValidator()],
help_text=_(
'An alpha-numeric string that uniquely identifies a repository.'),
)
default_branch = forms.ChoiceField(
label=_('Default branch'),
help_text=_('Gitweb displays this as a default branch.'))
def __init__(self, *args, **kwargs):
"""Initialize the form with extra request argument."""
super().__init__(*args, **kwargs)
branches = _get_branches(self.initial['name'])
self.fields['default_branch'].choices = branches
def clean_name(self):
"""Check if the name is valid."""
name = self.cleaned_data['name']
if 'name' in self.initial and name == self.initial['name']:
return name
if name.endswith('.git'):
name = name[:-4]
for repo in gitweb.get_repo_list():
if name == repo['name']:
raise ValidationError(
_('A repository with this name already exists.'))
return name