rexer 0.11.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b367d481c9b2b2c3acee47b4070f5f65b014c0de97841b556d40edf1ca5626c7
4
- data.tar.gz: fce6d233a643fa0779e03a11f2a37de8781bcb1bfbaf44a7d33a85103d5033fb
3
+ metadata.gz: 13397d6c2ce415899b6435654c7bfabdf78c6c85aa2988a7879b64b630550dba
4
+ data.tar.gz: c1edbd2a8faa415b4796218176367c5f3b97cb9c5ce755c060029c1b1e4290af
5
5
  SHA512:
6
- metadata.gz: c2577989d226043cdf48d55f4ef3ecfdfc02b030723ec449a5d27d78501a6ade52dee4bd61ddcc0fba67e1e741167bac698cde9333cd985e34196a8891791b67
7
- data.tar.gz: f5c069c050a4dc5f260c3949af7d5db1cdb47136bf4f19848d0c70302a4a96565303a260e43c066218e051c5040f862d8932ef29b5f7a8470553bece488e0900
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
- [![demo](docs/demo-v0.8.0.gif)](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 # Show the list of environments and their extensions defined in .extensions.rb
84
- rex help [COMMAND] # Describe available commands or one specific command
85
- rex init # Create a new .extensions.rb file
86
- rex install [ENV] # Install the definitions in .extensions.rb for the specified environment
87
- rex state # Show the current state of the installed extensions
88
- rex switch [ENV] # Uninstall extensions for the currently installed environment and install extensions for the specified environment
89
- rex uninstall # Uninstall extensions for the currently installed environment based on the state in .extensions.lock and remove the lock file
90
- rex update # Update extensions for the currently installed environment to the latest version
91
- rex version # Show Rexer version
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
@@ -63,7 +63,7 @@ module Rexer
63
63
  end
64
64
 
65
65
  def build_source(opts)
66
- type = opts.keys.find { Rexer::Source.names.include?(_1) }
66
+ type = (opts.keys & Rexer::Source::TYPE.keys).first
67
67
  Source.new(type, opts[type]) if type
68
68
  end
69
69
  end
@@ -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
@@ -1,10 +1,6 @@
1
1
  module Rexer
2
2
  module Source
3
3
  class Base
4
- def self.inherited(subclass)
5
- Source.names << subclass.name.split("::").last.downcase.to_sym
6
- end
7
-
8
4
  # Load the source to the given path.
9
5
  def load(_path)
10
6
  raise "Not implemented"
data/lib/rexer/source.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  module Rexer
2
2
  module Source
3
- def self.names = @names ||= []
3
+ TYPE = {
4
+ git: Git,
5
+ github: Github
6
+ }.freeze
4
7
 
5
8
  def self.from_definition(source)
6
- const_get(source.type.capitalize).new(**source.options)
9
+ TYPE[source.type].new(**source.options)
7
10
  end
8
11
  end
9
12
  end
data/lib/rexer/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rexer
2
- VERSION = "0.11.1"
2
+ VERSION = "0.13.0"
3
3
  end
data/lib/rexer.rb CHANGED
@@ -32,4 +32,3 @@ require "zeitwerk"
32
32
 
33
33
  loader = Zeitwerk::Loader.for_gem
34
34
  loader.setup
35
- loader.eager_load
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.11.1
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-09-15 00:00:00.000000000 Z
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/theme.rb
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