Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11724581
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/Core.tcl b/Core.tcl
index 3e51189..2d2a924 100644
--- a/Core.tcl
+++ b/Core.tcl
@@ -1,237 +1,288 @@
#
# TCL helpers
#
#Determines if $proc exists
proc proc_exists {proc} {
expr {[info procs $proc] == $proc}
}
#
# Trivial procs
#
#Determines if $v is a number
proc isnumber {v} {
return [expr {! [catch {expr {int($v)}}]}]
}
#Returns "s" if $count implies a plural
#TODO: keep this method for French (ie NOT adjusting values for English)
# and grab the plural proc from wiki.tcl.tk for English.
proc s {count} {
if {$count >= 2 || $count <= 2} {return "s"}
}
#
# Strings
#
#Completes $text by spaces or $char so the returned text length is $len
proc completestring {text len {char " "}} {
set curlen [string length $text]
if {$curlen >= $len} {
return $text
}
if {[string length $char] < 2} {
append text [string repeat $char [expr $len - $curlen]]
} {
while {[string length $text] < $len} {
append text $char
}
string range $text 0 $len+1
}
}
proc completestringright {text len {char " "}} {
set curlen [string length $text]
if {$curlen >= $len} {
return $text
}
set completedtext [string range [completestring $text $len $char] $curlen end]
append completedtext $text
}
## Prepends 0s to a number
##
## @param $number The number to zerofill
## @param $digits The number length
## @return The zerofilled number
proc zerofill {number digits} {
format "%0${digits}d" $number
}
#
# SQL
#
#Reconnects to the sql & sql2 server
proc sqlrehash {} {
global sql
catch {
sql disconnect
sql2 disconnect
}
sql connect $sql(host) $sql(user) $sql(pass)
sql2 connect $sql(host) $sql(user) $sql(pass)
sql selectdb $sql(database)
sql2 selectdb $sql(database)
}
#Escape a string to use as sql query parameter
proc sqlescape {data} {
#\ -> \\
#' -> \'
string map {"\\" "\\\\" "'" "\\'"} $data
}
#Gets the first item of the first row of a sql query (scalar results)
proc sqlscalar {sql} {
lindex [lindex [sql $sql] 0] 0
}
#Adds specified data to specified SQL table
proc sqladd {table {data1 ""} {data2 ""}} {
if {$data1 == ""} {
set fields ""
#Prints field to fill
foreach row [sql "SHOW COLUMNS FROM $table"] {
lappend fields [lindex $row 0]
}
return $fields
}
if {$data2 == ""} {
set sql "INSERT INTO $table VALUES ("
set data $data1
} {
set sql "INSERT INTO $table (`[join $data1 "`, `"]`) VALUES ("
set data $data2
}
set first 1
foreach value $data {
if {$first == 1} {set first 0} {append sql ", "}
append sql "'[sqlescape $value]'"
}
append sql ")"
sql $sql
}
# Gets the value of the AUTOINCREMENT column for the last INSERT
#
# @return the last value of the primary key
proc sqllastinsertid {} {
sql "SELECT LAST_INSERT_ID()"
}
#
# Registry
#
#Gets, sets, deletes or increments a registry value
proc registry {command key {value ""}} {
switch -- $command {
"add" {
sqladd registry "data value" [list $key $value]
}
"get" {
sqlscalar "SELECT value FROM registry WHERE `data` = '$key'"
}
"set" {
sqlreplace registry "data value" [list $key $value]
}
"del" {
registry delete $key $value
}
"delete" {
set sql "DELETE FROM registry WHERE `data` = '$key'"
putdebug $sql
sql $sql
}
"incr" {
set current [registry get $key]
if {$value == ""} {set term 1}
if {$current == ""} {
registry set $key $term
} {
registry set $key [incr current $term]
}
}
default {
error "unknown subcommand: must be add, get, set, incr or delete"
}
}
}
#
# Users information
#
#Gets user_id from a username, idx or user_id
proc getuserid {data} {
if {$data == ""} {
return
} elseif {![isnumber $data]} {
#username -> user_id
sql "SELECT user_id FROM users WHERE username = '[sqlescape $data]'"
} elseif {$data < 1000} {
#idx -> user_id
getuserid [idx2hand $data]
} else {
#user_id -> user_id (or "" if not existing)
sql "SELECT user_id FROM users WHERE user_id = $data"
}
}
#
# Text parsing
#
proc geturls {text} {
#Finds the first url position
set pos -1
foreach needle "http:// https:// www. youtu.be" {
set pos1 [string first $needle $text]
if {$pos1 != -1 && ($pos == -1 || $pos1 < $pos)} {
set pos $pos1
}
}
#No URL found
if {$pos == -1} {return}
#URL found
set pos2 [string first " " $text $pos]
if {$pos2 == -1} {
#Last URL to be found
string range $text $pos end
} {
#Recursive call to get other URLs
concat [string range $text $pos $pos2-1] [geturls [string range $text $pos2+1 end]]
}
}
#Reads specified URL and returns content
proc geturltext {url {trim 1}} {
package require http
if {[string range [string tolower $url] 0 5] == "https:"} {
package require tls
http::register https 443 tls::socket
}
set fd [http::geturl $url]
set text [http::data $fd]
http::cleanup $fd
if $trim {
string trim $text
} {
return $text
}
}
+
+proc numeric2ordinal {n} {
+ switch $n {
+ 1 { return first }
+ 2 { return second }
+ 3 { return third }
+ 5 { return fifth }
+ 8 { return eight }
+ 9 { return ninth }
+ #todo: ve -> f / y -> ie
+ 12 { return twelfth }
+ default {
+ set ordinal "[numeric2en $n]th"
+ set m [expr $n % 10]
+ if {$m == 0} {
+ return [string map "yth ieth" $ordinal]
+ }
+ if {$n < 20} { return $ordinal }
+ if {$n > 100} { return "${n}th" }
+ return "[numeric2en [expr $n - $m]]-[numeric2ordinal $m]"
+ }
+ }
+}
+
+proc numeric2en {n {optional 0}} {
+ #---------------- English spelling for integer numbers
+ if {[catch {set n [expr $n]}]} {return $n}
+ if {$optional && $n==0} {return ""}
+ array set dic {
+ 0 zero 1 one 2 two 3 three 4 four 5 five 6 six 7 seven
+ 8 eight 9 nine 10 ten 11 eleven 12 twelve
+ }
+ if [info exists dic($n)] {return $dic($n)}
+ foreach {value word} {1000000 million 1000 thousand 100 hundred} {
+ if {$n>=$value} {
+ return "[numeric2en $n/$value] $word [numeric2en $n%$value 1]"
+ }
+ } ;#--------------- composing between 13 and 99...
+ if $n>=20 {
+ set res $dic([expr $n/10])ty
+ if $n%10 {append res -$dic([expr $n%10])}
+ } else {
+ set res $dic([expr $n-10])teen
+ } ;#----------- fix over-regular compositions
+ regsub "twoty" $res "twenty" res
+ regsub "threet" $res "thirt" res
+ regsub "fourty" $res "forty" res
+ regsub "fivet" $res "fift" res
+ regsub "eightt" $res "eight" res
+ set res
+} ;#RS
\ No newline at end of file
diff --git a/Nasqueron/Gerrit.tcl b/Nasqueron/Gerrit.tcl
index a3ff2e3..25d980c 100644
--- a/Nasqueron/Gerrit.tcl
+++ b/Nasqueron/Gerrit.tcl
@@ -1,145 +1,292 @@
# .tcl source scripts/Nasqueron/Gerrit.tcl
+package require json
+
bind dcc - gerrit dcc:gerrit
+# Gerrit eventss are at the bottom of the file
+
#
# Gerrit helper methods
#
namespace eval ::ssh:: {
proc set_agent {{tryToStartAgent 1}} {
global env
set file $env(HOME)/bin/ssh-agent-session
if {![file exists $file]} {
putcmdlog "Can't find SSH agent information - $file doesn't exist."
}
#TCSH rules -> set through env array
set fp [open $file]
fconfigure $fp -buffering line
gets $fp line
while {$line != ""} {
foreach "command variable value" [split $line] {}
if {$command == "setenv"} {
set env($variable) [string range $value 0 end-1]
}
gets $fp line
}
close $fp
#Checks if agent exists
- if {[string first ssh-agent [get_pid $env(SSH_AGENT_PID)]] == -1} {
+ if {[string first ssh-agent [get_processname $env(SSH_AGENT_PID)]] == -1} {
putcmdlog "SSH agent isn't running"
if {$tryToStartAgent} {
putdebug "Trying to launch SSH agent..."
exec -- ssh-agent -c | grep -v echo > $env(HOME)/bin/ssh-agent-session
if {![add_key]} {
# TODO: send a note to relevant people key should manually added
# something like sendNoteToGroup $username T "Key sould be manually added"
}
set_agent 0
}
}
}
proc add_key {{key ""}} {
if {$key == ""} { set key [registry get ssh.key] }
if {$key != ""} {
catch { exec -- ssh-add $key } result
putdebug "Adding SSH key: $result"
expr [string first "Identity added" $result] > -1
} {
return 0
}
}
- proc get_pid {pid} {
+ proc get_processname {pid} {
set processes [exec ps xw]
foreach process [split $processes \n] {
set current_pid [lindex $process 0]
set command [lrange $process 4 end]
if {$pid == $current_pid} { return $command }
}
}
# Gets appropriate connection parameter
#
# @param $server The server to connect
# @return The server domain name, prepent by SSH options
proc get_connection_parameter {server} {
#TODO: return -p 29418 username@review.anothersite.com when appropriate instead to create SSH config alias
return $server
}
}
namespace eval ::gerrit:: {
## Queries a Gerrit server
##
## @param $query The query to send
## @seealso http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-query.html
proc query {server query} {
exec -- ssh [ssh::get_connection_parameter $server] gerrit query $query
}
## Launches a socket to monitor Gerrit events in real time and initializes events.
## This uses a node gateway.
##
## @seealso http://gerrit-documentation.googlecode.com/svn/Documentation/2.5/cmd-stream-events.html
proc setup_stream_events {server} {
control [connect [registry get gerrit.$server.streamevents.host] [registry get gerrit.$server.streamevents.port]] gerrit::listen:stream_event
}
proc listen:stream_event {idx text} {
global buffers
if {$text == ""} {
putdebug "Connection to Gerrit stream-events gateway closed."
if [info exists buffers($idx)] { unset buffers($idx) }
} elseif {$text == "--"} {
# Process gerrit event
set event [json::json2dict $buffers($idx)]
set buffers($idx) ""
- registry incr gerrit.stats.type.[dict get $event type]
+ set type [dict get $event type]
+ #todo: handle here multiservers
+ callevent wmreview $type $event
} {
append buffers($idx) $text
}
return 0
}
+
+ # Registers a new event
+ #
+ proc event {type callback} {
+ dict lappend gerrit::events $type $callback
+ }
+
+ # Calls an event proc
+ #
+ # @param $type the Gerrit type
+ # @param $message a dict representation of the JSON message sent by Gerrit
+ proc callevent {server type message} {
+ if [dict exists $gerrit::events all] {
+ foreach procname [dict get $gerrit::events all] {
+ $procname $server $type $message
+ }
+ }
+
+ if [dict exists $gerrit::events $type] {
+ # Determines the proc arguments from the Gerrit message type
+ switch $type {
+ "patchset-created" { set params "change patchSet uploader" }
+ "change-abandoned" { set params "change patchSet abandoner" }
+ "change-restored" { set params "change patchSet restorer" }
+ "change-merged" { set params "change patchSet submitter" }
+ "comment-added" { set params "change patchSet author approvals comment" }
+ "ref-updated" { set params "submitter refUpdate" }
+
+ default {
+ putdebug "Unknown Gerrit type in gerrit::callevent: $type"
+ return
+ }
+ }
+
+ # Gets the values of the proc arguments
+ set args $server
+ foreach param $params {
+ if [dict exists $message $param] {
+ lappend args [dict get $message $param]
+ } {
+ lappend args ""
+ }
+ }
+
+ # Calls callbacks procs
+ foreach procname [dict get $gerrit::events $type] {
+ $procname {*}$args
+ }
+ }
+ }
+
+ # The events callback methods
+ set events {}
+
+ # # # # # # # # # # # # # # #
+
+ # Handles statistics
+ proc stats {server type message} {
+ registry incr gerrit.stats.type.$type
+ }
+
+ # Announces a call
+ proc debug {server type message} {
+ putdebug "$server -> $type +1"
+ }
+
+ proc onNewPatchset {server change patchset uploader} {
+ # Gets relevant variables from change, patchset & uploader
+ set who [dict get $uploader name]
+ foreach var "project branch topic subject url" {
+ if [dict exists $change $var] {
+ set $var [dict get $change $var]
+ } {
+ set $var ""
+ }
+ }
+ set patchsetNumber [dict get $patchset number]
+
+ #IRC notification
+ if {$server == "wmreview" && $who != "L10n-bot"} {
+ set message "\[$project] $who uploaded a [numeric2ordinal $patchsetNumber] patchset to change '$subject'"
+ if {$branch != "master"} { append message " in branch $branch" }
+ append message " - $url"
+ }
+ #if {[string range $project 0 9] == "mediawiki/"} {
+ # puthelp "PRIVMSG #mediawiki :$message"
+ #}
+ }
+
+ proc onCommentAdded {server change patchset author approvals comment} {
+ # Gets relevant variables from change, patchset & uploader
+ set who [dict get $author name]
+ foreach var "project branch topic subject url" {
+ if [dict exists $change $var] {
+ set $var [dict get $change $var]
+ } {
+ set $var ""
+ }
+ }
+
+ #IRC notification
+ if {$server == "wmreview" && $who != "jenkins-bot"} {
+ set verbs {
+ "\0034puts a veto on\003"
+ "\0034suggests improvement on\003"
+ "comments"
+ "\0033approves\003"
+ "\0033definitely approves\003"
+ }
+ set CR 0
+ if {$approvals != ""} {
+ foreach approval $approvals {
+ if {[dict get $approval type] == "CRVW"} {
+ set CR [dict get $approval value]
+ break
+ }
+ }
+ }
+ set verb [lindex $verbs [expr $CR + 2]]
+ set message "\[$project] $who $verb change '$subject'"
+ if {$comment != ""} {
+ if {[strlen $message] > 160} {
+ append message ": '[string range $comment 0 158]...'"
+ } {
+ append message ": '$comment'"
+ }
+ }
+ append message " - $url"
+ if {[string range $project 0 9] == "mediawiki/" && ($comment != "" || $CR < 0)} {
+ #putdebug "OK -> $message"
+ puthelp "PRIVMSG #mediawiki :$message"
+ } {
+ putdebug "Not on IRC -> $message"
+ }
+ }
+ }
}
#
# Gerrit binds
#
proc dcc:gerrit {handle idx arg} {
switch $arg {
"" {
putdcc $idx "Usage: .gerrit <query>"
putdcc $idx "Cmds: .gerrit stats"
return 0
}
"stats" {
foreach row [sql "SELECT SUBSTRING(data, 19), value FROM registry WHERE LEFT(data, 18) = 'gerrit.stats.type.'"] {
putdcc $idx $row
}
return 1
}
default {
# TODO: support several Gerrit servers
set server [registry get gerrit.defaultserver]
putdcc $idx [gerrit::query $server $arg]
putcmdlog "#$handle# gerrit ..."
return 0
}
}
}
#
# Initialization code
#
-ssh::set_agent
\ No newline at end of file
+ssh::set_agent
+
+gerrit::event all gerrit::stats
+gerrit::event all gerrit::debug
+gerrit::event patchset-created gerrit::onNewPatchset
+gerrit::event comment-added gerrit::onCommentAdded
diff --git a/Tech.tcl b/Tech.tcl
index ffeceb3..44888aa 100644
--- a/Tech.tcl
+++ b/Tech.tcl
@@ -1,181 +1,181 @@
unbind dcc n rehash *dcc:rehash
bind dcc T rehash dcc:rehash
bind dcc T s dcc:source
unbind dcc n tcl *dcc:tcl
bind dcc T tcl dcc:tcl
bind dcc T sql dcc:sql
bind dcc T sql? dcc:sql?
bind dcc T sql! dcc:sql!
bind dcc T sql1 dcc:sql1
bind dcc T sql1? dcc:sql1?
bind dcc T sql1! dcc:sql1!
bind dcc T sqlrehash dcc:sqlrehash
bind dcc T tcldoc dcc:tcldoc
#
# Helpers methods
#
#Logs a timestamped message to the specified file
proc log {logfile handle message} {
global username
set fd [open "logs/$username/$logfile.log" a]
puts $fd "\[[unixtime]\] <$handle> $message"
close $fd
}
#Prints a message to all the techs
-proc putdebug {{message ""}} {
- if {$message == ""} {
+proc putdebug {{message d41d8cd98f00b204e98}} {
+ if {$message == "d41d8cd98f00b204e98"} {
global errorInfo
set message $errorInfo
}
foreach conn [dcclist CHAT] {
foreach "idx handle uhost type flags idle" $conn {}
#dccputchan 0 "(debug) $conn"
if [matchattr $handle T] {
putdcc $idx "\[DEBUG\] $message"
}
}
}
#
# Tech commands
#
#Disconnect SQL, then rehash (to prevent sql connect fatal errors)
proc dcc:rehash {handle idx arg} {
catch {
sql disconnect
sql2 disconnect
}
*dcc:rehash $handle $idx $arg
}
#Loads a script
proc dcc:source {handle idx arg} {
if {$arg == ""} {
putdcc $idx "Usage: .s <script> [script2 ...]"
return
}
foreach file $arg {
if ![sourcetry $file] {
putdcc $idx "Can't find script $file"
}
}
}
#Tries to load a script
proc sourcetry {file} {
global username
set scriptlist "$file $file.tcl scripts/$file scripts/$file.tcl scripts/$username/$file scripts/$username/$file.tcl"
foreach script $scriptlist {
if [file exists $script] {
source $script
return 1
}
}
return 0
}
#.tcl with tech.log logging
proc dcc:tcl {handle idx arg} {
#Logs every .tcl commands, except sql connect
#You should add here any line with password.
catch {
if ![string match "*sql*connect*" $arg] {
log tech $handle $arg
}
}
*dcc:tcl $handle $idx $arg
}
#
# SQL
#
#Reconnects to the MySQL main server (sql and sql2)
proc dcc:sqlrehash {handle idx arg} {
sqlrehash
return 1
}
#
# dcc:sql1 dcc:sql1? and dcc:sql1! are the main procedures
# They will be cloned for the 9 other connections command
#
#Executes a query
proc dcc:sql1 {handle idx arg} {
if {$arg == ""} {
putdcc $idx "Usage: .sql1 <query>"
return
}
#Executes the query and prints the query one row per line
set t1 [clock milliseconds]
if [catch {
foreach row [sql1 $arg] {
putdcc $idx $row
}
} err] {
putdcc $idx $err
}
#Warns after a long query
set delta_t [expr [clock milliseconds] - $t1]
if {$delta_t > 1999} {
putcmdlog "Fin de la requête SQL ($delta_t ms)."
}
#Logs the query
log sql $handle "sql1\t$arg"
}
#Dumps (SELECT * FROM <table>) a table
proc dcc:sql1! {handle idx arg} {
if {$arg == ""} {
putdcc $idx "Usage: .sql1! <table>"
return
}
dcc:sql1 $handle $idx "SELECT * FROM $arg"
}
#Without parameters, list the tables (SHOW TABLES)
#With a parameter, dump tables info (SHOW CREATE TABLE)
proc dcc:sql1? {handle idx arg} {
if {$arg == ""} {
dcc:sql1 $handle $idx "SHOW TABLES"
}
foreach table $arg {
dcc:sql1 $handle $idx "SHOW CREATE TABLE $table"
}
}
#Clones .sql1, .sql1? and .sql1! commands into .sql, .sql? and .sql!
proc dcc:sql {handle idx arg} [string map "sql1 sql" [info body dcc:sql1]]
proc dcc:sql? {handle idx arg} [string map "sql1 sql" [info body dcc:sql1?]]
proc dcc:sql! {handle idx arg} [string map "sql1 sql" [info body dcc:sql1!]]
proc sqlreplace {table {data1 ""} {data2 ""}} [string map {"INSERT INTO" "REPLACE INTO"} [info body sqladd]]
#Clones .sql1, .sql1? and .sql1! commands into .sql2, .sql3, ..., .sql10.
for {set i 2} {$i < 11} {incr i} {
bind dcc T sql$i dcc:sql$i
bind dcc T sql$i? dcc:sql$i?
bind dcc T sql$i! dcc:sql$i!
proc dcc:sql$i {handle idx arg} [string map "sql1 sql$i" [info body dcc:sql1]]
proc dcc:sql$i! {handle idx arg} [string map "sql1 sql$i" [info body dcc:sql1!]]
proc dcc:sql$i? {handle idx arg} [string map "sql1 sql$i" [info body dcc:sql1?]]
}
#
# Reference documentation
#
proc dcc:tcldoc {handle idx arg} {
putdcc $idx [exec -- grep $arg doc/tcl-commands.doc]
return 1
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Sep 18, 16:20 (3 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2991069
Default Alt Text
(19 KB)
Attached To
Mode
rVIPER ViperServ scripts
Attached
Detach File
Event Timeline
Log In to Comment