KAKEHASHI Tech Blog

カケハシのEngineer Teamによるブログです。

リポジトリを作成したら最初に設定すること(2022冬⛄️)

本記事は カケハシ Advent Calendar 2022 10 日目の記事です。


Musubi 開発チームの加藤です。1 年ぶり ですね。 今回は「私の開発環境 2022 冬」をお送りしようと思ったのですが、今年はリポジトリをたくさんセットアップしたので、 「リポジトリを作成したら最初にすること」をお送りします1

はじめに

プロジェクトなり個人開発なりでリポジトリを作成する際、組織や開発者の中で共通して使用するツールやその設定ファイルがあると思います。

2022 年を通じて自分の中である程度型ができてきたので、それをご紹介しようと思います。

前提

Python で実装したコードを Serverless FrameworkAWS Lambda にデプロイするケースをモデルとして、リポジトリを設定していきます。

初期状態は、GitHub リポジトリを作成直後のリポジトリ名が入った README.md がある状態から開始します。

思想

  • 「計算機がチェックできることはすべてチェックさせる」
  • 「各コミット時点で可能な限り環境が固定されている状態を保つ」
  • 「可能な限り仕組みを自作せずにサードパーティーのツールに頼り、楽をする」

の 3 点です。

.gitignore を作成する

.gitignore は、リポジトリに含めたくないファイルを指定するファイルです。

手作りすることもできますが、使用するツールや環境に合わせて gitignore.io で作成すると良いです。

今回は実装言語である Python, デプロイするために用いる Serverless Framework, その実行言語の Node.js, そして各 OS を指定して作成しました。

gitignore.io

作成ボタンを押下すると、生成された .gitignore が表示されます。 ブラウザからコピペするか、URL をコピペして、 curl で取得することもできます。

curl $(pbpaste) > .gitignore

また、ファイル冒頭には作成時の情報の URL がコメントに記載されているため、 対象を追加・削除するときはその URL を参照してください。

Created by をクリックすると同じ対象で .gitignore を取得できます。 Edit at をクリックすると同じ対象を選択した状態で選択画面に遷移します。

# Created by https://www.toptal.com/developers/gitignore/api/macos,linux,windows,python,serverless,node
# Edit at https://www.toptal.com/developers/gitignore?templates=macos,linux,windows,python,serverless,node

.editorconfig を作成する

.editorconfig は、エディタに依存せずにインデントや改行の扱いを共有するためのファイルです。 Visual Studio Code, Vim, JetBrains 製品 など多くのエディタ でサポートされています。

公式ページ に基礎的な使い方が記載されています。

今回は以下の内容にしました。

# Editor configuration, see https://editorconfig.org/
root = true # リポジトリトップに配置する場合は、より上位のディレクトリの影響を防ぐために必要

[*] # 全ファイル共通の設定(上書き可)
charset = utf-8 # 文字コードを UTF-8 とする
end_of_line = lf # 改行コードを LF とする
indent_size = 2 # インデント幅を 2 とする
indent_style = space # インデントをスペースで行う
insert_final_newline = true # ファイル末尾に改行を入れる
trim_trailing_whitespace = true # 行末の空白を削除する

[*.py] # .py ファイルのみに適用する設定
indent_size = 4 # インデント幅を 4 とする

Renovate を設定する

Renovate は、依存ライブラリや言語処理系の更新を自動化するツールです(無料!)。

デフォルトブランチにある renovate.json 2 に設定を記載することで、自動的に PR 作成やマージをおこなってくれます。

初期段階で入れるのはこんな感じです。

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:base",
    ":timezone(Asia/Tokyo)",
    ":enablePreCommit",
    "group:recommended",
    "group:monorepos"
  ],
  "schedule": [
    "after 10:30 before 18:00 every weekday except after 12:00 before 13:00"
  ],
  "labels": ["renovate"],
  "pin": {
    "automerge": false
  },
  "pinDigest": {
    "automerge": false
  },
  "lockFileMaintenance": {
    "enabled": true
  },
  "packageRules": [
    {
      "matchManagers": ["pre-commit"],
      "automerge": true
    },
    {
      "matchPackageNames": ["python"],
      "allowedVersions": "<3.10"
    },
    {
      "groupName": "boto3",
      "matchPackageNames": [
        "boto3",
        "boto3-stubs",
        "botocore",
        "botocore-stubs"
      ],
      "automerge": true
    },
    {
      "groupName": "serverless",
      "matchPackageNames": [
        "serverless",
        "serverless-plugin-lambda-insights",
        "serverless-prune-plugin",
        "serverless-python-requirements"
      ]
    }
  ]
}

上から設定内容を説明します。

  • $schema で、このファイルのスキーマを指定する。これをすることで Visual Studio Code などのエディタで補完が効くようになる
  • config:baseextends に指定して、基本的な設定を継承する
  • :timezone(Asia/Tokyo)extends に指定して、時刻指定のタイムゾーンを日本時間にする
  • :enablePreCommit で、後述の pre-commit の hook の更新を有効にする
  • group:recommended, group:monoreposextends に指定して、メジャーなパッケージをグループ化して 1 つの PR で更新できるようにする
  • schedule で、平日の 10:30 〜 18:00 のみ動作させる
  • labels で、Renovate が作成した PR に renovate ラベルを付与する
  • pin, pinDigest"automerge": true を指定して、ピン留めの PR を自動マージする
  • lockFileMaintenance"enabled": true を指定して、package-lock.jsonpoetry.lock の更新 PR を有効にする
  • 以下 packageRules の指定
    • pre-commit (後述)の hook の更新は自動マージする
    • Python のバージョンを 3.10 未満に制限する(AWS Lambda のランタイムが 3.9 までなので)
    • boto3 および関連パッケージは 1 つの PR にまとめて自動マージする(デプロイに含めず AWS Lambda 上のものを使用するので、ローカルも常に最新を使う)
    • Serverless Framework および関連プラグインを 1 つの PR にまとめる

プロジェクトや開発フェーズによってポリシーが異なるので、適宜変更すると良いと思います。

また、 renovate.json の記述ミスに備えて、以下の GitHub Actions workflow を設定します。

name: Validate renovate.json

on:
  push:
    branches:
      - main # メインブランチ名で置き換えてください
    paths:
      - renovate.json
  pull_request:
    branches:
      - main # メインブランチ名で置き換えてください
    paths:
      - renovate.json

jobs:
  validate:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v3
      - name: Validate renovate.json
        uses: rinchsan/renovate-config-validator@v0.0.12
        with:
          pattern: renovate.json

.python-version.node-version の設定

pyenvnodenv を使っている場合3.python-version.node-version に、プロジェクトの Python と Node.js のバージョンを記載します。

このファイルは開発者間のバージョン分散を防ぐ他にも actions/setup-python action や actions/setup-node action の python-version-filenode-version-file に渡すことができ、また Renovate が更新 PR を作成してくれるため、

Renovate が更新 PR を作成する -> GitHub Actions で新しいバージョンで CI を実行 -> 通過したらマージ

というフローを組むことができるので大変便利です。

pre-commit と各種 hook の設定

pre-commit は、 Gitpre-commit hook から lint, formatter 等を実行するツールです。 pre-commit の個々の処理もまた "hook" と呼ばれています。

Homebrew 等で pre-commit をインストールした後、設定ファイル .pre-commit-config.yaml があるリポジトリで pre-commit install を実行すると、 Git hooks に自身を呼び出すコードをインストールしします。

すると git commit コマンドを実行するときに各 hook を実行して、そのうち 1 つでも失敗するとコミットを中止します4

Formatter の hook は未フォーマットのため失敗した時はファイルを書き換えた状態で停止するので、再度 git add してからもう一度 git commit をすればコミットができます。

.pre-commit-config.yaml に、以下のような hook の設定を記載します。

  • https://github.com/pre-commit/pre-commit-hooks は、 pre-commit が提供する共通の hook リポジトリです。
    • trailing-whitespace は行末の空白を削除します
      • .gitignore に CR で終わる行があるため、この hook の適用対象から除外しています
    • end-of-file-fixer はファイルの末尾に改行を追加します
    • check-added-large-files はコミットに大きなサイズのファイルのコミットを防ぎます
  • https://github.com/adrienverge/yamllint.gityamllint hook は、yamllintyaml ファイルのフォーマットをチェックします
  • https://github.com/asottile/pyupgradepyupgrade hook は、 pyupgrade Python の古い記述を新しい記述に更新します
  • https://github.com/pycqa/isortisort hook は、isort を使用して Python の import 文を整形します
    • additional_packagestoml を指定することで、 pyproject.toml に記載された設定を読み込みます
  • https://github.com/psf/blackblack hook は、 black を使用して Python のコードをフォーマットします
    • args で引数に --config pyproject.toml を指定することで、 pyproject.toml に記載された設定を読み込みます
  • https://github.com/PyCQA/flake8flake8 hook は、 flake8 Python のコードを lint します
    • additional_packagespyproject-flake8 を指定して、 entrypflake8 を指定することで、flake8 が対応していない pyproject.toml から設定を読み込むことができます。
  • https://github.com/pre-commit/mirrors-mypymypy hook は、 mypy を使用して Python の型チェックを実行します
    • additional_packages に使用するパッケージの型スタブ パッケージを指定しています
  • https://github.com/pre-commit/mirrors-prettierprettier hook は、Prettier を使用してファイルフォーマットを行います
    • YAML ファイルや JSON ファイルに対応しているため、GitHub Actions の設定ファイルや renovate.json をフォーマットできるので、 Python プロジェクトのリポジトリでも入れる価値は大きいです
  • https://github.com/python-poetry/poetrypoetry-check hook は、 Poetry の設定ファイルをチェックします
  • local リポジトリで custom hook を作成します
    • additional_packages に書いたパッケージをインストールした上で、 entry に記載の secretlint を実行します
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.3.0
    hooks:
      - id: trailing-whitespace
        exclude_types:
          - gitignore
      - id: end-of-file-fixer
      - id: check-added-large-files

  - repo: https://github.com/adrienverge/yamllint.git
    rev: v1.28.0
    hooks:
      - id: yamllint

  - repo: https://github.com/asottile/pyupgrade
    rev: v3.2.2
    hooks:
      - id: pyupgrade

  - repo: https://github.com/pycqa/isort
    rev: 5.10.1
    hooks:
      - id: isort
        additional_dependencies:
          - toml

  - repo: https://github.com/psf/black
    rev: 22.10.0
    hooks:
      - id: black
        args: ["--config", "pyproject.toml"]

  - repo: https://github.com/PyCQA/flake8
    rev: 5.0.4
    hooks:
      - id: flake8
        entry: pflake8
        additional_dependencies:
          - pyproject-flake8

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v0.971
    hooks:
      - id: mypy
        name: mypy
        additional_dependencies:
          - "boto3-stubs[s3,dynamodb,cloudfront,stepfunctions]"
          - typing-extensions
          - types-python-dateutil

  - repo: https://github.com/pre-commit/mirrors-prettier
    rev: v2.7.1
    hooks:
      - id: prettier

  - repo: https://github.com/python-poetry/poetry
    rev: 1.2.2
    hooks:
      - id: poetry-check

  - repo: local
    hooks:
      - id: secretlint
        name: secretlint
        language: node
        additional_dependencies:
          - "secretlint"
          - "@secretlint/secretlint-rule-preset-recommend"
        entry: secretlint

.pre-commit-config.yaml に記載した各 hook の設定内容は別途必要です。

yamllint の設定

.yamllint を以下の内容で作成します。

デフォルトのルールからマッチしないものを削っています。

---
# https://yamllint.readthedocs.io/en/stable/configuration.html

yaml-files:
  - "*.yaml"
  - "*.yml"
  - ".yamllint"

rules:
  braces: enable
  brackets: enable
  colons: enable
  commas: enable
  comments:
    level: warning
  comments-indentation:
    level: warning
  document-end: disable
  document-start: disable
  empty-lines: enable
  empty-values: disable
  hyphens: enable
  indentation: enable
  key-duplicates: enable
  key-ordering: disable
  line-length: disable
  new-line-at-end-of-file: enable
  new-lines: enable
  octal-values: disable
  quoted-strings: disable
  trailing-spaces: enable
  truthy:
    level: warning

isort, black, flake8, mypy の設定

isort, black, flake8, mypy の 4 つのツールは、 pyproject.toml に設定を記載します。

[tool.isort]
combine_as_imports = true
default_section = "THIRDPARTY"
include_trailing_comma = true
sections = "FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,LOCALFOLDER"
line_length = 119 # black と合わせる
multi_line_output = 3

[tool.black]
line-length = 119 # isort と合わせる
target-version = ['py39']
include = '\.pyi?$'

[tool.flake8]
# black や isort と干渉するチェックを除外
# https://www.flake8rules.com/
# Whitespace before ':' (E203)
# Line too long (82 &gt; 79 characters) (E501)
# Redefinition of unused name from line n (F811)
# Line break occurred before a binary operator (W503)
# Do not use variables named 'I', 'O', or 'l' (E741)
ignore = "E203,E501,F811,W503,E741"
exclude = ".git,__pycache__/*"
max-complexity = 10

[tool.mypy]
disallow_untyped_defs = true
ignore_missing_imports = true
python_version = 3.9

Prettier の設定

package.json, package-lock.json はフォーマットされると困ることがあるので、 Prettier の適用外にするため .prettierignore を作成して記載します。

package*.json

secretlint の設定

secretlint の設定は .secretlint.json で行います。

{
  "rules": [
    {
      "id": "@secretlint/secretlint-rule-preset-recommend"
    }
  ]
}

pre-commit を GitHub Actions で実行する

pre-commit は、ローカルでユーザが pre-commit install を実行することで、 そのリポジトリの Git hooks にインストールされ git commit 等を実行した時に実行されるようになり、 チェックを通過しない場合はコミットを中断します。

稀にユーザが pre-commit install を実行せずにコミットを行ってしまった場合にそなえ、 pre-commit/action action を CI で実行すると良いです。

pre-commit/action は、 pre-commit run --all-files を実行して、Pass すれば成功、Fail すれば失敗となる action です。

.github/workflows/pre-commit.yml を以下の内容で作成します。

name: pre-commit

on:
  pull_request:
  push:
    branches:
      - main # メインブランチ名で置き換えてください

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-python@v3
      - uses: pre-commit/action@v3.0.0

README.md

せっかくツールを導入しても、使ってもらわないと意味がありません。

リポジトリの玄関である README.md に使い方を記載しましょう。

# 【リポジトリ名】

【リポジトリを端的に説明する文章】

## 必要なツール

以下のツールを事前にインストールしてください。

- [nodenv]()
- [pyenv]()
  - 上記 2 つは [anyenv]() を経由してインストールすると良い
- [pre-commit]()

使用しているエディタが [EditorConfig](https://editorconfig.org/) に対応しているか確認してください。

https://editorconfig.org/#pre-installed

## セットアップ

リポジトリをチェックアウトしたら最初に以下を実行してください。

<pre class="code shell" data-lang="shell" data-unlink>pre-commit install
nodenv install
pyenv install</pre>

【その他】 大事なこと

さいごに

以上が、2022 年中のリポジトリセットアップで共通して設定している内容です。

初期にガッチリ設定してしまえば、以降の開発はかなり楽になります。 テンプレートリポジトリ化しておきましょう。

Node.js バージョンなどもそのうち書きたいと思います。

もっと便利なツールを知ってる方は是非カケハシへ!


明日は中野さんの「Prophetが新型ウイルス感染者数予測に適していない2つの理由」です。お楽しみに!


  1. 開発環境のほうが大きく変わらなかったので、書くことがないというのもあり。
  2. https://docs.renovatebot.com/configuration-options/ に設定ファイル名や場所のバリエーションが記載されています。
  3. どちらも anyenv からインストールできます。
  4. 設定すれば git rebasegit push のタイミングで処理を行うことができます。