JenkinsでSubModuleの更新に失敗する

経緯

複数のリポジトリから参照されるスタイル設定情報をSubModule化しました。
ローカルでは問題なかったのですが、Jenkinsでエラーが発生するようになります。

対応

Jenkinsのジョブのソースコード管理>GitからAdvanced sub-modules behavioursを追加します。
Recursively update submodulesをチェックするとサブモジュールも更新されるらしい。
が、それでもエラーが出ます・・・

00:00:12.424  > git remote # timeout=10
00:00:12.426  > git submodule init # timeout=10
00:00:12.445  > git submodule sync # timeout=10
00:00:12.464  > git config --get remote.origin.url # timeout=10
00:00:12.469  > git submodule update --init --recursive
00:00:12.543 FATAL: Command "git submodule update --init --recursive" returned status code 1:
00:00:12.543 stdout: 
00:00:12.543 stderr: Cloning into 'submodule_lib'...
00:00:12.543 fatal: Authentication failed for 'http://git.example.com/scm/projects/submodule_lib.git/'
00:00:12.543 Clone of 'http://git.example.com/scm/projects/submodule_lib.git' into submodule path 'submodule_lib' failed
00:00:12.543 
00:00:12.543 hudson.plugins.git.GitException: Command "git submodule update --init --recursive" returned status code 1:
00:00:12.543 stdout: 
00:00:12.543 stderr: Cloning into 'submodule_lib'...
00:00:12.543 fatal: Authentication failed for 'http://git.example.com/scm/projects/submodule_lib.git/'
00:00:12.543 Clone of 'http://git.example.com/scm/projects/submodule_lib.git' into submodule path 'submodule_lib' failed
00:00:12.543 
00:00:12.543     at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:1719)
00:00:12.543     at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.access$500(CliGitAPIImpl.java:63)
00:00:12.543     at org.jenkinsci.plugins.gitclient.CliGitAPIImpl$7.execute(CliGitAPIImpl.java:967)
00:00:12.543     at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler$1.call(RemoteGitImpl.java:152)
00:00:12.543     at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler$1.call(RemoteGitImpl.java:145)
00:00:12.543     at hudson.remoting.UserRequest.perform(UserRequest.java:152)
00:00:12.543     at hudson.remoting.UserRequest.perform(UserRequest.java:50)
00:00:12.543     at hudson.remoting.Request$2.run(Request.java:332)
00:00:12.543     at hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:68)
00:00:12.543     at java.util.concurrent.FutureTask.run(FutureTask.java:262)
00:00:12.543     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
00:00:12.543     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
00:00:12.543     at java.lang.Thread.run(Thread.java:745)
00:00:12.543     at ......remote call to ci-slave-linux1(Native Method)
00:00:12.543     at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1416)
00:00:12.543     at hudson.remoting.UserResponse.retrieve(UserRequest.java:252)
00:00:12.543     at hudson.remoting.Channel.call(Channel.java:781)
00:00:12.543     at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler.execute(RemoteGitImpl.java:145)
00:00:12.543     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
00:00:12.543     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
00:00:12.543     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
00:00:12.543     at java.lang.reflect.Method.invoke(Method.java:606)
00:00:12.543     at org.jenkinsci.plugins.gitclient.RemoteGitImpl$CommandInvocationHandler.invoke(RemoteGitImpl.java:131)
00:00:12.543     at com.sun.proxy.$Proxy83.execute(Unknown Source)
00:00:12.543     at hudson.plugins.git.extensions.impl.SubmoduleOption.onCheckoutCompleted(SubmoduleOption.java:90)
00:00:12.543     at hudson.plugins.git.GitSCM.checkout(GitSCM.java:1134)
00:00:12.543     at hudson.scm.SCM.checkout(SCM.java:485)
00:00:12.543     at hudson.model.AbstractProject.checkout(AbstractProject.java:1269)
00:00:12.543     at hudson.model.AbstractBuild$AbstractBuildExecution.defaultCheckout(AbstractBuild.java:607)
00:00:12.543     at jenkins.scm.SCMCheckoutStrategy.checkout(SCMCheckoutStrategy.java:86)
00:00:12.543     at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:529)
00:00:12.543     at hudson.model.Run.execute(Run.java:1738)
00:00:12.544     at hudson.model.FreeStyleBuild.run(FreeStyleBuild.java:43)
00:00:12.544     at hudson.model.ResourceController.execute(ResourceController.java:98)
00:00:12.544     at hudson.model.Executor.run(Executor.java:410)

ぐぐるとこんなのが・・・

[JENKINS-20941] Stored git credentials not used when submodule is updated - Jenkins JIRA

つい数日前に直したのでexperimental pluginsからGit Pluginの3.0.0-beta2を入れるとUse credentials from default remote of parent repositoryというのが出るのでそれをチェックすると。なんか良さそうだけど入れたくないなぁ。

というわけで、Disable submodules processingにチェックしてサブモジュールを更新しないようにし、手動で設定ファイルを置いとくことにしました。
Git Pluginが正式リリースされたらもとに戻すということで。

SourceTreeでサブツリーを作るとき

  • いきなり失敗したので、φ(..)メモメモ

SourceTreeでSubTreeを作成

サブツリーの追加/リンクを押すと、ソースパス/URLブランチ/コミットローカルパスを入れる画面が出ますが、わざわざ空のディレクトリ作ってローカルパスを指定したのが失敗でした。

サブツリー情報とリンク

既にフォルダが存在するパスが指定されています。
指定されたリモート情報と既存のフォルダにリンクを作成しますか?
(注意, リンクを作成してもリポジトリは変更されません)

キャンセル  / リンクを作成する

このようなメッセージが出てキャンセルをしたら駄目だし、リンクを作成するとしても意図した階層でプル出来ません。

何の事はない、あらかじめディレクトリを作成するとか余計なことをせずに、存在しないディレクトリをローカルパスに指定すれば良いだけでした。_| ̄|○

結局使わなかった・・・

SubTreeはいろいろと出来ることが多くて良いとは思うのですが、リモートからCloneしたら自動的にリンクが出来るわけじゃないのでメンバーの習熟度やリンク部分は基本あまり変更しない想定なので結局SubModuleを使うことにしました。

C#でファイルバージョンは自動的なリビジョンを入れ、製品バージョンを手動で振る

  • デフォルトではAssemblyVersionとAssemblyFileVersionがあるのですが、なぜか両方あると*が効きませんよね。
    • AssemblyFileVersionを指定しなければ自動的なリビジョンが振られます。
// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を 
// 既定値にすることができます:
[assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
  • ただし、これだとファイルのプロパティから確認するときに製品バージョンにも同じ値が入ってしまいます。
    • やはり製品バージョンは手動で値を振りたい。
    • 製品バージョンはAssemblyInformationalVersionを使うと振れます。
  • 最終形
// すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を 
// 既定値にすることができます:
[assembly: AssemblyVersion("1.0.*")]
//[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("0.9.1.0")]

これでファイルバージョンはどんどん変わっていくけど製品バージョンは手動で変えたい時だけ変えることが出来ます。やったね!

GradleでやってるようにGitのコミットIDを入れられたらもっといいんだけどね。(まあ今日はここまでにしておこう)

Jettyのstderrout.logとrequest.logのパスを環境変数で指定する

Jettyのログを相対パスで指定していたのですが、異なるドライブレターに出したいというご要望です

共通

  • Windowsサーバです。
  • ドライブレターは環境変数環境変数で指定したものをプロパティとして起動時の引数で渡している想定です。
  • jetty.homeとjetty.baseは分離しています。
  • jettyは以下のように起動します。
set JETTY_HOME=C:\foo\runtime\jetty
set LOGDIR=C:\bar\logs\hogehoge

java -Djetty.home=%JETTY_HOME% -jar start.jar --exec -DLOGDIR=%LOGDIR%
  • ログディレクトリの指定を絶対パスにして各サービスのあちこちに埋め込むと導入時や開発時に書き換えが必要な場合にミスしやすいので、できれば使いたくありません。

stderrout.log

  • プロパティとして渡しているLOGDIRが効かないのでstart.iniの中で相対パスで指定していました。
--module=logging
jetty.logs=logs/hogehoge
  • 例えば出力先をD:\logsに変えたいとした時に相対パスではダメです。
    • jetty.homeetc\jetty-logging.xml${jetty.base}\etcにコピーして<Property name="jetty.logs" default="./logs"/>の所をLOGDIRに書き換えれば出来そうです。(start.iniのjetty.logsは消しておきます)
<Configure id="logging" class="org.eclipse.jetty.util.log.Log">

    <New id="ServerLog" class="java.io.PrintStream">
      <Arg>
        <New class="org.eclipse.jetty.util.RolloverFileOutputStream">
          <Arg><Property name="jetty.logs" default="./logs"/>/yyyy_mm_dd.stderrout.log</Arg><Arg><Property name="LOGDIR" />/yyyy_mm_dd.stderrout.log</Arg>

request.log

  • こちらもプロパティとして渡しているLOGDIRが効かないのでstart.iniの中で相対パスで指定していました。(指定の仕方は微妙に違います)
--module=requestlog
requestlog.filename=logs/hogehoge/yyyy_mm_dd.request.log
  • stderrout.logとは微妙に指定の仕方が違います。
    • jetty.homeetc\jetty-requestlog.xml${jetty.base}\etcにコピーして<Property name="jetty.base" default="." /><Property name="requestlog.filename" default="/logs/yyyy_mm_dd.request.log"/></Set>の所をLOGDIRに書き換えれば出来そうです。(start.iniのrequestlog.filenameは消しておきます)
<Configure id="Server" class="org.eclipse.jetty.server.Server">

  <!-- =========================================================== -->
  <!-- Configure Request Log -->
  <!-- =========================================================== -->
  <Ref refid="Handlers">
    <Call name="addHandler">
      <Arg>
        <New id="RequestLog" class="org.eclipse.jetty.server.handler.RequestLogHandler">
          <Set name="requestLog">
            <New id="RequestLogImpl" class="org.eclipse.jetty.server.AsyncNCSARequestLog">
            <Set name="filename"><Property name="jetty.base" default="." /><Property name="requestlog.filename" default="/logs/yyyy_mm_dd.request.log"/></Set><Set name="filename"><Property name="LOGDIR" /><Property name="requestlog.filename" default="/yyyy_mm_dd.request.log"/></Set>

これで出たと思う。

log4netのログファイルを環境変数で設定する

log4netのログファイルの指定でパスの部分を環境変数から取得して設定したくなりました

<file value="log\hogehoge.log" />
  • ドライブレターを変えたいというご要望なので、以下のようにしたら環境変数LOG_BASEでちゃんと置換されました。
    • 環境変数LOG_BASEは例えばE:\logというように設定しておきます。
<file value="${LOG_BASE}\hogehoge.log" />

改造しなくてよかったw

追記

  • 環境変数がセットされているcmd.exeから呼び出すとかだと上のでいいのですが、Windowsサービス等にしていてシステム環境変数を弄りたくない時などにプログラムの中から環境変数をセットして展開したい時もあります。
    • 以下のようにしたら出来ました。
Environment.SetEnvironmentVariable("LOG_BASE", @"E:\log");
log4net.Config.XmlConfigurator.Configure(logconfig);

MSTestのAssertでコンソール出力の文言を比較する

コンソールアプリの中で標準出力している文言をMSTestでテストしたい

  • テスト対象の戻り値が成功でも状態によって出力される文言が変わるのでテストしておきたい
  • StringWriter でコンソール出力を奪ってからメソッドを実行し、StringReaderで読み出せば良いらしい
[TestClass]
public class HogehogeTest
{
    [TestMethod]
    public void fooという処理でbarという文字列が出てくる()
    {
        string expected = "bar";

        var builder = new StringBuilder();
        var writer = new StringWriter(builder);
        // コンソール出力を奪い取る
        Console.SetOut(writer);

        // テスト対象(このメソッドの中でコンソールに出力している)
        Foo();

        // コンソール出力を読みだす
        var reader = new StringReader(builder.ToString());
        string consoleMessage = reader.ReadToEnd();

        // consoleMessageにexpectedが含まれていればOK
        StringAssert.Contains(consoleMessage, expected);
    }
}
  • StringWriter/StringReader使うときにusingで囲んだ方がいいし、後始末でコンソールを戻すとかあるけど、ざっくりこんな感じ。
  • ここを参考にしました。

How to redirect the standard console output, to assert logmessages written by log4net. – Knowledgebase

追記

  • consoleMessageに改行が含まれているとうまく評価できないようなので以下のようにしました。
StringAssert.Contains(consoleMessage, expected);
 ↓
StringAssert.Contains(consoleMessage.Replace(Environment.NewLine, ""), expected);
  • 変更後ソース
[TestClass]
public class HogehogeTest
{
    [TestMethod]
    public void fooという処理でbarという文字列が出てくる()
    {
        string expected = "bar";

        var builder = new StringBuilder();
        var writer = new StringWriter(builder);
        // コンソール出力を奪い取る
        Console.SetOut(writer);

        // テスト対象(このメソッドの中でコンソールに出力している)
        Foo();

        // コンソール出力を読みだす
        var reader = new StringReader(builder.ToString());
        string consoleMessage = reader.ReadToEnd();

        // consoleMessageにexpectedが含まれていればOK
        StringAssert.Contains(consoleMessage.Replace(Environment.NewLine, ""), expected);
    }
}

GradleのdependenciesをSnapshotビルドとReleaseビルドで切り替えたい

やりたい事

  • build.gradleに記述しているdependenciesの中で参照している自作のライブラリがあるが、開発中の時はライブラリがSNAPSHOTの時もある。
  • リリースをするときは基本的にSNAPSHOT無しのライブラリを使う。
  • いちいち書き換えてコミットするのはなんだかなー。
  • タスクで切り替えられるように出来るんじゃ?

ダメだったこと

  • dependenciesの中で指定しているjarファイルのversionに拡張プロパティで定義したプロパティを指定して、gradle.taskGraph.whenReadyの中で切り替えようとしたらダメだった。
    • 調べたらdependenciesはタスクが動いた時に定義されて後から書き換わらないような感じ。

やったこと

  • gradle.taskGraph.whenReadyの中で切り替えたいdependenciesをまるごと定義してみた。→上手くいった!
    • 以下に一部抜粋(このままじゃ動かないかな)
ext {
    // dependencies version
    foo_version = "2.1.0"
    bar_version = "2.2.0"
}

task distribution(type: Copy, dependsOn: jar) {
    // インストーラ作る処理
}
distribution.group 'package'
distribution.description 'Copies snapshot installer'

task release(dependsOn: distribution) << {
}
release.group 'package'
release.description 'Copies release installer'

gradle.taskGraph.whenReady {taskGraph ->
    // リリースじゃないタスクはSNAPSHOTを使う
    if (!taskGraph.hasTask(release)) {
        version = "${version}-SNAPSHOT"
        foo_version = "${foo_version}-SNAPSHOT"
        bar_version = "${bar_version}-SNAPSHOT"
    }
    dependencies {
        compile group: 'com.example.foo', name: 'foo', version: foo_version
        compile group: 'com.example.bar', name: 'bar', version: bar_version
    }
}

dependencies {
    compile group: 'commons-pool', name: 'commons-pool', version: '1.3'
    compile group: 'commons-lang', name: 'commons-lang', version: '2.1'
    runtime group: 'log4j', name: 'log4j', version: '1.2.14'
    testCompile group: 'junit', name: 'junit', version: '4.8.2'
    deployerJars group: 'org.apache.maven.wagon', name: 'wagon-ssh', version: '2.4'
}

調べてもあまり引っかからなかったけど、みんなどうしているんだろう?