DRYな備忘録

Don't Repeat Yourself.

ElectronデスクトップアプリによるGoogleのOAuth2クライアント実装

目的

  • 僕が、ElectronデスクトップアプリによるOAuthプロセスを知る

うるせえ動くもん見せろ

はい。

github.com

ゴール

  • 手元のElectronアプリで、ログインユーザ(この場合、僕自身)のAPIトークンで、GoogleのなんらかのAPIが叩ける
    • ← 登録したアプリ下でわざわざ作成したAPIトークンではないのがミソ

本稿でやらないこと

  • OAuthとはなにかのもっとわかりやすいやつ(こんど別エントリで書きたい)
  • 得られたAPI Tokenの永続保存(electron-json-storageでも使う)
  • Expire時のRefreshの実装 (割愛)

参考資料

目次

  1. デスクトップアプリの設計概要
  2. Google Cloud Console での準備
  3. 最低限のElectronアプリと動線を作成
  4. OAuthクライアントの実装

1. デスクトップアプリの設計概要

こういうものをつくっていきます

f:id:otiai10:20190502154537j:plain
アプリのペーパープロトタイプ(検索の索をまちがえている)

  1. メインのウィンドウでトップ画面が開く
  2. Googleでログイン」的なボタンを押すと別ウィンドウが開く
  3. そこでGoogleにログインして、権限も許可する
  4. 成功するとそのウィンドウは消えて、メインのウィンドウで認証情報が得られる
  5. なんらかのAPI(たとえばYouTube検索API)が使える

みたいな感じで。

2. Google Cloud Console での準備

この手順はもちろん「OAuthプロバイダ」によって操作が異なります

が、

OAuthのプロトコルに則る以上、以下のものを得る・設定する手順には変わりありません

  1. クライアントID(ひつようですね)
  2. クライアントシークレット(ひつようですね)
  3. リダイレクトURI(指定可能だが、登録するひつようがある)

今回は、GoogleをOAuthプロバイダとしてこれをやっていきます。

OAuthプロトコルとしては些末なことなのでここに折りたたんでおきます😉

ⅰ) プロジェクトを作成 - Google Cloud Platform ここに行く

f:id:otiai10:20190502160416p:plain
Google Console 上で新しいプロジェクトを作成(名前なんでもいい)
ⅱ) 使う予定のAPIを有効化 f:id:otiai10:20190502160658p:plain:w320 f:id:otiai10:20190502160725p:plain:w320 f:id:otiai10:20190502160935p:plain:w320
ⅲ) (番外)アプリのAPIトークンで最終目的地を確認
これは番外なんですが、とりあえずYouTubeAPIが叩けることを確認するために、アプリのAPIトークンを作成して、APIが叩けているということをクリアにしておきたい。個人的な性癖です。でもだいじ。こういうのだいじ。 f:id:otiai10:20190502161204p:plain:w320 認証情報、から f:id:otiai10:20190502161537p:plain APIキーのほうを作成して、 f:id:otiai10:20190502161723p:plain で、`curl`で試してみる。 - Search: list  |  YouTube Data API  |  Google Developers
f:id:otiai10:20190502162551p:plain
curlで叩けている(jqとかはノリです)
User Token の場合は、`Authorization Header`に入れる必要があるが、とりあえずこのAPIを叩けることは確認できた。
ⅳ) OAuthクライアントを登録
f:id:otiai10:20190502164911p:plain f:id:otiai10:20190502165218p:plain f:id:otiai10:20190502165414p:plain このように得られました。

はい!と、いうことでね!上記3点を得ることができました!

3. 最低限のElectronアプリと動線を作成

ファイルぜんぶここで晒すのめんどくさいので、そのdiffをおいておきます。

Implement minimum transitions btw windows · otiai10/electron-playground@7085aed · GitHub

ポイントとしては、Electronにはmainプロセスとrendererプロセスがあり、rendererプロセスを起因としてGoogleの認証画面を開かせる流れなので、

  • トップ画面のrendererプロセスは「Googleでログイン」ボタンをきっかけとして、mainプロセスへipcRenderer.send('auth-start')などのメッセージを送る
  • mainプロセスは、ipcMain.on('auth-start', () => ...)などでその認証プロセスをスタートさせる
    • mainプロセスは、あたらしい画面(BrowserWindow)をつくり、Googleのページへ誘導する
    • この画面におけるGoogleとのやりとりが成功したトリガーを検知し、成果物を取得し、この画面を閉じる
    • mainプロセスは、この成果物をトップ画面へ win.send('auth-success', tokens)などで通知する
  • トップ画面は ipcRenderer.on('auth-success, tokens => ...)などでこのメッセージを受け取る
    • トップ画面は、トークンが得られたら、「Googleでログイン」ボタンを非表示にし、かわりに主なコンテンツを表示する

4. OAuthクライアントの実装

ファイルぜんぶここで晒すのめんどくさいので、そのdiffをおいておきます。

Implement OAuth handshake inside the app · otiai10/electron-playground@5f9b18e · GitHub

ポイントをいくつか列挙すると

  • これべんり GitHub - googleapis/google-auth-library-nodejs: 🔑 Google Auth Library for Node.js
  • プロバイダ(Google)画面における認証と権限許可が成功したことをデスクトップアプリでどう検知するか
    • ウェブサービスであれば、Callback URLないしRedirect URIなどと呼ばれるURLに、ブラウザがリダイレクトされる
    • ウェブサービスではない。BrowserWindowfile://から始まるURLにリダイレクトさせることを考えるが、これはプロバイダ側から許可されないことが多い
    • 苦肉の策ではあるが、BrowserWindowpage-title-updatedイベントを使い、Google上の認証認可が成功したことを検知する
  • 検知したら、codeを取得し、これをaccess tokenと交換してもらう
    • これはgoogle-auth-libraryがやってくれる。べんり。
  • あとはこれをrendererプロセスへ通知し、rendererプロセスにおいてYouTube Data API / Searchを叩けばよい

完成したものです

雑感

  • page-title-updatedとかダサすぎか
  • <template>タグべんり
  • fetchべんり
  • async/awaitべんり
  • OAuthについて学んだことをまとめようかと思ったけどちょっとだるいなこれは
  • なんか最近「おちついてやる」という能力が板についてきて、よい
  • GW長い
  • 令和!

DRYな備忘録として

Elasticsearch: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

tl;dr

Elasticsearchが動くコンテナの中に以下の環境変数をねじこめばよい。

参考: Running Elasticsearch 5 - Build Environment - CircleCI Discuss

# これ
# transport.host=localhost
#
# と、これ
# bootstrap.system_call_filter=false
# 
# docker run で渡す場合は、

docker run -d \
    -e transport.host=localhost \
    -e bootstrap.system_call_filter=false \
    -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:6.6.1

# となる

問題

  • CircleCI上でElasticsearchのDockerイメージがうごいてくれない
ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

原因

vm.max_map_countとは、

1プロセスあたり所有できるメモリマップの数で、/etc/sysctl.confで指定できる。

解決(ホストをいじれる場合)

ので、ホストの/etc/sysctl.confをいじって許容を広げればよい。

sudo sysctl -w vm.max_map_count=262144

しかしながら、CircleCIインスタンス上ではこれはpermission deniedとなる。さもありなん。

解決(Circle CI)

したがって、なんらかの方法でコンテナ側の要求を下げる必要がある。

docker run -d \
    -e transport.host=localhost \
    -e bootstrap.system_call_filter=false \
    -p 9200:9200 docker.elastic.co/elasticsearch/elasticsearch:6.6.1

あるいは、CircleCI v2 であれば、dockerディレクティブの中で

docker:
  - image: docker.elastic.co/elasticsearch/elasticsearch:6.6.1
     environment:
         transport.host: localhost
         bootstrap.system_call_filter: false

などとすればよい。

DRYな備忘録として

Pythonでdictionaryの各要素に処理を加えた別のdictionaryをつくる

TL;DR

>>> { k:list(map(lambda s: int(s)**2, v.split('-'))) for (k,v) in src.items()}
{'foo': [1, 4, 9], 'bar': [16, 25, 36]}

f:id:otiai10:20190412125100p:plain

やりたいこと

入力

{
  'foo': '1-2-3',
  'bar': '4-5-6',
}

出力

{
  'foo': [1, 4, 9],
  'bar': [16, 25, 36],
}

みたいなこと。

解決

src = {'foo': '1-2-3', 'bar': '4-5-6'}

dest = {
    k: list(map(
            lambda s: int(s)**2,
            v.split('-'),
    )) for (k, v) in src.items()
}

# {'foo': [1, 4, 9], 'bar': [16, 25, 36]}

知見

  1. dictは、itemsメソッドで、(key, value)のタプルのリストが得られる
    1. 正確には得られるのはdict_itemsであり、appendなどは無いが、リストとして扱う上では困らない
  2. タプルのリストはforでそれぞれ多値を拾うことができる
    1. for (name, age) in [('otiai10', 100), ('otiai20', 200)] のように
  3. map関数でリストのそれぞれの要素に対して処理をapplyできる
    1. ただし、この返り値はmap objectであり、listではないので、list()でリストにしてやる必要がある
  4. 文字列分割はstr.split(delim)
  5. 無名関数をlambdaで作ることができる
    1. ただし、複数行のlambdaをつくることはできない?っぽいので、見通しも悪くなるので関数は別定義したほうがよい
  6. dictの初期化において、keyにも変数を使える
    1. 下記参照
>>> key = 'name'
>>> val = 'otiai10'
>>> {key:val}
{'name': 'otiai10'}

WETな備忘録として

Go1.11でAppEngineをはじめる

tl;dr

これの通りです

うごくやつです

作業環境

% gcloud -v
Google Cloud SDK 235.0.0
app-engine-go
app-engine-python 1.9.83
bq 2.0.41
cloud-datastore-emulator 2.1.0
core 2019.02.15
gsutil 4.36
% go version
go version go1.11 darwin/amd64

Overview

  1. プロジェクトの作成とRegionの設定
  2. ローカルで開発
  3. デプロイ
  4. 確認

プロジェクトの作成とRegionの設定

プロジェクトIDで、ローカルのSDKを設定しておく

% gcloud config set project otiai10-sandbox

ローカルで開発

% mkdir -p $GOPATH/src/github.com/otiai10/gae-go-sandbox
% cd $GOPATH/src/github.com/otiai10/gae-go-sandbox
% vi main.go

main.go

package main

import (
    "fmt"
    "net/http"
    "os"

    "github.com/otiai10/marmoset"
)

func main() {
    router := marmoset.NewRouter()
    router.GET("/", func(w http.ResponseWriter, r *http.Request) {
        w.Write([]byte("Hi"))
    })
    router.GET("/hello", func(w http.ResponseWriter, r *http.Request) {
        message := fmt.Sprintf("Hello, %s!", r.FormValue("name"))
        w.Write([]byte(message))
    })

    port := os.Getenv("PORT")
    if port == "" {
        port = "8080"
    }
    fmt.Println("Listening port", port)

    http.ListenAndServe(fmt.Sprintf(":%s", port), router)
}

ローカルでの挙動確認

% go run main.go
Listening port 8080

f:id:otiai10:20190226123753p:plain
http://localhost:8080/hello?name=otiai10

いいかんじ。

デプロイ

app.yamlつくる

% vi app.yaml
runtime: go111

おしまい。

% gcloud app deploy
# もしかすると
# gcloud auth login
# が必要かも

とすると、

Updating service [default]...failed.
ERROR: (gcloud.app.deploy) Error Response: [9] Cloud build 01b7bef8-3e62-4956-9bc6-8e3a4b4cb1fe status: FAILURE.
Build error details: go: finding github.com/otiai10/marmoset v0.4.0
go: finding golang.org/x/net v0.0.0-20190225153610-fe579d43d832
go: downloading github.com/otiai10/marmoset v0.4.0
.
Check the build log for errors: https://console.cloud.google.com/gcr/builds/01b7bef8-3e62-4956-9bc6-8e3a4b4cb1fe?project=199462931903

言われたとおりエラーログ見に行くと、

f:id:otiai10:20190226134218p:plain
上記のエラーログ

GCR(コンテナレジストリ)へのイメージのアップロードで403を食らっているような雰囲気がある。ためしに、コンテナレジストリのコンソールに行くと。

f:id:otiai10:20190226134342p:plain

ふむ。Quick Startのドキュメントをよく見ると

Before you begin

Use the GCP Console to create a Google Cloud Platform project, choose a region where you want your application's resources to be located, and enable billing:

https://cloud.google.com/appengine/docs/standard/go111/quickstart#costs

とある。たぶん、AppEngineが使うストレージとは別に、GCRが使う領域を追加するのに課金の有効化(金がかかるとは言っていない)が必要だと思われる。ので、ここ https://console.cloud.google.com/billingから、このプロジェクトの課金を有効化して、再度GCRを見に行くと、

f:id:otiai10:20190226134743p:plain

見えるようになった。気を取り直して、

% gcloud app deploy
# 中略

Deployed service [default] to [https://otiai10-sandbox.appspot.com]

You can stream logs from the command line by running:
  $ gcloud app logs tail -s default

To view your application in the web browser run:
  $ gcloud app browse

いいかんじにコマンドは成功した。

確認

f:id:otiai10:20190226135054p:plain

いいじゃん

雑感

DRYな備忘録として

tigが「dyld: Library not loaded」とか言うので、ソースからコンパイルして使う

背景

tig好きなんですよ。だけどbrewから入れようとしたら以下の症状になるんで、手元でコンパイルすりゃいいか、となりました。

qiita.com

tigとは

これ

github.com

tigの良さについて過去に備忘録書いてるかと思ったら書いてなかったので、自分使いの例で言うと

  • tigとすると、git logがめっちゃ見やすく出るやつ
    • jkカーソル移動でpatchの内容も開ける
  • tig blame とかすると、line by line のblameだけではなく、当該blameを含んだpatchが見れるので、意図をblameできる

みたいなやつです。とにかく入れてみてtigと打ちましょう。

f:id:otiai10:20190116100954p:plain
tigの図

ゴール

参照

ログ

brewで入れちゃったものがある場合、まずこれ

% brew uninstall tig

ソースの取得

# このへんは気持ち。手元コンパイルするツールはこの中においてるだけ。
% mkdir -p ~/opt/src
% mkdir -p ~/opt/bin
% cd ~/opt/src
% git clone https://github.com/jonas/tig.git
% cd tig

コンパイル

% make
# ログからは分かりづらいが、成果物の実行可能ファイルが
# src/tig に配置されている。

確認

% ./src/tig --version
tig version 2.4.1-7-g93ea970
ncurses version 5.7.20081102
readline version 8.0
%

いい感じ。PATH通す

% ln -s ./src/tig ~/opt/bin/tig
% export PATH=${HOME}/opt/bin:${PATH}
% which tig
/Users/otiai10/opt/bin/tig
% tig --help
tig 2.4.1-7-g93ea970

Usage: tig        [options] [revs] [--] [paths]
   or: tig log    [options] [revs] [--] [paths]
   or: tig show   [options] [revs] [--] [paths]
   or: tig blame  [options] [rev] [--] path
   or: tig grep   [options] [pattern]
   or: tig refs
   or: tig stash
   or: tig status
   or: tig <      [git command output]

Options:
  +<number>       Select line <number> in the first view
  -v, --version   Show version and exit
  -h, --help      Show help message and exit
%

おしまい。

DRYな備忘録として

Travis-CIによるイベントホームページ自動デプロイと告知ツイートの自動化 #YUKEMULI

背景

自分が関わっているイベントのホームページをGitHubで管理、GitHub Pagesでホストしているんですが、

  1. masterブランチが更新されたら自動でデプロイしたい
  2. ホームページの更新内容ってほとんど「イベントに関する新しい情報」に他ならないので、これを自動で告知としてツイートできればいいのではないか

という気持ちがありました。

TL;DR

1月19日(土)に鶴見のスーパー銭湯でDJイベントやるから風呂入るついでに顔出してくれるとそれはとってもうれしいな!

yukemuli.dance

Hugoによる静的サイト生成

前々より、僕自身は「GitHub PagesでホストするんならJekyllでいいじゃん、カスタマイズしやすいし」派だったのですが、 いかんせんJekyllだとGitHub Pages用にすぐ使えるテーマが少ないという問題があります。自分でTheme書けるんだったらJekyllで十分良いと思うんですけどね。

そこでちょっと前に話題になったHugoです。Hugoというのは、静的サイト生成ツールであり、Markdownで書いといたらいい感じのHTML吐いてくれる、みたいな雑な理解です。

gohugo.io

Goで出来てるとかビルドが早いとかは顧客価値ではないと思うので置いといて、特筆すべき点は、そのThemeの開発しやすさ・自作Theme開発後簡単にデリバリできるエコシステム、結果として利用可能なテーマが豊富にあるというところだと思います。

実際、今回はデザインに関してはベースになるテーマをオーガナイザDJのたこっぷ氏に選んでもらい、すきまき先生アートディレクションのもと、僕が実装する形だったので、ある程度テーマができていて選択肢が多いhugoが良いかという技術選定でした。

HugoとTravisの連携と自動デプロイ

ただし、Hugoは(今のところ)GitHub Pagesにサポートされていないため、自動でGitHub Pagesに反映(便宜的にこれをデプロイと言ってます)するためには、たとえばindex.htmlを更新するコミットをCI上で作成 & Repoにプッシュするという要件が発生します。

その点、Jekyllはめっちゃ楽なんですよ、ビルド済みHTMLをコミットする必要が無いので。

ここでHugoのGitHub Pagesへの自動デプロイの詳細は解説しませんが、インターネッツに参考例がゴロゴロしているのでぜひ厳選のうえご参考ください。

強いて注意点があるなら、

  1. コミット&プッシュするアカウントのSSH PRIVATE KEY を、Travisが提供する暗号化を施してプロジェクトに含める必要がある
  2. 以前にJekyllによってGitHub Pagesがビルドされたことがあるリポジトリには.nojekyllという空ファイルをコミットしJekyllを使わないことを明示する必要がある

という2点が重要な知見でした。

ホームページ更新内容と「告知」の概念

さて、一方で「イベントの告知」というオペレーションについての知見なのですが、これは常々巧いなあと思っているとーま先輩から着想を得ました。

彼はイベントの情報を一挙に公開せず、小出しにすることにより告知機会そのものを増やし、自然と露出・拡散が促されていました。

「段階的に情報を小出しにする」という特徴は、gitのコミットに非常に親和性があると思われます。

そこで、コミットメッセージを告知サマリとすることでコミットログから告知文言を自動生成するという結論に帰着し、これを実装しました。

このへん👇

github.com

今後の展望

もちろん、コミットメッセージを告知サマリにする、ということは「適切なコミット単位」を意識し、意味のあるコミットログを積み重ねていくことに他なりません。ので、開発やっててとてもたのしいです。

それと同時に、不可避的に「告知する必要の無い開発案件のコミット」も発生するので、今後はこれを除外していく仕組みも必要かと思われます。

現時点では、コミットメッセージに DONOTANNOUNCE: みたいなのをつけたらそのコミットメッセージは告知ツイートに含めない、みたいなのでいっかなーって思ってる。

あと、コミットログの取得→リリースアナウンス、みたいなワークフローはわりと他のプロジェクトでも使うので、インターフェースを整えてこれをnpmパッケージに切り出すのも面白いかな、と思います。

まとめ 告知

1月19日(土)に鶴見のスーパー銭湯でDJイベントやるから風呂入るついでに顔出してくれるとそれはとってもうれしいな!

yukemuli.dance

前半はまったり寝転がったりできる「宴会モード」。後半はVJもつけて「クラブモード」となります。会場の「ラクスパ鶴見」がふつうに良いスーパー銭湯なので、スーパー銭湯をメインディッシュに、ちょっと顔出してくれるだけでもオッケーっす!

お待ちしてます ;)

DRYな備忘録として

さようなら、CWL

このエントリは、 Common Workflow Language (CWL) Advent Calendar 2018 - Qiita の25日目です。

tl;dr

CWLって何よ

2017年4月にベルリンから逃げ帰って、某大学研究所に研究員として就職しました。当初は、自分が持ってるバイオインフォの個人事業で独立しようと思っており、半分はその営業・コネクション作りのつもりで行ったのですが、なんやかんやでそこで働くことになってました。今にして思えば、よい判断だったと思います。理由は最後のほうで書く。

ゲノム解析のプラットフォーム構築、あるいはその技術検証」というようなミッションで、ゲノム解析の実情・課題国内のスパコンの利用実態、日本および海外のクラウドサービスの利用状況、Docker / Singularity を用いた仮想化・再現性の担保などの研究?実装をしました。

そんな中、国際学会でもにわかに注目を集めていたのが「Common Workflow Language」略して「CWL」です。

CWLって何よ、っていうのは、この辺見たらよろしい。

世界中のゲノム解析作業者(←そのすべてが「バイオインフォマティシャン」とは限らないところがミソ)がそれぞれのスパコンのスペックで、秘伝のパッケージがインストールされた環境で、秘伝のシェルスクリプトを丹念につくりあげて論文を発表するも、当然それでは再現性が無かったりしてどーなのよ、となるのを「じゃあ統一記法があったらいいんでしょ」って発想で「Common」な「Workflow」の「Language」を 作りたかったらしい 作ったのが「Common Workflow Language」です。

正直しんどい

「お、じゃあいっちょCWLの思想信条を理解するのも兼ねて、エンジンをつくってみよじゃないの」と思ってGoによるシンプルなエンジンの実装を始めたんですけど、これがまた、しんどい。色々な概念をモリモリにしてしまって、仕様が収集つかなくなってる感じがあります。

細かいことは、去年 Webプログラマから見た「CWL」の功績と罪過 - DRYな備忘録 でガッツリ書いたのでここでは詳細は割愛しますが。

すっげー乱暴に言ったら、哲学が先行しており、実践がついてきてなくね?という感じです。

帰納的アプローチ

CWL自身も「いや、ウチは別に絶対的な統一記法を発明しようとしているわけちゃうよ?」「他にもいろいろあるからええやつ選んだらええんちゃう?」という態度です。

であるのであれば、無理にCWLに追従するのではなく、独自のワークフロー記法を作るぐらいの勢いでやるのがいいんじゃないかと思うんですが。

そのとき、CWLのように「かくあるべき」から降りてきて、途中途中に要求を吸収していくようなやり方ではなく、それぞれの参加者がそれぞれの業務フローに特化したソフトウェアをまずつくり、共通概念を抽出していくような、いわば帰納的なアプローチを取るべきだ(あるいはCWLはそういうアプローチを取るべきだったのではないか。まあそれをやるにはワールドワイドすぎるとは思うけど)と思うわけです僕は。

そんなわけで、ってほど必然性は無いんですが、僕は仕事の一環で「クラウドサービス上で任意のワークフローを動かし結果だけを得るエンジン」を作っておりました。「hotsub」っていうんですけど。

名前が某ポルノサイトに酷似してるってツッコミを受けました。ポルノだけに。

12月は無職でした

もちろん、業務フローに特化したエンジンを作ると、そこそこ長所短所・得手不得手があり、なんとなく職場ではこの "hotsub" 使われずじまいだったので、いちおう目標であった論文だけ(インターネットジャーナルですけど)投稿して、11月末に退職、ターンエンドと相成りました。

転職活動のほうは、各方面に迷惑をかけつつも、1月からちゃんと就職できることになりました。ありがとうございました。

となると12月無職になるわけですが、友人各位はご存知のように(?)当方は貯金など一切無く、12月どうやって行きていけばいいのかワカラン、仕方ないので持ち前のコネクションとコミュ力ゲノム解析の単発受託などをやり糊口を凌いでおりました。

そう、まさにこの "hotsub" を使ってね!

このままでも「hotsub」を使って受託をこなすのはドチャクソ便利なんですが、やはりソフトウェアというのは、実際に運用してみて始めて洗練されていくもので、いろいろな課題や要望が見えてきました。

そのうちの一つが「hotsubというエンジンに依存しないジョブインターフェースの定義」であります。

この延長に「ワークフロー記述方法」が透けて見えており、かなり手応えを感じます。(時すでに退職おそし感)

今後の展望

すでにhotsub*1がCWLの実行を部分的にサポートしているのですが、今後はさらにhotsubが完全に「CWLのクラウド上実行のエンジン」となっていく展望は具体的に見えているのですが、いかんせん俺はもうバイオインフォ関係ない人になっちゃう。

加えて、CWLのコミュニティ活動の一環として上記 yacle*2の実装も、時間さえあれば進めていくつもりですが、もちろん給料など出ないのでありますから、牛の歩みになってしまうと思われます。

興味関心としても、上記のように「CWLは絶対のワークフロー記述方法ではない」という立場ですので、今後は僕は「CWLの動向をかなり遠くから見守りつつ、興味範囲でエンジンの実装と、独自ワークフロー記法の発明を進める」という感じになっていくと思われます。

まとめ

ドイツから帰ったとき、いきなり個人事業に集中していたら、たぶんこんなに業界のことを知れなかっただろうし、ネットワークもつくれなかったと思う。特に、CWL界隈・ワークフロー界隈・ワークフローミートアップのお歴々には、ソフトウェアへの貴重なご助言・フィードバックをいただき、彼ら無しでは僕はきっとバイオインフォで一切成果を出せなかったとすら思います。マジで。

なんかまともな謝辞になった。

まぁ、まとまらないんですけど、まとめると、「さようなら、CWL」となります。

WETな DRYな備忘録として