Subversion Repositories Koakuma

Rev

Rev 14 | Rev 18 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 nishi 1
#!/usr/bin/env tclsh
2
# $Id: koakuma.cgi.in 15 2024-10-02 06:42:12Z nishi $
3
 
4
set KOAKUMA_VERSION "1.00"
5
set components ""
6
 
13 nishi 7
chan configure stdout -buffering none
8
 
2 nishi 9
proc exiting {code} {
10
	exit $code
11
}
12
 
3 nishi 13
proc loop_components {run} {
14
	global components
6 nishi 15
	foreach {name description version genre} $components {
3 nishi 16
		eval $run
17
	}
18
}
19
 
2 nishi 20
proc crash {reason} {
21
	global components KOAKUMA_VERSION
22
	puts stderr "----- Start Koakuma Crash dump log -----"
23
	puts stderr "Included components:"
3 nishi 24
	loop_components {
2 nishi 25
		puts stderr "	$name: $description, version $version"
26
	}
27
	puts stderr "Reason: $reason"
13 nishi 28
	puts stderr "Code: $::errorCode"
29
	puts stderr "Info: $::errorInfo"
2 nishi 30
	puts stderr "----- End Koakuma Crash dump log -----"
31
	puts	"Content-Type: text/html"
32
	puts	"Status: 500 Internal Server Error"
33
	puts	""
34
	puts	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">"
35
	puts	"<html>"
36
	puts	"	<head>"
37
	puts	"		<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"
38
	puts	"		<title>Oops</title>"
39
	puts	"	</head>"
40
	puts	"	<body>"
41
	puts	"		<h1>Oops</h1>"
42
	puts	"		<hr>"
43
	puts	"		Koakuma version $KOAKUMA_VERSION crashed, reason: <code>$reason</code><br>"
44
	puts	"		See the server error log for details."
45
	puts	"	</body>"
46
	puts	"</html>"
47
	exiting 1
48
}
49
 
3 nishi 50
if { ![info exists env(PATH_INFO)] } {
51
	puts "Status: 301 Moved Permanently"
52
	puts "Location: $env(SCRIPT_NAME)/"
53
	puts ""
54
	exiting 0
2 nishi 55
}
56
 
13 nishi 57
if { [file exists "@@PREFIX@@/etc/koakuma/cgi.conf"] } {
58
	if { [catch {
59
		source "@@PREFIX@@/etc/koakuma/cgi.conf"
60
	}] } {
61
		crash "Config failure"
62
	}
63
}
64
 
3 nishi 65
if { [catch {
13 nishi 66
	set tdom_version "[package require tdom]"
4 nishi 67
	dom createNodeCmd -tagName "rpc" elementNode rootXML
6 nishi 68
	dom createNodeCmd -tagName "project" elementNode keyProject
4 nishi 69
	dom createNodeCmd -tagName "version" -jsonType NONE elementNode keyVersion
5 nishi 70
	dom createNodeCmd -tagName "error" -jsonType NONE elementNode keyError
6 nishi 71
	dom createNodeCmd -tagName "name" -jsonType NONE elementNode keyName
72
	dom createNodeCmd -tagName "description" -jsonType NONE elementNode keyDescription
73
	dom createNodeCmd -tagName "vcs" -jsonType NONE elementNode keyVCS
74
	dom createNodeCmd -tagName "url" -jsonType NONE elementNode keyURL
4 nishi 75
	dom createNodeCmd -jsonType STRING textNode valueString
3 nishi 76
}] } {
77
	crash "Failed to load tDOM"
2 nishi 78
}
3 nishi 79
 
80
if { [catch {
13 nishi 81
	set tclx_version "[package require Tclx]"
82
}] } {
83
	crash "Failed to load TclX"
84
}
85
 
86
proc Get_KV {lst key} {
87
	foreach {k v} $lst {
88
		if { "$k" == "$key" } {
89
			return "$v"
90
		}
91
	}
92
	return ""
93
}
94
 
95
proc URL_parse {url} {
96
	if { [regexp {^([^:]+)://(([^:]+:[^@]+|[^:]+:|[^:]+)@)?([^/]+)(.+)?$} "$url" -> scheme userpass_at userpass host path] } {
97
		lappend result "scheme" "$scheme"
98
		lappend result "userpass" "$userpass"
99
		lappend result "host" "$host"
100
		lappend result "path" "$path"
101
		return $result
102
	} elseif { [regexp {^/.+$} "$url" path] } {
103
		lappend result "scheme" "file"
104
		lappend result "userpass" ""
105
		lappend result "host" ""
106
		lappend result "path" "$path"
107
		return $result
108
	}
109
}
110
 
111
if { [catch {
3 nishi 112
	foreach path [glob @@PREFIX@@/lib/koakuma/component/*.tcl] {
113
		source "$path"
114
	}
115
}] } {
116
	crash "Could not load components"
2 nishi 117
}
3 nishi 118
 
5 nishi 119
set data ""
120
 
121
while { [gets stdin line] >= 0 } {
122
	if { "$data" == "" } {
123
		set data "$line"
124
	} else {
125
		set data "$data\n$line"
126
	}
127
}
13 nishi 128
chan close stdin
5 nishi 129
 
3 nishi 130
set toc ""
131
set result ""
132
set content ""
133
 
6 nishi 134
proc write_db {data} {
135
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.db" "w"]
136
	puts $fid "$data"
137
	close $fid
138
}
139
 
140
proc readall_db {} {
141
	set data ""
142
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.db" "r"]
143
	while { [gets $fid line] >= 0 } {
144
		if { "$data" == "" } {
145
			set data "$line"
146
		} else {
147
			set data "$data\n$line"
148
		}
149
	}
150
	close $fid
151
	return "$data"
152
}
153
 
3 nishi 154
proc rputs {data} {
155
	global result
156
	if { "$result" == "" } {
157
		set result "$data"
158
	} else {
159
		set result "$result\n$data"
160
	}
161
}
162
 
163
proc tputs {data} {
164
	global content
165
	if { "$content" == "" } {
166
		set content "$data"
167
	} else {
168
		set content "$content\n$data"
169
	}
170
}
171
 
172
proc html_escape {data} {
13 nishi 173
	set tmp "[regsub -all {<} "[regsub -all {>} "$data" {\&gt;}]" {\&lt;}]"
174
	return "[regsub -all {[^:]+://[^ ]+} "$tmp" {<a href="\0">\0</a>}]"
3 nishi 175
}
176
 
177
proc open_projects {} {
178
	while 1 {
179
		if { ![info exists "@@PREFIX@@/lib/koakuma/db/projects.lock"] } {
180
			break
181
		}
182
		set fid [open "@@PREFIX@@/lib/koakuma/db/projects.lock" "w"]
183
		if { ![info exists "/proc/[gets $fid line]"] } {
184
			close $fid
185
			break
186
		}
187
		after 10
188
		close $fid
189
	}
190
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.lock" "w"]
191
	puts $fid "[pid]"
192
	close $fid
193
}
194
 
195
proc scan_projects {run} {
196
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.db" "r"]
197
	set content ""
198
	while { [gets $fid line] >= 0 } {
199
		if { "$content" == "" } {
200
			set content "$line"
201
		} else {
202
			set content "$content\n$line"
203
		}
204
	}
205
	close $fid
206
	set dom [dom parse "$content"]
207
	set doc [$dom documentElement]
208
	foreach elem [$doc selectNodes "/projects/project"] {
209
		set name "[$elem selectNodes "string(name)"]"
210
		set description "[$elem selectNodes "string(description)"]"
13 nishi 211
		set vcs "[$elem selectNodes "string(vcs)"]"
212
		set vcs_url "[$elem selectNodes "string(url)"]"
3 nishi 213
		eval $run
214
	}
215
}
216
 
217
proc project_exists {projname} {
218
	set desc ""
219
	scan_projects {
220
		upvar 1 desc desc
221
		upvar 1 projname projname
222
		if { "$name" == "$projname" } {
223
			set desc "$description"
224
			break
225
		}
226
	}
227
	return "$desc"
228
}
229
 
230
proc close_projects {} {
231
	file delete "@@PREFIX@@/lib/koakuma/db/projects.lock"
232
}
233
 
234
proc start_html {title has_toc} {
13 nishi 235
	global toc env koakuma_png css
3 nishi 236
	rputs	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
237
	rputs	"<html>"
238
	rputs	"	<head>"
239
	rputs	"		<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"
240
	rputs	"		<title>$title - Koakuma</title>"
13 nishi 241
	rputs	"		<link rel=\"stylesheet\" href=\"$css\">"
3 nishi 242
	rputs	"	</head>"
243
	rputs	"	<body>"
244
	rputs	"		<a href=\"/koakuma\" id=\"gomain\">"
13 nishi 245
	rputs	"			<img src=\"$koakuma_png\" alt=\"Koakuma by Kasuya Baian\" height=\"192px\">"
3 nishi 246
	rputs	"		</a>"
247
	rputs	"		<div id=\"space\"></div>"
248
	rputs	"		<div id=\"title\">"
249
	rputs	"			Koakuma"
250
	rputs	"		</div>"
12 nishi 251
	rputs	"		<a href=\"$env(SCRIPT_NAME)\">Root</a>"
3 nishi 252
	rputs	"		<div id=\"clearfix\"></div>"
253
	if { "$has_toc" == "1" } {
254
		rputs	"		<div id=\"toc\">"
255
		rputs	"			<div id=\"toctitle\">TOC</div>"
256
		foreach sect $toc {
257
			if { "[string range "[regsub -all { } "$sect" "-"]" 0 0]" == "-" } {
258
				rputs "<a class=\"shiftlink\" href=\"#TOC-[regsub {^-} "[regsub -all { } "$sect" "-"]" ""]\">[regsub {^-} "$sect" ""]</a><br>"
259
			} else {
260
				rputs "<a href=\"#TOC-[regsub -all { } "$sect" "-"]\">$sect</a><br>"
261
			}
262
		}
263
		rputs	"		</div>"
264
	}
265
	if { "$has_toc" == "1" } {
266
		rputs	"		<div id=\"doc\">"
267
	} else {
268
		rputs	"		<div id=\"doc-notoc\">"
269
	}
270
	rputs	"			<h1>$title</h1>"
271
}
272
proc end_html {has_toc} {
273
	global KOAKUMA_VERSION
274
	rputs	"		</div>"
275
	rputs	"		<hr>"
276
	rputs	"		<i>Powered by <a href=\"http://nishi.boats/koakuma\">Koakuma</a> $KOAKUMA_VERSION</i>"
277
	rputs	"	</body>"
278
	rputs	"</html>"
279
}
280
 
281
proc add_toc {data} {
282
	global toc
283
	tputs	"<h2 id=\"TOC-[regsub -all { } "$data" "-"]\"><a href=\"#TOC-[regsub -all { } "$data" "-"]\">#</a> $data</h2>"
284
	lappend toc "$data"
285
}
286
 
287
proc add_toc2 {data} {
288
	global toc
289
	tputs	"<h3 id=\"TOC-[regsub -all { } "$data" "-"]\"><a href=\"#TOC-[regsub -all { } "$data" "-"]\">#</a> $data</h3>"
290
	lappend toc "-$data"
291
}
292
 
293
if { [catch {
294
	set path "[regsub -all {/+} "$env(PATH_INFO)" "/"]"
4 nishi 295
	if { [regexp {^/rpc(/.*)?$} "$path"] } {
296
		rputs "Content-Type: application/json"
297
	} else {
12 nishi 298
		if { ![regexp {/$} "$env(PATH_INFO)"] } {
299
			puts "Status: 301 Moved Permanently"
300
			puts "Location: $env(SCRIPT_NAME)$env(PATH_INFO)/"
301
			puts ""
302
			exiting 0
303
		}
4 nishi 304
		rputs "Content-Type: text/html"
305
	}
3 nishi 306
	if { "$path" == "/" } {
307
		add_toc "Tcl Information"
308
		tputs	"<table border=\"0\">"
309
		tputs	"	<tr>"
310
		tputs	"		<th>"
311
		tputs	"			Version"
312
		tputs	"		</th>"
313
		tputs	"		<td>"
314
		tputs	"			$tcl_version"
315
		tputs	"		</td>"
316
		tputs	"	</tr>"
317
		tputs	"	<tr>"
318
		tputs	"		<th>"
319
		tputs	"			Platform"
320
		tputs	"		</th>"
321
		tputs	"		<td>"
322
		tputs	"			$tcl_platform(os)/$tcl_platform(machine) $tcl_platform(osVersion)"
323
		tputs	"		</td>"
324
		tputs	"	</tr>"
13 nishi 325
		tputs	"	<tr>"
326
		tputs	"		<th>"
327
		tputs	"			tDOM version"
328
		tputs	"		</th>"
329
		tputs	"		<td>"
330
		tputs	"			$tdom_version"
331
		tputs	"		</td>"
332
		tputs	"	</tr>"
333
		tputs	"	<tr>"
334
		tputs	"		<th>"
335
		tputs	"			TclX version"
336
		tputs	"		</th>"
337
		tputs	"		<td>"
338
		tputs	"			$tclx_version"
339
		tputs	"		</td>"
340
		tputs	"	</tr>"
3 nishi 341
		tputs	"</table>"
342
		add_toc "Components"
343
		loop_components {
344
			if { [llength [info procs "${name}_info"]] > 0 } {
345
				${name}_info
346
			}
347
		}
348
		set has_projects 0
349
		add_toc "Projects"
350
		open_projects
351
		scan_projects {
352
			upvar 1 has_projects has_projects
353
			if { "$has_projects" == "0" } {
354
				set has_projects 1
355
				tputs	"<table border=\"0\">"
356
			}
357
			tputs	"<tr>"
358
			tputs	"	<th><a href=\"/koakuma/project/$name\">$name</a></th>"
13 nishi 359
			tputs	"	<td>[html_escape "$description"]</td>"
3 nishi 360
			tputs	"</tr>"
361
		}
362
		close_projects
363
		if { "$has_projects" == "1" } {
364
			tputs	"</table>"
365
		} else {
366
			tputs	"No projects have been added, yet."
367
		}
368
 
369
		rputs ""
370
		start_html "Main" 1
371
		rputs "$content"
372
		end_html 1
4 nishi 373
	} elseif { [regexp {^/rpc(/.*)?$} "$path"] } {
374
		regexp {^/rpc(/.*)?$} "$path" -> api
375
		set doc [dom createDocumentNode]
5 nishi 376
		$doc appendFromScript {
377
			keyVersion {valueString "$KOAKUMA_VERSION"}
378
		}
4 nishi 379
		if { "$api" == "" || "$api" == "/" } {
5 nishi 380
			rputs ""
381
			rputs "[$doc asJSON]"
12 nishi 382
		} elseif { "$api" == "/launch-job" } {
383
			if { [catch {dom parse -json "$data" clidoc}] } {
384
				rputs "Status: 400 Bad Request"
385
				$doc appendFromScript {
386
					keyError {valueString "Bad JSON"}
387
				}
388
			} else {
389
				set projname "[$clidoc selectNodes "string(/name)"]"
390
				set builddesc "[$clidoc selectNodes "string(/description)"]"
391
				if { "$projname" == "" || "$builddesc" == "" } {
392
					rputs "Status: 400 Bad Request"
393
					$doc appendFromScript {
394
						keyError {valueString "Required field missing"}
395
					}
396
				} else {
397
					set has_name 0
13 nishi 398
					set use_vcs ""
399
					set use_vcs_url ""
12 nishi 400
					open_projects
401
					scan_projects {
402
						upvar 1 has_name has_name
403
						upvar 1 projname projname
13 nishi 404
						upvar 1 use_vcs use_vcs
405
						upvar 1 use_vcs_url use_vcs_url
12 nishi 406
						if { "$name" == "$projname" } {
407
							set has_name 1
13 nishi 408
							set use_vcs "$vcs"
409
							set use_vcs_url "$vcs_url"
12 nishi 410
							break
411
						}
412
					}
413
					close_projects
414
					if { $has_name == 0 } {
415
						rputs "Status: 400 Bad Request"
416
						$doc appendFromScript {
417
							keyError {valueString "Project does not exist"}
418
						}
419
					} else {
13 nishi 420
						set cont 1
421
						if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"] } {
422
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock" "r"]
423
							set readpid "[gets $fid]"
424
							close $fid
425
							if { [file exists "/proc/$readpid"] } {
426
								set cont 0
427
								rputs "Status: 403 Forbidden"
428
								$doc appendFromScript {
429
									keyError {valueString "Other building process has been running"}
430
								}
431
							}
432
						}
433
						if { $cont == 1 } {
434
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "r"]
435
							set count [expr [gets $fid] + 1]
436
							close $fid
437
 
438
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "w"]
439
							puts $fid "$count"
440
							close $fid
441
 
442
							set count "[format %08s "$count"]"
443
 
444
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun" "w"]
445
							puts $fid "[clock seconds]"
446
							close $fid
447
 
448
							file mkdir "@@PREFIX@@/lib/koakuma/db/data/$projname/build-$count"
449
 
450
							set pid [fork]
451
							if { $pid } {
452
								set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock" "w"]
453
								puts $fid "$pid"
454
								close $fid
455
							} else {
456
								set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build-$count/log" "w"]
457
								set fail 0
458
 
459
								dup $fid stdout
460
								dup $fid stderr
461
 
462
								puts "===== Checkout"
463
								puts "Using VCS: $use_vcs"
464
								if { [llength [info procs "${use_vcs}_repository"]] == 0 } {
465
									puts "Component internal failure"
466
									set fail 1
467
								} else {
468
									cd "@@PREFIX@@/lib/koakuma/db/data/$projname"
469
									if { [${use_vcs}_repository "$use_vcs_url" "workspace"] } {
470
										puts "Checkout failure"
471
										set fail 1
472
									}
473
								}
474
								if { $fail == 0 } {
475
									puts "===== Build"
476
									cd "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace"
477
									if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace/Koakumafile"] } {
478
										if { [catch {
479
											namespace eval koakumafile {
480
												source "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace/Koakumafile"
481
											}
482
											koakumafile::run "$projname"
483
										}] } {
484
											puts "Failed to run Koakumafile"
485
											set fail 1
486
										}
487
									} else {
488
										puts "Nothing to do"
489
									}
490
								}
491
								if { $fail == 0 } {
492
									puts "Build successful"
493
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun" "w"]
494
									puts $fidsuc "[clock seconds]"
495
									close $fidsuc
496
 
497
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "r"]
498
									set sucbul [gets $fidsuc]
499
									close $fidsuc
500
 
501
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "w"]
502
									puts $fidsuc "[expr $sucbul + 1]"
503
									close $fidsuc
504
								}
505
 
506
								close $fid
507
 
508
								file delete "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"
509
								exit 0
510
							}
511
						}
12 nishi 512
					}
513
				}
514
			}
515
			rputs ""
516
			rputs "[$doc asJSON]"
5 nishi 517
		} elseif { "$api" == "/create-project" } {
518
			if { [catch {dom parse -json "$data" clidoc}] } {
519
				rputs "Status: 400 Bad Request"
520
				$doc appendFromScript {
521
					keyError {valueString "Bad JSON"}
522
				}
523
			} else {
524
				set projname "[$clidoc selectNodes "string(/name)"]"
525
				set projdescription "[$clidoc selectNodes "string(/description)"]"
6 nishi 526
				set projvcs "[$clidoc selectNodes "string(/vcs)"]"
5 nishi 527
				set url "[$clidoc selectNodes "string(/url)"]"
6 nishi 528
				if { "$projname" == "" || "$projdescription" == "" || "$projvcs" == "" || "$url" == "" } {
5 nishi 529
					rputs "Status: 400 Bad Request"
530
					$doc appendFromScript {
531
						keyError {valueString "Required field missing"}
532
					}
533
				} else {
534
					set has_vcs 0
6 nishi 535
					set has_name 0
5 nishi 536
					loop_components {
537
						upvar 1 has_vcs has_vcs
6 nishi 538
						upvar 1 projvcs projvcs
539
						if { "$name" == "$projvcs" && "$genre" == "VCS" } {
5 nishi 540
							set has_vcs 1
541
							break
542
						}
543
					}
6 nishi 544
					open_projects
545
					scan_projects {
546
						upvar 1 has_name has_name
547
						upvar 1 projname projname
548
						if { "$name" == "$projname" } {
549
							set has_name 1
550
							break
551
						}
552
					}
553
					close_projects
5 nishi 554
					if { $has_vcs == 0 } {
555
						rputs "Status: 400 Bad Request"
556
						$doc appendFromScript {
557
							keyError {valueString "Not a valid VCS"}
558
						}
6 nishi 559
					} elseif { $has_name == 1 } {
560
						rputs "Status: 400 Bad Request"
561
						$doc appendFromScript {
562
							keyError {valueString "Project already exists"}
563
						}
5 nishi 564
					} else {
565
						open_projects
6 nishi 566
						set xml "[readall_db]"
567
						set xmldoc [dom parse "$xml"]
568
						set root [$xmldoc documentElement]
569
						$root appendFromScript {
570
							keyProject {
571
								keyName {valueString "$projname"}
572
								keyDescription {valueString "$projdescription"}
573
								keyVCS {valueString "$projvcs"}
574
								keyURL {valueString "$url"}
575
							}
576
						}
577
						write_db "[$xmldoc asXML]"
11 nishi 578
						file mkdir "@@PREFIX@@/lib/koakuma/db/data/$projname"
13 nishi 579
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "w"]
580
						puts $fid "0"
581
						close $fid
582
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "w"]
583
						puts $fid "0"
584
						close $fid
5 nishi 585
						close_projects
586
					}
587
				}
588
			}
589
			rputs ""
590
			rputs "[$doc asJSON]"
591
		} else {
4 nishi 592
			$root appendFromScript {
5 nishi 593
				keyError {valueString "No such endpoint"}
4 nishi 594
			}
5 nishi 595
			rputs "Status: 404 Not Found"
596
			rputs ""
4 nishi 597
			rputs "[$doc asJSON]"
598
		}
3 nishi 599
	} elseif { [regexp {^/project/[^/]+.*$} "$path"] } {
12 nishi 600
		regexp {^/project/([^/]+)(.*)$} "$path" -> projname projpath
3 nishi 601
		open_projects
602
		set has_project [project_exists "$projname"]
603
		close_projects
604
 
605
		if { "$has_project" != "" } {
12 nishi 606
			if { "$projpath" == "" || "$projpath" == "/" } {
607
				add_toc "Description"
608
				tputs "[html_escape "$has_project"]"
609
				add_toc "Details"
610
				tputs	"<table border=\"0\">"
611
				tputs	"	<tr>"
612
				tputs	"		<th>"
15 nishi 613
				tputs	"			Status"
12 nishi 614
				tputs	"		</th>"
615
				tputs	"		<td>"
15 nishi 616
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"] } {
617
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.loc" "r"]
618
					if { [file exists "/proc/[gets $fid]"] } {
619
						tputs "Running"
620
					} else {
621
						tputs "Idle"
622
					}
12 nishi 623
					close $fid
624
				} else {
15 nishi 625
					tputs "Idle"
12 nishi 626
				}
627
				tputs	"			"
628
				tputs	"		</td>"
629
				tputs	"	</tr>"
630
				tputs	"	<tr>"
631
				tputs	"		<th>"
15 nishi 632
				tputs	"			Last run"
14 nishi 633
				tputs	"		</th>"
634
				tputs	"		<td>"
15 nishi 635
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun"] } {
636
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun" "r"]
637
					set date "[clock format "[gets $fid]" -format "%a %b %d %H:%M:%S %Z %Y"]"
14 nishi 638
					close $fid
15 nishi 639
					tputs "$date"
14 nishi 640
				} else {
15 nishi 641
					tputs "No builds yet"
14 nishi 642
				}
643
				tputs	"			"
644
				tputs	"		</td>"
645
				tputs	"	</tr>"
646
				tputs	"	<tr>"
647
				tputs	"		<th>"
12 nishi 648
				tputs	"			Last successful run"
649
				tputs	"		</th>"
650
				tputs	"		<td>"
651
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun"] } {
652
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun" "r"]
653
					set date "[clock format "[gets $fid]" -format "%a %b %d %H:%M:%S %Z %Y"]"
654
					close $fid
655
					tputs "$date"
656
				} else {
657
					tputs "No successful builds yet"
658
				}
659
				tputs	"			"
660
				tputs	"		</td>"
661
				tputs	"	</tr>"
13 nishi 662
				set builds [lsort -ascii [glob -nocomplain "@@PREFIX@@/lib/koakuma/db/data/$projname/build-*"]]
663
				if { [llength $builds] > 0 } {
664
					tputs	"	<tr>"
665
					tputs	"		<th>"
666
					tputs	"			Successful builds"
667
					tputs	"		</th>"
668
					tputs	"		<td>"
669
					if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild"] } {
670
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "r"]
671
						tputs "[format %.2f [expr [gets $fid] / [llength $builds] * 100]]%"
672
						close $fid
673
					}
674
					tputs	"			"
675
					tputs	"		</td>"
676
					tputs	"	</tr>"
677
				}
12 nishi 678
				tputs	"</table>"
13 nishi 679
 
680
				set builds [lsort -ascii [glob -nocomplain "@@PREFIX@@/lib/koakuma/db/data/$projname/build-*"]]
681
				if { [llength $builds] > 0 } {
682
					add_toc "Last build log"
683
					set lastbuild "[lindex $builds [expr [llength $builds] - 1]]"
684
					set fid [open "$lastbuild/log" "r"]
685
					tputs "<pre>"
686
					while { [gets $fid line] >= 0 } {
687
						tputs "[html_escape "$line"]"
688
					}
689
					tputs "</pre>"
690
					close $fid
691
				}
12 nishi 692
 
693
				rputs ""
694
				start_html "Project: $projname" 1
695
				rputs "$content"
696
				end_html 1
11 nishi 697
			} else {
12 nishi 698
				tputs "I could not find the endpoint you were finding."
699
 
700
				rputs "Status: 404 Not Found"
701
				rputs ""
702
				start_html "Project: $projname" 1
703
				rputs "$content"
704
				end_html 1
11 nishi 705
			}
3 nishi 706
		} else {
707
			tputs "I could not find the project you were finding."
708
 
709
			rputs "Status: 404 Not Found"
710
			rputs ""
711
			start_html "Not Found" 0
712
			rputs "$content"
713
			end_html 0
714
		}
715
	} else {
716
		tputs "I could not find the content you were finding."
717
 
718
		rputs "Status: 404 Not Found"
719
		rputs ""
720
		start_html "Not Found" 0
721
		rputs "$content"
722
		end_html 0
723
	}
724
}] } {
725
	crash "Could not render the HTML"
726
} else {
727
	puts "$result"
728
}
729
exiting 0