カレーのライスをtech忘れ

odmishienのtechメモ

GitHubのプロフィールに表示されるREADMEをいい感じにする

人に見てもらう意味でも、自分を客観的に見つめる意味でも、技術ポートフォリオをアップデートしていく作業は大切だなと思う。でも改めて自分のスキルや技術スタックをまとめるのは色々面倒。

割と今更ですが、今回はサクッとGitHubのプロフィールのトップに表示されるREADMEをいい感じにします。こんな感じになった。(一応)バックエンドエンジニアなのにHTMLが多くなってしまうの、悔しい。

github.com

f:id:odmishien:20201020090458p:plain
イケてる感じになった

リポジトリとREADMEを準備する

何はともあれ、READMEとそれを置いておくリポジトリが必要。公式ドキュメントにも書いてあるように、

  • 自分のユーザー名と同じ名前のパブリックリポジトリを作る(odmishien/odmishien みたいな感じ)
  • リポジトリのルートに README.md を置く

を実行しておけば、自身のプロフィールページ(https://github.com/{username})にREADME.mdの内容が表示される。

GitHub Profile Summary Cardsを表示する

こんな便利なActionsを用意してくれている人がインターネットには存在する。

github.com

これを先ほど作ったリポジトリに仕込んでおけば、勝手にプロフィールに表示される言語やコミットのスタッツのカード画像を更新してくれる。手順としては、

.github/workflows 以下に GitHubActionsを定義するyamlファイルを作成

  • secrets.GITHUB_TOKENgithub.repository_owner は特に何もしなくても Actions側で読んでくれる(便利!!)
  • カード画像の生成タイミングはcronを仕込めるので、お好みのタイミングを指定しておく
  • github-profile-summary-cardsのREADMEにも書いてあるが、GitHub側のキャッシュが残ったりするのでcronで仕込んだ時間と表示されるプロフィール画面とで少しタイムラグが生じることはあるらしい

gist0ce2d68a7d9de2d166ca0efd55d908b3

Private Repository の情報も必要な場合は secretsPERSONAL_ACCESS_TOKEN を追加しておく

  • Access Tokenは Settings > Developer Setting > Personal access tokens のページで Generate New Token をすればOK
  • 権限は repo にフルアクセスをしておけば問題なさそう
  • secrets への追加手順は 公式ドキュメントが参考になる

README.md に好みの画像のパスを追加する

  • 正しくworkflowが実行されたら、 profile-summary-card-output 以下にカード画像が生成される
  • 10種類くらいカラーテーマがあるので好きな色の好きなパーツのパスを指定する
[![](https://raw.githubusercontent.com/odmishien/odmishien/master/profile-summary-card-output/nord_dark/0-profile-details.svg)](https://github.com/vn7n24fzkq/github-profile-summary-cards)
[![](https://raw.githubusercontent.com/odmishien/odmishien/master/profile-summary-card-output/nord_dark/1-repos-per-language.svg)](https://github.com/vn7n24fzkq/github-profile-summary-cards)
[![](https://raw.githubusercontent.com/odmishien/odmishien/master/profile-summary-card-output/nord_dark/2-most-commit-language.svg)](https://github.com/vn7n24fzkq/github-profile-summary-cards)

まとめ

  • GitHub Actions、最高
  • ダークテーマ、最高

参考リンク

simple-minds-think-alike.hatenablog.com

今回作ったリポジトリ

github.com

git push --force はなぜダメなの

git push --force って、なんか絶対やってはいけない空気を感じますよね。でも結局なぜダメなのか。あまり理解してない気がしたので、まとめてみます。

そもそもどういう場面で使うか

チーム開発をやっている時に、remoteとlocalで整合性が取れなくなることがある。例えば以下のような状況では git push できない。

<remote>           <local>
     A                 A
     |                 |
     B                 B
     |                 |
     C                 D ← 存在するはずの C というコミットがないのでpushできない!!

この場合いくつか解決方法はあるが、私だったら以下のようにする。

git checkout local
git stash save "stash D" // Dという変更を一旦退避
git merge remote // remoteをマージして Cコミットを取り込む
git stash apply // これで A-B-C-D という順番になる
git push // ✌️

しかし様々な事情から stashした変更をapplyするのがめちゃくちゃ面倒だったりして、「あ〜もう上書きしていいスカ(笑)」となる瞬間は誰しもある。ここで登場するのが git push --force である。

force オプションの何が悪か

1人で開発している際には大きな問題になることはなさそう。問題となるのはチーム開発の時である。先ほどの例を取ると、 git push --force はかなり破壊的なコマンドであることが分かる。

git checkout local // local は A-B-D, remoteは A-B-C
git push --force // remote は localに上書きされてしまい、 A-B-Dとなる(Cというコミットが消える)

チームのメンバーのコミットを吹き飛ばす、みたいなことが起こり得るので結構危険である。

じゃあ脳死で force-with-lease を使えばいいのか

--force-with-lease はこのような破壊的な force オプションを回避するためのオプションである。--force-with-lease はremoteのrefsとlocalのrefsを比較して、他人のコミットなどがあればpushをrejectしてくれる君である。これで安心。

と言いたいところだが、仕組みとしてこの--force-with-lease を付けていても、remoteを上書きしてしまう場合がある。それは push する前に fetchコマンドを実行していた時である。 --force-with-lease は詰まるところ単にremoteのrefsとlocalのrefsを比較しているだけなので、ここが揃ってしまっていたら、すり抜けてしまう。

結局どうしたらいいの

チームメンバーとコミュニケーションを取りながら開発をしましょう。極力共同で作業しているブランチでは --force--force-with-lease も使わないようにして、面倒でもブランチの整合性を取るように心がけましょう。

参考記事

www.atlassian.com

git-scm.com

denpa-shinbun.com

v-showとbootstrapのFlexboxを一緒に使うとv-showが効かなくなる

  • v-show は単に display:none; にするだけなので bootstrapのFlexbox(display:flex;!important) と衝突する
  • Vue.js の公式リポジトリでも議論がなされている

  • 例えば以下のような解決策がある

    • CSS*[style*="display: none"] { display: none !important } を書く
    • 以下みたいに、classのバインディングで同じコンディションを設定する
<div v-show="someCondition" :class="{ 'd-flex': someCondition }">
    <!--content-->
</div>

TypeScriptで簡単な対話形式のCLI符計算アプリを作ってみる

仕事ですぐに使えるTypeScript を読んだ。最近はインプットばかりで、何かを作ることもできていなかったので、久しぶりに手を動かしてみることにした。

  • TypeScriptを書く
  • npm packageデビューする
  • まずはWebなどではなく、CLIでシンプルに作る

この辺りを目標にした。最近は麻雀にハマっていて(天鳳か雀魂をやっている)、符計算の簡単なCLIがあれば良さそうと思ったので作ることにした。

符計算について

麻雀に明るくない方向けに軽く符計算について説明をする。

麻雀は 4つのメンツ(3個 or 4個のセット)と1つのアタマ(2個のセット) の合わせて 14~18個の牌を揃えるゲームである。その際、役などを数えたりして得点計算をする。「符」とは役がそこまで大きくない場合の得点を判定するための数値で、その計算を「符計算」と呼ぶ。これが結構複雑で、初心者殺し感がある。Wikipediaにも複雑な表が置いてある。

ja.wikipedia.org

符は

  • メンツの種類
  • アタマの種類
  • ガリの待ち方(両面待ちとか)
  • ガリ方(ツモ or ロン)
  • 鳴いたかどうか

などから算出される。また、一部の役(七対子など)は符が一律で決まっていることがあるが、今回のアプリでは考慮していない。

できたもの

https://user-images.githubusercontent.com/25533384/92455643-eb4d8480-f1fc-11ea-88b4-53b30e581ff2.gif

github.com

npmにもパッケージ化して置いてあるので、すぐにご利用できます。

www.npmjs.com

CLIフレームワークについて

TS(というかNode.js)でCLIを作る際に使うフレームワークについては何も知らない状態でスタートした。5分くらい調べて、Herokuが作っている oclif が良さそうと思って採用した。 oclifは対話形式でCLIアプリケーションに合った雛形を作ってくれる。

singlemulti が選べる。 multi はサブコマンドを作りたい時に使うらしい。今回はサブコマンド要らなそうなので single を選択した。

🐑🌙 npx oclif single fucalc-ts
 
      _-----_     ╭──────────────────────────╮
     |       |    │      Time to build a     │
     |--(o)--|    │  single-command CLI with │
    `---------´   │  oclif! Version: 1.16.1  │
     ( _´U`_ )    ╰──────────────────────────╯
     /___A___\   /
      |  ~  |
    __'.___.'__
  ´   `  |° ´ Y `
 
 ? command bin name the CLI will export fucalc-ts
 ? description the app for calculating fu in mahjong.
 ? author odmishien
 ? Select a package manager npm
 ? TypeScript Yes
 ? Use eslint (linter for JavaScript and Typescript) Yes
 ? Use mocha (testing framework) No
 ? Add CI service config circleci (continuous integration/delivery service)

色々と質問に答えると、npm install などはもちろん、READMEなどもいい感じに作ってくれる。カスタマイズ自分でしたい人にはちょっとお節介かもしれないくらいである。実行後は以下のようなディレクトリ構成となる。

🐑🌙 tree  -L 1
 .
 ├── README.md
 ├── bin
 ├── node_modules
 ├── package-lock.json
 ├── package.json
 ├── src
 └── tsconfig.json
 
 3 directories, 4 files

src の中には index.ts ができていて、実行が可能。

🐑🌙 ./bin/run
hello world from ./src/index.js!

インタラクティブなプロンプトを実現する

この記事がとても参考になった。

dev.to

プロンプトをインタラクティブにするのに inquirer を使う。プロンプトで聞くことのできる質問のタイプも色々用意されていて便利。今回は List 形式しか使わない。

Command の中の runというメソッドの中に prompt を定義する。 prompt には質問のオブジェクトを Array にして渡すと、上から順番に聞いてくれる。

import { Command } from "@oclif/command";
import { prompt } from "inquirer";

class sampleCommand extends Command {
  async run() {
    const userInput = await prompt([
      {
        type: "list",
        name: "animal",
        message: "好きな動物は?",
        choices: [
            {
              name: '🐢 かめさん',
              value: 'turtle',
             },
            {
              name: '🐇 うさぎさん',
              value: 'rabbit',
             },
        ],
      },
      {
        type: "list",
        name: "fruit",
        message: "好きな果物は?",
        choices: [
            {
              name: '🍎 りんご',
              value: 'apple',
             },
            {
              name: '🍇 ぶどう',
              value: 'grape',
             },
            {
              name: '🍊 オレンジ',
              value: 'orange',
             },
        ],
      },
    ]);
  console.log(userInput);
  }
}

export = sampleCommand;

ユーザーの入力は userInput に入る。

./bin/run
? 好きな動物は?
> 🐢 かめさん
  🐇 うさぎさん
? 好きな果物は?
> 🍎 りんご
  🍇 ぶどう
  🍊 オレンジ

{
  animal: "turtle",
  fruit: "apple",
}

符を計算する

ここからはかなり素朴で、ユーザーの入力を元に switch 文で符を計算していく。符は基本点のようなものが20点あるので、初期値は 20 である。 また、最終的に1の位は切り上げを行う。例えば 3240 となる。

    let fu = 20;
    fu += calc.calcMen(userInput.men1);
    fu += calc.calcMen(userInput.men2);
    fu += calc.calcMen(userInput.men3);
    fu += calc.calcMen(userInput.men4);
    fu += calc.calcAtama(userInput.atama);
    fu += calc.calcMachi(userInput.machi);
    fu += calc.calcAgari(userInput.agari);
    fu += calc.calcMenzen([
      userInput.men1,
      userInput.men2,
      userInput.men3,
      userInput.men4,
    ]);
    const ceiledFu = calc.calcCeilFu(fu);
    console.info(`\n🀄️あなたの手は ${ceiledFu} (合計:${fu}) 符です🀄️`);

calc... はそれぞれの条件(メンツとかアタマとか)に合わせて関数を作っている。型安全に作るなら、独自の型を作ったりして、VSCodeで補完を利かせまくるのがいいのだろうが、面倒でしていない。 例えば、待ち方から符を計算する calcaMachi は以下のようになっている。

const calcMachi = (machiType: string): number => {
  switch (machiType) {
    case "ryanmen":
      return 0;
      break;
    case "shanpon":
      return 0;
      break;
    case "tanki":
      return 2;
      break;
    case "kanchan":
      return 2;
      break;
    case "penchan":
      return 2;
      break;
    default:
      return -1;
      break;
  }
};

ハマったこと

  • promptPromise<any>型を返すので、awaitを使った方がいい

    • then でチェーンしてもいいけど
  • switch のなかに || は書けない(全然TS関係ない)

// これはできない
 switch(text){
    case "hoge" || "huga":
        ...
    case "piyo":
        ...
 }
 
 // ちゃんと case を書く
 switch (text) {
    case "hoge":
        ...
    case "huga":
        ...
    case "piyo":
        ...
 }

感想

とても簡単、かつある程度綺麗な見た目の対話形式のCLIアプリがサクッと書けるのは、とても体験が良い。課題としては

  • エラーハンドリングをやる
  • テストを書く
  • 色々フラグを付けられるような機能を考える(役確認とか?)
  • サブコマンドも使えるなら実装したい

などが挙げられそう。

皆様も良いCLIアプリ開発ライフを。

参考リンク

chaika.hatenablog.com

qiita.com

ghqとanyframeで快適にGitリポジトリを管理する

研究室のScrapBoxに書いたものを転載!!!

想定環境としては

です

なにが快適になるか

  • git clone をどこにしようか迷う必要がなくなる
  • git clone したものをどこに保存していたか分からなくなることがなくなる

ghqとは

  • Goで実装されたリポジトリ管理ツール
  • イントロダクションや使い方はハンドブックを参考にしてください
  • anyframe導入してしまったら主に使うコマンドは ghq get <repositoryのURL> くらいです

Goをインストールする

  • Goで実装されているのでGoがまだ入っていなければインストールが必要です
    • 個人的にはgoenvを使ってバージョン管理するのがおすすめです
    • とはいえ, ghqのためにしかGoを使わないのであればbrew isntall go で十分な気がする
  • $GOPATH というものを設定する必要がある
    • zsh を使ってる場合は.zprofileとかに以下の設定を書いておく
export GOPATH=~/.go
export PATH=$PATH:$GOPATH/bin

source .zprofile などして設定を反映しておく

ghqをインストールする

brew install ghq で入る(Windowsは知らん!)

anyframeをインストールする

リスト表示する系コマンドの出力結果を受け取ってインタラクティブに選択させてくれるやつ
リスト表示 -> 絞り込み -> 選択 -> 実行の4ステップをシームレスにつなぐにくいやつ
現代CLIの革命児

  • 今回は ghq で取得してきたリポジトリキーバインドを入力するだけで検索&目的のリポジトリがあるディレクトリまで移動を実現するために使います

  • brew install peco を実行

    • anyframeが使う peco というツールをインストールする
  • mkdir ~/.zsh を実行して、anyframeを入れておくディレクトリを作成する

  • cd ~/.zsh.zshの中に入って git clone https://github.com/mollifier/anyframe.git を実行

  • ~/.zshrc の適当な場所に以下を追記

fpath=($HOME/.zsh/anyframe(N-/) $fpath)
autoload -Uz anyframe-init
anyframe-init
  • source ~/.zshrc などして 設定を反映

anyframeの便利キーバインドを設定する

  • この辺からは好みが出てくると思いますので 私 の環境を参考程度に貼っておきます
  • 再び ~/.zshrc に書いていく
bindkey '^xr' anyframe-widget-execute-history
bindkey '^x^r' anyframe-widget-execute-history

bindkey '^xp' anyframe-widget-put-history
bindkey '^x^p' anyframe-widget-put-history

bindkey '^xg' anyframe-widget-cd-ghq-repository
bindkey '^x^g' anyframe-widget-cd-ghq-repository

bindkey '^xk' anyframe-widget-kill
bindkey '^x^k' anyframe-widget-kill

bindkey '^xi' anyframe-widget-insert-git-branch
bindkey '^x^i' anyframe-widget-insert-git-branch

bindkey '^xf' anyframe-widget-insert-filename
bindkey '^x^f' anyframe-widget-insert-filename
  • bindkey の後ろにあるキーバインドで その後ろに書いてあるようなことが実行される

  • 例えばよく使うものだと

    • ^x^g or ^xg : ghqリポジトリを一覧表示&検索&選択したらそのリポジトリに移動
    • ^x^r or ^xr : ターミナルで実行したコマンドの履歴を一覧表示&検索&選択したらそのコマンドを実行
    • ^x^p or ^xp : ターミナルで実行したコマンドの履歴を一覧表示&検索&選択したらそのコマンドを呼び出す(実行はされない)
      • さっき使ったコマンドをちょっと変更して実行したいときとかに使います

まとめ

これからgitリポジトリを管理する流れとしては

という流れで自分が今どのディレクトリにいるのか、repositoryはどこに保存されているのかなどを気にせずに作業ができる!!

参考サイト

Pythonのリストとタプルについてメモ

リストとタプルの違い

リスト: 動的/値の変更が可能(ミュータブル)/リサイズが可能
タプル: 静的/値の変更が不可(イミュータブル)/リサイズが不可

リストの動き

  • サイズNのリストが追加された時、その後の追加を見込んだ分のM個の領域が確保される(M>N)
    • 領域の再確保の回数を減らす
    • メモリのコピーの回数を減らす
  • appendを繰り返し N==M になると、また余剰領域を確保したリストが作成される
  • 元の値が新たなリストへコピーされる

タプルの動き

  • 新しいタプルを作るたびに領域確保とコピーの処理が必要
    • タプルには appendがないのでタプルの結合をするしかない
  • 余剰領域は確保しないのでリストよりリソースは少なくて済む
  • 小さめのタプルはPythonガベージコレクションによってメモリが解放されない
    • 将来の再利用のため
    • つまりOSの呼び出しを抑えられるのでリストよりも高速に生成できる