kikukawa's diary

都内で活動するシステムエンジニアが書いてます。 興味を持った技術やハマったポイント、自分用メモをつけてます。 最近はweb中心

Docker内でGithubのプライベートリポジトリのdepを解決する

goのパッケージ管理ツールのdepに 自作のプライベートリポジトリを登録している状態で Docker上で、dep ensureすると Githubの認証に弾かれます。 それを解決する方法です。

基本的には dep公式のFAQに書いてある通りです。 depは、 .netrc を頼りにGithubの認証を解決するので、これをDockerfileで作って上げます。 ただ、FAQに書かれているフォーマットは

machine github.com
    login [YOUR_GITHUB_USERNAME]
    password [YOUR_GITHUB_TOKEN]

ですが、これだとうまく行かなかったので

machine github.com
    login [YOUR_GITHUB_TOKEN]

という形にしています。

Dockerfileはこんな感じになります

FROM golang:alpine

RUN apk --update add tzdata git && \
    : "depのインストール" \
    mkdir -p $$GOPATH/bin && \
    go get github.com/golang/dep/cmd/dep

ADD . /go/src/github.com/foo/bar
WORKDIR /go/src/github.com/foo/bar

EXPOSE 8080

CMD  echo machine github.com > /root/.netrc && \
     echo login [YOUR_GITHUB_TOKEN] >> /root/.netrc && \
     dep ensure -vendor-only

生成するGithubトークンのパーミッションは、 repo にだけチェックがついてれば十分です。

composer.lockのhashがコンフリクトしたとき

毎回調べてるのでメモ

git管理しているcomposer.lockがコンフリクトし、
かつそのコンフリクトの内容がhash, content-hash だけだった場合は、 そこを更新しなおすだけで良いです。

git checkout --ours composer.lock
composer update --lock

上記では --ours でcheckoutしてますが、どっちの内容残しても結局更新し直すので --theirsでも問題ありません。

phpmdでFileCacheDriverのエラー

自分用メモ。
vagrant環境で、phpmdを走らせててエラーが発生しました。
原因は、vagrantのhdの容量不足でした。

エラー内容

下記のようなエラーが大量に流れてて、
原因が分からずしばらく悩みました。
細かいエラーメッセージはちょっとずつ違いますが、
共通しているのはFileCacheDriverでエラーを起こしていることです

PHP Warning:  fopen(/home/vagrant/.pdepend/4q/4q542z177ea.5.6.cache): failed to open stream: No such file or directory in /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Util/Cache/Driver/FileCacheDriver.php on line 163
PHP Stack trace:
PHP   1. {main}() /path/to/vendor/phpmd/phpmd/src/bin/phpmd:0
PHP   2. PHPMD\TextUI\Command::main() /path/to/vendor/phpmd/phpmd/src/bin/phpmd:122
PHP   3. PHPMD\TextUI\Command->run() /path/to/vendor/phpmd/phpmd/src/main/php/PHPMD/TextUI/Command.php:173
PHP   4. PHPMD\PHPMD->processFiles() /path/to/vendor/phpmd/phpmd/src/main/php/PHPMD/TextUI/Command.php:133
PHP   5. PHPMD\Parser->parse() /path/to/vendor/phpmd/phpmd/src/main/php/PHPMD/PHPMD.php:222
PHP   6. PDepend\Engine->analyze() /path/to/vendor/phpmd/phpmd/src/main/php/PHPMD/Parser.php:123
PHP   7. PDepend\Engine->performParseProcess() /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Engine.php:323
PHP   8. PDepend\Source\Language\PHP\AbstractPHPParser->parse() /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Engine.php:575
PHP   9. PDepend\Util\Cache\Driver\FileCacheDriver->store() /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Source/Language/PHP/AbstractPHPParser.php:403
PHP  10. PDepend\Util\Cache\Driver\FileCacheDriver->write() /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Util/Cache/Driver/FileCacheDriver.php:151
PHP  11. fopen() /path/to/vendor/pdepend/pdepend/src/main/php/PDepend/Util/Cache/Driver/FileCacheDriver.php:163

容量を喰う理由

phpmdを走らせると、依存ライブラリのpdepend がhome配下に、.pdepend というディレクトリを作り、
そこにファイルのキャッシュを溜め込みます。
このディレクトリの容量が増え続けるので、容量が足りないと正常に実行できません。

検証ファイル数が膨大というわけではないですが、 最終的に500M程度の容量が必要でした。

PHPMDのルールセットの作り方

phpmdのrulesetはxmlファイルとしてまとめておくことができます。
このファイルの内容を毎回調べてるので自分用メモ
公式ドキュメントに書いてあることがほとんどです。

https://phpmd.org/documentation/creating-a-ruleset.html

初期状態

雛形です。
nameとdescriptionは、それぞれ自分のプロジェクトにあったものにします。
descriptionはなくても大丈夫です。
ファイルの配置場所と名前はなんでも大丈夫ですが、
私は、大抵の場合、phpcsと一緒に使うので、phpmd.xmlというファイル名にすることが多いです。
配置場所は、プロジェクトのルートディレクトリにおいてしまうことが多いです。

<?xml version="1.0"?>
<ruleset name="My first PHPMD rule set"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <description>
        My custom rule set that checks my code...
    </description>
</ruleset>

ルールセットを追加してみる

phpmdが元々持っているunused code のrulesetを追加してみます。
使ってない変数とかを警告してくれるものなので、どんなプロジェクトでも入れるでしょう。
下記の書き方だと、 unused codeのすべてのruleを追加します。

unusedcodeの部分はドキュメントを見ても、
具体的にどんなものを指定すればよいか書いてないのですが、

https://phpmd.org/rules/index.html

のCurrent Rulesetsに書いてあるリストを、全て小文字にして、Rulesを消したもので大丈夫です。
具体的には、 Unused Code Rules: だったら、unusedcodeになります。

関係ないですが、下記ではdesctiprionを消してます。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
</ruleset>

ルールセットの中から一つだけルール追加してみる

さっきは、phpmdが用意してくれているルールセットの中から一つを選んで、
そのルールセットが持っているすべてのルールを追加しました。
今度は、ルールを一つだけ追加してみます。

Controversial Rulesというルールセットだと、
メソッド名などにキャメルケースを強制させるルールがあります。
しかし、プロジェクトによっては、スネークケースにしているところもあるでしょう。
その場合でも、Superglobals のルールだけは適用したいという場合の書き方です。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
    <!-- スネークケースのプロジェクトなので、Superglobalsだけ適用 -->
    <rule ref="rulesets/controversial.xml/Superglobals"/>
</ruleset>

ちなみに、1つのルールだけを除外したい場合は、excludeを使います。
例えば、Controversial Rulesというルールセットから、
CamelCaseClassName だけを除外したい場合は
下記のように書きます。

<rule ref="rulesets/controversial.xml">
    <exclude name="CamelCaseClassName"/>
</rule>

なので、前述のSuperglobalsだけ適用する場合は、

<rule ref="rulesets/controversial.xml/Superglobals"/>

このように書き換えても同様です。
適用ルールが多いか、除外ルールが多いかで決めればいいと思います。

<rule ref="rulesets/controversial.xml">
    <exclude name="CamelCaseMethodName"/>
    <exclude name="CamelCaseParameterName"/>
    <exclude name="CamelCaseVariableName"/>
    <exclude name="CamelCasePropertyName"/>
    <exclude name="CamelCaseClassName"/>
</rule>

ルールのプロパティを変えたい場合

Naming Rules を適用すると、$id というよく使う変数名まで警告されてしまいます。
ShortVariable ルールのデフォルトのminimumが3だからです。
2文字以下の変数名は警告されます。
なので、$id だけは、警告から除外されるようにします。

ポイントは、一旦ShortVariableを除外することです。
除外しないと、元々の値が適用されてしまいます。
プロパティ名やどんな値を指定するかは、ドキュメントに書いてあります。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <!-- unused code の rule set を追加 -->
    <rule ref="rulesets/unusedcode.xml"/>
    <!-- スネークケースのプロジェクトなので、Superglobalsだけ適用 -->
    <rule ref="rulesets/controversial.xml/Superglobals"/>
    <!-- id という変数名だけは許可 -->
    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable"/>
    </rule>
    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="exceptions" value="id"/>
        </properties>
    </rule>
</ruleset>

汎用サンプル

コードスタイルにPSRを適用していて、
phpunitで、テストコード書いているようなプロジェクトなら、
全部盛りで下記のような感じになると思います。

TooManyMethods,TooManyPublicMethodsで、
メソッド名がtestから始まる場合は、カウントから除外するようにしています。
テストクラスは、メソッドが増えること多いので。

<?xml version="1.0"?>
<ruleset name="PHPMD rule set for foo"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                     http://pmd.sf.net/ruleset_xml_schema.xsd"
         xsi:noNamespaceSchemaLocation="
                     http://pmd.sf.net/ruleset_xml_schema.xsd">
    <rule ref="rulesets/unusedcode.xml"/>
    <rule ref="rulesets/design.xml"/>
    <rule ref="rulesets/controversial.xml"/>
    <!-- id という変数名だけは許可 -->
    <rule ref="rulesets/naming.xml">
        <exclude name="ShortVariable"/>
    </rule>
    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="exceptions" value="id"/>
        </properties>
    </rule>
    <!-- テストのメソッドはカウントから除外 -->
    <rule ref="rulesets/codesize.xml">
        <exclude name="TooManyMethods"/>
        <exclude name="TooManyPublicMethods"/>
    </rule>
    <rule ref="rulesets/codesize.xml/TooManyMethods">
        <properties>
            <property name="ignorepattern" value="(^(set|get|test))i"/>
        </properties>
    </rule>
    <rule ref="rulesets/codesize.xml/TooManyPublicMethods">
        <properties>
            <property name="ignorepattern" value="(^(set|get|test))i"/>
        </properties>
    </rule>
</ruleset>

composer scriptでphpのci周りを登録する

毎回調べているのでメモ

phpでciを回すのによく使うものに

があります。
php7ならphanもですね。

これを、composerのrun-script 経由で叩けるようにしておくと、
毎回オプションなどを指定しなくて済むので楽です。
書き方を毎回調べているのでメモ的に残します。

composer.json(抜粋)

    "scripts": {
        "l": "php -l ./src",
        "test": "./vendor/bin/phpunit -c ./phpunit.xml",
        "cs": "./vendor/bin/phpcs --standard=./codesniffer/ruleset.xml ./src ./tests",
        "cbf": "./vendor/bin/phpcbf --standard=./codesniffer/ruleset.xml ./src ./tests",
        "md": "./vendor/bin/phpmd ./src,./tests text ./messdetector/ruleset.xml",
        "ci": [
            "@l",
            "@cs",
            "@md",
            "@test"
        ]
    }

解説

各コマンドのキーになっているところが、 composerのサブコマンドになります。
サブコマンドは、@ つきでエイリアスとして使用できるので、2重定義しなくて済みます。
複数のコマンドを実行したい時は、配列で指定します。

実行方法

composer run-script ci

定義した ci を実行します。
または、単に

composer ci

としても動きます。 サブコマンドにオプションを渡したいときは、 -- で区切ります。

composer run-script test -- --group=Foo

上記は、 test に--group=Foo を渡して実行してくれます。

./vendor/bin/phpunit -c ./phpunit.xml --group=Foo

と同義です。