初めてOSS(Apacheモジュール)にPRだした
今回PRをだした対象
ApacheのVirtualHost単位でMaxClientsを設定することが可能な、Apacheモジュールです。 詳細は↓
ApacheのVirtualHost単位でMaxClientsを設定するApacheモジュールをOSS化 - 人間とウェブの未来
どんな機能を実装したか
MaxClientsの設定を有効にする時間帯を指定可能にする機能
本モジュールの利用を予定しているサーバの負荷は、常に一定ではなく高い時もあれば低い時もあります。 負荷の低い時間帯はMaxClientsを設定せず、自由に利用してもらうため、本機能の追加を行いました。
やったこと
途中色々アドバイスを受けながらあれこれ手戻ったりしてますが、簡略化するため一部改変してます。関数とか用語とか勘違いしてたら教えてあげてください やったこと、と言っても別に特別なことをしたわけではない
1. ソースを読む
mod_vhost_maxclients/mod_vhost_maxclients.c at master · matsumoto-r/mod_vhost_maxclients · GitHub
行数が短く、比較的読みやすいコードでした。
以下参考
- Cやってみよう #1 - 臥薪嘗胆 本モジュールのソース解説
- 人間とウェブの未来 - apache2系のmodule開発
- モジュールの Apache 1.3 から Apache 2.0 への移植 - Apache HTTP サーバ バージョン 2.4
- Apache2: Configuration
上から構造体や関数定義などが並んでますが、とりあえず一番したから読む。
apxsを使用してApacheのモジュールを作成する場合、同じ形式になっているもよう。
#ifdef __APACHE24__ AP_DECLARE_MODULE(vhost_maxclients) = { #else module AP_MODULE_DECLARE_DATA vhost_maxclients_module = { #endif STANDARD20_MODULE_STUFF, NULL, /* create per-dir config structures */ NULL, /* merge per-dir config structures */ # サーバ設定作成関数 vhost_maxclients_create_server_config, /* create per-server config structures */ # サーバ設定関数 vhost_maxclients_create_server_merge_conf, /* merge per-server config structures */ # directiveの値を取得する関数 vhost_maxclients_cmds, /* table of config file commands */ # フックするポイントを登録する関数 vhost_maxclients_register_hooks};
それぞれ、登録された関数が何をやっているか見ました。
vhost_maxclients_create_server_config
サーバ設定作成関数の名の通り、本モジュールで使用する設定値がデフォルト値とともに定義されています。
static void *vhost_maxclients_create_server_config(apr_pool_t *p, server_rec *s) { vhost_maxclients_config *scfg = (vhost_maxclients_config *)apr_pcalloc(p, sizeof(*scfg)); scfg->dryrun = -1; scfg->log_path = NULL; scfg->vhost_maxclients = 0; scfg->vhost_maxclients_log = 0; scfg->vhost_maxclients_per_ip = 0; scfg->ignore_extensions = apr_array_make(p, VHOST_MAXEXTENSIONS, sizeof(char *)); return scfg; }
- vhost_maxclients_create_server_merge_conf
static void *vhost_maxclients_create_server_merge_conf(apr_pool_t *p, void *b, void *n) { vhost_maxclients_config *base = (vhost_maxclients_config *)b; vhost_maxclients_config *new = (vhost_maxclients_config *)n; vhost_maxclients_config *scfg = (vhost_maxclients_config *)apr_pcalloc(p, sizeof(*scfg)); if (new->dryrun > -1) { scfg->dryrun = new->dryrun; } else { scfg->dryrun = base->dryrun; } scfg->log_path = base->log_path; scfg->vhost_maxclients = new->vhost_maxclients; scfg->vhost_maxclients_log = new->vhost_maxclients_log; scfg->vhost_maxclients_per_ip = new->vhost_maxclients_per_ip; scfg->ignore_extensions = new->ignore_extensions; return scfg; }
- vhost_maxclients_cmds
ディレクティブの定義をおこなっています。
AP_INIT_TAKE1
を例にすると、directive指定はhoge 100
のようにdirective名 値
と名前の通り一個だけ取得する定義です。
指定しているのはdirective名
,directiveの値を変数にセットするための関数
mconfig(何かよく分かってない)
,定義されている場所
,Help
になります。
static command_rec vhost_maxclients_cmds[] = { AP_INIT_FLAG("VhostMaxClientsDryRun", set_vhost_maxclients_dryrun, NULL, ACCESS_CONF | RSRC_CONF, "Enable dry-run which don't return 503, logging only: On / Off (default Off)"), AP_INIT_TAKE1("VhostMaxClients", set_vhost_maxclientsvhost, NULL, RSRC_CONF | ACCESS_CONF, "maximum connections per Vhost"), AP_INIT_TAKE1("VhostMaxClientsLogOnly", set_vhost_maxclientsvhost_log, NULL, RSRC_CONF | ACCESS_CONF, "loggign only: maximum connections per Vhost"), AP_INIT_TAKE1("VhostMaxClientsLogPath", set_vhost_maxclientsvhost_log_path, NULL, RSRC_CONF | ACCESS_CONF, "logging file path instead of error_log"), AP_INIT_TAKE1("VhostMaxClientsPerIP", set_vhost_maxclientsvhost_perip, NULL, RSRC_CONF | ACCESS_CONF, "maximum connections per IP of Vhost"), AP_INIT_ITERATE("IgnoreVhostMaxClientsExt", set_vhost_ignore_extensions, NULL, ACCESS_CONF | RSRC_CONF, "Set Ignore Extensions."), {NULL}, };
directiveの値を変数にセットするための関数
を一個例に見ると set_vhost_ignore_extensions
をちょっと見てみると、
バリデーションと値の取得をやってました。
static const char *set_vhost_ignore_extensions(cmd_parms *parms, void *mconfig, const char *arg) { vhost_maxclients_config *scfg = (vhost_maxclients_config *)ap_get_module_config(parms->server->module_config, &vhost_maxclients_module); if (VHOST_MAXEXTENSIONS < scfg->ignore_extensions->nelts) { return "the number of ignore extensions exceeded"; } *(const char **)apr_array_push(scfg->ignore_extensions) = arg; return NULL; }
- vhost_maxclients_register_hooks
ハンドラをフックポイントに登録する関数
人間とウェブの未来 - Apache hook関数 が参考になる。
initルーチンとかを呼ぶ時にap_hook_post_config
クライアントが認証受ける前に追加でアクセスチェックをしたい時にap_hook_access_checker
をフックさせてます。本モジュールの役割的にap_hook_access_checker(vhost_maxclients_handler, NULL, NULL, APR_HOOK_MIDDLE);
が実際に制限させる処理をやってるぽいです。
static void vhost_maxclients_register_hooks(apr_pool_t *p) { ap_hook_post_config(vhost_maxclients_init, NULL, NULL, APR_HOOK_MIDDLE); ap_hook_access_checker(vhost_maxclients_handler, NULL, NULL, APR_HOOK_MIDDLE); }
2. 書いてみる
vhost_maxclients_configに追加が必要なものを考える
今回は制限する時間帯を指定したかったので、
制限開始する時間
と制限終了する時間
があればよさそうなので、vhost_maxclients_config構造体に2つ変数を追加
unsigned int vhost_maxclients_time_from; unsigned int vhost_maxclients_time_to;
directiveから値を取得する
2つ数字を渡せればいいので、1directive追加します。これで
VhostMaxClientsTimeSlot 1100 2300
のようにconfに指定できます。AP_INIT_TAKE2
はAP_INIT_TAKE1
の引数が一個増えた版。
AP_INIT_TAKE2("VhostMaxClientsTimeSlot", set_vhost_maxclients_time, NULL, RSRC_CONF | ACCESS_CONF, "Time to enable the VhostMaxClients. (default 0:00 ~ 23:59)"),
TAKE2なのでarg1とarg2に一個目二個目の設定値が入ってくるので、時間のバリデーションをし、取得します。
static const char *set_vhost_maxclients_time(cmd_parms *parms, void *mconfig, const char *arg1, const char *arg2) { vhost_maxclients_config *scfg = (vhost_maxclients_config *)ap_get_module_config(parms->server->module_config, &vhost_maxclients_module); scfg->vhost_maxclients_time_from = atoi(arg1); scfg->vhost_maxclients_time_to = atoi(arg2); if(scfg->vhost_maxclients_time_from < 0 || scfg->vhost_maxclients_time_from > 2359){ return "VhostMaxClientsTimeSlot_From is invalid. should be set range 0 < VhostMaxClientsTimeSlot_From < 2359"; } if (scfg->vhost_maxclients_time_to < 0 || scfg->vhost_maxclients_time_to > 2359){ return "VhostMaxClientsTimeSlot_To is invalid. should be set range 0 < VhostMaxClientsTimeSlot_To < 2359"; } return NULL; }
vhost_maxclients_handlerの処理中で時間を判定する処理の追加
今回の機能は実際に制限をおこなうロジックの手前でやる必要があります。拡張子の判定処理の下あたりに入れます
/* check time */ if (check_time_slot(r->pool, scfg->vhost_maxclients_time_from, scfg->vhost_maxclients_time_to)) { return DECLINED; }
インラインでずらずら処理を書いてもいいのですが、汚いので関数に切り出します。
判定のやりかたは当初request_rec構造体のrequest_timeの値を利用しようとしましたが、apr_time_nowの値を使用することにしました。
apr_time_nowは現在時刻をUTC+マイクロ秒で返すので扱いにくいため、apr_time_exp_ltで人間向けに変換し、 時
分
をとってくっつけてintにしてます。
日をまたいだ指定( 23:00 ~ 2:00
など)のため、toよりfromが小さければ24時間プラスしてます。あとは現在時刻が指定した範囲内か判定します。
static int check_time_slot(apr_pool_t *p, unsigned int from, unsigned int to) { unsigned int cur; apr_time_exp_t tm; apr_time_exp_lt(&tm, apr_time_now()); cur = atoi(apr_psprintf(p, "%02d%02d", tm.tm_hour, tm.tm_min)); if (from > to){ to += 2400; } if ((from < cur) && (to > cur)){ return 0; } return 1; }
だいたい、上記のようなことをやりました。
あとは、以下のようにdirective指定し、想定通りの動作をするか確認します。
<VirtualHost *> DocumentRoot /var/www/html ServerName localhost VhostMaxClients 1 VhostMaxClientsTimeSlot 2130 2300 </VirtualHost>
3. 検証
- 正常動作 既存の同時接続数を制限する機能と時間の指定がどちらも動作するか確認します。
- パフォーマンス 判定を一個入れたので、少なからず遅くなってるはずです。問題ないレベルかチェックします。見た感じ問題なさそう…
# 改修前のバージョン [vagrant@localhost ~]$ ab -n100000 -c100 http://127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests Server Software: Apache/2.2.15 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 17 bytes Concurrency Level: 100 Time taken for tests: 16.434 seconds Complete requests: 100000 Failed requests: 0 Write errors: 0 Total transferred: 28507125 bytes HTML transferred: 1700425 bytes Requests per second: 6084.97 [#/sec] (mean) Time per request: 16.434 [ms] (mean) Time per request: 0.164 [ms] (mean, across all concurrent requests) Transfer rate: 1693.99 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 8 3.7 8 43 Processing: 1 9 3.6 8 45 Waiting: 0 7 3.5 7 43 Total: 1 16 4.2 15 57 Percentage of the requests served within a certain time (ms) 50% 15 66% 16 75% 17 80% 18 90% 19 95% 21 98% 33 99% 38 100% 57 (longest request) # 改修後のバージョン_1 制限を行う時間外 [vagrant@localhost ~]$ ab -n100000 -c100 http://127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests Server Software: Apache/2.2.15 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 17 bytes Concurrency Level: 100 Time taken for tests: 13.657 seconds Complete requests: 100000 Failed requests: 0 Write errors: 0 Total transferred: 28508550 bytes HTML transferred: 1700510 bytes Requests per second: 7322.41 [#/sec] (mean) Time per request: 13.657 [ms] (mean) Time per request: 0.137 [ms] (mean, across all concurrent requests) Transfer rate: 2038.59 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 4 3.2 5 15 Processing: 2 9 2.7 9 30 Waiting: 0 8 2.8 8 27 Total: 2 14 2.5 13 34 Percentage of the requests served within a certain time (ms) 50% 13 66% 14 75% 15 80% 15 90% 17 95% 18 98% 20 99% 22 100% 34 (longest request) # 改修後のバージョン_2 制限を行う時間内 [vagrant@localhost ~]$ ab -n100000 -c100 http://127.0.0.1/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking 127.0.0.1 (be patient) Completed 10000 requests Completed 20000 requests Completed 30000 requests Completed 40000 requests Completed 50000 requests Completed 60000 requests Completed 70000 requests Completed 80000 requests Completed 90000 requests Completed 100000 requests Finished 100000 requests Server Software: Apache/2.2.15 Server Hostname: 127.0.0.1 Server Port: 80 Document Path: / Document Length: 17 bytes Concurrency Level: 100 Time taken for tests: 15.214 seconds Complete requests: 100000 Failed requests: 0 Write errors: 0 Total transferred: 28514250 bytes HTML transferred: 1700850 bytes Requests per second: 6572.97 [#/sec] (mean) Time per request: 15.214 [ms] (mean) Time per request: 0.152 [ms] (mean, across all concurrent requests) Transfer rate: 1830.30 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 5 3.6 6 18 Processing: 2 10 3.0 9 39 Waiting: 0 9 2.9 9 35 Total: 2 15 3.1 15 43 Percentage of the requests served within a certain time (ms) 50% 15 66% 16 75% 17 80% 17 90% 19 95% 21 98% 22 99% 24 100% 43 (longest request)
振り返り
apr_xxxxxが便利 Apache Portable Runtimeというサポートライブラリがあって、これが非常に便利なやつでした。
たとえばcur = atoi(apr_psprintf(p, "%02d%02d", tm.tm_hour, tm.tm_min));
の部分
当初は以下のように書いてました。c char hoge[5]; sprintf(hoge,"%02d%02d", tm.tm_hour, tm.tm_min));
apr_psprintfを使用すると、hogeのようなメモリ割り当てが不要になり、request_rec構造体のプールを使わせることができます。
戻り値をそのまま別の関数にくわせて、欲しい型でとれるから便利
人間とウェブの未来 - apr_psprintfはかなり楽(apache2.x系)自分の英語雑過ぎる コメント無し
普段コードを書いていないと基本的なことを忘れていく 関数名・変数名が長い、短くするとき意味がわからない。エラーメッセージに必要な内容が載ってない。
if(!((a!=b)&&(c<b))
みたいなこと書いちゃう。関係性の無い判定をelseifしちゃう。 酷い感じでした- httpdのソースを眺めながら書くとよい 変数の型や、利用する関数でソース中調べると色々助かる。コード量がおおいので、GNU Globalを使いました 人間とウェブの未来 - GNU GLOBALとvimで巨大なコードでも快適にコードリーディング - Forkwell
初めてcを書き、初めてOSSへPRを出すことができました。
cの入門書を眺めてただけだとしれなかったことがたくさんあり、やれてよかったと思います。
びびりまくってましたが、いざやってみるとみんな優しくて色々教えてくださったので、やってみたほうがいいです。
iTerm2 3.0にあげたらAlfredのWorkflowから起動できなくなったから対応
使っているのはこれ github.com
実行するとiTermを起動し、指定したホスト名へSSH接続する
動かなくなった原因はApplescriptの互換性が無いかららしい。iTermの公式ページに対応版コードが載ってたからそれを使うだけ。
手順
- Version 3 Beta - iTerm2 - Mac OS Terminal Replacement へアクセス
Alfred Support
の iTerm2 3.0 Alfred Script を開く- 全文コピーして、以下に貼り付ける
参考
http://harasou.github.io/2015/09/09/Alfred-iTerm2/harasou.github.io
最近覚えたシェルスクリプトの小ネタ
シェルスクリプト神から教えていただいた。忘れないように書いとく
(追記)聞いて、自分の記憶した内容をそのまま書いちゃったので、ちゃんとマニュアル通りか確認してなかったので反省
$ man bash
目次
- xargsでfunctionを叩く
- 連想配列もどき
- 変数間接参照
- なんでもかんでもawkで整形しない
- 文字列の末尾から数えて○文字目を△文字取り出す
- 番外編:やたら
if [ ]; then
を使わない
※Markdownで目次の書き方がわからんかった
xargsでfunctionを叩く
下の例だと find xxxx | xargs cp xxxx
ってかけば良さそうでイマイチだけど、もっと複雑な処理やらせたいときに。
- 前はこう書いてた
#!/bin/bash for x in `find /var/www -name xxx` do cmd="cp -v /hoge/fuga/xxx $x" [[ `md5sum $x | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | : done
- xargsで5多重で同じことやらせる
#!/bin/bash function sample() { cmd="cp -v /hoge/fuga/xxx $1" [[ `md5sum $1 | awk '{print $1}'` != "xxxxxxxxxxxxxxxxxxxxxxx" ]] && eval $cmd | : } # これしたらxargsから呼べることをしらなかった export -f sample find /var/www -name xxx | xargs -P5 -I{} bash -c "sample {}"
連想配列もどき
# 連想配列もどきのHを定義 declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" for x in a b c d e do # とりだす echo ${H["$x"]} done
ちなみに、
- こっちは配列見える
declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" function sample(){ # 682f7xxxxxxxxxxxxxxxxxxx0 echo ${H["a"]} } sample
- こっちは配列見えない
#!/bin/bash declare -A H H["a"]="682f7xxxxxxxxxxxxxxxxxxx0" H["b"]="7808axxxxxxxxxxxxxxxxxxx2" H["c"]="c6ba1xxxxxxxxxxxxxxxxxxx6" H["d"]="64186xxxxxxxxxxxxxxxxxxxb" H["e"]="50d1dxxxxxxxxxxxxxxxxxxxc" function sample(){ # 682f7xxxxxxxxxxxxxxxxxxx0 echo ${H["a"]} echo "hoge" } # ここで配列はexportされないらしいけど、あんまり詳しく調べてない。当たり前の動き? export -f sample find ~/test -type f | xargs bash -c "sample"
変数間接参照
#!/bin/bash var1="xxxxxxxxxxxxxxxx" var2="yyyyyyyyyyyyyyyy" var3="zzzzzzzzzzzzzzzz" for i in `seq 1 3` do # 変数を使って作る変数名は一度変数に入れなきゃだめ tmp="var${i}" echo ${!tmp} done
なんでもかんでもawkで整形しない
/home/hoge/fuga/piyo/php5.2.cgi
のようなパスから、末尾の php5.2.cgi
のみ抽出し、かつその値を利用して色々処理したい時に、つい以下のようなことを書いてた。
[root@hoge ~]# cat test.sh #!/bin/bash for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"` do echo $x | awk -F/ '{print $10}' done
このくらいのことをawk出してるとコストが高いので、bashの変数操作を使って以下の様に書く
[root@hoge ~]# cat test2.sh #!/bin/bash for x in `find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi"` do echo ${x##/*/} done
すると
[root@hoge ~]# time ./test.sh ・・・ real 0m8.316s user 0m2.367s sys 0m7.828s [root@hoge ~]# time ./test2.sh ・・・ real 0m0.763s user 0m0.357s sys 0m0.394s
圧倒的に早い!!!
捕捉 この例だと
find ~/home/users/php-bin/ -path "*/.php-bin/php*.cgi" | awk -F/ '{print $10}'
でいいけど、今回のこれは、①findしたパス②awkで整形した値両方を使用して、いろいろ処理する必要があって、forを利用してた
文字列の末尾から数えて○文字目を△文字取り出す
以下は乱暴な例ですが、 5.x
の x
の値に応じて処理したい要件があった。
先頭の文字はファイルによってバラバラの長さのため、cutコマンドでは取得できないため、「末尾から何文字目」という指定をしたかった
[user ~/work/test]$ ll total 0 -rw-r--r-- 1 user staff 0B 6 28 00:49 hogehogeho_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 xxxx.yyyy-abcdefghijk_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 xxxxxxxxxxxxxxxxxxxxxxxxxxx_ver5.2.sh -rw-r--r-- 1 user staff 0B 6 28 00:49 yyyyyyyyyyyy-aaaaaaaaaaaaaaaa_ver5.3.sh [user ~/work/test]$ for x in `find . -type f` > do > echo ${x:${#x}-4:1} > done 2 2 2 3
番外編:やたら if [ ]; then
を使わない
testコマンドを使うとフォークしてしまって大量に叩くとパフォーマンス悪いとのこと。
[PMAC226S ~/work]$ cat test.sh #!/bin/bash for i in `seq 1 1000000` do if [ "$a" = "hoge" ]; then echo "hoge" else echo "fuga" fi done
と
[PMAC226S ~/work]$ cat test2.sh #!/bin/bash for i in `seq 1 1000000` do [[ "$a" = "hoge" ]] && echo "hoge" || echo "fuga" done
があった時、後者の方が結構はやい
[PMAC226S ~/work]$ time ./test.sh ・・・ real 0m30.164s user 0m20.777s sys 0m2.917s [PMAC226S ~/work]$ time ./test2.sh ・・・ real 0m25.246s user 0m17.777s sys 0m1.812s
第9回 コンテナ型仮想化の情報交換会@福岡で「Alpine Linux 入門した」という発表をした
人生初しゃべりでした。声が震えてた
内容のレベルは正直低くて、まだまだ勉強不足感がありあり。
だけど、特性を知った上で計測をしつつ、良い使い道を模索できたので、そんなに悪い結果ではなかったと思いたい。
スライド内で使用したDockerfileはGithubに
(追記)サイズの話ばかりしてたけど、小さいってことは余計なものが入ってないから、セキュアなのでした。 公式サイトにも記載あるし。
よく使うGitHubのリポジトリは、ブラウザのカスタム検索設定をしておくと便利
よくあるアレ 設定が面倒くさくて放置してたけど、便利
やりたいこと
ブラウザのアドレスバーから https://git.tahira.com/hoge/fuga
のissue検索を行いたい
通常のフローだと、3ステップ
https://git.tahira.com/hoge/fuga
へアクセス- 左上の検索欄にキーワード入力して検索
- issueタブを選択
設定すると、1ステップ
- ブラウザのアドレスバーから直接ジャンプ
設定方法
GoogleChromeの場合
(1) GoogleChromeの設定画面から 検索エンジンの管理...
を選択
(2) 検索エンジンを追加
より新規追加
- 検索エンジンを追加・・・任意の名前
- キーワード・・・アドレスバーからカスタム検索を実行するためのキーワード(なんでもよい)
- URL・・・通常のフローで検索した際のアドレスバーからコピる。サンプル:
https://git.tahira.com/hoge/fuga/search?q=検索したいキーワード&type=Issues&utf8=%E2%9C%93
これの、q=XXXXXX
をq=%s
に修正して記載
使用方法
(1) アドレスバーに設定したキーワード hogefuga
と入力
(2) スペースを開けると GH:E hogefuga を検索
と表示が切り替わるので続けて検索したいキーワードを入力しEnter
以上!