howzit 2.0.3 → 2.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +18 -0
- data/bin/howzit +2 -2
- data/howzit.gemspec +1 -0
- data/lib/howzit/buildnote.rb +10 -0
- data/lib/howzit/hash.rb +20 -0
- data/lib/howzit/task.rb +5 -3
- data/lib/howzit/topic.rb +95 -59
- data/lib/howzit/version.rb +1 -1
- data/lib/howzit.rb +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99e8c7d76873acb118176001c8ff721dc9ed193587d5d9c7ef6875f3a537bf56
|
4
|
+
data.tar.gz: 6b969ea8400973fd90ac096d4a7a22a98fbcfee63e478f6d8963d37d3f1b5ce1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 216b6dd76366a6add390d8604eb7fed21a074d607ec86347a4b06b410c3a3ba91c0eb56391a31dc40d90ae7c434c1812bd08e137f92350e487cd8e6629fdb836
|
7
|
+
data.tar.gz: 17fcb459b9ba39dd8fb2647a9072defe684887f7c53decb940f3f606dd36c98c3fdf3868eb7cb42bcf5d969e22c82b014e4510b7c1baed878c79f402003c90fe
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,32 @@
|
|
1
|
+
### 2.0.6
|
2
|
+
|
3
|
+
2022-08-04 11:08
|
4
|
+
|
5
|
+
### 2.0.5
|
6
|
+
|
7
|
+
2022-08-04 10:50
|
8
|
+
|
9
|
+
#### NEW
|
10
|
+
|
11
|
+
- Make any task optional when running by adding a ? (@open?(...))
|
12
|
+
- Optional tasks default to yes when you hit enter, invert by using a ! (@open?!(...)) to make default "no"
|
13
|
+
|
14
|
+
#### FIXED
|
15
|
+
|
16
|
+
- Replace escaped newlines in task list output so that they don't trigger a newline in the shell
|
17
|
+
|
18
|
+
### 2.0.4
|
19
|
+
|
20
|
+
2022-08-04 06:25
|
21
|
+
|
22
|
+
#### IMPROVED
|
23
|
+
|
24
|
+
- Ask to open new buildnote for editing after creation
|
25
|
+
|
26
|
+
#### FIXED
|
27
|
+
|
28
|
+
- Loop when creating new buildnote
|
29
|
+
|
1
30
|
### 2.0.3
|
2
31
|
|
3
32
|
2022-08-04 05:31
|
data/README.md
CHANGED
@@ -94,6 +94,18 @@ You can include commands that can be executed by howzit. Commands start at the b
|
|
94
94
|
|
95
95
|
A block defined between @after and @end markers will be displayed after a topic is run. Use it to remind yourself of additional tasks after automated ones have been executed. The content between these markers is still included when viewing the topic, but the tags themselves do not show up in output.
|
96
96
|
|
97
|
+
### Adding Titles
|
98
|
+
|
99
|
+
When displaying a topic, howzit can display a title instead of the contents of a directive. Any text after the closing parenthesis will be considered the task title.
|
100
|
+
|
101
|
+
```
|
102
|
+
@run(~/scripts/deploy.sh) Deploy script
|
103
|
+
```
|
104
|
+
|
105
|
+
When the topic is displayed, it will now show "Run Deploy script" instead of the path to the script. This can be overridden with the `--show-code` flag, which will instead show the contents of the directive.
|
106
|
+
|
107
|
+
For adding titles to run blocks (fenced code), see below.
|
108
|
+
|
97
109
|
### Run blocks (embedded scripts)
|
98
110
|
|
99
111
|
For longer scripts you can write shell scripts and then call them with `@run(myscript.sh)`. For those in-between cases where you need a few commands but aren't motivated to create a separate file, you can use fenced code blocks with `run` as the language.
|
@@ -117,6 +129,12 @@ Example:
|
|
117
129
|
|
118
130
|
Multiple blocks can be included in a topic. @commands take priority over code blocks and will be run first if they exist in the same topic.
|
119
131
|
|
132
|
+
### Requiring Confirmation
|
133
|
+
|
134
|
+
You can have howzit confirm whether to execute an @command at runtime by including a question mark to the directive, e.g. `@run?(...)`. This will present a yes/no dialog before running the directive, with a default response of "yes" if you just hit return. To make the default response "no," add an exclamation point, e.g. `@run?!(...)`.
|
135
|
+
|
136
|
+
This works for any directive, including `@include`, and run blocks, which can use `run?` as the language specifier.
|
137
|
+
|
120
138
|
### Variables
|
121
139
|
|
122
140
|
When running commands in a topic, you can use a double dash (`--`) in the command line (surrounded by spaces) and anything after it will be interpreted as shell arguments. These can be used in commands with `$` placeholders. `$1` represents the first argument, counting up from there. Use `$@` to pass all arguments as a shell-escaped string.
|
data/bin/howzit
CHANGED
@@ -114,8 +114,8 @@ OptionParser.new do |opts|
|
|
114
114
|
raise 'Argument must be KEY=VALUE' unless key =~ /\S=\S/
|
115
115
|
|
116
116
|
parts = key.split(/=/)
|
117
|
-
k = parts
|
118
|
-
v = parts
|
117
|
+
k = parts.shift.sub(/^:/, '')
|
118
|
+
v = parts.join(' ')
|
119
119
|
|
120
120
|
if Howzit.options.key?(k.to_sym)
|
121
121
|
Howzit.options[k.to_sym] = v.to_config_value(Howzit.options[k.to_sym])
|
data/howzit.gemspec
CHANGED
data/lib/howzit/buildnote.rb
CHANGED
@@ -106,9 +106,11 @@ module Howzit
|
|
106
106
|
end
|
107
107
|
|
108
108
|
title = File.basename(Dir.pwd)
|
109
|
+
# prompt = TTY::Prompt.new
|
109
110
|
if default
|
110
111
|
input = title
|
111
112
|
else
|
113
|
+
# title = prompt.ask("{bw}Project name:{x}".c, default: title)
|
112
114
|
printf "{bw}Project name {xg}[#{title}]{bw}: {x}".c
|
113
115
|
input = $stdin.gets.chomp
|
114
116
|
title = input unless input.empty?
|
@@ -166,6 +168,14 @@ module Howzit
|
|
166
168
|
f.puts note
|
167
169
|
puts "{by}Build notes for #{title} written to #{fname}".c
|
168
170
|
end
|
171
|
+
|
172
|
+
if File.exist?(fname) && !default
|
173
|
+
res = Prompt.yn("{bg}Do you want to open {bw}#{file} {bg}for editing?{x}".c, default: false)
|
174
|
+
|
175
|
+
edit_note if res
|
176
|
+
end
|
177
|
+
|
178
|
+
Process.exit 0
|
169
179
|
end
|
170
180
|
|
171
181
|
def note_file
|
data/lib/howzit/hash.rb
CHANGED
@@ -32,4 +32,24 @@ class ::Hash
|
|
32
32
|
def deep_thaw!
|
33
33
|
replace deep_thaw
|
34
34
|
end
|
35
|
+
|
36
|
+
# Turn all keys into string
|
37
|
+
#
|
38
|
+
# Return a copy of the hash where all its keys are strings
|
39
|
+
def stringify_keys
|
40
|
+
each_with_object({}) { |(k, v), hsh| hsh[k.to_s] = v.is_a?(Hash) ? v.stringify_keys : v }
|
41
|
+
end
|
42
|
+
|
43
|
+
def stringify_keys!
|
44
|
+
replace stringify_keys
|
45
|
+
end
|
46
|
+
|
47
|
+
# Turn all keys into symbols
|
48
|
+
def symbolize_keys
|
49
|
+
each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
|
50
|
+
end
|
51
|
+
|
52
|
+
def symbolize_keys!
|
53
|
+
replace symbolize_keys
|
54
|
+
end
|
35
55
|
end
|
data/lib/howzit/task.rb
CHANGED
@@ -2,13 +2,15 @@
|
|
2
2
|
|
3
3
|
module Howzit
|
4
4
|
class Task
|
5
|
-
attr_reader :type, :title, :action, :parent
|
5
|
+
attr_reader :type, :title, :action, :parent, :optional, :default
|
6
6
|
|
7
|
-
def initialize(type, title, action, parent = nil)
|
7
|
+
def initialize(type, title, action, parent = nil, optional: false, default: true)
|
8
8
|
@type = type
|
9
9
|
@title = title
|
10
10
|
@action = action.render_arguments
|
11
11
|
@parent = parent
|
12
|
+
@optional = optional
|
13
|
+
@default = default
|
12
14
|
end
|
13
15
|
|
14
16
|
def to_s
|
@@ -16,7 +18,7 @@ module Howzit
|
|
16
18
|
end
|
17
19
|
|
18
20
|
def to_list
|
19
|
-
" * #{@type}: #{@title}"
|
21
|
+
" * #{@type}: #{@title.gsub(/\\n/, '\n')}"
|
20
22
|
end
|
21
23
|
end
|
22
24
|
end
|
data/lib/howzit/topic.rb
CHANGED
@@ -28,12 +28,19 @@ module Howzit
|
|
28
28
|
if @tasks.count.positive?
|
29
29
|
unless @prereqs.empty?
|
30
30
|
puts @prereqs.join("\n\n")
|
31
|
-
res = Prompt.yn('This
|
31
|
+
res = Prompt.yn('This topic has prerequisites, have they been met?', default: true)
|
32
32
|
Process.exit 1 unless res
|
33
33
|
|
34
34
|
end
|
35
35
|
|
36
36
|
@tasks.each do |task|
|
37
|
+
if task.optional
|
38
|
+
q = %({bg}#{task.type.to_s.capitalize} {xw}"{bw}#{task.title}{xw}"{x}).c
|
39
|
+
res = Prompt.yn(q, default: task.default)
|
40
|
+
next unless res
|
41
|
+
|
42
|
+
end
|
43
|
+
|
37
44
|
if task.type == :block
|
38
45
|
warn "{bg}Running block {bw}#{title}{x}".c if Howzit.options[:log_level] < 2
|
39
46
|
block = task.action
|
@@ -74,7 +81,7 @@ module Howzit
|
|
74
81
|
end
|
75
82
|
end
|
76
83
|
else
|
77
|
-
warn "{r}--run: No {br}@directive{xr} found in {bw}#{
|
84
|
+
warn "{r}--run: No {br}@directive{xr} found in {bw}#{@title}{x}".c
|
78
85
|
end
|
79
86
|
output.push("{bm}Ran #{tasks} #{tasks == 1 ? 'task' : 'tasks'}{x}".c) if Howzit.options[:log_level] < 2 && !nested
|
80
87
|
|
@@ -139,49 +146,57 @@ module Howzit
|
|
139
146
|
output.push('')
|
140
147
|
end
|
141
148
|
topic = @content.dup
|
142
|
-
topic.gsub!(/(?mi)^(`{3,})run *([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run \
|
149
|
+
topic.gsub!(/(?mi)^(`{3,})run([?!]*) *([^\n]*)[\s\S]*?\n\1\s*$/, '@@@run\2 \3') unless Howzit.options[:show_all_code]
|
143
150
|
topic.split(/\n/).each do |l|
|
144
151
|
case l
|
145
152
|
when /@(before|after|prereq|end)/
|
146
153
|
next
|
147
|
-
when /@include
|
148
|
-
m = Regexp.last_match
|
149
|
-
matches = Howzit.buildnote.find_topic(m[
|
154
|
+
when /@include(?<optional>[!?]{1,2})?\((?<action>.*?)\)/
|
155
|
+
m = Regexp.last_match.named_captures.symbolize_keys
|
156
|
+
matches = Howzit.buildnote.find_topic(m[:action])
|
150
157
|
unless matches.empty?
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
158
|
+
i_topic = matches[0]
|
159
|
+
rule = '{kKd}'
|
160
|
+
color = '{Kyd}'
|
161
|
+
option = if i_topic.tasks.empty?
|
162
|
+
''
|
163
|
+
else
|
164
|
+
optional = m[:optional] =~ /[?!]+/ ? true : false
|
165
|
+
default = m[:optional] =~ /!/ ? false : true
|
166
|
+
if optional
|
167
|
+
default ? " {xKk}[{gbK}Y{xKk}/{dbwK}n{xKk}]{x}#{color}".c : " {xKk}[{dbwK}y{xKk}/{bgK}N{xKk}]{x}#{color}".c
|
168
|
+
else
|
169
|
+
''
|
170
|
+
end
|
171
|
+
end
|
172
|
+
title = "#{opt[:single] ? 'From' : 'Include'} #{i_topic.title}#{option}:"
|
173
|
+
options = { color: color, hr: '.', border: rule }
|
174
|
+
unless Howzit.inclusions.include?(i_topic)
|
175
|
+
output.push("#{'> ' * @nest_level}#{title}".format_header(options))
|
162
176
|
end
|
163
177
|
|
164
|
-
if opt[:single]
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
@nest_level -= 1
|
172
|
-
end
|
173
|
-
unless Howzit.inclusions.include?(matches[0])
|
174
|
-
output.push("#{'> ' * @nest_level}...".format_header({ color: color, hr: '.', border: rule }))
|
175
|
-
end
|
178
|
+
if opt[:single] && Howzit.inclusions.include?(i_topic)
|
179
|
+
output.push("#{'> ' * @nest_level}#{title} included above".format_header(options))
|
180
|
+
elsif opt[:single]
|
181
|
+
@nest_level += 1
|
182
|
+
output.concat(i_topic.print_out({ single: true, header: false }))
|
183
|
+
output.push("#{'> ' * @nest_level}...".format_header(options))
|
184
|
+
@nest_level -= 1
|
176
185
|
end
|
177
|
-
Howzit.inclusions.push(
|
186
|
+
Howzit.inclusions.push(i_topic)
|
178
187
|
end
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
188
|
+
when /@(?<cmd>run|copy|open|url|include)(?<optional>[?!]{1,2})?\((?<action>.*?)\) *(?<title>.*?)$/
|
189
|
+
m = Regexp.last_match.named_captures.symbolize_keys
|
190
|
+
cmd = m[:cmd]
|
191
|
+
obj = m[:action]
|
192
|
+
title = m[:title].empty? ? obj : m[:title].strip
|
193
|
+
optional = m[:optional] =~ /[?!]+/ ? true : false
|
194
|
+
default = m[:optional] =~ /!/ ? false : true
|
195
|
+
option = if optional
|
196
|
+
default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
|
197
|
+
else
|
198
|
+
''
|
199
|
+
end
|
185
200
|
icon = case cmd
|
186
201
|
when 'run'
|
187
202
|
"\u{25B6}"
|
@@ -191,14 +206,28 @@ module Howzit
|
|
191
206
|
"\u{279A}"
|
192
207
|
end
|
193
208
|
|
194
|
-
output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\n')}{x}".c)
|
195
|
-
when /(
|
196
|
-
m = Regexp.last_match
|
197
|
-
|
209
|
+
output.push("{bmK}#{icon} {bwK}#{title.gsub(/\\n/, '\n')}{x}#{option}".c)
|
210
|
+
when /(?<fence>`{3,})run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
|
211
|
+
m = Regexp.last_match.named_captures.symbolize_keys
|
212
|
+
optional = m[:optional] =~ /[?!]+/ ? true : false
|
213
|
+
default = m[:optional] =~ /!/ ? false : true
|
214
|
+
option = if optional
|
215
|
+
default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
|
216
|
+
else
|
217
|
+
''
|
218
|
+
end
|
219
|
+
desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
|
198
220
|
output.push("{bmK}\u{25B6} {bwK}#{desc}{x}\n```".c)
|
199
|
-
when /@@@run *(
|
200
|
-
m = Regexp.last_match
|
201
|
-
|
221
|
+
when /@@@run(?<optional>[!?]{1,2})? *(?<title>.*?)$/i
|
222
|
+
m = Regexp.last_match.named_captures.symbolize_keys
|
223
|
+
optional = m[:optional] =~ /[?!]+/ ? true : false
|
224
|
+
default = m[:optional] =~ /!/ ? false : true
|
225
|
+
option = if optional
|
226
|
+
default ? ' {xk}[{g}Y{xk}/{dbw}n{xk}]{x}'.c : ' {xk}[{dbw}y{xk}/{g}N{xk}]{x}'.c
|
227
|
+
else
|
228
|
+
''
|
229
|
+
end
|
230
|
+
desc = m[:title].length.positive? ? "Block: #{m[:title]}#{option}" : "Code Block#{option}"
|
202
231
|
output.push("{bmK}\u{25B6} {bwK}#{desc}{x}".c)
|
203
232
|
else
|
204
233
|
l.wrap!(Howzit.options[:wrap]) if Howzit.options[:wrap].positive?
|
@@ -215,18 +244,27 @@ module Howzit
|
|
215
244
|
@prereqs = @content.scan(/(?<=@before\n).*?(?=\n@end)/im).map(&:strip)
|
216
245
|
@postreqs = @content.scan(/(?<=@after\n).*?(?=\n@end)/im).map(&:strip)
|
217
246
|
|
218
|
-
rx = /(
|
219
|
-
|
247
|
+
rx = /(?mix)(?:
|
248
|
+
@(?<cmd>include|run|copy|open|url)(?<optional>[!?]{1,2})?\((?<action>[^)]*?)\)(?<title>[^\n]+)?
|
249
|
+
|(?<fence>`{3,})run(?<optional2>[!?]{1,2})?(?<title2>[^\n]+)?(?<block>.*?)\k<fence>
|
250
|
+
)/
|
251
|
+
matches = []
|
252
|
+
@content.scan(rx) { matches << Regexp.last_match }
|
253
|
+
matches.each do |m|
|
254
|
+
c = m.named_captures.symbolize_keys
|
220
255
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
256
|
+
if c[:cmd].nil?
|
257
|
+
optional = c[:optional2] =~ /[?!]{1,2}/ ? true : false
|
258
|
+
default = c[:optional2] =~ /!/ ? false : true
|
259
|
+
title = c[:title2].nil? ? '' : c[:title2].strip
|
260
|
+
block = c[:block]&.strip
|
261
|
+
runnable << Howzit::Task.new(:block, title, block, optional: optional, default: default)
|
226
262
|
else
|
227
|
-
cmd = c[
|
228
|
-
|
229
|
-
|
263
|
+
cmd = c[:cmd]
|
264
|
+
optional = c[:optional] =~ /[?!]{1,2}/ ? true : false
|
265
|
+
default = c[:optional] =~ /!/ ? false : true
|
266
|
+
obj = c[:action]
|
267
|
+
title = c[:title].nil? ? obj : c[:title].strip
|
230
268
|
|
231
269
|
case cmd
|
232
270
|
when /include/i
|
@@ -239,17 +277,15 @@ module Howzit
|
|
239
277
|
# end
|
240
278
|
# runnable.concat(tasks)
|
241
279
|
# end
|
242
|
-
title
|
243
|
-
runnable << Howzit::Task.new(:include, title, obj)
|
280
|
+
runnable << Howzit::Task.new(:include, title, obj, optional: optional, default: default)
|
244
281
|
when /run/i
|
245
|
-
title = c[3] || obj
|
246
282
|
# warn "{bg}Running {bw}#{obj}{x}".c if Howzit.options[:log_level] < 2
|
247
|
-
runnable << Howzit::Task.new(:run, title, obj)
|
283
|
+
runnable << Howzit::Task.new(:run, title, obj, optional: optional, default: default)
|
248
284
|
when /copy/i
|
249
285
|
# warn "{bg}Copied {bw}#{obj}{bg} to clipboard{x}".c if Howzit.options[:log_level] < 2
|
250
|
-
runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj))
|
286
|
+
runnable << Howzit::Task.new(:copy, title, Shellwords.escape(obj), optional: optional, default: default)
|
251
287
|
when /open|url/i
|
252
|
-
runnable << Howzit::Task.new(:open, title, obj)
|
288
|
+
runnable << Howzit::Task.new(:open, title, obj, optional: optional, default: default)
|
253
289
|
end
|
254
290
|
end
|
255
291
|
end
|
data/lib/howzit/version.rb
CHANGED
data/lib/howzit.rb
CHANGED