[!NOTE|label:references:]
.gitconfig
$ git config --global gitreview.username <UserName>
$ git config --global gitreview.remote origin
default groups
[!TIP]
System Groups
-
- Administrators
- Non-Interactive Users
- ~Service Users~
Special references
refs/changes/*
refs/meta/config
refs/meta/dashboards/*
refs/notes/review
Magic references
refs/for/<branch ref>
refs/meta/config
get project.config
clone the repo
$ git clone <repo url> # or update the local repo to HEAD $ git pull [--rebase]
checkout
meta/config
$ git fetch origin refs/meta/config:refs/remotes/origin/meta/config $ git checkout meta/config
or
$ git fetch ssh://localhost:29418/project refs/meta/config $ git checkout FETCH_HEAD
publish to remote
$ git add --all .
$ git commit -m "<add your comments here>"
submit directly
$ git push origin meta/config:meta/config
or
$ git push origin HEAD:refs/meta/config
submit review
[!NOTE|label:references:]
$ git push origin HEAD:refs/for/refs/meta/config
- or
$ git push origin meta/config:refs/for/refs/meta/config
- or
update meta/config if remotes update
$ git fetch origin --force refs/meta/config:refs/remotes/origin/meta/config
$ git pull origin refs/meta/config
# or
$ git merge meta/config
reset to remotes
$ git fetch origin --force refs/meta/config:refs/remotes/origin/meta/config
$ git reset --hard remotes/origin/meta/config
useful refs
sandbox:
refs/heads/sandbox/${username}/*
its-jira:
for project specific
[commentlink "its-jira"] match = ^[ \\t]*PROJECT-([0-9]{1,5}): link = https://<jira-domain>:<jira-port>/browse/PROJECT-$1
for common setup
[plugin "its-jira"] association = OPTIONAL branch = ^refs/heads/.* branch = ^refs/heads/stable-.* commentOnChangeAbandoned = false commentOnChangeCreated = true commentOnChangeMerged = true commentOnChangeRestored = false commentOnCommentAdded = false commentOnFirstLinkedPatchSetCreated = true commentOnPatchSetCreated = false commentOnRefUpdatedGitWeb = true enabled = enforced [commentlink "its-jira"] match = ^[ \\t]*([A-Za-z]*-[0-9]{1,5}): link = https://<jira-domain>:<jira-port>/browse/$1 [commentlink "changeid"] match = (I[0-9a-f]{8,40}) link = "#/q/$1"
verified label
[label "Verified"]
function = MaxWithBlock
defaultValue = 0
copyAllScoresIfNoCodeChange = true
value = -1 Fails
value = 0 No score
value = +1 Verified
change-id
[receive]
requireChangeId = true
createNewChangeForAllNotInTarget = false
maxObjectSizeLimit = 6m
maxBatchChanges = 1
[commentlink "changeid"]
match = (I[0-9a-f]{8,40})
link = "#/q/$1"
freeze master
branch
[!TIP] One quirk is that the shortest possible pattern expansion must be a valid ref name
thus^refs/heads/.*/name
will fail becauserefs/heads//name
is not a valid reference
but^refs/heads/.+/name
will work.
About the refs/for
namespace
[!TIP] references:
- what is the use refs/for/refs/* in gerrit?
refs/for/*
syntax is just a short name forrefs/for/refs/*
:
project.config
[access "refs/for/refs/heads/master"] push = block group user/Marslo Jiao (marslo) push = block group Registered Users submit = block group Registered Users submit = block group group user/Marslo Jiao (marslo) addPatchSet = block group user/Marslo Jiao (marslo) addPatchSet = block group Registered Users pushMerge = block group user/Marslo Jiao (marslo) pushMerge = block group Registered Users
groups
... global:Project-Owners Project Owners global:Registered-Users Registered Users ... user:marslo user/Marslo Jiao(marslo) ...
freeze multiple branches (stable
& release
) for the specific account
project.config
[access "^refs/for/refs/heads/(stable|release)$"] push = block group Registered Users submit = block group Registered Users addPatchSet = block group Registered Users pushMerge = block group Registered Users [access "^refs/heads/(stable|release)$"] read = group user/Marslo Jiao (marslo) push = +force group user/Marslo Jiao (marslo) pushMerge = group user/Marslo Jiao (marslo)
- or using
exclusiveGroupPermissions
[access "^refs/heads/backup/(master|dev|staging|stable)/.+$"] exclusiveGroupPermissions = create delete push pushMerge create = group Project Owners create = block group Registered Users delete = block group Registered Users push = block group Registered Users pushMerge = block group Registered Users [access "^refs/for/refs/heads/backup/(master|dev|staging|stable)/.+$"] exclusiveGroupPermissions = addPatchSet create push pushMerge submit addPatchSet = block group Registered Users create = block group Registered Users push = block group Registered Users pushMerge = block group Registered Users submit = block group Registered Users
- or using
groups
... global:Project-Owners Project Owners global:Registered-Users Registered Users ... user:marslo user/Marslo Jiao(marslo) ...
restriction for branches (feature1
, feature2
and master
) for only allow code review merge, forbidden code push
project.config
[access "refs/*"] read = group Project Owners read = group user/Marslo Jiao (marslo) [access "refs/for/*"] addPatchSet = group Project Owners addPatchSet = group user/Marslo Jiao (marslo) push = group Project Owners push = group user/Marslo Jiao (marslo) pushMerge = group Project Owners pushMerge = group user/Marslo Jiao (marslo) [access "^refs/heads/(feature1|feature2|master)$"] push = block group Registered Users pushMerge = block group Registered Users submit = group Change Owner
groups
... global:Project-Owners Project Owners global:Registered-Users Registered Users ... user:marslo user/Marslo Jiao(marslo) ...
example of project.config
- project.config
[project] description = Gerrit Code Review [access "refs/*"] owner = group google/gerritcodereview-maintainers@googlegroups.com [access "refs/heads/*"] label-Code-Review = -2..+2 group google/gerritcodereview-maintainers@googlegroups.com label-Code-Review = -2..+2 group polygerrit-maintainers label-Verified = -1..+1 group Change Owner label-Verified = -1..+1 group gerrit-verifiers label-Code-Style = -1..+1 group gerrit-verifiers label-Verified-Notedb = -1..+1 group gerrit-verifiers label-Library-Compliance = -1..+1 group gerrit-lib label-Library-Compliance = -1..+0 group google/gerritcodereview-maintainers@googlegroups.com submit = group Change Owner submit = group google/gerritcodereview-maintainers@googlegroups.com create = group google/gerritcodereview-maintainers@googlegroups.com abandon = group gerrit-verifiers editTopicName = +force group google/gerritcodereview-maintainers@googlegroups.com removeReviewer = group google/gerritcodereview-maintainers@googlegroups.com publishDrafts = group google/gerritcodereview-maintainers@googlegroups.com [access "refs/tags/*"] create = group gerrit-release-creators create = group google/gerritcodereview-maintainers@googlegroups.com createTag = group gerrit-release-creators createTag = group google/gerritcodereview-maintainers@googlegroups.com createSignedTag = group gerrit-release-creators createSignedTag = group google/gerritcodereview-maintainers@googlegroups.com [access] inheritFrom = Public-Projects [receive] rejectImplicitMerges = true [reviewer] enableByEmail = true [label "Verified"] function = MaxNoBlock copyAllScoresIfNoCodeChange = true value = -1 Fails value = 0 No score value = +1 Verified defaultValue = 0 [label "Code-Style"] function = MaxWithBlock copyAllScoresIfNoCodeChange = true value = -1 Wrong Style or Formatting value = 0 No score value = +1 Style Verified defaultValue = 0 [label "Library-Compliance"] function = MaxWithBlock copyAllScoresIfNoCodeChange = true copyAllScoresOnTrivialRebase = true value = -1 Do not submit value = 0 No score value = +1 Approved defaultValue = 0 [access "refs/for/refs/meta/dashboards/*"] push = group google/gerritcodereview-maintainers@googlegroups.com [access "refs/meta/dashboards/*"] label-Code-Review = -2..+2 group google/gerritcodereview-maintainers@googlegroups.com label-Code-Review = -1..+1 group Registered Users label-Verified = -1..+1 group gerrit-verifiers label-Verified = -1..+1 group google/gerritcodereview-maintainers@googlegroups.com submit = group google/gerritcodereview-maintainers@googlegroups.com forgeAuthor = group google/gerritcodereview-maintainers@googlegroups.com label-Code-Style = -1..+1 group google/gerritcodereview-maintainers@googlegroups.com [access "refs/for/refs/meta/config"] push = group gerrit-verifiers [notify "polygerrit-reviews"] email = polygerrit-reviews@google.com type = all_comments type = submitted_changes header = cc filter = file:polygerrit-ui [access "refs/heads/infra/config"] push = group gerrit-tricium-admins
rules.pl
submit by a non author
[!TIP] check also:
submit_rule(S) :-
gerrit:default_submit(X),
X =.. [submit | Ls],
add_non_author_approval(Ls, R),
S =.. [submit | R].
add_non_author_approval(S1, S2) :-
gerrit:commit_author(A),
gerrit:commit_label(label('Code-Review', 2), R),
R \= A, !,
S2 = [label('Non-Author-Code-Review', ok(R)) | S1].
add_non_author_approval(S1, [label('Non-Author-Code-Review', need(_)) | S1]).

- by
project.config
[access "refs/*"] label-Code-Review = block -2..+2 group Change Owner exclusiveGroupPermissions = label-Code-Review
ticket check
[!TIP] check also:
optional validation
submit_rule(S) :- gerrit:default_submit(X), X =.. [submit | Ls], require_ticket_check_for_ticket(Ls, Nls), S =.. [submit | Nls]. require_ticket_check_for_ticket(S1, S2) :- gerrit:commit_message_matches('^issue-[\\d]+\\s?:\\s?[\\w\\W]+'), !, S2 = [label('Ticket-Checked', need(_)) | S1]. require_ticket_check_for_ticket(S1, S2) :- !, S2 = S1.

optional validation with auto vote
submit_rule(S) :- gerrit:default_submit(X), X =.. [submit | Ls], require_ticket_check_for_ticket(Ls, Nls), S =.. [submit | Nls]. require_ticket_check_for_ticket(S1, S2) :- gerrit:commit_message_matches('\\[issue-[\\d]{2}\\]\\s?:\\s?[\\w\\W]+'), !, S2 = [label('Ticket-Checked', ok(user(824))) | S1]. require_ticket_check_for_ticket(S1, S2) :- !, S2 = S1.


mandatory validation
submit_rule(S) :- gerrit:default_submit(X), % get the current submit structure X=.. [submit | Ls], require_ticket_check_for_ticket(Ls, Nls), S=.. [submit | Nls]. require_ticket_check_for_ticket(S1, S2) :- gerrit:commit_message_matches('\\[issue-[\\d]{2}\\][\\s\\S]+'), !, S2 = [label('Ticket-Checked', ok(user(790))) | S1]. % Add the label and automatically approval by user-id: 790 require_ticket_check_for_ticket(S1, [label('Ticket-Checked', need(_)) | S1]).


api
basic usage
regular options
a might means [a]pi
⇡
$ curl -X PUT http://domain.name/a/path/to/api/
$ curl -X POST http://domain.name/a/path/to/api/
$ curl -X DELETE http://domain.name/a/path/to/api/
sending data
json with file
$ curl -X PUT \ -d@testdata.json \ --header "Content-Type: application/json" \ http://domain.name/a/path/to/api/
json with string
$ curl -X POST \ -H "Content-Type: application/json" https://domain.name/a/changes/<number>/move \ -d '{ "destination_branch" : "target/branch/name" }' )]}' { "id": "marslo-project~target%2Fbranch%2Fname~Id90057ab632eb93be2fa9128a9d624664008cb4a", "project": "marslo-project", "branch": "target/branch/name", "hashtags": [], "change_id": "Id90057ab632eb93be2fa9128a9d624664008cb4a", "subject": "marslo: testing api move", "status": "NEW", "created": "2022-01-21 05:21:25.000000000", "updated": "2022-05-17 06:56:37.000000000", "submit_type": "FAST_FORWARD_ONLY", "mergeable": false, "insertions": 8, "deletions": 8, "unresolved_comment_count": 0, "has_review_started": true, "_number": 94490, "owner": { "_account_id": 790 }, "requirements": [] } # or $ curl -X POST \ -H "Content-Type: application/json" https://domain.name/a/changes/<number>/move \ -d '{ "destination_branch" : "target/branch/name" }' | tail -n +2 | jq -r .branch
txt
$ curl -X PUT \ --data-binary @testdata.txt \ --header "Content-Type: text/plain" \ http://domain.name/a/path/to/api/
verifying header content
$ curl -v -n -X DELETE http://domain.name/a/path/to/api/
change
get change via change-id
$ curl -X GET 'https://domina.name/a/changes/<change-id>'
get change via commit-id
$ changeid=$(git show <commit-id> --no-patch --format="%s%n%n%b" | sed -nre 's!Change-Id: (.*$)!\1!p') $ curl -X GET "https://domina.name/a/changes/${changeid}"
or
$ project=$(echo 'path/to/project' | sed 's:/:%2F:g') $ branch='dev' $ changeid=$(git show <commit-id> --no-patch --format="%s%n%n%b" | sed -nre 's!Change-Id: (.*$)!\1!p') $ curl -X GET "https://domina.name/a/changes/${project}~${branch}~${changeid}"
who approval the CR+2
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r '.labels."Code-Review".approved.name'
get all vote CR-2
- example output for
.labels.<tag>.all[]
{ "value": -2, "date": "2021-05-31 07:57:14.000000000", "permitted_voting_range": { "min": -2, "max": 2 }, "_account_id": 790, "name": "Marslo Jiao", "email": "marslo.jiao@gmail.com", "username": "marslo" } { "value": 0, "permitted_voting_range": { "min": -2, "max": 2 }, "_account_id": 124, "name": "John Doe", "email": "john@gmail.com", "username": "john" }
reference:
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r '.labels."Code-Review".all[] | select ( .value == -2 ) | .username'
# : |⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂⠂| :
# : ⇣ :
# : select ".value"== -2 :
# : :
# ⇣ ⇣
# pipe pipe
# or
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r '( .labels."Code-Review".all[] | select ( .value == -2 ) ).username'
: :
⇣ ⇣
expression expression
# or
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r '[ .labels."Code-Review".all[] | select ( .value == -2 ) ][].username'
: :
⇣ ⇣
expression expression
# or
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r '.labels."Code-Review".all[] | select ( .value == -2 )' |
jq -r .username :
⇣
pipe
who approval the V+1
$ curl -s -X GET https://domain.name/a/changes/${changeid}/detail |
tail -n +2 |
jq -r .labels.Verified.approved.username
access list contains account
[!NOTE]
# i.e. : check all repos who contains account marslo@sample.com
$ while read -r _proj; do
output=$( curl -fsSL https://gerrit.sample.com/a/projects/"${_proj}"/access |
tail -n+2 |
jq -r '.. | .rules? | select(. != null) | keys[] | ascii_downcase | select(contains("marslo@sample.com"))';
)
[[ -z "${output}" ]] || echo ">> https://gerrit.sample.com/admin/repos/$(sed 's:%2F:/:g' <<< "${_proj}")"
done < <( curl -fsSL https://gerrit.sample.com/a/projects/?d |
tail -n+2 |
jq -r '.[].id' |
grep --color=never -E 'keyword|keyword'
)
all reviews at a certain time
[!NOTE|label:references:]
project='PROJECT'
branch='BRANCH'
start='2023-01-01'
end='2024-01-01'
curlOpt='--silent --insecure --globoff --netrc-file ~/.netrc'
query="project:${project}+branch:${branch}+after:${start}+before:${end}"
filter by status if necessary
query="${query}+is:closed+-is:abandoned"
echo ">> ${project} ~ ${branch}"
while IFS='|' read -r _change_id _id; do
echo -e "\t- [${_id}] [_change_id]"
$( eval "curl ${curlOpt} 'https://gerrit.sample.com/a/changes/?q=${query}'" |
tail -n +2 |
jq -r '.[] | .change_id + "|" + .id'
)
get review rate in certain time
sum=0
rnum=0
onum=0
echo ">> ${project} ~ ${branch}"
while IFS='|' read -r _change_id _id; do
sum=$(( sum+1 ))
output=$( eval "curl ${curlOpt} 'https://gerrit.sample.com/a/changes/${_id}/detail' | tail -n+2" )
reviewed=$( jq -r '.labels."Code-Review".all[] | select(.value != null) | select( .value | contains(2) ) | .username' <<< "${output}" )
owned=$( jq -r '.owner.username' <<< "${output}" )
if grep 'marslo' <<< "${reviewed}" >/dev/null; then rnum=$(( rnum+1 )); fi
if grep 'marslo' <<< "${owned}" >/dev/null; then onum=$(( onum+1 )); fi
$( eval "curl ${curlOpt} 'https://gerrit.sample.com/a/changes/?q=${query}'" |
tail -n +2 |
jq -r '.[] | .change_id + "|" + .id'
)
echo "${sum} ${rnum} ${onum} $(( sum-onum ))" |
awk '{ sum=$1; reviewed=$2; owned=$3; rsum=$4; rate=$2*100/$4 } END { printf("\t- gerrit review: %s/(%s-%s) ( %s% )\n", reviewed, sum, owned, rate) }'
reference
- project owner guide
- Gerrit Code Review - Access Controls
- Gerrit Code Review - Uploading Changes
- The refs/for namespace
- gerrit/gerrit/refs/meta/config
- gerrit 权限控制
- its-jira plugin md
- Rule base configuration
- Gerrit push not working. Remote rejected, prohibited by gerrit
- Gerrit Code Review - Project Configuration File Format
- Review UI
integrate in Jenkins
[!NOTE|label:references:]
stream-events
# permission requies $ ssh -i id_rsa jenkins@gerrit.domain.com -p 29418 gerrit stream-events stream events not permitted # verify $ ssh -i id_rsa jenkins@gerrit.domain.com -p 29418 gerrit stream-events | jq -r .type ref-updated comment-added
build current patches only
[!NOTE] Warning: The current implementation takes into account that 'Build Current Patches Only' with 'Abort new patch sets' and 'Abort patch sets with same topic' are enabled (see help for more).
1.4.3.6.7 -- gerrit trigger generate ssh-key
$ keyname='devops@jenkins' $ ssh-keygen -m PEM -t rsa -f ~/.ssh/${keyname} -C "${keyname}" -P '' -q
css for code block
.gr-formatted-text-0 gr-linked-text.pre.gr-formatted-text,
gr-linked-text[class*="pre"], gr-linked-text[class*="pre"] #output {
font-family: "Comic Mono", "Monaco", "Menlo", "Andale Mono", "Ubuntu Mono", "monofur" !important;
font-size: 16px !important;
}
.gr-formatted-text-0 gr-linked-text.pre.gr-formatted-text,
gr-linked-text[class*="pre"] {
color: #c8c8c8 !important;
background: #272727 !important;
border-radius: .75em !important;
box-shadow: 0 4px 8px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
overflow: auto;
display: block;
padding: 12px 12px 1px 12px;
margin: 0px;
}
gruvbox
- background :
#272727
- front-color:
#e8dbb6
1.4.3.6.8 -- gruvbox code block - background :
ubunut
- background :
#3a122e
- front-color:
#eee
1.4.3.6.9 -- ubuntu code block - background :
solarized
- background :
#0d2a34
- front-color:
#869395
1.4.3.6.10 -- solarized code block - background :