10

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

Jillian Hoenig
  • 137
  • 1
  • 6
  • 28

3 Answers3

8

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.

Yusef Maali
  • 2,201
  • 2
  • 23
  • 29
  • Thank you so much - this answer worked well. Thanks for making your response so clear and concise. Including the post-checkout hook was also a great catch.. wouldn't have thought of it, but it'll save me in the future when I ultimately run into it. – Jillian Hoenig Jun 22 '20 at 16:07
3

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 :

  1. 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
    
  2. push that artifact your production server

    scp myapp-x.y.z.tgz production-server:
    
  3. 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.

LeGEC
  • 46,477
  • 5
  • 57
  • 104
  • 3
    Your question was "how do I prevent git from changing the owner ?", this answer is technically not an direct answer to your question. The direct answer is : when `git` creates some files, it does it with the current user. Change to the user you expect before pulling : something like `sudo -u www-data git pull` – LeGEC Jun 21 '20 at 13:54
  • 2
    I would like to point out that there is no reason not to use git to deploy: https://security.stackexchange.com/questions/45452/is-using-git-for-deploying-a-bad-practice – online Thomas Jun 22 '20 at 14:17
  • @LeGEC Actually the `www-data` user usually is a non-login user, its just for running web service daemons(such `nginx` `php-fpm` `httpd`) so you can not use `sudo -u www-data`. – Willis Feb 15 '22 at 05:36
0

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.

Willis
  • 599
  • 3
  • 25