Some add-ons introduce generic functionality that may be useful across Atlassian Server products. Rather than maintaining duplicate code for each product, you can create a genric add-on that runs across products. And fortunately its relatively simple to accomplish.
The article below outlines the necessary configuration to write an add-on that will run in products of your choice.
The first, and perhaps obvious thing to note is that your code will need to rely on the apis and services available across all the products you plan to support. This gets complicated in some areas like User Management due to the staggered pace of upgrades across products. We'll also talk about work-arounds when you just need a product specific API.
You'll want to declare the base platform APIs in your pom.xml. This is libraries/services common to all products, and gives you most basic functionality (admin pages, servlets, REST, etc).
<dependencies> <dependency> <groupId>com.atlassian.platform</groupId> <artifactId>platform</artifactId> <version>${platform.version}</version> <type>pom</type> <scope>import</scope> </dependency> ... </dependencies>
At the time of this writing 3.2.0 was a recent compatible version, but you can always search https://maven.atlassian.com for latest.
And of course you can't use the 'jira-maven-plugin' or any product specific AMPS plugin, but instead the generic one.
<build> <plugins> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-amps-plugin</artifactId> <version>${amps.version}</version> <extensions>true</extensions> ... </plugin> ... </plugins> .... </build>
** Starting the product now with atlas-run will default to RefApp, we'll fix that below.
The atlas-run command supports a few flags that indicate which product to use, namely --product and --version.
atlas-run --product bamboo --version 5.13.1
Now you can fire up your add-on in each product individually and confirm it works.
The snippet above is enough to let you write a basic add-on with a servlet or REST api, but you may need access to product specific services. And to do that you'll need to add dependencies for each product.
<dependency> <groupId>com.atlassian.jira</groupId> <artifactId>jira-api</artifactId> <version>${jira.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> <exclusion> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.confluence</groupId> <artifactId>confluence-java-api</artifactId> <version>${confluence.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.bamboo</groupId> <artifactId>atlassian-bamboo-api</artifactId> <version>${bamboo.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> <exclusion> <groupId>xml-apis</groupId> <artifactId>xml-apis</artifactId> </exclusion> <exclusion> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.stash</groupId> <artifactId>stash-api</artifactId> <version>${stash.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.bitbucket.server</groupId> <artifactId>bitbucket-api</artifactId> <version>${bitbucket.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>com.atlassian.fisheye</groupId> <artifactId>atlassian-fisheye-api</artifactId> <version>${fecru.version}</version> <scope>provided</scope> <exclusions> <exclusion> <groupId>javax.jms</groupId> <artifactId>jms</artifactId> </exclusion> <exclusion> <groupId>jta</groupId> <artifactId>jta</artifactId> </exclusion> </exclusions> </dependency>
Rather than specifying a version each time and dealing with the different ports and contexts, I chose to make everything run at localhost:5990/product this simplifies testing and is easier to remember than 6 product specific combos. Just specify the default versions and info as properties:
<properties> .... <amps.version>6.2.11</amps.version> <sal.version>3.1.0</sal.version> <platform.version>3.2.0</platform.version> <product.httpPort>5990</product.httpPort> <product.contextPath>/product</product.contextPath> <refapp.version>3.2.2</refapp.version> <confluence.version>6.0.4</confluence.version> <jira.version>7.3.0</jira.version> <jira.servicedesk.version>3.3.2</jira.servicedesk.version> <bamboo.version>5.13.1</bamboo.version> <bitbucket.version>4.0.0</bitbucket.version> <stash.version>3.0.0</stash.version> <fecru.version>4.3.0-20170119092650</fecru.version> </properties>
And extend the amps-maven-plugin with a products block:
<build> <plugins> <plugin> <groupId>com.atlassian.maven.plugins</groupId> <artifactId>maven-amps-plugin</artifactId> <version>${amps.version}</version> <extensions>true</extensions> <configuration> <!--You can also pass these to atlas-run (\-\- is just xml escaping, use 2 dashes) --> <!-- atlas-run \-\-testGroup bamboo \-\-jvmargs "-Dhttp.proxyHost=www-proxy.lmig.com -Dhttp.proxyPort=80" --> <!--<jvmArgs>-Dhttp.proxyHost=proxyhost.domain -Dhttp.proxyPort=80</jvmArgs>--> <log4jProperties>src/test/resources/log4j.properties</log4jProperties> <enableQuickReload>true</enableQuickReload> <enableDevToolbox>false</enableDevToolbox> <enableFastdev>false</enableFastdev> <compressJs>false</compressJs> <!-- See here for an explanation of default instructions: --> <!-- https://developer.atlassian.com/docs/advanced-topics/configuration-of-instructions-in-atlassian-plugins --> <instructions> <Atlassian-Plugin-Key>${atlassian.plugin.key}</Atlassian-Plugin-Key> <!-- Add package to export here --> <!-- Add package import here --> <!-- Ensure plugin is spring powered --> <Spring-Context>*</Spring-Context> </instructions> <products> <product> <id>refapp</id> <version>${refapp.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> <product> <id>jira</id> <version>${jira.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> <product> <id>jira</id> <instanceId>jira-sd</instanceId> <version>${jira.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> <applications> <application> <applicationKey>jira-servicedesk</applicationKey> <version>${jira.servicedesk.version}</version> </application> </applications> </product> <product> <id>confluence</id> <version>${confluence.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> <productDataVersion>${confluence.version}</productDataVersion> </product> <product> <id>bamboo</id> <version>${bamboo.version}</version> <server>localhost</server> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> <product> <id>stash</id> <version>${stash.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> <product> <id>bitbucket</id> <version>${bitbucket.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> <product> <id>fecru</id> <version>${fecru.version}</version> <httpPort>${product.httpPort}</httpPort> <contextPath>${product.contextPath}</contextPath> </product> </products>
Now you can still use --product when you call atlas-run, but the other details will be set based on common config.
The file atlassian-plugin-marketing.xml is a hndy file ingested by marketplace to automatically configure your marketing page and compatibility info. I love keeping the source of truth for all things in version control, so we state our cross-product compatibility there.
<atlassian-plugin-marketing> <compatibility> <product name="jira" min="6.3.1" max="7.3.1" /> <product name="bamboo" min="5.10.0" max="5.15.0"/> <product name="bitbucket" min="4.0.0" max="4.13.0"/> <product name="confluence" min="5.10.2" max="6.0.5"/> <!-- fecru uses build-date appended to version in maven repo, so we cant use real versions :( --> <product name="fecru" min="4.1.0-20160624111830" max="4.3.0-20170119092650"/> </compatibility> .... </atlassian-plugin-marketing>
Everything cited in this article can be viewed in the source code of my StatusPage.io integration add-on which supports JIRA (JSD,Software, Core), Bamboo, Bitbucket, Confluence and Fisheye/Crucible.
https://bitbucket.org/eddiewebb/statuspage-banner-atlassian-server/src
Testing across all these products and versions manually would be a royal PITA, so please read my upcomming article that dives into the automation of testing across products based on the specs in atlassian-plugin-marketing.xml.
(and for bonus points we drive an automated release into Marketplace using Bitbuckets Pipelines !)
EddieW
0 comments