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 / fixes.inc.php (1b70d8bfdb477542dc7006f326148d2d3223f4ea) (6,710B) (mode 100644) [raw]
<?php
//
// This is a set of fixes that must be applied when the software is upgraded.
// If the structure of authorized_keys may change, we must add a fix to mark
// the stat dirty.
//
include_once($INC . "/sql.inc.php");
include_once($INC . "/state.inc.php");
include_once($INC . "/util.inc.php");
include_once($INC . "/user.inc.php");
include_once($INC . "/repo.inc.php");

$rg_fixes = array();
$rg_fixes[1] = array("rg_fixes_user_index_by_id");
$rg_fixes[2] = array("rg_fixes_repo_index_by_id");

// This must be the last line
$rg_fixes_ver = count($rg_fixes);

/*
 * Fix one repo (make the names as relative links to ids)
 */
function rg_fixes_repo_index_by_id_one($uid, $repo_id, $repo_name)
{
	rg_log("Fix repo path: uid=$uid repo_id=$repo_id repo_name=$repo_name");

	$ret = FALSE;
	do {
		// we expect to have a folder .../repos/by_id
		$by_id = rg_repo_path_by_id($uid, $repo_id);
		$p = dirname($by_id);
		if (!is_dir($p) && (mkdir($p, 0755, TRUE) === FALSE)) {
			rg_log("Cannot create [$p] folder!");
			break;
		}

		// we expect to have a folder .../repos/by_name
		$by_name = rg_repo_path_by_name($uid, $repo_name);
		$p = dirname($by_name);
		if (!is_dir($p) && (mkdir($p, 0755, TRUE) === FALSE)) {
			rg_log("Cannot create [$p] folder!");
			break;
		}

		// We already moved it?
		$new_path = rg_repo_path_by_id($uid, $repo_id);
		if (!is_dir($new_path)) {
			$old_path = rg_user_path_by_id($uid) . "/repos/" . $repo_name . ".git";
			$r = rename($old_path, $new_path);
			if ($r !== TRUE) {
				rg_log("Cannot rename $old_path -> $new_path!");
				break;
			}
		}

		// Now, make links from by_name
		if (!is_link($by_name)) {
			$by_id_rel = rg_repo_path_by_id_rel($uid, $repo_id);
			$r = symlink($by_id_rel, $by_name);
			if ($r !== TRUE) {
				rg_log("Cannot symlink $by_id_rel <- $by_name ($php_errormsg)!");
				break;
			}
		}

		$ret = TRUE;
	} while (0);

	return $ret;
}

/*
 * Reindex repos by id so we can rename repos easier.
 * And is more normal to index them by the unique id.
 */
function rg_fixes_repo_index_by_id($db)
{
	rg_prof_start("fixes_repo_index_by_id");

	$ret = FALSE;
	do {
		$sql = "SELECT uid, repo_id, name FROM repos"
			. " WHERE git_dir_done > 0";
		$res = rg_sql_query($db, $sql);
		if ($res === FALSE)
			break;

		$all_repos_moved = TRUE;
		while (($row = rg_sql_fetch_array($res))) {
			$r = rg_fixes_repo_index_by_id_one($row['uid'],
				$row['repo_id'], $row['name']);
			if ($r !== TRUE) {
				$all_repos_moved = FALSE;
				break;
			}
		}
		rg_sql_free_result($res);

		if ($all_repos_moved !== TRUE)
			break;

		$ret = TRUE;
	} while (0);

	rg_prof_end("fixes_repo_index_by_id");
	return $ret;
}

/*
 * Index user by id and make links for names
 */
function rg_fixes_user_index_by_id_one($uid, $username)
{
	global $rg_repos;

	rg_log("Fix user path: uid=$uid username=$username");

	$ret = FALSE;
	do {
		$user_path_uid = rg_user_path_by_id($uid);
		$user_path_name = rg_user_path_by_name($username);
		rg_log("\tuser_path=[$user_path_uid][$user_path_name]");

		// parend dir exits? if not, create it
		$p = dirname($user_path_uid);
		if (!is_dir($p) && (mkdir($p, 0755, TRUE) === FALSE)) {
			rg_log("cannot create parent dir [$p]");
			break;
		}

		// parend dir exits? if not, create it
		$p = dirname($user_path_name);
		if (!is_dir($p) && (mkdir($p, 0755, TRUE) === FALSE)) {
			rg_log("cannot create parent dir [$p]");
			break;
		}

		// We already moved it?
		if (!is_dir($user_path_uid)) {
			$x = $username . "_";
			$old_path = $rg_repos . "/users/" . $x[0] . "/" . $x[1]
				. "/" . $username;
			if (!is_dir($old_path)) {
				$ret = TRUE;
				break;
			}

			$r = rename($old_path, $user_path_uid);
			if ($r !== TRUE) {
				rg_log("Cannot rename $old_path -> $user_path_uid!");
				break;
			}
		}

		// Now, make links from name to id
		if (!is_link($user_path_name)) {
			$by_id_rel = rg_user_path_by_id_rel($uid);
			$r = symlink($by_id_rel, $user_path_name);
			if ($r !== TRUE) {
				rg_log("Cannot symlink $by_id_rel <- $user_path_name ($php_errormsg)!");
				break;
			}
		}

		$ret = TRUE;
	} while (0);

	return $ret;
}

/*
 * Reindex users by id so we can rename users easier.
 * And is more normal to index them by the unique id.
 */
function rg_fixes_user_index_by_id($db)
{
	global $rg_repos;

	rg_prof_start("fixes_user_index_by_id");

	$ret = FALSE;
	do {
		$sql = "SELECT uid, username FROM users";
		$res = rg_sql_query($db, $sql);
		if ($res === FALSE)
			break;

		$all_users_moved = TRUE;
		while (($row = rg_sql_fetch_array($res))) {
			$r = rg_fixes_user_index_by_id_one($row['uid'],
				$row['username']);
			if ($r !== TRUE) {
				$all_users_moved = FALSE;
				break;
			}
		}
		rg_sql_free_result($res);

		if ($all_users_moved !== TRUE)
			break;

		// Remove old structure dirs
		$r = rg_rmdir($rg_repos . "/users");
		if ($r === FALSE) {
			rg_log("Cannot remove old structure dirs"
				. " (" . rg_util_error() . ").");
			break;
		}

		$ret = TRUE;
	} while (0);

	rg_prof_end("fixes_user_index_by_id");
	return $ret;
}


/*
 * Apply fixes
 */
function rg_fixes_run($db, $old_ver)
{
	global $rg_fixes;
	global $rg_fixes_ver;

	rg_log("rg_fixes_run: old_ver=$old_ver...");

	for ($i = $old_ver + 1; $i <= $rg_fixes_ver; $i++) {
		foreach ($rg_fixes[$i] as $function) {
			rg_log("Calling function $function...");
			$r = $function($db);
			if ($r !== TRUE) {
				rg_log("Function $function returned error!");
				return FALSE;
			}
		}
	}

	return TRUE;
}

/*
 * Tests if fixes are needed. Return old version
 */
function rg_fixes_needed($db)
{
	global $rg_fixes_ver;

	rg_log("fixes_needed:");

	$old = rg_state_get($db, "fixes_version");
	if ($old === FALSE) {
		//TODO: error rg_log("\tDEBUG: schema is up to date!");
		return FALSE;
	}

	if (empty($old))
		$old = 0;

	rg_log("\tDEBUG: old=$old new=$rg_fixes_ver");
	if ($old == $rg_fixes_ver) {
		rg_log("\tDEBUG: fixes are up to date!");
		return FALSE;
	}

	return $old;
}

/*
 * Apply fixes if needed
 * Returns FALSE in case of error
 * This must not be run by web user because of the owner of the locking file.
 */
function rg_fixes_update($db)
{
	global $rg_fixes_ver;

	rg_log("fixes_update:");

	$old = rg_fixes_needed($db);
	if ($old === FALSE)
		return TRUE;

	// If we cannot lock, return error
	if (rg_lock("fixes.lock") === FALSE)
		return FALSE;

	$ret = FALSE;
	$rollback = 0;
	do {
		$r = rg_fixes_run($db, $old);
		if ($r !== TRUE) {
			rg_internal_error("Cannot apply fixes.");
			break;
		}

		$r = rg_state_set($db, "fixes_version", $rg_fixes_ver);
		if ($r !== TRUE) {
			rg_log("Cannot set state ver (" . rg_state_error() . ")");
			break;
		}

		$ret = TRUE;
	} while (0);

	rg_unlock("fixes.lock");

	return $ret;
}

?>
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