tinyci 0.1.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.
@@ -0,0 +1,111 @@
1
+ require 'open3'
2
+ require 'tinyci/logging'
3
+
4
+ module TinyCI
5
+ # Methods for executing subprocesses in various ways and collecting the results.
6
+ module Subprocesses
7
+
8
+ # Synchronously execute a command as a subprocess and return the output.
9
+ #
10
+ # @param [Array<String>] command The command line
11
+ # @param [String] label A label for debug and logging purposes
12
+ #
13
+ # @return [String] The output of the command
14
+ # @raise [SubprocessError] if the subprocess returns status > 0
15
+ def execute(*command, label: nil)
16
+ output, status = Open3.capture2(*command.flatten)
17
+
18
+ log_debug caller[0]
19
+ log_debug "CMD: #{command.join(' ')}"
20
+ log_debug "OUT: #{output}"
21
+
22
+ unless status.success?
23
+ log_error output
24
+ raise SubprocessError.new(label, command.join(' '), status.to_i)
25
+ end
26
+
27
+ output.chomp
28
+ end
29
+
30
+ # Synchronously execute a chain multiple commands piped into each other as a
31
+ # subprocess and return the output.
32
+ #
33
+ # @param [Array<Array<String>>] commands The command lines
34
+ # @param [String] label A label for debug and logging purposes
35
+ #
36
+ # @return [String] The output of the command
37
+ # @raise [SubprocessError] if the subprocess returns status > 0
38
+ def execute_pipe(*commands, label: nil)
39
+ stdout, waiters = Open3.pipeline_r(*commands)
40
+ output = stdout.read
41
+
42
+ waiters.each_with_index do |waiter, i|
43
+ status = waiter.value
44
+ unless status.success?
45
+ log_error output
46
+ raise SubprocessError.new(label, commands[i].join(' '), status.to_i)
47
+ end
48
+ end
49
+
50
+ output.chomp
51
+ end
52
+
53
+ # Synchronously execute a command as a subprocess and and stream the output
54
+ # asynchronously to `STDOUT`
55
+ #
56
+ # @param [Array<String>] command The command line
57
+ # @param [String] label A label for debug and logging purposes
58
+ # @param [String] pwd Optionally specify a different working directory in which to execute the command
59
+ #
60
+ # @return [TrueClass] `true` if the command executed successfully
61
+ # @raise [SubprocessError] if the subprocess returns status > 0
62
+ def execute_stream(*command, label: nil, pwd: nil)
63
+ opts = {}
64
+ opts[:chdir] = pwd unless pwd.nil?
65
+
66
+ Open3.popen2e(command.join(' '), opts) do |stdin, stdout_and_stderr, wait_thr|
67
+ stdin.close
68
+
69
+ Thread.new do
70
+ stdout_and_stderr.each_line do |l|
71
+ log_info l.chomp
72
+ $stdout.flush
73
+ end
74
+ end
75
+
76
+ unless wait_thr.value.success?
77
+ raise SubprocessError.new(label, command.join(' '), wait_thr.value)
78
+ end
79
+ stdout_and_stderr.close
80
+ end
81
+
82
+ true
83
+ end
84
+
85
+ # An error raised when any of the {Subprocesses} methods fail
86
+ #
87
+ # @attr_reader [Integer] status The return code of the process
88
+ # @attr_reader [String] command The command used to spawn the process
89
+ class SubprocessError < RuntimeError
90
+ attr_reader :status
91
+ attr_reader :command
92
+
93
+ def initialize(label, command, status, message = "`#{label || command}` failed with code #{status.to_i}")
94
+ @status = status
95
+ @command = command
96
+ super(message)
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ def self.included(base)
103
+ base.include TinyCI::Logging
104
+ end
105
+
106
+ def self.extended(base)
107
+ base.extend TinyCI::Logging
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,22 @@
1
+ module TinyCI
2
+ module Symbolize
3
+ # recursively make all keys of `hash` into symbols
4
+ # @param [Hash] hash The hash
5
+ def symbolize(hash)
6
+ {}.tap do |h|
7
+ hash.each { |key, value| h[key.to_sym] = map_value(value) }
8
+ end
9
+ end
10
+
11
+ def map_value(thing)
12
+ case thing
13
+ when Hash
14
+ symbolize thing
15
+ when Array
16
+ thing.map { |v| map_value(v) }
17
+ else
18
+ thing
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,40 @@
1
+ require 'tinyci/executor'
2
+
3
+ module TinyCI
4
+ module Testers
5
+ class RktTester < TinyCI::Executor
6
+ def test
7
+ cmd = [
8
+ 'sudo',
9
+ 'rkt',
10
+ 'run',
11
+ '--net=host',
12
+ '--insecure-options=image',
13
+ '--volume',
14
+ "src,kind=host,source=#{@config[:target]}/src,readOnly=false",
15
+ '--mount',
16
+ "volume=src,target=#{@config[:src_path]}",
17
+ @config[:image],
18
+ '--working-dir',
19
+ @config[:src_path],
20
+ set_env,
21
+ '--exec',
22
+ @config[:command]
23
+ ].flatten
24
+
25
+ log_info "RKT test command: #{cmd.join(' ')}"
26
+
27
+ execute_stream(*cmd, label: 'test')
28
+
29
+ end
30
+
31
+ private
32
+
33
+ def set_env
34
+ return [] if @config[:env].nil?
35
+
36
+ @config[:env].map {|k,v| "--set-env=#{k.upcase}=#{v}"}
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ require 'tinyci/executor'
2
+
3
+ module TinyCI
4
+ module Testers
5
+ class ScriptTester < TinyCI::Executor
6
+ def test
7
+ execute_stream(script_location, label: 'test', pwd: @config[:target])
8
+ end
9
+
10
+ private
11
+
12
+ def script_location
13
+ File.join @config[:target], @config[:command]
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require 'tinyci/executor'
2
+
3
+ module TinyCI
4
+ module Testers
5
+ class TestTester < TinyCI::Executor
6
+ def test
7
+ raise 'Simulated test failed' if @config[:result] == false
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module TinyCI
2
+ VERSION = "0.1.0"
3
+ end
data/tinyci.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tinyci/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tinyci"
8
+ spec.version = TinyCI::VERSION
9
+ spec.authors = ["Jonathan Davies"]
10
+ spec.email = ["jonnie@cleverna.me"]
11
+
12
+ desc = "A minimal Continuous Integration system, written in ruby, powered by git"
13
+ spec.summary = desc
14
+ spec.description = desc
15
+ spec.homepage = "https://github.com/JonnieCache/tinyci"
16
+ spec.license = "MIT"
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against " \
24
+ "public gem pushes."
25
+ end
26
+
27
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
28
+ f.match(%r{^(test|spec|features)/})
29
+ end
30
+ spec.executables = ["tinyci"]
31
+ spec.require_paths = ["lib"]
32
+
33
+ LOGO = File.read(File.expand_path('lib/tinyci/logo.txt', __dir__))
34
+
35
+ spec.post_install_message = (LOGO % TinyCI::VERSION) + "\n"
36
+
37
+ spec.add_development_dependency "bundler", "~> 1.14"
38
+ spec.add_development_dependency 'awesome_print'
39
+ spec.add_development_dependency 'rspec'
40
+ spec.add_development_dependency 'barrier'
41
+ spec.add_development_dependency 'rake'
42
+ spec.add_development_dependency 'yard'
43
+ spec.add_development_dependency 'redcarpet'
44
+ end
metadata ADDED
@@ -0,0 +1,179 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tinyci
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jonathan Davies
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: awesome_print
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
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: barrier
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: redcarpet
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: A minimal Continuous Integration system, written in ruby, powered by
112
+ git
113
+ email:
114
+ - jonnie@cleverna.me
115
+ executables:
116
+ - tinyci
117
+ extensions: []
118
+ extra_rdoc_files: []
119
+ files:
120
+ - ".gitignore"
121
+ - ".rspec"
122
+ - ".ruby-version"
123
+ - ".yardopts"
124
+ - Gemfile
125
+ - Gemfile.lock
126
+ - Guardfile
127
+ - LICENSE
128
+ - README.md
129
+ - Rakefile
130
+ - bin/tinyci
131
+ - lib/pidfile.rb
132
+ - lib/tinyci/builders/rkt_builder.rb
133
+ - lib/tinyci/builders/script_builder.rb
134
+ - lib/tinyci/builders/test_builder.rb
135
+ - lib/tinyci/cli.rb
136
+ - lib/tinyci/config.rb
137
+ - lib/tinyci/executor.rb
138
+ - lib/tinyci/git_utils.rb
139
+ - lib/tinyci/installer.rb
140
+ - lib/tinyci/logging.rb
141
+ - lib/tinyci/logo.txt
142
+ - lib/tinyci/multi_logger.rb
143
+ - lib/tinyci/runner.rb
144
+ - lib/tinyci/scheduler.rb
145
+ - lib/tinyci/subprocesses.rb
146
+ - lib/tinyci/symbolize.rb
147
+ - lib/tinyci/testers/rkt_tester.rb
148
+ - lib/tinyci/testers/script_tester.rb
149
+ - lib/tinyci/testers/test_tester.rb
150
+ - lib/tinyci/version.rb
151
+ - tinyci.gemspec
152
+ homepage: https://github.com/JonnieCache/tinyci
153
+ licenses:
154
+ - MIT
155
+ metadata:
156
+ allowed_push_host: https://rubygems.org
157
+ post_install_message: " _____ _ _____ _____\n/__ (_)_ __ _ _ /
158
+ ___/ /_ _/\n | || | '_ \\| | | |/ / / /\n | || | | | | |_| / /___/\\/ /_
159
+ \ \n |_||_|_| |_|\\__, \\____/\\____/ 0.1.0\n |___/\n\n"
160
+ rdoc_options: []
161
+ require_paths:
162
+ - lib
163
+ required_ruby_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ requirements: []
174
+ rubyforge_project:
175
+ rubygems_version: 2.6.13
176
+ signing_key:
177
+ specification_version: 4
178
+ summary: A minimal Continuous Integration system, written in ruby, powered by git
179
+ test_files: []