■ はじめに
https://dk521123.hatenablog.com/entry/2024/04/13/232832
https://dk521123.hatenablog.com/entry/2024/04/18/161200
の続き。
reviewdog の Reviewdog Diagnostic Format (RDFormat) について
切り出して、取り上げる。
目次
【1】Reviewdog Diagnostic Format (RDFormat)
1)rdjson
2)rdjsonl
【2】RDFormat を使用した方法
【3】注意点
1)変換を jq -f <file>で行う場合、改行コードはLFにしておくこと
【4】サンプル
例1:SQLFluff - Jq バージョン
例2:SQLFluff - Python バージョン
【5】補足:「どうやって組み込んでいったか?」について
1)準備:ローカル環境構築
2)環境設定する上での注意点
3)出力フォーマット解析
* reviewdog 独自のフォーマットである 「rdjson」又は「rdjsonl」に変換して
Linterの結果を reviewdog に食わせる
https://github.com/reviewdog/reviewdog?tab=readme-ov-file#reviewdog-diagnostic-format-rdformat
1)rdjson
* Reviewdog Diagnostic JSON Format
https://github.com/reviewdog/reviewdog/tree/master/proto/rdf#rdjson
2)rdjsonl
* Reviewdog Diagnostic JSON Lines Format
https://github.com/reviewdog/reviewdog/tree/master/proto/rdf#rdjsonl
* 以下の公式ドキュメントより抜粋
https://github.com/reviewdog/reviewdog?tab=readme-ov-file#reviewdog-diagnostic-format-rdformat
全体イメージ
$ <linter> | <convert-to-rdjson> | reviewdog -f=rdjson -reporter=github-pr-review
$ <linter> | <convert-to-rdjsonl> | reviewdog -f=rdjsonl -reporter=github-pr-review
【3】注意点
1)変換を jq -f で行う場合、改行コードはLFにしておくこと
下記「例1:SQLFluff - jq バージョン」のように、
jq -f <file>で、「rdjson」又は「rdjsonl」に変換する場合、
ファイル(<file>)の改行コードは、CRLFではなくLFにしておくこと
=> 以下「エラーメッセージ」のようになってしまう可能性がある
エラーメッセージ
jq: error: syntax error, unexpected INVALID_CHARACTER (Unix shell quoting issues?)
at <top-level>, line1:
{
【4】サンプル
https://dk521123.hatenablog.com/entry/2024/04/18/161200
で行ったThird party で行った SQLFluff with reviewdog を自分でかつ
最新 SQLFluff v3 対応版を実装してみる
例1:SQLFluff - jq バージョン
.github/workflows/demo_reviewdogs.yml
name: DemoForReviewdogs
on:
- pull_request
env:
REVIEWDOG_GITHUB_API_TOKEN: "${{ secrets.REVIEWDOG_GITHUB_API_TOKEN }}"
jobs:
demo-job:
name: lint
runs-on: ubuntu-latest
steps:
- name: Run checkout
uses: actions/checkout@v4
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install sqlfluff==3.0.4
- uses: reviewdog/action-setup@v1
with:
reviewdog_version: latest
- name: lint with SQLFluff
run: |
for sql_file in `find sqls/ -name '*.sql'`
do
echo "sql_file=$sql_file"
sqlfluff lint $sql_file --dialect snowflake --format json | jq -f .github/template.jq | reviewdog -name="sqlfluff" -f=rdjson -reporter=github-pr-review
done
.github/template.jq (★注意点:改行コードはLFにしておくこと★)
{
source: {
name: "sqlfluff",
url: "https://github.com/sqlfluff/sqlfluff"
},
severity: "ERROR",
diagnostics: (. // {}) | map(. as $file | $file.violations[] as $violation | (if $file.violations[].warning == true then "WARNING" else "ERROR" end) as $severity | {
message: "[\($violation.name)] - \($violation.description)",
location: {
path: $file.filepath,
range: {
start: {
line: $violation.start_line_no,
column: $violation.start_line_pos
},
end: {
line: $violation.end_line_no,
column: $violation.end_line_pos
},
}
},
suggestions: ([$violation.fixes[] as $suggestion | {
range: {
start: {
line: $suggestion.start_line_no,
column: $suggestion.start_line_pos
},
end: {
line: $suggestion.end_line_no,
column: $suggestion.end_line_pos
}
},
text: $suggestion.edit
}]),
severity: $severity,
code: {
value: $violation.code,
url: "https://docs.sqlfluff.com/en/stable/rules.html#rule-\($violation.code)"
},
})
}
NG.sql (テスト用SQL)
SELECT a + b FROM tbl;
例2:SQLFluff - Python バージョン
今回、<convert-to-rdjson>部分を jq コマンドを使っているが、
複雑で込み入った処理になった場合の汎用性がないので、
Python使ってもいいかも、、、
.github/workflows/demo_reviewdogs.yml
- id: lint-with-sqlfluff
name: lint with SQLFluff
env:
RESULT_FILE_PATH: ./result_from_sqlfluff.json
RDJSON_FILE_PATH: ./result_with_rdjson.json
shell: /usr/bin/bash {0}
run: |
is_error=false
for sql_file in `find sqls/ -name '*.sql'`
do
echo "sql_file=$sql_file"
sqlfluff lint $sql_file --dialect snowflake --format json --write-output ${{ env.RESULT_FILE_PATH }}
python .github/convert.py ${{ env.RESULT_FILE_PATH }} ${{ env.RDJSON_FILE_PATH }}
cat ${{ env.RDJSON_FILE_PATH }} | reviewdog -name="sqlfluff" -tee -fail-on-error -f=rdjson -reporter=github-pr-review
if [ $? -ne 0 ] ; then
is_error=true
fi
rm ${{ env.RESULT_FILE_PATH }} 2> /dev/null
rm ${{ env.RDJSON_FILE_PATH }} 2> /dev/null
done
if [ "$is_error" == "true" ] ; then
echo "Error"
exit 1
else
echo "Success!"
fi
.github/convert.py (変換用Python)
import json
import sys
def get_severity(is_warning):
return "WARNING" if is_warning else "ERROR"
def main(input_file, output_file):
with open(input_file, "r", newline="\n") as in_file:
input_json_dict = json.load(in_file)
diagnostics = []
has_error = False
for input in input_json_dict:
path = input.get("filepath")
for violation in input.get("violations"):
is_warning = violation.get("warning", True)
if not is_warning:
has_error = True
severity = get_severity(is_warning)
suggestions = []
for fix in violation.get("fixes"):
suggestion = {
"range": {
"start": {
"line": fix.get("start_line_no"),
"column": fix.get("start_line_pos")
},
"end": {
"line": fix.get("end_line_no"),
"column": fix.get("end_line_pos")
}
},
"text": fix.get("edit")
}
suggestions.append(suggestion)
diagnostic = {
"message": f"[{violation.get('name')}] - {violation.get('description')}",
"location": {
"path": path,
"range": {
"start": {
"line": violation.get("start_line_no"),
"column": violation.get("start_line_pos")
},
"end": {
"line": violation.get("end_line_no"),
"column": violation.get("end_line_pos")
},
}
},
"suggestions": suggestions,
"severity": severity,
"code": {
"value": violation.get("code"),
"url": f"https://docs.sqlfluff.com/en/stable/rules.html#rule-{violation.get('code')}"
}
}
diagnostics.append(diagnostic)
main_severity = get_severity(not has_error)
output_json_dict = {
"source": {
"name": "sqlfluff",
"url": "https://github.com/sqlfluff/sqlfluff"
},
"severity": main_severity,
"diagnostics": diagnostics
}
with open(output_file, "w", newline="\n") as out_file:
json.dump(output_json_dict, out_file)
if __name__ == "__main__":
if len(sys.argv) > 3:
print("[How to use]")
print(" python convert.py input.json ouptput.json")
print(" args1: Input file with json from SQLFluff (e.g. input.json)")
print(" args2: Output file with json for reviewdog (e.g. ouptput.json)")
else:
input_file = sys.argv[1]
output_file = sys.argv[2]
main(input_file, output_file)
【5】補足:「どうやって組み込んでいったか?」について
* 「例1:SQLFluff」で実際にどうやって組み込んでいったかについてメモしておく
1)準備:ローカル環境構築
* フォーマットが分かっていない状況で、Github Actionsを動かしながら実装するのは
めんどいので、まずは、ローカル環境に以下をインストールした
~~~~~
+ Linter(今回は SQLFluff)」
+ reviewdog
~~~~~
=> インストールについては、以下の関連記事を参照のこと
SQL Linter ~ SQLFluff ~
https://dk521123.hatenablog.com/entry/2024/02/28/225002
Github ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2019/07/18/234652
2)環境設定する上での注意点
* Linterはバージョン違いにより出力フォーマットが異なることもあるので
Github Actionsを動かす環境とローカル環境のバージョンを一致させること
(バージョン固定してもいいかも)
出力結果 - SQLFluff v2.3.5
[
{
"filepath": "test.sql",
"violations": [
{
"line_no": 1,
"line_pos": 1,
"code": "LT01",
"description": "Expected only single space before 'SELECT' keyword. Found ' '.",
"name": "layout.spacing"
},
・・・
{
"line_no": 1,
"line_pos": 27,
"code": "LT01",
"description": "Unnecessary trailing whitespace.",
"name": "layout.spacing"
}
]
}
]
出力結果 - SQLFluff v3.0.4 (上の「SQLFluff v2.3.5」と比較)
[
{
"filepath": "test.sql",
"violations": [
{
"start_line_no": 1,
"start_line_pos": 1,
"code": "LT01",
"description": "Expected only single space before 'SELECT' keyword. Found ' '.",
"name": "layout.spacing",
"warning": false,
"fixes": [
{
"type": "replace",
"edit": " ",
"start_line_no": 1,
"start_line_pos": 1,
"start_file_pos": 0,
"end_line_no": 1,
"end_line_pos": 3,
"end_file_pos": 2
}
],
"start_file_pos": 0,
"end_line_no": 1,
"end_line_pos": 3,
"end_file_pos": 2
},
・・・
{
"start_line_no": 1,
"start_line_pos": 27,
"code": "LT01",
"description": "Unnecessary trailing whitespace.",
"name": "layout.spacing",
"warning": false,
"fixes": [
{
"type": "delete",
"edit": "",
"start_line_no": 1,
"start_line_pos": 27,
"start_file_pos": 26,
"end_line_no": 1,
"end_line_pos": 29,
"end_file_pos": 28
}
],
"start_file_pos": 26,
"end_line_no": 1,
"end_line_pos": 29,
"end_file_pos": 28
}
],
"statistics": {
"source_chars": 29,
"templated_chars": 29,
"segments": 33,
"raw_segments": 20
},
"timings": {
"templating": 0.0033749999997780833,
"lexing": 0.0008491999997204402,
"parsing": 0.0106869999999617,
"linting": 0.027636198999971384,
"AL01": 0.00011649999987639603,
・・・
"TQ01": 2.769999991869554e-05
}
}
]
3)出力フォーマット解析
* 後は、実際に出力したりして、
どういったフォーマットになっているかをみて
寄せていくだけ
コマンド例
$ sqlfluff lint test.sql --dialect ansi --format json
$ sqlfluff lint test.sql --dialect ansi --format json | jq
$ sqlfluff lint test.sql --dialect ansi --format json | jq -f template.jq
$ sqlfluff lint test.sql --dialect ansi --format json | jq -f template.jq | reviewdog -f=rdjson -name="hello" -reporter=github-pr-review
関連記事
reviewdog ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2024/04/13/232832
Github ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2019/07/18/234652
Github Actions ~ 基礎知識編 ~
https://dk521123.hatenablog.com/entry/2021/11/04/142835
Github Actions ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2022/06/16/151443
Github Actions ~ 基本編 ~
https://dk521123.hatenablog.com/entry/2023/12/22/195715
Github Actions ~ pull_request / pull_request_target ~
https://dk521123.hatenablog.com/entry/2024/04/10/152101
Github Actions ~ SQL Linter ~
https://dk521123.hatenablog.com/entry/2024/03/04/180308
SQL Linter ~ SQLFluff ~
https://dk521123.hatenablog.com/entry/2024/02/28/225002
GitHub CLI ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2024/02/17/233836
jq コマンド ~ JSON を扱う ~
https://dk521123.hatenablog.com/entry/2020/02/01/000000
シェル ~ ファイル処理あれこれ ~
https://dk521123.hatenablog.com/entry/2020/09/28/000000
JSON
https://dk521123.hatenablog.com/entry/2019/10/19/104805
JSONあれこれ
https://dk521123.hatenablog.com/entry/2022/02/14/000000