power_stencil 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/.gitlab-ci.yml +2 -0
  3. data/README.md +3 -7
  4. data/doc/builds.md +10 -1
  5. data/doc/entities.md +59 -50
  6. data/doc/images/power-stencil-entity-creation.svg +247 -114
  7. data/doc/plugins.md +1 -0
  8. data/doc/templates.md +23 -18
  9. data/etc/base_commands_definition.yml +14 -0
  10. data/etc/power_stencil.yaml +7 -1
  11. data/etc/templates/project/.zzzgitignore.erb +8 -3
  12. data/lib/power_stencil.rb +1 -0
  13. data/lib/power_stencil/command_processors/build.rb +16 -3
  14. data/lib/power_stencil/command_processors/check.rb +12 -0
  15. data/lib/power_stencil/command_processors/create.rb +6 -5
  16. data/lib/power_stencil/command_processors/delete.rb +23 -15
  17. data/lib/power_stencil/command_processors/edit.rb +4 -2
  18. data/lib/power_stencil/command_processors/init.rb +2 -2
  19. data/lib/power_stencil/command_processors/plugin.rb +21 -6
  20. data/lib/power_stencil/command_processors/root.rb +1 -1
  21. data/lib/power_stencil/command_processors/shell.rb +10 -3
  22. data/lib/power_stencil/dsl/entities.rb +0 -16
  23. data/lib/power_stencil/engine/build_handling.rb +1 -2
  24. data/lib/power_stencil/engine/directory_processor.rb +6 -1
  25. data/lib/power_stencil/engine/entities_definitions.rb +16 -7
  26. data/lib/power_stencil/engine/entities_handling.rb +1 -1
  27. data/lib/power_stencil/engine/project_engine.rb +6 -10
  28. data/lib/power_stencil/initializer.rb +4 -7
  29. data/lib/power_stencil/plugins/base.rb +3 -1
  30. data/lib/power_stencil/plugins/capabilities.rb +9 -4
  31. data/lib/power_stencil/plugins/entity_definitions.rb +15 -0
  32. data/lib/power_stencil/plugins/require.rb +1 -0
  33. data/lib/power_stencil/plugins/templates.rb +3 -3
  34. data/lib/power_stencil/project/base.rb +24 -9
  35. data/lib/power_stencil/project/create.rb +23 -2
  36. data/lib/power_stencil/project/git.rb +75 -0
  37. data/lib/power_stencil/project/info.rb +14 -1
  38. data/lib/power_stencil/project/paths.rb +22 -6
  39. data/lib/power_stencil/project/plugins.rb +7 -8
  40. data/lib/power_stencil/project/templates.rb +15 -22
  41. data/lib/power_stencil/system_entity_definitions/all.rb +2 -1
  42. data/lib/power_stencil/system_entity_definitions/buildable.rb +1 -1
  43. data/lib/power_stencil/system_entity_definitions/entity_override.rb +6 -0
  44. data/lib/power_stencil/system_entity_definitions/entity_project_common.rb +13 -5
  45. data/lib/power_stencil/system_entity_definitions/{has_associated_files.rb → entity_templates.rb} +2 -2
  46. data/lib/power_stencil/system_entity_definitions/project_config.rb +1 -1
  47. data/lib/power_stencil/system_entity_definitions/project_entity.rb +7 -0
  48. data/lib/power_stencil/system_entity_definitions/simple_exec.rb +3 -3
  49. data/lib/power_stencil/system_entity_definitions/source_provider.rb +15 -0
  50. data/lib/power_stencil/version.rb +1 -1
  51. data/power_stencil.gemspec +2 -1
  52. metadata +22 -6
@@ -31,22 +31,6 @@ module PowerStencil
31
31
  PowerStencil.project.engine.entity type, name, @universe
32
32
  end
33
33
 
34
- # Need to implement a `replace` method which would rename files before...
35
- # def edit_entity(type, name)
36
- # tmp = Object.new
37
- # tmp.extend PowerStencil::Utils::FileEdit
38
- # org = entity(type, name)
39
- # unless org.nil?
40
- # mod = tmp.securely_edit_entity entity(type, name)
41
- # raise PowerStencil::Error "You cannot "
42
- #
43
- #
44
- # universe = org.universe
45
- # universe.delete org
46
- # universe.add mod
47
- # end
48
- # end
49
-
50
34
  def entities(criterion: nil, value: nil, &filter_block)
51
35
  PowerStencil.project.engine.entities @universe, criterion: criterion, value: value, &filter_block
52
36
  end
@@ -39,8 +39,7 @@ module PowerStencil
39
39
  build_entity entity_to_build, build_entity_target_path
40
40
 
41
41
  entity_build_report << 'Ok'
42
- rescue => e
43
- logger.error "Failed building '#{entity_to_build.as_path}' because '#{e.message}' !"
42
+ rescue SyntaxError, StandardError => e
44
43
  entity_build_report << e.message
45
44
  if fail_on_error
46
45
  raise e
@@ -97,7 +97,12 @@ module PowerStencil
97
97
  raise PowerStencil::Error, "File '#{source}' is supposed to be rendered using '#{engine_name}', but seems not to be supported !"
98
98
  end
99
99
  context = running_context universe, main_entry_point: main_entry_point
100
- return send templating_engine_method_name, source, context
100
+ begin
101
+ return send templating_engine_method_name, source, context
102
+ rescue StandardError, SyntaxError => e
103
+ logger.puts_and_logs "Error rendering #{engine_name} template '#{source}'", logs_as: :error, check_verbose: false
104
+ raise e
105
+ end
101
106
  end
102
107
  end
103
108
  logger.warn "File '#{source}' was supposed to be processed but there is no template engine defined ! Applying simple copy."
@@ -7,7 +7,7 @@ module PowerStencil
7
7
 
8
8
  include PowerStencil::Utils::SecureRequire
9
9
 
10
- def require_definition_files(files_or_dirs)
10
+ def require_definition_files(files_or_dirs, source)
11
11
  required_files = []
12
12
  files_or_dirs.each do |file_or_dir|
13
13
  if File.directory? file_or_dir and File.readable? file_or_dir
@@ -21,21 +21,30 @@ module PowerStencil
21
21
  next
22
22
  end
23
23
  # This is a ruby library or there is something wrong
24
- securely_require file_or_dir
25
- # unless securely_require file_or_dir
26
- # logger.warn "While trying to load definition files, found that '#{file_or_dir}' has a problem. Ignored..."
27
- # end
24
+ securely_require_with_entity_class_detection file_or_dir, source
25
+
28
26
  end
29
- required_files.sort!.each {|file| securely_require file, fail_on_error: true}
27
+ required_files.sort!.each { |file| securely_require_with_entity_class_detection file, source }
30
28
  required_files
31
29
  end
32
30
 
33
31
  private
34
32
 
35
33
  def load_system_entities_definition
36
- require_definition_files [SYSTEM_ENTITY_DEFINITION_ENTRY_POINT]
34
+ require_definition_files [SYSTEM_ENTITY_DEFINITION_ENTRY_POINT], PowerStencil
37
35
  end
38
36
 
37
+ def securely_require_with_entity_class_detection(entity_definition_file_path, source)
38
+ before = PowerStencil::Engine::EntitiesHandling.all_types
39
+ securely_require entity_definition_file_path, fail_on_error: true
40
+ after = PowerStencil::Engine::EntitiesHandling.all_types
41
+ after.reject { |k, _| before.keys.include? k }
42
+ .each do |_, plugin_entity_class|
43
+ plugin_entity_class.instance_eval do
44
+ @entity_type_source_provider = source
45
+ end
46
+ end
47
+ end
39
48
  end
40
49
 
41
50
  end
@@ -59,7 +59,7 @@ module PowerStencil
59
59
  raise PowerStencil::Error, "Unknown entity type: '#{type}'"
60
60
  end
61
61
  fields[:name] = fields[:name].to_s
62
- res = @available_entities_hash[type.to_sym].new fields: fields, universe: work_universe
62
+ res = @available_entities_hash[type.to_sym].new fields: fields, universe: work_universe, user: user
63
63
  logger.debug "Created new '#{type}': \n#{fields.to_yaml}"
64
64
  work_universe.add res
65
65
  if work_universe == PowerStencil.project.engine.root_universe
@@ -36,7 +36,7 @@ module PowerStencil
36
36
  if Dir.exist? dir and File.readable? dir
37
37
  logger.info 'Loading project specific entity definitions.'
38
38
  $LOAD_PATH << dir
39
- require_definition_files [dir]
39
+ require_definition_files [dir], project
40
40
  end
41
41
  end
42
42
 
@@ -62,24 +62,20 @@ module PowerStencil
62
62
  end
63
63
 
64
64
  def load_project_entities
65
- logger.debug 'Loading project entities - NOT IMPLEMENTED'
66
65
  root_universe.import project.project_entities_path, stop_on_error: false do |new_entity|
67
- process_entity new_entity
66
+ logger.debug "Loaded entity: '#{new_entity.as_path}'"
68
67
  end
69
68
  end
70
69
 
71
70
  def load_user_entities
72
- logger.debug 'Loading user entities - NOT IMPLEMENTED'
73
71
  root_universe.import project.user_entities_path, stop_on_error: false do |new_entity|
74
- process_entity new_entity
72
+ logger.debug "Loaded unversioned entity: '#{new_entity.as_path}'"
73
+ new_entity.instance_eval do
74
+ @is_versioned_entity = false
75
+ end
75
76
  end
76
77
  end
77
78
 
78
- def process_entity(entity)
79
- logger.debug "New entity: '#{entity.name}' (#{entity.type})"
80
- end
81
-
82
-
83
79
  end
84
80
 
85
81
  end
@@ -8,17 +8,15 @@ module PowerStencil
8
8
 
9
9
  include Climatic::Proxy
10
10
 
11
+ def name
12
+ 'PowerStencil core'
13
+ end
14
+
11
15
  def bootstrap(cmd_line_args = ARGV.dup)
12
16
  setup_climatic cmd_line_args
13
17
  logger.debug 'Starting PowerStencil initialization...'
14
18
  setup_system_processors
15
19
  setup_universe_compiler_logger
16
- # @project = try_to_load_project fail_on_error: false
17
- begin
18
- project.setup_templates_for_entities # unless project.nil?
19
- rescue => e
20
- logger.debug PowerStencil::Error.report_error e
21
- end
22
20
  logger.debug 'PowerStencil initialization complete'
23
21
  end
24
22
 
@@ -32,7 +30,6 @@ module PowerStencil
32
30
 
33
31
  def try_to_load_project(fail_on_error: true)
34
32
  PowerStencil::Project::Base.instantiate_from_config config
35
-
36
33
  end
37
34
 
38
35
  def setup_system_processors
@@ -44,10 +44,12 @@ module PowerStencil
44
44
  @gem_spec = PowerStencil::Plugins::Base.find_locally_installed_gem_spec name, gem_req
45
45
  raise PowerStencil::Error, "Cannot find plugin '#{name}'. Try 'power_stencil plugin --install'" if gem_spec.nil?
46
46
  raise PowerStencil::Error, "Invalid plugin '#{name}' ! Missing metadata 'plugin_name' in spec !" if gem_spec.metadata['plugin_name'].nil?
47
+ logger.debug "Plugin '#{name}' real name is '#{gem_spec.metadata['plugin_name']}'."
47
48
  @name = gem_spec.metadata['plugin_name']
48
- logger.debug "Plugin real name is '#{name}'."
49
49
  when :local
50
50
  logger.debug "Plugin '#{name}' is provided locally by the project."
51
+ else
52
+ raise PowerStencil::Error, "Unsupported plugin type (#{type}) for plugin '#{name}' !"
51
53
  end
52
54
 
53
55
  logger.debug "Loading plugin '#{name}'..."
@@ -21,11 +21,16 @@ module PowerStencil
21
21
  raise PowerStencil::Error, "Plugin '#{self.name}' has no definition file !"
22
22
  end
23
23
  logger.debug "Loading plugin '#{self.name}' capabilities..."
24
- @plugin_definition = yaml_file_to_hash yaml_file
25
- %i(processors entity_definitions templates build dsl).each do |capability|
26
- unless plugin_definition[capability].nil? or plugin_definition[capability].empty?
27
- capabilities[capability] = true
24
+ begin
25
+ @plugin_definition = yaml_file_to_hash yaml_file
26
+ %i(processors entity_definitions templates build dsl).each do |capability|
27
+ unless plugin_definition[capability].nil? or plugin_definition[capability].empty?
28
+ capabilities[capability] = true
29
+ end
28
30
  end
31
+ rescue => e
32
+ logger.debug PowerStencil::Error.report_error(e)
33
+ raise PowerStencil::Error, "Invalid plugin capabilities file '#{yaml_file}' for plugin '#{self.name}'"
29
34
  end
30
35
 
31
36
  end
@@ -6,9 +6,24 @@ module PowerStencil
6
6
  def require_plugin_entity_definitions
7
7
  return unless capabilities[:entity_definitions]
8
8
  logger.info "Requiring '#{self.name}' plugin entity definitions..."
9
+ securely_require_with_entity_class_detection
10
+ end
11
+
12
+
13
+ private
14
+
15
+ def securely_require_with_entity_class_detection
16
+ before = PowerStencil::Engine::EntitiesHandling.all_types
9
17
  plugin_definition[:entity_definitions].each do |entity_definition_file_path|
10
18
  securely_require entity_definition_file_path, fail_on_error: true
11
19
  end
20
+ after = PowerStencil::Engine::EntitiesHandling.all_types
21
+ after.reject { |k, _| before.keys.include? k }.each do |_, plugin_entity_class|
22
+ plugin = self
23
+ plugin_entity_class.instance_eval do
24
+ @entity_type_source_provider = plugin
25
+ end
26
+ end
12
27
  end
13
28
 
14
29
  end
@@ -21,6 +21,7 @@ module PowerStencil
21
21
  $LOAD_PATH << plugin_root_path
22
22
  securely_require entry_point_path unless plugin_definition[:plugin_module].nil?
23
23
  rescue LoadError => e
24
+ @entry_point_path = nil
24
25
  logger.warn "As plugin '#{name}' code is invalid, removing '#{plugin_root_path}' from LOAD_PATH"
25
26
  $LOAD_PATH.delete plugin_root_path
26
27
  end
@@ -3,15 +3,15 @@ module PowerStencil
3
3
 
4
4
  module Templates
5
5
 
6
- def register_plugin_templates
6
+ def register_plugin_templates_templates
7
7
  return unless capabilities[:templates]
8
8
  logger.info "Loading '#{self.name}' plugin templates..."
9
9
  plugin_definition[:templates].each do |templates_path|
10
- plugin_templates_path = File.join self.path, templates_path
10
+ plugin_templates_path = File.join self.plugin_path, templates_path
11
11
  Dir.entries(plugin_templates_path).reject { |e| %w(. ..).include? e }.each do |entry|
12
12
  template_path = File.join(plugin_templates_path, entry)
13
13
  if Dir.exist? template_path
14
- project.register_template_path_for_type entry.to_sym, template_path
14
+ project.register_template_template_path_for_type entry.to_sym, template_path
15
15
  end
16
16
  end
17
17
  end
@@ -5,6 +5,7 @@ require 'power_stencil/project/versioning'
5
5
  require 'power_stencil/project/info'
6
6
  require 'power_stencil/project/templates'
7
7
  require 'power_stencil/project/plugins'
8
+ require 'power_stencil/project/git'
8
9
 
9
10
  require 'power_stencil/engine/project_engine'
10
11
  require 'power_stencil/engine/entity_engine'
@@ -30,40 +31,54 @@ module PowerStencil
30
31
  include PowerStencil::Project::Templates
31
32
  include PowerStencil::Project::Plugins
32
33
  include PowerStencil::Project::Info
34
+ include PowerStencil::Project::Git
33
35
  extend PowerStencil::Project::Create
34
36
 
35
37
  attr_reader :engine, :entity_engine
36
38
 
39
+ def name
40
+ File.dirname project_config_root
41
+ end
42
+
37
43
  def initialize(search_from_path: Dir.pwd)
38
44
  initialize_paths search_from_path
39
45
  load_project_specific_config
40
46
  check_project_version
41
47
  bootstrap_plugins
42
48
  build_engines
43
- register_system_templates
44
- register_project_templates
49
+ register_system_templates_templates
50
+ register_plugins_templates_templates
51
+ register_project_templates_templates
52
+ setup_git_tracking
45
53
  end
46
54
 
47
55
  private
48
56
 
49
- def register_project_templates
50
- dir = project_templates_path
57
+ def register_plugins_templates_templates
58
+ plugins.each do |_, plugin|
59
+ plugin.register_plugin_templates_templates
60
+ end
61
+ end
62
+
63
+
64
+ def register_project_templates_templates
65
+ dir = project_templates_templates_path
51
66
  if Dir.exist? dir and File.readable? dir
52
67
  logger.info 'Registering project specific templates.'
53
68
  Dir.entries(dir).each do |potential_entity_type|
54
69
  next if potential_entity_type.match /^\./
55
70
  template_dir = File.join(dir, potential_entity_type)
56
71
  next unless File.directory? template_dir
57
- register_template_path_for_type potential_entity_type.to_sym, template_dir
72
+ register_template_template_path_for_type potential_entity_type.to_sym, template_dir
58
73
  end
59
74
  end
60
75
  end
61
76
 
62
- def register_system_templates
77
+ def register_system_templates_templates
63
78
  logger.debug 'Registering system templates'
64
79
  %i(plugin_definition simple_exec).each do |template_name|
65
- template_path = template_path template_name
66
- register_template_path_for_type template_name, template_path
80
+ template_path = system_template_template_path template_name
81
+ register_template_template_path_for_type template_name, template_path
67
82
  end
68
83
  end
69
84
 
@@ -76,4 +91,4 @@ module PowerStencil
76
91
  end
77
92
 
78
93
  end
79
- end
94
+ end
@@ -5,6 +5,8 @@ module PowerStencil
5
5
 
6
6
  module Create
7
7
 
8
+ INITIAL_REPOSITORY_COMMIT_MESSAGE = 'Initial commit for project "%s".'
9
+
8
10
  include Climatic::Proxy
9
11
 
10
12
  def create_project_tree(path, config_directory_name = PowerStencil.config[:default_config_directory_name])
@@ -14,14 +16,33 @@ module PowerStencil
14
16
  raise PowerStencil::Error, "The directory '#{path}' already contains a PowerStencil project !" unless config[:force]
15
17
  end
16
18
  logger.info "Creating project in '#{path}'..."
17
- render_project_template_in(path)
19
+ render_project_template_in path
20
+ initialize_git_repository path
18
21
  end
19
22
 
20
23
  private
21
24
 
25
+ def initialize_git_repository(repo_path)
26
+ if config[:'no-git']
27
+ puts_and_logs 'Do not initialize project git repository as per config request.', logs_as: :debug
28
+ return
29
+ end
30
+ if Dir.exists? File.join(repo_path, '.git')
31
+ puts_and_logs 'Git repository already exists. Skipping.', logs_as: :debug, check_verbose: false
32
+ return
33
+ end
34
+ puts_and_logs 'Initializing git repository...', logs_as: :debug
35
+ git = ::Git.init repo_path
36
+ logger.debug 'Adding all files.'
37
+ git.add repo_path
38
+ logger.debug 'Committing initial status'
39
+ commit_msg = INITIAL_REPOSITORY_COMMIT_MESSAGE % [File.basename(repo_path)]
40
+ git.commit_all commit_msg
41
+ end
42
+
22
43
  def render_project_template_in(new_project_config_path)
23
44
  engine = PowerStencil::Engine::InitEngine.new
24
- engine.render_source PowerStencil::Project::Paths.project_system_template_path, new_project_config_path
45
+ engine.render_source PowerStencil::Project::Paths.project_system_template_template_path, new_project_config_path
25
46
  end
26
47
 
27
48
  end
@@ -0,0 +1,75 @@
1
+ module PowerStencil
2
+ module Project
3
+
4
+ module Git
5
+
6
+ include Climatic::Utils::Input
7
+
8
+ def track_action_with_git(action_message,
9
+ user_validation_required: false,
10
+ validation_message: 'Commit changes ?',
11
+ show_files_to_commit: false,
12
+ &block)
13
+ return yield if git.nil?
14
+
15
+ status_before_action = git.status
16
+ yield
17
+ status_after_action = git.status
18
+
19
+ files_introduced_by_action = status_after_action.untracked.reject { |f| status_before_action.untracked.keys.include? f}
20
+ files_deleted_by_action = status_after_action.deleted.reject { |f| status_before_action.deleted.keys.include? f}
21
+ files_modified_by_action = status_after_action.changed.reject { |f| status_before_action.changed.keys.include? f}
22
+ files_to_commit = [files_introduced_by_action, files_deleted_by_action, files_modified_by_action].map(&:keys).flatten.sort.uniq
23
+
24
+ if files_to_commit.empty?
25
+ puts_and_logs 'Nothing to commit.'
26
+ return
27
+ end
28
+
29
+ if show_files_to_commit
30
+ header = 'Following file'
31
+ header << 's' if files_to_commit.count > 1
32
+ header << ' will be committed:'
33
+ puts header
34
+ puts files_to_commit.map { |filename| " - '#{filename}'" }
35
+ end
36
+
37
+ if user_validation_required
38
+ unless get_user_confirmation(default_choice: 'Yes', prompt: validation_message)
39
+ puts_and_logs 'Commit cancelled by user.', check_verbose: false
40
+ return
41
+ end
42
+ end
43
+
44
+ files_to_commit.each { |filename| git.add filename }
45
+
46
+ # Verify files to be committed really are
47
+ files_really_to_be_committed = git.diff.stats[:files].keys.select { |filename| files_to_commit.include? filename }
48
+ return if files_really_to_be_committed.empty?
49
+ files_really_to_be_committed.each { |filename| logger.info "File '#{filename}' will be committed" }
50
+
51
+ git.commit action_message
52
+ puts_and_logs 'All changes introduced have been committed.'
53
+ end
54
+
55
+ private
56
+
57
+ attr_reader :git
58
+
59
+ def setup_git_tracking
60
+ @git = nil
61
+ if config[:'no-git']
62
+ logger.debug "Won't track any changes with git as per config request!"
63
+ return
64
+ end
65
+ @git = ::Git.open(self.project_root, :log => PowerStencil.logger)
66
+ logger.debug 'Following project changes with git'
67
+ rescue => e
68
+ logger.debug PowerStencil::Error.report_error(e)
69
+ logger.warn 'This project is not managed by git'
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+ end