patch
1 #!/usr/bin/python3 2 3 import cgi 4 import cgitb 5 import html 6 from urllib.parse import quote_plus 7 import time 8 import json 9 from util import * 10 from config import * 11 12 cgitb.enable() 13 14 HTML_HEAD=''' 15 <!doctype html> 16 <html lang="en"> 17 <head> 18 <meta charset="utf-8"> 19 <meta name="viewport" content="width=device-width, initial-scale=1"> 20 <title>Cradicle Explorer</title> 21 <link href="/css/bootstrap/bootstrap.min.css" rel="stylesheet"> 22 <style> 23 .form-control-dark::placeholder { 24 color: #aaa; 25 opacity: 1; 26 } 27 .diff-add { color: #22c55e; } 28 .diff-del { color: #ef4444; } 29 .diff-hunk { color: #6366f1; } 30 .diff-file { color: #facc15; font-weight: bold; } 31 </style> 32 <link rel="stylesheet" href="/assets/fontawesome/css/all.min.css"> 33 <link rel="icon" type="image/png" href="/favicon.png"> 34 ''' 35 36 print(HTML_HEAD) 37 38 form = cgi.FieldStorage() 39 rid = form.getvalue('rid') 40 patch_id = form.getvalue('id') 41 42 repo_name = '' 43 if rid and len(rid): 44 ret,out,err = exec_command(crad_bin,['inspect','-R',rid,'--identity']) 45 if not ret: 46 doc = json.loads(out) 47 payload_val = doc['payload']['xyz.radicle.project'] 48 repo_name = payload_val['name'] 49 50 patch_doc = None 51 patch_err = None 52 patch_title = '' 53 if patch_id and len(patch_id): 54 ret,out,err = exec_command(crad_bin,['patch','show',patch_id,'-R',rid,'--json']) 55 if ret: 56 patch_err = err 57 elif len(out): 58 patch_doc = json.loads(out) 59 patch_title = patch_doc['title'] 60 61 diff_output = '' 62 if patch_doc and patch_id: 63 ret,out,err = exec_command(crad_bin,['patch','diff',patch_id,'-R',rid]) 64 if not ret and out: 65 diff_output = out 66 67 HTML_DASHBOARD_START=f''' 68 <link href="/css/dashboard.css" rel="stylesheet"> 69 </head> 70 <body> 71 <header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow"> 72 <a class="navbar-brand col-md-3 col-lg-2 me-0 px-3 fs-6" href="/">Cradicle Explorer</a> 73 <button class="navbar-toggler position-absolute d-md-none collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#sidebarMenu" aria-controls="sidebarMenu" aria-expanded="false" aria-label="Toggle navigation"> 74 <span class="navbar-toggler-icon"></span> 75 </button> 76 <form method="get" action="/cgi-bin/main" style="width:100%;"><input class="form-control form-control-dark w-100 rounded-0 border-0" type="text" name="q" placeholder="Search repos" aria-label="Search"></form> 77 <div class="navbar-nav flex-row"> 78 <div class="nav-item text-nowrap"> 79 <a class="nav-link px-3 active" href="/cgi-bin/repo?id={quote_plus(rid)}">{html.escape(repo_name)}</a> 80 </div> 81 <div class="nav-item text-nowrap"> 82 <a class="nav-link px-3 active" href="/cgi-bin/patch?id={quote_plus(patch_id)}&rid={quote_plus(rid)}">{html.escape(patch_title[:16])}</a> 83 </div> 84 </div> 85 </header> 86 <div class="container-fluid"> 87 <div class="row"> 88 <nav id="sidebarMenu" class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse"> 89 <div class="position-sticky pt-3 sidebar-sticky"> 90 <ul class="nav flex-column"> 91 </ul> 92 </div> 93 </nav> 94 <main class="col-md-9 ms-sm-auto col-lg-10"> 95 <div class="container px-1 py-3"> 96 ''' 97 HTML_DASHBOARD_END = ''' 98 </div> 99 </main> 100 </div> 101 </div> 102 ''' 103 104 def format_diff(diff_text): 105 result = '' 106 for line in diff_text.splitlines(): 107 escaped = html.escape(line) 108 if line.startswith('+++') or line.startswith('---'): 109 result += f'<span class="diff-file">{escaped}</span>\n' 110 elif line.startswith('@@'): 111 result += f'<span class="diff-hunk">{escaped}</span>\n' 112 elif line.startswith('+'): 113 result += f'<span class="diff-add">{escaped}</span>\n' 114 elif line.startswith('-'): 115 result += f'<span class="diff-del">{escaped}</span>\n' 116 elif line.startswith('diff '): 117 result += f'<span class="diff-file">{escaped}</span>\n' 118 else: 119 result += escaped + '\n' 120 return result 121 122 dashboard_content = '' 123 124 if patch_doc: 125 status_html = html.escape(patch_doc['status']) 126 labels_html = '' 127 if 'labels' in patch_doc: 128 labels_html = f'''<div><span class="text-secondary">Labels</span> <span>{html.escape(','.join(patch_doc['labels']))}</span></div>''' 129 assigned_html = '' 130 if 'assignees' in patch_doc: 131 assigned_html = f'''<div><span class="text-secondary">Assigned to</span> <span>{html.escape(','.join(patch_doc['assignees']))}</span></div>''' 132 desc_html = '' 133 if patch_doc['description'] and len(patch_doc['description']): 134 desc_html = f'''<div><span class="text-secondary">Description</span> <span>{html.escape(patch_doc['description'])}</span></div>''' 135 136 dashboard_content += f'''<div class="list-group"> 137 <div class="list-group-item"> 138 <div><span class="text-secondary">Title</span> <span>{html.escape(patch_doc['title'])}</span></div> 139 <div><span class="text-secondary">Patch ID</span> <span>{html.escape(patch_doc['patch'])}</span></div> 140 <div><span class="text-secondary">Author</span> <span class="repo-item">{html.escape(patch_doc['alias'])}</span>-{html.escape(patch_doc['author'][8:])}</div> 141 <div><span class="text-secondary">Opened on</span> <span>{html.escape(time.ctime(patch_doc['timestamp']))}</span></div> 142 <div><span class="text-secondary">Status</span> <span class="repo-item">{status_html}</span></div> 143 <div><span class="text-secondary">Head</span> <span>{html.escape(patch_doc['head'])}</span></div> 144 <div><span class="text-secondary">Base</span> <span>{html.escape(patch_doc['base'])}</span></div> 145 {labels_html} 146 {assigned_html} 147 {desc_html} 148 </div> 149 </div>''' 150 151 if diff_output: 152 dashboard_content += f''' 153 <div class="list-group mt-3"> 154 <div class="list-group-item"> 155 <div class="mb-2" style="font-weight:bold;">Diff</div> 156 <pre style="margin:0; font-size:0.85rem; overflow-x:auto;">{format_diff(diff_output)}</pre> 157 </div> 158 </div>''' 159 160 elif patch_err: 161 dashboard_content += f'<p class="error-message">{html.escape(patch_err)}</p>' 162 163 print(HTML_DASHBOARD_START) 164 print(dashboard_content) 165 print(HTML_DASHBOARD_END) 166 167 HTML_FOOTER=''' 168 </body> 169 </html> 170 ''' 171 print(HTML_FOOTER)