miniexec 0.0.9 → 0.2.2
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 +4 -4
- data/bin/miniexec +18 -6
- data/lib/miniexec.rb +141 -104
- data/lib/util.rb +19 -0
- metadata +3 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 69219c3771eda716d56f9ee00bb7d8a7a9c214b38a35d17d2c14e297a0887420
         | 
| 4 | 
            +
              data.tar.gz: 5ad61f25f0f1f757325e3e05be347ab3eae5d1861853c67ef938cc286fbdecd8
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d2413f6f8cf00ee930073576d7f82272bb46927ae74bbe0877ab49564ffe37ea2ca5b7183ae62fbc0fe58025d53877cd585b0ee30313116ab67f7aac553a71eb
         | 
| 7 | 
            +
              data.tar.gz: 6b99388154b691babbd489e16e8ea54e711b9451776e99792cdcf9e3bea8798e7295abf6eabb018d7fb5a0f99d444f0d0b481969743e996d7cd2c18a287cf6e2
         | 
    
        data/bin/miniexec
    CHANGED
    
    | @@ -10,7 +10,9 @@ options = { | |
| 10 10 | 
             
              binds: [],
         | 
| 11 11 | 
             
              env: {},
         | 
| 12 12 | 
             
              path: '.',
         | 
| 13 | 
            -
              docker: ENV['DOCKER_HOST'] || '/run/docker.sock'
         | 
| 13 | 
            +
              docker: ENV['DOCKER_HOST'] || '/run/docker.sock',
         | 
| 14 | 
            +
              cwd: true,
         | 
| 15 | 
            +
              file: nil
         | 
| 14 16 | 
             
            }
         | 
| 15 17 |  | 
| 16 18 | 
             
            OptionParser.new do |opts|
         | 
| @@ -40,13 +42,23 @@ OptionParser.new do |opts| | |
| 40 42 | 
             
                      'Location of the docker socket') do |sock|
         | 
| 41 43 | 
             
                options[:docker] = sock
         | 
| 42 44 | 
             
              end
         | 
| 45 | 
            +
              opts.on('-f', '--file FILE',
         | 
| 46 | 
            +
                      'Manually specify a custom .gitlab-ci.yml file.') do |file|
         | 
| 47 | 
            +
                options[:file] = file
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
              opts.on('-n', '--no-mount-cwd',
         | 
| 50 | 
            +
                      'Don\'t mount the CWD in the container\'s WORKDIR by default.') do
         | 
| 51 | 
            +
                options[:cwd] = false
         | 
| 52 | 
            +
              end
         | 
| 43 53 | 
             
            end.parse!
         | 
| 44 54 |  | 
| 45 55 | 
             
            raise OptionParser::MissingArgument, 'Specify a job with -j' if options[:job].nil?
         | 
| 46 56 |  | 
| 47 | 
            -
            MiniExec.config(project_path: options[:path])
         | 
| 48 | 
            -
            exec = MiniExec.new options[:job],
         | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 57 | 
            +
            MiniExec::MiniExec.config(project_path: options[:path])
         | 
| 58 | 
            +
            exec = MiniExec::MiniExec.new options[:job],
         | 
| 59 | 
            +
                                          docker_url: options[:docker],
         | 
| 60 | 
            +
                                          binds: options[:binds],
         | 
| 61 | 
            +
                                          env: options[:env],
         | 
| 62 | 
            +
                                          mount_cwd: options[:cwd] || false,
         | 
| 63 | 
            +
                                          file: options[:file]
         | 
| 52 64 | 
             
            exec.run_job
         | 
    
        data/lib/miniexec.rb
    CHANGED
    
    | @@ -1,124 +1,161 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 3 | 
             
            # Main class
         | 
| 4 | 
            -
             | 
| 5 | 
            -
               | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 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 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 17 | 
            +
                class << self
         | 
| 18 | 
            +
                  attr_accessor :project_path, :workflow_file
         | 
| 19 | 
            +
                end
         | 
| 18 20 |  | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 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 | 
            -
             | 
| 27 | 
            +
                attr_accessor :script
         | 
| 28 | 
            +
                attr_reader :runlog
         | 
| 26 29 |  | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 30 | 
            +
                def initialize(job,
         | 
| 31 | 
            +
                               project_path: self.class.project_path,
         | 
| 32 | 
            +
                               docker_url: nil,
         | 
| 33 | 
            +
                               binds: [],
         | 
| 34 | 
            +
                               env: {},
         | 
| 35 | 
            +
                               mount_cwd: true,
         | 
| 36 | 
            +
                               file: nil)
         | 
| 37 | 
            +
                  @job_name = job
         | 
| 38 | 
            +
                  @project_path = project_path
         | 
| 39 | 
            +
                  file ||= "#{@project_path}/#{MiniExec.workflow_file}"
         | 
| 40 | 
            +
                  @workflow = YAML.load(File.read(file))
         | 
| 41 | 
            +
                  @job = @workflow[job]
         | 
| 42 | 
            +
                  @job['name'] = job
         | 
| 43 | 
            +
                  @default_image = @workflow['image'] || 'debian:buster-slim'
         | 
| 44 | 
            +
                  @image = set_job_image
         | 
| 45 | 
            +
                  @entrypoint = set_job_entrypoint
         | 
| 46 | 
            +
                  @binds = binds
         | 
| 47 | 
            +
                  @mount_cwd = mount_cwd
         | 
| 48 | 
            +
                  @env = {}
         | 
| 49 | 
            +
                  [
         | 
| 50 | 
            +
                    env,
         | 
| 51 | 
            +
                    gitlab_env,
         | 
| 52 | 
            +
                    @workflow['variables'],
         | 
| 53 | 
            +
                    @job['variables']
         | 
| 54 | 
            +
                  ].each do |var_set|
         | 
| 55 | 
            +
                    @env.merge!(var_set.transform_values { |v| Util.expand_var(v.to_s, @env) }) if var_set
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                  @script = compile_script
         | 
| 58 | 
            +
                  @runlog = []
         | 
| 59 | 
            +
                  configure_logger
         | 
| 60 | 
            +
                  Docker.options[:read_timeout] = 6000
         | 
| 61 | 
            +
                  Docker.url = docker_url if docker_url
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                def run_job
         | 
| 65 | 
            +
                  script_path = "/tmp/#{@job['name']}.sh"
         | 
| 66 | 
            +
                  @logger.info "Fetching image #{@image}"
         | 
| 67 | 
            +
                  Docker::Image.create(fromImage: @image)
         | 
| 68 | 
            +
                  @logger.info 'Image fetched'
         | 
| 49 69 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
                   | 
| 57 | 
            -
                   | 
| 58 | 
            -
             | 
| 59 | 
            -
                     | 
| 60 | 
            -
                     | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 70 | 
            +
                  config = Docker::Image.get(@image).info['Config']
         | 
| 71 | 
            +
                  working_dir = if config['WorkingDir'].empty?
         | 
| 72 | 
            +
                                  '/gitlab'
         | 
| 73 | 
            +
                                else
         | 
| 74 | 
            +
                                  config['WorkingDir']
         | 
| 75 | 
            +
                                end
         | 
| 76 | 
            +
                  binds = @binds
         | 
| 77 | 
            +
                  binds.push "#{ENV['PWD']}:#{working_dir}" if @mount_cwd
         | 
| 78 | 
            +
                  Dir.chdir(@project_path) do
         | 
| 79 | 
            +
                    @logger.info 'Creating container'
         | 
| 80 | 
            +
                    container = Docker::Container.create(
         | 
| 81 | 
            +
                      Image: @image,
         | 
| 82 | 
            +
                      Cmd: ['/usr/bin/env', 'bash', script_path],
         | 
| 83 | 
            +
                      WorkingDir: working_dir,
         | 
| 84 | 
            +
                      Entrypoint: @entrypoint,
         | 
| 85 | 
            +
                      Volumes: binds.map { |b| { b => { path_parent: 'rw' } } }.inject(:merge),
         | 
| 86 | 
            +
                      Env: @env.map { |k, v| "#{k}=#{v}" }
         | 
| 87 | 
            +
                    )
         | 
| 88 | 
            +
                    container.store_file(script_path, @script)
         | 
| 89 | 
            +
                    container.start({ Binds: [@binds] })
         | 
| 90 | 
            +
                    container.tap(&:start).attach { |_, chunk| puts chunk; @runlog.push chunk}
         | 
| 91 | 
            +
                    @logger.info 'Job finished. Removing container.'
         | 
| 92 | 
            +
                    # After running, we want to remove the container.
         | 
| 93 | 
            +
                    container.remove
         | 
| 94 | 
            +
                    @logger.info 'Container removed.'
         | 
| 95 | 
            +
                  end
         | 
| 66 96 | 
             
                end
         | 
| 67 | 
            -
              end
         | 
| 68 97 |  | 
| 69 | 
            -
             | 
| 98 | 
            +
                private
         | 
| 70 99 |  | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 100 | 
            +
                def set_job_image
         | 
| 101 | 
            +
                  if @job['image']
         | 
| 102 | 
            +
                    image = @job['image'] if @job['image'].instance_of?(String)
         | 
| 103 | 
            +
                    image = @job['image']['name'] if @job['image'].instance_of?(Hash)
         | 
| 104 | 
            +
                  end
         | 
| 73 105 |  | 
| 74 | 
            -
             | 
| 75 | 
            -
             | 
| 106 | 
            +
                  image || @default_image
         | 
| 107 | 
            +
                end
         | 
| 76 108 |  | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
              def gitlab_env
         | 
| 80 | 
            -
                g = Git.open(@project_path)
         | 
| 81 | 
            -
                commit = g.gcommit 'HEAD'
         | 
| 82 | 
            -
                tag = g.tags.find { |t| t.objectish == commit.sha }
         | 
| 83 | 
            -
                commit_branch = g.branch.name
         | 
| 84 | 
            -
                if tag.nil?
         | 
| 85 | 
            -
                  ref_name = g.branch.name
         | 
| 86 | 
            -
                  commit_tag = nil
         | 
| 87 | 
            -
                else
         | 
| 88 | 
            -
                  ref_name = tag.name
         | 
| 89 | 
            -
                  commit_tag = ref_name
         | 
| 109 | 
            +
                def set_job_entrypoint
         | 
| 110 | 
            +
                  @job['image']['entrypoint'] if @job['image'].instance_of?(Hash)
         | 
| 90 111 | 
             
                end
         | 
| 91 | 
            -
                {
         | 
| 92 | 
            -
                  'CI': true,
         | 
| 93 | 
            -
                  'CI_COMMIT_REF_SHA': commit.sha,
         | 
| 94 | 
            -
                  'CI_COMMIT_SHORT_SHA': commit.sha[0, 8],
         | 
| 95 | 
            -
                  'CI_COMMIT_REF_NAME': ref_name,
         | 
| 96 | 
            -
                  'CI_COMMIT_BRANCH': commit_branch,
         | 
| 97 | 
            -
                  'CI_COMMIT_TAG': commit_tag,
         | 
| 98 | 
            -
                  'CI_COMMIT_MESSAGE': commit.message,
         | 
| 99 | 
            -
                  'CI_COMMIT_REF_PROTECTED': false,
         | 
| 100 | 
            -
                  'CI_COMMIT_TIMESTAMP': commit.date.strftime('%FT%T')
         | 
| 101 | 
            -
                }.transform_keys(&:to_s)
         | 
| 102 | 
            -
              end
         | 
| 103 112 |  | 
| 104 | 
            -
             | 
| 105 | 
            -
                 | 
| 106 | 
            -
                 | 
| 107 | 
            -
             | 
| 108 | 
            -
             | 
| 113 | 
            +
                # Set gitlab's predefined env vars as per
         | 
| 114 | 
            +
                # https://docs.gitlab.com/ee/ci/variables/predefined_variables.html
         | 
| 115 | 
            +
                def gitlab_env
         | 
| 116 | 
            +
                  g = Git.open(@project_path)
         | 
| 117 | 
            +
                  commit = g.gcommit 'HEAD'
         | 
| 118 | 
            +
                  tag = g.tags.find { |t| t.objectish == commit.sha }
         | 
| 119 | 
            +
                  commit_branch = g.branch.name
         | 
| 120 | 
            +
                  if tag.nil?
         | 
| 121 | 
            +
                    ref_name = g.branch.name
         | 
| 122 | 
            +
                    commit_tag = nil
         | 
| 123 | 
            +
                  else
         | 
| 124 | 
            +
                    ref_name = tag.name
         | 
| 125 | 
            +
                    commit_tag = ref_name
         | 
| 126 | 
            +
                  end
         | 
| 127 | 
            +
                  {
         | 
| 128 | 
            +
                    'CI': true,
         | 
| 129 | 
            +
                    'CI_COMMIT_REF_SHA': commit.sha,
         | 
| 130 | 
            +
                    'CI_COMMIT_SHORT_SHA': commit.sha[0, 8],
         | 
| 131 | 
            +
                    'CI_COMMIT_REF_NAME': ref_name,
         | 
| 132 | 
            +
                    'CI_COMMIT_BRANCH': commit_branch,
         | 
| 133 | 
            +
                    'CI_COMMIT_TAG': commit_tag,
         | 
| 134 | 
            +
                    'CI_COMMIT_MESSAGE': commit.message,
         | 
| 135 | 
            +
                    'CI_COMMIT_REF_PROTECTED': false,
         | 
| 136 | 
            +
                    'CI_COMMIT_TIMESTAMP': commit.date.strftime('%FT%T')
         | 
| 137 | 
            +
                  }.transform_keys(&:to_s)
         | 
| 138 | 
            +
                end
         | 
| 109 139 |  | 
| 110 | 
            -
             | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
                   | 
| 140 | 
            +
                def variables
         | 
| 141 | 
            +
                  globals = @workflow['variables'] || {}
         | 
| 142 | 
            +
                  job_locals = @job['variables'] || {}
         | 
| 143 | 
            +
                  globals.merge job_locals
         | 
| 144 | 
            +
                end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                def configure_logger
         | 
| 147 | 
            +
                  @logger = Logger.new($stdout)
         | 
| 148 | 
            +
                  @logger.formatter = proc do |severity, _, _, msg|
         | 
| 149 | 
            +
                    "[#{severity}]: #{msg}\n"
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
                  @logger.level = ENV['LOGLEVEL'] || Logger::INFO
         | 
| 114 152 | 
             
                end
         | 
| 115 | 
            -
                @logger.level = ENV['LOGLEVEL'] || Logger::INFO
         | 
| 116 | 
            -
              end
         | 
| 117 153 |  | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 154 | 
            +
                def compile_script
         | 
| 155 | 
            +
                  before_script = @job['before_script'] || ''
         | 
| 156 | 
            +
                  script = @job['script'] || ''
         | 
| 157 | 
            +
                  after_script = @job['after_script'] || ''
         | 
| 158 | 
            +
                  [before_script, script, after_script].flatten.join("\n").strip
         | 
| 159 | 
            +
                end
         | 
| 123 160 | 
             
              end
         | 
| 124 161 | 
             
            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. | 
| 4 | 
            +
              version: 0.2.2
         | 
| 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- | 
| 11 | 
            +
            date: 2021-06-01 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
         |