gem_hadar 1.25.0 → 1.27.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.
data/lib/gem_hadar.rb CHANGED
@@ -51,6 +51,12 @@ class GemHadar
51
51
  block and instance_eval(&block)
52
52
  end
53
53
 
54
+ # The has_to_be_set method raises an error if a required gem configuration
55
+ # attribute is not set.
56
+ #
57
+ # @param name [ String ] the name of the required attribute
58
+ #
59
+ # @raise [ ArgumentError ] if the specified attribute has not been set
54
60
  def has_to_be_set(name)
55
61
  fail "#{self.class}: #{name} has to be set for gem"
56
62
  end
@@ -134,7 +140,7 @@ class GemHadar
134
140
  end
135
141
 
136
142
  dsl_accessor :yard_dir do
137
- 'yard'
143
+ 'doc'
138
144
  end
139
145
 
140
146
  dsl_accessor :extensions do FileList['ext/**/extconf.rb'] end
@@ -182,6 +188,18 @@ class GemHadar
182
188
  dsl_accessor :gemset do @outer_scope.name end
183
189
  end
184
190
 
191
+ # The rvm method configures RVM (Ruby Version Manager) settings for the gem
192
+ # project.
193
+ #
194
+ # This method initializes and returns an RvmConfig object that holds RVM-specific
195
+ # configuration such as the Ruby version to use and the gemset name.
196
+ # If a block is provided, it configures the RvmConfig object with the given
197
+ # settings. If no block is provided and no existing RvmConfig object exists,
198
+ # it creates a new one with default settings.
199
+ #
200
+ # @param block [ Proc ] optional block to configure RVM settings
201
+ #
202
+ # @return [ GemHadar::RvmConfig ] the RVM configuration object
185
203
  def rvm(&block)
186
204
  if block
187
205
  @rvm = RvmConfig.new(&block)
@@ -193,6 +211,12 @@ class GemHadar
193
211
 
194
212
  dsl_accessor :default_task_dependencies, [ :gemspec, :test ]
195
213
 
214
+ # The default_task method defines the default Rake task for the gem project.
215
+ #
216
+ # This method sets up a Rake task named :default that depends on the tasks
217
+ # specified in the default_task_dependencies accessor. It provides a convenient
218
+ # way to run the most common or essential tasks for the project with a single
219
+ # command.
196
220
  def default_task
197
221
  desc 'Default task'
198
222
  task :default => default_task_dependencies
@@ -200,11 +224,26 @@ class GemHadar
200
224
 
201
225
  dsl_accessor :build_task_dependencies, [ :clobber, :gemspec, :package, :'version:tag' ]
202
226
 
227
+ # The build_task method defines a Rake task that orchestrates the complete
228
+ # build process for packaging the gem.
229
+ #
230
+ # This method sets up a :build task that depends on the tasks specified in
231
+ # the build_task_dependencies accessor. It provides a convenient way to
232
+ # execute all necessary steps for building packages for a release with a
233
+ # single command.
203
234
  def build_task
204
235
  desc 'Build task (builds all packages for a release)'
205
236
  task :build => build_task_dependencies
206
237
  end
207
238
 
239
+ # The install_library method sets up a Rake task for installing the library
240
+ # or executable into site_ruby directories.
241
+ #
242
+ # This method configures an :install task that depends on the
243
+ # :prepare_install task and executes the provided block. It stores the block
244
+ # in an instance variable to be called later when the task is executed.
245
+ #
246
+ # @param block [ Proc ] the block containing the installation logic
208
247
  def install_library(&block)
209
248
  @install_library_block = -> do
210
249
  desc 'Install executable/library into site_ruby directories'
@@ -212,6 +251,15 @@ class GemHadar
212
251
  end
213
252
  end
214
253
 
254
+ # The clean method manages the CLEAN file list for Rake tasks.
255
+ #
256
+ # When called without arguments, it returns the current CLEAN file list.
257
+ # When called with arguments, it adds the specified files to the CLEAN list.
258
+ #
259
+ # @param args [ Array<String> ] optional list of files to add to the CLEAN list
260
+ #
261
+ # @return [ FileList, nil ] the CLEAN file list when no arguments provided,
262
+ # nil otherwise
215
263
  def clean(*args)
216
264
  if args.empty?
217
265
  CLEAN
@@ -220,6 +268,15 @@ class GemHadar
220
268
  end
221
269
  end
222
270
 
271
+ # The clobber method manages the CLOBBER file list for Rake tasks.
272
+ #
273
+ # When called without arguments, it returns the current CLOBBER file list.
274
+ # When called with arguments, it adds the specified files to the CLOBBER list.
275
+ #
276
+ # @param args [ Array<String> ] optional list of files to add to the CLOBBER list
277
+ #
278
+ # @return [ FileList, nil ] the CLOBBER file list when no arguments provided,
279
+ # nil otherwise
223
280
  def clobber(*args)
224
281
  if args.empty?
225
282
  CLOBBER
@@ -228,6 +285,15 @@ class GemHadar
228
285
  end
229
286
  end
230
287
 
288
+ # The ignore method manages the list of files to be ignored by the gem.
289
+ #
290
+ # When called without arguments, it returns the current set of ignored files.
291
+ # When called with arguments, it adds the specified files to the ignore list.
292
+ #
293
+ # @param args [ Array<String> ] optional list of file patterns to add to the ignore list
294
+ #
295
+ # @return [ Set<String>, nil ] the set of ignored files when no arguments provided,
296
+ # nil otherwise
231
297
  def ignore(*args)
232
298
  if args.empty?
233
299
  ignore_files
@@ -236,6 +302,17 @@ class GemHadar
236
302
  end
237
303
  end
238
304
 
305
+ # The package_ignore method manages the list of files to be ignored during
306
+ # gem packaging.
307
+ #
308
+ # When called without arguments, it returns the current set of package ignore
309
+ # files. When called with arguments, it adds the specified file patterns to
310
+ # the package ignore list.
311
+ #
312
+ # @param args [ Array<String> ] optional list of file patterns to add to the package ignore list
313
+ #
314
+ # @return [ Set<String>, nil ] the set of package ignore files when no arguments provided,
315
+ # nil otherwise
239
316
  def package_ignore(*args)
240
317
  if args.empty?
241
318
  package_ignore_files
@@ -244,14 +321,28 @@ class GemHadar
244
321
  end
245
322
  end
246
323
 
324
+ # The dependency method adds a new runtime dependency to the gem.
325
+ #
326
+ # @param args [ Array ] the arguments defining the dependency
247
327
  def dependency(*args)
248
328
  @dependencies << args
249
329
  end
250
330
 
331
+ # The development_dependency method adds a new development-time dependency to
332
+ # the gem.
333
+ #
334
+ # @param args [ Array ] the arguments defining the development dependency
251
335
  def development_dependency(*args)
252
336
  @development_dependencies << args
253
337
  end
254
338
 
339
+ # The gems_install_task method defines a Rake task for installing all gem
340
+ # dependencies specified in the Gemfile.
341
+ #
342
+ # This method sets up a :gems:install task that executes a block to install
343
+ # gems. If no block is provided, it defaults to running 'bundle install'.
344
+ #
345
+ # @param block [ Proc ] optional block containing the installation command
255
346
  def gems_install_task(&block)
256
347
  block ||= proc { sh 'bundle install' }
257
348
  desc 'Install all gems from the Gemfile'
@@ -260,6 +351,14 @@ class GemHadar
260
351
  end
261
352
  end
262
353
 
354
+ # The version_task method defines a Rake task that generates a version file
355
+ # for the gem.
356
+ #
357
+ # This method creates a task named :version that writes version information
358
+ # to a Ruby file in the lib directory. The generated file contains constants
359
+ # for the version and its components, as well as an optional epilogue
360
+ # section. The task ensures the target directory exists and uses secure file
361
+ # writing to prevent permission issues.
263
362
  def version_task
264
363
  desc m = "Writing version information for #{name}-#{version}"
265
364
  task :version do
@@ -281,6 +380,13 @@ class GemHadar
281
380
  end
282
381
  end
283
382
 
383
+ # The version_show_task method defines a Rake task that displays the current
384
+ # version of the gem.
385
+ #
386
+ # This method creates a :version:show task under the Rake namespace that
387
+ # reads the version from the generated version file in the lib directory and
388
+ # compares it with the version specified in the GemHadar configuration. It
389
+ # then outputs a message indicating whether the versions match or not.
284
390
  def version_show_task
285
391
  namespace :version do
286
392
  desc "Displaying the current version"
@@ -299,6 +405,21 @@ class GemHadar
299
405
  end
300
406
  end
301
407
 
408
+ # The version_log_diff method generates a git log output containing patch
409
+ # differences between two specified versions.
410
+ #
411
+ # This method retrieves the commit history between a starting version and an
412
+ # ending version, including detailed changes (patch format) for each commit.
413
+ # It supports comparing against the current HEAD or specific version tags,
414
+ # and automatically determines the appropriate previous version when only a
415
+ # target version is provided.
416
+ #
417
+ # @param to_version [ String ] the ending version tag or 'HEAD' to compare up to the latest commit
418
+ # @param from_version [ String, nil ] the starting version tag; if nil, it defaults based on to_version
419
+ #
420
+ # @raise [ RuntimeError ] if the specified version tags are not found in the repository
421
+ #
422
+ # @return [ String ] the git log output in patch format showing changes between the two versions
302
423
  def version_log_diff(to_version: 'HEAD', from_version: nil)
303
424
  if to_version == 'HEAD'
304
425
  if from_version.blank?
@@ -331,6 +452,15 @@ class GemHadar
331
452
  end
332
453
  end
333
454
 
455
+ # The version_diff_task method defines Rake tasks for listing and displaying
456
+ # git version differences.
457
+ #
458
+ # This method sets up two subtasks under the :version namespace:
459
+ #
460
+ # - A :list task that fetches all git tags, ensures the operation succeeds,
461
+ # and outputs the sorted list of versions.
462
+ # - A :diff task that calculates the version range, displays a colored diff
463
+ # between the versions, and shows the changes.
334
464
  def version_diff_task
335
465
  namespace :version do
336
466
  desc "List all versions in order"
@@ -349,9 +479,18 @@ class GemHadar
349
479
  end
350
480
  end
351
481
 
482
+ # The gem_hadar_update_task method defines a Rake task that updates the
483
+ # gem_hadar dependency version in the gemspec file.
484
+ #
485
+ # This method creates a :gem_hadar:update task under the Rake namespace that
486
+ # prompts the user to specify a new gem_hadar version.
487
+ # It then reads the existing gemspec file, modifies the version constraint
488
+ # for the gem_hadar dependency, and writes the updated content back to the
489
+ # file. If the specified version is already present in the gemspec, it skips
490
+ # the update and notifies the user.
352
491
  def gem_hadar_update_task
353
492
  namespace :gem_hadar do
354
- desc 'Update gem_hadar a different version'
493
+ desc 'Update gem_hadar to a different version'
355
494
  task :update do
356
495
  answer = ask?("Which gem_hadar version? ", /^((?:\d+.){2}(?:\d+))$/)
357
496
  unless answer
@@ -377,6 +516,14 @@ class GemHadar
377
516
  end
378
517
  end
379
518
 
519
+ # The gemspec_task method defines a Rake task that generates and writes a
520
+ # gemspec file for the project.
521
+ #
522
+ # This method creates a :gemspec task that depends on the :version task,
523
+ # ensuring the version is set before generating the gemspec. It constructs
524
+ # the filename based on the project name, displays a warning message
525
+ # indicating the file being written, and uses secure_write to create the
526
+ # gemspec file with content generated by the gemspec method.
380
527
  def gemspec_task
381
528
  desc 'Create a gemspec file'
382
529
  task :gemspec => :version do
@@ -386,6 +533,12 @@ class GemHadar
386
533
  end
387
534
  end
388
535
 
536
+ # The package_task method sets up a Rake task for packaging the gem.
537
+ #
538
+ # This method configures a task that creates a package directory, initializes
539
+ # a Gem::PackageTask with the current gem specification, and specifies that
540
+ # tar files should be created. It also includes the files to be packaged by
541
+ # adding gem_files to the package_files attribute of the Gem::PackageTask.
389
542
  def package_task
390
543
  clean 'pkg'
391
544
  Gem::PackageTask.new(gemspec) do |pkg|
@@ -394,24 +547,20 @@ class GemHadar
394
547
  end
395
548
  end
396
549
 
550
+ # The install_library_task method executes the installed library task block
551
+ # if it has been defined.
397
552
  def install_library_task
398
553
  @install_library_block.full?(:call)
399
554
  end
400
555
 
401
- def doc_task
402
- clean 'doc'
403
- desc "Creating documentation"
404
- task :doc do
405
- sh 'yard doc'
406
- cmd = 'yardoc'
407
- if readme
408
- cmd << " --readme '#{readme}'"
409
- end
410
- cmd << ' - ' << doc_files * ' '
411
- sh cmd
412
- end
413
- end
414
-
556
+ # The test_task method sets up a Rake task for executing the project's test
557
+ # suite.
558
+ #
559
+ # This method configures a Rake task named :test that runs the test suite
560
+ # using Rake::TestTask. It includes the test directory and required paths in
561
+ # the load path, specifies the test files to run, and enables verbose output.
562
+ # The task also conditionally depends on the :compile task if project
563
+ # extensions are present.
415
564
  def test_task
416
565
  tt = Rake::TestTask.new(:run_tests) do |t|
417
566
  t.libs << test_dir
@@ -423,6 +572,12 @@ class GemHadar
423
572
  task :test => [ (:compile if extensions.full?), tt.name ].compact
424
573
  end
425
574
 
575
+ # The spec_task method sets up a Rake task for executing RSpec tests.
576
+ #
577
+ # This method configures a :spec task that runs the project's RSpec test
578
+ # suite. It initializes an RSpec::Core::RakeTask with appropriate Ruby
579
+ # options, test pattern, and verbose output. The task also conditionally
580
+ # depends on the :compile task if project extensions are present.
426
581
  def spec_task
427
582
  defined? ::RSpec::Core::RakeTask or return
428
583
  st = RSpec::Core::RakeTask.new(:run_specs) do |t|
@@ -434,6 +589,15 @@ class GemHadar
434
589
  task :spec => [ (:compile if extensions.full?), st.name ].compact
435
590
  end
436
591
 
592
+ # The rcov_task method sets up a Rake task for executing code coverage tests
593
+ # using RCov.
594
+ #
595
+ # This method configures a :rcov task that runs the project's test suite with
596
+ # RCov to generate code coverage reports. It includes the test directory and
597
+ # required paths in the load path, specifies the test files to run, and
598
+ # enables verbose output. The task also conditionally depends on the :compile
599
+ # task if project extensions are present. If RCov is not available, it
600
+ # displays a warning message suggesting to install RCov.
437
601
  def rcov_task
438
602
  if defined?(::Rcov)
439
603
  rt = ::Rcov::RcovTask.new(:run_rcov) do |t|
@@ -455,6 +619,23 @@ class GemHadar
455
619
  end
456
620
  end
457
621
 
622
+ # The version_bump_task method defines Rake tasks for incrementing the gem's
623
+ # version number.
624
+ #
625
+ # This method sets up a hierarchical task structure under the :version
626
+ # namespace:
627
+ #
628
+ # - It creates subtasks in the :version:bump namespace for explicitly bumping
629
+ # major, minor, or build versions.
630
+ # - It also defines a :version:bump task that automatically suggests the
631
+ # appropriate version bump type by analyzing recent changes using AI. The
632
+ # suggestion is based on the git log diff between the previous version and
633
+ # the current HEAD, and it prompts the user for confirmation before applying
634
+ # the bump.
635
+ #
636
+ # The tasks utilize the version_log_diff method to gather change information,
637
+ # the ollama_generate method to get AI-powered suggestions, and the
638
+ # version_bump_to method to perform the actual version update.
458
639
  def version_bump_task
459
640
  namespace :version do
460
641
  namespace :bump do
@@ -474,7 +655,7 @@ class GemHadar
474
655
  end
475
656
  end
476
657
 
477
- desc 'Bump version with suggestion'
658
+ desc 'Bump version with AI suggestion'
478
659
  task :bump do
479
660
  log_diff = version_log_diff(from_version: nil, to_version: 'HEAD')
480
661
  system = xdg_config('version_bump_system_prompt.txt', default_version_bump_system_prompt)
@@ -499,6 +680,15 @@ class GemHadar
499
680
  end
500
681
  end
501
682
 
683
+ # The version_tag_task method defines a Rake task that creates a Git tag for
684
+ # the current version.
685
+ #
686
+ # This method sets up a :version:tag task under the Rake namespace that
687
+ # creates an annotated Git tag for the project's current version. It checks
688
+ # if a tag with the same name already exists and handles the case where the
689
+ # tag exists but is different from the current commit. If the tag already
690
+ # exists and is different, it prompts the user to confirm whether to
691
+ # overwrite it forcefully.
502
692
  def version_tag_task
503
693
  namespace :version do
504
694
  desc "Tag this commit as version #{version}"
@@ -523,10 +713,24 @@ class GemHadar
523
713
  end
524
714
  end
525
715
 
716
+ # The git_remote method retrieves the primary Git remote name configured for
717
+ # the project.
718
+ #
719
+ # It first checks the GIT_REMOTE environment variable for a custom remote
720
+ # specification. If not set, it defaults to 'origin'. When multiple remotes
721
+ # are specified in the environment variable, only the first one is returned.
526
722
  def git_remote
527
723
  ENV.fetch('GIT_REMOTE', 'origin').split(/\s+/).first
528
724
  end
529
725
 
726
+ # The master_prepare_task method defines a Rake task that sets up a remote
727
+ # Git repository for the project.
728
+ #
729
+ # This method creates a :master:prepare task under the Rake namespace that
730
+ # guides the user through creating a new bare Git repository on a remote
731
+ # server via SSH. It prompts for the remote name, directory path, and SSH
732
+ # account details to configure the repository and establish a connection back
733
+ # to the local project.
530
734
  def master_prepare_task
531
735
  namespace :master do
532
736
  desc "Prepare a remote git repository for this project"
@@ -544,6 +748,20 @@ class GemHadar
544
748
  end
545
749
  end
546
750
 
751
+ # The version_push_task method defines Rake tasks for pushing version tags to
752
+ # Git remotes.
753
+ #
754
+ # This method sets up a hierarchical task structure under the :version
755
+ # namespace:
756
+ #
757
+ # - It creates subtasks in the :version:push namespace for each configured
758
+ # Git remote, allowing individual pushes to specific remotes.
759
+ # - It also defines a top-level :version:push task that depends on all the
760
+ # individual remote push tasks, enabling a single command to push the version
761
+ # tag to all remotes.
762
+ #
763
+ # The tasks utilize the git_remotes method to determine which remotes are
764
+ # configured and generate appropriate push commands for each one.
547
765
  def version_push_task
548
766
  namespace :version do
549
767
  git_remotes.each do |gr|
@@ -560,6 +778,19 @@ class GemHadar
560
778
  end
561
779
  end
562
780
 
781
+ # The master_push_task method defines Rake tasks for pushing the master
782
+ # branch to configured Git remotes.
783
+ #
784
+ # This method sets up a hierarchical task structure under the :master namespace:
785
+ #
786
+ # - It creates subtasks in the :master:push namespace for each configured Git
787
+ # remote, allowing individual pushes to specific remotes.
788
+ # - It also defines a top-level :master:push task that depends on all the individual
789
+ # remote push tasks, enabling a single command to push the master branch to
790
+ # all remotes.
791
+ #
792
+ # The tasks utilize the git_remotes method to determine which remotes are
793
+ # configured and generate appropriate push commands for each one.
563
794
  def master_push_task
564
795
  namespace :master do
565
796
  git_remotes.each do |gr|
@@ -576,6 +807,17 @@ class GemHadar
576
807
  end
577
808
  end
578
809
 
810
+ # The gem_push_task method defines a Rake task for pushing the generated gem
811
+ # file to RubyGems.
812
+ #
813
+ # This method sets up a :gem:push task under the Rake namespace that handles
814
+ # the process of uploading the gem package to RubyGems. It checks if the
815
+ # project is in developing mode and skips the push operation if so.
816
+ # Otherwise, it verifies the existence of the gem file, prompts the user for
817
+ # confirmation before pushing, and uses the gem push command with an optional
818
+ # API key from the environment. If the gem file does not exist or the user
819
+ # declines to push, appropriate messages are displayed and the task exits
820
+ # accordingly.
579
821
  def gem_push_task
580
822
  namespace :gem do
581
823
  path = "pkg/#{name_version}.gem"
@@ -604,6 +846,15 @@ class GemHadar
604
846
  end
605
847
  end
606
848
 
849
+ # The git_remotes_task method defines a Rake task that displays all Git
850
+ # remote repositories configured for the project.
851
+ #
852
+ # This method sets up a :git_remotes task under the Rake namespace that
853
+ # retrieves and prints the list of Git remotes along with their URLs. It uses
854
+ # the git_remotes method to obtain the remote names and then fetches each
855
+ # remote's URL using the `git remote get-url` command. The output is
856
+ # formatted to show each remote name followed by its corresponding URL on
857
+ # separate lines.
607
858
  def git_remotes_task
608
859
  task :git_remotes do
609
860
  puts git_remotes.map { |r|
@@ -613,6 +864,14 @@ class GemHadar
613
864
  end
614
865
  end
615
866
 
867
+ # The create_git_release_body method generates a changelog for a GitHub
868
+ # release by analyzing the git diff between the previous version and the
869
+ # current HEAD.
870
+ #
871
+ # It retrieves the log differences, fetches or uses default system and prompt
872
+ # templates, and utilizes an AI model to produce a formatted changelog entry.
873
+ #
874
+ # @return [ String ] the generated changelog content for the release body
616
875
  def create_git_release_body
617
876
  log_diff = version_log_diff(to_version: version)
618
877
  system = xdg_config('release_system_prompt.txt', default_git_release_system_prompt)
@@ -620,26 +879,14 @@ class GemHadar
620
879
  ollama_generate(system:, prompt:)
621
880
  end
622
881
 
623
- def edit_temp_file(content)
624
- editor = ENV.fetch('EDITOR', `which vi`.chomp)
625
- unless File.exist?(editor)
626
- warn "Can't find EDITOR. => Returning."
627
- return
628
- end
629
- temp_file = Tempfile.new('changelog')
630
- temp_file.write(content)
631
- temp_file.close
632
-
633
- unless system("#{editor} #{temp_file.path}")
634
- warn "#{editor} returned #{$?.exitstatus} => Returning."
635
- return
636
- end
637
-
638
- File.read(temp_file.path)
639
- ensure
640
- temp_file&.close&.unlink
641
- end
642
-
882
+ # The github_release_task method defines a Rake task that creates a GitHub
883
+ # release for the current version.
884
+ #
885
+ # This method sets up a :github:release task that prompts the user to confirm
886
+ # publishing a release message on GitHub. It retrieves the GitHub API token
887
+ # from the environment, derives the repository owner and name from the git
888
+ # remote URL, generates a changelog using AI, and creates the release via the
889
+ # GitHub API.
643
890
  def github_release_task
644
891
  namespace :github do
645
892
  unless github_api_token = ENV['GITHUB_API_TOKEN'].full?
@@ -647,7 +894,7 @@ class GemHadar
647
894
  task :release
648
895
  return
649
896
  end
650
- desc "Create a new GitHub release for the current version with a changelog"
897
+ desc "Create a new GitHub release for the current version with a AI-generated changelog"
651
898
  task :release do
652
899
  yes = ask?(
653
900
  "Do you want to publish a release message on github? (y/n%{default}) ",
@@ -681,6 +928,14 @@ class GemHadar
681
928
 
682
929
  dsl_accessor :push_task_dependencies, %i[ modified build master:push version:push gem:push github:release ]
683
930
 
931
+ # The push_task method defines a Rake task that orchestrates the complete
932
+ # process of pushing changes and artifacts to remote repositories and package
933
+ # managers.
934
+ #
935
+ # This method sets up multiple subtasks including preparing the master
936
+ # branch, pushing version tags, pushing to gem repositories, and creating
937
+ # GitHub releases. It also includes a check for uncommitted changes before
938
+ # proceeding with the push operations.
684
939
  def push_task
685
940
  master_prepare_task
686
941
  version_push_task
@@ -695,10 +950,26 @@ class GemHadar
695
950
  exit 1
696
951
  end
697
952
  end
698
- desc "Push master and version #{version} all git remotes: #{git_remotes * ' '}"
953
+ desc "Push all changes for version #{version} into the internets."
699
954
  task :push => push_task_dependencies
700
955
  end
701
956
 
957
+ # The release_task method defines a Rake task that orchestrates the complete
958
+ # release process for the gem.
959
+ #
960
+ # This method sets up a :release task that depends on the :push task,
961
+ # ensuring all necessary steps for publishing the gem are executed in
962
+ # sequence. It provides a convenient way to perform a full release workflow
963
+ # with a single command.
964
+ def release_task
965
+ desc "Release the new version #{version} for the gem #{name}."
966
+ task :release => :push
967
+ end
968
+
969
+ # The compile_task method sets up a Rake task to compile project extensions.
970
+ #
971
+ # This method creates a :compile task that iterates through the configured
972
+ # extensions and compiles them using the system's make command.
702
973
  def compile_task
703
974
  for file in extensions
704
975
  dir = File.dirname(file)
@@ -716,6 +987,16 @@ class GemHadar
716
987
  end
717
988
  end
718
989
 
990
+ # The rvm_task method creates a .rvmrc file that configures RVM to use the
991
+ # specified Ruby version and gemset for the project.
992
+ #
993
+ # This task generates a .rvmrc file in the project root directory with commands to:
994
+ # - Use the Ruby version specified by the rvm.use accessor
995
+ # - Create the gemset specified by the rvm.gemset accessor
996
+ # - Switch to using that gemset
997
+ #
998
+ # The generated file is written using the secure_write method to ensure
999
+ # proper file permissions.
719
1000
  def rvm_task
720
1001
  desc 'Create .rvmrc file'
721
1002
  task :rvm do
@@ -729,15 +1010,55 @@ class GemHadar
729
1010
  end
730
1011
  end
731
1012
 
1013
+ def yard_doc_task
1014
+ YARD::Rake::YardocTask.new(:yard_doc) do |t|
1015
+ t.files = files.select { _1 =~ /\.rb\z/ }
1016
+
1017
+ output_dir = yard_dir
1018
+ t.options = [ "--output-dir=#{output_dir}" ]
1019
+
1020
+ # Include private & protected methods in documentation
1021
+ t.options << '--private' << '--protected'
1022
+
1023
+ # Handle readme if present
1024
+ if readme && File.exist?(readme)
1025
+ t.options << "--readme=#{readme}"
1026
+ end
1027
+
1028
+ # Add additional documentation files
1029
+ if doc_files&.any?
1030
+ t.files.concat(doc_files.flatten)
1031
+ end
1032
+
1033
+ # Add before hook for cleaning
1034
+ t.before = proc {
1035
+ clean output_dir
1036
+ puts "Generating full documentation in #{output_dir}..."
1037
+ }
1038
+ end
1039
+ end
1040
+
1041
+ # The yard_task method sets up and registers Rake tasks for generating and
1042
+ # managing YARD documentation.
1043
+ #
1044
+ # It creates multiple subtasks under the :yard namespace, including tasks for
1045
+ # creating private documentation, viewing the generated documentation,
1046
+ # cleaning up documentation files, and listing undocumented elements.
1047
+ # If YARD is not available, the method returns early without defining any tasks.
732
1048
  def yard_task
733
1049
  defined? YARD or return
1050
+ yard_doc_task
1051
+ desc 'Create yard documentation (including private)'
1052
+ task :doc => :yard_doc
734
1053
  namespace :yard do
735
1054
  my_yard_dir = Pathname.new(yard_dir)
736
1055
 
737
- desc 'Create yard documentation (including private)'
738
- task :private do
739
- sh "yardoc -o #{my_yard_dir}"
740
- end
1056
+ task :private => :yard_doc
1057
+
1058
+ task :public => :yard_doc
1059
+
1060
+ desc 'Create yard documentation'
1061
+ task :doc => :yard_doc
741
1062
 
742
1063
  desc 'View the yard documentation'
743
1064
  task :view do
@@ -761,12 +1082,79 @@ class GemHadar
761
1082
  task :yard => %i[ yard:private yard:view ]
762
1083
  end
763
1084
 
1085
+ def config_task
1086
+ namespace :gem_hadar do
1087
+ desc "Display current gem_hadar configuration"
1088
+ task :config do
1089
+ puts "=== GemHadar Configuration ==="
1090
+
1091
+ # RubyGems
1092
+ if ENV['GEM_HOST_API_KEY'].present?
1093
+ puts "RubyGems API Key: *** (set)"
1094
+ else
1095
+ puts "RubyGems API Key: Not set"
1096
+ end
1097
+
1098
+ # GitHub
1099
+ if ENV['GITHUB_API_TOKEN'].present?
1100
+ puts "GitHub API Token: *** (set)"
1101
+ else
1102
+ puts "GitHub API Token: Not set"
1103
+ end
1104
+
1105
+ # Ollama
1106
+ puts "Ollama Model: #{ollama_model} (default is #{ollama_model_default})"
1107
+
1108
+ if url = ollama_client&.full?(:base_url)&.to_s
1109
+ puts "Ollama Base URL: #{url.inspect}"
1110
+ else
1111
+ puts "Ollama Base URL: Not set"
1112
+ end
1113
+
1114
+ if ENV['OLLAMA_MODEL_OPTIONS']
1115
+ puts "Ollama Model Options: #{ENV['OLLAMA_MODEL_OPTIONS']}"
1116
+ else
1117
+ puts "Ollama Model Options: Not set (using defaults)"
1118
+ end
1119
+
1120
+ # XDG config home
1121
+ puts "XDG config home: #{xdg_config_home.to_s.inspect}"
1122
+
1123
+ # General
1124
+ puts "Gem Name: #{name}"
1125
+ puts "Version: #{version}"
1126
+
1127
+ # Build/Development
1128
+ puts "MAKE: #{ENV['MAKE'] || 'Not set (will use gmake or make)'}"
1129
+ puts "EDITOR: #{ENV['EDITOR'] || 'Not set (will use vi)'}"
1130
+
1131
+ # Git
1132
+ puts "Git Remote(s): #{ENV['GIT_REMOTE'] || 'origin'}"
1133
+
1134
+ # Other
1135
+ puts "Force Operations: #{ENV['FORCE'] || '0'}"
1136
+ puts "Version Override: #{ENV['VERSION'] || 'Not set'}"
1137
+ puts "GitHub Release Enabled: #{ENV['GITHUB_RELEASE_ENABLED'] || 'Not set'}"
1138
+
1139
+ puts "\n=== AI Prompt Configuration (Default Values) ==="
1140
+ arrow = ?⤵
1141
+ puts bold{"version_bump_system_prompt.txt"} + "#{arrow}\n" + italic{default_version_bump_system_prompt}
1142
+ puts bold{"version_bump_prompt.txt"} + "#{arrow}\n#{default_version_bump_prompt}"
1143
+ puts bold{"release_system_prompt.txt"} + "#{arrow}\n" + italic{default_git_release_system_prompt}
1144
+ puts bold{"release_prompt.txt"} + "#{arrow}\n" + italic{default_git_release_prompt}
1145
+
1146
+ puts "=== End Configuration ==="
1147
+ end
1148
+ end
1149
+ end
1150
+
764
1151
  # The create_all_tasks method sets up and registers all the Rake tasks for
765
1152
  # the gem project.
766
1153
  #
767
1154
  # @return [GemHadar] the instance of GemHadar
768
1155
  def create_all_tasks
769
1156
  default_task
1157
+ config_task
770
1158
  build_task
771
1159
  rvm_task
772
1160
  version_task
@@ -775,7 +1163,6 @@ class GemHadar
775
1163
  gem_hadar_update_task
776
1164
  gemspec_task
777
1165
  gems_install_task
778
- doc_task
779
1166
  if test_dir
780
1167
  test_task
781
1168
  rcov_task
@@ -787,6 +1174,7 @@ class GemHadar
787
1174
  version_bump_task
788
1175
  version_tag_task
789
1176
  push_task
1177
+ release_task
790
1178
  write_ignore_file
791
1179
  write_gemfile
792
1180
  if extensions.full?
@@ -798,20 +1186,78 @@ class GemHadar
798
1186
  self
799
1187
  end
800
1188
 
1189
+ # The edit_temp_file method creates a temporary file with the provided
1190
+ # content, opens it in an editor for user modification, and returns the
1191
+ # updated content.
1192
+ #
1193
+ # @param content [ String ] the initial content to write to the temporary file
1194
+ def edit_temp_file(content)
1195
+ editor = ENV.fetch('EDITOR', `which vi`.chomp)
1196
+ unless File.exist?(editor)
1197
+ warn "Can't find EDITOR. => Returning."
1198
+ return
1199
+ end
1200
+ temp_file = Tempfile.new('changelog')
1201
+ temp_file.write(content)
1202
+ temp_file.close
1203
+
1204
+ unless system("#{editor} #{temp_file.path}")
1205
+ warn "#{editor} returned #{$?.exitstatus} => Returning."
1206
+ return
1207
+ end
1208
+
1209
+ File.read(temp_file.path)
1210
+ ensure
1211
+ temp_file&.close&.unlink
1212
+ end
1213
+
1214
+ dsl_accessor :ollama_model_default, 'llama3.1'.freeze
1215
+
1216
+ # The ollama_model method retrieves the name of the Ollama AI model to be
1217
+ # used for generating responses.
1218
+ #
1219
+ # It first checks the OLLAMA_MODEL environment variable for a custom model
1220
+ # specification. If the environment variable is not set, it falls back to
1221
+ # using the default model name, which is determined by the
1222
+ # ollama_model_default dsl method.
1223
+ #
1224
+ # @return [ String ] the name of the Ollama AI model to be used
1225
+ def ollama_model
1226
+ ENV.fetch('OLLAMA_MODEL', ollama_model_default)
1227
+ end
1228
+
1229
+ # The ollama_client method creates and returns an Ollama::Client instance
1230
+ # configured with a base URL derived from environment variables.
1231
+ #
1232
+ # It first checks for the OLLAMA_URL environment variable to determine the
1233
+ # base URL. If that is not set, it falls back to using the OLLAMA_HOST
1234
+ # environment variable, defaulting to 'localhost:11434' if that is also not
1235
+ # set. The method then constructs the full base URL and initializes an
1236
+ # Ollama::Client with appropriate timeouts for read and connect operations.
1237
+ #
1238
+ # @return [Ollama::Client, nil] An initialized Ollama::Client instance if a valid base URL is present, otherwise nil.
1239
+ def ollama_client
1240
+ base_url = ENV['OLLAMA_URL']
1241
+ if base_url.blank?
1242
+ host = ENV.fetch('OLLAMA_HOST', 'localhost:11434')
1243
+ base_url = 'http://%s' % host
1244
+ end
1245
+ base_url.present? or return
1246
+ ollama = Ollama::Client.new(base_url:, read_timeout: 600, connect_timeout: 60)
1247
+ end
1248
+
801
1249
  # Generates a response from an AI model using the Ollama::Client.
802
1250
  #
803
1251
  # @param [String] system The system prompt for the AI model.
804
1252
  # @param [String] prompt The user prompt to generate a response to.
805
1253
  # @return [String, nil] The generated response or nil if generation fails.
806
1254
  def ollama_generate(system:, prompt:)
807
- base_url = ENV['OLLAMA_URL']
808
- if base_url.blank? && host = ENV['OLLAMA_HOST'].full?
809
- base_url = 'http://%s' % host
1255
+ unless ollama = ollama_client
1256
+ warn "Ollama is not configured. => Returning."
1257
+ return
810
1258
  end
811
- base_url.present? or return
812
- ollama = Ollama::Client.new(base_url:, read_timeout: 600, connect_timeout: 60)
813
- model = ENV.fetch('OLLAMA_MODEL', 'llama3.1')
814
- options = ENV['OLLAMA_OPTIONS'].full? { |o| JSON.parse(o) } || {}
1259
+ model = ollama_model
1260
+ options = ENV['OLLAMA_MODEL_OPTIONS'].full? { |o| JSON.parse(o) } || {}
815
1261
  options |= { "temperature" => 0, "top_p" => 1, "min_p" => 0.1 }
816
1262
  ollama.generate(model:, system:, prompt:, options:, stream: false, think: false).response
817
1263
  end
@@ -941,8 +1387,12 @@ class GemHadar
941
1387
  s.rdoc_options << '--title' << "#{name.camelize} - #{summary}"
942
1388
  end
943
1389
  if readme
944
- s.rdoc_options << '--main' << readme
945
- s.extra_rdoc_files << readme
1390
+ if File.exist?(readme)
1391
+ s.rdoc_options << '--main' << readme
1392
+ s.extra_rdoc_files << readme
1393
+ else
1394
+ warn "Add a #{readme} file to document your gem!"
1395
+ end
946
1396
  end
947
1397
  doc_files.full? { |df| s.extra_rdoc_files.concat Array(df) }
948
1398
  end
@@ -1045,6 +1495,11 @@ class GemHadar
1045
1495
  end
1046
1496
  end
1047
1497
 
1498
+ # The version_untag method removes the 'v' prefix from a version tag string.
1499
+ #
1500
+ # @param version_tag [ String ] the version tag string that may start with 'v'
1501
+ #
1502
+ # @return [ String ] the version string with the 'v' prefix removed
1048
1503
  def version_untag(version_tag)
1049
1504
  version.sub(/\Av/, '')
1050
1505
  end