run_tasks 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f7e70e3d810695cb07c97a531311bae1ee7d8a78
4
- data.tar.gz: 263c45ce78aef65d05cc2c5036f6862d1b1c4558
2
+ SHA256:
3
+ metadata.gz: 857247c762c39960c83e5689ec558c8e39a5b36aafc84db9a8fd08931697df22
4
+ data.tar.gz: 90dd7fd4183acee7232f3c881c2fe550deeb4c326ca23c0c10fd7330b60d2b54
5
5
  SHA512:
6
- metadata.gz: 97893a7e2faf8f9d8cc3a0851c37efcac9fc8f12f6a57324d40c0b5af1a4fc77e38939e0c5bffdc3003497727e99230e08e9ff3d4acd8189a0d98796cec25729
7
- data.tar.gz: 0f417ff1bb2c43fd099d73084f9f7b32f8a77207e84da78466c2ca60a859be4437ad73d9a6047ac6bee5f28761c21ffe5826bfb31a96c7b9e34f862ba19ae1b3
6
+ metadata.gz: cb448955e6889e99bdf60235edb7294af0c381a90c8dddf7d805fbcc7ce56d307dbafcf128ec2ff66f786184df5324743b4d60925894bb44012e6737f511ccc1
7
+ data.tar.gz: f96b6ea0d728dcaff2b851163bf173af427729dab2df12bbddaf6adef442117b88b77a4d3255d185384ffbe3521a36beb34470f4545e0a4d9b9f570216c4ae54
data/bin/run CHANGED
@@ -1,3 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require_relative '../src/bootstrap'
4
+ require_relative "../src/bootstrap"
data/completions/_run ADDED
@@ -0,0 +1,11 @@
1
+ #compdef run
2
+
3
+ _run() {
4
+ local output
5
+ output="$(run --completions 2>/dev/null)" || return
6
+ local -a tasks
7
+ tasks=(${(f)output})
8
+ _describe 'task' tasks
9
+ }
10
+
11
+ _run "$@"
data/src/bootstrap.rb CHANGED
@@ -1,21 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "digest"
2
4
  require "fileutils"
3
5
  require "io/console"
4
6
  require "readline"
5
7
  require "rubygems"
6
8
  require "securerandom"
9
+ require "rb_monkey"
10
+ require "rb_gemspec"
11
+ require "rb_markdown"
7
12
 
8
13
  # Require all files.
9
14
  Dir.glob(
10
- File.join(__dir__, "{monkey,gemspec,markdown,version,run}", "**", "*.rb"),
15
+ File.join(__dir__, "run", "**", "*.rb"),
11
16
  &method(:require)
12
17
  )
13
18
 
14
- # Check for new versions.
15
- if rand(1..5) == 5
16
- Run::Core::VerifyRunVersion.run
17
- end
18
-
19
19
  # Expose global methods.
20
20
  [:task, :run].each do |name|
21
21
  define_method name do |*args, **options, &block|
@@ -27,8 +27,12 @@ end
27
27
  end
28
28
  end
29
29
 
30
- # Define global tasks.
31
- task :rspec do |path|
30
+ define_method :version do |version|
31
+ Run::Core.runfile_version = version
32
+ end
33
+
34
+ # Run Rspec tests (if any).
35
+ task :rspec do |path = "spec/src"|
32
36
  command = "bundle exec rspec"
33
37
 
34
38
  if !path
@@ -40,6 +44,39 @@ task :rspec do |path|
40
44
  end
41
45
  end
42
46
 
47
+ # Install ZSH completions.
48
+ task :install_completions do
49
+ source = File.expand_path(File.join(__dir__, '..', 'completions', '_run'))
50
+ dir = File.expand_path('~/.zfunc')
51
+ dest = File.join(dir, '_run')
52
+ zshrc = File.expand_path('~/.zshrc')
53
+
54
+ FileUtils.mkdir_p(dir)
55
+ FileUtils.cp(source, dest)
56
+
57
+ content = File.exist?(zshrc) ? File.read(zshrc) : ''
58
+ additions = []
59
+ additions << 'fpath=(~/.zfunc $fpath)' unless content.include?('~/.zfunc')
60
+ additions << 'autoload -Uz compinit && compinit' unless content.match?(/compinit/)
61
+
62
+ if additions.any?
63
+ File.open(zshrc, 'a') { |f| f.puts "\n# run completions\n#{additions.join("\n")}" }
64
+ end
65
+
66
+ puts "ZSH completions installed."
67
+ puts "Run 'source ~/.zshrc' to activate."
68
+ end
69
+
70
+ # Publish the gem (if publishable).
71
+ task :publish do
72
+ gemspec_files = Dir.glob(File.join(Dir.pwd, "*.gemspec"))
73
+ raise ".gemspec file not found in the project directory" if gemspec_files.empty?
74
+ gemspec = Gem::Specification::load(gemspec_files.first)
75
+ run "gem build #{gemspec.name}"
76
+ run "gem push #{gemspec.name}-#{gemspec.version}.gem"
77
+ `rm #{gemspec.name}-#{gemspec.version}.gem`
78
+ end
79
+
43
80
  # Expose helpers.
44
81
  Dir.glob(File.join(__dir__, "run", "helper", "*.rb")) do |path|
45
82
  filename = File.basename(path, ".rb")
@@ -78,14 +115,20 @@ rescue SyntaxError => error
78
115
  rescue ArgumentError => error
79
116
  format_error error
80
117
  exit 6
81
- rescue Run::Error::NonExistingRunfile
82
- puts "Runfile.rb does not exist in '#{Dir.pwd}'".red
118
+ rescue Run::Error::NonExistingRunfile => error
119
+ puts error.message.red
83
120
  exit 7
84
121
  rescue Run::Error::Aborted => error
85
122
  exit 9
86
123
  rescue Run::Error::ExistingTask => error
87
124
  puts error.message.red
88
125
  exit 10
126
+ rescue Run::Error::NonExistingTask => error
127
+ puts error.message.red
128
+ exit 12
129
+ rescue Run::Error::RunfileVersionMismatch => error
130
+ puts error.message.red
131
+ exit 13
89
132
  rescue => error
90
133
  format_error error
91
134
  exit 4
data/src/run/core/help.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ripper"
2
4
 
3
5
  module Run
@@ -16,9 +18,10 @@ module Run
16
18
 
17
19
  if help_verbatim[:comments].size > 0
18
20
  help_verbatim[:comments].each_with_index do |comment, comment_line|
21
+ terminal_width = (STDOUT.winsize[1] rescue 80)
19
22
  split_comment = split_verbatim(
20
23
  comment,
21
- STDOUT.winsize[1] - tasks_column_length - 2
24
+ terminal_width - tasks_column_length - 2
22
25
  )
23
26
  split_comment.map.with_index do |chunk, chunk_line|
24
27
  text = comment_line == 0 && chunk_line == 0 ?
@@ -32,10 +35,17 @@ module Run
32
35
  output += "#{name_verbatim}\n"
33
36
  end
34
37
 
38
+ if help_verbatim[:params].any?
39
+ indent = " " * tasks_column_length
40
+ help_verbatim[:params].each do |p|
41
+ output += "#{indent} - #{format_param(p)}\n"
42
+ end
43
+ end
44
+
35
45
  output
36
46
  end
37
47
 
38
- puts output
48
+ output
39
49
  end
40
50
 
41
51
  private
@@ -50,6 +60,7 @@ module Run
50
60
  lines_to_scan = (0..(task[:line] - 2 < 0 ? 0 : task[:line] - 2)).to_a.reverse
51
61
  {
52
62
  names: task[:names],
63
+ params: task[:params],
53
64
  comments: lines_to_scan.each_with_object([]) do |current_line, comments|
54
65
  match = /^\s*#\s*(?<comment>.*?)\s*$/.match(lines[current_line])
55
66
  break comments if !match
@@ -62,8 +73,10 @@ module Run
62
73
  # @param contents [String]
63
74
  # @return [Array<Hash>]
64
75
  def self.extract_tasks(contents)
65
- Ripper.sexp(contents)[1].each_with_object([]) do |(id, sexp), tasks|
66
- next if id != :method_add_block || sexp.fetch(1, nil)&.fetch(1, nil) != "task"
76
+ Ripper.sexp(contents)[1].each_with_object([]) do |node, tasks|
77
+ next if node[0] != :method_add_block
78
+ sexp = node[1]
79
+ next if sexp.fetch(1, nil)&.fetch(1, nil) != "task"
67
80
 
68
81
  names = case sexp[2][1][0][0]
69
82
  when :symbol_literal
@@ -86,12 +99,85 @@ module Run
86
99
  tasks << {
87
100
  names: names,
88
101
  line: line,
102
+ params: extract_params(node[2]),
89
103
  }
90
104
  end.sort do |a, b|
91
105
  a[:names].first <=> b[:names].first
92
106
  end
93
107
  end
94
108
 
109
+ # @param block_node [Array, nil]
110
+ # @return [Array<Hash>]
111
+ def self.extract_params(block_node)
112
+ return [] if block_node.nil?
113
+ block_var = block_node[1]
114
+ return [] if block_var.nil?
115
+ params = block_var[1]
116
+ return [] if params.nil?
117
+
118
+ result = []
119
+
120
+ (params[1] || []).each do |p|
121
+ result << { name: p[1], required: true, keyword: false, default: nil }
122
+ end
123
+
124
+ (params[2] || []).each do |p|
125
+ result << { name: p[0][1], required: false, keyword: false, default: node_to_s(p[1]) }
126
+ end
127
+
128
+ (params[5] || []).each do |p|
129
+ name = p[0][1].chomp(":")
130
+ if p[1] == false
131
+ result << { name: name, required: true, keyword: true, default: nil }
132
+ else
133
+ result << { name: name, required: false, keyword: true, default: node_to_s(p[1]) }
134
+ end
135
+ end
136
+
137
+ result
138
+ end
139
+
140
+ # @param node [Array, nil]
141
+ # @return [String]
142
+ def self.node_to_s(node)
143
+ return "nil" if node.nil?
144
+ case node[0]
145
+ when :string_literal
146
+ node[1][1..-1].map { |p| p[0] == :@tstring_content ? p[1] : "..." }.join
147
+ when :@int, :@float
148
+ node[1]
149
+ when :var_ref
150
+ node[1][1]
151
+ when :symbol_literal
152
+ node[1][1][1]
153
+ when :array
154
+ "[]"
155
+ when :hash
156
+ "{}"
157
+ else
158
+ "..."
159
+ end
160
+ end
161
+
162
+ # @param param [Hash]
163
+ # @return [String]
164
+ def self.format_param(param)
165
+ name = param[:name]
166
+ if param[:keyword]
167
+ if param[:required]
168
+ "<#{name}=<value>>"
169
+ else
170
+ "[#{name}=<value>] " + param[:default].bright_cyan
171
+ end
172
+ else
173
+ if param[:required]
174
+ "<#{name}>"
175
+ else
176
+ "[#{name}] " + param[:default].bright_cyan
177
+ end
178
+ end
179
+ end
180
+
95
181
  # @param tasks [Array<Hash>]
96
182
  # @return [Integer]
97
183
  def self.compute_tasks_column_length(tasks)
data/src/run/core.rb CHANGED
@@ -1,14 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Core
3
- RUNFILE_FILENAME = "Runfile.rb"
5
+ RUNFILE_FILENAME = ENV.fetch('RUNFILE', 'Runfile.rb')
4
6
  RESERVED_TASK_NAMES = ["help", "version"]
5
7
  @@tasks = []
8
+ @@runfile_version = nil
9
+
10
+ # @param version [Integer]
11
+ # @return [void]
12
+ def self.runfile_version=(version)
13
+ @@runfile_version = version.to_i
14
+ end
6
15
 
7
16
  # @return [void]
8
17
  def self.run_run
9
- raise Run::Error::NonExistingRunfile.new if !File.exist?(RUNFILE_FILENAME)
10
- require "./#{RUNFILE_FILENAME}"
11
- run_requested_task if !display_help_if_needed
18
+ if ARGV.size == 1 && ARGV[0] == "--completions"
19
+ require File.expand_path(RUNFILE_FILENAME) if File.exist?(RUNFILE_FILENAME)
20
+ puts completions.join("\n")
21
+ return
22
+ end
23
+ raise Run::Error::NonExistingRunfile.new if !File.file?(RUNFILE_FILENAME)
24
+ if ARGV.size == 1 && ARGV[0] == "version"
25
+ puts version
26
+ elsif ARGV.size == 0 || (ARGV.size == 1 && ARGV[0] == "help")
27
+ require File.expand_path(RUNFILE_FILENAME)
28
+ check_runfile_version!
29
+ display_help
30
+ else
31
+ require File.expand_path(RUNFILE_FILENAME)
32
+ check_runfile_version!
33
+ run_requested_task
34
+ end
12
35
  end
13
36
 
14
37
  # @param task_name_or_command [Symbol, String]
@@ -16,10 +39,13 @@ module Run
16
39
  # @param options [Hash] Optional options sent to the task.
17
40
  # @return [void]
18
41
  def self.run(task_name_or_command, *arguments, **options)
19
- if task_name_or_command.is_a?(Symbol)
20
- run_block_task(task_name_or_command, *arguments, **options)
21
- else
22
- run_system_task(task_name_or_command)
42
+ quiet = options.delete(:quiet) || quiet?
43
+ with_quiet_context(quiet) do
44
+ if task_name_or_command.is_a?(Symbol)
45
+ run_block_task(task_name_or_command, *arguments, **options)
46
+ else
47
+ run_system_task(task_name_or_command)
48
+ end
23
49
  end
24
50
  end
25
51
 
@@ -45,16 +71,39 @@ module Run
45
71
 
46
72
  private
47
73
 
48
- # @return [Boolean]
49
- def self.display_help_if_needed
50
- if ARGV.size == 0 || (ARGV.size == 1 && ["help", "version"].include?(ARGV[0]))
74
+ # @return [void]
75
+ def self.check_runfile_version!
76
+ if (@@runfile_version || 2) != version.to_s.split(".").first.to_i
77
+ raise Run::Error::RunfileVersionMismatch.new
78
+ end
79
+ end
80
+
81
+ # @return [void]
82
+ def self.display_version
83
+ puts Gemspec::Metadata.new("run_tasks").read.version
84
+ end
85
+
86
+ # @return [void]
87
+ def self.completions
88
+ (@@tasks.flat_map{ |t| t[:names] }.map(&:to_s) + RESERVED_TASK_NAMES).sort.uniq
89
+ end
90
+
91
+ # @return [void]
92
+ def self.display_help
93
+ puts
94
+ puts " Run v#{version}".bright_blue
95
+ contents = Run::Core::Help.run(File.read(File.join(Dir.pwd, RUNFILE_FILENAME)))
96
+ if contents.strip.size > 0
51
97
  puts
52
- puts " Run v#{Gemspec::Metadata.new("run_tasks").read.version}".bright_blue
98
+ puts " Project tasks:".magenta
53
99
  puts
54
- Run::Core::Help.run(File.read("./#{RUNFILE_FILENAME}"))
55
- return true
100
+ puts contents
56
101
  end
57
- false
102
+ puts
103
+ puts " Global tasks:".magenta
104
+ puts
105
+ puts Run::Core::Help.run(File.read(File.join(__dir__, "..", "bootstrap.rb")))
106
+ puts
58
107
  end
59
108
 
60
109
  # @return [void]
@@ -62,17 +111,25 @@ module Run
62
111
  name = ARGV[0].gsub('-', '_').to_sym # Auto-replace hyphens to underscores.
63
112
  raise Run::Error::UnknownTask.new(name) if !task_exist?(name)
64
113
 
65
- # Cast value to the right type.
66
- args = ARGV.slice(1, ARGV.size - 1).map do |arg|
67
- value = Float(arg) rescue nil
68
- next value if !value.nil?
69
- next true if arg == "true"
70
- next false if arg == "false"
71
- next arg.to_sym if arg.match?(/^\w+$/)
72
- arg
114
+ args = []
115
+ options = {}
116
+
117
+ ARGV.slice(1, ARGV.size - 1).each do |arg|
118
+ if (match = arg.match(/^\+(\w+)$/))
119
+ options[match[1].to_sym] = true
120
+ elsif (match = arg.match(/^-(\w+)$/))
121
+ options[match[1].to_sym] = false
122
+ else
123
+ value = Float(arg) rescue nil
124
+ next args << value if !value.nil?
125
+ next args << true if arg == "true"
126
+ next args << false if arg == "false"
127
+ next args << arg.to_sym if arg.match?(/^\w+$/)
128
+ args << arg
129
+ end
73
130
  end
74
131
 
75
- run name, *args
132
+ run name, *args, **options
76
133
  end
77
134
 
78
135
  # @param name [Symbol]
@@ -86,13 +143,61 @@ module Run
86
143
  # @param options [Hash]
87
144
  # @return [void]
88
145
  def self.run_block_task(name, *args, **options)
89
- @@tasks.find{ |item| item[:names].include? name }[:task].run(*args, **options)
146
+ task = @@tasks.find{ |item| item[:names].include? name }
147
+ raise Error::NonExistingTask.new(name) if task.nil?
148
+
149
+ task[:task].run(*args, **options)
90
150
  end
91
151
 
92
- # @param name [Symbol]
152
+ # @param command [String]
153
+ # @return [void]
154
+ def self.run_system_task(command)
155
+ Run::Task::SystemTask.new(command).run
156
+ end
157
+
158
+ # @return [String]
159
+ def self.version
160
+ gemspec_files = Dir.glob(File.join(Dir.pwd, "*.gemspec"))
161
+
162
+ if gemspec_files.first
163
+ Gem::Specification::load(gemspec_files.first).version.to_s
164
+ else
165
+ Gemspec::Metadata.new("run_tasks").read.version
166
+ end
167
+ end
168
+
169
+ # @return [Boolean]
170
+ def self.quiet?
171
+ !!Thread.current[:quiet]
172
+ end
173
+
174
+ # @param quiet [Boolean]
93
175
  # @return [void]
94
- def self.run_system_task(name)
95
- Run::Task::SystemTask.new(name).run
176
+ def self.with_quiet_context(quiet)
177
+ already_quiet = quiet?
178
+ Thread.current[:quiet] = quiet || already_quiet
179
+
180
+ prev_stdout = nil
181
+ prev_stderr = nil
182
+
183
+ if quiet && !already_quiet
184
+ prev_stdout = $stdout.dup
185
+ prev_stderr = $stderr.dup
186
+ $stdout.reopen(File::NULL)
187
+ $stderr.reopen(File::NULL)
188
+ end
189
+
190
+ yield
191
+ rescue Interrupt
192
+ raise unless Thread.current[:quiet]
193
+ ensure
194
+ if prev_stdout
195
+ $stdout.reopen(prev_stdout)
196
+ $stderr.reopen(prev_stderr)
197
+ prev_stdout.close
198
+ prev_stderr.close
199
+ end
200
+ Thread.current[:quiet] = already_quiet
96
201
  end
97
202
  end
98
203
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Error
3
5
  class Aborted < StandardError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Error
3
5
  class ExistingTask < StandardError
@@ -1,6 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Error
3
5
  class NonExistingRunfile < StandardError
6
+ def initialize
7
+ path = ENV['RUNFILE'] || 'Runfile.rb'
8
+ super File.file?(path) ? "Runfile.rb does not exist in '#{Dir.pwd}'" : "'#{path}' is not a valid Runfile"
9
+ end
4
10
  end
5
11
  end
6
12
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Run
4
+ module Error
5
+ class NonExistingTask < StandardError
6
+ # @param name [String]
7
+ def initialize(name)
8
+ super "'#{name}' does not exist"
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Error
3
5
  class ReservedTaskName < StandardError
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Run
4
+ module Error
5
+ class RunfileVersionMismatch < StandardError
6
+ def initialize
7
+ super "Runfile version mismatches Run version"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Error
3
5
  class UnknownTask < StandardError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Helper
3
5
  class AreYouSureHelper
@@ -17,7 +19,7 @@ module Run
17
19
 
18
20
  # @return [String, Nil]
19
21
  def answer
20
- STDIN.gets.chomp.downcase.chars.first
22
+ STDIN.gets&.chomp&.downcase&.chars&.first || "n"
21
23
  end
22
24
  end
23
25
  end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Run
4
+ module Helper
5
+ class BindHelper
6
+ TERM_TIMEOUT = 3
7
+ COLORS = %i[cyan magenta yellow green blue]
8
+
9
+ def initialize(*names)
10
+ @names = names
11
+ end
12
+
13
+ def run
14
+ pgids = {}
15
+ threads = []
16
+ mutex = Mutex.new
17
+ shutting_down = false
18
+
19
+ shutdown = lambda do
20
+ return if shutting_down
21
+ shutting_down = true
22
+ pgids.each_value { |pgid| Process.kill('TERM', -pgid) rescue nil }
23
+ Thread.new do
24
+ sleep TERM_TIMEOUT
25
+ pgids.each_value { |pgid| Process.kill('KILL', -pgid) rescue nil }
26
+ end
27
+ end
28
+
29
+ Signal.trap('INT') { shutdown.call }
30
+ Signal.trap('TERM') { shutdown.call }
31
+
32
+ @names.each_with_index do |name, index|
33
+ prefix = "[#{name}]".send(COLORS[index % COLORS.size])
34
+ rd, wr = IO.pipe
35
+
36
+ pid = fork do
37
+ rd.close
38
+ $stdout.reopen(wr)
39
+ $stderr.reopen(wr)
40
+ wr.close
41
+ Process.setpgrp
42
+ Signal.trap('TERM') { exit 0 }
43
+ begin
44
+ Run::Core.run(name)
45
+ exit 0
46
+ rescue SystemExit => e
47
+ exit e.status
48
+ rescue Exception
49
+ exit 1
50
+ end
51
+ end
52
+
53
+ wr.close
54
+ pgids[name] = pid
55
+
56
+ threads << Thread.new(rd, prefix) do |pipe, pre|
57
+ pipe.each_line { |line| mutex.synchronize { STDOUT.write("#{pre} #{line}") } }
58
+ pipe.close
59
+ end
60
+ end
61
+
62
+ begin
63
+ loop do
64
+ Process.wait(-1)
65
+ shutdown.call
66
+ end
67
+ rescue Errno::ECHILD
68
+ end
69
+
70
+ threads.each(&:join)
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Helper
3
5
  class CatchInterruptionHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "shellwords"
2
4
 
3
5
  module Run
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "tty-prompt"
2
4
 
3
5
  module Run
@@ -1,10 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Helper
3
5
  class PauseHelper
6
+ DEFAULT_TEXT = "Press enter to continue"
7
+
8
+ # @param text [String]
9
+ def initialize(text = DEFAULT_TEXT)
10
+ raise ArgumentError.new("'text' must be a String") if !text.is_a?(String)
11
+
12
+ @text = text
13
+ end
14
+
4
15
  # @return [void]
5
16
  def run
17
+ print "#{@text}..."
6
18
  STDIN.gets("\n")
7
19
  end
8
20
  end
9
21
  end
10
- end
22
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "tty-prompt"
2
4
 
3
5
  module Run
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Helper
3
5
  class WaitForInterruptionHelper
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Task
3
5
  class BlockTask
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Run
2
4
  module Task
3
5
  class SystemTask
@@ -6,10 +8,8 @@ module Run
6
8
  @command = command
7
9
  end
8
10
 
9
- # @param arguments [Array] (unused)
10
- # @param options [Hash] (unused)
11
11
  # @return [void]
12
- def run(*arguments, **options)
12
+ def run
13
13
  puts ">".bright_blue + " #{@command}".bright_white
14
14
  puts
15
15
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: run_tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aurélien Delogu
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-05 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: tty-prompt
@@ -24,7 +23,48 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: 0.23.1
27
- description:
26
+ - !ruby/object:Gem::Dependency
27
+ name: rb_monkey
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: 0.1.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: 0.1.0
40
+ - !ruby/object:Gem::Dependency
41
+ name: rb_gemspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: 0.1.0
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 0.1.0
54
+ - !ruby/object:Gem::Dependency
55
+ name: rb_markdown
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 0.1.0
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 0.1.0
28
68
  email: aurelien.delogu@gmail.com
29
69
  executables:
30
70
  - run
@@ -32,23 +72,19 @@ extensions: []
32
72
  extra_rdoc_files: []
33
73
  files:
34
74
  - bin/run
75
+ - completions/_run
35
76
  - src/bootstrap.rb
36
- - src/gemspec/metadata.rb
37
- - src/markdown/abstract_tag.rb
38
- - src/markdown/bold_tag.rb
39
- - src/markdown/code_tag.rb
40
- - src/markdown/engine.rb
41
- - src/markdown/italic_tag.rb
42
- - src/monkey/string.rb
43
77
  - src/run/core.rb
44
78
  - src/run/core/help.rb
45
- - src/run/core/verify_run_version.rb
46
79
  - src/run/error/aborted.rb
47
80
  - src/run/error/existing_task.rb
48
81
  - src/run/error/non_existing_runfile.rb
82
+ - src/run/error/non_existing_task.rb
49
83
  - src/run/error/reserved_task_name.rb
84
+ - src/run/error/runfile_version_mismatch.rb
50
85
  - src/run/error/unknown_task.rb
51
86
  - src/run/helper/are_you_sure_helper.rb
87
+ - src/run/helper/bind_helper.rb
52
88
  - src/run/helper/catch_interruption_helper.rb
53
89
  - src/run/helper/expand_helper.rb
54
90
  - src/run/helper/menu_helper.rb
@@ -57,15 +93,10 @@ files:
57
93
  - src/run/helper/wait_for_interruption_helper.rb
58
94
  - src/run/task/block_task.rb
59
95
  - src/run/task/system_task.rb
60
- - src/version/local_gemspec_version.rb
61
- - src/version/remote_gemspec_version.rb
62
- - src/version/semver.rb
63
- - src/version/unreachable_error.rb
64
96
  homepage: https://github.com/pyrsmk/run
65
97
  licenses:
66
98
  - MIT
67
99
  metadata: {}
68
- post_install_message:
69
100
  rdoc_options: []
70
101
  require_paths:
71
102
  - lib
@@ -80,9 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
111
  - !ruby/object:Gem::Version
81
112
  version: '0'
82
113
  requirements: []
83
- rubyforge_project:
84
- rubygems_version: 2.6.14.4
85
- signing_key:
114
+ rubygems_version: 4.0.11
86
115
  specification_version: 4
87
- summary: Easy project management for the masses
116
+ summary: Task runner for the masses
88
117
  test_files: []
@@ -1,23 +0,0 @@
1
- module Gemspec
2
- class Metadata
3
- # @param lib_name [String]
4
- def initialize(lib_name)
5
- @lib_name = lib_name
6
- end
7
-
8
- # @return [Hash]
9
- def read
10
- @_read ||= (
11
- path = "#{__dir__}/../../#{@lib_name}.gemspec"
12
-
13
- # Development.
14
- if File.exist?(path)
15
- Gem::Specification::load(path)
16
- # Production.
17
- else
18
- Gem::Specification::find_by_name(@lib_name) rescue nil
19
- end
20
- )
21
- end
22
- end
23
- end
@@ -1,47 +0,0 @@
1
- module Markdown
2
- module AbstractTag
3
- def initialize(value)
4
- case value
5
- when String
6
- @string = value
7
- when AbstractTag
8
- @tag = value
9
- else
10
- raise ArgumentError.new("Invalid value of '#{value.class.name}' class")
11
- end
12
- end
13
-
14
- def to_ansi
15
- string = @string || @tag.to_ansi
16
- pattern = Regexp.new(
17
- "(^|.+?)" \
18
- "(?:#{tokens.map{ |token| "\\" + token.chars.join("\\") }.join('|')})" \
19
- "(.+?)" \
20
- "(?:#{tokens.map{ |token| "\\" + token.chars.join("\\") }.join('|')})" \
21
- "($|.+)"
22
- )
23
-
24
- loop do
25
- string = string.sub(pattern) do
26
- Regexp.last_match[1] + convert(Regexp.last_match[2]) + Regexp.last_match[3]
27
- end
28
- break if !Regexp.last_match
29
- end
30
-
31
- string
32
- end
33
-
34
- protected
35
-
36
- # @return [Array<String>]
37
- def tokens
38
- raise NotImplementedError.new
39
- end
40
-
41
- # @param string [String]
42
- # @return [String]
43
- def convert(string)
44
- raise NotImplementedError.new
45
- end
46
- end
47
- end
@@ -1,20 +0,0 @@
1
- require_relative "./abstract_tag"
2
-
3
- module Markdown
4
- class BoldTag
5
- include AbstractTag
6
-
7
- protected
8
-
9
- # @return [Array<String>]
10
- def tokens
11
- ["**", "__"]
12
- end
13
-
14
- # @param string [String]
15
- # @return [String]
16
- def convert(string)
17
- string.bold
18
- end
19
- end
20
- end
@@ -1,20 +0,0 @@
1
- require_relative "./abstract_tag"
2
-
3
- module Markdown
4
- class CodeTag
5
- include AbstractTag
6
-
7
- protected
8
-
9
- # @return [Array<String>]
10
- def tokens
11
- ["`"]
12
- end
13
-
14
- # @param string [String]
15
- # @return [String]
16
- def convert(string)
17
- string.cyan
18
- end
19
- end
20
- end
@@ -1,17 +0,0 @@
1
- require_relative "./bold_tag"
2
- require_relative "./code_tag"
3
- require_relative "./italic_tag"
4
-
5
- module Markdown
6
- class Engine
7
- def initialize(string)
8
- @string = string
9
- end
10
-
11
- def to_ansi
12
- # The tags are ordered by priority.
13
- # For example: `Bold` should run before `Italic`.
14
- CodeTag.new(ItalicTag.new(BoldTag.new(@string))).to_ansi
15
- end
16
- end
17
- end
@@ -1,20 +0,0 @@
1
- require_relative "./abstract_tag"
2
-
3
- module Markdown
4
- class ItalicTag
5
- include AbstractTag
6
-
7
- protected
8
-
9
- # @return [Array<String>]
10
- def tokens
11
- ["*", "_"]
12
- end
13
-
14
- # @param string [String]
15
- # @return [String]
16
- def convert(string)
17
- string.italic
18
- end
19
- end
20
- end
data/src/monkey/string.rb DELETED
@@ -1,55 +0,0 @@
1
- # https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
2
- class String
3
- @@styles = {
4
- :bold => "1",
5
- :dim => "2",
6
- :italic => "3",
7
- :underline => "4",
8
- :inverse => "7",
9
- :strikethrough => "9",
10
- :fg_black => "30",
11
- :fg_red => "31",
12
- :fg_green => "32",
13
- :fg_yellow => "33",
14
- :fg_blue => "34",
15
- :fg_magenta => "35",
16
- :fg_cyan => "36",
17
- :fg_white => "37",
18
- :fg_bright_black => "30;1",
19
- :fg_bright_red => "31;1",
20
- :fg_bright_green => "32;1",
21
- :fg_bright_yellow => "33;1",
22
- :fg_bright_blue => "34;1",
23
- :fg_bright_magenta => "35;1",
24
- :fg_bright_cyan => "36;1",
25
- :fg_bright_white => "37;1",
26
- }
27
-
28
- # To be able to handle more styles (with method chaining) we should refactor this.
29
- def stylize(style)
30
- "\033[#{@@styles[style.to_sym]}m#{self}\033[0m"
31
- end
32
-
33
- def bold; stylize(:bold); end
34
- def dim; stylize(:dim); end
35
- def italic; stylize(:italic); end
36
- def underline; stylize(:underline); end
37
- def inverse; stylize(:inverse); end
38
- def strikethrough; stylize(:strikethrough); end
39
- def black; stylize(:fg_black); end
40
- def red; stylize(:fg_red); end
41
- def green; stylize(:fg_green); end
42
- def yellow; stylize(:fg_yellow); end
43
- def blue; stylize(:fg_blue); end
44
- def magenta; stylize(:fg_magenta); end
45
- def cyan; stylize(:fg_cyan); end
46
- def white; stylize(:fg_white); end
47
- def bright_black; stylize(:fg_bright_black); end
48
- def bright_red; stylize(:fg_bright_red); end
49
- def bright_green; stylize(:fg_bright_green); end
50
- def bright_yellow; stylize(:fg_bright_yellow); end
51
- def bright_blue; stylize(:fg_bright_blue); end
52
- def bright_magenta; stylize(:fg_bright_magenta); end
53
- def bright_cyan; stylize(:fg_bright_cyan); end
54
- def bright_white; stylize(:fg_bright_white); end
55
- end
@@ -1,23 +0,0 @@
1
- module Run
2
- module Core
3
- module VerifyRunVersion
4
- REMOTE_GEMSPEC_URL = "https://raw.githubusercontent.com/pyrsmk/run/master/run_tasks.gemspec"
5
-
6
- # @return [void]
7
- def self.run
8
- local_version = Version::Semver.new(
9
- Version::LocalGemspecVersion.new(Gemspec::Metadata.new("run_tasks")).extract
10
- )
11
- remote_version = Version::Semver.new(
12
- Version::RemoteGemspecVersion.new(REMOTE_GEMSPEC_URL).extract
13
- )
14
- if local_version.major == remote_version.major && local_version < remote_version
15
- puts
16
- puts " A new version of Run is available: #{remote_version.major}.#{remote_version.minor}.#{remote_version.patch}".bright_yellow
17
- puts " Please update with: `gem update run_tasks`".bright_yellow
18
- end
19
- rescue Version::UnreachableError
20
- end
21
- end
22
- end
23
- end
@@ -1,13 +0,0 @@
1
- module Version
2
- class LocalGemspecVersion
3
- # @param gemspec [Gemspec::Metadata]
4
- def initialize(gemspec)
5
- @gemspec = gemspec
6
- end
7
-
8
- # @return [String]
9
- def extract
10
- @gemspec.read.version.to_s
11
- end
12
- end
13
- end
@@ -1,23 +0,0 @@
1
- require "open-uri"
2
- require "socket"
3
-
4
- module Version
5
- class RemoteGemspecVersion
6
- # @param url [String]
7
- def initialize(url)
8
- @url = url
9
- end
10
-
11
- # @return [String]
12
- def extract
13
- # `mode` and `perm` options are not used, but we need to set them in order to set
14
- # options...
15
- contents = URI.parse(@url).open("r", 0666, open_timeout: 1, read_timeout: 1).read
16
- matches = /^\s*s.version\s*=\s*"(.+?)"\s*$/.match(contents)
17
- raise UnreachableError.new if matches.nil?
18
- matches[1]
19
- rescue SocketError
20
- raise UnreachableError.new
21
- end
22
- end
23
- end
@@ -1,62 +0,0 @@
1
- module Version
2
- class Semver
3
- # @return [String]
4
- def initialize(version)
5
- match = /^(\d+)\.(\d+)\.(\d+)$/.match(version)
6
- raise ArgumentError.new("Invalid SEMVER number") if match.nil?
7
-
8
- @major = match[1].to_i
9
- @minor = match[2].to_i
10
- @patch = match[3].to_i
11
- end
12
-
13
- # @return [Integer]
14
- def major
15
- @major
16
- end
17
-
18
- # @return [Integer]
19
- def minor
20
- @minor
21
- end
22
-
23
- # @return [Integer]
24
- def patch
25
- @patch
26
- end
27
-
28
- # @return [Boolean]
29
- def <(semver)
30
- is_a_semver?(semver)
31
-
32
- @major < semver.major ||
33
- (@major == semver.major && @minor < semver.minor) ||
34
- (@major == semver.major && @minor == semver.minor && @patch < semver.patch)
35
- end
36
-
37
- # @return [Boolean]
38
- def >(semver)
39
- is_a_semver?(semver)
40
-
41
- @major > semver.major ||
42
- (@major == semver.major && @minor > semver.minor) ||
43
- (@major == semver.major && @minor == semver.minor && @patch > semver.patch)
44
- end
45
-
46
- # @return [Boolean]
47
- def ==(semver)
48
- is_a_semver?(semver)
49
-
50
- @major == semver.major && @minor == semver.minor && @patch == semver.patch
51
- end
52
-
53
- private
54
-
55
- # @return [Boolean]
56
- def is_a_semver?(semver)
57
- if !semver.respond_to?(:major) || !semver.respond_to?(:minor) || !semver.respond_to?(:patch)
58
- raise ArgumentError.new("SemVer compatible object expected")
59
- end
60
- end
61
- end
62
- end
@@ -1,4 +0,0 @@
1
- module Version
2
- class UnreachableError < StandardError
3
- end
4
- end