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 +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/Guardfile +42 -0
- data/LICENSE.txt +21 -0
- data/README.md +75 -0
- data/Rakefile +10 -0
- data/atlassian_app_versions.gemspec +36 -0
- data/bin/console +16 -0
- data/bin/setup +8 -0
- data/lib/atlassian_app_versions.rb +386 -0
- metadata +140 -0
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
data/.travis.yml
ADDED
data/Gemfile
ADDED
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,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,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: []
|