Class: Omnibus::Project

Inherits:
Object
  • Object
show all
Includes:
Cleanroom, Digestable, Logging, NullArgumentable, Sugarable, Util
Defined in:
lib/omnibus/project.rb

Constant Summary

Constants included from Util

Util::SHELLOUT_OPTIONS

Constants included from NullArgumentable

NullArgumentable::NULL

DSL methods collapse

Public API collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Util

#copy_file, #create_directory, #create_file, #create_link, included, #remove_directory, #remove_file, #shellout, #shellout!, #windows_safe_path

Methods included from Sugarable

included, #node

Methods included from NullArgumentable

included, #null?

Methods included from Logging

included

Methods included from Digestable

#digest, #digest_directory

Constructor Details

#initialize(filepath = nil) ⇒ Project


68
69
70
# File 'lib/omnibus/project.rb', line 68

def initialize(filepath = nil)
  @filepath = filepath
end

Class Method Details

.load(name) ⇒ Project


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/omnibus/project.rb', line 30

def load(name)
  loaded_projects[name] ||= begin
    filepath = Omnibus.project_path(name)

    if filepath.nil?
      raise MissingProject.new(name)
    else
      log.internal(log_key) do
        "Loading project `#{name}' from `#{filepath}'."
      end
    end

    instance = new(filepath)
    instance.evaluate_file(filepath)
    instance.load_dependencies
    instance
  end
end

Instance Method Details

#<=>(other) ⇒ 1, ...

Comparator for two projects (name)


902
903
904
# File 'lib/omnibus/project.rb', line 902

def <=>(other)
  self.name <=> other.name
end

#==(other) ⇒ true, false Also known as: eql?

Determine if two projects are identical.


1010
1011
1012
# File 'lib/omnibus/project.rb', line 1010

def ==(other)
  self.hash == other.hash
end

#build_iteration(val = NULL) ⇒ Fixnum

Set or retrieve the build iteration of the project. Defaults to 1 if not otherwise set.

Examples:

build_iteration 5

385
386
387
388
389
390
391
# File 'lib/omnibus/project.rb', line 385

def build_iteration(val = NULL)
  if null?(val)
    @build_iteration || 1
  else
    @build_iteration = val
  end
end

#build_meObject


909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
# File 'lib/omnibus/project.rb', line 909

def build_me
  FileUtils.rm_rf(install_dir)
  FileUtils.mkdir_p(install_dir)

  # Cache the build order so we don't re-compute
  softwares = library.build_order

  # Download all softwares in parallel
  ThreadPool.new(Config.workers) do |pool|
    softwares.each do |software|
      pool.schedule { software.fetch }
    end
  end

  # Now build each software
  softwares.each do |software|
    software.build_me
  end

  # Health check
  HealthCheck.run!(self)

  # Package
  package_me

  # Compress
  compress_me
end

#build_version(val = NULL, &block) ⇒ String

Set or retrieve the version of the project.

When using the :git source, by default the output format of the build_version is semver. This can be modified using the :output_format parameter to any of the methods of BuildVersion. For example:

build version do
  source :git, from_dependency: 'chef'
  output_format :git_describe
end

Examples:

Using a string

build_version '1.0.0'

From git

build_version do
  source :git
end

From the version of a dependency

build_version do
  source :version, from_dependency: 'chef'
end

From git of a dependency

build_version do
  source :git, from_dependency: 'chef'
end

See Also:


355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/omnibus/project.rb', line 355

def build_version(val = NULL, &block)
  if block && !null?(val)
    raise Error, 'You cannot specify additional parameters to ' \
      '#build_version when a block is given!'
  end

  if block
    @build_version_dsl = BuildVersionDSL.new(&block)
  else
    if null?(val)
      @build_version_dsl.build_version
    else
      @build_version_dsl = BuildVersionDSL.new(val)
    end
  end
end

#build_version_dslBuildVersionDSL

The DSL for this build version.


837
838
839
# File 'lib/omnibus/project.rb', line 837

def build_version_dsl
  @build_version_dsl
end

#compress(id, &block) ⇒ Object

Add or override a customization for the compressor with the given id. When given multiple blocks with the same id, they are evaluated _in order_, so the last block evaluated will take precedence over the previous ones.

If multiple compress blocks are specified, the “most prefered” one for the current system will be used.

Examples:

With customization

compress :dmg do
  window_bounds '10, 20, 30, 40'
end

Without customization

compress :tgz

436
437
438
439
440
441
442
# File 'lib/omnibus/project.rb', line 436

def compress(id, &block)
  if block
    compressors[id] << block
  else
    compressors[id] << Proc.new {}
  end
end

#compress_meObject


966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
# File 'lib/omnibus/project.rb', line 966

def compress_me
  destination = File.expand_path('pkg', Config.project_root)

  # Create the destination directory
  unless File.directory?(destination)
    FileUtils.mkdir_p(destination)
  end

  # Evaluate any compressor-specific blocks, in order.
  compressors[compressor.id].each do |block|
    compressor.evaluate(&block)
  end

  # Run the actual compressor
  compressor.run!

  # Copy the compressed package and metadata back into the workspace, if it
  # exists
  package_path = File.join(Config.package_dir, compressor.package_name)

  if File.file?(package_path)
    FileUtils.cp(package_path, destination)
    FileUtils.cp("#{package_path}.metadata.json", destination)
  end
end

#compressor~Compressor::Base

Instantiate a new instance of the best compressor for this system.


828
829
830
# File 'lib/omnibus/project.rb', line 828

def compressor
  @compressor ||= Compressor.for_current_system(compressors.keys).new(self)
end

#compressorsHash<Symbol, Array<Proc>>

The list of compressors, in the following format:

{
  id: [#<Proc:0x001>, #<Proc:0x002>],
  # ...
}

819
820
821
# File 'lib/omnibus/project.rb', line 819

def compressors
  @compressors ||= Hash.new { |h, k| h[k] = [] }
end

#config_file(val) ⇒ Array<String>

Add a config file.

Examples:

config_file '/path/to/config.rb'

629
630
631
632
# File 'lib/omnibus/project.rb', line 629

def config_file(val)
  config_files << val
  config_files.dup
end

#config_filesArray<String>

The list of config files for this software.


724
725
726
# File 'lib/omnibus/project.rb', line 724

def config_files
  @config_files ||= []
end

#conflict(val) ⇒ Array<String>

Add to the list of packages this one conflicts with.

Examples:

conflicts 'foo'
conflicts 'bar'

309
310
311
312
# File 'lib/omnibus/project.rb', line 309

def conflict(val)
  conflicts << val
  conflicts.dup
end

#conflictsArray<String>

The list of things this project conflicts with.


753
754
755
# File 'lib/omnibus/project.rb', line 753

def conflicts
  @conflicts ||= []
end

#culpritSoftware?

The software definition which dirtied this project.


884
885
886
# File 'lib/omnibus/project.rb', line 884

def culprit
  @culprit
end

#default_rootString

The default root where a project should be installed. On Windows-based systems, this value defaults to C:. On non-Windows systems, this value defaults to /opt.

Examples:

install_dir "#{default_root}/chef" #=> Produces +C:/chef+ on Windows and
                                   #=> +/opt/chef+ on Linux

185
186
187
188
189
190
191
# File 'lib/omnibus/project.rb', line 185

def default_root
  if windows?
    'C:'
  else
    '/opt'
  end
end

#dependenciesArray<String>

The list of software dependencies for this project. These is the software that comprises your project, and is distinct from runtime dependencies.

See Also:


704
705
706
# File 'lib/omnibus/project.rb', line 704

def dependencies
  @dependencies ||= []
end

#dependency(val) ⇒ Array<String>

Add a software dependency.

Note that this is a *build time* dependency. If you need to specify an external dependency that is required at runtime, see #runtime_dependency instead.

Examples:

dependency 'foo'
dependency 'bar'

571
572
573
574
# File 'lib/omnibus/project.rb', line 571

def dependency(val)
  dependencies << val
  dependencies.dup
end

#dependency?(software) ⇒ true, false

Indicates whether the given software is defined as a software component of this project.


850
851
852
853
# File 'lib/omnibus/project.rb', line 850

def dependency?(software)
  name = software.is_a?(Software) ? software.name : software
  dependencies.include?(name)
end

#description(val = NULL) ⇒ String

Set or retrieve the project description.

Examples:

description 'This is my description'

266
267
268
269
270
271
272
# File 'lib/omnibus/project.rb', line 266

def description(val = NULL)
  if null?(val)
    @description || "The full stack of #{name}"
  else
    @description = val
  end
end

#dirty!(software) ⇒ true, false

Dirty the cache for this project. This can be called by other projects, install path cache, or software definitions to invalidate the cache for this project.


874
875
876
877
# File 'lib/omnibus/project.rb', line 874

def dirty!(software)
  raise ProjectAlreadyDirty.new(self) if culprit
  @culprit = software
end

#dirty?true, false

Determine if the cache for this project is dirty.


893
894
895
# File 'lib/omnibus/project.rb', line 893

def dirty?
  !!culprit
end

#exclude(pattern) ⇒ Array<String>

Add a new exclusion pattern for a list of files or folders to exclude when making the package.

Examples:

exclude '.git'

611
612
613
614
# File 'lib/omnibus/project.rb', line 611

def exclude(pattern)
  exclusions << pattern
  exclusions.dup
end

#exclusionsArray<String>

The list of exclusions for this project.


771
772
773
# File 'lib/omnibus/project.rb', line 771

def exclusions
  @exclusions ||= []
end

#extra_package_file(val) ⇒ Array<String>

Add other files or dirs outside of install_dir. These files retain their relative paths inside the scratch directory:

/path/to/foo.txt #=> /tmp/package/path/to/foo.txt

Examples:

extra_package_file '/path/to/file'

651
652
653
654
# File 'lib/omnibus/project.rb', line 651

def extra_package_file(val)
  extra_package_files << val
  extra_package_files.dup
end

#extra_package_files(val = NULL) ⇒ Array<String>

The list of files and directories used to build this project.


733
734
735
# File 'lib/omnibus/project.rb', line 733

def extra_package_files(val = NULL)
  @extra_package_files ||= []
end

#filepathString?

The path (on disk) where this project came from. Warning: this can be nil if a project was dynamically created!


714
715
716
# File 'lib/omnibus/project.rb', line 714

def filepath
  @filepath
end

#files_pathString

Path to the /files directory in the omnibus project. This directory can contain arbritary files used by the project.

Examples:

patch = File.join(files_path, 'rubygems', 'patch.rb')

204
205
206
# File 'lib/omnibus/project.rb', line 204

def files_path
  File.expand_path("#{Config.project_root}/files")
end

#friendly_name(val = NULL) ⇒ String

Set or retrieve a friendly name for the project. This defaults to the capitalized name if not specified.

Examples:

friendly_name 'Chef'

114
115
116
117
118
119
120
# File 'lib/omnibus/project.rb', line 114

def friendly_name(val = NULL)
  if null?(val)
    @friendly_name || name.capitalize
  else
    @friendly_name = val
  end
end

#hashFixnum

The unique “hash” for this project.

See Also:

  • Omnibus::Project.((#shasum)

999
1000
1001
# File 'lib/omnibus/project.rb', line 999

def hash
  shasum.hash
end

#homepage(val = NULL) ⇒ String

**[Required]** Set or retrive the package homepage.

Examples:

homepage 'https://www.getchef.com'

Raises:


246
247
248
249
250
251
252
# File 'lib/omnibus/project.rb', line 246

def homepage(val = NULL)
  if null?(val)
    @homepage || raise(MissingRequiredAttribute.new(self, :homepage, 'https://www.getchef.com'))
  else
    @homepage = val
  end
end

#install_dir(val = NULL) ⇒ String

**[Required]** Set or retrieve the path at which the project should be installed by the generated package.

Even on Windows-based systems, this path should be the Unix-like path, since that's what Ruby expects. In the event ++ is used as a file separator, it will be replaced with /. This method also attempts to remove duplicate slashes which might be caused as a result of string interpolation.

Examples:

install_dir '/opt/chef'

Raises:


165
166
167
168
169
170
171
# File 'lib/omnibus/project.rb', line 165

def install_dir(val = NULL)
  if null?(val)
    @install_dir || raise(MissingRequiredAttribute.new(self, :install_dir, '/opt/chef'))
  else
    @install_dir = val.gsub('\\', '/').squeeze('/').chomp('/')
  end
end

#libraryLibrary

The library for this Omnibus project.


860
861
862
# File 'lib/omnibus/project.rb', line 860

def library
  @library ||= Library.new(self)
end

#load_dependenciestrue

Recursively load all the dependencies for this project.


686
687
688
689
690
691
692
# File 'lib/omnibus/project.rb', line 686

def load_dependencies
  dependencies.each do |dependency|
    Software.load(self, dependency)
  end

  true
end

#maintainer(val = NULL) ⇒ String

**[Required]** Set or retrieve the the package maintainer.

Examples:

maintainer 'Chef Software, Inc.'

Raises:


223
224
225
226
227
228
229
# File 'lib/omnibus/project.rb', line 223

def maintainer(val = NULL)
  if null?(val)
    @maintainer || raise(MissingRequiredAttribute.new(self, :maintainer, 'Chef Software, Inc.'))
  else
    @maintainer = val
  end
end

#name(val = NULL) ⇒ String

**[Required]** Set or retrieve the name of the project.

Examples:

name 'chef'

Raises:


93
94
95
96
97
98
99
# File 'lib/omnibus/project.rb', line 93

def name(val = NULL)
  if null?(val)
    @name || raise(MissingRequiredAttribute.new(self, :name, 'hamlet'))
  else
    @name = val
  end
end

#ohaiOhai

A proxy method to the underlying Ohai system.

Examples:

ohai['platform_family']

665
666
667
# File 'lib/omnibus/project.rb', line 665

def ohai
  Ohai
end

#override(name, val = NULL) ⇒ Hash

Set or retrieve the overrides hash for one piece of software being overridden. Calling it as a setter does not merge hash entries and it will set all the overrides for a given software definition.

Examples:

override 'chef', version: '1.2.3'

482
483
484
485
486
487
488
# File 'lib/omnibus/project.rb', line 482

def override(name, val = NULL)
  if null?(val)
    overrides[name.to_sym]
  else
    overrides[name.to_sym] = val
  end
end

#overridesHash

Retrieve the list of overrides for all software being overridden.


780
781
782
# File 'lib/omnibus/project.rb', line 780

def overrides
  @overrides ||= {}
end

#package(id, &block) ⇒ Object

Add or override a customization for the packager with the given id. When given multiple blocks with the same id, they are evaluated _in order_, so the last block evaluated will take precedence over the previous ones.

Examples:

package :id do
  key 'value'
end

407
408
409
410
411
412
413
# File 'lib/omnibus/project.rb', line 407

def package(id, &block)
  unless block
    raise InvalidValue.new(:package, 'have a block')
  end

  packagers[id] << block
end

#package_group(val = NULL) ⇒ String

Set or retrieve the group the package should install as. This varies with operating system and may be ignored if the underlying packager does not support it.

Defaults to Ohai. If +Ohai+ is nil, it defaults to “root”.

Examples:

package_group 'build'

507
508
509
510
511
512
513
# File 'lib/omnibus/project.rb', line 507

def package_group(val = NULL)
  if null?(val)
    @package_group || Ohai['root_group'] || 'root'
  else
    @package_group = val
  end
end

#package_meObject


941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
# File 'lib/omnibus/project.rb', line 941

def package_me
  destination = File.expand_path('pkg', Config.project_root)

  # Create the destination directory
  unless File.directory?(destination)
    FileUtils.mkdir_p(destination)
  end

  # Evaluate any packager-specific blocks, in order.
  packagers[packager.id].each do |block|
    packager.evaluate(&block)
  end

  # Run the actual packager
  packager.run!

  # Copy the generated package and metadata back into the workspace
  package_path = File.join(Config.package_dir, packager.package_name)
  FileUtils.cp(package_path, destination, preserve: true)
  FileUtils.cp("#{package_path}.metadata.json", destination, preserve: true)
end

#package_name(val = NULL) ⇒ String

Set or retrieve the package name of the project. Defaults to the package name defaults to the project name.

Examples:

package_name 'com.chef.project'

135
136
137
138
139
140
141
# File 'lib/omnibus/project.rb', line 135

def package_name(val = NULL)
  if null?(val)
    @package_name || name
  else
    @package_name = val
  end
end

#package_scripts_path(arg = NULL) ⇒ String

The path to the package scripts directory for this project. These are optional scripts that can be bundled into the resulting package for running at various points in the package management lifecycle.

These scripts and their names vary with operating system.


545
546
547
548
549
550
551
# File 'lib/omnibus/project.rb', line 545

def package_scripts_path(arg = NULL)
  if null?(arg)
    @package_scripts_path || "#{Config.project_root}/package-scripts/#{name}"
  else
    @package_scripts_path = File.expand_path(arg)
  end
end

#package_user(val = NULL) ⇒ String

Set or retrieve the user the package should install as. This varies with operating system, and may be ignored if the underlying packager does not support it.

Defaults to “root”.

Examples:

package_user 'build'

460
461
462
463
464
465
466
# File 'lib/omnibus/project.rb', line 460

def package_user(val = NULL)
  if null?(val)
    @package_user || 'root'
  else
    @package_user = val
  end
end

#packager~Packager::Base

Instantiate a new instance of the best packager for this system.


804
805
806
# File 'lib/omnibus/project.rb', line 804

def packager
  @packager ||= Packager.for_current_system.new(self)
end

#packagersHash<Symbol, Array<Proc>>

The list of packagers, in the following format:

{
  id: [#<Proc:0x001>, #<Proc:0x002>],
  # ...
}

795
796
797
# File 'lib/omnibus/project.rb', line 795

def packagers
  @packagers ||= Hash.new { |h, k| h[k] = [] }
end

#replace(val = NULL) ⇒ String

Add to the list of packages this one replaces.

This should only be used when renaming a package and obsoleting the old name of the package. **Setting this to the same name as package_name will cause RPM upgrades to fail.**

Examples:

replace 'the-old-package'

290
291
292
293
# File 'lib/omnibus/project.rb', line 290

def replace(val = NULL)
  replaces << val
  replaces.dup
end

#replacesArray<String>

The list of things this project replaces with.


762
763
764
# File 'lib/omnibus/project.rb', line 762

def replaces
  @replaces ||= []
end

#resources_path(val = NULL) ⇒ String

Set or retrieve the path to the resources on disk for use in packagers.

Examples:

resources_path '/path/to/resources'

527
528
529
530
531
532
533
# File 'lib/omnibus/project.rb', line 527

def resources_path(val = NULL)
  if null?(val)
    @resources_path || "#{Config.project_root}/resources/#{name}"
  else
    @resources_path = File.expand_path(val)
  end
end

#runtime_dependenciesArray<String>

The list of software dependencies for this project.

These is the software that is used at runtime for your project.


744
745
746
# File 'lib/omnibus/project.rb', line 744

def runtime_dependencies
  @runtime_dependencies ||= []
end

#runtime_dependency(val) ⇒ Array<String>

Add a package that is a runtime dependency of this project.

This is distinct from a build-time dependency, which should correspond to a software definition.

Examples:

runtime_dependency 'foo'

592
593
594
595
# File 'lib/omnibus/project.rb', line 592

def runtime_dependency(val)
  runtime_dependencies << val
  runtime_dependencies.dup
end

#shasumString

The unique SHA256 for this project.

A project is defined by its name, its build_version, its install_dir, and any overrides (as JSON). Additionally, if provided, the actual file contents are included in the SHA to ensure uniqueness.


1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
# File 'lib/omnibus/project.rb', line 1024

def shasum
  @shasum ||= begin
    digest = Digest::SHA256.new

    update_with_string(digest, name)
    update_with_string(digest, install_dir)
    update_with_string(digest, JSON.fast_generate(overrides))

    if filepath && File.exist?(filepath)
      update_with_file_contents(digest, filepath)
    else
      update_with_string(digest, '<DYNAMIC>')
    end

    digest.hexdigest
  end
end