Find wrapped strings in a pre-push git hook - bash

I am trying to go through the changes being made in a git push and find any instance of a wrapped string like so: ___('something to be translated') so that I can make some subsequent API calls with that information. But for some reason I am unable to get any sort of useful output when I try to use a git diff --cached call. I already have my regex pattern figured out to do the search. Any suggestions on this would be great.
My code looks something like this:
FILES_PATTERN="/___[^)]+\)/gm"
git diff --cached --name-only | \
grep -E $FILES_PATTERN | \
GREP_COLOR='4;5;37;41' xargs grep --color --with-filename -n echo 'COMMIT contains strings that need to be uploaded to Lokalise.' && exit 1

Try this. I think your bash script is the main issue. It's not formatted properly. This will also make it so the output will only be the modified lines in addition to the ___() matches.
pattern="___\([^)]+\)"
echo $(git diff $remote_sha $local_sha | grep -E $pattern | grep '^[+-]')
Hope this works.

Related

How to run another git command only if ”git diff“ outputs something?

How can I run a command in bash or any other bash-like shell only if another command outputs something?
I am working on an API that updates the cache storage of a tool and commits the updated cache to GitHub. What I want to do is, after rebuilding the cache, I want to check if git diff HEAD ./path/to/cache outputs something and if it does, then I want to run git add ./path/to/cache.
Unfortunately, this doesn't work:
git diff HEAD ./path/to/cache && git add ./path/to/cache
I think because the first command doesn't return false or exit and instead outputs an empty string or nothing at all. So, what's the correct way to achieve this in bash?
Use the --exit-code option, which causes git diff to exit with 0 if there are no diffs and 1 otherwise. Then you can ignore the output.
git diff --exit-code HEAD ./path/to/cache || git add ./path/to/cache
The answer posted by #chepner is definitely the best for my use case. But my question was originally for any use case. If you want to achieve the same behavior for any other commands, you can use the grep command to check if the first command outputs anything.
git diff HEAD ./path/to/cache | grep -q . && git add ./path/to/cache
[ -z $(git diff HEAD ./path/to/cache) ] || git add ./path/to/cache
# [ EXPRESSION ], -z the length of STRING is zero.
# || If no, execute subsequent shell

One-liner to check whether a file exists, then feed it to xargs

I have a one-liner that spits out all of the files modified in my current feature branch, which is branched off of a shared, upstream development branch. I then hope to feed the files that exist to the phpcs linter via xargs -- something like this:
git diff --name-only shared-upstream-development-branch | grep "\.php$" | xargs test -f {} && echo {} | xargs vendor/bin/phpcs
However, when I run this, I get something like the following:
test: extra argument
‘path/to/my/file.php’
I feel like I'm close to having a working solution.
How can I modify the one-liner above to correctly see if each PHP file still exists, then feed it onward to phpcs?
I know that everything up through the output of the grep command works well, as removing the two parts of the one-liner that refer to xargs gives me a nice list of file names.
(I also tried using --diff-filter=d to filter out deleted files, but this does not seem to work with my version of git, as I still get a complaint from phpcs about how a file "does not exist.")
&& separates commands, and is not an argument to xargs; you need to execute an explicit shell to use &&.
xargs sh -c 'test -f "$1" && echo "$1"' _ {}

Is there any way to type some text automatically on terminal?

When I use git via terminal I open a tab dedicated to git command only, then I don't want to type "git " every time. Is there any way to make some text automatically typed on every line?
You can define every git command as an alias, so that for example typing diff mybranch will invoke git diff mybranch. To invoke the normal shell command, type a backslash before it, for example \diff file ../elsewhere/file invokes /usr/bin/diff and not git diff.
Put the following code in a file ~/.git.bashrc. Configure your git terminal to run bash --rcfile ~/.git.bashrc instead of just running bash.
. ~/.bashrc
for c in $(COLUMNS=4 git help -a | sed -n 's/^ \([a-z]\)/\1/p';
git config --get-regexp '^alias.' | sed 's/alias\.//; s/ .*//')
do
alias "$c=git $c"
complete -F _complete_alias foo
done
The complete line requires the _complete_alias function.
I created this .bashrc function that pushes the code and tags it.
All you need to give it is the comment you want for the push.
The alias of the function is "gp" (which stands for git push).
So if you want to push and tag some code all you need after you add this code to your .bashrc is:
$ gp "test my new git push function"
gpfunction() {
git status
echo [Enter to continue...]
read a
git pull
git commit -am"$1"
git push
tag_major_min=$(git tag |sort -V|tail -1|awk -F. '{print $1 "." $2 "."}')
echo Tag major min $tag_major_min
latest_tag_number=$(git tag |sort -V|tail -1|awk -F. '{print $3}')
echo Latest tag number $latest_tag_number
next=$(echo $latest_tag_number + 1 | bc)
echo Next $next
new_tag=$(echo $tag_major_min $next | sed 's/ //g')
echo New tag $new_tag
git tag $new_tag
git push origin $new_tag
}
alias gp=gpfunction
This script uses a major.minor.patch version standard and increments the patch version.
You can tweak it as you please.

flake8 behaves differently when run from within a bash script

I suppose the answer here might be trivial, but I it might require some intricate bash knowledge. I have been browsing bash docs for a few hours now and can't seem to find the answer.
I'm working on a python repository, and came up with a simple script to lint only the files that differ between the current branch and master. Here's the minimal working example, extracted from said script (lint.sh):
#!/bin/bash
paths=$(git diff --name-only -r origin/master...HEAD | grep \.py$)
flake8 $paths
For testing purposes, let's say I only committed one file, bad.py, with the following contents:
hello
there
The expected output of bash lint.sh is:
bad.py:1:1: F821 undefined name 'hello'
bad.py:2:1: F821 undefined name 'there'
However, the output is empty. When run in debug mode, bash shows the following commands:
++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths='bad.py'
+ flake8 'bad.py'
Which is what I expect. Also, when I simply run flake8 bad.py, the output is as expected.
I expect this might have something to do with parameter passing which varies between different bash versions. The output of bash --version:
GNU bash, version 4.4.23(1)-release (x86_64-apple-darwin17.5.0)
I will appreciate all insights
Very sorry this isn't exactly an answer, but it surely didn't fit in a comment!
The hint here to me is the following:
+ paths='bad.py'
+ flake8 'bad.py'
In my execution of the same script, I get the following:
$ bash -x lint.sh
++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths=bar.py
+ flake8 bar.py
bar.py:1:1: F821 undefined name 'hello'
bar.py:2:1: F821 undefined name 'world'
Notice here how my output does not contain quotes around the filename or the assignment. bash won't usually add quotes unless they are necessary. What this tells me is there's probably some sort of control character in that string (my best guess is either colors or \b + some other characters (this might be one of the few cases where a screenshot is actually helpful!)).
Here's one way that I was able to reproduce your findings:
mkdir -p bin
cat > bin/grep << EOF
#!/usr/bin/env bash
exec /bin/grep --color=always "\$#"
EOF
chmod +x bin/grep
# simulate having this `grep` on your path
PATH=$PWD/bin:$PATH bash -x lint.sh
(and while this seems like an odd thing to do, in the past I've put my own grep in ~/bin so I could add --line-buffered --color=auto now that GREP_OPTIONS is deprecated -- one might erroneously add --color=always and have it work... for the most part). Today I use an alias instead since I ran into sharp edges even with that.
The output in that case matches yours above:
$ PATH=$PWD/bin:$PATH bash -x lint.sh
++ git diff --name-only -r origin/master...HEAD
++ grep '.py$'
+ paths='bar.py'
+ flake8 'bar.py'
But the tricky hint is in the highlighting
addendum
While unrelated to your problem, here's probably a better way to accomplish what you want:
# if you have GNU xargs
git diff -z --name-only origin/master...HEAD -- '*.py' | xargs --null --no-run-if-empty flake8
# if you need to be more portable (I see you're probably on macos)
git diff -z --name-only origin/master...HEAD -- '*.py' | xargs -0 flake8 /dev/null
Explanation of the different parts:
git diff -z: output filenames with null bytes delimiting. This prevents splicing if filenames contain spaces or other special characters
xargs --null: split the input by null bytes when splatting arguments
xargs --no-run-if-empty: don't run the executable at all if there's no arguments (this is a GNU extension)
xargs -0: same as xargs --null, however if you're stuck with non-GNU xargs you won't have access to the long options
flake8 /dev/null: this is a sneaky trick, since there's no "no run if empty" option to bsd xargs, it's always going to invoke flake8. If flake8 gets invoked with zero arguments, it defaults to recursing your current working directory (and linting all your files). By putting /dev/null at the beginning this prevents this behaviour and instead lints an empty file!
Addendum 2, you probably might want to consider using a git hooks framework to handle all of this for you, I maintain pre-commit which aims to smooth out a lot of the rough edges around git (such as this one!).

Git pre-commit hook: Prevent commits that contain executable files

I'm trying to avoid accidentally committing binaries into my repo. I considered a hook that detects filesizes above some threshold but I think it will be more useful to fail the pre-commit hook anytime my commit changes a file with an executable permission bit.
I know how to tackle this with python/ruby/other scripting languages but ideally I can do it with just bash. Any ideas?
I ended up with this. It lists the filenames being committed relative to REPO_ROOT. It passes those to ls with -1 flag for one-per-line and -F flag that appends * to executables. It greps for trailing *. Any matching grep fails the hook.
cd $REPO_ROOT
STAGED_EXECUTABLES=$(git diff --diff-filter=ACMRTUXB --cached HEAD --name-only | xargs ls -1F | egrep '\*$')
EXECUTABLES_MISSING=$?
if [ $EXECUTABLES_MISSING -eq 0 ]; then
echo "You tried to commit an executable file. Override with \`git commit --no-verify\` if required." > /dev/stderr
exit 1
fi

Resources