WSL2のUbuntu 20.04にFocalboardを生やす

FocalboardというNotionクローンがあるらしく試してみた結果のメモ。
外部に公開することは一切考慮していません。ローカルマシンでの個人利用を想定しています。

確認環境

Env Ver
Ubuntu 20.04.4 LTS
WSL2 1.0.3.0
Focalboard 0.15.0
MySQL Ver 15.1 Distrib 10.3.37-MariaDB

前提条件

  • MySQLはセットアップ済み

手順

基本的なセットアップ

wget https://github.com/mattermost/focalboard/releases/download/v0.15.0/focalboard-server-linux-amd64.tar.gz
tar -xvzf focalboard-server-linux-amd64.tar.gz
sudo mv focalboard /var/lib/

cat <<'EOF' | sudo tee /etc/nginx/conf.d/focalboard.conf
upstream focalboard {
   server localhost:15001;
   keepalive 32;
}

server {
   server_name  focalboard.test;
   access_log   /var/log/nginx/focalboard.access.log;
   error_log    /var/log/nginx/focalboard.error.log;

   location ~ /ws/* {
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       client_max_body_size 50M;
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Frame-Options SAMEORIGIN;
       proxy_buffers 256 16k;
       proxy_buffer_size 16k;
       client_body_timeout 60;
       send_timeout 300;
       lingering_timeout 5;
       proxy_connect_timeout 1d;
       proxy_send_timeout 1d;
       proxy_read_timeout 1d;
       proxy_pass http://focalboard;
   }

   location / {
       client_max_body_size 50M;
       proxy_set_header Connection "";
       proxy_set_header Host $http_host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header X-Frame-Options SAMEORIGIN;
       proxy_buffers 256 16k;
       proxy_buffer_size 16k;
       proxy_read_timeout 600s;
       proxy_cache_revalidate on;
       proxy_cache_min_uses 2;
       proxy_cache_use_stale timeout;
       proxy_cache_lock on;
       proxy_http_version 1.1;
       proxy_pass http://focalboard;
   }
}
EOF

sudo cat << 'EOF' | sudo tee /etc/init.d/focalboard
#!/bin/sh
## BEGIN INIT INFO
# Provides:          focalboard
# Required-Start:    $syslog $network
# Required-Stop:     $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: A self-hosted Git service written in Go.
# Description:       A self-hosted Git service written in Go.
## END INIT INFO

. /lib/lsb/init-functions

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Focalboard server"
NAME=focalboard
SERVICEVERBOSE=yes
PIDFILE=/run/$NAME.pid
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"

do_start()
{
    start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \
        --background --chdir /var/lib/focalboard \
        --exec /var/lib/focalboard/bin/focalboard-server
}

do_stop()
{
    kill -9 $(cat /run/focalboard.pid)
    # does not work
    #start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo
    rm -f $PIDFILE
}

do_status()
{
    if [ -f $PIDFILE ]; then
        if kill -0 $(cat "$PIDFILE"); then
            echo "$NAME is running, PID is $(cat $PIDFILE)"
        else
            echo "$NAME process is dead, but pidfile exists"
        fi
    else
        echo "$NAME is not running"
    fi
}

case "$1" in
    start)
        log_daemon_msg "Starting $DESC" "$NAME"
        do_start
        ;;
    stop)
        log_daemon_msg "Stopping $DESC" "$NAME"
        do_stop
        ;;
    status)
        do_status
        ;;
    restart)
        log_daemon_msg "Restarting $DESC" "$NAME"
        do_stop
        do_start
        ;;
    *)
        log_daemon_msg "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
        exit 2
        ;;
esac

exit 0
EOF

sudo chmod 755 /etc/init.d/focalboard
sudo update-rc.d focalboard defaults

mysql -u XXX -pYYY

DBの作成

CREATE DATABASE boards;
GRANT ALL on boards.* to 'boardsuser'@'localhost' identified by 'boardsuser-password';
exit

DBとportの変更

nano /var/lib/focalboard/config.json

portとdbtypeを以下の内容に書き換え

    "port": 15001,
    "dbtype": "mysql",

起動

以下を叩き http://focalboard.test/ にアクセスできればOK

sudo service focalboard start

感想

Notionっぽいけどかなりしょぼい
ボードが作れてmdが書ける程度
2カラムレイアウト作るとか、画像をドラッグで移動するとかはできない
表組みへの画像挿入は「画像添付 -> 表のmdを書く -> HTMLからblob URLを貼る」で可能だが、見切れるので微妙

今のところ実用性はあまりないかも

参考

バッチファイルで文字列置換する

変数の文字列を文字列で置換

@ECHO OFF

SET text="hoge piyo fuga"
REM 置換文字を標準出力
ECHO %text:hoge=XXX%
REM 置換文字を変数に設定
SET replaced_text=%text:hoge=XXX%
ECHO %replaced_text%

@ECHO ON

変数の文字列を変数の文字列で置換

SET "var=value"とSET var="value"の差は後述のAppendixを参照
CALLを付けているのは、これがないと変数名そのものが文字列として扱われるため

@ECHO OFF

SET "text2=foo bar baz"
SET "search_word=baz"
SET "replacer=ZZZ"

REM 置換文字を標準出力
CALL ECHO %%text2:%search_word%=%replacer%%%
REM 置換文字を変数に設定
CALL SET replaced_text2=%%text2:%search_word%=%replacer%%%
ECHO %replaced_text2%
REM 検索文字列を削除
CALL SET replaced_text3=%%text2:%search_word%=%%
ECHO %replaced_text3%

@ECHO ON

Appendix

SETコマンドの記述方法を変えた場合の出力結果の差について

SET "var=foo bar ZZZ" SET var="foo bar ZZZ"
foo bar ZZZ "foo bar ZZZ"

参考

WSL2のUbuntu 20.04にGiteaを生やす

ローカルで個人メモを管理したいがRedmineでは物足りない。そうだGiteaを使おう。
この記事ではDockerを使用せず、バイナリからセットアップします。
Gitのリポジトリをホスティングできる部分までは確認してます。

外部に公開することは一切考慮していません。ローカルマシンでの個人利用を想定しています。

確認環境

Env Ver
Ubuntu 20.04.4 LTS
WSL2 1.0.3.0
Gitea 1.18.5
MySQL Ver 15.1 Distrib 10.3.37-MariaDB
Git 2.25.1

前提条件

  • WSL2上にUbuntu 20.04.4 LTSをインストール済
  • GitとMySQLは構築済

手順

公式の手順ではユーザーがgitですが、ここではgiteaに変えてます
他にも多少変えてます

Giteaディレクトリの作成とバイナリ配置

# 動作用ユーザーの追加
sudo adduser \
   --system \
   --shell /bin/bash \
   --gecos 'Git Version Control' \
   --group \
   --disabled-password \
   --home /home/gitea \
   gitea

# 必要なフォルダを切る
sudo mkdir -p /var/lib/gitea/{custom,data,log}
sudo chown -R gitea:gitea /var/lib/gitea/
sudo chmod -R 750 /var/lib/gitea/
sudo mkdir /etc/gitea
sudo chown root:gitea /etc/gitea
sudo chmod 770 /etc/gitea

# バイナリのインストール
wget -O gitea https://dl.gitea.com/gitea/1.18.5/gitea-1.18.5-linux-amd64
chmod +x gitea
sudo cp gitea /usr/local/bin/gitea

# mysqlにログイン
mysql -u XXX -pYYY

DBのセットアップ

SET old_passwords=0;
CREATE USER 'gitea' IDENTIFIED BY 'gitea';
CREATE DATABASE giteadb CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
GRANT ALL PRIVILEGES ON giteadb.* TO 'gitea';
FLUSH PRIVILEGES;

quit

Giteaの起動とWebインストール

以下のコマンドを叩き表示されたURLを開き、Webインストーラーで適当に設定

gitea web -c /etc/gitea/app.ini

Webインストール後の後処理とデーモンへの登録

# 自分以外書き換え不能に変更
sudo chmod 750 /etc/gitea
sudo chmod 640 /etc/gitea/app.ini

# デーモンの定義を作成
cat <<'EOF' | sudo tee /etc/init.d/gitea
#!/bin/sh
## BEGIN INIT INFO
# Provides:          gitea
# Required-Start:    $syslog $network
# Required-Stop:     $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: A self-hosted Git service written in Go.
# Description:       A self-hosted Git service written in Go.
## END INIT INFO

# Do NOT "set -e"

# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin
DESC="Gitea - Git with a cup of tea"
NAME=gitea
SERVICEVERBOSE=yes
PIDFILE=/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
WORKINGDIR=/var/lib/$NAME
DAEMON=/usr/local/bin/$NAME
DAEMON_ARGS="web -c /etc/$NAME/app.ini"
USER=gitea
STOP_SCHEDULE="${STOP_SCHEDULE:-QUIT/5/TERM/1/KILL/5}"

# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME

# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0

do_start()
{
    GITEA_ENVS="USER=$USER GITEA_WORK_DIR=$WORKINGDIR HOME=/home/$USER"
    GITEA_EXEC="$DAEMON -- $DAEMON_ARGS"
    sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
        --background --chdir $WORKINGDIR --chuid $USER \\
        --exec /bin/bash -- -c '/usr/bin/env $GITEA_ENVS $GITEA_EXEC'"
}

do_stop()
{
    start-stop-daemon --stop --quiet --retry=$STOP_SCHEDULE --pidfile $PIDFILE --name $NAME --oknodo
    rm -f $PIDFILE
}

do_status()
{
    if [ -f $PIDFILE ]; then
        if kill -0 $(cat "$PIDFILE"); then
            echo "$NAME is running, PID is $(cat $PIDFILE)"
        else
            echo "$NAME process is dead, but pidfile exists"
        fi
    else
        echo "$NAME is not running"
    fi
}

case "$1" in
    start)
        echo "Starting $DESC" "$NAME"
        do_start
        ;;
    stop)
        echo "Stopping $DESC" "$NAME"
        do_stop
        ;;
    status)
        do_status
        ;;
    restart)
        echo "Restarting $DESC" "$NAME"
        do_stop
        do_start
        ;;
    *)
        echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2
        exit 2
        ;;
esac

exit 0
EOF

# デーモン登録
sudo chmod 755 /etc/init.d/gitea
sudo update-rc.d gitea defaults

# デーモン起動
sudo service gitea start

自動起動する場合はWindowsのスタートアップ時に以下が走るようにしておけばOK

wsl -d Ubuntu -u root -- service gitea start

トラブルシュート

この記事を書くにあたってハマったことのまとめ

gitea 起動時にpermission deniedとエラーが出る

/var/lib/gitea/custom/options/locale: permission denied

以下のコマンドを流せばいける。公式のコマンドではコケる

gitea web -c /etc/gitea/app.ini

サービス起動時にFailed to get home directoryとエラーが出る

2023/03/03 13:17:38 ...s/setting/setting.go:656:loadFromConf() [F] Failed to get home directory: cannot get home directory

/etc/init.d/giteaのdo_start()の部分を以下の内容にすればいける

do_start()
{
    GITEA_ENVS="USER=$USER GITEA_WORK_DIR=$WORKINGDIR HOME=/home/$USER"
    GITEA_EXEC="$DAEMON -- $DAEMON_ARGS"
    sh -c "start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \\
        --background --chdir $WORKINGDIR --chuid $USER \\
        --exec /bin/bash -- -c '/usr/bin/env $GITEA_ENVS $GITEA_EXEC'"
}

git pushでエラーが出る

RPC failed; HTTP 413 curl 22 The requested URL returned error: 413

nginxからgiteaにリバースプロキシしてる場合はconfのserverセクションに以下を追加

client_max_body_size 0;

参考

Slackのリマインダー操作方法(登録、削除、一覧)

公式のリファレンスが情報少なすぎるのでメモ
特にこれというフォーマットはなく、ある程度幅広い書式があるようで、公式のドキュメントを見る限り、多少のブレはよしなに解釈してくれるっぽい?
この記事ではoperatorなどの用語が登場しますが、これは私が勝手に名付けたものなので、特に公式の用語ではありません

手組するの面倒なので雑にコマンド生成ツールを作りました。

https://tool.lycolia.info/slack-remider-creator

登録

基本

  • フォーマット
    • /remind [宛先] [メッセージ] [実行日時]
  • 例
    • /remind #random "hoge piyo fuga" at 10:00 every monday, sunday

書式

宛先
宛先 意味
me 自分
@someone メンション
#channel チャンネル
メッセージ

"hoge piyo fuga"のように書けるが、hoge piyo fugaでも一応通じる
ダブルクォートで囲む場合、改行も利用可能

実行日時
  • <oparator> <repeat-operator>
    • <oparator>で実行日時を設定
    • <repeat-operator>で繰り返しを設定
  • 例
    • at 10:00 every monday, sunday
oparator
オペレーター 例 意味
in <value> (seconds | minutes | hours) in 10 minutes 指定時間後に実行
at <HH:mm> at 1700 指定時刻に実行
on <yyyy-MM-dd HH:mm> on 2023-10-10 09:15 指定日時に実行
repeat-operator
  • every <operator>
  • 例
    • every monday, sunday
operator
day 毎日
weekday 平日
sunday, monday, tuesday, wednesday, thursday, friday, saturday 毎週の曜日、カンマ区切りで増やせる

削除

  • /remind listでリスト表示して消す

一覧

  • /remind list