rexer 0.11.1 → 0.13.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.
- checksums.yaml +4 -4
- data/README.md +103 -11
- data/lib/rexer/cli.rb +5 -0
- data/lib/rexer/commands/reinstall.rb +53 -0
- data/lib/rexer/definition/dsl.rb +1 -1
- data/lib/rexer/extension/plugin/action.rb +65 -0
- data/lib/rexer/extension/plugin/install.rb +39 -0
- data/lib/rexer/extension/plugin/reload_source.rb +27 -0
- data/lib/rexer/extension/plugin/uninstall.rb +32 -0
- data/lib/rexer/extension/plugin/update.rb +29 -0
- data/lib/rexer/extension/theme/action.rb +44 -0
- data/lib/rexer/extension/theme/install.rb +27 -0
- data/lib/rexer/extension/theme/reload_source.rb +26 -0
- data/lib/rexer/extension/theme/uninstall.rb +27 -0
- data/lib/rexer/extension/theme/update.rb +23 -0
- data/lib/rexer/source/base.rb +0 -4
- data/lib/rexer/source.rb +5 -2
- data/lib/rexer/version.rb +1 -1
- data/lib/rexer.rb +0 -1
- metadata +13 -4
- data/lib/rexer/extension/plugin.rb +0 -172
- data/lib/rexer/extension/theme.rb +0 -127
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13397d6c2ce415899b6435654c7bfabdf78c6c85aa2988a7879b64b630550dba
|
4
|
+
data.tar.gz: c1edbd2a8faa415b4796218176367c5f3b97cb9c5ce755c060029c1b1e4290af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 267698125ab41b6886d7a0f01bb4db14e0c2703de47dc8099d4f6ebe59bf18410785ddb2a11815c3cc6dbe3aa47e3c35abacc32bdefed265f003bb066ee2c4e4
|
7
|
+
data.tar.gz: eb76a0f49f4c13e47855e4b3eb2443e413aa25a87d66e854eccc820d21e55a3940480af9acdb0bc729efb0b1959891245bf43ba6f41964d6a7b31f8cb9c79478
|
data/README.md
CHANGED
@@ -16,8 +16,6 @@ Rexer is a command-line tool for managing Redmine Extension (Plugin and Theme).
|
|
16
16
|
|
17
17
|
It is mainly aimed at helping with the development of Redmine and its plugins, allowing you to define extensions in a Ruby DSL and install, uninstall, update, and switch between different sets of the extensions.
|
18
18
|
|
19
|
-
[](https://asciinema.org/a/672754)
|
20
|
-
|
21
19
|
## What is Redmine Extension?
|
22
20
|
|
23
21
|
Redmine [Plugin](https://www.redmine.org/projects/redmine/wiki/Plugins) and [Theme](https://www.redmine.org/projects/redmine/wiki/Themes) are called Redmine Extension in this tool.
|
@@ -80,15 +78,16 @@ This command uninstalls the extensions and deletes the `.extensions.lock`.
|
|
80
78
|
```
|
81
79
|
$ rex
|
82
80
|
Commands:
|
83
|
-
rex envs
|
84
|
-
rex help [COMMAND]
|
85
|
-
rex init
|
86
|
-
rex install [ENV]
|
87
|
-
rex
|
88
|
-
rex
|
89
|
-
rex
|
90
|
-
rex
|
91
|
-
rex
|
81
|
+
rex envs # Show the list of environments and their extensions defined in .extensions.rb
|
82
|
+
rex help [COMMAND] # Describe available commands or one specific command
|
83
|
+
rex init # Create a new .extensions.rb file
|
84
|
+
rex install [ENV] # Install the definitions in .extensions.rb for the specified environment
|
85
|
+
rex reinstall [PLUGIN or THEME] # Uninstall extensions for the currently installed environment and install them again
|
86
|
+
rex state # Show the current state of the installed extensions
|
87
|
+
rex switch [ENV] # Uninstall extensions for the currently installed environment and install extensions for the specified environment
|
88
|
+
rex uninstall # Uninstall extensions for the currently installed environment based on the state in .extensions.lock and remove the lock file
|
89
|
+
rex update # Update extensions for the currently installed environment to the latest version
|
90
|
+
rex version # Show Rexer version
|
92
91
|
|
93
92
|
Options:
|
94
93
|
-v, [--verbose], [--no-verbose], [--skip-verbose] # Detailed output
|
@@ -110,6 +109,99 @@ If the specified ENV is currently installed, it compares the current `.extension
|
|
110
109
|
|
111
110
|
Loads `.extensions.lock` and updates the currently installed extensions to the latest version. `.extensions.rb` is NOT referenced in this command.
|
112
111
|
|
112
|
+
### Short commands available
|
113
|
+
|
114
|
+
The command to execute is determined by a forward match, so you can use like the following short commands.
|
115
|
+
|
116
|
+
```
|
117
|
+
rex ins # means install
|
118
|
+
rex st # maens state
|
119
|
+
rex sw # means switch
|
120
|
+
rex e # means envs
|
121
|
+
rex v # means version
|
122
|
+
|
123
|
+
and more...
|
124
|
+
```
|
125
|
+
|
126
|
+
## Syntax of .extensions.rb file
|
127
|
+
|
128
|
+
### Plugin
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
plugin :plugin_name, <source>: { ... }
|
132
|
+
```
|
133
|
+
|
134
|
+
### Theme
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
theme :theme_name, <source>: { ... }
|
138
|
+
```
|
139
|
+
|
140
|
+
### Source
|
141
|
+
|
142
|
+
#### Git
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
plugin :redmine_issues_panel, git: { url: "https://github.com/redmica/redmine_issues_panel" }
|
146
|
+
```
|
147
|
+
```ruby
|
148
|
+
# Specify the branch
|
149
|
+
plugin :redmine_issues_panel, git: { url: "https://github.com/redmica/redmine_issues_panel", branch: "main" }
|
150
|
+
# Specify the tag
|
151
|
+
plugin :redmine_issues_panel, git: { url: "https://github.com/redmica/redmine_issues_panel", tag: "v1.0.0" }
|
152
|
+
# Specify the commit
|
153
|
+
plugin :redmine_issues_panel, git: { url: "https://github.com/redmica/redmine_issues_panel", ref: "5cfb8ccbabb2fad2c8f2273a4dda3f16ef2de124" }
|
154
|
+
```
|
155
|
+
|
156
|
+
#### GitHub
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
plugin :redmine_issues_panel, github: { repo: "redmica/redmine_issues_panel", tag: "v1.0.0" }
|
160
|
+
```
|
161
|
+
|
162
|
+
### Env
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
plugin :redmine_issues_panel, github: { repo: "redmica/redmine_issues_panel" }
|
166
|
+
|
167
|
+
# This is the same as the above.
|
168
|
+
env :default do
|
169
|
+
plugin :redmine_issues_panel, github: { repo: "redmica/redmine_issues_panel" }
|
170
|
+
end
|
171
|
+
|
172
|
+
env :stable do
|
173
|
+
plugin :redmine_issues_panel, github: { repo: "redmica/redmine_issues_panel", tag: "v1.0.2" }
|
174
|
+
end
|
175
|
+
|
176
|
+
env :default, :stable do
|
177
|
+
theme :bleuclair, github: { repo: "farend/redmine_theme_farend_bleuclair" }
|
178
|
+
end
|
179
|
+
```
|
180
|
+
|
181
|
+
### Hooks
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
plugin :redmine_issues_panel, github: { repo: "redmica/redmine_issues_panel" } do
|
185
|
+
installed do
|
186
|
+
puts "The plugin has been installed."
|
187
|
+
end
|
188
|
+
|
189
|
+
uninstalled do
|
190
|
+
puts "The plugin has been uninstalled."
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
theme :bleuclair, github: { repo: "farend/redmine_theme_farend_bleuclair" } do
|
195
|
+
installed do
|
196
|
+
puts "The theme has been installed."
|
197
|
+
end
|
198
|
+
|
199
|
+
uninstalled do
|
200
|
+
puts "The theme has been uninstalled."
|
201
|
+
end
|
202
|
+
end
|
203
|
+
```
|
204
|
+
|
113
205
|
## Advanced Usage
|
114
206
|
|
115
207
|
### Defining for each environment and extension
|
data/lib/rexer/cli.rb
CHANGED
@@ -24,6 +24,11 @@ module Rexer
|
|
24
24
|
Commands::Uninstall.new.call
|
25
25
|
end
|
26
26
|
|
27
|
+
desc "reinstall [PLUGIN or THEME]", "Uninstall extensions for the currently installed environment and install them again"
|
28
|
+
def reinstall(extension_name)
|
29
|
+
Commands::Reinstall.new.call(extension_name)
|
30
|
+
end
|
31
|
+
|
27
32
|
desc "switch [ENV]", "Uninstall extensions for the currently installed environment and install extensions for the specified environment"
|
28
33
|
def switch(env = "default")
|
29
34
|
Commands::Switch.new.call(env&.to_sym)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Commands
|
3
|
+
class Reinstall
|
4
|
+
include ActionCallable
|
5
|
+
|
6
|
+
Action = Data.define(:install, :uninstall)
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@lock_definition = Definition::Lock.load_data
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(extension_name)
|
13
|
+
return if no_lock_file_found
|
14
|
+
|
15
|
+
extension, action = find_extension_with_action(extension_name.to_sym)
|
16
|
+
|
17
|
+
if extension.nil?
|
18
|
+
puts "#{extension_name} is not installed"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
|
22
|
+
reinstall(extension, action)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :lock_definition
|
28
|
+
|
29
|
+
def find_extension_with_action(name)
|
30
|
+
lock_definition.plugins.find { _1.name == name }&.then do |plugin|
|
31
|
+
action = Action.new(Extension::Plugin::Install, Extension::Plugin::Uninstall)
|
32
|
+
return [plugin, action]
|
33
|
+
end
|
34
|
+
|
35
|
+
lock_definition.themes.find { _1.name == name }&.then do |theme|
|
36
|
+
action = Action.new(Extension::Theme::Install, Extension::Theme::Uninstall)
|
37
|
+
return [theme, action]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def reinstall(extension, action)
|
42
|
+
call_action action.uninstall, extension
|
43
|
+
call_action action.install, extension
|
44
|
+
end
|
45
|
+
|
46
|
+
def no_lock_file_found
|
47
|
+
lock_definition.nil?.tap { |result|
|
48
|
+
puts "No lock file found" if result
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/rexer/definition/dsl.rb
CHANGED
@@ -0,0 +1,65 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "wisper"
|
3
|
+
|
4
|
+
module Rexer
|
5
|
+
module Extension
|
6
|
+
module Plugin
|
7
|
+
class Action
|
8
|
+
include Wisper::Publisher
|
9
|
+
|
10
|
+
def initialize(definition)
|
11
|
+
@definition = definition
|
12
|
+
@name = definition.name
|
13
|
+
@hooks = definition.hooks || {}
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
attr_reader :name, :hooks, :definition
|
19
|
+
|
20
|
+
def plugin_root_dir
|
21
|
+
Pathname.new("plugins")
|
22
|
+
end
|
23
|
+
|
24
|
+
def plugin_dir
|
25
|
+
@plugin_dir ||= plugin_root_dir.join(name.to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
def plugin_exists?
|
29
|
+
plugin_dir.exist? && !plugin_dir.empty?
|
30
|
+
end
|
31
|
+
|
32
|
+
def needs_db_migration?
|
33
|
+
plugin_dir.join("db", "migrate").then {
|
34
|
+
_1.exist? && !_1.empty?
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def run_db_migrate(extra_envs = {})
|
39
|
+
return unless needs_db_migration?
|
40
|
+
|
41
|
+
envs = {"NAME" => name.to_s}.merge(extra_envs)
|
42
|
+
cmds = build_cmd("bundle", "exec", "rake", Rexer.verbosity.debug? ? nil : "-q", "redmine:plugins:migrate", envs:)
|
43
|
+
|
44
|
+
broadcast(:processing, "Execute #{cmds}")
|
45
|
+
|
46
|
+
if Rexer.verbosity.debug?
|
47
|
+
system(cmds, exception: true)
|
48
|
+
else
|
49
|
+
_, error, status = Open3.capture3(cmds)
|
50
|
+
raise error unless status.success?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def source
|
55
|
+
@source ||= Source.from_definition(definition.source)
|
56
|
+
end
|
57
|
+
|
58
|
+
def build_cmd(*command, envs: {})
|
59
|
+
envs_str = envs.map { [_1, _2].join("=") }
|
60
|
+
[Rexer.config.command_prefix, *command, *envs_str].compact.join(" ")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Plugin
|
4
|
+
class Install < Action
|
5
|
+
def call
|
6
|
+
broadcast(:started, "Install #{name}")
|
7
|
+
|
8
|
+
if plugin_exists?
|
9
|
+
broadcast(:skipped, "Already exists")
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
load_from_source
|
14
|
+
run_bundle_install
|
15
|
+
run_db_migrate
|
16
|
+
hooks[:installed]&.call
|
17
|
+
|
18
|
+
broadcast(:completed)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def load_from_source
|
24
|
+
source.load(plugin_dir.to_s)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_bundle_install
|
28
|
+
return unless plugin_dir.join("Gemfile").exist?
|
29
|
+
|
30
|
+
cmds = build_cmd("bundle", "install", Rexer.verbosity.debug? ? nil : "--quiet")
|
31
|
+
|
32
|
+
broadcast(:processing, "Execute #{cmds}")
|
33
|
+
|
34
|
+
system(cmds, exception: true)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Plugin
|
4
|
+
class ReloadSource < Action
|
5
|
+
def call
|
6
|
+
return unless plugin_exists?
|
7
|
+
|
8
|
+
broadcast(:started, "Reload #{name} source")
|
9
|
+
|
10
|
+
reload_source
|
11
|
+
run_db_migrate
|
12
|
+
|
13
|
+
broadcast(:completed)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def reload_source
|
19
|
+
plugin_dir.to_s.then { |dir|
|
20
|
+
FileUtils.rm_rf(dir)
|
21
|
+
source.load(dir)
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Plugin
|
4
|
+
class Uninstall < Action
|
5
|
+
def call
|
6
|
+
broadcast(:started, "Uninstall #{name}")
|
7
|
+
|
8
|
+
unless plugin_exists?
|
9
|
+
broadcast(:skipped, "Not exists")
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
reset_db_migration
|
14
|
+
remove_plugin
|
15
|
+
hooks[:uninstalled]&.call
|
16
|
+
|
17
|
+
broadcast(:completed)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def reset_db_migration
|
23
|
+
run_db_migrate("VERSION" => "0")
|
24
|
+
end
|
25
|
+
|
26
|
+
def remove_plugin
|
27
|
+
plugin_dir.rmtree
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Plugin
|
4
|
+
class Update < Action
|
5
|
+
def call
|
6
|
+
return unless plugin_exists?
|
7
|
+
|
8
|
+
broadcast(:started, "Update #{name}")
|
9
|
+
|
10
|
+
unless source.updatable?
|
11
|
+
broadcast(:skipped, "Not updatable")
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
15
|
+
update_source
|
16
|
+
run_db_migrate
|
17
|
+
|
18
|
+
broadcast(:completed)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def update_source
|
24
|
+
source.update(plugin_dir.to_s)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "wisper"
|
2
|
+
|
3
|
+
module Rexer
|
4
|
+
module Extension
|
5
|
+
module Theme
|
6
|
+
class Action
|
7
|
+
include Wisper::Publisher
|
8
|
+
|
9
|
+
def initialize(definition)
|
10
|
+
@definition = definition
|
11
|
+
@name = definition.name
|
12
|
+
@hooks = definition.hooks || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
attr_reader :name, :hooks, :definition
|
18
|
+
|
19
|
+
def theme_root_dir
|
20
|
+
public_themes = Pathname.pwd.join("public", "themes")
|
21
|
+
|
22
|
+
if public_themes.exist?
|
23
|
+
# When Redmine version is v5.1 or older, public/themes is used.
|
24
|
+
public_themes
|
25
|
+
else
|
26
|
+
Pathname.new("themes")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def theme_dir
|
31
|
+
@theme_dir ||= theme_root_dir.join(name.to_s)
|
32
|
+
end
|
33
|
+
|
34
|
+
def theme_exists?
|
35
|
+
theme_dir.exist? && !theme_dir.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def source
|
39
|
+
@source ||= Source.from_definition(definition.source)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Theme
|
4
|
+
class Install < Action
|
5
|
+
def call
|
6
|
+
broadcast(:started, "Install #{name}")
|
7
|
+
|
8
|
+
if theme_exists?
|
9
|
+
broadcast(:skipped, "Already exists")
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
load_from_source
|
14
|
+
hooks[:installed]&.call
|
15
|
+
|
16
|
+
broadcast(:completed)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def load_from_source
|
22
|
+
source.load(theme_dir.to_s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Theme
|
4
|
+
class ReloadSource < Action
|
5
|
+
def call
|
6
|
+
return unless theme_exists?
|
7
|
+
|
8
|
+
broadcast(:started, "Reload #{name} source")
|
9
|
+
|
10
|
+
reload_source
|
11
|
+
|
12
|
+
broadcast(:completed)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def reload_source
|
18
|
+
theme_dir.to_s.then { |dir|
|
19
|
+
FileUtils.rm_rf(dir)
|
20
|
+
source.load(dir)
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Theme
|
4
|
+
class Uninstall < Action
|
5
|
+
def call
|
6
|
+
broadcast(:started, "Uninstall #{name}")
|
7
|
+
|
8
|
+
unless theme_exists?
|
9
|
+
broadcast(:skipped, "Not exists")
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
13
|
+
remove_theme
|
14
|
+
hooks[:uninstalled]&.call
|
15
|
+
|
16
|
+
broadcast(:completed)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def remove_theme
|
22
|
+
theme_dir.rmtree
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Rexer
|
2
|
+
module Extension
|
3
|
+
module Theme
|
4
|
+
class Update < Action
|
5
|
+
def call
|
6
|
+
return unless theme_exists?
|
7
|
+
|
8
|
+
broadcast(:started, "Update #{name}")
|
9
|
+
|
10
|
+
update_source
|
11
|
+
|
12
|
+
broadcast(:completed)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def update_source
|
18
|
+
source.update(theme_dir.to_s)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/rexer/source/base.rb
CHANGED
data/lib/rexer/source.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
module Rexer
|
2
2
|
module Source
|
3
|
-
|
3
|
+
TYPE = {
|
4
|
+
git: Git,
|
5
|
+
github: Github
|
6
|
+
}.freeze
|
4
7
|
|
5
8
|
def self.from_definition(source)
|
6
|
-
|
9
|
+
TYPE[source.type].new(**source.options)
|
7
10
|
end
|
8
11
|
end
|
9
12
|
end
|
data/lib/rexer/version.rb
CHANGED
data/lib/rexer.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rexer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katsuya Hidaka
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -115,6 +115,7 @@ files:
|
|
115
115
|
- lib/rexer/commands/envs.rb
|
116
116
|
- lib/rexer/commands/init.rb
|
117
117
|
- lib/rexer/commands/install.rb
|
118
|
+
- lib/rexer/commands/reinstall.rb
|
118
119
|
- lib/rexer/commands/state.rb
|
119
120
|
- lib/rexer/commands/switch.rb
|
120
121
|
- lib/rexer/commands/uninstall.rb
|
@@ -124,8 +125,16 @@ files:
|
|
124
125
|
- lib/rexer/definition/diff.rb
|
125
126
|
- lib/rexer/definition/dsl.rb
|
126
127
|
- lib/rexer/definition/lock.rb
|
127
|
-
- lib/rexer/extension/plugin.rb
|
128
|
-
- lib/rexer/extension/
|
128
|
+
- lib/rexer/extension/plugin/action.rb
|
129
|
+
- lib/rexer/extension/plugin/install.rb
|
130
|
+
- lib/rexer/extension/plugin/reload_source.rb
|
131
|
+
- lib/rexer/extension/plugin/uninstall.rb
|
132
|
+
- lib/rexer/extension/plugin/update.rb
|
133
|
+
- lib/rexer/extension/theme/action.rb
|
134
|
+
- lib/rexer/extension/theme/install.rb
|
135
|
+
- lib/rexer/extension/theme/reload_source.rb
|
136
|
+
- lib/rexer/extension/theme/uninstall.rb
|
137
|
+
- lib/rexer/extension/theme/update.rb
|
129
138
|
- lib/rexer/source.rb
|
130
139
|
- lib/rexer/source/base.rb
|
131
140
|
- lib/rexer/source/git.rb
|
@@ -1,172 +0,0 @@
|
|
1
|
-
require "open3"
|
2
|
-
require "wisper"
|
3
|
-
|
4
|
-
module Rexer
|
5
|
-
module Extension
|
6
|
-
module Plugin
|
7
|
-
def self.dir
|
8
|
-
Pathname.new("plugins")
|
9
|
-
end
|
10
|
-
|
11
|
-
class Base
|
12
|
-
include Wisper::Publisher
|
13
|
-
|
14
|
-
def initialize(definition)
|
15
|
-
@definition = definition
|
16
|
-
@name = definition.name
|
17
|
-
@hooks = definition.hooks || {}
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
attr_reader :name, :hooks, :definition
|
23
|
-
|
24
|
-
def plugin_dir
|
25
|
-
@plugin_dir ||= Plugin.dir.join(name.to_s)
|
26
|
-
end
|
27
|
-
|
28
|
-
def plugin_exists?
|
29
|
-
plugin_dir.exist? && !plugin_dir.empty?
|
30
|
-
end
|
31
|
-
|
32
|
-
def needs_db_migration?
|
33
|
-
plugin_dir.join("db", "migrate").then {
|
34
|
-
_1.exist? && !_1.empty?
|
35
|
-
}
|
36
|
-
end
|
37
|
-
|
38
|
-
def run_db_migrate(extra_envs = {})
|
39
|
-
return unless needs_db_migration?
|
40
|
-
|
41
|
-
envs = {"NAME" => name.to_s}.merge(extra_envs)
|
42
|
-
cmds = build_cmd("bundle", "exec", "rake", Rexer.verbosity.debug? ? nil : "-q", "redmine:plugins:migrate", envs:)
|
43
|
-
|
44
|
-
broadcast(:processing, "Execute #{cmds}")
|
45
|
-
|
46
|
-
if Rexer.verbosity.debug?
|
47
|
-
system(cmds, exception: true)
|
48
|
-
else
|
49
|
-
_, error, status = Open3.capture3(cmds)
|
50
|
-
raise error unless status.success?
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def source
|
55
|
-
@source ||= Source.from_definition(definition.source)
|
56
|
-
end
|
57
|
-
|
58
|
-
def build_cmd(*command, envs: {})
|
59
|
-
envs_str = envs.map { [_1, _2].join("=") }
|
60
|
-
[Rexer.config.command_prefix, *command, *envs_str].compact.join(" ")
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
class Install < Base
|
65
|
-
def call
|
66
|
-
broadcast(:started, "Install #{name}")
|
67
|
-
|
68
|
-
if plugin_exists?
|
69
|
-
broadcast(:skipped, "Already exists")
|
70
|
-
return
|
71
|
-
end
|
72
|
-
|
73
|
-
load_from_source
|
74
|
-
run_bundle_install
|
75
|
-
run_db_migrate
|
76
|
-
hooks[:installed]&.call
|
77
|
-
|
78
|
-
broadcast(:completed)
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def load_from_source
|
84
|
-
source.load(plugin_dir.to_s)
|
85
|
-
end
|
86
|
-
|
87
|
-
def run_bundle_install
|
88
|
-
return unless plugin_dir.join("Gemfile").exist?
|
89
|
-
|
90
|
-
cmds = build_cmd("bundle", "install", Rexer.verbosity.debug? ? nil : "--quiet")
|
91
|
-
|
92
|
-
broadcast(:processing, "Execute #{cmds}")
|
93
|
-
|
94
|
-
system(cmds, exception: true)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class Uninstall < Base
|
99
|
-
def call
|
100
|
-
broadcast(:started, "Uninstall #{name}")
|
101
|
-
|
102
|
-
unless plugin_exists?
|
103
|
-
broadcast(:skipped, "Not exists")
|
104
|
-
return
|
105
|
-
end
|
106
|
-
|
107
|
-
reset_db_migration
|
108
|
-
remove_plugin
|
109
|
-
hooks[:uninstalled]&.call
|
110
|
-
|
111
|
-
broadcast(:completed)
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
|
116
|
-
def reset_db_migration
|
117
|
-
run_db_migrate("VERSION" => "0")
|
118
|
-
end
|
119
|
-
|
120
|
-
def remove_plugin
|
121
|
-
plugin_dir.rmtree
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
class Update < Base
|
126
|
-
def call
|
127
|
-
return unless plugin_exists?
|
128
|
-
|
129
|
-
broadcast(:started, "Update #{name}")
|
130
|
-
|
131
|
-
unless source.updatable?
|
132
|
-
broadcast(:skipped, "Not updatable")
|
133
|
-
return
|
134
|
-
end
|
135
|
-
|
136
|
-
update_source
|
137
|
-
run_db_migrate
|
138
|
-
|
139
|
-
broadcast(:completed)
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
def update_source
|
145
|
-
source.update(plugin_dir.to_s)
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
class ReloadSource < Base
|
150
|
-
def call
|
151
|
-
return unless plugin_exists?
|
152
|
-
|
153
|
-
broadcast(:started, "Reload #{name} source")
|
154
|
-
|
155
|
-
reload_source
|
156
|
-
run_db_migrate
|
157
|
-
|
158
|
-
broadcast(:completed)
|
159
|
-
end
|
160
|
-
|
161
|
-
private
|
162
|
-
|
163
|
-
def reload_source
|
164
|
-
plugin_dir.to_s.then { |dir|
|
165
|
-
FileUtils.rm_rf(dir)
|
166
|
-
source.load(dir)
|
167
|
-
}
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require "wisper"
|
2
|
-
|
3
|
-
module Rexer
|
4
|
-
module Extension
|
5
|
-
module Theme
|
6
|
-
def self.dir
|
7
|
-
public_themes = Pathname.pwd.join("public", "themes")
|
8
|
-
|
9
|
-
if public_themes.exist?
|
10
|
-
# When Redmine version is v5.1 or older, public/themes is used.
|
11
|
-
public_themes
|
12
|
-
else
|
13
|
-
Pathname.new("themes")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Base
|
18
|
-
include Wisper::Publisher
|
19
|
-
|
20
|
-
def initialize(definition)
|
21
|
-
@definition = definition
|
22
|
-
@name = definition.name
|
23
|
-
@hooks = definition.hooks || {}
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
attr_reader :name, :hooks, :definition
|
29
|
-
|
30
|
-
def theme_dir
|
31
|
-
@theme_dir ||= Theme.dir.join(name.to_s)
|
32
|
-
end
|
33
|
-
|
34
|
-
def theme_exists?
|
35
|
-
theme_dir.exist? && !theme_dir.empty?
|
36
|
-
end
|
37
|
-
|
38
|
-
def source
|
39
|
-
@source ||= Source.from_definition(definition.source)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class Install < Base
|
44
|
-
def call
|
45
|
-
broadcast(:started, "Install #{name}")
|
46
|
-
|
47
|
-
if theme_exists?
|
48
|
-
broadcast(:skipped, "Already exists")
|
49
|
-
return
|
50
|
-
end
|
51
|
-
|
52
|
-
load_from_source
|
53
|
-
hooks[:installed]&.call
|
54
|
-
|
55
|
-
broadcast(:completed)
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def load_from_source
|
61
|
-
source.load(theme_dir.to_s)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Uninstall < Base
|
66
|
-
def call
|
67
|
-
broadcast(:started, "Uninstall #{name}")
|
68
|
-
|
69
|
-
unless theme_exists?
|
70
|
-
broadcast(:skipped, "Not exists")
|
71
|
-
return
|
72
|
-
end
|
73
|
-
|
74
|
-
remove_theme
|
75
|
-
hooks[:uninstalled]&.call
|
76
|
-
|
77
|
-
broadcast(:completed)
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def remove_theme
|
83
|
-
theme_dir.rmtree
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
class Update < Base
|
88
|
-
def call
|
89
|
-
return unless theme_exists?
|
90
|
-
|
91
|
-
broadcast(:started, "Update #{name}")
|
92
|
-
|
93
|
-
update_source
|
94
|
-
|
95
|
-
broadcast(:completed)
|
96
|
-
end
|
97
|
-
|
98
|
-
private
|
99
|
-
|
100
|
-
def update_source
|
101
|
-
source.update(theme_dir.to_s)
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
class ReloadSource < Base
|
106
|
-
def call
|
107
|
-
return unless theme_exists?
|
108
|
-
|
109
|
-
broadcast(:started, "Reload #{name} source")
|
110
|
-
|
111
|
-
reload_source
|
112
|
-
|
113
|
-
broadcast(:completed)
|
114
|
-
end
|
115
|
-
|
116
|
-
private
|
117
|
-
|
118
|
-
def reload_source
|
119
|
-
theme_dir.to_s.then { |dir|
|
120
|
-
FileUtils.rm_rf(dir)
|
121
|
-
source.load(dir)
|
122
|
-
}
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|