discourse_theme 1.0.1 → 1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc25d48532311173b00bda69ffeb62ac04ada54bee21d428f7b1402a73715915
4
- data.tar.gz: 6c96df10a9701d44e2124a9efde5421c4fd96a2b9be060a33c6ea24c0293c203
3
+ metadata.gz: 1e3352f4282ac30c46402e5cdf6f7c9ba504e7c8b5980884e4a42b700eef3e90
4
+ data.tar.gz: b13f657e0e023c3a6bee4eca8dc3a9090c55333f444bc5b9a55d67781baac017
5
5
  SHA512:
6
- metadata.gz: 37c3272d730598d0931372ff5cee3ac6dee268e1b21dbadac8eae774976268f06cb81f141d6eb85d1d0b5cd3e27751723190200b16160c7ea8c752ef882972d0
7
- data.tar.gz: 266dafc9e80dee4994b827a0cb9518b009dc2e3cbf5a996b5e70d5494e38548070e3cae107b193eefb6f1e7cc30a14af42866df70c1b21f0e96b0e344c19444a
6
+ metadata.gz: 9900153835d064382bdde7fbd395345a66cdeb5bd4b5df85f825c2d8ac12556bc4c329d56bd91030a5efae836c567db79ab219d7276d3e5cef784e51aedeba82
7
+ data.tar.gz: 26aec19d02acf892b7f5434ec7f141c1afa7e3d8e099917e40d15cafa6d8a2ff27a7076769eaa6319b44d0d46a4561d12374fa295929b140e2e0938789334792
data/CHANGELOG.md CHANGED
@@ -5,9 +5,22 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.1.0] - 2024-01-10
9
+
10
+ ### Added
11
+
12
+ - Add upload theme migrations prompt to `watch` command for `discourse_theme` CLI (#38)
13
+
14
+ ## [1.0.2] - 2023-12-08
15
+
16
+ ### Fixed
17
+
18
+ - `discourse_theme rspec` command using Docker container not copying theme to the right directory that is mounted inside
19
+ the Docker container.
20
+
8
21
  ## [1.0.1] - 2023-10-19
9
22
 
10
- ## Fixed
23
+ ### Fixed
11
24
 
12
25
  - Spec path was not preserved when running rspec against a local Discourse repository.
13
26
 
@@ -36,7 +36,7 @@ module DiscourseTheme
36
36
  exit 1
37
37
  end
38
38
 
39
- def run(args)
39
+ def run(args, &block)
40
40
  usage unless args[1]
41
41
 
42
42
  reset = !!args.delete("--reset")
@@ -66,9 +66,11 @@ module DiscourseTheme
66
66
  theme_list = client.get_themes_list
67
67
 
68
68
  options = {}
69
+
69
70
  if theme_id && theme = theme_list.find { |t| t["id"] == theme_id }
70
71
  options["Sync with existing theme: '#{theme["name"]}' (id:#{theme_id})"] = :default
71
72
  end
73
+
72
74
  options["Create and sync with a new theme"] = :create
73
75
  options["Select a different theme"] = :select
74
76
 
@@ -89,6 +91,7 @@ module DiscourseTheme
89
91
  rescue StandardError
90
92
  nil
91
93
  end
94
+
92
95
  already_uploaded = !!theme
93
96
  is_component = theme&.[]("component")
94
97
  component_count = about_json&.[]("components")&.length || 0
@@ -111,7 +114,9 @@ module DiscourseTheme
111
114
  )
112
115
 
113
116
  UI.progress "Uploading theme from #{dir}"
114
- settings.theme_id = theme_id = uploader.upload_full_theme
117
+
118
+ settings.theme_id =
119
+ theme_id = uploader.upload_full_theme(ignore_files: ignored_migrations(theme, dir))
115
120
 
116
121
  UI.success "Theme uploaded (id:#{theme_id})"
117
122
  UI.info "Preview: #{client.url}/?preview_theme_id=#{theme_id}"
@@ -126,7 +131,7 @@ module DiscourseTheme
126
131
 
127
132
  watcher = DiscourseTheme::Watcher.new(dir: dir, uploader: uploader)
128
133
  UI.progress "Watching for changes in #{dir}..."
129
- watcher.watch
134
+ watcher.watch(&block)
130
135
  elsif command == "download"
131
136
  client = DiscourseTheme::Client.new(dir, settings, reset: reset)
132
137
  downloader = DiscourseTheme::Downloader.new(dir: dir, client: client)
@@ -200,6 +205,43 @@ module DiscourseTheme
200
205
 
201
206
  private
202
207
 
208
+ def ignored_migrations(theme, dir)
209
+ return [] unless theme && Dir.exist?(File.join(dir, "migrations"))
210
+
211
+ existing_migrations =
212
+ theme
213
+ .dig("theme_fields")
214
+ &.filter_map do |theme_field|
215
+ theme_field["name"] if theme_field["target"] == "migrations"
216
+ end || []
217
+
218
+ new_migrations =
219
+ Dir["#{dir}/migrations/**/*.js"]
220
+ .reject do |f|
221
+ existing_migrations.any? do |existing_migration|
222
+ File.basename(f).include?(existing_migration)
223
+ end
224
+ end
225
+ .map { |f| Pathname.new(f).relative_path_from(Pathname.new(dir)).to_s }
226
+
227
+ if !new_migrations.empty?
228
+ options = { "Yes" => :yes, "No" => :no }
229
+
230
+ choice = UI.select(<<~TEXT, options.keys)
231
+ Would you like to upload and run the following pending theme migration(s): #{new_migrations.join(", ")}
232
+ TEXT
233
+
234
+ if options[choice] == :no
235
+ UI.warn "Pending theme migrations have not been uploaded, run `discourse_theme upload #{dir}` if you wish to upload and run the theme migrations."
236
+ new_migrations
237
+ else
238
+ []
239
+ end
240
+ else
241
+ []
242
+ end
243
+ end
244
+
203
245
  def command?(cmd)
204
246
  exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
205
247
  ENV["PATH"]
@@ -37,7 +37,7 @@ module DiscourseTheme
37
37
 
38
38
  if settings.local_discourse_directory.empty?
39
39
  run_tests_with_docker(
40
- File.basename(dir),
40
+ dir,
41
41
  spec_directory,
42
42
  spec_path,
43
43
  headless: headless,
@@ -93,13 +93,15 @@ module DiscourseTheme
93
93
  end
94
94
 
95
95
  def run_tests_with_docker(
96
- theme_directory_name,
96
+ theme_directory,
97
97
  spec_directory,
98
98
  spec_path,
99
99
  headless: false,
100
100
  verbose: false,
101
101
  rebuild: false
102
102
  )
103
+ theme_directory_name = File.basename(theme_directory)
104
+
103
105
  image = "discourse/discourse_test:release"
104
106
  UI.progress("Running RSpec tests using '#{image}' Docker image...")
105
107
 
@@ -191,9 +193,7 @@ module DiscourseTheme
191
193
  rspec_envs = rspec_envs.map { |env| "-e #{env}" }.join(" ")
192
194
 
193
195
  begin
194
- tmp_theme_directory = File.join(DISCOURSE_THEME_TEST_TMP_DIR, theme_directory_name)
195
- FileUtils.mkdir_p(tmp_theme_directory) if !Dir.exist?(tmp_theme_directory)
196
- FileUtils.cp_r(spec_directory, File.join(tmp_theme_directory))
196
+ FileUtils.cp_r(theme_directory, DISCOURSE_THEME_TEST_TMP_DIR)
197
197
 
198
198
  execute(
199
199
  command:
@@ -203,7 +203,7 @@ module DiscourseTheme
203
203
  stream: true,
204
204
  )
205
205
  ensure
206
- FileUtils.rm_rf(File.join(tmp_theme_directory, "/spec"))
206
+ FileUtils.rm_rf(File.join(DISCOURSE_THEME_TEST_TMP_DIR, theme_directory_name))
207
207
  end
208
208
  end
209
209
 
@@ -28,6 +28,10 @@ module DiscourseTheme
28
28
  puts @@pastel.red("✘ #{message}")
29
29
  end
30
30
 
31
+ def self.warn(message)
32
+ puts @@pastel.yellow("⚠ #{message}")
33
+ end
34
+
31
35
  def self.success(message)
32
36
  puts @@pastel.green("✔ #{message}")
33
37
  end
@@ -1,6 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
  module DiscourseTheme
3
3
  class Uploader
4
+ def self.upload_full_theme_callbacks
5
+ @upload_callbacks ||= []
6
+ end
7
+
8
+ # Used in the test environment to register a callback that is called with the directory of the theme being uploaded.
9
+ def self.register_upload_full_theme_callback(&block)
10
+ self.upload_full_theme_callbacks << block
11
+ end
12
+
13
+ # Used in the test environment to clear the registered callbacks.
14
+ def self.reset_upload_full_theme_callbacks
15
+ self.upload_full_theme_callbacks.clear
16
+ end
17
+
4
18
  def initialize(dir:, client:, theme_id: nil, components: nil)
5
19
  @dir = dir
6
20
  @client = client
@@ -53,9 +67,24 @@ module DiscourseTheme
53
67
  UI.error "(end of errors)" if diagnose_errors(json) != 0
54
68
  end
55
69
 
56
- def upload_full_theme
70
+ def upload_full_theme(ignore_files: [])
57
71
  filename = "#{Pathname.new(Dir.tmpdir).realpath}/bundle_#{SecureRandom.hex}.tar.gz"
58
- compress_dir(filename, @dir)
72
+ temp_dir = nil
73
+
74
+ theme_dir =
75
+ if !ignore_files.empty?
76
+ temp_dir = Dir.mktmpdir
77
+ FileUtils.copy_entry(@dir, temp_dir)
78
+ dir_pathname = Pathname.new(@dir)
79
+ ignore_files.each { |file| FileUtils.rm_f(File.join(temp_dir, file)) }
80
+ temp_dir
81
+ else
82
+ @dir
83
+ end
84
+
85
+ self.class.upload_full_theme_callbacks.each { |cb| cb.call(theme_dir) }
86
+
87
+ compress_dir(filename, theme_dir)
59
88
 
60
89
  File.open(filename) do |tgz|
61
90
  response = @client.upload_full_theme(tgz, theme_id: @theme_id, components: @components)
@@ -66,7 +95,8 @@ module DiscourseTheme
66
95
  @theme_id
67
96
  end
68
97
  ensure
69
- FileUtils.rm_f filename
98
+ FileUtils.rm_rf(temp_dir) if temp_dir
99
+ FileUtils.rm_f(filename)
70
100
  end
71
101
  end
72
102
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module DiscourseTheme
3
- VERSION = "1.0.1"
3
+ VERSION = "1.1.0"
4
4
  end
@@ -1,14 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
  module DiscourseTheme
3
3
  class Watcher
4
+ LISTEN_IGNORE_PATTERNS = [%r{migrations/.+/.+\.js}]
5
+
4
6
  def self.return_immediately!
5
7
  @return_immediately = true
6
8
  end
7
9
 
10
+ def self.return_immediately=(val)
11
+ @return_immediately = val
12
+ end
13
+
8
14
  def self.return_immediately?
9
15
  !!@return_immediately
10
16
  end
11
17
 
18
+ def self.subscribe_start(&block)
19
+ @subscribers ||= []
20
+ @subscribers << block
21
+ end
22
+
23
+ def self.call_start_subscribers
24
+ @subscribers&.each(&:call)
25
+ end
26
+
27
+ def self.reset_start_subscribers
28
+ @subscribers = []
29
+ end
30
+
12
31
  def initialize(dir:, uploader:)
13
32
  @dir = dir
14
33
  @uploader = uploader
@@ -16,7 +35,9 @@ module DiscourseTheme
16
35
 
17
36
  def watch
18
37
  listener =
19
- Listen.to(@dir) do |modified, added, removed|
38
+ Listen.to(@dir, ignore: LISTEN_IGNORE_PATTERNS) do |modified, added, removed|
39
+ yield(modified, added, removed) if block_given?
40
+
20
41
  begin
21
42
  if modified.length == 1 && added.length == 0 && removed.length == 0 &&
22
43
  (resolved = resolve_file(modified[0]))
@@ -31,12 +52,14 @@ module DiscourseTheme
31
52
  )
32
53
  else
33
54
  count = modified.length + added.length + removed.length
55
+
34
56
  if count > 1
35
57
  UI.progress "Detected changes in #{count} files, uploading theme"
36
58
  else
37
59
  filename = modified[0] || added[0] || removed[0]
38
60
  UI.progress "Detected changes in #{filename.gsub(@dir, "")}, uploading theme"
39
61
  end
62
+
40
63
  @uploader.upload_full_theme
41
64
  end
42
65
  UI.success "Done! Watching for changes..."
@@ -47,7 +70,8 @@ module DiscourseTheme
47
70
  end
48
71
 
49
72
  listener.start
50
- sleep unless self.class.return_immediately?
73
+ self.class.call_start_subscribers
74
+ sleep 1 while !self.class.return_immediately?
51
75
  end
52
76
 
53
77
  protected
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discourse_theme
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Saffron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-19 00:00:00.000000000 Z
11
+ date: 2024-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitar