jirasync 0.4.2 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
- *.gem
2
1
  *.rbc
3
2
  .bundle
4
3
  .config
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # jira-sync
2
2
 
3
- A suite of utilities to synchronise jira projects to the local file system
3
+ A suite of utilities to synchronise JIRA projects to the local file system
4
4
 
5
5
  ## Installation
6
6
 
@@ -44,26 +44,34 @@ The `sync_state.json` file contains information about the last sync, such as the
44
44
 
45
45
  The following statement will sync issues that have changed or where added during the last sync:
46
46
 
47
- jira-sync \
48
- --baseurl https://jira.myorganisation.com \
49
- --project MYPROJ \
50
- --user jira_user \
51
- --password jira_password \
52
- --target issues/MYPROJ/json \
53
- update
47
+ ~~~ {.bash}
48
+
49
+ jira-sync \
50
+ --baseurl https://jira.myorganisation.com \
51
+ --project MYPROJ \
52
+ --user jira_user \
53
+ --password jira_password \
54
+ --target issues/MYPROJ/json \
55
+ update
54
56
 
57
+ ~~~
55
58
 
56
59
  ### Formatting Issues
57
60
 
58
- While json files are very handy to use in code, they are not very readable. The `jira-format-issues` command
59
- formats json issues to markdown. It is invoked as follows:
61
+ While JSON files are very handy to use in code, they are not very readable. The `jira-format-issues` command
62
+ formats JSON issues to markdown. It is invoked as follows:
63
+
64
+ ~~~ {.bash}
60
65
 
61
66
  jira-format-issues \
62
67
  --source issues/MYPROJECT/json \
63
68
  --target issues/MYPROJECT/markdown
64
69
 
70
+ ~~~
71
+
65
72
  This will create the following structure in the `issues/MYPROJ/markdown`:
66
73
 
74
+
67
75
  MYPROJ-1.md
68
76
  MYPROJ-2.md
69
77
  MYPROJ-3.md
@@ -73,41 +81,43 @@ This will create the following structure in the `issues/MYPROJ/markdown`:
73
81
 
74
82
  The individual files look like this:
75
83
 
76
- [MYPROJ-1](https://jira.myorganisation.co/browse/MYPROJ-1): Build a working System
77
- ==================================================================================
84
+ ~~~ {.md}
85
+ [MYPROJ-1](https://jira.myorganisation.co/browse/MYPROJ-1): Build a working System
86
+ ==================================================================================
78
87
 
79
- Type
80
- : Story
88
+ Type
89
+ : Story
81
90
 
82
- Status
83
- : Closed
91
+ Status
92
+ : Closed
84
93
 
85
- Reporter
86
- : fleipold
94
+ Reporter
95
+ : fleipold
87
96
 
88
- Labels
89
- : triaged
97
+ Labels
98
+ : triaged
90
99
 
91
- Updated
92
- : 20. Jan 2014 11:30 (UTC)
100
+ Updated
101
+ : 20. Jan 2014 11:30 (UTC)
93
102
 
94
- Created
95
- : 01. Aug 2013 12:29 (UTC)
103
+ Created
104
+ : 01. Aug 2013 12:29 (UTC)
96
105
 
97
106
 
98
- Description
99
- -----------
107
+ Description
108
+ -----------
100
109
 
101
- The myproj system shall be built to be *delpoyable* and *working*.
110
+ The myproj system shall be built to be *delpoyable* and *working*.
102
111
 
103
112
 
104
- Comments
105
- --------
113
+ Comments
114
+ --------
106
115
 
107
- ### fleipold - 20. Jan 2014 12:19 (UTC):
116
+ ### fleipold - 20. Jan 2014 12:19 (UTC):
108
117
 
109
- Is this still relevant?
118
+ Is this still relevant?
110
119
 
120
+ ~~~
111
121
 
112
122
  These files can be easily searched by ensuring they get indexed by a desktop search engine, e.g.
113
123
  [spotlight](https://gist.github.com/gereon/3150445) on the Mac.
@@ -116,28 +126,39 @@ These files can be easily searched by ensuring they get indexed by a desktop sea
116
126
  data* fields which are rendered as definitions at the top of the ticket and *sections* that are rendered as paragraph
117
127
  with a heading. Here is an example file, `custom-data.json`:
118
128
 
119
- {
120
- "simple_fields" : {
121
- "Audience" : ["customfield_10123", "value"]
122
- },
123
- "sections" : {
124
- "Release Notes" : ["customfield_10806"]
125
- }
126
- }
129
+ ~~~ {.json}
130
+ {
131
+ "simple_fields" : {
132
+ "Audience" : ["customfield_10123", "value"]
133
+ },
134
+ "sections" : {
135
+ "Release Notes" : ["customfield_10806"]
136
+ }
137
+ }
138
+
139
+ ~~~
140
+
127
141
 
128
142
  This file can be passed in like this:
129
143
 
130
- jira-format-issues \
131
- --source issues/MYPROJECT/json \
132
- --target issues/MYPROJECT/markdown \
133
- --custom-data-path custom-data.json
144
+ ~~~ {.bash}
145
+
146
+ jira-format-issues \
147
+ --source issues/MYPROJECT/json \
148
+ --target issues/MYPROJECT/markdown \
149
+ --custom-data-path custom-data.json
150
+
151
+ ~~~
134
152
 
135
153
  ## Motivation
136
154
 
137
- Having a local, unix-friendly copy to avoid jira performance issues and make information available offline.
155
+ Having a local, unix-friendly copy to avoid JIRA performance issues and make information available offline.
138
156
 
139
157
  ## Potential Future Work
140
158
 
141
- * Remove tickets that have been moved to a different project
142
- * Use OAuth authentication
143
- * Improved error handling
159
+ - [X] Make progress bar work
160
+ - [X] Make output less noisy
161
+ - [ ] Deal with authentication problems explicitly
162
+ - [ ] Remove tickets that have been moved to a different project
163
+ - [ ] Use OAuth authentication
164
+
@@ -29,4 +29,5 @@ Gem::Specification.new do |s|
29
29
  s.add_dependency('trollop')
30
30
  s.add_dependency('httparty')
31
31
  s.add_dependency('parallel')
32
+ s.add_dependency('ruby-progressbar')
32
33
  end
@@ -4,6 +4,7 @@ module JiraSync
4
4
  require 'uri'
5
5
  require 'json'
6
6
 
7
+
7
8
  class FetchError < StandardError
8
9
  attr_reader :status, :url
9
10
 
@@ -24,12 +25,16 @@ module JiraSync
24
25
  @username = username
25
26
  @password = password
26
27
  @baseurl = baseurl
28
+ @timeout = 15
29
+ @first_requets_timeout = 60
27
30
  end
28
31
 
32
+
33
+
29
34
  def get(jira_id)
30
35
  url = "#{@baseurl}/rest/api/latest/issue/#{jira_id}"
31
36
  auth = {:username => @username, :password => @password}
32
- response = HTTParty.get url, {:basic_auth => auth}
37
+ response = HTTParty.get url, {:basic_auth => auth, :timeout => @timeout}
33
38
  if response.code == 200
34
39
  response.parsed_response
35
40
  else
@@ -41,11 +46,15 @@ module JiraSync
41
46
  url = "#{@baseurl}/rest/api/2/search?"
42
47
  auth = {:username => @username, :password => @password}
43
48
 
44
- response = HTTParty.get url, {:basic_auth => auth, :query => {:jql => 'project="' + project_id + '" order by created', fields: 'summary,updated', maxResults: '1'}}
49
+ response = HTTParty.get url, {
50
+ :basic_auth => auth,
51
+ :query => {:jql => 'project="' + project_id + '" order by created', fields: 'summary,updated', maxResults: '1'},
52
+ :timeout => @first_requets_timeout
53
+ }
45
54
  if response.code == 200
46
55
  response.parsed_response
47
56
  else
48
- raise FetchError.new(response.status, url)
57
+ raise FetchError.new(response.code, url)
49
58
  end
50
59
  end
51
60
 
@@ -54,22 +63,30 @@ module JiraSync
54
63
  auth = {:username => @username, :password => @password}
55
64
  jql = 'project = "' + project_id + '" AND updated > ' + (date.to_time.to_i * 1000).to_s
56
65
  # "' + date.to_s + '"'
57
- response = HTTParty.get url, {:basic_auth => auth, :query => {:jql => jql, fields: 'summary,updated', maxResults: '1000'}}
66
+ response = HTTParty.get url, {
67
+ :basic_auth => auth,
68
+ :query => {:jql => jql, fields: 'summary,updated', maxResults: '1000'},
69
+ :timeout => @timeout
70
+ }
58
71
  if response.code == 200
59
72
  response.parsed_response
60
73
  else
61
- raise FetchError.new(response.status, url)
74
+ raise FetchError.new(response.code, url)
62
75
  end
63
76
  end
64
77
 
65
78
  def project_info(project_id)
66
79
  url = "#{@baseurl}/rest/api/2/project/#{project_id}"
67
80
  auth = {:username => @username, :password => @password}
68
- response = HTTParty.get url, {:basic_auth => auth, :query => {:jql => 'project="' + project_id + '"', fields: 'summary,updated', maxResults: '50'}}
81
+ response = HTTParty.get url, {
82
+ :basic_auth => auth,
83
+ :query => {:jql => 'project="' + project_id + '"', fields: 'summary,updated', maxResults: '50'},
84
+ :timeout => @timeout
85
+ }
69
86
  if response.code == 200
70
87
  response.parse_response
71
88
  else
72
- raise FetchError(response.status, url)
89
+ raise FetchError(response.code, url)
73
90
  end
74
91
 
75
92
  end
@@ -19,32 +19,34 @@ module JiraSync
19
19
  # couldn't be fetched.
20
20
  def fetch(keys)
21
21
  keys_with_errors = []
22
- Parallel.each_with_index(keys, :in_threads => 64) do |key, index|
23
- STDERR.puts(key) if ((index % 100) == 0)
22
+ tickets_moved = []
23
+ Parallel.each_with_index(keys, :in_threads => 64, :progress => {title:"Fetching", output: STDERR}) do |key, index|
24
24
  begin
25
25
  issue = @client.get(key)
26
26
  issue_project_key = issue['fields']['project']['key']
27
27
  if (issue_project_key == @project_key)
28
28
  @repo.save(issue)
29
29
  else
30
- STDERR.puts("Skipping ticket #{key} which has moved to #{issue_project_key}.")
30
+ tickets_moved.push(issue_project_key)
31
31
  end
32
32
 
33
33
  rescue FetchError => e
34
34
  if (e.status != 404)
35
- STDERR.puts(e.to_s)
36
35
  keys_with_errors.push(key)
37
36
  else
38
- STDERR.puts("Ignoring 404 for ticket #{key}")
37
+ # Ticket has disappeared
39
38
  end
40
39
  rescue => e
41
40
  STDERR.puts(e.to_s)
42
41
  keys_with_errors.push(key)
43
42
  end
44
43
  end
45
- keys_with_errors.sort
44
+ keys_with_errors.sort!
45
+ STDERR.puts("Errors fetching these tickets: #{keys_with_errors.join(",")}")
46
+ keys_with_errors
46
47
  end
47
48
 
49
+ # Fetches all tickets for the project
48
50
  def fetch_all
49
51
  start_time = DateTime.now
50
52
 
@@ -54,16 +56,16 @@ module JiraSync
54
56
  @repo.save_state({"time" => start_time, "errors" => keys_with_errors})
55
57
  end
56
58
 
59
+ # Fetches only tickets that have been changed/ added since the previous fetch/ update
57
60
  def update()
58
61
  state = @repo.load_state()
59
62
  start_time = DateTime.now
60
63
  since = DateTime.parse(state['time']).new_offset(0)
61
64
  STDERR.puts("Fetching issues that have changes since #{since.to_s}")
62
65
  issues = @client.changed_since(@project_key, since)['issues'].map { |issue| issue['key'] }
63
- STDERR.puts("Updated Issues")
64
- STDERR.puts(issues.empty? ? "None" : issues.join(", "))
65
- STDERR.puts("Issues with earlier errors")
66
- STDERR.puts(state['errors'].empty? ? "None" : state['errors'].join(", "))
66
+ STDERR.puts("Updated Issues: #{issues.empty? ? "None" : issues.join(",")}")
67
+ STDERR.print("Retrying issues with earlier errors: ")
68
+ STDERR.puts(state['errors'].empty? ? "None" : state['errors'].join(","))
67
69
  keys_with_errors = fetch(issues + state['errors'])
68
70
  @repo.save_state({"time" => start_time, "errors" => keys_with_errors})
69
71
  end
@@ -1,3 +1,3 @@
1
1
  module JiraSync
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jirasync
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-03-20 00:00:00.000000000 Z
12
+ date: 2015-04-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: ruby-progressbar
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
62
78
  description: ! "jirasync synchronises tickets from a jira project to the local\n file
63
79
  system. It supports a complete fetch operation as well as\n an
64
80
  incremental update.\n\n Each ticket is stored in a simple, pretty