miniexec 0.0.7 → 0.2.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.
Files changed (5) hide show
  1. checksums.yaml +4 -4
  2. data/bin/miniexec +5 -6
  3. data/lib/miniexec.rb +139 -101
  4. data/lib/util.rb +19 -0
  5. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e698920b225de9f23f4eb3822d1eec118aaed8a64c7a4b3b517d90866af47f46
4
- data.tar.gz: 7b4b0b8a671528ac4872286aff9451988ab820fd11179a7750dde0f1648755af
3
+ metadata.gz: 6097e4b1de5d81d2e75d5817d9fd6a722ad40c9add96a1ff817cbbe64614f5e2
4
+ data.tar.gz: 9d639bc96b249926a411662b4a54027ebbb8381bcda243b56261eb5ed7530077
5
5
  SHA512:
6
- metadata.gz: f261c258a1c28e15ef19bae2a620e7301274c58c1f0672e3f45623e43a7f506572502ef6a4f4582aebe6047818e01220465710372d0e1d8bf51f94ba00dd90dd
7
- data.tar.gz: ef7e02e019e1405be2baed577db4812e6ae828d20f2685f3903fdf8d6355e06722e8875ee92539bd664ea0763dcb94241fd35c5027af7e4b2a59316924707349
6
+ metadata.gz: 042ac22614eedb274be50d0cbac5f13e1e8bc05bf8421559206dcf433365b205b892dab091aff64dd07ec7da2e782a9a0f4e63ecc0341424032d56a8070755a0
7
+ data.tar.gz: b62dd5d5dddd6e0abb6057052b25cdcef90f9167c068e7821de9f928a215e06b57593fdced5c3b92f686254afe95cf37fb320cec3aa88531b34e02988840937e
data/bin/miniexec CHANGED
@@ -5,7 +5,6 @@ lib = File.expand_path('../lib', __dir__)
5
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
  require 'miniexec'
7
7
  require 'optparse'
8
- require 'pry'
9
8
 
10
9
  options = {
11
10
  binds: [],
@@ -45,9 +44,9 @@ end.parse!
45
44
 
46
45
  raise OptionParser::MissingArgument, 'Specify a job with -j' if options[:job].nil?
47
46
 
48
- MiniExec.config(project_path: options[:path])
49
- exec = MiniExec.new options[:job],
50
- docker_url: options[:docker],
51
- binds: options[:binds],
52
- env: options[:env]
47
+ MiniExec::MiniExec.config(project_path: options[:path])
48
+ exec = MiniExec::MiniExec.new options[:job],
49
+ docker_url: options[:docker],
50
+ binds: options[:binds],
51
+ env: options[:env]
53
52
  exec.run_job
data/lib/miniexec.rb CHANGED
@@ -1,121 +1,159 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Main class
4
- class MiniExec
5
- require 'logger'
6
- require 'docker-api'
7
- require 'json'
8
- require 'tempfile'
9
- require 'yaml'
10
- require 'git'
11
- # Class instance variables
12
- @project_path = '.'
13
- @workflow_file = '.gitlab-ci.yml'
4
+ module MiniExec
5
+ class MiniExec
6
+ require 'logger'
7
+ require 'docker-api'
8
+ require 'json'
9
+ require 'tempfile'
10
+ require 'yaml'
11
+ require 'git'
12
+ require_relative './util'
13
+ # Class instance variables
14
+ @project_path = '.'
15
+ @workflow_file = '.gitlab-ci.yml'
14
16
 
15
- class << self
16
- attr_accessor :project_path, :workflow_file
17
- end
17
+ class << self
18
+ attr_accessor :project_path, :workflow_file
19
+ end
18
20
 
19
- def self.config(project_path: @project_path, workflow_file: @workflow_file)
20
- @project_path = project_path
21
- @workflow_file = workflow_file
22
- self
23
- end
21
+ def self.config(project_path: @project_path, workflow_file: @workflow_file)
22
+ @project_path = project_path
23
+ @workflow_file = workflow_file
24
+ self
25
+ end
24
26
 
25
- attr_accessor :script
27
+ attr_accessor :script
28
+ attr_reader :runlog
26
29
 
27
- def initialize(job,
28
- project_path: self.class.project_path,
29
- docker_url: nil,
30
- binds: [],
31
- env: {})
32
- @job_name = job
33
- @project_path = project_path
34
- @workflow = YAML.load(File.read("#{@project_path}/#{MiniExec.workflow_file}"))
35
- @job = @workflow[job]
36
- @job['name'] = job
37
- @default_image = @workflow['image'] || 'debian:buster-slim'
38
- @image = set_job_image
39
- @script = compile_script
40
- @binds = binds
41
- @env = env.merge gitlab_env, variables
42
- configure_logger
43
- Docker.options[:read_timeout] = 6000
44
- Docker.url = docker_url if docker_url
45
- end
30
+ def initialize(job,
31
+ project_path: self.class.project_path,
32
+ docker_url: nil,
33
+ binds: [],
34
+ env: {},
35
+ mount_cwd: true)
36
+ @job_name = job
37
+ @project_path = project_path
38
+ @workflow = YAML.load(File.read("#{@project_path}/#{MiniExec.workflow_file}"))
39
+ @job = @workflow[job]
40
+ @job['name'] = job
41
+ @default_image = @workflow['image'] || 'debian:buster-slim'
42
+ @image = set_job_image
43
+ @entrypoint = set_job_entrypoint
44
+ @binds = binds
45
+ @mount_cwd = mount_cwd
46
+ @env = {}
47
+ [
48
+ env,
49
+ gitlab_env,
50
+ @workflow['variables'],
51
+ @job['variables']
52
+ ].each do |var_set|
53
+ @env.merge!(var_set.transform_values { |v| Util.expand_var(v.to_s, @env) }) if var_set
54
+ end
55
+ @script = compile_script
56
+ @runlog = []
57
+ configure_logger
58
+ Docker.options[:read_timeout] = 6000
59
+ Docker.url = docker_url if docker_url
60
+ end
61
+
62
+ def run_job
63
+ script_path = "/tmp/#{@job['name']}.sh"
64
+ @logger.info "Fetching image #{@image}"
65
+ Docker::Image.create(fromImage: @image)
66
+ @logger.info 'Image fetched'
46
67
 
47
- def run_job
48
- script_path = "/tmp/#{@job['name']}.sh"
49
- @logger.info "Fetching image #{@image}"
50
- Docker::Image.create(fromImage: @image)
51
- @logger.info 'Image fetched'
52
- Dir.chdir(@project_path) do
53
- @logger.info 'Creating container'
54
- container = Docker::Container.create(
55
- Cmd: ['/bin/bash', script_path],
56
- Image: @image,
57
- Volumes: @binds.map { |b| { b => { path_parent: 'rw' } } }.inject(:merge),
58
- Env: @env.map { |k, v| "#{k}=#{v}" }
59
- )
60
- container.store_file(script_path, @script)
61
- container.start({ Binds: [@binds] })
62
- container.tap(&:start).attach { |_, chunk| puts chunk }
68
+ config = Docker::Image.get(@image).info['Config']
69
+ working_dir = if config['WorkingDir'].empty?
70
+ '/gitlab'
71
+ else
72
+ config['WorkingDir']
73
+ end
74
+ binds = @binds
75
+ binds.push "#{ENV['PWD']}:#{working_dir}" if @mount_cwd
76
+ Dir.chdir(@project_path) do
77
+ @logger.info 'Creating container'
78
+ container = Docker::Container.create(
79
+ Image: @image,
80
+ Cmd: ['/usr/bin/env', 'bash', script_path],
81
+ WorkingDir: working_dir,
82
+ Entrypoint: @entrypoint,
83
+ Volumes: @binds.map { |b| { b => { path_parent: 'rw' } } }.inject(:merge),
84
+ Env: @env.map { |k, v| "#{k}=#{v}" }
85
+ )
86
+ container.store_file(script_path, @script)
87
+ container.start({ Binds: [@binds] })
88
+ container.tap(&:start).attach { |_, chunk| puts chunk; @runlog.push chunk}
89
+ @logger.info 'Job finished. Removing container.'
90
+ # After running, we want to remove the container.
91
+ container.remove
92
+ @logger.info 'Container removed.'
93
+ end
63
94
  end
64
- end
65
95
 
66
- private
96
+ private
67
97
 
68
- def set_job_image
69
- return @job['image'] if @job['image']
98
+ def set_job_image
99
+ if @job['image']
100
+ image = @job['image'] if @job['image'].instance_of?(String)
101
+ image = @job['image']['name'] if @job['image'].instance_of?(Hash)
102
+ end
70
103
 
71
- @default_image
72
- end
104
+ image || @default_image
105
+ end
73
106
 
74
- # Set gitlab's predefined env vars as per
75
- # https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
76
- def gitlab_env
77
- g = Git.open(@project_path)
78
- commit = g.gcommit 'HEAD'
79
- tag = g.tags.find { |t| t.objectish == commit.sha }
80
- commit_branch = g.branch.name
81
- if tag.nil?
82
- ref_name = g.branch.name
83
- commit_tag = nil
84
- else
85
- ref_name = tag.name
86
- commit_tag = ref_name
107
+ def set_job_entrypoint
108
+ @job['image']['entrypoint'] if @job['image'].instance_of?(Hash)
87
109
  end
88
- {
89
- 'CI': true,
90
- 'CI_COMMIT_REF_SHA': commit.sha,
91
- 'CI_COMMIT_SHORT_SHA': commit.sha[0, 8],
92
- 'CI_COMMIT_REF_NAME': ref_name,
93
- 'CI_COMMIT_BRANCH': commit_branch,
94
- 'CI_COMMIT_TAG': commit_tag,
95
- 'CI_COMMIT_MESSAGE': commit.message,
96
- 'CI_COMMIT_REF_PROTECTED': false,
97
- 'CI_COMMIT_TIMESTAMP': commit.date.strftime('%FT%T')
98
- }.transform_keys(&:to_s)
99
- end
100
110
 
101
- def variables
102
- globals = @workflow['variables'] || {}
103
- job_locals = @job['variables'] || {}
104
- globals.merge job_locals
105
- end
111
+ # Set gitlab's predefined env vars as per
112
+ # https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
113
+ def gitlab_env
114
+ g = Git.open(@project_path)
115
+ commit = g.gcommit 'HEAD'
116
+ tag = g.tags.find { |t| t.objectish == commit.sha }
117
+ commit_branch = g.branch.name
118
+ if tag.nil?
119
+ ref_name = g.branch.name
120
+ commit_tag = nil
121
+ else
122
+ ref_name = tag.name
123
+ commit_tag = ref_name
124
+ end
125
+ {
126
+ 'CI': true,
127
+ 'CI_COMMIT_REF_SHA': commit.sha,
128
+ 'CI_COMMIT_SHORT_SHA': commit.sha[0, 8],
129
+ 'CI_COMMIT_REF_NAME': ref_name,
130
+ 'CI_COMMIT_BRANCH': commit_branch,
131
+ 'CI_COMMIT_TAG': commit_tag,
132
+ 'CI_COMMIT_MESSAGE': commit.message,
133
+ 'CI_COMMIT_REF_PROTECTED': false,
134
+ 'CI_COMMIT_TIMESTAMP': commit.date.strftime('%FT%T')
135
+ }.transform_keys(&:to_s)
136
+ end
106
137
 
107
- def configure_logger
108
- @logger = Logger.new($stdout)
109
- @logger.formatter = proc do |severity, _, _, msg|
110
- "[#{severity}]: #{msg}\n"
138
+ def variables
139
+ globals = @workflow['variables'] || {}
140
+ job_locals = @job['variables'] || {}
141
+ globals.merge job_locals
142
+ end
143
+
144
+ def configure_logger
145
+ @logger = Logger.new($stdout)
146
+ @logger.formatter = proc do |severity, _, _, msg|
147
+ "[#{severity}]: #{msg}\n"
148
+ end
149
+ @logger.level = ENV['LOGLEVEL'] || Logger::INFO
111
150
  end
112
- @logger.level = ENV['LOGLEVEL'] || Logger::INFO
113
- end
114
151
 
115
- def compile_script
116
- before_script = @job['before_script'] || ''
117
- script = @job['script'] || ''
118
- after_script = @job['after_script'] || ''
119
- [before_script, script, after_script].flatten.join("\n").strip
152
+ def compile_script
153
+ before_script = @job['before_script'] || ''
154
+ script = @job['script'] || ''
155
+ after_script = @job['after_script'] || ''
156
+ [before_script, script, after_script].flatten.join("\n").strip
157
+ end
120
158
  end
121
159
  end
data/lib/util.rb ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniExec
4
+ module Util
5
+ # Given a string and an env, replace any instance of shell-style variables
6
+ # with their value in the env. NOT POSIX COMPLIANT, just mostly hacky so
7
+ # I can get gitlab-ci.yml parsing to work properly. Required in MiniExec
8
+ # because of https://docs.gitlab.com/ee/ci/variables/where_variables_can_be_used.html#gitlab-internal-variable-expansion-mechanism
9
+ def self.expand_var(string, env)
10
+ # Match group 1 = the text to replace
11
+ # Match group 2 = the key from env we want to replace it with
12
+ regex = /(\${?(\w+)}?)/
13
+ string.scan(regex).uniq.each do |match|
14
+ string.gsub! match[0], env[match[1]].to_s
15
+ end
16
+ string
17
+ end
18
+ end
19
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miniexec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Martin Pugh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-16 00:00:00.000000000 Z
11
+ date: 2021-05-07 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A minimal interpretor/executor for .gitlab-ci.yml
14
14
  email: pugh@s3kr.it
@@ -19,6 +19,7 @@ extra_rdoc_files: []
19
19
  files:
20
20
  - bin/miniexec
21
21
  - lib/miniexec.rb
22
+ - lib/util.rb
22
23
  homepage: https://github.com/s3krit/miniexec
23
24
  licenses:
24
25
  - AGPL-3.0-or-later