I've done some research and I'm attempting to reproduce something similar to JONATHAN NICOL's blog post, "Automated git deployments from Bitbucket".


I've enabled logging of the output from the exec() function with the addition of 2>&1 at the end of my command.

When I print the contents of what is returned to deploy.log I receive the following:

//FROM exec('cd /home/apache/www.websitename.org.git && git fetch 2>&1');

    [0] => fatal: Not a git repository (or any parent up to mount point /home)
    [1] => Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

//OUTPUT FROM exec('cd /home/apache/www.websitename.org.git && GIT_WORK_TREE=/var/www/html/www.websitename.org /usr/bin/git checkout -f');

    [0] => fatal: Not a git repository (or any parent up to mount point /home)
    [1] => Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).

Description of Problem:

If I copy the output the commands executed within the exec() function contained in the referenced PHP Web Hook script, the changes made to files when my feature branch is merged into dev are replicated in the appropriate directory. My problem is that I would like to ensure that the commands running within exec() function are actually being executed.

At first I thought this was due to a permissions issue, but I'm issuing the same commands from the shell as the same user that is issuing the commands via PHP's exec() function.

I can't seem to figure out why the commands generated by the Web Hook script work from the apache user's shell, but they do not work when executed by PHP which is being run as apache and generating them.

Description of Environment:

I have BitBucket server v4.7.1 running on a RedHat 7.2 machine. This server also houses a development environment where I would like to automatically deploy files when specific branches are merged.

My web root is /var/www/html which apache has access to. When I execute a <?php echo exec('whoami'); ?> the output is apache.

I'm running PHP 5.6.23, and I can execute shell commands from php scripts.

My php user is apache, apache also has a shell connected to the user.

My default branch in GIT is master, and I also have dev and staging branches. Typical workflow is to create a branch off of master and commit changes to the feature branch. Then merge that feature branch into dev, then staging and eventually back into master.

Using the structure of Nicol's blog post and the amended BitBucket Repo for use with bitbucket.org, I've been able to replicate the following:

The GIT repository is cloned as a mirror with the following command:

AS apache user:

cd /home/apache/
git clone --mirror ssh://git@path.to.bitbucket.local:7999/wn/www.websitename.org.git


cd /home/apache/www.websitename.org.git
GIT_WORK_TREE=/var/www/html/www.websitename.org git checkout -f dev

Apache has appropriate permissions to the .git repo, apache has access to git in its $PATH, and apache has ownership and permissions of /var/www/html/www.websitename.org

I'm checking out dev in this scenario because my Web Hook in BitBucket is setup to respond when feature branches are merged into dev and pushed up to BitBucket.

The PHP script called by the Bitbucket Server Web Post Hooks Plugin resembles the following:



//$repo_name = $_GET['repoName'];
//$client = $_GET['client'];

//for debug
//file_put_contents('deploy.log', serialize($_POST['payload']), FILE_APPEND);
//file_put_contents('deploy.log', $_GET['client'], FILE_APPEND);
//file_put_contents('deploy.log', $_GET['repoName'], FILE_APPEND);

//file_put_contents('deploy.log', print_r($_POST), FILE_APPEND);

// Full path to git binary is required if git is not in your PHP user's path. Otherwise just use 'git'.
$git_bin_path = '/usr/bin/git';

echo getcwd() . "\n";

$update = false;

// Parse data from Bitbucket hook payload
//$payload = json_decode($_POST['payload']);

if ( isset($_POST['payload']) ) { // old method
    $payload = $_POST['payload'];
} else { // new method
    $payload = json_decode(file_get_contents('php://input'),false);

/*if(function_exists('exec')) {
file_put_contents('deploy.log', print_r("exec enabled",true), FILE_APPEND);
    file_put_contents('deploy.log', print_r("exec NOT enabled",true), FILE_APPEND);

if(function_exists('chdir')) {
file_put_contents('deploy.log', print_r("chdir enabled",true), FILE_APPEND);
    file_put_contents('deploy.log', print_r("chdir NOT enabled",true), FILE_APPEND);

file_put_contents('deploy.log', print_r($payload,true), FILE_APPEND);*/

//set repo name
$repo_name = $payload->repository->slug;

file_put_contents('/var/www/html/deploy/deploy.log', print_r($repo_name.":",true), FILE_APPEND);

$web_root_dir = '/var/www/html/lt/'.$repo_name;
$repo_dir = '/home/apache/'.$repo_name.'.git';

$dir = getcwd();
file_put_contents('/var/www/html/deploy/deploy.log',print_r("BEFORECHDIR:".$dir."CHDIRBEFORE\r\n"), FILE_APPEND);
file_put_contents('/var/www/html/deploy/deploy.log', print_r("repo".$repo_dir."repo\r\n",true), FILE_APPEND);


file_put_contents('/var/www/html/deploy/deploy.log', print_r("DIR:repo".getcwd()."repo:DIR \r\n",true), FILE_APPEND);

$test = chdir($repo_dir);

file_put_contents('/var/www/html/deploy/deploy.log', print_r($test,true), FILE_APPEND);

if(count($payload->refChanges) == '0'){
    $update = false;
}elseif($payload->refChanges[0]->refId == 'refs/heads/dev' ){
    $update = true;

file_put_contents('/var/www/html/deploy/deploy.log', print_r("This is the update:".$update.": this ends update \r\n",true), FILE_APPEND);

if ($update) {
    file_put_contents('/var/www/html/deploy/deploy.log', print_r("It's an update",true), FILE_APPEND);
    // Do a git checkout to the web root

    $cmd1 = $git_bin_path  . ' fetch';
    $cmd2 = 'GIT_WORK_TREE=' . $web_root_dir . ' ' . $git_bin_path  . ' checkout -f ';

file_put_contents('/var/www/html/deploy/deploy.log', print_r($cmd1,true), FILE_APPEND);
file_put_contents('/var/www/html/deploy/deploy.log', print_r($cmd2,true), FILE_APPEND);

    $test = chdir($repo_dir);
    file_put_contents('/var/www/html/deploy/deploy.log', print_r($test,true), FILE_APPEND);

    exec("cd ".$repo_dir." && ". $cmd1 ." 2>&1",$out1,$outtest);
    if($outtest !==0 ){
        file_put_contents('/var/www/html/deploy/deploy.log', print_r($out1,true), FILE_APPEND);

    exec("cd ".$repo_dir." && ". $cmd2 ." 2>&1",$out2,$outtest1);
    if($outtest1 !==0 ){
        file_put_contents('/var/www/html/deploy/deploy.log', print_r($out2,true), FILE_APPEND);

    file_put_contents('/var/www/html/deploy/deploy.log', print_r("made it past exec",true), FILE_APPEND);
    //$test = shell_exec($cmd1);
    //$test2 = shell_exec($cmd2);

    file_put_contents('/var/www/html/deploy/deploy.log', print_r($cmd1,true), FILE_APPEND);
    file_put_contents('/var/www/html/deploy/deploy.log', print_r($cmd2,true), FILE_APPEND);

    // Log the deployment
    $commit_hash = exec('cd ' . $repo_dir . ' && ' .$git_bin_path  . ' rev-parse --short HEAD');
    file_put_contents('/var/www/html/deploy/deploy.log', date('m/d/Y h:i:s a') . " Deployed branch: " .  $branch . " Commit: " . $commit_hash . "\n", FILE_APPEND);


What am I possibly overlooking that would be causing PHP's exec() function to be executing with insufficient privileges when using exec() as opposed to executing directly from the php user's shell?


Turned out to be a SELinux Permission issue with the apache user under RedHat 7.2.

I created another user and added a bash script to execute the git fetch and checkout.

I also modified my sudoers file to allow apache to run the bash script as the newly created user.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.