How to add build information into hybris

Usually it is extremely useful for QA and dev departments to identify build number on environment and verify that required code changes was really deployed. Unfortunately OOTB hybris doesn’t provide any mechanism for that.

To figure it out, property file with git information would be created during the ant build process and loaded as regular properties on hybris startup. With such approach it would be possible to identify build number via hac by any person. Sometimes it is easier to identify build by build number of external system(like Jenkins). To fulfill that requirement would be introduced java system property -Dbuild.jenkins.number.

First of all extend ant build with before build command to aggregate git info and save it in properties file in resources folder of current extension.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

<macrodef name="yourextension_before_build">
    <sequential>
        <property file="${ext.yourextension.path}/project.properties"/>

        <!-- Pass jenkins build number via -D command line argument -->
        <property name="build.jenkins.number" value="NOT_SET"/>

        <!-- Clean build properties file -->
        <delete file="${ext.yourextension.path}/resources/${qa.properties.file.name}" failonerror="false"/>
        <touch file="${ext.yourextension.path}/resources/${qa.properties.file.name}"/>

        <!-- Get info from git -->
        <exec executable="git" outputproperty="git.revision"
              dir="${HYBRIS_BIN_DIR}/../../"
              failifexecutionfails="false">
            <arg value="log"/>
            <arg value="-1"/>
            <arg value="--pretty=format:%H"/>
        </exec>

        <exec executable="git" outputproperty="git.revision.date"
              dir="${HYBRIS_BIN_DIR}/../../"
              failifexecutionfails="false">
            <arg value="log"/>
            <arg value="-1"/>
            <arg value="--format=%cd"/>
        </exec>

        <exec executable="git" outputproperty="git.branch"
              dir="${HYBRIS_BIN_DIR}/../../"
              failifexecutionfails="false">
            <arg line="rev-parse --abbrev-ref HEAD"/>
        </exec>

        <echo message="==========================================================="/>
        <echo message="Jenkins build number: ${build.jenkins.number}"/>
        <echo message="==========================================================="/>

        <!-- Write build properties to file -->
        <propertyfile file="${ext.yourextension.path}/resources/${qa.properties.file.name}">
            <entry key="qa.build.date" type="date" value="now"/>
            <entry key="qa.build.hybris.version" value="${hybris.build.version}"/>

            <entry key="qa.build.git.branch.name" value="${git.branch}"/>
            <entry key="qa.build.git.commit.date" value="${git.revision.date}"/>
            <entry key="qa.build.git.commit.hash" value="${git.revision}"/>

            <entry key="qa.build.jenkins.number" value="${build.jenkins.number}"/>
        </propertyfile>
    </sequential>
</macrodef>

File name of created property file can be setup via qa.properties.file.name property in project.properties file of your extension. Don’t forget to change yourextension on your real extension name after copy-pasting this snippet.

As a result of ant build execution would be created properties file wih current date, hybris version, git branch name, commit date, commit hash and empty jenkins build number.

Example of such file:

1
2
3
4
5
6
7
8
9
#Mon, 27 Apr 2020 13:44:43 +0300


qa.build.date            = 2020/04/27 13\:44
qa.build.hybris.version  = 6.6.0.22
qa.build.git.branch.name = TBC-11679
qa.build.git.commit.date = Mon Apr 27 13\:42\:42 2020 +0300
qa.build.git.commit.hash = 843169077bf121de1de2e0e813e2d5229e255d6f
qa.build.jenkins.number  = NOT_SET

To add jenkins build number into this file ant build must be executed with property -Dbuild.jenkins.number=${NUMBER_OF_BUILD}, for example ant clean all -Dbuild.jenkins.number=1204.

To load created properties file would be used approach with TenatListener (as an alternative can be used approach with InitializingBean in global context):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package com.blog.core.properties.listeners;

import com.blog.core.utils.BuildPropertiesUtils;
import de.hybris.platform.core.MasterTenant;
import de.hybris.platform.core.Registry;
import de.hybris.platform.core.Tenant;
import de.hybris.platform.core.TenantListener;
import de.hybris.platform.jalo.JaloSystemException;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import org.apache.commons.configuration.Configuration;
import org.apache.log4j.Logger;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class BuildPropertiesInitializer implements TenantListener {
    private static final Logger LOG = Logger.getLogger(BuildPropertiesInitializer.class);


    @Resource
    private ConfigurationService configurationService;

    @Resource
    private BuildPropertiesUtils buildPropertiesUtils;

    BuildPropertiesInitializer() {
        Registry.registerTenantListener(this);
    }

    @Override
    public void afterTenantStartUp(Tenant tenant) {
        if (tenant instanceof MasterTenant) {
            onApplicationLoad();
        }
    }

    void onApplicationLoad() {
        try {
            Properties buildProperties = buildPropertiesUtils.loadBuildProperties();
            final Configuration configuration = configurationService.getConfiguration();

            Set<Map.Entry<Object, Object>> entries = buildProperties.entrySet();
            entries.forEach(entry -> configuration.addProperty(entry.getKey().toString(), entry.getValue()));
        } catch (JaloSystemException ex) {
            LOG.warn("System is not initialized. DB config properties will be load on system init.");
            if (LOG.isDebugEnabled()) {
                LOG.debug(ex);
            }
        } catch (IOException e) {
            LOG.warn("QA properties file not found.");
            if (LOG.isDebugEnabled()) {
                LOG.debug("Exception:", e);
            }
        }
    }


    @Override
    public void beforeTenantShutDown(Tenant tenant) {
        // No actions on shutdown, only on tenant creation
    }

    @Override
    public void afterSetActivateSession(Tenant tenant) {
        // No actions on set of session, only on tenant creation
    }

    @Override
    public void beforeUnsetActivateSession(Tenant tenant) {
        // No actions on unset of session, only on tenant creation
    }

}

And util class to load file as properties:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.blog.core.utils;

import de.hybris.platform.core.Registry;
import de.hybris.platform.servicelayer.config.ConfigurationService;
import org.springframework.core.io.InputStreamSource;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.Properties;


public class BuildPropertiesUtils {

    @Resource
    private ConfigurationService configurationService;

    public Properties loadBuildProperties() throws IOException {
        String propertiesFileName = configurationService.getConfiguration().getString("qa.properties.file.name");
        return loadProperties(propertiesFileName);
    }

    private Properties loadProperties(String resourceFileName) throws IOException {
        Properties buildProperties = new Properties();
        return extendProperties(buildProperties, resourceFileName);
    }

    private Properties extendProperties(Properties properties, String resourceFileName) throws IOException {
        InputStreamSource resource = Registry.getApplicationContext().getResource(resourceFileName);
        properties.load(resource.getInputStream());
        return properties;
    }
}

Don’t forget to initialize beans in spring context:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="buildPropertiesUtils" class="com.blog.core.utils.BuildPropertiesUtils"/>
    <bean id="buildPropertiesInitializer" class="com.blog.core.properties.listeners.BuildPropertiesInitializer"/>

</beans>

P.S. ant production will depends on build target, so you need to run it with -Dbuild.jenkins.number=${NUMBER_OF_BUILD} in your CI/CD pipeline to not clear build number after build stage execution.

comments powered by Disqus