[!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).
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
- background :
ubunut
- background :
#3a122e
- front-color:
#eee
- background :
solarized
- background :
#0d2a34
- front-color:
#869395
- background :