startgitStatic 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>
4 #include <git2wrap/error.hpp>
5 #include <git2wrap/libgit2.hpp>
6 #include <hemplate/classes.hpp>
7 #include <poafloc/poafloc.hpp>
9 #include "arguments.hpp"
10 #include "common.hpp"
11 #include "repository.hpp"
13 namespace
14 {
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 }
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
83 static const poafloc::arg_t arg {
84 options,
85 parse_opt,
86 "repositories...",
87 "",
88 };
89 // NOLINTEND
91 } // namespace
93 int main(int argc, char* argv[])
94 {
95 using namespace hemplate; // NOLINT
96 using namespace startgit; // NOLINT
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 }
103 try {
104 const git2wrap::libgit2 libgit;
106 auto& output_dir = args.output_dir;
107 std::filesystem::create_directories(output_dir);
108 output_dir = std::filesystem::canonical(output_dir);
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);
118 ofs << html::h1(args.title);
119 ofs << html::p(args.description);
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();
131 for (const auto& repo_path : args.repos) {
132 try {
133 const repository repo(repo_path);
135 for (const auto& branch : repo.get_branches()) {
136 if (branch.get_name() != "master") {
137 continue;
138 }
140 const auto url = repo.get_name() + "/master/log.html";
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 }
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 }
160 ofs << html::tbody();
161 ofs << html::table();
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 }
172 return 0;
173 }