desi 0.0.2 → 0.1.0
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/.yardopts +1 -0
- data/Guardfile +5 -0
- data/README.md +50 -13
- data/Rakefile +2 -0
- data/desi.gemspec +5 -0
- data/lib/desi.rb +7 -6
- data/lib/desi/http_client.rb +28 -3
- data/lib/desi/index_manager.rb +114 -0
- data/lib/desi/local_install.rb +49 -2
- data/lib/desi/process_manager.rb +4 -1
- data/lib/desi/runner.rb +23 -5
- data/lib/desi/version.rb +1 -1
- data/tasks/yard.rake +9 -0
- metadata +70 -2
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private --protected lib/**/*.rb - README.md LICENSE
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -2,8 +2,56 @@
|
|
2
2
|
|
3
3
|
Desi (Developper ElasticSearch Installer) is very simple tool to quickly set up
|
4
4
|
an [Elastic Search](http://www.elasticsearch.org/) local install for
|
5
|
-
development purposes.
|
6
|
-
|
5
|
+
development purposes.
|
6
|
+
|
7
|
+
It can:
|
8
|
+
|
9
|
+
* download and install ElasticSearch (the latest release by default)
|
10
|
+
* start/stop/restart it.
|
11
|
+
* do basic indices management (list, delete, empty a given set of indices)
|
12
|
+
|
13
|
+
It can be used both as a command-line tool and as a library.
|
14
|
+
|
15
|
+
|
16
|
+
## Usage
|
17
|
+
|
18
|
+
$ desi list # List locally installed ElasticSearch releases
|
19
|
+
$ desi releases # List all upstream Elastic Search releases (latest 5 by default)
|
20
|
+
$ desi install [VERSION] # Install a specific version (latest by default)
|
21
|
+
$ desi start # Start a local 1-node cluster (noop if active)
|
22
|
+
$ desi restart # (Re)start cluster (even if active)
|
23
|
+
$ desi stop # Stop cluster
|
24
|
+
$ desi status [--host HOST] # Show running cluster info
|
25
|
+
|
26
|
+
$ desi indices "^foo" # List all indices whose name match /^foo/
|
27
|
+
$ desi indices "^foo" --delete # Delete all matching indices
|
28
|
+
$ desi indices "bar$" --empty # Remove all records from the matching
|
29
|
+
# indices
|
30
|
+
|
31
|
+
## Examples
|
32
|
+
|
33
|
+
### Currently installed releases
|
34
|
+
|
35
|
+
The current version is the one symlinked to `$HOME/elasticsearch/current`
|
36
|
+
|
37
|
+
* command-line
|
38
|
+
|
39
|
+
```shell
|
40
|
+
$ desi list
|
41
|
+
Local ES installs (current one is tagged with '*'):
|
42
|
+
* elasticsearch-0.19.9 (/home/me/elasticsearch/elasticsearch-0.19.9)
|
43
|
+
- elasticsearch-0.19.8 (/home/me/elasticsearch/elasticsearch-0.19.8)
|
44
|
+
```
|
45
|
+
|
46
|
+
|
47
|
+
* library
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
Desi::LocalInstall.new.releases.map(&:name) #=> ["elasticsearch-0.19.8", "elasticsearch-0.19.9"]
|
51
|
+
Desi::LocalInstall.new.releases.detect(&:current?).version #=> "0.19.9"
|
52
|
+
```
|
53
|
+
|
54
|
+
|
7
55
|
|
8
56
|
## Installation
|
9
57
|
|
@@ -19,21 +67,10 @@ Or install it yourself as:
|
|
19
67
|
|
20
68
|
$ gem install desi
|
21
69
|
|
22
|
-
## Usage
|
23
|
-
|
24
|
-
$ desi list # List locally installed ElasticSearch versions
|
25
|
-
$ desi releases # List all upstream Elastic Search releases (latest 5 by default)
|
26
|
-
$ desi install [VERSION] # Install a specific version (latest by default)
|
27
|
-
$ desi start # Start a local 1-node cluster (noop if active)
|
28
|
-
$ desi restart # (Re)start cluster (even if active)
|
29
|
-
$ desi stop # Stop cluster
|
30
|
-
$ desi status # Show running cluster info
|
31
|
-
|
32
70
|
## TODO
|
33
71
|
|
34
72
|
* add tests, dammit!
|
35
73
|
|
36
|
-
* index management (list, create, delete ES indices)
|
37
74
|
* `desi upgrade` (Upgrade to latest version and migrate data)
|
38
75
|
* `desi switch VERSION` (Switch currently active ES version to VERSION)
|
39
76
|
* plugin management ? (list, install, remove ES plugins)
|
data/Rakefile
CHANGED
data/desi.gemspec
CHANGED
@@ -13,6 +13,11 @@ an Elastic Search local install for development purposes.}
|
|
13
13
|
|
14
14
|
gem.add_dependency "boson"
|
15
15
|
gem.add_dependency "cocaine"
|
16
|
+
gem.add_dependency "addressable"
|
17
|
+
|
18
|
+
gem.add_development_dependency "guard-yard"
|
19
|
+
gem.add_development_dependency "redcarpet"
|
20
|
+
gem.add_development_dependency "rb-inotify"
|
16
21
|
|
17
22
|
gem.files = `git ls-files`.split($\)
|
18
23
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/lib/desi.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
require "desi/version"
|
2
2
|
|
3
3
|
module Desi
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
require 'desi/downloader'
|
5
|
+
require 'desi/http_client'
|
6
|
+
require 'desi/local_install'
|
7
|
+
require 'desi/upstream'
|
8
|
+
require 'desi/installer'
|
9
|
+
require 'desi/process_manager'
|
10
|
+
require 'desi/index_manager'
|
10
11
|
end
|
11
12
|
|
data/lib/desi/http_client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require "net/https"
|
4
|
-
require "uri"
|
4
|
+
require "addressable/uri"
|
5
5
|
require "json"
|
6
6
|
|
7
7
|
module Desi
|
@@ -10,8 +10,8 @@ module Desi
|
|
10
10
|
|
11
11
|
attr_reader :uri
|
12
12
|
|
13
|
-
def initialize(
|
14
|
-
@uri =
|
13
|
+
def initialize(host_string)
|
14
|
+
@uri = to_uri(host_string)
|
15
15
|
|
16
16
|
case @uri.scheme
|
17
17
|
when 'https'
|
@@ -39,6 +39,31 @@ module Desi
|
|
39
39
|
raise response.error!
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
def delete(uri)
|
44
|
+
response = @http.request(Net::HTTP::Delete.new(uri))
|
45
|
+
|
46
|
+
case response
|
47
|
+
when Net::HTTPSuccess
|
48
|
+
response
|
49
|
+
else
|
50
|
+
raise response.error!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def to_uri(host_string)
|
57
|
+
scheme, host, port = ['http', '127.0.0.1', 9200]
|
58
|
+
|
59
|
+
%r{(?<scheme>(https?|))(?:\:\/\/|)(?<host>[^:]*?):?(?<port>\d*)/?$}.match(host_string) do |m|
|
60
|
+
scheme = m[:scheme] unless m[:scheme].empty?
|
61
|
+
host = m[:host] unless m[:host].empty?
|
62
|
+
port = m[:port] unless m[:port].empty?
|
63
|
+
end
|
64
|
+
|
65
|
+
Addressable::URI.new(scheme: scheme, host: host, port: port)
|
66
|
+
end
|
42
67
|
end
|
43
68
|
|
44
69
|
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "desi/http_client"
|
4
|
+
|
5
|
+
module Desi
|
6
|
+
|
7
|
+
# Performs some simple index-related operations on a local or distance
|
8
|
+
# Elastic Search cluster
|
9
|
+
class IndexManager
|
10
|
+
|
11
|
+
# Initializes a Desi::IndexManager instance
|
12
|
+
#
|
13
|
+
# @param [#to_hash] opts Hash of extra opts
|
14
|
+
# @option opts [#to_s] :host Host to manage indices for
|
15
|
+
# (default: 'http://127.0.0.1:9200')
|
16
|
+
# @option opts [Boolean] :verbose Whether to output the actions' result
|
17
|
+
# on STDOUT
|
18
|
+
#
|
19
|
+
# @return [undefined]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def initialize(opts = {})
|
23
|
+
@host = opts.fetch(:host, 'http://127.0.0.1:9200')
|
24
|
+
@verbose = opts[:verbose]
|
25
|
+
@client = Desi::HttpClient.new(@host)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# List index names for the specified cluster
|
30
|
+
#
|
31
|
+
# You can restrict the list using a regular expression pattern. (The default
|
32
|
+
# pattern being +/.*/+, all releases will be returned if you do not
|
33
|
+
# specify anything.)
|
34
|
+
#
|
35
|
+
# @param [#to_s] pattern Regexp pattern used to restrict the selection
|
36
|
+
# @return [Array<String>] List of index names of the ES cluster
|
37
|
+
#
|
38
|
+
# @note This method will also output its result on STDOUT if +@verbose+ is
|
39
|
+
# true
|
40
|
+
#
|
41
|
+
# @example List all indices whose name begins with "foo"
|
42
|
+
# Desi::IndexManager.new.list('^foo') #=> ["foo1", "foo2", "foo3"]
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
def list(pattern = '.*')
|
46
|
+
pattern = Regexp.new(pattern || '.*')
|
47
|
+
|
48
|
+
puts "Indices from host #{@client.uri} matching the pattern #{pattern.inspect}\n\n" if @verbose
|
49
|
+
|
50
|
+
list = indices(pattern).sort
|
51
|
+
list.each {|i| puts i } if @verbose
|
52
|
+
list
|
53
|
+
end
|
54
|
+
|
55
|
+
# Delete all indices matching the specified pattern
|
56
|
+
#
|
57
|
+
# @param [#to_s] pattern Regexp pattern used to restrict the selection
|
58
|
+
# @return [undefined]
|
59
|
+
#
|
60
|
+
# @note No confirmation is needed, so beware!
|
61
|
+
#
|
62
|
+
# @note This method will also output its result on STDOUT if +@verbose+ is
|
63
|
+
# true
|
64
|
+
#
|
65
|
+
# @example Delete all indices whose name begins with "test"
|
66
|
+
# Desi::IndexManager.new.delete!('^test') #=> nil
|
67
|
+
#
|
68
|
+
# @api public
|
69
|
+
def delete!(pattern)
|
70
|
+
warn "You must provide a pattern" and exit if pattern.nil?
|
71
|
+
|
72
|
+
puts "The following indices from host #{@client.uri} are now deleted" if @verbose
|
73
|
+
|
74
|
+
indices(Regexp.new(pattern)).each do |index|
|
75
|
+
@client.delete(index)
|
76
|
+
puts " * #{index}" if @verbose
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Empty (remove all records) from indices matching the specified pattern
|
81
|
+
#
|
82
|
+
# @param [#to_s] pattern Regexp pattern used to restrict the selection
|
83
|
+
# @return [undefined]
|
84
|
+
#
|
85
|
+
# @note No confirmation is needed, so beware!
|
86
|
+
#
|
87
|
+
# @note This method will also output its result on STDOUT if +@verbose+ is
|
88
|
+
# true
|
89
|
+
#
|
90
|
+
# @example Empty all indices whose name begins with "log"
|
91
|
+
# Desi::IndexManager.new.empty!('^log') #=> nil
|
92
|
+
#
|
93
|
+
# @api public
|
94
|
+
def empty!(pattern)
|
95
|
+
warn "You must provide a pattern" and exit if pattern.nil?
|
96
|
+
|
97
|
+
puts "The following indices from host #{@client.uri} are now emptied" if @verbose
|
98
|
+
|
99
|
+
indices(Regexp.new(pattern)).each do |index|
|
100
|
+
@client.delete("#{index}/_query?q=*")
|
101
|
+
puts " * #{index}" if @verbose
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
private
|
106
|
+
|
107
|
+
def indices(pattern)
|
108
|
+
JSON.parse(@client.get('_status').body)["indices"].keys.select {|i|
|
109
|
+
i =~ pattern
|
110
|
+
}
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|
data/lib/desi/local_install.rb
CHANGED
@@ -1,9 +1,56 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require "pathname"
|
4
|
+
|
3
5
|
module Desi
|
4
6
|
class LocalInstall
|
5
7
|
DEFAULT_DIR = '~/elasticsearch'
|
6
8
|
|
9
|
+
class Release
|
10
|
+
def self.all_in(workdir)
|
11
|
+
Dir[workdir.join('*')].
|
12
|
+
select {|subdir| File.directory?(subdir) && File.basename(subdir) =~ /^elasticsearch\-\d+\.\d+\.\d+/ }.
|
13
|
+
map {|dirname| new(dirname, workdir) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(dirname, workdir)
|
17
|
+
@dirname = Pathname.new(dirname)
|
18
|
+
@workdir = workdir
|
19
|
+
end
|
20
|
+
|
21
|
+
def name
|
22
|
+
@name ||= File.basename(@dirname)
|
23
|
+
end
|
24
|
+
|
25
|
+
def current?
|
26
|
+
current_symlink? && current_symlink.realpath == @dirname
|
27
|
+
end
|
28
|
+
|
29
|
+
def version
|
30
|
+
@version ||= /^elasticsearch\-(?<version>.*)$/.match(name.to_s)[:version]
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_s
|
34
|
+
current_mark = current? ? '*' : '-'
|
35
|
+
|
36
|
+
" #{current_mark} #{name} (#{@dirname})"
|
37
|
+
end
|
38
|
+
|
39
|
+
def <=>(other)
|
40
|
+
name <=> other.name
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def current_symlink
|
46
|
+
@current_symlink ||= Pathname(@workdir).join('current')
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_symlink?
|
50
|
+
current_symlink.exist?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
7
54
|
def initialize(workdir = nil)
|
8
55
|
@workdir = Pathname(File.expand_path(workdir || DEFAULT_DIR))
|
9
56
|
end
|
@@ -20,8 +67,8 @@ module Desi
|
|
20
67
|
FileUtils.mkdir_p @workdir
|
21
68
|
end
|
22
69
|
|
23
|
-
def
|
24
|
-
|
70
|
+
def releases
|
71
|
+
Release.all_in(@workdir)
|
25
72
|
end
|
26
73
|
|
27
74
|
def to_path
|
data/lib/desi/process_manager.rb
CHANGED
@@ -3,14 +3,17 @@
|
|
3
3
|
require "json"
|
4
4
|
require "cocaine"
|
5
5
|
require "ostruct"
|
6
|
+
require "desi/http_client"
|
7
|
+
require "desi/local_install"
|
6
8
|
|
7
9
|
module Desi
|
8
10
|
class ProcessManager
|
9
11
|
|
10
12
|
def initialize(opts = {})
|
13
|
+
@host = opts.fetch(:host, 'http://127.0.0.1:9200')
|
11
14
|
@verbose = opts[:verbose]
|
12
15
|
@local_install = LocalInstall.new
|
13
|
-
@client = Desi::HttpClient.new(
|
16
|
+
@client = Desi::HttpClient.new(@host)
|
14
17
|
end
|
15
18
|
|
16
19
|
def start
|
data/lib/desi/runner.rb
CHANGED
@@ -13,9 +13,9 @@ module Desi
|
|
13
13
|
desc "List locally installed Elastic Search releases"
|
14
14
|
verbosity_option
|
15
15
|
def list(options = {})
|
16
|
-
puts "Local ES installs:" unless quiet?(options)
|
17
|
-
Desi::LocalInstall.new.
|
18
|
-
puts
|
16
|
+
puts "Local ES installs (current one is tagged with '*'):" unless quiet?(options)
|
17
|
+
Desi::LocalInstall.new.releases.sort.reverse.each do |v|
|
18
|
+
puts v
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -31,7 +31,7 @@ module Desi
|
|
31
31
|
releases
|
32
32
|
else
|
33
33
|
puts "Here are #{limit == 0 ? 'all the' : "the #{limit} latest"} releases"
|
34
|
-
releases.each {|rel| puts "
|
34
|
+
releases.each {|rel| puts " - #{rel.name} (#{rel.release_date})" }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -71,8 +71,26 @@ module Desi
|
|
71
71
|
|
72
72
|
desc "Show current status"
|
73
73
|
verbosity_option
|
74
|
+
option "--host", type: :string, desc: "Elastic Search cluster URL", default: '127.0.0.1:9200'
|
74
75
|
def status(options = {})
|
75
|
-
Desi::ProcessManager.new(verbose: !quiet?(options)).status
|
76
|
+
Desi::ProcessManager.new(verbose: !quiet?(options), host: options[:host]).status
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "List indices"
|
80
|
+
verbosity_option
|
81
|
+
option "--host", type: :string, desc: "Elastic Search cluster URL", default: '127.0.0.1:9200'
|
82
|
+
option "--delete", type: :boolean, desc: "Delete the specified indices (You've been warned!)", default: false
|
83
|
+
option "--empty", type: :boolean, desc: "Delete all documents from the specified indices", default: false
|
84
|
+
def indices(pattern = nil, options = {})
|
85
|
+
index_manager = Desi::IndexManager.new(verbose: !quiet?(options), host: options[:host])
|
86
|
+
|
87
|
+
if options[:delete]
|
88
|
+
index_manager.delete!(pattern)
|
89
|
+
elsif options[:empty]
|
90
|
+
index_manager.empty!(pattern)
|
91
|
+
else
|
92
|
+
index_manager.list(pattern)
|
93
|
+
end
|
76
94
|
end
|
77
95
|
|
78
96
|
# desc "Upgrade to latest ElasticSearch version"
|
data/lib/desi/version.rb
CHANGED
data/tasks/yard.rake
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: desi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
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: 2012-09-
|
12
|
+
date: 2012-09-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: boson
|
@@ -43,6 +43,70 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: addressable
|
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
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: guard-yard
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: redcarpet
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rb-inotify
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
46
110
|
description: ! 'Desi (Developper ElasticSearch Installer) is very simple tool to quickly
|
47
111
|
set up
|
48
112
|
|
@@ -55,7 +119,9 @@ extensions: []
|
|
55
119
|
extra_rdoc_files: []
|
56
120
|
files:
|
57
121
|
- .gitignore
|
122
|
+
- .yardopts
|
58
123
|
- Gemfile
|
124
|
+
- Guardfile
|
59
125
|
- LICENSE
|
60
126
|
- README.md
|
61
127
|
- Rakefile
|
@@ -65,12 +131,14 @@ files:
|
|
65
131
|
- lib/desi.rb
|
66
132
|
- lib/desi/downloader.rb
|
67
133
|
- lib/desi/http_client.rb
|
134
|
+
- lib/desi/index_manager.rb
|
68
135
|
- lib/desi/installer.rb
|
69
136
|
- lib/desi/local_install.rb
|
70
137
|
- lib/desi/process_manager.rb
|
71
138
|
- lib/desi/runner.rb
|
72
139
|
- lib/desi/upstream.rb
|
73
140
|
- lib/desi/version.rb
|
141
|
+
- tasks/yard.rake
|
74
142
|
homepage: https://github.com/AF83/desi/
|
75
143
|
licenses: []
|
76
144
|
post_install_message:
|