アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Jenkins から Common Lisp で Skype へ通知

こんにちは、tahara です。

いいかげん Jenkins くら導入しようよ、ということで導入しました(ステージングへの cap が Asset Pipeline の precompile で時間がかかるようになって、手で cap するのがめんどうになったのが本当の理由です)。

Jenkins のセットアップは色々なとこに情報があったのですんなりいきました。 でも、Jenkins から Skype への通知でてこずりました。 Skype Plugin があるので、これを使えば問題ないよね、と思っていたのですが、一日格闘しても動かせず。

Skype Plugin をあきらめ Common Lisp 経由で Skype を使うことにしたら、すんなりできました。 Jenkins → ビルドシェル → Common LispD-BusSkype という流れになります。

まず Linux上で動くSkype用のbotを作る方法 - muddy brown thang を参照して Skype を Xvfb で動くようにします。

sudo vi /etc/init.d/skype

#!/bin/bash
#
# Init file for daemonized Skype service
#
### BEGIN INIT INFO
# Provides: skype
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# X-Interactive:     false
# Short-Description: starts the skype on Xvfb
# Description:       starts skype using start-stop-daemon
### END INIT INFO

# sudo update-rc.d skype defaults

DAEMON=/usr/bin/skype
DAEMON_USER=jenkins
PIDFILE=/var/run/skype.pid
XSERVERNUM=20
XAUTHFILE=/var/run/skype/Xauthority
LOGFILE=/var/log/skype/error.log
DBPATH=/var/lib/jenkins/.Skype
XAUTHPROTO=.
USERNAME=your-skype-account
PASSWORD=your-skype-password
NAME=skype

test -x $DAEMON || exit 0

set -e

. /lib/lsb/init-functions

RETVAL=0

dircheck() {
    if [ ! -d `dirname "$1"` ]; then
        echo "`dirname \"$1\"` does not exist"
        return 1
    else
        return 0
    fi
}

start() {
    echo -n "Starting $DESC: "
    if ! dircheck "$LOGFILE" || ! dircheck "$XAUTHFILE"; then
        echo -n "ng. $LOGFILE or $XAUTHFILE"
        echo
        RETVAL=1
        return
    fi
    MCOOKIE=`mcookie` && \
        sudo -u "$DAEMON_USER" env XAUTHORITY=$XAUTHFILE sh -c "xauth add \":$XSERVERNUM\" \"$XAUTHPROTO\" \"$MCOOKIE\" >> \"$LOGFILE\" 2>&1" && \
        sudo -u "$DAEMON_USER" env XAUTHORITY=$XAUTHFILE sh -c "Xvfb :$XSERVERNUM -screen 0 800x600x8 -nolisten tcp >> \"$LOGFILE\" 2>&1 & echo \$!" >"$PIDFILE" &&
    sleep 3 && \
        (sudo -u "$DAEMON_USER" env DISPLAY=:$XSERVERNUM XAUTHORITY=$XAUTHFILE SKYPE="$DAEMON" sh -c "echo \"$USERNAME $PASSWORD\" | nohup \"$DAEMON\" --dbpath=\"$DBPATH\" --pipelogin &") >> "$LOGFILE" 2>&1 && \
        (echo -n "ok" && [ -d /var/lock/subsys ] && touch /var/lock/subsys/skype || true) \
        || (RETVAL=$?; kill -TERM `cat $PIDFILE`; echo -n "ng")
}

stop() {
    echo -n "Stopping" "skype"
    if [ -e "$PIDFILE" ]; then
        kill -TERM `cat $PIDFILE` && \
            rm -f $PIDFILE && \
            if [ -d /var/lock/subsys ]; then rm -f /var/lock/subsys/skype; fi && \
            echo -n "ok" || echo -n "ng"
    else
        echo "ng. maybe not running."
        RETVAL=1
    fi
}

usage() {
    echo "Usage: $NAME {start|stop|restart}"
}

case $1 in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        stop
        start
        ;;
    *)
        usage
        RETVAL=255
        ;;
esac

exit $RETVAL
$ sudo chmod +x /etc/init.d/skype
$ sudo mkdir /var/log/skype
$ sudo chown jenkins:jenkins /var/log/skype
$ sudo mkdir /var/run/skype
$ sudo chown jenkins:jenkins /var/run/skype
$ sudo update-rc.d skype defaults
$ sudo service skype start

Common LispQuicklisp のセットアップ。

$ sudo apt-get install sbcl
$ curl -O http://beta.quicklisp.org/quicklisp.lisp
$ sbcl --load quicklisp.lisp
* (quicklisp-quickstart:install)
* (ql:add-to-init-file)

Common Lisp から Skype を使うコードです。

vi /var/lib/jenkins/skype/skype.lisp

(load "~/quicklisp/setup.lisp")

(let* ((*standard-output* (make-broadcast-stream))
       (*error-output* *standard-output*))
  (ql:quickload :dbus))

(defpackage :skype
  (:use :cl :dbus))

(in-package :skype)

(defparameter *chat-id* "#xxxxxx/$xxxxxx;3xxxx9999xxxxx" "Skype チャットの ID")

(defun message (chat-id message)
  (with-open-bus (bus (session-server-addresses))
    (with-introspected-object (skype
                               bus
                               "/com/Skype"
                               "com.Skype.API")
      (flet ((skype (command)
               (print (skype "com.Skype.API" "Invoke" command))))
        (skype "NAME FromCommonLisp")
        (skype "PROTOCOL 8")
        (skype (format nil "CHATMESSAGE ~a ~a" chat-id message))))))

(let ((message (second sb-ext:*posix-argv*)))
  (message *chat-id* message))

Common Lisp からは D-Bus 経由のため、 環境変数 DBUS_SESSION_BUS_ADDRESS の設定が必要になります(cron と gconftool-2 について - AOTRの日記)。

/etc/init.d/skype の XSERVERNUM に 20 を指定しているので、次のようにすればよさそうです。

$ source `ls ~/.dbus/session-bus/*-20`
$ export DBUS_SESSION_BUS_ADDRESS

これで sbcl すれば skype でメッセージが送れるはずです。

$ sbcl --script /var/lib/jenkins/skype/skype.lisp 'テストメッセージ'

あ、Skype を Xvfb で動かす前に一度普通に動かして Common Lisp からパブリップ API を使えるよう許可しておく必要があります。 次のような感じです。

$ ssh -X jenkins@example.com
$ skype &
$ echo $DISPLAY   # DISPLAY 番号を確認 localhost:10.0
$ source `ls ~/.dbus/session-bus/*-10`
$ export DBUS_SESSION_BUS_ADDRESS
$ sbcl --noinform --disable-debugger --load /var/lib/jenkins/skype/skype.lisp 'テストメッセージ'

最後に Jenkins のビルドスクリプトです。 cap をして Skype で通知します。 git のコミットメッセージなども付けるようにします。

ビルドスクリプトリポジトリに入れておくのがベストプラクテスとのことですので vi $YOURE_RAILS_WORKSPACE/config/jenkins/build.sh

#!/bin/bash

# cap
rvm_path=/home/deployer/.rvm /home/deployer/.rvm/bin/rvm-shell 'ruby-1.9.3@iko-yo-rails3' -c 'cap staging deploy:migrations'

# 20 は /etc/init.d/skype の XSERVERNUM=20
source `ls ~/.dbus/session-bus/*-20`
export DBUS_SESSION_BUS_ADDRESS
sbcl --script /var/lib/jenkins/skype/skype.lisp "(ninja) Capistrano: いこーよを 確認(ステージング) 環境 http://outing.actindi.net/ にデプロイしました (h)`git log --pretty='%n%s%n%b%n%an' HEAD...HEAD~`"

Jenkins の "ビルド" > "シェルの実行" > "シェルスクリプト" に config/jenkins/build.sh を指定で、できあがり。

最後に、弊社ではエンジニア募集しています。お気軽にお問い合わせください。