paheko-fork/src/include/lib/KD2/Fossil.php
2024-01-19 16:39:49 +01:00

137 lines
No EOL
3.6 KiB
PHP

<?php
namespace KD2;
class Fossil
{
/**
* Length for SHA1
*/
const HNAME_MIN = 40;
protected $db;
protected $url;
public function __construct($repo_file, $url)
{
$this->db = new DB_SQLite3($repo_file, SQLITE3_OPEN_READONLY);
$this->db->createFunction('repo_url', [$this, 'getURL']);
$this->url = $url;
}
public function getURL($uri = '')
{
return $this->url . $uri;
}
public function listUnversioned()
{
return $this->db->getGrouped('SELECT uvid, name, sz AS size, mtime, hash, repo_url(\'uv/\' || name) AS url
FROM unversioned WHERE hash IS NOT NULL ORDER BY mtime DESC;');
}
public function getTicketsStatus()
{
return $this->db->get('SELECT DISTINCT status FROM ticket;');
}
public function getTicketsTypes()
{
return $this->db->get('SELECT DISTINCT type FROM ticket;');
}
public function listTickets(array $filters = [])
{
$query = 'SELECT *, repo_url(\'tktview?name=\' || substr(tkt_uuid, 1, 10)) AS url FROM ticket WHERE 1';
$args = [];
foreach ($filters as $field => $value)
{
$query .= ' AND ' . $field . ' = ?';
$args[] = $value;
}
$query .= ' ORDER BY tkt_mtime DESC;';
return $this->db->get($query, $args);
}
public function bundlePreview($file, $force = false)
{
if (!is_readable($file))
{
throw new \RuntimeException('Cannot read file: ' . $file);
}
try {
$this->db->preparedQuery('ATTACH ? AS b1;', [$file]);
$a = $this->db->prepare('SELECT bcname, bcvalue FROM b1.bconfig LIMIT 1;');
$b = $this->db->prepare('SELECT blobid, uuid, sz, delta, notes, data FROM b1.bblob;');
if (!$a || !$b)
{
return false;
}
} catch (\Exception $e) {
$this->db->exec('DETACH b1;');
throw new \RuntimeException('Not a valid bundle file: ' . $e->getMessage(), 0, $e);
}
/* Only import a bundle that was generated from a repo with the same
** project code, unless the --force flag is true */
if (!$force)
{
$exists = $this->db->firstColumn('SELECT 1 FROM config, bconfig
WHERE config.name = \'project-code\'
AND bconfig.bcname = \'project-code\'
AND config.value = bconfig.bcvalue;');
if (!$exists)
{
$this->db->exec('DETACH b1;');
throw new \RuntimeException('project-code in the bundle does not match the repository project code.');
}
}
/* If the bundle contains deltas with a basis that is external to the
** bundle and those external basis files are missing from the local
** repo, then the delta encodings cannot be decoded and the bundle cannot
** be extracted. */
$missing_deltas = $this->db->firstColumn('SELECT group_concat(substr(delta,1,10), \' \') FROM bblob WHERE typeof(delta) = \'text\' AND length(delta) >= ? AND NOT EXISTS(SELECT 1 FROM blob WHERE uuid = bblob.delta)', self::HNAME_MIN);
if ($missing_deltas)
{
$this->db->exec('DETACH b1;');
throw new \RuntimeException('delta basis artifacts not found in repository: ' . $missing_deltas);
}
$this->db->begin();
$this->db->exec('CREATE TEMP TABLE bix(
blobid INTEGER PRIMARY KEY,
delta INTEGER
);
CREATE INDEX bixdelta ON bix(delta);
INSERT INTO bix(blobid,delta)
SELECT blobid,
CASE WHEN typeof(delta)==\'integer\'
THEN delta ELSE 0 END
FROM bblob
WHERE NOT EXISTS(SELECT 1 FROM blob WHERE uuid=bblob.uuid AND size>=0);
CREATE TEMP TABLE got(rid INTEGER PRIMARY KEY ON CONFLICT IGNORE);');
/*
manifest_crosslink_begin();
bundle_import_elements(0, 0, isPriv);
manifest_crosslink_end(0);
describe_artifacts_to_stdout("IN got", "Imported content:");
*/
$this->db->commit();
// gzuncompress on each delta
$this->db->exec('DETACH b1;');
}
}