xaizek / rocketgit (License: AGPLv3+) (since 2018-12-09)
Light and fast Git hosting solution suitable to serve both as a hub or as a personal code storage with its tickets, pull requests, API and much more.
<root> / inc / wh / build.inc.php (d9ab16ea09b6124de8df44594a70518b9f04638d) (9,252B) (mode 100644) [raw]
<?php
require_once($INC . "/util.inc.php");
require_once($INC . "/log.inc.php");
require_once($INC . "/sql.inc.php");
require_once($INC . "/prof.inc.php");
require_once($INC . "/events.inc.php");
require_once($INC . '/builder.inc.php');
require_once($INC . "/wh/core.inc.php");

$rg_wh_build_functions = array(
	'wh_build_send' => 'rg_wh_build_send',
	'wh_build_send_one' => 'rg_wh_build_send_one',
	'wh_build_job_done' => 'rg_wh_build_job_done'
);
rg_event_register_functions($rg_wh_build_functions);


/*
 * This is called when a job is done
 */
function rg_wh_build_job_done($db, $ev)
{
	rg_prof_start('wh_build_job_done');
	rg_log_ml('wh_build_job_done: ev: ' . print_r($ev, TRUE));

	$job = $ev['job'];
	$req = isset($job['request']) ? $job['request'] : $job;

	$ret = FALSE;
	while (1) {
		$s = $ev['status'];
		$out = 'Worker: ' . $job['worker_name'] . "\n";
		$out .= 'Date (UTC): '
			. gmdate('Y-m-d H:i', $job['done']) . "\n";
		$out .= 'Elapsed time: '
			. ($job['done'] - $job['worker_started'])
			. 's' . "\n\n";

		$out .= 'Packages installation:' . "\n";
		$out .= $s['packages'] . "\n\n";

		foreach ($s['cmds'] as $index => $i) {
			if (empty($req['cmds'][$index]['cmd']))
				continue;

			$out .= 'Command['
				. $req['cmds'][$index]['cmd'] . ']:' . "\n"
				. trim($i['log']) . "\n\n";
		}

		// TODO: should we check error code?
		rg_wh_set_last_output($db, $ev['ui']['uid'],
			$req['hook_id'], $out);
		$ret = array();
		break;
	}

	rg_prof_end('wh_build_job_done');
	return $ret;
}

/*
 * Helper for rg_wh_build_send
 */
function rg_wh_build_send_one($db, $ev)
{
	rg_prof_start('wh_build_send_one');
	rg_log_ml('wh_build_send_one: event: ' . print_r($ev, TRUE));

	$ret = FALSE;

	$last_output = '';
	while (1) {
		// replace ##tags##
		rg_wh_replace_tags($ev);

		$ri = rg_repo_info($db, $ev['ri']['repo_id'], 0, '');
		if ($ri['ok'] != 1) {
			$last_output .= $ri['errmsg'];
			break;
		}

		$a = $ev['wh']['idata'];
		$a['hook_id'] = $ev['wh']['id'];
		$a['uid'] = $ev['ui']['uid'];
		$a['flags'] = $ev['wh']['flags'];
		$a['url'] = $ri['clone_url_ssh'];
		$a['head'] = $ev['new_rev'];
		$a['refname'] = $ev['refname'];
		$a['refname_short'] = rg_repo_ref_nice($ev['refname']);
		$a['env'] = $ev['env'];
		$a['packages'] = $ev['packages'];
		// TODO: pass e-mail notification

		// Call the function
		$r = rg_builder_add($db, $ev['ri']['repo_id'], $a);
		if ($r['ok'] != 1) {
			$last_output .= $r['errmsg'];
			break;
		}

		$ret = array();
		break;
	}

	if (!empty($last_output))
		rg_wh_set_last_output($db, $ev['ui']['uid'], $ev['wh']['id'],
			substr($last_output, 0, 4096));

	rg_prof_end('wh_build_send_one');
	return $ret;
}

/*
 * Generic function which will be called when a webhook must be posted
 */
function rg_wh_build_send($db, $ev)
{
	rg_prof_start('wh_build_send');
	rg_log_ml('wh_build_send: event: ' . print_r($ev, TRUE));

	$ret = array();

	// First, get the list of hooks
	$r = rg_wh_list($db, $ev['ui']['uid']);
	if ($r['ok'] != 1)
		return FALSE;

	// Filter
	foreach ($r['list'] as $id => $wh) {
		if (strcmp($wh['htype'], 'build') != 0)
			continue;

		// Disabled?
		if (strchr($wh['flags'], 'D'))
			continue;

		// If the web hook does not contain our type, skip it
		if (!strchr($wh['idata']['events'], $ev['wh_event'])) {
			rg_log('DEBUG: ' . $ev['wh_event']
				. ' is not present in '
				. $wh['idata']['events']);
			continue;
		}

		if (isset($ev['ri']['name'])
			&& !rg_repo_compare_refs($wh['repo'], $ev['ri']['name'])) {
			rg_log('hook is not for this repo');
			continue;
		}

		if (isset($ev['refname'])
			&& !rg_repo_compare_refs($wh['refname'], $ev['refname'])) {
			rg_log('hook is not for this ref');
			continue;
		}

		$x = $ev;
		$x['category'] = 'wh_build_send_one';
		$x['packages'] = $wh['idata']['packages'];
		$envs = $wh['idata']['envs']; unset($wh['idata']['envs']);
		$x['wh'] = $wh;
		foreach ($envs as $env => $junk) {
			$y = $x;
			$y['env'] = $env;
			$ret[] = $y;
		}
	}

	rg_prof_end('wh_build_send');
	return $ret;
}

/*
 * Some cosmetics applied to a webhook
 */
function rg_wh_build_cosmetic_pre(&$row)
{
}

/*
 * Some cosmetics applied to a webhook
 */
function rg_wh_build_cosmetic_post(&$row)
{
	$f = rg_template('user/settings/wh/build/show_one.html',
		$rg, TRUE/*xss*/);
	$list = '';
	foreach ($row['idata']['cmds'] as $i => &$info) {
		if (empty($info['cmd']))
			continue;
		$list .= str_replace('##i##', $i, $f);

		$info['abort_nice'] =
			$info['abort'] == 1 ? "Yes" : "No";
	}
	unset($info);
	$row['idata']['HTML:wh_list'] =
		rg_template_string($list, 0 /*off*/, $row['idata'], TRUE /*xss*/);

	$f = rg_template('user/settings/wh/build/env_show_one.html',
		$rg, TRUE/*xss*/);
	$list = '';
	//rg_log_ml('DEBUG: envs: ' . print_r($row['idata']['envs'], TRUE));
	foreach ($row['idata']['envs'] as $env => $junk) {
		$list .= str_replace('##env##', $env, $f);
	}
	$row['idata']['HTML:envs_list'] =
		rg_template_string($list, 0 /*off*/, $row['idata'], TRUE /*xss*/);

	$row['idata']['HTML:private'] = rg_template(
		'user/settings/wh/build/show.html', $row['idata'], TRUE /*xss*/);
}

/*
 * Fill private data based on parameters passed
 */
function rg_wh_build_fill_vars(&$rg)
{
	$a = $rg['wh']['idata'];

	$a['cmds'] = array();
	for ($i = 1; $i <= 5; $i++) {
		$p = 'wh::idata::cmds::' . $i . '::';

		$cmd = trim(rg_var_str($p . 'cmd'));
		if (empty($cmd)) {
			$a['cmds'][$i] = array(
				'cmd' => '',
				'label_ok' => '',
				'label_nok' => '',
				'abort' => 0
			);
			continue;
		}

		$a['cmds'][$i] = array();
		$a['cmds'][$i]['cmd'] = $cmd;
		$a['cmds'][$i]['label_ok'] = trim(rg_var_str($p . 'label_ok'));
		$a['cmds'][$i]['label_nok'] = trim(rg_var_str($p . 'label_nok'));
		$x = trim(rg_var_str($p . 'abort'));
		$a['cmds'][$i]['abort'] = strcasecmp($x, 'on') == 0 ? 1 : 0;
	}

	$a['envs'] = array();
	$envs = rg_var_str('wh::idata::envs');
	//rg_log_ml('envs: ' . print_r($envs, TRUE));
	foreach ($envs as $env => $on)
		$a['envs'][$env] = strcasecmp($on, 'on') == 0 ? 1 : 0;

	$a['packages'] = trim(rg_var_str('wh::idata::packages'));
	$a['packages'] = str_replace(';', ' ', $a['packages']);
	$a['packages'] = str_replace(',', ' ', $a['packages']);
	$a['packages'] = str_replace('\r', ' ', $a['packages']);
	$a['packages'] = str_replace('\n', ' ', $a['packages']);
	$a['packages'] = str_replace('\t', ' ', $a['packages']);

	$rg['wh']['idata'] = $a;

	//rg_log_ml('DEBUG: after fill_vars: ' . print_r($a, TRUE));
}

/*
 * Validate parameters passed
 */
function rg_wh_build_validate_vars($rg, &$errmsg)
{
	global $rg_wh_build_itypes;

	$a = $rg['wh'];

	$ret = FALSE;
	while (1) {
		$all_empty = TRUE;

		//rg_log_ml('DEBUG: cmds:' . print_r($a['idata']['cmds'], TRUE));
		if (empty($a['idata']['cmds'])) {
			$errmsg[] = rg_template('user/settings/wh/build/inv_cmd.txt',
				$rg, TRUE /*xss*/);
			break;
		}

		//rg_log_ml('DEBUG: envs:' . print_r($a['idata']['envs'], TRUE));
		if (empty($a['idata']['envs'])) {
			$errmsg[] = rg_template('user/settings/wh/build/inv_env.txt',
				$rg, TRUE /*xss*/);
			break;
		}

		$ret = TRUE;
		break;
	}

	return $ret;
}

/*
 * Transfers to $rg the custom parameters - used when showing the form
 */
function rg_wh_build_add_form_pre($db, &$rg)
{
	$a = $rg['wh']['idata'];

	$t = array(
		'cmd' => '',
		'label_ok' => '',
		'label_nok' => '',
		'abort' => 0
	);

	for ($i = 1; $i <= 5; $i++) {
		if (!isset($a['cmds'][$i]))
			$a['cmds'][$i] = $t;
	}

	if (!isset($a['envs']))
		$a['envs'] = array();

	if (!isset($a['packages']))
		$a['packages'] = '';

	if (!isset($a['events']))
		$a['events'] = 'P';

	$rg['wh']['idata'] = $a;
}

/*
 * Transfers to $rg the custom parameters - used when showing the form
 */
function rg_wh_build_add_form_post($db, &$rg)
{
	$f = rg_template_blind('user/settings/wh/build/form_cmd.html');
	$cmds = '';
	for ($i = 1; $i <= 5; $i++)
		$cmds .= str_replace('##i##', $i, $f);
	//rg_log_ml('DEBUG: cmds=' . $cmds);
	$rg['HTML:cmds'] =
		rg_template_string($cmds, 0 /*off*/, $rg, TRUE /*xss*/);

	$envs = rg_worker_environments($db, $rg['login_ui']['uid']);
	$f = rg_template_blind('user/settings/wh/build/form_env.html');
	$_s = '';
	foreach ($envs as $env)
		$_s .= str_replace('##env##', $env, $f);
	rg_log_ml('envs: ' . $_s);
	$rg['HTML:envs'] =
		rg_template_string($_s, 0 /*off*/, $rg, TRUE /*xss*/);

	//rg_log_ml('DEBUG:rg: ' . print_r($rg, TRUE));

	$rg['HTML:custom_form'] = rg_template('user/settings/wh/build/form.html',
		$rg, TRUE /*xss*/);
}

/*
 * Add custom hints
 */
function rg_wh_build_fill_hints($rg, &$hints)
{
	$hints[]['HTML:hint'] = rg_template('user/settings/wh/build/hints.html',
		$rg, TRUE /*xss*/);
}



$info = array(
	'htype' => 'build',
	'description' => 'Clones, builds and tests (Continuous Integration)',
	'cb' => array(
		'cosmetic_pre' => 'rg_wh_build_cosmetic_pre',
		'cosmetic_post' => 'rg_wh_build_cosmetic_post',
		'fill_vars' => 'rg_wh_build_fill_vars',
		'validate_vars' => 'rg_wh_build_validate_vars',
		'add_form_pre' => 'rg_wh_build_add_form_pre',
		'add_form_post' => 'rg_wh_build_add_form_post',
		'fill_hints' => 'rg_wh_build_fill_hints'
	),
	'have_events' => TRUE,
	'flags' => array()
);
rg_wh_register_type('build', $info);

$cb = array();
rg_wh_register_subtype('build', 'generic', 'Generic', $cb);

Hints

Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://code.reversed.top/user/xaizek/rocketgit

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@code.reversed.top/user/xaizek/rocketgit

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a pull request:
... clone the repository ...
... make some changes and some commits ...
git push origin master