(** Package list and detail pages *) let list_page ~html_dir = let packages = Day10_web_data.Package_data.list_packages ~html_dir in let rows = packages |> List.map (fun (name, version) -> Printf.sprintf {| %s %s %s View Docs |} name version name version (Layout.badge `Success) name version ) |> String.concat "\n" in let content = Printf.sprintf {|

Packages

%s
Package Version Docs Status Links
|} rows in Layout.page ~title:"Packages" ~content let detail_page ~html_dir ~cache_dir ~platform ~log_dir ~name ~version = let package = name ^ "." ^ version in if not (Day10_web_data.Package_data.package_has_docs ~html_dir ~name ~version) then Layout.page ~title:"Package Not Found" ~content:(Printf.sprintf {|

Package Not Found

No documentation found for %s

← Back to packages

|} package) else let all_versions = Day10_web_data.Package_data.list_package_versions ~html_dir ~name in let versions_list = all_versions |> List.map (fun v -> if v = version then Printf.sprintf "
  • %s (current)
  • " v else Printf.sprintf {|
  • %s
  • |} name v v ) |> String.concat "\n" in (* Get layer info for dependencies and build timestamp *) let layer_info = Day10_web_data.Layer_data.get_package_layer ~cache_dir ~platform ~package in (* Get latest run ID for log links *) let latest_run = Day10_web_data.Run_data.get_latest_run_id ~log_dir in (* Determine build status from multiple sources *) let build_status, build_time = match layer_info with | Some info -> let timestamp = Unix.gmtime info.created in let time_str = Printf.sprintf "%04d-%02d-%02d %02d:%02d:%02d UTC" (timestamp.Unix.tm_year + 1900) (timestamp.Unix.tm_mon + 1) timestamp.Unix.tm_mday timestamp.Unix.tm_hour timestamp.Unix.tm_min timestamp.Unix.tm_sec in let status = if info.exit_status = 0 then `Success else `Failed in (status, Some time_str) | None -> (* No layer info - check logs and summary *) match latest_run with | Some run_id -> (* Check if run is still in progress *) if Day10_web_data.Run_data.is_run_in_progress ~log_dir ~run_id then (* Check if we have logs for this package *) if Day10_web_data.Run_data.has_build_log ~log_dir ~run_id ~package then (`In_progress, None) else (`Pending, None) else (* Run finished - check summary for status *) begin match Day10_web_data.Run_data.get_package_status_from_summary ~log_dir ~run_id ~package with | Some `Success -> (`Success, None) | Some (`Failed _) -> (`Failed, None) | Some `Not_in_run -> (`Unknown, None) | None -> (`Unknown, None) end | None -> (`Unknown, None) in (* Build info section *) let status_badge = match build_status with | `Success -> Layout.badge `Success | `Failed -> Layout.badge `Failed | `In_progress -> {|building|} | `Pending -> {|pending|} | `Unknown -> Layout.badge `Skipped in let build_status_content = let time_line = match build_time with | Some t -> Printf.sprintf {|

    Built: %s

    |} t | None -> "" in Printf.sprintf {|

    Status: %s

    %s |} status_badge time_line in (* Log links - show if logs exist *) let log_links = match latest_run with | Some run_id -> let has_build = Day10_web_data.Run_data.has_build_log ~log_dir ~run_id ~package in let has_docs = Day10_web_data.Run_data.has_doc_log ~log_dir ~run_id ~package in if has_build || has_docs then let build_link = if has_build then Printf.sprintf {|Build Log →|} run_id package else {|No build log|} in let doc_link = if has_docs then Printf.sprintf {|Doc Log →|} run_id package else {|No doc log|} in Printf.sprintf {|

    %s | %s

    |} build_link doc_link else {|

    No logs in latest run.

    |} | None -> {|

    No runs recorded yet.

    |} in let build_info = Printf.sprintf {|

    Build & Logs

    %s %s
    |} build_status_content log_links in (* Parse "name.version" format - version starts at first .digit or .v followed by digit *) let parse_package_str s = let len = String.length s in let rec find_version_start i = if i >= len - 1 then None else if s.[i] = '.' then let next = s.[i + 1] in if next >= '0' && next <= '9' then Some i else if next = 'v' && i + 2 < len && s.[i + 2] >= '0' && s.[i + 2] <= '9' then Some i else find_version_start (i + 1) else find_version_start (i + 1) in match find_version_start 0 with | Some i -> Some (String.sub s 0 i, String.sub s (i + 1) (len - i - 1)) | None -> None in (* Dependencies section - always show *) let deps_section = let deps_content = match layer_info with | Some info when info.deps <> [] -> let deps_list = info.deps |> List.map (fun dep -> match parse_package_str dep with | Some (dep_name, dep_version) -> Printf.sprintf {|
  • %s
  • |} dep_name dep_version dep | None -> Printf.sprintf "
  • %s
  • " dep) |> String.concat "\n" in Printf.sprintf {||} deps_list | Some _ -> {|

    No dependencies.

    |} | None -> {|

    Dependency information not available.

    |} in let deps_count = match layer_info with | Some info -> List.length info.deps | None -> 0 in Printf.sprintf {|

    Dependencies (%d)

    %s
    |} deps_count deps_content in (* Reverse dependencies section - always show *) let reverse_deps = Day10_web_data.Layer_data.get_reverse_deps ~cache_dir ~platform ~package in let rev_deps_section = let rev_deps_content = if reverse_deps <> [] then let rev_deps_list = reverse_deps |> List.map (fun dep -> match parse_package_str dep with | Some (dep_name, dep_version) -> Printf.sprintf {|
  • %s
  • |} dep_name dep_version dep | None -> Printf.sprintf "
  • %s
  • " dep) |> String.concat "\n" in Printf.sprintf {||} rev_deps_list else {|

    No packages depend on this one.

    |} in Printf.sprintf {|

    Reverse Dependencies (%d)

    %s
    |} (List.length reverse_deps) rev_deps_content in let content = Printf.sprintf {|

    %s

    ← Back to packages

    Documentation

    %s

    View Documentation →

    %s %s %s

    Other Versions

    |} package (Layout.badge `Success) name version build_info deps_section rev_deps_section versions_list in Layout.page ~title:package ~content (** Combined build and doc logs page for a package *) let logs_page ~log_dir ~name ~version = let package = name ^ "." ^ version in let latest_run = Day10_web_data.Run_data.get_latest_run_id ~log_dir in match latest_run with | None -> Layout.page ~title:(package ^ " Logs") ~content:(Printf.sprintf {|

    %s Logs

    ← Back to package

    No run data available.

    |} package name version) | Some run_id -> let build_log = Day10_web_data.Run_data.read_build_log ~log_dir ~run_id ~package in let doc_log = Day10_web_data.Run_data.read_doc_log ~log_dir ~run_id ~package in let build_section = match build_log with | Some log -> Printf.sprintf {|

    Build Log

    From run %s

    %s
    |} run_id log | None -> {|

    Build Log

    No build log available for this package.

    |} in let doc_section = match doc_log with | Some log -> Printf.sprintf {|

    Documentation Log

    From run %s

    %s
    |} run_id log | None -> {|

    Documentation Log

    No doc log available for this package.

    |} in let content = Printf.sprintf {|

    %s Logs

    ← Back to package

    %s %s |} package name version build_section doc_section in Layout.page ~title:(package ^ " Logs") ~content