vtasks 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +51 -0
- data/CODEOWNERS +2 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +14 -0
- data/LICENSE +201 -0
- data/README.md +196 -0
- data/Rakefile +40 -0
- data/bin/console +7 -0
- data/bin/setup +11 -0
- data/gems.rb +2 -0
- data/lib/vtasks/docker/image/build.rb +54 -0
- data/lib/vtasks/docker/image/push.rb +17 -0
- data/lib/vtasks/docker/image/tag.rb +17 -0
- data/lib/vtasks/docker/image.rb +83 -0
- data/lib/vtasks/docker.rb +124 -0
- data/lib/vtasks/lint.rb +65 -0
- data/lib/vtasks/puppet.rb +212 -0
- data/lib/vtasks/release.rb +101 -0
- data/lib/vtasks/travisci.rb +58 -0
- data/lib/vtasks/utils/docker_shared_context.rb +91 -0
- data/lib/vtasks/utils/git.rb +46 -0
- data/lib/vtasks/utils/output.rb +68 -0
- data/lib/vtasks/utils/semver.rb +31 -0
- data/lib/vtasks/utils/system.rb +11 -0
- data/lib/vtasks/version.rb +5 -0
- data/lib/vtasks.rb +5 -0
- data/locales/config.yaml +21 -0
- data/vtasks.gemspec +37 -0
- metadata +188 -0
@@ -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
|
data/lib/vtasks/lint.rb
ADDED
@@ -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
|