こんにちはこんばんはおはようございますおやすみなさいお久しぶりです、Gehirn News ライターの KOBA789 です。

相変わらず弊社では Go 言語を書いています。そこでどうしても困るのは依存パッケージのインストールです。一人で書いているのであればよいのですが、複数人で開発をしていると、隣席から「なんか動かねぇんだけど!」とか「パッケージどれ入れりゃいいんだ!」とか怒鳴られることになります。まぁそれも「リスト作っておいたからこれ全部入れておいて〜」で解決できてしまいますし、わからなければ都度怒鳴れば聞けばいいわけです。しかし、最も問題なのは CI です。依存パッケージがインストールされていないとテストのたびに Jenkins おじさんからメールが来て怒られます。つらいです。

対策

考えられる対策はいくつかあります。まずひとつめ。

あらかじめ全てインストールしておく(努力目標)

努力目標とか御免です。誰も安全性を保証してくれません。却下。

リストを作ってそれに基づいてインストールする

Gondler とかがそんな感じですね。しかし、リストを作るのが面倒です。バージョン固定できるのは嬉しいけど、やっぱメンドイ。それにリストとソースの対応が保証されないのはやっぱりキモい。

ソースを解析して自動でインストールする

今回はこれを作りました。欠点はバージョンを固定できないこと。まぁ、適当になら使えます。

本来ならこれを応用して依存関係リストを自動生成して Gondler で〜、とかすべきでしょうが、手軽さ重点。

実装

go list というコマンドによって依存パッケージなどを解析することができます。これを用いてシェルスクリプトで実装しました。

#!/bin/bash

# Copyright (C) 2013 Hidekazu Kobayashi
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

function get_deps() {
    go list -e -f "{{range .TestImports}}{{.}};{{end}}{{range .Imports}}{{.}};{{end}}" $1
}

function get_relative_path() {
    source=$1
    target=$2

    common_part=$source
    back=
    while [ "${target#$common_part}" = "${target}" ]; do
        common_part=$(dirname $common_part)
        back="../${back}"
    done

    echo "./${back}${target#$common_part/}"
}

function resolve() {
    get_deps $1 | xargs -r -d ";" -n 1 go list -e -f "{{if not .Dir}}{{if .Incomplete}}{{.ImportPath}}{{end}}{{end}}" | xargs -r -n 1 go get -v
    CHILDREN=$(get_deps $1 | xargs -r -d ";" -n 1 go list -e -f "{{if .Dir}}{{if not .Standard}}{{if .Incomplete}}{{.Dir}};{{end}}{{end}}{{end}}" | xargs -r -d ";" -n 1 echo)
    for CHILD in $CHILDREN; do
        resolve $(get_relative_path `pwd` $CHILD)
    done
}

resolve .

使い方

パッケージ内で実行すると、依存関係を再帰で追いかけて全部インストールしてくれます。

$ cd foo_package
$ ./resolv.sh

すごく適当に使いたい時に、いい感じです。

まとめ

シェルスクリプト内の条件式が思いっきり秘伝のタレみたいになっててもう二度と読めません。意味は聞かないでください。