PHPの`$`と`->`がつらくなったらマクロを使えば良いことに気づいた
最近PHPを書くことが多いのですが、変数の前につける$
(ドル記号)とメソッド呼び出しやプロパティ呼び出しに使う->
(矢印)の指の動きの効率悪さに耐えられなくなりました。。。
PHPStormを使っているといい感じに$
を入力しなくても変換してくれたりするのですが、その動きもまた気持ち悪く感じてしまうのでどうしたものかと思っていました。
そこで今更ながら発見したのがエディタの「マクロ」機能です!
マクロ作成
PHPStormであれば上のキャプチャのように「Edit > Macros > Start Macro Recording」を実行することでマクロの記録を始めることができます。
記録中は下図のようなインジケーターが表示されます。
ここでマクロとして入力したいキーを押します。$
用のマクロを作りたいので$
を入力すると、記録されているのがわかります。
そして赤い■をクリックするとマクロの記録が終わるので、このマクロに名前をつけてあげます。わかりやすく$
にしておきます。
これでマクロは完成です!
同じ要領で->
も作っておきましょう。
マクロのショートカット作成
マクロが完成したら、今度は自分の好きなショートカットでこれが入力されるようにします。
「Preferences > Keymap」に進んで、虫眼鏡のところでmacro
と入力しましょう。下のように絞り込まれるはずです。
ここに先程作成した$
と->
があるのがわかります。あとはここに自分の好きなキーの組み合わせを設定することで、以後このキーを入力すれば$
と->
が入力できるようになります!
ちなみに僕は
$
→ctrl+s
->
→ctrl+.
という設定にしました。ちょっとですけど快適になった気がします!
他の皆様がどうしているのか気になったりします(むしろ気にしてないですかね。。。)
まとめ
- PHPStormのマクロを設定した
- マクロならコードスニペットとは違ったショートカットを作ることができる
explainshell.comはシェルの強力な助っ人
最近explainshell.comなるものを今更ながら知ることができたので紹介します!
概要
シェルコマンドを全部覚えるのって大変です。なんとなく覚えてても、オプションがどんな意味をもっていたかまではなかなか思い出せないです。おまけにシェルは強力なのでパイプ(|)でどんどん繋げていい感じにコマンドを書いていくことができます。そんな芸術ともいうコマンドを見ては、man
を見たりググったりして、分解しながらコマンドを理解していっているのはきっと僕だけじゃないはず。
そこで便利なのがこのexplainshell.comというサイトです!
使い方
使い方はいたって簡単で、インプットに知りたいコマンドを入力するだけ。
試しに最近覗いてみた下のk3sのインストールスクリプトから抜粋してみます。
lsof | sed -e 's/^[^0-9]*//g; s/ */\t/g' | grep -w 'k3s/data/[^/]*/bin/containerd-shim' | cut -f1 | sort -n -u
すごい!全部のコマンドに丁寧な説明がいい感じにつくんです!
そして「←→」で順番にコマンドの解説を表示していくことも可能。
とってもシンプルですけど、ブックマークしておけば困ったときにサクッと使えて重宝しそうです!
おまけ
おまけですが、僕の場合はAlfredにショートカットを作ってすぐに調べられるようにしてます。
参考までにWeb Searchの設定は下のようにしています。
そうすると下のようにすぐに調べることができます!
まとめ
- explainshell.comを試した
- シェルコマンド調べるときに便利
- Alfredなどツールと連携すればさらに強力!
Node.jsではてなの自分の記事一覧を取得する
はてなの自分の記事一覧をサクッと取得できるようにしたかったのですが、意外と
- Node.jsで書かれている
- 結果をJSONにする
ものが見当たらなかったので結局自分で作ることにしました!
ツールの流れ
はてなから自分の記事一覧を取得する流れは下のようになります。
- はてなのAtomPubを使う(認証はこの記事ではBasic認証を使います)
- エンドポイントをGETすると最新の記事が10件とれる
- 結果はXMLなのでJSONに変換する
- 2のレスポンスに次の10件のURLが記載されているので、全部取得終わるまで2, 3を繰り返す
はてなのAtomPub
はてなのAtomPubの仕様については以下Hatena Developer Centerに記載されています。
認証のところに書かれていますが、取得時に使える認証は
の3種類です。個人的にしか使わないし、簡単な方法でとりたいので今回はBasic認証を使うことにしました!
Basic認証に必要なユーザー名ははてなIDで、パスワードはAPIキーを使用します。
APIキーははてなブログの管理画面から設定>詳細設定に行った場所にあります!
下の方を見るとAtomPubの項目があり、そこにAPIキーがあるのでメモっておきましょう!
ブラウザからGETしてみる
それでは、試しに記事一覧をブラウザから取得してみます。
ドキュメントにも記載がありますが、以下のURLで取得できます。
GET https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry
僕の場合はてなID=kenev
でブログID=kenfdev.hateblo.jp
なので、以下になります。
https://blog.hatena.ne.jp/kenev/kenfdev.hateblo.jp/atom/entry
これをブラウザに入れると、下のようにBasic認証が聞かれるはずです。
ここにユーザー名とAPIキーを入力すると、下のようなXMLが表示されます。
<?xml version="1.0" encoding="utf-8"?> <feed xmlns="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app"> <link rel="first" href="https://blog.hatena.ne.jp/{はてなID}}/{ブログID}/atom/entry" /> <link rel="next" href="https://blog.hatena.ne.jp/{はてなID}/{ブログID}/atom/entry?page=1377584217" /> <title>ブログタイトル</title> <link rel="alternate" href="http://{ブログID}/"/> <updated>2013-08-27T15:17:06+09:00</updated> <author> <name>{はてなID}</name> </author> <generator uri="http://blog.hatena.ne.jp/" version="100000000">Hatena::Blog</generator> <id>hatenablog://blog/2000000000000</id> <entry> <id>tag:blog.hatena.ne.jp,2013:blog-{はてなID}-20000000000000-3000000000000000</id> <link rel="edit" href="https://blog.hatena.ne.jp/{はてなID}/ ブログID}/atom/edit/2500000000"/> <link rel="alternate" type="text/html" href="http://{ブログID}/entry/2013/09/02/112823"/> <author><name>{はてなID}</name></author> <title>記事タイトル</title> <updated>2013-09-02T11:28:23+09:00</updated> <published>2013-09-02T11:28:23+09:00</published> <app:edited>2013-09-02T11:28:23+09:00</app:edited> <summary type="text"> 記事本文 リスト1 リスト2 内容 </summary> <content type="text/x-hatena-syntax"> ** 記事本文 - リスト1 - リスト2 内容 </content> <hatena:formatted-content type="text/html" xmlns:hatena="http://www.hatena.ne.jp/info/xmlns#"> <div class="section"> <h4>記事本文</h4> <ul> <li>リスト1</li> <li>リスト2</li> </ul><p>内容</p> </div> </hatena:formatted-content> <app:control> <app:draft>no</app:draft> </app:control> </entry> <entry> ... </entry> ... </feed>
XMLが取得できそうだとわかったところで、Node.jsで実装してみます!
Node.jsで取得する
まずはソースを載せちゃってからポイントを解説します。
const axios = require('axios'); const parseString = require('xml2js').parseString; const hatenaAPIUser = '<USER_ID>'; const hatenaAPIPassword = '<API_KEY>'; const entryUrl = `https://blog.hatena.ne.jp/<USER_ID>/<BLOG_ID>/atom/entry`; const creds = `${hatenaAPIUser}:${hatenaAPIPassword}`; const encoded = Buffer.from(creds).toString('base64'); const authorizationHeader = `Basic ${encoded}`; /** * Promise based timeout * @param {number} msec wait milliseconds */ function wait(msec) { return new Promise(resolve => { setTimeout(() => { resolve(); }, msec); }); } /** * Promise based XML->JSON Parser * @param {object} data XML Data */ function parseStringPromise(data) { return new Promise((resolve, reject) => { parseString(data, (err, result) => { if (err) { reject(err); return; } resolve(result); }); }); } /** * Get all Hatena entries recursively * @param {string} url Entry URL */ async function getAllEntries(url) { console.log('fetching:', url); const entries = await axios.get(url, { headers: { Authorization: authorizationHeader, }, }); const { feed: { entry, link }, } = await parseStringPromise(entries.data); const next = link.find(l => l.$.rel === 'next'); const parsedEntries = entry .filter(e => { const { 'app:control': [ { 'app:draft': [isDraft], }, ], } = e; return !(isDraft === 'yes'); }) .map(e => { const categories = e.category ? e.category : []; return { id: e.id[0], title: e.title[0], url: e.link[1].$.href, published: e.published[0], publishedAt: new Date(e.published[0]).getTime(), updated: e.updated[0], updatedAt: new Date(e.updated[0]).getTime(), summary: e.summary[0]._, categories: categories.map(c => c.$.term), }; }); if (next) { // recursively fetch if 'next' exists const nextHref = next.$.href; await wait(500); const nextEntries = await getAllEntries(nextHref); return [...parsedEntries, ...nextEntries]; } else { return parsedEntries; } } getAllEntries(entryUrl).then(console.log);
ポイントとしては
- 外部リクエストにはaxiosを使い、BASIC認証用のHTTPヘッダーを作成している
- XMLからJSONにはxml2jsというライブラリを使っている
- 無限ループで負荷かけちゃうの怖いのでリクエスト間に500ミリ秒待つ
- 最終的なJSONの形は自分で決める
となります。順番に説明します!
外部リクエストにはaxiosを使い、BASIC認証用のHTTPヘッダーを作成している
最近axiosを使う機会が多かったので、なんとなく外部リクエストのライブラリはaxiosにしました。 ライブラリは割とどうでもいいのですが、ポイントはBASIC認証のHTTPヘッダーを作っている部分です。
const creds = `${hatenaAPIUser}:${hatenaAPIPassword}`; const encoded = Buffer.from(creds).toString('base64'); const authorizationHeader = `Basic ${encoded}`; ... const entries = await axios.get(url, { headers: { Authorization: authorizationHeader, }, }); ...
BASIC認証を実現するためにはHTTPヘッダーに Authorization
というものを作って、中身はBasic <ユーザー名:パスワードをbase64エンコードした値>
となります。
XMLからJSONにはxml2jsというライブラリを使っている
XMLからJSONにするために下のライブラリを使っています。
axiosで取得したXMLの中身をこのライブラリに通すことで、JSONを取得することができます。
const parseString = require('xml2js').parseString; /** * Promise based XML->JSON Parser * @param {object} data XML Data */ function parseStringPromise(data) { return new Promise((resolve, reject) => { parseString(data, (err, result) => { if (err) { reject(err); return; } resolve(result); }); }); } ... const { feed: { entry, link }, } = await parseStringPromise(entries.data); ...
個人的にCallbackがあまり好きじゃないので、Promiseにわざわざ変換したfunction
書いてますけど、これは別に必要ないです。Promiseにしてawait
したかっただけです。
無限ループで負荷かけちゃうの怖いのでリクエスト間に500ミリ秒待つ
コードがバグっていてはてなさんのサーバーに変な攻撃を起こしてしまうのが怖いので、リクエスト間に待ちを発生させるようにしてます。
/** * Promise based timeout * @param {number} msec wait milliseconds */ function wait(msec) { return new Promise(resolve => { setTimeout(() => { resolve(); }, msec); }); } ... if (next) { // recursively fetch if 'next' exists const nextHref = next.$.href; await wait(500); const nextEntries = await getAllEntries(nextHref); return [...parsedEntries, ...nextEntries]; } else { return parsedEntries; } ...
先程と同じく、setTimeout
をPromiseにしてawait
使いたかったのでわざわざfunction
作っています。あとは再帰呼び出しをする直前にawait wait(500)
と書いて500ミリ秒待つようにしています。
最終的なJSONの形は自分で決める
XMLからJSONに変換したものは、そのままではものすごく使いづらいので、適宜興味のある項目だけ抜き取って最終的な形に整形します。
... const categories = e.category ? e.category : []; return { id: e.id[0], title: e.title[0], url: e.link[1].$.href, published: e.published[0], publishedAt: new Date(e.published[0]).getTime(), updated: e.updated[0], updatedAt: new Date(e.updated[0]).getTime(), summary: e.summary[0]._, categories: categories.map(c => c.$.term), }; ...
こうすると、下のようなJSONになります。
{ "id": "xxxxxxxxxxxxxxxxxxx", "title": "AWS SAA再認定のために5日間頑張ったこと", "url": "https://kenfdev.hateblo.jp/entry/saa-recertification", "published": "2019-03-08T20:42:58+09:00", "publishedAt": 1552045378000, "updated": "2019-03-08T20:42:58+09:00", "updatedAt": 1552045378000, "summary": "昨日AWSソリューションアーキテクト・アソシエイト(以下SAA)に再認定されました。3月中に取得しないといけないというミッションがあったので慌てて先週申し込みました。試験会場の関係で試験日まで5日しか無かったのですが、合格できたので備忘録を残しておきます。 TL;DR(まとめ) …", "categories": ["AWS"] }
まとめ
以上ではてなの記事一覧の取得ができます!最後はfs
を使ったりしてファイルにJSONを保存すれば何か他の用途に使うことができるかと思います。
以下リポジトリにコードを格納していますので、興味がある方は使ってみてください!
「k3sとラズパイでフロントエンド開発に挑戦した話」を発表しました
今週は2つのイベントでLTをさせていただきました!ほぼ同じ内容で登壇したので、2日連続で参加された方には申し訳なかったのですが、少し内容を変える努力はしました。。。
発表資料
ストーリー
フロントエンジニアでラズパイ持っててkubernetesに興味があったら、とりあえずラズパイ上にNuxt.js乗せたくなりますよね!?
ということで、今回はNuxt.jsをラズパイに入れたk3s上にデプロイすることに挑戦したときの話をしました!ポイントとしては下のとおり!
- arm32v7/nodeの
alpine
だとnpm install
でコケる - なのでARMのイメージで
alpine
はあきらめる docker create manifest
でマルチアーキイメージを作れる(ARMとx86_64で同じイメージ名!)- Dockerfileの
FROM
にARG
を使うとイメージレイヤのキャッシュが効かない疑惑がある
ヘルプをもらえました!
node
のイメージを素で使ってましたけど、 node-slim
にしては? というアドバイスがいただけました!
build-argsとキャッシュ問題はこれじゃないかというアドバイスをいただきました!https://t.co/yUkdVBH7YV #cnjp
— Ken Fukuyama (@kenfdev) June 7, 2019
半分以下には落とせました!けれど2桁までは落としたい気持ちがあります。。。
ARG
使うとキャッシュは効かない!? についてのアドバイスもいただけました!
arm32v7/nodeの `slim` を使えばいいのではというアドバイスも!試します!
— Ken Fukuyama (@kenfdev) June 7, 2019
#cnjp pic.twitter.com/oA57vHNqfH
上の公式ドキュメント読みましたけど、毎回同じ値にしてるのにキャッシュ効かないんですよねー。これは引き続き調査 or Dockerfileを分ければいいのかなと思ったり。
おまけ
こういうコメントがもらえてうれしいです!アプリ寄りな話を増やしたい気持ちはあるんですよね。
@kenfdevさんの、k3s on ラズパイに作ったフロントを乗っけて、フロント開発したという話。フロントもやりたい、kubeも学びたい人に良さそうである(^ω^)#cnjp
— おかもん@子育てしながら系エンジニア(・ω・) (@mojogeek666) June 7, 2019
なかなか時間を作るのにも苦労していますが、今後もフロントエンドとコンテナをからめていきたいです!
カックさん(@kakakakakku)さんのメンタリングを卒業しました
3月から5月の3ヶ月間に渡るカックさん(@kakakakakku)のブログメンタリングを卒業しました!
ここに卒業記事を残し、今後についても書き残しておきたいと思います。
メンタリングをなぜ受けたのか
僕はアウトプットすること自体は好きで、ブログを書いたりイベントに登壇したりは不定期にしていました。自分でも認識していた問題はアウトプットの習慣化がまるでできていなかった点です。その点を改善すべく、メンタリングを受ける決意をしました。
アウトプットの習慣化がなぜできないのか
「なぜアウトプットの習慣化ができないのですか?」と聞かれたら多くの人が「忙しくて時間が無い」と答えるかと思います。
今、「うんうん」と頷いた方、ぜひぜひ下の記事を読んでいただきたいです。これだけでもマインドセットが変わると思います!
僕も「時間が無い」と思っていた中の一人ですが、「時間が無い」と定義していたのは自分です。誰かに強制的に自分の時間を奪われていたわけではありません(そういう人もいるかもしれませんが…)。時間の使い方をコントロールできていなかったのが大きな原因だと気づくことができました。
3ヶ月の実績
3ヶ月の実績は下のとおりです。
項目 | 3月初 | 5月末 |
---|---|---|
Twitter Follower | 393 | 410 |
はてブ | 0 | 12 |
読者数 | 0 | 31 |
メンタリングが始まって結構すぐにCloud Native Kansaiという大きめなイベントでLT登壇することができたので、PVがすごく上がっています。
そのときの記事がこちら。
けれど、この波に乗ることができずにいっきに低迷!そこからOPAネタを書いていきながら、最後は書評に挑戦して徐々にPVを上げていくことができたかなと思っています(まだまだですが)。
驚くような結果にはならなかったので反省していますが、今後も継続して精進していきます!
課題
卒業とともにカックさんに最後のフィードバックをもらいました。中でも特に僕が今後も改善していきたい点については「満足できるクオリティに達するまでの距離を遠くに置きがち」なところです。週1のアウトプットを出していくことはできるようになったのですが、1アウトプットするまでに要している時間が多すぎると感じています。
いつもどうしても
- まとまりが悪い
- ストーリー性が見えにくい
- もっと違う観点を取り入れたほうがいいのでは
- などなどetc.
と考えてしまって、書いては消して、書いては消してを続けてしまいます。「小さく考える力」が圧倒的に足りていないのを痛感しています。 カックさんにも薦められた「小さな習慣」を読み、「常にアジャイルに、小さく考えられるように」改善し続けたいと思っています!
- 作者: スティーヴン・ガイズ,田口未和
- 出版社/メーカー: ダイヤモンド社
- 発売日: 2017/04/27
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (4件) を見る
メンター、カックさんについて
カックさんは本当にすごい方です。そのすごさは他のメンティの卒業エントリを見てもわかると思います。今後もエントリは増えていくと思うので、下のようにググったりTwitter検索した内容を確認すると勉強になります。
すごい点は数え切れないほどあるのですが、僕からも1点共有しておきたいです。
それは「やってみせる・やらせてみる・フィードバックする」のサイクルが本当にうまく回っていると感じた点です。
この記事を書いている最中にもカックさんからこんなツイートが流れています。
普通に envsubst のことを知らなかったから書いたんだけど,Linux コマンド1個で記事が書けるんだよ!というネタ粒度の観点をブログメンティに伝える目的もあったのだ😀 / Shell で「テンプレートエンジン」のような仕組みを実現できる envsubst コマンド - kakakakakku blog https://t.co/LuKL3Vriz1
— カック@ブロガー / k9u (@kakakakakku) 2019年6月3日
カックさん自身、かなり多忙なはずなのですが、毎週必ずアウトプットをしています。メンターであるカックさんがやってみせてから、メンティにもやらせてみる。そして、やらせてみた後は必ずフィードバックがもらえます。フィードバックは「よかった」「悪かった」なんてシンプルなフィードバックじゃなくて、かなり濃厚で、親身なフィードバックがいただけます。すごいのは、それらのフィードバックや他の通常な会話も合わせて、メンティの僕が精神的に負荷を感じることが無い点です。「がんばろう」という気持ちに素直になりますし、「しんどい」ときにはどう乗り越えていくことができるか相談に乗ってくれます。メンターの鏡です!自分自身で最高のメンターによるメンタリングを経験することができたので、僕もこうなれるように、この点についても日々精進していきたいです。
今後について
このメンタリングを通して毎週最低1記事を書くということが習慣化できたので、自分は成長したと感じています。メンタリング中に初挑戦した書評ともちょうどフィットするのですが、メンタリングを受けたことで、セルフマネジメントのレベルが上がって、ブログを書くことの習慣化が可能になったのかなと思います。
今後3ヶ月の目標としては
- 週1アウトプットを続ける
- ホッテントリな記事を書く
- 英語の記事も書く(https://medium.com/@kenfdev)
としたいです。
習慣化したのを継続すること(当たり前ですが!)、質の高い記事が書けるようになること。そして、英語のアウトプットも増やしていけたらと思います!(世界は広い!)
またちょっと長い目な記事になってしまいましたが、以上で卒業報告とさせていただきます!
カックさん(@kakakakakku)、本当にありがとうございました!!!
Unit Test好きにはたまらない!Conftestで設定ファイルのテストをしてみた
KubeConでOpen Policy Agent関連の発表を追っていたところ、面白そうなプレゼンを発見しました!
Unit Testing Your Kubernetes Configuration with Open Policy Agent
僕はUnit Test大好き人間なのでUnit Testと聞くだけで興奮するのですが、それにさらにOpen Policy Agentが関わっているときたら放っておけません!
もしOpen Policy Agentについて初めて聞いたという方は以下の記事でも紹介しているので興味があればぜひ!
Conftest
ConftestはYAMLあるいはJSONで定義された設定ファイルに対してテストを書けるというツールです。
面白いのは、テストに使うのがOpen Policy AgentのRegoというポリシー用の言語だという点です。上に紹介した記事にも書いていますが、さらに気になる人は公式サイトでもRegoについて詳しく書かれているのでぜひ読んでみてください。
インストール
Macにインストール。僕はbrewを使いましたが、様々な方法でインストールできますので、READMEをチェックしましょう!
brew tap instrumenta/instrumenta brew install conftest
インストールはこれで完了です!
テストを書く
ではテストを書いてみましょう!KubernetesのYAMLがテストできるってところがハイライトされがちですけどYAML, JSONならなんでもOKです!
ということでせっかくなのでチュートリアルには無いCircleCIのYAMLを対象にしました!
テストを書くにあたって重要なのはRegoという言語を使うということです。
Regoはポリシーに特化した言語で、ちょっとトリッキーなところもありますが、宣言的で読みやすい言語だと思います。
例えばCircleCIの以下のようなconfigがあったとします。
version: 2.0 jobs: build: docker: - image: circleci/buildpack-deps steps: - checkout test: docker: - image: kenfdev/golang:1.12 steps: - checkout workflows: version: 2 build_and_test: jobs: - build - test
これを.circleci/config.yml
に置きます。
conftest
を導入すると何ができるかというと、このYAMLの記述内容に対してポリシーを書くことができます。
具体的にはこんなポリシーを割り当てることができます
version: 2.1
以上しか使っちゃいけない- CircleCI純正のイメージしか使っちゃいけない
- コンテナのイメージに
latest
を使っちゃいけない
などなどです。
実際にこのポリシーを適用したテストを順番に書いてみます。
conftest
はデフォルトでpolicy
ディレクトリ配下の.rego
ファイルを見てくれる様なので、circleci.rego
というファイルを作ります。
touch policy/circleci.rego
この中にルールを書いていきます。
package
を指定
Regoのpackage
はデフォルトでmain
が使われます。これは--namespace
オプションで変更可能とのこと。
なので、package
はmain
にしましょう。
package main
version: 2.1
以上しか使っちゃいけない
「CircleCIのOrbs機能を使いたいから、Versionを必ず2.1以上にしたい」というポリシーがあるかもしれません。その場合に書けるポリシーは以下のとおり。
deny[msg] { input.version < 2.1 msg = "Use version 2.1 or higher" }
deny
になる条件を探すので、「どうなっているとルール違反か?」というマインドが必要です。input
というのはRegoで特別な変数で、conftest
の場合ここにテスト対象のYAMLあるいはJSONが入っています。なのでinput.version
でversion
にアクセスできます。
conftest
を実行してみます!
$ conftest test config.yml config.yml Use version 2.1 or higher
2.0
をconfig.yml
に指定しているので怒られてますね!
CircleCI純正のイメージしか使っちゃいけない
こんなユースケースがあるかどうかはさておき、使うコンテナのimage
にポリシーを設けたい場合の例になります。
必ずimage
がcircleci/
から始まっているかどうかをチェックしてみます!
deny[msg] { docker_images := input.jobs[_].docker[_].image not startswith(docker_images, "circleci/") msg = "Only use official CircleCI images" }
Regoのちょっとトリッキーな書き方をさっそく取り入れてみました。
docker_images := input.jobs[_].docker[_].image
これを言語化すると「jobs
の中に含まれる複数のキー(build
, test
)の中にあるdocker
という配列の中のimage
キーの値」になります。
こうやって書くことでOPAがよしなに順番に拾ってくれます。そして、それらがcircleci/
で始まってなかったらアウトにします。
not startswith(docker_images, "circleci/")
ここで使っているstartswith
はRegoに予め組み込まれているビルトイン関数です。OPAのドキュメントにも載っているので、どういうものがあるかチェックしておくといざとなったら使えて便利です!
「circleci/
で始まっていないもの」を探すので、先頭にnot
をつけている点もお忘れなく!
実行してみます!
$ conftest test config.yml config.yml Only use official CircleCI images Use version 2.1 or higher
kenfdev/golang:1.12
を使っているので怒られていますね!
コンテナのイメージにlatest
を使っちゃいけない
またまたimage
に関連するポリシーです。安定したCIを回すためにimage
に必ずタグを指定するというポリシーが必要になるかもしれません。latest
を使っていたらルール違反にしましょう。
deny[msg] { docker_images := input.jobs[_].docker[_].image tag_is_latest(split(docker_images, ":")) msg = "Do not use `latest` container image tags" } # helpers tag_is_latest(images) { count(images) < 2 } tag_is_latest([_, tag]) { tag == "latest" }
docker_images
はさっきと同じですが、2行目がまたちょっとトリッキーです。latest
を検知する方法は以下の2つの観点にしています。
:
で分割した場合に要素が2個より小さい:
で分割して末端側(tag側)の値がlatest
になっている
いずれかにヒットしたらルール違反にします!ここでポイントなのは、「いずれか」ということ。つまりOR条件になります。Regoのルール内の行は全てANDになり、同じルール名のルール(この場合tag_is_latest
)を並べるとORになるという特性があります。これはIncremental Definitionsと言って公式ドキュメントでも説明があるので要チェックです。
また、僕が以前書いた記事にもANDとOR、そしてルール内から別のルールを呼び出すことについて触れていますので興味があればぜひ参考にしてみてください!
上のやり方ではtag_is_latest
というルールを別途2つ作って、それぞれの判定条件を書きました。
:
で分割した場合に要素が2個より小さい
tag_is_latest(images) { count(images) < 2 }
images
にはsplit(docker_images, ":")
で分割された値が入ってきます。今回の例であれば下のようになります。
["circleci/buildpack-deps"] # 要素数は1つ ["kenfdev/golang", "1.12"] # 要素数は2つ
タグが無い場合のデフォルトがlatest
になるので、分割された要素の数が2より小さいかどうかをチェックします。
:
で分割して末端側(tag側)の値がlatest
になっている
tag_is_latest([_, tag]) { tag == "latest" }
また新たな記法が引数の場所([_, tag]
)に登場しました。これは、いい感じに配列の値を分割代入させる書き方です。
例えば下のように書くとイメージしやすいと思います。
[name, tag] = ["circleci/buildpack-deps", "latest"]
name
にはcircleci/buildpack-deps
が対応し、tag
にはlatest
が対応して代入されるようなイメージです。でも、name
には今回は特に興味がないので、上の例では[_, tag]
と書いています。そして、ルール内ではtag == "latest"
というように評価することで、tag
がlatest
かどうかチェックすることができます。
それでは、テストを実行してみましょう!
$ conftest test config.yml config.yml Only use official CircleCI images Do not use `latest` container image tags Use version 2.1 or higher
image: circleci/buildpack-deps
がタグを指定していないので、ちゃんと怒られていますね!ちなみにimage: circleci/buildpack-deps:latest
としてもちゃんと怒られます。
conftest
でテストができました!念の為ポリシーの全体像も載せておきます。
# .circleci/policy/circleci.rego package main deny[msg] { input.version < 2.1 msg = "Use version 2.1 or higher" } deny[msg] { docker_images := input.jobs[_].docker[_].image not startswith(docker_images, "circleci/") msg = "Only use official CircleCI images" } deny[msg] { docker_images := input.jobs[_].docker[_].image tag_is_latest(split(docker_images, ":")) msg = "Do not use `latest` container image tags" } # helpers tag_is_latest(images) { count(images) < 2 } tag_is_latest([_, tag]) { tag == "latest" }
config.ymlの修正
怒られっぱなしなのもちょっと悲しいので、config.yml
を直しましょう!
全てのポリシーを満たすために下のように修正しました。
version: 2.1 # 2.1に変更 jobs: build: docker: - image: circleci/buildpack-deps:jessie # latest使うのやめた steps: - checkout test: docker: - image: circleci/golang:1.12 # circleciのイメージに変更した steps: - checkout workflows: version: 2 build_and_test: jobs: - build - test
それではテスト実行です!
$ conftest test config.yml
config.yml
ちょっとわかりづらいんですけど、何も怒られなくなりましたね!
いい感じにconftest
で設定ファイルに対してテストが書けることがわかりました。Regoへの慣れは必要ですが、今後Open Policy Agentも様々な場所で使われていくことになりそうなので、Regoは学んでおいて損は無いんじゃないかなと思っています。
今回のコードは以下のリポジトリに置いてます。
まとめ
- YAML, JSONの設定ファイルに対してテストが書ける
conftest
を試した conftest
は宣言的にOpen Policy AgentのRegoを使ってテストを書ける- どこがルール違反になっているか、場所も教えてくれるとさらにうれしい
- 全部ポリシーをパスしてたらもうちょっとうれしい出力がほしい(笑)
- 今後に期待したい!!!
参考
Conftestを使ったテストの例 github.com
Open Policy Agentのチュートリアル www.openpolicyagent.org
セルフマネジメントについて考える / 『管理ゼロで成果はあがる〜「見直す・なくす・やめる」で組織を変えよう』を読んで②
「管理ゼロで成果はあがる〜「見直す・なくす・やめる」で組織を変えよう」という本を読み終えました!
管理ゼロで成果はあがる ~「見直す・なくす・やめる」で組織を変えよう
- 作者: 倉貫義人
- 出版社/メーカー: 技術評論社
- 発売日: 2019/01/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る
この本の中でも特に「生産的に働く」ことについては以下の記事で書評を書いたので、興味がある方はぜひ!
この記事では、第2部で紹介されている「セルフマネジメントのレベル」と「師匠が背中を見せる」ことについて注目した書評を書きます。
目次
- はじめに
- 第1部 生産的に働く ~楽に成果をあげるために“見直す”
- やり方を見直す ~「ふりかえり」で抜本的に生産性を改善する
- 生産性を見直す ~「時間対効果」の高い仕事をする
- タスクを見直す ~「タスクばらし」で小口化する
- やる気を見直す ~無理に上げない、なくさない状況をつくる
- 信頼関係を見直す ~「心理的安全性」を生み出す環境
- 会議を見直す ~口を動かすだけでなく、いっしょに手を動かす
- 雑談を見直す ~ホウレンソウから「ザッソウ」へ
- 社内業務を見直す ~人手に頼らない「業務ハック」で改善を続ける
- 価値を見直す ~受託脳よりも提案脳で考える
- 第2部 自律的に働く ~人を支配しているものを“なくす”
- 管理をなくす ~セルフマネジメントで働くチームをつくる
- 階層をなくす ~「ホラクラシー」組織を実現する仕組み
- 評価をなくす ~個人の成長と会社の貢献の「すりあわせ」をする
- 数字をなくす ~組織のビジョンよりも自分のためならがんばれる
- 組織の壁をなくす ~信頼しあえる企業文化の育て方
- 急募をなくす ~仕事があっても、いい人がいなければ採用しない
- 教育をなくす ~自分の頭で考える社員の育て方
- 制度をなくす ~本質ありきで考える「そもそも思考」
- 通勤をなくす ~働く場所に縛られない「リモートチーム」
- 第3部 独創的に働く ~常識や慣習に従うことを“やめる”
- おわりに
セルフマネジメントの3つのレベル
本書では「管理をなくす」にあたって、セルフマネジメントについて紹介されています。「仕事」、「組織」、「自分」という3つの観点で、それぞれレベルを3つに分けて定義しています。
観点 | Lv1 | Lv2 | Lv3 |
---|---|---|---|
仕事 | タスクを管理する | リソースを管理する | 価値を生み出す |
組織 | 周囲に伝える | 周囲と協調する | 周囲を活かす |
自分 | 休憩をとる | 安定して働く | 将来を考える |
何ができていると、どこのレベルにいるかというのが述べられています。「自分はそれぞれの観点でどこのレベルにいるのか?」というのを確認できて面白いですし、何ができればレベルが上がるのかというのも参考にすることができます。
「休憩をとる」ということ
セルフマネジメントのレベルの中でも注目したいのは「自分」の観点の中のLv1に入っている「休憩をとる」ことです。
休憩が習慣化していない人にとっては「いつ休憩をとるのか?」というのが難しいと感じそうですが、僕はポモドーロ・テクニックがここにフィットすると感じました。
何度か僕のブログで紹介していますが、ポモドーロ・テクニックについては僕のブログメンターでもあるカックさん( @kakakakakku )の以下の記事がおすすめです!
ポモドーロ・テクニックを使って「25分間は超絶に集中し、その後は5分間休憩をとる」というサイクルを繰り返していくと休憩も1つのタスクのように感じられるようになり、自然と習慣化もしていきます。
だれかが管理しないと休めないようでは、マネジメントする側の負担は大きいままです。
と、本書でも述べられています。休憩を自らとれるかとれないかは、自分だけの問題ではないということに気づく必要があります。休憩の習慣化は小さく見えてセルフマネジメントにおいては大きな一歩だと感じました。
師匠が背中を見せる
本書には「教育をなくす」という章があり、「教育する」のではなく「仕事の中で育てる」方法について紹介しています。
これには以下3つのポイントが述べられています。
- やってみせる
- やらせてみる
- フィードバックする
本書では育てる側を「師匠」、育てられる側を「弟子」と呼んでいて、1点目の「やってみせる」というのは「師匠が弟子に背中を見せる」ということです。ポイントとなるのは、いかにして師匠は弟子に自分の技術を盗ませるかです。そのためにまずは「やってみせる」ことが大事だと述べられています。個人的にふりかえってみても、物理的に離れていた新人に仕事をやってみてもらったときよりも、隣に座って一緒に仕事をしていた新人の方が圧倒的に成長のスピードが早かったのが記憶に残っています。これは、自然と自分の仕事を「やってみせる」ことができていたのではないかと思います。
気になった点としては、最初から物理的に離れている者同士(リモート)の場合、「やってみせる」にはどうしたら良いかということです。フルリモートをしている身としては今後の課題として追求していきたいです。
まとめ
- 『管理ゼロで成果はあがる〜「見直す・なくす・やめる」で組織を変えよう』を読んだ
- セルフマネジメントに悩んでいるor興味がある人にオススメできる
- セルフマネジメントできる人を増やしたい人にもオススメできる
- フルリモートにおける師弟関係について追求していきたい
管理ゼロで成果はあがる ~「見直す・なくす・やめる」で組織を変えよう
- 作者: 倉貫義人
- 出版社/メーカー: 技術評論社
- 発売日: 2019/01/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る