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'
}

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

SonarQubeをインストール

  • 久しぶりにSonarQubeをインストールしようと思ったらいろいろ忘れていてハマりました。
    • 特に公式ドキュメントにDBのつくりかたちゃんと書いてないし・・・

Installing the Server - SonarQube Documentation - SonarQube

DB作成

postgres=# CREATE USER sonarqube WITH PASSWORD 'sonarqube';
CREATE ROLE

postgres=# ALTER ROLE sonarqube CREATEDB;
ALTER ROLE

postgres=# CREATE DATABASE sonar WITH ENCODING 'UTF-8' OWNER=sonarqube;
CREATE DATABASE

postgres=# grant all privileges on database sonar to sonarqube;
GRANT

postgres=# \du
                             List of roles
 Role name |                   Attributes                   | Member of
-----------+------------------------------------------------+-----------
 postgres  | Superuser, Create role, Create DB, Replication | {}
 sonarqube | Create DB                                      | {}

postgres=# \l
                                   List of databases
   Name    |   Owner   | Encoding |   Collate   |    Ctype    |    Access privileges
-----------+-----------+----------+-------------+-------------+-------------------------
 postgres  | postgres  | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 |
 sonar     | sonarqube | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =Tc/sonarqube          +
           |           |          |             |             | sonarqube=CTc/sonarqube
 template0 | postgres  | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres            +
           |           |          |             |             | postgres=CTc/postgres
 template1 | postgres  | UTF8     | ja_JP.UTF-8 | ja_JP.UTF-8 | =c/postgres            +
           |           |          |             |             | postgres=CTc/postgres
(4 rows)
  • このままだとサーチパスが想定と違うので以下のようなエラーになります。
org.jruby.rack.RackInitializationException: ActiveRecord::JDBCError: ERROR: no schema has been selected to create in: CREATE TABLE "schema_migrations" ("version" varchar(10485760) NOT NULL) 
  • なのでサーチパスの設定をします。

kashi.way-nifty.com

postgres=# ALTER USER sonarqube SET search_path to public;
ALTER ROLE

モジュールのインストール

  • いつの間にかLTSができていたのでちょっと古いけどLTSにしました。
$ cd /usr/local/src
$ wget https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-4.5.6.zip
$ cd ..
$ unzip src/sonarqube-4.5.6.zip
$ ln -s sonarqube-4.5.6 sonar

設定

  • DBの設定
$ cd sonar/conf
$ vi sonar.properties

#sonar.jdbc.username=sonar
#sonar.jdbc.password=sonarsonar.jdbc.username=sonarqube
sonar.jdbc.password=sonarqube

#----- PostgreSQL 8.x/9.x
#sonar.jdbc.url=jdbc:postgresql://localhost/sonarsonar.jdbc.url=jdbc:postgresql://localhost/sonar
$ vi conf/sonar.properties

#---------------------------------------------------------
# LDAP Plugin
#---------------------------------------------------------

# his property must be defined to ask the Sonar server to use first the LDAP plugin when trying to authenticate a user.
sonar.security.realm=LDAP
# URL of the LDAP server. Note that if you are using ldaps, then you should install server certificate into java truststore.
ldap.url=ldap://ldap.example.com:389
# Bind DN is the username of an LDAP user to connect (or bind) with. Leave blank for anonymous access to the LDAP directory.
ldap.bindDn=cn=user,dc=ldap,dc=example,dc=com
# Bind Password is the password of the user to connect with. Leave blank for anonymous access to the LDAP directory.
ldap.bindPassword=xxxxxxxxxxx

# Distinguished Name (DN) of the root node in LDAP from which to search for users.
ldap.user.baseDn=ou=Users,dc=ldap,dc=example,dc=com
# Attribute in LDAP holding the user’s real name.
ldap.user.realNameAttribute=displayName
  • Proxyの設定
    • 上のLDAPプラグインを入れようとすると、プラグインのダウンロードに失敗する
    • 普通に設定しようとするとエラーが出るのでstackoverflowを参考に設定する

stackoverflow.com

Fail to download the plugin (csharp, version 5.0) from https://sonarsource.bintray.com/Distribution/sonar-csharp-plugin/sonar-csharp-plugin-5.0.jar (error is : Fail to download: https://sonarsource.bintray.com/Distribution/sonar-csharp-plugin/sonar-csharp-plugin-5.0.jar (no proxy))


↓だめ
https.proxyHost=proxy.example.com
https.proxyPort=8080

OK
sonar.web.javaAdditionalOpts=-Dhttps.proxyHost=proxy.example.com -Dhttps.proxyPort=8080

これで動いた

UbuntuのDISK容量がいつの間にか逼迫していた

前提条件

  • 確認しているバージョンはUbuntu 14.04.4 LTSです。
  • Jenkinsのマスターサーバなので一定のDISK空き容量がないと上手く動かなくなるプラグインがある。

対応

  • $ sudo du -sh /*して、でかいディレクトリを調べる
    • どうやら/lib/modulesが肥大化しているらしい
$ cd /lib/modules
$ ll
drwxr-xr-x  5 root root 4096  4月  6  2015 3.13.0-48-generic/
drwxr-xr-x  5 root root 4096  6月  8  2015 3.13.0-53-generic/
drwxr-xr-x  5 root root 4096  1月  4 11:35 3.13.0-74-generic/
drwxr-xr-x  5 root root 4096  3月 11 11:35 3.13.0-79-generic/
drwxr-xr-x  5 root root 4096  3月 30 09:53 3.13.0-83-generic/

$ du -sh *
184M    3.13.0-48-generic
184M    3.13.0-53-generic
184M    3.13.0-74-generic
184M    3.13.0-79-generic
185M    3.13.0-83-generic
  • なにやらでかいkernelモジュールらしきものがいっぱいある
$ sudo aptitude search 3.13.0-48-generic
p   linux-cloud-tools-3.13.0-48-generic                                          - Linux kernel version specific cloud tools for version 3.13.0-48
i A linux-headers-3.13.0-48-generic                                              - Linux kernel headers for version 3.13.0 on 64 bit x86 SMP
p   linux-headers-3.13.0-48-generic:i386                                         - Linux kernel headers for version 3.13.0 on 32 bit x86 SMP
i A linux-image-3.13.0-48-generic                                                - Linux kernel image for version 3.13.0 on 64 bit x86 SMP
p   linux-image-3.13.0-48-generic:i386                                           - Linux kernel image for version 3.13.0 on 32 bit x86 SMP
i A linux-image-extra-3.13.0-48-generic                                          - Linux kernel extra modules for version 3.13.0 on 64 bit x86 SMP
p   linux-image-extra-3.13.0-48-generic:i386                                     - Linux kernel extra modules for version 3.13.0 on 32 bit x86 SMP
p   linux-signed-image-3.13.0-48-generic                                         - Signed kernel image generic
p   linux-tools-3.13.0-48-generic                                                - Linux kernel version specific tools for version 3.13.0-48
p   linux-tools-3.13.0-48-generic:i386                                           - Linux kernel version specific tools for version 3.13.0-48
  • ほうほう。さくっと消してみるか。
$ sudo aptitude remove linux-headers-3.13.0-48-generic linux-image-3.13.0-48-generic linux-image-extra-3.13.0-48-generic

他の古いバージョンも消してみたらごっそり空いた。

2017/6/29追記

なんと、apt-get autoremoveでさくっと消せることが判明。こんな感じです。

$ sudo apt-get autoremove
パッケージリストを読み込んでいます... 完了
依存関係ツリーを作成しています
状態情報を読み取っています... 完了
以下のパッケージは「削除」されます:
  linux-headers-4.4.0-77 linux-headers-4.4.0-77-generic linux-image-4.4.0-77-generic linux-image-extra-4.4.0-77-generic
アップグレード: 0 個、新規インストール: 0 個、削除: 4 個、保留: 3 個。
この操作後に 297 MB のディスク容量が解放されます。
続行しますか? [Y/n] Y
(データベースを読み込んでいます ... 現在 131730 個のファイルとディレクトリがインストールされています。)
linux-headers-4.4.0-77-generic (4.4.0-77.98) を削除しています ...
linux-headers-4.4.0-77 (4.4.0-77.98) を削除しています ...
linux-image-extra-4.4.0-77-generic (4.4.0-77.98) を削除しています ...
run-parts: executing /etc/kernel/postinst.d/apt-auto-removal 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
run-parts: executing /etc/kernel/postinst.d/initramfs-tools 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
update-initramfs: Generating /boot/initrd.img-4.4.0-77-generic
run-parts: executing /etc/kernel/postinst.d/unattended-upgrades 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
run-parts: executing /etc/kernel/postinst.d/update-notifier 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
run-parts: executing /etc/kernel/postinst.d/zz-update-grub 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.4.0-81-generic
Found initrd image: /boot/initrd.img-4.4.0-81-generic
Found linux image: /boot/vmlinuz-4.4.0-78-generic
Found initrd image: /boot/initrd.img-4.4.0-78-generic
Found linux image: /boot/vmlinuz-4.4.0-77-generic
Found initrd image: /boot/initrd.img-4.4.0-77-generic
done
linux-image-4.4.0-77-generic (4.4.0-77.98) を削除しています ...
Examining /etc/kernel/postrm.d .
run-parts: executing /etc/kernel/postrm.d/initramfs-tools 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
update-initramfs: Deleting /boot/initrd.img-4.4.0-77-generic
run-parts: executing /etc/kernel/postrm.d/zz-update-grub 4.4.0-77-generic /boot/vmlinuz-4.4.0-77-generic
Generating grub configuration file ...
Found linux image: /boot/vmlinuz-4.4.0-81-generic
Found initrd image: /boot/initrd.img-4.4.0-81-generic
Found linux image: /boot/vmlinuz-4.4.0-78-generic
Found initrd image: /boot/initrd.img-4.4.0-78-generic
done

普段使っている aptitude には autoremove が無かった・・・。