[med-svn] [Git][med-team/community/helper-scripts][master] Commit script to configure all repositories adapted from Debian GIS team
Andreas Tille
gitlab at salsa.debian.org
Mon Feb 24 19:56:47 GMT 2020
Andreas Tille pushed to branch master at Debian Med / community / helper-scripts
Commits:
fbed735f by Andreas Tille at 2020-02-24T20:56:11+01:00
Commit script to configure all repositories adapted from Debian GIS team
- - - - -
1 changed file:
- + salsa-configure-repositories.pl
Changes:
=====================================
salsa-configure-repositories.pl
=====================================
@@ -0,0 +1,742 @@
+#!/usr/bin/perl -w
+#
+# Copyright 2018, Bas Couwenberg <sebastic at debian.org>
+#
+# 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 2 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, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+use strict;
+use Data::Dumper;
+use File::Basename;
+use File::Slurp;
+use Getopt::Long qw(:config bundling no_ignore_case);
+use HTTP::Request::Common;
+use JSON;
+use LWP::UserAgent;
+use URI::Escape;
+
+$|=1;
+
+my %cfg = (
+ config_file => '',
+ url => 'https://salsa.debian.org/api/v4',
+ token => '',
+ token_file => $ENV{HOME}.'/.salsa-token',
+ namespace => '2799', # curl -s -f -XGET "https://salsa.debian.org/api/v4/groups/med-team?with_projects=false" | jq '.id'
+ user => '',
+ group => 'med-team',
+ ci_cfg_path => 'debian/.gitlab-ci.yml',
+ email_recipients => 'debian-med-commit at lists.alioth.debian.org',
+ irc_recipients => '#debian-med',
+ kgb_baseurl => 'http://kgb.debian.net:9418/webhook/',
+ kgb_network => 'oftc',
+ project => '',
+ debug => 1,
+ verbose => 0,
+ help => 0,
+ );
+
+my $result = GetOptions(
+ 'C|config-file=s' => \$cfg{config_file},
+ 'u|url=s' => \$cfg{url},
+ 'n|namespace=i' => \$cfg{namespace},
+ 'U|user=s' => \$cfg{user},
+ 'G|group=s' => \$cfg{group},
+ 't|token=s' => \$cfg{token},
+ 'c|ci-cfg-path=s' => \$cfg{ci_cfg_path},
+ 'e|email-recipients=s' => \$cfg{email_recipients},
+ 'i|irc-recipients=s' => \$cfg{irc_recipients},
+ 'k|kgb-baseurl=s' => \$cfg{kgb_baseurl},
+ 'N|kgb-network=s' => \$cfg{kgb_network},
+ 'p|project=s' => \$cfg{project},
+ 'd|debug' => \$cfg{debug},
+ 'v|verbose' => \$cfg{verbose},
+ 'h|help' => \$cfg{help},
+ );
+
+if($cfg{config_file}) {
+ if(!-r $cfg{config_file}) {
+ print "Error: Cannot read config file: $cfg{config_file}\n";
+ exit 1;
+ }
+
+ my $data = from_json(read_file($cfg{config_file}));
+
+ foreach my $key (keys %cfg) {
+ if(exists $data->{$key}) {
+ $cfg{$key} = $data->{$key};
+ }
+ }
+}
+
+if(!$result || $cfg{help}) {
+ print STDERR "\n" if(!$result);
+
+ print "Usage: ". basename($0) ." [OPTIONS]\n\n";
+ print "Options:\n";
+ print "-C, --config-file <PATH> Path to config file\n";
+ print "\n";
+ print "-u, --url <URL> Salsa URL ($cfg{url})\n";
+ print "-t, --token <STRING> Salsa token (". '*' x length($cfg{token}) .")\n";
+ print "-T, --token-file <PATH> Salsa token file ($cfg{token_file})\n";
+ print "\n";
+ print "-n, --namespace <ID> Salsa namespace ($cfg{namespace})\n";
+ print "-U, --user <NAME> Salsa user path ($cfg{user})\n";
+ print "-G, --group <NAME> Salsa group path ($cfg{group})\n";
+ print "\n";
+ print "-c, --ci-cfg-path <PATH> CI Config Path ($cfg{ci_cfg_path})\n";
+ print "-e, --email-recipients <EMAIL> Email recipients ($cfg{email_recipients})\n";
+ print "-i, --irc-recipients <CHANNEL> IRC recipients ($cfg{irc_recipients})\n";
+ print "-k, --kgb-baseurl <URL> KGB base URL ($cfg{kgb_baseurl})\n";
+ print "-N, --kgb-network <NAME> KGB network name ($cfg{kgb_network})\n";
+ print "\n";
+ print "-p, --project <NAME> Project to configure, instead of all\n";
+ print "\n";
+ print "-d, --debug Enable debug output\n";
+ print "-v, --verbose Enable verbose output\n";
+ print "-h, --help Display this usage information\n";
+
+ exit 1;
+}
+
+my $ua = new LWP::UserAgent(agent => basename($0));
+
+$cfg{token} = get_private_token() if(!$cfg{token});
+
+if(!$cfg{token}) {
+ print "Error: No private token specified with file nor option!\n";
+
+ exit 1;
+}
+
+$cfg{namespace} = get_namespace() if(!$cfg{namespace});
+
+if(!$cfg{namespace}) {
+ print "Error: No namespace found! Specify namespace, user or group!\n";
+
+ exit 1;
+}
+
+# Get projects for team
+my $team_projects = get_team_projects();
+
+print "team projects:\n".Dumper($team_projects) if($cfg{debug});
+
+# Get service settings
+foreach my $project (@{$team_projects}) {
+ next if($cfg{project} && lc($project->{name}) ne lc($cfg{project}) && lc($project->{path}) ne lc($cfg{project}));
+
+ print "\nProject: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ # Disable issues
+ if(!disable_issues($project)) {
+ exit 1;
+ }
+
+ # Set merge method
+ if(!set_merge_method($project)) {
+ exit 1;
+ }
+
+ # Disable Show link to create/view merge request when pushing from the command line
+ if(!disable_printing_merge_request_link($project)) {
+ exit 1;
+ }
+
+ # Set CI config path
+ if(!set_ci_config_path($project)) {
+ exit 1;
+ }
+
+ # Configure Emails on push service
+ if(!configure_emails_on_push_service($project)) {
+ exit 1;
+ }
+
+ # Configure Irker service
+ if(!configure_irker_service($project)) {
+ exit 1;
+ }
+
+ # Configure KGB webhook
+ if(!configure_kgb_webhook($project)) {
+ exit 1;
+ }
+}
+
+exit 0;
+
+################################################################################
+# Subroutines
+
+sub get_private_token {
+ my $token = '';
+
+ if(!$cfg{token_file}) {
+ print "No private token file to check.\n" if($cfg{verbose});
+
+ return $token;
+ }
+
+ print "Checking file for private token: $cfg{token_file}\n" if($cfg{debug});
+
+ if(-r $cfg{token_file}) {
+ my $mode = sprintf("%o", (stat($cfg{token_file}))[2] & 07777);
+ if($mode != 600) {
+ print "Warning: Permissions on $cfg{token_file} are not 600 (-rw-------)\n";
+ }
+
+ print "Loading private token from: $cfg{token_file} ($mode)\n" if($cfg{debug});
+
+ open(F, $cfg{token_file}) || die "Error: Failed to open file: $cfg{token_file} ($!)";
+ while(<F>) {
+ next if(/^\s*#|^\s*$/);
+
+ if(/^(\S{10,})\s*/) {
+ $token = $1;
+ last;
+ }
+ }
+ close F;
+ }
+ else {
+ print "File not readable: $cfg{token_file}\n" if($cfg{debug});
+ }
+
+ return $token;
+}
+
+sub get_namespace {
+ my $url = $cfg{url}.'/namespaces';
+
+ print "Getting namespaces\n" if($cfg{verbose});
+
+ my $req = new HTTP::Request(GET => $url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ my $data = decode_json($content);
+
+ print "namespaces:\n".Dumper($data) if($cfg{debug});
+
+ my $namespace = '';
+
+ foreach my $ns (@{$data}) {
+ if($cfg{user} && $ns->{kind} eq 'user' && $ns->{path} eq $cfg{user}) {
+ print "Found namespace: ". $ns->{id} ." for user: $cfg{user}\n" if($cfg{verbose});
+
+ $namespace = $ns->{id};
+
+ last;
+ }
+ elsif($cfg{group} && $ns->{kind} eq 'group' && $ns->{path} eq $cfg{group}) {
+ print "Found namespace: ". $ns->{id} ." for group: $cfg{group}\n" if($cfg{verbose});
+
+ $namespace = $ns->{id};
+
+ last;
+ }
+ }
+
+ return $namespace;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ exit 1;
+ }
+}
+
+sub get_team_projects {
+ my ($projects, $page) = @_;
+
+ $projects = [] if(!$projects);
+ $page = 1 if(!$page);
+
+ my $url = $cfg{url}.'/groups/'.uri_escape($cfg{namespace}).'/projects';
+ $url .= '?order_by=name';
+ $url .= '&sort=asc';
+ $url .= '&page='.uri_escape($page);
+ $url .= '&per_page=100';
+
+ print "Getting projects for namespace: $cfg{namespace} (page: $page)\n" if($cfg{verbose});
+
+ my $req = new HTTP::Request(GET => $url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ my $data = decode_json($content);
+
+ foreach my $project (@{$data}) {
+ push @{$projects}, $project;
+ }
+
+ my $next_page = $res->header('X-Next-Page');
+ if($next_page && $next_page ne $page) {
+ $projects = get_team_projects($projects, $next_page);
+ }
+
+ return $projects;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ exit 1;
+ }
+}
+
+sub disable_issues {
+ my ($project) = @_;
+
+ if($project->{issues_enabled}) {
+ print "Issues enabled.\n" if($cfg{verbose});
+
+ print "Disabling issues for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id});
+
+ my %param = (
+ id => $project->{id},
+ name => $project->{name},
+ issues_enabled => 'false',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Disabled issues for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Issues already disabled.\n" if($cfg{verbose});
+
+ return 2;
+ }
+}
+
+sub set_merge_method {
+ my ($project) = @_;
+
+ if($project->{merge_method} ne 'ff') {
+ print "Setting merge method for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id});
+
+ my %param = (
+ id => $project->{id},
+ name => $project->{name},
+ merge_method => 'ff',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Set merge method for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Merge method is already fast-forward.\n" if($cfg{verbose});
+
+ return 2;
+ }
+}
+
+sub disable_printing_merge_request_link {
+ my ($project) = @_;
+
+ if($project->{printing_merge_request_link_enabled}) {
+ print "Disabling merge request link when pushing from the command line for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id});
+
+ my %param = (
+ id => $project->{id},
+ name => $project->{name},
+ printing_merge_request_link_enabled => 'false',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Disabled merge request link when pushing from the command line for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Merge request link when pushing from the command line is already disabled.\n" if($cfg{verbose});
+
+ return 2;
+ }
+}
+
+sub set_ci_config_path {
+ my ($project) = @_;
+
+ if(!$project->{ci_config_path} || $project->{ci_config_path} ne $cfg{ci_cfg_path}) {
+ print "Current CI config path: ". $project->{ci_config_path} ."\n" if($cfg{verbose} && $project->{ci_config_path});
+
+ print "Setting CI config path '$cfg{ci_cfg_path}' for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id});
+
+ my %param = (
+ id => $project->{id},
+ name => $project->{name},
+ ci_config_path => $cfg{ci_cfg_path},
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Set CI config path for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Current CI config path is already '$cfg{ci_cfg_path}'.\n" if($cfg{verbose});
+
+ return 2;
+ }
+}
+
+sub configure_emails_on_push_service {
+ my ($project) = @_;
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/services/emails-on-push';
+
+ my $req = new HTTP::Request(GET => $url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ my $data = decode_json($content);
+
+ print "Emails on push:\n". Dumper($data) if($cfg{debug});
+
+ if(!$data->{active}) {
+ print "Activating Emails on push for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/services/emails-on-push';
+
+ my %param = (
+ recipients => $cfg{email_recipients},
+ disable_diffs => 'false',
+ send_from_committer_email => 'false',
+ push_events => 'true',
+ tag_push_events => 'true',
+ branches_to_be_notified => 'all',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Activated Emails on push service for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Emails on push service already active for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 2;
+ }
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+}
+
+sub configure_irker_service {
+ my ($project) = @_;
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/services/irker';
+
+ my $req = new HTTP::Request(GET => $url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ my $data = decode_json($content);
+
+ print "Irker:\n". Dumper($data) if($cfg{debug});
+
+ if($data->{active}) {
+ print "Deactivating Irker for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/services/irker';
+
+ my $req = HTTP::Request::Common::DELETE($url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Deactivated Irker service for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "Irker service already deactivated for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 2;
+ }
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+}
+
+sub configure_kgb_webhook {
+ my ($project) = @_;
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/hooks';
+
+ my $req = new HTTP::Request(GET => $url);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ my $data = decode_json($content);
+
+ print "hooks:\n". Dumper($data) if($cfg{debug});
+
+ my $kgb_url = $cfg{kgb_baseurl}.'?channel='.uri_escape($cfg{irc_recipients}).'&network='.uri_escape($cfg{kgb_network});
+
+ my $hook_id = 0;
+ my $same_url = 0;
+ foreach my $hook (@{$data}) {
+ if($hook->{url} =~ /\Q$cfg{kgb_baseurl}\E/) {
+ $hook_id = $hook->{id};
+ if($hook->{url} eq $kgb_url) {
+ $same_url = 1;
+ }
+ last;
+ }
+ }
+
+ if(!$hook_id) {
+ print "Adding KGB webhook for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/hooks';
+
+ my %param = (
+ id => $project->{id},
+ url => $kgb_url,
+ push_events => 'true',
+ issues_events => 'true',
+ merge_requests_events => 'true',
+ tag_push_events => 'true',
+ note_events => 'true',
+ job_events => 'true',
+ pipeline_events => 'true',
+ wiki_events => 'true',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::POST($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Added KGB webhook for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ elsif($hook_id && !$same_url) {
+ print "Editing KGB webhook for: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ my $url = $cfg{url}.'/projects/'.uri_escape($project->{id}).'/hooks/'.uri_escape($hook_id);
+
+ my %param = (
+ id => $project->{id},
+ hook_id => $hook_id,
+ url => $kgb_url,
+ push_events => 'true',
+ issues_events => 'true',
+ merge_requests_events => 'true',
+ tag_push_events => 'true',
+ note_events => 'true',
+ job_events => 'true',
+ pipeline_events => 'true',
+ wiki_events => 'true',
+ );
+
+ print "param:\n".Dumper(\%param) if($cfg{debug});
+
+ my $req = HTTP::Request::Common::PUT($url, [ %param ]);
+ $req->header('PRIVATE-TOKEN' => $cfg{token});
+
+ my $res = $ua->request($req);
+ if($res->is_success) {
+ my $content = $res->content;
+
+ print "Content:\n".Dumper($content) if($cfg{debug});
+
+ print "Edited KGB webhook for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 1;
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+ }
+ else {
+ print "KGB webhook already added for project: ". $project->{name} ." (". $project->{id} .")\n" if($cfg{verbose});
+
+ return 2;
+ }
+ }
+ else {
+ print "Error: Request failed! ($url)\n";
+ print "HTTP Status: ".$res->code." ".$res->message."\n";
+ print $res->content if($res->content);
+
+ return;
+ }
+}
+
View it on GitLab: https://salsa.debian.org/med-team/community/helper-scripts/-/commit/fbed735f335e04d75dcf7f3480b33b1c9f93ae8f
--
View it on GitLab: https://salsa.debian.org/med-team/community/helper-scripts/-/commit/fbed735f335e04d75dcf7f3480b33b1c9f93ae8f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20200224/ac637fe0/attachment-0001.html>
More information about the debian-med-commit
mailing list