2020-07-01 03:51:00 +02:00
|
|
|
/* This file is part of FediBlock-backend.
|
|
|
|
* Copyright © 2020 tastytea <tastytea@tastytea.de>
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
|
|
* the Free Software Foundation, version 3.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Affero General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "git.hpp"
|
|
|
|
#include "files.hpp"
|
2020-07-01 08:48:36 +02:00
|
|
|
#include "fs-compat.hpp"
|
|
|
|
#include "json.hpp"
|
2020-07-01 03:51:00 +02:00
|
|
|
|
|
|
|
#include <cstdint>
|
2020-07-01 08:48:36 +02:00
|
|
|
#include <fstream>
|
2020-07-01 08:48:26 +02:00
|
|
|
#include <git2.h>
|
2020-07-01 03:51:00 +02:00
|
|
|
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
|
|
|
|
2020-07-01 09:22:36 +02:00
|
|
|
#if LIBGIT2_VER_MAJOR == 0
|
|
|
|
# if LIBGIT2_VER_MINOR < 28
|
|
|
|
# define git_error_last giterr_last
|
|
|
|
# endif
|
2020-07-01 11:25:43 +02:00
|
|
|
# if LIBGIT2_VER_MINOR < 99
|
|
|
|
# define git_credential_ssh_key_new git_cred_ssh_key_new
|
|
|
|
# define git_push_options_init git_push_init_options
|
|
|
|
# endif
|
2020-07-01 09:22:36 +02:00
|
|
|
#endif
|
|
|
|
|
2020-07-01 08:50:16 +02:00
|
|
|
namespace FediBlock::git
|
2020-07-01 03:51:00 +02:00
|
|
|
{
|
|
|
|
|
2020-07-01 08:48:36 +02:00
|
|
|
using std::ofstream;
|
2020-07-01 03:51:00 +02:00
|
|
|
using std::runtime_error;
|
|
|
|
using std::stoull;
|
|
|
|
using std::string;
|
|
|
|
using std::string_view;
|
2020-07-01 08:48:36 +02:00
|
|
|
using std::to_string;
|
2020-07-01 03:51:00 +02:00
|
|
|
using std::uint64_t;
|
|
|
|
|
2020-07-01 08:48:26 +02:00
|
|
|
git_repository *_repo{nullptr};
|
2020-07-01 03:51:00 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
void check(int error)
|
2020-07-01 03:51:00 +02:00
|
|
|
{
|
2020-07-01 04:38:08 +02:00
|
|
|
if (error != 0)
|
2020-07-01 03:51:00 +02:00
|
|
|
{
|
|
|
|
const git_error *e = git_error_last();
|
|
|
|
throw runtime_error{e->message};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-01 10:18:53 +02:00
|
|
|
int cred_acquire(git_cred **cred, const char * /*url*/,
|
|
|
|
const char *username_from_url, unsigned int /*allowed_types*/,
|
|
|
|
void * /*payload*/)
|
|
|
|
{
|
2020-07-01 11:04:23 +02:00
|
|
|
return git_credential_ssh_key_new(
|
2020-07-01 20:51:35 +02:00
|
|
|
cred, username_from_url, (files::get_datadir() / "ssh_id.pub").c_str(),
|
|
|
|
(files::get_datadir() / "ssh_id").c_str(), nullptr);
|
2020-07-01 10:18:53 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
void clone()
|
2020-07-01 04:38:08 +02:00
|
|
|
{
|
2020-07-01 10:18:53 +02:00
|
|
|
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
|
|
|
options.fetch_opts.callbacks.credentials = cred_acquire;
|
|
|
|
check(git_clone(&_repo, "git@schlomp.space:FediBlock/data.git",
|
2020-07-01 20:51:35 +02:00
|
|
|
(files::get_tmpdir() / "repo").c_str(), &options));
|
2020-07-01 04:38:08 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 03:51:00 +02:00
|
|
|
uint64_t get_last_id()
|
|
|
|
{
|
2020-07-01 10:17:32 +02:00
|
|
|
constexpr string_view branch_prefix{"web-"};
|
2020-07-01 03:51:00 +02:00
|
|
|
uint64_t id{0};
|
|
|
|
|
|
|
|
git_branch_iterator *it;
|
|
|
|
if (git_branch_iterator_new(&it, _repo, GIT_BRANCH_ALL) == 0)
|
|
|
|
{
|
|
|
|
git_reference *ref;
|
|
|
|
git_branch_t type;
|
|
|
|
while (git_branch_next(&ref, &type, it) == 0)
|
|
|
|
{
|
|
|
|
const char *out{nullptr};
|
|
|
|
git_branch_name(&out, ref);
|
|
|
|
if (type == GIT_BRANCH_REMOTE)
|
|
|
|
{
|
|
|
|
const string branch_name{out};
|
|
|
|
size_t pos{branch_name.find(branch_prefix)};
|
|
|
|
if (pos != string::npos)
|
|
|
|
{
|
|
|
|
pos += branch_prefix.size();
|
|
|
|
uint64_t newid{stoull(branch_name.substr(pos))};
|
|
|
|
if (newid > id)
|
|
|
|
{
|
|
|
|
id = newid;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
git_reference_free(ref);
|
|
|
|
}
|
|
|
|
git_branch_iterator_free(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
return id;
|
|
|
|
}
|
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
void create_branch()
|
2020-07-01 04:38:08 +02:00
|
|
|
{
|
2020-07-01 08:48:26 +02:00
|
|
|
const auto id{get_last_id() + 1};
|
|
|
|
const string branch_name = "web-" + std::to_string(id);
|
2020-07-01 10:19:18 +02:00
|
|
|
const string ref_name{"refs/heads/" + branch_name};
|
2020-07-01 04:38:08 +02:00
|
|
|
git_oid oid_parent;
|
|
|
|
git_commit *commit;
|
|
|
|
|
|
|
|
// Get SHA1 of HEAD.
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_reference_name_to_id(&oid_parent, _repo, "HEAD"));
|
2020-07-01 04:38:08 +02:00
|
|
|
|
|
|
|
// Translate SHA-1 to git_commit.
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_commit_lookup(&commit, _repo, &oid_parent));
|
2020-07-01 04:38:08 +02:00
|
|
|
|
|
|
|
git_reference *branch;
|
|
|
|
// Create new branch.
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_branch_create(&branch, _repo, branch_name.c_str(), commit, 0));
|
2020-07-01 10:19:18 +02:00
|
|
|
|
|
|
|
check(git_repository_set_head(_repo, ref_name.c_str()));
|
2020-07-01 04:38:08 +02:00
|
|
|
}
|
|
|
|
|
2020-07-01 20:51:35 +02:00
|
|
|
void commit(const cgi::entry_type &entry)
|
2020-07-01 08:48:36 +02:00
|
|
|
{
|
|
|
|
// Write files.
|
|
|
|
const auto id{get_last_id() + 1};
|
2020-07-01 20:51:35 +02:00
|
|
|
const string basename{files::get_tmpdir() / "repo"
|
|
|
|
/ ("web-" + to_string(id))};
|
2020-07-01 08:48:36 +02:00
|
|
|
|
|
|
|
ofstream file(basename + ".json");
|
|
|
|
if (!file.good())
|
|
|
|
{
|
|
|
|
throw; // FIXME
|
|
|
|
}
|
2020-07-01 20:51:35 +02:00
|
|
|
file << json::to_json(entry);
|
2020-07-01 08:48:36 +02:00
|
|
|
file.close();
|
|
|
|
|
|
|
|
if (!entry.screenshot_filepath.empty())
|
|
|
|
{
|
2020-07-01 11:59:02 +02:00
|
|
|
const string extension{fs::path(entry.screenshot_filepath).extension()};
|
|
|
|
fs::copy(entry.screenshot_filepath, basename + extension);
|
2020-07-01 08:48:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add files.
|
|
|
|
git_index *index{nullptr};
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_repository_index(&index, _repo));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_index_add_all(index, nullptr, 0, nullptr, nullptr));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_index_write(index));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
|
|
|
// Create commit.
|
|
|
|
git_signature *sig;
|
|
|
|
git_oid oid_commit;
|
|
|
|
git_oid oid_tree;
|
|
|
|
git_oid oid_parent;
|
|
|
|
git_tree *tree;
|
|
|
|
git_object *parent{nullptr};
|
|
|
|
git_reference *ref{nullptr};
|
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_signature_now(&sig, "Web", "Don't @ me"));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_revparse_ext(&parent, &ref, _repo, "HEAD"));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_repository_index(&index, _repo));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_index_write_tree(&oid_tree, index));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_index_write(index));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_tree_lookup(&tree, _repo, &oid_tree));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_reference_name_to_id(&oid_parent, _repo, "HEAD"));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg)
|
2020-07-01 08:55:13 +02:00
|
|
|
check(git_commit_create_v(&oid_commit, _repo, "HEAD", sig, sig, nullptr,
|
|
|
|
("New entry: " + entry.instance).c_str(), tree,
|
|
|
|
parent != nullptr ? 1 : 0, parent));
|
2020-07-01 08:48:36 +02:00
|
|
|
|
|
|
|
git_index_free(index);
|
|
|
|
git_signature_free(sig);
|
|
|
|
git_tree_free(tree);
|
|
|
|
}
|
|
|
|
|
2020-07-01 10:24:38 +02:00
|
|
|
void push()
|
|
|
|
{
|
|
|
|
git_push_options options;
|
|
|
|
git_remote *remote{nullptr};
|
|
|
|
const string branch_name{"web-" + std::to_string(get_last_id() + 1)};
|
|
|
|
string refspec_str{("refs/heads/" + branch_name)};
|
|
|
|
char *refspec = &refspec_str[0];
|
|
|
|
const git_strarray refspecs = {&refspec, 1};
|
|
|
|
|
|
|
|
check(git_remote_lookup(&remote, _repo, "origin"));
|
|
|
|
check(git_push_options_init(&options, GIT_PUSH_OPTIONS_VERSION));
|
2020-07-01 10:48:12 +02:00
|
|
|
options.callbacks.credentials = cred_acquire;
|
2020-07-01 10:24:38 +02:00
|
|
|
|
|
|
|
check(git_remote_push(remote, &refspecs, &options));
|
|
|
|
}
|
|
|
|
|
2020-07-01 08:50:16 +02:00
|
|
|
} // namespace FediBlock::git
|