feature(repo): added ability to bootstrap repositories on creation
This commit is contained in:
parent
6f780dccb1
commit
ee52770fff
4 changed files with 176 additions and 30 deletions
|
|
@ -204,6 +204,9 @@ def create_repo(form_data, cur_user):
|
|||
fork_of = form_data.get("fork_parent_id")
|
||||
state = form_data.get("repo_state", Repository.STATE_PENDING)
|
||||
|
||||
repo_bootstrap_readme = form_data.get("repo_bootstrap_readme")
|
||||
repo_bootstrap_svn_branch_struct = form_data.get("repo_bootstrap_svn_branch_struct")
|
||||
|
||||
# repo creation defaults, private and repo_type are filled in form
|
||||
defs = SettingsModel().get_default_repo_settings(strip_prefix=True)
|
||||
enable_statistics = form_data.get("enable_statistics", defs.get("repo_enable_statistics"))
|
||||
|
|
@ -261,6 +264,14 @@ def create_repo(form_data, cur_user):
|
|||
repo_id = repo.repo_id
|
||||
repo_data = repo.get_api_data()
|
||||
|
||||
commit = RepoModel().bootstrap(
|
||||
db_repo=repo,
|
||||
db_user=cur_user,
|
||||
readme=repo_bootstrap_readme,
|
||||
svn_struct=repo_bootstrap_svn_branch_struct,
|
||||
)
|
||||
log.debug("Bootstrap commit: %s", commit)
|
||||
|
||||
audit_logger.store(
|
||||
"repo.create",
|
||||
action_data={"data": repo_data},
|
||||
|
|
|
|||
|
|
@ -297,6 +297,9 @@ def RepoForm(localizer, edit=False, old_data=None, repo_groups=None, allow_disab
|
|||
repo_enable_downloads = v.StringBoolean(if_missing=False)
|
||||
repo_enable_locking = v.StringBoolean(if_missing=False)
|
||||
|
||||
repo_bootstrap_readme = v.StringBoolean(if_missing=False)
|
||||
repo_bootstrap_svn_branch_struct = v.StringBoolean(if_missing=False)
|
||||
|
||||
if edit:
|
||||
# this is repo owner
|
||||
user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser(localizer, allow_disabled))
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ from dataclasses import dataclass, field
|
|||
from enum import StrEnum, auto
|
||||
|
||||
from sqlalchemy.orm import aliased
|
||||
from zope.cachedescriptors.property import Lazy as LazyProperty
|
||||
|
||||
from rhodecode import events
|
||||
from rhodecode.lib.auth import HasUserGroupPermissionAny
|
||||
|
|
@ -47,6 +46,7 @@ from rhodecode.lib.utils2 import (
|
|||
action_logger_generic,
|
||||
)
|
||||
from rhodecode.lib.vcs.backends import get_backend
|
||||
from rhodecode.lib.vcs.backends.base import EmptyCommit
|
||||
from rhodecode.lib.vcs.nodes import NodeKind
|
||||
from rhodecode.model import BaseModel
|
||||
from rhodecode.model.db import (
|
||||
|
|
@ -677,6 +677,48 @@ class RepoModel(BaseModel):
|
|||
|
||||
return run_task(tasks.create_repo, form_data, cur_user)
|
||||
|
||||
def bootstrap(self, db_repo, db_user, readme=None, svn_struct=None):
|
||||
"""
|
||||
Bootstrap new created repo with some files/structure
|
||||
"""
|
||||
from rhodecode.model.scm import ScmModel
|
||||
from rhodecode.model.settings import SettingsModel
|
||||
|
||||
_branch_name, _sha_commit_id, is_head = db_repo.landing_ref_name, "", True
|
||||
author = db_user.full_contact
|
||||
repo_name = db_repo.repo_name
|
||||
message = "Bootstrap: created initial project files"
|
||||
commit = EmptyCommit(alias=db_repo.repo_type, branch=_branch_name)
|
||||
|
||||
rc_config = SettingsModel().get_all_settings(cache=True, from_request=False)
|
||||
default_renderer = rc_config.get("rhodecode_markup_renderer", "rst")
|
||||
|
||||
nodes = {}
|
||||
if readme:
|
||||
if default_renderer == "markdown":
|
||||
ext = "md"
|
||||
readme_content = f"# {repo_name}\n"
|
||||
elif default_renderer == "rst":
|
||||
ext = "rst"
|
||||
readme_content = f"{repo_name}\n" + "-" * len(repo_name) + "\n"
|
||||
else:
|
||||
ext = ".txt"
|
||||
readme_content = f"{repo_name}\n"
|
||||
|
||||
nodes[safe_bytes(f"readme.{ext}")] = {"content": safe_bytes(readme_content)}
|
||||
|
||||
if nodes:
|
||||
commit = ScmModel().create_nodes(
|
||||
user=db_user.user_id,
|
||||
repo=db_repo,
|
||||
message=message,
|
||||
nodes=nodes,
|
||||
parent_commit=commit,
|
||||
author=author,
|
||||
)
|
||||
return commit
|
||||
return None
|
||||
|
||||
def update_permissions(
|
||||
self, repo, perm_additions=None, perm_updates=None, perm_deletions=None, check_perms=True, cur_user=None
|
||||
):
|
||||
|
|
|
|||
|
|
@ -5,16 +5,91 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
<div class="fields">
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
<label for="repo_name">${_('Repository name')}:</label>
|
||||
<label for="repo_name">${_('Repository')}:</label>
|
||||
</div>
|
||||
<style>
|
||||
.help-block-repo-create {
|
||||
|
||||
color: #949494;
|
||||
|
||||
}
|
||||
.repo-header-row {
|
||||
display: flex; /* Modern way to align elements inline */
|
||||
align-items: center; /* Keeps text perfectly leveled vertically */
|
||||
gap: 4px; /* Space between the label and the link */
|
||||
margin: 10px 0; /* Space below the entire row */
|
||||
|
||||
}
|
||||
|
||||
#copy_perms {
|
||||
/* Layout & Alignment */
|
||||
display: flex;
|
||||
align-items: center; /* Aligns checkbox with the first line of text */
|
||||
|
||||
/* Spacing for the whole row */
|
||||
margin: 15px 0; /* Space above and below the row */
|
||||
|
||||
/* Internal Spacing */
|
||||
gap: 6px; /* Space specifically between checkbox and text */
|
||||
}
|
||||
|
||||
#checkbox-span {
|
||||
align-items: center; /* Aligns checkbox with the first line of text */
|
||||
padding-top: 2px; /* Micro-adjustment to center checkbox with text line */
|
||||
}
|
||||
|
||||
.help-block-inline {
|
||||
font-size: 14px; /* Standard readable size */
|
||||
line-height: 1.4; /* Improves readability for multi-line text */
|
||||
color: #333; /* Professional dark grey */
|
||||
}
|
||||
|
||||
.repo-create-spacer {
|
||||
margin: 0 4px;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<div class="input">
|
||||
${h.text('repo_name', class_="medium")}
|
||||
<div class="info-block">
|
||||
<a id="remote_clone_toggle" href="#">${_('Import Existing Repository ?')}</a>
|
||||
|
||||
${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
|
||||
<span class="repo-create-spacer"> / </span>
|
||||
${h.text('repo_name', class_="medium", id="repo_name_input")}
|
||||
|
||||
% if c.personal_repo_group:
|
||||
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
|
||||
${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}
|
||||
</a>
|
||||
% endif
|
||||
|
||||
<div class="repo-header-row">
|
||||
<span class="help-block-repo-create">
|
||||
${_('Repository Group / Repository Name')}.
|
||||
</span>
|
||||
<div class="import-repo">
|
||||
<a id="remote_clone_toggle" href="#">${_('Import an existing repository.')}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## COPY PERMS
|
||||
<div id="copy_perms">
|
||||
<div id="checkbox-span">
|
||||
${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
|
||||
</div>
|
||||
|
||||
<div class="help-block-inline">
|
||||
${_('Copy permissions from parent repository group.')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## TODO: what is this ? i don't think we use it anymore
|
||||
%if not c.rhodecode_user.is_admin:
|
||||
${h.hidden('user_created',True)}
|
||||
%endif
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div id="remote_clone" class="field" style="display: none;">
|
||||
|
|
@ -37,20 +112,6 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
<label for="repo_group">${_('Repository group')}:</label>
|
||||
</div>
|
||||
<div class="select">
|
||||
${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
|
||||
% if c.personal_repo_group:
|
||||
<a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
|
||||
${_('Select my personal group ({})').format(c.personal_repo_group.group_name)}
|
||||
</a>
|
||||
% endif
|
||||
<span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="label">
|
||||
|
|
@ -58,7 +119,6 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
</div>
|
||||
<div class="fields repo-type-radio">
|
||||
|
||||
|
||||
% for backend in c.backends:
|
||||
% if loop.index == 0:
|
||||
<input id="repo_type_${backend}" name="repo_type" type="radio" value="${backend}" checked="checked"/>
|
||||
|
|
@ -73,7 +133,6 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
|
||||
% endfor
|
||||
|
||||
|
||||
<span class="help-block">${_('Set the type of repository to create.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -98,15 +157,7 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="copy_perms" class="field">
|
||||
<div class="label label-checkbox">
|
||||
<label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
|
||||
</div>
|
||||
<div class="checkboxes">
|
||||
${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
|
||||
<span class="help-block">${_('Copy permissions from parent repository group.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<div class="label label-checkbox">
|
||||
<label for="repo_private">${_('Private Repository')}:</label>
|
||||
|
|
@ -116,6 +167,28 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
<span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field repo-bootstrap-items">
|
||||
<div class="label label-checkbox">
|
||||
<label for="repo_bootstrap_readme">${_('Add Readme')}:</label>
|
||||
</div>
|
||||
<div class="checkboxes">
|
||||
${h.checkbox('repo_bootstrap_readme',value="True")}
|
||||
<span class="help-block">${_('Initialize repository with a README. Allows you to immediately clone this project’s repository.')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field repo-bootstrap-items">
|
||||
<div class="label label-checkbox">
|
||||
<label for="repo_bootstrap_svn_branch_struct">${_('Add SVN structure')}:</label>
|
||||
</div>
|
||||
<div class="checkboxes">
|
||||
${h.checkbox('repo_bootstrap_svn_branch_struct',value="True")}
|
||||
<span class="help-block">${_('Initialize repository with a branches/tags DIRS')}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="buttons">
|
||||
${h.submit('save',_('Create Repository'),class_="btn")}
|
||||
</div>
|
||||
|
|
@ -135,6 +208,8 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
$('#remote_clone_toggle').on('click', function(e){
|
||||
$('#remote_clone').show();
|
||||
e.preventDefault();
|
||||
$('#remote_clone_toggle').hide();
|
||||
$('.repo-bootstrap-items').remove();
|
||||
});
|
||||
|
||||
if($('#remote_clone input').hasClass('error')){
|
||||
|
|
@ -163,6 +238,21 @@ ${h.secure_form(h.route_path('repo_create'), request=request)}
|
|||
$("#repo_group").val($(this).data('personalGroupId')).trigger("change");
|
||||
})
|
||||
|
||||
|
||||
$('#clone_uri').on('input paste', function(e) {
|
||||
var url = $(this).val().trim();
|
||||
if (url) {
|
||||
var parts = url.split('/').filter(Boolean);
|
||||
var lastPart = parts[parts.length - 1];
|
||||
var repoName = lastPart.replace(/\.git$/, "");
|
||||
|
||||
$('#repo_name_input').val(repoName);
|
||||
}
|
||||
else {
|
||||
$('#repo_name_input').val("");
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
</script>
|
||||
${h.end_form()}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue