startgit

Static page generator for git repositories
git clone git://git.dimitrijedobrota.com/startgit.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |

commit737b1d9858fa2c82ecd1cff6d9dfe14536a7e727
parent018e37d0b990bcd8557ad3db5c5c6e41d8eb2bee
authorDimitrije Dobrota <mail@dimitrijedobrota.com>
dateWed, 8 Jan 2025 22:18:39 +0100

Proper decompositon and imporved safety

Diffstat:
MCMakeLists.txt|+++--
Asource/branch.cpp|++++++++++++
Asource/branch.hpp|+++++++++++++++++++++++
Dsource/lib.cpp|------
Dsource/lib.hpp|---------------------
Msource/main.cpp|+++++++++++++++++++++++++++++++++++++++++++---------------------------------------
Asource/repository.cpp|+++++++++++++++++++++++++++++++++++++++++
Asource/repository.hpp|+++++++++++++++++++++++++++++++++++++++++
Mtest/source/startgit_test.cpp|++------

9 files changed, 172 insertions(+), 80 deletions(-)


diff --git a/CMakeLists.txt b/CMakeLists.txt

@@ -4,7 +4,7 @@ include(cmake/prelude.cmake)

project(
startgit
VERSION 0.1.4
VERSION 0.1.5
DESCRIPTION "Static page generator for git repositories"
HOMEPAGE_URL "https://git.dimitrijedobrota.com/stargit.git"
LANGUAGES CXX

@@ -23,7 +23,8 @@ find_package(poafloc 1 CONFIG REQUIRED)

add_library(
startgit_lib OBJECT
source/lib.cpp
source/branch.cpp
source/repository.cpp
)
target_link_libraries(startgit_lib PUBLIC git2wrap::git2wrap hemplate::hemplate poafloc::poafloc)

diff --git a/source/branch.cpp b/source/branch.cpp

@@ -0,0 +1,12 @@

#include "branch.hpp"
namespace startgit
{
branch::branch(git2wrap::branch brnch)
: m_branch(std::move(brnch))
, m_name(m_branch.get_name())
{
}
} // namespace startgit

diff --git a/source/branch.hpp b/source/branch.hpp

@@ -0,0 +1,23 @@

#pragma once
#include <string>
#include <git2wrap/branch.hpp>
namespace startgit
{
class branch
{
public:
explicit branch(git2wrap::branch brnch);
const std::string& get_name() const { return m_name; }
private:
git2wrap::branch m_branch;
std::string m_name;
};
} // namespace startgit

diff --git a/source/lib.cpp b/source/lib.cpp

@@ -1,6 +0,0 @@

#include "lib.hpp"
library::library()
: name {"startgit"}
{
}

diff --git a/source/lib.hpp b/source/lib.hpp

@@ -1,21 +0,0 @@

#pragma once
#include <string>
/**
* @brief The core implementation of the executable
*
* This class makes up the library part of the executable, which means that the
* main logic is implemented here. This kind of separation makes it easy to
* test the implementation for the executable, because the logic is nicely
* separated from the command-line logic implemented in the main function.
*/
struct library
{
/**
* @brief Simply initializes the name member to the name of the project
*/
library();
std::string name;
};

diff --git a/source/main.cpp b/source/main.cpp

@@ -14,6 +14,8 @@

#include <hemplate/classes.hpp>
#include <poafloc/poafloc.hpp>
#include "repository.hpp"
struct arguments_t
{
std::filesystem::path output_dir = ".";

@@ -31,7 +33,8 @@ std::string long_to_string(int64_t date)

void write_header(std::ostream& ost,
const std::string& repo,
const std::string& branch,
const std::string& author)
const std::string& author,
const std::string& description)
{
using namespace hemplate; // NOLINT

@@ -45,7 +48,7 @@ void write_header(std::ostream& ost,

.add(html::meta({{"charset", "UTF-8"}}))
.add(html::meta({{"name", "author"}, {"content", author}}))
.add(html::meta(
{{"name", "description"}, {"content", "Content of " + name}}))
{{"name", "description"}, {"content", description}}))
.add(
html::meta({{"content", "width=device-width, initial-scale=1"},
{"name", "viewport"}}))

@@ -100,9 +103,7 @@ void write_commit_table(std::ostream& ost, git2wrap::revwalk& rwalk)

}
void write_repo_table(std::ostream& ost,
const arguments_t* args,
const std::string& description,
const std::string& owner)
const std::vector<startgit::repository>& repos)
{
using namespace hemplate; // NOLINT

@@ -116,14 +117,13 @@ void write_repo_table(std::ostream& ost,

ost << html::thead();
ost << html::tbody();
for (const auto& repo_path : args->repos) {
const std::string repo_name = repo_path.stem().string();
for (const auto& repo : repos) {
ost << html::tr()
.add(html::td().add(html::a(repo_name).set(
"href", repo_name + "/master_log.html")))
.add(html::td(description))
.add(html::td(owner))
.add(html::td().add(
html::a(repo.get_name())
.set("href", repo.get_name() + "/master_log.html")))
.add(html::td(repo.get_description()))
.add(html::td(repo.get_owner()))
.add(html::td(""));
}

@@ -153,7 +153,7 @@ int parse_opt(int key, const char* arg, poafloc::Parser* parser)

args->url = arg;
break;
case poafloc::ARG:
args->repos.emplace_back(arg);
args->repos.emplace_back(std::filesystem::canonical(arg));
break;
default:
break;

@@ -188,56 +188,61 @@ int main(int argc, char* argv[])

return 1;
}
using namespace git2wrap; // NOLINT
const libgit2 libgit;
for (const auto& repo_path : args.repos) {
try {
const std::string repo_name = repo_path.stem().string();
repository repo(nullptr);
try {
const git2wrap::libgit2 libgit;
std::vector<startgit::repository> repos;
// open all repositories
for (const auto& repo_path : args.repos) {
try {
repo = repository::open(
repo_path.c_str(), GIT_REPOSITORY_OPEN_NO_SEARCH, nullptr);
repos.emplace_back(repo_path);
} catch (const git2wrap::error& err) {
std::cerr << std::format("Warning: {} is not a repository\n",
repo_name);
continue;
repo_path.string());
}
}
for (const auto& repo : repos) {
for (const auto& branch : repo.get_branches()) {
std::filesystem::create_directory(args.output_dir / repo.get_name());
for (auto it = repo.branch_begin(GIT_BRANCH_LOCAL);
it != repo.branch_end();
++it)
{
const std::string filename = it->get_name() + "_log.html";
std::filesystem::create_directory(args.output_dir / repo_name);
std::ofstream ofs(args.output_dir / repo_name / filename);
const std::string filename = branch.get_name() + "_log.html";
std::ofstream ofs(args.output_dir / repo.get_name() / filename);
revwalk rwalk(repo);
git2wrap::revwalk rwalk(repo.get());
const object obj = repo.revparse(it->get_name().c_str());
const git2wrap::object obj =
repo.get().revparse(branch.get_name().c_str());
rwalk.push(obj.get_id());
write_header(ofs, repo_name, it->get_name(), "Dimitrije Dobrota");
write_header(ofs,
repo.get_name(),
branch.get_name(),
repo.get_owner(),
repo.get_description());
write_commit_table(ofs, rwalk);
write_footer(ofs);
}
} catch (const git2wrap::error& err) {
std::cerr << std::format("({}:{}) Error {}/{}: {}\n",
err.get_file(),
err.get_line(),
err.get_error(),
err.get_klass(),
err.get_message());
}
// Build repo index
std::ofstream ofs(args.output_dir / "index.html");
write_header(ofs, "Git repository", "~", "Dimitrije Dobrota");
write_repo_table(ofs, &args, "Desc", "Own");
write_header(ofs,
"Git repository",
"~",
"Dimitrije Dobrota",
"Collection of all public git repositories");
write_repo_table(ofs, repos);
write_footer(ofs);
} catch (const git2wrap::error& err) {
std::cerr << std::format("({}:{}) Error {}/{}: {}\n",
err.get_file(),
err.get_line(),
err.get_error(),
err.get_klass(),
err.get_message());
}
return 0;

diff --git a/source/repository.cpp b/source/repository.cpp

@@ -0,0 +1,41 @@

#include <fstream>
#include "repository.hpp"
namespace startgit
{
repository::repository(const std::filesystem::path& path)
: m_repo(git2wrap::repository::open(
path.c_str(), GIT_REPOSITORY_OPEN_NO_SEARCH, nullptr))
{
m_name = path.stem().string();
read_file(m_owner, path, "owner");
read_file(m_description, path, "description");
for (auto it = m_repo.branch_begin(GIT_BRANCH_LOCAL);
it != m_repo.branch_end();
++it)
{
m_branches.emplace_back(it->dup());
}
}
void repository::read_file(std::string& out,
const std::filesystem::path& base,
const char* file)
{
std::ifstream ifs(base / file);
if (!ifs.is_open()) {
ifs = base / ".git" / file;
}
if (ifs.is_open()) {
std::getline(ifs, out, '\n');
} else {
out = "Unknown";
}
}
} // namespace startgit

diff --git a/source/repository.hpp b/source/repository.hpp

@@ -0,0 +1,41 @@

#pragma once
#include <filesystem>
#include <string>
#include <vector>
#include <git2wrap/repository.hpp>
#include "branch.hpp"
namespace startgit
{
class repository
{
public:
explicit repository(const std::filesystem::path& path);
const git2wrap::repository& get() const { return m_repo; }
const std::string& get_name() const { return m_name; }
const std::string& get_owner() const { return m_owner; }
const std::string& get_description() const { return m_description; }
const auto& get_branches() const { return m_branches; }
private:
static void read_file(std::string& out,
const std::filesystem::path& base,
const char* file);
git2wrap::repository m_repo;
std::string m_name;
std::string m_owner = "Unknown";
std::string m_description;
std::vector<branch> m_branches;
};
} // namespace startgit

diff --git a/test/source/startgit_test.cpp b/test/source/startgit_test.cpp

@@ -1,8 +1,4 @@

#include "lib.hpp"
auto main() -> int
int main()
{
auto const lib = library {};
return lib.name == "startgit" ? 0 : 1;
return 0;
}