Subversion Repositories Koakuma

Rev

Rev 31 | Rev 35 | 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 33 2024-10-02 09:29:41Z 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} {
22 nishi 173
	set tmp "[regsub -all {<} "[regsub -all {>} "[string trim "$data"]" {\&gt;}]" {\&lt;}]"
174
	set link "[regsub -all {[^: ]+://[^ \n]+} "$tmp" {<a href="\0">\0</a>}]"
175
	return "[regsub -all {\n} "$link" {<br>}]"
3 nishi 176
}
177
 
178
proc open_projects {} {
179
	while 1 {
180
		if { ![info exists "@@PREFIX@@/lib/koakuma/db/projects.lock"] } {
181
			break
182
		}
183
		set fid [open "@@PREFIX@@/lib/koakuma/db/projects.lock" "w"]
184
		if { ![info exists "/proc/[gets $fid line]"] } {
185
			close $fid
186
			break
187
		}
188
		after 10
189
		close $fid
190
	}
191
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.lock" "w"]
192
	puts $fid "[pid]"
193
	close $fid
194
}
195
 
196
proc scan_projects {run} {
197
	set fid [open "@@PREFIX@@/lib/koakuma/db/projects.db" "r"]
198
	set content ""
199
	while { [gets $fid line] >= 0 } {
200
		if { "$content" == "" } {
201
			set content "$line"
202
		} else {
203
			set content "$content\n$line"
204
		}
205
	}
206
	close $fid
207
	set dom [dom parse "$content"]
208
	set doc [$dom documentElement]
209
	foreach elem [$doc selectNodes "/projects/project"] {
210
		set name "[$elem selectNodes "string(name)"]"
211
		set description "[$elem selectNodes "string(description)"]"
13 nishi 212
		set vcs "[$elem selectNodes "string(vcs)"]"
213
		set vcs_url "[$elem selectNodes "string(url)"]"
3 nishi 214
		eval $run
215
	}
216
}
217
 
218
proc project_exists {projname} {
219
	set desc ""
220
	scan_projects {
221
		upvar 1 desc desc
222
		upvar 1 projname projname
223
		if { "$name" == "$projname" } {
224
			set desc "$description"
225
			break
226
		}
227
	}
228
	return "$desc"
229
}
230
 
231
proc close_projects {} {
232
	file delete "@@PREFIX@@/lib/koakuma/db/projects.lock"
233
}
234
 
33 nishi 235
set what ""
236
 
3 nishi 237
proc start_html {title has_toc} {
33 nishi 238
	global toc env koakuma_png css what
3 nishi 239
	rputs	"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
240
	rputs	"<html>"
241
	rputs	"	<head>"
242
	rputs	"		<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">"
243
	rputs	"		<title>$title - Koakuma</title>"
13 nishi 244
	rputs	"		<link rel=\"stylesheet\" href=\"$css\">"
24 nishi 245
	set msie " src=\"$koakuma_png\""
246
	if { [info exists "env(HTTP_USER_AGENT)"] } {
247
		if { [regexp {MSIE 6} "$env(HTTP_USER_AGENT)"] } {
248
			set msie " style=\"filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(src='$koakuma_png', sizingMethod='scale');\" src=\"/static/transparent.gif\""
249
		}
250
	}
3 nishi 251
	rputs	"	</head>"
252
	rputs	"	<body>"
253
	rputs	"		<a href=\"/koakuma\" id=\"gomain\">"
24 nishi 254
	rputs	"			<img alt=\"Koakuma by Kasuya Baian\" height=\"128px\"$msie>"
3 nishi 255
	rputs	"		</a>"
256
	rputs	"		<div id=\"space\"></div>"
257
	rputs	"		<div id=\"title\">"
258
	rputs	"			Koakuma"
259
	rputs	"		</div>"
12 nishi 260
	rputs	"		<a href=\"$env(SCRIPT_NAME)\">Root</a>"
3 nishi 261
	if { "$has_toc" == "1" } {
29 nishi 262
		rputs	"		<div id=\"doc\">"
263
	} else {
264
		rputs	"		<div id=\"doc-notoc\">"
265
	}
30 nishi 266
	rputs	"		<div style=\"clear: both;\"></div>"
29 nishi 267
	if { "$has_toc" == "1" } {
30 nishi 268
		rputs	"		<div id=\"toc\">"
269
		rputs	"			<div id=\"tocinside\">"
31 nishi 270
		rputs	"				<span id=\"toctitle\">TOC</span><hr>"
3 nishi 271
		foreach sect $toc {
272
			if { "[string range "[regsub -all { } "$sect" "-"]" 0 0]" == "-" } {
273
				rputs "<a class=\"shiftlink\" href=\"#TOC-[regsub {^-} "[regsub -all { } "$sect" "-"]" ""]\">[regsub {^-} "$sect" ""]</a><br>"
274
			} else {
275
				rputs "<a href=\"#TOC-[regsub -all { } "$sect" "-"]\">$sect</a><br>"
276
			}
277
		}
30 nishi 278
		rputs	"			</div>"
3 nishi 279
		rputs	"		</div>"
280
	}
29 nishi 281
	rputs	"			<div id=\"docinside\">"
282
	rputs	"				<h1>$title</h1>"
33 nishi 283
	rputs	"				$what"
31 nishi 284
	rputs	"				<div id=\"shift\">"
3 nishi 285
}
286
proc end_html {has_toc} {
28 nishi 287
	global KOAKUMA_VERSION toc
31 nishi 288
	rputs	"				</div>"
29 nishi 289
	rputs	"			</div>"
3 nishi 290
	rputs	"		</div>"
28 nishi 291
	rputs	"		<div id=\"clearfix\"></div>"
3 nishi 292
	rputs	"		<hr>"
293
	rputs	"		<i>Powered by <a href=\"http://nishi.boats/koakuma\">Koakuma</a> $KOAKUMA_VERSION</i>"
294
	rputs	"	</body>"
295
	rputs	"</html>"
296
}
297
 
298
proc add_toc {data} {
299
	global toc
300
	tputs	"<h2 id=\"TOC-[regsub -all { } "$data" "-"]\"><a href=\"#TOC-[regsub -all { } "$data" "-"]\">#</a> $data</h2>"
301
	lappend toc "$data"
302
}
303
 
304
proc add_toc2 {data} {
305
	global toc
306
	tputs	"<h3 id=\"TOC-[regsub -all { } "$data" "-"]\"><a href=\"#TOC-[regsub -all { } "$data" "-"]\">#</a> $data</h3>"
307
	lappend toc "-$data"
308
}
309
 
310
if { [catch {
311
	set path "[regsub -all {/+} "$env(PATH_INFO)" "/"]"
4 nishi 312
	if { [regexp {^/rpc(/.*)?$} "$path"] } {
313
		rputs "Content-Type: application/json"
314
	} else {
12 nishi 315
		if { ![regexp {/$} "$env(PATH_INFO)"] } {
316
			puts "Status: 301 Moved Permanently"
317
			puts "Location: $env(SCRIPT_NAME)$env(PATH_INFO)/"
318
			puts ""
319
			exiting 0
320
		}
4 nishi 321
		rputs "Content-Type: text/html"
322
	}
3 nishi 323
	if { "$path" == "/" } {
33 nishi 324
		set what "This is the main page."
28 nishi 325
		set has_projects 0
326
		add_toc "Projects"
327
		open_projects
328
		scan_projects {
329
			upvar 1 has_projects has_projects
330
			if { "$has_projects" == "0" } {
331
				set has_projects 1
332
				tputs	"<table border=\"0\">"
333
			}
334
			tputs	"<tr>"
335
			tputs	"	<th><a href=\"/koakuma/project/$name\">$name</a></th>"
336
			tputs	"	<td>[html_escape "$description"]</td>"
337
			tputs	"</tr>"
338
		}
339
		close_projects
340
		if { "$has_projects" == "1" } {
341
			tputs	"</table>"
342
		} else {
343
			tputs	"No projects have been added, yet."
344
		}
3 nishi 345
		add_toc "Tcl Information"
346
		tputs	"<table border=\"0\">"
347
		tputs	"	<tr>"
348
		tputs	"		<th>"
349
		tputs	"			Version"
350
		tputs	"		</th>"
351
		tputs	"		<td>"
352
		tputs	"			$tcl_version"
353
		tputs	"		</td>"
354
		tputs	"	</tr>"
355
		tputs	"	<tr>"
356
		tputs	"		<th>"
357
		tputs	"			Platform"
358
		tputs	"		</th>"
359
		tputs	"		<td>"
360
		tputs	"			$tcl_platform(os)/$tcl_platform(machine) $tcl_platform(osVersion)"
361
		tputs	"		</td>"
362
		tputs	"	</tr>"
13 nishi 363
		tputs	"	<tr>"
364
		tputs	"		<th>"
365
		tputs	"			tDOM version"
366
		tputs	"		</th>"
367
		tputs	"		<td>"
368
		tputs	"			$tdom_version"
369
		tputs	"		</td>"
370
		tputs	"	</tr>"
371
		tputs	"	<tr>"
372
		tputs	"		<th>"
373
		tputs	"			TclX version"
374
		tputs	"		</th>"
375
		tputs	"		<td>"
376
		tputs	"			$tclx_version"
377
		tputs	"		</td>"
378
		tputs	"	</tr>"
3 nishi 379
		tputs	"</table>"
380
		add_toc "Components"
381
		loop_components {
28 nishi 382
			add_toc2 "${name} (${genre})"
3 nishi 383
			if { [llength [info procs "${name}_info"]] > 0 } {
384
				${name}_info
385
			}
386
		}
387
 
388
		rputs ""
389
		start_html "Main" 1
390
		rputs "$content"
391
		end_html 1
4 nishi 392
	} elseif { [regexp {^/rpc(/.*)?$} "$path"] } {
393
		regexp {^/rpc(/.*)?$} "$path" -> api
394
		set doc [dom createDocumentNode]
5 nishi 395
		$doc appendFromScript {
396
			keyVersion {valueString "$KOAKUMA_VERSION"}
397
		}
4 nishi 398
		if { "$api" == "" || "$api" == "/" } {
5 nishi 399
			rputs ""
400
			rputs "[$doc asJSON]"
12 nishi 401
		} elseif { "$api" == "/launch-job" } {
402
			if { [catch {dom parse -json "$data" clidoc}] } {
403
				rputs "Status: 400 Bad Request"
404
				$doc appendFromScript {
405
					keyError {valueString "Bad JSON"}
406
				}
407
			} else {
18 nishi 408
				set projname "[regsub -all { } "[$clidoc selectNodes "string(/name)"]" "-"]"
12 nishi 409
				set builddesc "[$clidoc selectNodes "string(/description)"]"
410
				if { "$projname" == "" || "$builddesc" == "" } {
411
					rputs "Status: 400 Bad Request"
412
					$doc appendFromScript {
413
						keyError {valueString "Required field missing"}
414
					}
415
				} else {
416
					set has_name 0
13 nishi 417
					set use_vcs ""
418
					set use_vcs_url ""
12 nishi 419
					open_projects
420
					scan_projects {
421
						upvar 1 has_name has_name
422
						upvar 1 projname projname
13 nishi 423
						upvar 1 use_vcs use_vcs
424
						upvar 1 use_vcs_url use_vcs_url
12 nishi 425
						if { "$name" == "$projname" } {
426
							set has_name 1
13 nishi 427
							set use_vcs "$vcs"
428
							set use_vcs_url "$vcs_url"
12 nishi 429
							break
430
						}
431
					}
432
					close_projects
433
					if { $has_name == 0 } {
434
						rputs "Status: 400 Bad Request"
435
						$doc appendFromScript {
436
							keyError {valueString "Project does not exist"}
437
						}
438
					} else {
13 nishi 439
						set cont 1
440
						if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"] } {
441
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock" "r"]
442
							set readpid "[gets $fid]"
443
							close $fid
444
							if { [file exists "/proc/$readpid"] } {
445
								set cont 0
446
								rputs "Status: 403 Forbidden"
447
								$doc appendFromScript {
448
									keyError {valueString "Other building process has been running"}
449
								}
450
							}
451
						}
452
						if { $cont == 1 } {
453
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "r"]
454
							set count [expr [gets $fid] + 1]
455
							close $fid
456
 
457
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "w"]
458
							puts $fid "$count"
459
							close $fid
460
 
461
							set count "[format %08s "$count"]"
462
 
463
							set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun" "w"]
464
							puts $fid "[clock seconds]"
465
							close $fid
466
 
467
							file mkdir "@@PREFIX@@/lib/koakuma/db/data/$projname/build-$count"
468
 
469
							set pid [fork]
470
							if { $pid } {
471
								set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock" "w"]
472
								puts $fid "$pid"
473
								close $fid
474
							} else {
475
								set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build-$count/log" "w"]
476
								set fail 0
477
 
478
								dup $fid stdout
479
								dup $fid stderr
480
 
481
								puts "===== Checkout"
482
								puts "Using VCS: $use_vcs"
483
								if { [llength [info procs "${use_vcs}_repository"]] == 0 } {
484
									puts "Component internal failure"
485
									set fail 1
486
								} else {
487
									cd "@@PREFIX@@/lib/koakuma/db/data/$projname"
488
									if { [${use_vcs}_repository "$use_vcs_url" "workspace"] } {
489
										puts "Checkout failure"
490
										set fail 1
491
									}
492
								}
493
								if { $fail == 0 } {
494
									puts "===== Build"
495
									cd "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace"
496
									if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace/Koakumafile"] } {
497
										if { [catch {
498
											namespace eval koakumafile {
499
												source "@@PREFIX@@/lib/koakuma/db/data/$projname/workspace/Koakumafile"
500
											}
501
											koakumafile::run "$projname"
502
										}] } {
503
											puts "Failed to run Koakumafile"
504
											set fail 1
505
										}
506
									} else {
507
										puts "Nothing to do"
508
									}
509
								}
510
								if { $fail == 0 } {
511
									puts "Build successful"
512
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun" "w"]
513
									puts $fidsuc "[clock seconds]"
514
									close $fidsuc
515
 
516
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "r"]
517
									set sucbul [gets $fidsuc]
518
									close $fidsuc
519
 
520
									set fidsuc [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "w"]
521
									puts $fidsuc "[expr $sucbul + 1]"
522
									close $fidsuc
523
								}
524
 
525
								close $fid
526
 
527
								file delete "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"
528
								exit 0
529
							}
530
						}
12 nishi 531
					}
532
				}
533
			}
534
			rputs ""
535
			rputs "[$doc asJSON]"
5 nishi 536
		} elseif { "$api" == "/create-project" } {
537
			if { [catch {dom parse -json "$data" clidoc}] } {
538
				rputs "Status: 400 Bad Request"
539
				$doc appendFromScript {
540
					keyError {valueString "Bad JSON"}
541
				}
542
			} else {
18 nishi 543
				set projname "[regsub -all { } "[$clidoc selectNodes "string(/name)"]" "-"]"
5 nishi 544
				set projdescription "[$clidoc selectNodes "string(/description)"]"
6 nishi 545
				set projvcs "[$clidoc selectNodes "string(/vcs)"]"
5 nishi 546
				set url "[$clidoc selectNodes "string(/url)"]"
6 nishi 547
				if { "$projname" == "" || "$projdescription" == "" || "$projvcs" == "" || "$url" == "" } {
5 nishi 548
					rputs "Status: 400 Bad Request"
549
					$doc appendFromScript {
550
						keyError {valueString "Required field missing"}
551
					}
552
				} else {
553
					set has_vcs 0
6 nishi 554
					set has_name 0
5 nishi 555
					loop_components {
556
						upvar 1 has_vcs has_vcs
6 nishi 557
						upvar 1 projvcs projvcs
558
						if { "$name" == "$projvcs" && "$genre" == "VCS" } {
5 nishi 559
							set has_vcs 1
560
							break
561
						}
562
					}
6 nishi 563
					open_projects
564
					scan_projects {
565
						upvar 1 has_name has_name
566
						upvar 1 projname projname
567
						if { "$name" == "$projname" } {
568
							set has_name 1
569
							break
570
						}
571
					}
572
					close_projects
5 nishi 573
					if { $has_vcs == 0 } {
574
						rputs "Status: 400 Bad Request"
575
						$doc appendFromScript {
576
							keyError {valueString "Not a valid VCS"}
577
						}
6 nishi 578
					} elseif { $has_name == 1 } {
579
						rputs "Status: 400 Bad Request"
580
						$doc appendFromScript {
581
							keyError {valueString "Project already exists"}
582
						}
5 nishi 583
					} else {
584
						open_projects
6 nishi 585
						set xml "[readall_db]"
586
						set xmldoc [dom parse "$xml"]
587
						set root [$xmldoc documentElement]
588
						$root appendFromScript {
589
							keyProject {
590
								keyName {valueString "$projname"}
591
								keyDescription {valueString "$projdescription"}
592
								keyVCS {valueString "$projvcs"}
593
								keyURL {valueString "$url"}
594
							}
595
						}
596
						write_db "[$xmldoc asXML]"
11 nishi 597
						file mkdir "@@PREFIX@@/lib/koakuma/db/data/$projname"
13 nishi 598
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/buildcount" "w"]
599
						puts $fid "0"
600
						close $fid
601
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "w"]
602
						puts $fid "0"
603
						close $fid
5 nishi 604
						close_projects
605
					}
606
				}
607
			}
608
			rputs ""
609
			rputs "[$doc asJSON]"
610
		} else {
4 nishi 611
			$root appendFromScript {
5 nishi 612
				keyError {valueString "No such endpoint"}
4 nishi 613
			}
5 nishi 614
			rputs "Status: 404 Not Found"
615
			rputs ""
4 nishi 616
			rputs "[$doc asJSON]"
617
		}
3 nishi 618
	} elseif { [regexp {^/project/[^/]+.*$} "$path"] } {
12 nishi 619
		regexp {^/project/([^/]+)(.*)$} "$path" -> projname projpath
3 nishi 620
		open_projects
621
		set has_project [project_exists "$projname"]
622
		close_projects
623
 
624
		if { "$has_project" != "" } {
12 nishi 625
			if { "$projpath" == "" || "$projpath" == "/" } {
33 nishi 626
				set what "This is the project page."
12 nishi 627
				add_toc "Description"
628
				tputs "[html_escape "$has_project"]"
629
				add_toc "Details"
630
				tputs	"<table border=\"0\">"
631
				tputs	"	<tr>"
632
				tputs	"		<th>"
15 nishi 633
				tputs	"			Status"
12 nishi 634
				tputs	"		</th>"
635
				tputs	"		<td>"
15 nishi 636
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock"] } {
18 nishi 637
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/build.lock" "r"]
15 nishi 638
					if { [file exists "/proc/[gets $fid]"] } {
639
						tputs "Running"
640
					} else {
641
						tputs "Idle"
642
					}
12 nishi 643
					close $fid
644
				} else {
15 nishi 645
					tputs "Idle"
12 nishi 646
				}
647
				tputs	"			"
648
				tputs	"		</td>"
649
				tputs	"	</tr>"
650
				tputs	"	<tr>"
651
				tputs	"		<th>"
15 nishi 652
				tputs	"			Last run"
14 nishi 653
				tputs	"		</th>"
654
				tputs	"		<td>"
15 nishi 655
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun"] } {
656
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastrun" "r"]
657
					set date "[clock format "[gets $fid]" -format "%a %b %d %H:%M:%S %Z %Y"]"
14 nishi 658
					close $fid
15 nishi 659
					tputs "$date"
14 nishi 660
				} else {
15 nishi 661
					tputs "No builds yet"
14 nishi 662
				}
663
				tputs	"			"
664
				tputs	"		</td>"
665
				tputs	"	</tr>"
666
				tputs	"	<tr>"
667
				tputs	"		<th>"
12 nishi 668
				tputs	"			Last successful run"
669
				tputs	"		</th>"
670
				tputs	"		<td>"
671
				if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun"] } {
672
					set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/lastsuccessfulrun" "r"]
673
					set date "[clock format "[gets $fid]" -format "%a %b %d %H:%M:%S %Z %Y"]"
674
					close $fid
675
					tputs "$date"
676
				} else {
677
					tputs "No successful builds yet"
678
				}
679
				tputs	"			"
680
				tputs	"		</td>"
681
				tputs	"	</tr>"
13 nishi 682
				set builds [lsort -ascii [glob -nocomplain "@@PREFIX@@/lib/koakuma/db/data/$projname/build-*"]]
683
				if { [llength $builds] > 0 } {
684
					tputs	"	<tr>"
685
					tputs	"		<th>"
686
					tputs	"			Successful builds"
687
					tputs	"		</th>"
688
					tputs	"		<td>"
689
					if { [file exists "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild"] } {
690
						set fid [open "@@PREFIX@@/lib/koakuma/db/data/$projname/successbuild" "r"]
691
						tputs "[format %.2f [expr [gets $fid] / [llength $builds] * 100]]%"
692
						close $fid
693
					}
694
					tputs	"			"
695
					tputs	"		</td>"
696
					tputs	"	</tr>"
697
				}
12 nishi 698
				tputs	"</table>"
13 nishi 699
 
700
				set builds [lsort -ascii [glob -nocomplain "@@PREFIX@@/lib/koakuma/db/data/$projname/build-*"]]
701
				if { [llength $builds] > 0 } {
702
					add_toc "Last build log"
703
					set lastbuild "[lindex $builds [expr [llength $builds] - 1]]"
704
					set fid [open "$lastbuild/log" "r"]
705
					tputs "<pre>"
706
					while { [gets $fid line] >= 0 } {
707
						tputs "[html_escape "$line"]"
708
					}
709
					tputs "</pre>"
710
					close $fid
711
				}
12 nishi 712
 
713
				rputs ""
714
				start_html "Project: $projname" 1
715
				rputs "$content"
716
				end_html 1
11 nishi 717
			} else {
12 nishi 718
				tputs "I could not find the endpoint you were finding."
719
 
720
				rputs "Status: 404 Not Found"
721
				rputs ""
722
				start_html "Project: $projname" 1
723
				rputs "$content"
724
				end_html 1
11 nishi 725
			}
3 nishi 726
		} else {
727
			tputs "I could not find the project you were finding."
728
 
729
			rputs "Status: 404 Not Found"
730
			rputs ""
731
			start_html "Not Found" 0
732
			rputs "$content"
733
			end_html 0
734
		}
735
	} else {
736
		tputs "I could not find the content you were finding."
737
 
738
		rputs "Status: 404 Not Found"
739
		rputs ""
740
		start_html "Not Found" 0
741
		rputs "$content"
742
		end_html 0
743
	}
744
}] } {
745
	crash "Could not render the HTML"
746
} else {
747
	puts "$result"
748
}
749
exiting 0