Technology Engineering

178inaba の技術ブログ

GitHub Gistにファイルをアップロードするコマンドをgolangで作った

Gistを作る時、毎回catでファイル内容を表示してコピーして、GistのWeb画面にペーストしてたんだけど、
これが地味にめんどくなってきたので、ファイルを渡すとGistを作ってくれるコマンドをgolangで作った。

github.com

使い方

  • target.goをGistにアップロードする場合。
$ gistup target.go
  • 複数ファイルをGistにアップロードする場合。
$ gistup target.go target_test.go target_linux.go
  • 標準入力をGistにアップロードする場合。
$ stdin | gistup

アップロードが成功するとブラウザで作成されたGistのページを開きます。
(開けなかった場合はURLを表示。)

デフォルトでは最初にユーザ名とパスワードでログインを求められます。
(後述のオプションで匿名を選択すればログイン不要。)

また、デフォルトでは非公開のGistとして作成されます。
(こちらもオプションで公開のGistとして作成することも可能です。)

オプション

  • -a
    • 匿名のGistを投稿します。
  • -d <description>
    • Gistの説明を追加します。
  • -n <file_name>
    • 標準入力からGistにアップロードする場合のファイル名を設定します。
    • 指定しない場合はGistのデフォルトgistfile1.txtになります。
  • -p
    • パブリック(公開)のGistを投稿します。

所感など

入力時のコンテキストキャンセル検知

ユーザ名、パスワード入力は下記のようにコンテキストのキャンセルを検知できるようにしている。

func readString(ctx context.Context, hint string, readFunc func(t *tty.TTY) (string, error), t *tty.TTY) (string, error) {
    fmt.Printf("%s: ", hint)
    ch := make(chan string)
    errCh := make(chan error)
    go func() {
        s, err := readFunc(t)
        if err != nil {
            errCh <- err
        }
        ch <- s
    }()
    var s string
    select {
    case <-ctx.Done():
        return "", ctx.Err()
    case s = <-ch:
    case err := <-errCh:
        return "", err
    }
    return s, nil
}

理由はgistupはInterruptシグナル(Ctrl+C)を受けてコンテキストをキャンセルする仕組み(下記参照)になっているのだが、

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, os.Interrupt)
ctx, cancel := context.WithCancel(context.Background())
go func() {
    <-sigCh
    cancel()
}()

これだと、入力中にCtrl+Cを押しても終わらない。
なので、ゴルーチンで入力処理を実行して、コンテキストのキャンセルと、入力終了をselectで同時に待つようにした。

結果、入力中にCtrl+Cを押すと終了するようにできた。

ユーザ名、パスワードの入力はTTYを使用

gistupではユーザ名とパスワードの入力にTTYを使用している。

理由は、最初、標準入力からユーザ名とパスワードを取得していたけど、
標準入力をGistにアップロードする箇所を実装してる時にユーザ名、パスワードの取得ができなくなる事に気づいた。

いろいろ調べたけど引き続き標準入力からユーザ名、パスワードを取得するのは難しそうで、調べて出てきたのはperlの質問でTTYを使っているやつだった。

stackoverflow.com

「goでTTYって確かmattnさんが作ってたよな」と探したらあって、

github.com

ただ、パスワードを読むメソッドしか無かったのでReadStringメソッドをプルリクエストした。
その後、速攻でマージして頂けたのでユーザ名の入力にはReadStringを使用。

結果、ユーザ名、パスワードを入力しつつ、標準入力から取得した文字列をGistにアップロードすることができた。

まとめ

これでコピペせずにGistにアップロードできるようになった。

またなんか作ったら書きます。

あと、PR大歓迎です😆