アカベコマイリ

HEAR NOTHING SEE NOTHING SAY NOTHING

あなたも作れる!Redmine テーマ - 追補編

December 14, 2019イベントRedmine, Theme

本記事は Redmine Advent Calendar 2019 の 14 日目です。redmine.tokyo 17 登壇で発表した「あなたも作れる!Redmine テーマ」を掘り下げる追補編となります。めちゃくちゃ長いです。年末の夜長にでもお読みください。

環境構築

テーマ開発にあたり、開発環境をすべて自前で構築するのは厳しいのでスターターを用意した。

しかしこれを動かすための環境を理解するのは登壇とスライドだけでは厳しそう。というわけで、より詳細に説明してゆく。

ターミナル

スターターに関する操作は CLI (Command Line Interface) となる。CLI 操作するツールは様々な呼ばれ方をするが、ここではターミナルに統一。俗称として「黒い画面」とか言われたりもする。

OS ターミナル 説明
Windows コマンド プロンプト OS 標準付属ツール。後継の PowerShell や Windows Terminal が利用できるなら、操作性 (コピペなど) の観点からそちらのほうがよい。
PowerShell OS 標準付属ツール & 言語。2019/12 時点で OS 標準付属にこだわるならオススメ。
Windows Terminal (Preview) 現在の Windows には標準付属しないが、Microsoft がコマンド プロンプトや PowerShell (CLI ツール) を踏まえてモダーンなターミナルを提供するため開発しているもの。
その他 参考 Windowsで使えるターミナルとシェルのまとめ - Qiita
macOS Terminal.app OS 標準付属ツール。割と多機能なのでこだわりがなければこれで。
iTerm2 定番のサードパーティー製ツール。私はこれを利用している。redmine.tokyo 17 登壇時のデモで見せたのもこれ。
Linux ディストリビューションに応じてお好みで。

CLI について難しそうと感じるかもしれない。しかしスターターとしては README に書いてあるとおりコマンドをコピペして実行するだけで済む。実際に操作してみれば、拍子抜けするぐらい簡単だったりする。

この「コピペできる」というのが CLI のよいところ。説明もしやすく、操作そのものがテキストだからコピペしたり自分の環境に書き換えるといったことも可能。

これが GUI だと文章で説明するのは難しく、画像や動画を駆使してもユーザー環境ごとの外観差やマウス操作のミス (特にドラッグ アンド ドロップは事故りやすい) などによりサポート負担が大きい。

もし CLI を苦手とするなら、ぜひスターターを入門のきっかけにしてほしい。いま流行りの RPA 的な業務の自動化なども CLI の知見が役立つはず。ちょっとしたツールを作るとしても GUI (= 絵がある) は面倒だが、CLI ならずっと簡単だ。

ターミナルと CLI 説明に関する余談。これらについて解説された技術記事で

$ コマンド
# コマンド

のように先頭へ $# などが記載されているを不思議に思ったことがあるかもしれない。これはプロンプトであり、その行がコマンド入力を受け付けている (いた) ことを表す。$ が一般ユーザー、# は管理者であることが多い。$% だったり > なのもたまに見かける。

実際にコマンドを実行する際は、これらの記号を入力しなくてよい。それにも関わらずなぜ記載するのかというと、私の場合は CLI の入力と結果を区別しやすくするためである。例えば

$ コマンド 1
結果出力 1
結果出力 2

$ コマンド 2
結果出力 1
結果出力 2

のようになっていれば、複数のコマンドとそれぞれの結果出力を視認しやすい。本記事でも CLI 解説ではこの表記でゆく。この書き方に名前があるのかは知らないが、これまで私の読んできた技術書やブログなどでもよく目にする方法で、簡潔かつ分かりやすいため採用している。

Node.js

スターターにおける CSS 生成やテーマをリリースするための処理は Node.js を通して実行される。インストールは何通りかあるので、好みの方法を選ぼう。以下にそれぞれの方法についてまとめる。

方法 難易度 説明
公式インストーラー Node.js に公開されているもの。他の方法に比べてインストールが簡単。スターター的にバージョンは LTS と最新版のどちらでも OK。特にこだわりがなければ最新版で。
パッケージ管理 自分の利用している OS にあわせて、お好みで。私は macOS で Homebrew を利用している。他には aptdfnyumChocolatey など。
nodenv 複数の Node.js バージョンを管理するツール。主にプログラマー向け。

Node.js はプログラミング入門としてもオススメ。こうしたスクリプト言語は他にも Perl、Python、Ruby など数多いが、Node.js は JavaScript で書ける点がよい。

JavaScript は Web ブラウザー向け標準言語なので、そちらの知見も活かせる。また言語としても ES2015 以降は相当にモダーンとなった。

ここ 5 年ぐらいの私は、ちょっとした自動化ツールなどは Node.js 向けに書いている。ファイル操作と通信があるだけでも、大抵の用途には十分すぎるぐらいだ。

Docker + compose

Docker はコンテナと呼ばれる単位の仮想環境 (小さい OS) を動かすためのソフトウェア。

仮想環境といえば VMware とか VirtualBox、これらの管理と操作を便利にしてくれる Vagrant なんかが有名だ。Redmine 界隈だと Redmine view customize plugin で有名な onozaty さんが Vagrant 向けに Redmine 用の仮想環境を作っている。

私も以前はこれを利用していた。ではなぜ Docker に移行したのか。それは設計的な違いから Docker のほうが仮想環境のサイズが小さく、起動も高速だから。もう一つ Redmine テーマを開発していて CSS 更新を反映させる際に、

  • Vagrant + VirtualBox は Web ブラウザーのリロードだけではダメで、Redmine や仮想環境の再起動が必要
  • Docker は Web ブラウザーのリロードだけで OK

という開発体験の改善もあった。これは私の環境 (macOS) に依存する問題かもしれないが、原因不明で対策方法も思いつかない。そんな折にサイズと起動速度の面から Docker を試したらこの問題が解決したので、移行することにした。

仮想環境について。

Redmine を動作させるためには本体だけでなく、Ruby、Rails、HTTP サーバーと DBMS など様々なミドルウェアが必要になる。こうしたものを自前で揃えたり、動作させるサーバーを用意するのは大変だ。自分の PC へ入れるにしても、ミドルウェアは環境への影響が大きいため問題が起きたときが怖い。

そこで仮想環境の出番がくる。これを動かすためのソフトウェアは必要となるが、その中のものを気にしなくて済むメリットのほうが大きい。また Docker や Vagrant は他人が作った仮想環境を再利用できる点も便利。特にこだわりがなければ、誰かの手による鉄板構成をそのまま使うほうが簡単で安全だ。

Docker Compose について。

Docker の扱うコンテナ (仮想環境) を複数、組み合わせたくなることがある。Redmine の場合、本体に必要な構成は固定的だが DB 部分は好みが分かれるので個別に扱いたい。そして Docker 公式 Redmine イメージもそれを想定した設計になっている。

こうした複数のコンテナを組み合わせて管理するツールとして Docker Compose がある。設定ファイル (YAML) に組み合わせるコンテナを定義すれば、自動的に協調動作させてくれるスグレモノ。スターターでは Docker + Docker Compose を前提とした設定ファイルを用意した。

version: "3"
services:
  redmine:
    container_name: redmine
    image: redmine:4.0.5-passenger
    restart: always
    ports:
      - 8080:3000
    environment:
      TZ: Asia/Tokyo
      REDMINE_DB_MYSQL: mysql
      REDMINE_DB_DATABASE: redmine_mytheme
      REDMINE_DB_USERNAME: redmine_mytheme
      REDMINE_DB_PASSWORD: redmine_mytheme
      REDMINE_DB_ENCODING: utf8mb4
    depends_on:
      - mysql
    volumes:
      - ./src/assets:/usr/src/redmine/public/themes/mytheme
  mysql:
    container_name: mysql
    image: mysql:5.7
    restart: always
    environment:
      TZ: Asia/Tokyo
      MYSQL_ROOT_PASSWORD: P@ssw0rd
      MYSQL_DATABASE: redmine_mytheme
      MYSQL_USER: redmine_mytheme
      MYSQL_PASSWORD: redmine_mytheme
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_bin

この例だと Passenger で動作する Redmine 4.0.5 と MySQL 5.7 を組み合わせている。Redmine 側の volumes によって開発環境で生成されたテーマのファイルが Docker コンテナへ晒される。スターターではテーマ名を mytheme にしているが、これを変えたければ YAML ファイル内のそれを一括変換すればよい。

Docker 関連のインストールについて。

昔は Docker と docker-compose を個別にインストールしたものだが、macOS と Windows であれば現在は Dock Desktop だけで全て揃う。

私も macOS 版を利用している。ただし Windows だと Hyper-V を有効化する必要があるそうで、これが問題になるかもしれない。

残念ながら Hyper-V は Windows 10 Home だとサポートされず、Pro 以上が必要になる。登壇時はこれに気付かず「Windows でも簡単にセットアップできます」的な発言をしてしまい、申し訳ない。

これに関しては Docker Desktop WSL 2 を利用することが対策になるかもしれない。

WSL2 は Hyper-V の応用だが、直に依存しているわけではないので Windows 10 Home でも有効化できる。ただし WSL2 自体がデフォルトで有効になるのは 2020/5 と言われているので、現時点だと Preview 版になってしまう。

こうした問題を抱えてまで Windows で開発するか?というのがあるため、Windows でスターターを使いたいユーザー向けに Vagrant 版の提供を検討している。希望されるユーザーが多ければ対応するので、本記事を読まれた後にアカベコ宛へ意見をください。特になければ現状どおり Docker のみのままにします。

作ってみる

スターターを動作させるための環境が整ったら、実際にテーマ開発してみよう。といっても

  • SCSS とは一体...
  • SCSS 編集にはどんなツールを使えばいいのか?

というユーザーも居ると思うので、これらも掘り下げる。

SCSS

CSS は CSS3 以降、ずいぶんと機能強化された。最近ではカスタムプロパティとして変数がサポートされたりしている。それでもセレクターの適用範囲に関する問題は CSS 標準だと解決されそうにない。

セレクターの優先順位は宣言位置と詳細度で決まる。しかしこれらだけでは規模の拡大にともない管理しにくくなる。後に宣言されたものが優先されるため、前に宣言されたものの都合が悪くなると後に宣言されたもので上書きしたり、詳細度を高めるために idclass を追加してお茶を濁すというのはよく見られる。

また、これらを無視する恐るべき !important がある。これは宣言位置や詳細度に関わらず、その定義を強制的に最優先する。そのためこれを上書きするために打ち消し用の !important を追加して...という連鎖へ陥りがち。これを見かけたら地獄開始の合図だと警戒しよう。

この問題はプログラミング言語だとスコープ機能によって対策される。ファイルや指定された名前空間などの単位下に宣言されたものは、外部に影響しない。この機能によりソフトウェアの規模が拡大しても個々を疎結合にできる。一応、CSS でも :scope として提案されたが採用は進まず、利用するには厳しい。

その他、様々な CSS に不足している機能の補完や拡張を目的として SassStylus などが開発された。これらは CSS を拡張した記法で書き、それを CSS に変換する。開発中は便利な書き方をして、エンド ユーザー向けには CSS として出力すればよいという設計。

こうした言語とツールのうち 2019 現在も開発が継続されていて普及しているものとして、スターターでは Sass を採用した。ただし Sass 標準の記法はかなり癖がある。そのため記法については CSS 標準に近い SCSS を選んだ。ややこしいのだけど

  • Sass の方言として SCSS がある
  • SCSS は CSS と互換性がある

と認識してもらえればよい。

では SCSS がどうやってスコープ問題を解決するのか。CSS 標準でもスコープ風に詳細度をコントロールするため

.header {
  display: flex;
  justify-content: space-between;
}

.header .logo {
  width: 1.5rem;
  height: 1.5rem;
}

.header .menu {
  text-align: center;
}

のように書くことがある。単なる定義と命名ルールなのでプログラミング言語におけるスコープ機能のような強制力と安全性はないが、それは仕方ない。SCSS の場合、これを以下のように書ける。

.header {
  display: flex;
  justify-content: space-between;

  .logo {
    width: 1.5rem;
    height: 1.5rem;
  }

  .menu {
    text-align: center;
  }
}

定義をネストさせることで、親子関係が強制化された。親となる .header の名前を変更した場合も .logo.menu は自動的にそちらへ属し、CSS に変換されれば CSS 標準の記法に基づいた出力を得られる。これを利用すれば宣言順と詳細度を気にするのはルート部分だけで済む。スコープ問題への対処としては不完全だが、壊れにくくなった。

もう一つ便利な機能として & による連結がある。例えば CSS の疑似要素。これを

.menu:before {
}

.menu:after {
}

としているなら SCSS では

.menu {
  &:before {
  }

  &:after {
  }
}

のように & を付けることで親へ連結してくれる。疑似要素や親子関係が増えるほど便利さを実感する機能だ。

SCSS には他にも便利な機能がたくさんある。記法自体は CSS 標準に寄せつつ、こうした拡張機能を使って楽をしよう。

エディター

特にこだわりがなければ Visual Studio Code (以下、VS Code) を推奨する。SCSS 編集に便利なプラグインとして以下を入れておこう。

プラグイン 説明
Sass SCSS の構文強調や入力補完を提供してくれる。
Bracket Pair Colorizer 2 {} などの括弧が見やすくなる。
Prettier SCSS の記法を整形してくれる。例えばインデントを揃えるなど。
colorize CSS の色指定 #FFF などを見やすくしてくれる。

SCSS を CSS に変換する

ターミナルを起動してスターターの場所まで移動する。もし一度も npm i していないなら、これも実行しておく。

$ cd スターターのパス
$ npm i

次に SCSS ファイル監視モードを起動。

$ npm start

これでスターター内の SCSS ファイルを更新して保存すると自動的に CSS が生成される。もし SCSS の記法に誤りがあればターミナルにエラーが表示されるので修正しよう。

監視モードを終了するにはキーボードの control + C を押す。ターミナルや OS のショートカット キーをいじっていなければ、Windows、macOS、Linux 共通でこの操作が効くはず。

テーマをプレビューする

無事に Docker + Compose をインストールできたら、テーマをプレビューするためにターミナルから Docker コンテナ上の Redmine を起動する。初回だとコンテナのイメージをダウンロードするので少し待つ。

$ cd スターターのパス
$ docker-compose up -d

成功したなら Web ブラウザーで http://localhost:8080/ を開く。すると Redmine が表示されることを確認できるはず。Redmine の管理画面を開くと、表示画面のテーマ欄から作成しているテーマ (スタータの初期状態なら mytheme) を選べるので変更しておく。

Docker コンテナを終了させる場合は以下を実行する。

$ docker-compose stop

テーマを編集する

ここまでの環境が整ったら、以下のようにテーマを編集してゆく。

  1. スターターの SCSS ファイル監視モードを起動
  2. スターターの Docker コンテナを起動
  3. Web ブラウザーで http://localhost:8080/ を開く
  4. SCSS ファイルを編集して保存
  5. Redmine を表示している Web ブラウザー上のタブを再読み込み
  6. Redmine 上で変更が反映されていることを確認

これを納得ゆくまで繰り返す。

編集したい箇所を調べる

さあこれでテーマ作れるぞ!となりそうだが、実際にはどのように SCSS を書けばよいか迷うはず。Redmine 上で変更したい場所はわかるのだが redmine.org などを見ても標準 CSS の詳細解説はないため、自分で調べなくてはならない。ここではその方法について説明しておく。

スターターの初期状態では、その時点で最新の Redmine 標準 CSS を同梱して読み込んでいる。場所は src/scss/defult。ファイルの内訳は以下。

ファイル 内容
application.css 通常表示用。
responsive.css ページ幅が狭い場合の表示用。

これらを開きながら、Redmine を Chdome や Firefox などの Web ブラウザーで開こう。スターターのプレビューを起動できているならそれを、Docker の問題などで見られないなら他の Redmine を開く。

次に Web ブラウザーの開発者ツールを表示する。そして Redmine の変更したい箇所を開発者ツール上で選択しよう。以下は Firefox の開発者ツールで Redmine のヘッダー部分を選択している状態。

Firefox の開発者ツールで Redmine のヘッダー部分を選択している状態

ヘッダー部分の HTML は <div id="header">、CSS としては application.css 29 行目の #header であることがわかったのでエディターで開いてみると、こんな感じになっていた。

#header {min-height:5.3em;margin:0;background-color:#628DB6;color:#f8f8f8; padding: 4px 8px 20px 6px; position:relative;}

この中から背景色を赤に変えたいなら、スターターの App.scss を以下のように定義する。

// build: application.css

@import "../../node_modules/normalize.css/normalize";
@import "./default/application";

#header {
  background-color: #FF0000;
}

#header が追記した部分。これより前の @import で標準の application.css を読み込んでいるため「宣言順として追記部分は後 = 定義を上書き」となる。こんな感じで変更したい箇所を特定、上書きというのを繰り返しながらテーマを作り上げてゆく。

標準 CSS は Redmine 上の大まかな部分単位でまとまっているため、大雑把にあたりを付けて Web ブラウザーの開発者ツールと突き合わせれば、構造も把握しやすいはず。

スターターの更新反映

Redmime のアップデートにあわせてスターターの標準も CSS 更新している。それを反映する場合は以下をダウンロードして自分の環境へ上書きしよう。

これでベースになる CSS とプレビュー用 Redmine の両方が最新版へ更新される。

フルスクラッチ

スターターでは基本的に標準 CSS から差分で上書きすることを想定している。しかし慣れてきたり度胸のあるユーザーは、もっと大規模に書き換えたくなるはずだ。

その場合は App.scssResponsive.scssapplication.cssresponsive.css@import している部分を削除し、かわりに標準 CSS の内容をコピペする。不要なら normalize.css の行も消してよい。

これでスターターの SCSS が Redmine 標準 CSS 相当になったので、後は自身の SCSS/CSS 技術力にあわせて自由に編集してゆこう。私が minimalflat2 を開発した時はこんな感じで始めた。

しかし問題点として Redmine の更新追従がある。私の場合、

  1. redmine.org の Download から Redmine 標準イメージ ZIP を入手
  2. ZIP を展開して標準 CSS を入手
  3. 前回 1 〜 2 を実行して入手したものと差分を比較
  4. 差分を自作テーマの SCSS へ反映

という感じで作業している。差分比較は macOS の Xcode に付属する FileMerge。他のツール、例えば Windows なら WinMerge を利用してもよい。

ツールで見つかった差分については以下のようにチェック。

  • 自作テーマで上書きしていないならそのまま最新をコピペ
  • 上書きしている場合はその内容を踏まえて反映

この作業をやりやすくするため、フルスクラッチで作成するとしてもセレクターの宣言位置は Redmine 標準へ合わせておいたほうがいい。minimalflat2 では当初、宣言位置も独自に整理していたが差分チェックの面倒さにより Redmine 標準を踏襲するように修正した。

テーマをリリースする

納得のゆくテーマができたら、本番の Redmine へインストールするためのイメージを作成しよう。まずはテーマの名前とバージョンを編集するためにスターターの package.json ファイルを開き、以下を編集する。

{
  "name": "mytheme",
  "description": "Starter kit for theme of Redmine.",
  "version": "1.0.0",
  "author": "author"
}
設定 内容
name テーマ名。リリースするテーマのフォルダー名に使用される。安全のため、使用する文字は英数へ限定することを推奨。
description テーマの説明。ユーザーには見えないので面倒ならそのままでもよい。
version バージョン番号。リリースするテーマを ZIP 圧縮した際のファイル名に使用される。
author 作者。ユーザーには見えないので面倒ならそのままでもよい。

これらを設定したらターミナルを開き、以下を実行する。

$ cd スターターのパス
$ npm run release

少し待つとスターターのディレクトリー内に mytheme-1.0.0.zip といった名前の ZIP ファイルが出力される。これがテーマのリリース用イメージになるので、自分の Web サイトに公開するなどして配信しよう。

私が作成している minimalflat2 というテーマでは、このように生成した ZIP を GitHub の Releases で配信している。

自分の作成したテーマを宣伝する手段として Redmine 公式のコミュニティー サイトである redmine.org にも告知しよう。ここに Theme List という Wiki ページが用意されているので、既にある記述を参考に自作テーマの紹介を書いてゆく。

まとめ

昨年の Redmine Advent Calendar 2018 でテーマ作成の話を書き、これを下地として redmine.tokyo 17 で登壇した。

そして本記事にいたる。足掛け 1 年、テーマについて解説したわけだが、これで一人でも Redmine テーマ開発者が増えたら幸いだ。

本記事をきっかけにテーマ開発を試みて、つまずくことがあったら Twitter のアカベコまでどうぞ。可能な限り相談にのります。

Redmine Advent Calendar 2019、15 日目は taikiix さんです。

Copyright © 2009 - 2023 akabeko.me All Rights Reserved.