Subversion Repositories Koakuma

Rev

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