1# frozen_string_literal: true
2
3module Ci
4  ##
5  # This module implements methods that need to read and write
6  # metadata for CI/CD entities.
7  #
8  module Metadatable
9    extend ActiveSupport::Concern
10
11    included do
12      has_one :metadata, class_name: 'Ci::BuildMetadata',
13                         foreign_key: :build_id,
14                         inverse_of: :build,
15                         autosave: true
16
17      accepts_nested_attributes_for :metadata
18
19      delegate :timeout, to: :metadata, prefix: true, allow_nil: true
20      delegate :interruptible, to: :metadata, prefix: false, allow_nil: true
21      delegate :has_exposed_artifacts?, to: :metadata, prefix: false, allow_nil: true
22      delegate :environment_auto_stop_in, to: :metadata, prefix: false, allow_nil: true
23      delegate :set_cancel_gracefully, to: :metadata, prefix: false, allow_nil: false
24      delegate :cancel_gracefully?, to: :metadata, prefix: false, allow_nil: false
25      before_create :ensure_metadata
26    end
27
28    def ensure_metadata
29      metadata || build_metadata(project: project)
30    end
31
32    def degenerated?
33      self.options.blank?
34    end
35
36    def degenerate!
37      self.class.transaction do
38        self.update!(options: nil, yaml_variables: nil)
39        self.needs.all.delete_all
40        self.metadata&.destroy
41      end
42    end
43
44    def options
45      read_metadata_attribute(:options, :config_options, {})
46    end
47
48    def yaml_variables
49      read_metadata_attribute(:yaml_variables, :config_variables, [])
50    end
51
52    def options=(value)
53      write_metadata_attribute(:options, :config_options, value)
54
55      ensure_metadata.tap do |metadata|
56        # Store presence of exposed artifacts in build metadata to make it easier to query
57        metadata.has_exposed_artifacts = value&.dig(:artifacts, :expose_as).present?
58        metadata.environment_auto_stop_in = value&.dig(:environment, :auto_stop_in)
59      end
60    end
61
62    def yaml_variables=(value)
63      write_metadata_attribute(:yaml_variables, :config_variables, value)
64    end
65
66    def interruptible
67      metadata&.interruptible
68    end
69
70    def interruptible=(value)
71      ensure_metadata.interruptible = value
72    end
73
74    private
75
76    def read_metadata_attribute(legacy_key, metadata_key, default_value = nil)
77      read_attribute(legacy_key) || metadata&.read_attribute(metadata_key) || default_value
78    end
79
80    def write_metadata_attribute(legacy_key, metadata_key, value)
81      ensure_metadata.write_attribute(metadata_key, value)
82      write_attribute(legacy_key, nil)
83    end
84  end
85end
86
87Ci::Metadatable.prepend_mod_with('Ci::Metadatable')
88