offline_deployer 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e66d353f43a526988013626ca3b3d0a7e191899e
4
+ data.tar.gz: 30ec00953479ea736fcdceaa15152bbada32d191
5
+ SHA512:
6
+ metadata.gz: c98d724d69cb26af9246e48ad8369c6e7ca5ef716686a1f147a3d8477466ce523a69fcebdcd997e73bfc99900104bb549830867f7040cdc23a6f8469c0751bf4
7
+ data.tar.gz: 15210d61771f50849a0a77f339cb921056920fb96c72158bf6f3cebc05d4615e194c01fd3de7dbcea4798146e7c98db354407cdcb97e7e29c4cbe290191a791f
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in offline_deployer.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Vladimir Rosancic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,62 @@
1
+ # OfflineDeployer
2
+
3
+ **This gem is still in development.**
4
+
5
+ A gem for building releases using a git diff between 2 tags or commits. When OfflineDeployer::Maker.call is executed,
6
+ archive containing all changed files between specified tags will be created. After that, you can use
7
+ OfflineDeployer::Updater to extract those files and update your Rails application.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'offline_deployer'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install offline_deployer
24
+
25
+ ## Usage
26
+
27
+ Your application need to define few settings in initializer or environment config.
28
+ Settings can be defined using OfflineDeployer.settings method:
29
+
30
+ ```ruby
31
+ OfflineDeployer.setup do |settings|
32
+ settings.gems_source_path = `gem env gemdir`.strip # from where to copy gems
33
+ settings.app_root = Rails.root # git repository in which diff is going to be executed
34
+ settings.gems_vendor_path = 'vendor/bundle/ruby/2.2.0' # where to copy gems in release archive
35
+ settings.archive_name_prefix = 'application-name' # final archive file will be named application-name-x.x.x
36
+ settings.restart_server_command = 'touch tmp/restart.txt' # how to restart application server after update
37
+ end
38
+ ```
39
+
40
+ Also, you need to create update_script.sh in your app root folder which will be executed after update process finishes.
41
+ Example:
42
+
43
+ ```
44
+ RAILS_ENV=production bundle exec bundle install --path ./vendor/bundle --without development:test
45
+ RAILS_ENV=production bundle exec rake db:migrate
46
+ RAILS_ENV=production bundle exec rake assets:precompile
47
+ ```
48
+
49
+ ## Development
50
+
51
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
52
+
53
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
54
+
55
+ ## Contributing
56
+
57
+ Bug reports and pull requests are welcome on GitHub at https://github.com/infinum/offline_deployer.
58
+
59
+
60
+ ## License
61
+
62
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "offline_deployer"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,18 @@
1
+ require 'offline_deployer/version'
2
+ require 'offline_deployer/settings'
3
+ require 'offline_deployer/gems_handler'
4
+ require 'offline_deployer/maker'
5
+ require 'offline_deployer/tagger'
6
+ require 'offline_deployer/updater'
7
+
8
+ require 'offline_deployer/railtie' if defined?(Rails)
9
+
10
+ module OfflineDeployer
11
+ def self.setup(&block)
12
+ block.call(settings)
13
+ end
14
+
15
+ def self.settings
16
+ @settings ||= Settings.new
17
+ end
18
+ end
@@ -0,0 +1,87 @@
1
+ module OfflineDeployer
2
+ class GemsHandler
3
+ def initialize(from_tag, to_tag, app_root, zip_file_path, from_path = nil)
4
+ @zip_file_path = zip_file_path
5
+ @from_tag = from_tag
6
+ @to_tag = to_tag
7
+ @app_root = app_root
8
+ @from_path = from_path || default_from_path
9
+ @branch = current_branch
10
+ end
11
+
12
+ attr_reader :zip_file_path, :from_tag, :to_tag, :app_root, :from_path, :branch
13
+
14
+ def call
15
+ prepare_tmp_folder
16
+ copy_gems_to_tmp_folder
17
+ add_gems_to_zip
18
+ destroy_tmp_folder
19
+ checkout_branch
20
+ end
21
+
22
+ private
23
+
24
+ def default_from_path
25
+ OfflineDeployer.settings.gems_source_path
26
+ end
27
+
28
+ def tmp_release_folder
29
+ "#{app_root}/tmp/releases/gems_handler"
30
+ end
31
+
32
+ def current_branch
33
+ `cd #{app_root} && git rev-parse --abbrev-ref HEAD`
34
+ end
35
+
36
+ def prepare_tmp_folder
37
+ FileUtils.mkdir_p "#{to_path}/build_info"
38
+ FileUtils.mkdir_p "#{to_path}/cache"
39
+ FileUtils.mkdir_p "#{to_path}/gems"
40
+ FileUtils.mkdir_p "#{to_path}/specifications"
41
+ end
42
+
43
+ def to_path
44
+ "#{tmp_release_folder}/#{OfflineDeployer.settings.gems_vendor_path}"
45
+ end
46
+
47
+ def gem_list(tag)
48
+ Dir.chdir(app_root) do
49
+ `git checkout #{tag}`
50
+ Bundler.with_clean_env do
51
+ `bundle list`.gsub(' * ', '')
52
+ end
53
+ end
54
+ end
55
+
56
+ def gems
57
+ gem_list(to_tag).split("\n") - gem_list(from_tag).split("\n")
58
+ end
59
+
60
+ def copy_gems_to_tmp_folder
61
+ gems.each do |gem_spec|
62
+ name, version = gem_spec.split(' ')
63
+ version = version[1...-1]
64
+ copy_gem("#{name}-#{version}")
65
+ end
66
+ end
67
+
68
+ def copy_gem(gem_version)
69
+ `rsync --progress #{from_path}/build_info/#{gem_version}.info #{to_path}/build_info/`
70
+ `rsync --progress #{from_path}/cache/#{gem_version}.gem #{to_path}/cache/`
71
+ `rsync --progress -r #{from_path}/gems/#{gem_version} #{to_path}/gems/`
72
+ `rsync --progress #{from_path}/specifications/#{gem_version}.gemspec #{to_path}/specifications/`
73
+ end
74
+
75
+ def add_gems_to_zip
76
+ `cd #{tmp_release_folder} && zip -r #{zip_file_path} vendor/*`
77
+ end
78
+
79
+ def destroy_tmp_folder
80
+ FileUtils.remove_dir(tmp_release_folder)
81
+ end
82
+
83
+ def checkout_branch
84
+ `cd #{app_root} && git checkout #{branch}`
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,120 @@
1
+ module OfflineDeployer
2
+ class Maker
3
+ def initialize(from_tag = nil, to_tag = nil, destination = nil)
4
+ @from_tag = from_tag.presence || default_from_tag
5
+ @to_tag = to_tag.presence || default_to_tag
6
+ @destination = destination || default_destination
7
+ @long_name = from_tag.present?
8
+ end
9
+
10
+ attr_reader :from_tag, :to_tag, :destination, :long_name
11
+
12
+ def call
13
+ pull_and_bundle unless Rails.env.development?
14
+ write_update_info_and_deleted_files
15
+ add_files_to_archive
16
+ handle_gems
17
+ move_archive_to_destination
18
+ print_release_info
19
+ return_file_path_and_name
20
+ end
21
+
22
+ private
23
+
24
+ def app_root
25
+ OfflineDeployer.settings.app_root
26
+ end
27
+
28
+ def pull_and_bundle
29
+ Dir.chdir(app_root) do
30
+ `git pull`
31
+ Bundler.with_clean_env do
32
+ `bundle install`
33
+ end
34
+ end
35
+ end
36
+
37
+ def tags
38
+ Tagger.new.tag_list
39
+ end
40
+
41
+ def default_from_tag
42
+ tags[1]
43
+ end
44
+
45
+ def default_to_tag
46
+ tags[0]
47
+ end
48
+
49
+ def default_destination
50
+ "#{app_root}/tmp/releases/versions/"
51
+ end
52
+
53
+ def write_update_info_and_deleted_files
54
+ File.open('.update', 'w+') do |f|
55
+ f.write(
56
+ {
57
+ FROM_VERSION: from_tag,
58
+ TO_VERSION: to_tag,
59
+ DELETED_FILES: get_file_names(deleted_files)
60
+ }.to_yaml
61
+ )
62
+ end
63
+
64
+ `mv .update #{app_root}` unless Rails.env.development?
65
+ end
66
+
67
+ def changed_files
68
+ @changed_files ||= `cd #{app_root} && git diff --name-status #{from_tag} #{to_tag}`.split("\n")
69
+ end
70
+
71
+ def deleted_files
72
+ @deleted_files ||= changed_files.select { |file| file.starts_with?('D') }
73
+ end
74
+
75
+ def archive_files
76
+ @archive_files ||= get_file_names(changed_files - deleted_files)
77
+ end
78
+
79
+ def get_file_names(files_array)
80
+ files_array.map { |file| file.split("\t")[1] }
81
+ end
82
+
83
+ def add_files_to_archive
84
+ `cd #{app_root} && git archive -o #{archive_file_path} #{to_tag} #{archive_files.join(' ')}`
85
+ `cd #{app_root} && zip #{archive_file_path} .update`
86
+ end
87
+
88
+ def archive_file_name
89
+ long_name ? "#{archive_name_prefix}-#{from_tag}-#{to_tag}.zip" : "#{archive_name_prefix}-#{to_tag}.zip"
90
+ end
91
+
92
+ def archive_file_path
93
+ "#{app_root}/#{archive_file_name}"
94
+ end
95
+
96
+ def archive_name_prefix
97
+ OfflineDeployer.settings.archive_name_prefix
98
+ end
99
+
100
+ def handle_gems
101
+ OfflineDeployer::GemsHandler.new(from_tag, to_tag, app_root, archive_file_path).call
102
+ end
103
+
104
+ def move_archive_to_destination
105
+ FileUtils.mkdir_p destination
106
+ `mv #{archive_file_path} #{destination}`
107
+ end
108
+
109
+ def print_release_info
110
+ puts 'Added files:'
111
+ puts archive_files.join("\n")
112
+ puts "FROM_VERSION: #{from_tag}, TO_VERSION: #{to_tag}"
113
+ puts "Created #{archive_file_name} in #{destination}"
114
+ end
115
+
116
+ def return_file_path_and_name
117
+ ["#{destination}/#{archive_file_name}", archive_file_name]
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,7 @@
1
+ module OfflineDeployer
2
+ class Railtie < Rails::Railtie
3
+ rake_tasks do
4
+ load 'offline_deployer/tasks/update.rake'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ module OfflineDeployer
2
+ class Settings
3
+ attr_accessor :app_root
4
+ attr_accessor :gems_source_path
5
+ attr_accessor :gems_vendor_path
6
+ attr_accessor :archive_name_prefix
7
+ attr_accessor :restart_server_command
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ module OfflineDeployer
2
+ class Tagger
3
+ def tag(tag_name, tag_message, commit = nil)
4
+ `git tag -a #{tag_name} -m "#{tag_message}" #{commit.presence}`
5
+ puts "Created new tag - #{tag_name}"
6
+ puts 'If you want to make a new release, run release maker.'
7
+ end
8
+
9
+ def tag_list
10
+ `cd #{app_root} && git tag -l #{tag_list_sort}`.split("\n")
11
+ end
12
+
13
+ def fetch_tags
14
+ `cd #{app_root} && git fetch --tags`
15
+ end
16
+
17
+ private
18
+
19
+ def tag_list_sort
20
+ Rails.env.development? ? '--sort=-version:refname' : '| sort -Vr'
21
+ end
22
+
23
+ def app_root
24
+ OfflineDeployer.settings.app_root
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ desc 'Make update zip'
2
+ task make_update: :environment do
3
+ OfflineDeployer::Maker.new(ENV['FROM'], ENV['TO'], ENV['MV']).call
4
+ end
5
+
6
+ desc 'Create tag'
7
+ task create_tag: :environment do
8
+ OfflineDeployer::Tagger.new.tag(ENV['NAME'], ENV['MESSAGE'], ENV['COMMIT'])
9
+ end
10
+
11
+ desc 'Fetch tags'
12
+ task fetch_tags: :environment do
13
+ OfflineDeployer::Tagger.new.fetch_tags
14
+ end
@@ -0,0 +1,73 @@
1
+ module OfflineDeployer
2
+ class Updater
3
+ def initialize(zip_file)
4
+ @zip_file = zip_file
5
+ end
6
+
7
+ attr_reader :zip_file
8
+
9
+ EXTRACT_DIRECTORY = 'tmp/releases/updater'
10
+
11
+ def call
12
+ validate_zip_file
13
+ remove_old_update_files
14
+ extract_new_version
15
+ validate_version
16
+ update_changed_files
17
+ remove_deleted_files
18
+ run_update_script
19
+ finish_and_restart_server
20
+ end
21
+
22
+ private
23
+
24
+ def validate_zip_file
25
+ fail 'Invalid file' if File.extname(zip_file.original_filename) != '.zip'
26
+ end
27
+
28
+ def remove_old_update_files
29
+ FileUtils.remove_dir(EXTRACT_DIRECTORY) if File.exist?(EXTRACT_DIRECTORY)
30
+ end
31
+
32
+ def extract_new_version
33
+ `echo 'Extracting zip' >> log/update.log`
34
+ Archive::Zip.extract(zip_file.path, EXTRACT_DIRECTORY)
35
+ end
36
+
37
+ def old_version_info
38
+ YAML.load_file('.update')
39
+ end
40
+
41
+ def new_version_info
42
+ YAML.load_file(File.join(EXTRACT_DIRECTORY, '.update'))
43
+ end
44
+
45
+ def validate_version
46
+ return if old_version_info[:TO_VERSION] == new_version_info[:FROM_VERSION]
47
+ `echo 'Wrong version' >> log/update.log`
48
+ fail 'Wrong version'
49
+ end
50
+
51
+ def update_changed_files
52
+ `rsync -r -v #{EXTRACT_DIRECTORY}/* ./ >> log/update.log`
53
+ end
54
+
55
+ def remove_deleted_files
56
+ deleted_files = new_version_info[:DELETED_FILES]
57
+ return if deleted_files.nil?
58
+ deleted_files.each { |deleted_file| `rm #{deleted_file}` }
59
+ end
60
+
61
+ def run_update_script
62
+ `./update_script.sh`
63
+ errors = File.open('log/update_error.log').read
64
+ fail 'There were some errors while running the script!' if errors.present?
65
+ end
66
+
67
+ def finish_and_restart_server
68
+ `cp #{EXTRACT_DIRECTORY}/.update ./.update`
69
+ `#{OfflineDeployer.settings.restart_server_command}`
70
+ `echo 'Server restarted' >> log/update.log`
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,3 @@
1
+ module OfflineDeployer
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'offline_deployer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'offline_deployer'
8
+ spec.version = OfflineDeployer::VERSION
9
+ spec.authors = ['Vladimir Rosancic']
10
+ spec.email = ['vladimir.rosancic@infinum.hr']
11
+
12
+ spec.summary = 'A gem for building releases using a git diff between 2 tags or commits'
13
+ spec.homepage = 'https://github.com/infinum/offline_deployer'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.10'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rails'
24
+ spec.add_development_dependency 'rspec'
25
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: offline_deployer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Vladimir Rosancic
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-06-08 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.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rails
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: rspec
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
+ description:
70
+ email:
71
+ - vladimir.rosancic@infinum.hr
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".travis.yml"
79
+ - Gemfile
80
+ - LICENSE.txt
81
+ - README.md
82
+ - Rakefile
83
+ - bin/console
84
+ - bin/setup
85
+ - lib/offline_deployer.rb
86
+ - lib/offline_deployer/gems_handler.rb
87
+ - lib/offline_deployer/maker.rb
88
+ - lib/offline_deployer/railtie.rb
89
+ - lib/offline_deployer/settings.rb
90
+ - lib/offline_deployer/tagger.rb
91
+ - lib/offline_deployer/tasks/update.rake
92
+ - lib/offline_deployer/updater.rb
93
+ - lib/offline_deployer/version.rb
94
+ - offline_deployer.gemspec
95
+ homepage: https://github.com/infinum/offline_deployer
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.4.5.1
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: A gem for building releases using a git diff between 2 tags or commits
119
+ test_files: []