knife-changelog 0.5.20 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7f5cdeee5d1cffb63b524ec79a1bdcfcfb154a74
4
- data.tar.gz: e68caccf393dab3d26ce0324b84cc7394fb38744
3
+ metadata.gz: 705a36f063f6249e387aa5d52cd1784cb38f1ac9
4
+ data.tar.gz: 521f2a3658ca50040b0f119437fb38d37111fefa
5
5
  SHA512:
6
- metadata.gz: 0b79c827c13afa739ebfd008527155a93abf70aef63767802a8ce2ed391432e8127b4f8e2dbb096401757d671eba38d707c13f49fa7b5565781a33156668ff86
7
- data.tar.gz: aae9c4b834f21b0d7879c225664aae9203348457456d34fa5f000a7e2aa54225c151d9670b7961297b01d22898e51f323413f63c2511bf5a009c25e36070e08a
6
+ metadata.gz: 6f32a1750c57c95b8dc9144c4783d95d0d6281bcd76bd2e0e77def896d0b6eaca27d88b4b0f2a5e1eb4dd4aa7ddd856f90f06a2a303b95944cb5c090f6c30898
7
+ data.tar.gz: 87ad561196aa635e2508eb0871f235d216fd66e5141bb12a10d334c4b72e807e349a68c88e48c24bdff0c519e4fa3d86d579792901b5ecf8c271aa2c53471056
data/.gitignore CHANGED
@@ -1,2 +1,2 @@
1
1
  .bundle/
2
- *lock
2
+ Gemfile.lock*
data/.rubocop.yml CHANGED
@@ -1,5 +1,33 @@
1
- AllCops:
2
- TargetRubyVersion: 2.1
1
+ # Configuration parameters: AllowHeredoc, AllowURI, URISchemes.
2
+ # URISchemes: http, https
3
+ Metrics/ClassLength:
4
+ Max: 200
3
5
 
4
6
  Metrics/LineLength:
5
- Max: 150
7
+ Max: 230
8
+
9
+ Metrics/MethodLength:
10
+ Max: 30
11
+
12
+ # Cop supports --auto-correct.
13
+ # Configuration parameters: SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
14
+ # SupportedStyles: ruby19, ruby19_no_mixed_keys, hash_rockets
15
+ Style/HashSyntax:
16
+ EnforcedStyle: ruby19_no_mixed_keys
17
+
18
+ Style/Documentation:
19
+ Enabled: false
20
+
21
+ Metrics/AbcSize:
22
+ Max: 52
23
+
24
+ Metrics/PerceivedComplexity:
25
+ Max: 10
26
+
27
+ Metrics/CyclomaticComplexity:
28
+ Max: 25
29
+
30
+ AllCops:
31
+ Exclude:
32
+ - bundle/**/*
33
+ TargetRubyVersion: 2.3
data/.travis.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.2
3
+ - 2.4
4
4
  deploy:
5
5
  provider: rubygems
6
6
  api_key:
@@ -8,5 +8,5 @@ deploy:
8
8
  gem: knife-changelog
9
9
  on:
10
10
  tags: true
11
- repo: kamaradclimber/knife-changelog
11
+ repo: criteo/knife-changelog
12
12
  sudo: false # use docker based infra
data/README.md CHANGED
@@ -15,6 +15,8 @@ Options:
15
15
  - generate changelogs for some supermarket hosted cookbooks
16
16
  - generate changelogs for all git located cookbooks
17
17
 
18
+ This plugin works in policyfile style repositories or classical repositories with a Berksfile
19
+
18
20
  ## Todos
19
21
 
20
22
  - (optionaly) link commit ref to their web page to ease reviews
@@ -22,10 +22,13 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake"
23
23
  spec.add_development_dependency "rspec"
24
24
  spec.add_development_dependency "rubocop"
25
+ spec.add_development_dependency "webmock"
26
+ spec.add_development_dependency "pry"
25
27
 
26
28
 
27
29
  spec.add_dependency 'berkshelf'
28
30
  spec.add_dependency 'rest-client'
29
31
  spec.add_dependency 'mixlib-shellout'
30
32
  spec.add_dependency 'chef'
33
+ spec.add_dependency 'chef-dk'
31
34
  end
@@ -10,13 +10,19 @@ class Chef
10
10
  deps do
11
11
  require "knife/changelog/version"
12
12
  require "knife/changelog/changelog"
13
+ require "knife/changelog/policyfile"
14
+ require "knife/changelog/berksfile"
13
15
  require "berkshelf"
14
16
  end
15
17
 
16
18
  def initialize(options)
17
19
  super
18
- berksfile = Berkshelf::Berksfile.from_options({})
19
- @changelog = KnifeChangelog::Changelog.new(berksfile.lockfile.locks, config, berksfile.sources)
20
+ @changelog = if File.exists?(config[:policyfile])
21
+ KnifeChangelog::Changelog::Policyfile.new(config[:policyfile], config)
22
+ else
23
+ berksfile = Berkshelf::Berksfile.from_options({})
24
+ KnifeChangelog::Changelog::Berksfile.new(berksfile.lockfile.locks, config, berksfile.sources)
25
+ end
20
26
  end
21
27
 
22
28
  option :linkify,
@@ -46,6 +52,11 @@ class Chef
46
52
  :long => '--submodules SUBMODULE[,SUBMODULE]',
47
53
  :description => 'Submoduless to check for changes as well (comma separated)'
48
54
 
55
+ option :policyfile,
56
+ :long => '--policyfile PATH',
57
+ :description => 'Link to policyfile, defaults to Policyfile.rb',
58
+ :default => 'Policyfile.rb'
59
+
49
60
 
50
61
  def run
51
62
  Log.info config
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'changelog'
4
+
5
+ class KnifeChangelog
6
+ class Changelog
7
+ class Berksfile < Changelog
8
+ def initialize(locked_versions, config, sources)
9
+ require 'berkshelf'
10
+ @locked_versions = locked_versions
11
+ @sources = sources
12
+ super(config)
13
+ end
14
+
15
+ def all_cookbooks
16
+ @locked_versions.keys
17
+ end
18
+
19
+ def new_cookbook?(name)
20
+ ck_dep(name).nil?
21
+ end
22
+
23
+ # return true if cookbook is downloaded from supermarket
24
+ def supermarket?(name)
25
+ # here is berkshelf "expressive" way to say cookbook
26
+ # comes from supermarket
27
+ ck_dep(name).location.is_a?(NilClass)
28
+ end
29
+
30
+ # return true if cookbook is downloaded from git
31
+ def git?(name)
32
+ ck_dep(name).location.is_a?(Berkshelf::GitLocation)
33
+ end
34
+
35
+ # return true if cookbook is downloaded from local path
36
+ def local?(name)
37
+ ck_dep(name).location.is_a?(Berkshelf::PathLocation)
38
+ end
39
+
40
+ # return a Changelog::Location for this cookbook
41
+ def git_location(name)
42
+ raise "#{name} has not a git location" unless git?(name)
43
+ Location.from_berk_git_location(ck_dep(name).location)
44
+ end
45
+
46
+ # return a list of supermarket uri for a given cookbook
47
+ # example: [ 'https://supermarket.chef.io' ]
48
+ def supermarkets_for(_name)
49
+ @sources.map(&:uri)
50
+ end
51
+
52
+ def guess_version_for(name)
53
+ @locked_versions[name].locked_version.to_s
54
+ end
55
+
56
+ private
57
+
58
+ def ck_dep(name)
59
+ @locked_versions[name]
60
+ end
61
+ end
62
+ end
63
+ end
@@ -1,28 +1,77 @@
1
1
  # coding: utf-8
2
2
  require 'chef/log'
3
3
  require 'chef/knife'
4
+ require 'chef/version_class'
4
5
  require 'rest-client'
5
6
  require 'json'
7
+ require_relative 'git'
6
8
 
7
9
  class KnifeChangelog
8
10
  class Changelog
9
- def initialize(locked_versions, config = {}, sources=[])
10
- require 'berkshelf'
11
+
12
+ Location = Struct.new(:uri, :revision, :rev_parse) do
13
+ # todo move this method to Changelog::Berkshelf
14
+ def self.from_berk_git_location(location)
15
+ Location.new(location.uri,
16
+ location.revision.strip,
17
+ location.instance_variable_get(:@rev_parse))
18
+ end
19
+ end
20
+
21
+ def initialize(config)
11
22
  @tmp_prefix = 'knife-changelog'
12
- @locked_versions = locked_versions
13
23
  @config = config
14
24
  @tmp_dirs = []
15
- @sources = sources
16
- if sources.empty? # preserve api compat
17
- @sources = [ Berkshelf::Source.new("https://supermarket.chef.io") ]
18
- end
19
25
  end
20
26
 
27
+ # returns a list of all cookbooks names
28
+ def all_cookbooks
29
+ raise NotImplementedError
30
+ end
31
+
32
+ # return true if cookbook is not already listed as dependency
33
+ def new_cookbook?(name)
34
+ raise NotImplementedError
35
+ end
36
+
37
+ # return true if cookbook is downloaded from supermarket
38
+ def supermarket?(name)
39
+ raise NotImplementedError
40
+ end
41
+
42
+ # return true if cookbook is downloaded from git
43
+ def git?(name)
44
+ raise NotImplementedError
45
+ end
46
+
47
+ # return true if cookbook is downloaded from local path
48
+ def local?(name)
49
+ raise NotImplementedError
50
+ end
51
+
52
+ # return a Changelog::Location for a given cookbook
53
+ def git_location(name)
54
+ raise NotImplementedError
55
+ end
56
+
57
+ # return a list of supermarket uri for a given cookbook
58
+ # example: [ 'https://supermarket.chef.io' ]
59
+ def supermarkets_for(name)
60
+ raise NotImplementedError
61
+ end
62
+
63
+ # return current locked version for a given cookbook
64
+ def guess_version_for(name)
65
+ raise NotImplementedError
66
+ end
67
+
68
+
69
+
21
70
  def run(cookbooks)
22
71
  changelog = []
23
72
  begin
24
73
  if cookbooks.empty? and @config[:allow_update_all]
25
- cks = @locked_versions.keys
74
+ cks = all_cookbooks
26
75
  else
27
76
  cks = cookbooks
28
77
  end
@@ -44,47 +93,29 @@ class KnifeChangelog
44
93
 
45
94
  def clean
46
95
  @tmp_dirs.each do |dir|
47
- FileUtils.rm_r dir
96
+ FileUtils.rm_rf dir
48
97
  end
49
98
  end
50
99
 
51
- def ck_dep(name)
52
- @locked_versions[name]
53
- end
54
-
55
- def ck_location(name)
56
- begin
57
- ck_dep(name).location
58
- rescue
59
- puts "Failed to get location for cookbook: #{name}"
60
- raise
61
- end
62
- end
63
-
64
- def guess_version_for(name)
65
- @locked_versions[name].locked_version.to_s
66
- end
67
-
68
- def handle_new_cookbook(name)
100
+ def handle_new_cookbook
69
101
  stars = '**' if @config[:markdown]
70
102
  ["#{stars}Cookbook was not in the berksfile#{stars}"]
71
103
  end
72
104
 
73
- def execute(name, submodule=false)
105
+ def execute(name, submodule = false)
74
106
  version_change, changelog = if submodule
75
107
  handle_submodule(name)
76
- elsif ck_dep(name).nil?
77
- ["", handle_new_cookbook(name)]
108
+ elsif new_cookbook?(name)
109
+ ['', handle_new_cookbook]
78
110
  else
79
- loc = ck_location(name)
80
- case loc
81
- when NilClass
82
- handle_source name, ck_dep(name)
83
- when Berkshelf::GitLocation
84
- handle_git name, loc
85
- when Berkshelf::PathLocation
111
+ case true
112
+ when supermarket?(name)
113
+ handle_source(name)
114
+ when git?(name)
115
+ handle_git(name, git_location(name))
116
+ when local?(name)
86
117
  Chef::Log.debug "path location are always at the last version"
87
- ["", ""]
118
+ ['', '']
88
119
  else
89
120
  raise "Cannot handle #{loc.class} yet"
90
121
  end
@@ -105,12 +136,14 @@ class KnifeChangelog
105
136
  end
106
137
 
107
138
  def get_from_supermarket_sources(name)
108
- @sources.map do |s|
139
+ supermarkets_for(name).map do |uri|
109
140
  begin
141
+ # TODO: we could call /universe endpoint once
142
+ # instead of calling /api/v1/cookbooks/ for each cookbook
110
143
  RestClient::Request.execute(
111
- url: "#{s.uri}/api/v1/cookbooks/#{name}",
144
+ url: "#{uri}/api/v1/cookbooks/#{name}",
112
145
  method: :get,
113
- verify_ssl: false # TODO make this configurable
146
+ verify_ssl: false # TODO make this configurable
114
147
  )
115
148
  rescue => e
116
149
  Chef::Log.debug "Error fetching package from supermarket #{e.class.name} #{e.message}"
@@ -134,35 +167,24 @@ class KnifeChangelog
134
167
  .last
135
168
  end
136
169
 
137
- def handle_source(name, dep)
170
+ def handle_source(name)
138
171
  url = get_from_supermarket_sources(name)
139
172
  raise "No source found in supermarket for cookbook '#{name}'" unless url
140
173
  Chef::Log.debug("Using #{url} as source url")
141
174
  case url.strip
142
175
  when /(gitlab.*|github).com\/([^.]+)(.git)?/
143
176
  url = "https://#{$1}.com/#{$2.chomp('/')}.git"
144
- options = {
145
- :git => url,
146
- :revision => guess_version_for(name),
147
- }
148
- location = Berkshelf::GitLocation.new dep, options
177
+ location = Location.new(url, guess_version_for(name), 'master')
149
178
  handle_git(name, location)
150
179
  else
151
180
  fail "External url #{url} points to unusable location! (cookbook: #{name})"
152
181
  end
153
182
  end
154
183
 
155
- def revision_exists?(dir, revision)
156
- Chef::Log.debug "Testing existence of #{revision}"
157
- revlist = Mixlib::ShellOut.new("git rev-list --quiet #{revision}", :cwd => dir)
158
- revlist.run_command
159
- not revlist.error?
160
- end
161
-
162
- def detect_cur_revision(name, dir, rev)
163
- unless revision_exists?(dir, rev)
184
+ def detect_cur_revision(name, rev, git)
185
+ unless git.revision_exists?(rev)
164
186
  prefixed_rev = 'v' + rev
165
- return prefixed_rev if revision_exists?(dir, prefixed_rev)
187
+ return prefixed_rev if git.revision_exists?(prefixed_rev)
166
188
  fail "#{rev} is not an existing revision (#{name}), not a tag/commit/branch name."
167
189
  end
168
190
  rev
@@ -178,43 +200,38 @@ class KnifeChangelog
178
200
  subm_revision.error!
179
201
  revision = subm_revision.stdout.strip.split(' ').first
180
202
  revision.gsub!(/^\+/, '')
181
- loc = Berkshelf::Location.init(nil, {git: url,revision: revision})
203
+ loc = Location.new(url, revision, 'master')
182
204
  handle_git(name, loc)
183
205
  end
184
206
 
207
+ # take cookbook name and Changelog::Location instance
185
208
  def handle_git(name, location)
186
- tmp_dir = shallow_clone(@tmp_prefix,location.uri)
209
+ # todo: remove this compat check
210
+ raise "should be a location" unless location.is_a?(Changelog::Location)
211
+ git = Git.new(@tmp_prefix, location.uri)
212
+ @tmp_dirs << git.shallow_clone
187
213
 
188
- rev_parse = location.instance_variable_get(:@rev_parse)
189
- cur_rev = location.revision.rstrip
190
- cur_rev = detect_cur_revision(name, tmp_dir, cur_rev)
191
- ls_tree = Mixlib::ShellOut.new("git ls-tree -r #{rev_parse}", :cwd => tmp_dir)
192
- ls_tree.run_command
193
- changelog_file = ls_tree.stdout.lines.find { |line| line =~ /\s(changelog.*$)/i }
214
+ rev_parse = location.rev_parse
215
+ cur_rev = detect_cur_revision(name, location.revision, git)
216
+ changelog_file = git.files(rev_parse).find { |line| line =~ /\s(changelog.*$)/i }
194
217
  changelog = if changelog_file and !@config[:ignore_changelog_file]
195
218
  Chef::Log.info "Found changelog file : " + $1
196
- generate_from_changelog_file($1, cur_rev, rev_parse, tmp_dir)
219
+ generate_from_changelog_file($1, cur_rev, rev_parse, git)
197
220
  end
198
- changelog ||= generate_from_git_history(tmp_dir, location, cur_rev, rev_parse)
199
- [ "#{cur_rev}->#{rev_parse}", changelog ]
221
+ changelog ||= generate_from_git_history(git, location, cur_rev, rev_parse)
222
+ ["#{cur_rev}->#{rev_parse}", changelog]
200
223
  end
201
224
 
202
- def generate_from_changelog_file(filename, current_rev, rev_parse, tmp_dir)
203
- diff = Mixlib::ShellOut.new("git diff #{current_rev}..#{rev_parse} --word-diff -- #{filename}", :cwd => tmp_dir)
204
- diff.run_command
205
- ch = diff.
206
- stdout.
207
- lines.
208
- collect {|line| $1.strip if line =~ /^{\+(.*)\+}$/}.compact.
209
- map { |line| line.gsub(/^#+(.*)$/, "\\1\n---")}. # replace section by smaller header
210
- select { |line| !(line =~ /^===+/)}.compact # remove header lines
225
+ def generate_from_changelog_file(filename, current_rev, rev_parse, git)
226
+ ch = git.diff(filename, current_rev, rev_parse)
227
+ .collect { |line| $1.strip if line =~ /^{\+(.*)\+}$/ }.compact
228
+ .map { |line| line.gsub(/^#+(.*)$/, "\\1\n---")} # replace section by smaller header
229
+ .select { |line| !(line =~ /^===+/)}.compact # remove header lines
211
230
  ch.empty? ? nil : ch
212
231
  end
213
232
 
214
- def generate_from_git_history(tmp_dir, location, current_rev, rev_parse)
215
- log = Mixlib::ShellOut.new("git log --no-merges --abbrev-commit --pretty=oneline #{current_rev}..#{rev_parse}", :cwd => tmp_dir)
216
- log.run_command
217
- c = log.stdout.lines
233
+ def generate_from_git_history(git, location, current_rev, rev_parse)
234
+ c = git.log(current_rev, rev_parse)
218
235
  n = https_url(location)
219
236
  c = linkify(n, c) if @config[:linkify] and n
220
237
  c = c.map { |line| "* " + line } if @config[:markdown]
@@ -241,16 +258,5 @@ class KnifeChangelog
241
258
  "%s/%s" % [$1,$2]
242
259
  end
243
260
  end
244
-
245
- def shallow_clone(tmp_prefix, uri)
246
- Chef::Log.debug "Cloning #{uri} in #{tmp_prefix}"
247
- dir = Dir.mktmpdir(tmp_prefix)
248
- @tmp_dirs << dir
249
- clone = Mixlib::ShellOut.new("git clone --bare #{uri} bare-clone", :cwd => dir)
250
- clone.run_command
251
- clone.error!
252
- ::File.join(dir, 'bare-clone')
253
- end
254
-
255
261
  end
256
262
  end
@@ -0,0 +1,46 @@
1
+ class KnifeChangelog
2
+ class Git
3
+ attr_accessor :tmp_prefix, :uri
4
+
5
+ def initialize(tmp_prefix, uri)
6
+ @tmp_prefix = tmp_prefix
7
+ @uri = uri
8
+ end
9
+
10
+ def shallow_clone
11
+ Chef::Log.debug "Cloning #{uri} in #{tmp_prefix}"
12
+ dir = Dir.mktmpdir(tmp_prefix)
13
+ clone = Mixlib::ShellOut.new("git clone --bare #{uri} bare-clone", cwd: dir)
14
+ clone.run_command
15
+ clone.error!
16
+ @clone_dir = ::File.join(dir, 'bare-clone')
17
+ @clone_dir
18
+ end
19
+
20
+ def files(rev_parse)
21
+ ls_tree = Mixlib::ShellOut.new("git ls-tree -r #{rev_parse}", cwd: @clone_dir)
22
+ ls_tree.run_command
23
+ ls_tree.error!
24
+ ls_tree.stdout.lines.map(&:strip)
25
+ end
26
+
27
+ def diff(filename, current_rev, rev_parse)
28
+ diff = Mixlib::ShellOut.new("git diff #{current_rev}..#{rev_parse} --word-diff -- #{filename}", cwd: @clone_dir)
29
+ diff.run_command
30
+ diff.stdout.lines
31
+ end
32
+
33
+ def log(current_rev, rev_parse)
34
+ log = Mixlib::ShellOut.new("git log --no-merges --abbrev-commit --pretty=oneline #{current_rev}..#{rev_parse}", cwd: @clone_dir)
35
+ log.run_command
36
+ log.stdout.lines
37
+ end
38
+
39
+ def revision_exists?(revision)
40
+ Chef::Log.debug "Testing existence of #{revision}"
41
+ revlist = Mixlib::ShellOut.new("git rev-list --quiet #{revision}", cwd: @clone_dir)
42
+ revlist.run_command
43
+ !revlist.error?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'changelog'
4
+
5
+ class KnifeChangelog
6
+ class Changelog
7
+ class Policyfile < Changelog
8
+ attr_reader :policy, :lock
9
+
10
+ def initialize(policyfile_path, config)
11
+ require 'chef-dk'
12
+ require 'chef-dk/policyfile_compiler'
13
+ lock_path = policyfile_path.gsub(/.rb$/, '.lock.json')
14
+ @policy = ChefDK::PolicyfileCompiler.evaluate(File.read(policyfile_path), policyfile_path)
15
+ @lock = ChefDK::PolicyfileLock.new(policy.storage_config).build_from_lock_data(JSON.parse(File.read(lock_path)))
16
+ super(config)
17
+ end
18
+
19
+ def all_cookbooks
20
+ policy.all_possible_dep_names
21
+ end
22
+
23
+ # return true if cookbook is not already listed as dependency
24
+ def new_cookbook?(name)
25
+ policy.send(:best_source_for, name).nil?
26
+ end
27
+
28
+ # return true if cookbook is downloaded from supermarket
29
+ def supermarket?(name)
30
+ # it's hard to get location_specs for supermarket cookbooks without having policy_compiler starting to download all cookbooks
31
+ # in the meantime, we procede by elimination
32
+ !(git?(name) || local?(name))
33
+ end
34
+
35
+ def guess_version_for(name)
36
+ lock.solution_dependencies.cookbook_dependencies.keys.find { |dep| dep.name == name }.version
37
+ end
38
+
39
+ # return true if cookbook is downloaded from git
40
+ def git?(name)
41
+ # cookbook_location_specs contains only cookbooks refered via git and path
42
+ policy.cookbook_location_specs[name] && policy.cookbook_location_specs[name].source_type == :git
43
+ end
44
+
45
+ # return true if cookbook is downloaded from local path
46
+ def local?(name)
47
+ # cookbook_location_specs contains only cookbooks refered via git and path
48
+ policy.cookbook_location_specs[name] && policy.cookbook_location_specs[name].source_type.nil?
49
+ end
50
+
51
+ # return a Changelog::Location for a given cookbook
52
+ def git_location(name)
53
+ return nil unless git?(name)
54
+ spec = lock.cookbook_locks[name].source_options
55
+ Location.new(spec[:git], spec[:revision], spec[:branch])
56
+ end
57
+
58
+ # return a list of supermarket uri for a given cookbook
59
+ # example: [ 'https://supermarket.chef.io' ]
60
+ def supermarkets_for(name)
61
+ [policy.send(:best_source_for, name).uri]
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,5 @@
1
1
  module Knife
2
2
  module Changelog
3
- VERSION = "0.5.20"
3
+ VERSION = "1.0.1"
4
4
  end
5
5
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://mysupermarket.io'
4
+ source 'https://mysupermarket2.io'
5
+
6
+ cookbook 'uptodate'
7
+ cookbook 'outdated1'
8
+ cookbook 'second_out_of_date'
9
+ cookbook 'othercookbook'
@@ -0,0 +1,11 @@
1
+ DEPENDENCIES
2
+ othercookbook
3
+ outdated1
4
+ second_out_of_date
5
+ uptodate
6
+
7
+ GRAPH
8
+ othercookbook (1.0.0)
9
+ outdated1 (1.0.0)
10
+ second_out_of_date (1.0.0)
11
+ uptodate (1.0.0)
@@ -0,0 +1,72 @@
1
+ {
2
+ "revision_id": "61fc0aa9ad414968cced34e8cf88189559b9ce8ec69d1514714822e77b0a2449",
3
+ "name": "test_policy",
4
+ "run_list": [
5
+ "recipe[uptodate::default]"
6
+ ],
7
+ "cookbook_locks": {
8
+ "uptodate": {
9
+ "version": "1.0.0",
10
+ "identifier": "c29076b3a9aae74c7edd9790343eb1502e2fa8f2",
11
+ "dotted_decimal_identifier": "54764984976648935.21531588425692222.194957930375410",
12
+ "cache_key": "uptodate-1.0.0-mysupermarket2.io",
13
+ "origin": "https://mysupermarket2.io:443/api/v1/cookbooks/uptodate/versions/1.0.0/download",
14
+ "source_options": {
15
+ "artifactserver": "https://mysupermarket2.io:443/api/v1/cookbooks/uptodate/versions/1.0.0/download",
16
+ "version": "1.0.0"
17
+ }
18
+ },
19
+ "outdated1": {
20
+ "version": "1.0.0",
21
+ "identifier": "c29076b3a9aae74c7edd9790343eb1502e2fa8f1",
22
+ "dotted_decimal_identifier": "54764984976648935.21531588425692222.194957930375411",
23
+ "cache_key": "outdated1-1.0.0-mysupermarket2.io",
24
+ "origin": "https://mysupermarket2.io:443/api/v1/cookbooks/outdated1/versions/1.0.0/download",
25
+ "source_options": {
26
+ "artifactserver": "https://mysupermarket2.io:443/api/v1/cookbooks/outdated1/versions/1.0.0/download",
27
+ "version": "1.0.0"
28
+ }
29
+ },
30
+ "second_out_of_date": {
31
+ "version": "1.0.0",
32
+ "identifier": "c29076b3a9aae74c7edd9790343eb1502e2fa8f1",
33
+ "dotted_decimal_identifier": "54764984976648935.21531588425692222.194957930375411",
34
+ "cache_key": "outdated1-1.0.0-mysupermarket2.io",
35
+ "origin": "https://mysupermarket2.io:443/api/v1/cookbooks/second_out_of_date/versions/1.0.0/download",
36
+ "source_options": {
37
+ "artifactserver": "https://mysupermarket2.io:443/api/v1/cookbooks/second_out_of_date/versions/1.0.0/download",
38
+ "version": "1.0.0"
39
+ }
40
+ }
41
+ },
42
+ "default_attributes": {
43
+
44
+ },
45
+ "override_attributes": {
46
+
47
+ },
48
+ "solution_dependencies": {
49
+ "Policyfile": [
50
+ [
51
+ "uptodate",
52
+ "= 1.0.0"
53
+ ],
54
+ [
55
+ "outdated1",
56
+ "= 1.0.0"
57
+ ],
58
+ [
59
+ "second_out_of_date",
60
+ "= 1.0.0"
61
+ ]
62
+ ],
63
+ "dependencies": {
64
+ "uptodate (1.0.0)": [
65
+ ],
66
+ "outdated1 (1.0.0)": [
67
+ ],
68
+ "second_out_of_date (1.0.0)": [
69
+ ]
70
+ }
71
+ }
72
+ }
@@ -0,0 +1,10 @@
1
+ name 'test_policy'
2
+
3
+ run_list [
4
+ 'recipe[uptodate]',
5
+ 'recipe[second_out_of_date]',
6
+ 'recipe[outdated1]'
7
+ ]
8
+
9
+ default_source :supermarket, 'https://mysupermarket.io'
10
+ default_source :supermarket, 'https://mysupermarket2.io'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../lib/knife/changelog/changelog.rb'
4
+ require_relative '../lib/knife/changelog/berksfile'
5
+ require_relative '../lib/knife/changelog/policyfile'
6
+
7
+ # This file was generated by the `rspec --init` command. Conventionally, all
8
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
9
+ # Require this file using `require "spec_helper"` to ensure that it is only
10
+ # loaded once.
11
+ #
12
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
13
+ RSpec.configure do |config|
14
+ config.run_all_when_everything_filtered = true
15
+ config.filter_run :focus
16
+
17
+ # Run specs in random order to surface order dependencies. If you find an
18
+ # order dependency and want to debug it, you can fix the order by providing
19
+ # the seed, which is printed after each run.
20
+ # --seed 1234
21
+ config.order = 'random'
22
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../spec_helper'
4
+ require 'webmock/rspec'
5
+ require 'berkshelf'
6
+
7
+ WebMock.disable_net_connect!
8
+
9
+ RSpec.shared_examples 'changelog generation' do
10
+ # this supposes that "changelog" is an instance of KnifeChangelog::Changelog
11
+ it 'detects basic changelog' do
12
+ changelog_txt = changelog.run(%w[new_cookbook uptodate outdated1 second_out_of_date])
13
+ expect(changelog_txt).to match(/commit in outdated1/)
14
+ expect(changelog_txt).to match(/commit in second_out_of_date/)
15
+ expect(changelog_txt).not_to match(/uptodate/)
16
+ expect(changelog_txt).to match(/new_cookbook: \n.*\nCookbook was not/)
17
+ end
18
+ end
19
+
20
+ describe KnifeChangelog::Changelog do
21
+ before(:each) do
22
+ stub_request(:get, %r{https://mysupermarket.io/api/v1/cookbooks/})
23
+ .to_return(status: 404, body: '{}')
24
+
25
+ mock_supermarket('uptodate', %w[1.0.0])
26
+ mock_supermarket('outdated1', %w[1.0.0 1.1.0])
27
+ # TODO: we should make second_out_of_date a git location
28
+ mock_supermarket('second_out_of_date', %w[1.0.0 1.2.0])
29
+
30
+ mock_universe('https://mysupermarket2.io', uptodate: %w[1.0.0], outdated1: %w[1.0.0 1.1.0], second_out_of_date: %w[1.0.0 1.2.0])
31
+ mock_universe('https://mysupermarket.io', {})
32
+
33
+ mock_git('second_out_of_date', <<-EOH)
34
+ aaaaaa commit in second_out_of_date
35
+ bbbbbb bugfix in second_out_of_date
36
+ EOH
37
+ mock_git('outdated1', <<-EOH)
38
+ aaaaaa commit in outdated1
39
+ bbbbbb bugfix in outdated1
40
+ EOH
41
+ mock_git('uptodate', '')
42
+ end
43
+
44
+ def mock_git(name, changelog)
45
+ expect(KnifeChangelog::Git).to receive(:new)
46
+ .with(anything, /#{name}(.git|$)/)
47
+ .and_return(double(name,
48
+ shallow_clone: '/tmp/randomdir12345',
49
+ revision_exists?: true,
50
+ files: [],
51
+ log: changelog.split("\n")))
52
+ end
53
+
54
+ def mock_supermarket(name, versions)
55
+ stub_request(:get, %r{https://mysupermarket2.io/api/v1/cookbooks/#{name}})
56
+ .to_return(status: 200, body: supermarket_versions(name, versions))
57
+ end
58
+
59
+ def supermarket_versions(name, versions)
60
+ {
61
+ name: name,
62
+ maintainer: 'Linus',
63
+ description: 'small project on the side',
64
+ category: 'Operating System',
65
+ source_url: "https://github.com/chef-cookbooks/#{name}",
66
+ versions: []
67
+ }.tap do |json|
68
+ versions.each do |v|
69
+ json[:versions] << "https://source.io/#{name}/#{v}"
70
+ end
71
+ end.to_json
72
+ end
73
+
74
+ def mock_universe(supermarket_url, cookbooks)
75
+ universe = cookbooks.transform_values do |versions|
76
+ versions.map do |v|
77
+ [v, {
78
+ location_type: 'opscode',
79
+ location_path: "#{supermarket_url}/api/v1",
80
+ download_url: "#{supermarket_url}/api/v1/cookbooks/insertnamehere/versions/#{v}/download",
81
+ dependencies: {}
82
+ }]
83
+ end.to_h
84
+ end
85
+ stub_request(:get, "#{supermarket_url}/universe")
86
+ .to_return(status: 200, body: universe.to_json)
87
+ end
88
+
89
+ context 'in Berksfile mode' do
90
+ let(:berksfile) do
91
+ Berkshelf::Berksfile.from_options(
92
+ berksfile: File.join(File.dirname(__FILE__), '../data/Berksfile')
93
+ )
94
+ end
95
+
96
+ let(:changelog) do
97
+ KnifeChangelog::Changelog::Berksfile.new(berksfile.lockfile.locks, {}, berksfile.sources)
98
+ end
99
+
100
+ include_examples 'changelog generation'
101
+ end
102
+
103
+ context 'in policyfile mode' do
104
+ let(:policyfile_path) { File.join(File.dirname(__FILE__), '../data/Policyfile.rb') }
105
+
106
+ let(:changelog) do
107
+ KnifeChangelog::Changelog::Policyfile.new(policyfile_path, {})
108
+ end
109
+
110
+ before(:each) do
111
+ allow(changelog.policy).to receive(:cache_fixed_version_cookbooks)
112
+ end
113
+
114
+ include_examples 'changelog generation'
115
+ end
116
+ end
117
+
118
+ class Hash
119
+ unless Chef::Version.new(RUBY_VERSION) >= Chef::Version.new('2.4')
120
+ def transform_values
121
+ map { |k, v| [k, (yield v)] }.to_h
122
+ end
123
+ end
124
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knife-changelog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.20
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gregoire Seux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-15 00:00:00.000000000 Z
11
+ date: 2017-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: berkshelf
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +150,20 @@ dependencies:
122
150
  - - ">="
123
151
  - !ruby/object:Gem::Version
124
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: chef-dk
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
125
167
  description: ''
126
168
  email:
127
169
  - kamaradclimber@gmail.com
@@ -138,10 +180,19 @@ files:
138
180
  - Rakefile
139
181
  - knife-changelog.gemspec
140
182
  - lib/chef/knife/changelog.rb
183
+ - lib/knife/changelog/berksfile.rb
141
184
  - lib/knife/changelog/changelog.rb
185
+ - lib/knife/changelog/git.rb
186
+ - lib/knife/changelog/policyfile.rb
142
187
  - lib/knife/changelog/version.rb
143
188
  - resources/Berksfile
144
189
  - resources/Berksfile.lock
190
+ - spec/data/Berksfile
191
+ - spec/data/Berksfile.lock
192
+ - spec/data/Policyfile.lock.json
193
+ - spec/data/Policyfile.rb
194
+ - spec/spec_helper.rb
195
+ - spec/unit/changelog_spec.rb
145
196
  homepage: https://github.com/kamaradclimber/knife-changelog
146
197
  licenses:
147
198
  - MIT
@@ -166,4 +217,10 @@ rubygems_version: 2.6.13
166
217
  signing_key:
167
218
  specification_version: 4
168
219
  summary: Facilitate access to cookbooks changelog
169
- test_files: []
220
+ test_files:
221
+ - spec/data/Berksfile
222
+ - spec/data/Berksfile.lock
223
+ - spec/data/Policyfile.lock.json
224
+ - spec/data/Policyfile.rb
225
+ - spec/spec_helper.rb
226
+ - spec/unit/changelog_spec.rb