jira-remotelinker 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ # Local additions and development tools go here.
5
+ if File.exists? "#{__FILE__}.local"
6
+ eval(File.read("#{__FILE__}.local"), binding)
7
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Charlie Sharpsteen
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,74 @@
1
+ # jira-remotelinker
2
+
3
+ A small command line tool for creating bidirectional remote links between JIRA instances through REST API calls.
4
+
5
+ ## Usage
6
+
7
+ To use this tool, you will need to first connect your JIRA instances through [Application Links](https://confluence.atlassian.com/display/JIRA/Linking+to+Another+Application).
8
+ Then, create a YAML file describing the JIRA instances along with the links that connect them:
9
+
10
+ ```yaml
11
+ public:
12
+ base_url: 'https://jira6-public.example.com'
13
+ name: 'Name of Application Link for Private instance'
14
+ uuid: 'UUID of Application Link for Private instance'
15
+ username: 'some user with create link permissions'
16
+ password: 'password for user'
17
+ private:
18
+ base_url: 'https://jira6-private.example.com'
19
+ name: 'Name of Application Link for Public instance'
20
+ uuid: 'UUID of Application Link for Public instance'
21
+ username: 'some user with create link permissions'
22
+ password: 'password for user'
23
+ ```
24
+
25
+ Discovering the UUID associated with an application link is a bit tricky as this value does not appear to be publicly exposed.
26
+ However, the UUID is required for the creation of fully functioning cross-application links.
27
+ One way to extract the `uuid` associated with an application link is through the following database query (tested on JIRA 6.1.x backed by PostgreSQL):
28
+
29
+ ```sql
30
+ /* Retrieve Application Link names and UUIDs From JIRA
31
+ * Based on a query described in the Atlassian troubleshooting docs:
32
+ *
33
+ * https://confluence.atlassian.com/display/JIRAKB/How+to+remove+Application+link+directly+through+database
34
+ *
35
+ */
36
+ SELECT
37
+ propertystring.propertyvalue AS "name",
38
+ SUBSTR(propertyentry.property_key,16,36) AS "uuid"
39
+ FROM
40
+ propertyentry, propertystring
41
+ WHERE
42
+ propertyentry.id = propertystring.id AND
43
+ propertyentry.property_key like 'applinks.admin%name';
44
+
45
+ ```
46
+
47
+ The second piece of required input is a CSV file containing the issue link data.
48
+ The required pieces of data are:
49
+
50
+ - **source_instance:** The name of a JIRA instance defined in the YAML configuration file.
51
+ - **source_id:** The global numeric id of the source ticket.
52
+ - **source_key:** The project-specific id of the source ticket (i.e. "PUB-2").
53
+ - **source_relation:** The relationship between the source ticket and the target ticket (i.e. "blocks").
54
+ - **target_instance:** The name of a JIRA instance defined in the YAML configuration file.
55
+ - **target_id:** The global numeric id of the target ticket.
56
+ - **target_key:** The project-specific id of the target ticket (i.e. "PRIV-20").
57
+ - **target_relation:** The relationship between the target ticket and the source ticket (i.e. "blocked by").
58
+
59
+ These fields are laid in a CSV file in the following order:
60
+
61
+ source_instance | source_id | source_key | source_relation | target_instance | target_id | target_key | target_relation
62
+ --- | --- | --- | --- | --- | --- | --- | ---
63
+ public | 10020 | PUB-2 | blocks | private | 11234 | PRIV-20 | blocked by
64
+ public | 10042 | PUB-7 | duplicated by | private | 11555 | PRIV-42 | duplicates
65
+
66
+
67
+ Once the YAML and CSV files have been created, the tool can be invoked:
68
+
69
+ jira-remotelinker jira_instances.yaml link_data.csv
70
+
71
+ ---
72
+ <p align="center">
73
+ <img src="http://i.imgur.com/iDFAxAM.jpg" />
74
+ </p>
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'methadone'
5
+ require 'progressbar'
6
+
7
+ require 'yaml'
8
+ require 'csv'
9
+
10
+ require 'jira-remotelinker'
11
+
12
+ class App
13
+ include Methadone::Main
14
+ include Methadone::CLILogging
15
+
16
+ description 'A small command line tool for creating remote links through the JIRA REST API'
17
+ version JiraRemotelinker::VERSION
18
+
19
+ use_log_level_option
20
+
21
+ arg :jira_config, 'A YAML file containing configuration and API access info for the target JIRA instances'
22
+ arg :link_data, 'A CSV file containing issue link data'
23
+
24
+ main do |jira_config, link_data|
25
+ config = YAML.load_file jira_config
26
+
27
+ # Create a REST client for each JIRA instance defined in the config.
28
+ config.each do |_, instance|
29
+ instance['client'] = JiraRemotelinker::REST.factory instance['base_url'], instance['username'], instance['password'],
30
+ instance['name'], instance['uuid']
31
+ end
32
+
33
+ links = CSV.table link_data
34
+ pbar = ProgressBar.new 'making links', links.length
35
+
36
+ links.each do |row|
37
+ # Create link from source JIRA -> target JIRA
38
+ config[row[:source_instance]]['client'].post_issue_remotelink row[:source_key], row[:source_relation],
39
+ config[row[:target_instance]]['client'], row[:target_key], row[:target_id]
40
+
41
+ # Create reciprocal link from target JIRA -> source JIRA
42
+ config[row[:target_instance]]['client'].post_issue_remotelink row[:target_key], row[:target_relation],
43
+ config[row[:source_instance]]['client'], row[:source_key], row[:source_id]
44
+
45
+ pbar.inc
46
+ end
47
+
48
+ pbar.finish
49
+ end
50
+
51
+ go!
52
+ end
@@ -0,0 +1,14 @@
1
+ /* Retrieve Application Link names and UUIDs From JIRA
2
+ * Based on a query retrieved from the Atlassian troubleshooting docs:
3
+ *
4
+ * https://confluence.atlassian.com/display/JIRAKB/How+to+remove+Application+link+directly+through+database
5
+ *
6
+ */
7
+ SELECT
8
+ propertystring.propertyvalue AS "name",
9
+ SUBSTR(propertyentry.property_key,16,36) AS "uuid"
10
+ FROM
11
+ propertyentry, propertystring
12
+ WHERE
13
+ propertyentry.id = propertystring.id AND
14
+ propertyentry.property_key like 'applinks.admin%name';
@@ -0,0 +1,91 @@
1
+ /* Huge, hairy SELECT statement that copies link data to CSV */
2
+ COPY (
3
+ WITH
4
+
5
+ /* This defines the group of projects which are migrating to a new JIRA
6
+ * instance
7
+ */
8
+ migrated_projects AS (
9
+ VALUES
10
+ ('FACT'),
11
+ ('PP'),
12
+ ('HI'),
13
+ ('OS'),
14
+ ('MCO'),
15
+ ('PDB'),
16
+ ('GTO'),
17
+ ('RAZOR')
18
+ ),
19
+
20
+ /* JIRA 6.1 replaced the pkey column which held the issue key, "PP-22", with
21
+ * the issuenum column which just holds the issue number: "22". This view
22
+ * re-constitutes the issue keys, plus saves the project identifier, "PP", so
23
+ * that it can be later checked against the list defined above.
24
+ */
25
+ munged_issues AS (
26
+ SELECT
27
+ jiraissue.id,
28
+ project.pkey || '-' || jiraissue.issuenum AS "key",
29
+ project.pkey
30
+ FROM
31
+ jiraissue,
32
+ project
33
+ WHERE
34
+ jiraissue.project = project.id
35
+ ),
36
+
37
+ /* This selects all links that originate OR terminate in the group of
38
+ * migrated projects but do not originate AND terminate in the group of
39
+ * migrated projects.
40
+ */
41
+ munged_links AS (
42
+ SELECT
43
+ issuelink.*
44
+ FROM
45
+ issuelink
46
+ JOIN
47
+ munged_issues AS source_issue
48
+ ON issuelink.source = source_issue.id
49
+ JOIN
50
+ munged_issues AS dest_issue
51
+ ON issuelink.destination = dest_issue.id
52
+ WHERE
53
+ (
54
+ source_issue.pkey IN (SELECT * FROM migrated_projects) AND
55
+ dest_issue.pkey NOT IN (SELECT * FROM migrated_projects)
56
+ ) OR
57
+ (
58
+ source_issue.pkey NOT IN (SELECT * FROM migrated_projects) AND
59
+ dest_issue.pkey IN (SELECT * FROM migrated_projects)
60
+ )
61
+ )
62
+
63
+ /* This selection gathers link data targetted by the munged_links query and
64
+ * exports it to CSV in the format expected by the jira-remotelinker tool.
65
+ */
66
+ SELECT
67
+ CASE
68
+ WHEN source_issue.pkey IN (SELECT * FROM migrated_projects) THEN 'public'
69
+ ELSE 'private' END
70
+ AS "source_instance",
71
+ source AS "source_id",
72
+ source_issue.key AS "source_key",
73
+ outward AS "source_relation",
74
+ CASE
75
+ WHEN dest_issue.pkey NOT IN (SELECT * FROM migrated_projects) THEN 'private'
76
+ ELSE 'public' END
77
+ AS "target_instance",
78
+ destination AS "target_id",
79
+ dest_issue.key AS "target_key",
80
+ inward AS "target_relation"
81
+ FROM munged_links AS issuelink
82
+ JOIN
83
+ issuelinktype AS type
84
+ ON issuelink.linktype = type.id
85
+ JOIN
86
+ munged_issues AS source_issue
87
+ ON issuelink.source = source_issue.id
88
+ JOIN
89
+ munged_issues AS dest_issue
90
+ ON issuelink.destination = dest_issue.id )
91
+ TO '/tmp/links_to_migrate.csv' WITH CSV HEADER;
@@ -0,0 +1,13 @@
1
+ public:
2
+ base_url: 'https://jira6-public.example.com'
3
+ # See get_applink_uuid.sql for a method of retrieving the Name and UUID
4
+ name: 'Name of Application Link for Private instance'
5
+ uuid: 'UUID of Application Link for Private instance'
6
+ username: 'some user with create link permissions'
7
+ password: 'password for user'
8
+ private:
9
+ base_url: 'https://jira6-private.example.com'
10
+ name: 'Name of Application Link for Public instance'
11
+ uuid: 'UUID of Application Link for Public instance'
12
+ username: 'some user with create link permissions'
13
+ password: 'password for user'
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'jira-remotelinker/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'jira-remotelinker'
8
+ spec.version = JiraRemotelinker::VERSION
9
+ spec.authors = ['Charlie Sharpsteen']
10
+ spec.email = ['source@sharpsteen.net']
11
+ spec.description = %q{A small command line tool for creating remote links through the JIRA REST API}
12
+ spec.summary = %q{This tool reads in issue link information from CSV and creates remote links using v2 of the JIRA REST API.}
13
+ spec.homepage = 'https://github.com/Sharpie/jira-remotelinker'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.require_paths = ['lib']
18
+ spec.bindir = 'bin'
19
+ spec.executables = ['jira-remotelinker']
20
+
21
+ spec.add_runtime_dependency 'methadone', '~> 1.3.0'
22
+ spec.add_runtime_dependency 'httparty', '~> 0.12.0'
23
+ spec.add_runtime_dependency 'progressbar'
24
+ end
@@ -0,0 +1,5 @@
1
+ module JiraRemotelinker
2
+ end
3
+
4
+ require 'jira-remotelinker/version'
5
+ require 'jira-remotelinker/rest'
@@ -0,0 +1,65 @@
1
+ require 'httparty'
2
+
3
+ module JiraRemotelinker
4
+ module REST
5
+ # An abstract template class that talks to v2 of the JIRA API.
6
+ # Instantiate concrete subclasses using the module function `factory`.
7
+ class AbstractV2
8
+ API_PATH = 'rest/api/2'
9
+ include HTTParty
10
+ format :json
11
+
12
+ # Sets three instance variables that represent a JIRA Application Link
13
+ class << self
14
+ attr_accessor :jira_type, :jira_name, :jira_uuid, :jira_url
15
+ end
16
+
17
+ def self.get_issue(issue_key)
18
+ get "/issue/#{issue_key}"
19
+ end
20
+
21
+ def self.get_issue_remotelinks(issue_key)
22
+ get "/issue/#{issue_key}/remotelink"
23
+ end
24
+
25
+ def self.post_issue_remotelink(issue_key, relationship, target_jira, target_key, target_id)
26
+ link_data = {
27
+ 'globalId' => "appId=#{target_jira.jira_uuid}&issueId=#{target_id}",
28
+ 'application' => {
29
+ 'type' => target_jira.jira_type,
30
+ 'name' => target_jira.jira_name
31
+ },
32
+ 'relationship' => relationship,
33
+ 'object' => {
34
+ 'url' => "#{target_jira.jira_url}/browse/#{target_key}",
35
+ 'title' => target_key
36
+ }
37
+ }
38
+
39
+ post "/issue/#{issue_key}/remotelink", {:body => link_data.to_json, :headers => {'Content-Type' => 'application/json'}}
40
+ end
41
+
42
+ def self.delete_issue_remotelink(issue_key, link_id)
43
+ delete "/issue/#{issue_key}/remotelink/#{link_id}"
44
+ end
45
+ end
46
+
47
+ module_function
48
+
49
+ # Create a concrete class that talks to a particular JIRA instance using the V2 API.
50
+ def factory(url, username, password, name, uuid, type = 'com.atlassian.jira')
51
+ Class.new(AbstractV2) do |klass|
52
+ klass.base_uri(File.join(url, AbstractV2::API_PATH))
53
+ klass.basic_auth(username, password)
54
+
55
+ # These are used during Remote Link creation and allow JIRA instances
56
+ # to pull issue data from each other and ensure that users are properly
57
+ # authorized to view such data.
58
+ klass.jira_name = name
59
+ klass.jira_uuid = uuid
60
+ klass.jira_type = type
61
+ klass.jira_url = url
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module JiraRemotelinker
2
+ VERSION = "0.1.1"
3
+ end
metadata ADDED
@@ -0,0 +1,110 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jira-remotelinker
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Charlie Sharpsteen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-10-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: methadone
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.3.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: httparty
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.12.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.12.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: progressbar
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: A small command line tool for creating remote links through the JIRA
63
+ REST API
64
+ email:
65
+ - source@sharpsteen.net
66
+ executables:
67
+ - jira-remotelinker
68
+ extensions: []
69
+ extra_rdoc_files: []
70
+ files:
71
+ - .gitignore
72
+ - Gemfile
73
+ - LICENSE
74
+ - README.md
75
+ - bin/jira-remotelinker
76
+ - examples/get_applink_uuid.sql
77
+ - examples/get_links_to_migrate.sql
78
+ - examples/jira_config.yaml
79
+ - jira-remotelinker.gemspec
80
+ - lib/jira-remotelinker.rb
81
+ - lib/jira-remotelinker/rest.rb
82
+ - lib/jira-remotelinker/version.rb
83
+ homepage: https://github.com/Sharpie/jira-remotelinker
84
+ licenses:
85
+ - MIT
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ requirements: []
103
+ rubyforge_project:
104
+ rubygems_version: 1.8.23
105
+ signing_key:
106
+ specification_version: 3
107
+ summary: This tool reads in issue link information from CSV and creates remote links
108
+ using v2 of the JIRA REST API.
109
+ test_files: []
110
+ has_rdoc: