#!/usr/bin/perl =head1 NAME salsa - tool to manipulate salsa repositories and group members =head1 SYNOPSIS # salsa salsa whoami salsa search_project devscripts salsa search_project qa/qa salsa search_group js-team salsa search_group perl-team/modules salsa search_user yadd salsa push_repo . --group js-team --kgb --irc devscripts --tagpending salsa update_repo node-mongodb --group js-team --disable-kgb --desc \ --desc-pattern "Package %p" salsa update_repo js-team/node-mongodb --kgb --irc debian-js salsa update_safe --all --desc --desc-pattern "Debian package %p" \ --group js-team salsa checkout node-mongodb --group js-team salsa checkout js-team/node-mongodb salsa add_user developer foobar --group-id 2665 salsa update_user maintainer foobar --group js-team salsa del_user foobar --group js-team salsa last_ci_status js-team/nodejs salsa pipelines js-team/nodejs =head1 DESCRIPTION B is designed to create and configure repositories on L and manage users of groups. A Salsa token is required, except for search* commands, and must be set in command line I<(see below)>, or in your configuration file I<(~/.devscripts)>: SALSA_TOKEN=abcdefghi or SALSA_TOKEN=`cat ~/.token` or SALSA_TOKEN_FILE=~/.dpt.conf If you choose to link another file using SALSA_TOKEN_FILE, it must contain a line with one of (no differences): SALSA_PRIVATE_TOKEN=xxxx SALSA_TOKEN=xxxx This allows for example to use dpt(1) configuration file (~/.dpt.conf) which contains: DPT_SALSA_PRIVATE_TOKEN=abcdefghi =head1 COMMANDS =head2 Managing users and groups =over =item B Request access to a group. salsa join js-team salsa join --group js-team salsa join --group-id 1234 =item B Add a user to a group. salsa --group js-group add_user guest foouser salsa --group-id 1234 add_user guest foouser salsa --group-id 1234 add_user maintainer 1245 First argument is the GitLab's access levels: guest, reporter, developer, maintainer, owner. =item B Remove a user from a group salsa --group js-team del_user foouser salsa --group-id=1234 del_user foouser =item B List sub groups of current one if group is set, groups of current user else. =item B Show group members. salsa --group js-team group salsa --group-id 1234 group =item B Search for a group using given string. Shows group id and other information. salsa search_group perl-team salsa search_group perl-team/modules salsa search_group 2666 =item B Search for a user using given string. Shows user id and other information. salsa search_user yadd =item B Update user role in a group. salsa --group-id 1234 update_user guest foouser salsa --group js-team update_user maintainer 1245 First argument is the GitLab's access levels: guest, reporter, developer, maintainer, owner. =item B Gives information on the token owner salsa whoami =back =head2 Managing repositories One of C<--group>, C<--group-id>, C<--user> or C<--user-id> is required to manage repositories. If both are set, salsa warns and only C<--user>/C<--user-id> is used. If none is given, salsa uses current user id I<(token owner)>. =over =item B Verify that repo(s) are well configured. It works exactly like B except that it does not modify anything but just lists projects not well configured with found errors. salsa --user yadd --tagpending --kgb --irc=devscripts check_repo test salsa --group js-team check_repo --all salsa --group js-team --rename-head check_repo test1 test2 test3 =item B or B Clone repo in current dir. If directory already exists, update local repo. salsa --user yadd co devscripts salsa --group js-team co node-mongodb salsa co js-team/node-mongodb You can clone more than one repository or all repositories of a group or a user: salsa --user yadd co devscripts autodep8 salsa co yadd/devscripts js-team/npm salsa --group js-team co --all # All js-team active repos salsa co --all-archived # All your repos including archived =item B Create public empty project. If C<--group>/C<--group-id> is set, project is created in group directory, else in user directory. salsa --user yadd create_repo test salsa --group js-team --kgb --irc-channel=devscripts create_repo test =item B Delete a repository. =item B Forks a project in group/user repository and set "upstream" to original project. Example: $ salsa fork js-team/node-mongodb --verbose ... salsa.pl info: node-mongodb ready in node-mongodb/ $ cd node-mongodb $ git remote --verbose show origin git@salsa.debian.org:me/node-mongodb (fetch) origin git@salsa.debian.org:me/node-mongodb (push) upstream git@salsa.debian.org:js-team/node-mongodb (fetch) upstream git@salsa.debian.org:js-team/node-mongodb (push) For a group: salsa fork --group js-team user/node-foo =item B List forks of project(s). salsa forks qa/qa debian/devscripts Project can be set using full path or using B<--group>/B<--group-id> or B<--user>/B<--user-id>, else it is searched in current user namespace. =item B Push relevant packaging refs to origin Git remote. To be run from packaging working directory. salsa push It pushes the following refs to the configured remote for the debian-branch or, falling back, to the "origin" remote: =over =item "master" branch (or whatever is set to debian-branch in gbp.conf) =item "upstream" branch (or whatever is set to upstream-branch in gbp.conf) =item "pristine-tar" branch =item tags named "debian/*" (or whatever is set to debian-tag in gbp.conf) =item tags named "upstream/*" (or whatever is set to upstream-tag in gbp.conf) =item all tags, if the package's source format is "3.0 (native)" =back =item B or B Shows projects owned by user or group. If second argument exists, search only matching projects salsa --group js-team list_repos salsa --user yadd list_repos foo* =item B or B Displays last continuous integration result. Use B<--verbose> to see URL of pipeline when result isn't B. Unless B<--no-fail> is set, B will stop on first "failed" status. salsa --group js-team last_ci_status --all --no-fail salsa --user yadd last_ci_status foo salsa last_ci_status js-team/nodejs This commands returns the number of "failed" status found. "success" entries are displayed using STDOUT while other are displayed I<(with details)> using STDERR. Then you can easily see only failures using: salsa --group js-team last_ci_status --all --no-fail >/dev/null =item B, B Control pipeline schedule =item B, B Lists current pipeline schedule items. You can use B<--no-fail> and B<--all> options here. =item B, B Creates a merge request. Suppose you created a fork using B, modify some things in a new branch using one commit and want to propose it to original project I<(branch "master")>. You just have to launch this in source directory: salsa mr Other example: salsa mr --mr-dst-project debian/foo --mr-dst-branch debian/master or simply salsa mr debian/foo debian/master Note that unless destination project has been set using command line, B will search it in the following order: =over 4 =item using GitLab API: salsa will detect from where this project was forked =item using "upstream" origin =item else salsa will use source project as destination project =back To force salsa to use source project as destination project, you can use "same": salsa mr --mr-dst-project same # or salsa mr same New merge request will be created using last commit title and description. See B<--mr-*> options for more. =item B, B List opened merge requests for project(s) salsa mrs qa/qa debian/devscripts Project can be set using full path or using B<--group>/B<--group-id> or B<--user>/B<--user-id>, else it is searched in current user namespace. =item B Protect/unprotect a branch. =over =item Set protection # project branch merge push salsa --group js-team protect_branch node-mongodb master m d "merge" and "push" can be one of: =over =item B, B: owner only =item B, B: B + maintainers allowed =item B, B: B + developers allowed =item B, B: B + reporters allowed =item B, B: B + guest allowed =back =item Unprotect salsa --group js-team protect_branch node-mongodb master no =back =item B List protected branches salsa --group js-team protected_branches node-mongodb =item B Create a new project from a local Debian source directory configured with git. B executes the following steps: =over =item gets project name using debian/changelog file; =item launches B; =item launches B; =item pushes local repo. =back Examples: salsa --user yadd push_repo ./test salsa --group js-team --kgb --irc-channel=devscripts push_repo . =item B Rename branch given in B<--source-branch> with name given in B<--dest-branch>. You can use B<--no-fail>, B<--all> and B<--all-archived> options here. =item B, B, B Search for a project using given string. Shows name, owner id and other information. salsa search devscripts salsa search debian/devscripts salsa search 18475 =item B Configure repo(s) using parameters given to command line. A repo name has to be given unless B<--all> or B<--all-archived> is set. Prefer to use B. salsa --user yadd --tagpending --kgb --irc=devscripts update_repo test salsa --group js-team update_repo --all salsa --group js-team --rename-head update_repo test1 test2 test3 salsa update_repo js-team/node-mongodb --kgb --irc debian-js By default when using B<--all>, salsa will fail on first error. If you want to continue, set B<--no-fail>. In this case, salsa will display a warning for each project that has fail but continue with next project. Then to see full errors, set B<--verbose>. =item B Launch B and ask before launching B (unless B<--yes>). salsa --user yadd --tagpending --kgb --irc=devscripts update_safe test salsa --group js-team update_safe --all salsa --group js-team --rename-head update_safe test1 test2 test3 salsa update_safe js-team/node-mongodb --kgb --irc debian-js =back =head2 Other =over =item B Empty local cache. =back =head1 OPTIONS =head2 General options =over =item B<-C>, B<--chdir> Change directory before launching command salsa -C ~/debian co debian/libapache2-mod-fcgid =item B<--cache-file> File to store cached values. Default to B<~/.cache/salsa.json>. An empty value disables cache. C<.devscripts> value: B =item B<--no-cache> Disable cache usage. Same as B<--cache-file ''> =item B<--conffile>, B<--conf-file> Add or replace default configuration files (C and C<~/.devscripts>). This can only be used as the first option given on the command-line. =over =item replace: salsa --conf-file test.conf ... salsa --conf-file test.conf --conf-file test2.conf ... =item add: salsa --conf-file +test.conf ... salsa --conf-file +test.conf --conf-file +test2.conf ... If one B<--conf-file> has no C<+>, default configuration files are ignored. =back =item B<--no-conf>, B<--noconf> Don't read any configuration files. This can only be used as the first option given on the command-line. =item B<--debug> Enable debugging output =item B<--group> Team to use. Use C to find it. If you want to use a subgroup, you have to set its full path: salsa --group perl-team/modules/packages check_repo lemonldap-ng C<.devscripts> value: B Be careful when you use B in your C<.devscripts> file. Every B command will be executed in group space, for example if you want to propose a little change in a project using B + B, this "fork" will be done in group space unless you set a B<--user>/B<--user-id>. Prefer to use an alias in your C<.bashrc> file. Example: alias jsteam_admin="salsa --group js-team" or alias jsteam_admin="salsa --conf-file ~/.js.conf or to use both .devscripts and .js.conf: alias jsteam_admin="salsa --conf-file +~/.js.conf then you can fix B in C<~/.js.conf> To enable bash completion for your alias, add this in your .bashrc file: _completion_loader salsa complete -F _salsa_completion jsteam_admin =item B<--group-id> Group id to use. Use C to find it. C<.devscripts> value: B Be careful when you use B in your C<.devscripts> file. Every B command will be executed in group space, for example if you want to propose a little change in a project using B + B, this "fork" will be done in group space unless you set a B<--user>/B<--user-id>. Prefer to use an alias in your C<.bashrc> file. Example: alias jsteam_admin="salsa --group-id 2666" or alias jsteam_admin="salsa --conf-file ~/.js.conf then you can fix B in C<~/.js.conf> =item B<--help>: displays this manpage =item B<-i>, B<--info> Prompt before sensible changes. C<.devscripts> value: B (yes/no) =item B<--path> Repo path. Default to group or user path. C<.devscripts> value: B =item B<--token> Token value (see above). =item B<--token-file> File to find token (see above). =item B<--user> Username to use. If neither B<--group>, B<--group-id>, B<--user> or B<--user-id> is set, salsa uses current user id (corresponding to salsa private token). =item B<--user-id> User id to use. Use C to find one. If neither B<--group>, B<--group-id>, B<--user> or B<--user-id> is set, salsa uses current user id (corresponding to salsa private token). C<.devscripts> value: B =item B<--verbose> Enable verbose output. =item B<--yes> Never ask for consent. C<.devscripts> value: B (yes/no) =back =head2 List/search repo options =over =item B<--archived>, B<--no-archived> Instead of looking to active projects, list or search in archived projects. Note that you can't have both archived and unarchived projects in the same request. Default: no I<(ie --no-archived)>. C<.devscripts> value: B (yes/no) =back =head2 Update/create repo options =over =item B<--all>, B<--all-archived> When set, all projects of group/user are affected by command. B<--all> will filter all active projects, whereas B<--all-archived> will include active and archived projects. =over =item B<--skip>: ignore project with B<--all> or B<--all-achived>. Example: salsa update_repo --tagpending --all --skip qa --skip devscripts C<.devscripts> value: B. To set multiples values, use spaces. Example SALSA_SKIP=qa devscripts =item B<--skip-file>: ignore projects in this file (1 project per line) salsa update_repo --tagpending --all --skip-file ~/.skip C<.devscripts> value: B =back =item B<--build-timeout> The maximum amount of time, in seconds, that a job can run. Default: 3600 (60 minutes) salsa update_safe myrepo --build-timeout 3600 C<.devscripts> value: B =item B<--avatar-path> Path to an image for the project's avatar. If path value contains "%p", it is replaced by project name. C<.devscripts> value: B =item B<--ci-config-path> Configure configuration file path of GitLab CI. Default: empty. Example: salsa update_safe --ci-config-path recipes/debian.yml@salsa-ci-team/pipeline debian/devscripts C<.devscripts> value: B =item B<--desc>, B<--no-desc> Configure repo description using pattern given in B C<.devscripts> value: B (yes/no) =item B<--desc-pattern> Repo description pattern. Default to "Debian package %p". "%p" is replaced by repo name, while "%P" is replaced by repo name given in command (may contains full path). C<.devscripts> value: B =item B<--email>, B<--no-email>, B<--disable-email> Enable, ignore or disable email-on-push. C<.devscripts> value: B (yes/ignore/no, default: ignore) =item B<--email-recipient> Email-on-push recipient. Can be multi valued: $ salsa update_safe myrepo \ --email-recipient foo@foobar.org \ --email-recipient bar@foobar.org If recipient value contains "%p", it is replaced by project name. C<.devscripts> value: B (use spaces to separate multiples recipients) =item B<--issues> Set issues feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--repo> Set repository feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--mr> Set merge requests feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--forks>, B<--forks-mr> Set forking a repository feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--lfs> Set Large File Storage (LFS) feature. C<.devscripts> values: B (yes/no, default: yes) =item B<--packages> Set packages feature. C<.devscripts> values: B (yes/no, default: yes) =item B<--jobs> Set jobs feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--container> Set container feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--analytics> Set analytics feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--requirements> Set requirements feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--wiki> Set wiki feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--snippets> Set snippets feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--pages> Set pages feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--releases> Set releases feature with permissions. C<.devscripts> values: B (yes/private/no, default: yes) =item B<--auto-devops> Set auto devops feature. C<.devscripts> values: B (yes/no, default: yes) =item B<--request-acc> Set request access feature. C<.devscripts> values: B (yes/no, default: yes) =item B<--enable-remove-source-branch>, B<--disable-remove-source-branch> Enable or disable deleting source branch option by default for all new merge requests. C<.devscripts> values: B (yes/no, default: yes) =item B<--irc-channel> IRC channel for KGB or Irker. Can be used more than one time only with B<--irker>. B: channel must not include the first "#". If salsa finds a channel starting with "#", it will consider that the channel starts with 2 "#"! C<.devscript> value: B. Multiple values must be space separated. Since configuration files are read using B, be careful when using "#": you must enclose the channel with quotes, else B will consider it as a comment and will ignore this value. =item B<--irker>, B<--no-irker>, B<--disable-irker> Enable, ignore or disable Irker service C<.devscripts> values: B (yes/ignore/no, default: ignore) =item B<--irker-host> Irker host. Default: ruprecht.snow-crash.org C<.devscripts> value: B =item B<--irker-port> Irker port. Default: empty (default value) C<.devscripts> value: B =item B<--kgb>, B<--no-kgb>, B<--disable-kgb> Enable, ignore or disable KGB webhook. C<.devscripts> value: B (yes/ignore/no, default: ignore) =item B<--kgb-options> List of KGB enabled options (comma separated). Default: issues_events, merge_requests_events, note_events, pipeline_events, push_events, tag_push_events, wiki_page_events, enable_ssl_verification $ salsa update_safe debian/devscripts --kgb --irc-channel devscripts \ --kgb-options 'merge_requests_events,issues_events,enable_ssl_verification' List of available options: confidential_comments_events, confidential_issues_events, confidential_note_events, enable_ssl_verification, issues_events, job_events, merge_requests_events, note_events, pipeline_events, tag_push_events, wiki_page_events C<.devscripts> value: B =item B<--no-fail> Don't stop on error when using B with B<--all> or B<--all-archived>. C<.devscripts> value: B (yes/no) =item B<--request-access> Allow users to request member access. C<.devscripts> value: B (yes/no) =item B<--rename-head>, B<--no-rename-head> Rename HEAD branch given by B<--source-branch> into B<--dest-branch> and change "default branch" of project. Works only with B. C<.devscripts> value: B (yes/no) =over =item B<--source-branch>: default "master" C<.devscripts> value: B =item B<--dest-branch>: default "debian/master" C<.devscripts> value: B =back =item B<--tagpending>, B<--no-tagpending>, B<--disable-tagpending> Enable, ignore or disable "tagpending" webhook. C<.devscripts> value: B (yes/ignore/no, default: ignore) =back =head2 Pipeline schedules =over =item B<--schedule-desc> Description of the pipeline schedule. =item B<--schedule-ref> Branch or tag name that is triggered. =item B<--schedule-cron> Cron schedule, for example: 0 1 * * *. =item B<--schedule-tz> Time zone to run cron schedule. Default: UTC =item B<--schedule-enable>, B<--schedule-disable> Enable/disable the pipeline schedule to run. Default: disabled =item B<--schedule-run> Trigger B<--schedule-desc> scheduled pipeline to run immediately. Default: =item B<--schedule-delete> Delete B<--schedule-desc> pipeline schedule =back =head2 Merge requests options =over =item B<--mr-title> Title for merge request. Default: last commit title. =item B<--mr-desc> Description of new MR. Default: =over =item empty if B<--mr-title> is set =item last commit description if any =back =item B<--mr-dst-branch> (or second command line argument) Destination branch. Default to "master". =item B<--mr-dst-project> (or first command line argument) Destination project. Default: project from which the current project was forked; or, if not found, "upstream" value found using B; or using source project. If B<--mr-dst-project> is set to B, salsa will use source project as destination. =item B<--mr-src-branch> Source branch. Default: current branch. =item B<--mr-src-project> Source project. Default: current project found using B. =item B<--mr-allow-squash>, B<--no-mr-allow-squash> Allow upstream project to squash your commits, this is the default. C<.devscripts> value: B (yes/no) =item B<--mr-remove-source-branch>, B<--no-mr-remove-source-branch> Remove source branch if merge request is accepted. Default: no. C<.devscripts> value: B (yes/no) =back =head2 Options to manage other Gitlab instances =over =item B<--api-url> GitLab API. Default: L. C<.devscripts> value: B =item B<--git-server-url> Default to "git@salsa.debian.org:" C<.devscripts> value: B =item B<--irker-server-url> Default to "ircs://irc.oftc.net:6697/" C<.devscripts> value: B =item B<--kgb-server-url> Default to L C<.devscripts> value: B =item B<--tagpending-server-url> Default to L C<.devscripts> value: B =back =head3 Configuration file example Example to use salsa with L (group "lemonldap-ng"): SALSA_TOKEN=`cat ~/.ow2-gitlab-token` SALSA_API_URL=https://gitlab.ow2.org/api/v4 SALSA_GIT_SERVER_URL=git@gitlab.ow2.org: SALSA_GROUP_ID=34 Then to use it, add something like this in your C<.bashrc> file: alias llng_admin='salsa --conffile ~/.salsa-ow2.conf' =head1 SEE ALSO B =head1 AUTHOR Xavier Guimard Eyadd@debian.orgE =head1 COPYRIGHT AND LICENSE Copyright (C) 2018, Xavier Guimard Eyadd@debian.orgE It contains code formerly found in L I<(pkg-perl-tools)> copyright 2018, gregor herrmann Egregoa@debian.orgE. This library 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, 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 L. =cut use Devscripts::Salsa; exit Devscripts::Salsa->new->run;