tinyci 0.4.2 → 0.5

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.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tinyci/runner'
2
4
  require 'tinyci/subprocesses'
3
5
  require 'tinyci/git_utils'
@@ -7,82 +9,82 @@ module TinyCI
7
9
  # Manages the execution of test jobs. Responsible for deciding which
8
10
  # commits need to be built and tested. Also manages the pidfile. This
9
11
  # is the main entrypoint for TinyCI.
10
- #
12
+ #
11
13
  # @attr_reader [String] working_dir The working directory to execute against
12
14
  class Scheduler
13
15
  include Subprocesses
14
16
  include Logging
15
17
  include GitUtils
16
-
18
+
17
19
  attr_reader :working_dir
18
-
20
+
19
21
  # Constructor, allows injection of configuration and custom {Runner} class.
20
22
  # Config params are passed to {Runner} instances.
21
- #
23
+ #
22
24
  # @param working_dir [String] The working directory to execute against
23
25
  # @param logger [Logger] Logger object
24
26
  # @param commit [String] specific git object to run against
25
27
  # @param runner_class [TinyCI::Runner] Injection of {Runner} dependency
26
28
  def initialize(
27
- working_dir: nil,
28
- logger: nil,
29
- commit: nil,
30
- runner_class: Runner
31
- )
32
-
29
+ working_dir: nil,
30
+ logger: nil,
31
+ commit: nil,
32
+ runner_class: Runner
33
+ )
34
+
33
35
  @working_dir = working_dir || repo_root
34
36
  @logger = logger
35
37
  @runner_class = runner_class
36
38
  @commit = commit
37
39
  end
38
-
40
+
39
41
  # Runs the TinyCI system against the relevant commits. Also sets up the pidfile.
40
- #
42
+ #
41
43
  # @return [Boolean] `true` if all commits built and tested successfully, `false` otherwise
42
44
  def run!
43
45
  pid = PidFile.new(pidfile: 'tinyci.pid', piddir: @working_dir)
44
-
46
+
45
47
  result = if @commit
46
- run_commit get_commit @commit
47
- else
48
- run_all_commits
49
- end
50
-
48
+ run_commit get_commit @commit
49
+ else
50
+ run_all_commits
51
+ end
52
+
51
53
  pid.release
52
-
54
+
53
55
  result
54
56
  end
55
-
57
+
56
58
  private
57
-
59
+
58
60
  # Git objects to be executed against, all those without a tinyci tag
59
- #
61
+ #
60
62
  # @return [Array<String>] the sha1 hashes in reverse order of creation time
61
- def get_commits
63
+ def retrieve_commits
62
64
  log = execute(git_cmd('log', '--notes=tinyci*', '--format=%H %ct %N§§§', '--reverse'))
63
- lines = log.split("§§§")
64
-
65
- lines.map {|l| format_commit_data(l)}.select {|c| c[:result].nil?}
65
+ lines = log.split('§§§')
66
+
67
+ lines.map { |l| format_commit_data(l) }.select { |c| c[:result].nil? }
66
68
  end
67
-
69
+
68
70
  def get_commit(sha)
69
- data = execute(git_cmd('show', '--quiet', '--notes=tinyci*', "--format=%H %ct", sha))
70
-
71
+ data = execute(git_cmd('show', '--quiet', '--notes=tinyci*', '--format=%H %ct', sha))
72
+
71
73
  format_commit_data(data)
72
74
  end
73
-
75
+
74
76
  # Instantiates {Runner} for a given git object, runs it, and stores the result
75
77
  def run_commit(commit)
76
78
  result = @runner_class.new(
77
- working_dir: @working_dir,
79
+ working_dir: @working_dir,
78
80
  commit: commit[:sha],
79
81
  time: commit[:time],
80
82
  logger: @logger
81
83
  ).run!
82
-
84
+
83
85
  set_result(commit, result)
84
86
  end
85
-
87
+
86
88
  def format_commit_data(data)
87
89
  parts = data.split(' ')
88
90
  {
@@ -91,22 +93,22 @@ module TinyCI
91
93
  result: parts[2]
92
94
  }
93
95
  end
94
-
96
+
95
97
  # Repeatedly gets the list of eligable commits and runs TinyCI against them until there are no more remaining
96
98
  def run_all_commits
97
- commits = get_commits
98
-
99
- until commits.empty? do
100
- commits.each {|c| run_commit(c)}
101
-
102
- commits = get_commits
99
+ commits = retrieve_commits
100
+
101
+ until commits.empty?
102
+ commits.each { |c| run_commit(c) }
103
+
104
+ commits = retrieve_commits
103
105
  end
104
106
  end
105
-
107
+
106
108
  # Stores the result in a git note
107
109
  def set_result(commit, result)
108
110
  result_message = result ? 'success' : 'failure'
109
-
111
+
110
112
  execute git_cmd('notes', '--ref', 'tinyci-result', 'add', '-m', result_message, commit[:sha])
111
113
  end
112
114
  end
@@ -1,44 +1,48 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'open3'
4
+ require 'English'
2
5
  require 'tinyci/logging'
3
6
 
4
7
  module TinyCI
5
8
  # Methods for executing subprocesses in various ways and collecting the results.
6
9
  module Subprocesses
7
-
8
10
  # Synchronously execute a command as a subprocess and return the output.
9
- #
11
+ #
10
12
  # @param [Array<String>] command The command line
11
13
  # @param [String] label A label for debug and logging purposes
12
- #
14
+ #
13
15
  # @return [String] The output of the command
14
16
  # @raise [SubprocessError] if the subprocess returns status > 0
15
17
  def execute(*command, label: nil)
16
- output, status = Open3.capture2(*command.flatten)
17
-
18
+ stdout, stderr, status = Open3.capture3(*command.flatten)
19
+
18
20
  log_debug caller[0]
19
21
  log_debug "CMD: #{command.join(' ')}"
20
- log_debug "OUT: #{output}"
21
-
22
+ log_debug "OUT: #{stdout}"
23
+ log_debug "ERR: #{stderr}"
24
+
22
25
  unless status.success?
23
- log_error output
26
+ log_error stdout
27
+ log_error stderr
24
28
  raise SubprocessError.new(label, command.join(' '), status)
25
29
  end
26
-
27
- output.chomp
30
+
31
+ stdout.chomp
28
32
  end
29
-
33
+
30
34
  # Synchronously execute a chain multiple commands piped into each other as a
31
35
  # subprocess and return the output.
32
- #
36
+ #
33
37
  # @param [Array<Array<String>>] commands The command lines
34
38
  # @param [String] label A label for debug and logging purposes
35
- #
39
+ #
36
40
  # @return [String] The output of the command
37
41
  # @raise [SubprocessError] if the subprocess returns status > 0
38
42
  def execute_pipe(*commands, label: nil)
39
43
  stdout, waiters = Open3.pipeline_r(*commands)
40
44
  output = stdout.read
41
-
45
+
42
46
  waiters.each_with_index do |waiter, i|
43
47
  status = waiter.value
44
48
  unless status.success?
@@ -46,67 +50,72 @@ module TinyCI
46
50
  raise SubprocessError.new(label, commands[i].join(' '), status)
47
51
  end
48
52
  end
49
-
53
+
50
54
  output.chomp
51
55
  end
52
-
56
+
53
57
  # Synchronously execute a command as a subprocess and and stream the output
54
58
  # to `STDOUT`
55
- #
59
+ #
56
60
  # @param [Array<String>] command The command line
57
61
  # @param [String] label A label for debug and logging purposes
58
62
  # @param [String] pwd Optionally specify a different working directory in which to execute the command
59
- #
63
+ #
60
64
  # @return [TrueClass] `true` if the command executed successfully
61
65
  # @raise [SubprocessError] if the subprocess returns status > 0
62
66
  def execute_stream(*command, label: nil, pwd: nil)
63
67
  opts = {}
64
68
  opts[:chdir] = pwd unless pwd.nil?
65
-
69
+
66
70
  log_debug "CMD: #{command.join(' ')}"
67
-
71
+
68
72
  Open3.popen2e(command.join(' '), opts) do |stdin, stdout_and_stderr, wait_thr|
69
73
  stdin.close
70
-
74
+
71
75
  until stdout_and_stderr.closed? || stdout_and_stderr.eof?
72
76
  line = stdout_and_stderr.gets
73
77
  log_info line.chomp
74
78
  $stdout.flush
75
79
  end
76
-
77
- unless wait_thr.value.success?
80
+
81
+ unless wait_thr.value.success?
78
82
  raise SubprocessError.new(label, command.join(' '), wait_thr.value)
79
83
  end
84
+
85
+ ensure
80
86
  stdout_and_stderr.close
81
87
  end
82
-
88
+
83
89
  true
84
90
  end
85
-
91
+
92
+ def execute_and_return_status(command)
93
+ system(*command, out: File::NULL, err: File::NULL)
94
+
95
+ $CHILD_STATUS
96
+ end
97
+
86
98
  # An error raised when any of the {Subprocesses} methods fail
87
- #
99
+ #
88
100
  # @attr_reader [Integer] status The return code of the process
89
101
  # @attr_reader [String] command The command used to spawn the process
90
102
  class SubprocessError < RuntimeError
91
103
  attr_reader :status
92
104
  attr_reader :command
93
-
105
+
94
106
  def initialize(label, command, status, message = "#{label}: `#{command}` failed with status #{status.exitstatus}")
95
107
  @status = status
96
108
  @command = command
97
109
  super(message)
98
110
  end
99
111
  end
100
-
101
- private
102
-
112
+
103
113
  def self.included(base)
104
114
  base.include TinyCI::Logging
105
115
  end
106
-
116
+
107
117
  def self.extended(base)
108
118
  base.extend TinyCI::Logging
109
119
  end
110
-
111
120
  end
112
121
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TinyCI
2
4
  module Symbolize
3
5
  # recursively make all keys of `hash` into symbols
@@ -7,7 +9,7 @@ module TinyCI
7
9
  hash.each { |key, value| h[key.to_sym] = map_value(value) }
8
10
  end
9
11
  end
10
-
12
+
11
13
  def map_value(thing)
12
14
  case thing
13
15
  when Hash
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tinyci/executor'
2
4
 
3
5
  module TinyCI
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'tinyci/executor'
2
4
 
3
5
  module TinyCI
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TinyCI
2
- VERSION = "0.4.2"
4
+ VERSION = '0.5'
3
5
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'redcarpet'
2
4
 
3
5
  # This file fixes the mismatch between how github handles links to other
@@ -27,7 +29,13 @@ class CompatMarkdown
27
29
 
28
30
  def initialize(text)
29
31
  renderer = YARD::Templates::Helpers::HtmlHelper::MDLinkRenderer.new
30
- markdown = Redcarpet::Markdown.new(renderer, no_intra_emphasis: true, gh_blockcode: true, fenced_code_blocks: true, autolink: true)
32
+ opts = {
33
+ no_intra_emphasis: true,
34
+ gh_blockcode: true,
35
+ fenced_code_blocks: true,
36
+ autolink: true
37
+ }
38
+ markdown = Redcarpet::Markdown.new(renderer, opts)
31
39
  @to_html = markdown.render(text)
32
40
  end
33
41
  end
@@ -1,50 +1,57 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
3
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
5
  require 'tinyci/version'
5
6
 
6
7
  Gem::Specification.new do |spec|
7
- spec.name = "tinyci"
8
+ spec.name = 'tinyci'
8
9
  spec.version = TinyCI::VERSION
9
- spec.authors = ["Jonathan Davies"]
10
- spec.email = ["jonnie@cleverna.me"]
10
+ spec.authors = ['Jonathan Davies']
11
+ spec.email = ['jonnie@cleverna.me']
11
12
 
12
- desc = "A minimal Continuous Integration system, written in ruby, powered by git"
13
+ desc = 'A minimal Continuous Integration system, written in ruby, powered by git'
13
14
  spec.summary = desc
14
15
  spec.description = desc
15
- spec.homepage = "https://github.com/JonnieCache/tinyci"
16
- spec.license = "MIT"
16
+ spec.homepage = 'https://github.com/JonnieCache/tinyci'
17
+ spec.license = 'MIT'
17
18
 
18
19
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
20
  # to allow pushing to a single host or delete this section to allow pushing to any host.
20
21
  if spec.respond_to?(:metadata)
21
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
22
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
22
23
  else
23
- raise "RubyGems 2.0 or newer is required to protect against " \
24
- "public gem pushes."
24
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
25
+ 'public gem pushes.'
25
26
  end
26
27
 
27
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
29
  f.match(%r{^(test|spec|features)/})
29
30
  end
30
- spec.executables = ["tinyci"]
31
- spec.require_paths = ["lib"]
31
+ spec.executables = ['tinyci']
32
+ spec.require_paths = ['lib']
32
33
 
33
34
  LOGO = File.read(File.expand_path('lib/tinyci/logo.txt', __dir__))
34
35
 
35
36
  spec.post_install_message = (LOGO % TinyCI::VERSION) + "\n"
36
37
 
37
- spec.add_development_dependency 'rspec', '>= 3.8.0'
38
+ spec.add_dependency 'file-tail'
39
+ spec.add_dependency 'git_clone_url'
40
+ spec.add_dependency 'net-ssh'
41
+
42
+ spec.add_development_dependency 'awesome_print'
38
43
  spec.add_development_dependency 'barrier'
39
- spec.add_development_dependency 'rake'
40
- spec.add_development_dependency 'yard'
41
- spec.add_development_dependency 'redcarpet'
44
+ spec.add_development_dependency 'fuubar'
45
+ spec.add_development_dependency 'guard-rspec'
46
+ spec.add_development_dependency 'pry'
42
47
  spec.add_development_dependency 'pry-byebug'
43
48
  spec.add_development_dependency 'pry-doc'
44
- spec.add_development_dependency 'pry'
45
- spec.add_development_dependency 'guard-rspec'
46
- spec.add_development_dependency 'fuubar'
47
- spec.add_development_dependency 'simplecov'
49
+ spec.add_development_dependency 'rake'
50
+ spec.add_development_dependency 'redcarpet'
51
+ spec.add_development_dependency 'rspec', '>= 3.8.0'
48
52
  spec.add_development_dependency 'rspec-nc'
53
+ spec.add_development_dependency 'rubocop'
54
+ spec.add_development_dependency 'simplecov'
49
55
  spec.add_development_dependency 'terminal-notifier', '1.7.2'
56
+ spec.add_development_dependency 'yard'
50
57
  end
metadata CHANGED
@@ -1,29 +1,71 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tinyci
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: '0.5'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Davies
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-28 00:00:00.000000000 Z
11
+ date: 2020-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rspec
14
+ name: file-tail
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.8.0
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: git_clone_url
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-ssh
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: awesome_print
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
20
62
  type: :development
21
63
  prerelease: false
22
64
  version_requirements: !ruby/object:Gem::Requirement
23
65
  requirements:
24
66
  - - ">="
25
67
  - !ruby/object:Gem::Version
26
- version: 3.8.0
68
+ version: '0'
27
69
  - !ruby/object:Gem::Dependency
28
70
  name: barrier
29
71
  requirement: !ruby/object:Gem::Requirement
@@ -39,7 +81,7 @@ dependencies:
39
81
  - !ruby/object:Gem::Version
40
82
  version: '0'
41
83
  - !ruby/object:Gem::Dependency
42
- name: rake
84
+ name: fuubar
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
87
  - - ">="
@@ -53,7 +95,7 @@ dependencies:
53
95
  - !ruby/object:Gem::Version
54
96
  version: '0'
55
97
  - !ruby/object:Gem::Dependency
56
- name: yard
98
+ name: guard-rspec
57
99
  requirement: !ruby/object:Gem::Requirement
58
100
  requirements:
59
101
  - - ">="
@@ -67,7 +109,7 @@ dependencies:
67
109
  - !ruby/object:Gem::Version
68
110
  version: '0'
69
111
  - !ruby/object:Gem::Dependency
70
- name: redcarpet
112
+ name: pry
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
115
  - - ">="
@@ -109,7 +151,7 @@ dependencies:
109
151
  - !ruby/object:Gem::Version
110
152
  version: '0'
111
153
  - !ruby/object:Gem::Dependency
112
- name: pry
154
+ name: rake
113
155
  requirement: !ruby/object:Gem::Requirement
114
156
  requirements:
115
157
  - - ">="
@@ -123,7 +165,7 @@ dependencies:
123
165
  - !ruby/object:Gem::Version
124
166
  version: '0'
125
167
  - !ruby/object:Gem::Dependency
126
- name: guard-rspec
168
+ name: redcarpet
127
169
  requirement: !ruby/object:Gem::Requirement
128
170
  requirements:
129
171
  - - ">="
@@ -137,7 +179,21 @@ dependencies:
137
179
  - !ruby/object:Gem::Version
138
180
  version: '0'
139
181
  - !ruby/object:Gem::Dependency
140
- name: fuubar
182
+ name: rspec
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: 3.8.0
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: 3.8.0
195
+ - !ruby/object:Gem::Dependency
196
+ name: rspec-nc
141
197
  requirement: !ruby/object:Gem::Requirement
142
198
  requirements:
143
199
  - - ">="
@@ -151,7 +207,7 @@ dependencies:
151
207
  - !ruby/object:Gem::Version
152
208
  version: '0'
153
209
  - !ruby/object:Gem::Dependency
154
- name: simplecov
210
+ name: rubocop
155
211
  requirement: !ruby/object:Gem::Requirement
156
212
  requirements:
157
213
  - - ">="
@@ -165,7 +221,7 @@ dependencies:
165
221
  - !ruby/object:Gem::Version
166
222
  version: '0'
167
223
  - !ruby/object:Gem::Dependency
168
- name: rspec-nc
224
+ name: simplecov
169
225
  requirement: !ruby/object:Gem::Requirement
170
226
  requirements:
171
227
  - - ">="
@@ -192,6 +248,20 @@ dependencies:
192
248
  - - '='
193
249
  - !ruby/object:Gem::Version
194
250
  version: 1.7.2
251
+ - !ruby/object:Gem::Dependency
252
+ name: yard
253
+ requirement: !ruby/object:Gem::Requirement
254
+ requirements:
255
+ - - ">="
256
+ - !ruby/object:Gem::Version
257
+ version: '0'
258
+ type: :development
259
+ prerelease: false
260
+ version_requirements: !ruby/object:Gem::Requirement
261
+ requirements:
262
+ - - ">="
263
+ - !ruby/object:Gem::Version
264
+ version: '0'
195
265
  description: A minimal Continuous Integration system, written in ruby, powered by
196
266
  git
197
267
  email:
@@ -222,6 +292,7 @@ files:
222
292
  - lib/tinyci/builders/script_builder.rb
223
293
  - lib/tinyci/builders/test_builder.rb
224
294
  - lib/tinyci/cli.rb
295
+ - lib/tinyci/cli_ssh_delegator.rb
225
296
  - lib/tinyci/compactor.rb
226
297
  - lib/tinyci/config.rb
227
298
  - lib/tinyci/config_transformer.rb
@@ -229,9 +300,11 @@ files:
229
300
  - lib/tinyci/git_utils.rb
230
301
  - lib/tinyci/hookers/script_hooker.rb
231
302
  - lib/tinyci/installer.rb
303
+ - lib/tinyci/log_viewer.rb
232
304
  - lib/tinyci/logging.rb
233
305
  - lib/tinyci/logo.txt
234
306
  - lib/tinyci/multi_logger.rb
307
+ - lib/tinyci/path_utils.rb
235
308
  - lib/tinyci/runner.rb
236
309
  - lib/tinyci/scheduler.rb
237
310
  - lib/tinyci/subprocesses.rb
@@ -248,7 +321,7 @@ metadata:
248
321
  allowed_push_host: https://rubygems.org
249
322
  post_install_message: " _____ _ _____ _____\n/__ (_)_ __ _ _ /
250
323
  ___/ /_ _/\n | || | '_ \\| | | |/ / / /\n | || | | | | |_| / /___/\\/ /_
251
- \ \n |_||_|_| |_|\\__, \\____/\\____/ 0.4.2\n |___/\n\n"
324
+ \ \n |_||_|_| |_|\\__, \\____/\\____/ 0.5\n |___/\n\n"
252
325
  rdoc_options: []
253
326
  require_paths:
254
327
  - lib