🛠️

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

2020/11/08に公開

仕組み

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

    "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が分かれば

$ code --folder-uri vscode-remote://dev-container%2B2f557365722f6e616d652f646576656c6f702f70726f6a656374/usr/src

このように直接CLIから立ち上げる事が可能です。

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

"vscode-remote://" + URI.encode_www_form_component("dev-container+" + "/User/name/develop/project".unpack('H*')) + "/usr/src"

となっているようです。

スクリプトの作成

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

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が生成されます

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

AlfredのWorkflowsの作成

今回はghqと組み合わせて動作するWorkflowsを作成してみました。
動作イメージとして↓のREADMEのようにリポジトリ名を指定して直接VSCodeをコンテナ内で起動しています。
https://gist.github.com/Slowhand0309/253bb296cd7acb089601d2b32da4723b

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

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をベースに作ってますが、お使いのシェルでお好みで修正して頂ければ 🙏

Discussion