Subversion Repositories Koakuma

Rev

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