こまぶろ

技術のこととか仕事のこととか。

pom.xmlの特定の要素をxmllintで書き換える

CIからpom.xmlを自動で更新するような仕組みを入れようと思って、pom.xmlを書き換える方法を調べたのでメモ。

ポイントとしては、

  • 名前空間付きのもののときは *[local-name()='hoge'] を使う
  • 同名のタグが複数ある場合に特定のノードのものを選択するには and を使う

になる。

例題

以下のようなpom.xmlで、 maven-compiler-plugin のバージョンを変えたい。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>junit5-jupiter-starter-maven</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>${maven.compiler.source}</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.junit</groupId>
                <artifactId>junit-bom</artifactId>
                <version>5.9.1</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
            </plugin>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M6</version>
            </plugin>
        </plugins>
    </build>

</project>

(コードは https://github.com/junit-team/junit5-samples/blob/f7029d7b9e5c73a8f3615a761d51e6e756e3e09d/junit5-jupiter-starter-maven/pom.xml のもの)

対応方法

たとえば以下のように記述すればよい。

VERSION=3.9.0
xmllint --shell pom.xml << EOF
cd /*[local-name()='project']/*[local-name()='build']/*[local-name()='plugins']/*[local-name()='plugin' and *[local-name()='artifactId']/text() = 'maven-compiler-plugin']/*[local-name()='version']
set $VERSION
save
EOF

名前空間付きのタグに対しては local-name() を使う

タグ名は実際には名前空間付きなので、テキストファイル上の名前を使って /project/build/... のように指定することはできない。local-name() を使って対象のタグを指定する必要がある。

同名のタグが複数ある場合に特定のノードのものを選択するには and を使う

書き換えたい要素は <plugins> 配下に複数ある <plugin> のうちの特定のものなので、条件を追加して指定する必要がある。以下の部分。

*[local-name()='plugin' and *[local-name()='artifactId']/text() = 'maven-compiler-plugin']

<plugin> で、かつ <artifactId> の内容が maven-compiler-plugin であるものを選択するためにこのような記述をしている。

local-name() を何度も書きたくないときは

何回も local-name() を書くのは見た目的にもわかりづらくなるので、ルートノードからの厳密なパスを指定せずに // を使って指定することもできる。

VERSION=3.9.0
xmllint --shell pom.xml << EOF
cd //*[local-name()='plugin' and *[local-name()='artifactId']/text() = 'maven-compiler-plugin']/*[local-name()='version']
set $VERSION
save
EOF

参考

atmarkit.itmedia.co.jp

techblog.zozo.com