Compare commits
64 Commits
0.1.2
..
659e78b8ec
| Author | SHA1 | Date | |
|---|---|---|---|
| 659e78b8ec | |||
| 965304c47d | |||
| 77251ba53f | |||
| b9867aea04 | |||
| 0d5d40b5f0 | |||
| 24ae7cec60 | |||
| 1f36d2a8ee | |||
| 7d87f87938 | |||
| 7f780395f2 | |||
| 777f071c7b | |||
| 464eb90c10 | |||
| f5b8938e4e | |||
| 9141811f0f | |||
| 539858b151 | |||
| e39d628852 | |||
| 13f2118267 | |||
| d9726c4235 | |||
| 7240b05feb | |||
| 1424b9c900 | |||
| c828d1283e | |||
| d0aed91490 | |||
| 6f11eeea30 | |||
| 3110653221 | |||
| 2b4695ce9b | |||
| 5827126335 | |||
| 888db5d007 | |||
| 1576084f3c | |||
| 78e59dad96 | |||
| 3ff410734b | |||
| 496baa75c9 | |||
| cd5ac0a52e | |||
| 3812704ba2 | |||
| bf9164f6d3 | |||
| 74e4a6eff4 | |||
| ac2f2559c1 | |||
| 2caa2e716e | |||
| 88883ffc16 | |||
| 8c99a3c403 | |||
| 0f406d8cb0 | |||
| 4b9de29fdc | |||
| 3c287e04d0 | |||
| 73ccb7a559 | |||
| 8feaea1879 | |||
| a7dfd2ccc9 | |||
| 09e95f4ad5 | |||
| 0bc4abf1a7 | |||
| f0153ea396 | |||
| 1c413d630a | |||
| 496a57f48d | |||
| 12b75ae6d3 | |||
| ef6d96e3ac | |||
| ce4a195b4a | |||
| c4473d11e3 | |||
| 84865134b6 | |||
| 09657d9adf | |||
| 44c660e6c2 | |||
| b49660a2eb | |||
| 5fd2ce55fb | |||
| 47b2d73e13 | |||
| b63ae0b9ca | |||
| 723822fbb2 | |||
| 236ec5bb8b | |||
| 86e8d16759 | |||
| 898bf237ac |
@@ -1,12 +1,11 @@
|
|||||||
name: Build executables
|
name: Build executables
|
||||||
|
|
||||||
"on":
|
"on":
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- published
|
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
pull_request:
|
pull_request:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
@@ -149,28 +148,3 @@ jobs:
|
|||||||
name: ${{ env.BIN_NAME }}-linux-${{ matrix.arch }}-musl
|
name: ${{ env.BIN_NAME }}-linux-${{ matrix.arch }}-musl
|
||||||
path: ${{ env.BIN_NAME }}-linux-${{ matrix.arch }}-musl.tar.gz
|
path: ${{ env.BIN_NAME }}-linux-${{ matrix.arch }}-musl.tar.gz
|
||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
release-assets:
|
|
||||||
name: Attach release assets
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs:
|
|
||||||
- windows-x86_64
|
|
||||||
- macos-universal
|
|
||||||
- linux-musl
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
steps:
|
|
||||||
- name: Download packaged binaries
|
|
||||||
uses: actions/download-artifact@v4
|
|
||||||
with:
|
|
||||||
path: release-assets
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Attach binaries to release
|
|
||||||
uses: softprops/action-gh-release@v2
|
|
||||||
with:
|
|
||||||
tag_name: ${{ github.event.release.tag_name }}
|
|
||||||
files: release-assets/*
|
|
||||||
fail_on_unmatched_files: true
|
|
||||||
overwrite_files: true
|
|
||||||
|
|||||||
Generated
+1
-1
@@ -1118,7 +1118,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "refray"
|
name = "refray"
|
||||||
version = "0.1.2"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "refray"
|
name = "refray"
|
||||||
version = "0.1.2"
|
version = "0.1.1"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
authors = ["Azalea"]
|
authors = ["Azalea"]
|
||||||
description = "∞-way read-write git mirroring tool"
|
description = "∞-way read-write git mirroring tool"
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Created becasue github is so unusable and [unreliable](https://red-squares.cian.
|
|||||||
- **read-write mirrors**: Make changes from any provider, and the changes will sync to the others
|
- **read-write mirrors**: Make changes from any provider, and the changes will sync to the others
|
||||||
- **webhook support**: Sync right after push, reduce potential divergence window
|
- **webhook support**: Sync right after push, reduce potential divergence window
|
||||||
- **conflict handling**: Rebase or open pull requests when two platforms diverge
|
- **conflict handling**: Rebase or open pull requests when two platforms diverge
|
||||||
- **tracks state**: Branches/repo deletions and force pushes also sync across platforms (with backup)
|
- **tracks deletions**: Branches/repo deletions sync across platforms (with backup)
|
||||||
- **selective sync**: Sync subset of repos by regex white/black list, or by private/public visibility
|
- **selective sync**: Sync subset of repos by regex white/black list, or by private/public visibility
|
||||||
- **multithreaded**: Process multiple repos simultaneously!
|
- **multithreaded**: Process multiple repos simultaneously!
|
||||||
|
|
||||||
@@ -210,11 +210,6 @@ refray webhook update https://new.example.com/webhook
|
|||||||
|
|
||||||
Issues and pull requests are not mirrored.
|
Issues and pull requests are not mirrored.
|
||||||
|
|
||||||
## TODO
|
|
||||||
|
|
||||||
- [ ] Sync releases and released assets
|
|
||||||
|
|
||||||
|
|
||||||
<!-- ## Sync Semantics
|
<!-- ## Sync Semantics
|
||||||
|
|
||||||
Each mirror group is treated as a set of equivalent namespaces. Repositories are matched by repository name across all endpoints.
|
Each mirror group is treated as a set of equivalent namespaces. Repositories are matched by repository name across all endpoints.
|
||||||
@@ -243,8 +238,6 @@ Conflict resolution strategies are configured per mirror group:
|
|||||||
- `pull_request`: push temporary `refray/conflicts/...` branches and open provider pull requests/merge requests so a person can resolve the divergence.
|
- `pull_request`: push temporary `refray/conflicts/...` branches and open provider pull requests/merge requests so a person can resolve the divergence.
|
||||||
- `auto_rebase_pull_request`: try `auto_rebase` first, then fall back to pull requests if rebase hits a conflict.
|
- `auto_rebase_pull_request`: try `auto_rebase` first, then fall back to pull requests if rebase hits a conflict.
|
||||||
|
|
||||||
GitLab protected branches reject force-pushes unless the protected branch rule allows them. By default, `refray` temporarily enables GitLab project-level force-push permission for protected branches it already knows it must force-push, then restores the prior setting immediately afterward. Set `allow_temporary_gitlab_force_push = false` on a mirror group to disable this behavior.
|
|
||||||
|
|
||||||
When a previously opened conflict pull request is merged, the next sync sees the merged branch as the winning tip, pushes it to the other endpoints, and closes stale `refray/conflicts/...` pull requests for that branch.
|
When a previously opened conflict pull request is merged, the next sync sees the merged branch as the winning tip, pushes it to the other endpoints, and closes stale `refray/conflicts/...` pull requests for that branch.
|
||||||
|
|
||||||
Force-pushes are propagated only when `refray` can infer intent from the previous successful sync state. If a branch previously matched everywhere, one endpoint rewrites that branch to a non-descendant tip, and the other endpoints still have the previous tip, `refray` writes local backup refs and a bundle under the work-dir `backups/` directory before force-pushing the rewritten tip to the other endpoints. If multiple endpoints rewrite the branch differently, or another endpoint also advances independently, the branch is treated as a conflict and skipped.
|
Force-pushes are propagated only when `refray` can infer intent from the previous successful sync state. If a branch previously matched everywhere, one endpoint rewrites that branch to a non-descendant tip, and the other endpoints still have the previous tip, `refray` writes local backup refs and a bundle under the work-dir `backups/` directory before force-pushing the rewritten tip to the other endpoints. If multiple endpoints rewrite the branch differently, or another endpoint also advances independently, the branch is treated as a conflict and skipped.
|
||||||
|
|||||||
@@ -68,8 +68,6 @@ pub struct MirrorConfig {
|
|||||||
pub visibility: Visibility,
|
pub visibility: Visibility,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub conflict_resolution: ConflictResolutionStrategy,
|
pub conflict_resolution: ConflictResolutionStrategy,
|
||||||
#[serde(default = "default_true", skip_serializing_if = "is_true")]
|
|
||||||
pub allow_temporary_gitlab_force_push: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
@@ -197,10 +195,6 @@ fn default_true() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_true(value: &bool) -> bool {
|
|
||||||
*value
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_jobs() -> usize {
|
fn default_jobs() -> usize {
|
||||||
DEFAULT_JOBS
|
DEFAULT_JOBS
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ fn add_sync_group_styled(config: &mut Config, theme: &ColorfulTheme) -> Result<(
|
|||||||
delete_missing,
|
delete_missing,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution,
|
conflict_resolution,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
});
|
});
|
||||||
prompt_webhook_setup_styled(config, theme)?;
|
prompt_webhook_setup_styled(config, theme)?;
|
||||||
|
|
||||||
|
|||||||
@@ -40,11 +40,6 @@ pub struct PullRequestInfo {
|
|||||||
pub url: Option<String>,
|
pub url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Deserialize)]
|
|
||||||
struct GitlabProtectedBranch {
|
|
||||||
allow_force_push: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum WebhookInstallOutcome {
|
pub enum WebhookInstallOutcome {
|
||||||
Created,
|
Created,
|
||||||
@@ -190,38 +185,6 @@ impl<'a> ProviderClient<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gitlab_protected_branch_allow_force_push(
|
|
||||||
&self,
|
|
||||||
endpoint: &EndpointConfig,
|
|
||||||
repo_name: &str,
|
|
||||||
branch: &str,
|
|
||||||
) -> Result<Option<bool>> {
|
|
||||||
if self.site.provider != ProviderKind::Gitlab {
|
|
||||||
bail!("protected branch force-push lookup is only supported for GitLab");
|
|
||||||
}
|
|
||||||
let url = self.gitlab_protected_branch_url(endpoint, repo_name, branch);
|
|
||||||
match self.get_json::<GitlabProtectedBranch>(&url) {
|
|
||||||
Ok(protected_branch) => Ok(Some(protected_branch.allow_force_push)),
|
|
||||||
Err(error) if is_not_found_error(&error) => Ok(None),
|
|
||||||
Err(error) => Err(error),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_gitlab_protected_branch_allow_force_push(
|
|
||||||
&self,
|
|
||||||
endpoint: &EndpointConfig,
|
|
||||||
repo_name: &str,
|
|
||||||
branch: &str,
|
|
||||||
allow_force_push: bool,
|
|
||||||
) -> Result<()> {
|
|
||||||
if self.site.provider != ProviderKind::Gitlab {
|
|
||||||
bail!("protected branch force-push update is only supported for GitLab");
|
|
||||||
}
|
|
||||||
let url = self.gitlab_protected_branch_url(endpoint, repo_name, branch);
|
|
||||||
self.patch_json::<serde_json::Value>(&url, &json!({ "allow_force_push": allow_force_push }))
|
|
||||||
.map(|_| ())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn install_webhook(
|
pub fn install_webhook(
|
||||||
&self,
|
&self,
|
||||||
endpoint: &EndpointConfig,
|
endpoint: &EndpointConfig,
|
||||||
@@ -950,19 +913,6 @@ impl<'a> ProviderClient<'a> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gitlab_protected_branch_url(
|
|
||||||
&self,
|
|
||||||
endpoint: &EndpointConfig,
|
|
||||||
repo_name: &str,
|
|
||||||
branch: &str,
|
|
||||||
) -> String {
|
|
||||||
format!(
|
|
||||||
"{}/protected_branches/{}",
|
|
||||||
self.gitlab_project_url(endpoint, repo_name),
|
|
||||||
urlencoding(branch)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn gitlab_project_url(&self, endpoint: &EndpointConfig, repo_name: &str) -> String {
|
fn gitlab_project_url(&self, endpoint: &EndpointConfig, repo_name: &str) -> String {
|
||||||
let project = format!("{}/{repo_name}", endpoint.namespace);
|
let project = format!("{}/{repo_name}", endpoint.namespace);
|
||||||
format!(
|
format!(
|
||||||
|
|||||||
+3
-196
@@ -5,7 +5,7 @@ use std::sync::{Arc, Mutex, mpsc};
|
|||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::{SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
use anyhow::{Context, Result, anyhow, bail};
|
use anyhow::{Context, Result, bail};
|
||||||
use console::style;
|
use console::style;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
@@ -658,19 +658,6 @@ struct RepoSyncContext<'a> {
|
|||||||
jobs: usize,
|
jobs: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
|
||||||
struct GitlabForcePushTarget {
|
|
||||||
endpoint: EndpointConfig,
|
|
||||||
branch: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
struct GitlabForcePushToggle {
|
|
||||||
endpoint: EndpointConfig,
|
|
||||||
branch: String,
|
|
||||||
previous_allow_force_push: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct RepoSyncOutcome {
|
struct RepoSyncOutcome {
|
||||||
state_update: Option<RepoStateUpdate>,
|
state_update: Option<RepoStateUpdate>,
|
||||||
@@ -1561,27 +1548,13 @@ fn push_repo_refs(
|
|||||||
close_resolved_pull_requests(context, mirror_repo, remotes, repos, &pushed_branch_names)?;
|
close_resolved_pull_requests(context, mirror_repo, remotes, repos, &pushed_branch_names)?;
|
||||||
}
|
}
|
||||||
if !rebased_branch_updates.is_empty() {
|
if !rebased_branch_updates.is_empty() {
|
||||||
push_branch_updates_with_temporary_gitlab_force_push(
|
mirror_repo.push_branch_updates(remotes, &rebased_branch_updates)?;
|
||||||
context,
|
|
||||||
mirror_repo,
|
|
||||||
repo_name,
|
|
||||||
remotes,
|
|
||||||
repos,
|
|
||||||
&rebased_branch_updates,
|
|
||||||
)?;
|
|
||||||
close_resolved_pull_requests(context, mirror_repo, remotes, repos, &rebased_branch_names)?;
|
close_resolved_pull_requests(context, mirror_repo, remotes, repos, &rebased_branch_names)?;
|
||||||
}
|
}
|
||||||
if !force_push_updates.is_empty() {
|
if !force_push_updates.is_empty() {
|
||||||
print_branch_force_pushes(&force_pushes);
|
print_branch_force_pushes(&force_pushes);
|
||||||
backup_force_pushed_branches(context, mirror_repo, repo_name, &force_pushes, current_refs)?;
|
backup_force_pushed_branches(context, mirror_repo, repo_name, &force_pushes, current_refs)?;
|
||||||
push_branch_updates_with_temporary_gitlab_force_push(
|
mirror_repo.push_branch_updates(remotes, &force_push_updates)?;
|
||||||
context,
|
|
||||||
mirror_repo,
|
|
||||||
repo_name,
|
|
||||||
remotes,
|
|
||||||
repos,
|
|
||||||
&force_push_updates,
|
|
||||||
)?;
|
|
||||||
close_resolved_pull_requests(
|
close_resolved_pull_requests(
|
||||||
context,
|
context,
|
||||||
mirror_repo,
|
mirror_repo,
|
||||||
@@ -1827,172 +1800,6 @@ fn force_push_updates(force_pushes: &[BranchForcePush]) -> Vec<BranchUpdate> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_branch_updates_with_temporary_gitlab_force_push(
|
|
||||||
context: &RepoSyncContext<'_>,
|
|
||||||
mirror_repo: &GitMirror,
|
|
||||||
repo_name: &str,
|
|
||||||
remotes: &[RemoteSpec],
|
|
||||||
repos: &[EndpointRepo],
|
|
||||||
updates: &[BranchUpdate],
|
|
||||||
) -> Result<()> {
|
|
||||||
let toggles = enable_temporary_gitlab_force_push(context, repo_name, repos, updates)?;
|
|
||||||
let push_result = mirror_repo.push_branch_updates(remotes, updates);
|
|
||||||
let restore_result = restore_temporary_gitlab_force_push(context, repo_name, &toggles);
|
|
||||||
|
|
||||||
match (push_result, restore_result) {
|
|
||||||
(Ok(()), Ok(())) => Ok(()),
|
|
||||||
(Err(error), Ok(())) => Err(error),
|
|
||||||
(Ok(()), Err(error)) => Err(error),
|
|
||||||
(Err(push_error), Err(restore_error)) => Err(anyhow!(
|
|
||||||
"git push failed and failed to restore temporary GitLab force-push permissions: {push_error:#}; restore error: {restore_error:#}"
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enable_temporary_gitlab_force_push(
|
|
||||||
context: &RepoSyncContext<'_>,
|
|
||||||
repo_name: &str,
|
|
||||||
repos: &[EndpointRepo],
|
|
||||||
updates: &[BranchUpdate],
|
|
||||||
) -> Result<Vec<GitlabForcePushToggle>> {
|
|
||||||
if !context.mirror.allow_temporary_gitlab_force_push || context.dry_run {
|
|
||||||
return Ok(Vec::new());
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut toggles = Vec::new();
|
|
||||||
for target in temporary_gitlab_force_push_targets(context, repos, updates)? {
|
|
||||||
let result = enable_temporary_gitlab_force_push_for_target(context, repo_name, target);
|
|
||||||
match result {
|
|
||||||
Ok(Some(toggle)) => toggles.push(toggle),
|
|
||||||
Ok(None) => {}
|
|
||||||
Err(error) => {
|
|
||||||
let restore_result =
|
|
||||||
restore_temporary_gitlab_force_push(context, repo_name, &toggles);
|
|
||||||
return match restore_result {
|
|
||||||
Ok(()) => Err(error),
|
|
||||||
Err(restore_error) => Err(anyhow!(
|
|
||||||
"failed to enable temporary GitLab force-push permissions: {error:#}; restore error: {restore_error:#}"
|
|
||||||
)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(toggles)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn enable_temporary_gitlab_force_push_for_target(
|
|
||||||
context: &RepoSyncContext<'_>,
|
|
||||||
repo_name: &str,
|
|
||||||
target: GitlabForcePushTarget,
|
|
||||||
) -> Result<Option<GitlabForcePushToggle>> {
|
|
||||||
let site = context.config.site(&target.endpoint.site).unwrap();
|
|
||||||
let client = ProviderClient::new(site)?;
|
|
||||||
let label = target.endpoint.label();
|
|
||||||
match client
|
|
||||||
.gitlab_protected_branch_allow_force_push(&target.endpoint, repo_name, &target.branch)
|
|
||||||
.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to inspect GitLab protected branch {} on {}",
|
|
||||||
target.branch, label
|
|
||||||
)
|
|
||||||
})? {
|
|
||||||
None | Some(true) => Ok(None),
|
|
||||||
Some(false) => {
|
|
||||||
crate::logln!(
|
|
||||||
" {} branch {} on {}",
|
|
||||||
style("allow force-push").cyan().bold(),
|
|
||||||
style(&target.branch).cyan(),
|
|
||||||
style(&label).dim()
|
|
||||||
);
|
|
||||||
client
|
|
||||||
.set_gitlab_protected_branch_allow_force_push(
|
|
||||||
&target.endpoint,
|
|
||||||
repo_name,
|
|
||||||
&target.branch,
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.with_context(|| {
|
|
||||||
format!(
|
|
||||||
"failed to allow GitLab force-push for branch {} on {}",
|
|
||||||
target.branch, label
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
Ok(Some(GitlabForcePushToggle {
|
|
||||||
endpoint: target.endpoint,
|
|
||||||
branch: target.branch,
|
|
||||||
previous_allow_force_push: false,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restore_temporary_gitlab_force_push(
|
|
||||||
context: &RepoSyncContext<'_>,
|
|
||||||
repo_name: &str,
|
|
||||||
toggles: &[GitlabForcePushToggle],
|
|
||||||
) -> Result<()> {
|
|
||||||
let mut failures = Vec::new();
|
|
||||||
for toggle in toggles.iter().rev() {
|
|
||||||
let site = context.config.site(&toggle.endpoint.site).unwrap();
|
|
||||||
let label = toggle.endpoint.label();
|
|
||||||
crate::logln!(
|
|
||||||
" {} branch {} on {}",
|
|
||||||
style("restore force-push").cyan().bold(),
|
|
||||||
style(&toggle.branch).cyan(),
|
|
||||||
style(&label).dim()
|
|
||||||
);
|
|
||||||
let result = ProviderClient::new(site).and_then(|client| {
|
|
||||||
client.set_gitlab_protected_branch_allow_force_push(
|
|
||||||
&toggle.endpoint,
|
|
||||||
repo_name,
|
|
||||||
&toggle.branch,
|
|
||||||
toggle.previous_allow_force_push,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
if let Err(error) = result {
|
|
||||||
failures.push(format!("branch {} on {}: {error:#}", toggle.branch, label));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if failures.is_empty() {
|
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
bail!(
|
|
||||||
"failed to restore temporary GitLab force-push permissions: {}",
|
|
||||||
failures.join("; ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn temporary_gitlab_force_push_targets(
|
|
||||||
context: &RepoSyncContext<'_>,
|
|
||||||
repos: &[EndpointRepo],
|
|
||||||
updates: &[BranchUpdate],
|
|
||||||
) -> Result<Vec<GitlabForcePushTarget>> {
|
|
||||||
let repos_by_remote = endpoint_repos_by_remote_name(context, repos)?;
|
|
||||||
let mut seen = BTreeSet::new();
|
|
||||||
let mut targets = Vec::new();
|
|
||||||
|
|
||||||
for update in updates.iter().filter(|update| update.force) {
|
|
||||||
let Some(endpoint_repo) = repos_by_remote.get(&update.target_remote) else {
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
let site = context.config.site(&endpoint_repo.endpoint.site).unwrap();
|
|
||||||
if site.provider != ProviderKind::Gitlab {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let target = GitlabForcePushTarget {
|
|
||||||
endpoint: endpoint_repo.endpoint.clone(),
|
|
||||||
branch: update.branch.clone(),
|
|
||||||
};
|
|
||||||
if seen.insert(target.clone()) {
|
|
||||||
targets.push(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(targets)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn open_conflict_pull_requests(
|
fn open_conflict_pull_requests(
|
||||||
context: &RepoSyncContext<'_>,
|
context: &RepoSyncContext<'_>,
|
||||||
mirror_repo: &GitMirror,
|
mirror_repo: &GitMirror,
|
||||||
|
|||||||
@@ -59,7 +59,6 @@ fn parses_value_tokens() {
|
|||||||
Some("-archive$".to_string())
|
Some("-archive$".to_string())
|
||||||
);
|
);
|
||||||
assert!(!config.mirrors[0].delete_missing);
|
assert!(!config.mirrors[0].delete_missing);
|
||||||
assert!(config.mirrors[0].allow_temporary_gitlab_force_push);
|
|
||||||
let webhook = config.webhook.unwrap();
|
let webhook = config.webhook.unwrap();
|
||||||
assert!(webhook.install);
|
assert!(webhook.install);
|
||||||
assert_eq!(webhook.url, "https://mirror.example.test/webhook");
|
assert_eq!(webhook.url, "https://mirror.example.test/webhook");
|
||||||
@@ -117,49 +116,6 @@ fn mirror_defaults_to_deleting_missing_repos_for_existing_configs() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert!(config.mirrors[0].delete_missing);
|
assert!(config.mirrors[0].delete_missing);
|
||||||
assert!(config.mirrors[0].allow_temporary_gitlab_force_push);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mirror_can_disable_temporary_gitlab_force_push() {
|
|
||||||
let config: Config = toml::from_str(
|
|
||||||
r#"
|
|
||||||
[[mirrors]]
|
|
||||||
name = "personal"
|
|
||||||
allow_temporary_gitlab_force_push = false
|
|
||||||
|
|
||||||
[[mirrors.endpoints]]
|
|
||||||
site = "github"
|
|
||||||
kind = "user"
|
|
||||||
namespace = "alice"
|
|
||||||
|
|
||||||
[[mirrors.endpoints]]
|
|
||||||
site = "gitlab"
|
|
||||||
kind = "group"
|
|
||||||
namespace = "acme"
|
|
||||||
"#,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(!config.mirrors[0].allow_temporary_gitlab_force_push);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn mirror_serializes_temporary_gitlab_force_push_opt_out() {
|
|
||||||
let mut mirror = mirror_config();
|
|
||||||
mirror.allow_temporary_gitlab_force_push = false;
|
|
||||||
let config = Config {
|
|
||||||
jobs: crate::config::DEFAULT_JOBS,
|
|
||||||
sites: vec![site("github", ProviderKind::Github)],
|
|
||||||
mirrors: vec![mirror],
|
|
||||||
webhook: None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let encoded = toml::to_string(&config).unwrap();
|
|
||||||
let decoded: Config = toml::from_str(&encoded).unwrap();
|
|
||||||
|
|
||||||
assert!(encoded.contains("allow_temporary_gitlab_force_push = false"));
|
|
||||||
assert!(!decoded.mirrors[0].allow_temporary_gitlab_force_push);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -181,7 +137,6 @@ fn validation_rejects_unknown_sites_and_single_endpoint_groups() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -212,7 +167,6 @@ fn validation_rejects_unknown_sites_and_single_endpoint_groups() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -318,7 +272,6 @@ fn validation_rejects_duplicate_mirror_endpoints() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -366,7 +319,6 @@ fn mirror_config() -> MirrorConfig {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -258,7 +258,6 @@ fn wizard_starts_existing_config_at_sync_group_menu() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -289,7 +288,6 @@ fn wizard_can_ask_to_run_full_sync_after_config() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -367,7 +365,6 @@ fn wizard_edits_existing_sync_group_from_menu() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Public,
|
visibility: Visibility::Public,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -459,7 +456,6 @@ fn wizard_prefills_existing_sync_group_when_editing() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -525,7 +521,6 @@ fn wizard_deletes_existing_sync_group_from_menu() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -586,7 +581,6 @@ fn wizard_can_go_back_from_delete_menu() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ where
|
|||||||
delete_missing,
|
delete_missing,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution,
|
conflict_resolution,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
});
|
});
|
||||||
prompt_webhook_setup(reader, writer, config)?;
|
prompt_webhook_setup(reader, writer, config)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -686,119 +686,6 @@ fn delete_repo_deletes_url_encoded_gitlab_project() {
|
|||||||
handle.join().unwrap();
|
handle.join().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gitlab_protected_branch_allow_force_push_reads_setting() {
|
|
||||||
let (api_url, handle) = one_request_server(
|
|
||||||
"200 OK",
|
|
||||||
r#"{"name":"main","allow_force_push":false}"#,
|
|
||||||
|request| {
|
|
||||||
assert!(
|
|
||||||
request.starts_with("GET /projects/parent%2Falice%2Frepo/protected_branches/main "),
|
|
||||||
"request was {request}"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
request
|
|
||||||
.to_ascii_lowercase()
|
|
||||||
.contains("private-token: secret"),
|
|
||||||
"request was {request}"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let site = SiteConfig {
|
|
||||||
api_url: Some(api_url),
|
|
||||||
..site(ProviderKind::Gitlab, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let allow = ProviderClient::new(&site)
|
|
||||||
.unwrap()
|
|
||||||
.gitlab_protected_branch_allow_force_push(
|
|
||||||
&EndpointConfig {
|
|
||||||
site: "gitlab".to_string(),
|
|
||||||
kind: NamespaceKind::Group,
|
|
||||||
namespace: "parent/alice".to_string(),
|
|
||||||
},
|
|
||||||
"repo",
|
|
||||||
"main",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(allow, Some(false));
|
|
||||||
handle.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn gitlab_protected_branch_allow_force_push_treats_404_as_unprotected() {
|
|
||||||
let (api_url, handle) = one_request_server(
|
|
||||||
"404 Not Found",
|
|
||||||
r#"{"message":"404 Not found"}"#,
|
|
||||||
|request| {
|
|
||||||
assert!(
|
|
||||||
request
|
|
||||||
.starts_with("GET /projects/alice%2Frepo/protected_branches/feature%2Fsync "),
|
|
||||||
"request was {request}"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let site = SiteConfig {
|
|
||||||
api_url: Some(api_url),
|
|
||||||
..site(ProviderKind::Gitlab, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
let allow = ProviderClient::new(&site)
|
|
||||||
.unwrap()
|
|
||||||
.gitlab_protected_branch_allow_force_push(
|
|
||||||
&EndpointConfig {
|
|
||||||
site: "gitlab".to_string(),
|
|
||||||
kind: NamespaceKind::User,
|
|
||||||
namespace: "alice".to_string(),
|
|
||||||
},
|
|
||||||
"repo",
|
|
||||||
"feature/sync",
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(allow, None);
|
|
||||||
handle.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn set_gitlab_protected_branch_allow_force_push_patches_branch() {
|
|
||||||
let (api_url, handle) = one_request_server(
|
|
||||||
"200 OK",
|
|
||||||
r#"{"name":"main","allow_force_push":true}"#,
|
|
||||||
|request| {
|
|
||||||
assert!(
|
|
||||||
request
|
|
||||||
.starts_with("PATCH /projects/parent%2Falice%2Frepo/protected_branches/main "),
|
|
||||||
"request was {request}"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
request.contains(r#"{"allow_force_push":true}"#),
|
|
||||||
"request was {request}"
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let site = SiteConfig {
|
|
||||||
api_url: Some(api_url),
|
|
||||||
..site(ProviderKind::Gitlab, None)
|
|
||||||
};
|
|
||||||
|
|
||||||
ProviderClient::new(&site)
|
|
||||||
.unwrap()
|
|
||||||
.set_gitlab_protected_branch_allow_force_push(
|
|
||||||
&EndpointConfig {
|
|
||||||
site: "gitlab".to_string(),
|
|
||||||
kind: NamespaceKind::Group,
|
|
||||||
namespace: "parent/alice".to_string(),
|
|
||||||
},
|
|
||||||
"repo",
|
|
||||||
"main",
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
handle.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn delete_repo_deletes_gitea_repo() {
|
fn delete_repo_deletes_gitea_repo() {
|
||||||
let (api_url, handle) = one_request_server("204 No Content", "", |request| {
|
let (api_url, handle) = one_request_server("204 No Content", "", |request| {
|
||||||
|
|||||||
@@ -448,73 +448,6 @@ fn endpoint_remote_names_do_not_slug_collide() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn temporary_gitlab_force_push_targets_selects_only_gitlab_force_updates() {
|
|
||||||
let mirror = MirrorConfig {
|
|
||||||
endpoints: vec![endpoint("github"), endpoint("gitlab"), endpoint("gitea")],
|
|
||||||
..test_mirror()
|
|
||||||
};
|
|
||||||
let config = Config {
|
|
||||||
jobs: crate::config::DEFAULT_JOBS,
|
|
||||||
sites: vec![
|
|
||||||
site_config("github", crate::config::ProviderKind::Github),
|
|
||||||
site_config("gitlab", crate::config::ProviderKind::Gitlab),
|
|
||||||
site_config("gitea", crate::config::ProviderKind::Gitea),
|
|
||||||
],
|
|
||||||
mirrors: vec![mirror.clone()],
|
|
||||||
webhook: None,
|
|
||||||
};
|
|
||||||
let context = RepoSyncContext {
|
|
||||||
config: &config,
|
|
||||||
mirror: &mirror,
|
|
||||||
work_dir: Path::new("."),
|
|
||||||
redactor: Redactor::new(Vec::new()),
|
|
||||||
dry_run: false,
|
|
||||||
jobs: crate::config::DEFAULT_JOBS,
|
|
||||||
};
|
|
||||||
let updates = vec![
|
|
||||||
BranchUpdate {
|
|
||||||
branch: "main".to_string(),
|
|
||||||
sha: "a".repeat(40),
|
|
||||||
target_remote: remote_key("gitlab"),
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
BranchUpdate {
|
|
||||||
branch: "main".to_string(),
|
|
||||||
sha: "a".repeat(40),
|
|
||||||
target_remote: remote_key("gitlab"),
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
BranchUpdate {
|
|
||||||
branch: "feature".to_string(),
|
|
||||||
sha: "b".repeat(40),
|
|
||||||
target_remote: remote_key("gitlab"),
|
|
||||||
force: false,
|
|
||||||
},
|
|
||||||
BranchUpdate {
|
|
||||||
branch: "main".to_string(),
|
|
||||||
sha: "c".repeat(40),
|
|
||||||
target_remote: remote_key("github"),
|
|
||||||
force: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
let repos = vec![
|
|
||||||
endpoint_repo("github"),
|
|
||||||
endpoint_repo("gitlab"),
|
|
||||||
endpoint_repo("gitea"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let targets = temporary_gitlab_force_push_targets(&context, &repos, &updates).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
targets,
|
|
||||||
vec![GitlabForcePushTarget {
|
|
||||||
endpoint: endpoint("gitlab"),
|
|
||||||
branch: "main".to_string(),
|
|
||||||
}]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn targeted_endpoint_repos_synthesize_clone_urls_without_listing() {
|
fn targeted_endpoint_repos_synthesize_clone_urls_without_listing() {
|
||||||
let mirror = MirrorConfig {
|
let mirror = MirrorConfig {
|
||||||
@@ -531,7 +464,6 @@ fn targeted_endpoint_repos_synthesize_clone_urls_without_listing() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: crate::config::Visibility::Private,
|
visibility: crate::config::Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
};
|
};
|
||||||
let config = Config {
|
let config = Config {
|
||||||
jobs: crate::config::DEFAULT_JOBS,
|
jobs: crate::config::DEFAULT_JOBS,
|
||||||
@@ -671,7 +603,6 @@ fn test_mirror() -> MirrorConfig {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: crate::config::Visibility::Private,
|
visibility: crate::config::Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,17 +618,6 @@ fn remote_key(site: &str) -> String {
|
|||||||
remote_name_for_endpoint(&endpoint(site))
|
remote_name_for_endpoint(&endpoint(site))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn site_config(name: &str, provider: crate::config::ProviderKind) -> crate::config::SiteConfig {
|
|
||||||
crate::config::SiteConfig {
|
|
||||||
name: name.to_string(),
|
|
||||||
provider,
|
|
||||||
base_url: format!("https://{name}.invalid"),
|
|
||||||
api_url: None,
|
|
||||||
token: crate::config::TokenConfig::Value("token".to_string()),
|
|
||||||
git_username: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_repo(site: &str) -> EndpointRepo {
|
fn endpoint_repo(site: &str) -> EndpointRepo {
|
||||||
EndpointRepo {
|
EndpointRepo {
|
||||||
endpoint: endpoint(site),
|
endpoint: endpoint(site),
|
||||||
|
|||||||
@@ -118,7 +118,6 @@ fn matches_jobs_by_provider_and_namespace() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -147,7 +146,6 @@ fn matching_jobs_respects_repo_name_filters() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
};
|
};
|
||||||
let config = Config {
|
let config = Config {
|
||||||
jobs: crate::config::DEFAULT_JOBS,
|
jobs: crate::config::DEFAULT_JOBS,
|
||||||
@@ -367,7 +365,6 @@ fn uninstall_webhooks_skips_blocked_provider_access() {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}],
|
}],
|
||||||
webhook: None,
|
webhook: None,
|
||||||
};
|
};
|
||||||
@@ -722,7 +719,6 @@ fn filtered_mirror() -> MirrorConfig {
|
|||||||
delete_missing: true,
|
delete_missing: true,
|
||||||
visibility: Visibility::Private,
|
visibility: Visibility::Private,
|
||||||
conflict_resolution: ConflictResolutionStrategy::Fail,
|
conflict_resolution: ConflictResolutionStrategy::Fail,
|
||||||
allow_temporary_gitlab_force_push: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user