My First Atlassian Plugin

Goal

An Atlassian Macro that will query Stash and return a list of valid branches from a particular repo, with the option to filter.

Basically, this rest call:

stash/rest/api/1.0/projects/{project}/repos/{repo}/branches?filterText=release

Problem

Where I work we have a couple dozen micro-services that may or may not need to be updated during a particular release phase. In order to not miss an artifact I wanted to make a way to get automatic exposure to the required deployment.

Recently, I had configured each one of our projects to align with GitFlow (using JGitFlow).  The workflow looks something like this: A developer makes update to develop branch and when they determine their fix/feature is complete the cut a new release using the JGitFlow tools. The automatic build sees this new branch, creates and deploys to the test servers and then QA gets around to it when they have time.  One of the advantages of the Maven JGitFlow plugin is that it only allows for one release branch at a time. The idea being that if someone looks at a repo and see that it has a release branch, then it needs to be part of the next production release.

We use Confluence and Stash, the simple solution would be to have a macro ask for release branches and display them on our release page. After searching I found one potential candidate that would display information from Stash on a confluence page. There were two problem with this plugin: 1) Even though it would allow you to list branches, it would not allow you to filter branches, and 2) it cost money.

So, what’s a developer to do? Write my own plugin. This turned out to be more of a chore than I expected. I won’t delve into my–mostly–negative opinions about the Atlassian developer community or the Atlassian documentation, but what I will do is give a brief rundown of what I did and what I learned.

Getting Started

Skip the blah, blah and go straight to the code.

Whenever I read the words, “just install the SDK”, I cringe a little inside. It never seems to be that straight forward and the Atlassian SDK is no different. The problem for me is that I do regular Maven development and that gets in the way the Atlassian’s flavor of Maven that is built into their SDK. I would rant about how the Atlassian SDK could be greatly simplified by using a Gradle-like wrapper feature, but that’s not the point of this blog. I hope here to give a short concise example of creating an Atlassian plugin and clear up some ambiguity that I found along the way. I’ll also briefly touch on some solutions to problems I had.

Installing

To install, use the standard documentation. When you go to run commands skip any troubles you might have by explicitly pointing the the SDK settings instead of trying to incorporate the Atlassian settings with what is probably you corporate settings that are already in your ~/.m2/settings.xml file.

atlas-run --settings /usr/share/atlassian-plugin-sdk-6.2.6/apache-maven-3.2.1/conf/settings.xml

This is a problem with Maven not allowing mirror definitions in profiles.

Running

Running confluence is pretty straight forward, but you’ll probably want to add a few developer flags to make debugging easier.

atlas-run --settings /usr/share/atlassian-plugin-sdk-6.2.6/apache-maven-3.2.1/conf/settings.xml \
--http-port 1990 -Datlassian.dev.mode=true \
-Datlassian.webresource.disable.minification=true

After you have confluence up and running, you’ll inevitably do some configuration of the app. If you’re working with two apps you’ll inevitably create a link between the two. This can be a tedious process (watch out for case-sensitivity when defining url links). At about a day of when you first started the app it will complain about licensing. The way to fix this is to run an atlas-clean and rerun the app. However that will destroy the work that you’ve done. To get around this you need to create a snapshot of your configuration using the atlas-home command. It will create a zip directory that you can refer to in your pom.xml file.

<productDataPath>${project.basedir}/src/test/resources/generated-test-resources.zip</productDataPath>

There are two things to remember here: 1) The app must be running when you call the atlas-run command, so open another window in the same directory, and 2) if you make changes or addition to how you app is setup, you must rerun the atlas-home command–it’s not automatic.

For developing a macro that talks from one app to another (Confluence to Stash), you have to have both apps running and connected. There is supposedly a way to define multiple apps in your pom.xml file and run concurrently but I kept having trouble with that so I just created two projects, one for the macro code and one just to keep the configuration of Stash.

Structure of a Macro

These are the parts to a macro

  • Plugin definition (atlassian-plugin.xml)
  • Macro definition (xhtml-macro inside of the plugin definition)
  • Macro code (which is run during the running of the macro)
    • com.atlassian.confluence.macro.Macro
  • JavaScript (used when rendering the macro editor)
    • optional, used if you want the macro editor to have dynamic behavior…so not really optional.
  • Template (velocity template for generating the resulting macro on the page.)

I’m not going to go into creating a Hello World macro. The standard doc is just fine for getting you up and running.

Using JavaScript to get Dynamic Behavior in Macro Editor

This is where the real power to create dynamic behavior is. For my macro I wanted to get a list of available projects, let the user select a project, then get a list of available repos in that project and let the user select a repo and finally store that information to be used later when rendering the macro on page. The basic features found in the xhmtl-macro tag don’t come anywhere close to this. Here is a couple things I learned. Here is the reference file.

  • AJS.MacroBrowser.setMacroJsOverride has a slew of undocumented methods that allow you to hook into the creation lifecycle, such as  beforeParamsSet, beforeParamsRetrieved, fields, etc
  • beforeParamsSet and beforeParamsRetrieved are important to saving parameters and making sure that your code does not overwrite previously selected values.
  • AJS.$ is just jQuery. When you are wondering how to dynamically change the macro browser, do not search for “how to edit confluence drop down”. Search for “how to edit a drop down with jQuery”.
  • Confluence will combine all the JavaScript and put it into one file. Find that file in the browser developer console, put a breakpoint in your code and explore the other methods available. There is intentionally no documentation for this stuff.

Most of the code speaks for itself. If you do the Hello World example, then take a look at my project, it will fill in some of the blank spots.

https://github.com/Scuilion/confluence-stash-macro

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s