class Blufin::Projects
Constants
- ALIAS
- API
- BUILD
- BUILD_SCRIPTS
- CG_API_SIMPLE
- CG_QUASAR
- CG_QUASAR_PAGES
- CG_QUASAR_PAGES_IGNORE
- CG_QUASAR_ROOT
- CG_QUASAR_ROUTER_VIEW_LAYOUT
- CG_QUASAR_ROUTES_FILE
- CODEGEN
- COMMANDS
- DEPLOYMENT
- DEPLOYMENT_BUCKET
- DEPLOYMENT_FILES
- DOMAIN
- DOWNSTREAM
- FILE
- ID
- LAMBDA
- LOCAL
- PORT
- PORTS
- PROJECT
- PROJECT_ID
- PROJECT_NAME
- PROJECT_NAME_PASCAL_CASE
- PROJECT_ROOT
- REMOTE
- REPOSITORY
- RUN
- RUN_SCRIPTS
- SCHEDULER
- SCHEMA_FILE
- SCHEMA_FILE_CODEGEN
- SCRIPT_BUILD
- SCRIPT_DEPLOY
- SCRIPT_RUN
- SCRIPT_TEST
- STAGES
- TEST
- TEST_SCRIPTS
- TITLE
- TRANSIENT_DATA
- TYPE
- TYPE_ALEXA
- TYPE_API
- TYPE_API_SIMPLE
- TYPE_LAMBDA
- TYPE_MOBILE
- TYPE_MVN_LIB
- TYPE_NPM_LIB
- TYPE_QUASAR
- TYPE_UI
- UI
- UPSTREAM
- VALID_TYPES
- WORKER
Public Class Methods
Gets API(s). @return Hash
# File lib/core/projects.rb, line 154 def self.get_apis @@apis end
Gets a hash of dependant projects (with the Project IDs as keys). processed_projects Array prevents cyclic-dependency stack overflow. @return Hash
# File lib/core/projects.rb, line 234 def self.get_dependant_projects(project_id, streams: [UPSTREAM, DOWNSTREAM], processed_projects: []) valid_streams = [UPSTREAM, DOWNSTREAM] raise RuntimeError, "Expected Array, instead got: #{streams.class}" unless streams.is_a?(Array) streams.each { |s| raise RuntimeError, "Invalid stream: #{s}" unless valid_streams.include?(s) } key = "#{project_id}-#{streams.join('-')}" return @@dependant_projects_cache[key] if @@dependant_projects_cache.has_key?(key) dependant_projects = {} project = get_project_by_id(project_id, true) streams.each do |stream| if project.has_key?(stream) project[stream].each do |dependant_project_id| unless processed_projects.include?(dependant_project_id) processed_projects << dependant_project_id dependant_projects[dependant_project_id] = get_project_by_id(dependant_project_id, true) unless dependant_projects.has_key?(dependant_project_id) dependant_projects_inner = Blufin::Projects::get_dependant_projects(dependant_project_id, streams: streams, processed_projects: processed_projects) # Add nested dependant projects (if any). dependant_projects_inner.each { |k, v| dependant_projects[k] = v unless dependant_projects.has_key?(k) } if dependant_projects_inner.any? end end end end # Don't include project itself in dependant projects. dependant_projects.delete(project_id) if dependant_projects.has_key?(project_id) @@dependant_projects_cache[key] = dependant_projects @@dependant_projects_cache[key] end
Same as above, but only gets the repositories. Sometimes multiple dependant_project IDs may have the same repository. @return Hash
# File lib/core/projects.rb, line 264 def self.get_dependant_repos(project_id, streams: [UPSTREAM, DOWNSTREAM]) key = "#{project_id}-#{streams.join('-')}" return @@dependant_repos_cache[key] if @@dependant_repos_cache.has_key?(key) dependant_repos = {} get_dependant_projects(project_id, streams: streams).each do |k, v| next if k == project_id repo = v[REPOSITORY][REMOTE] dependant_repos[repo] = { :projects => [] } unless dependant_repos.include?(repo) dependant_repos[repo][:path] = v[REPOSITORY][LOCAL] if v[REPOSITORY].has_key?(LOCAL) dependant_repos[repo][:projects] << k unless dependant_repos[repo][:projects].include?(k) end @@dependant_repos_cache[key] = dependant_repos @@dependant_repos_cache[key] end
Gets Lambda(s). @return Hash
# File lib/core/projects.rb, line 160 def self.get_lambdas @@lambdas end
Attempts to get a project's data by ID
, displays error if not exists. @return string
# File lib/core/projects.rb, line 166 def self.get_project_by_id(project_id, runtime_error = false) raise RuntimeError, "Expected String, instead got: #{project_id.class}" unless project_id.is_a?(String) return @@projects_cache[project_id] if @@projects_cache.has_key?(project_id) project_data = nil if @@projects_arr.is_a?(Array) @@projects_arr.each do |project| raise RuntimeError, 'Missing Project ID.' unless project.has_key?(PROJECT_ID) if project[PROJECT_ID].strip.downcase == project_id.strip.downcase project_data = project break end end end raise RuntimeError, "Unrecognized Project ID: #{project_id}" if project_data.nil? && runtime_error Blufin::Terminal::error("Unrecognized Project ID: #{Blufin::Terminal::format_invalid(project_id)} . Available Projects IDs are:", get_project_ids) if project_data.nil? && !runtime_error @@projects_cache[project_id] = project_data @@projects_cache[project_id] end
Gets Project ID(s). @return Array
# File lib/core/projects.rb, line 142 def self.get_project_ids @@project_ids end
Gets Project Name(s). @return Array
# File lib/core/projects.rb, line 136 def self.get_project_names @@project_names end
Gets the path to a project. By default, gets the root (IE: doesn't take into account 'Repository.ProjectRoot' key). If you want the full path to the inner project, to_inner_project must be set to TRUE. If local, simply returns path in projects.yml. If not local (IE: on EC2), returns a standardized /tmp path. @return string
# File lib/core/projects.rb, line 209 def self.get_project_path(project_id, to_inner_project = false, is_ec2: false, project: nil) key = "#{project_id}|#{to_inner_project}|#{is_ec2}" return @@project_path_cache[key] if @@project_path_cache.has_key?(key) project = project.nil? ? get_project_by_id(project_id, true) : project repo_data = project[REPOSITORY] inner_path = repo_data.has_key?(PROJECT_ROOT) ? Blufin::Strings::remove_surrounding_slashes(repo_data[PROJECT_ROOT]) : '' project_path = nil if repo_data.has_key?(LOCAL) && !is_ec2 root_path = Blufin::Strings::remove_surrounding_slashes(File.expand_path(repo_data[LOCAL])) project_path = "/#{root_path}" unless to_inner_project project_path = "/#{root_path}#{inner_path.length > 0 ? '/' : ''}#{inner_path}" if to_inner_project else rs = repo_data[REMOTE].split('/') tmp_path = "/tmp/repo-#{rs[rs.length - 1].gsub(/\.git$/i, '')}" project_path = tmp_path unless to_inner_project project_path = "#{tmp_path}#{inner_path.length > 0 ? '/' : ''}#{inner_path}" if to_inner_project end raise RuntimeError, "Project Path should never be nil or an empty string: #{key}" if project_path.nil? || project_path.strip == '' @@project_path_cache[key] = project_path @@project_path_cache[key] end
Gets repo-name from project_id. @return string
# File lib/core/projects.rb, line 283 def self.get_project_repo_name(project_id) project = get_project_by_id(project_id, true) Blufin::Git::extract_repo_name(project[REPOSITORY][REMOTE]) end
Gets Project(s) – as a nested hash with -> [PROJECT] as the keys. @return Hash
# File lib/core/projects.rb, line 114 def self.get_projects @@projects end
Gets Project(s) – but in a single array. @return Array
# File lib/core/projects.rb, line 120 def self.get_projects_as_array(group: nil, types: nil) projects_arr = [] unless types.nil? types = [types] unless types.is_a?(Array) types.each { |type| raise RuntimeError, "Invalid type: #{type}" unless VALID_TYPES.include?(type) } end @@projects_arr.each do |project| next if !group.nil? && group != project[PROJECT] next if !types.nil? && !types.include?(project[TYPE]) projects_arr << project end projects_arr end
Gets an array of project(s) from current path. @return Array
# File lib/core/projects.rb, line 187 def self.get_projects_by_path projects = {} current_path = Blufin::Strings::strip_newline(`pwd`) get_projects_as_array.each do |project| if project.has_key?(REPOSITORY) if project[REPOSITORY].has_key?(LOCAL) project_path = File.expand_path(project[REPOSITORY][LOCAL]) if current_path =~ /^#{project_path}/ raise RuntimeError, 'Missing Project ID.' unless project.has_key?(PROJECT_ID) projects[project[PROJECT_ID]] = project end end end end projects end
Gets Script(s). @return Hash
# File lib/core/projects.rb, line 148 def self.get_scripts @@scripts end
Takes a Hash that needs to have a 'Projects' key. This can come from both .awx.yml or .blufin.yml (root). @return void
# File lib/core/projects.rb, line 92 def initialize(projects, profile = nil) raise RuntimeError, 'Cannot run Blufin::Projects.new() more than once.' if !@@projects.nil? || !@@scripts.nil? raise RuntimeError, "Need either a Local or S3Bucket key, found neither: #{projects.keys}" unless projects.has_key?(LOCAL) || projects.has_key?('S3Bucket') @@projects = {} @@scripts = {} if projects.has_key?(LOCAL) source_file = File.expand_path(projects[LOCAL][FILE]) # Throw error if source file doesn't exist. Blufin::Terminal::error("Cannot find source file: #{Blufin::Terminal::format_directory(source_file)}") unless Blufin::Files::file_exists(source_file) # Validate the source file against the expected schema. process_source_file(source_file) elsif projects.has_key?('S3Bucket') # Throw an error if we don't have an AWS Profile. Blufin::Terminal::error("Your configuration is getting #{Blufin::Terminal::format_highlight('projects.yml')} from S3, but you have no AWS Profile set.") if profile.nil? s3 = projects['S3Bucket'] tmp_path = Blufin::AWS::download_s3_data(s3['Name'], s3['Path'], file: s3['File'], profile: profile, region: s3['Region'], use_cache: true) process_source_file(tmp_path) end end
Shows a prompt and returns project Hash once selected. If only one project exists, prompt not displayed. @return Hash
# File lib/core/projects.rb, line 291 def self.show_project_prompt(array_of_projects) raise RuntimeError, "Expected Array, instead got: #{array_of_projects.class}" unless array_of_projects.is_a?(Array) Blufin::Terminal::error('No projects found.') unless array_of_projects.any? return array_of_projects[0] if array_of_projects.length == 1 projects = [] array_of_projects.each { |project| projects << {:text => project[PROJECT_ID], :value => project} } Blufin::Terminal::prompt_select('Select project:', projects) end
Private Instance Methods
Standardized way of displaying YML Parser (Kwalify) errors. Script will terminate in this method. @return void
# File lib/core/projects.rb, line 536 def display_parse_errors_if_any(errors, source_file) if errors && !errors.empty? errors_output = [] errors.each { |e| errors_output << "[#{e.path}] #{e.message}" } Blufin::Terminal::error("Your configuration file is invalid. Please fix: #{Blufin::Terminal::format_directory(source_file)}", errors) end end
Parses a YML file, bombs-out if something fails. @return Hash
# File lib/core/projects.rb, line 548 def parse_yml(source_file) begin file_parsed = YAML.load_file(source_file) rescue => e Blufin::Terminal::error("Failed to parse config file: #{Blufin::Terminal::format_directory(source_file)}", e.message) end file_parsed end
Validate
the Project YML. @return void
# File lib/core/projects.rb, line 304 def process_source_file(source_file) # Skip empty file. return if Blufin::Files::is_empty(source_file) # Otherwise, validate file. document, errors = Blufin::Config::validate_file(source_file, SCHEMA_FILE) display_parse_errors_if_any(errors, source_file) projects_yml = parse_yml(source_file) # Buffer script(s). [RUN_SCRIPTS, TEST_SCRIPTS, BUILD_SCRIPTS].each do |script_type| if projects_yml.has_key?(script_type) if projects_yml[script_type].is_a?(Array) script_key = script_key_mapper(script_type) @@scripts[script_key] = {} unless @@scripts.has_key?(script_key) projects_yml[script_type].each do |script| script_id = script[ID] # Throw error if duplicate script is found for type. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 Duplicate script found: #{Blufin::Terminal::format_invalid(script_id)}") if @@scripts[script_key].has_key?(script_id) @@scripts[script_key][script_id] = script # Check that if script is AWS, it doesn't have --region or --profile flag. if script.has_key?(COMMANDS) script[COMMANDS].each do |command| if command.strip =~ /^aws/i Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 AWS scripts cannot have: #{Blufin::Terminal::format_invalid('--region')} flag.", command) if command =~ /\s--region\s/ Blufin::Terminal::error("#{Blufin::Terminal::format_highlight("#{script_type} - #{script_id}")} \xe2\x80\x94 AWS scripts cannot have: #{Blufin::Terminal::format_invalid('--profile')} flag.", command) if command =~ /\s--profile\s/ end end end end end end end # TODO - This file is very broken: 06/01/20 -- Need to remove all the API SIMPLE + QUASAR STUFF! # Buffer/validate project(s) stuff. projects_yml['Projects'].each do |project| project_id = project[PROJECT_ID] project_type = project[TYPE] if project_type == TYPE_MVN_LIB || TYPE_NPM_LIB Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Duplicate Library Project.", "A library with this ID (#{Blufin::Terminal::format_invalid(project_id)}) has already been registered.") if @@libs.keys.include?(project_id) @@libs[project_id] = project elsif project_type == TYPE_API || project_type == TYPE_API_SIMPLE [ALIAS, PROJECT_NAME, PROJECT_NAME_PASCAL_CASE, TITLE].each do |x| @@api_data[x] = [] unless @@api_data.has_key?(x) && @@api_data[x].is_a?(Array) property_value = project[API][x].strip.downcase Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Duplicate #{x} API Property.", "An API Property with this value (#{Blufin::Terminal::format_invalid(property_value)}) has already been registered.") if @@api_data[x].include?(property_value) @@api_data[x] << property_value end end end used_ports = {} # Run through once quickly to populate critical objects (required for validation). projects_yml['Projects'].each do |project| @@project_names << project[PROJECT] @@project_ids << project[PROJECT_ID] end # Loop (and validate) projects. projects_yml['Projects'].each do |project| # Validate keys are in specific order. expected = { PROJECT_ID => true, PROJECT => true, TYPE => true, REPOSITORY => true, UPSTREAM => false, DOWNSTREAM => false, RUN => false, TEST => false, BUILD => false, API => false, LAMBDA => false, UI => false, DEPLOYMENT => false, } Blufin::Validate::assert_valid_keys(expected, project.keys, source_file) project_id = project[PROJECT_ID] project_name = project[PROJECT] project_type = project[TYPE] validate_type(project_type, project_id) @codegen_file = "#{Blufin::Projects::get_project_path(project_id, project: project)}/.#{CODEGEN}/#{CODEGEN}.yml" if [TYPE_API_SIMPLE, TYPE_QUASAR].include?(project_type) # TODO - REMOVE THIS? # # Validate (and parse) .codegen/codegen.yml. # Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Missing file: #{Blufin::Terminal::format_directory(@codegen_file)}", 'This file needs to exist.', true) unless Blufin::Files::file_exists(@codegen_file) # # Parse .codegen/codegen.yml # document, errors = Blufin::Config::validate_file(@codegen_file, SCHEMA_FILE_CODEGEN) # display_parse_errors_if_any(errors, @codegen_file) # @codegen_yml = parse_yml(@codegen_file) end # Validate Script(s). [RUN, TEST, BUILD].each do |script_type| if project.has_key?(script_type) # Validate the LAMBDA functions don't need build scripts. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Project type: #{Blufin::Terminal::format_highlight(TYPE_LAMBDA)} does not require #{Blufin::Terminal::format_invalid(script_type)} script(s).", 'This type of project does not support this.', true) if [BUILD].include?(script_type) && project_type == TYPE_LAMBDA if project[script_type].is_a?(Hash) script_key = script_key_mapper(script_type) valid_scripts = [] valid_scripts = @@scripts[script_key].keys if @@scripts.is_a?(Hash) && @@scripts.has_key?(script_key) script = project[script_type] script_name = script['Script'] unless valid_scripts.include?(script_name) error = valid_scripts.any? ? 'Valid values are:' : "There currently are no #{script_key} script(s) defined." Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 #{Blufin::Terminal::format_highlight(script_type)} \xe2\x80\x94 Invalid script reference: #{Blufin::Terminal::format_invalid(script_name)}. #{error}", valid_scripts) end end end end # Validate Repository property. if project.has_key?(REPOSITORY) if project[REPOSITORY].has_key?(LOCAL) repo_path = project[REPOSITORY][LOCAL] Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Repository path not found: #{Blufin::Terminal::format_invalid(repo_path)}") unless Blufin::Files::path_exists(repo_path) end end # Validate Deployment property. if project.has_key?(DEPLOYMENT) expected = { DEPLOYMENT_BUCKET => true, DEPLOYMENT_FILES => true } Blufin::Validate::assert_valid_keys(expected, project[DEPLOYMENT].keys, source_file) end # Validate API property. if project_type == TYPE_API || project_type == TYPE_API_SIMPLE # Make sure we have the API property. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Missing property: #{Blufin::Terminal::format_highlight(API)}", "This property is required for project(s) with type: #{API}", true) unless project.has_key?(API) # Validate keys are in specific order. expected = { TITLE => true, ALIAS => true, DOMAIN => true, PROJECT_NAME => true, PROJECT_NAME_PASCAL_CASE => true, PORTS => true } Blufin::Validate::assert_valid_keys(expected, project[API].keys, source_file) expected_ports = { API => true, SCHEDULER => true, WORKER => true } Blufin::Validate::assert_valid_keys(expected_ports, project[API][PORTS].keys, source_file) validate_ports([project[API][PORTS][API], project[API][PORTS][SCHEDULER], project[API][PORTS][WORKER]], project_id, used_ports) # Add ports to used_ports. used_ports[project[API][PORTS][API]] = project_id used_ports[project[API][PORTS][SCHEDULER]] = project_id used_ports[project[API][PORTS][WORKER]] = project_id @@apis = {} if @@apis.nil? @@apis[project[PROJECT_ID]] = project else # Make sure we DON'T have the API key. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Property not supported: #{Blufin::Terminal::format_invalid(API)}", "This property is only allowed for project(s) with type: #{API}", true) if project.has_key?(API) end # TODO - REMOVE? # Validate ApiSimple property. # if project_type == TYPE_API_SIMPLE # Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Missing property #{Blufin::Terminal::format_highlight(CG_API_SIMPLE)} in: #{Blufin::Terminal::format_directory(@codegen_file)}", "This property is required for project(s) with type: #{TYPE_API_SIMPLE}", true) unless @codegen_yml.has_key?(CG_API_SIMPLE) # project[TRANSIENT_DATA] = @codegen_yml[CG_API_SIMPLE] # end # Validate Lambda property. if project_type == TYPE_LAMBDA # Make sure we have the Lambda property. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Missing property: #{Blufin::Terminal::format_highlight(LAMBDA)}", "This property is required for project(s) with type: #{TYPE_LAMBDA}", true) unless project.has_key?(LAMBDA) # Validate keys are in specific order. expected = { TITLE => true, ALIAS => true, PORT => true, STAGES => true } Blufin::Validate::assert_valid_keys(expected, project[LAMBDA].keys, source_file) validate_ports([project[LAMBDA][PORT]], project_id, used_ports) # Add ports to used_ports. used_ports[project[LAMBDA][PORT]] = project_id @@lambdas = {} if @@lambdas.nil? Blufin::Terminal::error("Duplicate Lambda project: #{Blufin::Terminal::format_invalid(project[PROJECT_ID])}") if @@lambdas.has_key?(project[PROJECT_ID]) @@lambdas[project[PROJECT_ID]] = project else # Make sure we DON'T have the Lambda key. Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Property not supported: #{Blufin::Terminal::format_invalid(LAMBDA)}", "This property is only allowed for project(s) with type: #{TYPE_LAMBDA}", true) if project.has_key?(LAMBDA) end # TODO - REMOVE? # Validate Quasar property. # if project_type == TYPE_QUASAR # Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Missing property #{Blufin::Terminal::format_highlight(CG_QUASAR)} in: #{Blufin::Terminal::format_directory(@codegen_file)}", "This property is required for project(s) with type: #{TYPE_QUASAR}", true) unless @codegen_yml.has_key?(CG_QUASAR) # project[TRANSIENT_DATA] = @codegen_yml[CG_QUASAR] # end # Validate upstream/downstream Libs(s). [UPSTREAM, DOWNSTREAM].each do |stream| if project.has_key?(stream) project[stream].each do |library| case stream when UPSTREAM Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Unrecognized #{Blufin::Terminal::format_action(UPSTREAM)} library: #{Blufin::Terminal::format_invalid(library)}") unless @@libs.keys.include?(library) when DOWNSTREAM Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Unrecognized #{Blufin::Terminal::format_action(DOWNSTREAM)} library: #{Blufin::Terminal::format_invalid(library)}") unless @@project_ids.include?(library) else raise RuntimeError, "Unrecognized stream: #{stream}" end end end end @@projects[project_name] = {} unless @@projects.has_key?(project_name) Blufin::Terminal::error("Duplicate project ID: #{Blufin::Terminal::format_invalid(project_id)}") if @@projects[project_name].has_key?(project_id) @@projects[project_name][project_id] = project @@projects_arr << project end @@project_names.uniq! @@project_names.sort! end
Maps root-level property to enum. @return string
# File lib/core/projects.rb, line 566 def script_key_mapper(script_type) if [RUN_SCRIPTS, RUN].include?(script_type) return SCRIPT_RUN elsif [TEST_SCRIPTS, TEST].include?(script_type) return TEST_SCRIPTS elsif [BUILD_SCRIPTS, BUILD].include?(script_type) return BUILD_SCRIPTS else raise RuntimeError, "Unhandled script type: #{script_type}" end end
Validate
the ports. Make sure none of the ports conflict with other projects. @return void
# File lib/core/projects.rb, line 580 def validate_ports(ports, project_id, used_ports) ports.each do |port| if used_ports.has_key?(port) Blufin::Terminal::error("#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 Duplicate port detected: #{Blufin::Terminal::format_invalid(port)} ", ["The conflicting project is: #{Blufin::Terminal::format_highlight(used_ports[port])}"]) end end end
Validate
project type. @return void
# File lib/core/projects.rb, line 559 def validate_type(project_type, project_id = nil) project_id = project_id.nil? ? nil : "#{Blufin::Terminal::format_highlight(project_id)} \xe2\x80\x94 " Blufin::Terminal::error("#{project_id}Invalid Project Type: #{Blufin::Terminal::format_invalid(project_type)}. Valid types are:", VALID_TYPES, true) unless VALID_TYPES.include?(project_type) end