knife-changelog 0.5.20 → 1.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.
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