On note_com from August 24 to 31, 2021.

Summary of articles posted in the last week.

直近一週間に投稿した記事のまとめ

Qt5再入門: 状態の保存|チブル・システムズ|note

Qt5再入門: 状態の読み込み|チブル・システムズ|note

Qt5再入門: QStandardItem|チブル・システムズ|note

Qt5再入門: QStandardItemModel|チブル・システムズ|note

Qt5再入門: シグナル/スロット|チブル・システムズ|note

Qt5再入門: モデルデータ 追加と削除|チブル・システムズ|note

Qt5再入門: ブックマークの保存、復元|チブル・システムズ|note

Qt5再入門・・・計7件

On note.com from August 17 to 23, 2021.

Summary of articles posted in the last week.

直近一週間に投稿した記事のまとめ

Notes C API探訪: NLS_string_bytes/NLS_string_chars(関数)|チブル・システムズ|note

Notes C API探訪: NLS_translate(関数)(その1)|チブル・システムズ|note

Notes C API探訪: NLS_translate(関数)(その2)|チブル・システムズ|note

Notes C API探訪: LMBCS文字列のクラス化|チブル・システムズ|note

Notes C API探訪: LMBCS文字列とQString|チブル・システムズ|note

Notes C API探訪: PathNetをラップする|チブル・システムズ|note

Notes C API探訪: データベースハンドルをラップする|チブル・システムズ|note

Notes C API探訪・・・計7件

突然Git Pushができなくなった時の一つの解

それはある日突然訪れた。

取引先と共有しているBitBucket上のリモートリポジトリに、ローカルでコミットしたソースコードがPushできなくなった。ソースコードはSails.jsサーバ上で組んでいるもので、クライアントはReactを使用してビルドしたものだ。

Sails.js | Realtime MVC Framework for Node.js

React – ユーザインターフェース構築のための JavaScript ライブラリ

これまで問題なく使えていたのだが、ある日突然次のようなログに見舞われPushできなくなった。

Enumerating objects: 47, done.
Counting objects: 100% (47/47), done.
Delta compression using up to 12 threads
Compressing objects: 100% (29/29), done.
fatal: the remote end hung up unexpectedly
Connection to bitbucket.org closed by remote host.
fatal: the remote end hung up unexpectedly

少量の変更なら問題なく使えたので、どう切り分ければいいのか皆目見当が付かない。

主眼を「大量データのPush」に絞り、 git push fatal hung up のようなキーワードでググり、.git/config上で ssh.postBuffer をいじってみたり、~/.ssh/config上に ServerAliveInterval を設定してみたりと手を尽くしたものの、一切変化がなかった。

もしやBitBucket特有のものかと思っていたら、別の取引先でGitHub上に共有していたソースコードも同様のログとともにPushできない事象に見舞われた。OMG

実は問題なく使えていた時と、問題が発生した時の環境で唯一違いがあった。それは「引越し」をしたことだ。プロバイダや回線は全く一緒なのだが、引っ越したことでネットワーク環境に何らかの差異が発生したのではと思った。

そこで自宅のLAN環境を無効にして、モバイルWi-Fiルーターで試してみた。が、問題は解決しなかった。なんやねん!

目の前が真っ暗になりかけたその時、検索画面に次の記事の検索結果が引っかかった。

GitLabとSSH接続 - Qiita

根本的な解決方法ではなさそうなものの、見出しが気になりリンクをたどると、内容は「ssh接続をhttpsポート(443)でつなぐ」というものだった。なるほど、22番以外でも接続することはできるのかと思い、失敗していたGitHub.comへの接続設定に適用してみた。

「こいつ・・・動くぞ!」

なんとこれまでウンともスンともPushできなかったコミットが、さらっと completed のメッセージとともにPushを完了した。そこで問題のBitbucketの方もやってみたところ、こちらも問題解決!見事に突破した。

22番ポートの何がダメになってしまったのか、そもそもの話はまるでわからないままだが、443番ポートを介することで、できなかったことができたことに違いはない。

参考にしたサイトに謝意を述べるとともに、リンクを掲載しておく。もし postBufferServerAliveInterval などで一切解決できなかった方はお試しあれ。

BitbucketにHTTPSポートでSSH接続する方法 – Memoteki

22 番ポートが通らない環境で GitHub / BitBucket / GitLab を SSH 経由で利用する方法 - Qiita

GitリポジトリサーバのSSH鍵を使い分け

自分用のメモ。

基本的には以下のページが非常に参考になる。

qiita.com

謝意を述べたい。

これに、私なりの理解や補足を加えておく。

結論

同一のリポジトリサーバ(GitHubやBitBucketのような)内を、目的やアカウントで、使用するSSH鍵を使い分けたい場合、SSHのホスト設定とGitのローカル設定を駆使すればできる。

Gitグローバル設定

まず、Gitのグローバル設定は空っぽが望ましい。Gitをインストールすると、大抵 git config --global user.email の設定を推奨されるが、アカウントの使い分けをする上では「うっかりミス」を誘発してしまう。もしEメールを使い分けるなら user.email をグローバル設定には置かないようにする。もしあるなら以下のようにする。

$ git config --global --unset user.email

SSH設定

例えば、A社用とB社用でGitHubで使うEメールアドレスやSSH鍵を使い分けなければならない場合、それぞれにSSH鍵を発行、登録していたとして、~/.ssh/configファイルに次のように設定する。

Host github_a_company
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_github_a => これの公開鍵をA社アカウントでGitHubに登録
    IdentitiesOnly yes

Host github_b_company
    HostName github.com
    User git
    IdentityFile ~/.ssh/id_rsa_github _b => これの公開鍵をB社アカウントでGitHubに登録
    IdentitiesOnly yes

Gitローカル設定

あとはプロジェクトのディレクトリに移動し、Gitのローカル側設定を以下のようにする。

$ cd project_a
$ git init
$ git config user.name "My Name"
$ git config user.email myname@a.com
$ git config url.github_a_company.insteadof git@github.com
$ cd project_b
$ git init
$ git config user.name "My Name"
$ git config user.email myname@b.com
$ git config url.github_b_company.insteadof git@github.com

こうすると、例えばA社のプロジェクトでソースコードをPushすると、

$ cd project_a
$ git remote -v
origin  git@github.com:myaccount/myproject.git (fetch)
origin  git@github.com:myaccount/myproject.git (push)
$ git push origin master

Push時のホスト名は以下のように変化する。

git@github.com => github_a_company (.git/configを適用)
github_a_company => git@github.com (~/.ssh/configを適用)

?元に戻っただけ?

否!

github_a_company を適用した時点で、A社用の「SSH公開鍵」が使われるようになっている。

Gitクローン時

なお、 .git/config がまだ存在しない、例えば git clone 操作する場合はどうするか? その場合、ホスト名を ~/.ssh/config に設定した Host xxxxxx を代わりに使えばできる。

例えば、B社用のSSH鍵でsomeprojectのソースコードをクローンしたい場合は次のようにする。

$ git clone git@github_b_company:someaccout/someproject.git

あとは必要に応じてプロジェクト内でGitを初期化、ローカル設定すればいい。

DominoでGraphQL

DominoサーバでGraphQLを作れたらいいな・・・と思ったので、超簡単なプロトタイプを作ってみました。

GraphQL、ご存じない方のために簡単に説明すると、「つながり」でできたデータ構造に問い合わせてデータを取得したり、更新したりすることができる「クエリ仕様」と言えばいいだろうか。

Facebook社が開発したんだけど、Facebook内のデータっていろんなところに「つながり」を持っていて、それを捌くのに開発したんじゃねっていう理解。なので、ツリー構造になっているNotesデータベース構造って、GraphQLでも扱いやすいんじゃないかと思ったのがきっかけ。

GraphQLはスキーマを決めて、スキーマに沿って問い合わせればいい、至極簡単。でもそれって使う側の言い分。実際問題、提供する側は結構面倒。JSONっぽいQueryは似て非なり。なので改めてQueryを字句解析しないといけない。C++での実装はそこが骨の折れるところ。でも優秀なライブラリを見つけたので、今回はこれのおかげ。

github.com

Microsoftとあるけど、LinuxMacでもできるらしい。

ちなみに、試しに作ったスキーマはこれ。

schema {
  query: Query
}

type Query {
  dbDirectory(rootPath: String!): [DbNode!]!
}

interface DbNode {
  path: String!
  isDirectory: Boolean!
}

type DbDirectory implements DbNode {
  path: String!
  isDirectory: Boolean!
  dbDirectory: [DbNode!]!
}

type Database implements DbNode {
  path: String!
  isDirectory: Boolean!
  replicaId: String!
}

最初の schema はGraphQLの3つの機能、問い合わせ(Query)、変更(Mutation)、サブスクリプション(Subscription)のどれを使うかってことらしい。サブスクは基本的にWebSocketなどがないと動かない。Domino HTTPのアドイン(DSAPI)ではおそらくなんともしがたいと思う。

今回はデータの取得だけなので、Queryのみを定義する。

続く type Query は、その問い合わせの仕様について。 dbDirectory というキーワードに rootPath でどのディレクトリ内にあるデータベースやサブディレクトリを取得するかを特定する引数を定義している。後ろの [DbNode!]! は、 DbNode のリストを意味する戻り値の仕様。

interface DbNode は先の DbNode の仕様に他ならないが、先頭が type ではなく interface なのがミソ。Javaなどのインターフェースと同義で、仕様のみで実装はない。Notes C APIではNotesデータベースとディレクトリって扱いが似ていて、ディレクトリもデータベースと同じくデータベースハンドル(DBHANDLE)で扱う。なので、両社に共通のパス名と、ディレクトリであるか否かを判定できるブール値を仕様にしている。

type DbDirectory ではその DbNode を実装してディレクトリを定義している。データベースとの違いは、さらに別のデータベースやディレクトリを配下に持てるので、再帰的に dbDirectory を持っている。最初の Query との違いは、すでにパスを保持している点にある。

type DatabaseDbNode を実装しつつ、ディレクトリにはないものを実装する。今回レプリカIDを取得できるようにしてみた。

例えば、これを使って、 mail ディレクトリ内のデータベースを取得しようと思ったら、次のようなGraphQLを書けばよい。

query {
    dbDirectory(rootPath: "mail") {
        path
        isDirectory
        ...on Database {
            replicaId
        }
    }
}

これでDomino GraphQLに問い合わせると、

{
    "data": {
        "dbDirectory": [
            {
                "path": "mail\\admin.nsf",
                "isDirectory": false,
                "replicaId": "49257D76xxxxxxxx"
            },
            {
                "path": "mail\\hkobayas.nsf",
                "isDirectory": false,
                "replicaId": "492581B9xxxxxxxx"
            },
            {
                "path": "mail\\kshiden.nsf",
                "isDirectory": false,
                "replicaId": "492581B9xxxxxxxx"
            },
            {
                "path": "mail\\rhosei.nsf",
                "isDirectory": false,
                "replicaId": "492581B9xxxxxxxx"
            }
        ]
    }
}

のようなJSONデータが返ってくる。なお、 replicaId を付与すると、一つ一つDBをオープンしてレプリカIDを取得するような実装方法を取ったため、取得するDBやディレクトリが多いと途端に遅くなるので、改善の余地がある。

GraphQLがRESTなどに比べてすごいところは、取りたいデータをコントロールするすべを仕様が定義しているところ。例えば、先ほどのクエリを以下のように変えてみる。

query {
    dbDirectory(rootPath: "mail") {
        path
    }
}

すると、返ってくるデータは次のようになる。

{
    "data": {
        "dbDirectory": [
            {
                "path": "mail\\admin.nsf",
            },
            {
                "path": "mail\\hkobayas.nsf",
            },
            {
                "path": "mail\\kshiden.nsf",
            },
            {
                "path": "mail\\rhosei.nsf",
            }
        ]
    }
}

求めたいフィールドを変更すると、戻り値のフィールドも変更される。GraphQLはそれを仕様として定義している。RESTでももちろんできるが、仕様ではないので、手法は実装者に委ねられている。

今回は本当に序の口。まだまだ紹介できるレベルには達していない。でもなんか元旦早々嬉しくなったので、早出ししてみた。