rexer 0.12.0 → 0.14.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: 236c94fc48362e83c23049edb3b1b21311385622a66793fa19240f42fb702388
4
- data.tar.gz: 7707f18ae6e414d9390080944292f8537a11b19f03ce93d6142dc106129f7e04
3
+ metadata.gz: 9226515e3b9599a9915d140c03d8153f1e562409b5c2d8d3f28501d547511ffc
4
+ data.tar.gz: 8aa2e357ea18131f0fdef8a00276a891f8dec119ad0c9171524384ac4a6119f7
5
5
  SHA512:
6
- metadata.gz: 12a8e53ccdb53e9da0bbd66787eb4377031353e51d3d3785d3242006cbfe7445acbd5f136664bc5e681d76333ea8e082f7b92b8f1c0fe4923fb41a8a947526bd
7
- data.tar.gz: f487865de3c6f4064f72e0dae4fca83c9a8c17a711d26e0fb1e2c78058db44b510930cc67d856a19d71059e9cf46f95aeb3d24821266157dfd3eb2e627bfae77
6
+ metadata.gz: 002633010dcb32959079688a3510edda19e93784145b47b5ca60cf48749348d36f63691c94df30568b8a1fbc3acf63940a832ffa9afc80b0325a430398e9b122
7
+ data.tar.gz: 236685fa7ad6d2760831d2a416b81a1e2e23efb588399e83134e81dbcf06fa3a6b83351f383eae4f59adf3e8aae7c772ed70b6fbe3066004fa92664fca88ecc3
data/README.md CHANGED
@@ -28,7 +28,7 @@ gem install rexer
28
28
 
29
29
  ## Supported Redmine
30
30
 
31
- Rexer is tested with Redmine v5.1 and trunk.
31
+ Rexer is tested with Redmine v6.0 and trunk.
32
32
 
33
33
  ## Usage
34
34
 
@@ -109,6 +109,99 @@ If the specified ENV is currently installed, it compares the current `.extension
109
109
 
110
110
  Loads `.extensions.lock` and updates the currently installed extensions to the latest version. `.extensions.rb` is NOT referenced in this command.
111
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
+
112
205
  ## Advanced Usage
113
206
 
114
207
  ### Defining for each environment and extension
data/lib/rexer/cli.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "thor"
2
2
  require "dotenv"
3
3
  require "erb"
4
+ require "active_support/core_ext/object/blank"
4
5
 
5
6
  module Rexer
6
7
  class Cli < Thor
@@ -34,7 +35,7 @@ module Rexer
34
35
  Commands::Switch.new.call(env&.to_sym)
35
36
  end
36
37
 
37
- desc "update", "Update extensions for the currently installed environment to the latest version"
38
+ desc "update", "Update extensions for the currently installed environment to the latest version if extensions are updateable"
38
39
  def update
39
40
  Commands::Update.new.call
40
41
  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"
@@ -1,4 +1,5 @@
1
1
  require "git"
2
+ require "uri"
2
3
 
3
4
  module Rexer
4
5
  module Source
@@ -8,6 +9,7 @@ module Rexer
8
9
  @branch = branch
9
10
  @tag = tag
10
11
  @ref = ref
12
+ @reference = branch || tag || ref
11
13
  end
12
14
 
13
15
  def load(path)
@@ -20,19 +22,30 @@ module Rexer
20
22
  end
21
23
 
22
24
  def updatable?
23
- !branch.nil?
25
+ branch || reference.nil?
24
26
  end
25
27
 
26
28
  def info
27
- branch || tag || ref || "master"
29
+ URI.parse(url).then do |uri|
30
+ "#{uri.host}#{uri.path}@#{reference_name}"
31
+ end
28
32
  end
29
33
 
30
34
  private
31
35
 
32
- attr_reader :url, :branch, :tag, :ref
36
+ attr_reader :url, :reference, :branch, :tag, :ref
33
37
 
34
38
  def checkout(git)
35
- (branch || tag || ref)&.then { git.checkout(_1) }
39
+ reference&.then { git.checkout(_1) }
40
+ end
41
+
42
+ def reference_name
43
+ branch || tag || ref || "main"
44
+ end
45
+
46
+ def short_ref
47
+ return unless ref
48
+ ref.match?(/^[a-z0-9]+$/) ? ref.slice(0, 7) : ref
36
49
  end
37
50
  end
38
51
  end
@@ -2,8 +2,13 @@ module Rexer
2
2
  module Source
3
3
  class Github < Git
4
4
  def initialize(repo:, branch: nil, tag: nil, ref: nil)
5
+ @repo = repo
5
6
  super(url: "https://github.com/#{repo}", branch: branch, tag: tag, ref: ref)
6
7
  end
8
+
9
+ def info
10
+ "#{@repo}@#{reference_name}"
11
+ end
7
12
  end
8
13
  end
9
14
  end
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.12.0"
2
+ VERSION = "0.14.0"
3
3
  end
data/lib/rexer.rb CHANGED
@@ -27,9 +27,9 @@ module Rexer
27
27
  end
28
28
  end
29
29
 
30
+ require "active_support"
30
31
  require "pathname"
31
32
  require "zeitwerk"
32
33
 
33
34
  loader = Zeitwerk::Loader.for_gem
34
35
  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.12.0
4
+ version: 0.14.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-25 00:00:00.000000000 Z
11
+ date: 2024-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '3.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activesupport
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '7.0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '7.0'
97
111
  description: Rexer is a command-line tool for managing Redmine Extension (Plugin and
98
112
  Themes). It allows you to define extensions in a Ruby DSL and install, uninstall,
99
113
  update, and switch between different sets of the extensions.
@@ -125,8 +139,16 @@ files:
125
139
  - lib/rexer/definition/diff.rb
126
140
  - lib/rexer/definition/dsl.rb
127
141
  - lib/rexer/definition/lock.rb
128
- - lib/rexer/extension/plugin.rb
129
- - lib/rexer/extension/theme.rb
142
+ - lib/rexer/extension/plugin/action.rb
143
+ - lib/rexer/extension/plugin/install.rb
144
+ - lib/rexer/extension/plugin/reload_source.rb
145
+ - lib/rexer/extension/plugin/uninstall.rb
146
+ - lib/rexer/extension/plugin/update.rb
147
+ - lib/rexer/extension/theme/action.rb
148
+ - lib/rexer/extension/theme/install.rb
149
+ - lib/rexer/extension/theme/reload_source.rb
150
+ - lib/rexer/extension/theme/uninstall.rb
151
+ - lib/rexer/extension/theme/update.rb
130
152
  - lib/rexer/source.rb
131
153
  - lib/rexer/source/base.rb
132
154
  - 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