diff options
Diffstat (limited to '')
-rw-r--r-- | lib/Devscripts/Salsa/merge_request.pm | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/lib/Devscripts/Salsa/merge_request.pm b/lib/Devscripts/Salsa/merge_request.pm new file mode 100644 index 0000000..b76c6e8 --- /dev/null +++ b/lib/Devscripts/Salsa/merge_request.pm @@ -0,0 +1,174 @@ +# Creates a merge request from current directory (or using parameters) +package Devscripts::Salsa::merge_request; + +use strict; +use Devscripts::Output; +use Dpkg::IPC; +use Moo::Role; + +with 'Devscripts::Salsa::search_project'; + +sub merge_request { + my ($self, $dst_project, $dst_branch) = @_; + my $src_branch = $self->config->mr_src_branch; + my $src_project = $self->config->mr_src_project; + $dst_project ||= $self->config->mr_dst_project; + $dst_branch ||= $self->config->mr_dst_branch; + my $title = $self->config->mr_title; + my $desc = $self->config->mr_desc; + + if ($src_branch) { + unless ($src_project and $dst_project) { + ds_warn "--mr-src-project and --mr-src-project " + . "are required when --mr-src-branch is set"; + return 1; + } + unless ($src_project =~ m#/#) { + $src_project = $self->project2path($src_project); + } + } else { # Use current repository to find elements + ds_verbose "using current branch as source"; + my $out; + unless ($src_project) { + # 1. Verify that repo is ready + spawn( + exec => [qw(git status -s -b -uno)], + wait_child => 1, + to_string => \$out + ); + chomp $out; + # Case "rebased" + if ($out =~ /\[/) { + ds_warn "Current branch isn't pushed, aborting:\n"; + return 1; + } + # Case else: nothing after src...dst + unless ($out =~ /\s(\S+)\.\.\.(\S+)/s) { + ds_warn + "Current branch as no origin or isn't pushed, aborting"; + return 1; + } + # 2. Set source branch to current branch + $src_branch ||= $1; + ds_verbose "Found current branch: $src_branch"; + } + unless ($src_project and $dst_project) { + # Check remote links + spawn( + exec => [qw(git remote --verbose show)], + wait_child => 1, + to_string => \$out, + ); + my $origin = $self->config->api_url; + $origin =~ s#api/v4$##; + # 3. Set source project using "origin" target + unless ($src_project) { + if ($out + =~ /origin\s+(?:\Q$self->{config}->{git_server_url}\E|\Q$origin\E)(\S*)/m + ) { + $src_project = $1; + $src_project =~ s/\.git$//; + } else { + ds_warn +"Unable to find project origin, set it using --mr-src-project"; + return 1; + } + } + # 4. Steps to find destination project: + # - command-line + # - GitLab API (search for "forked_from_project" + # - "upstream" in git remote + # - use source project as destination project + + # 4.1. Stop if dest project has been given in command line + unless ($dst_project) { + my $project = $self->api->project($src_project); + + # 4.2. Search original project from GitLab API + if ($project->{forked_from_project}) { + $dst_project + = $project->{forked_from_project}->{path_with_namespace}; + } + if ($dst_project) { + ds_verbose "Project was forked from $dst_project"; + + # 4.3. Search for an "upstream" target in `git remote` + } elsif ($out + =~ /upstream\s+(?:\Q$self->{config}->{git_server_url}\E|\Q$origin\E)(\S*)/m + ) { + $dst_project = $1; + $dst_project =~ s/\.git$//; + ds_verbose 'Use "upstream" target as dst project'; + # 4.4. Use source project as destination + } else { + ds_warn +"No upstream target found, using current project as target"; + $dst_project = $src_project; + } + ds_verbose "Use $dst_project as dest project"; + } + } + # 5. Search for MR title and desc + unless ($title) { + ds_warn "Title not set, using last commit"; + spawn( + exec => ['git', 'show', '--format=format:%s###%b'], + wait_child => 1, + to_string => \$out, + ); + $out =~ s/\ndiff.*$//s; + my ($t, $d) = split /###/, $out; + chomp $d; + $title = $t; + ds_verbose "Title set to $title"; + $desc ||= $d; + # Replace all bug links by markdown links + if ($desc) { + $desc =~ s@#(\d{6,})\b@[#$1](https://bugs.debian.org/$1)@mg; + ds_verbose "Desc set to $desc"; + } + } + } + if ($dst_project eq 'same') { + $dst_project = $src_project; + } + my $src = $self->api->project($src_project); + unless ($title) { + ds_warn "Title is required"; + return 1; + } + unless ($src and $src->{id}) { + ds_warn "Target project not found $src_project"; + return 1; + } + my $dst; + if ($dst_project) { + $dst = $self->api->project($dst_project); + unless ($dst and $dst->{id}) { + ds_warn "Target project not found"; + return 1; + } + } + return 1 + if ( + ds_prompt( +"You're going to push an MR to $dst_project:$dst_branch. Continue (Y/n)" + ) =~ refuse + ); + my $res = $self->api->create_merge_request( + $src->{id}, + { + source_branch => $src_branch, + target_branch => $dst_branch, + title => $title, + remove_source_branch => $self->config->mr_remove_source_branch, + squash => $self->config->mr_allow_squash, + ($dst ? (target_project_id => $dst->{id}) : ()), + ($desc ? (description => $desc) : ()), + }); + ds_warn "MR '$title' posted:"; + ds_warn $res->{web_url}; + return 0; +} + +1; |