🦕

Deno+daxでDotfilesのセットアップ用CLIツールを整備する

2024/04/17に公開

はじめに

Dotfilesのセットアップ用のスクリプトはシェルスクリプトで整備するのが一般的です。簡単な処理であれば問題ありませんが、シェルスクリプトは複雑な処理を記述するには癖が強く、保守が大変になるという課題が知られています。

https://blog.8-p.info/ja/2021/09/15/bash/

https://zenn.dev/overflow_offers/articles/20220606-zx-introduction

そこで、シェルスクリプトではなくDenoとdaxでDotfilesのセットアップ用CLIツールを整備したところ、良い開発体験が得られたため、使用した技術を紹介します。


デモ

技術概要

Deno

DenoはJavaScript/TypeScriptランタイムの実装のひとつです。Denoには以下のような特徴があり、Node.jsの反省を生かして開発されています。

  • ES Moduleのみをサポート
  • node_module/, package.jsonの代わりに、依存関係を直接コード内にURLで記述するかdeno.jsonを使用する
  • パーミッションの明示的な指定を強制
  • TypeScriptの直接実行をサポート
  • トップレベルのawaitをサポート
  • フォーマッタ、リンタ、テストランナー、実行ファイルへのコンパイラをランタイムに同梱

https://deno.com/

詳細や使用例は他の記事をご参照ください。

https://zenn.dev/topics/deno

Node.jsよりも後発のランタイムとしてはBunにもBun Shellというものがありますが、まだ発展途上の機能であることや、tsconfig.json, package.jsonなどが散らからず、フォーマッタやリンタなどの設定を別で行う必要がないため、軽量のツールの開発については現状はDenoの方が優勢であると考え、こちらを採用しました。

https://twitter.com/bunjavascript/status/1748587391433253044

https://bun.sh/docs/runtime/shell

dax

daxはDenoで動作するクロスプラットフォームのシェルツールです。Googleのzxから影響を受けており、インスピレーション元と同様にシェルスクリプトとTypeScriptを組み合わせたスクリプトの開発が行えます。詳細は以下のリポジトリのREADMEをご覧ください。

https://github.com/dsherret/dax

#!/usr/bin/env -S deno run --allow-all
import $ from "@david/dax"; // "dax-sh" in Node

// run a command
await $`echo 5`; // outputs: 5

// outputting to stdout and running a sub process
await $`echo 1 && deno run main.ts`;

// parallel
await Promise.all([
  $`sleep 1 ; echo 1`,
  $`sleep 2 ; echo 2`,
  $`sleep 3 ; echo 3`,
]);

Inquirer.js

Inquirer.jsは対話型インターフェースを実装するためのライブラリです。daxにも対話型インターフェースは実装されていますが、こちらの方が高機能です。

https://github.com/SBoudrias/Inquirer.js

import { $ } from "https://deno.land/x/dax@0.39.1/mod.ts"
import { input } from "npm:@inquirer/prompts@4.3.2"

await $.prompt({
  message: "What's your name?",
  default: "dax",
  noClear: true,
});

await input({
  message: "What's your name?",
  default: "inquirer"
});


daxとInquirer.jsの入力の比較

実行可能ファイルの継続的デリバリー

ここでは、GitHub ActionsでDenoのスクリプトを実行可能ファイルにコンパイルし、GitHub Pagesでデプロイする方法を紹介します。他にはReleaseで実行ファイルを生成するなどの方法も良いと思います。

Denoではスクリプトの実行可能ファイルへのコンパイルを行えるため、生成した実行ファイルをデプロイすることで、Denoが存在しない環境においても実行ファイルをダウンロードすることでDenoのスクリプトを実行できるようになります。

https://docs.deno.com/runtime/manual/tools/compiler

これを行うには以下のようなワークフローを作成します。

.github/workflows/cd.yaml
name: CD

on:
  push:
    branches:
      - main
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write
  actions: read

jobs:
  compile:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout your repository using git
        uses: actions/checkout@v4
      - name: Setup Deno
        uses: denoland/setup-deno@v1
      - name: Compile install script
        run: deno compile --allow-all --output dist/setup scripts/setup.ts
      - name: Upload Pages Artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: dist

  deploy:
    needs: compile
    runs-on: ubuntu-22.04
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - name: Deploy to GitHub Pages
        id: deployment
        uses: actions/deploy-pages@v4

リポジトリ名がdotfilesのとき、実行可能ファイルはドメインを設定していなければhttps://username.github.io/dotfiles/setupで公開されます。これにより、セットアップを行いたい環境で以下のようなコマンドを実行することで、Denoによるセットアップ用のスクリプトを実行できます。

wget https://username.github.io/dotfiles/setup
chmod u+x setup
./setup

おわりに

私の作成したセットアップ用のスクリプトは以下の通りです。 (あまり洗練されていないので、無いよりマシ程度に見てください)

https://github.com/3w36zj6/dotfiles/blob/main/scripts/setup.ts

みなさんも是非Deno+daxでCLIツールを開発してみてください。

Discussion