Add push to remote mirror repository (#15157)
* Added push mirror model. * Integrated push mirror into queue. * Moved methods into own file. * Added basic implementation. * Mirror wiki too. * Removed duplicated method. * Get url for different remotes. * Added migration. * Unified remote url access. * Add/Remove push mirror remotes. * Prevent hangs with missing credentials. * Moved code between files. * Changed sanitizer interface. * Added push mirror backend methods. * Only update the mirror remote. * Limit refs on push. * Added UI part. * Added missing table. * Delete mirror if repository gets removed. * Changed signature. Handle object errors. * Added upload method. * Added "upload" unit tests. * Added transfer adapter unit tests. * Send correct headers. * Added pushing of LFS objects. * Added more logging. * Simpler body handling. * Process files in batches to reduce HTTP calls. * Added created timestamp. * Fixed invalid column name. * Changed name to prevent xorm auto setting. * Remove table header im empty. * Strip exit code from error message. * Added docs page about mirroring. * Fixed date. * Fixed merge errors. * Moved test to integrations. * Added push mirror test. * Added test.
This commit is contained in:
parent
5d113bdd19
commit
440039c0cc
39 changed files with 2468 additions and 885 deletions
|
@ -36,7 +36,7 @@
|
|||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}}
|
||||
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{(MirrorRemoteAddress $.Mirror).Address}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{(MirrorRemoteAddress $.Mirror).Address}}{{end}}</a></div>{{end}}
|
||||
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
|
||||
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}}
|
||||
</div>
|
||||
|
|
|
@ -70,81 +70,165 @@
|
|||
|
||||
</div>
|
||||
|
||||
{{if .Repository.IsMirror}}
|
||||
{{if or .Repository.IsMirror (not .DisabledMirrors)}}
|
||||
<h4 class="ui top attached header">
|
||||
{{.i18n.Tr "repo.settings.mirror_settings"}}
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<form class="ui form" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="mirror">
|
||||
<div class="inline field {{if .Err_EnablePrune}}error{{end}}">
|
||||
<label>{{.i18n.Tr "repo.mirror_prune"}}</label>
|
||||
<div class="ui checkbox">
|
||||
<input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline field {{if .Err_Interval}}error{{end}}">
|
||||
<label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label>
|
||||
<input id="interval" name="interval" value="{{.MirrorInterval}}">
|
||||
</div>
|
||||
<div class="field {{if .Err_MirrorAddress}}error{{end}}">
|
||||
<label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label>
|
||||
<input id="mirror_address" name="mirror_address" value="{{MirrorFullAddress .Mirror}}" required>
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>
|
||||
</div>
|
||||
<details class="ui optional field" {{if .Err_Auth}}open{{else if (MirrorUserName .Mirror)}}open{{end}}>
|
||||
<summary class="p-2">
|
||||
{{.i18n.Tr "repo.need_auth"}}
|
||||
</summary>
|
||||
<div class="p-2">
|
||||
<div class="inline field {{if .Err_Auth}}error{{end}}">
|
||||
<label for="mirror_username">{{.i18n.Tr "username"}}</label>
|
||||
<input id="mirror_username" name="mirror_username" value="{{MirrorUserName .Mirror}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
|
||||
</div>
|
||||
<input class="fake" type="password">
|
||||
<div class="inline field {{if .Err_Auth}}error{{end}}">
|
||||
<label for="mirror_password">{{.i18n.Tr "password"}}</label>
|
||||
<input id="mirror_password" name="mirror_password" type="password" placeholder="{{if MirrorPassword .Mirror }}{{.i18n.Tr "repo.mirror_password_placeholder"}}{{else}}{{.i18n.Tr "repo.mirror_password_blank_placeholder"}}{{end}}" value="" {{if not .mirror_password}}data-need-clear="true"{{end}} autocomplete="off">
|
||||
</div>
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_password_help"}}</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
{{if .LFSStartServer}}
|
||||
<div class="inline field">
|
||||
<label>{{.i18n.Tr "repo.mirror_lfs"}}</label>
|
||||
<div class="ui checkbox">
|
||||
<input id="mirror_lfs" name="mirror_lfs" type="checkbox" {{if .Mirror.LFS}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.mirror_lfs_desc"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field {{if .Err_LFSEndpoint}}error{{end}}">
|
||||
<label for="mirror_lfs_endpoint">{{.i18n.Tr "repo.mirror_lfs_endpoint"}}</label>
|
||||
<input id="mirror_lfs_endpoint" name="mirror_lfs_endpoint" value="{{.Mirror.LFSEndpoint}}" placeholder="{{.i18n.Tr "repo.migrate_options_lfs_endpoint.placeholder"}}">
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | Str2html}}</p>
|
||||
</div>
|
||||
{{$.i18n.Tr "repo.settings.mirror_settings.docs" | Safe}}
|
||||
<table class="ui table">
|
||||
{{if or .Repository.IsMirror .Repository.PushMirrors}}
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:40%">{{$.i18n.Tr "repo.settings.mirror_settings.mirrored_repository"}}</th>
|
||||
<th>{{$.i18n.Tr "repo.settings.mirror_settings.direction"}}</th>
|
||||
<th>{{$.i18n.Tr "repo.settings.mirror_settings.last_update"}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
{{end}}
|
||||
{{if .Repository.IsMirror}}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{(MirrorRemoteAddress .Mirror).Address}}</td>
|
||||
<td>{{$.i18n.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
|
||||
<td>{{.Mirror.UpdatedUnix.AsTime}}</td>
|
||||
<td class="right aligned">
|
||||
<form method="post" style="display: inline-block">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="mirror-sync">
|
||||
<button class="ui blue tiny button inline text-thin">{{$.i18n.Tr "repo.settings.sync_mirror"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<form class="ui form" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="mirror">
|
||||
<div class="inline field {{if .Err_EnablePrune}}error{{end}}">
|
||||
<label>{{.i18n.Tr "repo.mirror_prune"}}</label>
|
||||
<div class="ui checkbox">
|
||||
<input id="enable_prune" name="enable_prune" type="checkbox" {{if .MirrorEnablePrune}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.mirror_prune_desc"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline field {{if .Err_Interval}}error{{end}}">
|
||||
<label for="interval">{{.i18n.Tr "repo.mirror_interval"}}</label>
|
||||
<input id="interval" name="interval" value="{{.MirrorInterval}}">
|
||||
</div>
|
||||
{{$address := MirrorRemoteAddress .Mirror}}
|
||||
<div class="field {{if .Err_MirrorAddress}}error{{end}}">
|
||||
<label for="mirror_address">{{.i18n.Tr "repo.mirror_address"}}</label>
|
||||
<input id="mirror_address" name="mirror_address" value="{{$address.Address}}" required>
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>
|
||||
</div>
|
||||
<details class="ui optional field" {{if or .Err_Auth $address.Username}}open{{end}}>
|
||||
<summary class="p-2">
|
||||
{{.i18n.Tr "repo.need_auth"}}
|
||||
</summary>
|
||||
<div class="p-2">
|
||||
<div class="inline field {{if .Err_Auth}}error{{end}}">
|
||||
<label for="mirror_username">{{.i18n.Tr "username"}}</label>
|
||||
<input id="mirror_username" name="mirror_username" value="{{$address.Username}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
|
||||
</div>
|
||||
<input class="fake" type="password">
|
||||
<div class="inline field {{if .Err_Auth}}error{{end}}">
|
||||
<label for="mirror_password">{{.i18n.Tr "password"}}</label>
|
||||
<input id="mirror_password" name="mirror_password" type="password" placeholder="{{if $address.Password}}{{.i18n.Tr "repo.mirror_password_placeholder"}}{{else}}{{.i18n.Tr "repo.mirror_password_blank_placeholder"}}{{end}}" value="" {{if not .mirror_password}}data-need-clear="true"{{end}} autocomplete="off">
|
||||
</div>
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_password_help"}}</p>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="ui divider"></div>
|
||||
|
||||
<form class="ui form" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="mirror-sync">
|
||||
<div class="inline field">
|
||||
<label>{{.i18n.Tr "repo.mirror_last_synced"}}</label>
|
||||
<span>{{.Mirror.UpdatedUnix.AsTime}}</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui blue button">{{$.i18n.Tr "repo.settings.sync_mirror"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
{{if .LFSStartServer}}
|
||||
<div class="inline field">
|
||||
<label>{{.i18n.Tr "repo.mirror_lfs"}}</label>
|
||||
<div class="ui checkbox">
|
||||
<input id="mirror_lfs" name="mirror_lfs" type="checkbox" {{if .Mirror.LFS}}checked{{end}}>
|
||||
<label>{{.i18n.Tr "repo.mirror_lfs_desc"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field {{if .Err_LFSEndpoint}}error{{end}}">
|
||||
<label for="mirror_lfs_endpoint">{{.i18n.Tr "repo.mirror_lfs_endpoint"}}</label>
|
||||
<input id="mirror_lfs_endpoint" name="mirror_lfs_endpoint" value="{{.Mirror.LFSEndpoint}}" placeholder="{{.i18n.Tr "repo.migrate_options_lfs_endpoint.placeholder"}}">
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_lfs_endpoint_desc" "https://github.com/git-lfs/git-lfs/blob/main/docs/api/server-discovery.md#server-discovery" | Str2html}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "repo.settings.update_settings"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<thead><tr><th colspan="4"></th></tr></thead>
|
||||
{{end}}
|
||||
<tbody>
|
||||
{{range .Repository.PushMirrors}}
|
||||
<tr>
|
||||
{{$address := MirrorRemoteAddress .}}
|
||||
<td>{{$address.Address}}</td>
|
||||
<td>{{$.i18n.Tr "repo.settings.mirror_settings.direction.push"}}</td>
|
||||
<td>{{if .LastUpdateUnix}}{{.LastUpdateUnix.AsTime}}{{else}}{{$.i18n.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip="{{.LastError}}">{{$.i18n.Tr "error"}}</div>{{end}}</td>
|
||||
<td class="right aligned">
|
||||
<form method="post" style="display: inline-block">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="push-mirror-remove">
|
||||
<input type="hidden" name="push_mirror_id" value="{{.ID}}">
|
||||
<button class="ui basic red tiny button inline text-thin">{{$.i18n.Tr "remove"}}</button>
|
||||
</form>
|
||||
<form method="post" style="display: inline-block">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="push-mirror-sync">
|
||||
<input type="hidden" name="push_mirror_id" value="{{.ID}}">
|
||||
<button class="ui blue tiny button inline text-thin">{{$.i18n.Tr "repo.settings.sync_mirror"}}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{{else}}
|
||||
<tr>
|
||||
<td>{{$.i18n.Tr "repo.settings.mirror_settings.push_mirror.none"}}</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<form class="ui form" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="action" value="push-mirror-add">
|
||||
<div class="field {{if .Err_PushMirrorAddress}}error{{end}}">
|
||||
<label for="push_mirror_address">{{.i18n.Tr "repo.settings.mirror_settings.push_mirror.remote_url"}}</label>
|
||||
<input id="push_mirror_address" name="push_mirror_address" value="{{.push_mirror_address}}" required>
|
||||
<p class="help">{{.i18n.Tr "repo.mirror_address_desc"}}</p>
|
||||
</div>
|
||||
<details class="ui optional field" {{if or .Err_PushMirrorAuth .push_mirror_username}}open{{end}}>
|
||||
<summary class="p-2">
|
||||
{{.i18n.Tr "repo.need_auth"}}
|
||||
</summary>
|
||||
<div class="p-2">
|
||||
<div class="inline field {{if .Err_PushMirrorAuth}}error{{end}}">
|
||||
<label for="push_mirror_username">{{.i18n.Tr "username"}}</label>
|
||||
<input id="push_mirror_username" name="push_mirror_username" value="{{.push_mirror_username}}">
|
||||
</div>
|
||||
<input class="fake" type="password">
|
||||
<div class="inline field {{if .Err_PushMirrorAuth}}error{{end}}">
|
||||
<label for="push_mirror_password">{{.i18n.Tr "password"}}</label>
|
||||
<input id="push_mirror_password" name="push_mirror_password" type="password" value="{{.push_mirror_password}}" autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
</details>
|
||||
<div class="inline field {{if .Err_PushMirrorInterval}}error{{end}}">
|
||||
<label for="push_mirror_interval">{{.i18n.Tr "repo.mirror_interval"}}</label>
|
||||
<input id="push_mirror_interval" name="push_mirror_interval" value="{{if .push_mirror_interval}}{{.push_mirror_interval}}{{else}}{{.DefaultMirrorInterval}}{{end}}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<button class="ui green button">{{$.i18n.Tr "repo.settings.mirror_settings.push_mirror.add"}}</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue