Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

zx を使っていい感じに SIGINT を捌く

シェルスクリプトで ctrl + C を押して中断したときのクリーンアップ処理を書くのはそこそこ大変です(頑張ればできますが)。今回は zx を使った、クリーンアップ処理を含むコードの書き方を紹介します。

そもそも zx とは?

alt シェルスクリプト的なものです。JS ファイル内にシェルスクリプトを書くことができます。

import { $ } from 'zx';
const has = (command) => $`type "${command}" > /dev/null 2>&1`.then(() => true, () => false)

if (await has('rbenv')) {
  await $`rbenv init -`;
}

…まあ上のコードが読みやすいかはともかく、 async/await が使えたり、JS の各種メソッドが使えるので非常に便利です。

zx を使って SIGINT を捌いてみる

この processWithCleanup という関数を使ってコードを書いていきます。

import { $ } from 'zx';
import process from 'process';
const processWithCleanup = async (f, cleanup) => {
  process.on('SIGINT', () => {});
  try {
    await f();
  } catch (e) {
    await cleanup();
    throw e;
  }
};

この関数は、受け取った関数 f を実行し、SIGINT を受信したら cleanup を実行します(SIGINT かどうか判別してないですが)。cleanup 後に throw e しているので、このあとどう処理するか任せることができます。

実例

const main = async () => {
  await processWithCleanup(
    async () => {
      await $`git stash`;
      await $`docker build -t hoge ./`;
    },
    () => $`git stash pop`
  );
  $`git stash pop`;
  await $`docker push hoge`;

  await $`deploy`;
};
main().catch(console.error);

この例ではビルドして Docker Hub へプッシュし、最後にデプロイコマンドを実行します。ビルド中にエラーが起きたらデプロイは走りません。