How to run a script on every commit - bash

I want to run a script locally whenever someone in RepoA, makes a commit on a branch
currentBranch=$(git rev-parse --abbrev-ref HEAD)
if [[ $currentBranch = *"my-"* ]]; then
echo "contains my"
else
echo "Hold on there! you need to rename your branch before you commit!
"
fi
I have this running so far which works, whenever I run npm run test:script it runs > "test:script": "./branchname.sh"
however, I have some issues. one how can I run this every time someone commits?
I have tried putting it in my package.json
"pre-commit": [
"lint",
"test:script"
],
but it doesn't run on every commit
also, how can I get the commit itself to abort if the script fails, i.e. jump into the else block

You can take advantage of git hooks. There is a bunch of files you can find in your .git/hooks folder whitin your project folder.
Here the full documentation: https://git-scm.com/book/gr/v2/Customizing-Git-Git-Hooks
Note that you need to give exec permissions to hook files.
Hooks can be basically bash files, so you can abort the commit exiting with a value != 0.
Here an example: https://github.com/xyzale/my-stuff/blob/master/git/hooks/pre-commit
In order to share the hooks with your collaborators you can add a hooks/ folder to your repository and either symlinking it to .git/hooks or editing the git config about the hooks location through the command
git config core.hooksPath hooks

Related

Why all the script in my git hooks (pre-commit, post-commit, pre-receive, pre-push etc) do not run?

Why all the script in my git hooks (pre-commit, post-commit, pre-receive, pre-push etc) do not run?
Note:
this question is not a duplicate;
I have try the answer to each of the other questions but none of them work.
I did chmod +x, added the path to hook. rename script, neither of them solve my issue.
Inside my git
branches config description HEAD hooks info objects refs
Inside hooks:
applypatch-msg.sample fsmonitor-watchman.sample post-update.sample pre-commit prepare-commit-msg.sample pre-rebase.sample update.sample
commit-msg post-merge.sh pre-applypatch.sample pre-commit.sample pre-push pre-receive
I run them manually and they are all working fine.:
$ bash pre-commit
You are about to commit to master
Do you really want to do this? [y/n] y
pre-commit script
#!/bin/sh
#
# An example hook script to verify what is about to be committed.
# Called by "git commit" with no arguments. The hook should
# exit with non-zero status after issuing an appropriate message if
# it wants to stop the commit.
#
# To enable this hook, rename this file to "pre-commit".
echo "You are about to commit" $(git diff --cached --name-only --diff-filter=ACM)
echo "to" $(git branch --show-current)
while : ; do
read -p "Do you really want to do this? [y/n] " RESPONSE < /dev/tty
case "${RESPONSE}" in
[Yy]* ) exit 0; break;;
[Nn]* ) exit 1;;
esac
done
But when i git commit and git push to the repository none of the scripts work.
$git commit -m "Test hooks"
[master] Test hooks 1 file
changed, 1 insertion(+)
My git version is 2.39.1
I created the repository on a VM with Ubuntu 18.04.6 LTS installed
Here was the procedure fro creating the repo.
mkdir project1.git
cd project1.git
git init --bare
After the creation i clone the repo to my local computer (windows).
Clone the git repository
git clone git#{ip}:/home/git/git_repositories/project1.git/
But I want use the scripts in project1.git/hooks to make this work.
A pre-commit hook for instance would not work in a bare repository (which has no working tree).
It would work only in your cloned repository, on Windows, where you can create a myClonedRepo/.git/hook/pre-commit script (no extension, no .sh), which will be run before each commit.
From the comments:
all users could create/clone their repositories from a shared Git template repository. The article "Creating a Custom Git Template" gives an illustration of that approach, but means that:
every user must access the same shared folder
they need to activate the init.templateDir config setting in their global Git configuration
any check which must be enforced for all the team members, especially for a distributed team, is best managed by server-side hooks instead.

Prevent git from overwriting file owner upon git pull

I've seen a handful of similar questions on here, but none of the solutions given seem to be working... wondering if they're outdated, or this case is somehow different...so I wanted to open up a new thread to talk about it.
I've run into a frustrating problem where, every time I perform and git pull, it changes the owner to the pull-er's user. What happens then is that the site shows the following error:
Warning: file_get_contents(/var/www/html/wp-content/themes/<my-theme>/resources/views/<changed-file>): failed to open stream: Permission denied in /var/www/html/wp-includes/class-wp-theme.php on line 1207
which can only be fixed by running chown www-data on the changed file.
This will become an issue when more people begin to work on the site, or when important files are change (default template/header/footer..), and the site goes blank until chown is run.
Site details
Laravel, wordpress, ubuntu 18, armor hosting
Git repo stored in custom theme
I've tried a few solutions, but none seem to work, (perhaps because they're implemented incorrectly..)
Solutions I've tried
1: set filemode to false - I set filemode to false, locally and globally, on my local machine and the server in question. I've tried changing the case to "fileMode" too.
2: implement post-update hook - I added a post update hook to automatically update the file permissions/ownership. Here's the script (note that the git repo is in the custom theme):
#!/bin/sh
# default owner user
OWNER="www-data:www-data"
# changed file permission
PERMISSION="664"
# web repository directory
REPO_DIR="/var/www/html/wp-content/themes/quorum-theme"
# remote repository
REMOTE_REPO="origin"
# public branch of the remote repository
REMOTE_REPO_BRANCH="master"
cd $REPO_DIR || exit
unset GIT_DIR
files="$(git diff-tree -r --name-only --no-commit-id HEAD#{1} HEAD)"
git merge FETCH_HEAD
for file in $files
do
sudo chown $OWNER $file
sudo chmod $PERMISSION $file
done
exec git-update-server-info
Let me know if there is anything else worth trying, or if you notice an issue with my code...
All the best,
Jill
You are pretty close to the correct solution.
You need to enable the following hooks:
post-merge, called after a successful git pull
post-checkout, called after a successful git checkout
If you are sure to only use git pull, the post-merge hook is enough.
Enabling both hooks guarantee you the hook is always called at not extra cost.
The content of the hook should be like:
#!/bin/sh
# default owner user
OWNER="www-data:www-data"
# web repository directory
REPO_DIR="/var/www/html/wp-content/themes/quorum-theme"
echo
echo "---"
echo "--- Resetting ownership to ${OWNER} on ${REPO_DIR}"
sudo chown -R $OWNER $REPO_DIR
echo "--- Done"
echo "---"
The script will reset the ownership to OWNER of all files and directory inside REPO_DIR.
I have copied the values from your post, eventually change it to your needs.
To enable the hook you should:
create a file named post-merge with the script above
move it inside the directory .git/hook/ of your repo
give it the executable permission with chmod +x post-merge
Repeat eventually these steps for the post-checkout hook, that needs to be equal to the post-merge hook.
Pay attention to perform a sudo git pull if your user is not root. All the files and directories in the target directory are owned by www-data, you need to perform the git pull command with a superuser privilege or the command will fail.
From the looks of your question, it looks like you are using git pull to deploy in production.
git is not a deployment tool. If you want to deploy your code, I would invite you to write a deployment script.
The first version of your script could be :
# deploy.sh
# cd to the appropriate directory :
cd /var/www/mysite
# change to the correct user before pulling :
sudo -u www-data git pull
An updated version would be to stop depending on git pull.
Ideally : you want to be able to identify the versions of your code that can be deployed to productions, and not depend on the fact that "git pull will work without triggering merge conflicts".
Here is the outline of a generic workflow you can follow :
When you want to deploy to production :
produce some artifact that packs your code from an identified commit : for php code this can be a simple .tar.gz
# set a clearly identifiable tag on target commit
git tag v-x.y.z
# create a tar.gz archive that stores the files :
# look at 'git help archive'
git archive -o ../myapp-x.y.z.tgz v-x.y.z
push that artifact your production server
scp myapp-x.y.z.tgz production-server:
run your deployment script, without calling git anymore :
# deploy.sh :
# usage : ./deploy.sh myapp-x.y.z.tgz
archive="$1"
# extract the archive to a fresh folder :
mkdir /var/www/mysite.new
tar -C /var/www/mysite.new -xzf "$archive"
chown -R www-data: /var/www/mysite.new
# replace old folder with new folder :
mv /var/www/mysite /var/www/mysite.old
mv /var/www/mysite.new /var/www/mysite
Some extra actions you will generally want to manage around your deployment :
backup your database before deploying
hanlde config parameters (copy your production config file ? setup the environment ? ...)
apply migration actions
restart apache or nginx
...
You probably want to version that deploy.sh script along with your project.
My approach works for me.
First, add a file named post-merge to /path/to/your_project/.git/hooks/
cd /path/to/your_project/.git/hooks/
touch post-merge
Then, change it's ownership to same as <your_project> folder(this is the same as nginx and php-fpm runner), in my case, I use www:www
sudo chown www:www post-merge
Then change it's file mode to 775(then it can be executed)
sudo chmod 775 post-merge
Then put the snippet below to post-merge. To understand the snippet, see here(actually that's me).
#!/bin/sh
# default owner user
OWNER="www:www"
# changed file permission
PERMISSION="664"
# web repository directory
REPO_DIR="/www/wwwroot/your_project/"
# remote repository
REMOTE_REPO="origin"
# public branch of the remote repository
REMOTE_REPO_BRANCH="master"
cd $REPO_DIR || exit
unset GIT_DIR
files="$(git diff-tree -r --name-only --no-commit-id HEAD#{1} HEAD)"
for file in $files
do
sudo chown $OWNER $file
sudo chmod $PERMISSION $file
done
exec git-update-server-info
Everything is done, now, go back to your_project folder
cd /path/to/your_project/
run git pull under your_project folder, remember you must run as root or sudo(I remember sudo)
sudo git pull
Now check the new file that pulled from remote repository, see if its ownership has been changed to www:www(if it was as expected, the ownership of the new pulled file should be changed to www:www).
This approach is much better than sudo chown -R www:www /www/wwwroot/your_project/, because it only change the new file's ownership, not all of then! Say I just pulled 2 new file, if you change the whole folder's ownership, it's costs more time and server resources(cpu usage, memory usage...), that's totally unnecessary.

Commit git hook to repo

I have a problem with configuration tslint and pre-commit hook.
The point is that i created tsconfig file which work's well. And added bash script which not allow me to commit if tslint return any bugs. Problme is that i need commit this hook file for other people in my team. This should automatly replace pre-commit hook from .git folder. I just found a bash script which check my hooks in 'hooks' folder and replace them in .git folder. How can I commit this and make this 'automatically' for my team?
"Committing a hook" is not possible for security reasons. If you could, then someone just cloning your repo and running basic operations could get arbitrary code executed on their machines.
Two common ways to deal with this are:
Document what people have to do to get the hook operational in their repository.
Automate what people have to do to get it. For example, in a project using a Makefile, I have this in the Makefile and people can just run make setup-pre-push-hook to get the hook to run "make check" whenever they push:
setup-pre-push-hook: setup-pre-push-hook-file
grep -q 'make check' .git/hooks/pre-push || \
printf '\n%s\n\n' 'make check' >> .git/hooks/pre-push
setup-pre-push-hook-file:
test -f .git/hooks/pre-push || echo '#!/bin/sh' >.git/hooks/pre-push
test -x .git/hooks/pre-push || chmod +x .git/hooks/pre-push

Test push conflicts on git push via Pre-Receive Hook

I'm making a Pre-Receive Hook on BitBucket that is supposed to confirm that all pushes made in a branch are up-to-date with parent Branches.
I mean, in a temporal evolution, we have several branches creations:
Branch creation during time
With the above example os 3 branches, Dev, Feature1, and my Local, i want to, before making push of Local to remote/origins/Feature1, make git merge from the latest Feature1 with the recent on-push Local Code. In this way, i can confirm that, whoever is making the push, is using the latest version of feature1, and there will be no conflict.
If it were any conflict, i would return 1, to avoid making the push! and obligate the Developer to pull from Feature before push is code.
This is my script on Pre-Receive Hook.
while read from_ref to_ref ref_name; do
echo "Ref update:"
echo " Old value: $from_ref"
echo " New value: $to_ref"
echo " Ref name: $ref_name"
echo " Diff:"
git clone --progress -v $GIT_URL $CLONE_DIR1
cd $CLONE_DIR1
git checkout -b test remotes/origin/Feature1
git merge --no-commit -m "Merging feature with local on-push code" $ref_name
(....)
done
I've tried with ref_name, to_ref, and having no success.
Anyone can help me?
How can I access the recent pushed code, and merge by parent branch with this code?
This seems like a very odd thing to do, and it is probably doomed to failure. It will certainly be complicated and you will want to change your test behavior based on which ref(s) are being updated and whether these update add merge commit(s).
That said, there are some special rules for pre-receive and update hooks, and if you obey them you will get somewhat further:
Do not chdir or cd away from the current directory. Or, if you do, make sure you chdir back, but usually it's not too difficult to make sure that operations that must run in another directory, run as a separate process: either a sub-shell, or another script.
Remove $GIT_DIR from the environment before attempting git commands that need to use a different repository. The reason is that the hook is run in the top level directory with $GIT_DIR set to either .git (non-bare repo) or . (bare repository).
Putting those two together, you might move all your verifier code into a separate script and do something like this:
exitstatus=0
while read from_ref to_ref ref_name; do
... maybe some setup code here to see if $ref_name
is being created or destroyed ...'
case "$ref_name" in
... add cases as needed to choose action based on ref ...
if (unset GIT_DIR; /path/to/check_script arg1 arg2 ...); then
echo "push being rejected because ..."
exitstatus=1
fi
...
esac
...
done
exit $exitstatus
There is still one very big problem here. You want check_script to be able to access any proposed new commits that would become reachable from $ref_name if the hook script exits 0 so that the proposed update to it is allowed. That update has not yet occurred: $ref_name still points to the old SHA-1 $from_ref. Meanwhile, the new SHA-1 in $to_ref might not have any name pointing to it (though it does still exist in the underlying repository).
Among other things, if $to_ref points to new commits (the usual case), any clone you make at this point, via normal git operations, will not contain those commits, so you will not be able to use them.
There are two obvious ways to handle this:
Make a new (temporary) reference that points to $to_ref. You can then see the proposed commits in the clone.
Don't use a clone. Copy the repository some other way, or use the original repository itself directly, e.g., as an "alternate", or by creating a temporary work tree directory and pointing $GIT_WORK_TREE there, or using some of the new git worktree features that have appeared in git 2.6+. (If you choose the manual temporary work-tree method, be sure to think about the normal shared $GIT_INDEX_FILE as well.)
Remember also to check for forced pushes that remove commits from a branch, or even remove-some-and-add-others all in one push.
UPDATE:
This question is resolved for me.
The final code is this:
#!/bin/bash
DIR=xpto/external-hooks/code_review
CLONE_DIR=$DIR/test_conflict_push-$(date +%s)
GIT_URL=myGitUrl
exitStatus=0
read oldrev newrev refname
feature_branch=${refname##refs/heads/}
echo "Feature branch-> $feature_branch"
#Clone feature branch from remote repo to be update via merged.
git clone --progress -v $GIT_URL $CLONE_DIR
currentDir=$PWD
cd $CLONE_DIR
#create branch named 'latest' to put new and modify files
git checkout -b latest remotes/origin/$feature_branch
#go back to PWD otherwise cant make git diff
cd $currentDir
# Get the file names, without directory, of the files that have been modified
# between the new revision and the old revision
echo "Getting files"
files=`git diff --name-only ${oldrev} ${newrev}`
echo "Files -> $files"
# Get a list of all objects in the new revision
echo "Getting objects"
objects=`git ls-tree --full-name -r ${newrev}`
echo "objects -> $objects"
# Iterate over each of these files
for file in ${files}; do
# Search for the file name in the list of all objects
object=`echo -e "${objects}" | egrep "(\s)${file}\$" | awk '{ print $3 }'`
# If it's not present, then continue to the the next itteration
if [ -z ${object} ];
then
continue;
fi
# Otherwise, create all the necessary sub directories in the new temp directory
mkdir -p "${CLONE_DIR}/`dirname ${file}`" &>/dev/null
# and output the object content into it's original file name
git cat-file blob ${object} > ${CLONE_DIR}/${file}
done;
echo "Ready for start merging."
cd $CLONE_DIR
#add new files to branch
echo $(git add .)
#commit added and modify files to branch
echo $(git commit -a -m "Merge latest to original feature")
#get generated commit id
echo $(git log -1)
#create branch named 'merged' to merge above commited files
echo $(git checkout -b merged remotes/origin/$feature_branch)
#merge only occurs for madded and modify files!
echo "Merging committed files to 'merged' branch with from 'latest' branch."
mergeResult=$(git merge --no-commit latest)
echo "Merge Result -> $mergeResult"
##to lower case
if [[ "${mergeResult,,}" == *"conflict"* ]]
then
echo "Merge contains conflicts."
echo "Update your $feature_branch branch!"
exitStatus=1
else
echo "Merge don't contains conflict."
echo "Push to $feature_branch can proceed."
exitStatus=0
fi
#remove temporary branches
echo $(git checkout master)
echo $(git branch -D latest)
echo $(git branch -D merged)
#delete temporary clone dir
rm -rf $CLONE_DIR
exit $exitStatus
Many Thanks.

git commit and push via batch file on Windows

I do same task often of committing and pushing changes to remote branch. Being lazy sometimes, I needed to put set of git commands to automatically perform these steps:
cd D:\wamp\www\projectName
git checkout dev
git add .
git commit -am "made changes"
git push
pause
I also tried:
cd D:\wamp\www\projectName
call git checkout dev
call git add .
call git commit -am "made changes"
call git push
pause
and
cd D:\wamp\www\projectName
git.exe checkout dev
git.exe add .
git.exe commit -am "made changes"
git.exe push
pause
Everything works excpet for the final push command. Here is output:
D:\wamp\www\givingcircle>git checkout dev
Already on 'dev'
Your branch is ahead of 'origin/dev' by 1 commit.
D:\wamp\www\givingcircle>git add .
D:\wamp\www\givingcircle>git commit -am "made changes"
# On branch dev
# Your branch is ahead of 'origin/dev' by 1 commit.
#
nothing to commit, working directory clean
D:\wamp\www\givingcircle>git push
Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
D:\wamp\www\givingcircle>pause
Press any key to continue . . .
As you can see, for push, I am getting:
D:\wamp\www\givingcircle>git push
Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
When I run above commands via git shell itself, everything works fine. I have also added git to Windows Path env variables.
Does anyone have an idea of why it works on git shell and not on batch command ? (even though other commands work but not push)
For me, by default, Windows executes .sh files correctly using Git Bash. So I would write your script as a regular bash shell script:
#!/bin/sh
cd /d/wamp/www/projectName
git checkout dev
git add .
git commit -am "made changes"
git push
echo Press Enter...
read
I had a similar need, to be able to move code from BBCloud to our development test servers, for stage 1 testing.
To do this, I created a Windows scheduled task:
Under "Actions", I added "C:\Program Files\Git\bin\bash.exe" in Program/script field (the quotes were required).
In the "Add arguments" field, I entered c:\path\to\bash script\pull.sh.
I then completed the Task Scheduler wizard (run frequency, time, etc.).
I then created a bash script, using Nano in Git Bash for Windows containing:
#!/bin/bash
cd /c/path/to/bash script
git pull
I would prefer a push to the repository automatically pushing down to the test server, but Pipes, Webhooks, and DeployHQ don't seem to be a solution for our environment.
Try this one !!
cd c://TESTS/path
set HOME=%USERPROFILE%
GIT COMMAND GOES HERE
pause

Resources