ぽよメモ

レガシーシステム考古学専攻

deanishe/awgoを使って簡単にAlfred Workflowを作る

はじめに

 これはGo3 Advent Calendar 2019,7日目の記事です.

qiita.com

Alfred Workflowとは?

 macOS向けの多機能ランチャーであるAlfredの自動化のためのプラットフォームです.Alfredとは,簡単に言ってしまうとmacOSのSpotlightの機能強化版です.

www.alfredapp.com

 過去に以下のようなものを作りました.

poyo.hatenablog.jp

poyo.hatenablog.jp

poyo.hatenablog.jp

最後の一つはAlfredのUIを一切使っていないので少し毛色が違いますが,上2つの様な,

  1. 何らかのトリガーとなるコマンドに対して
  2. 選択肢を提示し
  3. ユーザの選択によって何かアクションを起こす

というものを作りたくなったときに便利に使えます.なお,Alfred本体は無料なのですが,それとは別にPowerPackと呼ばれる拡張パッケージを購入することで使えるようになります.現在のメジャーバージョンでのみ使えるSingle License(25ユーロ),永久ライセンスのMega Supporter(45ユーロ)という二つの形態のライセンスがあるのでお好きな方を購入してください.

www.alfredapp.com

Alfred Workflowの仕組み

 様々な形態のWorkflowがありますが,基本的には用意されているコンポーネントGUIで繋いでいき,あるコンポーネントから別のコンポーネントへ,文字列を受け渡して様々なアクションを起こします.

 自作のスクリプト/バイナリを使ってAlfredのUIを使用したい場合,Script Filterというコンポーネント内でそのスクリプト/バイナリを実行し,所定の形式を持つJSON*1を標準出力へprintします.そのJSONをAlfredが読み取り,良い感じにUIを表示して,選択されたオブジェクト(の中の,次のコンポーネントへ渡す引数となる文字列)を接続されているコンポーネントへ受け渡します.

 実行できるアクションには様々な物があり,

  • ブラウザで検索
  • URLを開く
  • 任意のアプリケーションで開く
  • 任意のスクリプトを実行する

などなど,多数のアクションが用意されています*2.こうしたアクション以外にも,クリップボードへコピーしたり,通知をだしたりも出来ます.便利ですね.

Alfred WorkflowとGo

 Alfred Workflowで動かすアプリケーションをGoで書くというアプローチは,数年前から行われています.

techblog.kayac.com

blog.tsub.me

kyokomi.hatenablog.com

これらのサイトでも述べられていますが,

  • シングルバイナリになるので簡単に配布して簡単に使える

という部分が最も大きいです.全てをシェルスクリプトで書ける猛者ならまだしも,どんなモジュールが入っているか分からないPythonPerlで頑張るよりも簡単かつ安全かと思います.

github.com/deanishe/awgo

 今までは自分の手で書いたstructをjson.Marshal()していましたが,便利なライブラリが開発されていました.

github.com

Alfred3および4用のライブラリで,どちらでも使うことが出来ます*3

 また,表示するアイテムの出力だけでなく,様々な機能のサポートが含まれています.長くなるので今回は紹介しませんが,一通り調べてみたので機会があればそれについても記事を書きたいと思っています.

チュートリアル

以下の環境で実行しています.

  • macOS 10.15
  • Go 1.13
  • Alfred 4.0.6
  • github.com/deanishe/awgo v0.21.0

あまり良い題材を思い浮かばなかったので,今回はこのAlfred Workflowで使った依存パッケージのライセンスを閲覧できるAlfred Workflowを作ってみようと思います.ビルドしたバイナリを配布する場合,使用した各モジュールのライセンスに則って著作権表示等が必要となります*4

f:id:pudding_info:20191206182837g:plain
選択したモジュールのLICENSEを表示

リポジトリはこちら

github.com

1. 空のWorkflowを作る

左下の+からBlank Workflowを選択すると新しいWorkflowを作成するウィンドウが開きます.

f:id:pudding_info:20191205224816p:plain
Blank Workflow

Bundle idはとにかく一意な値であれば良いので,自分の持っているドメイン等があればそこから適当な値を自分で考えれば良いと思います.ここで記述した内容は後からでも変更可能なので,あまり迷う必要はありません.

2. 実行するアクションを並べる

 依存パッケージとライセンスの取得には以下のパッケージを利用させて頂きます.

github.com

これは出力方式としてJSONを選択することができ,その場合各モジュールの名前とLICENSEファイルの中身を取得できます.閲覧のためにはその中身を一旦ファイルに出力する必要があるので,以下の様な構成とします.

  1. モジュール名とライセンスファイルの中身から表示するアイテムを作成するスクリプトを実行
  2. 選択したモジュールのライセンスが文字列で渡されるので,これをWorkflow内のテンポラリディレクトリに出力
  3. そのファイルを規定のプログラムで開く

加えて,Modifierを使ったときの例も示したいので,1の後にCommand + Enterを押すことでそのモジュールのURLをブラウザで開く,というタスクも追加します.

コンポーネントは,右クリックして出てきたメニューから選択して作成できます.各コンポーネント間はドラッグで紐付けることが出来ます.虫のマークはデバッグコンポーネントで,そのコンポーネント間でやりとりされる引数や変数を見ることが出来ます.

f:id:pudding_info:20191206163051p:plain
コンポーネント一覧
コンポーネントの設定はそのコンポーネントをダブルクリックで開けます.

f:id:pudding_info:20191205230549p:plain
Script Filterの設定

f:id:pudding_info:20191205230746p:plain
Write Text Fileの設定

Open File,Open URLコンポーネントはデフォルトのままです.Command+Enterでアイテムを選択したときにOpen URLへ動作を分岐させる方法は,コンポーネント間の接続(エッジ)をダブルクリックすると出てくるメニューから,使用するModifierにチェックを入れるだけです.他のModifierもあるので,更に様々な操作を行うことが出来ます.

3. info.plistを手に入れる

次に,左にあるWorkflow一覧から,このWorkflowを選び,オプションメニューからFinderで開くを選択します.

f:id:pudding_info:20191206163352p:plain
Open in Finder

info.plistというファイルがあると思うので,それを次に作るGoのプロジェクト内にコピーします.このファイルを単体で編集するのは骨が折れるので,基本的にコンポーネントの操作等はAlfredのUI上で行い,それをその都度Goのプロジェクトへコピーするようにしています.

4. Goでプログラムを書く

 まずはプロジェクトを作り,deanishe/awgoをインストールします.

$ mkdir sample-alfred-workflow
$ cd sample-alfred-workflow
$ go mod init
$ go get github.com/deanishe/awgo

Songmu/gocreditsが吐くJSONは以下の様なフォーマットなのでそれに合わせてstructを作ります.

{
  "Licenses": [
    {
      "Name": "モジュール名",
      "URL": "URL",
      "FilePath": "ファイルパス",
      "Content": "LICENSEの中身"
    }
  ]
}

license.goとして以下の内容を記述します.特に難しいところは無いはずです.

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
)

// 各モジュールごとのLicenseを表すstruct
type license struct {
    Name     string
    URL      string
    FilePath string
    Content  string
}

// モジュールごとのライセンスを全て内包するstruct
type licenseJson struct {
    Licenses []license
}

// 与えられたパス名からライセンスを読み取り,ライセンスの一覧を返す
func getLicenses(path string) ([]license, error) {
    contents, err := ioutil.ReadFile("./credits.json")
    if err != nil {
        return nil, fmt.Errorf("%s does not exists.\n", path)
    }
    var licenses licenseJson
    if err = json.Unmarshal(contents, &licenses); err != nil {
        return nil, fmt.Errorf("Failed to parse %s.\n", path)
    }
    return licenses.Licenses, nil
}

ようやくdeanishe/awgoを使ってメインのロジックを書いていきます.とは言っても,この程度のAlfred Workflowならば非常に簡単になります.

package main

import (
    "fmt"
    "os"
    "strings"

    aw "github.com/deanishe/awgo"
)

var (
    // このプロジェクト全体で参照するaw.Workflow
    wf *aw.Workflow
)

func init() {
    // 初期化時にインスタンスを生成する
    wf = aw.New()
}

func run() {
    licenses, err := getLicenses("./credits.json")
    if err != nil {
        // wf.Fatal~~は,エラーメッセージを出力して終了させる
        wf.FatalError(err)
    }
    for _, c := range licenses {
        // ループを回して一つずつItemを作っていく
        // Itemの持つ各メソッドはItem自身を返すのでメソッドチェーンで書ける
        item := wf.NewItem(c.Name).
            // Argは選択されたときに次のコンポーネントへ渡す引数となる文字列
            Arg(c.Content).
            Subtitle(strings.Split(c.Content, "\n")[0]).
            // Valid(true)としないと選択できないので注意
            Valid(true)
        // そのアイテムがCommand+Enterで選択されたときの動作を変更する
        item.Cmd().
            Arg(c.URL).
            Subtitle(fmt.Sprintf("Open %s in browser", c.URL)).
            Valid(true)
    }
    // 与えられた文字列でアイテムをフィルタリングする
    args := os.Args
    if len(args) > 1 {
        // https://godoc.org/github.com/deanishe/awgo/fuzzy
        wf.Filter(args[1])
    }
    // 最終的に表示すべきアイテムが無かったときに表示するエラー文
    wf.WarnEmpty("No credits were found.", "Try different query.")
    // 標準出力へ最終的なJSONをプリントする
    wf.SendFeedback()
}

func main() {
    // 内部でpanic等をうまくハンドリングしてくれる
    wf.Run(run)
}

wf.NewItem()を呼び出した時点で表示するアイテム一覧の中にそのアイテムが追加されるため,どこかにappendしたりする操作は不要です.

6. パッケージングする

 ググっているとAlfredのUI上からExportしないとAlfred Workflowとしては動作しないというように言われていますが,実は.alfredworkflowというファイルの実態は単なるzipファイルです.そのため,単に必要なファイルを全てzipでアーカイブにして拡張子を変えるだけで済みます.今回は以下の様なMakefileを用意しました.

SHELL := /bin/bash

PLIST=info.plist
CREDITS=credits.json
EXEC_BIN=sample-alfred-workflow
DIST_FILE=sample.alfredworkflow
GO_SRCS=$(shell find -f . \( -name \*.go \))

all: $(DIST_FILE)

$(CREDITS): go.sum
  gocredits -json . > $(CREDITS)

$(EXEC_BIN): $(GO_SRCS)
  go build -o $(EXEC_BIN) .

$(DIST_FILE): $(EXEC_BIN) $(CREDITS) $(PLIST)
  zip -r $(DIST_FILE) $(PLIST) $(CREDITS) $(EXEC_BIN)

単にmakeとするだけでsample.alfredworkflowが生成されます.

$ make
# 関連付けされているのでAlfredが開いてインストールできるはず
$ open sample.alfredworkflow

7. CI/CDする

 GitHub Actionsが正式リリースになっているのでこれを使いましょう..github/workflows/release.yamlとして保存します.

name: Release
on:
  push:
    tags:
      - "v*"
jobs:
  release:
    runs-on: macos-latest
    steps:
      - name: Checkout source codes
        uses: actions/checkout@v1
        with:
          fetch-depth: 1

      - name: Setup Go environment
        uses: actions/setup-go@v1
        with:
          go-version: 1.13

      - name: Restore cache if available
        uses: actions/cache@v1
        id: cache
        with:
          path: ~/go/pkg/mod
          key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
          restore-keys: |
            ${{ runner.os }}-go-

      - name: download modules
        if: steps.cache.outputs.cache-hit != 'true'
        run: go mod download

      - name: Build
        run: make

      - name: Create new release
        id: create_release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false

      - name: upload release asset
        id: upload-release-asset
        uses: actions/upload-release-asset@v1.0.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./sample.alfredworkflow
          asset_name: sample.alfredworkflow
          asset_content_type: application/zip

これでタグを付けると以下の様にリリースされます🎉

f:id:pudding_info:20191206181142p:plain
First release!

macOSのセキュリティ

 Catalinaでは,署名されていない,インターネットからダウンロードしたバイナリの実行をデフォルトでブロックします.そのため,この方法でAlfred WorkflowをGitHub Releasesからダウンロードすると以下の様に警告を吐かれます.

f:id:pudding_info:20191206181331p:plain
警告その1

システム環境設定 > セキュリティとプライバシーから実行を許可して再度実行するとまた警告が出ます.

f:id:pudding_info:20191206181521p:plain
警告その2

ここで「開く」を選択すると,ようやく次からAlfred Workflowを使用できます.
別にマルウェアなどは仕込んでいないので安心してください……

deanishe/awgoの微妙なところ

  • 何もかもstructなのでテストしづらい
    • aw.Workflowがあらゆるビジネスロジックを担っている巨大structなので剥がしづらい
    • 小さいInterfaceを自分で定義していくのがいいかも

  • グローバルなaw.Workflowを使い回す設計なのでテストしづらい
    • wf.Run(fn func())の中で実行することを想定しているので,この設計以外で使えない

  • wf.SendFeedback()がDIの機構を持たないので出力をテストしづらい
    • 直接stdoutに書き込んでいて外側からインジェクション出来ない
    • wf.Feedback.MarshalJSON()で生成されるJSONをテストするだけで凌ぐ…?

  • wf.NewItem()がgoroutine safeじゃない
    • 内部的には全てのアイテムはwf.Feedbackが管理しており,wf.NewItem()wf.Feedback.NewItem()を呼んでいる
    • FeedbackのItemsに単に作ったItemをappendしており,goroutineで普通に叩くと壊れる https://github.com/deanishe/awgo/blob/master/feedback.go#L421
    • 普通にaw.NewItem()みたいなのとwf.AddItem()みたいなのがあれば十分だったと思う

  • wf.NewItem()が返すItemがデフォルトでvalid=falseになっている
    • valid=falseなオブジェクトはUIに表示できるが選択できない(エラーメッセージとかを表示するのに使う)
    • デフォルトがfalseなのはAlfredの文脈的には直感に反する
    • 作者の意見:github.com

紹介しきれていない機能

リポジトリに上がっている参考実装がかなりためになります.https://github.com/deanishe/awgo/tree/master/_examples

などなど

まとめ

  • Alfred WorkflowをGoで書くならdeanishe/awgoは機能が豊富で便利
  • GitHub ActionsでCI/CDも出来る
  • macOS Catalinaのセキュリティ機能はかなりおせっかい
  • deanishe/awgoは癖が強いのでテストはちょっと色々考える必要がある

皆さんの面白いAlfred Workflowをお待ちしています.


*1:www.alfredapp.com

*2:www.alfredapp.com

*3:Alfred2まではJSONではなくXMLを使って居ました.これはdeprecatedになっています.Alfred3および4はJSON形式に対応しています https://www.alfredapp.com/help/workflows/inputs/script-filter/xml/

*4:加えてGoの自体のライセンス(BSD3条項ライセンス)も含める必要があります.これ知らなかったので,今後気をつけます…

内部向けWikiに技術記事を書くモチベーションと意義,そして今の悩み

はじめに

 これはあくあたん工房アドベントカレンダー2019,2日目の記事です.技術の欠片もないポエムです.

 他の記事はこちらからご覧頂けます.

adventar.org

内部向けWikiとは?

 例えばサークル・研究室・企業など,何らかの集団において,そのメンバーが様々な情報の共有に使用するドキュメンテーションツールを指しています.どういったツールを使っているかは,その集団の特性等に大きく左右されると思います.例えば

などなど,色々なツールが開発・利用されています.僕自身はサークル・研究室共にesa.ioのお世話になっています.
 まだ学生の身なので企業内部でのWikiの使われ方はよくわかりませんが,今回の記事の中には通ずる部分もあるだろうと思っています.

ありがちな問題

 こういったツールの問題として,基本的に「よく書く人と全く書かない人に分かれる」というのがあると考えています.書かない人たちからは

  • 書くことが無い(何を書いたら良いか分からない)
  • ググれば出てくる
  • 書かなくてもみんな知ってる

などの声を聞くことがあります.特に,「既にググれば出てくるであろう知識」を書くモチベーションは欠けてしまいがちになっているように感じます.

ググって出てくるから書かなくて良い?

 そもそも,ググれば出てくる==書く必要が無いは本当に真なのでしょうか.

 現代のインターネットは非常に便利で,どう考えてもマイナーな内容でもググれば数件はヒットするという世界です.しかし一方で,現代は情報が氾濫しており,情報の取捨選択が難しいという問題が起きています.ゆえに自分に代わって情報の取捨選択をしてくれるキュレーションメディアが流行し,乱立していると言えるでしょう.

 また,Google検索にはノイズも多く,

  • プログラミングスクールの薄っぺらい記事
  • StackOverflowの機械翻訳サイト
  • 既に使えなくなっている古代の知識で書かれたブログ

などなど,分かっている人にとってはただただ邪魔な,分かっていない人にとってはより混乱の原因となるノイズが多数紛れています*1

 まとめると,ググって出てくる情報は

  • あまりにも数が多く,その価値の判断が難しい上に時間がかかる
  • 多数のノイズを効率よく弾くだけの基礎知識が必要

ということです.内部向け技術記事ではこの逆を目指していきたいというのが今回の記事の主旨です.

みんなに書いてもらうために

身内だけで通じる価値観を理解する

 一部のスーパープログラマを除いて,大抵の技術者は世の中からほとんど認知されていません.その実力がどれくらいなのか,何に詳しいのか,どういうことに興味があるのか,そういった情報は基本的に明らかではありません. そのため,インターネット上に溢れる大抵のまとめ記事は価値が非常に低く見積もられがちです*2

 一方で,特定の集団内においては互いのことをある程度知っています.そのため,その人の書く記事がたとえ同じようなまとめ記事だったとしても信頼度が大きく変わってきます.もちろんその信頼性は多くの場合身内だけに通じるものなので,外部に公開するかどうかはそれを書いた本人がその価値を見積もって考えれば良いと思います.少なくとも,内部に向けた情報を記述するに当たって,ググれば出てくる情報の焼き増しをすることを恐れる必要は無い,ということは言えると思います.

 つまり「ググれば出てくるから書かない」と言われたら,

  • インターネット上の赤の他人の記事よりは,身内の記事の方が信頼できる(質問もできる)
  • 既存の記事をまとめただけのものであっても,自分で探す手間が省けるから助かる

と言いましょう.

気軽に書ける雰囲気を作る

僕は少し前にサークル内向けに以下の様なLTをしました.


 そもそも書くハードルを低くしておかなければ誰も書いてくれません.そのためには使いやすいツールを用意することが不可欠です.僕は前述したようにesa.ioを活用させて頂いており,その利便性に日々感謝しています.おすすめです.

 また,どういった内容を書くのかについて,小さいチームなので特にルールを定めていません.弊サークルではメンバーごとに自分のネームスペースを持っているため,ただのメモ書きのようなものも好きに書けるようにしています*3.大きいチームでの運用に当たってはこの辺りの「遊び(余裕)」の調整が必要かとは思います.

既にある記事の更新を躊躇わない

 知識はどんどん古くなっていきます.記事として残した情報も,いつかは古くなってしまう物です.「これは自分より詳し い〜〜さんが書いた物だから」とか「自分が書いても間違ってるかも」とか思わずに,どんどん更新されていくべきです.

 簡単なtypo,実行するコマンドの変更,一部APIの使用方法の変化,…etc,細かい変化は常に起こり続けています.気が付いたら「バージョンXXからはYYはZZになりました」程度で良いので更新していければ,記事が陳腐化していくことを避けれるのではないでしょうか.これは個人ブログではなかなかできないので,多数の人間が関わって管理しているからこそ得られるメリットであると考えています.

 集団で知識を蓄えていくというのはそういうことであり,上で述べた「書くハードル」にも近いですが「更新するハードル」も低くあるべきです.そのためには個人間で変な縄張り意識を持たず,自分の書いた記事への他の人からの変更を歓迎することが大事かも知れません.GitHubでPull Request出してくれた人にThank youと言うのと同じような文化が技術記事にも根付くと嬉しいです*4

書くメリットを意識する

 「そうは言われても書いたからって給料が上がるわけじゃないが?」みたいなことを言われればそれはそうなのですが,そもそも書くメリットを認識できていない人が多いのかなという気がしています.個人的に思っているメリットについていくつか書きます.

  • 自分自身の理解の向上に繋がる
  • 書くことで集団内での自分のブランディングができる
    • 〜〜に詳しい人,みたいな
  • 他人に教えるときに「とりあえずこれ読んで」と言える
  • 他人に理解してもらう文章を書くのは難しいので,普段からそれを意識して書いておくことは,対外発表の練習にもなる

これは内部Wikiではなく,公開するブログに書く場合も同様なので,そもそも他人に見てもらうものを書く習慣があるかどうかの違いが大きいと思います.不特定多数に対して公開するブログと違って,明確にその集団のレベルアップに貢献できるという意味で,内部Wikiの方がメリットはわかりやすいかも知れません.

 これは別にメリットではないんですが,僕は普段色々なブログ等にお世話になっているので,何か貢献できたらいいなという気持ちでブログを書いています.「GitHubでコントリビュートするのは難しいけど」みたいな人は,感謝の気持ちを込めてブログを書きましょう.

あるべき内部Wikiの姿

 何か調べたいときに,まず最初に調べる場所が内部Wikiになるのがベストだと個人的には思っています.

 そこには既に見知った人間がまとめた情報があるはずで,自分でググりながら真偽を確かめていく過程を省くことが出来るからです.その上で,その内容では足りない・間違っているなど追加/修正の必要が生じれば記事を更新する,そもそも記事が無く今後も必要となると判断したなら記事を書く,そういうサイクルをうまく回していけると,ナレッジベースとしての内部Wikiがうまく機能するのではないでしょうか.

今の悩み

どのレベルから知識を共有するか

 当然個人間のスキルセットには差があるため,全員がどこまで理解していて,どこから分かっていないかを決めることは難しいです.例えばDockerの使い方について記事を書くとして,僕は「仮想化」というもので何を実現したいのか,どういうことが起きるのかをなんとなく理解していますが,今までそういったやり方に触れてこなかった人にはまずそこの説明から必要になってきます.しかしそれを書くためにじゃあLinuxカーネルってなんだ,そもそもLinuxってなんだ,OSとは?コンピュータとは?みたいなことまで書くかと言われれば,(僕自身の知識不足ももちろんありますが),時間的にも,費用対効果的にもしないでしょう.

記事が増えすぎたときにどうなるのか

 今はせいぜい数百程度の記事しかなく,一覧で見てもなんとかなります.しかしこれが数千,数万に膨れ上がったら?それはソフトウェアで解決するのだろうか?というのは気になっています.

 もちろん検索はあるので,キーワード検索やタグ検索などの方法をとることは可能です.しかし,「検索したいワードが分かっていないと検索は出来ない」という問題があり,そもそもどう検索すれば良いのかわからないシチュエーションでは内部Wikiもうまく効果を発揮できません.そもそもそれ以上時間をとらずに済むからWikiに書いたのに,「Wikiに書いてある内容を検索するための質問」とかをされるようになったら本末転倒です.

結局当人以外が記事を更新しない

 特に研究室などでは,数年ごとに人が入れ替わります.そのため,過去に誰かが書いた記事がそのまま放置され,閲覧も更新もされない死んだ情報になってしまう可能性があります.属人性を排除するために記事として残したのに,その情報の修正/更新が属人化してしまっては元も子もありません.

 更新されない理由として

  • どういう記事があるのか知らない
  • それを更新して良い物なのか判断できない

などがあると思っていて,今どういう記事が存在するのかをなんとなくでいいので把握しておくことが重要だと思います.僕は新しく記事を書いたときにSlack等で「こういうのを書きました」みたいなことを一言言うようにはしていますし,後輩と話しながら「じゃあそれ追記しておいて」とかは気軽に言うようにしています.とはいえ,そもそもSlackすらまともに見てくれない人たちや,自分がいなくなった後に入ってくる人たちにまで知らせることは難しいです.僕は他の人がどういうものを書いたのか見るのが好きで,更新があれば軽く覗いたりコメントしたりしているのですが,全員がこれをやる必要があるとまでは絶対言えないので,何かうまい対策が必要だなと感じています.

 また,内容を盲目的に信じ切ってしまう人が出てくるのも課題だと感じています.「書いてあるから」「そうらしいから」などと信じ切ってしまう人が出てくると,その情報は更新もされずただただやってみたが上手くいかなかった「使えない情報」として処理されたり,時代錯誤なやり方が残って負の遺産となってしまう可能性があります.個人のレベルはまちまちなので仕方ないと言えばそれまでなのかも知れません.

最後に

 情報が氾濫している現代では,情報の価値はそれを書いた人を自分がどれくらい信頼できるかで決まっていると感じています.同じ内容を知らない学生が言うのと,親しい友人が言うのではその重みが違うように,インターネット上でも知っている人の書いた情報と,赤の他人が書いた情報の間には大きな隔たりがあるはずです.
 内部Wikiは現代では貴重な「ある程度信頼できる人が集まって作ったナレッジベース」であり,そこに情報を蓄積することは,自分だけでなく組織全体にとって意味があることです.全員が発信していける場を作り,全員で高め合っていけると最高ですね.


*1:僕はuBlacklistのお世話になっています

*2:一部例外もあります.何書いてもバズる人というのが世の中にはいます

*3:スマブラのキャラ対策・論文のメモ等色々乱立しています

*4:Qiitaの編集リクエスト機能はまさにこれのはずなのですが,全然うまく機能しているように思えません.悲しい

分割HHKB配列が実現できる自作キーボードキットChoco60を買った

はじめに

 素晴らしいキットを制作,販売しておられるrecompile keysさん,自作キーボード関連の製品を手広くてがけておられる遊舎工房さんに感謝します.

Choco60とは?

 recompile keysさんが今年開発された自作キーボードキットで,特徴としては以下の点が上げられます.

  • CherryMX互換キースイッチ対応の62キーの分割キーボード
  • HHKBをそのまま分割したような配列
  • アンダーグロー非対応
  • アクリルプレートサンドイッチマウント

詳細は以下の記事をご覧ください.

recompile.net

最上段の配列を削った40%キーボードのCocoa40という姉妹モデルもあります.

recompile.net

どこで買える?

 遊舎工房で委託販売されています.また,イベントへの出展時に少数頒布されている場合もあるようなので,公式のTwitterアカウント(@recomplie_keys)をフォローするのが良いと思います.

yushakobo.jp

売り切れになっていることが多い人気のキーボードキットですので,欲しい人はTwitterアカウントをフォローしてツイートの通知をオンにしておくと良いと思います.

なぜChoco60?

 以前まではMint60を使用していました.

poyo.hatenablog.jp

これはこれで大変良いキーボードキットで,一番最初にこれを経験できたのは大変幸運だったと感じています.しかし,上の記事中でも言及しているように,バックスラッシュやチルダ,バックスペースの位置がデフォルトだと合わず,HHKBライクにキーマップを変更して使って居ました.物理的に右上のキー数が足りないため,

  • HHKBのESCの位置にチルダ
  • ESCはAの横のコントロール単押しで対応
  • 通常のUS配列のバックスラッシュとバックスペースを入れ替え

という様な使い方をしていたため,しばらくHHKBを触った後に戻ってくると,混乱してESCの代わりにチルダとか,チルダの代わりにバックスラッシュとかを乱発したりするようになってしまいました*1

 研究室ではMint60,バイト先と家ではHHKBを使って居るため,できればHHKBに合わせたいところです.

  • 耐久性と信頼性の観点から,仕事で使う道具はHHKB ProfessionalのUS配列が良い(Type-Sだとなお良い)
  • でも分割キーボードは身体が楽なのでプライベートではなんとかしたい
  • HHKBを単に二つ並べても,Fnがもう片方には伝播しないので難しい
  • じゃあ分割キーボードでHHKB配列だ!

となるのは当然の流れだと思います.地味にHHKB配列は通常のUSともそこまで大きく乖離しないので,MacBookを使っても,ESCやチルダなど一部のキーを除けばちゃんと打てるのも個人的にはポイントが高いです.

キースイッチやキーキャップなど

 recompile keysさんが技術書典で頒布された冊子Learning Custom Mechanical Keyboardを参考に,個人的にはリニアよりもタクタイルの方が好みだったため,以下の様な選択になりました.全て遊舎工房で購入することが出来ます.

  • キースイッチ:ZealPC Zilent V2 62g
    • ハウジング等の潤滑:Tribosys 3203
    • スプリングの潤滑:Krytox GPL 105

キーキャップは当初JTK HyperFuseを検討していたのですが,お金が無くて手が出ず,次点で検討していた以下のキーキャップと同等の物が遊舎工房の店舗にあったのでこれを購入しました.

talpkeyboard.stores.jp

キースイッチの潤滑

 具体的な方法は下記の記事が大変参考になります.

keys.recompile.net

 キースイッチオープナーは下記の物を注文しました.

make.dmm.com

素材はMJFのPA12GBです.エクスプレスサービスを利用したので通常注文はわかりませんが,注文してから4日目に手元に届きました.

後はAmazonでマイクロアプリケーターを買いました.ルブステーションは使用しませんでした.感想としては「これを1日でやりきるスケジュールにした自分をひっぱたきたい」です.

ホームポジションの目印を作る

 このキーキャップはFキーとJキーの位置に触って分かる目印が付いていません,個人的にはかなりこれに頼っているところがあるため,自作することにしました.

 作り方は道具さえあれば難しくなく,UVレジンとUV照射器を使い,FキーとJキーに少しだけレジンを垂らして固めるだけです.少なくとも頑張って剥がそうとしてもびくともしない,くらいには頑丈になります.耐久性はこれから評価します.

遊舎工房の店舗では他に

  • 瞬間接着剤とラインストーンを使う
  • 虫ピンを熱して刺す

などを紹介していただけました.ありがとうございます.

作り方

基本的には以下のサイトの通りです.キットにも特に説明書等は付属しないので,これを見てさっぱりわからんという人には難しいと思います*2

keys.recompile.net

1. ダイオードを基板に刺す

f:id:pudding_info:20191104124907j:plain
基板とダイオード

 キットの中身からまずは基板とダイオードを取り出します.適当にニッパで切りながら,基板に刺していきます.僕は指で適当に曲げながら差し込んでいきましたが,汚いので出来ればリードベンダー*3とかを使った方が良いです.

 また,基板から浮いてしまうとスイッチと干渉するため,足を折り曲げるだけでなく,マスキングテープを使って固定した方がやりやすいです*4.足を先に切る派,半田付け後に切る派がいるらしいですが,どちらの場合でもニッパは小さい方がやりやすいです*5

f:id:pudding_info:20191104125656j:plain
全てのダイオードを半田付けした様子

2. TRRSジャックとリセットスイッチのはんだづけ

 本家の説明が少しわかりにくいのですが,Choco60というロゴのある方が表面で,recompile keysというロゴのある方が裏面になります*6ダイオードは表面から付けて,裏面からはんだ付けしたことになります.

f:id:pudding_info:20191104130457j:plain
TRRSジャックは裏面から付けて表面からはんだ付けする

f:id:pudding_info:20191104130549j:plain
リセットスイッチも同様に裏面に付けて表面からはんだ付け

3. スタビライザーを装着

 これは絶対lubeした方が良いことが経験的に分かっているので,少し過剰なくらいlubeしました.高い潤滑剤を使うともったいないので,Super lubeで十分です.それ以外には特に工夫はしていません.

f:id:pudding_info:20191104131051j:plain
左右のシフトとスペースキーにスタビライザーを付ける

4. 表側アクリルプレートを装着

 先に表になるアクリルプレートを装着するのですが,順番的に先に3mmのスペーサーと8mmのネジをアクリルプレートに固定すると楽です.

f:id:pudding_info:20191104131427j:plain
小さい方が3mmのスペーサー

短い方のネジでは明らかに長さが足りないのですぐ気付くと思います.長い方のネジを使って固定します.

f:id:pudding_info:20191104131533j:plain
表側アクリルプレートにネジとスペーサーを固定

これを基板の表面側(ダイオードのある側)から,各ネジの位置を合わせてはめ込み,裏側から長い方のスペーサーで固定します.無理に硬く締めると割れる可能性があるので,対角線上から順に少しずつ締めましょう.

f:id:pudding_info:20191104131803j:plain
アクリルプレートをはめた様子

5. キースイッチをはめ込んではんだ付け

 少し押し込んで固定することになるのですが,このときアクリルプレートを一緒に押し込まないように注意しましょう.割れます.

 

f:id:pudding_info:20191104132012j:plain
全ての位置にキースイッチをはめる

 後は裏面からはんだ付けするのですが,このときキースイッチが浮き上がっていないか確認しましょう.キーキャップを付けたとき,仕上がりが凸凹になってしまいます.

6. ProMicroをはんだ付け

 最近のChoco60にはコンスルーが付属しているのでこれに準拠します.

f:id:pudding_info:20191104132514j:plain
基板にコンスルーを刺す

 ProMicroを裏向けにしてかぶせます.スペーサーの高さから分かるとおり,かなりギリギリです.少し浮かせてしまうと,簡単に干渉します.気をつけてはんだ付けしましょう.

f:id:pudding_info:20191104132603j:plain
ProMicroを裏向きにしてはんだ付け

7. ボトムプレートを装着

 裏面からボトムプレートを装着し,ゴム足を貼り付けます.僕はコンスルーのピンとボトムプレートが少し干渉してしまい,若干たわんでいます.締める際は対角線上から順に,少しずつ締めていき,大丈夫か確認しましょう.

f:id:pudding_info:20191104132817j:plain
裏面アクリルプレートとゴム足を装着

8. キーキャップをはめる

 裏返して全てのスイッチにキーキャップを装着します.今回のキーキャップセットには合うスペースキーがなかったため,Shiftで代用しています.「-」と「=」が入れ替わっているのはこれを書いている最中に気付いて直しました…

f:id:pudding_info:20191104133230j:plain
完成!

ファームウェアを書き込む

 Mint60はデフォルトでファームウェアが焼かれたProMicroが同梱されていたのですが,こちらは自分で書き込んでからで無いと動作確認もできません,幸いビルド環境を持たない人のために,デフォルトのhexファイルが頒布されているので,こだわりのない人はこれを使いましょう.

https://qmk.fm/compiled/choco60_default.hex

Dockerでビルド

 前回の記事から少し更新があり,QMKが公式のDockerコンテナを出しました.少し使い勝手が違うので,紹介します.

# 自分でキーマップを書く人向け
$ git clone --recurse-submodules https://github.com/qmk/qmk_firmware
# 適当にキーマップを書く
$ KEYMAP_NAME=キーマップ名
$ cp -r qmk_firmware/keyboards/choco60/keymaps/default qmk_firmware/keyboards/choco60/keymaps/${KEYMAP_NAME}
$ vim qmk_firmware/keyboards/choco60/keymaps/${KEYMAP_NAME}/keymap.c
# Dockerコンテナにクローンしたリポジトリをマウントしてビルドする
# qmk_firmwareディレクトリまでの絶対パスを自分の環境に置き換える
$ docker run --rm \
    -v $PWD/qmk_firmware:/qmk_firmware \
    qmkfm/qmk_firmware \
    make choco60:${KEYMAP_NAME}
# .hexファイルができる
# ls -l | grep hex
-rw-r--r--    1 pudding  staff  45333 Nov  4 13:50 choco60_pudding.hex

avrdudeで書き込み

 最初に,macOS Catalinaの人はこちらを読んでください.

poyo.hatenablog.jp

それ以外の人は,普通にavrdudeで書き込みが出来ます.裏面のリセットスイッチを押し,以下のコマンドを実行します.

$ sudo avrdude -p atmega32u4 -c avr109 -U flash:w:choco60_${KEYMAP_NAME}.hex

使ってみた感想

f:id:pudding_info:20191104141530j:plain
普段の作業風景

 やはり慣れている配列というのは素晴らしく,新しいキーボードなのにすぐに手に馴染みます.キースイッチをタクタイルにしたのも個人的にはかなり正解だったと感じていますし,リニア軸に馴染めなかった人は検討してみると良いのではないでしょうか.作る上でもそんなに難しい箇所はなく,作りやすいキットだったと感じています.lubeしたり,macOS Catalinaでファームウェア書き込みするために調べまくっていた時間の方が,はんだ付けしている時間よりはるかに長かったです.
 本当は家用にもう一台欲しいのですが,これ一台を作るために(いくら諸々の初期投資費用がかかっているとは言え)既にHHKB Professionalを2台くらい買える金額になり始めているので,ちょっと難しそうです.

まとめ

  • 写真はちゃんとミラーレス一眼で撮れば良かった…
  • Choco60はHHKBをそのまま左右に分割したような自作キーボード
  • 作る上で難しいところは特になく,初心者でもおすすめできる
  • macOS Catalinaはまだちょっとやめておいた方がいい

この記事はChoco60 + Zilent V2 62gで書きました.もう少し軽いタッチでも良かったので,リーフもlubeして良かったかも.


*1:Vimmerとしては致命的

*2:そんな人おるんか…?

*3:決まったピッチで綺麗に折り曲げられる道具. https://www.amazon.co.jp/dp/B00J3E11VQ/ref=cm_sw_r_tw_dp_U_x_bf6VDbEJQT8E

*4:僕は手元に無かったので,ダイオードをまとめているテープ(写真の白と赤のテープ)を剥がして使いました.明らかにこれはバカなので,マスキングテープくらい用意しましょう.

*5:僕はデカいニッパで基板に傷を付けました

*6:これはもしかしたら初期ロットと少し違うかも知れません