package API::GitForge::GitLab; # ABSTRACT: common git forge operations using the GitLab API # # Copyright (C) 2020 Sean Whitton # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . =head1 DESCRIPTION See L and L for how to use this class. =cut use 5.028; use strict; use warnings; use Role::Tiny::With; use Carp; use GitLab::API::v4; with "API::GitForge::Role::GitForge"; sub _make_api { my $self = shift; my %opts = (url => "https://" . $self->{_domain} . "/api/v4"); $opts{private_token} = $self->{_access_token} if exists $self->{_access_token}; $self->{_api} = GitLab::API::v4->new(%opts); } sub _ensure_fork { my ($self, $upstream) = @_; my ($path, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; my @user_repos; my $update_user_repos = sub { @user_repos = @{ $self->{_api}->projects({ search => "$user/$repo" }) }; }; my $repo_exists = sub { grep { $_->{path_with_namespace} eq "$user/$repo" } @user_repos; }; &$update_user_repos; if (&$repo_exists) { $self->_assert_fork_has_parent($upstream); } else { $self->{_api}->fork_project("$path/$repo"); until (&$repo_exists) { sleep 5; &$update_user_repos; } } return "https://" . $self->{_domain} . "/$user/$repo.git"; } sub _assert_fork_has_parent { my ($self, $upstream) = @_; my ($path, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; my $fork = $self->{_api}->project("$user/$repo"); $fork->{forked_from_project}{path_with_namespace} eq $path . "/" . $repo or croak "$user/$repo does not have parent $upstream; don't know what to do"; } sub _clean_config_repo { my ($self, $upstream) = @_; my (undef, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; $self->{_api}->edit_project( "$user/$repo", { issues_access_level => "disabled", merge_requests_access_level => "disabled", }); } sub _clean_config_fork { my ($self, $upstream) = @_; my (undef, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; $self->{_api}->edit_project( "$user/$repo", { default_branch => "gitforge", description => "Temporary fork for merge request(s)", issues_access_level => "disabled", # merge requests have to be enabled in the fork in order # to submit merge requests to the upstream repo from which # we forked, it seems merge_requests_access_level => "enabled", }); } sub _ensure_repo { my ($self, $target) = @_; my ($ns, $repo) = _extract_project_id($target); return if $self->{_api}->project($target); my $namespace = $self->{_api}->namespace($ns) or croak "invalid project namespace $ns"; $self->{_api} ->create_project({ name => $repo, namespace_id => $namespace->{id} }); } sub _nuke_fork { my ($self, $upstream) = @_; $self->_assert_fork_has_parent($upstream); my (undef, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; $self->{_api}->delete_project("$user/$repo"); } sub _ensure_fork_branch_unprotected { my ($self, $upstream, $branch) = @_; my (undef, $repo) = _extract_project_id($upstream); my $user = $self->{_api}->current_user->{username}; return unless $self->{_api}->protected_branch("$user/$repo", $branch); $self->{_api}->unprotect_branch("$user/$repo", $branch); } sub _extract_project_id { my $project = shift; $project =~ s#(?:\.git)?/?$##; $project =~ m#/([^/]+)$#; ($`, $1); } 1;