atlassian_app_versions 0.1.4

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: adf2ac0abf92f4d86b0639d92003fece5c53d041
4
+ data.tar.gz: c78319e42020b8e8ebb00cbf3a2000b7d950a352
5
+ SHA512:
6
+ metadata.gz: 514cb6322b96b3e64850bb4e96a9370396e99badc115831272a54c2ececca96701d4b795b328c5218a785e1bd8a1b3c4ca03f4c3bf3934a52b359d0a35f5564d
7
+ data.tar.gz: 075ec3e2a2e3f1dc8bb1deda238caa5620c003f654d8c347e4b8a275532e53a40b1025b2396c5f8bbab8efcd7833d4f72aaa6642f7abddbfa28f0051a458648d
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in atlassian_app_versions.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ guard :minitest do
19
+ # with Minitest::Unit
20
+ watch(%r{^test/(.*)\/?test_(.*)\.rb$})
21
+ watch(%r{^lib/(.*/)?([^/]+)\.rb$}) { |m| "test/#{m[1]}test_#{m[2]}.rb" }
22
+ watch(%r{^test/test_helper\.rb$}) { 'test' }
23
+
24
+ # with Minitest::Spec
25
+ # watch(%r{^spec/(.*)_spec\.rb$})
26
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
27
+ # watch(%r{^spec/spec_helper\.rb$}) { 'spec' }
28
+
29
+ # Rails 4
30
+ # watch(%r{^app/(.+)\.rb$}) { |m| "test/#{m[1]}_test.rb" }
31
+ # watch(%r{^app/controllers/application_controller\.rb$}) { 'test/controllers' }
32
+ # watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| "test/integration/#{m[1]}_test.rb" }
33
+ # watch(%r{^app/views/(.+)_mailer/.+}) { |m| "test/mailers/#{m[1]}_mailer_test.rb" }
34
+ # watch(%r{^lib/(.+)\.rb$}) { |m| "test/lib/#{m[1]}_test.rb" }
35
+ # watch(%r{^test/.+_test\.rb$})
36
+ # watch(%r{^test/test_helper\.rb$}) { 'test' }
37
+
38
+ # Rails < 4
39
+ # watch(%r{^app/controllers/(.*)\.rb$}) { |m| "test/functional/#{m[1]}_test.rb" }
40
+ # watch(%r{^app/helpers/(.*)\.rb$}) { |m| "test/helpers/#{m[1]}_test.rb" }
41
+ # watch(%r{^app/models/(.*)\.rb$}) { |m| "test/unit/#{m[1]}_test.rb" }
42
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jeff Turner
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # AtlassianAppVersions
2
+
3
+ This Gem obtains info about Atlassian product and plugin releases from the https://marketplace.atlassian.com and https://my.atlassian.com REST APIs.
4
+
5
+ See also the `atlassian_app_upgradereport` gem which uses this info to produce Confluence reports.
6
+
7
+ ## Usage
8
+
9
+ There are two top-level classes, `App` instantiated with a product name, and `Plugin` instantiated with a plugin key (key names are from https://marketplace.atlassian.com). `App` and `Plugin` behave similarly, in that they expose a bunch of properties (call the `.keys` method to see what's available), have `version` and `versions` methods to list their versions, and a set of methods (
10
+
11
+ Sample API use:
12
+
13
+ ```ruby
14
+ require 'atlassian_app_versions'
15
+ include AtlassianAppVersions
16
+
17
+ # Info about an Atlassian product
18
+
19
+ conf = App.new("confluence")
20
+ conf.latest # #<AtlassianAppVersions::LatestAppVersion:70118429916620 5.9.10>
21
+ conf.version("5.9.10") # => #<AtlassianAppVersions::LatestAppVersion:70271671608160 5.9.10>
22
+ conf.version("5.9.10").version # => => "5.9.10"
23
+ conf.version("5.9.10").releaseDate # => #<DateTime: 2016-05-05T00:00:00+00:00 ((2457514j,0s,0n),+0s,2299161j)>
24
+ conf.version("5.9.10").relNotes # => "http://confluence.atlassian.com/display/DOC/Confluence+5.9.10+Release+Notes"
25
+
26
+
27
+ conf.versions("5.9.1", "5.9.5") # => [#<AtlassianAppVersions::ArchivedAppVersion:70118438447880 5.9.5>,
28
+ #<AtlassianAppVersions::ArchivedAppVersion:70118438447860 5.9.4>,
29
+ #<AtlassianAppVersions::ArchivedAppVersion:70118438447840 5.9.3>,
30
+ #<AtlassianAppVersions::ArchivedAppVersion:70118438447820 5.9.2>]
31
+ conf.latest.relNotes # => "http://confluence.atlassian.com/display/DOC/Confluence+5.9.10+Release+Notes"
32
+ conf.latest.keys # => ["description", "zipUrl", "tarUrl", "md5", "size", "released", ...]
33
+ conf.latest.zipUrl # => "https://www.atlassian.com/software/confluence/downloads/binary/atlassian-confluence-5.9.10.tar.gz"
34
+
35
+
36
+ # JQL for bugs fixed in the latest release
37
+ conf.bugsJQL(conf.versions[1], conf.versions[0]) # => "issuetype=Bug AND project=CONF AND fixVersion in (5.9.10) AND status in (Resolved, Closed, Soaking) ORDER BY votes DESC, priority DESC, key DESC"
38
+
39
+
40
+ # Info about an Atlassian plugin
41
+
42
+ plugin = Plugin.new("de.scandio.confluence.plugins.pocketquery")
43
+ plugin.name # => "PocketQuery"
44
+ # Available info on the plugin
45
+ plugin.keys # => ["downloadCount", "name", "deployment", "deployable", "lastModified", "description",..]
46
+
47
+ # Available info a specific release
48
+ plugin.latest.keys # => ["supportType", "releaseDate", "deployment", "deployable", "marketplaceType", "compatibilities", "screenshots", "releasedBy", "license", "version", "links", "status", "marketplaceAgreementAccepted", "pluginSystemVersion", "instructions", "autoUpdateAllowed", "compatibleApplications", "stable", "buildNumber", "summary", "addOnType"]
49
+
50
+ plugin.latest.version # => "2.0.7"
51
+
52
+ ```
53
+
54
+
55
+ ## Installation
56
+
57
+ - Install Ruby 2.3.1 or above (I use [ruby-install](https://github.com/postmodern/ruby-install) + [chruby](https://github.com/postmodern/chruby)). Then:
58
+ ```bash
59
+ gem install bundler
60
+ git clone https://redradish@bitbucket.org/redradish/atlassian_app_versions.git
61
+ cd atlassian_app_versions
62
+ bundle install # Install this gem's deps
63
+ bundle exec rake install # Install this gem
64
+ bundle exec rake test # Ensure it's all happy
65
+ ```
66
+ Then try the sample code above in `irb` or `pry`.
67
+
68
+ ## Development
69
+
70
+ In the finest tradition of Ruby gems, this gem is a hasty hack thrown together. The code would make experienced Rubyists cry. There is no guarantee of API stability.
71
+
72
+ ## License
73
+
74
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
75
+
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'atlassian_app_versions'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "atlassian_app_versions"
8
+ spec.version = AtlassianAppVersions::VERSION
9
+ spec.authors = ["Jeff Turner"]
10
+ spec.email = ["jeff@redradishtech.com"]
11
+
12
+ spec.summary = %q{Obtain info about Atlassian JIRA and other product releases.}
13
+ spec.description = %q{Wraps the marketplace.atlassian.com and my.atlassian.com REST APIs to obtain info about JIRA releases. See commented-out code near end for examples.}
14
+ #
15
+ spec.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
+ # delete this section to allow pushing this gem to any host.
19
+ #if spec.respond_to?(:metadata)
20
+ # spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ #else
22
+ # raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
+ #end
24
+
25
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
26
+ spec.bindir = "exe"
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ["lib"]
29
+
30
+ spec.add_development_dependency "bundler", "~> 1.11"
31
+ spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "minitest", "~> 5.0"
33
+ spec.add_development_dependency "minitest-reporters", "~> 1.1"
34
+ spec.add_development_dependency "guard"
35
+ spec.add_development_dependency "guard-minitest"
36
+ end
data/bin/console ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "atlassian_app_versions"
5
+ include AtlassianAppVersions
6
+
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+
11
+ # (If you use this, don't forget to add pry to your Gemfile!)
12
+ # require "pry"
13
+ # Pry.start
14
+
15
+ require "irb"
16
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,386 @@
1
+ #######
2
+ # Wraps the marketplace.atlassian.com and my.atlassian.com REST APIs to obtain info about JIRA releases. See commented-out code near end for examples.
3
+ #
4
+ # marketplace.atlassian.com data returns simple version data for plugins and apps.
5
+ # my.atlassian.com data returns release notes and download locations, but only for apps.
6
+ #
7
+ # We maintain the facade of having Version objects. Versions get their data by requesting either MPAC or MAC JSON from their @parent, which caches the JSON.
8
+ #
9
+ require 'open-uri'
10
+ require 'json'
11
+ require 'cgi'
12
+
13
+ module AtlassianAppVersions
14
+
15
+ VERSION = "0.1.4"
16
+
17
+ # A Plugin or App. This class assumes the MPAC API will have info about the product.
18
+ class AbstractProduct
19
+
20
+ JAC_URL="https://jira.atlassian.com"
21
+
22
+ attr_accessor :key
23
+ # Initialize with a MPAC key (e.g. "jira" or "com.pyxis.greenhopper.jira")
24
+ def initialize(key)
25
+ @key=key
26
+ end
27
+
28
+ # JQL for all issues resolved after +fromVer+ up to and including +toVer+.
29
+ def allissuesJQL(fromVer=nil, toVer=nil)
30
+ templateJQL(fromVer, toVer)
31
+ end
32
+
33
+ # JQL for all Bugs resolved after +fromVer+ up to and including +toVer+.
34
+ def bugsJQL(fromVer=nil, toVer=nil)
35
+ "issuetype=Bug AND " + templateJQL(fromVer, toVer)
36
+ end
37
+
38
+ # JQL for all features resolved after +fromVer+ up to and including +toVer+.
39
+ def featuresJQL(fromVer=nil, toVer=nil)
40
+ "issuetype!=Bug AND " + templateJQL(fromVer, toVer)
41
+ end
42
+
43
+ # JQL for all new, unresolved Bugs _introduced_ in the (up to) 3 versions before +toVer+, but after +fromVer+. This means bugs reported against a version in the range, but not reported against an earlier version (up to +pastVersionsToIgnore+ old versions are considered, since some really old versions are missing from JAC and break the query).
44
+ def recentbugsJQL(fromVer, toVer=nil, includedVersionCount=3, pastVersionsToIgnore=50)
45
+ new = versions(fromVer, toVer).first(includedVersionCount)
46
+ old = versions.select { |v| v < new.last }.first(pastVersionsToIgnore)
47
+
48
+ projectJQL(fromVer) + " AND issuetype=Bug AND affectedVersion in (#{versionsListJQL(new)}) AND affectedVersion not in (#{versionsListJQL(old)}) AND resolution is empty ORDER BY votes DESC, priority DESC, key DESC"
49
+ end
50
+
51
+ # URL for all issues resolved after +fromVer+ up to and including +toVer+.
52
+ def allissuesURL(fromVer=nil, toVer=nil)
53
+ JAC_URL + "/issues/?jql=#{CGI::escape(allissuesJQL(fromVer, toVer))}"
54
+ end
55
+
56
+ # URL for all Bugs resolved after +fromVer+ up to and including +toVer+.
57
+ def bugsURL(fromVer=nil, toVer=nil)
58
+ JAC_URL + "/issues/?jql=#{CGI::escape(bugsJQL(fromVer, toVer))}"
59
+ end
60
+
61
+ # URL for all features resolved after +fromVer+ up to and including +toVer+.
62
+ def featuresURL(fromVer=nil, toVer=nil)
63
+ JAC_URL + "/issues/?jql=#{CGI::escape(featuresJQL(fromVer, toVer))}"
64
+ end
65
+
66
+ # URL for all new, unresolved Bugs _introduced_ after +fromVer+ up to and including +toVer+. This means bugs reported against a version in the range, but not reported against an earlier version.
67
+ def recentbugsURL(fromVer=nil, toVer=nil)
68
+ JAC_URL + "/issues/?jql=#{CGI::escape(recentbugsJQL(fromVer, toVer))}"
69
+ end
70
+
71
+ def marketplaceJSON
72
+ if !@marketplaceJSON then
73
+ mpacKey = case key
74
+ when "jira-core", "jira-software" then
75
+ # mpac still only knows about 'jira'
76
+ "jira"
77
+ when "stash" then "bitbucket" # MPAC actually redirects stash to bitbucket, but be nice and do it here.
78
+ else
79
+ key
80
+ end
81
+ url = "https://marketplace.atlassian.com/rest/1.0/#{product_type}/#{mpacKey}"
82
+ begin
83
+ jsonStr = open(url).read
84
+ rescue OpenURI::HTTPError => error
85
+ if error.io.status[0] == "404" then
86
+ raise "No plugin with key '#{key}' found"
87
+ else
88
+ raise error
89
+ end
90
+ end
91
+ @marketplaceJSON = JSON.parse( jsonStr )
92
+ end
93
+ @marketplaceJSON
94
+ end
95
+
96
+ ## Find versions in a range, from +fromVer+ (exclusive) to +toVer+ (inclusive). If +toVer+ is omitted, all versions up to the latest are found. If +fromVer+ and +toVer+ are omitted, all versions are returned.
97
+ def versions(fromVer=nil, toVer=nil) # fromVer and toVer may be a string or a *Version
98
+ if (!fromVer && !toVer) then
99
+ allVersions
100
+ else
101
+ # allVersions is ordered most to least recent
102
+ # If 'fromVer' was unspecified, count from the end (allVersions.size = last = oldest)
103
+ fromIdx = fromVer ? allVersions.find_index { |v| v.version == fromVer.to_s } || ( raise "Couldn't find #{@key} fromVer #{fromVer}" ) : allVersions.size
104
+ # If toVer was unspecified, count from the beginning (0 = newest)
105
+ toIdx = toVer ? allVersions.find_index{ |v| v.version == toVer.to_s } || ( raise "Couldn't find #{@key} toVer #{toVer}" ) : 0
106
+ # fromIdx may be greater than toIdx, which the slice doesn't like, so get the max/min here
107
+ from = [fromIdx, toIdx].min
108
+ to = [fromIdx, toIdx].max
109
+ allVersions.slice(from, (to - from))
110
+ end
111
+ end
112
+
113
+ ## Find a specific version
114
+ def version(ver)
115
+ allVersions.find { |v| v.version == ver }
116
+ end
117
+
118
+
119
+ def allVersions
120
+ raise "Override me!"
121
+ end
122
+ def latest
123
+ allVersions.first
124
+ end
125
+
126
+ # Display all 'properties' of the app/plugin. These can be used as methods, e.g. 'jira.name' or 'plugin.summary'
127
+ def keys
128
+ marketplaceJSON.keys
129
+ end
130
+
131
+ def method_missing(name, *args, &block)
132
+ if keys and keys.member? name.to_s then
133
+ marketplaceJSON[name.to_s]
134
+ else
135
+ super
136
+ end
137
+ end
138
+
139
+ def respond_to_missing?(name, include_private = false)
140
+ keys.member? name.to_s || super
141
+ end
142
+
143
+ private
144
+
145
+ def jacKey
146
+ case @key
147
+ when "jira-core" then "JRA"
148
+ when "jira-software" then ["JRA", "JSW"]
149
+ when "confluence" then ["CONF", "CRA"]
150
+ when "gh" then"GHS"
151
+ when "stash" then "BSERV"
152
+ when "bitbucket" then "BSERV"
153
+ when "fisheye" then "FE"
154
+ when "com.pyxis.greenhopper.jira" then "JSW"
155
+ end
156
+ end
157
+
158
+ # "plugins" or "applications"
159
+ def product_type
160
+ raise "Override me!"
161
+ end
162
+
163
+
164
+ # Return a 'project in (...)' JQL clause.
165
+ def projectJQL(fromVer=nil, toVer=nil)
166
+ (jacKey.respond_to?(:each) ? "project in (#{jacKey.join(',')})" : "project=#{jacKey}")
167
+ end
168
+
169
+ # Return a comma-separated list of JAC version strings, given an Array of versions
170
+ def versionsListJQL(versions)
171
+ # Atlassian started appending ' Server' to JIRA version names after 7.1.6
172
+ if @key=~/jira/ then
173
+ old, new = versions.partition { |v| v < "7.1.6"}
174
+ (new.map { |v| "'#{v} Server'" } + old).join(", ")
175
+ else
176
+ versions.join(", ")
177
+ end
178
+ end
179
+
180
+ # JQL common between bugs and features
181
+ def templateJQL(fromVer=nil, toVer=nil)
182
+ vers = versionsListJQL(versions(fromVer, toVer))
183
+ projectJQL(fromVer, toVer) +
184
+ " AND fixVersion in (#{vers}) AND status in (Resolved, Closed, Soaking, \"Released to Server\") ORDER BY votes DESC, priority DESC, key DESC"
185
+ end
186
+ end
187
+
188
+ ## Represents a plugin, with information taken from https://marketplace.atlassian.com
189
+ class Plugin < AbstractProduct
190
+
191
+ def versionsJSON
192
+ marketplaceJSON["versions"]["versions"]
193
+ end
194
+ def product_type
195
+ "plugins"
196
+ end
197
+
198
+ def allVersions
199
+ versionsJSON.collect { |v| PluginVersion.new(v["version"], self) }
200
+ end
201
+
202
+ # A few plugins are tracked on JAC, so if asked for JQL, return their issue-tracker link.
203
+ def allissueJQL(fromVer=nil, toVer=nil)
204
+ links = versionsJSON.first["links"]
205
+ if links then
206
+ tracker = links.find { |l| l["rel"] == "issue-tracker" }
207
+ if tracker then
208
+ if tracker["href"] =~ /jira\.atlassian\.com/ then
209
+ templateJQL(fromVer, toVer)
210
+ else
211
+ nil
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+
218
+ ## Represents an application on jira.atlassian.com / my.atlassian.com
219
+ class App < AbstractProduct
220
+ VALID_PRODUCTS=["jira-core", "jira-software", "confluence", "bitbucket", "stash", "fisheye", "crowd"]
221
+ def initialize(key)
222
+ if !VALID_PRODUCTS.member? key then
223
+ raise "Unknown product: #{key}. Use one of #{VALID_PRODUCTS.join(", ")}"
224
+ end
225
+ if key=="stash" then key="bitbucket"; end
226
+ super
227
+ end
228
+
229
+ def getjson(url)
230
+ open(url).read.gsub(/^downloads\(/, '').gsub(/\)$/, '')
231
+ end
232
+
233
+ def latestReleaseJSON
234
+ if !@latestReleaseJSON then
235
+
236
+ macKey = @key == "bitbucket" ? "stash" : @key # MAC still thinks it's 'stash'
237
+ url="https://my.atlassian.com/download/feeds/current/#{macKey}.json"
238
+ jsonStr = getjson(url)
239
+ @latestReleaseJSON = JSON.parse(jsonStr)
240
+ end
241
+ @latestReleaseJSON
242
+ end
243
+
244
+ def archivedReleaseJSON
245
+ if !@archivedReleaseJSON then
246
+ macKey = @key == "bitbucket" ? "stash" : @key # MAC still thinks it's 'stash'
247
+ url="https://my.atlassian.com/download/feeds/archived/#{macKey}.json"
248
+ jsonStr = getjson(url)
249
+ @archivedReleaseJSON = JSON.parse( jsonStr )
250
+ end
251
+ @archivedReleaseJSON
252
+ end
253
+
254
+ def versionsJSON
255
+ marketplaceJSON["versions"]
256
+ end
257
+
258
+ def allVersions
259
+ first=true
260
+ versionsJSON.collect { |v|
261
+ if first then
262
+ first=false
263
+ LatestAppVersion.new(v["version"], self)
264
+ else
265
+ ArchivedAppVersion.new(v["version"], self)
266
+ end
267
+ }
268
+ end
269
+
270
+ def product_type
271
+ "applications"
272
+ end
273
+ end
274
+
275
+ ## A plugin or app version
276
+ class AbstractVersion
277
+ include Comparable
278
+
279
+ attr_accessor :version
280
+ def initialize(versionName, parent)
281
+ @version = versionName
282
+ @parent = parent
283
+ end
284
+
285
+ def versionJSON
286
+ @parent.versionsJSON.find { |v| v["version"] == @version }
287
+ end
288
+
289
+ def releaseDate
290
+ DateTime.strptime(versionJSON["releaseDate"], '%Y-%m-%dT%H:%M:%S.%L%z') if versionJSON
291
+ end
292
+
293
+ def inspect
294
+ "#<#{self.class.name}:#{self.object_id} #{to_s}>"
295
+ end
296
+
297
+ def to_s
298
+ @version
299
+ end
300
+
301
+ def <=>(other_version)
302
+ # Confluence has a version with underscores (3.0.0_01) which Gem::Version doesn't like, so strip it
303
+ v1 = @version.gsub('_', '.')
304
+ v2 = other_version.to_s.gsub('_', '.')
305
+ begin
306
+ Gem::Version.new(v1) <=> Gem::Version.new(v2)
307
+ rescue ArgumentError => e
308
+ $stderr.puts e.message
309
+ end
310
+
311
+ end
312
+
313
+ def keys
314
+ versionJSON.keys
315
+ end
316
+
317
+ def method_missing(name, *args, &block)
318
+ if versionJSON and versionJSON.keys.member? name.to_s then
319
+ versionJSON[name.to_s]
320
+ else
321
+ super
322
+ end
323
+ end
324
+
325
+ def respond_to_missing?(name, include_private = false)
326
+ keys.member? name.to_s || super
327
+ end
328
+ end
329
+
330
+ class PluginVersion < AbstractVersion
331
+ # Some plugin release don't have release notes, in which case return nil
332
+ def relNotes
333
+ if versionJSON["releaseNotes"] then
334
+ versionJSON["releaseNotes"].scan(/(?<=href=")[^"]+elease\+[nN]otes/)&.first&.strip
335
+ end
336
+ end
337
+ end
338
+
339
+ class AppVersion < AbstractVersion
340
+ def releaseJSON
341
+ raise "Override me"
342
+ end
343
+
344
+ def relNotes
345
+ # RC releases don't have release notes, in which case return nil
346
+ if releaseJSON
347
+ releaseJSON["releaseNotes"]&.strip
348
+ end
349
+ end
350
+
351
+ def keys
352
+ # Combine versionJSON keys with releaseJSON keys. Note that Confluence 5.6 and earlier doesn't have releaseJSON.
353
+ releaseJSON ? releaseJSON.keys + super : super
354
+ end
355
+
356
+ def method_missing(name, *args, &block)
357
+ if releaseJSON and releaseJSON.keys.member? name.to_s then
358
+ releaseJSON[name.to_s]
359
+ else
360
+ super
361
+ end
362
+ end
363
+
364
+ def respond_to_missing?(name, include_private = false)
365
+ keys.member? name.to_s || super
366
+ end
367
+ end
368
+
369
+ class LatestAppVersion < AppVersion
370
+ def releaseJSON
371
+ @parent.latestReleaseJSON.select {|e|
372
+ url=e["zipUrl"]
373
+ @parent.key=="fisheye" ||
374
+ url =~ /.tar.gz$/ && url !~ /(war|cluster).tar.gz$/
375
+ }[0]
376
+ end
377
+ end
378
+ class ArchivedAppVersion < AppVersion
379
+ # Note: returns nil if that version wasn't available in the archive
380
+ def releaseJSON
381
+ @parent.archivedReleaseJSON.find { |v| v["version"] == @version }
382
+ end
383
+ end
384
+
385
+ end
386
+ # vim: set smartindent sw=4:
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: atlassian_app_versions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.4
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Turner
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: minitest-reporters
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Wraps the marketplace.atlassian.com and my.atlassian.com REST APIs to
98
+ obtain info about JIRA releases. See commented-out code near end for examples.
99
+ email:
100
+ - jeff@redradishtech.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".travis.yml"
107
+ - Gemfile
108
+ - Guardfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - atlassian_app_versions.gemspec
113
+ - bin/console
114
+ - bin/setup
115
+ - lib/atlassian_app_versions.rb
116
+ homepage:
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ">="
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project:
136
+ rubygems_version: 2.5.1
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Obtain info about Atlassian JIRA and other product releases.
140
+ test_files: []