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
I've been trying to customize my Bash prompt so that it will look like
[feralin#localhost ~]$ _
with colors. I managed to get constant colors (the same colors every time I see the prompt), but I want the username ('feralin') to appear red, instead of green, if the last command had a nonzero exit status. I came up with:
\e[1;33m[$(if [[ $? == 0 ]]; then echo "\e[0;31m"; else echo "\e[0;32m"; fi)\u\e[m#\e[1;34m\h \e[0;35m\W\e[1;33m]$ \e[m
However, from my observations, the $(if ...; fi) seems to be evaluated once, when the .bashrc is run, and the result is substituted forever after. This makes the name always green, even if the last exit code is nonzero (as in, echo $?). Is this what is happening? Or is it simply something else wrong with my prompt? Long question short, how do I get my prompt to use the last exit code?
As you are starting to border on a complex PS1, you might consider using PROMPT_COMMAND. With this, you set it to a function, and it will be run after each command to generate the prompt.
You could try the following in your ~/.bashrc file:
PROMPT_COMMAND=__prompt_command # Function to generate PS1 after CMDs
__prompt_command() {
local EXIT="$?" # This needs to be first
PS1=""
local RCol='\[\e[0m\]'
local Red='\[\e[0;31m\]'
local Gre='\[\e[0;32m\]'
local BYel='\[\e[1;33m\]'
local BBlu='\[\e[1;34m\]'
local Pur='\[\e[0;35m\]'
if [ $EXIT != 0 ]; then
PS1+="${Red}\u${RCol}" # Add red if exit code non 0
else
PS1+="${Gre}\u${RCol}"
fi
PS1+="${RCol}#${BBlu}\h ${Pur}\W${BYel}$ ${RCol}"
}
This should do what it sounds like you want. Take a look a my bashrc's sub file if you want to see all the things I do with my __prompt_command function.
If you don't want to use the prompt command there are two things you need to take into account:
getting the value of $? before anything else. Otherwise it'll be overridden.
escaping all the $'s in the PS1 (so it's not evaluated when you assign it)
Working example using a variable
PS1="\$(VALU="\$?" ; echo \$VALU ; date ; if [ \$VALU == 0 ]; then echo zero; else echo nonzero; fi) "
Working example without a variable
Here the if needs to be the first thing, before any command that would override the $?.
PS1="\$(if [ \$? == 0 ]; then echo zero; else echo nonzero; fi) "
Notice how the \$() is escaped so it's not executed right away, but each time PS1 is used. Also all the uses of \$?.
Compact solution:
PS1='... $(code=${?##0};echo ${code:+[error: ${code}]})'
This approach does not require PROMPT_COMMAND (apparently this can be slower sometimes) and prints [error: <code>] if the exit code is non-zero, and nothing if it's zero:
... > false
... [error: 1]> true
... >
Change the [error: ${code}] part depending on your liking, with ${code} being the non-zero code to print.
Note the use of ' to ensure the inline $() shell gets executed when PS1 is evaluated later, not when the shell is started.
As bonus, you can make it colorful in red by adding \e[01;31m in front and \e[00m after to reset:
PS1='... \e[01;31m$(code=${?##0};echo ${code:+[error: ${code}]})\e[00m'
--
How it works:
it uses bash parameter substitution
first, the ${?##0} will read the exit code $? of the previous command
the ## will remove any 0 pattern from the beginning, effectively making a 0 result an empty var (thanks #blaskovicz for the trick)
we assign this to a temporary code variable as we need to do another substitution, and they can't be nested
the ${code:+REPLACEMENT} will print the REPLACEMENT part only if the variable code is set (non-empty)
this way we can add some text and brackets around it, and reference the variable again inline: [error: ${code}]
I wanted to keep default Debian colors, print the exact code, and only print it on failure:
# Show exit status on failure.
PROMPT_COMMAND=__prompt_command
__prompt_command() {
local curr_exit="$?"
local BRed='\[\e[0;91m\]'
local RCol='\[\e[0m\]'
PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u#\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
if [ "$curr_exit" != 0 ]; then
PS1="[${BRed}$curr_exit${RCol}]$PS1"
fi
}
The following provides a leading green check mark when the exit code is zero and a red cross in all other cases. The remainder is a standard colorized prompt. The printf statements can be modified to present the two states that were originally requested.
PS1='$(if [ $? -eq 0 ]; then printf "\033[01;32m""\xE2\x9C\x93"; else printf "\033[01;31m""\xE2\x9C\x95"; fi) \[\e[00;32m\]\u#\h\[\e[00;30m\]:\[\e[01;33m\]\w\[\e[01;37m\]\$ '
Why didn't I think about that myself? I found this very interesting and added this feature to my 'info-bar' project. Eyes will turn red if the last command failed.
#!/bin/bash
eyes=(O o ∘ ◦ ⍤ ⍥) en=${#eyes[#]} mouth='_'
face () { # gen random face
[[ $error -gt 0 ]] && ecolor=$RED || ecolor=$YLW
if [[ $1 ]]; then printf "${eyes[$[RANDOM%en]]}$mouth${eyes[$[RANDOM%en]]}"
else printf "$ecolor${eyes[$[RANDOM%en]]}$YLW$mouth$ecolor${eyes[$[RANDOM%en]]}$DEF"
fi
}
info () { error=$?
[[ -d .git ]] && { # If in git project folder add git status to info bar output
git_clr=('GIT' $(git -c color.ui=always status -sb)) # Colored output 4 info
git_tst=('GIT' $(git status -sb)) # Simple output 4 test
}
printf -v line "%${COLUMNS}s" # Set border length
date=$(printf "%(%a %d %b %T)T") # Date & time 4 test
test=" O_o $PWD ${git_tst[*]} $date o_O " # Test string
step=$[$COLUMNS-${#test}]; [[ $step -lt 0 ]] && step=0 # Count spaces
line="$GRN${line// /-}$DEF\n" # Create lines
home="$BLD$BLU$PWD$DEF" # Home dir info
date="$DIM$date$DEF" # Colored date & time
#------+-----+-------+--------+-------------+-----+-------+--------+
# Line | O_o |homedir| Spaces | Git status | Date| o_O | Line |
#------+-----+-------+--------+-------------+-----+-------+--------+
printf "$line $(face) $home %${step}s ${git_clr[*]} $date $(face) \n$line" # Final info string
}
PS1='${debian_chroot:+($debian_chroot)}\n$(info)\n$ '
case "$TERM" in xterm*|rxvt*)
PS1="\[\e]0;${debian_chroot:+($debian_chroot)} $(face 1) \w\a\]$PS1";;
esac
Improved demure answer:
I think this is important because the exit status is not always 0 or 1.
if [ $EXIT != 0 ]; then
PS1+="${Red}${EXIT}:\u${RCol}" # Add red if exit code != 0
else
PS1+="${Gre}${EXIT}:\u${RCol}" # Also displays exit status
fi
To preserve the original prompt format (not just colors),
you could append following to the end of file ~/.bashrc:
PS1_ORIG=$PS1 # original primary prompt value
PROMPT_COMMAND=__update_prompt # Function to be re-evaluated after each command is executed
__update_prompt() {
local PREVIOUS_EXIT_CODE="$?"
if [ $PREVIOUS_EXIT_CODE != 0 ]; then
local RedCol='\[\e[0;31m\]'
local ResetCol='\[\e[0m\]'
local replacement="${RedCol}\u${ResetCol}"
# Replace username color
PS1=${PS1_ORIG//]\\u/]$replacement}
## Alternative: keep same colors, append exit code
#PS1="$PS1_ORIG[${RedCol}error=$PREVIOUS_EXIT_CODE${ResetCol}]$ "
else
PS1=$PS1_ORIG
fi
}
See also the comment about the alternative approach that preserves username color and just appends an error code in red to the end of the original prompt format.
You can achieve a similar result to include a colored (non-zero) exit code in a prompt, without using subshells in the prompt nor prompt_command.
You color the exit code portion of the prompt, while having it only appear when non-zero.
Core 2$ section of the prompt: \\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]
Key elements:
return code, if not 0: \${?#0} (specificly "removes prefix of 0")
change color without adding to calculated prompt-width: \\[\\033[0;31m\\]
\\[ - begin block
\\033 - treat as 0-width, in readline calculations for cmdline editing
[0;31;4m - escape code, change color, red fg, underline
\\] - end block
Components:
\\[\\033[0;31;4m\\] - set color 0;31m fg red, underline
\${?#0} - display non-zero status (by removing 0 prefix)
\\[\\033[0;33m\\] - set color 0;33m fg yellow
\$ - $ or # on EUID
\\[\\033[0m\\] - reset color
The full PS1 I use (on one host):
declare -x PS1="\\[\\033[0;35m\\]\\h\\[\\033[1;37m\\] \\[\\033[0;37m\\]\\w \\[\\033[0;33m\\]\\[\\033[0;31;4m\\]\${?#0}\\[\\033[0;33m\\]\$ \\[\\033[0m\\]"
Note: this addresses a natural extension to this question, in a more enduring way then a comment.
Bash
function my_prompt {
local retval=$?
local field1='\u#\h'
local field2='\w'
local field3='$([ $SHLVL -gt 1 ] && echo \ shlvl:$SHLVL)$([ \j -gt 0 ] && echo \ jobs:\j)'"$([ ${retval} -ne 0 ] && echo \ exit:$retval)"
local field4='\$'
PS1=$'\n'"\e[0;35m${field1}\e[m \e[0;34m${field2}\e[m\e[0;31m${field3}\e[m"$'\n'"\[\e[0;36m\]${field4}\[\e[m\] "
}
PROMPT_COMMAND="my_prompt; ${PROMPT_COMMAND}"
Zsh
PROMPT=$'\n''%F{magenta}%n#%m%f %F{blue}%~%f%F{red}%(2L. shlvl:%L.)%(1j. jobs:%j.)%(?.. exit:%?)%f'$'\n''%F{cyan}%(!.#.$)%f '
Images of prompt
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
}
I've got a touch screen without backlight control and and turning off the HDMI-output just brings up a "no signal" placeholder on the screen.
I've soldered a octocoupler to the power toggle to control it via the GPIO-pins and that works fine.
What I would like to do is to toggle a shell script (screen_toggle.sh) everytime the Pi changes DPMS state. I.e as soon as DMPS blanks the screen the screen_toggle.sh would be triggered, and as soon as mouse (touch) input is registered screen_toggle.sh would be triggered again to wake the screen.
Anyone with ideas on how to get this working?
For anyone coming here looking for a solution.
Got around to fix it by doing the following. screen_switch.sh calls the GPIO in the Pi in my case but could do what ever someone would want.
#!/bin/bash
idletime=60000 # in milliseconds
screen_toggle=1
while true; do
idle=`xprintidle`
#echo $idle >> /home/pi/idle.log
if [ "$idle" -gt "$idletime" ] && [ "$screen_toggle" == 1 ]
then
screen_toggle=0
source /home/pi/screen_switch.sh
echo "Turned off" >> /home/pi/screen_check.log
elif [ "$idle" -lt "$idletime" ] && [ "$screen_toggle" == 0 ]
then
screen_toggle=1
source /home/pi/screen_switch.sh
echo "Turned on" >> /home/pi/screen_check.log
fi
sleep 1
done
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.)