ラベル Ruby の投稿を表示しています。 すべての投稿を表示
ラベル Ruby の投稿を表示しています。 すべての投稿を表示

2026年6月12日金曜日

VSCode の Ruby LSP をセットアップする方法

VSCode の Ruby LSP をセットアップする方法

概要

https://marketplace.visualstudio.com/items?itemName=Shopify.ruby-lsp これをセットアップする方法を紹介します

環境

  • macOS 26.5.1
  • VSCode 1.123.1
    • Ruby LSP 0.10.4

watchman のインストール

  • brew install watchman

watchman はグローバルにあるものを使うので homebrew でインストールします

sorbet のインストール

  • vim Gemfile
group :development do
  gem 'sorbet'
  gem 'sorbet-runtime'
  gem 'tapioca'
end

sorbet を使うのでインストールします
これは開いているプロジェクト内で bundle 経由で実行するので Gemfile からインストールします

tapioca の初期化

必要な rbi ファイルを作成します

  • bundle exec tapioca init
  • bundle exec tapioca gems

これで bin/tapioca と sorbet/ が生成されます

rubocop を使っている場合は

.rubocop.yaml に以下を追記しておきましょう

Style/StringLiterals:
  Exclude:
    - 'bin/tapioca'

動作確認

VSCode を開くと自動的に Ruby LSP が起動します
状態が Idle になれば OK です

Restarting を繰り返している場合は Output を確認し Sorbet や watchman が正しく動作しているか確認しましょう
起動後に自作のクラスなどで警告が出る場合は tapioca で rbi ファイルなどが生成されている確認しましょう

最後に

bundle exec tapioca gems は定期的に実行する必要があります

2026年2月16日月曜日

Github actions で kitchen test をする場合は buildkit を無効にしなければ動かなくなった

Github actions で kitchen test をする場合は buildkit を無効にしなければ動かなくなった

概要

おそらく本当に最近仕様変更があった模様

環境

  • Github actions (2026/02/13時点)
  • Ruby 4.0.1
    • test-kitchen 3.9.1
    • kitchen-docker 3.0.0

Github actions の設定

name: Kitchen test

on:
  push:
    branches:
      - "master"

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: linux/arm64

      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Install dependencies
        run: bundle install

      - name: Run Test Kitchen
        run: |
          bundle exec kitchen test
        env:
          CHEF_LICENSE: accept
          DOCKER_BUILDKIT: 0

デバッグログを表示する場合

name: Kitchen test

on:
  push:
    branches:
      - "master"

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
        with:
          platforms: linux/arm64

      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Install dependencies
        run: bundle install

      - name: Run Test Kitchen
        run: |
          bundle exec kitchen test --log-level debug || true
        env:
          CHEF_LICENSE: accept
          DOCKER_BUILDKIT: 0

      - name: Show Kitchen logs
        if: always()
        run: |
          echo "===== kitchen.log ====="
          cat .kitchen/logs/kitchen.log || true
          echo "===== instance log ====="
          cat .kitchen/logs/default-ubuntu-2204.log || true

最後に

内部的に buildkit を使うようになっており docker の出力形式が変更されたために起きていたエラーでした

2026年1月9日金曜日

script タグで管理されている JavaScript ライブラリを nodejs で管理する方法

script タグで管理されている JavaScript ライブラリを nodejs で管理する方法

概要

esbuild を使います

環境

  • macOS 15.7.2
  • Ruby 4.0.0
  • nodejs 22.21.0
    • esbuild 0.27.2

erb の script タグ

これを nodejs で管理します

<script src="https://cdnjs.cloudflare.com/ajax/libs/plyr/3.7.8/plyr.min.js" integrity="sha512-vONptKEoKbP1gaC5UkbYDa9OPr04ur4bxaaqT7DAJxGHB2oogtseCPrl5e5hPFokGYotlGNV4d+GM593ka7iNA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

esbuild のインストール

  • npm install --save-dev esbuild

plyr のインストール

  • npm install plyr

src/podcast.js の作成

esbuild が自動で作成する plyr の JavaScript ファイルを作成するための設定的なものを書きます
また plyr に関する処理も書きます
このファイルを元に自動で配信用の JavaScript ファイルを esbuild が作成してくれます

import Plyr from "plyr";

document.addEventListener("DOMContentLoaded", () => {
  const player = new Plyr("#player", {
    speed: { selected: 1, options: [1, 1.5, 2] },
  });
});

package.json に追記

scripts に追記します
esbuild を使ってビルドし自動で JavaScript ファイルを作成する処理を記載します
実際にアプリで使用する public/js 配下になります

"scripts": {
  "build:podcast": "esbuild src/podcast.js --bundle --minify --outfile=public/js/podcast.js"
}

ビルド

  • npm run build:podcast

これで public/js/podcast.js ができます
冒頭の erb はこのファイルだけを参照すれば OK です
erb コード内で直接参照していた script タグは削除して OK です

おまけ: .gitignore

自動生成される public/js/podcast.js は .gitignore に入れたほうがいいです
理由は git grep などで検索するときに大変になるからです
なので Dockerfile などでビルドする設定を入れるようにしましょう

###############################
# Stage 1: JS/CSS Build
###############################
FROM node:22.21-slim AS build

WORKDIR /app

# package.json だけコピーして npm install を早くする
COPY package.json package-lock.json ./
RUN npm install

# ソースコードコピー
COPY src ./src
COPY public ./public

# CSS minify tool
RUN npm install -g clean-css-cli

# CSS minify
RUN find public/css -name "*.css" -exec sh -c 'cleancss -o "$1" "$1"' _ {} \;

# JS build
RUN npm run build:podcast

ただビルドする環境がなくリポジトリの内容をそのままデプロイするような環境では .gitignore せずに git 管理に含めるようにしましょう

最後に

今後は npm update すれば自動で更新してくれます
esbuild はミニファイもしてくれるのでそのまま配信用として使えます

2025年10月9日木曜日

Ruby OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 対策

Ruby OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 対策

概要

macOS + openssl@3.6.0 で発生します
回避策を紹介します

エラー全文

2025-10-09 10:48:58 - OpenSSL::SSL::SSLError - SSL_connect returned=1 errno=0 peeraddr=[2606:4700::6810:190e]:443 state=error: certificate verify failed (unable to get certificate CRL) (OpenSSL::SSL::SSLError):

環境

  • macOS 15.7.1
  • Ruby 3.4.4
  • openssl 3.6.0

回避策

  • vim libs/ssl_patch.rb
# frozen_string_literal: true

require 'openssl'

# 開発環境限定で SSL 検証をスキップする
if ENV['MODE'] == 'dev'
  s = OpenSSL::X509::Store.new.tap(&:set_default_paths)
  OpenSSL::SSL::SSLContext.send(:remove_const, :DEFAULT_CERT_STORE)
  OpenSSL::SSL::SSLContext.const_set(:DEFAULT_CERT_STORE, s.freeze)
end

あとはメインなどの先頭で

require './libs/ssl_patch'

パッチなので可能な限り先に読み込むようにしてください

最後に

SSL 関係はいろいろとハックしなければいけないケースが多いので辛いです

参考サイト

2025年7月26日土曜日

Sinatra で静的ファイルにレスポンスヘッダーを追加する方法

Sinatra で静的ファイルにレスポンスヘッダーを追加する方法

概要

Rackミドルウェアを使います

環境

  • Ruby 3.4.4
  • Sinatra 4.1.1

設定方法

# 静的ファイルに対するヘッダーを設定するミドルウェア
class StaticHeadersMiddleware
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)

    path = env['PATH_INFO']
    if path =~ %r{^/(robots\.txt|favicon\.ico|img/|js/|css/|ipa/)}
      headers['Content-Security-Policy'] ||= "default-src 'self';"
      headers['Cross-Origin-Embedder-Policy'] ||= 'require-corp'
      headers['Cross-Origin-Opener-Policy'] ||= 'same-origin'
      headers['Cross-Origin-Resource-Policy'] ||= 'same-origin'
    end

    [status, headers, body]
  end
end

use StaticHeadersMiddleware

最後に

これで zaproxy の Insufficient Site Isolation Against Spectre Vulnerability に静的ファイルも対処できます

2025年7月25日金曜日

Sinatra で Cookie に SameSite 属性を設定する方法

Sinatra で Cookie に SameSite 属性を設定する方法

概要

Sinatra::Base で設定します

環境

  • Ruby 3.4.4
  • Sinatra 4.1.1

設定方法

class BasePage < Sinatra::Base
  enable :sessions
  set :session_secret, Credential::APP_SESSION_SECRET
  set :sessions,
      httponly: true,
      secure: production?,
      same_site: :lax
end

最後に

これで zaproxy の Cookie without SameSite Attribute に対応できます

2025年7月24日木曜日

Sinatra に CORP, COEP, COOP ヘッダを設定する方法

Sinatra に CORP, COEP, COOP ヘッダを設定する方法

概要

Sinatra::Base で設定します

環境

  • Ruby 3.4.4
  • Sinatra 4.1.1

設定方法

class BasePage < Sinatra::Base
  before do
    setup_response_headers
  end

  def setup_response_headers
    # Content Security Policy (CSP) の設定
    unless request.path.start_with?('/podcast/feed')
      csp = CSP.new.header
      headers 'Content-Security-Policy' => csp
    end
    # Cross-Origin Resource Policy (CORP) の設定
    headers 'Cross-Origin-Resource-Policy' => 'same-origin'
    # Cross-Origin Embedder Policy (COEP) の設定
    headers 'Cross-Origin-Embedder-Policy' => 'unsafe-none'
    # Cross-Origin Opener Policy (COOP) の設定
    headers 'Cross-Origin-Opener-Policy' => 'same-origin'
  end
end

最後に

これで zaproxy の Insufficient Site Isolation Against Spectre Vulnerability に対応できます

2025年7月23日水曜日

Sinatra で CSP レスポンスヘッダーを設定して XSS 対策する方法

Sinatra で CSP レスポンスヘッダーを設定して XSS 対策する方法

概要

CSP ヘッダを明示しなくても大きな問題にはなりませんが基本的に何でも受け入れる設定なので可能な限り設定したほうがいいかなと思います
そもそも外部にアクセスせずに self のみで完結するのが理想ですがそういうわけにもいかないのでアクセスするべき外部サイトを明示化しておくことでセキュリティ対策になる感じです

今回は Sinatra アプリで CSP (Content Security Policy) レスポンスヘッダーを設定する方法を紹介します

環境

  • Ruby 3.4.4
  • Sinatra 4.1.1

サンプルコード

まずは CSP を管理するクラスを定義します

  • vim libs/csp.rb
# frozen_string_literal: true

# Content-Security-Policy (CSP) の設定を行うクラス
class CSP
  def header
    "#{script_src} #{img_src} #{style_src} #{frame_src} #{connect_src} #{media_src} #{other_src}"
  end

  def script_src # rubocop:disable Metrics/MethodLength
    script_src_url_list = [
      'https://cdn.jsdelivr.net',
      'https://code.jquery.com',
      'https://www.googletagmanager.com',
      'https://cdnjs.cloudflare.com',
      'https://d3js.org',
      'https://unpkg.com',
      'https://www.youtube.com'
    ]
    gtag_hash = "'sha256-3j3z4K5sw7JEbB9oTbDCJixRv+lWuUYVNr8dOyKK7C0='"
    "script-src 'self' #{gtag_hash} #{script_src_url_list.join(' ')};"
  end

  def img_src # rubocop:disable Metrics/MethodLength
    img_src_url_list = [
      'https://lh3.googleusercontent.com',
      'https://blogger.googleusercontent.com',
      'https://addons.mozilla.org',
      'https://pbs.twimg.com',
      'http://pbs.twimg.com',
      'https://avatars.githubusercontent.com',
      'https://c10.patreonusercontent.com'
    ]
    "img-src 'self' data: #{img_src_url_list.join(' ')};"
  end

  def style_src
    style_src_url_list = [
      'https://cdn.jsdelivr.net',
      'https://unpkg.com',
      'https://cdnjs.cloudflare.com',
      'https://cdn.plyr.io/'
    ]
    # public/js/fontawesome-all.min.js が書き換わったら以下のハッシュが変わる可能性もある
    font_awesome_hash = "'sha256-bviLPwiqrYk7TOtr5i2eb7I5exfGcGEvVuxmITyg//c='"
    # views/alchol.erb で使用しているd3.jsのハッシュ
    d3_hash = "'sha256-WrkFMt0yMbnytekpJNs62cGCUpYDzgmKFnWzVnKQ6YY='"
    "style-src 'self' #{font_awesome_hash} #{d3_hash} #{style_src_url_list.join(' ')};"
  end

  def frame_src
    frame_src_url_list = [
      'https://www.youtube.com'
    ]
    "frame-src 'self' #{frame_src_url_list.join(' ')};"
  end

  def connect_src
    connect_src_url_list = [
      'https://cdn.plyr.io'
    ]
    "connect-src 'self' #{connect_src_url_list.join(' ')};"
  end

  def media_src
    media_src_url_list = [
      'https://storage.googleapis.com'
    ]
    "media-src 'self' #{media_src_url_list.join(' ')};"
  end

  def other_src
    [
      "form-action 'self';",
      "frame-ancestors 'self';",
      "font-src 'self';",
      "object-src 'self';",
      "manifest-src 'self';"
    ].join(' ')
  end
end

基本的には各 CSP ヘッダごとに「許可するサイト」「許可するハッシュ」を定義します
許可するサイトは見慣れた CDN サイトが並ぶ感じになります

許可するハッシュはどうしても style タグを使って直接 CSS を指定している場合にだけ使うので CSS ファイルを外部ファイルで管理している場合には不要です

使う側

CSP レスポンスヘッダーを指定するのは before が一番簡単です
特定のパスだけ適用したくない場合などは unless いましょう

before do
  # Content Security Policy (CSP) の設定
  unless request.path.start_with?('/podcast/feed')
    csp = CSP.new.header
    headers 'Content-Security-Policy' => csp
  end
end

動作確認

アプリを起動して問題なくすべてのコンテンツ (js or css or img etc) が取得できることを確認しましょう
Chrome などの開発者ツールでコンソールにエラーがないことを書くにしましょう
CSP は基本的にホワイトリスト形式なので設定が足りていないとコンテンツが取得できずエラーになりうまくサイトが表示できなくなります

基本的には CSP を設定した場合には style タグや onclick タグは使えないと思ったほうがいいです
css ファイルや js ファイルにすべて処理を移す感じになります

最後に

CSP レスポンスヘッダーを正しく設定することで XSS 対策しセキュアコードしましょう

参考サイト

2025年7月16日水曜日

slack-ruby-client でファイルを添付してメッセージを送信する方法

slack-ruby-client でファイルを添付してメッセージを送信する方法

概要

かなり昔に使い方を紹介したのですがその際はリアルタイムメッセージAPIの使い方を紹介しました
今回はファイルを添付したメッセージの送信方法を紹介します

環境

  • macOS 15.5
  • Ruby 3.4.4
  • slack-ruby-client 2.6.0

サンプルコード

# frozen_string_literal: true

require 'slack-ruby-client'

# MySlack クラスは Slack へのメッセージ送信とファイルアップロードを行います。
class MySlack
  USER_ID = 'U1234567890' # ユーザーIDを定義
  TOKEN = 'xoxb-xxx-xxx-xxx' # Slack API トークンを定義

  def initialize
    # Slack クライアントの初期化
    Slack.configure do |config|
      config.token = TOKEN
    end
    @client = Slack::Web::Client.new
  end

  def channel_id_for_sending_file
    # ボットと指定のユーザーとの会話を開始し、チャンネルIDを取得
    response = @client.conversations_open(users: USER_ID)
    response.channel.id
  end

  def send_message(message_text, channel_id)
    # メッセージを送信
    @client.chat_postMessage(
      channel: channel_id,
      text: message_text
    )
  end

  def upload_file(file_path, message_text, channel_id)
    # ファイルをアップロード
    @client.files_upload_v2(
      filename: File.basename(file_path),
      content: File.read(file_path),
      channels: [channel_id],
      initial_comment: message_text,
      snippet_type: 'text',
      title: '添付されたテキストファイル'
    )
  end
end

ポイント

Slack のファイルアップロード API は v1 から v2 になり大きく変更されています
チャネル名が指定不可になった他ボットからファイルを送信するためにはチャネルにボットが参加しているなどの条件があります
一番確実な方法として特定のユーザとの会話チャネルに送信する方法があるので今回はその方法を採用しています (参考)

トラブルシューティング

invalid_arguments (Slack::Web::Api::Errors::InvalidArguments)

ファイルが空の場合に発生します
どうやら空ファイルは送信できないようです

最後に

slack-ruby-client で file uplaod API v2 を使ってファイルのアップロードを行ってみました
v2 からかなり厳格なチェックが入っておりかつ v1 との互換性はないので注意が必要です

参考サイト

2025年5月26日月曜日

Github Action で rubocop を実行する方法

Github Action で rubocop を実行する方法

概要

.ruby-version がある場合は ruby-version の指定は不要です

環境

  • Github Action (2025/05/15時点)

.github/workflows/rspec.yml

name: Rspec test

on:
  push:
    branches:
      - "master"

jobs:
  rspec:
    runs-on: ubuntu-latest
    env:
      PASS_SUPPORT: true
      MODE: dev
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true
      - name: Run Rspec
        run: bundle exec rspec -f d spec/

最後に

env で環境変数が指定できます

2025年5月25日日曜日

Github Action で rspec を実行する方法

Github Action で rspec を実行する方法

概要

.ruby-version がある場合は ruby-version の指定は不要です

環境

  • Github Action (2025/05/15時点)

.github/workflows/rubocop.yml

name: RuboCop Linter

on:
  push:
    branches:
      - "master"

jobs:
  rubocop:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v3
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
	      # ruby-version: '3.4'
          bundler-cache: true
      - name: Run RuboCop
        run: bundle exec rubocop

最後に

参考サイト他のジョブと依存性を持たせたい場合は workflow_run を使いましょう

2024年11月23日土曜日

ruby で parallel する場合にはデータをスライスしてから実行したほうがいいかもしれない

ruby で parallel する場合にはデータをスライスしてから実行したほうがいいかもしれない

概要

そうしないとプロセスが複数立ち上がっても CPU の使用率が偏る気がします

環境

  • macOS 15.1.1
  • Ruby 3.3.5
  • parallel 1.26.3

サンプルコード

def analyze
  @data.each_slice(batch_size) do |sliced_data|
    tweets = Parallel.map(sliced_data, in_processes: parallel_process_count) do |d|
      parse_and_add_tweet(d['tweet']['full_text'])
    end
    @tweets.append(tweets)
  end
end
  • batch_size は @data / CPU 数
  • parallel_process_count は CPU 数
  • Parallel.map で各コアが処理した結果は @tweets に結合していく
  • こうすることで各コアに均等に処理するデータが渡り処理が効率化される (各コアのCPU使用率がちゃんと均等化される)

2024年11月22日金曜日

Ruby で Thread によるシングルコアとマルチコア化について

Ruby で Thread によるシングルコアとマルチコア化について

概要

Ruby は GVL の関係で基本はスレッド化してもシングルコアでしか動きません
てっとり早くマルチコア化した場合は parallel を使います

環境

  • macOS 15.1.1
  • Ruby 3.3.5
  • parallel 1.26.3

Thread 化する前のコード

配列を単純に順番に処理する関数です

スレッド化することを前提にコードを作成しており add はインスタンス変数の配列にデータを登録するよういになっています
またインスタンス変数は基本的に順番を保証しないことを考慮した作りにしたほうがいいです (Thread でどんどん処理されるので)

def parse_and_add_tweet(full_text)
  tweet = Tweet.new(full_text, @mecab)
  tweet.analyze
  add(tweet)
end

def exec
  @data.each do |d|
    parse_and_add_tweet(d['tweet']['full_text'])
  end
end

通常の Thread

配列のデータをスライスしてスライスしたデータをスレッドに渡して処理させるようなコードに変更します

thread_batch_size は作成するスレッド数ごとにデータが均等に割り当たるようにしています

def thread_batch_size
  (@data.size / thread_count.to_f).ceil
end

def thread_count
  32
end

def parse_and_add_tweet(full_text)
  tweet = Tweet.new(full_text, @mecab)
  tweet.analyze
  add(tweet)
end

def analyze
  threads = []
  @data.each_slice(thread_batch_size) do |sliced_data|
    threads << Thread.new do
      sliced_data.each do |d|
        parse_and_add_tweet(d['tweet']['full_text'])
      end
    end
  end
  threads.each(&:join)
end

これで実行するとわかりますがシングルコアの CPU を100%使用しますが残りのコアは何もしていないことが確認できます

parallel を使う

parallel_process_count は CPU コア数になります
こちらの方が単純に書くことができます
また実行するとすべての CPU が使われ 100% になっていることが確認できると思います

def parallel_process_count
  Parallel.processor_count
end

def parse_and_add_tweet(full_text)
  tweet = Tweet.new(full_text)
  tweet.analyze
  add(tweet)
end

def analyze
  @tweets = Parallel.map(@data, in_processes: parallel_process_count) do |d|
    parse_and_add_tweet(d['tweet']['full_text'])
  end
end

parallel を使う際は少し工夫が必要なのでそれに関しては後述のトラブルシューティングに記載しています

結果比較

通常 の Thread

1775.98s user 2.64s system 99% cpu 29:40.44 total

parallel を使う

3552.51s user 269.73s system 701% cpu 9:04.50 total

3-4倍くらい速くなりました

トラブルシューティング

'dump': no _dump_data is def ined for class FFI::Pointer (TypeError)

parallel にわたすブロックに Marshal.dump できない値は渡せません
今回で言うと mecab オブジェクトが FFI::Pointer をクラスを使っており渡せないので mecab オブジェクトの生成は Tweet クラス側の initialize ではなく各種関数側で行うように修正しました

最後に

  • そんなに処理時間がかからない -> 通常のループ
  • シングルコアは MAX に使いたい -> Thread を使ったループ
  • マルチコアであるだけリソースを使いたい -> parallel を使ったループ

という感じで使い分けるといいかなと思います
parallel の場合ほぼリソースを食い尽くすことができてしまうので他のプロセスの邪魔にならないように注意しましょう

参考サイト

2024年6月20日木曜日

Github Actions で rubygems に自動で push する (2要素認証対応あり)

Github Actions で rubygems に自動で push する (2要素認証対応あり)

概要

前回 dockerhub に push するアクションを作成しました
今回は rubygems に push するアクションを作成しました

環境

  • macOS 11.7.10
  • GithubActions v3
  • rubygems (2024/06/18時点)

gem.yml

  • vim .github/workflows/gem.yml
name: ci

on:
  workflow_dispatch:
    inputs:
      rubygems-otp-code:
        description: RubyGems OTP code
        required: true

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: 3.2.2
      - run: bundle install

      - name: Setup git config
        run: |
          git config --global user.email "your-github-email-address@xxx.com"
          git config --global user.name "your-github-name"

      - name: git
        run: |
          git status
          git diff

      - name: Publish to RubyGems
        run: bundle exec rake release
    env:
      GEM_HOST_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}

Rubygems の API キーの取得

設定 -> API キー

で作成できます
rubygems に push できる権限を扶養しましょう

RUBYGEMS_API_KEY の登録

リポジトリのシークレットに登録します

自分のプロジェクト -> Settings -> Secrets and variables -> Actions -> Repository secrets -> New repository secret

トラブルシューティング

There are files that need to be committed first.

ローカルの開発環境が Mac の場合 nokogiri など darwin 用のパッケージがインストールされます

     nokogiri (1.16.6-x86_64-darwin)
       racc (~> 1.4)

この状態の Gemfile.lock で Github Actions 上の Ubuntu で bundle install を実行すると linux 用の nokogiri がインストールされます

+    nokogiri (1.16.6-x86_64-linux)
+      racc (~> 1.4)

Github Actions では必ずこれがインストールされてしまうので事前に Mac 上で Linux 用のパッケージも追加してあげる必要があります

  • bundle lock --add-platform linux

You have enabled multi-factor authentication.

Rubygems で 2auth を有効にしている場合はワンタイムパスワードを入力する必要があります

env に GEM_HOST_OTP_CODE: ${{ github.event.inputs.rubygems-otp-code }} を追加することでアクション実行時にワンタイムパスワードを入力することができるのでこれで対応します

動作確認

version.rb や bundle update をして何かしら更新しましょう
そして Github に push します

今回ワークフローは手動でなので手動実行します
さらにワンタイムパスワードを入力しましょう

するとワークフローが実行されて version.rb に記載した新しいバージョンの gem が公開されていることが確認できると思います

最後に

Github Actions で rubygems に自動で push してみました
master に push されると自動で実行されます

Rubygems の API キーには有効期限があるので切れた場合は再度 Github に登録してあげましょう

参考サイト

2024年2月16日金曜日

Sinatra v4 にバージョンアップしてみた

Sinatra v4 にバージョンアップしてみた

概要

自分のアプリを Sinatra v4 にアップデートしてみました
少し躓く点があったので紹介します

環境

  • macOS 11.7.10
  • Ruby 3.2.2
  • Sinatra 3.2.0 -> 4.0.0

アップデート方法

thin -> puma に変更

thin だと rack のバージョンが3以下でなければなりません
しかし sinatra が rack のバージョン3以上を必要としているため thin では v4 にアップデートできません

なので

gem 'thin'

↓

gem 'puma'

rackup gem のインストール

rack v3 にすることで個別に rackup コマンドをインストールする必要があります

gem 'rackup'

アップデート

あとはアップデートするだけです

  • bundle update

rackup が初回インストールの場合はインストールもしましょう

  • bundle install

トラブルシュート

rackup コマンドがない場合に以下のエラーが発生します
config.ru でアプリを参照できていないわけではないので注意しましょう

bundler: failed to load command: rackup (/Users/hawk/data/repo/ruby-homepage/vendor/ruby/3.2.0/bin/rackup)
/Users/hawk/data/repo/ruby-homepage/vendor/ruby/3.2.0/gems/bundler-2.3.7/lib/bundler/rubygems_integration.rb:319:in `block in replace_bin_path': can't find executable rackup for gem rack (Gem::Exception)

最後に

リリースノートはこちらです

2023年12月6日水曜日

bundler の with と without オプションは非推奨になっていた

bundler の with と without オプションは非推奨になっていた

概要

推奨方法を紹介します
結論としては環境変数を使いましょう

環境

  • macOS 11.7.10
  • Ruby 3.2.2
  • bundler 2.3.7

今まで

  • bundle install --with development
  • bundle install --without development

これから

  • bundle config set with '' && bundle config set without 'development' && bundle install
  • bundle config set without '' && bundle config set with 'development' && bundle install

環境変数を使うことはできなさそう

BUNDLE_WITHOUT or BUNDLER_WITHOUT を設定すればいけるという記事を何個か見たのですがうまく動作しませんでした

もし BUNDLE_WITHOUT を使いたい場合は結局 bundle config set しないといけないので上記と同じ手順を踏むことになりそうです

最後に

なんか劣化したような気がします

参考サイト

2023年12月5日火曜日

bundler-audit で Gemfile 内で使っている gem の脆弱性チェックをする

bundler-audit で Gemfile 内で使っている gem の脆弱性チェックをする

概要

過去にtrivyを使って脆弱性スキャンを紹介しました
今回は gem に特化したツールを紹介します

環境

  • macOS 11.7.10
  • Ruby 3.2.2
    • bundler-audit 0.9.1

インストール

  • vim Gemfile
gem 'bundler-audit'
  • bundle config path vendor
  • bundle install

使ってみる

まずは最新の脆弱性情報を取得します

  • bundle exec bundler-audit update
Download ruby-advisory-db ...
Cloning into '/Users/user01/.local/share/ruby-advisory-db'...
remote: Enumerating objects: 11402, done.
remote: Counting objects: 100% (1519/1519), done.
remote: Compressing objects: 100% (349/349), done.
remote: Total 11402 (delta 1237), reused 1228 (delta 1160), pack-reused 9883
Receiving objects: 100% (11402/11402), 1.82 MiB | 8.14 MiB/s, done.
Resolving deltas: 100% (6446/6446), done.
ruby-advisory-db:
  advisories:   827 advisories
  last updated: 2023-11-30 12:36:04 -0800
  commit:       d821bf162550302abd1fa1fe15007f3012b76f32

ローカルにキャッシュされます

あとは実行すれば OK です

  • bundle exec bundler-audit
No vulnerabilities found

と表示された場合は特に脆弱な gem をプロジェクト内で使用していないことになります

更新とスキャンを同時に行う

  • bundle exec bundler-audit check --update

こっちを使ったほうがいいかもです

最後に

あとはこれを CI に追加すれば gem のセキュリティチェックができるようになります
当然ですが CVE ベースなのでゼロデイ攻撃などには対応していません

trivy でもできそうですがすでに bundler-audit を使っている場合はこれで十分かもしれないです

参考サイト

2023年12月4日月曜日

global な環境にインストールした gem をすべて削除する方法

global な環境にインストールした gem をすべて削除する方法

概要

Homebrew などでインストールした gem コマンドでインストールするとすべての環境に共通の場所に gem がインストールされます
まちがってグローバルな環境にいろいろな gem をインストールしてしまった場合に一旦すべてを削除する方法を紹介します

環境

  • macOS 14.1.2
  • Ruby 3.2.2

コマンド

  • gem uninstall -I -a -x --user-install --force

rbenv など使っている場合は必ず system になるパスで上記を実行しましょう

トラブルシューティング

アンインストール中に以下のようなエラーになり削除できないケースがあります

ERROR:  While executing gem ... (Gem::InstallError)
    test-unit is not installed in GEM_HOME, try:
        gem uninstall -i /opt/homebrew/Cellar/ruby/3.2.2_1/lib/ruby/gems/3.2.0 test-unit

そんな場合は素直に記載のコマンドを実行してアンインストールしましょう

  • gem uninstall -i /opt/homebrew/Cellar/ruby/3.2.2_1/lib/ruby/gems/3.2.0 test-unit

再度必要なものだけインストールする

  • gem install bundler
  • gem install solargraph
  • gem install rubocop

最後に

アンインストール後に gem list でインストール済みの gem の一覧を見ると結構あるので実は削除されていないものがあるような気もします

参考サイト

2023年7月2日日曜日

ruby sidekiq-scheduler を試す

ruby sidekiq-scheduler を試す

概要

Sidekiq のキューイングを cron のように定期的に実行できるのが sidekiq-scheduler です
今回はインストールから簡単な動作確認までやってみました

環境

  • macOS 13.4
  • redis 7.0.11
  • Ruby 3.2.2
  • sidekiq-scheculer 5.0.3

インストール

  • vim Gemfile
gem 'sidekiq-scheduler'
  • bundle config path vendor
  • bundle install

サンプルコード (ワーカー)

エンキューはスケジューラが実行するため必要なのはワーカーになります
ワーカーは普通の sidekiq のように perform を実装するだけです

  • vim worker.rb
# frozen_string_literal: true

require 'sidekiq-scheduler'

# ワーカークラス
class HelloWorld
  include Sidekiq::Worker

  def perform
    puts 'Hello world'
  end
end

スケジュール設定 (yaml ファイル)

この設定ファイルを元にスケジューラがエンキューします
sidekiq-scheduler の cron 設定は秒単位まで指定できます

  • vim config/sidekiq.yml
:scheduler:
  :schedule:
    hello_world:
      cron: '0 * * * * *'  # 毎分0秒に実行
      class: HelloWorld

スケジューラ起動

あとはワーカーとともにスケジューラを起動するだけです

  • bundle exec sidekiq -r ./worker.rb

動作確認

ワーカーのログに毎分 0 秒のときに以下のようなログが出力されれば OK です

2023-06-12T01:28:09.357Z pid=87946 tid=1x4e INFO: Running in ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
2023-06-12T01:28:09.357Z pid=87946 tid=1x4e INFO: See LICENSE and the LGPL-3.0 for licensing details.
2023-06-12T01:28:09.357Z pid=87946 tid=1x4e INFO: Upgrade to Sidekiq Pro for more features and support: https://sidekiq.org
2023-06-12T01:28:09.357Z pid=87946 tid=1x4e INFO: Sidekiq 7.1.1 connecting to Redis with options {:size=>10, :pool_name=>"internal", :url=>nil}
2023-06-12T01:28:09.359Z pid=87946 tid=1x4e INFO: Sidekiq 7.1.1 connecting to Redis with options {:size=>5, :pool_name=>"default", :url=>nil}
2023-06-12T01:28:09.360Z pid=87946 tid=1x4e INFO: Loading Schedule
2023-06-12T01:28:09.360Z pid=87946 tid=1x4e INFO: Scheduling hello_world {"cron"=>"0 * * * * *", "class"=>"HelloWorld", "queue"=>"default"}
2023-06-12T01:28:09.527Z pid=87946 tid=1x4e INFO: Schedules Loaded
2023-06-12T01:28:09.527Z pid=87946 tid=1x4e INFO: Starting processing, hit Ctrl-C to stop
2023-06-12T01:29:00.036Z pid=87946 tid=1wwa INFO: queueing HelloWorld (hello_world)
2023-06-12T01:29:00.037Z pid=87946 tid=1wwu class=HelloWorld jid=a2522a3f2f3de9a7c706f1f9 INFO: start
Hello world
2023-06-12T01:29:00.038Z pid=87946 tid=1wwu class=HelloWorld jid=a2522a3f2f3de9a7c706f1f9 elapsed=0.001 INFO: done

ちょっと応用: スケジューラの登録を動的に行う

yaml ファイルでスケジュールを管理するのは大変なので ruby から動的に登録することも可能です

  • vim job_create.rb
# frozen_string_literal: true

require 'sidekiq-scheduler'

Sidekiq.set_schedule('hello', { 'every' => ['1m'], 'class' => 'HelloWorld' })

これを起動することでスケジューラを登録できます

  • bundle exec ruby job_create.rb

redis を確認するとスケジューラが登録されています

redis-cli keys '*'
1) "schedules_changed"
2) "schedules"

redis-cli hgetall 'schedules'
1) "hello"
2) "{\"every\":[\"1m\"],\"class\":\"HelloWorker\"}"

また設定ファイルでは dynamic というオプションを true にします

  • vim config/sidekiq.yml
:scheduler:
  :dynamic: true

あとはワーカーを起動して毎分実行されることを確認しましょう

  • bundle exec sidekiq -r ./worker.rb

ワーカーがスケジューラを認識しない場合はサイド job_create.rb を実行してみてください
同一スケジューラ名の場合は新規で追加はせずスケジューラが更新されるだけになります
またスケジューラの変更はワーカーは動的に行ってくれます

最後に

とりあえず動かすところまでやってみました
スケジュールの設定が yaml ファイルなのが特徴的なところかなと思います
また秒単位まで指定できるので細かい時間指定も可能かなと思います

参考サイト

https://github.com/sidekiq-scheduler/sidekiq-scheduler

2023年6月26日月曜日

emacsでruby-lspを使ってみる

emacsでruby-lspを使ってみる

概要

solargraph に変えて ruby-lsp を使ってみました
emacs のクライアントは lsp-mode を使います

環境

  • macOS 11.7.6
  • emacs 28.2
    • lsp-mode 20230524.1820
  • Ruby 3.2.1
    • ruby-lsp 0.5.1

lsp-mode アップデート or インストール

ruby-lsp に対応しているバージョンをインストールしましょう

  • M-x package-list-packages

ruby-lsp

solargraph のアンインストール

  • gem uninstall solargraph

ruby-lsp のインストール

  • gem install ruby-lsp

ruby-lsp コマンドが実行できることを確認してください
また emacs から ruby-lsp コマンドが見つからず Command "ruby-lsp" is not present on the path. という感じでうまく起動できない場合はターミナルや tmux から一度抜けて再度試してみてください

emacs の設定

(require 'lsp-mode)
; (setq lsp-ruby-lsp-use-bundler t)
(add-hook 'ruby-mode-hook 'lsp)

bundler 配下で使用する場合には Gemfile に

gem 'ruby-lsp'

をし bundle install した後上記コメント部分をアウトしてください

動作確認

トラブルシューティング

solargraph から移行した場合には前のセッションが残っておりうまく動作しないことがあるのでセッションを削除して再度 ruby-lsp を起動してみてください

  • rm ~/.emacs.d/.lsp-session-v1
The connected server(s) does not support method textDocument/definition.
To find out what capabilities support your server use ‘M-x lsp-describe-session’
and expand the capabilities section

となり定義へのジャンプはできませんでした
おそらく https://github.com/Shopify/ruby-lsp/pull/195 にあるように特殊なコメントを記載することでそこに飛ぶことはできるようです
またその場合は lsp-mode 側で lsp-enable-links を有効にする必要がありそうです

最後に

まだまだ開発途中という感じがします
また vscode に特化しているのと記述が Spotify 基準になっているので少し特殊な書き方が必要かもしれません
当面は solargraph + robe という気がします

参考サイト