Class: Vanagon::Component

Inherits:
Object
  • Object
show all
Includes:
HashableAttributes, Utilities
Defined in:
lib/vanagon/component.rb,
lib/vanagon/component/dsl.rb,
lib/vanagon/component/rules.rb,
lib/vanagon/component/source.rb,
lib/vanagon/extensions/hashable.rb,
lib/vanagon/component/source/git.rb,
lib/vanagon/component/source/http.rb,
lib/vanagon/component/source/local.rb,
lib/vanagon/component/source/rewrite.rb

Defined Under Namespace

Classes: DSL, Rules, Source

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from HashableAttributes

#to_hash, #to_json

Methods included from Utilities

#erb_file, #erb_string, #ex, #find_program_on_path, #get_md5sum, #get_sum, #http_request, #http_request_code, #http_request_generic, #local_command, #remote_ssh_command, #retry_with_timeout, #rsync_from, #rsync_to, #ssh_command

Constructor Details

#initialize(name, settings, platform) ⇒ Vanagon::Component

Component constructor.

Parameters:

  • name (String)

    the name of the component

  • settings (Hash)

    the settings to be used in the component

  • platform (Vanagon::Platform)

    the platform to build the component for



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/vanagon/component.rb', line 155

def initialize(name, settings, platform) # rubocop:disable Metrics/AbcSize
  @name = name
  @settings = settings
  @platform = platform
  @options = {}
  @build_requires = []
  @requires = []
  @configure = []
  @install = []
  @build = []
  @check = []
  @patches = []
  @files = Set.new
  @ghost_files = Set.new
  @directories = []
  @replaces = []
  @provides = []
  @conflicts = []
  @environment = Vanagon::Environment.new
  @sources = []
  @preinstall_actions = []
  @install_triggers = []
  @interest_triggers = []
  @activate_triggers = []
  @postinstall_actions = []
  @preremove_actions = []
  @postremove_actions = []
  @install_only = false
  @service = []
end

Instance Attribute Details

#activate_triggersObject

activate_triggers is a one-dimentional Array of Strings, describing scripts that should be executed when a package identifies an activate trigger



108
109
110
# File 'lib/vanagon/component.rb', line 108

def activate_triggers
  @activate_triggers
end

#buildObject

build will hold an Array of the commands required to build a given component



50
51
52
# File 'lib/vanagon/component.rb', line 50

def build
  @build
end

#build_dirObject

the optional name of a directory to build a component in; most likely to be used for cmake projects, which do not like to be configured or compiled in their own top-level directories.



47
48
49
# File 'lib/vanagon/component.rb', line 47

def build_dir
  @build_dir
end

#build_requiresObject

build_requires holds an Array with a list of the dependencies that a given component needs satisfied before it can be built.



84
85
86
# File 'lib/vanagon/component.rb', line 84

def build_requires
  @build_requires
end

#checkObject

check will hold an Array of the commands required to validate/test a given component



53
54
55
# File 'lib/vanagon/component.rb', line 53

def check
  @check
end

#cleanup_sourceObject

cleanup_source contains whatever value a given component’s Source has specified as instructions for cleaning up after a build is completed. usually a String, but not required to be.



121
122
123
# File 'lib/vanagon/component.rb', line 121

def cleanup_source
  @cleanup_source
end

#configureObject

how should this component be configured?



43
44
45
# File 'lib/vanagon/component.rb', line 43

def configure
  @configure
end

#conflictsObject

conflicts holds an Array of OpenStructs that describe a package that a given component will replace on installation.



96
97
98
# File 'lib/vanagon/component.rb', line 96

def conflicts
  @conflicts
end

#directoriesObject

directories holds an Array with a list of expected directories that will be packed into the resulting artifact’s bill of materials.



81
82
83
# File 'lib/vanagon/component.rb', line 81

def directories
  @directories
end

#dirnameObject

holds the expected directory name of a given component, once it’s been unpacked/decompressed. For git repos, it’s usually the directory that they were cloned to. For the outlying flat files, it’ll end up being defined explicitly as the string ‘./’



38
39
40
# File 'lib/vanagon/component.rb', line 38

def dirname
  @dirname
end

#environmentObject

holds a Vanagon::Environment object, to map out any desired environment variables that should be rendered into the Makefile



60
61
62
# File 'lib/vanagon/component.rb', line 60

def environment
  @environment
end

#extract_withArray

Returns the specific tool or command line invocations that should be used to extract a given component’s primary source.

Returns:

  • (Array)

    the specific tool or command line invocations that should be used to extract a given component’s primary source



41
42
43
# File 'lib/vanagon/component.rb', line 41

def extract_with
  @extract_with
end

#filesSet (readonly)

Retrieve all items from @files not marked as configuration files

Returns:

  • (Set)

    all files not marked as configuration files



# File 'lib/vanagon/component.rb', line 11

#homepageObject

Returns the value of attribute homepage.



28
29
30
# File 'lib/vanagon/component.rb', line 28

def homepage
  @homepage
end

#installObject

install will hold an Array of the commands required to install a given component



56
57
58
# File 'lib/vanagon/component.rb', line 56

def install
  @install
end

#install_onlyObject

When dealing with compiled artifacts generated by setting ‘project.generate_archives true`, you only need to install the component. By setting install_only to true the component ’build’ will exclude the unpack, patch, configure, build, and check steps.



127
128
129
# File 'lib/vanagon/component.rb', line 127

def install_only
  @install_only
end

#install_triggersObject

install_triggers is a one-dimensional Array of OpenStructs, describing scripts that should be executed when a package is installed or upgraded



102
103
104
# File 'lib/vanagon/component.rb', line 102

def install_triggers
  @install_triggers
end

#interest_triggersObject

interest_triggers is a one-dimensional Array of OpenStructs, describing scripts that should be executed when a package identifies an interest trigger



105
106
107
# File 'lib/vanagon/component.rb', line 105

def interest_triggers
  @interest_triggers
end

#licenseObject

Returns the value of attribute license.



27
28
29
# File 'lib/vanagon/component.rb', line 27

def license
  @license
end

#mirrorsSet

Returns a list of unique mirror URIs that should be used to retrieve the upstream source before attempting to retrieve from whatever URI was defined for #uri. If no mirrors are set and the deprecated rewrite system has been configured, this will return rewritten URIs.

Returns:

  • (Set)

    a list of unique mirror URIs that should be used to retrieve the upstream source before attempting to retrieve from whatever URI was defined for #uri. If no mirrors are set and the deprecated rewrite system has been configured, this will return rewritten URIs



243
244
245
# File 'lib/vanagon/component.rb', line 243

def mirrors
  @mirrors
end

#nameObject

The name, version, primary source, supplementary sources, associated patches, upstream URL (for fetching the source), homepage, and license of a given component



20
21
22
# File 'lib/vanagon/component.rb', line 20

def name
  @name
end

#optionsObject

used to hold the checksum settings or other weirdo metadata related to building a given component (git ref, sha, etc.). Probably conflicts or collides with #settings to some degree.



70
71
72
# File 'lib/vanagon/component.rb', line 70

def options
  @options
end

#patchesObject

Returns the value of attribute patches.



24
25
26
# File 'lib/vanagon/component.rb', line 24

def patches
  @patches
end

#platformObject

the platform that a given component will be built for – due to the fact that Ruby is pass-by-reference, it’s usually just a reference to the same Platform object that the overall Project object also contains. This is a definite code smell, and should be slated for refactoring ASAP because it’s going to have weird side-effects if the underlying pass-by-reference assumptions change.



77
78
79
# File 'lib/vanagon/component.rb', line 77

def platform
  @platform
end

#postinstall_actionsObject

postinstall_actions is a two-dimensional Array, describing scripts that should be executed after a given component is installed.



111
112
113
# File 'lib/vanagon/component.rb', line 111

def postinstall_actions
  @postinstall_actions
end

#postremove_actionsObject

preinstall_actions is a two-dimensional Array, describing scripts that should be executed after a given component is uninstalled.



117
118
119
# File 'lib/vanagon/component.rb', line 117

def postremove_actions
  @postremove_actions
end

#preinstall_actionsObject

preinstall_actions is a two-dimensional Array, describing scripts that should be executed before a given component is installed.



99
100
101
# File 'lib/vanagon/component.rb', line 99

def preinstall_actions
  @preinstall_actions
end

#preremove_actionsObject

preremove_actions is a two-dimensional Array, describing scripts that should be executed before a given component is uninstalled.



114
115
116
# File 'lib/vanagon/component.rb', line 114

def preremove_actions
  @preremove_actions
end

#providesObject

provides holds an Array of OpenStructs that describe any capabilities that a given component will provide beyond the its filesystem payload.



93
94
95
# File 'lib/vanagon/component.rb', line 93

def provides
  @provides
end

#replacesObject

replaces holds an Array of OpenStructs that describe a package that a given component will replace on installation.



90
91
92
# File 'lib/vanagon/component.rb', line 90

def replaces
  @replaces
end

#requiresObject

requires holds an Array with a list of all dependencies that a given component needs satisfied before it can be installed.



87
88
89
# File 'lib/vanagon/component.rb', line 87

def requires
  @requires
end

#serviceObject

holds an OpenStruct describing all of the particular details about how any services associated with a given component should be defined.



32
33
34
# File 'lib/vanagon/component.rb', line 32

def service
  @service
end

#settingsObject

holds a OpenStruct, or an Array, or maybe it’s a Hash? It’s often overloaded as a freeform key-value lookup for platforms that require additional configuration beyond the “basic” component attributes. it’s pretty heavily overloaded and should maybe be refactored before Vanagon 1.0.0 is tagged.



66
67
68
# File 'lib/vanagon/component.rb', line 66

def settings
  @settings
end

#sourceObject

Returns the value of attribute source.



22
23
24
# File 'lib/vanagon/component.rb', line 22

def source
  @source
end

#sourcesObject

Returns the value of attribute sources.



23
24
25
# File 'lib/vanagon/component.rb', line 23

def sources
  @sources
end

#urlObject

Returns the value of attribute url.



25
26
27
# File 'lib/vanagon/component.rb', line 25

def url
  @url
end

#versionObject

Returns the value of attribute version.



21
22
23
# File 'lib/vanagon/component.rb', line 21

def version
  @version
end

Class Method Details

.load_component(name, configdir, settings, platform) ⇒ Vanagon::Component

Loads a given component from the configdir

Parameters:

  • name (String)

    the name of the component

  • configdir (String)

    the path to the component config file

  • settings (Hash)

    the settings to be used in the component

  • platform (Vanagon::Platform)

    the platform to build the component for

Returns:

Raises:

  • if the instance_eval on Component fails, the exception is reraised



137
138
139
140
141
142
143
144
145
146
147
# File 'lib/vanagon/component.rb', line 137

def self.load_component(name, configdir, settings, platform)
  compfile = File.join(configdir, "#{name}.rb")
  dsl = Vanagon::Component::DSL.new(name, settings, platform)
  dsl.instance_eval(File.read(compfile), compfile, 1)
  dsl._component
rescue StandardError => e
  VanagonLogger.error "Error loading project '#{name}' using '#{compfile}':"
  VanagonLogger.error e
  VanagonLogger.error e.backtrace.join("\n")
  raise e
end

Instance Method Details

#add_file(file) ⇒ Set?

Adds the given file to the list of files and returns @files.

Parameters:

Returns:

  • (Set, nil)

    Returns @files if file is successfully added to @files or nil if file already exists



191
192
193
# File 'lib/vanagon/component.rb', line 191

def add_file(file)
  @files.add file
end

#add_rpm_ghost_file(file) ⇒ Set?

Adds the given file to the list of %ghost files to be added to an rpm spec’s %files.

Parameters:

Returns:

  • (Set, nil)

    Returns @ghost_files if the file is successfully added to @ghost_files or nil if the file already exists.



201
202
203
# File 'lib/vanagon/component.rb', line 201

def add_rpm_ghost_file(file)
  @ghost_files.add file
end

#configfilesSet

Retrieve all items from @files explicitly marked as configuration files

Returns:

  • (Set)

    all files explicitly marked as configuration files



226
227
228
# File 'lib/vanagon/component.rb', line 226

def configfiles
  @files.select(&:configfile?)
end

#delete_file(file) ⇒ Set?

Deletes the given file from the list of files and returns @files.

Parameters:

  • file (String)

    path of file to delete from a component’s list of files

Returns:

  • (Set, nil)

    Returns @files if file is successfully deleted from @files or nil if file doesn’t exist; this matches strictly on the path of a given file, and ignores other attributes like :mode, :owner, or :group.



212
213
214
# File 'lib/vanagon/component.rb', line 212

def delete_file(file)
  @files.delete_if { |this_file| this_file.path == file }
end

#environment_variablesObject



426
427
428
# File 'lib/vanagon/component.rb', line 426

def environment_variables
  environment.map { |key, value| %(export #{key}="#{value}") }
end

#fetch_mirrors(options) ⇒ Boolean

Retrieve upstream source file from a mirror, by randomly iterating through #mirrors until there’s no more mirrors left. Will #warn if the mirror’s URI cannot be resolved or if the URI cannot be retrieved. Does not suppress any errors from Vanagon::Component::Source.

Returns:

  • (Boolean)

    return True if the source can be retrieved, or False otherwise. This is because because each subclass of Vanagon::Component::Source returns an inconsistent value if #fetch is successful.



257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/vanagon/component.rb', line 257

def fetch_mirrors(options)
  mirrors.to_a.shuffle.each do |mirror|
    begin
      VanagonLogger.info %(Attempting to fetch from mirror URL "#{mirror}")
      @source = Vanagon::Component::Source.source(mirror, **options)
      return true if source.fetch
    rescue SocketError
      # SocketError means that there was no DNS/name resolution
      # for whatever remote protocol the mirror tried to use.
      VanagonLogger.error %(Unable to resolve mirror URL "#{mirror}")
    rescue RuntimeError
      # Source retrieval does not consistently return a meaningful
      # namespaced error message, which means we're brute-force rescuing
      # RuntimeError. Not a good look, and we should fix this.
      VanagonLogger.error %(Unable to retrieve mirror URL "#{mirror}")
    end
  end
  false
end

#fetch_url(options) ⇒ Boolean

Retrieve upstream source file from the canonical URL. Does not suppress any errors from Vanagon::Component::Source.

Returns:

  • (Boolean)

    return True if the source can be retrieved, or False otherwise



282
283
284
285
286
287
288
289
# File 'lib/vanagon/component.rb', line 282

def fetch_url(options)
  VanagonLogger.info %(Attempting to fetch from canonical URL "#{url}")
  @source = Vanagon::Component::Source.source(url, **options)
  # Explicitly coerce the return value of #source.fetch,
  # because each subclass of Vanagon::Component::Source returns
  # an inconsistent value if #fetch is successful.
  !!source.fetch
end

#force_versionObject

Force version determination for components

If the component doesn’t already have a version set (which normally happens for git sources), the source will be fetched into a temporary directory to attempt to figure out the version if the source type supports :version. This directory will be cleaned once the get_sources method returns

Raises:

  • Vanagon::Error raises a vanagon error if we’re unable to determine the version



396
397
398
399
400
401
402
403
404
# File 'lib/vanagon/component.rb', line 396

def force_version
  if @version.nil?
    Dir.mktmpdir do |dir|
      get_source(dir)
    end
  end
  raise Vanagon::Error, "Unable to determine source version for component #{@name}!" if @version.nil?
  @version
end

#get_build_dirObject

Expands the build directory



336
337
338
339
340
341
342
# File 'lib/vanagon/component.rb', line 336

def get_build_dir
  if @build_dir
    File.join(@dirname, @build_dir)
  else
    @dirname
  end
end

#get_dependency_hashObject



344
345
346
# File 'lib/vanagon/component.rb', line 344

def get_dependency_hash
  { name => { 'version' => version, 'url' => url, 'ref' => options[:ref] }.delete_if { |_, v| !v } }
end

#get_environmentString

Deprecated.

Prints the environment in a way suitable for use in a Makefile or shell script. This is deprecated, because all Env. Vars. are moving directly into the Makefile (and out of recipe subshells).

Returns:

  • (String)

    environment suitable for inclusion in a Makefile



412
413
414
415
416
417
418
419
420
421
422
423
424
# File 'lib/vanagon/component.rb', line 412

def get_environment
  VanagonLogger.info <<-WARNING.undent
    #get_environment is deprecated; environment variables have been moved
    into the Makefile, and should not be used within a Makefile's recipe.
    The #get_environment method will be removed by Vanagon 1.0.0.
  WARNING

  if environment.empty?
    ": no environment variables defined"
  else
    environment_variables
  end
end

#get_patches(patch_root) ⇒ Object

Fetches patches if any are provided for the project.

Parameters:

  • patch_root (String)

    working directory to put the patches into



376
377
378
379
380
381
382
383
384
385
386
387
# File 'lib/vanagon/component.rb', line 376

def get_patches(patch_root)
  return if @patches.empty?
  @patches.each do |patch|
    patch_assembly_path = File.join(patch_root, patch.assembly_path)
    if File.exist?(patch_assembly_path)
      raise Vanagon::Error, "Duplicate patch files detected, '#{patch.origin_path}' would have overwritten '#{patch.assembly_path}'. Ensure all patch file names within a component are unique."
    end
    patch_target_directory = File.dirname(patch_assembly_path)
    FileUtils.mkdir_p(patch_target_directory)
    FileUtils.cp(patch.origin_path, patch_assembly_path)
  end
end

#get_source(workdir) ⇒ Object

Fetches the primary source for the component. As a side effect, also sets @extract_with, @dirname and @version for the component for use in the makefile template

Parameters:

  • workdir (String)

    working directory to put the source into



306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
# File 'lib/vanagon/component.rb', line 306

def get_source(workdir) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
  opts = options.merge({ workdir: workdir, dirname: dirname })
  if url || !mirrors.empty?
    if ENV['VANAGON_USE_MIRRORS'] == 'n' or ENV['VANAGON_USE_MIRRORS'] == 'false'
      fetch_url(opts)
    else
      fetch_mirrors(opts) || fetch_url(opts)
    end
    source.verify
    extract_with << source.extract(platform.tar) if source.respond_to? :extract

    @cleanup_source = source.cleanup if source.respond_to?(:cleanup)
    @dirname ||= source.dirname

    # Git based sources probably won't set the version, so we load it if it hasn't been already set
    if source.respond_to?(:version)
      @version ||= source.version
    end
  else
    VanagonLogger.info "No source given for component '#{@name}'"

    # If there is no source, we don't want to try to change directories, so we just change to the current directory.
    @dirname = './'

    # If there is no source, there is nothing to do to extract
    extract_with << ': no source, so nothing to extract'
  end
end

#get_sources(workdir) ⇒ Object

Fetches secondary sources for the component. These are just dumped into the workdir currently.

Parameters:

  • workdir (String)

    working directory to put the source into



351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
# File 'lib/vanagon/component.rb', line 351

def get_sources(workdir) # rubocop:disable Metrics/AbcSize
  sources.each do |source|
    src = Vanagon::Component::Source.source(
      source.url, workdir: workdir, ref: source.ref, sum: source.sum
    )
    src.fetch
    src.verify
    if source.erb
      erb_file(src.file, File.join(File.dirname(src.file), File.basename(src.file, ".erb")), true)
    end
    # set src.file to only be populated with the basename instead of entire file path
    src.file = File.basename(src.url)
    extract_with << src.extract(platform.tar) if src.respond_to? :extract
  end
end

#rpm_ghost_filesArray

Retrieve all the files intended as %ghost entries for an rpm spec %files section.

Returns:

  • (Array)

    of all the rpm %ghost files.



234
235
236
# File 'lib/vanagon/component.rb', line 234

def rpm_ghost_files
  @ghost_files.to_a
end

#rules(project, platform) ⇒ Object



430
431
432
# File 'lib/vanagon/component.rb', line 430

def rules(project, platform)
  Vanagon::Component::Rules.new(self, project, platform)
end