omnibus 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.travis.yml +3 -11
  4. data/CHANGELOG.md +50 -0
  5. data/MAINTAINERS.md +26 -0
  6. data/README.md +61 -4
  7. data/appveyor.yml +35 -0
  8. data/docs/Build Cache.md +28 -3
  9. data/docs/Building on RHEL.md +1 -1
  10. data/features/commands/publish.feature +4 -9
  11. data/features/step_definitions/generator_steps.rb +14 -1
  12. data/features/support/env.rb +5 -3
  13. data/lib/omnibus.rb +10 -0
  14. data/lib/omnibus/build_version.rb +34 -25
  15. data/lib/omnibus/build_version_dsl.rb +43 -4
  16. data/lib/omnibus/builder.rb +30 -11
  17. data/lib/omnibus/changelog.rb +52 -0
  18. data/lib/omnibus/changelog_printer.rb +77 -0
  19. data/lib/omnibus/cli.rb +37 -2
  20. data/lib/omnibus/cli/changelog.rb +149 -0
  21. data/lib/omnibus/cli/publish.rb +30 -10
  22. data/lib/omnibus/config.rb +41 -2
  23. data/lib/omnibus/digestable.rb +6 -1
  24. data/lib/omnibus/exceptions.rb +15 -1
  25. data/lib/omnibus/fetcher.rb +78 -34
  26. data/lib/omnibus/fetchers/git_fetcher.rb +84 -42
  27. data/lib/omnibus/fetchers/net_fetcher.rb +64 -13
  28. data/lib/omnibus/fetchers/null_fetcher.rb +8 -1
  29. data/lib/omnibus/fetchers/path_fetcher.rb +24 -1
  30. data/lib/omnibus/file_syncer.rb +52 -1
  31. data/lib/omnibus/generator.rb +22 -21
  32. data/lib/omnibus/generator_files/.kitchen.yml.erb +8 -12
  33. data/lib/omnibus/generator_files/Berksfile.erb +4 -4
  34. data/lib/omnibus/generator_files/Gemfile.erb +3 -3
  35. data/lib/omnibus/generator_files/README.md.erb +17 -0
  36. data/lib/omnibus/generator_files/omnibus.rb.erb +6 -0
  37. data/lib/omnibus/git_repository.rb +43 -0
  38. data/lib/omnibus/health_check.rb +5 -1
  39. data/lib/omnibus/manifest.rb +134 -0
  40. data/lib/omnibus/manifest_diff.rb +88 -0
  41. data/lib/omnibus/manifest_entry.rb +43 -0
  42. data/lib/omnibus/metadata.rb +19 -1
  43. data/lib/omnibus/package.rb +9 -0
  44. data/lib/omnibus/packagers/base.rb +1 -1
  45. data/lib/omnibus/packagers/bff.rb +5 -5
  46. data/lib/omnibus/packagers/deb.rb +11 -4
  47. data/lib/omnibus/packagers/msi.rb +243 -2
  48. data/lib/omnibus/packagers/rpm.rb +68 -14
  49. data/lib/omnibus/packagers/solaris.rb +17 -23
  50. data/lib/omnibus/project.rb +129 -16
  51. data/lib/omnibus/publisher.rb +62 -49
  52. data/lib/omnibus/publishers/artifactory_publisher.rb +96 -5
  53. data/lib/omnibus/publishers/s3_publisher.rb +20 -25
  54. data/lib/omnibus/s3_cache.rb +13 -34
  55. data/lib/omnibus/s3_helpers.rb +119 -0
  56. data/lib/omnibus/semantic_version.rb +57 -0
  57. data/lib/omnibus/software.rb +87 -28
  58. data/lib/omnibus/sugarable.rb +18 -0
  59. data/lib/omnibus/templating.rb +8 -1
  60. data/lib/omnibus/version.rb +1 -1
  61. data/omnibus.gemspec +10 -7
  62. data/resources/bff/gen.template.erb +1 -1
  63. data/resources/msi/bundle.wxs.erb +17 -0
  64. data/resources/msi/localization-en-us.wxl.erb +1 -1
  65. data/resources/rpm/spec.erb +1 -1
  66. data/spec/functional/builder_spec.rb +15 -7
  67. data/spec/functional/fetchers/git_fetcher_spec.rb +44 -12
  68. data/spec/functional/fetchers/net_fetcher_spec.rb +171 -20
  69. data/spec/functional/fetchers/path_fetcher_spec.rb +16 -1
  70. data/spec/functional/file_syncer_spec.rb +58 -5
  71. data/spec/functional/templating_spec.rb +17 -6
  72. data/spec/spec_helper.rb +17 -0
  73. data/spec/support/file_helpers.rb +12 -2
  74. data/spec/support/git_helpers.rb +23 -18
  75. data/spec/support/matchers.rb +22 -0
  76. data/spec/support/output_helpers.rb +29 -0
  77. data/spec/unit/build_version_dsl_spec.rb +31 -4
  78. data/spec/unit/build_version_spec.rb +11 -4
  79. data/spec/unit/builder_spec.rb +33 -0
  80. data/spec/unit/changelog_spec.rb +55 -0
  81. data/spec/unit/cleanroom_spec.rb +1 -1
  82. data/spec/unit/compressors/dmg_spec.rb +3 -3
  83. data/spec/unit/compressors/tgz_spec.rb +3 -3
  84. data/spec/unit/config_spec.rb +3 -1
  85. data/spec/unit/fetcher_spec.rb +35 -0
  86. data/spec/unit/fetchers/git_fetcher_spec.rb +28 -14
  87. data/spec/unit/fetchers/net_fetcher_spec.rb +178 -24
  88. data/spec/unit/fetchers/path_fetcher_spec.rb +8 -7
  89. data/spec/unit/generator_spec.rb +22 -21
  90. data/spec/unit/git_repository_spec.rb +60 -0
  91. data/spec/unit/manifest_diff_spec.rb +75 -0
  92. data/spec/unit/manifest_spec.rb +116 -0
  93. data/spec/unit/metadata_spec.rb +11 -0
  94. data/spec/unit/omnibus_spec.rb +9 -6
  95. data/spec/unit/package_spec.rb +1 -1
  96. data/spec/unit/packagers/base_spec.rb +8 -18
  97. data/spec/unit/packagers/bff_spec.rb +9 -5
  98. data/spec/unit/packagers/deb_spec.rb +44 -12
  99. data/spec/unit/packagers/makeself_spec.rb +4 -4
  100. data/spec/unit/packagers/msi_spec.rb +122 -6
  101. data/spec/unit/packagers/pkg_spec.rb +3 -3
  102. data/spec/unit/packagers/rpm_spec.rb +37 -12
  103. data/spec/unit/project_spec.rb +18 -1
  104. data/spec/unit/publisher_spec.rb +65 -0
  105. data/spec/unit/publishers/artifactory_publisher_spec.rb +26 -20
  106. data/spec/unit/publishers/s3_publisher_spec.rb +14 -30
  107. data/spec/unit/s3_cacher_spec.rb +1 -1
  108. data/spec/unit/s3_helpers_spec.rb +32 -0
  109. data/spec/unit/semantic_version_spec.rb +55 -0
  110. data/spec/unit/software_spec.rb +112 -4
  111. metadata +86 -16
@@ -14,6 +14,8 @@
14
14
  # limitations under the License.
15
15
  #
16
16
 
17
+ require 'time'
18
+
17
19
  module Omnibus
18
20
  class BuildVersionDSL
19
21
  include Logging
@@ -33,7 +35,7 @@ module Omnibus
33
35
  @output_method = nil
34
36
 
35
37
  if version_string
36
- @build_version = version_string
38
+ self.build_version = version_string
37
39
  elsif block_given?
38
40
  instance_eval(&block)
39
41
  construct_build_version unless from_dependency?
@@ -78,7 +80,7 @@ module Omnibus
78
80
  # @return [String]
79
81
  def explain
80
82
  if build_version
81
- "Build Version: #{@build_version}"
83
+ "Build Version: #{build_version}"
82
84
  else
83
85
  if from_dependency?
84
86
  "Build Version will be determined from software '#{version_dependency}'"
@@ -105,6 +107,43 @@ module Omnibus
105
107
  source_options[:from_dependency]
106
108
  end
107
109
 
110
+ def build_version=(new_version)
111
+ @build_version = maybe_append_timestamp(new_version)
112
+ end
113
+
114
+ # Append the build_start_time to the given string if
115
+ # Config.append_timestamp is true
116
+ #
117
+ # @param [String] version
118
+ # @return [String]
119
+ def maybe_append_timestamp(version)
120
+ if Config.append_timestamp && !has_timestamp?(version)
121
+ [version, Omnibus::BuildVersion.build_start_time].join("+")
122
+ else
123
+ version
124
+ end
125
+ end
126
+
127
+ # Returns true if a given version string Looks like it was already
128
+ # created with a function that added a timestamp. The goal of this
129
+ # is to avoid breaking all of the people who are currently using
130
+ # BuildVersion.semver to create dates.
131
+ #
132
+ # @param [String] version
133
+ # @return [Boolean]
134
+ def has_timestamp?(version)
135
+ _ver, build_info = version.split('+')
136
+ return false if build_info.nil?
137
+ build_info.split('.').any? do |part|
138
+ begin
139
+ Time.strptime(part, Omnibus::BuildVersion::TIMESTAMP_FORMAT)
140
+ true
141
+ rescue ArgumentError
142
+ false
143
+ end
144
+ end
145
+ end
146
+
108
147
  # Determines the build_version based on source_type, output_method.
109
148
  #
110
149
  # @param version_source [Omnibus::Software] Software object from which the
@@ -120,10 +159,10 @@ module Omnibus
120
159
  end
121
160
 
122
161
  output = output_method || :semver
123
- @build_version = version.send(output)
162
+ self.build_version = version.send(output)
124
163
  when :version
125
164
  if version_source
126
- @build_version = version_source.version
165
+ self.build_version = version_source.version
127
166
  else
128
167
  raise "Please tell me the source to get the version from"
129
168
  end
@@ -488,10 +488,16 @@ module Omnibus
488
488
  # @return (see #command)
489
489
  #
490
490
  def copy(source, destination, options = {})
491
- build_commands << BuildCommand.new("copy `#{source}' to `#{destination}'") do
491
+ command = "copy `#{source}' to `#{destination}'"
492
+ build_commands << BuildCommand.new(command) do
492
493
  Dir.chdir(software.project_dir) do
493
- FileSyncer.glob(source).each do |file|
494
- FileUtils.cp_r(file, destination, options)
494
+ files = FileSyncer.glob(source)
495
+ if files.empty?
496
+ log.warn(log_key) {"no matched files for glob #{command}"}
497
+ else
498
+ files.each do |file|
499
+ FileUtils.cp_r(file, destination, options)
500
+ end
495
501
  end
496
502
  end
497
503
  end
@@ -499,7 +505,7 @@ module Omnibus
499
505
  expose :copy
500
506
 
501
507
  #
502
- # Copy the given source to the destination. This method accepts a single
508
+ # Move the given source to the destination. This method accepts a single
503
509
  # file or a file pattern to match
504
510
  #
505
511
  # @param [String] source
@@ -511,10 +517,16 @@ module Omnibus
511
517
  # @return (see #command)
512
518
  #
513
519
  def move(source, destination, options = {})
514
- build_commands << BuildCommand.new("move `#{source}' to `#{destination}'") do
520
+ command = "move `#{source}' to `#{destination}'"
521
+ build_commands << BuildCommand.new(command) do
515
522
  Dir.chdir(software.project_dir) do
516
- FileSyncer.glob(source).each do |file|
517
- FileUtils.mv(file, destination, options)
523
+ files = FileSyncer.glob(source)
524
+ if files.empty?
525
+ log.warn(log_key) {"no matched files for glob #{command}"}
526
+ else
527
+ files.each do |file|
528
+ FileUtils.mv(file, destination, options)
529
+ end
518
530
  end
519
531
  end
520
532
  end
@@ -534,10 +546,16 @@ module Omnibus
534
546
  # @return (see #command)
535
547
  #
536
548
  def link(source, destination, options = {})
537
- build_commands << BuildCommand.new("link `#{source}' to `#{destination}'") do
549
+ command = "link `#{source}' to `#{destination}'"
550
+ build_commands << BuildCommand.new(command) do
538
551
  Dir.chdir(software.project_dir) do
539
- FileSyncer.glob(source).each do |file|
540
- FileUtils.ln_s(file, destination, options)
552
+ files = FileSyncer.glob(source)
553
+ if files.empty?
554
+ log.warn(log_key) {"no matched files for glob #{command}"}
555
+ else
556
+ files.each do |file|
557
+ FileUtils.ln_s(file, destination, options)
558
+ end
541
559
  end
542
560
  end
543
561
  end
@@ -580,7 +598,8 @@ module Omnibus
580
598
  #
581
599
  def build
582
600
  log.info(log_key) { 'Starting build' }
583
-
601
+ shasum # ensure shashum is calculated before build since the build can alter the shasum
602
+ log.internal(log_key) { "Cached builder checksum before build: #{shasum}"}
584
603
  if software.overridden?
585
604
  log.info(log_key) do
586
605
  "Version overridden from #{software.default_version} to "\
@@ -0,0 +1,52 @@
1
+ require 'omnibus/git_repository'
2
+
3
+ module Omnibus
4
+ class ChangeLog
5
+ CHANGELOG_TAG = "ChangeLog-Entry"
6
+
7
+ attr_reader :end_ref
8
+ def initialize(start_ref=nil, end_ref="HEAD", git_repo=GitRepository.new('./'))
9
+ @start_ref = start_ref
10
+ @end_ref = end_ref
11
+ @git_repo = git_repo
12
+ end
13
+
14
+ def authors
15
+ git_repo.authors(start_ref, end_ref)
16
+ end
17
+
18
+ def changelog_entries
19
+ entries = []
20
+ current_entry = []
21
+ git_repo.commit_messages(start_ref, end_ref).each do |l|
22
+ if blank?(l)
23
+ entries << current_entry
24
+ current_entry = []
25
+ elsif tagged?(l)
26
+ entries << current_entry
27
+ current_entry = Array(l.sub(/^#{CHANGELOG_TAG}:[\s]*/, ""))
28
+ elsif !current_entry.empty?
29
+ current_entry << l
30
+ end
31
+ end
32
+ entries << current_entry
33
+ entries.reject(&:empty?).map(&:join)
34
+ end
35
+
36
+ def start_ref
37
+ @start_ref ||= git_repo.latest_tag
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :git_repo
43
+
44
+ def blank?(line)
45
+ line =~ /^[\s]*$/
46
+ end
47
+
48
+ def tagged?(line)
49
+ line =~ /^#{CHANGELOG_TAG}:/
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,77 @@
1
+ module Omnibus
2
+ class ChangeLogPrinter
3
+
4
+ def initialize(changelog, diff, source_path="../")
5
+ @changelog = changelog
6
+ @diff = diff
7
+ @source_path = source_path
8
+ end
9
+
10
+ def print(new_version)
11
+ puts "## #{new_version} (#{Time.now.strftime('%Y-%m-%d')})"
12
+ print_changelog
13
+ if !diff.empty?
14
+ print_components
15
+ puts ""
16
+ end
17
+ print_contributors
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :changelog, :diff, :source_path
23
+
24
+ def print_changelog(cl=changelog, indent=0)
25
+ cl.changelog_entries.each do |entry|
26
+ puts "#{' ' * indent}* #{entry.sub("\n", "\n #{' ' * indent}")}\n"
27
+ end
28
+ end
29
+
30
+ def print_components
31
+ puts "### Components\n"
32
+ print_new_components
33
+ print_updated_components
34
+ print_removed_components
35
+ end
36
+
37
+ def print_new_components
38
+ return if diff.added.empty?
39
+ puts "New Components"
40
+ diff.added.each do |entry|
41
+ puts "* #{entry[:name]} (#{entry[:new_version]})"
42
+ end
43
+ puts ""
44
+ end
45
+
46
+ def print_updated_components
47
+ return if diff.updated.empty?
48
+ puts "Updated Components"
49
+ diff.updated.each do |entry|
50
+ puts sprintf("* %s (%.8s -> %.8s)",
51
+ entry[:name], entry[:old_version], entry[:new_version])
52
+ repo_path = ::File.join(source_path, entry[:name])
53
+ if entry[:source_type] == 'git' && ::File.directory?("#{repo_path}/.git")
54
+ cl = ChangeLog.new(entry[:old_version], entry[:new_version], GitRepository.new("#{repo_path}"))
55
+ print_changelog(cl, 2)
56
+ end
57
+ end
58
+ puts ""
59
+ end
60
+
61
+ def print_removed_components
62
+ return if diff.removed.empty?
63
+ puts "Removed Components"
64
+ diff.removed.each do |entry|
65
+ puts "* #{entry[:name]} (#{entry[:old_version]})"
66
+ end
67
+ puts ""
68
+ end
69
+
70
+ def print_contributors
71
+ puts "### Contributors\n"
72
+ changelog.authors.each do |author|
73
+ puts "* #{author}"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -61,12 +61,47 @@ module Omnibus
61
61
  #
62
62
  # $ omnibus build chefdk
63
63
  #
64
+ method_option :output_manifest,
65
+ desc: "Create version-manifest.json in current directory at the end of the build",
66
+ type: :boolean,
67
+ default: true
68
+ method_option :use_manifest,
69
+ desc: "Use the given manifest when downloading software sources.",
70
+ type: :string,
71
+ default: nil
64
72
  desc 'build PROJECT', 'Build the given Omnibus project'
65
73
  def build(name)
66
- project = Project.load(name)
74
+ manifest = if @options[:use_manifest]
75
+ Omnibus::Manifest.from_file(@options[:use_manifest])
76
+ else
77
+ nil
78
+ end
67
79
 
80
+ project = Project.load(name, manifest)
68
81
  say("Building #{project.name} #{project.build_version}...")
69
- project.build_me
82
+ project.download
83
+ project.build
84
+
85
+ if @options[:output_manifest]
86
+ FileUtils.mkdir_p('pkg')
87
+ File.open(::File.join('pkg', 'version-manifest.json'), 'w') do |f|
88
+ f.write(project.built_manifest.to_json)
89
+ end
90
+ end
91
+ end
92
+
93
+
94
+ register(Command::ChangeLog, 'changelog', 'changelog [COMMAND]', 'Create and view changelogs')
95
+ CLI.tasks['changelog'].options = Command::ChangeLog.class_options
96
+
97
+ #
98
+ # Generate a version manifest for the given project definition
99
+ #
100
+ # $ omnibus manifest PROJECT
101
+ #
102
+ desc 'manifest PROJECT', 'Print a manifest for the given Omnibus project'
103
+ def manifest(name)
104
+ puts JSON.pretty_generate(Project.load(name).built_manifest.to_hash)
70
105
  end
71
106
 
72
107
  #
@@ -0,0 +1,149 @@
1
+ #
2
+ # Copyright 2015 Chef Software, Inc.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+
17
+ require 'omnibus/changelog'
18
+ require 'omnibus/changelog_printer'
19
+ require 'omnibus/manifest_diff'
20
+ require 'omnibus/semantic_version'
21
+
22
+ module Omnibus
23
+ class Command::ChangeLog < Command::Base
24
+ namespace :changelog
25
+
26
+ #
27
+ # Generate a Changelog
28
+ #
29
+ # $ omnibus changelog generate
30
+ #
31
+ method_option :source_path,
32
+ desc: "Path to local checkout of git dependencies",
33
+ type: :string,
34
+ default: "../"
35
+
36
+ method_option :starting_manifest,
37
+ desc: "Path to version-manifest from the last version (we attempt to pull it from the git history if not given)",
38
+ type: :string
39
+
40
+ method_option :ending_manifest,
41
+ desc: "Path to the version-manifest from the current version",
42
+ type: :string,
43
+ default: "version-manifest.json"
44
+
45
+ method_option :skip_components,
46
+ desc: "Don't include component changes in the changelog",
47
+ type: :boolean,
48
+ default: false
49
+
50
+ method_option :major,
51
+ desc: "Bump the major version",
52
+ type: :boolean,
53
+ default: false
54
+
55
+ method_option :minor,
56
+ desc: "Bump the minor version",
57
+ type: :boolean,
58
+ default: true
59
+
60
+ method_option :patch,
61
+ desc: "Bump the patch version",
62
+ type: :boolean,
63
+ default: false
64
+
65
+ method_option :version,
66
+ desc: "Explicit version for this changelog",
67
+ type: :string
68
+
69
+ desc 'generate [START] [END]', 'Generate a changelog for a new release'
70
+ def generate(start_ref=nil, end_ref=nil)
71
+ @start_ref = start_ref
72
+ @end_ref = end_ref
73
+ diff = if @options[:skip_components]
74
+ Omnibus::EmptyManifestDiff.new
75
+ else
76
+ Omnibus::ManifestDiff.new(old_manifest, new_manifest)
77
+ end
78
+
79
+ Omnibus::ChangeLogPrinter.new(ChangeLog.new(starting_revision, ending_revision),
80
+ diff,
81
+ @options[:source_path]).print(new_version)
82
+ end
83
+
84
+ private
85
+
86
+ def local_git_repo
87
+ GitRepository.new
88
+ end
89
+
90
+ def old_manifest
91
+ @old_manifest ||= if @options[:starting_manifest]
92
+ Omnibus::Manifest.from_file(@options[:starting_manifest])
93
+ else
94
+ manifest_for_revision(starting_revision)
95
+ end
96
+ end
97
+
98
+ def new_manifest
99
+ @new_manifest ||= if @options[:ending_manifest]
100
+ Omnibus::Manifest.from_file(@options[:ending_manifest])
101
+ else
102
+ manifest_for_revision(ending_revision)
103
+ end
104
+ end
105
+
106
+ def manifest_for_revision(rev)
107
+ Omnibus::Manifest.from_hash(JSON.parse(local_git_repo.file_at_revision("version-manifest.json", rev)))
108
+ end
109
+
110
+
111
+ def new_version
112
+ if @options[:version]
113
+ @options[:version]
114
+ elsif @options[:patch]
115
+ Omnibus::SemanticVersion.new(local_git_repo.latest_tag).next_patch.to_s
116
+ elsif @options[:minor] && !@options[:major] # minor is the default so it will always be true
117
+ Omnibus::SemanticVersion.new(local_git_repo.latest_tag).next_minor.to_s
118
+ elsif @options[:major]
119
+ Omnibus::SemanticVersion.new(local_git_repo.latest_tag).next_major.to_s
120
+ elsif @options[:ending_manifest]
121
+ new_manifest.build_version
122
+ end
123
+ end
124
+
125
+ # starting_revision is taken from:
126
+ # - value passed as the first argument
127
+ # - value found in the starting manifest
128
+ # - the latest git tag in the local repository
129
+ def starting_revision
130
+ @start_ref ||= if @options[:starting_manifest]
131
+ old_manifest.build_git_revision
132
+ else
133
+ local_git_repo.latest_tag
134
+ end
135
+ end
136
+
137
+ # ending_revision is taken from:
138
+ # - value passed as the first argument
139
+ # - value found in the ending manifest
140
+ # - HEAD in the current git repository
141
+ def ending_revision
142
+ @end_ref ||= if @options[:ending_manifest]
143
+ new_manifest.build_git_revision
144
+ else
145
+ 'HEAD'
146
+ end
147
+ end
148
+ end
149
+ end