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)


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