MacでVSCode Remote ContainersをCLIから直接開く

## 仕組み
一度VSCode Remote Containersを開いた後 `cat ~/Library/Application\ Support/Code/storage.json` の
`windowsState` > `lastActiveWindow` > `folder` を見ると以下のような感じになっているのが分かります。

```json
    "windowsState": {
        "lastActiveWindow": {
            "folder": "vscode-remote://dev-container%2B2f557365722f6e616d652f646576656c6f702f70726f6a656374/usr/src",
            "backupPath": "...",
            "remoteAuthority": "dev-container+2f557365722f6e616d652f646576656c6f702f70726f6a656374",
            "uiState": {
                "mode": 1,
                "x": 0,
                "y": 23,
                "width": 3440,
                "height": 1417
            }
        },
        "openedWindows": []
    },
```
この `folder` のURLが分かれば

```sh
$ code --folder-uri vscode-remote://dev-container%2B2f557365722f6e616d652f646576656c6f702f70726f6a656374/usr/src
```
このように直接CLIから立ち上げる事が可能です。

`2f557365722f6e616d652f646576656c6f702f70726f6a656374` の部分は`16進数文字列`になっていて
これをデコードすると `/User/name/develop/project` となります。また `%2B` が含まれているのでURLエンコードされているのが分かります。
最後の `/usr/src` は `devcontainer.json` の `workspaceFolder` で指定したパスになります。
これらを踏まえて全体のURL構成要素としては

```ruby
"vscode-remote://" + URI.encode_www_form_component("dev-container+" + "/User/name/develop/project".unpack('H*')) + "/usr/src"
```
となっているようです。

## スクリプトの作成
パスを指定したらVSCode Remote Containersを起動する為のURLを生成してくれるスクリプトを書いてみます。
指定したパス配下の `.devcontainer/devcontainer.json` の `workspaceFolder` を読み込んで生成してます。

```ruby:main.rb
# frozen_string_literal: true
# !/usr/bin/env ruby
require 'json'

module VSCodeRemoteContainer
  class Utility
    def initialize
    end

    def generate_url(root_path)
      folder = find_workspace_folder(root_path)
      path = "dev-container+#{root_path.unpack('H*')[0]}"
      puts "vscode-remote://#{URI.encode_www_form_component(path)}#{folder}"
    end

    def find_workspace_folder(root_path)
      unless File.exist?("#{root_path}/.devcontainer/devcontainer.json")
        puts 'Not found devcontainer.json file.'
        return
      end

      config = JSON.parse(File.read("#{root_path}/.devcontainer/devcontainer.json"))
      config['workspaceFolder']
    end
  end
end

VSCodeRemoteContainer::Utility.new.generate_url(*ARGV)
```

上記を `main.rb` として保存し以下の様に実行するとURLが生成されます

```sh
$ ruby main.rb '/User/name/xxxx'
# => vscode-remote://dev-container%2B2f557365722f6e616d652f646576656c6f702f70726f6a656374/usr/src
```

## AlfredのWorkflowsの作成
今回は[ghq](https://github.com/x-motemen/ghq)と組み合わせて動作するWorkflowsを作成してみました。
動作イメージとして↓のようにリポジトリ名を指定して直接VSCodeをコンテナ内で起動しています。
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/299271/26e04b3e-70cb-5716-3afc-b47b7c38743c.gif">


Workflowsを作成するにあたって上記のスクリプトに追記しました。

```ruby
module VSCodeRemoteContainer
  class Utility
    attr_accessor :bin_path
    def initialize
      @bin_path = ENV['GHQ_PATH'] || '/usr/local/bin'
    end

    def ghq_exists?
      !`which #{@bin_path}/ghq`.empty?
    end

    def search
      return unless ghq_exists?

      result = []
      `#{@bin_path}/ghq list --full-path`.split(/\R/).each do |d|
        Dir.foreach(d) do |path|
          next if ['.', '..'].include?(path)

          file = File.join(d, path)
          result << d if file.include?('.devcontainer')
        end
      end
      result
    end
  end
end
```
やってる事は `ghq` を使って対象のリポジトリ一覧をとってきて、
`.devcontainer` ディレクトリがあるリポジトリだけを返すようにしてます。

出来上がったものは
https://gist.github.com/Slowhand0309/253bb296cd7acb089601d2b32da4723b
こちらに置いております。とりあえず作ってみた程度なので不具合等見つけたら
ご連絡頂けると助かります。
※ `zsh`をベースに作ってますが、お使いのシェルでお好みで修正して頂ければ :pray: