vtasks 0.0.8

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,54 @@
1
+ module Vtasks
2
+ class Docker
3
+ class Image
4
+ # Docker Build class
5
+ class Build
6
+ # Include utility modules
7
+ require 'vtasks/utils/git'
8
+ include Vtasks::Utils::Git
9
+ require 'vtasks/utils/output'
10
+ include Vtasks::Utils::Output
11
+
12
+ attr_reader :image, :path, :build_date, :build_tag
13
+
14
+ def initialize(image, path, args={})
15
+ @image ||= image
16
+ @path ||= path
17
+ @build_date ||= args.fetch(:build_date)
18
+ @build_tag ||= args.fetch(:build_tag)
19
+
20
+ @cmd = 'docker image build'
21
+ end
22
+
23
+ def without_arguments
24
+ info "Pulling #{image}" # to speed up the building process
25
+ system "docker pull #{image}" unless ENV['DOCKER_NO_CACHE']
26
+
27
+ info "Building #{image}:#{build_tag}"
28
+ system "#{@cmd} -t #{image}:#{build_tag} #{path}"
29
+
30
+ if $?.exitstatus != 0
31
+ error 'Build command failed!'
32
+ abort
33
+ end
34
+ end
35
+
36
+ def with_arguments
37
+ build_args = {
38
+ 'BUILD_DATE' => build_date,
39
+ 'VERSION' => build_tag,
40
+ 'VCS_URL' => git_url,
41
+ 'VCS_REF' => git_commit
42
+ }
43
+
44
+ build_args.map do |key, value|
45
+ @cmd += " --build-arg #{key}=#{value}"
46
+ end
47
+
48
+ without_arguments
49
+ end
50
+
51
+ end # class Build
52
+ end # class Image
53
+ end # class Docker
54
+ end # module Vtasks
@@ -0,0 +1,17 @@
1
+ module Vtasks
2
+ class Docker
3
+ class Image
4
+ # Docker Push class
5
+ class Push
6
+ # Include utility modules
7
+ require 'vtasks/utils/output'
8
+ include Vtasks::Utils::Output
9
+
10
+ def initialize(image, tag)
11
+ info "Pushing #{image}:#{tag}"
12
+ system "docker image push #{image}:#{tag}"
13
+ end
14
+ end # class Push
15
+ end # class Image
16
+ end # class Docker
17
+ end # module Vtasks
@@ -0,0 +1,17 @@
1
+ module Vtasks
2
+ class Docker
3
+ class Image
4
+ # Docker Tag module
5
+ class Tag
6
+ # Include utility modules
7
+ require 'vtasks/utils/output'
8
+ include Vtasks::Utils::Output
9
+
10
+ def initialize(image, oldtag, newtag)
11
+ info "Tagging #{image}:#{newtag}"
12
+ system "docker image tag #{image}:#{oldtag} #{image}:#{newtag}"
13
+ end
14
+ end # class Tag
15
+ end # class Image
16
+ end # class Docker
17
+ end # module Vtasks
@@ -0,0 +1,83 @@
1
+ module Vtasks
2
+ class Docker
3
+ # Docker Image class
4
+ class Image
5
+ # Include utility modules
6
+ require 'vtasks/utils/git'
7
+ include Vtasks::Utils::Git
8
+ require 'vtasks/utils/semver'
9
+ include Vtasks::Utils::Semver
10
+
11
+ # Include utility classes
12
+ require 'vtasks/docker/image/build'
13
+ require 'vtasks/docker/image/push'
14
+ require 'vtasks/docker/image/tag'
15
+
16
+ attr_reader :image, :path, :has_build_args, :tags
17
+
18
+ def initialize(image, path, args = {})
19
+ @image ||= image
20
+ @path ||= path
21
+ @has_build_args ||= args.fetch(:has_build_args, false)
22
+ end
23
+
24
+ def tags
25
+ major, minor, patch = [
26
+ semver[:major],
27
+ semver[:minor],
28
+ semver[:patch]
29
+ ].freeze
30
+ @tags = [
31
+ "#{major}.#{minor}.#{patch}",
32
+ "#{major}.#{minor}",
33
+ "#{major}",
34
+ 'latest'
35
+ ]
36
+ end
37
+
38
+ # Compose build date
39
+ def build_date
40
+ @build_date ||= ::Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
41
+ end
42
+
43
+ # Compose build tag
44
+ def build_tag
45
+ @build_tag ||= gitver.to_s
46
+ end
47
+
48
+ # Build image
49
+ def build
50
+ args = {
51
+ build_date: build_date,
52
+ build_tag: build_tag
53
+ }
54
+ build = Vtasks::Docker::Image::Build.new(image, path, args)
55
+ if has_build_args
56
+ build.with_arguments
57
+ else
58
+ build.without_arguments
59
+ end
60
+ end
61
+
62
+ # Tag image
63
+ def tag
64
+ tags.each do |tag|
65
+ Vtasks::Docker::Image::Tag.new(image, build_tag, tag)
66
+ end
67
+ end
68
+
69
+ # Build image with tags
70
+ def build_with_tags
71
+ build
72
+ tag
73
+ end
74
+
75
+ # Push image
76
+ def push
77
+ tags.each do |tag|
78
+ Vtasks::Docker::Image::Push.new(image, tag)
79
+ end
80
+ end
81
+ end # class Image
82
+ end # class Docker
83
+ end # module Vtasks
@@ -0,0 +1,124 @@
1
+ module Vtasks
2
+ require 'rake/tasklib'
3
+
4
+ # Docker tasks
5
+ class Docker < ::Rake::TaskLib
6
+ # Include utility modules
7
+ require 'vtasks/utils/output'
8
+ include Vtasks::Utils::Output
9
+ require 'vtasks/utils/system'
10
+ include Vtasks::Utils::System
11
+
12
+ # Include utility classes
13
+ require 'vtasks/docker/image'
14
+
15
+ attr_reader :args, :repo
16
+
17
+ def initialize(args = {})
18
+ @args ||= args
19
+ @repo ||= args.fetch(:repo)
20
+
21
+ check_docker
22
+ define_tasks
23
+ end
24
+
25
+ def define_tasks
26
+ namespace :docker do
27
+ list_images
28
+ garbage_collect
29
+ tasks
30
+
31
+ dockerfiles.each do |dockerfile|
32
+ path = File.basename(dockerfile)
33
+ add_namespace("#{repo}/#{path}", path)
34
+ end # dockerfiles.each
35
+ end # namespace :docker
36
+ end # def define_tasks
37
+
38
+ # Image namespace
39
+ def add_namespace(image, path)
40
+ namespace path.to_sym do |_args|
41
+ require 'rspec/core/rake_task'
42
+ ::RSpec::Core::RakeTask.new(:spec) do |task|
43
+ task.pattern = "#{path}/spec/*_spec.rb"
44
+ end
45
+
46
+ docker_image = Vtasks::Docker::Image.new(image, path, args)
47
+
48
+ lint_image(path)
49
+
50
+ desc 'Build and tag docker image'
51
+ task :build do
52
+ docker_image.build_with_tags
53
+ end
54
+
55
+ desc 'Publish docker image'
56
+ task :push do
57
+ docker_image.push
58
+ end
59
+ end
60
+ end
61
+
62
+ # Tasks
63
+ def tasks
64
+ # Run tasks one by one for all images
65
+ [:spec, :lint].each { |task_name| run_task(task_name) }
66
+ # Run tasks in parallel for all images
67
+ [:build, :push].each { |task_name| run_task_parallel(task_name) }
68
+ end
69
+
70
+ # Run a task for all images
71
+ def run_task(name)
72
+ desc "Run #{name} for all images in repository"
73
+ task name => dockerfiles
74
+ .collect { |image| "docker:#{File.basename(image)}:#{name}" }
75
+ end
76
+
77
+ # Run a task for all images in parallel
78
+ def run_task_parallel(name)
79
+ desc "Run #{name} for all images in repository in parallel"
80
+ multitask name => dockerfiles
81
+ .collect { |image| "docker:#{File.basename(image)}:#{name}" }
82
+ end
83
+
84
+ # List all folders containing Dockerfiles
85
+ def dockerfiles
86
+ @dockerfiles = Dir.glob('*').select do |dir|
87
+ File.directory?(dir) && File.exist?("#{dir}/Dockerfile")
88
+ end
89
+ end
90
+
91
+ # Check Docker is installed
92
+ def check_docker
93
+ task :docker do
94
+ raise 'These tasks require docker to be installed' unless command? 'docker'
95
+ end
96
+ end
97
+
98
+ # List all images
99
+ def list_images
100
+ desc 'List all Docker images'
101
+ task :list do
102
+ info dockerfiles.map { |image| File.basename(image) }
103
+ end
104
+ end
105
+
106
+ # Lint image
107
+ def lint_image(path)
108
+ desc 'Run Hadolint against the Dockerfile'
109
+ task :lint do
110
+ dockerfile = "#{path}/Dockerfile"
111
+ info "Running Hadolint to check the style of #{dockerfile}"
112
+ system "docker container run --rm -i lukasmartinelli/hadolint hadolint --ignore DL3008 --ignore DL3013 - < #{dockerfile}"
113
+ end
114
+ end
115
+
116
+ # Garbage collect
117
+ def garbage_collect
118
+ desc 'Garbage collect unused docker data'
119
+ task :gc do
120
+ system 'docker system prune --all --force'
121
+ end
122
+ end
123
+ end # class Docker
124
+ end # module Vtasks
@@ -0,0 +1,65 @@
1
+ module Vtasks
2
+ require 'rake/tasklib'
3
+
4
+ # Lint tasks
5
+ class Lint < ::Rake::TaskLib
6
+ attr_reader :file_list
7
+
8
+ def initialize(options = {})
9
+ @file_list ||= options.fetch(:file_list, FileList['{lib,spec}/**/*.rb'])
10
+ define_tasks
11
+ end
12
+
13
+ # Define tasks
14
+ def define_tasks
15
+ desc 'Check for code smells with Reek and Rubocop'
16
+ task lint: ['lint:reek', 'lint:rubocop']
17
+
18
+ namespace :lint do
19
+ rubocop
20
+ reek
21
+ rubycritic
22
+ end
23
+ end
24
+
25
+ # RuboCop
26
+ def rubocop
27
+ begin
28
+ require 'rubocop/rake_task'
29
+ rescue LoadError
30
+ nil # Might be in a group that is not installed
31
+ end
32
+ desc 'Run RuboCop on the tasks and lib directory'
33
+ ::RuboCop::RakeTask.new(:rubocop) do |task|
34
+ task.patterns = file_list
35
+ task.options = ['--display-cop-names', '--extra-details']
36
+ end
37
+ end
38
+
39
+ # Reek
40
+ def reek
41
+ begin
42
+ require 'reek/rake/task'
43
+ rescue LoadError
44
+ nil # Might be in a group that is not installed
45
+ end
46
+ ::Reek::Rake::Task.new do |task|
47
+ task.source_files = file_list
48
+ task.fail_on_error = false
49
+ task.reek_opts = '--wiki-links --color'
50
+ end
51
+ end
52
+
53
+ # Ruby Critic
54
+ def rubycritic
55
+ begin
56
+ require 'rubycritic/rake_task'
57
+ rescue LoadError
58
+ nil # Might be in a group that is not installed
59
+ end
60
+ ::RubyCritic::RakeTask.new do |task|
61
+ task.paths = file_list
62
+ end
63
+ end
64
+ end # class Lint
65
+ end # module Vtasks
@@ -0,0 +1,212 @@
1
+ module Vtasks
2
+ require 'rake/tasklib'
3
+
4
+ # Puppet tasks
5
+ class Puppet < ::Rake::TaskLib
6
+ # Include utility modules
7
+ require 'vtasks/utils/git'
8
+ include Vtasks::Utils::Git
9
+ require 'vtasks/utils/output'
10
+ include Vtasks::Utils::Output
11
+
12
+ require 'json'
13
+ require 'open-uri'
14
+ require 'yaml'
15
+
16
+ attr_reader :exclude_paths
17
+
18
+ def initialize(options = {})
19
+ # Fix for namespaced :syntax task
20
+ task syntax: ['puppet:syntax']
21
+
22
+ namespace :puppet do
23
+ begin
24
+ require 'r10k/cli'
25
+ require 'r10k/puppetfile'
26
+ require 'puppet_forge'
27
+ require 'puppetlabs_spec_helper/rake_tasks' # ORDER IS VERY IMPORTANT BECAUSE IT OVERRIDES A LOT OF OTHER TASKS; AS OF NOW IT NEEDS TO BE AFTER `r10k` and `puppet_forge` (BECAUSE OF FAST_GETTEXT INITIALIZATION) BUT BEFORE puppet-strings (BECAUSE ERROR: `Don't know how to build task 'spec_prep'`)
28
+ require 'metadata-json-lint/rake_task'
29
+ require 'puppet-syntax/tasks/puppet-syntax'
30
+ require 'puppet-lint/tasks/puppet-lint'
31
+ require 'puppet-strings/tasks'
32
+ require 'puppet_blacksmith/rake_tasks'
33
+ rescue LoadError
34
+ nil # Might be in a group that is not installed
35
+ end
36
+
37
+ @exclude_paths ||= options.fetch(
38
+ :exclude_paths,
39
+ [
40
+ 'bundle/**/*',
41
+ 'modules/**/*',
42
+ 'pkg/**/*',
43
+ 'spec/**/*',
44
+ 'tmp/**/*',
45
+ 'vendor/**/*'
46
+ ]
47
+ )
48
+
49
+ define_tasks
50
+ end # namespace :puppet
51
+ end
52
+
53
+ def define_tasks
54
+ ::PuppetLint::RakeTask.new :lint do |config|
55
+ config.relative = true
56
+ config.with_context = true
57
+ config.fail_on_warnings = true
58
+ config.ignore_paths = exclude_paths
59
+ config.disable_checks = [
60
+ '140chars'
61
+ ]
62
+ end
63
+
64
+ # Puppet syntax tasks
65
+ ::PuppetSyntax.exclude_paths = exclude_paths
66
+
67
+ desc 'Run syntax, lint, and spec tests'
68
+ task test: [
69
+ :metadata_lint,
70
+ :syntax,
71
+ :lint,
72
+ :unit
73
+ ]
74
+
75
+ desc 'Run unit tests'
76
+ task unit: [
77
+ :r10k_install_modules,
78
+ :spec_prep,
79
+ :spec_standalone
80
+ ]
81
+
82
+ desc 'Run acceptance tests'
83
+ task integration: [
84
+ :spec_prep,
85
+ :beaker
86
+ ]
87
+
88
+ desc 'Clean all test files'
89
+ task clean: [:spec_clean]
90
+
91
+ desc 'Use R10K to download all modules'
92
+ task :r10k_install_modules do
93
+ r10k_install_modules
94
+ end
95
+
96
+ desc 'Generates a new .fixtures.yml from a Puppetfile'
97
+ task :generate_fixtures do
98
+ generate_fixtures
99
+ end
100
+
101
+ desc 'Print outdated Puppetfile modules'
102
+ task :puppetfile_inspect do
103
+ check_puppetfile_versions
104
+ end
105
+ end # def define_tasks
106
+
107
+ def puppetfile
108
+ @puppetfile ||= ::R10K::Puppetfile.new(pwd)
109
+ end
110
+
111
+ def check_puppetfile
112
+ puppetfile.load
113
+ error 'Puppetfile was not found or is empty!' if puppetfile.modules.empty?
114
+ end
115
+
116
+ def r10k_install_modules
117
+ info 'Updating modules with R10K'
118
+ ::R10K::CLI.command.run(%w(puppetfile install --force --verbose))
119
+ rescue SystemExit # because R10K::CLI.command.run calls `exit 0`
120
+ info 'Modules have been updated'
121
+ end
122
+
123
+ def generate_fixtures
124
+ info 'Generating fixtures file'
125
+
126
+ check_puppetfile
127
+
128
+ fixtures = {
129
+ 'fixtures' => {
130
+ 'symlinks' => {
131
+ 'role' => '#{source_dir}/dist/role',
132
+ 'profile' => '#{source_dir}/dist/profile'
133
+ },
134
+ 'repositories' => {}
135
+ }
136
+ }
137
+
138
+ puppetfile.modules.each do |mod|
139
+ module_name = mod.title.tr('/', '-')
140
+ remote = mod.instance_variable_get('@remote')
141
+ ref = mod.instance_variable_get('@desired_ref')
142
+
143
+ fixtures['fixtures']['repositories'][module_name] = {
144
+ 'repo' => remote,
145
+ 'ref' => ref
146
+ }
147
+ end
148
+
149
+ File.open('.fixtures.yml', 'w') { |file| file.write(fixtures.to_yaml) }
150
+ info 'Done'
151
+ end # def generate_fixtures
152
+
153
+ def check_puppetfile_versions
154
+ puppetfile.load
155
+ error 'Puppetfile was not found or is empty!' if puppetfile.modules.empty?
156
+
157
+ puppetfile.modules.each do |mod|
158
+ if mod.class == ::R10K::Module::Forge
159
+ module_name = mod.title.tr('/', '-')
160
+ forge_version = ::PuppetForge::Module.find(module_name)
161
+ .current_release.version
162
+ installed_version = mod.expected_version
163
+ if installed_version != forge_version
164
+ puts "#{module_name} is OUTDATED: " \
165
+ "#{installed_version} vs #{forge_version}"
166
+ .red
167
+ else
168
+ puts "#{module_name}: #{forge_version}".green
169
+ end
170
+ elsif mod.class == ::R10K::Module::Git
171
+ # Try to extract owner and repo name from remote string
172
+ remote = mod.instance_variable_get('@remote')
173
+ owner = remote.gsub(%r{(.*)\/(.*)\/(.*)}, '\\2')
174
+ repo = remote.gsub(%r{(.*)\/(.*)\/}, '\\3')
175
+
176
+ # It's better to query the API authenticated because of the rate
177
+ # limit. You can make up to 5,000 requests per hour. For unauthenticated
178
+ # requests, the rate limit is only up to 60 requests per hour.
179
+ # (https://developer.github.com/v3/#rate-limiting)
180
+ tags = if GITHUB_TOKEN
181
+ open("https://api.github.com/repos/#{owner}/#{repo}/tags?access_token=#{GITHUB_TOKEN}")
182
+ else
183
+ open("https://api.github.com/repos/#{owner}/#{repo}/tags")
184
+ end
185
+
186
+ # Get rid of non-semantic versions (for example
187
+ # https://github.com/puppetlabs/puppetlabs-ntp/releases/tag/push)
188
+ all_tags = JSON.parse(tags.read).select do |tag|
189
+ tag['name'] =~ /v?\d+\.\d+\.\d+/
190
+ end
191
+
192
+ # Use Gem::Version to sort tags
193
+ latest_tag = all_tags.map do |line|
194
+ ::Gem::Version.new line['name'].gsub(/[v]?(.*)/, '\\1')
195
+ end.max.to_s
196
+
197
+ # Print results
198
+ installed_version = mod.version.gsub(/[v]?(.*)/, '\\1')
199
+ if installed_version == 'master'
200
+ puts "#{mod.title}: 'master' branch (#{latest_tag})".blue
201
+ elsif installed_version != latest_tag
202
+ puts "#{mod.title} is OUTDATED: " \
203
+ "#{installed_version} vs #{latest_tag}"
204
+ .red
205
+ else
206
+ puts "#{mod.title}: #{latest_tag}".green
207
+ end
208
+ end
209
+ end
210
+ end # def check_puppetfile_versions
211
+ end # class Puppet
212
+ end # module Vtasks
@@ -0,0 +1,101 @@
1
+ module Vtasks
2
+ require 'rake/tasklib'
3
+
4
+ # Release tasks
5
+ class Release < ::Rake::TaskLib
6
+ # Include utility modules
7
+ require 'vtasks/utils/git'
8
+ include Vtasks::Utils::Git
9
+ require 'vtasks/utils/output'
10
+ include Vtasks::Utils::Output
11
+ require 'vtasks/utils/semver'
12
+ include Vtasks::Utils::Semver
13
+
14
+ attr_reader :write_changelog,
15
+ :wait_for_ci_success,
16
+ :bug_labels,
17
+ :enhancement_labels
18
+
19
+ def initialize(options = {})
20
+ @write_changelog = options.fetch(:write_changelog, false)
21
+ @wait_for_ci_success = options.fetch(:wait_for_ci_success, false)
22
+ @bug_labels = options.fetch(:bug_labels, 'bug')
23
+ @enhancement_labels = options.fetch(:enhancement_labels, 'enhancement')
24
+ define_tasks
25
+ end
26
+
27
+ # Configure the github_changelog_generator/task
28
+ def changelog(config, release: nil)
29
+ config.bug_labels = bug_labels #'Type: Bug'
30
+ config.enhancement_labels = enhancement_labels #'Type: Enhancement'
31
+ config.future_release = "v#{release}" if release
32
+ end
33
+
34
+ def define_tasks
35
+ desc "Release patch version"
36
+ task release: ['release:patch']
37
+
38
+ namespace :release do
39
+ begin
40
+ require 'github_changelog_generator/task'
41
+
42
+ # Create release:changes task
43
+ ::GitHubChangelogGenerator::RakeTask.new(:changes) do |config|
44
+ changelog(config)
45
+ end
46
+ rescue LoadError
47
+ nil # Might be in a group that is not installed
48
+ end
49
+
50
+ SEM_LEVELS.each do |level|
51
+ desc "Release #{level} version"
52
+ task level.to_sym do
53
+ new_version = bump(level)
54
+ release = "#{new_version[:major]}.#{new_version[:minor]}.#{new_version[:patch]}"
55
+ release_branch = "release_v#{release.gsub(/[^0-9A-Za-z]/, '_')}"
56
+ initial_branch = git_branch
57
+
58
+ info 'Check if the repository is clean'
59
+ git_clean_repo
60
+
61
+ # Write changelog
62
+ # Create a separate release branch (works with protected branches as well)
63
+ if write_changelog == true
64
+ info 'Generate new changelog'
65
+ ::GitHubChangelogGenerator::RakeTask.new(:latest_release) do |config|
66
+ changelog(config, release: release)
67
+ end
68
+ task('latest_release').invoke
69
+
70
+ if system 'git diff --quiet HEAD'
71
+ info 'CHANGELOG has not changed. Skipping...'
72
+ else
73
+ info 'Create a new release branch'
74
+ sh "git checkout -b #{release_branch}"
75
+
76
+ info 'Commit the new changes'
77
+ sh "git commit --gpg-sign --message 'Update change log for v#{release}' CHANGELOG.md"
78
+
79
+ info 'Push the new changes'
80
+ sh "git push --set-upstream origin #{release_branch}"
81
+
82
+ if wait_for_ci_success == true
83
+ info 'Waiting for CI to finish'
84
+ sleep 5 until git_ci_status(release_branch) == 'success'
85
+ end
86
+
87
+ info 'Merge release branch'
88
+ sh "git checkout #{initial_branch}"
89
+ sh "git merge --gpg-sign --no-ff --message 'Release v#{release}' #{release_branch}"
90
+ end
91
+ end
92
+
93
+ info "Tag #{release}"
94
+ sh "git tag --sign v#{release} --message 'Release v#{release}'"
95
+ sh 'git push --follow-tags'
96
+ end # task
97
+ end # LEVELS
98
+ end # namespace :release
99
+ end # def define_tasks
100
+ end # class Release
101
+ end # module Vtasks