JSON Schema の力で書きやすいオレオレ YAML ファイルを作る

YAML を用いる独自の設定ファイル（いわゆる「オレオレ YAML」）を作る際は、その YAML ファイルのスキーマ (その設定ファイルがどのような key, value を持つべきなのかの情報) を [JSON Schema](https://json-schema.org/) として書いて紐付けを行うと、エディタのサポートが得られて便利、という話をします。

## JSON Schema を用意すると、YAML Language Server が補完等を行ってくれる

YAML ファイルの記述を支援するツールとして、[YAML Language Server](https://github.com/redhat-developer/yaml-language-server)[^1] があり、YAML ファイルのバリデーション、入力の補完や、値の説明などを行ってくれます。

[![](https://raw.githubusercontent.com/redhat-developer/vscode-yaml/main/images/demo.gif)](https://github.com/redhat-developer/vscode-yaml#features)
（Ref: https://github.com/redhat-developer/vscode-yaml#features)

補完等を行うために、 YAML Language Server はそのファイルに合わせた [JSON Schema](https://json-schema.org/) を参照しています。 (**JSON** Schema という名前にも関わらずびっくりですが使えます) 

基本的には [JSON Schema Store](https://www.schemastore.org/json/) という様々なファイルの JSON Schema を保管するサイトから引っ張ってきているのですが、自分で独自の YAML ファイルと JSON Schema の紐付けの設定を行うことも出来ます。

## オレオレ YAML の JSON Schema を用意して YAML Language Server の支援を受けられるようにするやり方

…ということで、オレオレ YAML が YAML Language Server の支援を受けられるように、オレオレ YAML 用の JSON Schema を作って紐付るやり方を説明していきます。

必要なのは、

1. オレオレ YAML 用の JSON Schema ファイルを用意する
2. 用意した JSON Schema ファイルと YAML ファイルと紐付けする （YAML ファイルのコメントとして、対応する JSON Schema を宣言する)

の2ステップです。

今回、例として Docker Compose っぽい内容の設定ファイルを例として用意しています。(サンプルリポジトリ:  https://github.com/tomoasleep/yaml-json-schema-example)
必要な設定を行うと、以下のような補完などが行えるようになります。

![completion.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7048/64242071-6c0b-92ee-469f-6c6320766466.png)

![validation.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7048/56640074-4439-ed3c-317d-1d10a0cf3198.png)


### 1. オレオレ YAML に対応した JSON Schema を用意する

用意する方法は言語などによって様々なやり方があり、もちろん手で書くのもありなのですが、言語によってはコードから生成することが出来ます。一から JSON Schema を書いて、 YAML の内容と対応するように維持し続けるのは骨が折れるので、コード生成等を利用するのはおすすめです。

例えば、 golang では、 https://github.com/invopop/jsonschema が type 定義から対応する JSON Schema を生成できます。 `yaml` tag の情報も参照出来るので一貫性をもたせやすく、出力する JSON Schema のカスタマイズも出来るので便利です。

```golang
type Config struct {
	Version  string             `yaml:"version"`
	Services map[string]Service `yaml:"services" jsonschema:"title=Services,description=Container definitions"`
}

type Service struct {
	Image string            `yaml:"image" jsonschema:"required,title=Image,description=The image to run,example=alpine:latest"`
	Ports []PortMapping     `yaml:"ports" jsonschema:"title=Ports,description=Ports to expose,example=3000:3000"`
	Env   map[string]string `yaml:"env"`
}
```

### 2. YAML ファイルのコメントで、対応する JSON Schema を宣言する

独自の YAML ファイルとに対応する JSON Schema の紐付けは、以下の `# yaml-language-server:` から始まるコメントを YAML ファイルの先頭行に記述すると出来ます。

```yaml
# yaml-language-server: $schema=<urlOrPathToTheSchema>

services:
  web:
    image: alpine:latest
    ports:
      - "3000:3000"
      - "8080:8080"
```

`<urlOrPathToTheSchema>` には以下のように、相対パス、絶対パス (~ での Home Directory は指定不可)、 URL のいずれかになります。

```yaml
# yaml-language-server: $schema=/absolute/path/to/schema
```

```yaml
# yaml-language-server: $schema=../relative/path/to/schema
```

```yaml
# yaml-language-server: $schema=http://url-to-schema
```

基本的には同じリポジトリに YAML ファイルと JSON Schema ファイルを同梱出来るなら相対パスで指定、同梱が難しいなら JSON Schema ファイルをパブリックな場所にアップロードして、その URL を指定する、などになると思います。

そして、YAML ファイルの雛形を生成するさいに、自動でこのコメントを含めるようにすると、 YAML Language Server を使うエディタ拡張をインストールしているユーザーは、追加の設定不要でいきなり補完などが行えるようになります。

#### 2'. (他の方法) YAML Language Server の設定として紐付けを書く

コメントとして書く方法の他には、 YAML Language Server の設定として以下のような設定を渡すと、紐付けが行なえます。

```json
"yaml.schemas": {
    "path-to-json-schema": "/myYamlFile.yaml"
}
``` 

YAML Language Server への設定の渡し方はエディタによって異なるのですが、例えば VSCode では [.vscode/settings.json](https://code.visualstudio.com/docs/getstarted/settings#_settingsjson) に書いて渡すことが出来ます。

## おわりに

YAML 関連の一部ツールでは JSON Schema が使われていて、 YAML Language Server の他にも YAML の Validator (https://json-schema-everywhere.github.io/yaml) などもあったり、他にも JSON Schema からドキュメント生成などにも流用できたりします。

なので、 YAML で独自の設定ファイルを用意する際は、そのスキーマとなる JSON Schema ファイルも用意するといろいろと便利でおすすめです :information_desk_person: 

[^1]: YAML Language Server は VSCode の [YAML](https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml) extension などのバックエンドとして使われていて、 [Language Server](https://code.visualstudio.com/api/language-extensions/language-server-extension-guide) なので、 VSCode に限らず様々なエディタから使うことが出来ます。