git.ioは非推奨(read only)になってしまったので注意する必要がありそう

表題が全てシリーズです。(シリーズとは)

完全にノーマークだったのですが、GitHubが提供していた短縮URLサービスのgit.ioが4月27日を持ってread onlyに移行しています。

2022-04-27 Update: While the git.io url redirection service is read-only and use of the service is limited, we have received feedback from developers and academic researchers who have published git.io links in print documentation and research papers. In order to preserve the integrity of these historical documents, we have decided to archive the current git.io links in a new read-only service that will allow us to serve redirects for those links longer term.

As we continue our analysis, we may remove individual links that point to spammy, malicious or 404 links. Our goal is to not break links relied on for legitimate use, especially by the academic community, while preserving the security of developers on GitHub.

That said, we still encourage users to make use of one of the many URL shortening services available with greater functionality than the git.io service provided. GitHub support will not be able to update or edit redirection records served by the git.io archive service.

github.blog

もともとは4月29日に全てのリダイレクトを止める方針だったようですが、論文等ですでに利用されていることもあり、リダイレクトは止めず、新たに追加/編集等ができない読み取り専用に移行したようです。

(これはgit.ioがスパムなどの目的で使われてしまっており、そのメンテナンスコストを鑑みての判断らしいです。)

read-onlyになったのでまだ使用できるとはいえ、非推奨になってしまったので、新たなURLに移行するのが良さそうです。

Perlの人むけ情報

Perlの人向け情報としては、cpmcpmを使ってインストールする方法が、以前はgit.ioを使ってインストールする方法が推奨されており、この問題で推奨インストール先のURLが変更されているので注意が必要です。

github.com

これは作者のid:shoichikajiさんからもCHANGELOGで変更が呼びかけられています。

github.com

差分は次のような感じです。

旧版

$curl -fsSL https://git.io/cpm | perl - install -g --with-develop --with-recommends --show-build-log-on-failure

移行版

$curl -fsSL https://raw.githubusercontent.com/skaji/cpm/main/cpm | perl - install -g --with-develop --with-recommends --show-build-log-on-failure

ということで気づきベースで変更していくのが良さそうです。git.ioがこんな状況になってたの知らなかった....。

5.32以降のdocker-perlでのcpanmは`https://www.cpan.org`に繋ぎに行くようになります

2022/06/13 追記

一旦この修正はrevertされたので、現在はhttpsで繋ぎにいかなくなっています

2022/06/03 23:46追記

skajiさんの指摘でPERL_CPANM_OPTを指定した状態だと、cpanmの一部の挙動に制限がかかることがPRに書かれました。 この状態だとmetacpanやcpanmetadbのresolverが使用できなくなるので、今後変更があるかもしれません。

github.com

追記ここまで


表題がすべてですが、 library/perl: Update for cpanm update and HTTPS install by default by zakame · Pull Request #12569 · docker-library/official-images · GitHub のPRがmergeされ次第、5.32以降のdocker-perlでのcpanmはhttps://www.cpan.orgに繋ぎに行くようになります。

これは先日docker-perlでインストールされるcpanmのバージョンを最新にしたところ、docker-perlのメンテナのzakameさんが以前のCPAN脆弱性対応のために乗り気で入れたものです。

具体的に何をしたかというと、環境変数としてPERL_CPANM_OPT"--from https://www.cpan.org"を設定しています。 これはcpanmがデフォルトで接続に行く先をhttps://www.cpan.orgにする設定です。

この変更によって表題のことが行われます。

影響のあるPerlバージョン

5.32以降の安定バージョンです。 正確に言うと5.32.1, 5.34.1, 5.36.0の3バージョンが影響を受けます。  docker-perlのtagの関係上、5.32と指定した場合は5.32.1と同じ意味になりますので、5.32以降のPerlをdocker-perlで使っている方は影響があります。

https://hub.docker.com/_/perl

対して5.30以前のPerl Imageは影響を受けません。 これはdocker-perlの現在の開発方針が、「EOLを迎えていないPerlバージョンのみImageの積極的に更新を行う」方針であるためです。

endoflife.date

(そのため現在でもDebian11(bullseye)ベースのイメージは5.30以前のPerlバージョンはなかったりします。)

以下にこの変更による影響をまとめます。

DockerImage自体

ca-certificatescurlがslimのImageでもインストールされたままになります。

github.com

cpanm

cpanmのバージョンが最新になります。

特に問題なくcpanmでモジュールインストールが可能です。 テストではMojoliciousをインストールしています。

github.com

ciの結果を見る限りしっかりhttpsでインストールされてます。

carton

Cartonに関しては環境変数が別となるのでhttpのままになります。

github.com

いやでもhttpsで接続したいんだが...!? という方はCartonの後継プロジェクトとなっているCarmelの方をお使いください。 CarmelはCartonと同じ機能を持ちながら、デフォルトでhttps://cpan.metacpan.org/に繋ぎに行くので安心です。

github.com

cpm

特に影響はないと思われます。

まとめ

  • 5.32以降のdocker-perlでのcpanmはhttps://www.cpan.orgに繋ぎに行くようになります
  • 同時にcpanmも最新になります
  • docker-perlはEOLを迎えてないPerlのバージョンのみ面倒を見ているので、docker-perlを使う方はバージョンに注意しましょう

現場からは以上です

windowsファイル転送バトル

実家に帰省するなどすると色々あり、例えばwindows8自体のPC(i3, メモリ4GB)を無理やりwindows10にしたかろうじて動いているPCからデータを別のwindowsPCに移動させるなどのミッションが生じる。

ここで外付けHDDやUSBメモリを使わないことを考えると、windows間で自宅ネットワークを活用してファイル転送をしたい。 ということで様々な方法でwindows間のファイル移動を試みた。

windows自体のファイル共有機能を使う -> 失敗

windows ファイル移動 ネットワーク越し」的なのでぐぐるとまず出てくるやつ。

support.microsoft.com

ネットワーク共有設定からファイル探索を許可した上で、共有したいファイルを選択するもの。 しかしこの方法ではwindowsPCが古すぎるのが原因か、微妙に違うSSIDに接続しているのが原因かは定かではないが、転送を受ける側のPCで転送元のPCがネットワーク探索できなかった。ので断念。

新パソコン側からsftpで旧パソコンからデータを回収する => 失敗

ファイル共有機能が使えなかったので、別の方法でネットワーク越しにファイルを転送することを考える。 市販のファイル転送ソフトは金かかるので却下。無料で行きたい。 windowsはデフォルトでrsyncが使えないこともあり、外部アプリを入れずに素手で戦う場合の次の一手はsftpになった。 windowsWinSCPというイカしたGUIアプリがあるのでこれを使うと便利。

forest.watch.impress.co.jp

sftpを使うということはつまりssh接続が必要になるのでsshdを動かす必要がある。 なんと最近のwindows10はアプリストア的なところからsshdをインストールできるので古い方のPCでインストールをしようとしたところ、なぜかGUI経由だと20%でインストールが止まる。そんな。

調べたらPowerShellでインストールする方法があったので試したらこれで行けた。やはりCUIは正義。 docs.microsoft.com

Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0

# Start the sshd service
Start-Service sshd

# OPTIONAL but recommended:
Set-Service -Name sshd -StartupType 'Automatic'

# Confirm the Firewall rule is configured. It should be created automatically by setup. Run the following to verify
if (!(Get-NetFirewallRule -Name "OpenSSH-Server-In-TCP" -ErrorAction SilentlyContinue | Select-Object Name, Enabled)) {
    Write-Output "Firewall Rule 'OpenSSH-Server-In-TCP' does not exist, creating it..."
    New-NetFirewallRule -Name 'OpenSSH-Server-In-TCP' -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
} else {
    Write-Output "Firewall rule 'OpenSSH-Server-In-TCP' has been created and exists."
}

これでsshdは元気に動き出したのだけど、sshしようとすると同頑張ってもPermissionDeniedになる。そんな。22番ポートは空いてるのに。

ユーザー側の設定がだいぶカオスな事になっていそうなので、ログインユーザーでログインするのを諦めてadministratorでsshできるように仕込んだ。

qiita.com

これでsshできたぞ!!! sftp/scpや!!! と思ったら、なんとadministratorでsshすると、移動したいファイルがsshした先では確認できるのに、sftp/scpすると確認できない(転送されない)という自体が出た。 所有権の問題かと思ってtakwownで所有権を直したのだけど、効果は無く、キレそう。

atmarkit.itmedia.co.jp

ということでこの方法も諦めて、逆の発想で新PCから旧PCにsftpしてファイルを持ってくることにした

旧パソコン側からsftpで新パソコンにデータを送る ==> 成功

新パソコン側でsshdの設定をし、旧パソコンにWinSCPをまずいれる。 このインストールが曲者で、メモリが4GBしかないのでなんか良くわからないアプリが立ち上がるとすぐ固まる。 起動時にその部分さえケアできればあとは新PCに接続し、ファイルを転送すればすんなりクリアだった。

というわけでファイル転送バトルでした。年末という感じですね。良いお年を。

いい感じにuseするのを助けてくれるApp::perlimportsのご紹介

これはPerlアドベントカレンダー2021の12日目の記事です。

昨日はshogo82148さんで、Perl 5.35.5 の iterating over multiple values at a time を先取りでした。

今回はPerlでもいい感じのモジュールのImportをしてくれるApp::perlimportsの紹介です。

App::perlImports

このモジュールはThe Perl and Raku Conference 2021で作者本人からトークされていて、スライド及び動画が上がっています。

www.youtube.com

スライドはGitHub上にHTMLがあるので手で落とす必要があるようです...。(GitHubPagesは特に使っていない模様)

curl https://raw.githubusercontent.com/oalders/presentations/main/slides/6-perlimports/remark.html -o perlimports.html && open perlimports.html

「Where Did That Symbol Come From?」とトークタイトルが示す通り、Perlのシンボルがどこからimportされたかを明確にしたいというのが実装の動機になっているようです。合わせて動き自体はgolangでimportしているモジュールをいい感じに管理してくれるgoimportsを参考にしているようです。

このモジュールについて説明するにはまず、Perlのモジュールロードがどのように行われているかを確認する必要があります。ざっと見てみましょう。

Perlのモジュールロード

Perlはモジュールをロードする方法に主にuseを使った方法とrequireを使った方法があります。*1 歴史的経緯でrequireの方が先に出ていて、 Perl5でuseが導入されています。 requireはモジュールロード以外に外部ファイルの読み込みにも使われます。

pointoht.ti-da.net

近年はuseを使ってモジュールロードをするのが一般的です。

例えば、PerlにデフォルトでバンドルされているHTTPクライアントのHTTP::Tinyを使う場合は次のようにロードします。

use HTTP::Tiny;

my $ua = HTTP::Tiny->new();

ロードタイミング

Perl動的言語でありますが、useを使ったモジュールロードの処理は、該当のPerlコードをPerlインタプリタが読み込む、コンパイルタイムに行われます。 例えば存在しないHogeモジュールをロードしようとすると、次のようにprintが走ることはなくエラーで死にます。

#!/usr/bin/env perl

use strict;
use warnings;
print "hello";
use Hoge;
❯ perl hoge.pl
Can't locate Hoge.pm in @INC (you may need to install the Hoge module) (@INC contains: /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/site_perl/5.32.0/darwin-2level /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/site_perl/5.32.0 /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0/darwin-2level /Users/anatofuz/.plenv/versions/5.32.0/lib/perl5/5.32.0) at hoge.pl line 2.
BEGIN failed--compilation aborted at hoge.pl line 2.

対して、各関数呼び出しなどの処理は実行時に解決されます。 そのためuseを忘れても、対象のメソッドを実行しない限りは処理が止まることはありません。 例えば次のコードは、HTTP::Tinyをuseしていませんが、呼び出す前にdie(Perlでのエラー終了)をして処理を止めるので、ロードエラーは発生しません。

#!/usr/bin/env perl
use strict;
use warnings;


print "hello\n";
die;

my $ua = HTTP::Tiny->new();
$ua->get('https://example.com');
perl hoge.pl
hello
Died at hoge.pl line 6.

useの処理

ではuseは何をしているのでしょうか。Perldoc.jpを見るとこう書かれています。

指定したモジュールから、現在のパッケージにさまざまな内容をインポートします; 多くは、パッケージのサブルーチン名や、変数名に別名を付けることで、 実現されています。 これは、以下は等価ですが

BEGIN { require Module; Module->import( LIST ); }

https://perldoc.jp/func/use

BEGIN { }はBEGIN ブロックと呼ばれるもので*2コンパイルタイムに実行させるという処理です。 中ではrequireを使い、モジュールを読み込んだ後に、そのモジュールにあるimportメソッドを実行しています。

つまり、前述したPerlのモジュールロードのuserequireを比較すると次のような違いがあることがわかります。

項目 use require
ロードタイミング コンパイルタイム 実行時
副作用 importを実行する 読み込みのみ

整理したところで、このimportとは一体何でしょうか。 一般的には、これはモジュールで定義しているメソッドを、呼び出し元のコードで定義したかのように使うために実行されるものです。 詳しく見てみましょう。

import

Perlでは特定のモジュールをuseすると使えるようになるメソッドがいくつか存在します。 例えばコアモジュールのJSONを扱うライブラリであるJSON::PPをuseすると、任意のPerlのハッシュをJSON文字列化できるencode_jsonが使えるようになります。

use strict;
use warnings;

use JSON::PP;

print encode_json({hoge => 'hello', isBool => \0});

encode_jsonJSON::PPで定義されているメソッドなので、通常はJSON::PP->encode_jsonのように使う必要があります。 importは特定のメソッドを、useしたタイミングで通常の関数のように呼び出せるようによしなにやってくれる処理になっています。

この処理のことをPerlでは関数のエクスポートなどと読んでいます。 近年ではExporterモジュールが行っています。 あるモジュールで、Exporterモジュールを継承、もしくはuseすると、そのモジュール内で自動的に関数のエクスポート処理をimport関数として登録してくれます。

あくまでExporterを使った場合なので、独自にimport関数を定義することも可能です。 その場合は通常の関数のように呼び出せるようによしなにやってくれる処理以外の処理をuse時に行うように差し込むことが可能となります。 例えばClass::Accessor::LiteはimportをExport以外の目的に使っており、オブジェクトのアクセサを生成する処理を行っています。

use Class::Accessor::Lite (
    new => 1,
    rw  => [ qw(foo bar) ],
    ro  => [ qw(baz) ],
    wo  => [ qw(hoge) ],
);

Perlでモジュールをuseする際に、exportしたい関数名の指定ではないような引数を渡しているモジュールは、ほぼ独自にimportを書いていると考えても良いでしょう。

use時に差し込まれるモノたち

Exporterを使っているモジュールがuseされたときに差し込むものは、モジュール側で定義されている@EXPORT配列に積んだシンボルになっています。((ここでのシンボルはPerlのメソッド(関数、サブルーチン)の他に、スカラ変数や配列も含みますが、Exporterを使う上ではメソッド以外のシンボルを使うと問題が発生しがちなので、基本はメソッドのことです ))

例えば、Hoge::Firstモジュールをuseしたときに、hogeメソッドを差し込みたい場合は、次の様に定義します。

package Hoge::First;
use strict;
use warnings;
use feature qw/say/;

use Exporter 'import';

our @EXPORT = (qw/hoge/); #サブルーチンの名前を文字列で配列の中にいれるとEXPORTされる

sub hoge {
  say 'this is first!';
}

1;

Perl@EXPORTに積まれているオブジェクトをすべてロードします。 このためuseしているモジュールが、内部で@EXPORTに何かしら詰めていた場合、予期せぬシンボルをロードする可能性があります。 また、あるPerlファイルの中で出てきているメソッドが、一体どのモジュールから提供されているか、はたまたデフォルトのメソッドであるかの判断が難しくなってしまいます。 使用しているメソッドが、どのモジュールから提供されているのが解りづらいのは、リファクタリングの結果すでにあるモジュールが提供しているメソッドを使わなくなったのに、useし続けてしまうモジュールが出たり、逆にプログラミングしている上で使えるだろうと思って書いたメソッドが、実はuseしないといけなかったなどの問題を誘発しがちです。

また、同じ名前のオブジェクトをロードしてしまうと、呼び出し順序によって使われる関数が異なってしまいます。 例えばHoge::FirstHoge::SecondモジュールでそれぞれhogeをEXPORTしてみます。

package Hoge::First;
use strict;
use warnings;
use feature qw/say/;

use Exporter 'import';

our @EXPORT = (qw/hoge/);

sub hoge {
  say 'this is first!';
}

1;
package Hoge::Second;
use strict;
use warnings;
use feature qw/say/;

use Exporter 'import';

our @EXPORT = (qw/hoge/);

sub hoge {
  say 'this is second!';
}

1;
#!/usr/bin/env perl
# main.pl
use strict;
use warnings;

use Hoge::First;
use Hoge::Second;

hoge();

実行すると、最後にロードされたSecondが出てきます。

❯ perl -Ilib hoge.pl
this is second!

@EXPORTはこのような思いがけない処理が走る可能性があるので、Exporterモジュールのコメントにも「とりあえずEXPORTするのはやめよう」といったことが書かれています。 pointoht.ti-da.net

さらにはPerlのLinterであるPerl::Criticにも@EXPORTを警告するルールが追加されています。

metacpan.org

とはいえ関数のエクスポートは便利なので、それはそれで使用したいです。 Perlでは@EXPORTの他に@EXPORT_OKがあります。これはデフォルトではすべてをロードせず、use時にエクスポートしたい関数名を書くことを強制させる機能です。

例えばこんな感じにuse時に関数名を指定します。

use JSON qw(encode_json);

実は@EXPORTで宣言している場合もuse時に関数名を指定している場合はそのメソッドしかエクスポートされません。関数名を何も指定せずに()を書いた場合は何もエクスポートされないため、宣言的に関数名を書くようにするとほぼ@EXPORT_OKと同様の振る舞いをします。

自分が書いているコードに出てくるメソッドの宣言元をはっきりさせる単純な解決方法はEXPORTをやめ、Hoge::Foo->bar()の様にフルパスでメソッドを実行することですが、テストヘルパなどEXPORTした方が利便性が高いものもあるでしょう。 decode_utf8など、EXPORTされたメソッドを使うのがイディオム的に認知されているものもあります。 エクスポートした関数を使うのと、どこから取り込んできたのかの視認性を高めるには、use時にエクスポートしたい関数名を漏れなく書くしか無いでしょう。 しかしこれを人間がやるのは面倒!!! ということで作られたのがperlimportsです。

....と、ここまで書いたところで奇跡的に裏番組のはてなエンジニア Advent Calendar 2021id:papixさんが紹介している内容と前提知識が同じだった!!そんなことってあるんだ

papix.hatenablog.com

あらためてApp::perlImports

  • どこから来たのかわからないシンボルをエクスポートしているuseを特定したい
  • @EXPORTを全部インポートするのではなく、一部だけ指定してインポートしたい

これらの課題に対して、golangのgoimportsを参考に、構文解析ライブラリPPIの力を借りて誕生したのがApp::perlimportsです。 metacpan.org

著者のOALDERSさんはLWPなどのメンテナもやられています。

使ってみる

例えば次の様なモジュールがあったとしましょう。

package MyPkg::One;

use strict;
use warnings;

use Exporter 'import';
our @EXPORT = qw(f1);

sub f1 {
  print "f1\n";
}

1;
package MyPkg::Two;

use strict;
use warnings;

use Exporter 'import';
our @EXPORT_OK = qw(f2);

sub f2 {
  print "f2\n";
}

1;
package MyPkg::Three;

use strict;
use warnings;

use Exporter 'import';
our @EXPORT_OK = qw(f3);

sub f3 {
  print "f3\n";
}

1;
package MyPkg::Four;

use strict;
use warnings;

use Exporter 'import';
our @EXPORT = qw(f4);

sub f4 {
  print "f4\n";
}

1;

これをhoge.plから以下の様に呼び出します。

use strict;
use warnings;
use MyPkg::One;
use MyPkg::Two;
use MyPkg::Three;
use MyPkg::Four;


f1();
f2();
MyPkg::Three->f3();

hoge.plの状況は次のとおりです。

  • f1はMyPkg::OneがEXPORTしているものを使っている
  • f2はMyPkg::TwoがEXPORT_OKしているが、コード上は指定を忘れている
  • f3はフルパスで呼び出している
  • f4はuseしているが特に使っていない

このコードはそのまま実行すると, f2の解釈で実行時エラーが発生します。また、どのモジュールからエクスポートされているのかパット見ではわかりません。 さらにMyPkg::Fourは必要がないf4をエクスポートしてしまっています。

❯ perl -Ilib hoge.pl
f1
Undefined subroutine &main::f2 called at hoge.pl line 10.

これをApp::perlimportsに通すと、以下のようにコードが書き換わります。

❯ perlimports --libs lib  hoge.pl
use strict;
use warnings;
use MyPkg::One qw( f1 );
use MyPkg::Two qw( f2 );
use MyPkg::Three ();
use MyPkg::Four ();


f1();
f2();
MyPkg::Three->f3();

見ると次の様な変換が行われています。

  • f1,f2がEXPORT元のモジュールが明記されるようになった
  • エクスポートした関数を使用していないThreeFourは、空配列を指定することになり、何もエクスポートしなくなっている

変換されたコードはそのまま動かす事ができます。(今回はパイプで動かしています) こうすると、実行時エラーを未然に防ぐことが可能になり、どのモジュールからエクスポートしているか、パット見でわかるようになりました。

❯ perlimports --libs lib  hoge.pl | perl -Ilib
f1
f2
f3

さらに、無駄にロードしているMyPkg::Fourを削除することも可能です。

❯ perlimports --libs lib --no-preserve-unused  hoge.pl
use strict;
use warnings;
use MyPkg::One qw( f1 );
use MyPkg::Two qw( f2 );
use MyPkg::Three ();


f1();
f2();
MyPkg::Three->f3();

こうすると、無駄なファイルをuseし続ける事もなくなり便利ですね!

インストール方法

cpanmもしくはcpmを使ってインストールしましょう

$ cpanm App::perlimports
$ cpm install -g App::perlimports
$ plenv rehash

使い方

CLIツールとして提供されているので、perltidyの様にコマンドで実行します。

$  perlimports --libs lib,local/lib/perl5 -f hoge.pl

主要なオプションを見てみます。

  • --libs
    • Perl-Iと同様。基本はlibを指定することになるでしょう
    • カンマ区切りです
    • Carton, cpmでinstallしたローカルディレクトリ上のモジュールも含めたい場合は、local/lib/perl5を加えます
  • -f
    • 変換対象のファイル名
  • --no-padding
    • perlimportsが変換したあと、モジュール名の前後に余計な空白をいれない
    • デフォルトではuse Foo qw( bar baz );の様になるが、--no-paddingを指定するとuse Foo qw(bar baz);になる
  • --inplace-edit, -i
    • デフォルトでは変換したコードは標準出力に出るが、このオプションを指定すると変換元のファイルを上書きする
  • --no-preserve-unused
    • 使っていなさそうなモジュールをuseしないようにする

注意点

自動でuseしてくれる訳ではない

下手にgoimportsを使ったことがあると誤解しやすい点で、perlimportsの(現状)の興味の対象はEXPORTしたシンボルをいい感じに特定することのみです。goimportsはimportの追加などもしていましたが、それに対応する機能はありません。モジュールのuseし忘れなどは興味の範囲外になっています。

例えばMyPkg::One->f1()の様なメソッド呼び出しをしていて、MyPkg::Oneをuseし忘れた場合、perlimportsはこれを補完しません。 また、そもそもuseしているモジュールがない状態で実行しても補完はしてくれません。

そのためモジュールのuse忘れなどのimportエラーは別系統で判断する必要があります。

❯ cat hoge.pl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};

f1();
~/workspace/test3 via 🐪 v5.34.0
❯ perlimports --libs lib -f hoge.pl
use strict;
use warnings;
use FindBin ();
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};

f1();

関数エクスポート以外のuseは工夫の余地がある

また、Class::Accessor::LiteClass::Enumemonのようなuse時にエクスポートする関数名を指定する以外に、独自にimportsを定義しているモジュールは、perlimportsはuseしたものを使っていないと見なしてしまいます。これはperiimportsの作者によって名指しでperlimportsの自動整形下に置かない様に書かれているモジュール群か、構文解析の結果自動調整を逃れたもの以外は、空配列のimportに変換されてしまいます。

❯ cat hoge.pl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};
use Class::Accessor::Lite (
    rw => [qw/ hoge/],
);

f1();


~/workspace/test3 via 🐪 v5.34.0
❯ perlimports --libs lib,local/lib/perl5 -f hoge.pl
use strict;
use warnings;
use FindBin ();
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};
use Class::Accessor::Lite ();

f1();

これを回避するには、調整したくないモジュールをuseしている行の後ろに特定のアノテーション(## no perlimports)を書くか*3コマンド実行時の引数でモジュール名を直接列挙、またはモジュール名が列挙されたファイルを渡すことで回避可能です。この指定には正規表現が使用可能です。

作者にPRを送るとignoreするモジュールに追加してくれる様なので、普段使うモジュールで管理されるとまずいものがあればPRを送ると良いでしょう。

~/workspace/test3 via 🐪 v5.34.0
❯ cat hoge.pl
use strict;
use warnings;
use FindBin;
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};
use Class::Accessor::Lite (
    rw => [qw/ hoge/],
);

f1();
~/workspace/test3 via 🐪 v5.34.0
❯ perlimports --libs lib,local/lib/perl5 --ignore-modules-pattern '^Class::' -f hoge.pl
use strict;
use warnings;
use FindBin ();
use lib "$FindBin::Bin/lib";

use DDP {deparse => 1, hash_max => 0};
use Class::Accessor::Lite (
    rw => [qw/ hoge/],
);

f1();

メソッドの追加などの副作用で持つモジュールをuseしている場合は、--no-preserve-unusedに気をつける

Perlのモジュールでは、useすると自分以外のモジュールのメソッドをオーバーライドやメソッドを名前空間に追加するものがあります。 例えばHTTP::Message::PSGIは、useするとHTTP::Request名前空間to_psgiがメソッドとして追加されます。

これはuseすることで効果を発揮するモジュールであるため、HTTP::Message::PSGIがEXPORTしているメソッドを直接使っていないケースや、HTTP::Message::PSG->で何かを呼び出しているコードが無い限り、perlimportsで--no-preserve-unusedオプションを付けて実行してしまうと、何も意味がないモジュールを呼び出しているとuseを削除されてしまいます。

実際はHTTP::Message::PSGIはperlimports側で明示的に整形する対象に含まないように指定されているので、この様なことは起きないのですが、自分たちで書いているライブラリに似た挙動があるものがあれば、使用する際は気をつけましょう。


明日はid:mp0liiu さんで、immutableなコレクションを作るです!

*1:動的ロードなどを考慮するとこの限りではない

*2:awkから来ていた気がする

*3:ブロック的に囲うことも可能です

https://metacpan.org/pod/perlimports#ANNOTATIONS/IGNORING-MODULES

ラズパイにgolangで書かれているdocker composeをinstallする

多分令和最新版。

linuxでdocker composeを使いたい場合、macOSwindowsDocker for xxx系とは違い、docker composeはバンドルされてないので、自分でインストールする必要があった。

特にラズパイではdocker-compose時代(Pythonスクリプト)の場合はpipでインストールする必要があった。 我々はdockerでローカルのことを考えずに動かしたいのにPython3関係の構築をしないといけないのは面倒....。

現代ではdocker composeはgolangで書き直されており、これを使う場合はpip installが不必要。 さらにARMバイナリ用のdocker composeはGitHub Releaseで配布されているので、落としてくれば使えるという極めて親切な設計になっている。

今回はすでにdockerはインストールされているものとして、docker composeのインストールを見てみる。

インストール方法

基本はcomposeのREADMEに書いてあるとおり。

github.com

自分だけで使う場合は、$HOME/.docker/cli-pluginsに、全ユーザーで使いたい場合は/usr/local/lib/docker/cli-pluginsなどにdocker-composeの名前でバイナリをポン置きすれば使えるようになる。

バイナリはリリースから持ってくれば良い。

github.com

気をつける点として、uname -mすると微妙にarmv7lとcomposeで打たれているアーキテクチャ名(armv7)と異なる。ワンライナーの中でuname -mしていい感じに取得しようとする場合は注意。

$ uname -m
armv7l

自宅のラズパイ4で、全ユーザーにインストールしたい場合は次のような感じでインストールする。

とりあえずrootになり

$ sudo -i 

後は素朴に

# mkdir -p  /usr/local/libexec/docker/cli-plugins
# cd /usr/local/libexec/docker/cli-plugins
# wget https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-armv7 -O docker-compose
# chmod +x docker-compose

こうするとdocker composeが使えるようになる。

$ docker compose

Usage:  docker compose [OPTIONS] COMMAND

Docker Compose

Options:
      --ansi string                Control when to print ANSI control characters ("never"|"always"|"auto") (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --env-file string            Specify an alternate environment file.
  -f, --file stringArray           Compose configuration files
      --profile stringArray        Specify a profile to enable
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the Compose file)
  -p, --project-name string        Project name

Commands:
  build       Build or rebuild services
  convert     Converts the compose file to platform's canonical format
  cp          Copy files/folders between a service container and the local filesystem

バイナリポン置きなのでローカル環境に色々いれなくていいので便利ですね。現場からは以上です。

入社してから書いていた分報の行数を眺めてみる

この記事ははてなエンジニア Advent Calendar 2021 の4日目の記事です。

昨日は同期のid:gurriumくんでした。MOD Pythonで色々できるんだ...。僕も趣味で好きなゲームに色々コード書いて楽しんでますが、それは社のアドベントカレンダーには書けないゲームなのでまたの機会に書こうと思います。

社ではScrapboxを使っていて、僕は分報の置き場として色々書いています。個人的にはTwitter感覚で使っています。 分報は以下のフォーマットで基本書いています。

anatofuznote 2021/11/29 - 12/03
[anatofuznote 2021/11/22 - 11/26]  <=> [anatofuznote 2021/12/06 - 12/10]

#anatofuz #anatofuznote
[** 2021/12/3]

実際には↓の用な感じでScrapbox上は見れる f:id:anatofuz:20211204183001p:plain

忙しいときはツイート数が減っている気がしていて、逆になんか盛り上がっているときはツイート数が多いかなとなんとなく感じています。 つまりツイート数で自分の状態が測れるのではと思っています。 せっかくなので、本当にそうなのか調べてみました!!! 分報なのでツイート数==scrapboxの行数として考えてみます。

準備

APIを使ってかっこよくやるとかは頭になく、素朴にざっくりと外観が知られればいいので、雑スクリプトで集計します。 分報は先程例示したフォーマットで書いているので、[** 2021/%m/%d]に囲まれている間の行数を雑にカウントする方法でやってみます。

データはどうやって持ってくるかというと、コピペですね。はい。素朴にやるのが早いねんな。 ということで次のようなPerlスクリプトを書きました。

use strict;
use warnings;
use Time::Piece;

my $current_date = undef;
my $result = {};

while (my $line = <DATA>) {
  if ($line =~ /^\A\s*\[\*.+\s*(2021.*)\]/) {
      $current_date = $1;
  }
  $result->{$current_date}++;
}


my @sorted_date = sort {  Time::Piece->strptime($a, '%Y/%m/%d') <=>  Time::Piece->strptime($b, '%Y/%m/%d') } keys %$result;

for my $date (@sorted_date) {
    print "$date $result->{$date}\n";
}


__DATA__
[** 2021/12/3]
  今日も
  1日
[** 2021/12/2]
   オハヨッ
....

Perl__DATA__以下に書いたテキストを<DATA>で読めるので、Scrapboxからかき集めた分報をDATA以下にコピっています。 それを素朴な集計方法で数え上げて、最後に日付順にソートして表示します。

実行するとこんな感じ

❯ perl note.pl | head -4
2021/4/8 503
2021/4/9 11
2021/4/12 260
2021/4/14 256

これをグラフ化したいので、学生時代から慣れ親しんでいるgnuplotでシュッとグラフを作ります。matplotlib? 俺はgnuplotのほうが慣れてるんだ...。

❯ gnuplot

        G N U P L O T
        Version 5.4 patchlevel 2    last modified 2021-06-01

        Copyright (C) 1986-1993, 1998, 2004, 2007-2021
        Thomas Williams, Colin Kelley and many others

        gnuplot home:     http://www.gnuplot.info
        faq, bugs, etc:   type "help FAQ"
        immediate help:   type "help"  (plot window: hit 'h')

Terminal type is now 'qt'
gnuplot> set term svg

Terminal type is now 'svg'
Options are 'size 600,480 fixed enhanced font 'Arial,12' butt dashlength 1.0 '
gnuplot> set output 'out.svg'
gnuplot> set xdata time
gnuplot> set timefmt '%Y/%m/%d'
gnuplot> plot '<perl note.pl' using 1:2 with line
gnuplot> exit

ということでできたのがこれです。

GnuplotProduced by GNUPLOT 5.4 patchlevel 2 0 100 200 300 400 500 600 04/01/21 05/01/21 06/01/21 07/01/21 08/01/21 09/01/21 10/01/21 11/01/21 12/01/21 01/01/22 '<perl note.pl' using 1:2 '<perl note.pl' using 1:2

見ていると入社当時は盛り上がっていたものの、9月から口数が少なくなり、11月は一般人みたいなツイート数になってましたね..。 最近ツイート数が上がってきているので盛り上がってきているという感じなきがする。 初期が長めなのは環境構築のログがはられていたりするからっぽかった。

可視化してみると色々見れて便利ですね。ここまで書いてmackerelのサービスメトリックに日毎に送信すると面白いのでは? みたいな気持ちになったので、調べたところ、過去のデータは送信できなさそう? なので次回にご期待ください。

明日はid:mizdraさんです。