howzit 2.1.35 → 2.1.38

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.
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'fileutils'
5
+ require 'tmpdir'
6
+
7
+ describe 'Stack mode and directory execution' do
8
+ let(:temp_dir) { File.expand_path(Dir.mktmpdir('howzit_stack_test')) }
9
+ let(:parent_dir) { File.expand_path(File.join(temp_dir, 'parent')) }
10
+ let(:current_dir) { File.expand_path(File.join(temp_dir, 'parent', 'current')) }
11
+
12
+ before do
13
+ # Create directory structure
14
+ FileUtils.mkdir_p(parent_dir)
15
+ FileUtils.mkdir_p(current_dir)
16
+
17
+ # Create parent build note
18
+ parent_note = <<~EONOTE
19
+ # Parent Build Note
20
+
21
+ ## Parent Topic
22
+
23
+ @run(echo "parent_task") Parent task
24
+ EONOTE
25
+ File.write(File.join(parent_dir, 'buildnotes.md'), parent_note)
26
+
27
+ # Create current build note
28
+ current_note = <<~EONOTE
29
+ # Current Build Note
30
+
31
+ ## Current Topic
32
+
33
+ @run(echo "current_task") Current task
34
+ EONOTE
35
+ File.write(File.join(current_dir, 'buildnotes.md'), current_note)
36
+
37
+ # Change to current directory
38
+ Dir.chdir(current_dir)
39
+ end
40
+
41
+ after do
42
+ Dir.chdir(Dir.tmpdir) # Change out of test directory
43
+ FileUtils.rm_rf(temp_dir) if Dir.exist?(temp_dir)
44
+ Howzit.instance_variable_set(:@buildnote, nil)
45
+ Howzit.options[:stack] = false
46
+ end
47
+
48
+ describe 'source_file tracking' do
49
+ context 'without --stack mode' do
50
+ before do
51
+ Howzit.options[:stack] = false
52
+ end
53
+
54
+ it 'sets source_file to current directory build note' do
55
+ buildnote = Howzit::BuildNote.new
56
+ topic = buildnote.find_topic('Current Topic')[0]
57
+
58
+ expected_file = File.expand_path(File.join(current_dir, 'buildnotes.md'))
59
+ actual_file = File.expand_path(topic.source_file)
60
+ # Use realpath to handle macOS /var -> /private/var symlink
61
+ expect(File.realpath(actual_file)).to eq(File.realpath(expected_file))
62
+ expect(File.realpath(File.dirname(actual_file))).to eq(File.realpath(current_dir))
63
+ end
64
+
65
+ it 'does not include parent directory topics' do
66
+ buildnote = Howzit::BuildNote.new
67
+ matches = buildnote.find_topic('Parent Topic')
68
+
69
+ expect(matches).to be_empty
70
+ end
71
+ end
72
+
73
+ context 'with --stack mode' do
74
+ before do
75
+ Howzit.options[:stack] = true
76
+ end
77
+
78
+ it 'sets source_file correctly for current directory topic' do
79
+ buildnote = Howzit::BuildNote.new
80
+ topic = buildnote.find_topic('Current Topic')[0]
81
+
82
+ expected_file = File.expand_path(File.join(current_dir, 'buildnotes.md'))
83
+ actual_file = File.expand_path(topic.source_file)
84
+ # Use realpath to handle macOS /var -> /private/var symlink
85
+ expect(File.realpath(actual_file)).to eq(File.realpath(expected_file))
86
+ expect(File.realpath(File.dirname(actual_file))).to eq(File.realpath(current_dir))
87
+ end
88
+
89
+ it 'sets source_file correctly for parent directory topic' do
90
+ buildnote = Howzit::BuildNote.new
91
+ topic = buildnote.find_topic('Parent Topic')[0]
92
+
93
+ expected_file = File.expand_path(File.join(parent_dir, 'buildnotes.md'))
94
+ actual_file = File.expand_path(topic.source_file)
95
+ # Use realpath to handle macOS /var -> /private/var symlink
96
+ expect(File.realpath(actual_file)).to eq(File.realpath(expected_file))
97
+ expect(File.realpath(File.dirname(actual_file))).to eq(File.realpath(parent_dir))
98
+ end
99
+
100
+ it 'includes topics from both current and parent directories' do
101
+ buildnote = Howzit::BuildNote.new
102
+ current_topic = buildnote.find_topic('Current Topic')[0]
103
+ parent_topic = buildnote.find_topic('Parent Topic')[0]
104
+
105
+ expect(current_topic).not_to be_nil
106
+ expect(parent_topic).not_to be_nil
107
+ # Use realpath to handle macOS /var -> /private/var symlink
108
+ expect(File.realpath(current_topic.source_file)).to eq(File.realpath(File.join(current_dir, 'buildnotes.md')))
109
+ expect(File.realpath(parent_topic.source_file)).to eq(File.realpath(File.join(parent_dir, 'buildnotes.md')))
110
+ end
111
+ end
112
+ end
113
+
114
+ describe 'task execution directory' do
115
+ context 'without --stack mode' do
116
+ before do
117
+ Howzit.options[:stack] = false
118
+ end
119
+
120
+ it 'sets source_file but does not prepare to change directory' do
121
+ buildnote = Howzit::BuildNote.new
122
+ topic = buildnote.find_topic('Current Topic')[0]
123
+ task = topic.tasks.first
124
+
125
+ expected_file = File.expand_path(File.join(current_dir, 'buildnotes.md'))
126
+ # Use realpath to handle macOS /var -> /private/var symlink
127
+ expect(File.realpath(task.source_file)).to eq(File.realpath(expected_file))
128
+
129
+ # In normal mode, exec_dir should be nil (no directory change)
130
+ # We can't easily test the actual chdir without mocking, but we can verify
131
+ # that the task has the correct source_file set
132
+ expect(task.source_file).not_to be_nil
133
+ end
134
+ end
135
+
136
+ context 'with --stack mode' do
137
+ before do
138
+ Howzit.options[:stack] = true
139
+ end
140
+
141
+ it 'sets source_file for current directory tasks' do
142
+ buildnote = Howzit::BuildNote.new
143
+ topic = buildnote.find_topic('Current Topic')[0]
144
+ task = topic.tasks.first
145
+
146
+ expected_file = File.expand_path(File.join(current_dir, 'buildnotes.md'))
147
+ # Use realpath to handle macOS /var -> /private/var symlink
148
+ expect(File.realpath(task.source_file)).to eq(File.realpath(expected_file))
149
+
150
+ # Current directory tasks should have source_file set but exec_dir logic
151
+ # should determine not to change (same directory)
152
+ expect(task.source_file).not_to be_nil
153
+ end
154
+
155
+ it 'sets source_file for parent directory tasks and prepares to change directory' do
156
+ buildnote = Howzit::BuildNote.new
157
+ topic = buildnote.find_topic('Parent Topic')[0]
158
+ task = topic.tasks.first
159
+
160
+ expected_file = File.expand_path(File.join(parent_dir, 'buildnotes.md'))
161
+ # Use realpath to handle macOS /var -> /private/var symlink
162
+ expect(File.realpath(task.source_file)).to eq(File.realpath(expected_file))
163
+
164
+ # Verify source_file is from parent directory
165
+ source_dir = File.realpath(File.dirname(task.source_file))
166
+ expect(source_dir).to eq(File.realpath(parent_dir))
167
+
168
+ # In stack mode with parent directory, the task should have source_file set
169
+ # and the execution logic should change to that directory
170
+ expect(task.source_file).not_to be_nil
171
+ expect(File.realpath(File.dirname(task.source_file))).not_to eq(File.realpath(current_dir))
172
+ end
173
+ end
174
+ end
175
+
176
+ describe 'template topics' do
177
+ let(:template_dir) { File.expand_path(File.join(temp_dir, 'templates')) }
178
+ let(:template_file) { File.expand_path(File.join(template_dir, 'test_template.md')) }
179
+
180
+ before do
181
+ FileUtils.mkdir_p(template_dir)
182
+
183
+ template_note = <<~EONOTE
184
+ # Template Note
185
+
186
+ ## Template Topic
187
+
188
+ @run(echo "template_task") Template task
189
+ EONOTE
190
+ File.write(template_file, template_note)
191
+
192
+ # Add template to current build note
193
+ current_note_with_template = <<~EONOTE
194
+ template: test_template
195
+
196
+ # Current Build Note
197
+
198
+ ## Current Topic
199
+
200
+ @run(echo "current_task") Current task
201
+ EONOTE
202
+ File.write(File.join(current_dir, 'buildnotes.md'), current_note_with_template)
203
+
204
+ # Set template folder in config
205
+ allow(Howzit.config).to receive(:template_folder).and_return(template_dir)
206
+ end
207
+
208
+ after do
209
+ # Reset template folder
210
+ allow(Howzit.config).to receive(:template_folder).and_call_original
211
+ end
212
+
213
+ it 'sets source_file for template topics' do
214
+ buildnote = Howzit::BuildNote.new
215
+ matches = buildnote.find_topic('Template Topic')
216
+
217
+ # Template topics might not be found if template loading fails
218
+ # Let's check if any topics exist first
219
+ if matches.empty?
220
+ # Try to find by checking all topics
221
+ all_topics = buildnote.topics
222
+ topic = all_topics.find { |t| t.title.include?('Template') }
223
+ else
224
+ topic = matches[0]
225
+ end
226
+
227
+ expect(topic).not_to be_nil, "Template topic not found. Available topics: #{buildnote.topics.map(&:title).join(', ')}"
228
+ expect(File.expand_path(topic.source_file)).to eq(File.expand_path(template_file))
229
+ end
230
+
231
+ it 'sets source_file for template tasks but does not prepare to change directory in stack mode' do
232
+ Howzit.options[:stack] = true
233
+
234
+ buildnote = Howzit::BuildNote.new
235
+ matches = buildnote.find_topic('Template Topic')
236
+ topic = matches.empty? ? buildnote.topics.find { |t| t.title.include?('Template') } : matches[0]
237
+
238
+ skip 'Template topic not found - template loading may have failed' if topic.nil?
239
+
240
+ task = topic.tasks.first
241
+
242
+ # Template tasks should have source_file set to template file
243
+ # Use realpath to handle macOS /var -> /private/var symlink
244
+ expect(File.realpath(task.source_file)).to eq(File.realpath(template_file))
245
+
246
+ # But in stack mode, templates should not change directory (they're detected as templates)
247
+ # The execution logic checks if source_file is in template_folder and skips directory change
248
+ expect(task.source_file).not_to be_nil
249
+ end
250
+
251
+ it 'sets source_file for template tasks but does not prepare to change directory in normal mode' do
252
+ Howzit.options[:stack] = false
253
+
254
+ buildnote = Howzit::BuildNote.new
255
+ matches = buildnote.find_topic('Template Topic')
256
+ topic = matches.empty? ? buildnote.topics.find { |t| t.title.include?('Template') } : matches[0]
257
+
258
+ skip 'Template topic not found - template loading may have failed' if topic.nil?
259
+
260
+ task = topic.tasks.first
261
+
262
+ # In normal mode, no directory changes should occur
263
+ expect(task.source_file).not_to be_nil
264
+ end
265
+ end
266
+
267
+ describe 'block tasks' do
268
+ before do
269
+ # Create a build note with a block task
270
+ block_note = <<~EONOTE
271
+ # Build Note
272
+
273
+ ## Block Topic
274
+
275
+ ```run
276
+ #!/usr/bin/env ruby
277
+ puts Dir.pwd
278
+ ```
279
+ EONOTE
280
+ File.write(File.join(current_dir, 'buildnotes.md'), block_note)
281
+ end
282
+
283
+ context 'with --stack mode and parent directory' do
284
+ before do
285
+ Howzit.options[:stack] = true
286
+
287
+ # Create parent with block task
288
+ parent_block_note = <<~EONOTE
289
+ # Parent Build Note
290
+
291
+ ## Parent Block Topic
292
+
293
+ ```run
294
+ #!/usr/bin/env ruby
295
+ puts Dir.pwd
296
+ ```
297
+ EONOTE
298
+ File.write(File.join(parent_dir, 'buildnotes.md'), parent_block_note)
299
+ end
300
+
301
+ it 'sets source_file for parent directory block tasks and prepares to change directory' do
302
+ buildnote = Howzit::BuildNote.new
303
+ topic = buildnote.find_topic('Parent Block Topic')[0]
304
+ task = topic.tasks.first
305
+
306
+ expected_file = File.expand_path(File.join(parent_dir, 'buildnotes.md'))
307
+ # Use realpath to handle macOS /var -> /private/var symlink
308
+ expect(File.realpath(task.source_file)).to eq(File.realpath(expected_file))
309
+
310
+ # Verify source_file is from parent directory
311
+ source_dir = File.realpath(File.dirname(task.source_file))
312
+ expect(source_dir).to eq(File.realpath(parent_dir))
313
+
314
+ # In stack mode with parent directory, the task should have source_file set
315
+ # and the execution logic should change to that directory
316
+ expect(task.source_file).not_to be_nil
317
+ expect(File.realpath(File.dirname(task.source_file))).not_to eq(File.realpath(current_dir))
318
+ end
319
+ end
320
+ end
321
+ end
data/src/_README.md CHANGED
@@ -17,6 +17,8 @@ Howzit is a tool that allows you to keep Markdown-formatted notes about a projec
17
17
  - Use `@include()` to import another topic's tasks
18
18
  - Use fenced code blocks to include/run embedded scripts
19
19
  - Scripts can communicate back to Howzit, sending log messages and setting variables
20
+ - Log helper functions output messages immediately with color-coded output (cyan for info, yellow for warn, red for error, dim for debug)
21
+ - Log helper supports `-r`/`--report` flag (or `report` parameter) for delayed output via communication file
20
22
  - Conditional blocks (`@if`/`@unless`/`@elsif`/`@else`) for conditionally including content and tasks
21
23
  - String comparison operators including fuzzy match (`**=`) for flexible pattern matching
22
24
  - File contents conditions to check file contents in conditional blocks
@@ -25,6 +27,8 @@ Howzit is a tool that allows you to keep Markdown-formatted notes about a projec
25
27
  - Templates for easily including repeat tasks
26
28
  - Grep topics for pattern and choose from matches
27
29
  - Use positional and named variables when executing tasks
30
+ - Topic display shows variable definitions with syntax highlighting (blue parentheses, bright white variable names, yellow defaults)
31
+ - Variable placeholders in content are highlighted to show where substitution occurs
28
32
 
29
33
  ## Getting Started
30
34
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: howzit
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.35
4
+ version: 2.1.38
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -327,6 +327,7 @@ files:
327
327
  - spec/sequential_conditional_spec.rb
328
328
  - spec/set_var_spec.rb
329
329
  - spec/spec_helper.rb
330
+ - spec/stack_mode_spec.rb
330
331
  - spec/stringutils_spec.rb
331
332
  - spec/task_spec.rb
332
333
  - spec/topic_spec.rb
@@ -369,6 +370,7 @@ test_files:
369
370
  - spec/sequential_conditional_spec.rb
370
371
  - spec/set_var_spec.rb
371
372
  - spec/spec_helper.rb
373
+ - spec/stack_mode_spec.rb
372
374
  - spec/stringutils_spec.rb
373
375
  - spec/task_spec.rb
374
376
  - spec/topic_spec.rb