Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A couple fixes and addons #6

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Git webhook deploy agent
Git webhook deploy agent with sudo support
==========

For sysadmins simply setup a post hook agent on server to deploy git projects like PaaS from your using third-party git service.
Expand Down Expand Up @@ -36,13 +36,13 @@ Additional things about git you should make sure:

Set a git post hook in the admin panel of your repository like this:

[POST]:http://user:[email protected]:6060/project/id@remote/branch
[POST]:http://user:[email protected]:6060/project/id@remote/branch/extraArg

* `user:password` is reqired part in post URL. The agent will check the request with HTTP basic authentication to avoid mistake request.

* `6060` as port is set in the config, you can change it as you wish.

* `/project/:id` is the router, `@remote/branch` is optional default to `origin/master`. If branch (or with remote) is set in hook URL, the part after `@` should be exactly match the config (see below).
* `/project/:id` is the router, `@remote/branch` is optional default to `origin/master`. If branch (or with remote) is set in hook URL, the part after `@` should be exactly match the config (see below). `extraArg` can be passed to router

### Configuration ###

Expand All @@ -63,12 +63,14 @@ Here is a sample of configuration structure:
"projects": {
"sample": {
// branch
"master": {
"origin/master/extraArg": {
// Project path
"path": "/var/www/sample",

// Task to be run after git pull, such as build etc.
// "shell": "./build.sh",
// run scirpt with sudo -H -u $user -g $group
// "sudo": true

// Users in list allow to trigger deploy
"users": ["abc"]
Expand All @@ -84,7 +86,10 @@ Here is a sample of configuration structure:
// Each user ID should match server user name.
"users": {
"abc": "ba1f2511fc30423bdbb183fe33f3dd0f"
}
},
//Group name for deploy tasks
"group": ["www-data"]

}
```

Expand Down
2 changes: 1 addition & 1 deletion bin/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ case $1 in
start )
if [[ $CONFIGURED = 1 ]]; then
echo "Starting deploy agent server..."
pm2 start $SERVER_SCRIPT --name $APP_NAME
pm2 start $SERVER_SCRIPT --name $APP_NAME --log $LOG_PATH/agent.log
else
check_config
fi
Expand Down
50 changes: 33 additions & 17 deletions bin/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,29 @@ PROJECT=$1

remote=$2
branch=$3
task=$4
path=$4
task=$5
sudo=$6
user=$7
group=$8

if [[ $sudo == true ]] ; then

echo "Sudo On: sudo -H -u$user -g$group ${BASH_SOURCE[0]} $PROJECT $remote $branch $path $task false $user $group"
sudo -H -u$user -g$group ${BASH_SOURCE[0]} $PROJECT $remote $branch $path $task false $user $group

else

date
echo "start deploying $remote/$branch"

TEMP_FLAG=$LOG_PATH/.$PROJECT.$remote-$branch

if [[ ! -f $TEMP_FLAG ]]; then
touch $TEMP_FLAG
touch $TEMP_FLAG
else
echo "still deploying..."
exit 0
echo "still deploying..."
exit 0
fi


Expand All @@ -30,27 +42,31 @@ git fetch $remote

for br in $(git branch | sed 's/^[\* ]*//')
do
if [[ $br == $branch ]]; then
echo "found branch: $branch"
found=1
fi
if [[ $br == $branch ]]; then
echo "found branch: $branch"
found=1
fi
done

if [[ $found == 1 ]]; then
echo "git checkout -q $branch"
git checkout -q $branch
echo "git merge $remote/$branch"
git merge $remote/$branch
echo "git checkout -q $branch"
git checkout -q $branch
echo "git merge $remote/$branch"
git merge $remote/$branch
else
echo "git checkout $remote/$branch -b $branch"
git checkout $remote/$branch -b $branch
echo "git checkout $remote/$branch -b $branch"
git checkout $remote/$branch -b $branch
fi

git submodule update --init --recursive
#git submodule update --init --recursive

if [[ $task != '' ]]; then
echo "start to run shell script: $task"
sh $task
echo "start to run shell script: sh $task $path $branch"
sh $task $path $branch
fi

rm $TEMP_FLAG

fi

date
6 changes: 4 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
"sample": {
"origin/master": {
"path": "/var/www/sample",
"users": ["abc"]
"users": ["abc"],
"sudo": true
}
}
},

"users": {
"abc": "ba1f2511fc30423bdbb183fe33f3dd0f"
}
},
"group": ["www-data"]
}
160 changes: 85 additions & 75 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,92 +8,102 @@ var basicAuth = require('basic-auth');


function hook(req, res, next) {
console.log('Deployment request received: ' + JSON.stringify(req.params));

var id = req.params[0];
if (!id) {
console.log('[400] Bad request. Without project id.');
return res.status(400).end();
}

// find project in config
var project = config.projects[id];
if (!project) {
console.log('[404] Not found. No project named as "' + id + '" found.');
return res.status(404).end();
}

// find branch options in config
var branch = req.params[1] || config.defaultBranch || 'master';
var options = project[branch];
if (!options) {
console.log('[404] No options of branch "' + branch + '" found. Please check config.');
return res.status(404).end();
}

var branchParam = branch.split('/');
var remote = branchParam[0];
if (branchParam.length == 1) {
remote = config.defaultRemote || 'origin';
} else {
branch = branchParam[1];
}

// check auth
var auth = basicAuth(req);
if (!auth ||
!auth.pass ||
options.users.indexOf(auth.name) < 0 ||
config.users[auth.name] != auth.pass) {
console.log('[403] Forbidden.');
return res.status(403).end();
}

console.log('Authentication passed.');

if (!fs.existsSync(options.path)) {
console.log('[404] No path found for project: "' + id + '"');
return res.status(404).end();
}

// need nodejs >= 0.12
var uid = parseInt(child_process.execSync('id -u ' + auth.name).toString().trim(), 10);
var home = child_process.execSync('echo ~' + auth.name).toString().trim();

if (!uid || !home) {
return res.status(400).send('not found');
}

res.status(200).send('ok');

child_process.execFile(path.join(__dirname, 'bin/deploy.sh'), [id, remote, branch, options.shell || ''], {
cwd: options.path,
uid: uid,
env: process.env
}, function (error, stdout, stderr) {
console.log(stdout);
if (error) {
console.log(error);
} else {
console.log('Deployment done.');
}
});

console.log('[200] Deployment started.');
console.log('Deployment request received: ' + JSON.stringify(req.params));

var id = req.params[0];
if (!id) {
console.log('[400] Bad request. Without project id.');
return res.status(400).end();
}

// find project in config
var project = config.projects[id];
if (!project) {
console.log('[404] Not found. No project named as "' + id + '" found.');
return res.status(404).end();
}

// find branch options in config
var branch = req.params[1] || config.defaultBranch || 'master';
var options = project[branch];
if (!options) {
console.log('[404] No options of branch "' + branch + '" found. Please check config.');
return res.status(404).end();
}

var branchParam = branch.split('/');
var remote = branchParam[0];
if (branchParam.length == 1) {
remote = config.defaultRemote || 'origin';
} else {
branch = branchParam[1];
}

// check auth
var auth = basicAuth(req);
if (!auth ||
!auth.pass ||
options.users.indexOf(auth.name) < 0 ||
config.users[auth.name] != auth.pass) {
console.log('[403] Forbidden.');
return res.status(403).end();
}

console.log('Authentication passed.');

if (!fs.existsSync(options.path)) {
console.log('[404] No path found for project: "' + id + '"');
return res.status(404).end();
}

if (!config.group){
config.group=auth.name;
}
// need nodejs >= 0.12
var uid = parseInt(child_process.execSync('id -u ' + auth.name).toString().trim(), 10);
var home = child_process.execSync('echo ~' + auth.name).toString().trim();
var gid = parseInt(child_process.execSync('id -g ' + config.group).toString().trim(), 10);

if (!uid || !home) {
return res.status(400).send('not found');
}

res.status(200).send('ok');

if ( options.sudo == true) {
uid = 0;
gid = 0;
}

child_process.execFile(path.join(__dirname, 'bin/deploy.sh'), [id, remote, branch, options.path, options.shell || '', options.sudo, auth.name, config.group ], {
cwd: options.path,
uid: uid,
gid: gid,
env: process.env
}, function (error, stdout, stderr) {
console.log(stdout);
if (error) {
console.log(error);
} else {
console.log('Deployment done.');
}
});

console.log('[200] Deployment started.');
}

var config = require('/etc/hookagent.json');

var agent = express();

agent.get('/', function (req, res, next) {
// indicate process is running
res.status(200).send('ok');
// indicate process is running
res.status(200).send('ok');
});

// [POST]:/project/project-name[@[remote/]branch-name]
agent.post(/\/project\/([\w\.\-]+)(?:@([\w\/\.\-]+))?/i, hook);

agent.listen(config.port, function() {
console.log("Hook agent started at %s. Listening on %d", new Date(), config.port);
console.log("Hook agent started at %s. Listening on %d", new Date(), config.port);
});