torutkのブログ

ソフトウェア・エンジニアのブログ

Java読書会「セキュア・バイ・デザイン」を読む会(第4回)開催

10月15日(土)、Java読書会BOF主催の「セキュア・バイ・デザイン」を読む会(第4回)を開催しました。

本日は、ドメイン・プリミティブの残り、Read-Onceオブジェクト(一度しか読み込めないオブジェクト)、エンティティの触りの部分(5章の途中から6章の途中)を読み進めました。

読書メモ
  • 機密性の高いデータの流出を低減する設計として、Read-Onceオブジェクトと呼ぶ設計パターンがある

    • 機密性の高い値または概念を表現する
    • 対象データが意図しない使われ方をしたことを検出しやすくする
    • ドメイン・プリミティブの一種とすることが多い
    • 値は1度しか読み込めない、シリアライズさせない、サブクラスを作成できない
      • 値を読み出すと、その値を消すことで2度目は読めないようにする
  • エンティティが、intやStringなどのプリミティブな(値の制約がない)データで構成されていると、エンティティ本来の処理(ビジネスロジック)に加えて、データの妥当性確認、不変条件確認などをする必要が増え複雑になってしまう

  • ドメイン・プリミティブの再確認(前回の範囲の復習)

    • p.139 「ソフトウェアの設計に事前条件と契約による設計(design by contract)を施すことで、ソフトウェアを効果的により安全にすることができる」
      • 契約による設計がサポートされていない言語では開発者自身がコード上に実装しなければならない
      • publicなメソッドで引数の事前条件確認
      • コンストラクタで不変条件確認
    • p.151 「システムの安全性を保つためには、データの妥当性を確認することが重要」
      • 妥当性確認の分類・順序として、オリジン(発生源)、サイズ、字句的内容(lexical content)、構文(syntax)、意味(semantics)がある。ドメイン・プリミティブでは、意味を除く他の妥当性の確認を含める(意味はエンティティで確認)
    • ドメイン駆動で紹介された値オブジェクト(value object)の重要な特徴である不変(immutable)と完結した概念(conceptual whole)に加えて、オブジェクト生成時に不変条件の確認を導入することで、生成されたオブジェクトが常に不変条件を維持する
  • ドメイン・プリミティブを適用するのに反対の声も大きい

    • 書くのが手間?
    • クラスが増え過ぎて理解容易性が下がる
  • ドメイン・プリミティブを適用しない場合、エンティティでデータの妥当性確認、不変条件の確認、機密性の高いデータのRead-Onceを実装する。

    • 複数のエンティティで同じ妥当性確認、不変条件確認、Read-Onceを実装する場合、DRY原則が崩れるのでドメイン・プリミティブを導入σルト良い
    • 単独のエンティティでしか実装しないとしても、これによりエンティティが複雑化(肥大化)するなら導入する価値がある
    • 妥当性確認・不変条件確認を共通関数ライブラリで外出しするのはいけていない。データとロジックが分離し、データを扱うときにそのロジックが適用されるのを忘れる
  • エンティティの生成

    • 引数なしコンストラクタは危険
      • O/Rマッパーの制約で引数なしコンストラクタが必須(ではない、回避方法あり)
      • O/Rマッパーは永続層に隔離し、ドメイン・オブジェクトは別途生成
      • privateコンストラクタでアノテーション付与すればO/Rマッパー適用可能、など
      • ビルダーパターンで内部クラスに実装
Javaの今後で検討されている言語機能の拡張

Value objectを言語機能に取り入れる JEPが検討中です。

JEP draft: Value Objects (Preview)

value classは、インスタンスの一意識別を持たず、デフォルトでfinal class、全てのフィールドがfinalとなり、==で全てのフィールドの値が一致するかを判定、synchronizedを持てないという仕様のようです。

セキュア・バイ・デザインで設計パターンとして紹介しているドメイン・プリミティブの実装に役立つ機能です。

次に、プログラマーが定義できるプリミティブ型を言語機能に取り入れるJEPが提案されています。

JEP 401: Primitive Classes (Preview)

これまでJava言語仕様に欠けていた、符号なし整数や、128bit整数、座標、複素数、単位を持つ数値、タプルなどをユーザー定義型で導入できるようになるようです。int型などのプリミティブ型同様nullを保持できない。 オブジェクトヘッダーを持たず、メモリ上に値が直接並ぶような構造になる模様。 これも、ドメイン・プリミティブの実装に役立つ機能となると思います。

ExcelからRedmineへのアクセス

はじめに

Redmineで複数のプロジェクトを跨ってチケットを一覧し、状況の整理や自分の作業メモを記載したく、Excel上からRedmineにアクセスして情報を取ってくる方法を探してみました。

ExcelRedmine情報をもってくる方法の調査

Redmineからチケット情報をCSVエクスポートしてもってくる方法は、Redmineのプロジェクト複数をまたがったチケットを収集するのにかなり手間がかかる(プロジェクトごとにチケット一覧を絞り込んでCSVエクスポートし、それをマージする)ほか、Excel側でメモなどを追記した後にチケットの状態(担当者、ステータスなど)が変化したときにそれをExcel側に反映するのが面倒です。

次に、REST APIExcel上から呼び出す方法を探しました。Excel VBAでMSXML2.DOMDocumentを使う方法は、Windows限定となってしまい、macOSでは動作しないのでこの方法は使えませんでした。REST APIの処理(応答のXMLをべたにVBAで解析する処理を記述)は、やりたくないなぁ。

ということで、PythonからRedmineExcelにアクセスして両者をつなぐ方法を使うことにしました。

調査の中で、RedmineExcelを結ぶ方法を一覧整理し紹介しているブログ記事を見つけましたので次にメモしておきます。この記事では、CSVエクスポート、REST APIだけでなく、Power QueryやAccessなどを使う方法(Redmineのバックエンドで動くRDBMSへのアクセスを含む)など幅広く紹介されています。

a1-style.net

Pythonからのアクセスライブラリ

PythonからRedmine

PythonからRedmineへのアクセスは、Python-Redmineライブラリを使いました。

Python-Redmine — Python-Redmine documentation 

REST APIを使ってPythonコード上でRedmineから情報を取得すること、Redmineの更新をすることが可能です。

次のWikiに極簡単なさわり部分(Redmineに接続してID指定でチケット情報を取得するだけの極簡単な)のコードを記述しました。

Python-Redmine - ソフトウェアエンジニアリング - Torutk

PythonからExcel

PythonからExcelへのアクセスは、openpyxlライブラリを使いました。PythonからExcelにアクセスするライブラリはざっと調べた限りでも4、5種類存在します。その中で、Excelファイル(.xlsx)の読み書きができ、シートのセルの読み書きが簡単に記述できるopenpyxlを使ってみました。

次のWikiPythonからExcelにアクセスするライブラリの一覧をメモしました。

Python-Excel - ソフトウェアエンジニアリング - Torutk

そのうち、コード例などを追記する予定です。

Java読書会「セキュア・バイ・デザイン」を読む会(第3回)開催

本日、Java読書会BOF主催の「セキュア・バイ・デザイン」を読む会(第3回)を開催しました。

本日は、第4章安全性を確立する実装テクニックの4.1不変性(immutability)から読み始めました。

読書メモ
  • クラスが可変となる設計をすると、排他制御が必要で完全性(integrity)が損なわれるので、不変な設計をしましょう

    • 不変性の実装は、JDK 17以降は record が使えるのでは?
  • 契約による設計で、速やかな失敗を

    • ホーアさん、凄い人
    • assertはあるけどあまり使われていないね。組み込み系ではassert使うけど(言語はJavaではないけど)
    • Androidでは、アノテーションで引数に@NonNullなど指定することが多い。
      • これはコンパイル時にチェックするので実行時にはチェックされないのでは?
  • 受け取った不正なデータをそのままログに出さない

    • なぜ? 9章によると、ログを読み込む別システム(ログ管理システム)が不正データを含むログを読み込みXSSになると
    • 開発観点ではログに不正データがないと問題切り分けが困難になるが安全性の観点が優先されるべき(この本の趣旨)
  • 妥当性の確認リスト

    • オリジン(発生源)の確認
    • データ・サイズの確認
    • 字句的内容(lexical content)の確認
    • 構文(syntax)の確認
    • データに対する意味(semantics)の確認
  • データサイズの確認は、10億桁の文字列を受け取って次の確認(正規表現エンジン等)に読み込ませるといったことを防ぐ意味がある

  • データに対する意味の確認は、この本ではドメイン・モデルで行うのが適していると著者は言っている

  • ドメイン・プリミティブはドメイン駆動設計で紹介された値オブジェクトをセキュリティを意識しながら改良したもので、セキュリティ問題の可能性をコードから取り除く

    • 不変条件を持ち、オブジェクト生成時に不変条件を確認
    • その存在だけでその値が有効であることを保証する厳格な定義
    • 操作メソッドをユーティリティクラスに出さない(振る舞いを変えた時にユーティリティクラスの操作を変更忘れる問題を防ぐため)
    • 冊数をintではなくQuantityクラス(ドメイン・プリミティブ)で正確かつ厳格に表現(冊数の有効範囲:例えば1冊以上200冊以下、を保証)
    • Quantityクラスに intを返すvalue()メソッドがあるが、これを出してしまうとドメインプリミティブが不完全にならないか?
      • Quantityの外部で、複数のQuantityインスタンスからvalue()でintで受け取った値同士を演算すると不変条件が破られていまう
  • メールアドレスの大文字・小文字で盛り上がる(書籍から脱線)

今日は、脱線した議論が多く、盛り上がりましたが読み進んだページ数は平均を下回っていました。

ドメイン・プリミティブの実装をrecordで再実装してみる

この本は、原書が2021年に出版されていますが、Javaのコード例はJava SE 8の範囲で書かれています。 Javaでは、Java SE 17においてrecordが正式導入されたので、値オブジェクトの実装が少ない記述量で実装できるようになっています。そこで、書籍のドメイン・プリミティブの例をrecordで再実装してみます。

Quantityクラスの例(リスト5.1よりコード抜粋、コメントは私が補足)

public final class Quantity {
    private final int value;

    public Quantity(final int value) {
        inclusiveBetween(1, 200, value);  // commons.lang の Validateクラスのstaticメソッドで範囲チェック
        this.value = value;
    }

    public int value() {
        return value;
    }
    
    pubilc Quantity add(final Quantity addend) {
        notNull(addend);  // commons.lang の Validateクラスのstaticメソッドでnullチェック
        return new Quantity(value + addend.value);
    }
    // ...略(equalsメソッド、hashCodeメソッド、toStringメソッドが略されていると思われる)
}

hashcodeやequalsメソッドは「...略」のところに現れるものと想定しておきます。 このクラスをrecord型で記述すると

public record Quantity(int value) {
    public Quantity {
        inclusiveBetween(1, 200, value)
    }

    public Quantity add(final Quantity addend) {
        notNull(addend);
        return new Quantity(value + addend.value);
    }
}

とシンプルに記述でき、フィールドの定義、コンストラクタの定義のうちフィールドへの代入、フィールドのgetterメソッド、equalsメソッド、hashCodeメソッド、toStringメソッドが自動で定義されます。

JDK 19リリースがもうすぐ

読書会開催時の雑談で、もうすぐ JDK 19 がリリースされると話題に上がりました。 JDK 19はどんな機能が入るのだろう?ということでリリースノートを見ると次のような機能が入ります。

JEPでは

JEP以外の更新では(抜粋)

  • System.outとSystem.err用のプロパティ stdout.encoding、stderr.encoding
  • Unicode 14.0
  • 追加のDate-Time Formats。DateTimeFormatter.ofLocalizedPattern("yMMM")などが記述可能

今月9月30日の夜に、JJUG主催ナイトセミナーでJDK 19の紹介があります。

jjug.doorkeeper.jp

Python tkinter GUIとmacOS 12 ダークモード

先日、macOS を 12(Monterey)にバージョンアップし、外観モードをダークに設定しました。

その後、PythonGUIライブラリ tkinterを使ってみたところ、ラベルやボタンが正常に表示されなくなる現象が発生しました。 次の画面は、tkinterで、ラベルとボタンを貼り付けた画面ですが、ラベルはテキストが表示されず、ボタンは背景と文字色がほぼ同色で、しかもチラチラとチラつきが生じていました。

調べてみたところ

  • tkinterはTcl/Tkのラッパーであり、Tclのバージョン 8.6でmacOSのダークモードに対応した
  • macOS 12では、pythonが標準ではインストールされ無くなっており、オプションでコマンドライン開発者ツールをインストールすることでpython 3.8がインストールされる
  • このpython 3.8でインストールされるtkinterは、Tcl 8.5であり、macOSのダークモードに対応できない

ということでした。

解決策

  • HomeBrewで、pythonpython-tkをインストールし、これを使用する

HomeBrewでpython(3.10)とpython-tk(3.10)をインストールすると、macOSダークモード対応のTclが含まれるので今回の問題の解決となります。

Java読書会BOF「セキュア・バイ・デザイン」を読む会(第1回)

本日より、Java読書会BOF主催「セキュア・バイ・デザイン」を読む会開始

Java読書会BOF では、本日より新しい課題図書「セキュア・バイ・デザイン

の読書会を開始しました。

Java読書会BOF開催実績

通算回数:279回目 書籍数:41冊目

本日の読書会メモ

本読書会の恒例で、表紙、表紙折り返し、巻末の著者紹介、まえがきから朗読&議論の形式で開始しました。

本日は、第1章 なぜ、設計がセキュリティにおいて重要なのか? と第2章 ちょっと休憩:「ハムレット」の悲劇 の途中までを読み進めました。

参加人数は9名でした。

第1章メモ

常にセキュリティ対策を意識したコードを書いていくアプローチではなく、安全なソフトウェアにつながる設計をするアプローチが良いという導入から始まりました。 ここで、設計は、設計書や図だけではなく、コードまで含めた範囲を設計としています。 Stringクラスで氏名を表現するのではなく、UserNameクラスを設計し、UserNameに許される文字列を制約できるようにする例を紹介しています。 多層セキュリティの実現の簡単な紹介をしています。

第2章メモ

オンライン書店で、セキュリティチームが冊数に-1を入れたところ、注文が確定され、売掛金元帳システムから返金が発生したという仮想のストーリーで始まりました。 在庫システムと売掛金元帳システムはマイナスの冊数が入ってくると、整合して状態を変化するので財務報告書に食い違いが生じず、年末の棚卸作業でも傷による破棄や紛失による差異があるので気づかない。さらに、本を購入する際に−1冊を紛れ込ませると値引き状態(支払いは発生するが本来の注文額より少なく済む)となる裏技が利用者に噂として広まり悪用されたというなかなか真に迫ったストーリーです。

この仮想ストーリーの問題を、この本ではモデリングが原因(浅いモデリング)としています。 冊数を表現するモデルとして、int型を短絡的に使ってしまう(間違ったモデリング)という問題提起をしています。

お昼ご飯

Java読書会BOFでは、お昼は参加者皆そろって近所のお昼処に行きます。混まないうちに入れるよう、11:45に午前の部を終えて向かいます。今回は、会場から徒歩6分のレストランユニオンでお昼を取りました。ここは、メニューが豊富でボリュームがあり、お値段も800円前後位です。

有志二次会

読書会終了後、近所のタップビールのお店で軽い2次会を実施しました。

Visual Studio CodeのJava Extensionsでmodule対応を確認

Visual Studio CodeJava Extensionsでmodule対応の確認

はじめに

Visual Studio Codeは、クロスプラットフォームな開発環境で、数多くのプログラミング言語やファイル形式に対応した高機能エディタ+軽量IDE統合開発環境)です。

特定のプログラミング言語/環境だけを扱うのであれば、その言語/環境に特化した高機能なIDEを使うと作業効率が高いでしょう(例:Windows OS上で、C#だけ扱う場合は、Visual Studioを使う)。

しかし、普段いろいろなプログラミング言語およびファイル形式(CSS、HTML、XMLMarkdown、reStructuredText)を扱うときは、幅の広いVisual Studio Codeが便利です。

今回、Visual Studio CodeJava Extensionsをインストールし、Javaプログラミングを行うときに、No build toolsプロジェクトでJava Platform Module System(JPMS)対応が可能かを確かめてみました。

Visual Studio CodeJava Extensionsの準備

Visual Studio Codeに、Java Extensionsを追加する方法は次に書いています。

www.torutk.com

Javaプロジェクトの作成

Visual Studio CodeJava Extensionsを追加すると、Javaのプロジェクトを作成し、ビルド・デバッグ・テスト・実行ができるようになります。

Java Extensionsで用意されるJavaのプロジェクトには、主に次の種類があります。

これ以外にも、特定のフレームワーク用のプロジェクト(Spring Boot、Quarkus、MicroProfile)もありますが、ここでは汎用のアプリケーションを作るプロジェクトを対象にします。

No Build Tools プロジェクトの作成

Visual Stuido Codeで、[View]メニュー > [Command Palette]をクリックし、[Java: Create Java Project...]を選択、[No build tools]を選択し、プロジェクトの基点となるディレクトリを置く親ディレクトリを指定します。次にプロジェクト名を入力、すると、プロジェクトの基点に指定したディレクトリの下に、プロジェクト名のディレクトリが生成され、中に雛形のディレクトリ・ファイルが生成されます。

C:\Users\torutk\Documents\work\
  +-- HelloLogging
    +-- .vscode
    |      +-- settings.json
    +-- bin
    +-- lib
    +-- src
        +-- App.java

このプログラムをビルドし実行してみます。[Run]メニューから[Run Without Debugging]を実行、またはApp.javaを開いたタブで右上の[▷]アイコンをクリックすると、プロジェクトが実行できます。

PS C:\Users\torutk\Documents\work\HelloLogging> c:; cd 'C:\Users\torutk\Documents\work\HelloLogging'; & 'C:\Program Files\Java\liberica-jdk-17.0.3-full\bin\java.exe' '-XX:+ShowCodeDetailsInExceptionMessages' '-cp' C:\Users\torutk\Documents\work\HelloLogging\bin' 'App'
Hello, World!
PS C:\Users\torutk\Documents\work\HelloLogging> 
No build toolsプロジェクトでJava Platform Module Systemを扱う

Java SE 9で追加されたJava Platform Module System(JPMS)をVisual Studio CodeJava Extensionsで扱えるかどうか確認してみます。

Googleで検索したところでは、No build toolsプロジェクトで、JPMSを扱えるという記述はみつかりませんでした。

では試してみましょう。

パッケージのフォルダを生成し、App.javaをパッケージフォルダに移動

Visual Studio Codeの左ペインでsrcディレクトリを右クリック、[New Folder]を選択、 @com\torutk\hello@と入力します。生成したフォルダに、App.javaをドラッグ&ドロップします。

Moveを選択すると、ファイル移動に伴いJavaコードのリファクタリングを行うか聞いてくるので、[OK]をクリックします。

すると、App.java に移動先フォルダ階層に適合するpackage文が生成されます。

package com.torutk.hello;
public class App {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello, World!");
    }
}
実行

プロジェクトを実行すると、srcフォルダのソースコードコンパイルされbinフォルダの下に展開されます。次に、これをクラスパスに指定し、プログラムが実行されます。

PS C:\Users\torutk\Documents\work\HelloLogging> c:; cd 'C:\Users\torutk\Documents\work\HelloLogging';   & 'C:\Program Files\Java\liberica-jdk-17.0.3-full\bin\java.exe' '-XX:+ShowCodeDetailsInExceptionMessages' '-cp' C:\Users\torutk\Documents\work\HelloLogging\bin' 'com.torutk.hello.App'
Hello, World!
PS C:\Users\torutk\Documents\work\HelloLogging> 
System.outをLoggingに書き換え

JPMSを確認するため、System.outをLoggingに書き換え、JPMSでjava.loggingを指定する設定をおこないます。まずは、System.out を Logger.getGlobal に書き換えます。

package com.torutk.hello;

import java.util.logging.Logger;

public class App {
    public static void main(String[] args) throws Exception {
        Logger.getGlobal().info("Hello, World!");
    }
}

ここで、まだJPMSの設定を追加する前に実行してみます。

PS C:\Users\torutk\Documents\work\HelloLogging> c:; cd 'C:\Users\torutk\Documents\work\HelloLogging';  & 'C:\Program Files\Java\liberica-jdk-17.0.3-full\bin\java.exe' '-XX:+ShowCodeDetailsInExceptionMessages' '-cp' C:\Users\torutk\Documents\work\HelloLogging\bin' 'com.torutk.hello.App'
7月 17, 2022 11:19:11 午後 com.torutk.hello.App main
情報: Hello, World!
PS C:\Users\torutk\Documents\work\HelloLogging> 
module-info.javaを追加

srcフォルダの直下に、module-info.javaを作成します。

module com.torutk.hello {
    requires java.logging;
}
C:\Users\torutk\Documents\work\
  +-- HelloLogging
    +-- .vscode
    |      +-- settings.json
    +-- bin
    +-- lib
    +-- src
         +-- module-info.java
         +-- com
               +-- torutk
                     +-- hello
                           +-- App.java
実行
PS C:\Users\torutk\Documents\work\HelloLogging> c:; cd 'c:\Users\torutk\Documents\work\HelloLogging'; & 'C:\Program Files\Java\liberica-jdk-17.0.3-full\bin\java.exe' '-XX:+ShowCodeDetailsInExceptionMessages' '--module-path' 'C:\Users\torutk\Documents\work\HelloLogging\bin' '-m' 'com.torutk.hello/com.torutk.hello.App' 
7月 17, 2022 11:26:54 午後 com.torutk.hello.App main
情報: Hello, World!
PS C:\Users\torutk\Documents\work\HelloLogging>

ソースパスに、module-info.javaを追加すると、JPMS対応と認識し、実行時のオプションが変化します。

  • --module-path JPMS対応のモジュールが格納されている場所を指定
  • -m 実行するJPMS対応のモジュールを指定
まとめ

Visual Studio CodeJava Extensionsで、No build toolsプロジェクトを生成し、module-info.javaをsrcフォルダ直下に配置したところ、module-info.javaの制約のチェック(requiresしないモジュールのクラスを参照するとエラーとなる)、実行時のモジュールパス、実行するモジュールとクラスの指定が行われます。

Redmine 5.0.0がリリースされていました

Redmine 5.0.0リリース

先月末の3月28日に、Redmine 5.0.0がリリースされていたことを知りました。

Redmine 5.0.0 リリース | Redmine.JP Blog

今回のメジャーバージョンアップでは、Rails 5.2から6.1への移行がなされ、合わせて Zeitwerkというオートロードに対応したとのことです。従来のclassicオートローダーはRails 6では非推奨で、Rails 7で廃止となります。この変更で、一部のプラグインが動作しなくなりプラグイン側で修正が必要となります。

その他、多数新機能が追加されています。

Redmine 4.2から5.0へのアップデート

Rocky Linux 8.5上で稼働しているRedmine 4.2(Unicorn + Nginx)を、Redmine 5.0にアップデートします。およその手順は次です。

Redmine 5.0 を github からクローン
~$ cd /var/lib
lib$ sudo git clone -b 5.0-stable https://github.com/redmine/redmine.git redmine-5.0-stable
lib$ sudo chown -R redmine:redmine redmine-5.0-stable/
Redmine 4.2のディレクトリから設定ファイルをRedmine 5.0のディレクトリへコピー
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/config/configuration.yml config
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/config/database.yml config
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/config/unicorn.rb config
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/config/additional_environment.rb config
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/Gemfile.local .
redmine-5.0-stable$ cp -p ../redmine-4.2-stable/config.ru .
redmine-5.0-stable$ 
Redmine 4.2 のディレクトリ下からプラグインRedmine 5.0のディレクトリへコピー

まず、Redmine 4.2のディレクトリ下からプラグインをコピーします。

lib$ cd redmine-5.0-stable
redmine-5.0-stable$ cp -pr ../redmine-4.2-stable/plugins/* plugins/
redmine-5.0-stable$ ls plugins/
README                    redmine_glossary         redmine_wiki_lists
google_analytics_plugin   redmine_issue_templates  redmine_xls_export
redmine_banner            redmine_latex_mathjax    sidebar_hide
redmine_cozy_wiki_macros  redmine_theme_changer    view_customize
redmine_github_hook       redmine_wiki_extensions
redmine-5.0-stable$ 

プラグインディレクトリで git pull 等を実行して最新にします。

また、各プラグインRedmine 5.0に対応しているかどうか配布元サイト等を確認しておきます。 (2022年4月24日調べ)

No. Plugin name Redmine 5.0対応 zeitwerk:check結果 最新版状況 備考
1 google_analytics_plugin 未記載 最新版v1.0.1は2020年
2 redmine_banner 未記載 最新版v0.3.4は2020年7月 agileware-jpフォーク版が対応
3 redmine_cozy_wiki_macros 未記載 最新版v0.3.1は2019年12月
4 redmine_github_hook 未記載 最新版v3.0.1は2019年7月
5 redmine_glossary 未記載 最新版v1.1.0は2020年1月
6 redmine_issue_template 未記載 最新版v1.1.0は2020年8月 agileware-jpフォーク版が対応
7 redmine_latex_jathjax 未記載 最新版は2020年4月
8 redmine_theme_changer 対応済み 対応版 v0.5.0
9 redmine_wiki_extensions 対応済み 対応版 v0.9.3
10 redmine_wiki_lists 未記載 最新版は2021年4月
11 redmine_xls_export 未記載 最新版v0.2.1.t11は2018年12月
12 sidebar_hide 未記載 最新版v0.0.8は2017年1月
13 view_customize 未記載 最新版v3.0.1は2022年1月
Redmine 4.2のディレクトリからテーマファイルをRedmine 5.0のディレクトリへコピー
redmine-5.0-stable$ ls public/themes/
README  alternate  classic
redmine-5.0-stable$ rsync -av --exclude alternate --exclude classic ../redmine-4.2-stable/public/themes/ public/themes/
redmine-5.0-stable$ 

各テーマ(alternateとclassicを除く)のディレクトリで git pull 等を実行して最新にします。

Redmine 5.0のディレクトリで必要なgemsをインストール
redmine-5.0-stable$ bundle install --path vendor/bundler --without development test
    :
Bundle complete! 50 Gemfile dependencies, 85 gems now installed.
    :
Redmine 5.0 のディレクトリで、5.0に非対応なプラグインをチェック

zeitwerk オートロードに対応しているかどうかをチェックします。エラーが発生するとそこでとまるようなので、非対応なプラグインを検知したらそのプラグインディレクトリを削除して、という作業を繰り返します。

redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
rake aborted!
LoadError: cannot load such file -- google_analytics_hooks
  :
redmine-5.0-stable$ rm -rf plugins/google_analytics_plugin/
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
rake aborted!
LoadError: cannot load such file -- banners/application_hooks
  :
redmine-5.0-stable$ rm -rf plugins/redmine_banner/
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
rake aborted!
LoadError: cannot load such file -- issue_templates/issues_hook
  :
redmine-5.0-stable$ rm -rf plugins/redmine_issue_templates
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
rake aborted!
LoadError: cannot load such file -- xlse_asset_helpers
  :
redmine-5.0-stable$ rm -rf plugins/redmine_xls_export/
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
rake aborted!
LoadError: cannot load such file -- sidebar_hook_listener
  :
redmine-5.0-stable$ rm -rf plugins/sidebar_hide/
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
Hold on, I am eager loading the application.
rake aborted!
NameError: uninitialized constant RedmineGithubHook::Version
Did you mean?  RedmineGithubHook::VERSION
  :
redmine-5.0-stable$ rm -rf plugins/redmine_github_hook/
redmine-5.0-stable$ bundle exec rake zeitwerk:check RAILS_ENV=production
Hold on, I am eager loading the application.
All is good!

この段階で、けっこう厳しい状況が分かりました。 banner、issue_template、github_hook、は使いたいので、もうちょっとRedmine 5.0対応は待った方がよさそうです。

ということで、次の作業は中断とし、しばらく様子見をすることにしました。

bannerとissue_templateは、フォーク版で対応していることが分かりました。以降に処置を記述しました。 github_hookは作者がRedmineを使わなくなったので後継者を募集中です。そのため処置が遅れそうです。また、フォーク版をざっと探しましたが対応しているものが見つからずでした。

非対応なプラグインの処置
google_analytics_plugin

Redmine 4.2のディレクトリでアンインストールします。

~$ cd /var/lib/redmine-4.2-stable
redmine-4.2-stable$ bundle exec rake redmine:plugins:migrate NAME=google_analytics_plugin VERSION=0 RAILS_ENV=production
redmine_banner

フォーク版が対応しているので、フォーク版をclone

redmine-5.0-stable$ cd plugins
plugins$ git clone https://github.com/agileware-jp/redmine_banner.git
  :

データベースの変更はないので、ディレクトリを削除しcloneすればOKのはず。

redmine_issute_template

フォーク版が対応しているので、フォーク版をclone

plugins$ git clone https://github.com/agileware-jp/redmine_issue_templates.git
  :
redmine_github_hook

Redmine 4.2のディレクトリでアンインストールします。

~$ cd /var/lib/redmine-4.2-stable
redmine-4.2-stable$ bundle exec rake redmine:plugins:migrate NAME=redmine_github_hook VERSION=0 RAILS_ENV=production
redmine_xls_export

Redmine 4.2のディレクトリでアンインストールします。

~$ cd /var/lib/redmine-4.2-stable
redmine-4.2-stable$ bundle exec rake redmine:plugins:migrate NAME=redmine_xls_export VERSION=0 RAILS_ENV=production
sidebar_hide

Redmine 4.2のディレクトリでアンインストールします。

~$ cd /var/lib/redmine-4.2-stable
redmine-4.2-stable$ bundle exec rake redmine:plugins:migrate NAME=sidebar_hide VERSION=0 RAILS_ENV=production
Redmine 5.0のディレクトリでマイグレート実行
Redmine 本体のデータベースマイグレーション
redmine-5.0-stable$ bundle exec rails db:migrate RAILS_ENV=production
== 20210704125704 AddTwofaRequiredToGroups: migrating =========================
-- add_column(:users, :twofa_required, :boolean, {:default=>false})
   -> 0.0113s
  :
== 20220224194639 DeleteOrphanedTimeEntryActivities: migrating ================
== 20220224194639 DeleteOrphanedTimeEntryActivities: migrated (0.0382s) ========
redmine-5.0-stable$ 
プラグインマイグレーション
redmine-5.0-stable$ bundle exec rails redmine:plugins:migrate RAILS_ENV=production
redmine-5.0-stable$ 

特に更新なし

Redmine 4.2のディレクトリから添付ファイルをRedmine 5.0のディレクトリへコピー
redmine-5.0-stable$ rsync -a ../redmine-4.2-stable/files/ files
redmine-5.0-stable$ 
セッション鍵の生成
redmine-5.0$ bundle exec rake generate_secret_token
Unicorn 設定ファイルの変更
/usr/lib/systemd/system/redmine-unicorn.service
- WorkingDirectory=/var/lib/redmine-4.2-stable
+ WorkingDirectory=/var/lib/redmine-5.0-stable

- PIDFile=/var/lib/redmine-4.2-stable/tmp/pids/unicorn.pid
+ PIDFile=/var/lib/redmine-5.0-stable/tmp/pids/unicorn.pid
config/routes.rb
Rails.application.routes.draw do
-   root :to => 'welcome#index', :as => 'home'
+  root :to => 'wiki#show', :project_id => 'swe', :as => 'home'

Unicorn を再起動

~$ sudo systemctl restart redmine-unicorn.service