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

startgit-index.cpp (4785B)


0 #include <format> 1 #include <fstream> 2 #include <iostream> 3 4 #include <git2wrap/error.hpp> 5 #include <git2wrap/libgit2.hpp> 6 #include <hemplate/classes.hpp> 7 #include <poafloc/poafloc.hpp> 8 9 #include "arguments.hpp" 10 #include "common.hpp" 11 #include "repository.hpp" 12 13 namespace 14 { 15 16 int parse_opt(int key, const char* arg, poafloc::Parser* parser) 17 { 18 auto* l_args = static_cast<startgit::arguments_t*>(parser->input()); 19 switch (key) { 20 case 'o': 21 l_args->output_dir = arg; 22 break; 23 case 'b': 24 l_args->base_url = arg; 25 if (l_args->base_url.back() == '/') { 26 l_args->base_url.pop_back(); 27 } 28 break; 29 case 'r': 30 l_args->resource_url = arg; 31 if (l_args->resource_url.back() == '/') { 32 l_args->resource_url.pop_back(); 33 } 34 break; 35 case 'a': 36 l_args->author = arg; 37 break; 38 case 'd': 39 l_args->description = arg; 40 break; 41 case 'f': 42 l_args->force = true; 43 break; 44 case poafloc::ARG: 45 try { 46 l_args->repos.emplace_back(std::filesystem::canonical(arg)); 47 } catch (const std::filesystem::filesystem_error& arr) { 48 std::cerr << std::format("Warning: {} doesn't exist\n", arg); 49 } 50 break; 51 case poafloc::END: 52 if (l_args->repos.empty()) { 53 std::cerr << std::format("Error: no repositories provided\n"); 54 return -1; 55 } 56 break; 57 case poafloc::ERROR: 58 poafloc::help(parser, stderr, poafloc::STD_ERR); 59 break; 60 default: 61 break; 62 } 63 return 0; 64 } 65 66 // NOLINTBEGIN 67 // clang-format off 68 static const poafloc::option_t options[] = { 69 {0, 0, 0, 0, "Output mode", 1}, 70 {"output", 'o', "DIR", 0, "Output directory"}, 71 {"force", 'f', 0, 0, "Force write even if file exists"}, 72 {0, 0, 0, 0, "General information", 2}, 73 {"base", 'b', "URL", 0, "Absolute destination URL"}, 74 {"resource", 'r', "URL", 0, "URL that houses styles and scripts"}, 75 {"author", 'a', "NAME", 0, "Owner of the repository"}, 76 {"title", 't', "TITLE", 0, "Title for the index page"}, 77 {"description", 'd', "DESC", 0, "Description for the index page"}, 78 {0, 0, 0, 0, "Informational Options", -1}, 79 {0}, 80 }; 81 // clang-format on 82 83 static const poafloc::arg_t arg { 84 options, 85 parse_opt, 86 "repositories...", 87 "", 88 }; 89 // NOLINTEND 90 91 } // namespace 92 93 int main(int argc, char* argv[]) 94 { 95 using namespace hemplate; // NOLINT 96 using namespace startgit; // NOLINT 97 98 if (poafloc::parse(&arg, argc, argv, 0, &args) != 0) { 99 std::cerr << "There was an error while parsing arguments\n"; 100 return 1; 101 } 102 103 try { 104 const git2wrap::libgit2 libgit; 105 106 auto& output_dir = args.output_dir; 107 std::filesystem::create_directories(output_dir); 108 output_dir = std::filesystem::canonical(output_dir); 109 110 std::ofstream ofs(args.output_dir / "index.html"); 111 write_header(ofs, 112 args.title, 113 args.description, 114 args.author, 115 "./", 116 /*has_feed=*/false); 117 118 ofs << html::h1(args.title); 119 ofs << html::p(args.description); 120 121 ofs << html::table(); 122 ofs << html::thead(); 123 ofs << html::tr() 124 .add(html::td("Name")) 125 .add(html::td("Description")) 126 .add(html::td("Owner")) 127 .add(html::td("Last commit")); 128 ofs << html::thead(); 129 ofs << html::tbody(); 130 131 for (const auto& repo_path : args.repos) { 132 try { 133 const repository repo(repo_path); 134 135 for (const auto& branch : repo.get_branches()) { 136 if (branch.get_name() != "master") { 137 continue; 138 } 139 140 const auto url = repo.get_name() + "/master/log.html"; 141 142 ofs << html::tr() 143 .add(html::td().add( 144 html::a(repo.get_name()).set("href", url))) 145 .add(html::td(repo.get_description())) 146 .add(html::td(repo.get_owner())) 147 .add(html::td(branch.get_commits()[0].get_time())); 148 goto next; 149 } 150 151 std::cerr << std::format("Warning: {} doesn't have master branch\n", 152 repo.get_path().string()); 153 next:; 154 } catch (const git2wrap::error<git2wrap::error_code_t::ENOTFOUND>& err) { 155 std::cerr << std::format("Warning: {} is not a repository\n", 156 repo_path.string()); 157 } 158 } 159 160 ofs << html::tbody(); 161 ofs << html::table(); 162 163 write_footer(ofs); 164 } catch (const git2wrap::runtime_error& err) { 165 std::cerr << std::format("Error (git2wrap): {}\n", err.what()); 166 } catch (const std::runtime_error& err) { 167 std::cerr << std::format("Error: {}\n", err.what()); 168 } catch (...) { 169 std::cerr << std::format("Unknown error\n"); 170 } 171 172 return 0; 173 }