ぺのめも

Web開発を勉強中。学んだことや思ったことなど

個人開発プロジェクトの開始からCI構築までにやったこと

はじめに

個人でRuby on RailsのWEBアプリ開発に着手しています。
初めてのCI環境構築で色々調べながら取り組んだので、次回はスッとできるよう、記録を残しておきます。

やったこと

ざっくり以下のようなステップを踏みました。

  • ブランチ運用の設計
  • CIの設計
  • CIの実装
    • テストを動かす
    • lintを動かす
  • CodeQLの設定
  • 必要なチェックをパスしないとPRをマージできないようにする
  • tagprを導入する

ブランチ運用の設計

そもそもCIの要件を整理するにあたり、ブランチの運用方法を決めました。
今回は、

  • 開発は基本的に一人で実施するので、できるだけシンプルにしたい
  • ローカル環境で確認後に即本番リリースはちょっと不安なので、ステージング環境で確認などしてからリリースできるようにしておきたい

という背景により、GitHub flowをベースに、下記のような運用にすることにします。

  • mainブランチと、各featureブランチのみで運用
    • featureブランチはmainへのマージ後に削除
  • mainブランチにfeatureブランチがマージされたら、ステージング環境へデプロイ
  • mainブランチにタグを打ったら、本番環境へデプロイ
    • どのコミットまでをリリースに含めるかを決めたら、タグを打つまでは追加でマージしないようにする
---
title: Git branch 運用イメージ
---
gitGraph
   commit
   branch feature1
   branch feature2
   checkout feature1
   commit
   commit
   checkout main
   merge feature1
   checkout feature2
   commit
   checkout main
   merge feature2
   branch feature3
   checkout feature3
   commit
   checkout main
   merge feature3
   commit tag: "v1.0.0"

CIの設計

上記の前提のうえで、CIをどのようにしたいかを(CDも含め)検討しました。

CIのチェックに含めるもの

  • lintの実行
  • テストの実行
  • CodeQLの実行(※GitHubが提供している、コードのセキュリティチェック)

やりたいこと

  • featureブランチからmainへのPRを作成した時
    • CIが動き、パスしなければPRはマージできない
  • featureブランチにコミットをpushした時
    • CIが動き、パスしなければPRはマージできない
  • mainブランチにfeatureブランチをマージした時
    • リリース用のブランチ・PRを作成(main←リリースブランチ)
      • リリースブランチは、機能の追加はせずにリリース用の変更(CHANGELOGの追記など)だけを行うブランチ
    • リリースブランチに対してCIが動き、パスしなければPRはマージできない
  • mainブランチにリリースブランチをマージした時
    • リリースバージョンのtagを打つ
    • tagが打たれたバージョンのmainを本番にデプロイする

ツール

Publicリポジトリであれば上限なく無料で利用でき、公式ドキュメントも豊富だったため、GitHub Actionsを利用することにしました。

docs.github.com

詳しくは上記の公式ドキュメントを読む必要がありますが、GitHub Actionsの基本的な概念は以下のとおりです。

  • ワークフロー
    • 1 つ以上のジョブを実行する自動化プロセス。リポジトリ内の .github/workflows 配下にYAML ファイルを作成して定義する。
  • イベント
    • ワークフローの実行をトリガーするアクティビティ。リモートブランチへのpush、PRの作成など。
  • ジョブ
  • アクション
    • GitHub Actions用に定義された、再利用可能なジョブ。GitHub Marketplaceにアクションが色々公開されている。独自で作ることもできる。
  • ランナー
    • ワークフローを実行するサーバー。

CIの設定ファイル(完成版)

CIのワークフローを定義していきます。こちらの記事を参考にしました。

zenn.dev

最終的なコードは下記になります。(クリックすると開きます)

========

github/workflows/ci.yml

name: ci

on:
  pull_request:
    branches: [ main ]

jobs:
  ci:
    runs-on: ubuntu-latest
    container:
      image: ruby:3.1.3
      env:
        RAILS_ENV: test
    steps:
      - name: Check out source code
         uses: actions/checkout@v3
      - name: Cache node modules
         uses: actions/cache@v3
         env:
           cache-name: cache-node-modules
         with:
           path: ~/.npm
           key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
           restore-keys: |
             ${{ runner.os }}-build-${{ env.cache-name }}-
             ${{ runner.os }}-build-
             ${{ runner.os }}-

      - name: Cache bundle gems
         uses: actions/cache@v3
         with:
           path: vendor/bundle
           key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
           restore-keys: |
             ${{ runner.os }}-gem-
             ${{ runner.os }}-

      - name: Install Node.js
         uses: actions/setup-node@v3
         with:
           node-version: '19.4'

      - name: Npm install
         run: npm install

      - name: Install Bundler
         run: gem install bundler --no-document -v $(grep "BUNDLED WITH" -1 Gemfile.lock | tail -n 1)

      - name: Bundle install
         run: |
           bundle config set --local path 'vendor/bundle'
           bundle install --jobs 4 --retry 3

      - name: Run rubocop
         run: bundle exec rubocop

      - name: Run lint
         run: npm run lint

      - name: Run tests
         run: bundle exec rspec

========

ワークフローの各部分について

上記ファイルの各部分の内容について、順に見ていきます。

実行のトリガー

実行のトリガーとなるイベントは、mainブランチへのPull requestが作成あるいは更新された時としています。
なおイベントの詳細については、下記のドキュメントに一覧が記載されているので参考になりました。

docs.github.com

name: ci

on:
  pull_request:
    branches: [ main ]

依存ライブラリのキャッシュ

node-modulesとRuby gemをキャッシュしておくことで、ワークフローの実行を速くすることができます。

      - name: Cache node modules
         uses: actions/cache@v3
         env:
           cache-name: cache-node-modules
         with:
           path: ~/.npm
           key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
           restore-keys: |
             ${{ runner.os }}-build-${{ env.cache-name }}-
             ${{ runner.os }}-build-
             ${{ runner.os }}-

      - name: Cache bundle gems
         uses: actions/cache@v3
         with:
           path: vendor/bundle
           key: ${{ runner.os }}-gem-${{ hashFiles('**/Gemfile.lock') }}
           restore-keys: |
             ${{ runner.os }}-gem-
             ${{ runner.os }}-
  • key
    • ハッシュ保存時に作成されるキー。検索時にも使われる。
  • path
    • キャッシュを保存・復元するときのパス。
  • restore-keys
    • key一致するキャッシュがない場合に探しにいくキーです。optionalですが、できるだけ多くのキャッシュヒットを得るためには設定しておくとよいようです。

docs.github.com

実際に試してみると、2回目の実行時は確かに速くなっていました。
←1回目 2回目→

Nodeのインストール・npm installの実行

Nodeのインストールには、actions/setup-nodeを使っています。

      - name: Install Node.js
         uses: actions/setup-node@v3
         with:
           node-version: '19.4'

      - name: Npm install
         run: npm install

このアクションを使うことで、Node.jsがキャッシュされると思ったのですが、現時点ではバージョン19.xの場合はキャッシュできないようです。

peno022.hatenablog.com

下記のようにNodeのインストールコマンドを直接実行した場合と比べると、10秒→5秒とactions/setup-nodeを使ったほうが速くなったので、19.4が対応されるのを待ちつつ、このまま使うことにしました。

      # 直接実行した版
      - name: Install Node.js
         run: |
           curl -sL https://deb.nodesource.com/setup_19.x | bash -
           apt-get install -y nodejs

Bundlerのインストール・bundle installの実行

      - name: Install Bundler
         run: gem install bundler --no-document -v $(grep "BUNDLED WITH" -1 Gemfile.lock | tail -n 1)

      - name: Bundle install
         run: |
           bundle config set --local path 'vendor/bundle'
           bundle install --jobs 4 --retry 3

gem installのオプション補足:

  • --no-document
    • gemに関するドキュメントの生成をスキップ。ライブラリ本体のダウンロードにとどめたほうが実行が速くなるため。
  • -v
    • bundlerのバージョンを指定。
    • grep "BUNDLED WITH" -1 Gemfile.lock | tail -n 1で、Gemfile.lockで指定しているBundlerバージョンを取得。

Command Reference - RubyGems Guides

bundle installのオプション補足:

  • --jobs
    • ダウンロード・インストールを並行して行うジョブ数を指定できる。

lintの実行

今回のプロジェクトでは、Rubocopとeslint、prettierを利用しています。

      - name: Run rubocop
         run: bundle exec rubocop

      - name: Run lint
         run: npm run lint

npm run lintの実行内容については、プロジェクトのpackage.jsonで指定しています。

// package.json

 "scripts": {
    // 中略
    "lint": "run-p lint:*",
    "lint:eslint": "eslint 'app/javascript/**/*.js' --max-warnings=0",
    "lint:prettier": "prettier app/javascript/**/*.js --check"
  },

テストの実行

今回のプロジェクトでは、RSpecを利用しています。
現段階では、ユニットテストの実行のみを確認しています。
システムテストの実行にはブラウザのインストールが必要など、実行するテストによって追加の設定が必要になります。

      - name: Run tests
         run: bundle exec rspec

CodeQLの設定

CodeQLのワークフローは、リポジトリ設定の「CodeQL analysis」をAdvancedで有効化することで、GitHubが作成してくれます。
今回はワークフローの内容は変更していません。

リポジトリ設定については、こちらの記事に詳細を記載しています。
peno022.hatenablog.com

ワークフローのymlファイルは下記になります。(クリックすると開きます)
========

.github/workflows/codeql.yml

# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"

on:
  push:
    branches: [ "main" ]
  pull_request:
    # The branches below must be a subset of the branches above
    branches: [ "main" ]
  schedule:
    - cron: '24 13 * * 6'

jobs:
  analyze:
    name: Analyze
    runs-on: ubuntu-latest
    permissions:
      actions: read
      contents: read
      security-events: write

    strategy:
      fail-fast: false
      matrix:
        language: [ 'javascript', 'ruby' ]
        # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
        # Use only 'java' to analyze code written in Java, Kotlin or both
        # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both
        # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support

    steps:
    - name: Checkout repository
      uses: actions/checkout@v3

    # Initializes the CodeQL tools for scanning.
    - name: Initialize CodeQL
      uses: github/codeql-action/init@v2
      with:
        languages: ${{ matrix.language }}
        # If you wish to specify custom queries, you can do so here or in a config file.
        # By default, queries listed here will override any specified in a config file.
        # Prefix the list here with "+" to use these queries and those in the config file.

        # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
        # queries: security-extended,security-and-quality


    # Autobuild attempts to build any compiled languages  (C/C++, C#, Go, or Java).
    # If this step fails, then you should remove it and run the build manually (see below)
    - name: Autobuild
      uses: github/codeql-action/autobuild@v2

    # ℹ️ Command-line programs to run using the OS shell.
    # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun

    #   If the Autobuild fails above, remove it and uncomment the following three lines.
    #   modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.

    # - run: |
    #   echo "Run, Build Application using script"
    #   ./location_of_script_within_repo/buildscript.sh

    - name: Perform CodeQL Analysis
      uses: github/codeql-action/analyze@v2
      with:
        category: "/language:${{matrix.language}}"

========

指定したチェックをパスしないとPRをマージできないようにする

Repository Settings の Code and automation > Branches の画面を開きます。
今回はmainブランチへのマージを制限したいので、Branch protection rulesのmainの設定を編集します。

Require status checks to pass before merging を有効にし、マージ時に必須にしたいジョブを検索・選択します。

Require status checks to pass before merging を有効にする
「Require status checks to pass before merging」を有効にする

この設定により、CIがパスしていない状態だと、下記のようにPRのマージができないよう警告表示になります。

リリース用PRの自動作成をする

mainブランチ上のタグでリリースバージョンを管理したいため、tagprというアクションを導入しました。
導入方法については、こちらの記事をご参照ください。
(長くなったので別記事に切り出しました。。)

peno022.hatenablog.com

完了!

これで、やりたいこととして最初に設定したことは実現できました。

再掲:

  • featureブランチからmainへのPRを作成した時
    • CIが動き、パスしなければPRはマージできない
  • featureブランチにpushしてコミットを追加した時
    • CIが動き、パスしなければPRはマージできない
  • featureブランチをmainへマージした時
    • リリース用のPRを作成(リリースブランチからmain)
    • リリースブランチに対してCIが動き、パスしなければPRはマージできない
  • リリース用のPRをmainへマージした時
    • リリースバージョンのtagを打つ
    • tagが打たれたmainを本番にデプロイする

GitHub Actionsって何?という状態からだったのでけっこう苦労しましたが、次回からはスッとできそうです。

個人開発のプロジェクトにtagprを導入した

はじめに

個人でRuby on Railsのサービス開発を始めており、CIの構築の中でtagprを導入しました。導入にあたって設定したことを記録しておきます。
tagprのおかげでCIが快適になりいい感じです。ありがとうございます!

tagprリポジトリgithub.com

tagprの作者の方のリリースブログ: songmu.jp

やりたいこと

今回のプロジェクトでは下記のようなブランチ運用にしたく、やり方を検討していたところ tagpr を見つけました。

  • mainブランチと、各featureブランチのみで運用
    • featureブランチはmainへのマージ後に削除
  • mainブランチにfeatureブランチがマージされたら、ステージング環境へデプロイ
  • mainブランチにタグを打ったら、本番環境へデプロイ
    • どのコミットまでをリリースに含めるかを決めたら、タグを打つまでは追加でマージしないようにする

自分がこのような運用にしたかった背景・CI設定の全体については、こちらの記事に記載しています。

peno022.hatenablog.com

導入結果

PRの確認を経てmainにfeatureがマージされると、

  • リリースブランチの作成
  • CHANGELOGの更新
  • PRの作成

を自動で実行してくれます。 PRをマージすると、mainに該当バージョンのタグが打たれ、デプロイできる状態になります。
OSSのリリース作業の属人性を下げることを主な動機として開発されたアクションとのことですが、個人開発であっても快適です。

ワークフローファイル(完成版)

リポジトリに記載のサンプルコードを参考に、tagpr 実行のワークフローファイルを作成しました。
最終的なコードは下記になります。

# .github/workflows/tagpr.yml

name: tagpr
on:
  push:
    branches:
      - main

jobs:
  tagpr:
    runs-on: ubuntu-latest
    outputs:
      tagpr-tag: ${{ steps.run-tagpr.outputs.tag }}
    env:
      GITHUB_TOKEN: ${{ secrets.GH_PAT }}
    steps:
      - name: Check out source code
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.GH_PAT }}

      - id: run-tagpr
        name: Run tagpr
        uses: Songmu/tagpr@v1

  release:
    needs: tagpr
    if: needs.tagpr.outputs.tagpr-tag != ''
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GH_PAT }}
    steps:
      - name: Check out source code
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.GH_PAT }}
      - name: Trigger release action
        uses: ./.github/actions/release

調べた箇所

リポジトリの許可Actionに登録する

リポジトリSettings > Actions > Generalの Actions permissions の設定を修正しました。
自分で追加したactionsか、GitHub認証済みのactionsのみ利用できる設定にしていたため、許可するactionsにtagprを追加しています。

後続のワークフローをトリガーできない問題

tagprのサンプルコードだとsecrets.GITHUB_TOKENを使っているのですが、このトークンを利用した場合、後続のワークフローのトリガーができません。(pushしたらCIのワークフローを起動する、等)

自分の場合は、リリース用PRについても「CIが通ったことを確認してマージ」としたかったので、GITHUB_TOKENを使うのをやめて、personal access tokenを利用することにしました(secrets.GH_PAT)。
PATを使うと、後続アクションをトリガーできるようになります。(ただし、PATには有効期限を適切に設定・更新する必要があったり、流出に注意が必要であったりと、GITHUB_TOKENと違って自分で管理する手間がかかるという面もあります)

personal access token を発行する

personal access tokenは、
Settings > Developper settings > Personal access tokens > Fine-grained tokens
から作成することができます。

Fine-grained tokens について少し調べると、2022年10月に発表された比較的新しい機能でした。
従来のトークンは発行者がアクセスできる全リポジトリに対して使えていましたが、Fine-grained tokens は、対象とする組織・リポジトリを選択することができるという点で、より「きめ細かい」トークンであるということのようです。

github.blog

docs.github.com

トークンの権限設定をする

トークンを作成したら、Permissions の項目を設定します。
tagprの実行には、下記の権限設定が必要でした。

  • Actions
    • Read and write
  • Contents
    • Read and write
  • Metadata
    • Read-only
      • mandatoryと表示され、Actions に Read and write 権限を付与すると自動的にこちらも設定されます
  • Pull requests
    • Read and write

最初にActionsの Read and write 権限だけを設定して実行したら、下記ログのように権限エラーが出たので、必要な権限を追加していった形です。

POST https://api.github.com/repos/peno022/kpi-tree-generator/releases/generate-notes  403 Resource not accessible by personal access token [] 


どのAPIにどの権限が必要かは、こちらのドキュメントに一覧でまとまっているため、ここを見ながら権限設定をしました。

docs.github.com

トークンをリポジトリに登録してワークフローで利用する

リポジトリSettings > Security > Secrets and variables > Actionsから、作成したトークンを登録します。
項目名に任意の名前(今回はGH_PAT)をつけ、Valueには生成したトークンの文字列をペーストして、登録完了です。


リリースのジョブをactionsへの切り出す

「タグが打たれたら本番環境にデプロイする」という部分について、デプロイの実行を./.github/actions/releaseに切り出しています。

      - name: Trigger release action
        uses: ./.github/actions/release

製作者の方のリリースブログに記載があった、 tagprのワークフローが途中で意図せず失敗してしまった場合に、リリースフローをリカバリするために手動でタグを打つワークフローを別途用意しやすくなる という利点のため、このようにしました。

actionsの構文について補足

workflow間に共通するようなジョブを切り出す際には、workflowではなくactionsとして作る必要があり、構文のルールがworkflowとは少し違っています。 下記のような条件があるので、注意が必要でした。

  • .github/actions/[任意のアクション名]/action.ymlというパス・ファイル名にする
  • workflowの場合は必須である、トリガーイベント、ランナーの指定は不要
  • using: "composite"の記述が必須
  • shell の指定が必須

参考: zenn.dev zenn.dev

公式ドキュメント:

docs.github.com

docs.github.com

GitHub アプリのレート制限エラー

403 You have exceeded a secondary rate limit. Please wait a few minutes before you try again. 

頻繁にリクエストを送りすぎると、一時的な制限がかかって出るエラーのようです。動作確認のために繰り返し実行していたタイミングで、一度だけ出ました。
メッセージに記載があるように、数分待ってからジョブの Rerun を行ったら解消しました。

docs.github.com

actions/setup-nodeでNode.jsがキャッシュされていない

TL;DR

自分が利用しているNode.jsのバージョンが、actions/node-versionsでまだ対応されていたかったことによるようです。
18系だとキャッシュされました。

状況

個人開発しているプロジェクトのCIを設定するにあたり、Node.jsのインストールが必要だったので、setup-nodeのアクションを利用しました。

# 自分が作成したワークフローファイルより抜粋

  name: Install Node.js
  uses: actions/setup-node@v3
    with:
      node-version: '19.4'

github.com

ただ、setup-nodeのREADMEにはOptionally downloading and caching distribution of the requested Node.js version, and adding it to the PATHという記載があるものの、 実行のたびにNode.jsのキャッシュが見つからずにダウンロードしていたので、なぜキャッシュが使えていないのだろう?を調査しました。

// 実行ログ

Run actions/setup-node@v3
/usr/bin/docker exec  f7656153548b0c0c871c87f547a809d02cea3ae16dbd0ba4734738022cf761da sh -c "cat /etc/*release | grep ^ID"
##[debug]ID=debian
##[debug]Running JavaScript Action with default external tool: node16
##[debug]isExplicit: 
##[debug]explicit? false
##[debug]isExplicit: 14.21.2
##[debug]explicit? true
##[debug]isExplicit: 16.19.0
##[debug]explicit? true
##[debug]isExplicit: 18.13.0
##[debug]explicit? true
##[debug]evaluating 3 versions
##[debug]match not found
Attempting to download 19.4...
##[debug]No manifest cached
##[debug]Getting manifest from actions/node-versions@main
##[debug]set auth
##[debug]check 18.13.0 satisfies 19.4
##[debug]check 18.12.1 satisfies 19.4
##[debug]check 18.12.0 satisfies 19.4

// 中略

##[debug]check 8.1.0 satisfies 19.4
##[debug]check 8.0.0 satisfies 19.4
##[debug]check 6.17.1 satisfies 19.4
Not found in manifest. Falling back to download directly from Node
##[debug]evaluating 555 versions
##[debug]matched: v19.4.0
Acquiring 19.4.0 - x64 from https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gz
##[debug]Downloading https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gz

// 以下略

結論

自分が利用しているNode.jsのバージョンが19.4だったことに起因していました。
取得元となっているactions/node-versions側で、まだ対応されていないバージョンのようです。

調査内容

ログに

##[debug]Getting manifest from actions/node-versions@main

が出ていたのでmanifestの取得箇所を確認してみると、actions/node-versionsから取得していそうです。(該当箇所

  private getManifest(): Promise<tc.IToolRelease[]> {
    core.debug('Getting manifest from actions/node-versions@main');
    return tc.getManifestFromRepo(
      'actions',
      'node-versions',
      this.nodeInfo.auth,
      'main'
    );
  }

そこでactions/node-versionsを見に行くと、18.13.0が最新で、バージョン19にはまだ対応していませんでした。

github.com

actions/node-versions リリース一覧画面
actions/node-versions リリース一覧画面

自分が利用している19.4はactions/node-versionsから取得できないことにより、都度https://nodejs.org/dist/v19.4.0/node-v19.4.0-linux-x64.tar.gzからダウンロードする形になっていたようでした。

バージョン18を指定して実行してみると、キャッシュが取得され、実行時間も短くなりました。

// 実行ログ

Run actions/setup-node@v3
/usr/bin/docker exec  c32933f9e63a5f41642ba7d0ce44bc6929286d7f1bbddf57c64c26b562e4ef3f sh -c "cat /etc/*release | grep ^ID"
##[debug]ID=debian
##[debug]Running JavaScript Action with default external tool: node16
##[debug]isExplicit: 
##[debug]explicit? false
##[debug]isExplicit: 14.21.2
##[debug]explicit? true
##[debug]isExplicit: 16.19.0
##[debug]explicit? true
##[debug]isExplicit: 18.13.0
##[debug]explicit? true
##[debug]evaluating 3 versions
##[debug]matched: 18.13.0
##[debug]checking cache: /__t/node/18.13.0/x64
##[debug]Found tool in cache node 18.13.0 x64
Found in cache @ /__t/node/18.13.0/x64
::group::Environment details
Environment details
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/tsc.json
##[debug]Added matchers: 'tsc'. Problem matchers scan action output for known warning or error strings and report these inline.
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/eslint-stylish.json
##[debug]Added matchers: 'eslint-stylish'. Problem matchers scan action output for known warning or error strings and report these inline.
##[add-matcher]/__w/_actions/actions/setup-node/v3/.github/eslint-compact.json
##[debug]Added matchers: 'eslint-compact'. Problem matchers scan action output for known warning or error strings and report these inline.
##[debug]Node Action run completed with exit code 0
##[debug]Set output node-version = v18.13.0
##[debug]Finishing: Install Node.js

はてなブログのMarkdown編集モードでMermaidの図を書く設定

Mermaid

Mermaidとは、テキストをレンダリングしてグラフやチャートを描けるJavaScriptのライブラリです。
はてなブログで記事を書く際にも利用したかったのですが、デフォルトでは利用できず追加の設定が必要だったので、設定方法をまとめておきます。

mermaid.js.org

設定方法

1)Mermaidのスクリプトをロード・埋め込む

  1. はてなブログの「設定」画面を開き、詳細設定をクリック
  2. <head>要素にメタデータを追加」の項目に、下記のスクリプトを追記
<script type="module">
import mermaid from 'https://unpkg.com/mermaid@9/dist/mermaid.esm.min.mjs';
mermaid.initialize({ startOnLoad: true });
</script>

2)CSS設定を追加

上記により```mermaidの記法が使えるようになりますが、このままだとはてなブログCSSがきいてコードブロックの背景が黒くなってしまい、mermaidの図がかなり見づらくなります。
それを避けるため、pre.code.mermaidのときはスタイルが当たらないように修正します。

手順:

  1. はてなブログの「デザイン」画面を開き、「カスタマイズ」タブをクリック
  2. 「デザインCSS」をクリックしてテキストボックスを開き、下記のCSSを追記
/*mermaid用*/
.entry-content pre.code.mermaid {
  background: none;
  border: none;
  padding: 0;
}
.entry-content pre.code.mermaid[data-lang="mermaid"]:before {
    content: none;
}

3)Mermaidを書く

ここまでの設定を終えると、Markdownモードの記事編集画面で、```mermaidの記法が使えるようになります。

 ```mermaid
   sequenceDiagram
       Alice->>John: Hello John, how are you?
       John-->>Alice: Great!
       Alice-)John: See you later!
 ```
sequenceDiagram
    Alice->>John: Hello John, how are you?
    John-->>Alice: Great!
    Alice-)John: See you later!

色んなチャートが簡単に書けて便利

フローチャート、シーケンス図、クラス図、ER図など、色々な図を簡単に書くことができます。
元々はGitのブランチ運用の図をサクッと書けないかな〜と思って探していたのですが、それもバッチリありました。

 ```mermaid
 ---
 title: Example Git diagram
 ---
 gitGraph
    commit
    commit
    branch develop
    checkout develop
    commit
    commit
    checkout main
    merge develop
    commit
    commit
 ```
---
title: Example Git diagram
---
gitGraph
   commit
   commit
   branch develop
   checkout develop
   commit
   commit
   checkout main
   merge develop
   commit
   commit

参考記事

個人開発用のGitHubリポジトリで設定した項目たち

はじめに

前提条件

今回は以下のようなリポジトリに対して、設定を行いました。

記載を割愛した項目は、デフォルトのまま変更しなかったもの、かつ変更しないことについて特に悩まなかったものです。

設定箇所

リポジトリの「Settings」画面内の項目を見ていきます。

Repository Settings
Repository Settings
GitHubリポジトリの設定画面
設定画面

General

Features

デフォルトのまま。

  • Sponsorships
    • スポンサーボタンを表示する設定。ひとまず不要そう。
  • Discussions
    • issueとは別で議論するためのスペースを設定できる。issueで十分そうなので設定せず。

Featuresの設定項目
Featuresの設定項目

Pull Requests

マージ方法の設定

squash and mergeのみを許可するように変更しました。
自分が現在rebase方式しか経験がなく、squash and mergeの方式も試したかったためです。

Pull Requestsのマージ方法の設定
Pull Requestsのマージ方法の設定

この設定をすると、PRをマージする時に「Squash and merge」しか選べないようになります。

Squash and merge しか選択できない
Squash and merge しか選択できない

ベースブランチ更新時にPRブランチの更新をサジェスト

Always suggest updating pull request branches を有効化しました。
PRを作成してからベースブランチに新たに変更が入った場合、画面上に「ブランチを更新する」ボタンが表示されるようになります。
基本的にベースブランチの最新を取り込んでCIのテストをパスしてからマージする形にしたいので、簡単にベースブランチの更新を取り込めるように設定しました。

PRマージ後にブランチを自動で削除

Automatically delete head branches を有効化しました。
マージ後に画面上のボタンを押してブランチを削除する必要がなくなります。大体の場合有効にしてよさそう。

Access

Collaborators

Interaction limits

議論が白熱している時などに、一時的に外部ユーザーとリポジトリとのやりとりを制限できる機能。
今回対象とした個人開発用リポジトリで議論が白熱することは当面なさそうなので、何も変更せず。

Code review limits

Limit to users explicitly granted read or higher access を有効化しました。
見知らぬユーザーがPRをApproveできないようにしたく、設定しておきました。
コメントは誰でもすることができる状態のままになります。

Code and automation

Branches

このリポジトリでは、GitHub flowをベースとしたブランチ運用を想定しています。

---
title: Git branch 運用イメージ
---
gitGraph
   commit
   branch feature1
   branch feature2
   checkout feature1
   commit
   commit
   checkout main
   merge feature1
   checkout feature2
   commit
   checkout main
   merge feature2
   branch feature3
   checkout feature3
   commit
   checkout main
   merge feature3
   commit tag: "v1.0.0"

上図のような形式で、

  • mainブランチと、各featureブランチのみで運用
    • featureブランチはmainへのマージ後に削除
  • mainブランチにタグを打ったら、本番環境へデプロイ

という運用ができるように、mainブランチに対して以下の保護設定をしています。

Branch protection rule

Require a pull request before merging を有効化しました。
mainへのマージは、基本的にPR上でレビューしてからにしたいためです。
ただ今回は自分ひとりで開発・確認・マージするシーンが多くなるはずので、approveは必須にしませんでした。

Require status checks to pass before merging を有効化しました。
指定したステータスチェックが通らないとマージできないようにすることができます。

Actions - General

Allow peno022(自分), and select non-peno022, actions and reusable workflows を選択しました。
品質の分からないworkflowを使ってしまうのを防ぐため、自分で作ったものか公式で認証済みのworkflowのみを許可したかったからです。

Security

Code security and analysis

Dependabot

Dependabot alerts と Dependabot security updates を有効化しました。
脆弱性の問題がなければ必ずしも常に最新バージョンにする必要はなさそうに思い、Dependabot version updatesは無効化のままとしています。

Dependabotは、依存しているライブラリのセキュリティ脆弱性を通知してくれ、最新バージョンにアップグレードするためのPull Requestの自動作成まで行ってくれるものです。

Dependabot
Dependabot

Code scanning

CodeQL analysis をAdvancedで有効化しました。

Defaultを選択するとGitHub側で自動的に CodeQL analysis が実行されます。
一方で、Advancedにすると、自分でワークフローを組む必要があります。
ただ、GitHubがデフォルト設定のymlファイルを作成してくれるので、ワークフローの設定自体はすぐにできました。
今回はワークフローの実行タイミングを自分でコントロールできたほうが都合がよいと考え、Advanced設定を選択しています。

CodeQL analysis
CodeQL analysis

Secret scanning

Receive alerts on GitHub for detected secrets, keys, or other tokens.を有効化しました。
publicリポジトリに秘密情報がアップロードされてしまった場合に、GitHubから通知が来るようになります。

Secrets and variables

Actions secretsに下記を設定しました。両方ともvalueにはtrueを設定します。
この設定により、デバッグログが有効になります。

  • ACTIONS_RUNNER_DEBUG
  • ACTIONS_STEP_DEBUG

docs.github.com

Integrations

特に変更せず、デフォルトのまま。
Email notificationsはデフォルトでActiveになっているため、メール通知は受け取れる状態になっています。

Dependabotのアラートが上がったけど自動PRが作成されない

状況

タイトルのとおりです。
自分は、Ruby on Railsで個人開発しているサービスのリポジトリにDependabotの以下の設定を入れています。
下記の機能を有効にしている状態です。

  • 依存ライブラリの脆弱性アラートを受信
  • アラート解決のために、Dependabotが自動的でPRを作成する

Repository Settings -&gt; Code security and analysis -&gt; Dependabot
Repository Settings -> Code security and analysis -> Dependabot

そのため、Dependabotのセキュリティアラートが出ると自動でPRが作成されるはずと思ったのですが、アラートが上がってきたのにPRが作成されていなかったので、あれ?と思いました。

結論

簡単なことだったんですが、まだ脆弱性が修正されたバージョンがリリースされていなかったことが理由でした。
Dependabotは、修正バージョンがリリース済みの場合に、そのバージョンに上げるようなPRを自動で作成してくれます。

アラートの内容。railsのバージョン7.0.4以下が対象になっています。

Dependabotのアラート
Dependabotのアラート

railsの最新バージョンはまだ7.0.4なので、バージョンを上げることによる自動修正はできない状態。

修正バージョンはまだリリースされていない
修正バージョンはまだリリースされていない

Dockerとは?開発環境導入までに必要だった最低限の基本知識

はじめに

個人でRuby on RailsWebサービスを開発しているのですが、Docker+VSCode Remote Containerでの開発環境を初めて作るにあたって、コンテナやDockerについて何も分からないところから始めたので、その時に調べた基本知識をまとめておきます。

コンテナとは、Dockerとは

コンテナとは

1つの共有されたホストOS上で動く、独立したアプリケーション実行環境のこと。 仮想マシンと比較されることがあるが、下記のように比較できる。

  • 仮想マシン
    • ハードウェアとOSの間にハイパーバイザという管理層をはさみ、ハイパーバイザが提供する仮想的なハードウェア上で複数のOSを同時に動かす
  • コンテナ
    • ホストOSのカーネル上で、そのOSカーネルの提供する環境隔離機能を使って、独立した実行環境を作る技術
    • コンテナ自体にはOSは含まれない

Docker

コンテナにまつわるワークフロー全体を管理できるアプリケーション。
Dockerで管理するコンテナ=「Dockerコンテナ」。

コンテナのワークフロー(Build, Ship, Run)

「Build, Ship, Run」は、コンテナのワークフローを示す、Docker社提唱のスローガン的な言葉。
ワークフローには、「Dockerイメージ」「Dockerレジストリ」「Dockerコンテナ」の3つの概念が登場します。

  • Build
    • コンテナ作成に必要なファイル群などの材料から、Dockerイメージを作成
  • Ship
  • Run
    • Dockerイメージから、Dockerコンテナを実行
Dockerイメージ
  • Dockerコンテナとして実行するためのアプリケーションやそれが依存するファイル群、コンテナ環境自体の設定などを定めたもの。
  • コンテキストとDockerfileをビルドすることで、コンテナイメージを作成する
    • コンテキストとは、アプリケーションの実行に必要なファイル群のこと。
    • Dockerfileとは、Dockerイメージの作成手順を記載したファイルのこと。
Dokerレジストリ
  • Dockerイメージが格納されている場所。
  • 代表的なのはDocker Hub Container Image LibraryGitHubのような感じと理解。
  • Dockerレジストリで配布されている基本のDockerイメージをベースにして、必要なものを追加する形で自分のDockerイメージを作る形になる。
Dockerコンテナ
  • 実際にアプリケーションを実行できる環境。
  • Dockerイメージを実行することで、Dockerコンテナを立ち上げる。

コンテナのレイヤー構造

コンテナは、「変更差分の集まり」になっている。各変更差分を「レイヤ」と呼び、レイヤを重ね合わせる形で構成される。

Dockerfileの書き方

DockerfileにはDockerイメージの作成手順を記載する。
手順1つ1つがレイヤとして重ねられていくイメージ。
下記のDockerファイルであれば、3つの手順ごとにレイヤが重ねられていく。

  • FROM mcr.microsoft.com/devcontainers/ruby:0-3.1
    • rubyのコンテナイメージを取得する(この中も実際はレイヤの重ね合わせになっている)
  • RUN gem install rails webdrivers
    • railsをインストールする
  • ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

Dockerfile(VSCode devcontainerでRuby on Railsの開発用に作成したもの):

FROM mcr.microsoft.com/devcontainers/ruby:0-3.1

# Install Rails
RUN gem install rails webdrivers

# Default value to allow debug server to serve content over GitHub Codespace's port forwarding service
# The value is a comma-separated list of allowed domains 
ENV RAILS_DEVELOPMENT_HOSTS=".githubpreview.dev,.preview.app.github.dev,.app.github.dev"

よく使いそうなDockerコマンド

  • docker build -t [イメージ名] [Dockerfileが置いてあるパス]
    • Dockerイメージを作成する
    • -tオプションを付けると名前を指定できる
  • docker run -it [コンテナ名] /bin/bash
    • Dockerコンテナを起動する
    • -it以降を付けることで、コンテナ内のシェルをインタラクティブに操作できる
  • docker image ls
    • Dockerイメージの一覧を表示
  • docker container ls
    • Dockerコンテナの一覧を表示
  • docker ps
    • 起動中のコンテナのステータスを表示
  • docker container prune
    • 停止中のコンテナをまとめて削除できる
  • docker image prune
    • タグがないイメージをまとめて削除できる
    • 例えばDockerfileを更新して再度ビルドすると、更新前の古いイメージIDに紐づくコンテナはタグが削除されてになる

Dockerfile A をビルドしてイメージを作成
Dockerfile A をビルドしてイメージを作成

Dockerfile Aを更新した A&#x27; をビルドしてイメージを作成
Dockerfile Aを更新した A' をビルドしてイメージを作成すると、古いほうはnoneになる

参考資料

書籍

イラストでわかる DockerとKubernetes技術評論社

かなり分かりやすくてよかったです。初学者向け。1-2時間でサクッと読めます。
分かりやすさ重視なので、ある程度知っている人にとっては内容が薄そう。

気に入ったところ

  • イラストでの図解が豊富&フルカラーなのでとっつきやすい
  • サンプルでこまめに挙動を確認し、実行したコマンドと表示結果にきちんと補足がある
  • サクッと読める

記事

基本事項について
開発環境構築について

最初にいきなりこれらの記事を読んだのですが、分かるような分からないような?だったので、上述の書籍を参照してコンテナの基本概念を理解してから、記事に戻った感じです。 qiita.com

qiita.com