明日にはでっかい太陽が昇るかもしれません。

「覚悟」とは!! 暗闇の荒野に!!進むべき道を切り開く事だッ!

luajit に入門してみた

ことの始まり

仕事で Lua を使うことになりそう (というか、使う方向に持って行こうとしている) ので、 LuaJIT の使い勝手を事前に確認してみた。

C との IF が多くあるっぽいので、個別に binding を実装するととんでもないことになりそうなので、 LuaJIT の FFI を使ってみようと思ってのことである。

環境準備

Ubuntu 14.04 64bit ベースで build-essential のインストールは完了している前提。

### とりあえず git から取得
$ git clone http://luajit.org/git/luajit-2.0.git
### リリース版が良かったので、ブランチを切り替え
$ git checkout -b v2.0.5-release refs/tags/v2.0.5
### コンパイル (本番は arm だけど、動作確認なのでホスト向けに)
$ make
### インストール (動作確認なので、システムにはインストールしない)
$ make install PREFIX=~/path/to/workspace
### パスを設定
$ export PATH=~/path/to/workspace/bin:$PATH

C ライブラリの準備

/* コンパイルして libsample.so を生成する */
#include <stdio.h>

struct sample_data {
        char name[100];
        int type;
};

int sample_c_func(struct sample_data* data)
{
        printf("%s:%d name: %s, type: %d\n", __func__, __LINE__, data->name, data->type);
        return data->type;
}

なんてことのない、独自構造体で文字列と数値のやりとりをするだけのものを準備した。

lua コード

ffi = require 'ffi'

ffi.cdef[[
struct sample_data {
        char name[100];
        int type;
};
int sample_c_func(struct sample_data* data);
]]

module = ffi.load('c/libsample.so')

data = ffi.new('struct sample_data')
data.name = 'aaa'
data.type = 12

local ret = module.sample_c_func(data)
print(ret)

データは Lua 側で生成して、 C 関数側に渡す形。

実行結果

sample_c_func:10 name: aaa, type: 12
12

以前、 Lua で C ライブラリをやりとりする処理を実装したことがあったけど、その時は自力でスタックに積んだりおろしたりしていたので、 binding の実装だけでかなりのコード数になったけど、、、なんだこれは!?簡単すぎる (うれしい)

実際にはもっと大きなデータのやりとりになるけど、文字列の取り扱いがメインになるっぽいので、文字列のやりとりがこんなにお手軽だと非常に助かるというのが実感。

ターゲット上で LuaJIT が動作することを早く確認しなければ。

Bitbucket のプルリクエストの差分を patch 形式で取得する

忘れそうなので備忘録として。

Github では、プルリクエストから patch 形式でダウンロードするのは単純に URL の最後に .patch をつければ良いけど、 Bitbucket では単純に同じことはできなかったので調べると、 API を叩けばできることがわかった。

https://bitbucket.org/api/2.0/repositories/GROUP/PROJECT/pullrequests/ID/diff

stackoverflow.com

Kallithea の DB を SQLite から PostgreSQL に移行する戦い

仕事で Kallithea を使用しているのだけれど、標準の SQLite にはプロジェクトの規模が大きすぎるのか、 python がお亡くなりになる現象が多発している。 (Windows 上で動作させていることも原因かも。。)

そのため、公式の情報に従い、 SQLite から PostgreSQL に DB を移行しようと考えたが、情報が少なかったので、試行錯誤の歴史をまとめておく。

やったこと

docker に postgresql のコンテナを作成

postgres:9.6-alpine をベースに作成した。

sqlalchemygrate で DB を移行

エラー山盛りで失敗。。

$ apt-get install -y libbq-dev
$ pip install psycopg2
$ grate migrate kallithea.model.meta:Base.metadata "sqlite:///kallithea.db" "postgresql://172.17.0.7/kallithea" --verbose
2017-04-05 15:56:48,887 INFO     Migrating table: ui
2017-04-05 15:56:48,887 INFO     No corresponding table found, skipping: ui
2017-04-05 15:56:48,887 INFO     Migrating table: db_migrate_version
2017-04-05 15:56:48,887 INFO     No corresponding table found, skipping: db_migrate_version
2017-04-05 15:56:48,887 INFO     Migrating table: users
2017-04-05 15:56:48,887 INFO     No corresponding table found, skipping: users
2017-04-05 15:56:48,888 INFO     Migrating table: settings
2017-04-05 15:56:48,888 INFO     No corresponding table found, skipping: settings
2017-04-05 15:56:48,888 INFO     Migrating table: cache_invalidation
2017-04-05 15:56:48,888 INFO     No corresponding table found, skipping: cache_invalidation
2017-04-05 15:56:48,888 INFO     Migrating table: alembic_version
...

SQLite の DB をダンプして、手動で PostgreSQL に流し込む

かなりの力技だったが、なんとか動作させることができた!

$ sqlite3 kallithea.db .dump > sqlite-dumpfile.sql
$ sed -i '/PRAGMA/d' sqlite-dumpfile.sql
$ sed -i '/sqlite_sequence/d' sqlite-dumpfile.sql
$ sed -i 's/INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT/SERIAL NOT NULL PRIMARY KEY/g' sqlite-dumpfile.sql
$ sed -i 's/DATETIME/TIMESTAMP/g' sqlite-dumpfile.sql
$ sed -i 's/VARCHAR(255)/TEXT/g' sqlite-dumpfile.sql
$ sed -i 's/BLOB/BYTEA/g' sqlite-dumpfile.sql
$ sed -i "s/X\('\w\+'\)/decode(\1,'hex')/g" sqlite-dumpfile.sql 
$ sed -i -e "s/\\(INSERT INTO \"user_to_notification\" VALUES\)(\([0-9]\+\),\([0-9]\+\),\([01]\),/\1(\2,\3,'\4',/g" sqlite-dumpfile.sql
$ sed -i -e "s/\tCHECK (\(\w\+\) IN (0, 1))/\tCHECK (\1 IN ('0', '1'))/g" sqlite-dumpfile.sql
# 残りの BOOLEAN のフィールドを '0' or '1' にしていく
$ vim sqlite-dumpfile.sql
$ docker cp kallithea:/var/lib/kallithea/sqlite-dumpfile.sql .
$ docker cp sqlite-dumpfile.sql postgres:/
$ createuser --createdb kallithea
$ createdb --owner=kallithea kallithea
$ psql -d kallithea -U kallithea -W < sqlite-dumpfile.sql
$ psql -d kallithea -U kallithea -c "select setval('cache_invalidation_cache_id_seq',(select max(cache_id) from cache_invalidation))"
$ psql -d kallithea -U kallithea -c "select setval('changeset_comments_comment_id_seq',(select max(comment_id) from changeset_comments))"
$ psql -d kallithea -U kallithea -c "select setval('changeset_statuses_changeset_status_id_seq',(select max(changeset_status_id) from changeset_statuses))"
$ psql -d kallithea -U kallithea -c "select setval('gists_gist_id_seq',(select max(gist_id) from gists))"
$ psql -d kallithea -U kallithea -c "select setval('groups_group_id_seq',(select max(group_id) from groups))"
$ psql -d kallithea -U kallithea -c "select setval('notifications_notification_id_seq',(select max(notification_id) from notifications))"
$ psql -d kallithea -U kallithea -c "select setval('permissions_permission_id_seq',(select max(permission_id) from permissions))"
$ psql -d kallithea -U kallithea -c "select setval('pull_request_reviewers_pull_requests_reviewers_id_seq',(select max(pull_requests_reviewers_id) from pull_request_reviewers))"
$ psql -d kallithea -U kallithea -c "select setval('pull_requests_pull_request_id_seq',(select max(pull_request_id) from pull_requests))"
$ psql -d kallithea -U kallithea -c "select setval('repo_to_perm_repo_to_perm_id_seq',(select max(repo_to_perm_id) from repo_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('repositories_fields_repo_field_id_seq',(select max(repo_field_id) from repositories_fields))"
$ psql -d kallithea -U kallithea -c "select setval('repositories_repo_id_seq',(select max(repo_id) from repositories))"
$ psql -d kallithea -U kallithea -c "select setval('settings_app_settings_id_seq',(select max(app_settings_id) from settings))"
$ psql -d kallithea -U kallithea -c "select setval('statistics_stat_id_seq',(select max(stat_id) from statistics))"
$ psql -d kallithea -U kallithea -c "select setval('ui_ui_id_seq',(select max(ui_id) from ui))"
$ psql -d kallithea -U kallithea -c "select setval('user_api_keys_user_api_key_id_seq',(select max(user_api_key_id) from user_api_keys))"
$ psql -d kallithea -U kallithea -c "select setval('user_email_map_email_id_seq',(select max(email_id) from user_email_map))"
$ psql -d kallithea -U kallithea -c "select setval('user_followings_user_following_id_seq',(select max(user_following_id) from user_followings))"
$ psql -d kallithea -U kallithea -c "select setval('user_group_user_group_to_perm_user_group_user_group_to_perm_seq',(select max(user_group_user_group_to_perm_id) from user_group_user_group_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('user_ip_map_ip_id_seq',(select max(ip_id) from user_ip_map))"
$ psql -d kallithea -U kallithea -c "select setval('user_logs_user_log_id_seq',(select max(user_log_id) from user_logs))"
$ psql -d kallithea -U kallithea -c "select setval('user_repo_group_to_perm_group_to_perm_id_seq',(select max(group_to_perm_id) from user_repo_group_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('user_to_perm_user_to_perm_id_seq',(select max(user_to_perm_id) from user_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('user_user_group_to_perm_user_user_group_to_perm_id_seq',(select max(user_user_group_to_perm_id) from user_user_group_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('users_group_repo_group_to_per_users_group_repo_group_to_per_seq',(select max(users_group_repo_group_to_perm_id) from users_group_repo_group_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('users_group_repo_to_perm_users_group_to_perm_id_seq',(select max(users_group_to_perm_id) from users_group_repo_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('users_group_to_perm_users_group_to_perm_id_seq',(select max(users_group_to_perm_id) from users_group_to_perm))"
$ psql -d kallithea -U kallithea -c "select setval('users_groups_members_users_group_member_id_seq',(select max(users_group_member_id) from users_groups_members))"
$ psql -d kallithea -U kallithea -c "select setval('users_groups_users_group_id_seq',(select max(users_group_id) from users_groups))"
$ psql -d kallithea -U kallithea -c "select setval('users_user_id_seq',(select max(user_id) from users))"

参考にしたサイト

jnoconor.github.io

Migrating OwnCloud from SQlite to PostgreSQL

stackoverflow.com

stackoverflow.com

追記

sqlalchemygrate を使用する場合は、事前にテーブルまでは作成しておく必要があることがわかった。

仕事で使用している定義を手で編集するのは気が滅入るので、 sqlalchemygrate を使用したほうが良さそうだ。

Hubot から ErrBot に乗り換えたい

Node.js および Coffee Script に関して、あまり詳しくないため、 Hubot では実現したい機能をどのように実装すればよいかわからないことが多い。

そのため Node.js よりは使い慣れている Python で実装されたチャットボットである ErrBot への乗り換えを検討している。

仕事で使用しているチャットシステムは Let’s Chat なのだが、 Hubot 向けには公式にアダプタが提供されているが、 ErrBot 向けにはバックエンド (Hubot でのアダプタ) が提供されていない。

ErrBot でも、 Slack や HipChat はサポートされているので、自力でも何とかなるだろうと思ったら、意外と苦戦している。

理由は、

  1. Let’s Chat の Hubot アダプタは Socket.io (WebSocket) を利用して実装されている
  2. Slack の ErrBot バックエンドはクライアントモジュールが提供されていたが、メッセージはポーリングで受け取っているので Socket.io とは異なっていた (流用できない)
  3. HipChat の ErrBot バックエンドは XMPP で実装されていた

Let’s Chat も XMPP は提供されているので、最悪 Socket.io を捨てて XMPP での実装を真似るようにすればよいかな?

Trac に JSON-RPC の機能を追加して、curl でチケットを作成してみた

今の仕事では、客先は Redmine を使用している。

だが、客先の Redmine には社内事情をかけないため、必然的に社内的な作業を管理する ITS が必要となるのだが、個人的な事情 (Ruby ×、Python ○ なので) により Trac を使用している。

ちょっとした事情から、Redmine のチケットを Trac に同期させたいなーと思い、調べたところ、客先の Redmine では REST API が解放されていることを発見!

これは RPC で Trac に自動的にチケットを同期するボットを作成するしかない!と思い、 TracXmlRpcPlugin をインストールしてみたのだが、 curl でのチケット作成でちょっと手間取ったので、メモを残しておく。

ちなみに、 Trac のアカウント管理には AccountManagerPlugin を入れているが、 RPC を試すにあたりサーバの構成上 API キーが欲しかったので 拡張 している。

$ cat body.json 
{
    "method": "ticket.create",
    "id": 1234,
    "params": [
        "summary",
        "description",
        {
            "keywords": "keywords",
            "refs": "123",
            "due_assign": {"__jsonclass__": ["datetime", "2017-01-01T00:00:00+0900"]}
        },
        false,
        {"__jsonclass__": ["datetime", "2017-01-01T00:00:00+0900"]}
    ]
}
$ curl -X POST -H "Content-Type: application/json" -H "X-Trac-Api-Key: TRAC_ADMIN-PERM-API-KEY" --data @body.json -v http://172.17.0.2:8000/trac-plugin-test/login/jsonrpc
* Hostname was NOT found in DNS cache
*   Trying 172.17.0.2...
* Connected to 172.17.0.2 (172.17.0.2) port 8000 (#0)
> POST /trac-plugin-test/login/jsonrpc HTTP/1.1
> User-Agent: curl/7.35.0
> Host: 172.17.0.2:8000
> Accept: */*
> Content-Type: application/json
> X-Trac-Api-Key: TRAC_ADMIN-PERM-API-KEY
> Content-Length: 353
> 
* upload completely sent off: 353 out of 353 bytes
< HTTP/1.1 200 Ok
< Content-Type: application/json
< Content-Length: 43
< Date: Sat, 18 Mar 2017 03:22:48 -0000
* Server 0.0.0.0 is not blacklisted
< Server: 0.0.0.0
< 
{"id": 1234, "result": 112, "error": null}
* Connection #0 to host 172.17.0.2 left intact

作成日や更新日も自由に設定できるため、 Redmine と同期できていい感じにできそう。

サーバアプリのアップデートに、えらい手間取っている話

会社のサーバ PC が近々新しくなるので、手元環境で開発に導入しているアプリをアップデートの予行演習をしようとしたら、めちゃくちゃ手間取っている。

全然進んでいないけど、本番環境のアップデートの際の備忘録として残しておく。

Let’s Chat の更新

更新内容

Node.js を 0.10.x から 6.10.x に上げる。

パフォーマンスアップが主な狙いだが、そもそも、 npm でインストール出来るパッケージは更新されているため、古い環境ではいろいろ不都合が出るであろうことが予測されるため、最新の安定版まで一気に更新してみる。

出会った問題

  1. Hubot アダプター (hubot-lets-chat) がインストール出来ない

ws という WebSocket のパッケージがネイティブコードのビルドエラーになってしまい、インストール自体ができなかった。

丸一日試行錯誤していたが、結局、依存関係でインストールされていた ws のパッケージを更新することで解決できた。

npm install hubot-lets-chat
...インストールエラーが発生する
npm install ws@0.8.1
npm install hubot-lets-chat

ws 自体は 2.2.0 までリリースされているが、最新にすると API 仕様が変わっていても厄介だと思ったので、マイナーバージョンのみ最新に更新した。 (0.8.1 の次は 1.0.0 だった)

Yahoo が継続的デリバリーシステムの ScrewDriver を OSS として公開した

github.com

米 Yahoo が作成した継続的デリバリーシステムの ScrewDriver を OSS として公開したらしい。

継続的デリバリーと継続的インテグレーションの厳密なカバー範囲はよくわかっていないけど、今プロジェクトで使用している Jenkins を置き換えることはできるかな??

この ScrewDriver も Node.js で作成されているし、開発で使用するツール類にも Node.js が増えてきた。

現在 Java で動作するのは Jenkins だけなので、置き換えができそうなら、積極的に狙うのもいいかもしれない。