FLIP で要素の移動のアニメーション

Vue.js のチュートリアルを読んでいて FLIP という要素の移動のためのアニメーションの手法を知りました。

要するに・・

  • First. 要素の最初の状態を取得
  • Last. 要素の最後の状態を取得
  • Invert. 要素の最初から最後までの状態変化を反転させた状態を適用
  • Play. 適用した状態を解除してアニメーションを開始

下記のように要素の移動をアニメーションさせられます。

要素の移動までの状態を取得して、

    // 要素の移動前の位置をマップに保存
    const map = new Map();
    for (const el of elem.querySelectorAll('li')) {
        const r = el.getBoundingClientRect();
        map.set(el, r);
    }

要素を実際に移動させます。どう移動するかは押したボタンによります。

    // コールバックで要素を移動させる
    callback();

移動後の要素の状態を取得して、

            // 移動後の要素の位置を得る
            const r2 = el.getBoundingClientRect();

反転して transform に適用します。

            // 要素の移動距離を反転して transform に適用する
            const x = r1.left - r2.left;
            const y = r1.top - r2.top;
            el.style.transitionProperty = 'none';
            el.style.transform = `translate(${x}px,${y}px)`;

そして transform を解除してアニメーションを開始します。

            requestAnimationFrame(() => {
                // 要素の transform を解除して本来の位置に戻す
                el.style.transitionProperty = '';
                el.style.transform = '';
            });

js で left や top を書き換えるよりもこちらのほうがだいぶパフォーマンス良いそうです。

CSS の attr がトグルボタンの ON/OFF でテキストを変えたいときに便利だった

HTML/CSS でチェックボックスを用いたトグルボタンをで実装しているとき、トグルの ON/OFF でテキストの文言も変えたいときは css だけでは難しいので js でやってましたが、css でも attr を使えば js 無しでできることを最近知りました。

次のように HTML の要素の属性値に ON と OFF のそれぞれのテキストを指定します。

<label>
    <input type="checkbox">
    <span data-off="無効" data-on="有効"></span>
</label>

これを要素の ::aftercontentattr で指定します。

input[type="checkbox"] ~ span::after {
    content: attr(data-off);
}

input[type="checkbox"]:checked ~ span::after {
    content: attr(data-on);
}

チェックボックスが OFF なら ::aftercontentdata-off の属性値が、ON なら data-on の属性値が使われます。

あとはトグルボタン風に CSS で装飾すれあ OK です。

MDN によるとこれは IE8 ぐらいから使えるようです。

次のような感じのトグルボタンが実装できます。

.

.

.

よく考えたら2つのボタンを重ねて表示して表示非表示を切り替えるだけでも出来ますね・・

Docker Desktop が重いのでリモートサーバの Docker で開発できるようにしてみた

Docker Desktop が重くて仕方ないのでリモートサーバの Docker へ unison で同期して開発できるようにしてみた。

Docker Remote API のセットアップ

どこかのリモートサーバに普通に Docker をセットアップした後、Docker Remote API を有効にします。

まずはローカルで証明書&秘密鍵を作ります。本来であればルート・サーバ・クライアントでそれぞれ作るものだと思いますが、面倒だし自分専用として作るだけなのでオレオレ1本でやります。

# オレオレ証明書&秘密鍵、subjectAltName のところはサーバ名に一致させる必要があります
openssl req -batch -new -x509 -newkey rsa:2048 -nodes -sha256 \
  -subj /O=oreore-for-docker -days 3650 \
  -addext subjectAltName=DNS:ore-no-server \
  -keyout ~/.docker/key.pem \
  -out ~/.docker/ca.pem

# オレオレ証明書をクライアント証明書としても使う
cp ~/.docker/ca.pem ~/.docker/cert.pem

# サーバに送る
rsync ~/.docker/ca.pem ~/.docker/cert.pem ~/.docker/key.pem ore-no-server:/etc/docker/ --rsync-path='sudo rsync' -v

systemd のユニットファイルで Remote API を有効にします。

# リモートサーバで作業します
ssh ore-no-server

# オリジナルの設定を確認しておく
cat /usr/lib/systemd/system/docker.service | grep -A3 ExecStart

# 環境変数でコマンドラインオプションを追加できるようにする
sudo mkdir -p /etc/systemd/system/docker.service.d/
sudo tee /etc/systemd/system/docker.service.d/sysconfig.conf <<'EOS'
[Service]
EnvironmentFile=/etc/sysconfig/docker
ExecStart=
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $OPTIONS
EOS

# Remote API 関係のコマンドラインオプションを指定
sudo tee /etc/sysconfig/docker <<'EOS'
OPTIONS="\
  -H 0.0.0.0:2376 \
  --tlsverify \
  --tlscacert=/etc/docker/ca.pem \
  --tlscert=/etc/docker/cert.pem \
  --tlskey=/etc/docker/key.pem \
"
EOS

# 反映
sudo systemctl daemon-reload
sudo systemctl restart docker
sudo systemctl status docker

# リモートサーバの作業終わり
ssh ore-no-server

ローカルから docker が操作できることを確認します。

env DOCKER_TLS_VERIFY=1 DOCKER_HOST=tcp://ore-no-server:2376 docker ps
env DOCKER_TLS_VERIFY=1 DOCKER_HOST=tcp://ore-no-server:2376 docker run --rm hello-world

ローカルの環境変数設定

direnv とかで環境変数を設定します。DOCKER_CERT_PATH はデフォルトが ~/.docker なので指定不要です。

export DOCKER_HOST=tcp://ore-no-server:2376
export DOCKER_TLS_VERIFY=1

ファイル同期

ローカルとリモートサーバでファイル同期する必要があります。

以前は autofs+cifs をよく使っていましたが、cifs でファイルシステムの I/O が遅いのがボトルネックになってるのかもしれないので unison で同期するようにしてみます。

unison は CentOS なら epel から入れられますが、それだとディレクトリ監視がまともに動かなかったので最新版を使います。ただ、自前でビルドするのはめんどくさいので次のように適当な docker イメージから引っこ抜きます。

docker run --rm -v /usr/local/bin:/x eugenmayer/unison:2.51.2.1 sh -c 'cp /usr/local/bin/unison* /x'

このイメージは alpine ベースなのですが unison のバイナリが静的リンクになっているようなので x86_64 ならそのまま動くと思います。

ローカルとリモートの両方に unisonunison-fsmonitor の2つのバイナリをインストールしたら、次のようにディレクトリを同期できます。

unison . "ssh://ore-no-server/$PWD" \
  -ignore 'Name .git' \
  -ignore 'Name storage' \
  -ignore 'Name vendor' \
  -ignore 'Name node_modules' \
  -auto -batch -repeat watch

ローカルとリモートでディレクトリ名を一致させるのが重要です。一致していないと docker-compose でボリュームマウントがうまくできません。

PhpStorm

PhpStorm で下記あたりを設定します。

  • Docker
    • URL は https://ore-no-server:2376 のように https とします
    • 証明書のパスは↑で作成した証明書一式を Windows 側にコピーしてそのディレクトリを指定
    • パスのマッピングは /c/Users -> C:\Users とかで
  • PHP Remote Interpreter
    • configuration option で -dxdebug.remote_host=192.0.2.123 のようにローカルの IP アドレスを指定
  • Test Framework
    • 普通に設定すれば OK です

いろいろ比較

劇的に動作が早くなったのだけど、そもそも Docker Desktop の何が原因でここまで遅かったのか謎い。ディレクトリのマウントが cifs なので遅いのは仕方ないけどそれだけでこれほど遅くなるものか・・というぐらい遅い。

いろいろ試行錯誤してみたところ、とあるプロジェクトのユニットテストの実行結果は以下の通りでした。

Docker Desktop  local   Time: 1.02 minutes
Docker Desktop  cifs    Time: 1.37 minutes
Hyper-V         local   Time: 1.16 minutes
Hyper-V         cifs    Time: 1.53 minutes
VirtualBox      local   Time: 44.74 seconds
VirtualBox      cifs    Time: 1.17 minutes
VirtualBox      vboxsf  Time: 1.13 minutes
ESXi            local   Time: 34.54 seconds
ESXi            cifs    Time: exceeded the timeout of 300 seconds

Hyper-V/VirtualBox/ESXi は CentOS7 に Docker を入れて Remote API 経由で実行しています。Hyper-V と VirtualBox はローカルホスト上なので TLS 無し、ESXi は別ホストなので TLS ありです。

local はローカルとのフォルダ共有は無しでリモートサーバ上にソースファイルを配置した場合です。Docker Desktop は無理やり MobyLinuxVM のホスト側の /var/lib/ にソースを配置して実行しました。

local よりも cifs や vboxsf のほうがパフォーマンスが落ちるのは当然として、なぜか Hyper-V よりも VirtualBox のほうがだいぶ性能が良い結果になっています。Hyper-V が劣ってるとも考えにくいですが・・・ディスクがシンプロビジョニングとかそういう違いがあるのかもしれない。

ESXi は物理ホスト自体が別なので比較の対象としては参考になりません。しかも Wi-Fi 経由なので cifs だとおそすぎて composer がデフォルトの 300s だとタイムアウトしました。これは実用に耐えません。逆に local だともっともパフォーマンスが出ました、単純に ESXi のホストがそれなりなのでホストの性能差と思います。

なお、cifs のマウントオプションは次の通り指定しました(Docker Desktop はマウントオプションいじれないので素のままです)。

rw,vers=3.02,iocharset=utf8,soft,nobrl,noperm,nounix,actimeo=1

vboxsf は次のとおりです。

rw,ttl=1000

↑の結果は MySQL を --innodb_flush_method=nosync --innodb_flush_log_at_trx_commit=0 を付けて実行しています。これらを付けないときと比較すると次のようになりました(nosync と書いているのが設定ありの方)。

Docker Desktop          Time: 1.57 minutes
Docker Desktop  nosync  Time: 1.02 minutes
Hyper-V                 Time: 1.42 minutes
Hyper-V         nosync  Time: 1.16 minutes
VirtualBox              Time: 51.13 seconds
VirtualBox      nosync  Time: 44.74 seconds
ESXi                    Time: 40.49 seconds
ESXi            nosync  Time: 34.54 seconds

いずれもフォルダ共有はなしでリモートサーバ上にソースを配置した場合の結果です。結構な違いがありました。

???

なぜか同じ PC の Docker Desktop でも接続している Wi-Fi ? によってだいぶパフォーマンスが異なる?

Docker Desktop  local   Time: 1.02 minutes  -> 1.09 minutes
Docker Desktop  cifs    Time: 1.37 minutes  -> 2.37 minutes
Hyper-V         local   Time: 1.16 minutes  -> 1.24 minutes
Hyper-V         cifs    Time: 1.53 minutes  -> 2.23 minutes
VirtualBox      local   Time: 44.74 seconds -> 45.33 seconds
VirtualBox      cifs    Time: 1.17 minutes  -> 1.23 minutes
VirtualBox      vboxsf  Time: 1.13 minutes  -> 54.86 seconds

左と右でそれぞれとある別の場所での結果ですが、Hyper-V + cifs という組み合わせで著しく性能が劣化することがある?

と思ったんだけど再起動直後に試したらそうでもなかった。

Docker Desktop  cifs    Time: 1.37 minutes  -> 1.50 minutes

なぜかはわからないけれども、Hyper-V で cifs だと長時間起動しているとパフォーマンスが超悪くなる音でもあるのだろうか(作業環境の都合で片方が長時間起動しっぱなしでもう片方はわりと細切れ・・シャットダウンはせずにスリープしてるだけだけど)。

さいごに

改めて確認すると、重いなーと感じたら再起動しておけばわりと改善するような気がしたので、普通に Docker Desktop 使っておけば良いような気がしてきた。

物理ホストが別な ESXi にコードを配置して実行するのが早いのですが、unison で同期させるのを忘れて「なぜか更新されない」となりそうです。

vagrant との共存考えると VirtualBox が都合が良いので Docker Desktop はやめて Docker Machine とすることも考えられますが・・・

WSL 2 で Docker が動くようになればそれで全て解決?なのかもしれないし、ひとまず現状維持しておこう。

AWS Certificate Manager Private Certificate Authority 素振り

AWS のマネージドなプライベート CA の AWS Certificate Manager Private Certificate Authority を素振りしたメモ。

費用

https://aws.amazon.com/jp/certificate-manager/pricing/

プライベート CA ごとに 400 USD の月額と、プライベート証明書を発行する都度の費用が必要で、月/リージョンで発行した証明書の数によって次のディスカウントが効いた費用が必要です。

月/リージョンの証明書の数 料金/証明書
1~1,000 0.75 USD
1,001~10,000 0.35 USD
10,001 以上 0.001 USD

プライベート CA の月額は削除した月内は日割りで計算されます。ただし削除した CA を復旧可能期間中に復旧させると、削除から復旧までの期間についても課金されます。

アカウントで最初に作成したプライベート CA は30日間の無料期間があります。

証明書について、プライベートキーにアクセスの無いプライベート CA から発行する証明書は無料、とのこと。後述のローカルで秘密鍵&CSRを作成してプライベート CA で証明書を作成するケースは無料ということかな?

ACM と ACM PCA

ACM(AWS Certificate Manager)と ACM PCA(AWS Certificate Manager Private Certificate Authority)はマネジメントコンソールでは同じところにあるものの実質は別のサービスで連携ができると考えておくほうがわかりやすいかも。

ACM PCA はいわゆるフルマネージドなプライベート CA サービスで、ユーザーが作成した CSR を元に署名された証明書を作成できます。作成した証明書を失効することもできて、失効リストも ACM PCA で作成&S3 に保存されます。

ACM は証明書&秘密鍵を管理するサービスです。証明書&秘密鍵は以下のいずれかの方法で登録できます。

  • パブリック証明書をリクエスト
    • AWS で署名された証明書を作成
      • 一般的なブラウザで信頼される証明書チェイン
    • 秘密鍵も自動で作成される
      • 秘密鍵はエクスポートできない
    • CN のドメイン検証が必要
      • メール、または、DNS
  • プライベート証明書をリクエスト
    • 発行元として ACM PCA で作成されたプライベート CA を指定
      • 一般的なブラウザで信頼されるかどうかはプライベート CA の上位 CA 次第
    • 証明書の CN のドメイン検証は不要
      • 任意の CN で作成できる
    • 秘密鍵も自動で作成される
      • 秘密鍵もエクスポートできる
  • 証明書&秘密鍵をインポート
    • 別のベンダーから取得した証明書&秘密鍵を ACM にインポート

オレオレルート CA の作成と プライベート CA の証明書作成のメモ

ACM PCA でプライベート CA を作成するためには、プライベート CA の証明書に署名するためのルート CA が必要です。つまり ACM PCA のプライベート CA はルート CA にはならず、こちらで用意する CA の下位 CA になります。

なお、マネジメントコンソールでプライベート CA を作成するときに「下位 CA」という1つしか選択できない選択肢があるので、将来的にはルート CA も作成できるようになるのかもしれません。

オレオレルート CA はいわゆる CA.sh (/etc/pki/tls/misc/CA) で作るのが普通かもしれませんが、これが対話式で辛いのでなるべく対話不要に作ります。

# basicConstraints を critical,CA:true にしておかないと証明書のインポートでコケる
cp -an /etc/pki/tls/openssl.cnf /etc/pki/tls/openssl.cnf.orig
vim /etc/pki/tls/openssl.cnf
#=> [ v3_ca ]
#=> basicConstraints = critical,CA:true

# index.txt を作成
touch /etc/pki/CA/index.txt

# ルート CA の秘密鍵&CSRを作成
openssl req -batch -new -newkey rsa:2048 -nodes -sha256 \
    -subj /C=JP/ST=Tokyo/O=oreore/OU=oreore/CN=oreore-root-ca \
    -keyout /etc/pki/CA/private/cakey.pem \
    -out /etc/pki/CA/careq.pem

# ルート CA の自己署名証明書を作成
openssl ca -batch -create_serial -selfsign -days 3650 -extensions v3_ca \
    -keyfile /etc/pki/CA/private/cakey.pem \
    -in /etc/pki/CA/careq.pem \
    -out /etc/pki/CA/cacert.pem

# ACM PCA プライベート CA の CSR を貼り付けて保存
vim acm-pca.csr

# ACM PCA プライベート CA の証明書を作成
openssl ca -batch -policy policy_anything -days 3650 -extensions v3_ca -in acm-pca.csr -out acm-pca.crt

# オレオレルート CA 証明書と ACM PCA プライベート CA 証明書を PEM 形式で表示してマネジメントコンソールからインポート
openssl x509 -in /etc/pki/CA/cacert.pem
openssl x509 -in acm-pca.crt

ローカルで秘密鍵&CSRを作成してプライベート CA で証明書を作成

ローカルで秘密鍵&CSRを作成し、プライベート CA で CSR から証明書を作成して署名できます。

# 秘密鍵&CSRを作成
openssl req -batch -new -newkey rsa:2048 -nodes -sha256 -subj /CN=foo.test -keyout client.key -out client.csr

# プライベートCAで署名して証明書作成
aws acm-pca issue-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --csr file://client.csr \
    --signing-algorithm "SHA256WITHRSA" \
    --validity Value=365,Type="DAYS"

# 証明書をエクスポートしてファイルに保存
aws acm-pca get-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --certificate-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad/certificate/f8e94e702ca3eef0bdd6d46e4ae00318" \
    --query "Certificate" --output text > client.crt

証明書のシリアルナンバーを指定して証明書を失効できます。

# シリアルナンバーを取得
openssl x509 -noout -serial -in client.crt
#=> serial=F8E94E702CA3EEF0BDD6D46E4AE00318

# 証明書を失効させる
aws acm-pca revoke-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --certificate-serial "F8E94E702CA3EEF0BDD6D46E4AE00318" \
    --revocation-reason "UNSPECIFIED"

しばらくすると失効リストに追加されます。失効リストはプライベート CA の作成時に指定した S3 に保存されます。

curl -s http://ore-no-ca.s3.ap-northeast-1.amazonaws.com/crl/9191161e-d094-4217-9796-45612de76aad.crl |
    openssl crl -inform DER -text -noout

作成された証明書をマネジメントコンソールや AWS CLI や AWS API で一覧を得ることはできなさそうです。なので aws acm-pca issue-certificate の応答の証明書の ARN を忘れてしまうと証明書が取得できなくなりそうです。

監査報告書を有効にすれば S3 に JSON や CSV で作成した証明書の一覧が保存されるので、それで確認できます。

ACM でプライベート証明書をリクエスト

ACM のマネジメントコンソールの「プライベート証明書のリクエスト」から証明書が作成できます。プライベート CA で自動で署名されるので証明書のドメイン名(CN)は何でも OK です。

証明書と秘密鍵が自動で作成されたうえでそれらが ACM で管理されます。また、ACM でパブリック証明書を作成すると秘密鍵はエクスポートできませんが、プライベート証明書なら秘密鍵もエクスポートが可能です。

作成される証明書の有効期限は365日で固定です。もしこの有効期限よりプライベートCAの有効期限が短いと、とてもわかりにくいエラーメッセージで失敗します。

マネジメントコンソールでプライベート証明書を作成するのと同じことが AWS CLI でもできます。

# 証明書の発行
aws acm request-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --domain-name "hoge.test"

# 証明書&秘密鍵のエクスポート
aws acm export-certificate \
    --certificate-arn \
    "arn:aws:acm:ap-northeast-1:999999999999:certificate/65039cd8-9b91-49be-8ea6-5de62244b152" \
    --passphrase "password"

ここで作成したプライベート証明書は AWS PCA のプライベート CA で署名されているので、プライベート CA の監査報告書にも追記されます。監査報告書には AWS PCA の証明書の ARN も記述されているので AWS PCA の AWS CLI で証明書を得ることもできます(つまり ACM と ACM PCA の両方の ARN がある)。

# ACM から証明書取得
aws acm get-certificate \
    --certificate-arn \
    "arn:aws:acm:ap-northeast-1:999999999999:certificate/65039cd8-9b91-49be-8ea6-5de62244b152" \
    --query 'Certificate' --outpu text | openssl x509 -noout -text

# ACM PCA から証明書取得(↑と同じ結果)
aws acm-pca get-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --certificate-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad/certificate/7e03d1acb41aa7c2083b16c1adc055a6" \
    --query 'Certificate' --outpu text | openssl x509 -noout -text

ACM のプライベート証明書の失効

ACM のマネジメントコンソールや AWS CLI でプライベート証明書を ACM から削除しても、プライベート CA の失効リストには追加されません。あくまでも ACM の管理上から削除されるだけです。

ACM のプライベート証明書は ACM PCA でも管理されているので、ACM PCA で署名した証明書と同様にプライベート証明書のシリアルナンバーを指定して証明書を失効できます。

# シリアルナンバーを取得
aws acm get-certificate \
    --certificate-arn \
    "arn:aws:acm:ap-northeast-1:999999999999:certificate/65039cd8-9b91-49be-8ea6-5de62244b152" \
    --query 'Certificate' --outpu text | openssl x509 -noout -serial -in client.crt
#=> serial=7E03D1ACB41AA7C2083B16C1ADC055A6

# 証明書を失効させる
aws acm-pca revoke-certificate \
    --certificate-authority-arn \
    "arn:aws:acm-pca:ap-northeast-1:999999999999:certificate-authority/9191161e-d094-4217-9796-45612de76aad" \
    --certificate-serial "7E03D1ACB41AA7C2083B16C1ADC055A6" \
    --revocation-reason "UNSPECIFIED"

参考

Gitlab CI のアーティファクトをブランチやタグとジョブ名を指定して特定のファイルを取得

以下のような URL で、ブランチやタグとジョブ名を指定して最新のアーティファクトから特定のファイルを取得できます。

https://example.com/projects/:id/jobs/artifacts/:ref_name/raw/*artifact_path?job=name
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>

しかし、あくまでも passed となった最新のパイプラインのアーティファクトが取得されるため、下記のように when: manualallow_failure: false を使って手動ジョブで後続のジョブをブロックしていると、build が終わってパイプラインが blocked となった時点ではまだこのアーティファクトは取得できません。手動ジョブをすべて実行してパイプラインが passed になったタイミングでアーティファクトが取得できます。

stages:
  - build
  - deploy/stage
  - deploy/prod

build:
  stage: build
  script:
    - env | grep CI | sort > env.txt
  artifacts:
    paths:
      - env.txt
    expire_in: 10 days

deploy/stage:
  stage: deploy/stage
  when: manual
  allow_failure: false
  script:
    - echo ok

deploy/prod:
  stage: deploy/prod
  when: manual
  allow_failure: false
  script:
    - echo ok

f:id:ngyuki:20190328085546p:plain

次のように allow_failure: false がなければ手動ジョブが未実行でもパイプラインは passed となるため、手動ジョブが未実行でも build ジョブのアーティファクトが取得できます。

stages:
  - build
  - deploy/stage
  - deploy/prod

build:
  stage: build
  script:
    - env | grep CI | sort > env.txt
  artifacts:
    paths:
      - env.txt
    expire_in: 10 days

deploy/stage:
  stage: deploy/stage
  when: manual
  script:
    - echo ok

deploy/prod:
  stage: deploy/prod
  when: manual
  script:
    - echo ok

f:id:ngyuki:20190328085602p:plain

ただ、when: manual なジョブの後段に when: manual ではないジョブがあるとき、前段のジョブが未実行でも後段のジョブが実行されてしまいます。

f:id:ngyuki:20190328085623p:plain

下記のようにジョブIDを指定するAPIを使えば blocked なパイプラインのアーティファクトも取れますが、ブランチ名やタグ名からジョブIDを取得する簡単な方法はなさそう(パイプラインやジョブのリストからフィルタするしかなさそう)。

https://example.com/projects/:id/jobs/:job_id/artifacts/*artifact_path

さいごに

パイプラインを when: manual で止めて、前段のアーティファクトを Gitlab の外であれこれ検証したうえで手動ジョブを実行して後続のジョブを実行、みたいなことをしていたのですが、手動ジョブを allow_failure: false にしたら↑の通りアーティファクトがうまく取れなくなりました。

アーティファクトはジョブID指定で取るとかしないとだめですね。

参考

WSL のコマンドラインから Windows 10 の通知を送るメモ

トースト通知というの?

いくつかツールを試してみたメモ。

Growl For Windows

Windows 10 の通知ではなく独自の通知が実装されています。

Chocolatey でサクッとインストールできるかと思いきや、インストールに失敗します。

こっちからインストーラーをダウンロードすれば OK です。

Growl 自体はわかりませんが Growl For Windows はもメンテされていないようです。

notifu

コマンドラインで使うことが想定されたものだと思いますが /v/? でダイアログが表示される。。。

toaster

リポジトリにまんまバイナリが入っているのでダウンロードすれば使えます(exe だけじゃなく dll も必要)

Welcome to toast.
Provide toast with a message and display it-
via the graphical notification system.
-Nels

---- Usage ----
toast <string>|[-t <string>][-m <string>][-p <string>]

---- Args ----
<string>                | Toast <string>, no add. args will be read.
[-t] <title string>     | Displayed on the first line of the toast.
[-m] <message string>   | Displayed on the remaining lines, wrapped.
[-p] <image URI>        | Display toast with an image
[-w]                    | Wait for toast to expire or activate.
?                       | Print these intructions. Same as no args.
Exit Status     :  Exit Code
Failed          : -1
Success         :  0
Hidden          :  1
Dismissed       :  2
Timeout         :  3

---- Image Notes ----
Images must be .png with:
        maximum dimensions of 1024x1024
        size <= 200kb
These limitations are due to the Toast notification system.
This should go without saying, but windows style paths are required.

SnoreToast

下記からバイナリがダウンロードできます。

Welcome to SnoreToast 0.5.2.
A command line application which is capable of creating Windows Toast notifications.

---- Usage ----
SnoreToast [Options]

---- Options ----
[-t] <title string>     | Displayed on the first line of the toast.
[-m] <message string>   | Displayed on the remaining lines, wrapped.
[-p] <image URI>        | Display toast with an image, local files only.
[-w]                    | Wait for toast to expire or activate.
[-id] <id>              | sets the id for a notification to be able to close it later.
[-s] <sound URI>        | Sets the sound of the notifications, for possible values see http://msdn.microsoft.com/en-us/library/windows/apps/hh761492.aspx.
[-silent]               | Don't play a sound file when showing the notifications.
[-appID] <App.ID>       | Don't create a shortcut but use the provided app id.
-close <id>             | Closes a currently displayed notification, in order to be able to close a notification the parameter -w must be used to create the notification.

-install <path> <application> <appID>| Creates a shortcut <path> in the start menu which point to the executable <application>, appID used for the notifications.

-v                      | Print the version and copying information.
-h                      | Print these instructions. Same as no args.
Exit Status     :  Exit Code
Failed          : -1
Success         :  0
Hidden          :  1
Dismissed       :  2
Timeout         :  3

---- Image Notes ----
Images must be .png with:
        maximum dimensions of 1024x1024
        size <= 200kb
These limitations are due to the Toast notification system.
This should go without saying, but windows style paths are required.

Powershell

Powershell だけでもできるようです。

さいごに

SnoreToast がバイナリポンで動くのと使い勝手が良さそうだったので、とりあえず下記のようなスクリプトをかまして使ってみます。

#!/bin/bash

if [[ $# -eq 0 ]]; then
  message="$(cat -)"
else
  message="$*"
fi

/mnt/c/path/to/SnoreToast/SnoreToast.exe -close SnoreToast >/dev/null
/mnt/c/path/to/SnoreToast/SnoreToast.exe -t SnoreToast -m "$message" -silent -w -id SnoreToast >/dev/null &

連続で表示したとき、普通にやると最初の通知を非表示にするまで次の通知が表示されないのですが、↑のように -id で ID を付ける& -w で通知が消えるまで待機しつつ、次の通知を表示する前に -close で ID を指定すれば、前の通知を非表示&次の通知をすぐ表示、とできます。

次のように使います。

# 引数でメッセージを指定
toast this is toast message
# 標準入力からメッセージを読む
ls | toast

標準入力から読めるようにしてみたものの、サイズ的に先頭の4行しか表示できないもよう。

docker-compose で MySQL レプリケーション環境をサクッと用意する

本番環境は MySQL のマスターとリードレプリカのスレーブで構成されているものの、ローカル環境はシングル構成なこと、よくあります。

そういうとき、レプリケーション遅延が原因による不自然な表示・・不具合が生じることがあったりするので、docker-compose なローカル環境でもサクッとレプリケーション環境を用意する方法。

Docker Hub のオフィシャルの MySQL イメージならコンテナの /docker-entrypoint-initdb.d/ にシェルスクリプトを置いておけば初回開始時に自動的に実行されるので、これを利用します。

docker-compose.yml

環境変数 MYSQL_REPLICATION_USER MYSQL_REPLICATION_PASSWORD でレプリケーションのユーザー名・パスワードを指定します。MYSQL_REPLICATION_HOST でマスターのホストを指定しますがこれはスレーブでのみ設定します。

version: '3.7'

services:

  mysql-master:
    image: mysql:8
    command:
      - --default_authentication_plugin=mysql_native_password
      - --gtid_mode=ON
      - --enforce_gtid_consistency=ON
      - --relay-log=relay-bin
      - --server_id=1
    environment: &environment
      TZ: Asia/Tokyo
      MYSQL_ALLOW_EMPTY_PASSWORD: 1
      MYSQL_DATABASE: test
      MYSQL_USER: test
      MYSQL_PASSWORD: pass
      MYSQL_REPLICATION_USER: repl
      MYSQL_REPLICATION_PASSWORD: pass
    volumes:
      - ./init.sh:/docker-entrypoint-initdb.d/init.sh:ro

  mysql-slave:
    image: mysql:8
    depends_on:
      - mysql-master
    command:
      - --default_authentication_plugin=mysql_native_password
      - --gtid_mode=ON
      - --enforce_gtid_consistency=ON
      - --relay-log=relay-bin
      - --server_id=2
    environment:
      <<: *environment
      MYSQL_REPLICATION_HOST: mysql-master
    volumes:
      - ./init.sh:/docker-entrypoint-initdb.d/init.sh:ro

init.sh

コンテナの /docker-entrypoint-initdb.d/init.sh にマウントされるスクリプトです。レプレーションユーザーを作成したり、スレーブを開始したりします。

オフィシャルの MySQL イメージだと entrypoint でユーザーを作成したりデータベースを作成したりいろいろ行われているので、そのままだとレプリケーションの開始後に更新が競合します。

なので、一通り共通の準備が終わったところで RESET MASTER でバイナリログをふっとばします。

#!/bin/bash

if [ -v MYSQL_REPLICATION_USER -a -v MYSQL_REPLICATION_PASSWORD ]; then
  mysql -u root -v mysql <<SQL
    CREATE USER '$MYSQL_REPLICATION_USER'@'%' IDENTIFIED BY '$MYSQL_REPLICATION_PASSWORD';
    GRANT REPLICATION SLAVE ON *.* TO '$MYSQL_REPLICATION_USER'@'%';
SQL
fi

mysql -u root -v mysql <<SQL
  RESET MASTER;
SQL

if [ -v MYSQL_REPLICATION_USER -a -v MYSQL_REPLICATION_PASSWORD -a -v MYSQL_REPLICATION_HOST ]; then
  mysqladmin ping --wait=5 -h "$MYSQL_REPLICATION_HOST"
  mysql -u root -v <<SQL
    CHANGE MASTER TO
      MASTER_HOST = '$MYSQL_REPLICATION_HOST',
      MASTER_USER = '$MYSQL_REPLICATION_USER',
      MASTER_PASSWORD = '$MYSQL_REPLICATION_PASSWORD',
      MASTER_AUTO_POSITION = 1,
      MASTER_DELAY = 10;
    START SLAVE;
SQL
fi

動作確認

docker-compose up -d

# レプリケーション状態 -> Slave_IO_Running と Slave_SQL_Running ともに Yes
docker-compose exec mysql-slave mysql -e 'show slave status \G'

# マスターにテーブルを作ってデータを入れる
docker-compose exec mysql-master mysql test -e 'create table t (id int not null primary key)'
docker-compose exec mysql-master mysql test -e 'insert into t values (1)'

# レプリケーション状態 -> Retrieved_Gtid_Set や Executed_Gtid_Set がちょっと進む
docker-compose exec mysql-slave mysql -e 'show slave status \G'

# レプリケーションされたデータが表示される
docker-compose exec mysql-slave mysql test -e 'select * from t'