tinyci 0.4 → 0.5.1
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/.gitignore +2 -2
- data/.rspec +2 -0
- data/.rubocop.yml +17 -2
- data/.ruby-version +1 -1
- data/.tinyci.yml +3 -1
- data/.yardopts +1 -0
- data/CHANGELOG.md +5 -0
- data/Dockerfile +3 -2
- data/Gemfile +2 -1
- data/Gemfile.lock +67 -39
- data/Guardfile +5 -3
- data/Rakefile +3 -1
- data/bin/tinyci +3 -2
- data/lib/pidfile.rb +28 -39
- data/lib/tinyci/builders/script_builder.rb +2 -0
- data/lib/tinyci/builders/test_builder.rb +3 -1
- data/lib/tinyci/cli.rb +159 -78
- data/lib/tinyci/cli_ssh_delegator.rb +111 -0
- data/lib/tinyci/compactor.rb +26 -26
- data/lib/tinyci/config.rb +22 -23
- data/lib/tinyci/config_transformer.rb +14 -16
- data/lib/tinyci/executor.rb +14 -12
- data/lib/tinyci/git_utils.rb +87 -18
- data/lib/tinyci/hookers/script_hooker.rb +26 -27
- data/lib/tinyci/installer.rb +28 -21
- data/lib/tinyci/log_viewer.rb +87 -0
- data/lib/tinyci/logging.rb +5 -5
- data/lib/tinyci/multi_logger.rb +30 -21
- data/lib/tinyci/path_utils.rb +44 -0
- data/lib/tinyci/runner.rb +95 -78
- data/lib/tinyci/scheduler.rb +44 -42
- data/lib/tinyci/subprocesses.rb +41 -32
- data/lib/tinyci/symbolize.rb +3 -1
- data/lib/tinyci/testers/script_tester.rb +2 -0
- data/lib/tinyci/testers/test_tester.rb +2 -0
- data/lib/tinyci/version.rb +3 -1
- data/lib/yard_plugin.rb +9 -1
- data/tinyci.gemspec +30 -22
- metadata +106 -19
    
        data/lib/tinyci/runner.rb
    CHANGED
    
    | @@ -1,5 +1,8 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'tinyci/subprocesses'
         | 
| 2 4 | 
             
            require 'tinyci/git_utils'
         | 
| 5 | 
            +
            require 'tinyci/path_utils'
         | 
| 3 6 | 
             
            require 'tinyci/logging'
         | 
| 4 7 | 
             
            require 'tinyci/config'
         | 
| 5 8 |  | 
| @@ -14,178 +17,191 @@ require 'fileutils' | |
| 14 17 |  | 
| 15 18 | 
             
            module TinyCI
         | 
| 16 19 | 
             
              # Responsible for managing the running of TinyCI against a single git object.
         | 
| 17 | 
            -
              # | 
| 18 | 
            -
              # @attr builder [TinyCI::Executor] Returns the Builder object. Used solely for testing | 
| 19 | 
            -
              # @attr tester [TinyCI::Executor] Returns the Tester object. Used solely for testing | 
| 20 | 
            +
              #
         | 
| 21 | 
            +
              # @attr builder [TinyCI::Executor] Returns the Builder object. Used solely for testing.
         | 
| 22 | 
            +
              # @attr tester [TinyCI::Executor] Returns the Tester object. Used solely for testing.
         | 
| 20 23 | 
             
              class Runner
         | 
| 21 24 | 
             
                include Subprocesses
         | 
| 22 25 | 
             
                include GitUtils
         | 
| 26 | 
            +
                include PathUtils
         | 
| 23 27 | 
             
                include Logging
         | 
| 24 | 
            -
             | 
| 28 | 
            +
             | 
| 29 | 
            +
                CONFIG_FILENAME = '.tinyci.yml'
         | 
| 30 | 
            +
             | 
| 25 31 | 
             
                attr_accessor :builder, :tester, :hooker
         | 
| 26 | 
            -
             | 
| 32 | 
            +
             | 
| 27 33 | 
             
                # Constructor, allows injection of generic configuration params.
         | 
| 28 | 
            -
                # | 
| 34 | 
            +
                #
         | 
| 29 35 | 
             
                # @param working_dir [String] The working directory to execute against.
         | 
| 30 36 | 
             
                # @param commit [String] SHA1 of git object to run against
         | 
| 31 37 | 
             
                # @param logger [Logger] Logger object
         | 
| 32 | 
            -
                # @param time [Time] Override time of object creation. Used solely for testing | 
| 33 | 
            -
                # @param config [Hash] Override TinyCI config object, normally loaded from `.tinyci` file. | 
| 38 | 
            +
                # @param time [Time] Override time of object creation. Used solely for testing.
         | 
| 39 | 
            +
                # @param config [Hash] Override TinyCI config object, normally loaded from `.tinyci` file.
         | 
| 40 | 
            +
                # Used solely for testing.
         | 
| 34 41 | 
             
                def initialize(working_dir: '.', commit:, time: nil, logger: nil, config: nil)
         | 
| 35 42 | 
             
                  @working_dir = working_dir
         | 
| 36 | 
            -
                  @logger = logger
         | 
| 37 43 | 
             
                  @config = config
         | 
| 38 44 | 
             
                  @commit = commit
         | 
| 39 | 
            -
                  @time = time | 
| 45 | 
            +
                  @time = time
         | 
| 46 | 
            +
                  @logger = logger.is_a?(MultiLogger) ? MultiLogger.new(quiet: logger.quiet) : nil
         | 
| 47 | 
            +
                  ensure_path target_path
         | 
| 48 | 
            +
                  setup_log
         | 
| 40 49 | 
             
                end
         | 
| 41 | 
            -
             | 
| 50 | 
            +
             | 
| 42 51 | 
             
                # Runs the TinyCI system against the single git object referenced in `@commit`.
         | 
| 43 | 
            -
                # | 
| 52 | 
            +
                #
         | 
| 44 53 | 
             
                # @return [Boolean] `true` if the commit was built and tested successfully, `false` otherwise
         | 
| 54 | 
            +
                # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
         | 
| 45 55 | 
             
                def run!
         | 
| 46 56 | 
             
                  begin
         | 
| 47 | 
            -
                    ensure_path target_path
         | 
| 48 | 
            -
                    setup_log
         | 
| 49 | 
            -
                    
         | 
| 50 57 | 
             
                    log_info "Commit: #{@commit}"
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                     | 
| 58 | 
            +
             | 
| 59 | 
            +
                    unless config_exists?
         | 
| 60 | 
            +
                      log_error "No config found for #{@commit}"
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      return false
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                    log_info 'Cleaning...'
         | 
| 53 66 | 
             
                    clean
         | 
| 54 | 
            -
             | 
| 55 | 
            -
                    log_info  | 
| 67 | 
            +
             | 
| 68 | 
            +
                    log_info 'Exporting...'
         | 
| 56 69 | 
             
                    ensure_path export_path
         | 
| 57 70 | 
             
                    export
         | 
| 58 | 
            -
             | 
| 71 | 
            +
             | 
| 59 72 | 
             
                    begin
         | 
| 60 | 
            -
                       | 
| 73 | 
            +
                      config
         | 
| 61 74 | 
             
                    rescue ConfigMissingError => e
         | 
| 62 75 | 
             
                      log_error e.message
         | 
| 63 76 | 
             
                      log_error 'Removing export...'
         | 
| 64 77 | 
             
                      clean
         | 
| 65 | 
            -
             | 
| 78 | 
            +
             | 
| 66 79 | 
             
                      return false
         | 
| 67 80 | 
             
                    end
         | 
| 68 81 | 
             
                    @builder ||= instantiate_builder
         | 
| 69 82 | 
             
                    @tester  ||= instantiate_tester
         | 
| 70 83 | 
             
                    @hooker  ||= instantiate_hooker
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                    log_info  | 
| 84 | 
            +
             | 
| 85 | 
            +
                    log_info 'Building...'
         | 
| 73 86 | 
             
                    run_hook! :before_build
         | 
| 74 87 | 
             
                    begin
         | 
| 75 88 | 
             
                      @builder.build
         | 
| 76 | 
            -
                    rescue => e
         | 
| 89 | 
            +
                    rescue StandardError => e
         | 
| 77 90 | 
             
                      run_hook! :after_build_failure
         | 
| 78 | 
            -
             | 
| 91 | 
            +
             | 
| 79 92 | 
             
                      raise e if ENV['TINYCI_ENV'] == 'test'
         | 
| 80 | 
            -
             | 
| 93 | 
            +
             | 
| 81 94 | 
             
                      log_error e
         | 
| 82 95 | 
             
                      log_debug e.backtrace
         | 
| 83 | 
            -
             | 
| 96 | 
            +
             | 
| 84 97 | 
             
                      return false
         | 
| 85 98 | 
             
                    else
         | 
| 86 99 | 
             
                      run_hook! :after_build_success
         | 
| 87 100 | 
             
                    ensure
         | 
| 88 101 | 
             
                      run_hook! :after_build
         | 
| 89 102 | 
             
                    end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    
         | 
| 92 | 
            -
                    log_info "Testing..."
         | 
| 103 | 
            +
             | 
| 104 | 
            +
                    log_info 'Testing...'
         | 
| 93 105 | 
             
                    run_hook! :before_test
         | 
| 94 106 | 
             
                    begin
         | 
| 95 107 | 
             
                      @tester.test
         | 
| 96 | 
            -
                    rescue => e
         | 
| 108 | 
            +
                    rescue StandardError => e
         | 
| 97 109 | 
             
                      run_hook! :after_test_failure
         | 
| 98 | 
            -
             | 
| 110 | 
            +
             | 
| 99 111 | 
             
                      raise e if ENV['TINYCI_ENV'] == 'test'
         | 
| 100 | 
            -
             | 
| 112 | 
            +
             | 
| 101 113 | 
             
                      log_error e
         | 
| 102 114 | 
             
                      log_debug e.backtrace
         | 
| 103 | 
            -
             | 
| 115 | 
            +
             | 
| 104 116 | 
             
                      return false
         | 
| 105 117 | 
             
                    else
         | 
| 106 118 | 
             
                      run_hook! :after_test_success
         | 
| 107 119 | 
             
                    ensure
         | 
| 108 120 | 
             
                      run_hook! :after_test
         | 
| 109 121 | 
             
                    end
         | 
| 110 | 
            -
             | 
| 122 | 
            +
             | 
| 111 123 | 
             
                    log_info "Finished #{@commit}"
         | 
| 112 | 
            -
                  rescue => e
         | 
| 124 | 
            +
                  rescue StandardError => e
         | 
| 113 125 | 
             
                    raise e if ENV['TINYCI_ENV'] == 'test'
         | 
| 114 | 
            -
             | 
| 126 | 
            +
             | 
| 115 127 | 
             
                    log_error e
         | 
| 116 128 | 
             
                    log_debug e.backtrace
         | 
| 117 129 | 
             
                    return false
         | 
| 118 130 | 
             
                  ensure
         | 
| 119 131 | 
             
                    run_hook! :after_all
         | 
| 120 132 | 
             
                  end
         | 
| 121 | 
            -
             | 
| 133 | 
            +
             | 
| 122 134 | 
             
                  true
         | 
| 123 135 | 
             
                end
         | 
| 124 | 
            -
                
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                def target_path
         | 
| 127 | 
            -
                  File.absolute_path("#{@working_dir}/builds/#{@time.to_i}_#{@commit}/")
         | 
| 128 | 
            -
                end
         | 
| 129 | 
            -
                
         | 
| 130 | 
            -
                # Build the export path
         | 
| 131 | 
            -
                def export_path
         | 
| 132 | 
            -
                  File.join(target_path, 'export')
         | 
| 133 | 
            -
                end
         | 
| 134 | 
            -
                
         | 
| 136 | 
            +
                # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/MethodLength, Metrics/CyclomaticComplexity
         | 
| 137 | 
            +
             | 
| 135 138 | 
             
                private
         | 
| 136 | 
            -
             | 
| 139 | 
            +
             | 
| 137 140 | 
             
                def run_hook!(name)
         | 
| 138 141 | 
             
                  return unless @hooker
         | 
| 139 | 
            -
             | 
| 142 | 
            +
             | 
| 140 143 | 
             
                  @hooker.send("#{name}!")
         | 
| 141 144 | 
             
                end
         | 
| 142 | 
            -
             | 
| 145 | 
            +
             | 
| 143 146 | 
             
                # Creates log file if it doesnt exist
         | 
| 144 147 | 
             
                def setup_log
         | 
| 145 148 | 
             
                  return unless @logger.is_a? MultiLogger
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  @logger. | 
| 148 | 
            -
             | 
| 149 | 
            -
                
         | 
| 150 | 
            -
                def logfile_path
         | 
| 151 | 
            -
                  File.join(target_path, 'tinyci.log')
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                  @logger.add_output_path logfile_path
         | 
| 151 | 
            +
                  @logger.add_output_path repo_logfile_path
         | 
| 152 152 | 
             
                end
         | 
| 153 | 
            -
             | 
| 153 | 
            +
             | 
| 154 154 | 
             
                # Instantiate the Builder object according to the class named in the config
         | 
| 155 155 | 
             
                def instantiate_builder
         | 
| 156 156 | 
             
                  klass = TinyCI::Builders.const_get(@config[:builder][:class])
         | 
| 157 | 
            -
                   | 
| 157 | 
            +
                  konfig = @config[:builder][:config].merge(
         | 
| 158 | 
            +
                    target: target_path,
         | 
| 159 | 
            +
                    export: export_path,
         | 
| 160 | 
            +
                    commit: @commit,
         | 
| 161 | 
            +
                    logger: @logger
         | 
| 162 | 
            +
                  )
         | 
| 163 | 
            +
                  klass.new(konfig)
         | 
| 158 164 | 
             
                end
         | 
| 159 | 
            -
             | 
| 165 | 
            +
             | 
| 160 166 | 
             
                # Instantiate the Tester object according to the class named in the config
         | 
| 161 167 | 
             
                def instantiate_tester
         | 
| 162 168 | 
             
                  klass = TinyCI::Testers.const_get(@config[:tester][:class])
         | 
| 163 | 
            -
                   | 
| 169 | 
            +
                  konfig = @config[:tester][:config].merge(
         | 
| 170 | 
            +
                    target: target_path,
         | 
| 171 | 
            +
                    export: export_path,
         | 
| 172 | 
            +
                    commit: @commit,
         | 
| 173 | 
            +
                    logger: @logger
         | 
| 174 | 
            +
                  )
         | 
| 175 | 
            +
                  klass.new(konfig)
         | 
| 164 176 | 
             
                end
         | 
| 165 | 
            -
             | 
| 177 | 
            +
             | 
| 166 178 | 
             
                # Instantiate the Hooker object according to the class named in the config
         | 
| 167 179 | 
             
                def instantiate_hooker
         | 
| 168 180 | 
             
                  return nil unless @config[:hooker].is_a? Hash
         | 
| 169 | 
            -
             | 
| 181 | 
            +
             | 
| 170 182 | 
             
                  klass = TinyCI::Hookers.const_get(@config[:hooker][:class])
         | 
| 171 | 
            -
                   | 
| 183 | 
            +
                  konfig = @config[:hooker][:config].merge(
         | 
| 184 | 
            +
                    target: target_path,
         | 
| 185 | 
            +
                    export: export_path,
         | 
| 186 | 
            +
                    commit: @commit,
         | 
| 187 | 
            +
                    logger: @logger
         | 
| 188 | 
            +
                  )
         | 
| 189 | 
            +
                  klass.new(konfig)
         | 
| 172 190 | 
             
                end
         | 
| 173 | 
            -
             | 
| 174 | 
            -
                 | 
| 175 | 
            -
             | 
| 176 | 
            -
                  @config ||= Config.new(working_dir: export_path)
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                def config_path
         | 
| 193 | 
            +
                  File.expand_path(CONFIG_FILENAME, export_path)
         | 
| 177 194 | 
             
                end
         | 
| 178 195 |  | 
| 179 | 
            -
                 | 
| 180 | 
            -
             | 
| 181 | 
            -
                  Time.at execute(git_cmd('show', '-s', '--format=%ct', @commit)).to_i
         | 
| 196 | 
            +
                def config_exists?
         | 
| 197 | 
            +
                  file_exists_in_git? CONFIG_FILENAME
         | 
| 182 198 | 
             
                end
         | 
| 183 199 |  | 
| 184 | 
            -
                #  | 
| 185 | 
            -
                def  | 
| 186 | 
            -
                   | 
| 200 | 
            +
                # The {Config} object from the `.tinyci.yml` file in the exported directory
         | 
| 201 | 
            +
                def config
         | 
| 202 | 
            +
                  @config ||= Config.new(config_path)
         | 
| 187 203 | 
             
                end
         | 
| 188 | 
            -
             | 
| 204 | 
            +
             | 
| 189 205 | 
             
                # Delete the export path
         | 
| 190 206 | 
             
                def clean
         | 
| 191 207 | 
             
                  FileUtils.rm_rf export_path
         | 
| @@ -196,7 +212,8 @@ module TinyCI | |
| 196 212 | 
             
                # a `git export` subcommand.
         | 
| 197 213 | 
             
                # see https://stackoverflow.com/a/163769
         | 
| 198 214 | 
             
                def export
         | 
| 199 | 
            -
                  execute_pipe git_cmd('archive', '--format=tar', | 
| 215 | 
            +
                  execute_pipe git_cmd('archive', '--format=tar',
         | 
| 216 | 
            +
                                       @commit), ['tar', '-C', export_path, '-xf', '-']
         | 
| 200 217 | 
             
                end
         | 
| 201 218 | 
             
              end
         | 
| 202 219 | 
             
            end
         | 
    
        data/lib/tinyci/scheduler.rb
    CHANGED
    
    | @@ -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 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
             | 
| 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 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 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  | 
| 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*',  | 
| 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 =  | 
| 98 | 
            -
             | 
| 99 | 
            -
                  until commits.empty? | 
| 100 | 
            -
                    commits.each {|c| run_commit(c)}
         | 
| 101 | 
            -
             | 
| 102 | 
            -
                    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
         | 
    
        data/lib/tinyci/subprocesses.rb
    CHANGED
    
    | @@ -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 | 
            -
                   | 
| 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: #{ | 
| 21 | 
            -
                  
         | 
| 22 | 
            +
                  log_debug "OUT: #{stdout}"
         | 
| 23 | 
            +
                  log_debug "ERR: #{stderr}"
         | 
| 24 | 
            +
             | 
| 22 25 | 
             
                  unless status.success?
         | 
| 23 | 
            -
                    log_error  | 
| 26 | 
            +
                    log_error stdout
         | 
| 27 | 
            +
                    log_error stderr
         | 
| 24 28 | 
             
                    raise SubprocessError.new(label, command.join(' '), status)
         | 
| 25 29 | 
             
                  end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                   | 
| 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
         | 
    
        data/lib/tinyci/symbolize.rb
    CHANGED
    
    | @@ -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
         | 
    
        data/lib/tinyci/version.rb
    CHANGED
    
    
    
        data/lib/yard_plugin.rb
    CHANGED
    
    | @@ -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 | 
            -
                 | 
| 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
         |