[OCaml] Mobile-friendly clone of cgit.
feat add favorite_repositories and archived_repositories config
New config.toml arrays allow partitioning repos on the root page: - favorite_repositories: displayed in a 'Favorites' section at the top - archived_repositories: displayed in an 'Archived' section at the bottom Entries are repo names (e.g. 'ogit.git') or directory names with a trailing slash (e.g. 'experiments/'). When both lists are empty the page renders as before without section headings.
Changed files
deploy/config.toml
@@ -2,7 +2,7 @@
2
2
# Place at /etc/ogit/config.toml (or set OGIT_CONFIG to override)
3
3
4
4
user = "git"
5
Removed:
default_branch = "main"
5
Added:
default_branch = "master"
6
6
git_project_root = "/srv/git"
7
7
commits_max_displayed = 10
8
8
host = "127.0.0.1"
lib/config.ml
@@ -10,6 +10,8 @@
10
10
title : string;
11
11
host : string;
12
12
port : int;
13
Added:
favorite_repositories : string list;
14
Added:
archived_repositories : string list;
13
15
}
14
16
15
17
type load_error =
@@ -34,6 +36,8 @@
34
36
title = "";
35
37
host = "127.0.0.1";
36
38
port = 8081;
39
Added:
favorite_repositories = [];
40
Added:
archived_repositories = [];
37
41
}
38
42
39
43
let locate_config_file () =
@@ -47,6 +51,9 @@
47
51
48
52
let to_table t =
49
53
let open Types in
54
Added:
let string_list_to_value lst =
55
Added:
TArray (if lst = [] then NodeEmpty else NodeString lst)
56
Added:
in
50
57
List.map
51
58
(fun (key, value) -> (Min.key key, value))
52
59
[
@@ -57,6 +64,8 @@
57
64
("title", TString t.title);
58
65
("host", TString t.host);
59
66
("port", TInt t.port);
67
Added:
("favorite_repositories", string_list_to_value t.favorite_repositories);
68
Added:
("archived_repositories", string_list_to_value t.archived_repositories);
60
69
]
61
70
|> Min.of_key_values
62
71
@@ -89,6 +98,14 @@
89
98
| Some _ -> Error (Invalid_value ("expected int for key: " ^ key))
90
99
| None -> Ok default
91
100
101
Added:
let find_string_list_opt table key =
102
Added:
match Types.Table.find_opt (Min.key key) table with
103
Added:
| Some (Types.TArray (Types.NodeString values)) -> Ok values
104
Added:
| Some (Types.TArray Types.NodeEmpty) -> Ok []
105
Added:
| Some _ ->
106
Added:
Error (Invalid_value ("expected array of strings for key: " ^ key))
107
Added:
| None -> Ok []
108
Added:
92
109
let of_table table =
93
110
let ( let* ) = Result.bind in
94
111
let* git_project_root = find_string table "git_project_root" in
@@ -98,6 +115,12 @@
98
115
let* title = find_string_opt table "title" ~default:default.title in
99
116
let* host = find_string_opt table "host" ~default:default.host in
100
117
let* port = find_int_opt table "port" ~default:default.port in
118
Added:
let* favorite_repositories =
119
Added:
find_string_list_opt table "favorite_repositories"
120
Added:
in
121
Added:
let* archived_repositories =
122
Added:
find_string_list_opt table "archived_repositories"
123
Added:
in
101
124
if commits_max_displayed <= 0 then
102
125
Error (Invalid_value "commits_max_displayed must be positive")
103
126
else if port < 1 || port > 65535 then
@@ -112,6 +135,8 @@
112
135
title;
113
136
host;
114
137
port;
138
Added:
favorite_repositories;
139
Added:
archived_repositories;
115
140
}
116
141
117
142
let io_error file error =
lib/handlers.ml
@@ -49,9 +49,27 @@
49
49
Lwt.return (path, date))
50
50
repo_paths
51
51
in
52
Added:
let node_name = function
53
Added:
| Resolvers.Repo { repo_name; _ } -> repo_name
54
Added:
| Resolvers.Directory (dir_name, _) -> dir_name ^ "/"
55
Added:
in
56
Added:
let is_favorite node =
57
Added:
List.mem (node_name node) config.Config.favorite_repositories
58
Added:
in
59
Added:
let is_archived node =
60
Added:
List.mem (node_name node) config.Config.archived_repositories
61
Added:
in
62
Added:
let favorites, rest = List.partition is_favorite nodes in
63
Added:
let archived, regular = List.partition is_archived rest in
64
Added:
(* Preserve the order specified in the config for favorites *)
65
Added:
let sorted_favorites =
66
Added:
List.filter_map
67
Added:
(fun name -> List.find_opt (fun n -> node_name n = name) favorites)
68
Added:
config.Config.favorite_repositories
69
Added:
in
52
70
let readme = Resolvers.read_root_readme config in
53
71
Views.root ~user:config.Config.user ~root_title:(root_title config) ~dates
54
Removed:
?readme nodes
72
Added:
~favorites:sorted_favorites ~archived ?readme regular
55
73
| Error error -> error_response error
56
74
57
75
module Repo = struct
lib/views/root.ml
@@ -55,13 +55,41 @@
55
55
];
56
56
])
57
57
58
Removed:
let render ~user ~root_title ~dates ?readme nodes =
59
Removed:
let tree =
58
Added:
let render ~user ~root_title ~dates ?(favorites = []) ?(archived = []) ?readme
59
Added:
nodes =
60
Added:
let repo_list nodes =
61
Added:
HTML.(ul [] (List.map (li_of_fs_node ~prefix:"" ~dates) nodes))
62
Added:
in
63
Added:
let favorites_section =
64
Added:
match favorites with
65
Added:
| [] -> HTML.null []
66
Added:
| _ ->
67
Added:
HTML.(
68
Added:
section
69
Added:
[ class_ "repo-section repo-favorites" ]
70
Added:
[ h3 [] [ txt "Favorites" ]; repo_list favorites ])
71
Added:
in
72
Added:
let main_section =
60
73
HTML.(
61
74
div
62
75
[ id "repositories" ]
63
Removed:
[ ul [] (List.map (li_of_fs_node ~prefix:"" ~dates) nodes) ])
76
Added:
(match (favorites, archived) with
77
Added:
| [], [] ->
78
Added:
(* No partitioning — render flat without a heading *)
79
Added:
[ repo_list nodes ]
80
Added:
| _ ->
81
Added:
if nodes = [] then []
82
Added:
else [ h3 [] [ txt "Repositories" ]; repo_list nodes ]))
64
83
in
84
Added:
let archived_section =
85
Added:
match archived with
86
Added:
| [] -> HTML.null []
87
Added:
| _ ->
88
Added:
HTML.(
89
Added:
section
90
Added:
[ class_ "repo-section repo-archived" ]
91
Added:
[ h3 [] [ txt "Archived" ]; repo_list archived ])
92
Added:
in
65
93
let readme_section =
66
94
match readme with
67
95
| None -> HTML.null []
@@ -96,5 +124,6 @@
96
124
repo = None;
97
125
subtitle = "";
98
126
active = Summary;
99
Removed:
content = [ tree; readme_section ];
127
Added:
content =
128
Added:
[ favorites_section; main_section; archived_section; readme_section ];
100
129
}
test/test_config.ml
@@ -14,6 +14,8 @@
14
14
title = "";
15
15
host = "127.0.0.1";
16
16
port = 9000;
17
Added:
favorite_repositories = [];
18
Added:
archived_repositories = [];
17
19
}
18
20
in
19
21
Ogit.Config.write_file ~file (Ogit.Config.to_table config);