野良 Scrapbox アプリ - 自分が作成したページ一覧表示と v1.0 リリース

社内で Scrapbox を使っていると大量のページが日々作られ、作成したページが埋もれて忘れてしまうことがよくあります。書きかけで放置してしまうことも。

そこで、作成したページを時折見直し加筆修正する契機になるよう、自分が作ったページ一覧を表示する機能を追加しました。

Scrapbox の API でページ一覧を取得すると、ページ毎に作成ユーザの ID が取得可能です。そこで、作成者のユーザ ID がログインユーザ ID と一致するものをフィルタリングして独立したタブに表示するようにしています。

f:id:kondoumh:20190824173419p:plain

頻繁な API 呼び出しを避けるために取得したデータはローカルにキャッシュするようにしました。

自分では作成していないけど編集したページの情報も表示したいところですが、こちらは個別ページの API でしか取れず、通信量が多くなってしまうので実装はしませんでした。ひとまずクライアントサイドでできることはこのレベルかなと。

今回のリリースで v1.0 にしました。0.9 まで来てたというのもありますが、Electron v6 にアップデートできたのが大きいです。v5 では WebView で Scrapbox のページが表示されなくて、ずっと v4 から移行できずにいました。最新の Chrome に追従できたので 1.0 ということにしました。

f:id:kondoumh:20190825115910p:plain

Release v1.0.0 · kondoumh/sbe · GitHub

GitHub Actions (beta) を使ってみる

今年の5月にベータ公開されてすぐに申し込んでいました。3ヶ月ぐらい経って私のアカウントにもローリングでリリースされました。

github.com

元々 HCL*1 ベースでグラフィカルな UI で構築する方式でしたが、私が使えるようになった時には HCL は非推奨となり 他の多くの CI ツール同様 YAML が標準になりました。

ワークフローがない状態で Actions の画面を開くとプロジェクトの内容に応じたワークフローの雛形がサジェストされます。一度作ってしまうとサジェストされなくなりますが、雛形は以下のリポジトリに集まっているようです。

github.com

例によって Spring Boot のサンプルアプリで試してみます。

github.com

Maven の雛形とDocker image の雛形から作成しました。

f:id:kondoumh:20190822211854p:plain

f:id:kondoumh:20190822223252p:plain

2つのジョブから構成しています。

  • package: Spring Boot アプリのパッケージ(ビルド、テスト、JAR 作成)
  • build-image: Docker イメージビルド

各ジョブは別の Docker インスタンスで実行されます。build-image は needs を使って package の後続ジョブとしています。

name: Example CI

on: [push]

jobs:
  package:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v1
    - name: Set up JDK 1.8
      uses: actions/setup-java@v1
      with:
        java-version: 1.8
    - name: Build with Maven
      run: ./mvnw package

    - uses: actions/upload-artifact@master
      with:
        name: sb-sample-service.jar
        path: ./target/sb-sample-service.jar

  build-image:

    needs: package
    runs-on: ubuntu-latest
 
    steps:
    - uses: actions/checkout@v1
    - uses: actions/download-artifact@master
      with:
        name: sb-sample-service.jar
        path: ./target

    - name: Build the Docker image
      run: docker build . --file Dockerfile --tag sb-sample-service:$(date +%s)

ビルド成果物の JAR ファイルを ジョブ間で受け渡しできるよう、公式 Action から upload-artifact と download-artifact を使ってみました*2

github.com

github.com

Actions のタブから実行結果を確認できます。

f:id:kondoumh:20190822224105p:plain

ステップ毎に詳細なログを取得できます。upload-artifact によって保存された JAR ファイルをダウンロードすることも可能です。

イメージのビルドもログを見ると成功してタグ付けされたものができているようです。

Run docker build . --file Dockerfile --tag sb-sample-service:$(date +%s)
Sending build context to Docker daemon  33.23MB

Step 1/3 : FROM openjdk:8-jdk-alpine
8-jdk-alpine: Pulling from library/openjdk
e7c96db7181b: Already exists
f910a506b6cb: Pulling fs layer
c2274a1a0e27: Pulling fs layer
f910a506b6cb: Download complete
f910a506b6cb: Pull complete
c2274a1a0e27: Verifying Checksum
c2274a1a0e27: Download complete
c2274a1a0e27: Pull complete
Digest: sha256:94792824df2df33402f201713f932b58cb9de94a0cd524164a0f2283343547b3
Status: Downloaded newer image for openjdk:8-jdk-alpine
 ---> a3562aa0b991
 Step 2/3 : COPY ./target/sb-sample-service.jar sb-sample-service.jar
 ---> f103d9d489ff
Step 3/3 : ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/sb-sample-service.jar"]
 ---> Running in be018d0c3ee0
Removing intermediate container be018d0c3ee0
 ---> 7297e5983b31
Successfully built 7297e5983b31
Successfully tagged sb-sample-service:1566479900

今回はイメージをビルドして終わりでしたが、これまたベータ公開が始まっている Package Registry を使ってビルドしたイメージを Publish することができるようになると思われます*3

github.com

ところで、GitLab もリポジトリ単位で CI / CD を定義できましたが、登録できるパイプライン定義は1つでした。

blog.kondoumh.com

このため、単体テスト、結合テスト、リリースビルドなど様々な目的でパイプラインを作成すると .gitlab-ci.yml が巨大で複雑なものになりがちです。GitHub Action ではワークフローが複数定義可能ですので、様々な目的のワークフローを程よい単位で作成できるのではないでしょうか。

以上、GitHub Actions を簡単に試してみました。リリース版ではさらに完成度が上がっていることを期待しています。

参考記事:

www.kaizenprogrammer.com

*1:HashiCorp configuration language

*2:廃止になったグラフィカルなエディタがあれば、このような Action をオーサリングするのが楽だったのでは・・と思います。

*3:現時点では私のアカウントではまだ使えません。

野良 Scrapbox アプリ - 直リンページャー機能

Scrapbox ページのリンク先をパラパラめくって閲覧するような UI が欲しいという要望がありました。ページ集約用のタグとして使われているページを開いて、リンク先の(そのページをタグに指定している)ページを順番に見ていくような使い方です。

Scrapbox ではリンク先のページが本文の下に並ぶので要らないのでは? と思いましたが、UI の練習問題と思って検討してみることにしました。

まず、ページ毎のリンクですが、直接リンク (1hop links) は API の JSON から relatedPages/links1hop で取れます。例えば、以下のページ

scrapbox.io

の直リンは

{"relatedPages" : 
  {"links1hop": [
      {"id":"5c67490adb266f001783335b","title":"UML"},
      {"id":"5c67490adb266f001783335c","title":"BPMN"},
      {"id":"5c67490adb266f0017833361","title":"DDD"},
      {"id":"5c6783776deb58001725c685","title":"Portfolio"}
    ]
  }
}

のような形で取れます。

さて、リンク先の内容を見るための UI ですが、最初に考えたのは、現在表示しているページのタブ内でリンク先のページを開くナビゲートをすることでした。しかし、通常の画面遷移と同じ扱いになり、ナビゲーションの UI を直感的にわかりやすく作れる気がしませんでした。

そこで、ページ情報やプロジェクトアクティビティを表示するのに使ったモーダルダイアログを使う方法で行くことにしました。

blog.kondoumh.com

blog.kondoumh.com

最初、ダイアログに WebView を貼り付けてリンク先のページを次々に読み込むようにしてみました。悪くはないのですが、ページ読み込みがもっさりするのと、読み込んだページにもリンクがあって、さらに遷移できてしまうというちょっとカオスな感じになります。

そこで、ページの本文だけをプレーンテキスト*1で取得して表示するようにしてみました。簡素ですが読めなくはありません。先ほどの JSON にはリンク先の概要(本文の先頭数行)も入っています。全文を取得するにはページ毎に API で取得する必要があるため、最初に概要を表示しておいて、全文取得後に置き換えるようにしました(取得データのキャッシュもしてます)。

ページをめくっている途中で、気になるページを後でちゃんと読みたい場合、裏で別タブで開いておく機能もつけました。

f:id:kondoumh:20190811131437g:plain

本当はタブ単位でダイアログを出してタブ間移動できる方がより直感的かと思いますが、このアプリではタブ自身も DOM で描画していてちょっと厳しいということでこの UI に落ち着きました。

Release v0.9.0 · kondoumh/sbe · GitHub

*1:見出しは分かるようにしました。

RHEL8 で Tig をビルドして使う

Red Hat Enterprise Linux 8 がリリースされて AppStream で最新の OSS が利用できるようになっています。システムが使うソフトウェアとユーザが使うソフトウェアが独立してインストールされるようですね。

access.redhat.com

Git や Emacs の最新版が yum (dnf) install 一発で入るので RHEL7 までの不自由さ *1 がかなり緩和されました。

ただ、愛用している Tig の rpm が提供されていません。

github.com

extra な yum リポジトリを探せばあるのかもしれませんが、面倒なのでビルドしました。自分の環境では make とビルド・実行に必要なライブラリ ncureses が入ってませんでした*2。cureses / ncureses は Tig のような TUI (Text User Interface) を実装するのに利用されるライブラリです。

$ sudo yum -y install ncurses-devel ncurses make

tig のリポジトリから最新リリースのソースコードを取ってきてビルドします。

$ curl -kOL https://github.com/jonas/tig/releases/download/tig-2.4.1/tig-2.4.1.tar.gz
$ tar xfvz tig-2.4.1.tar.gz
$ cd tig-2.4.1/
$ ./configure
$ make
$ sudo make install

無事使えるようになりました。

f:id:kondoumh:20190801145023p:plain

*1:CentOS のリポジトリから入れたり自分でビルドしたり・・

*2:インストール構成により異なる可能性があります。

静的サイトジェネレーターは Hugo を選びました

放置しているホームページですが Google Web Starter Kit で作った作成環境がとうとうエラーで起動しなくなりメンテナンス不能に陥りました。

一応、Static site generator に移行しやすいようにディレクトリ構成は昨年に見直していました。

blog.kondoumh.com

Static site generator は golang 製の Hugo にしました。

gohugo.io

golang のツールらしく環境構築はバイナリダウンロードしてパスを通すだけとシンプルです*1し、生成に特化しているところもいいかなと思いました。生成自体も速いです。

Theme は色々試すのが面倒で、最初に知った Ananke でいいやってなりました。

themes.gohugo.io

Ananke ではデフォルトの OGP 情報は、config.toml の [params] に書いておけば Hugo の template に渡して生成してくれます。更新頻度を考えると RSS は不要かなということで disableKinds で生成を抑止しました。

baseURL = "https://kondoumh.com/"
languageCode = "ja-jp"
title = "kondoumh Home"
theme = "ananke"
   :
disableKinds = ["RSS"]

[params]
  description = "kondoumh home page. write about software I made."
  images = ["/images/mh.png"]
   :

OGP 情報を上書きしたい場合、ページのヘッダーに個別に埋め込めます。

---
title: "iEdit"
date: 2019-07-25T00:41:11+09:00
description: "iEdit : idea processor"
images: ["/images/iedit/iedit_icon.png"]
draft: false
---

layouts はあまりいじってませんが、_default/baseof.html をオーバーライドして、RSS のリンクを削除してます。Ananke のナビゲーションメニューは、content の構造をスキャンして生成してくれます。全部出るとウザいので partials/site-navigation.html をオーバーライドして固定メニューを出すようにしました。

Hugo には shortcode というテンプレート機能を使ったスニペットが用意されています。

Shortcodes | Hugo

これを使うと YouTube や Twitter のリッチなリンクは簡単に作れます。しかし、汎用的な外部リンク用の shortcode は提供されていません。ということで、はてなブログカードの API を使って iframe で表示したりしています。

とにかく、やっと Google Web Starter kit とお別れできて HTML 編集も不要になりました。Netlify で公開すればワークフローがシンプルになりますが、ドメインごと引っ越しするのも面倒なのでレンタルサーバにアップロードするスタイルは変わってません。

kondoumh.com

サイトのコードは GitHub で管理しています。

github.com

2019.8.24 追記)

Ananke theme のソーシャルアイコンにはてなブログと Tumblr が収録されていないので SVG データを持ってきて追加しました。

f:id:kondoumh:20190824180047p:plain

*1:Homebrew などでもインストールできます

kind (Kubernetes IN Docker) を Docker Desktop for Mac で使う

kind は Kubernetes クラスターを Docker 上で動かすツールです。

github.com

コンテナオーケストレーションツールである Kubernetes 自体をコンテナ上で動かす・・ちょっとイメージしづらいですが、コンテナをノード *1 として利用することで、Minikube のようなシングルノードではない真のマルチノードが構成できて、コンテナ仮想化の恩恵により構築も速い。要らなくなったらサクッと消せる Immutable なクラスター環境が手に入ります。

Docker Desktop for Mac 環境に kind をインストールします。Docker Desktop の Kubernetes は無効化しておきます。kubectl も最新版をインストールしておきました。

kubernetes.io

kind コマンドを実行するユーザが sudo なしで docker コマンドを実行できるようにする必要がありますが、Docker Desktop の場合はすでにそうなっています。

Quick Start の通り、Go の最新版を導入。自分の場合 Go は brew ではなく公式のインストーラで入れてました。

golang.org

go と gopath に PATH を通します。

export PATH=$PATH:/usr/local/go/bin
export PATH=$PATH:$(go env GOPATH)/bin

go get で kind をインストールします。$GOPATH 配下にインストールされます。

$ GO111MODULE="on" go get sigs.k8s.io/kind@v0.4.0

とりあえず、クラスターを作成してみます。

$ kind create cluster

f:id:kondoumh:20190708001126p:plain

数分で作成されました。kubectl の環境変数を整えます(クラスター名はデフォルトで kind になっています)。

$ export KUBECONFIG="$(kind get kubeconfig-path --name=kind)"

nginx をデプロイして、ポートフォワードして確認してみます。

$ kubectl create deployment nginx --image=nginx
$ kubectl create service nodeport nginx --tcp=8080:80
$ kubectl port-forward --address localhost svc/nginx 8080:8080 

f:id:kondoumh:20190708234532p:plain

OK です。

Kubernetes のパッケージマネージャ Helm を導入してみます。

helm.sh

$ brew install kubernetes-helm

$ helm init --history-max 200
    :
$HELM_HOME has been configured at /Users/masa/.helm.

Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.

$ helm version
Client: &version.Version{SemVer:"v2.14.2", GitCommit:"a8b13cc5ab6a7dbef0a58f5061bcc7c0c61598e7", GitTreeState:"clean"}
Server: &version.Version{SemVer:"v2.14.2", GitCommit:"a8b13cc5ab6a7dbef0a58f5061bcc7c0c61598e7", GitTreeState:"clean"}

インストールはできました。

$ kubectl get pod -n kube-system
NAMESPACE     NAME                                         READY   STATUS    RESTARTS   AGE
  :
kube-system   tiller-deploy-6bfc8fcbf4-v825k               1/1     Running   0          30s

Helm のクラスター内サービスの Tiller も上がっています。しかしパッケージをインストールしようとすると、Error: no available release name found と言われてしまうので serviceacount の設定をします。

github.com

$ kubectl create serviceaccount --namespace kube-system tiller
serviceaccount/tiller created
$ kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
clusterrolebinding.rbac.authorization.k8s.io/tiller-cluster-rule created
$ kubectl patch deploy --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'
deployment.extensions/tiller-deploy patched

MySQL を Helm Charts でインストールしてみます。

$ helm install stable/mysql

NAME:   zinc-hog
LAST DEPLOYED: Wed Jul 31 23:29:40 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME                 DATA  AGE
zinc-hog-mysql-test  1     0s

    :
To connect to your database directly from outside the K8s cluster:
    MYSQL_HOST=127.0.0.1
    MYSQL_PORT=3306

    # Execute the following command to route the connection:
    kubectl port-forward svc/zinc-hog-mysql 3306

    mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}


$ kubectl get pod

NAMESPACE     NAME                                         READY   STATUS    RESTARTS   AGE
default       zinc-hog-mysql-67cd9bcc85-qjr7b              1/1     Running   0          30m

インストールできたようです。ポートフォワードして クラスターの外から繋げるようにしておきます。

$ kubectl port-forward svc/zinc-hog-mysql 3306
Forwarding from 127.0.0.1:3306 -> 3306
Forwarding from [::1]:3306 -> 3306

mysql client で接続してみます。

$ MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace default zinc-hog-mysql -o jsonpath="{.data.mysql-root-password}" 
$ MYSQL_HOST=127.0.0.1
$ MYSQL_PORT=3306
$ mysql -h ${MYSQL_HOST} -P${MYSQL_PORT} -u root -p${MYSQL_ROOT_PASSWORD}
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 469
Server version: 5.7.14 MySQL Community Server (GPL)

Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

接続できました。

普通の Kubernetes クラスターとして使えてますね。

マルチノードクラスターを作るのも、kind 用の config を書いて create すれば数分でできてしまいます。

# a cluster with 1 control-plane nodes and 3 workers
kind: Cluster
apiVersion: kind.sigs.k8s.io/v1alpha3
nodes:
- role: control-plane
- role: worker
- role: worker
- role: worker

内部的には kubeadm が使われていて、config で色々な追加設定が可能です。CI で色々な環境を擬似的に作成するのにも向いていそうです*2。Canonical が開発している MicroK8s も導入が簡単で Immutable な クラスター環境が構築できますが、構成のカスタマイズはさほど柔軟ではありません。

microk8s.io

kind はリリース前の WSL2 もサポートしていたりして、開発での利用を重視していることが伺えます。

https://kind.sigs.k8s.io/docs/user/using-wsl2/

こんなイケてる kind ですが、ググラビリティが低いのがちょっと残念です。

ちなみに社内ネットワークの proxy 配下の環境では現時点の最新版(v0.4.0) では deployment 作成時イメージ取得ができませんでした。v0.3.0 以降コンテナランタイムが containerd にスイッチされていて関連する issue が上がっていました。

github.com

コンテナランタイムに Docker を採用している v0.2.1 は proxy 配下でも動作してました。

*1:Kubernetes クラスタを構成するワーカーマシン

*2:もともと kind は Kubernetes 自身のテストのために設計されたそうです。

VS Code の Remote Development (Preview) を体験

VS Code 1.35.0 から Remote Development Extension (Preview) が使えるようになりました。

marketplace.visualstudio.com

ちなみに、このバージョンから VS Code は Stable と Insider という2つのリリースを出すことになり、それぞれのアイコンもリニューアルされています。

code.visualstudio.com

macOS の VS Code から VirtualBox の仮想マシン(Ubuntu) で Remote Development を使えるように設定してみます。事前に公開鍵認証で SSH できるようにしておく必要があります。

ゲスト OS 側 で /etc/ssh/sshd_config を編集して一時的にパスワード認証を有効化。

PubkeyAuthentication yes
PasswordAuthentication yes

sshd を再起動。

$ sudo systemctl restart sshd

macOS 側で公開鍵をゲスト OS に転送*1

$ ssh-copy-id masa@192.168.33.10
The authenticity of host '192.168.33.10 (192.168.33.10)' can't be established.
  :
Are you sure you want to continue connecting (yes/no)? yes
  :
Number of key(s) added:        1

Now try logging into the machine, with:   "ssh 'masa@192.168.33.10'"
and check to make sure that only the key(s) you wanted were added.

ゲスト OS 側 で SSH のパスワード認証をオフに戻して sshd を再起動。macOS 側で公開鍵認証でログインできることを確認。

$ ssh masa@192.168.33.10

Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-54-generic x86_64)

masa@ubuntu-bionic:~$ 

VS Code に Remote Development をインストールしたら、接続設定を行います。

~/.ssh/config を指定。

f:id:kondoumh:20190706215413p:plain

接続先設定を追加。

f:id:kondoumh:20190706220102p:plain

接続開始。

f:id:kondoumh:20190706215615p:plain

接続に成功するとステータスバーがコネクト状態に。

f:id:kondoumh:20190706220255p:plain

フォルダビューに切り替えるとリモートのフォルダを開くように促されます。

f:id:kondoumh:20190706220801p:plain

Git のリポジトリを clone したフォルダを開くとローカルでプロジェクトを開いている感じになります。ターミナルを開くとすでに SSH 接続状態になっています。

f:id:kondoumh:20190706223255p:plain

当然、リモート側には開発環境を構築しておく必要があります。ホストマシンの VS Code にインストールした言語用拡張については、シンタックスハイライトは効いていますが、コード補完などは動いていません。ここで拡張ビューに切り替えるとリモートマシンのセクションができていて、リモート用に拡張をインストールできるようになっています。

f:id:kondoumh:20190706231257p:plain

Go 拡張をインストールすると、リモートマシンのセクションにアイコンが現れ、拡張に必要な go のライブラリもリモート側にインストールされました。

f:id:kondoumh:20190706231610p:plain

コード補完も効いており、ローカル用の Go 拡張の設定がそのまま適用されている模様。

f:id:kondoumh:20190706231958p:plain

プレビュー版でこの完成度は凄いです。

最近は、EC2 インスタンスやローカルの仮想マシンに SSH して作業することが増えて、マシンを作るたびに Emacs や tmux をインストールするのも面倒なので *2 苦手な Vim を使うことが増えてきています。プログラミング言語だけでなく、YAML の設定ファイルを編集したりするのも VS Code の拡張が使えて、フォルダ構成が一覧できる状態で作業できるのは非常に助かります。

*1:ゲスト OS の ~/.ssh/authorized_keys に追加されます。

*2:特に RHEL / CentOS では自分でビルドしないと最新版が使えなかったり