[OCaml] Mobile-friendly clone of cgit.
Embed static assets into binary using ocaml-crunch
- Add dune rule to generate Static_assets module from lib/static/ (plain mode) - Create Static_handler module to serve embedded CSS/SVG with correct Content-Type - Replace Dream.static filesystem handler with embedded asset handler - Add crunch (>= 4.0.0) as a build dependency - Add tests verifying embedded assets are readable
dune-project
@@ -22,6 +22,7 @@
22
22
(dream-html (>= 3.11.2))
23
23
(git (>= 3.18.0))
24
24
(git-unix (>= 3.18.0))
25
Added:
(crunch (>= 4.0.0))
25
26
(ocamlformat (and :with-dev-setup (= 0.29.0)))
26
27
(toml (>= 7.1.0)))
27
28
(tags
lib/dune
@@ -5,3 +5,10 @@
5
5
(libraries dream dream-html git-unix toml)
6
6
(preprocess
7
7
(pps dream-html.ppx)))
8
Added:
9
Added:
(rule
10
Added:
(target static_assets.ml)
11
Added:
(deps
12
Added:
(source_tree static))
13
Added:
(action
14
Added:
(run ocaml-crunch -m plain -s -o %{target} static)))
lib/handlers.ml
@@ -77,5 +77,5 @@
77
77
get "/tags/" (handle tags);
78
78
get "/README" (handle readme);
79
79
];
80
Removed:
get "/static/**" (static "./lib/static");
80
Added:
get "/static/**" @@ Static_handler.handler;
81
81
]
lib/static_handler.ml
@@ -0,0 +1,24 @@
1
Added:
(* -*- mode: tuareg; -*- *)
2
Added:
3
Added:
let content_type_of_ext = function
4
Added:
| ".css" -> "text/css; charset=utf-8"
5
Added:
| ".svg" -> "image/svg+xml"
6
Added:
| ".html" -> "text/html; charset=utf-8"
7
Added:
| ".js" -> "application/javascript"
8
Added:
| ".png" -> "image/png"
9
Added:
| ".ico" -> "image/x-icon"
10
Added:
| _ -> "application/octet-stream"
11
Added:
12
Added:
let handler req =
13
Added:
let target = Dream.target req in
14
Added:
let path =
15
Added:
match String.split_on_char '/' target with
16
Added:
| "" :: "static" :: rest -> String.concat "/" rest
17
Added:
| _ -> ""
18
Added:
in
19
Added:
match Static_assets.read path with
20
Added:
| Some content ->
21
Added:
let ext = Filename.extension path in
22
Added:
let content_type = content_type_of_ext ext in
23
Added:
Dream.respond ~headers:[ ("Content-Type", content_type) ] content
24
Added:
| None -> Dream.respond ~status:`Not_Found "Not found"
test/test_ogit.ml
@@ -121,6 +121,15 @@
121
121
assert (hunk.new_count = 7)
122
122
| _ -> failwith "expected one diff hunk"
123
123
124
Added:
let test_static_assets () =
125
Added:
(match Ogit.Static_assets.read "styles.css" with
126
Added:
| Some content -> assert (String.length content > 0)
127
Added:
| None -> failwith "styles.css should be embedded");
128
Added:
(match Ogit.Static_assets.read "git_icon.svg" with
129
Added:
| Some content -> assert (String.length content > 0)
130
Added:
| None -> failwith "git_icon.svg should be embedded");
131
Added:
assert (Ogit.Static_assets.read "nonexistent" = None)
132
Added:
124
133
let () =
125
134
assert (Ogit.Resolvers.is_valid_repo_name "project.git");
126
135
assert (Ogit.Resolvers.is_valid_repo_name "project");
@@ -151,4 +160,5 @@
151
160
test_repository_layout ();
152
161
test_fallback_branch_candidates ();
153
162
test_line_diff ();
154
Removed:
test_diff_hunks ()
163
Added:
test_diff_hunks ();
164
Added:
test_static_assets ()