/* This file is part of FediBlock-backend. * Copyright © 2020 tastytea * * 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 . */ #include "git.hpp" #include "files.hpp" #include "fs-compat.hpp" #include "gitea.hpp" #include "json.hpp" #include #include #include #include #include #include #if LIBGIT2_VER_MAJOR == 0 # if LIBGIT2_VER_MINOR < 28 # define git_error_last giterr_last # endif # 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 #endif namespace FediBlock::git { using std::ofstream; using std::runtime_error; using std::string; using std::string_view; using std::to_string; using std::uint64_t; git_repository *_repo{nullptr}; constexpr string_view _clone_url{"git@schlomp.space:FediBlock/data.git"}; void check(int error) { if (error != 0) { const git_error *e = git_error_last(); throw runtime_error{e->message}; } } int cred_acquire(git_cred **cred, const char * /*url*/, const char *username_from_url, unsigned int /*allowed_types*/, void * /*payload*/) { const auto datadir{files::get_datadir()}; return git_credential_ssh_key_new(cred, username_from_url, (datadir / "ssh_id.pub").c_str(), (datadir / "ssh_id").c_str(), nullptr); } void clone() { git_clone_options options = GIT_CLONE_OPTIONS_INIT; options.fetch_opts.callbacks.credentials = cred_acquire; check(git_clone(&_repo, _clone_url.data(), (files::get_tmpdir() / "repo").c_str(), &options)); } void create_branch() { const string branch_name{get_branch_name()}; const string ref_name{"refs/heads/" + branch_name}; git_oid oid_parent; git_commit *commit{nullptr}; // Get SHA1 of HEAD. check(git_reference_name_to_id(&oid_parent, _repo, "HEAD")); // Translate SHA-1 to git_commit. check(git_commit_lookup(&commit, _repo, &oid_parent)); git_reference *branch{nullptr}; // Create new branch. check(git_branch_create(&branch, _repo, branch_name.c_str(), commit, 0)); check(git_repository_set_head(_repo, ref_name.c_str())); } void commit(const entry_type &entry) { // Write files. const string basename{files::get_tmpdir() / "repo" / get_branch_name()}; ofstream file(basename + ".json"); if (!file.good()) { throw runtime_error{"Could not create file: " + basename + ".json"}; } file << json::to_json(entry); file.close(); if (!entry.screenshot_filepath.empty()) { const string extension{fs::path(entry.screenshot_filepath).extension()}; fs::copy(entry.screenshot_filepath, basename + extension); } // Add files. git_index *index{nullptr}; check(git_repository_index(&index, _repo)); check(git_index_add_all(index, nullptr, 0, nullptr, nullptr)); check(git_index_write(index)); // Create commit. git_signature *sig{nullptr}; git_oid oid_commit; git_oid oid_tree; git_oid oid_parent; git_tree *tree{nullptr}; git_object *parent{nullptr}; git_reference *ref{nullptr}; check(git_signature_now(&sig, "Web", "Don't @ me")); check(git_revparse_ext(&parent, &ref, _repo, "HEAD")); check(git_repository_index(&index, _repo)); check(git_index_write_tree(&oid_tree, index)); check(git_index_write(index)); check(git_tree_lookup(&tree, _repo, &oid_tree)); check(git_reference_name_to_id(&oid_parent, _repo, "HEAD")); // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg) check(git_commit_create_v(&oid_commit, _repo, "HEAD", sig, sig, nullptr, ("New entry: " + entry.instance).c_str(), tree, parent != nullptr ? 1 : 0, parent)); git_index_free(index); git_signature_free(sig); git_tree_free(tree); git_object_free(parent); git_reference_free(ref); } void push() { git_push_options options; git_remote *remote{nullptr}; string refspec_str{("refs/heads/" + get_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)); options.callbacks.credentials = cred_acquire; check(git_remote_push(remote, &refspecs, &options)); git_remote_free(remote); } string get_branch_name() { const auto id{gitea::get_last_pr_number() + 1}; return "web-" + to_string(id); } } // namespace FediBlock::git