GitHubのプロフィールに表示されるREADMEをいい感じにする
人に見てもらう意味でも、自分を客観的に見つめる意味でも、技術ポートフォリオをアップデートしていく作業は大切だなと思う。でも改めて自分のスキルや技術スタックをまとめるのは色々面倒。
割と今更ですが、今回はサクッとGitHubのプロフィールのトップに表示されるREADMEをいい感じにします。こんな感じになった。(一応)バックエンドエンジニアなのにHTMLが多くなってしまうの、悔しい。
リポジトリとREADMEを準備する
何はともあれ、READMEとそれを置いておくリポジトリが必要。公式ドキュメントにも書いてあるように、
を実行しておけば、自身のプロフィールページ(https://github.com/{username}
)にREADME.md
の内容が表示される。
GitHub Profile Summary Cardsを表示する
こんな便利なActionsを用意してくれている人がインターネットには存在する。
これを先ほど作ったリポジトリに仕込んでおけば、勝手にプロフィールに表示される言語やコミットのスタッツのカード画像を更新してくれる。手順としては、
.github/workflows
以下に GitHubActionsを定義するyamlファイルを作成
secrets.GITHUB_TOKEN
やgithub.repository_owner
は特に何もしなくても Actions側で読んでくれる(便利!!)- カード画像の生成タイミングはcronを仕込めるので、お好みのタイミングを指定しておく
- github-profile-summary-cardsのREADMEにも書いてあるが、GitHub側のキャッシュが残ったりするのでcronで仕込んだ時間と表示されるプロフィール画面とで少しタイムラグが生じることはあるらしい
gist0ce2d68a7d9de2d166ca0efd55d908b3
Private Repository の情報も必要な場合は secrets
に PERSONAL_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://github.com/vn7n24fzkq/github-profile-summary-cards) [](https://github.com/vn7n24fzkq/github-profile-summary-cards) [](https://github.com/vn7n24fzkq/github-profile-summary-cards)
まとめ
- GitHub Actions、最高
- ダークテーマ、最高
参考リンク
simple-minds-think-alike.hatenablog.com
今回作ったリポジトリ
GitHubActionsではchange directoryしない方が良さそう
こんな風に cd
するよりも
- name: Setup Envs run: | cd app cp .env.dev .env
working-directory
オプションを使った方が良さそう
- name: Setup Envs working-directory: ./app run: cp .env.dev .env
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
も使わないようにして、面倒でもブランチの整合性を取るように心がけましょう。
参考記事
v-showとbootstrapのFlexboxを一緒に使うとv-showが効かなくなる
v-show
は単にdisplay:none;
にするだけなので bootstrapのFlexbox(display:flex;!important
) と衝突するVue.js の公式リポジトリでも議論がなされている
- v-show should be !important · Issue #3761 · vuejs/vue
v-show
に!important
を付けるような実装はしないから色々な方法で回避してねみたいなことが書いてある
例えば以下のような解決策がある
<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にも複雑な表が置いてある。
符は
などから算出される。また、一部の役(七対子など)は符が一律で決まっていることがあるが、今回のアプリでは考慮していない。
できたもの
npmにもパッケージ化して置いてあるので、すぐにご利用できます。
CLIフレームワークについて
TS(というかNode.js)でCLIを作る際に使うフレームワークについては何も知らない状態でスタートした。5分くらい調べて、Herokuが作っている oclif が良さそうと思って採用した。 oclifは対話形式でCLIアプリケーションに合った雛形を作ってくれる。
single
と multi
が選べる。 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!
インタラクティブなプロンプトを実現する
この記事がとても参考になった。
プロンプトをインタラクティブにするのに 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の位は切り上げを行う。例えば 32
は 40
となる。
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; } };
ハマったこと
prompt
はPromise<any>
型を返すので、awaitを使った方がいいthen
でチェーンしてもいいけど
switch
のなかに||
は書けない(全然TS関係ない)
// これはできない switch(text){ case "hoge" || "huga": ... case "piyo": ... } // ちゃんと case を書く switch (text) { case "hoge": ... case "huga": ... case "piyo": ... }
感想
とても簡単、かつある程度綺麗な見た目の対話形式のCLIアプリがサクッと書けるのは、とても体験が良い。課題としては
- エラーハンドリングをやる
- テストを書く
- 色々フラグを付けられるような機能を考える(役確認とか?)
- サブコマンドも使えるなら実装したい
などが挙げられそう。
参考リンク
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
とかに以下の設定を書いておく
- zsh を使ってる場合は
export GOPATH=~/.go export PATH=$PATH:$GOPATH/bin
source .zprofile
などして設定を反映しておく
ghqをインストールする
brew install ghq
で入る(Windowsは知らん!)
anyframeをインストールする
リスト表示する系コマンドの出力結果を受け取ってインタラクティブに選択させてくれるやつ
リスト表示 -> 絞り込み -> 選択 -> 実行の4ステップをシームレスにつなぐにくいやつ
現代CLIの革命児
今回は ghq で取得してきたリポジトリをキーバインドを入力するだけで検索&目的のリポジトリがあるディレクトリまで移動を実現するために使います
brew install peco
を実行- anyframeが使う
peco
というツールをインストールする
- anyframeが使う
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
の後ろにあるキーバインドで その後ろに書いてあるようなことが実行される例えばよく使うものだと
まとめ
これからgitリポジトリを管理する流れとしては
という流れで自分が今どのディレクトリにいるのか、repositoryはどこに保存されているのかなどを気にせずに作業ができる!!
参考サイト
Pythonのリストとタプルについてメモ
リストとタプルの違い
リスト: 動的/値の変更が可能(ミュータブル)/リサイズが可能
タプル: 静的/値の変更が不可(イミュータブル)/リサイズが不可
リストの動き
- サイズNのリストが追加された時、その後の追加を見込んだ分のM個の領域が確保される(M>N)
- 領域の再確保の回数を減らす
- メモリのコピーの回数を減らす
- appendを繰り返し N==M になると、また余剰領域を確保したリストが作成される
- 元の値が新たなリストへコピーされる
タプルの動き
- 新しいタプルを作るたびに領域確保とコピーの処理が必要
- タプルには
append
がないのでタプルの結合をするしかない
- タプルには
- 余剰領域は確保しないのでリストよりリソースは少なくて済む
- 小さめのタプルはPythonのガベージコレクションによってメモリが解放されない
- 将来の再利用のため
- つまりOSの呼び出しを抑えられるのでリストよりも高速に生成できる