Subversion Repositories Koakuma

Rev

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