Avoiding maven repository version collision when using feature branches

前端 未结 4 1606
粉色の甜心
粉色の甜心 2021-02-04 07:54

Question: How do you handle feature branches for maven multi project builds?

Jenkins builds and deploys these branches to keep build overhead on develop

相关标签:
4条回答
  • 2021-02-04 08:25

    We use a similar technique as Peter Kahn, modifying the version of the branch before building. We have three steps in our "Pre Steps":

    1. Execute shell: echo VERSION=$(echo ${GIT_BRANCH} | sed 's_^.*\/__') > env.properties
    2. Inject environment variables: env.properties
    3. Invoke top level Maven targets: versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION}-SNAPSHOT

    I am quite sure that this can be done with two or even one and only step as well, bit the principe behind it will be the same.

    The reason why we don't change the version in the pom.xml files in the branch directly is indeed merging. With SVN this was possible (merging with --accept-mine-conflict. With GIT this does not exist anymore, so we stopped changing versions and created this pre-build steps.

    0 讨论(0)
  • 2021-02-04 08:32

    How about the following approach:

    • Use the buildnumber-maven-plugin to fetch information from git and populate specific Maven properties (we are interested specifically in the scmBranch property (that is, the current git branch)
    • Use the build-helper-maven-plugin to check whether we are in a feature branch or not (via a regex, excluding well-known branches like master, develop, etc.) and populate (or not) a new Maven property, say branch.classifier
    • Use the maven-jar-plugin to set a classifier on the generated artifacts, based on what the previous step set, that is, using the new branch.classifier property: if empty, no classifier will be applied (default behavior, applied to the develop branch, for example); otherwise a classifier named after the current branch will be dynamically applied.

    Here is a minimal example:

    <build>
        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>buildnumber-maven-plugin</artifactId>
                <version>1.4</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>create</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>build-helper-maven-plugin</artifactId>
                <version>1.10</version>
                <executions>
                    <execution>
                        <id>regex-property</id>
                        <goals>
                            <goal>regex-property</goal>
                        </goals>
                        <configuration>
                            <name>branch.classifier</name>
                            <value>${scmBranch}</value>
                            <regex>(^develop)|(^master)|(^release.*)</regex>
                            <replacement></replacement>
                            <failIfNoMatch>false</failIfNoMatch>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <classifier>${branch.classifier}</classifier>
                </configuration>
            </plugin>
        </plugins>
    </build>
    

    The snippet below is basically populating the scmBranch property dynamically, then setting the branch.classifier to its value only if different than develop, master or release*, then setting it as a classifier.

    Main advantages of this approach:

    • No pom changes will be applied, hence no merging issues at all
    • No clashes on Nexus working on the same version of the project but on different branches: the classified artifact will have different Maven coordinates, that is, the GAV (groupId, artifactId, version) becomes unique GAVC (+classifier)
    • That's actually a meaningful usage of the classifier attribute of an artifact:

      The classifier allows to distinguish artifacts that were built from the same POM but differ in their content.

    • The generated artifact will be dynamically different in Nexus, according to its source branch, hence having implicit traceability: no intervention from developers (no error prone, implicit convention), no intervention from CI job (easier maintenance), completely transparent
    • Using classifiers, will be easier to use the artifacts generated by a branch as a maven dependency (e.g. in case of library project): I want to use the dependency currently under development on branch xxx

    Examples
    Hence, you would have the following artifacts generated:

    • When working on develop: e.g. project-1.0.0-SNAPSHOT.jar (empty classifier, hence not applied, as handled by the regex)
    • When working on featureA: e.g. project-1.0.0-SNAPSHOT-featureA.jar
    • When working on hotfix-JIRA123: e.g. project-1.0.0-hotfix-JIRA123.jar
    • When working on release-sprint42: that's up to you, I added this case to not apply the branch name, simply because in these cases I prefer to esplicitely set a special classifier, RC<number>, for release candidates, but that's a matter of conventions/taste/habits, you can apply the same approach on this branch as well, as long as no clashes will be created on Nexus. Also note: when using JIRA/Stash/Git integration, the release branch name is normally something like release/v0.1.0, where the / character may cause issues in some OS (still something fixeable via further regex replacing though, if really required).
    • When working on master: hey, no one should work on master :) the case is there just as a double check, but that's actually not required

    Warnings on this approach:

    • As explained in the discussion below via comments, if the concerned Maven project is already using classifiers and even more via inter-modules dependencies (e.g. dependendies on test scope classes from another module), then this approach should be carefully tested, since it might have some drawbacks
    • The publication of the <artifactId>.pom files containing branch classifier can get into conflicts with the mainline build (i.e. overriding it)
    0 讨论(0)
  • 2021-02-04 08:38

    For Maven ge 3.5.0 try this https://maven.apache.org/maven-ci-friendly.html This is the recommended Maven solution. The only problem (but unusual) can be the numeric versions resolution of the maven dependencies. But this only apears, if you use different SNAPSHOT dependencies of the the module, which is a bad idea anyway.

    0 讨论(0)
  • 2021-02-04 08:40

    This does not work Results in warnings throughout the build and GC error when run from top parent.

    Ideally, we want to use version as to differentiate feature branch from mainline because it is the normal maven way and classifier manipulation can result in all kinds of issues.

    Since maven can use environment variables for properties and we already initialize build environment with a script (we also have git hook scripts that can set environment variables from branch names) we can use env to control the version.

    <groupID>my.project</groupId>
    <artifactID>database</artifactId>
    <version>1.2.0${env.BRANCHMODIFIER}-SNAPSHOT</version>
    

    If on develop our scripts set BRANCHMODIFIER to ""
    If on feature/JIRA-30495 our scripts set BRANCHMODIFIER to ".30495"

    How does this work in eclipse or Intellij? No clue as of yet.

    0 讨论(0)
提交回复
热议问题