A collection of advanced tooling which provides seamless data migration from Assembla to Jira.
This is by far the best Assembla to Jira migration toolset around. Here are some of the reasons why this toolset beats out all others hands down:
- Fully automated requiring minimal manual actions.
- Configuration file with various options.
- Import users (names, emails, roles and permissions).
- Import tickets, comments, attachments and relationships.
- Retain ticket status, labels, ranking and custom fields.
- Link back to original Assembla tickets for reference.
- Save relevant Assembla context in user-defined fields.
- Embed image thumbnails in descriptions and comments.
- Convert markdown, users, urls, attachments and other links.
- Timetracking: estimated and remaining.
- Retain watchers of tickets for notifications.
- Create scrum or kanban board with workflow.
- Map the Assembla milestones to Jira sprints.
- Populate the backlog, future and current sprints.
- Assign stories to epics.
- Resolve cross linking between external projects.
- Export wiki to Confluence space (new!)
- Account for the API differences between hosted and cloud.
- Tons of great documentation and trouble-shooting guide.
If you need extra help just look here.
Have you ever wanted to use Jira instead of Assembla, but were afraid that the switch to Jira was too risky?
Are you worried that your business-critical data in Assembla will get corrupted or even lost during the conversion?
Jira already offers a number of standard add-ons to make certain migrations easier. Unfortunately, it does not offer a tool for migrating from Assembla. In fact, they have announced that they will NOT provide a native JIRA import from Assembla in the foreseeable future, bummer.
However, you are now in luck! By using this Assembla-to-Jira migration toolset, it is very easy to export all of the relevant Assembla data and import most (if not all) of it into a Jira project without loss or corruption of data.
By using the Assembla API and the Jira API together, both environments are hooked up in order to make all necessary data transformations run smoothly and automatically.
Most of the actions are done automatically via a pipeline of scripts. Just define the required parameters in the .env
configuration file, and you are ready to go.
Some manual actions are required since the Jira API does not support all of the required data transformations, however these actions are few and clearly documented below. It is very important NOT to skip these manual actions because a successful migration depends on them being done properly at the right time.
While not required, it is still best to start with a fresh installation, e.g. one in which the desired project has not yet been created and none of the project users are present yet. Otherwise, unexpected problems might occur.
At the beginning of the migration, a quick scan is made to detect if a given project already exists. If not then the new project is created for you, as well as all of the Assembla users who have not yet been created.
Although the official Atlassian documentation states that the Jira API for hosted server is nearly identical to the cloud server, there are some subtle, tricky differences that can bite you when you least expect.
Don't worry, if you read all of the instructions below, follow them carefully without skipping anything, you can avoid these annoying bumps in the road on your way to a successful migration.
Need help? Look here.
I have created this toolset in the spirit of open source. This means that everyone is free to use it as is, and any code modifications need to be reported back to me via pull requests.
Much time has been spent by me along with late night evening bouts of sweat and tears in order to reach this point and assure pretty good quality.
If you would like to express your thanks more generously, then donations will be greatly appreciated, half of which I will pass on to charity.
The toolset has been written with the Ruby programming language. In order to be able to use it, you will have to have downloaded and installed the following items on your computer:
Ensure that you have the correct version of ruby installed and set to using it for scripts.
$ rvm install `cat .ruby-version`
$ rvm use `cat .ruby-version`
Once this has been done, you can checkout and install the toolset from the github repository.
$ git clone https://github.com/kgish/assembla-to-jira-migration.git assembla-to-jira
$ cd assembla-to-jira
$ gem install bundler
$ bundle install
At this point everything should be ready to use as explained in the following sections.
Please note that only the following API versions are supported:
- Assembla API v1
api.assembla.com/v1
- JIRA API v2
rest/api/2
The complete migration from start to finish consists of a series of scripts which are to be executed in order.
Like a pipeline, each script processes data and generates a dump file to store the intermediate results. This output is used in turn as input for the following script.
The reason for doing this that if something goes wrong you do not lose everything and can restart from the previous step.
Each step will generate a log of the results in the form of a csv file for reference purposes, e.g. detecting which requests failed and why. For example, importing tickets will create the data/jira/:space/jira-tickets.csv
file where :space
is the Assembla space name ASSEMBLA_SPACE
in the .env
file hyphenated with the hostname-port
. For example: space-name-jira-example-org-8080
.
While the script is being executed, information will be logged to the console. Be sure to inspect the information, as certain instruction might be given that you must follow before continuing to the next step.
First we export all the data from Assembla.
- Space (spaces, space_tools, users, user roles, tags, milestones, ticket statuses, ticket custom fields, documents, wiki pages and tickets)
- Tickets (comments, attachments, tags, associations)
- Report users
- Report tickets
Now that all of the Assembla data is available, we can now take this and import it into Jira.
- Create project (and board)
- Create issue link types
- Get settings (issue types, priorities, resolutions, roles, statuses and projects)
- Import users
- Download ticket attachments
- Create custom fields
- Import custom fields
- Import tickets
- Resolve/update ticket links
- Import ticket comments
- Import ticket attachments
- Update attachment links
- Update ticket status (resolutions)
- Update ticket associations
- Update ticket watchers
- Resolve/update ticket and comment external links
- Move stories to epics
- Move stories to epics which could not be exported
- Rank tickets (cloud only)
Using the Agile extension, create the sprints and populate the scrum/kanban board.
- Create sprints
- Update board
- Migrate Assembla wiki to Confluence space.
- Manual cleanup actions
Congratulations, you did it!
You will need to go to to the Jira hosted (jira.example.org
) or cloud (id.atlassian.net
) instance and login as admin.
Create the admin user as defined in the .env
file as JIRA_API_ADMIN_USERNAME
and ensure that this user is activated and belongs to the following groups:
- site-admins
- jira-administrators
Create the following new issue types:
- Spike
- Bug (if not already present)
Those tickets whose summary starts with Spike:
or Bug:
will acquire these issue types so it is important that they already exist.
These are defined in the .env
file, see ASSEMBLA_TYPES_EXTRA
below.
You will also need to configure the issue type scheme
for the project like this:
An example configuration file .env.example
is provided for you to define a number environment parameters which affect the behavior.
# --- General settings --- #
DATA_DIR=data
# Only include Assembla tickets which have been created on or after the following date, disabled by default.
#TICKETS_CREATED_ON=YYYY-MM-DD
DEBUG=false
# --- Assembla settings --- #
# Do NOT change the following line!
#For companies in Europe you might have to use 'eu-api' instead of 'api'
#ASSEMBLA_API_HOST=https://eu-api.assembla.com/v1
ASSEMBLA_API_HOST=https://[eu-]api.assembla.com/v1
ASSEMBLA_API_KEY=api-key
ASSEMBLA_API_SECRET=api-secret
#ASSEMBLA_URL_TICKETS=https://eu-app.assembla.com/spaces/[:space-name]/tickets
ASSEMBLA_URL_TICKETS=https://app.assembla.com/spaces/[:space-path]/tickets
ASSEMBLA_SPACE="Assembla Space Name"
ASSEMBLA_WIKI=https://[:company-name].assembla.com/spaces/[:space-path]/wiki
ASSEMBLA_WIKI_NAME="Assembla Wiki Name"
#ASSEMBLA_SKIP_ASSOCIATIONS=parent,child,story,subtask
# Ticket types extracted from ticket summary, e.g. starting with 'Spike: '
ASSEMBLA_TYPES_EXTRA=spike,bug
# 0: All Tickets, 1: Active Tickets, order by milestone, 4: Closed Tickets, order by milestone, personal reports
# start with "u"
ASSEMBLA_TICKET_REPORT=0
ASSEMBLA_TIMEZONE=+0100
# --- Jira API settings --- #
# Server type must be 'hosted' or 'cloud'
JIRA_SERVER_TYPE=cloud
# Base must start with 'https?://'
JIRA_API_BASE=https://jira.example.org
# Do NOT change the following line!
JIRA_API_HOST=rest/api/2
JIRA_API_PROJECT_NAME="Jira Project Name"
JIRA_API_PROJECT_KEY=jira-key
JIRA_BOARD_NAME="Jira Project Name: [:jira-key] board"
# Project type must be scrum (default) or kanban
JIRA_API_PROJECT_TYPE=scrum
JIRA_API_KEY=secret
JIRA_API_ADMIN_USER=john.doe
# You will need account ids for the admin and project lead when creating a new project. They can be
# one and the same person. To generate a list of known Jira users, execute `ruby jira_get_all_users.rb`
JIRA_API_ADMIN_ACCOUNT_ID=account-id
JIRA_API_LEAD_ACCOUNT_ID=account-id
JIRA_API_ADMIN_PASSWORD=secret
[email protected]
# IMPORTANT: Previously 'jira-administrators' was the default admin group name, but since this has changed recently
# to 'jira-admins-[:company-name]`, the following extra parameter has been added as workaround.
JIRA_API_ADMINS_GROUP=jira-admins-[:company-name]
JIRA_API_UNKNOWN_USER=unknown.user
[email protected]
JIRA_API_IMAGES_THUMBNAIL=description:false,comments:true
JIRA_API_USER_GROUPS=jira-administrators,jira-core-users,site-admins,jira-software-users
# Issues Configuration
# Set Default Issue Type if Plan Level is not set in Assembla, Default: task, Must be valid JIRA issue type
# JIRA_ISSUE_DEFAULT_TYPE=story
JIRA_API_ASSEMBLA_ID_IN_TITLE=false
JIRA_API_SKIP_EMPTY_COMMENTS=true
JIRA_API_SKIP_COMMIT_COMMENTS=true
# If JIRA_API_SKIP_COMMIT_COMMENTS is false, use BitBucket table for translations of assembla links to bitbucket repos.
# Important: the placeholder '[[REPO-NAME]]' must NOT be removed/changed, it is used for inserting the repository name.
# The ':company' field should be replaced with the company path.
BITBUCKET_REPO_URL='https://bitbucket.org/[:company-path]/[[REPO-NAME]]/commits'
BITBUCKET_REPO_TABLE=from_repos1|to_repos1,from_repos2|to_repos2,...,from_reposn|to_reposn
# Or you can provide a csv file to read in with the following columns titles:
# Assembla Space Key,Assembla Space Name,Assembla Repo Name,BitBucket Repo Name,Bitbucket Repo URL,Assembla Repo URL
# Experimental so use with some caution.
BITBUCKET_REPO_CONVERSIONS=data/bitbucket-repo-conversions.csv
# Mangle external emails not ending with the following suffixes, (must start with a '@') comment line in order to disable.
# Important: this needs to be restored after the migration so that the user can access the project as usual.
# Set the following line to 'true' if you want to enable email mangling, default is false.
MANGLE_EXTERNAL_EMAILS=false
[email protected],@company2.com,bedrijf1.nl
[email protected],[email protected]
# Cross project ticket linking
JIRA_API_SPACE_TO_PROJECT=assembla-space1-name:project1-key,assembla-space2-name:project2-key,...
JIRA_API_RE_TICKET=https?://.*?\.assembla\.com/spaces/(.*?)/tickets/(\d+)(?:\-[^)\]]+)?(?:\?.*\b)?
JIRA_API_RE_COMMENT=https?://.*?\.assembla\.com/spaces/(.*?)/tickets/(\d+).*?\?comment=(\d+)(?:#comment:\d+)?
JIRA_API_BROWSE_ISSUE=browse/[:jira-ticket-key]
JIRA_API_BROWSE_COMMENT=browse/[:jira-ticket-key]?focusedCommentId=[:jira-comment-id]
# Convert the Assembla ticket statuses to the equivalient Jira issue status (from:to) or just keep (from)
# Important: make sure that the new issue statuses have been added to the workflow.
JIRA_API_STATUSES="New:To Do,In Progress,Blocked,Testable,In Acceptance Testing,Ready for Deploy,Done,Invalid:Done"
# --- Jira Agile settings --- #
JIRA_AGILE_HOST=rest/agile/1.0
# --- Confluence settings --- #
CONFLUENCE_API=https://[:company-name].atlassian.net/wiki/rest/api
CONFLUENCE_SPACE=space-key
CONFLUENCE_API_KEY=secret
[email protected]
CONFLUENCE_PASSWORD=secret
In order to simplify the process for the customer of providing a list of multiple projects which need to be migrated, the following template file can be used for providing data per project:
.env-example-template.txt
which contains a list of only those values that differ per project, e.g. once the global credential and keys have been properly setup.
By using the filter TICKETS_CREATED_ON
you can limited the tickets to those that were created on or after the date indicated. So for example:
TICKETS_CREATED_ON=2017-06-01
would only include those tickets created on or after the first of June in the year 2017.
IMPORTANT: Using this settings will result in some ticket links that cannot be resolved any more, since they were created in the past and not included in the import data.
$ cp .env.example .env
Although the official Jira documentation claims that the hosted and cloud APIs are identical, I've found out that this isn't entirely true. There are a couple of minor differences that must be taken into account:
- Performance - The cloud server is MUCH slower than the hosted server, meaning that when imported long lists of tickets or whatever extreme patience is required.
- Stability - The hosted server generally works flawlessy and completes after the first run whereas the cloud server occasionally times out or returns a server error. For example, importing attachments.
- Users - When creating users the hosted server will automatically set activated to true, whereas the cloud server will NOT. In addition, the server can take too long causing the connection to timeout, meaning that you have to keep re-running the script until all of the user are created.
- Reporter - The hosted server will allow you to set the reporter when creating issues while the cloud server will NOT.
- Ranking - The hosted server will allow you to set the issue rank when creating issues while the cloud server will NOT.
- Comments - The hosted server will allow original comment authors to import comments while cloud server will NOT.
- Attachments - The cloud server is problematic, and certain extra actions must be taken.
- Storage - The cloud server imposes a file upload size limit (100MB). If possible, compress the file and try again my manually adding it as an attachment to the Jira issue.
The Jira server type is given by JIRA_SERVER_TYPE
which is defined as either hosted
or cloud
, and is detected automatically by the call:
GET /rest/api/2/serverInfo
def jira_get_server_type
...
end
where the value of response['deploymentType']
is used: Server => hosted
or Cloud => cloud
. This value is cached in the jira-serverinfo.csv
dump file.
Make sure you're using your Atlassian account email address and password for basic authentication, not your Jira username.
You can run the export in a number of stages, output files being generated at each point in the process.
Make sure that you are using the correct version of ruby.
$ rvm use `cat .ruby-version`
The output files are located in the directory data/assembla/:space/
as follows:
$ ruby 01-assembla_export_space.rb # => space-tools.csv, users.csv, user-roles.csv, ticket-tags.csv, \
milestones-all.csv, tickets-statuses.csv, tickets-custom-fields.csv, documents.csv, \
wiki-pages.csv, tickets.csv
$ ruby 02-assembla_export_tickets.rb [type] # => ticket-comments.csv, ticket-attachments.csv, \
ticket-tags.csv, ticket-associations.csv
$ ruby 03-assembla_report_users.rb # => report-users.csv
$ ruby 04-assembla_report_tickets.rb # => report-tickets.csv
Executing 01-assembla_export_space.rb
can be very time consuming, so you might want to break it up into smaller chunks and running them in parallel, by passing the optional type
(space_tools, users, user_roles, tags, milestones/all, tickets/statuses, tickets/custom_fields, documents, wiki_pages, tickets) as the first argument.
$ ruby 01-assembla_export_space.rb space_tools # => space-tools.csv
$ ruby 01-assembla_export_space.rb users # => users.csv
$ ruby 01-assembla_export_space.rb user_roles # => user-roles.csv
$ ruby 01-assembla_export_space.rb tags # => ticket-tags.csv
$ ruby 01-assembla_export_space.rb milestones/all # => milestones-all.csv
$ ruby 01-assembla_export_space.rb tickets/statuses # => tickets-statuses.csv
$ ruby 01-assembla_export_space.rb tickets/custom_fields # => tickets-custom-fields.csv
$ ruby 01-assembla_export_space.rb documents # => documents.csv
$ ruby 01-assembla_export_space.rb wiki_pages # => wiki-pages.csv
$ ruby 01-assembla_export_space.rb tickets # => tickets.csv
The same applies when executing 02-assembla_export_tickets.rb
, and you can break it up into smaller chunks by passing the optional type
(comments, attachments, tags or associations) as the first argument.
$ ruby 02-assembla_export_tickets.rb comments # => ticket-comments.csv
$ ruby 02-assembla_export_tickets.rb attachments # => ticket-attachments.csv
$ ruby 02-assembla_export_tickets.rb tags # => ticket-tags.csv
$ ruby 02-assembla_export_tickets.rb associations # => ticket-associations.csv
This allows you to recover better to the previous step in case of failure, for example near the end where you would lose all the data in the dump files.
If you also want to capture the output, then you can run the above commands like this:
$ ruby nn-assembla_xxx.rb | tee logs/nn.log
In other words:
$ ruby 01-assembla_export_space.rb | tee logs/01.log
$ ruby 02-assembla_export_tickets.rb | tee logs/02.log
$ ruby 03-assembla_report_users.rb | tee logs/03.log
$ ruby 04-assembla_report_tickets.rb | tee logs/04.log
And watch the progress as follows:
$ tail -f logs/nn.log
You can run the import in a number of stages, output files being generated at each point in the process.
IMPORTANT: Previously jira-administrators
was the default admin group name, but since this has changed recently
to jira-admins-[:company-name]
, the following extra parameter has been added to the .env-file as a workaround:
JIRA_API_ADMINS_GROUP=jira-admins-[:company-name]
POST /rest/api/2/project
{
key: project_key,
name: project_name,
projectTypeKey: 'software',
description: project_description,
projectTemplateKey: "com.pyxis.greenhopper.jira:gh-#{type}-template",
leadAccountId: account_id
}
where #{type}
must be either scrum
or kanban
.
$ ruby 05-jira_create_project.rb # => data/jira/:space/jira-serverinfo.csv
IMPORTANT: If you are creating a JIRA project for the first time, e.g. it does not exist already, you will have to include the following line in the .env
file:
JIRA_API_LEAD_ACCOUNT_ID=account-id
To get a list of known Jira users, execute ruby jira_get_all_users.rb
and look for the user you want to define as the project lead.
Depending on the value of JIRA_API_PROJECT_TYPE
in the .env
file, a scrum or kanban board will be created as well with board name {projectKey} board
.
The projectKey
is usually just the abbreviation of the project name in all capitals. Here is an example of a project with key ECT
:
POST /rest/api/2/issueLinkType
{
name: name,
inward: inward,
outward: outward
}
Execute the followng command:
$ ruby 06-jira_create_issuelink_types.rb # => data/jira/:space/jira-issuelink-types.csv
Some extra general information needs gathering before the migration can start.
GET /rest/api/2/{issuetype|priority|resolution|role|status|project}
Execute the following commands:
$ ruby 07-jira_get_info.rb # => data/jira/:space/jira-issue-types.csv
which will generate the following output file in the data/jira/:space
directory:
- jira-issue-types.csv
- jira-priorities.csv
- jira-resolutions.csv
- jira-roles.csv
- jira-statuses.csv
- jira-projects.csv
POST /rest/api/2/user
{
name: user['login'],
password: user['login'],
emailAddress: user['email'],
displayName: user['name']
}
Read in the Assembla user file data/assembla/:space/users.csv
and create the Jira users if they do not already exist.
$ ruby 08-jira_import_users.rb # => data/jira/:space/jira-users.csv
Make sure that all of the users have been activated by going into the admin dashboard user page. In the hosted version this should be the default, however in the cloud version you will need to change each user manually.
If this is the case, you will be given a list of those users that need to be activated.
Go to the Admin User Management:
and after clicking on the username click on the [Activate]-button:
All new user are assigned by default to the jira-software-users
group only. So do not forget to restore the orignal Assembla admin users permissions by also assigning them to the jira-administrators
group.
The following user:
as defined in the .env
file as JIRA_API_UNKNOWN_USER
.
A sanity check will also be made to ensure that the admin user defined as JIRA_API_ADMIN_USER
in the .env
file actually exists, is activated and belongs to both the site-admins
and the jira-administrators
groups. Otherwise, an error message is generated explaining that this needs to be corrected.
NOTE: Initially the users are created with the password
equal to their username. This is needed in order for the migration to succeed because of certain user permissions required. Do NOT change until after the migration has been completed.
IMPORTANT: At the end of the import you may be given a warning that certain users need to activate before continuing. Do NOT forget to do this as later actions requiring these users may fail.
By default, new users will be sent a notification email inviting them to join the Jira project. For external users you may not want this to happen. As at the time of this writing there does not seem to be a reliable API flag to disbale this, the mangle option was created as a workaround.
Given the company email suffix, any users not belonging to the this email suffix can have there emails mangled so that the 'adapted' email will not arrive. Once the migration is complete, you can restore the original emails for these users.
By default, emails are not tampered with.
An example:
# Mangling of certain external emails been enabled.
MANGLE_EXTERNAL_EMAILS=true
[email protected],@company2.com
[email protected],[email protected]
If a given email doesn't end with @company1.com
or @company2.com
then it will be mangled.
The email [email protected]
will be converted to [email protected]
.
The email [email protected]
will NOT be mangled.
The email [email protected]
will also NOT be mangled although it is an external email.
Before the attachments can be imported, they must first be downloaded to a local directory after which they can be imported into Jira.
This is accomplished by executing the following command:
$ ruby 09-jira_download_attachments.rb # => data/jira/:space/jira-attachments-download.csv
The downloaded attachments are placed in the data/jira/:space/attachments
directory with the same filename, and the meta information is logged to the file data/jira/:space/jira-attachments-download.csv
containing the following columns:
created_at|assembla_ticket_id|jira_ticket_id|filename|content_type
which is used to import the attachments into Jira in the following section. A check is made if the file already exists in order to avoid name collisions.
Note that in Jira images are treated as attachments and can be accessed that way via [[image:IMAGE|NAME]]
.
Note: If you get the error NOK (Tool not found for this project)
, try running the assembla_enable_file_tool.rb
helper script to fix it. If this doesn't work, then you will have to enable the file set via the Assembla admin dashboard.
Important: this step MUST be done before importing tickets (next section) in order that the markdown for embedded attachment (images) will work correctly.
This step is very important, so do not skip it. You are now ready to create the Jira custom fields, so execute the following command:
$ ruby 10-jira_create_custom_fields.rb screen_id1 screen_id2
The values of screen_id1
and screen_id2
are found by going to the screens page on the admin dashboard at JIRA_API_HOST/secure/admin/ViewFieldScreens.jspa
and clicking on the project Bug Screen Scheme
and Default Screen Scheme
links respectively.
You will be taken to the page whose url JIRA_API_HOST/secure/admin/ConfigureFieldScreenScheme.jspa?id=10001
indicates the screen id, in this example id=10001
.
This scripts scans the current Jira custom fields and creates the Assembla fields Assembla-xxx
which are missing. After that the fields are assigned to the relevant screens given by screen_id1
and screen_id2
.
POST /rest/api/2/screens/{screenId}/tabs/{tabId}/fields
{
"fieldId": "summary"
}
def jira_add_field(screen_id, tab_id, field_id)
...
end
Once all of the custom fields have been created, you want to make sure that a free text searcher
search template is selected so that the custom field can be sorted in Jira.
Go to the Issues Custom Fields
page and click on the right of the given row to go to the Edit Custom Fields Details
.
If for one reason or the other the script fails, you will need to define manually the following custom fields (text field read-only):
- Assembla-Id
- Assembla-Created-On
- Assembla-Due-Date
- Assembla-Status
- Assembla-Milestone
- Assembla-Reporter
- Assembla-Assignee
- Assembla-Completed
- Assembla-Estimate
- Assembla-Worked
- Assembla-Remaining
- Assembla-(custom-field)
where Assembla-(custom-field)
is defined by ASSEMBLA_CUSTOM_FIELD=custom-field
in the .env
configuration file.
and assign them to the following screens:
- Bug Screen
- Default Issue Screen
Additionally the following already existing custom fields need to be assigned the the same screens:
- Epic Link
- Epic Name
- Rank
- Sprint
- Story Points
On the View Field Configuration Page
ensure the same for:
- Resolution
The same applies to the Configure Screen Page
for BOTH the Scrum Bug and Scrum Default Issue pages, so include the
following additional (default) fields:
- Epic Name
- Labels
- Assignee
- Rank
- Resolution
Assembla allows the use of a number of user-defined field types, namely: List
, Team List
, Numeric
and Text
.
These need to be mapped properly to the relevant Jira custom fields implemented as Jira plugins com.atlassian.jira.plugin.system.customfieldtypes:<type>
as follows:
Assembla type | Jira plugin | Searcher key |
---|---|---|
List | select | multiselectsearcher |
Team List | userpicker | userpickergroupsearcher |
Numeric | float | exactnumber |
Text | textfield | textsearcher |
POST /rest/api/2/screens/{screenId}/tabs/{tabId}/fields
{
"name": name,
"description": description,
"type": type,
"searcherKey": searcherKey
}
Execute the following script to have this done:
$ ruby 11-jira_import_custom_fields.rb
If any custom fields fail to be created, a list will be generated which you can use to fix manually to the Jira project, something like this:
IMPORTANT: the following custom JIRA fields MUST be linked to the Scrum Default and Scrum Bug screens.
* Coverage => type='List'
* Rates => type='List'
* Explanation => type='Text'
IMPORTANT: The following custom JIRA fields are LISTS and you MUST configure them and add the given options.
* Coverage => ["Low", "Medium", "High"]
* Rates => ["1", "2", "3", "5", "8"]
So clearly we have two IMPORTANT actions to take care of manually before we continue to the next step.
- The first action is to link the listed custom fields to the two screens mentioned above.
In the issues configure screen at the very bottom you will find a select field in which you can enter the name of the given custome field which needs to be added to the sreen..
For every listed custome field, please repeat this twice, once for the scrum default issue screen and once for the scrum bug screen.
- The second action is to configure the the listed custom fields and add the options shown.
Select the given custom field.
Configure it to include the correct item(s).
Alright, this is the moment we've all been waiting for! It's time to import the Assembla tickets and create the matching Jira issues. Here we go.
POST /rest/api/2/issue
{
create: {},
fields: {
project: { id: project_id },
summary: summary,
issuetype: { id: issue_type[:id] },
assignee: { name: assignee_name },
reporter: { name: reporter_name },
priority: { name: priority_name },
labels: labels,
description: description,
...
customfield_assembla_id: ticket_number,
customfield_assembla_custom: custom_field,
customfield_assembla_status: status_name,
customfield_assembla_milestone: milestone[:name],
customfield_rank: story_rank, # hosted only
customfield_assembla_reporter: UNKNOWN_USER, # if reporter is missing
customfield_assembla_assignee: '', # if assignee cannot be assigned issues
customfield_epic_name: EPIC_NAME, # if issue type is epic
parent: { id: parent_id }, # if issue type is sub-task
...
}
}
Custom fields are also handled accordingly:
if custom_field
assembla_custom_field = "Assembla-#{ASSEMBLA_CUSTOM_FIELD}"
payload[:fields]["#{@customfield_name_to_id[assembla_custom_field]}".to_sym] = custom_field
end
Now you are ready to import all of the tickets. Execute the following command:
$ ruby 12-jira_import_tickets.rb # => data/jira/:space/jira-tickets.csv
Results are saved in the output file data/jira/:space/jira-tickets.csv
with the following columns:
jira_ticket_id|jira_ticket_key|project_id|summary|issue_type_id|issue_type_name|assignee_name| \
reporter_name|priority_name|status_name|labels|description|assembla_ticket_id|assembla_ticket_number| \
custom_field|milestone_name|story_rank
During the conversion, any differences between the original Assembla ticket description and the newly created Jira issue description is recorded in the data/jira/:space/jira-tickets-diffs.csv
file. This is a good place to look so you can verify that indeed the markdown conversion produced the expected results.
An additional output file data/jira/:space/jira-ticket-links.csv
is created which contains those embedded ticket links that could not be resolved. This is used in the following step.
Once completed, check if there are any failed ticket imports where in the results
column a value of NOK
is indicated. If present, you can create the Jira issue manually.
Note: it is not possible for the original reporter (creator) of the Assembla ticket to be able to create a new issue, this is only allowed for the admin user, e.g. headers = JIRA_HEADERS_ADMIN
.
You might receive an error about a certain issue type that cannot be found. For example Spike
. This is because you did not create the needed issue types. Please follow instructions in the preparations section above very carefully, and then rerun the 07-jira_get_info.rb
script.
Another error message is Field 'field-name' cannot be set. It is not on the appropriate screen, or unknown
. This is because either a custom field has not been created or the custom field has not been added to the required screen. See previous section and follow instructions carefully.
In the ticket summary and description, ticket links #123
need to be converted to the relevant Jira issue links PRJ-456
, which can only be done AFTER all the tickets have been imported.
The output file data/jira/:space/jira-ticket-links.csv
generated in the previous step is used as the input.
Run the following command in order to do this:
$ ruby 13-jira_update_ticket_links.rb
Note: for one reason or another, not all Assembla links point to valid tickets (deleted, moved or whatever), so these will be marked as invalid by strikethru, e.g. -#123-.
POST /rest/api/2/issue/{issueIdOrKey}/comment
{
body: "comments go here..."
}
Now you are ready to import all of the comments. Execute the following command:
$ ruby 14-jira_import_comments.rb # => data/jira/:space/jira-comments.csv
Results are saved in the output file data/jira/:space/jira-comments.csv
with the following columns:
jira_comment_id|jira_ticket_id|assembla_comment_id|assembla_ticket_id|user_login|body
During the conversion, any differences between the original Assembla ticket comments and the newly created Jira issue comments is recorded in the data/jira/:space/jira-comments-diffs.csv
file. This is a good place to look so you can verify that indeed the markdown conversion produced the expected results.
Since there are so many more comments than tickets, this usually takes the longest by far of all the scripts. My experience using a normal Internet connection to a Jira Cloud instance is that I can import around 60-65 comments per hour.
So assuming we have 2000 tickets with on average 5 comments per ticket, there will be a total of 10,000 comments which will take about 2 hours and 45 minutes.
If JIRA_API_SKIP_EMPTY_COMMENTS
is true, then only non-empty comments will be imported, e.g. ignore Assembla history stuff. This will make the process go a bit faster, for those impatient folks in the crowd.
Note: we allow the original creators of the Assembla comments to be able to create the new Jira comments, therefore retaining ownership.
curl -D- -u admin:admin -X POST -H "X-Atlassian-Token: no-check" -F "[email protected]" \
api/2/issue/{issueIdOrKey}/attachments
Now you are ready to import all of the attachments that were downloaded earlier. Execute the following command:
$ ruby 15-jira_import_attachments.rb [restart_offset] # => \
data/jira/:space/jira-attachments-import-ok.csv
data/jira/:space/jira-attachments-import-nok.csv
Make sure that the admin settings for attachment size is large enough to allow all of the largest attachments to be uploaded.
When completed, don't forget to restore the size to the original value.
Note: The Jira server sometimes has problems processing attachments too quickly and might return an error. In that case, just restart the command and pass it the offset where you want to restart from.
IMORTANT: For the cloud version, the import might fail initially with a 401 Unauthorized
error. Try changing the admin login, logging out and then logging back in again. Hopefully it will now work.
I was able to get things working by defining the following headers:
auth = Base64.strict_encode64(admin_email + ':' + admin_password)
headers = { 'Authorization': "Basic #{auth}", 'X-Atlassian-Token': 'no-check' }
Once this script has completed, check out the jira-attachments-import-nok.csv
file and recover the failed attachments by manually adding them to the indicated Jira issue.
See: Atlassian Community Ticket.
Another note: we allow the original creators of the Assembla attachments to be able to create the new Jira attachments, therefore retaining ownership.
When the migration is completed, one may have a look at the jira-attachments-import-nok.csv
file and decide whether the failed attachments can be recovered.
Now that we have imported the attachments, we can convert the Assembla markdown format
[[file:attachment_id|filename]]
into the Jira markdown format
[filename|JIRA_API_BASE/secure/attachment/attachment_id/filename]
These markdown links can appear in both the issue description
or in the comment body
fields.
You will now need to execute the following script:
$ ruby 16-jira_update_attachment_links.rb
Now you are ready to update the Jira tickets in line with the original Assembla state. Execute the following command:
$ ruby 17-jira_update_status.rb # => data/jira/:space/jira-update-status.csv
If there are any status types which are missing, the script will abort and display a list of status names that you will have to add manually to Jira.
Important: the Jira API requests MUST be made with an Authorization Header constructed with the reporter_name
(issue creator), otherwise a 403 Forbidden
error will be returned.
For the default Assembla associations the relationship names are:
# | Name | Ticket2 | Ticket1 |
---|---|---|---|
0 | Parent | is parent of | is child of |
1 | Child | is child of | is parent of |
2 | Related | related to | |
3 | Duplicate | is duplication of | |
4 | Sibling | is sibling of | |
5 | Story | is story | is subtask of |
6 | Subtask | is subtask of | is story |
7 | Dependent | depends on | |
8 | Block | blocks |
or in understandable spoken word:
0 - Parent (ticket2 is parent of ticket1 and ticket1 is child of ticket2)
1 - Child (ticket2 is child of ticket1 and ticket2 is parent of ticket1)
2 - Related (ticket2 is related to ticket1)
3 - Duplicate (ticket2 is duplication of ticket1)
4 - Sibling (ticket2 is sibling of ticket1)
5 - Story (ticket2 is story and ticket1 is subtask of the story)
6 - Subtask (ticket2 is subtask of a story and ticket1 is the story)
7 - Dependent (ticket2 depends on ticket1)
8 - Block (ticket2 blocks ticket1)
For the default Jira issue link types we have:
Name | Inward | Outward |
---|---|---|
Blocks | is blocked by | blocks |
Cloners | is cloned by | clones |
Duplicate | is duplicated by | duplicates |
Relates | relates to | relates to |
POST /rest/api/2/issueLink
{
type: {
name: name
},
inwardIssue: {
id: ticket1_id
},
outwardIssue: {
id: ticket2_id
}
}
However, since Jira already takes care of a number of issue links during issue creation (story, subtask, etc), we should disable them in the .env
configuration file like this:
ASSEMBLA_SKIP_ASSOCIATIONS=parent,child,story,subtask
If for some reason you do not want to do this, simply comment out the line, or if you prefer to skip other Assembla association just edit the line.
Now you are ready to update the Jira tickets to reflect the original Assembla associations. Execute the following command:
$ ruby 18-jira_update_association.rb # => data/jira/:space/jira-update-associations.csv
Important: the Jira API requests MUST be made with an Authorization Header constructed with the reporter_name
(issue creator), otherwise a 403 Forbidden
error will be returned.
POST /rest/api/2/issue/{issueIdOrKey}/watchers
'"username"'
Now you are ready to convert the Assembla followers list to the Jira issue watchers list. Execute the following command:
$ ruby 19-jira_update_watchers.rb # => data/jira/:space/jira-update-watchers.csv
Important: the Jira API requests MUST be made with an Authorization Header constructed with the username
(watcher), otherwise a 403 Forbidden
error will be returned.
In the Assembla ticket description and comment body, we might have embedded (external) ticket links that have to be converted to the Jira format.
IMPORTANT: In order to be able to resolve links to external projects, all external projects which are sharing this data need to have been migrated up to but NOT including this step. Once all relevant projects have been migrated to this point, then it is possible to proceed.
These tickets can only be resolved using existing dumps files (data/jira/:space-name/jira-tickets.csv
and data/jira/:space-name/jira-comments.csv
) from previous migrations that are indicated in the .env
file as follows:
JIRA_API_SPACE_TO_PROJECT=space1-name:project1-key,space2-name:project2-key
Only values of space-name
present in the JIRA_API_SPACE_TO_PROJECT
parameter in order to be translated into the Jira equivalent.
For links that point to TICKETS, the captured format looks like:
BASE = https?://.*?\.assembla\.com/spaces/(:space-name)
BASE/tickets/(:ticket-number)
BASE/tickets/(:ticket-number)-.*#/activity/ticket:
BASE/tickets/(:ticket-number)/details
BASE/tickets/(:ticket-number)-.*/details
BASE/tickets/(:ticket-number)-.*/details#
REGEX = https?:\/\/.*?\.assembla\.com\/spaces\/(.*?)\/tickets\/(\d+)(?:\-.*)?(?:\?.*\b)?
$1 = space-name
$2 = ticket-number
For links that refer to COMMENTS, we have:
BASE = https?://.*?\.assembla\.com/spaces/(:space-name)
BASE/tickets/(:ticket-number)/details?comment=(:comment-id)
BASE/tickets/(:ticket-number)-.*/details?comment=(:comment-id)
REGEX = https?:\/\/.*?\.assembla\.com\/spaces\/(.*?)\/tickets\/(\d+).*?\?comment=(\d+)(?:#comment:\d+)?
$1 = space-name
$2 = ticket-number
$3 => comment-id
and then the links are converted like this:
issue => /browse/[JIRA_ISSUE_KEY]
comment => /browse/[JIRA_ISSUE_KEY]?focusedCommentId=[JIRA_COMMENT_ID]&page= \
com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-[JIRA_COMMENT_ID]
Experimental. For links that refer to COMMITS, we have:
# https?://.*?\.assembla\.com/spaces/(.*?)/git/commits/[:hash]
@re_commit = %r{https?://.*?\.assembla\.com/spaces/(.*?)/git/commits/([a-z0-9]+)}
# => https://bitbucket.org/[:company-name]/[[REPO-NAME]]/commits/[:commit_hash]'
If the environment variable JIRA_API_SKIP_COMMIT_COMMENTS
is set to false, then a BitBucket table can be used for
converting Assembla commit links to bitbucket repos.
BITBUCKET_REPO_URL='https://bitbucket.org/[:company-path]/[[REPO-NAME]]/commits'
BITBUCKET_REPO_TABLE=from_repos1|to_repos1,from_repos2|to_repos2,...,from_reposn|to_reposn
Rather than pasting all the from|to
combinations, for long lists it may be more feasible to load a csv-file instead.
# Assembla Space Key,Assembla Space Name,Assembla Repo Name,BitBucket Repo Name,Bitbucket Repo URL,Assembla Repo URL
BITBUCKET_REPO_CONVERSIONS=data/bitbucket-repo-conversions.csv
This script should be used carefully as it will cause irreversible updates. Check it out first to make sure that it is executing as expected by running the following command:
$ ./jira_dry_run_update_ext_links.sh >jira_dry_run_update_ext_links.log 2>&1
Execute the following command to update all external links:
$ ruby 20-jira_update_ext_links.rb => jira-links-external-all.csv
jira-links-external-updated.csv
Two output files are generated for reference:
jira-links-external-all.csv => all detected external links are listed
jira-links-external-updated.csv => only those external links actually updated
Check the output file jira-links-external-all.csv
for the external links that resulted in errors, e.g. result NOK
. The message
field will give you the error that the server returned, so that you can hopefully fix this manually.
POST /rest/agile/1.0/epic/{epicIdOrKey}/issue
{
"issues": issues
}
The Jira stories originally belonging to an epic in Assembla now need to be added to the newly created Jira epic.
In order to do this you need to execute the following command.
$ ruby 21-jira_update_epics.rb
The results are saved in the jira-update-epics.csv
output file.
Check this output file for the epics that resulted in errors, e.g. result NOK
. The message
field will give you the error that the server returned, so that you can hopefully fix this manually. For example, Issue 'EC-71' is an epic and therefore cannot be associated to another epic
is a common message.
If errors occur, e.g. Issue 'EC-71' is an epic and therefore cannot be associated to another epic
, you should run the following recovery script which will attempt to fix most of the problems:
$ ruby 22-jira_update_epics_nok.rb
The results are saved in the jira-update-epics_nok.csv
output file, a result of NOK
meaning that you may attempt to fix it manually with the help of the message
column giving the error text.
Only needed for the Jira server type is cloud
. Since this was not possible during the ticket creation, now is the time to rank the imported issues using the original Assembla values.
$ ruby 23-jira_rank_tickets.rb
You are now ready to setup the scrum board, create sprints, and assign issues to the correct sprints as well as the backlog. In the .env
file, take notice of the following values:
JIRA_API_PROJECT_NAME=name
JIRA_API_PROJECT_TYPE=scrum
JIRA_BOARD_NAME=name:Scrum Board Name
These will be used as placeholder values below.
When the scrum board was created with the project, all issues are assigned to the project are automatically put in the backlog.
Now you are ready to setup the sprints by executing the following command:
$ ruby 24-jira_create_sprints.rb # => data/jira/:space/jira-create-sprints.csv
The issues are redistributed to the sprints they belong to and the most recent sprint is set as the active
sprint.
If the milestone title is not less than 30 characters, then it will be truncated with an ellipsis before assigning the sprint name to it.
In order to be able to create a sprint, both a start and an end date must be provided.
If no start date is given, then by default a date 2 weeks previous to the end date will be used, and if there is no end date provided, then 2 weeks before the current date will be used.
If no end date is given, then by default a date 2 weeks after the start date will be used, and if there is no start date provided, then 2 weeks after the current date will be used.
You might receive an 403 Unauthorized
error. If this is the case, go to the Jira application, login as admin and try again.
The final step after the board and sprints have been created is to copy the Assembla cardwall columns (ticket statuses) to the Jira board and to order the issues by rank as they were in Assembla.
In order to achieve this, execute the following command:
$ ruby 25-jira_update_board.rb
GET /rest/agile/1.0/board/{boardId}/configuration
At the time of this writing, the Jira API does not yet support creating new columns. Therefore, when the command above is executed you will see some output:
Board columns needed: 7
* New => To Do
* In progress => In Progress
* Testable => Testable
* Ready for acceptance => Ready for Acceptance
* In acceptance testing => In Acceptance Testing
* Ready for deploy => Ready for Deploy
Board columns actual: 3
* To Do
* In Progress
* Done
Followed by instructions on which columns need to be added manaully with a link showing where this can be done:
Go to Configure 'BOARD_NAME | Column Management' and add the following columns:
* Testable
* Ready for Acceptance
* In Acceptance Testing
* Ready for Deploy
link: JIRA_API_BASE/secure/RapidView.jspa?rapidView=3&tab=columns
JIRA_API_STATUSES=New:To Do,In Progress,Blocked,Testable,Ready for Acceptance, \
In Acceptance Testing,Ready for Deploy,Done,Invalid:Done
This is a new script which allows one to take the previously generated Assembla wiki exported wiki-pages.csv
file,
and execute a best-effort import script to a given Confluence space.
The .env
file has been extended with the following extra items:
# --- Confluence settings --- #
CONFLUENCE_API=https://[:company-name].atlassian.net/wiki/rest/api
CONFLUENCE_SPACE=[:space-name]
[email protected]
CONFLUENCE_PASSWORD=secret
These values need to be updated to the once you will be using for the Wiki migration.
First create the new Confluence space by going to the https://[:company-name].atlassian.net/wiki/
page and clicking
the Create Space
-button at the top right of the page.
Select the Blank space
option and enter the Space name
corresponding to the value of [:space-name]
in the .env
file and optionally the Space key
if you desire one other than the generated default.
Once the page has been created you can go ahead and run the script:
$ ruby 26-wiki_to_confluence.rb
The script attempts to do the following tasks:
- upload all pages
- update all page links
- upload all images
- update all image links
- update all markdown page links
- update all markdown url links
- upload all documents
- update all document links
- update all ticket links
Dowloaded documents can be found in the confluence/documents
directory and the downloaded images in the confluence/images
directory.
The generated csv-files are saved in the confluence
directory. These are used within the script but can also be reviewed to detect
any warning or errors.
Additionally, a number of (commented out) scripts at the end are provided to assist you with trouble-shooting and analyzing possible missing or incorrect attachment, ticket or image links.
check_for_regexes([/#\d+/, /\[.*?\]\(.*?\)/, /<code>.*?<\/code>/])
check_for_header_lines
check_for_tickets
The import of wiki pages into confluence involves a complex set of text transformation in which a best effort is attempted to convert the wiki html, markdown and plain text formats into the appropriate Confluence XHTML format.
This is very challenged and there will be some anomolies in the resulting transformations, so one is advised to double-check the results as best as possible.
Finally, cleanup actions need to be taken to finish things off.
- Deactivate users not needed.
- Give admin rights to relevant users.
- Assign project leads (and give permissions).
- Ask users to change password, check email and create avatar.
- Recover failed attachment uploads listed in
jira-attachments-import-nok.csv
. - Resolve failed external links listed as
NOK
injira-external-links.csv
. - Recover failed epic updates listed in
jira-update-epics-nok.csv
. - Use label filters to move issue to correct types, e.g.
bug
might be a label. - Check that tickets which are spikes are NOT epics Issue 14.
- Make backup of
data
directory including.env
file for future reference.
You should also double check that the all of the Assembla-Status
were converted properly to the correct Jira status. If that is not the case, then you can make changes in bulk. For instance, filter on issues where Assembla-Status is closed
and Jira status is NOT closed
and by selecting all you can convert them to Done
in one go.
project = PROJECT_KEY and Assembla-Status ~ Done and status != Closed ORDER BY created DESC
Followed by transition issues to done:
Another example might be selecting all issues with Assembla-Type
equal to Bug
to be converted to the Jira Bug
issue type.
project = PROJECT_KEY and Assembla-Type ~ Bug and issuetype != Bug ORDER BY created DESC
Followed by move issues:
It can be slightly tedious running scripts that take a long time to complete and keeping track of where you stand in the scripts pipeline.
In order to make this easier for you to track, here is a simple checklist where you can sign off each step and remember where you are.
Step | Actions | Item | Dir | Start | Done |
---|---|---|---|---|---|
01 | Assembla | Space | dn | ||
02 | Assembla | Tickets | dn | ||
03 | Assembla | Users | na | ||
04 | Assembla | Reports | na | ||
05 | Jira | Projects | up | ||
06 | Jira | Issue links | na | ||
07 | Jira | General info | na | ||
08 | Jira | Users | up | ||
09 | Jira | Attachments | dn | ||
10 | Jira | Create custom fields | dn | ||
11 | Jira | Import Custom fields | up | ||
12 | Jira | Tickets | up | ||
13 | Jira | Ticket links | up | ||
14 | Jira | Comments | up | ||
15 | Jira | Attachments | up | ||
16 | Jira | Attachment links | up | ||
17 | Jira | Status | up | ||
18 | Jira | Associations | up | ||
19 | Jira | Watchers | up | ||
20 | Jira (1) | Ext links | up | ||
21 | Jira | Epics | up | ||
22 | Jira (2) | Epics NOK | up | ||
23 | Jira (3) | Ranking | up | ||
24 | Board | Sprints | up | ||
25 | Board | Update | up | ||
26 | Wiki | Import | up | ||
27 | Cleanup | See list | na |
(1) first complete all projects up to this point before continuing (in order to ensure that all of the external links are resolved correctly).
(2) only if errors occurred in the previous step.
(3) only for cloud server.
As mentioned previously, during each step of the migration pipeline, the script will generate output in the form of CSV files to capture the data at that given moment.
In the data/assembla/:space
directory:
- documents.csv
- milestones-all.csv
- report-tickets.csv
- report-users.csv
- spaces.csv
- space-tools.csv
- tags.csv
- ticket-associations.csv
- ticket-attachments.csv
- ticket-comments.csv
- tickets.csv
- tickets-custom-fields.csv
- tickets-statuses.csv
- ticket-tags.csv
- user-roles.csv
- users.csv
- wiki-pages.csv
In the data/jira/:space
directory:
- jira-attachments-download.csv
- jira-attachments-import-nok.csv
- jira-attachments-import-ok.csv
- jira-comments.csv
- jira-comments-diffs.csv
- jira-issuelink-types.csv
- jira-issue-types.csv
- jira-links-external-all.csv
- jira-links-external-updated.csv
- jira-priorities.csv
- jira-projects.csv
- jira-resolutions.csv
- jira-roles.csv
- jira-serverinfo.csv
- jira-sprints.csv
- jira-statuses.csv
- jira-ticket-links.csv
- jira-tickets-associations.csv
- jira-tickets.csv
- jira-tickets-diffs.csv
- jira-tickets-status-updates.csv
- jira-tickets-watchers.csv
- jira-update-epics.csv
- jira-update-epics-nok.csv
- jira-users.csv
Most of the ticket fields are converted from Assembla to Jira via a one-to-one mapping. These fields are indicated as bold below:
- id
- number
- summary
- description
- priority (1 - Highest, 2 - High, 3 - Medium, 4 - Low, 5 - Lowest)
- completed_date
- component_id (deprecated)
- created_on
- permission_type
- importance (Sorting criteria for Assembla Planner) => 10104 Rank
- is_story (true or false, if true hierarchy_type = 2)
- milestone_id => 10103 Sprint
- tags
- followers
- notification_list
- space_id
- state
- 0 - closed, 1 - open
- status (new, in progress, blocked, testable, ready for acceptance, in acceptance testing, ready for deploy, done, invalid)
- story_importance (1 - small, 4 - medium, 7 - large) => 10105 Story Points (for stories only)
- updated_at
- working_hours
- estimate
- total_estimate
- total_invested_hours
- total_working_hours
- assigned_to_id
- reporter_id
- custom_fields
- hierarchy_type (0 - No plan level, 1 - Subtask, 2 - Story, 3 - Epic)
- is_support
- due_date
- picture_url
- issuetype
- timespent
- project
- fixVersions
- aggregatetimespent
- resolution (done, won't do, duplicate)
- resolutiondate
- workratio
- lastViewed
- watches
- thumbnail
- created
- priority (1 - Highest, 2 - High, 3 - Medium, 4 - Low, 5 - Lowest)
- labels
- timeestimate
- aggregatetimeoriginalestimate
- versions
- issuelinks
- assignee
- updated
- status (todo, done)
- components
- issuekey
- timeoriginalestimate
- description
- timetracking
- security
- attachment
- aggregatetimeestimate
- summary
- creator
- subtasks
- reporter
- aggregateprogress
- environment
- duedate
- progress
- comments
- votes
- worklog
- 10000 Development
- 10001 Team
- 10002 Organizations
- 10003 Epic Name
- 10004 Epic Status
- 10005 Epic Color
- 10006 Epic Link
- 10007 Parent Link
- 10100 [CHART] Date of First Response
- 10101 [CHART] Time in Status
- 10102 Approvals
- 10103 Sprint
- 10104 Rank
- 10105 Story Points
- 10108 Test sessions
- 10109 Raised during
- 10200 Testing status
- 10300 Capture for Jira user agent
- 10301 Capture for Jira browser
- 10302 Capture for Jira operating system
- 10303 Capture for Jira URL
- 10304 Capture for Jira screen resolution
- 10305 Capture for Jira jQuery version
- 10400 Assembla
In the data/confluence
directory:
- check-tickets.csv
- created-pages.csv
- created-pages-nok.csv
- links.csv
- uploaded-documents.csv
- uploaded-images.csv
- wiki-documents.csv
- wiki-pages-fixed.csv
- wiki-tickets.csv
Downloaded files can be found in the following directories:
- data/confluence/documents
- data/confluence/images
Depending on the server type, the authorization is handled slightly differently. For the hosted server the user_login and password (same as user_login) are used, whereas for the cloud we use the user_email and password.
def headers_user_login(user_login, user_email)
cloud = JIRA_SERVER_TYPE == 'cloud'
user_login_or_email = cloud ? user_email : user_login
user_password = user_login
base64_encoded = Base64.strict_encode64(user_login_or_email + ':' + user_password)
{
'Authorization': "Basic #{base64_encoded}",
'Content-Type': 'application/json'
}
end
where user_login
is either the JIRA_API_ADMIN_USER
for global configurations (create/update projects, issue types, issue link types and sprints) or the reporter_name
(issue creator) for updating certain issue specific attributes (status, associations, watchers, issue description and comment body).
The Assembly associations are converted into Jira issue links.
0 - Parent (ticket2 is parent of ticket1 and ticket1 is child of ticket2)
1 - Child (ticket2 is child of ticket1 and ticket2 is parent of ticket1)
2 - Related (ticket2 is related to ticket1)
3 - Duplicate (ticket2 is duplication of ticket1)
4 - Sibling (ticket2 is sibling of ticket1)
5 - Story (ticket2 is story and ticket1 is subtask of the story)
6 - Subtask (ticket2 is subtask of a story and ticket1 is the story)
7 - Dependent (ticket2 depends on ticket1)
8 - Block (ticket2 blocks ticket1)
See: http://api-docs.assembla.cc/content/ref/ticket_associations_fields.html
The Assembla ticket statuses are: new
, in progress
, blocked
, testable
, ready for acceptance
, in acceptance testing
, ready for deploy
, done
and invalid
.
An Assembla ticket can have two states: 0 - closed
(done or invalid) and 1 - open
(all others).
The Jira statuses are: todo
and done
. On creation, all Jira tickets are set initially to todo
by default.
The possible transitions for this initial todo
state are start progress
=> in progress
and done
=> done
.
During the migration, Assembla tickets that are marked as closed
will result in Jira issues marked as done
with resolution set to fixed
for Assembla ticket status done
and won't fix
for Assembla ticket status invalid
.
For Assembla tickets marked as in progress
the imported Jira issue will be set to in progress
.
IMPORTANT: all the other statuses will be ignored unless the administrator modifies the workflow for the given Jira project to include them explicitly.
The names of these newly defined transitions MUST be the same as the Assembla status names in order for the status migration to work properly.
The story_importance
field for Assembla tickets is ONLY used for story
type Jira issues.
For the time being components have not yet been implemented.
According to the Assembla API Documentation: Ticket components API is deprecated. Please use custom fields.
The Assembla markdown syntax is different from Jira Markdown. Therefore, the certain markdown notations will need to be translated as follows.
h1. TITLE
h2. TITLE
*bold*
_italic_
Bullet list
Numbered list
Numbered - Bullet list
Wiki links
[[ticket:NUMBER]]
#TICKET_NR => JIRA_TICKET_KEY
[[image:IMAGE]] => !name(IMAGE)|thumbnail!
[[image:IMAGE|text]] => !name(IMAGE)|thumbnail!
@NAME => [~accountid:id]
[[user:NAME]] => [~accountid:id]
[[user:NAME|text]] => [~accountid:id]
@INLINE_CODE@ => {{INLINE_CODE}} (monospaced)
<code>INLINE_CODE</code> => {{INLINE_CODE}} (monospaced)
[[url:URL|TEXT]] => [TEXT|URL]
[[url:URL]] => [URL|URL]
<pre><code> code-snippet </code></pre> => {code:java} code-snippet {code}
[[file:attachment_id|filename]] => [filename|JIRA_API_BASE/secure/attachment/attachment_id/filename]
In Assembla a block of code looks like this:
<pre><code>
code-snippet
</code></pre>
which will be transformed into Jira format like this:
{code:java}
code-snippet
{code}
Note that the images will have original or thumbnail sizes depending on the value of JIRA_API_IMAGES_THUMBNAIL
in the .env
file.
So for example:
JIRA_API_IMAGES_THUMBNAIL=description:false,comments:true
would insert original size images in the Jira issue description and thumbnail images in the Jira issue comments (which happens to be the default).
For the content available in the ticket summaries, descriptions and comments we have:
[summary, description, comments].each do |content|
content = reformat_markdown(content, opts)
end
where reformat_markdown
will do the following global substitutions:
gsub(/<pre><code>/i,'{code:java}').
gsub(/<\/code><\/pre>/i,'{code}').
gsub(/\[\[url:(.*?)\|(.*?)\]\]/i, '[\2|\1]').
gsub(/\[\[url:(.*?)\]\]/i, '[\1|\1]').
gsub(/<code>(.*?)<\/code>/i,'{{\1}}').
gsub(/@([^@]*)@( |$)/, '{{\1}}\2').
gsub(/@([a-z.-_]*)/i) { |name| markdown_name(name, user_ids) }.
gsub(/\[\[user:(.*?)(\|(.*?))?\]\]/i) { |name| markdown_name(name, user_ids) }.
gsub(/\[\[image:(.*?)(\|(.*?))?\]\]/i) { |image| markdown_image(image, images, content_type) }
- Unable to download Assembla attachments
GET https://api.assembla.com/v1/spaces/:space/documents/dfVsC8_o4r6OoPaMlMwbiA/download => NOK (Tool not found for this project)
. Please ensure that the space has file toolsets enabled by running theassembla_enable_file_tool.rb
helper script. - Strange permission errors when creating tickets, adding watchers, etc. This is more than likely because the user defined by
JIRA_API_ADMIN_USER
does not belong to thejira-administrators
group. - Ticket import error
key='issuetype', reason='The issue type selected is invalid.'
. Go to the project issue types scheme, edit and ensure that issue type is included in the list, e.g. spike. - Error
403 Unauthorized
. Go to the Jira application, login as admin and try again. - Import users to the cloud fails for some user for some mysterious reason (500 Internal Server Error). This happens sometimes, just restart the script. It should recover and continue where it last failed. If the problem keeps repeating itself, just keep on retrying the script until you make your way through the complete list.
- A
403 Forbidden
or401 Unauthorized
error is returned. Ensure that the Authorization header is correct. if that doesn't work, log into your Atlassian account id.atlassian.com and try changing your password. There are some known problems with a recent cloud upgrade, see Atlassian Community Ticket, and certain extra actions must be taken. If problem persists, make sure that you are physically logged in to the hosted or cloud instance. - Error
User cannot be assigned issues.
Activate, login as user and then deactivate. - If issue is an epic then the epic name custom field is required.
- XSRF check failed => This is a known bug.
- Ticket or other import fails with the error message
Field 'field-name' cannot be set. It is not on the appropriate screen, or unknown
. Ensure that the custom field 'field-name' has been created and assigned to the required screens (see above). If this doesn't work, make sure that the user named in the authorization header has enough rights to make these changes. - Error
key='customfield_10100 (Assembla-Completed)', reason='Operation value must be a number'
, ensure that the custom field is the correct type: text field read-only.
With such a complicated tool, there will always be some loose ends and/or additional work to be done at a later time. Hopefully in the not so distant future, I'll have some time to tackle one or more of the following items:
- Must have: Update readme screenshots and relevant screen associations, e.g. only
Scrum Default Issue Screen
is required. Issue 6 - Bug: Ticket type 'Spike' is converted to an Epic. Issue 14
- Nice to have: Ignore sprints with no issues Issue 27
- Nice to have: Split large imports into smaller batches Issue 26
- Nice to have: Support multiple Assembla custom fields instead of just one. Issue 2
- Nice to have: Rank tickets (cloud) in batches of fifty instead of individually. Issue 15
- Nice to have: Create Jira board columns in line with the original Assembla cardwall columns (statuses = blocked, testable, ready for acceptance, in acceptance testing, ready for deploy) and populate with the relevant issues. Issue 4
- Nice to have: Allow data dumps to restart with all newer items since last dump, rather than having to start all over again. This is already the case for attachments, but should be possible with tickets, comments, etc. Issue 5
- Nice to have: Assembla tickets with tag
bug
should be converted into Jira issue of typebug
. Issue 7 - Wish: Allow themes to be converted into Epics (additional .env file option). Currently epics are only created for tickets with summaries that start with 'EPIC:' which in hindsight is probably not the best way of doing this. Issue 3
- Wish: Use a user-defined Jira project template instead of requiring the user to define stuff manually. Issue 9
- Wish: Assign original authors as creators of tickets (this might not be possible) Issue 10
- Refactor: Merge the recovery script
18-jira_update_epics_nok.rb
into18-jira_update_epics.rb
Issue 16 - Refactor: cleanup code, remove duplication, fix rubocop warnings, and make more object-oriented using classes. Issue 11
-
Assembla
-
Jira
Do you require assistance with the migration or need some new functionality that is not yet part of this package?
No worries, I can certainly help you out.
Feel free to contact me!
Kiffin Gish < [email protected] >
Gishtech => Advanced Software Development for the Web
"You're never too old to learn new stuff..."