how to config pre-receive rules of gitlab? - ruby

I have written the pre-receive script according to the
gitlab docs, and run it on server side.
the rules like this:
#!/usr/bin/env ruby
$refname = ARGV[0]
$oldrev = ARGV[1]
$newrev = ARGV[2]
$user = ENV['USER']
puts "Enforcing Policies..."
puts "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
$regex = /\[ref: (\d+)\]/
# enforced custom commit message format
def check_message_format
missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
missed_revs.each do |rev|
message = `git cat-file commit #{rev} | sed '1,/^$/d'`
if !$regex.match(message)
puts "[POLICY] Your message is not formatted correctly"
exit 1
end
end
end
check_message_format
but,when i push my code to the server. the git bash display like this:
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 276 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: /opt/gitlab/embedded/service/gitlab-shell/hooks/pre-receive.d/pre-receive:9:in `<main>': undefined method `[]' for nil:NilClass (NoMethodError)
remote: Enforcing Policies...
To http://[ip]/[project].git
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'http://[ip]/[project].git'
What should I do to make this script run normally?

Related

Create a git server side hook for sanity check of configuration file

I would like to create a server side git hook to perform a sanity check on a configuration file in a repository.
Based on my research I came across "pre-receive" hooks ( a hook that gets triggered upon push ).
I setup my testing atlassian bitbucket server and create a new repository to which I navigated into the pre-receive hook directory in which scripts should reside.
Created a test script that rejects any push on master with an error print. Everything works fine so far.
Now, to my sanity check hook.
I came up with the following.
#!/usr/bin/env bash
CONFIGFILE = "full-path-to-config-file"
EXPECTED_CONF_MASTER = "configEntry1=expectedValue1"
EXPECTED_CONF_TEST = "configEntry2=expectedValue2"
while read oldrev newrev refname; do
echo "$refname : $oldrev --> $newrev"
current_branch=$refname
short_current_branch="$(echo $current_branch | sed 's/refs\/heads\///g')"
newFiles=$(git diff --stat --name-only --diff-filter=ACMRT ${oldrev} ${newrev})
if [[ "$short_current_branch" = "test" ]]; then
echo $newFiles | grep $CONFIGFILE >/dev/null || exit 0 #CONFIGFILE has not been modified
git cat-file --textconv "${newrev}:$CONFIGFILE" | grep "$EXPECTED_CONF_TEST" || echo "invalid config" && exit 1
elif [[ "$short_current_branch" = "master" ]]; then
echo $newFiles | grep $CONFIGFILE >/dev/null || exit 0 #CONFIGFILE has not been modified
git cat-file --textconv "${newrev}:$CONFIGFILE" | grep "$EXPECTED_CONF_MASTER" || echo "invalid config" && exit 1
fi
done
However, I keep getting this error upon push. I am not sure what is wrong.
Username for 'http://localhost:7990': seif
Password for 'http://seif#localhost:7990':
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 386 bytes | 386.00 KiB/s, done.
Total 4 (delta 1), reused 0 (delta 0)
remote: refs/heads/test : 1820f938babb12800d7d7726859bb86c03edb12a --> 28a1e8113d9ceebc41c6dd2d29a1ec2ebf626510
remote:
remote: fatal: Invalid revision range 1820f938babb12800d7d7726859bb86c03edb12a..28a1e8113d9ceebc41c6dd2d29a1ec2ebf626510
remote:
remote: Create pull request for test:
remote: http://localhost:7990/projects/myproj1/repos/bamboo-specs/pull-requests?create&sourceBranch=refs/heads/test
remote:
To http://localhost:7990/scm/myproj1/bamboo-specs.git
1820f93..28a1e81 test -> test

Git completion for alias as if for Git itself

Background
I have successfully configured Bash completion for various Git aliases. For example:
$ git config alias.subject
!git --no-pager show --quiet --pretty='%s'
$ function _git_subject() { _git_show; }
$ git subject my<TAB>
$ git subject my-branch
Challenge
However, I have a Git alias that I don't know how to set up Bash completion for. The problem is that I want the alias to complete as if for the top-level Git command itself. The alias is this:
$ git config alias.alias
alias = !"f() { if [[ \"$#\" != 1 ]]; then >&2 echo \"Usage: git alias COMMAND\"; return 1; fi; git config alias.\"$1\"; }; f"
# Example
$ git alias s
status
I have tried using _git, __git_main, and __git_wrap__git_main, but none of them work (I think it leads to an infinite loop since it never returns after I press tab).
Is there a way to add completion for a Git alias that completes as if it was the top-level Git command? Or specifically how to have completion for this alias?
Tried but doesn't work
function _git_alias() { _git; }
function _git_alias() { __git_main; }
function _git_alias() { __git_wrap__git_main; }
Desired behavior
$ git alias su<TAB>
subject submodule
$ git alias sub
Alternatively, if there's an easy way to complete for only aliases that would be cool, too. I would like to know how to complete as if for the top-level Git command just for curiosity as well, though.
I was finally able to create a working solution with a bit of hackery around the "magic" Bash completion variables. I changed these variables to "pretend" we were completing the given command as given to git itself.
If anybody has any suggestions to simplify this I would totally be open to suggestions.
# This is complex because we want to delegate to the completion for Git
# itself without ending up with an infinite loop (which happens if you try
# to just delegate to _git).
_git_alias() {
if [[ "$COMP_CWORD" -lt 2 ]]; then
return
fi
local old_comp_line_length new_comp_line_length
COMP_WORDS=(git "${COMP_WORDS[#]:2}")
((COMP_CWORD -= 1))
old_comp_line_length=${#COMP_LINE}
if [[ "$COMP_LINE" =~ ^[^[:blank:]]+[[:blank:]]+[^[:blank:]]+[[:blank:]]+(.*)$ ]]; then
COMP_LINE="git ${BASH_REMATCH[1]}"
fi
new_comp_line_length=${#COMP_LINE}
(( COMP_POINT += new_comp_line_length - old_comp_line_length ))
_git "$#"
# git alias blah
# ^
# 01234567890123
# 0 1
# point: 11
# length: 13
#
# git blah
# ^
# 01234567
# point: 5
# length: 7
#
# point = point - (old length) + (new length)
# point = 11 - 13 + 7
# point = -2 + 7
# point = 5
}

Is it possible do git commit on server side update hook

Point of my task : gather info about repo and place it to file while update hook and commit it ( perfectly with new commit).
Problems : when I'm doing commit -> it lockes origin repository and after this push is failing. my code looks like this :
#!/bin/bash
# --- Command line
refname="$1"
oldrev="$2"
newrev="$3"
export GIT_WORK_TREE=$PWD
# --- Safety check
if [ -z "$GIT_DIR" ]; then
echo "Don't run this script from the command line." >&2
echo " (if you want, you could supply GIT_DIR then run" >&2
echo " $0 <ref> <oldrev> <newrev>)" >&2
exit 1
fi
version="${refname##*_}"
branchName="${refname##*/}"
filePath="_componentVersion/BranchVersion.ps1"
if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
echo "usage: $0 <ref> <oldrev> <newrev>" >&2
exit 1
fi
# --- Check types
# if $newrev is 0000...0000, it's a commit to delete a ref.
zero="0000000000000000000000000000000000000000"
if [ "$newrev" = "$zero" ]; then
newrev_type=delete
else
newrev_type=$(git cat-file -t $newrev)
fi
case "$refname","$newrev_type" in
refs/heads/*,commit)
if [ "$oldrev " != "$zero" ]; then
version="${refname##*_}"
branchName="${refname##*/}"
filePath="_componentVersion/BranchVersion.ps1"
countOfCommits=$(git rev-list --count START_$version..$newrev)
countOfPushes=$(git log --pretty=oneline START_$version..$newrev | grep 'Issue nr: HOOK_$version' | wc -l)
countOfPushes=$(($countOfPushes+1))
echo git log --pretty=oneline START_$version..$newrev
message="
# -----------------------
# Brancht Version Info
# -----------------------
\$branch = '$version'
\$countOfCommits = $countOfCommits
\$countOfPushes = $countOfPushes # push
\$commitHash = '$newrev'
"
# credits go to https://stackoverflow.com/questions/9670302/commit-directly-to-a-bare-repository
# branch commit - here we will do the magic about count of commits and about count of pushes
# here we create file for info
# Empty the index, not sure if this step is necessary
git read-tree --empty
# Load the current tree. A commit ref is fine, it'll figure it out.
git read-tree "${newrev}"
# create blob from stdin
BLOB_ID=$(echo "$message" | git hash-object -w --stdin)
# update indexes in git
git update-index --add --cacheinfo 100644 "$BLOB_ID" "$filePath"
# Create a tree from your new index
TREE_ID=$(git write-tree)
# Commit it.
NEW_COMMIT=$(echo "Issue nr: HOOK_$version $message" | git commit-tree "$TREE_ID" -p "$oldrev")
# Update the branch
git update-ref "$refname" "$NEW_COMMIT" "$oldrev"
fi
# Done
exit 0
;;
*)
# Other actions except commit to branch / for now - we won't check it
exit 0
;;
esac
# --- Finished
exit 0
I'm working with bare repo. And example of commit taken from here
Execte problem is
remote: error: cannot lock ref 'refs/heads/REL_7.0.0': ref refs/heads/REL_7.0.0 is at 54f2454ddab36eda001e27946733a7b0e981f097 but expected 89a3032e0bfb999273205e32b7f6d57173c4bd7e
You can create commits.
You cannot update references that are locked, which includes the one that the update hook is being called for.
Since git push can push multiple reference names, there may be additional locked references. In general it's not a good idea to update anything that anyone might be git pushing inside a hook invoked by git push. In other words, don't try to update any branch or tag name. If you want to create new objects, attach them to some name outside these two name-spaces.
(Aside: the git read-tree --empty is not necessary, but it's a good idea to use a temporary index file anyway, rather than using the main index.)

Travis-CI and Shell scripts

I use shunit2 to perform some unit tests on a shell script.
To be able to access the script's functions I source it
. script_file.sh --source-only
And in the script I have everything encapsulated in a function but for
if [ "${1}" != "--source-only" ]; then
main "${#}"
fi
This works on Linux and OS X. I have now set up Travis CI with the following .travis.yml
language: bash
before_script:
- curl -L "http://downloads.sourceforge.net/shunit2/shunit2-2.0.3.tgz" | tar zx
- chmod +x $(pwd)/shunit2-2.0.3/src/shell/shunit2
script:
- export SHUNIT2=$(pwd)/shunit2-2.0.3/src/shell/shunit2
- make test
On Travis CI I see
Using worker: worker-linux-docker-f8d37801.prod.travis-ci.org:travis-linux-1
[...]
git.checkout
0.38s$ git clone --depth=50 --branch=master https://github.com/matteocorti/check_ssl_cert.git matteocorti/check_ssl_cert
Cloning into 'matteocorti/check_ssl_cert'...
remote: Counting objects: 285, done.
remote: Compressing objects: 100% (97/97), done.
remote: Total 285 (delta 187), reused 268 (delta 170), pack-reused 0
Receiving objects: 100% (285/285), 191.59 KiB | 0 bytes/s, done.
Resolving deltas: 100% (187/187), done.
Checking connectivity... done.
$ cd matteocorti/check_ssl_cert
$ git checkout -qf 85cb898990e583d8eac14ec693dbd79d9f3e9e6b
This job is running on container-based infrastructure, which does not allow use of 'sudo', setuid and setguid executables.
If you require sudo, add 'sudo: required' to your .travis.yml
See http://docs.travis-ci.com/user/workers/container-based-infrastructure/ for details.
before_script.1
0.26s$ curl -L "http://downloads.sourceforge.net/shunit2/shunit2-2.0.3.tgz" | tar zx
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 418 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
0 385 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 34113 100 34113 0 0 133k 0 --:--:-- --:--:-- --:--:-- 133k
before_script.2
0.00s$ chmod +x $(pwd)/shunit2-2.0.3/src/shell/shunit2
0.00s$ export SHUNIT2=$(pwd)/shunit2-2.0.3/src/shell/shunit2
The command "export SHUNIT2=$(pwd)/shunit2-2.0.3/src/shell/shunit2" exited with 0.
0.02s$ make test
( cd test && ./unit_tests.sh )
+[ -z /home/travis/build/matteocorti/check_ssl_cert/shunit2-2.0.3/src/shell/shunit2 ]
+[ ! -x /home/travis/build/matteocorti/check_ssl_cert/shunit2-2.0.3/src/shell/shunit2 ]
+SCRIPT=../check_ssl_cert
+[ ! -r ../check_ssl_cert ]
+NAGIOS_OK=0
+NAGIOS_CRITICAL=1
+NAGIOS_WARNING=2
+NAGIOS_UNKNOWN=3
+. ../check_ssl_cert --source-only
+VERSION=1.18.0
+SHORTNAME=SSL_CERT
+VALID_ATTRIBUTES=,startdate,enddate,subject,issuer,serial,modulus,serial,hash,email,ocsp_uri,fingerprint,
+[ != --source-only ]
+main
[...]
It seems that the script is called w/o parameters although a couple of lines above it clearly shows that it is not the case
+. ../check_ssl_cert --source-only
Am I missing something?
Travis is most likely using some shell other than bash for its /bin/sh interpreter and arguments to the ./source operator are not POSIX specified and that shell clearly doesn't support them.

How to `git diff --name-only master` with rugged?

In Ruby, using the rugged gem, how does one do the equivalent of the following?
%x(git diff --name-only master)
I need to list changed files that are either staged or unstaged.
Here's the solution I came up with:
files0 = %x(git diff --name-only master).split($RS)
require 'rugged'
files1 = []
changed = %i(index_modified index_new worktree_modified worktree_new)
repo = Rugged::Repository.new(Dir.pwd)
repo.status { |f, d| files1 << f unless (changed & d).empty? }
puts(files0.sort == files1.sort ? "PASS" : "FAIL")

Resources