【Github】Github Actions ~ GITHUB_TOKEN / permissions ~

■ はじめに

https://dk521123.hatenablog.com/entry/2024/04/13/232832

で、はじめ、
環境変数「REVIEWDOG_GITHUB_API_TOKEN」を設定する際に
Tokenを発行していたが、「GITHUB_TOKEN シークレット」
というものを知ったのでメモ。
また、permissionsについても分かったので、それもメモ。

目次

【1】GITHUB_TOKEN シークレット
【2】GITHUB_TOKEN シークレット の参照
【3】permissions
【4】GITHUB_TOKEN シークレット の利点
【5】サンプル

【1】GITHUB_TOKEN シークレット

https://docs.github.com/ja/actions/security-guides/automatic-token-authentication#about-the-github_token-secret

より抜粋
~~~~~~~~~~~~~~
各ワークフロー ジョブの開始時に、GitHub によって、
ワークフローで使用する一意の GITHUB_TOKEN シークレットが自動的に作成
・・・略・・・
ジョブが終了するか最大 24 時間後に、GITHUB_TOKEN の有効期限が切れます。
~~~~~~~~~~~~~~

【2】GITHUB_TOKEN シークレット の参照

* ${{ secrets.GITHUB_TOKEN }} によって参照する

https://docs.github.com/ja/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow

    steps:
      env:
        REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}

【3】permissions

* permissions によって
 GITHUB_TOKEN に付与されたアクセス権限を指定する

jobs:
  demo-pull-request:
    runs-on: ubuntu-latest
    # ★この部分
    permissions:
      contents: read
      pull-requests: write
    # https://docs.github.com/ja/enterprise-cloud@latest/actions/using-jobs/assigning-permissions-to-jobs#defining-access-for-the-github_token-scopes  
    # permissions: read-all
    # permissions: write-all

https://docs.github.com/ja/enterprise-cloud@latest/actions/using-jobs/assigning-permissions-to-jobs#overview

permissions Explanations
actions GitHub Actions操作
checks チェック実行とチェック スイートを操作
contents リポジトリの内容を操作
deployments デプロイを操作
id-token OpenID Connect (OIDC) トークンをフェッチ
issues イシューを操作
discussions GitHub Discussions を操作
packages GitHub Packages を操作
pages GitHub Pages を操作
pull-requests pull request を操作
repository-projects GitHub プロジェクト (クラシック) を操作
security-events GitHub コード スキャンと Dependabot アラートを操作
statuses コミットの状態を操作

【4】GITHUB_TOKEN シークレット の利点

* 「PAT(Personal Access Tokens)」と比べて、以下の利点がある
~~~~~~~
[1] Tokenの有効期限は、その時限りなのでローテーションなど気にしなくていい
[2] ユーザが離脱するなど考えなくていい
[3] 権限操作がWorkflowごとで可能
~~~~~~~

【5】サンプル

https://docs.github.com/ja/actions/security-guides/automatic-token-authentication#example-1-passing-the-github_token-as-an-input

name: Open new issue
on: workflow_dispatch

jobs:
  open-issue:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      issues: write
    steps:
      - run: |
          gh issue --repo ${{ github.repository }} \
            create --title "Issue title" --body "Issue body"
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

その他
https://docs.github.com/ja/actions/security-guides/automatic-token-authentication#example-2-calling-the-rest-api

関連記事

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
reviewdog ~ 入門編 ~
https://dk521123.hatenablog.com/entry/2024/04/13/232832

【Java】Windows で JDK を設定する

■ はじめに

https://dk521123.hatenablog.com/entry/2017/08/01/234900

の Windows 版。

【0】インストール方法

https://learn.microsoft.com/ja-jp/java/openjdk/install

に記載されている。

[1] msi でインストール
[2] zip で インストール
[3] winget

etc...

【1】手順

1)ダウンロード

https://learn.microsoft.com/ja-jp/java/openjdk/download

から ダウンロードする
(今回は、「microsoft-jdk-11.0.23-windows-x64.zip」)

2)インストールする

zip の場合

[1] ダウンロードしたZIPファイルを解凍する(今回、「C:\jdk-11.0.23+9」)
[2] 以下の環境変数を設定
 * JAVA_HOME: [1] のファイルパス(今回、「C:\jdk-11.0.23+9」)
 * Path: %JAVA_HOME%\bin

3)確認

# コマンドプロンプトで以下をタイプ
java --version

関連記事

LinuxJDK を設定する
https://dk521123.hatenablog.com/entry/2017/08/01/234900
SBT ~ 環境構築編 ~
https://dk521123.hatenablog.com/entry/2024/01/13/002637
curl コマンド
https://dk521123.hatenablog.com/entry/2017/12/05/233100

【Github】reviewdog ~ RDFormat ~

■ はじめに

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)出力フォーマット解析

【1】Reviewdog Diagnostic Format (RDFormat)

* 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

【2】RDFormat を使用した方法

* 以下の公式ドキュメントより抜粋

https://github.com/reviewdog/reviewdog?tab=readme-ov-file#reviewdog-diagnostic-format-rdformat

全体イメージ

# Linter の結果を、jq などで「rdjson」又は「rdjsonl」に変換すればいいだけ
$ <linter> | <convert-to-rdjson> | reviewdog -f=rdjson -reporter=github-pr-review
# or
$ <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
          pip install sqlfluff==3.0.4
      # Step1: Install reviewdog
      - uses: reviewdog/action-setup@v1
        with:
          reviewdog_version: latest
      # Step2: Lint
      - 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

# 例1との変更点 + ついでにエラーハンドリング
      # Step2: Lint
      - 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"

            # Run linter
            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):
  # Step1: Read JSON
  with open(input_file, "r", newline="\n") as in_file:
    input_json_dict = json.load(in_file)

  # Step2: Convert
  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
  }

  # Step3: Write JSON
  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

# 見づらいので、jqを間にかます
$ sqlfluff lint test.sql --dialect ansi --format json | jq

# jqコマンドでrdjsonフォーマットに合うように試してみる
$ 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