2022年12月28日水曜日

Ruby でライセンスチェックをする方法

Ruby でライセンスチェックをする方法

概要

licensed を使うとソースコードが使用しているライセンスをチェックすることができます
基本的にはコードがライセンスに準拠していない場合には対応して対応が完了したらライセンスチェックをスルーするさせるようなツールになります

環境

  • macOS 11.7.2
  • Ruby 3.1.2
    • licensed 3.9.1

licensed のインストール

  • gem install licensed

設定ファイルの作成 .licensed.yml

設定ファイルを作成します
ライセンスチェック対象のファイルとソースコードの言語を設定します

allowed はチェック時に成功とするライセンスを記載します
自分のソースコードの使い方、ライセンス表記で条件を満たしているライセンスの一覧を記載することになります

ignored はライセンスチェックを無視する gem を記載します
ライセンス表記がない gem などがあるのでその場合は ignored に記載します

  • vim .licensed.yml
sources:
  bundler: true
apps:
  - source_path: ./libs
  - source_path: ./controllers
  - source_path: ./views
allowed:
  - mit
  - apache-2.0
  - other
  - none
ignored:
  bundler:
    - bundler
    - ruby2_keywords

キャッシュファイルの作成

まずはキャッシュファイルを作成する必要があります
作成が成功すると .licenses/ ディレクトリが作成されます

  • licensed cache

ライセンスチェック

最後にライセンスチェックをします

  • licensed status
............F.................................................FF..........................
Errors:
* views.bundler.e2mmap
  version: 0.1.0, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/e2mmap.dep.yml, license: bsd-2-clause, allowed: false
    - license needs review: bsd-2-clause

* views.bundler.reverse_markdown
  version: 2.1.1, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/reverse_markdown.dep.yml, license: wtfpl, allowed: false
    - license needs review: wtfpl

* views.bundler.rexml
  version: 3.2.5, filename: /Users/username/data/repo/ruby-test/.licenses/views/bundler/rexml.dep.yml, license: bsd-2-clause, allowed: false
    - license needs review: bsd-2-clause


90 dependencies checked, 3 errors found.

Licensed found errors during source enumeration.  Please see https://github.com/github/licensed/tree/master/docs/commands/status.md#status-errors-and-resolutions for possible resolutions.

最後に

あとは allowed に記載していないライセンスに準拠するようにソースコードやライセンス表記を修正したあとで allowed にライセンスを追加していく感じで OK です

参考サイト

2022年12月27日火曜日

licensee を使って gem のライセンス情報を取得してみる

licensee を使って gem のライセンス情報を取得してみる

概要

gem のライセンスを licensee を使って取得してみました
使用している gem のライセンスチェックをする際に使用できます

環境

  • macOS 11.7.2
  • Ruby 3.1.2
    • licensee 9.15.3
  • cmake 3.25.1

cmake のインストール

native extension で cmake に依存しているため事前にインストールしておきます

  • brew install cmake

licensee のインストール

  • gem install licensee

ライセンスチェック

  • licensee detect vendor/ruby/3.1.0/gems/sinatra-3.0.5
License:        MIT
Matched files:  LICENSE, sinatra.gemspec
LICENSE:
  Content hash:  4c2c763d64bbc7ef2e58b0ec6d06d90cee9755c9
  Attribution:   Copyright (c) 2007, 2008, 2009 Blake Mizerany
Copyright (c) 2010-2017 Konstantin Haase
Copyright (c) 2015-2017 Zachary Scott
  Confidence:    100.00%
  Matcher:       Licensee::Matchers::Exact
  License:       MIT
sinatra.gemspec:
  Confidence:  90.00%
  Matcher:     Licensee::Matchers::Gemspec
  License:     MIT

再帰的にチェックする

残念ながらコマンドラインから再帰的にチェックする機能はないので Ruby で直接チェックしましょう

require "licensee"

dirs = Dir.glob("./vendor/ruby/3.1.0/gems/*")

dirs.each do |dir|
  puts dir

  project = Licensee.project dir
  project.licenses.each do |license|
    puts license.key
  end

  # or
  # license = Licensee.license dir
  # puts license.key unless license.nil?
end

参考サイト

2022年12月26日月曜日

Gitlabにカスタムバッジを作成する方法

Gitlabにカスタムバッジを作成する方法

概要

デフォルトだとパイプラインやカバレッジなどのバッジは用意されています
これ以外のバッジを作成する場合は自分で作成する必要があります
今回はカスタムバッジを AnyBadge を使って作成する方法を紹介します

環境

  • Gitlab-ee 15.4.3-ee

.gitlab-ci.yml

流れとしてはまず CI 上でバッジ情報を作成します
そして作成したバッジをプロジェクトのバッジ設定から参照するようにする感じです

まずは .gitlab-ci.yml から作成します

stages:
    - build

create_badge_svg:
  stage: build
  image: python:3.10.9
  script:
    - echo "Python other dependencies installation"
  after_script:
    - pip install anybadge
    - d=$(date "+%Y/%m/%d")
    - anybadge -l "Today is" -v $d -f today.svg -c green
  artifacts:
    paths:
        - today.svg
    when: always
    expire_in: 4 weeks

ちょっと説明

簡単にやっていることを説明するとまず anybadge をインストールしています
すると anybadge というコマンドが使えるようになります
今回は日付の情報を表示するバッジを作成したいと思います
date コマンドで本日の日付を取得してその情報を anybadge コマンドに与えて svg ファイルを作成します
あとは作成した svg ファイルを artifacts で保存すれば OK です

プロジェクト側でバッジを参照する

Settings -> General -> Badges から新規でバッジを作成します

  • Name・・・なんでもOKです
  • Link・・・バッジをクリックしたときに飛ぶURLを記載します、これも何でもOKです
  • Badge Image URL・・・ここに gitlab-ci で作成したバッジの URL を記載します

サンプルは以下の通りです

svg を直接ダウンロードできる URL を指定する必要があります
今回は master ブランチの最新のジョブの svg ファイルを参照する API をコールすることで取得しています
他にも取得する方法はあるのでお好きな方法でアクセスしてください

動作確認

あとはバッジを追加してプロジェクトの先頭にいけばバッジがあることを確認できると思います

最後に

AnyBadge を使って Gitlab にカスタムバッジを作成する方法を紹介しました
今回は gitlab-ci と組み合わせて作成しましたが結局 svg ファイルがあればいいので無理に CI と組合わせる必要はありません

参考サイト

2022年12月23日金曜日

treemacs にアイコンを表示する方法

treemacs にアイコンを表示する方法

概要

treemacs-all-the-icons を使います

環境

  • macOS 11.7.2
  • emacs 28.1

インストール

package-list-packages -> treemacs-all-the-icons をインストール

.emacs

(when (display-graphic-p)
  (require 'all-the-icons))
(with-eval-after-load 'treemacs
  (require 'treemacs-all-the-icons)
  (treemacs-load-theme "all-the-icons"))

フォントのインストール

  • M-x all-the-icons-install-fonts

注意事項

emacs -nw では使えません

https://github.com/Alexander-Miller/treemacs/issues/53

最後に

デフォルトでも GUI モードの emacs であれば表示されますが足りないので all-the-icons を使います

参考サイト

2022年12月22日木曜日

Google App Script で使用している列の一覧を取得する方法

Google App Script で使用している列の一覧を取得する方法

概要

例えば A から AB まで列を広げている場合に A から AB までを持つ配列を取得する方法を紹介します

環境

  • Google App Script 2022/12/16 時点

サンプルコード

// 数字情報を列のアルファベットに変換するメソッド
function columnToLetter(column) {
  var temp, letter = '';
  while (column > 0)
  {
    temp = (column - 1) % 26;
    letter = String.fromCharCode(temp + 65) + letter;
    column = (column - temp - 1) / 26;
  }
  return letter;
}

// 指定の配列を number 個ずつ分割するメソッド
function sliceByNumber(array, number) {
  const length = Math.ceil(array.length / number)
  return new Array(length).fill().map((_, i) =>
    array.slice(i * number, (i + 1) * number)
  )
}

// メイン
function main() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadsheet.getActiveSheet();
  var activeCols = sliceByNumber([...Array(sheet.getLastColumn()).keys()].map(i => columnToLetter(i+1)), 3);
}

ちょっと解説

[...Array(sheet.getLastColumn()).keys()] で最初の列からアクティブになっている最後の列までの数字配列を取得します

その結果に対して map で columnToLetter を実行し数字 -> アルファベットの変換をしています
数字状態の配列は0から始まっているので1から始めるために+1しています (A列は1に割り当てているので)

あとは sliceByNumber で配列を更に3つずつに分割しています
分割が必要ない場合は sliceByNumber の実行は不要です

最後に

これでどんどん列が増えていっても自動で列情報を取得することができます

2022年12月21日水曜日

dataclass を使って配列内にある辞書データをオブジェクトに変換する方法

dataclass を使って配列内にある辞書データをオブジェクトに変換する方法

概要

前回 dataclass を使った JSON <-> オブジェクトの変換方法を紹介しました
今回はその中でもクセのある辞書データが配列内に直接置かれている場合を紹介したいと思います

また配列内にある辞書データはデータ構造がデータごとに異なる場合を想定しています

環境

  • macOS 11.7.1
  • Python 3.10.2
  • dataclass-wizard 0.22.2

サンプルコード

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import (List,
                    Optional)


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Profile():
    lang: Optional[List[Lang]] = None
    framework: Optional[List[Framework]] = None


@dataclass
class UserData(JSONWizard):
    name: str
    profile: List[Profile]


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)

ちょっと解説

今回は profile というフィールドが配列でこの中にデータ構造の異なる辞書データが複数個入ることを想定しています

ポイントは Profile クラスでここにデータ構造が異なるデータをデフォルト None ですべて定義する点です

配列の場合順番にオブジェクトに変換されます 最初は lang 情報が変換されます
Profile クラスに渡されるデータは lang 情報だけなので必ず他のフィールドはデフォルト値で初期化しておく必要があります
lang 変換時には framework フィールドの情報は渡されないので None が設定されます

つまり配列内の最初のデータは lang がオブジェクトに変換され framework が None として変換されます

配列の2つ目の値は framework になります
framework が Profile クラスに渡されて変換されます
同様に framework 情報のみが渡されるので lang は None として変換されます

このように配列それぞれのデータごとに変換が行われるので配列内で管理するデータ構造分定義する必要がありまたそれぞれのフィールドはデフォルト値を必ず設定する必要があります

tuple でもいけます

あとから気づいたんですが tuple を使ってもいけます
この場合は tuple に定義する順番は配列内の順番と同じである必要があります
None もないのでこちらのほうが綺麗かもしれません

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import List


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Frameworks():
    framework: List[Framework]


@dataclass
class Langs():
    lang: List[Lang]


@dataclass
class UserData(JSONWizard):
    name: str
    profile: tuple[Langs, Frameworks]


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)

更に配列の順番が固定であればオブジェクトから直接参照できるようにできる

例えば今回のように 1 番目が lang 2 番目が framework のように配列に格納されている順番が保証されているのであればわざわざ profile からインデックスで参照せずに直接 lang と framework を参照してもいいと思います

その場合は __post_init__ と組み合わせるといい感じになると思います

from dataclasses import dataclass
from dataclass_wizard import JSONWizard
from typing import (List,
                    Optional)


@dataclass
class Framework():
    sinatra: str
    flask: str


@dataclass
class Lang():
    ruby: str
    python: str


@dataclass
class Frameworks():
    framework: List[Framework]


@dataclass
class Langs():
    lang: List[Lang]


@dataclass
class Profile():
    lang: List[Lang]
    framework: List[Framework]


@dataclass
class UserData(JSONWizard):
    name: str
    profile: tuple[Langs, Frameworks]
    p: Optional[Profile] = None

    def __post_init__(self):
        self.p = Profile(self.profile[0].lang,
                         self.profile[1].framework)


if __name__ == "__main__":
    data = {
        "name": "hawksnowlog",
        "profile": [
            {
                "lang": [
                    {
                        "ruby": "3.1.2",
                        "python": "3.10.2",
                    }
                ]
            },
            {
                "framework": [
                    {
                        "sinatra": "3.0.0",
                        "flask": "1.0.0",
                    }
                ]
            },
        ]
    }
    result = UserData.from_dict(data)
    print(result)
    print(result.name)
    print(result.profile[0].lang[0].ruby)
    print(result.profile[1].framework[0].sinatra)
    print(result.p.lang[0].ruby)
    print(result.p.framework[0].sinatra)

最後に

オブジェクトに変換する場合は JSON の構造に合わせて型を定義する必要があるのでうまく行かない場合は型定義がちゃんと JSON から抽出できているかを確認しましょう

参考サイト

2022年12月20日火曜日

Trivy超入門

Trivy超入門

概要

Trivy はオープンソースの脆弱性チェックツールです
今回はインストールから簡単な実行方法まで紹介します

環境

  • Ubuntu 18.04
  • trivy 0.35.0

インストール

  • sudo apt-get install wget apt-transport-https gnupg lsb-release
  • wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | gpg --dearmor | sudo tee /usr/share/keyrings/trivy.gpg > /dev/null
  • echo "deb [signed-by=/usr/share/keyrings/trivy.gpg] https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main" | sudo tee -a /etc/apt/sources.list.d/trivy.list
  • sudo apt-get update
  • sudo apt-get install trivy

docker イメージのスキャン

  • trivy image python:3.10.2-buster

ローカルにあるファイルをスキャン

  • trivy fs --security-checks vuln,secret,config proj/path/to/secret.py
  • trivy fs --security-checks vuln,secret,config proj/path/to/docker
  • trivy fs --security-checks vuln,secret,config proj/path/to/Pipfilie.lock

シークレットなどを管理している python ファイルを指定したり Pipfile.lock や Dockerfile がある場所を指定します

再帰的にスキャンするには

どうやらないようです
https://github.com/aquasecurity/trivy/issues/1302

スキャン対象のファイルを特定して指定するしかないようです

最後に

Trivy を試してみました
インストールから実行するまではかなり簡単に試せるようです
基本的にはイメージにしてからそれをスキャンする形になると思います

がイメージによっては本質ではない ImageMagic やら関係のない CVE ばかり出てくるのでチェック対象を絞ってスキャンをする必要がありそうです

参考サイト

2022年12月19日月曜日

emacsのpipenv.elを使ってみる

emacsのpipenv.elを使ってみる

概要

pipenv 環境で lsp などを使う場合に便利そうなので使ってみました

環境

  • Ubuntu 18.04
  • emacs 27.1
  • Python 3.10.2

インストール

package-list-packages -> pipenv.el を選択してインストール

pipenv-mode の有効化

pipenv-mode を実行すれば OK です

現在の環境のアクティブ化

Python のプロジェクトを開いて pipenv-active を実行してみましょう

すると python-shell-virtualenv-root という変数に pipenv --venv で実行されたパスが設定されます

describe-variable python-shell-virtualenv-root

lsp-pyright と組み合わせる

前回紹介した中に virtualenv のパスを手動で設定する箇所を紹介しました

この変数に python-shell-virtualenv-root の値を設定することで動的に lsp-pyright への virtualenv のパスを設定することができそうです

(setq lsp-pyright-venv-path python-shell-virtualenv-root)

プロジェクトを切り替えた際に自動でアクティブ化する

こんな感じで .emacs に記載すればプロジェクト切り替え時にアクティブ化することができるようです

(use-package pipenv
  :hook (python-mode . pipenv-mode)
  :init
  (setq
   pipenv-projectile-after-switch-function
   #'pipenv-projectile-after-switch-extended))

これと更に先程紹介した lsp-pyright の設定を組み合わせればプロジェクトを切り替えた際に自動で pyright の向き先を変更することもできそうです

最後に

当然ですが pipenv で動作していないプロジェクト配下でアクティブ化しても python-shell-virtualenv-root には値は入りません

poetry などで動作している場合でも値は入らないので注意してください

参考サイト

2022年12月17日土曜日

Pythonでdataclassを使ってdictからオブジェクトに変換する方法

Pythonでdataclassを使ってdictからオブジェクトに変換する方法

概要

過去にmarshmallowを使った方法を紹介しました
今回はサードパーティのライブラリは使わずに dataclass だけを使って変換する方法を紹介します

環境

  • Ubuntu18.04
  • Python 3.10.2

サンプルコード

from dataclasses import dataclass

@dataclass
class Profile():
    lang: str
    framework: str

@dataclass
class User():
    name: str
    age: int
    profile: Profile


if __name__ == "__main__":
    dict_data = {
        "name": "hawksnowlog",
        "age": 10,
        "profile": {
            "lang": "ruby",
            "framework": "sinatra"
        }
    }
    user = User(**dict_data)
    print(user.name)
    print(user.age)
    print(user.profile.lang)
    print(user.profile.framework)

しかしこれだとネストしている Profile が展開されず辞書になってしまいます

ネストに対応するには

  • pip install dataclass-wizard
from dataclasses import dataclass

from dataclass_wizard import JSONWizard


@dataclass
class Profile():
    lang: str
    framework: str

@dataclass
class User(JSONWizard):
    name: str
    age: int
    profile: Profile


if __name__ == "__main__":
    dict_data = {
        "name": "hawksnowlog",
        "age": 10,
        "profile": {
            "lang": "ruby",
            "framework": "sinatra"
        }
    }
    user = User.from_dict(dict_data)
    print(user.name)
    print(user.age)
    print(user.profile.lang)
    print(user.profile.framework)

dict に戻すには

to_json メソッドをコールするだけです

from dataclasses import dataclass

from dataclass_wizard import JSONWizard


@dataclass
class Profile():
    lang: str
    framework: str

@dataclass
class User(JSONWizard):
    name: str
    age: int
    profile: Profile


if __name__ == "__main__":
    dict_data = {
        "name": "hawksnowlog",
        "age": 10,
        "profile": {
            "lang": "ruby",
            "framework": "sinatra"
        }
    }
    user = User.from_dict(dict_data)
    print(user.name)
    print(user.age)
    print(user.profile.lang)
    print(user.profile.framework)
    print(user.to_json())

datetime の使い方

from datetime import datetime
from dataclasses import dataclass

from dataclass_wizard import JSONWizard


@dataclass
class Profile():
    lang: str
    framework: str

@dataclass
class User(JSONWizard):
    name: str
    age: int
    created_at: datetime
    profile: Profile


if __name__ == "__main__":
    dict_data = {
        "name": "hawksnowlog",
        "age": 10,
        "created_at": "2010-06-10 15:50:00Z",
        "profile": {
            "lang": "ruby",
            "framework": "sinatra"
        }
    }
    user = User.from_dict(dict_data)
    print(user.name)
    print(user.age)
    print(user.created_at.year)
    print(user.profile.lang)
    print(user.profile.framework)
    print(user.to_json())

最後に

結局 pypi から追加のライブラリが必要になりそうです、、

参考サイト

2022年12月16日金曜日

emacsでコードを畳んだり開いたりする方法

emacsでコードを畳んだり開いたりする方法

概要

コードが長くなったときにクラスや関数を畳むことができます
今回は Python-mode で実行しています

環境

  • macOS 11.7.1
  • emacs 28.1

hs-minor-mode を使う

まずはこれを有効にします

hs-toggle-hiding (S-mouse-2)

クラス上で hs-toggle-hiding を実行するとクラスを畳むことができます
再度実行すると開くことができます

hs-show-all (C-c @ C-a)

畳んだすべてのクラスや関数を開きます

hs-hide-all (C-c @ C-t)

開いたクラスや関数をすべて閉じます

最後に

emacs28 であれば python-mode 上でデフォルトで使用できます
デフォルトのキーバインドが結構辛いので変更してもいいかもしれません
特にマウスを使うバインドは nw だと機能しない場合は多いです

また VScode 用の region … endregion は hs-mode では機能しないので注意してください

参考サイト

2022年12月15日木曜日

emacs + pyright 設定方法

emacs + pyright 設定方法

概要

pylsp から pyright に乗り換えてみました
pyright の設定方法を紹介します

また今回は pipenv 配下で使用する方法を紹介します

環境

  • Ubuntu 18.04
  • emacs 27.1
  • Python 3.10.2
  • pyright 1.1.284

pyright のインストール

  • pip install pyright

pylsp のように無理やり書き換える必要はないです
グローバルにインストールしましょう

lsp-pyright のインストール

  • package-list-packages -> lsp-pyright
  • package-list-packages -> leaf

をインストールしましょう
どちらも melpa からインストールしました

.emacs

(leaf lsp-pyright
  :ensure t
  :require t
  :after python
  :defvar lsp-pyright-venv-path
  :init
  (setq lsp-pyright-venv-path "/home/user/.local/share/virtualenvs/app-Yn-xfOlh")
  :hook
  (python-mode-hook . lsp))

少し解説

lsp-pyright-venv-path に pipenv の環境のパスを設定してあげます
pipenv --venv で確認できるのでそこで表示されたパスを記載しましょう
別の virtualenv を使いたい場合は setq の命令を scratch で実行した上で lsp-restart-workspace を実行すれば OK です

pyenv.el と組み合わせればダイナミックに切り替えできるっぽいですがうまくできなかったので直接指定しています

参考サイト

2022年12月13日火曜日

docker for mac のディスク逼迫対応

docker for mac のディスク逼迫対応

概要

Docker.raw が肥大化してディスクを逼迫している場合の対処方法です

環境

  • macOS 11.7
  • docker for mac 20.10.12

使用していないリソースの削除

  • docker system prune -a

仮想領域の上限サイズ指定

歯車 -> Resources -> Disk image size を調整します

2022年12月12日月曜日

robe で Date などのビルトインクラスのソースを確認する方法

robe で Date などのビルトインクラスのソースを確認する方法

概要

Date や String のメソッドに関しては pry-doc のリファレンスを参照することができますがクラスそのものにジャンプすることはできません (Can’t find the location になる)

一応たどる方法があるので紹介します

環境

  • macOS 11.7
  • emacs 28.1

方法: pry で show-source を実行し CRuby のソースコードを入手する

emacs で pry バッファに移動して以下のように show-source Date を実行しましょう

[11] pry(main)> show-source Date
Identifier not found - do you want to install CRuby sources to attempt to resolve the identifier there?
This allows the lookup of C internals Y/N Y
Downloading and setting up Ruby 3_1_2 source...
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 14.3M    0 14.3M    0     0  2518k      0 --:--:--  0:00:05 --:--:-- 2429k
Generating tagfile!
...Finished!

これで再度 Date からジャンプすればソースコードを参照できるようになります

最後に

ちなみに Date を継承している DateTime はこの方法でも参照できないようです
おそらく pry-doc で show-source DateTime すると親クラスの Date のソースが確認できるのでそもそも無理なのかもしれません

そもそもビルトイン関係の実装まで確認したいケースは少ないと思いますが一応これで確認できます
面倒な場合は emacs からでなく直接 CRuby のソースをダウンロードして見るのでもいいと思います

2022年12月9日金曜日

emacs + pylsp で補完しない場合の対処方法2

emacs + pylsp で補完しない場合の対処方法2

概要

その1はこちら

環境

  • Ubuntu 18.01
  • emacs 27.1

対処方法

  • helm-lsp

or

  • company

をインストールする
auto-complete だと補完ウィンドウが表示されないので注意

2022年12月8日木曜日

初回生成の Gemfile をテンプレート化する方法

初回生成の Gemfile をテンプレート化する方法

概要

bundle init を実行すると空の Gemfile が作成されます
いつも使うような gem は init 時に記載されていると嬉しいです
今回は Gemfile をテンプレート化する方法を紹介します

環境

  • Ruby 3.1.2

gemspec_template の作成

  • vim ~/.gemspec_template
Gem::Specification.new do |spec|
  spec.add_dependency "pry"
  spec.add_dependency "pry-doc"
  spec.add_dependency "solargraph"
end

gemspec オプションを使う

  • bundle init --gemspec=~/.gemspec_template

動作確認

  • cat Gemfile
source 'https://rubygems.org'
gem "pry", ">= 0"
gem "pry-doc", ">= 0"
gem "solargraph", ">= 0"

参考サイト

2022年12月7日水曜日

emacs Ruby 開発環境まとめ

emacs Ruby 開発環境まとめ

概要

emacs で Ruby を開発する際に使用している拡張などを紹介します

環境

  • macOS 11.7
  • emacs 28.1
  • Ruby 3.1.2

必要な gem

  • pry (bundle 配下)
  • pry-doc (bundle 配下)
  • solargraph (グローバルでOK)
  • rubocop (グローバルでOK)

pry で bundle 配下にインストールされた gem を読み込むスニペットはこちら

基本

  • ruby-mode
  • helm

補完

  • robe
  • auto-complete

補完におけるトラブルシューティングはこちら
robe を再起動する方法はこちら

ジャンプ

  • robe

ハイライト

  • lsp

スコープ外もハイライトするので注意

リネーム

  • lsp

その他

  • rbenv
  • inf-ruby

最後に

本当は lsp と robe は共存させたくないのですが robe だけだと足りない機能があるのと足りない機能を他の el で補えなかったので仕方なく lsp と共存させています

2022年12月6日火曜日

emacs robe 使い方 Tips

emacs robe 使い方 Tips

概要

基本的な設定や使い方はこちらを御覧ください
本記事ではトラブルシューティングや Tips を紹介します

環境

  • macOS 11.7
  • emacs 28.1

too long; didn’t read

  • vim vendor/ruby/3.1.0/bin/pry
require 'bundler'
Bundler.require
['lib', 'controller'].each { |dir| Dir["#{__dir__}/../../../../#{dir}/*.rb"].each {|file| require file } }

Can’t find the location と Method not found 対策

インストールした gem にしろ自分で作成した Ruby ファイルにしろ ruby-load-file で emacs に認識させないと上記のエラーになります

認識させたいファイルを開いて C-c C-l でロードできます

pry バッファで全部ロードする

robe-start すると pry が実行できるインタラクティブバッファが作成されるのでそこで自作の rb ファイル require するのはありです

例えば lib 配下にあるファイルをすべて ruby-load-file するには以下を実行します

Dir["./lib/*.rb"].each {|file| require file }

複数のディレクトリがある場合は以下のような書き方もありです

['lib', 'controller'].each { |dir| Dir["#{__dir__}/../../../../#{dir}/*.rb"].each {|file| require file } }

自動化するには

例えば pry ファイルに上記のロードスクリプトを一行足しても OK です
サンプルは相対パスを使っていますがフルパスでも OK です

  • vim vendor/ruby/3.1.0/bin/pry
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'pry' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'rubygems'
Dir["#{__dir__}/../../../../lib/*.rb"].each {|file| require file }

Gem.use_gemdeps

version = ">= 0.a"

str = ARGV.first
if str
  str = str.b[/\A_(.*)_\z/, 1]
  if str and Gem::Version.correct?(str)
    version = str
    ARGV.shift
  end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('pry', 'pry', version)
else
gem "pry", version
load Gem.bin_path("pry", "pry", version)
end

一部の gem がなぜかロードされていない

例えば slack-incoming-webhooks gem の Slack::Incoming::Webhooks がなぜか参照できません
理由は不明ですがこれも ruby-load-file で vendor ディレクトリ以下からロードすればちゃんとジャンプできるようになります

対策

これも pry でロードすれば OK です
以下 pry バッファで実行しましょう

require 'bundler'
Bundler.require

自動化するには

これも pry ファイルに書いてしまいましょう

  • vim vendor/ruby/3.1.0/bin/pry
#!/usr/bin/env ruby
#
# This file was generated by RubyGems.
#
# The application 'pry' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'bundler'
Bundler.require
Dir["#{__dir__}/../../../../lib/*.rb"].each {|file| require file }

require 'rubygems'

Gem.use_gemdeps

version = ">= 0.a"

str = ARGV.first
if str
  str = str.b[/\A_(.*)_\z/, 1]
  if str and Gem::Version.correct?(str)
    version = str
    ARGV.shift
  end
end

if Gem.respond_to?(:activate_bin_path)
load Gem.activate_bin_path('pry', 'pry', version)
else
gem "pry", version
load Gem.bin_path("pry", "pry", version)
end

Bundler.require は基本的に追記して OK だと思います
自分で作成したスクリプトを require する場合はメインとなるスクリプトを require してしまうと実行されてしまうので注意しましょう

ファイルが追加されたり gem が追加された場合は

pry を再起動したくなります
簡単なのは emacs をまるごと再起動すれば OK です
emacs を起動したまま robe を再起動したい場合は pry バッファを kill して再度 robe-start すれば OK です

複数のプロジェクトを開く場合には

複数のプロジェクトで robe-start してしまうと2つ目に開いたプロジェクトが1つ目の pry バッファを参照してしまいうまく動作しません
2つ目のプロジェクトで robe を起動したい場合は inf-ruby-console-auto を実行しましょう

最後に

面倒ですが robe-start 前に pry ファイルを弄っておくといい感じに補完やジャンプできるようになると思います

2022年12月5日月曜日

(solargraph) progn: Timeout while waiting for response. Method: textDocument definition. 対策

(solargraph) progn: Timeout while waiting for response. Method: textDocument definition. 対策

環境

  • macOS 11.7.1
  • emacs 28.1

原因

ネストしたプロジェクトに対しては動作しない模様

対策

一度プロジェクトを削除して再度プロジェクトを追加します

その際にネストしたプロジェクトの親ディレクトリは指定せず直接ネストしてあるディレクトリをプロジェクトとして追加しましょう

2022年12月2日金曜日

treemacs でファイル検索する方法

treemacs でファイル検索する方法

概要

treemacs の find-file だとその階層にいるファイルしか検索できないので projectile を使います

環境

  • Ubuntu 18.04
  • emacs 27.1

projectile のインストール

  • package-list-packages
  • projectile を探して i -> x

検索

projectile-find-file を使います

プロジェクト上にカーソルがある状態で実行するとそのプロジェクト内のファイルをルートからすべて検索してくれます

キーバインドはないので必要であれば設定しましょう

トラブルシューティング

fatal: detected dubious ownership in repository at '/home/xxx/app'
To add an exception for this directory, call:

        git config --global --add safe.directory /home/xxx/app

emacs を開いているユーザと検索しようとしているプロジェクトの権限が違う場合は上記のようなエラーが出ます

この場合は emacs を開いているユーザで上記のコマンドを実行すれば OK です

2022年12月1日木曜日

GitlabICIでスケジュールの場合だけ実行するジョブを設定する方法

GitlabICIでスケジュールの場合だけ実行するジョブを設定する方法

概要

GitlabCI のスケジュール機能はパイプラインを実行するので基本すべてのジョブが実行されます

スケジュール実行のときに特定のジョブだけ実行する方法を紹介します

バージョン

  • Gitlab-ee 15.4.3
  • Gitlab-runner 15.3.0

サンプル .gitlab-ci.yml

stages:
  - test

test_manual:
  stage: test
  image:
    name: python:3.10.2-buster
  script:
    - python -V
  except:
    - schedules

test_auto:
  stage: test
  image:
    name: python:3.10.2-buster
  script:
    - python -V
  only:
    - schedules

ちょっと解説

except と only を使います

except は除外なのでスケジュールのときは実行しません
only はそのときだけ実行するのでスケジュールのときだけ実行します
こうするとスケジュールのときだけ test_auto が実行されます
それ以外の手動で実行したい場合は test_manual を使います

ちなみに when: manual でスケジュール時の実行を無視できますが when を使った場合は push 時の自動実行もなくなります

スケジュールの登録方法

こちらで紹介しています

参考サイト

2022年11月22日火曜日

ffmpegでmovを圧縮するコマンド

ffmpegでmovを圧縮するコマンド

環境

  • macOS 11.7.1
  • ffmpeg 5.1.1

コマンド

  • ffmpeg -i input.mov -pix_fmt yuv420p output.mp4

2022年11月21日月曜日

Pythonでクラスなしの定数の一覧を参照する方法

Pythonでクラスなしの定数の一覧を参照する方法

概要

クラス配下の定数であれば ClassName.__dict__ などで参照できます
しかしクラス外に直接モジュール配下に定義している定数ではそうはいきません

今回はその方法を紹介します

環境

  • macOS 11.7.1
  • Python 3.10.2

サンプルコード

NAME = "hawk"
AGE = 20


def export():
    import sys
    current_module = sys.modules[__name__]
    return [(k, v) for k, v in current_module.__dict__.items() if k.isupper()]


if __name__ == "__main__":
    for i in export():
        print(i[0])
        print(i[1])

ちょっと解説

ポイントは sys.modules[__name__] です
これで現在のモジュールが取得できます
あとはそのモジュールに対して __dict__ を参照すれば OK です

今回は定数だけ取得するので key の値で先頭が大文字のフィールドだけを返却しています

また tuple で返却しているのでフィールド内に mutable な値 (dict型) が存在しているためで再度 dict などで返却しようとすると TypeError: unhashable type: 'dict' などが発生するのでその対処として tuple にしています