discourse_theme 0.3.4 → 0.5.1
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/.github/workflows/ci.yml +34 -5
- data/.gitignore +0 -10
- data/.rubocop.yml +7 -1
- data/Gemfile +1 -0
- data/Guardfile +1 -0
- data/README.md +9 -3
- data/Rakefile +1 -0
- data/bin/discourse_theme +1 -0
- data/discourse_theme.gemspec +11 -9
- data/lib/discourse_theme.rb +2 -0
- data/lib/discourse_theme/cli.rb +41 -60
- data/lib/discourse_theme/client.rb +18 -13
- data/lib/discourse_theme/config.rb +1 -0
- data/lib/discourse_theme/downloader.rb +1 -0
- data/lib/discourse_theme/scaffold.rb +91 -40
- data/lib/discourse_theme/ui.rb +35 -0
- data/lib/discourse_theme/uploader.rb +6 -5
- data/lib/discourse_theme/version.rb +1 -1
- data/lib/discourse_theme/watcher.rb +16 -7
- metadata +76 -48
- data/.travis.yml +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e820c35e9086c9f9e72fd374815e5514279db2ebb9609ef83482490313829586
|
4
|
+
data.tar.gz: 974908e9946ff58d402e94dbbf8bdb375f640c8fe017cbd091d397dc48771758
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d62b36ed3b6c05522af40b2ea7b88dee89b80b0a35262bd99cdecdb6f4dcd2330ba621b65603f703291b3322f73e9c1338f398c136db2c9453f9f76e1d5b2a88
|
7
|
+
data.tar.gz: 21c02268e21bb43a231ed0667ffdc4e9887b467f4da280fd02f63bf358ecad3be43972b69c9c98e8b179907d2b04b7e0ad3c409141f3935c920e43e061527088
|
data/.github/workflows/ci.yml
CHANGED
@@ -1,21 +1,50 @@
|
|
1
1
|
name: CI
|
2
2
|
|
3
3
|
on:
|
4
|
+
pull_request:
|
4
5
|
push:
|
5
6
|
branches:
|
6
7
|
- master
|
7
|
-
|
8
|
-
- v*
|
8
|
+
- main
|
9
9
|
|
10
10
|
jobs:
|
11
|
+
build:
|
12
|
+
runs-on: ubuntu-latest
|
13
|
+
|
14
|
+
strategy:
|
15
|
+
matrix:
|
16
|
+
ruby:
|
17
|
+
- 2.5
|
18
|
+
- 2.6
|
19
|
+
- 2.7
|
20
|
+
- 3.0
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
|
25
|
+
- name: Setup ruby
|
26
|
+
uses: ruby/setup-ruby@v1
|
27
|
+
with:
|
28
|
+
ruby-version: ${{ matrix.ruby }}
|
29
|
+
bundler-cache: true
|
30
|
+
|
31
|
+
- name: Lint
|
32
|
+
run: bundle exec rubocop
|
33
|
+
|
34
|
+
- name: Tests
|
35
|
+
run: bundle exec rake test
|
36
|
+
|
11
37
|
publish:
|
12
|
-
if:
|
38
|
+
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
|
39
|
+
needs: build
|
13
40
|
runs-on: ubuntu-latest
|
14
41
|
|
15
42
|
steps:
|
16
43
|
- uses: actions/checkout@v2
|
17
44
|
|
18
45
|
- name: Release Gem
|
19
|
-
uses:
|
46
|
+
uses: discourse/publish-rubygems-action@v2-beta
|
20
47
|
env:
|
21
|
-
RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
|
48
|
+
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
|
49
|
+
GIT_EMAIL: team@discourse.org
|
50
|
+
GIT_NAME: discoursebot
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Guardfile
CHANGED
data/README.md
CHANGED
@@ -20,11 +20,17 @@ For help run:
|
|
20
20
|
discourse_theme
|
21
21
|
```
|
22
22
|
|
23
|
-
|
23
|
+
### `discourse_theme new PATH`
|
24
24
|
|
25
|
-
|
25
|
+
Creates a new blank theme. The CLI will guide you through the process.
|
26
26
|
|
27
|
-
|
27
|
+
### `discourse_theme download PATH`
|
28
|
+
|
29
|
+
Downloads a theme from the server and stores in the designated directory.
|
30
|
+
|
31
|
+
### `discourse_theme watch PATH`
|
32
|
+
|
33
|
+
Monitors a theme or component for changes. When changed the program will synchronize the theme or component to your Discourse of choice.
|
28
34
|
|
29
35
|
## Contributing
|
30
36
|
|
data/Rakefile
CHANGED
data/bin/discourse_theme
CHANGED
data/discourse_theme.gemspec
CHANGED
@@ -23,18 +23,20 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
24
24
|
spec.require_paths = ["lib"]
|
25
25
|
|
26
|
+
spec.required_ruby_version = '>= 2.5.0'
|
27
|
+
|
28
|
+
spec.add_runtime_dependency "minitar", "~> 0.6"
|
29
|
+
spec.add_runtime_dependency "listen", "~> 3.1"
|
30
|
+
spec.add_runtime_dependency "multipart-post", "~> 2.0"
|
31
|
+
spec.add_runtime_dependency "tty-prompt", "~> 0.18"
|
32
|
+
spec.add_runtime_dependency "rubyzip", "~> 1.2"
|
33
|
+
|
26
34
|
spec.add_development_dependency "bundler", "~> 2.0"
|
27
|
-
spec.add_development_dependency "rake", "~>
|
35
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
28
36
|
spec.add_development_dependency "minitest", "~> 5.0"
|
29
37
|
spec.add_development_dependency "guard", "~> 2.14"
|
30
38
|
spec.add_development_dependency "guard-minitest", "~> 2.4"
|
31
39
|
spec.add_development_dependency "webmock", "~> 3.5"
|
32
|
-
|
33
|
-
spec.
|
34
|
-
spec.add_dependency "listen", "~> 3.1"
|
35
|
-
spec.add_dependency "multipart-post", "~> 2.0"
|
36
|
-
spec.add_dependency "tty-prompt", "~> 0.18"
|
37
|
-
spec.add_dependency "rubyzip", "~> 1.2"
|
38
|
-
|
39
|
-
spec.required_ruby_version = '>= 2.2.0'
|
40
|
+
spec.add_development_dependency "rubocop"
|
41
|
+
spec.add_development_dependency "rubocop-discourse"
|
40
42
|
end
|
data/lib/discourse_theme.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'fileutils'
|
2
3
|
require 'pathname'
|
3
4
|
require 'tempfile'
|
@@ -15,6 +16,7 @@ require 'tty/prompt'
|
|
15
16
|
|
16
17
|
require 'discourse_theme/version'
|
17
18
|
require 'discourse_theme/config'
|
19
|
+
require 'discourse_theme/ui'
|
18
20
|
require 'discourse_theme/cli'
|
19
21
|
require 'discourse_theme/client'
|
20
22
|
require 'discourse_theme/downloader'
|
data/lib/discourse_theme/cli.rb
CHANGED
@@ -1,45 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DiscourseTheme
|
2
3
|
class Cli
|
3
|
-
|
4
|
-
@@prompt = ::TTY::Prompt.new(help_color: :cyan)
|
5
|
-
@@pastel = Pastel.new
|
6
|
-
|
7
|
-
def self.yes?(message)
|
8
|
-
@@prompt.yes?(@@pastel.cyan("? ") + message)
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.ask(message, default: nil)
|
12
|
-
@@prompt.ask(@@pastel.cyan("? ") + message, default: default)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.select(message, options)
|
16
|
-
@@prompt.select(@@pastel.cyan("? ") + message, options)
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.info(message)
|
20
|
-
puts @@pastel.blue("i ") + message
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.progress(message)
|
24
|
-
puts @@pastel.yellow("» ") + message
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.error(message)
|
28
|
-
puts @@pastel.red("✘ #{message}")
|
29
|
-
end
|
30
|
-
|
31
|
-
def self.success(message)
|
32
|
-
puts @@pastel.green("✔ #{message}")
|
33
|
-
end
|
34
|
-
|
35
4
|
SETTINGS_FILE = File.expand_path("~/.discourse_theme")
|
36
5
|
|
37
6
|
def usage
|
38
7
|
puts "Usage: discourse_theme COMMAND [--reset]"
|
39
8
|
puts
|
40
|
-
puts "discourse_theme new DIR
|
41
|
-
puts "discourse_theme download DIR
|
42
|
-
puts "discourse_theme watch DIR
|
9
|
+
puts "discourse_theme new DIR - Creates a new theme in the designated directory"
|
10
|
+
puts "discourse_theme download DIR - Downloads a theme from the server and stores in the designated directory"
|
11
|
+
puts "discourse_theme watch DIR - Watches the theme directory and synchronizes with Discourse"
|
43
12
|
puts
|
44
13
|
puts "Use --reset to change the configuration for a directory"
|
45
14
|
exit 1
|
@@ -62,11 +31,7 @@ module DiscourseTheme
|
|
62
31
|
if command == "new"
|
63
32
|
raise DiscourseTheme::ThemeError.new "'#{dir} is not empty" if Dir.exists?(dir) && !Dir.empty?(dir)
|
64
33
|
DiscourseTheme::Scaffold.generate(dir)
|
65
|
-
|
66
|
-
args[0] = "watch"
|
67
|
-
Cli.progress "Running discourse_theme #{args.join(' ')}"
|
68
|
-
run(args)
|
69
|
-
end
|
34
|
+
watch_theme?(args)
|
70
35
|
elsif command == "watch"
|
71
36
|
raise DiscourseTheme::ThemeError.new "'#{dir} does not exist" unless Dir.exists?(dir)
|
72
37
|
client = DiscourseTheme::Client.new(dir, settings, reset: reset)
|
@@ -80,37 +45,47 @@ module DiscourseTheme
|
|
80
45
|
options["Create and sync with a new theme"] = :create
|
81
46
|
options["Select a different theme"] = :select
|
82
47
|
|
83
|
-
choice =
|
48
|
+
choice = UI.select('How would you like to sync this theme?', options.keys)
|
84
49
|
|
85
50
|
if options[choice] == :create
|
86
51
|
theme_id = nil
|
87
52
|
elsif options[choice] == :select
|
88
53
|
themes = render_theme_list(theme_list)
|
89
|
-
choice =
|
54
|
+
choice = UI.select('Which theme would you like to sync with?', themes)
|
90
55
|
theme_id = extract_theme_id(choice)
|
91
56
|
theme = theme_list.find { |t| t["id"] == theme_id }
|
92
57
|
end
|
93
58
|
|
94
|
-
|
59
|
+
about_json = JSON.parse(File.read(File.join(dir, 'about.json'))) rescue nil
|
60
|
+
already_uploaded = !!theme
|
61
|
+
is_component = theme&.[]("component")
|
62
|
+
component_count = about_json&.[]("components")&.length || 0
|
63
|
+
|
64
|
+
if !already_uploaded && !is_component && component_count > 0
|
95
65
|
options = {}
|
96
66
|
options["Yes"] = :sync
|
97
67
|
options["No"] = :none
|
98
68
|
options = options.sort_by { |_, b| b == components.to_sym ? 0 : 1 }.to_h if components
|
99
|
-
choice =
|
69
|
+
choice = UI.select('Would you like to update child theme components?', options.keys)
|
100
70
|
settings.components = components = options[choice].to_s
|
101
71
|
end
|
102
72
|
|
103
73
|
uploader = DiscourseTheme::Uploader.new(dir: dir, client: client, theme_id: theme_id, components: components)
|
104
74
|
|
105
|
-
|
75
|
+
UI.progress "Uploading theme from #{dir}"
|
106
76
|
settings.theme_id = theme_id = uploader.upload_full_theme
|
107
77
|
|
108
|
-
|
78
|
+
UI.success "Theme uploaded (id:#{theme_id})"
|
79
|
+
UI.info "Preview: #{client.root}/?preview_theme_id=#{theme_id}"
|
80
|
+
if client.is_theme_creator
|
81
|
+
UI.info "Manage: #{client.root}/my/themes"
|
82
|
+
else
|
83
|
+
UI.info "Manage: #{client.root}/admin/customize/themes/#{theme_id}"
|
84
|
+
end
|
109
85
|
watcher = DiscourseTheme::Watcher.new(dir: dir, uploader: uploader)
|
110
86
|
|
111
|
-
|
87
|
+
UI.progress "Watching for changes in #{dir}..."
|
112
88
|
watcher.watch
|
113
|
-
|
114
89
|
elsif command == "download"
|
115
90
|
client = DiscourseTheme::Client.new(dir, settings, reset: reset)
|
116
91
|
downloader = DiscourseTheme::Downloader.new(dir: dir, client: client)
|
@@ -118,33 +93,39 @@ module DiscourseTheme
|
|
118
93
|
FileUtils.mkdir_p dir unless Dir.exists?(dir)
|
119
94
|
raise DiscourseTheme::ThemeError.new "'#{dir} is not empty" unless Dir.empty?(dir)
|
120
95
|
|
121
|
-
|
96
|
+
UI.progress "Loading theme list..."
|
122
97
|
themes = render_theme_list(client.get_themes_list)
|
123
98
|
|
124
|
-
choice =
|
99
|
+
choice = UI.select('Which theme would you like to download?', themes)
|
125
100
|
theme_id = extract_theme_id(choice)
|
126
101
|
|
127
|
-
|
102
|
+
UI.progress "Downloading theme into #{dir}"
|
128
103
|
|
129
104
|
downloader.download_theme(theme_id)
|
130
105
|
settings.theme_id = theme_id
|
131
106
|
|
132
|
-
|
107
|
+
UI.success "Theme downloaded"
|
133
108
|
|
134
|
-
|
135
|
-
args[0] = "watch"
|
136
|
-
Cli.progress "Running discourse_theme #{args.join(' ')}"
|
137
|
-
run(args)
|
138
|
-
end
|
109
|
+
watch_theme?(args)
|
139
110
|
else
|
140
111
|
usage
|
141
112
|
end
|
142
113
|
|
143
|
-
|
114
|
+
UI.progress "Exiting..."
|
144
115
|
rescue DiscourseTheme::ThemeError => e
|
145
|
-
|
116
|
+
UI.error "#{e.message}"
|
146
117
|
rescue Interrupt, TTY::Reader::InputInterrupt => e
|
147
|
-
|
118
|
+
UI.error "Interrupted"
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def watch_theme?(args)
|
124
|
+
if UI.yes?("Would you like to start 'watching' this theme?")
|
125
|
+
args[0] = "watch"
|
126
|
+
UI.progress "Running discourse_theme #{args.join(' ')}"
|
127
|
+
run(args)
|
128
|
+
end
|
148
129
|
end
|
149
130
|
|
150
131
|
def render_theme_list(themes)
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DiscourseTheme
|
2
3
|
class Client
|
3
4
|
THEME_CREATOR_REGEX = /^https:\/\/theme-creator.discourse.org$/i
|
@@ -13,8 +14,8 @@ module DiscourseTheme
|
|
13
14
|
@is_theme_creator = !!(THEME_CREATOR_REGEX =~ @url)
|
14
15
|
|
15
16
|
if !self.class.has_needed_version?(discourse_version, "2.3.0.beta1")
|
16
|
-
|
17
|
-
|
17
|
+
UI.info "discourse_theme is designed for Discourse 2.3.0.beta1 or above"
|
18
|
+
UI.info "download will not function, and syncing destination will be unpredictable"
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
@@ -103,12 +104,16 @@ module DiscourseTheme
|
|
103
104
|
json["about"]["version"]
|
104
105
|
end
|
105
106
|
|
106
|
-
private
|
107
|
-
|
108
107
|
def root
|
109
108
|
@url
|
110
109
|
end
|
111
110
|
|
111
|
+
def is_theme_creator
|
112
|
+
@is_theme_creator
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
112
117
|
def request(request, never_404: false)
|
113
118
|
uri = URI.parse(@url)
|
114
119
|
http = Net::HTTP.new(uri.host, uri.port)
|
@@ -137,26 +142,26 @@ module DiscourseTheme
|
|
137
142
|
def guess_url(settings)
|
138
143
|
url = ENV['DISCOURSE_URL']
|
139
144
|
if url
|
140
|
-
|
145
|
+
UI.progress "Using #{url} from DISCOURSE_URL"
|
141
146
|
end
|
142
147
|
|
143
148
|
if !url && settings.url
|
144
149
|
url = settings.url
|
145
|
-
|
150
|
+
UI.progress "Using #{url} from #{DiscourseTheme::Cli::SETTINGS_FILE}"
|
146
151
|
end
|
147
152
|
|
148
153
|
if !url || @reset
|
149
|
-
url =
|
154
|
+
url = UI.ask("What is the root URL of your Discourse site?", default: url).strip
|
150
155
|
url = "http://#{url}" unless url =~ /^https?:\/\//
|
151
156
|
|
152
157
|
# maybe this is an HTTPS redirect
|
153
158
|
uri = URI.parse(url)
|
154
159
|
if URI::HTTP === uri && uri.port == 80 && is_https_redirect?(url)
|
155
|
-
|
160
|
+
UI.info "Detected that #{url} should be accessed over https"
|
156
161
|
url = url.sub("http", "https")
|
157
162
|
end
|
158
163
|
|
159
|
-
if
|
164
|
+
if UI.yes?("Would you like this site name stored in #{DiscourseTheme::Cli::SETTINGS_FILE}?")
|
160
165
|
settings.url = url
|
161
166
|
else
|
162
167
|
settings.url = nil
|
@@ -169,17 +174,17 @@ module DiscourseTheme
|
|
169
174
|
def guess_api_key(settings)
|
170
175
|
api_key = ENV['DISCOURSE_API_KEY']
|
171
176
|
if api_key
|
172
|
-
|
177
|
+
UI.progress "Using api key from DISCOURSE_API_KEY"
|
173
178
|
end
|
174
179
|
|
175
180
|
if !api_key && settings.api_key
|
176
181
|
api_key = settings.api_key
|
177
|
-
|
182
|
+
UI.progress "Using api key from #{DiscourseTheme::Cli::SETTINGS_FILE}"
|
178
183
|
end
|
179
184
|
|
180
185
|
if !api_key || @reset
|
181
|
-
api_key =
|
182
|
-
if
|
186
|
+
api_key = UI.ask("What is your API key?", default: api_key).strip
|
187
|
+
if UI.yes?("Would you like this API key stored in #{DiscourseTheme::Cli::SETTINGS_FILE}?")
|
183
188
|
settings.api_key = api_key
|
184
189
|
else
|
185
190
|
settings.api_key = nil
|
@@ -1,74 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
1
5
|
module DiscourseTheme
|
2
6
|
class Scaffold
|
3
7
|
|
4
8
|
BLANK_FILES = %w{
|
5
9
|
common/common.scss
|
6
|
-
common/header.html
|
7
|
-
common/after_header.html
|
8
|
-
common/footer.html
|
9
|
-
common/head_tag.html
|
10
|
-
common/body_tag.html
|
11
|
-
common/embedded.scss
|
12
|
-
|
13
|
-
desktop/desktop.scss
|
14
|
-
desktop/header.html
|
15
|
-
desktop/after_header.html
|
16
|
-
desktop/footer.html
|
17
|
-
desktop/head_tag.html
|
18
|
-
desktop/body_tag.html
|
19
|
-
|
20
|
-
mobile/mobile.scss
|
21
|
-
mobile/header.html
|
22
|
-
mobile/after_header.html
|
23
|
-
mobile/footer.html
|
24
|
-
mobile/head_tag.html
|
25
|
-
mobile/body_tag.html
|
26
|
-
|
27
|
-
locales/en.yml
|
28
10
|
|
29
11
|
settings.yml
|
30
12
|
}
|
31
13
|
|
32
|
-
ABOUT_JSON =
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
"license_url": null,
|
37
|
-
"assets": {
|
38
|
-
},
|
39
|
-
"color_schemes": {
|
40
|
-
}
|
14
|
+
ABOUT_JSON = {
|
15
|
+
about_url: nil,
|
16
|
+
license_url: nil,
|
17
|
+
assets: {}
|
41
18
|
}
|
42
|
-
STR
|
43
19
|
|
44
20
|
HELP = <<~STR
|
45
21
|
Are you a bit lost? Be sure to read https://meta.discourse.org/t/how-to-develop-custom-themes/60848
|
46
22
|
STR
|
47
23
|
|
48
24
|
GIT_IGNORE = <<~STR
|
49
|
-
|
50
|
-
|
51
|
-
|
25
|
+
.discourse-site
|
26
|
+
node_modules
|
27
|
+
HELP
|
28
|
+
STR
|
29
|
+
|
30
|
+
API_INITIALIZER = <<~STR
|
31
|
+
import { apiInitializer } from "discourse/lib/api";
|
32
|
+
|
33
|
+
export default apiInitializer("0.11.1", api => {
|
34
|
+
console.log("hello world from api initializer!");
|
35
|
+
});
|
36
|
+
STR
|
37
|
+
|
38
|
+
PACKAGE_JSON = <<~STR
|
39
|
+
{
|
40
|
+
"author": "#AUTHOR",
|
41
|
+
"license": "MIT",
|
42
|
+
"devDependencies": {
|
43
|
+
"eslint-config-discourse": "latest"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
STR
|
47
|
+
|
48
|
+
ESLINT_RC = <<~STR
|
49
|
+
{
|
50
|
+
"extends": "eslint-config-discourse"
|
51
|
+
}
|
52
|
+
STR
|
53
|
+
|
54
|
+
TEMPLATE_LINT_RC = <<~STR
|
55
|
+
module.exports = {
|
56
|
+
plugins: ["ember-template-lint-plugin-discourse"],
|
57
|
+
extends: "discourse:recommended",
|
58
|
+
};
|
59
|
+
STR
|
60
|
+
|
61
|
+
EN_YML = <<~YAML
|
62
|
+
en:
|
63
|
+
theme_metadata:
|
64
|
+
description: "#DESCRIPTION"
|
65
|
+
YAML
|
52
66
|
|
53
67
|
def self.generate(dir)
|
54
|
-
|
68
|
+
UI.progress "Generating a scaffold theme at #{dir}"
|
55
69
|
|
56
|
-
name =
|
70
|
+
name = UI.ask("What would you like to call your theme?").strip
|
71
|
+
is_component = UI.yes?("Is this a component?")
|
57
72
|
|
58
73
|
FileUtils.mkdir_p dir
|
59
74
|
Dir.chdir dir do
|
60
|
-
|
75
|
+
UI.info "Creating about.json"
|
76
|
+
about_template = ABOUT_JSON.dup
|
77
|
+
about_template[:name] = name
|
78
|
+
if is_component
|
79
|
+
about_template[:component] = true
|
80
|
+
else
|
81
|
+
about_template[:color_schemes] = {}
|
82
|
+
end
|
83
|
+
File.write('about.json', JSON.pretty_generate(about_template))
|
84
|
+
|
85
|
+
UI.info "Creating HELP"
|
61
86
|
File.write('HELP', HELP)
|
87
|
+
|
88
|
+
UI.info "Creating package.json"
|
89
|
+
author = UI.ask("Who is authoring the theme?").strip
|
90
|
+
File.write('package.json', PACKAGE_JSON.sub("#AUTHOR", author))
|
91
|
+
|
92
|
+
UI.info "Creating .template-lintrc.js"
|
93
|
+
File.write('.template-lintrc.js', TEMPLATE_LINT_RC)
|
94
|
+
|
95
|
+
UI.info "Creating .eslintrc"
|
96
|
+
File.write('.eslintrc', ESLINT_RC)
|
97
|
+
|
98
|
+
UI.info "Creating .gitignore"
|
62
99
|
File.write('.gitignore', GIT_IGNORE)
|
63
100
|
|
101
|
+
locale = "locales/en.yml"
|
102
|
+
UI.info "Creating #{locale}"
|
103
|
+
FileUtils.mkdir_p(File.dirname(locale))
|
104
|
+
description = UI.ask("How would you describe this theme?").strip
|
105
|
+
File.write(locale, EN_YML.sub("#DESCRIPTION", description))
|
106
|
+
|
107
|
+
initializer = "javascripts/discourse/api-initializers/#{name}.js"
|
108
|
+
UI.info "Creating #{initializer}"
|
109
|
+
FileUtils.mkdir_p(File.dirname(initializer))
|
110
|
+
File.write(initializer, API_INITIALIZER)
|
111
|
+
|
64
112
|
BLANK_FILES.each do |f|
|
65
|
-
|
113
|
+
UI.info "Creating #{f}"
|
66
114
|
FileUtils.mkdir_p File.dirname(f)
|
67
115
|
FileUtils.touch f
|
68
116
|
end
|
69
117
|
|
70
|
-
|
118
|
+
UI.info "Initializing git repo"
|
71
119
|
puts `git init .`
|
120
|
+
|
121
|
+
UI.info "Installing dependencies"
|
122
|
+
puts `yarn`
|
72
123
|
end
|
73
124
|
end
|
74
125
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module DiscourseTheme
|
3
|
+
class UI
|
4
|
+
@@prompt = ::TTY::Prompt.new(help_color: :cyan)
|
5
|
+
@@pastel = Pastel.new
|
6
|
+
|
7
|
+
def self.yes?(message)
|
8
|
+
@@prompt.yes?(@@pastel.cyan("? ") + message)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.ask(message, default: nil)
|
12
|
+
@@prompt.ask(@@pastel.cyan("? ") + message, default: default)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.select(message, options)
|
16
|
+
@@prompt.select(@@pastel.cyan("? ") + message, options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.info(message)
|
20
|
+
puts @@pastel.blue("i ") + message
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.progress(message)
|
24
|
+
puts @@pastel.yellow("» ") + message
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.error(message)
|
28
|
+
puts @@pastel.red("✘ #{message}")
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.success(message)
|
32
|
+
puts @@pastel.green("✔ #{message}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DiscourseTheme
|
2
3
|
class Uploader
|
3
4
|
|
@@ -33,9 +34,9 @@ module DiscourseTheme
|
|
33
34
|
json["theme"]["theme_fields"].each do |row|
|
34
35
|
if (error = row["error"]) && error.length > 0
|
35
36
|
count += 1
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
UI.error ""
|
38
|
+
UI.error "Error in #{row["target"]} #{row["name"]}: #{row["error"]}"
|
39
|
+
UI.error ""
|
39
40
|
end
|
40
41
|
end
|
41
42
|
count
|
@@ -58,7 +59,7 @@ module DiscourseTheme
|
|
58
59
|
response = @client.update_theme(@theme_id, args)
|
59
60
|
json = JSON.parse(response.body)
|
60
61
|
if diagnose_errors(json) != 0
|
61
|
-
|
62
|
+
UI.error "(end of errors)"
|
62
63
|
end
|
63
64
|
end
|
64
65
|
|
@@ -72,7 +73,7 @@ module DiscourseTheme
|
|
72
73
|
json = JSON.parse(response.body)
|
73
74
|
@theme_id = json["theme"]["id"]
|
74
75
|
if diagnose_errors(json) != 0
|
75
|
-
|
76
|
+
UI.error "(end of errors)"
|
76
77
|
end
|
77
78
|
@theme_id
|
78
79
|
end
|
@@ -1,5 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module DiscourseTheme
|
2
3
|
class Watcher
|
4
|
+
def self.return_immediately!
|
5
|
+
@return_immediately = true
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.return_immediately?
|
9
|
+
!!@return_immediately
|
10
|
+
end
|
11
|
+
|
3
12
|
def initialize(dir:, uploader:)
|
4
13
|
@dir = dir
|
5
14
|
@uploader = uploader
|
@@ -14,7 +23,7 @@ module DiscourseTheme
|
|
14
23
|
(resolved = resolve_file(modified[0]))
|
15
24
|
|
16
25
|
target, name, type_id = resolved
|
17
|
-
|
26
|
+
UI.progress "Fast updating #{target}.scss"
|
18
27
|
|
19
28
|
@uploader.upload_theme_field(
|
20
29
|
target: target,
|
@@ -25,22 +34,22 @@ module DiscourseTheme
|
|
25
34
|
else
|
26
35
|
count = modified.length + added.length + removed.length
|
27
36
|
if count > 1
|
28
|
-
|
37
|
+
UI.progress "Detected changes in #{count} files, uploading theme"
|
29
38
|
else
|
30
39
|
filename = modified[0] || added[0] || removed[0]
|
31
|
-
|
40
|
+
UI.progress "Detected changes in #{filename.gsub(@dir, '')}, uploading theme"
|
32
41
|
end
|
33
42
|
@uploader.upload_full_theme
|
34
43
|
end
|
35
|
-
|
44
|
+
UI.success "Done! Watching for changes..."
|
36
45
|
rescue DiscourseTheme::ThemeError => e
|
37
|
-
|
38
|
-
|
46
|
+
UI.error "#{e.message}"
|
47
|
+
UI.progress "Watching for changes..."
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
51
|
listener.start
|
43
|
-
sleep
|
52
|
+
sleep unless self.class.return_immediately?
|
44
53
|
end
|
45
54
|
|
46
55
|
protected
|
metadata
CHANGED
@@ -1,169 +1,197 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discourse_theme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Saffron
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-05-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: minitar
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
20
|
-
type: :
|
19
|
+
version: '0.6'
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: listen
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
type: :
|
33
|
+
version: '3.1'
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: multipart-post
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
type: :
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: tty-prompt
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
62
|
-
type: :
|
61
|
+
version: '0.18'
|
62
|
+
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0.18'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rubyzip
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '2
|
76
|
-
type: :
|
75
|
+
version: '1.2'
|
76
|
+
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '2
|
82
|
+
version: '1.2'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: bundler
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '2.0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '2.0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rake
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0
|
104
|
-
type: :
|
103
|
+
version: '13.0'
|
104
|
+
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
108
|
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0
|
110
|
+
version: '13.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: minitest
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
118
|
-
type: :
|
117
|
+
version: '5.0'
|
118
|
+
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '5.0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: guard
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '2.
|
132
|
-
type: :
|
131
|
+
version: '2.14'
|
132
|
+
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
136
|
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '2.
|
138
|
+
version: '2.14'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: guard-minitest
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - "~>"
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
146
|
-
type: :
|
145
|
+
version: '2.4'
|
146
|
+
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
152
|
+
version: '2.4'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
154
|
+
name: webmock
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
156
156
|
requirements:
|
157
157
|
- - "~>"
|
158
158
|
- !ruby/object:Gem::Version
|
159
|
-
version: '
|
160
|
-
type: :
|
159
|
+
version: '3.5'
|
160
|
+
type: :development
|
161
161
|
prerelease: false
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
163
163
|
requirements:
|
164
164
|
- - "~>"
|
165
165
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
166
|
+
version: '3.5'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rubocop
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rubocop-discourse
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ">="
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
167
195
|
description: CLI helper for creating Discourse themes
|
168
196
|
email:
|
169
197
|
- sam.saffron@gmail.com
|
@@ -175,7 +203,6 @@ files:
|
|
175
203
|
- ".github/workflows/ci.yml"
|
176
204
|
- ".gitignore"
|
177
205
|
- ".rubocop.yml"
|
178
|
-
- ".travis.yml"
|
179
206
|
- CODE_OF_CONDUCT.md
|
180
207
|
- Gemfile
|
181
208
|
- Guardfile
|
@@ -190,6 +217,7 @@ files:
|
|
190
217
|
- lib/discourse_theme/config.rb
|
191
218
|
- lib/discourse_theme/downloader.rb
|
192
219
|
- lib/discourse_theme/scaffold.rb
|
220
|
+
- lib/discourse_theme/ui.rb
|
193
221
|
- lib/discourse_theme/uploader.rb
|
194
222
|
- lib/discourse_theme/version.rb
|
195
223
|
- lib/discourse_theme/watcher.rb
|
@@ -205,14 +233,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
205
233
|
requirements:
|
206
234
|
- - ">="
|
207
235
|
- !ruby/object:Gem::Version
|
208
|
-
version: 2.
|
236
|
+
version: 2.5.0
|
209
237
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
210
238
|
requirements:
|
211
239
|
- - ">="
|
212
240
|
- !ruby/object:Gem::Version
|
213
241
|
version: '0'
|
214
242
|
requirements: []
|
215
|
-
rubygems_version: 3.
|
243
|
+
rubygems_version: 3.1.6
|
216
244
|
signing_key:
|
217
245
|
specification_version: 4
|
218
246
|
summary: CLI helper for creating Discourse themes
|