magma_cli 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.dockerignore +2 -0
- data/.github/CODE_OF_CONDUCT.md +74 -0
- data/.github/ISSUE_TEMPLATE/BUG_REPORT.md +26 -0
- data/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md +17 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +53 -0
- data/.ruby-version +1 -0
- data/.travis.yml +14 -0
- data/Dockerfile +36 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +62 -0
- data/LICENSE +504 -0
- data/Makefile +40 -0
- data/README.md +56 -0
- data/Rakefile +20 -0
- data/bin/console +13 -0
- data/bin/dmagma +4 -0
- data/bin/magma +9 -0
- data/bin/setup +8 -0
- data/entrypoint.sh +7 -0
- data/lib/magma/app.rb +73 -0
- data/lib/magma/common.rb +36 -0
- data/lib/magma/config/mlt.rb +90 -0
- data/lib/magma/config.rb +4 -0
- data/lib/magma/preparer.rb +85 -0
- data/lib/magma/renderer.rb +123 -0
- data/lib/magma/templater.rb +78 -0
- data/lib/magma/utils.rb +57 -0
- data/lib/magma/version.rb +3 -0
- data/lib/magma.rb +6 -0
- data/magma.gemspec +30 -0
- metadata +175 -0
data/lib/magma/app.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# rubocop:disable Lint/MissingCopEnableDirective, Metrics/LineLength
|
2
|
+
|
3
|
+
require 'etc'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'thor'
|
6
|
+
require 'magma/preparer'
|
7
|
+
require 'magma/renderer'
|
8
|
+
require 'magma/templater'
|
9
|
+
|
10
|
+
module Magma
|
11
|
+
# The CLI application
|
12
|
+
class App < Thor
|
13
|
+
class << self
|
14
|
+
def exit_on_failure?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'render [outfile]', 'Renders an MLT into an HLS video'
|
20
|
+
option :context, type: :hash, default: {}, aliases: 'c', desc: 'Render-time context'
|
21
|
+
option :data_file, type: :string, aliases: 'D', desc: 'Static data file, accepts JSON or YML'
|
22
|
+
option :enable_real_time, type: :boolean, default: false, aliases: 'r', desc: 'Render in real-time'
|
23
|
+
option :format, type: :string, default: 'mp4', aliases: 'f', enum: Renderer::SUPPORTED_FORMATS, desc: 'Encoding format'
|
24
|
+
option :globals, type: :hash, default: {}, aliases: 'g', desc: 'Global data overrides'
|
25
|
+
option :infile, type: :string, aliases: 'i', desc: 'Input file', required: true
|
26
|
+
option :length, type: :numeric, default: 2, desc: 'Set the target length in seconds'
|
27
|
+
option :preset, type: :string, default: 'fast', desc: 'Video preset for encoding'
|
28
|
+
option :real_time, type: :numeric, default: Etc.nprocessors, aliases: 'n', desc: 'Number of cores (real-time)'
|
29
|
+
option :size, type: :string, default: '1280x720', aliases: 's', desc: 'Frame size (width x height)'
|
30
|
+
option :skip_template, type: :boolean, default: false, desc: 'Skips template-filling step'
|
31
|
+
option :vcodec, type: :string, desc: 'Video codec to use for encoding'
|
32
|
+
def render(outfile)
|
33
|
+
if options[:skip_template]
|
34
|
+
Renderer.call outfile, options
|
35
|
+
else
|
36
|
+
template = Tempfile.new [File.basename(outfile), '.mlt']
|
37
|
+
|
38
|
+
begin
|
39
|
+
Preparer.call options[:infile], options.merge(outfile: template)
|
40
|
+
Templater.call template, options.merge(outfile: template)
|
41
|
+
Renderer.call outfile, options.merge(infile: template.path)
|
42
|
+
ensure
|
43
|
+
template.close
|
44
|
+
template.unlink
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'template [file]', 'Fills a template'
|
50
|
+
option :context, type: :hash, default: {}, aliases: 'c', desc: 'Render-time context'
|
51
|
+
option :globals, type: :hash, default: {}, aliases: 'g', desc: 'Global data overrides'
|
52
|
+
option :data_file, type: :string, aliases: 'D', desc: 'Static data file, accepts JSON or YML'
|
53
|
+
option :outfile, type: :string, aliases: 'o', desc: 'Output file'
|
54
|
+
option :overwrite, type: :boolean, default: false, aliases: 'O', desc: 'Overwrite input file'
|
55
|
+
option :print, type: :string, aliases: 'p', desc: 'Print output'
|
56
|
+
def template(infile = nil)
|
57
|
+
Templater.call infile, options
|
58
|
+
end
|
59
|
+
|
60
|
+
desc 'prepare [file]', 'Prepares a template'
|
61
|
+
option :outfile, type: :string, aliases: 'o', desc: 'Output file'
|
62
|
+
option :overwrite, type: :boolean, default: false, aliases: 'O', desc: 'Overwrite input file'
|
63
|
+
option :print, type: :string, aliases: 'p', desc: 'Print output'
|
64
|
+
def prepare(infile = nil)
|
65
|
+
Preparer.call infile, options
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'version', 'Displays the version'
|
69
|
+
def version
|
70
|
+
puts Magma::VERSION
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/magma/common.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
module Magma
|
4
|
+
module Common
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def call(*args)
|
9
|
+
new(*args).call
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle_output!(output, options)
|
14
|
+
output!(output) if options[:print]
|
15
|
+
overwrite!(options[:infile], output) if options[:overwrite]
|
16
|
+
save!(options[:outfile], output) if options[:outfile]
|
17
|
+
output
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def output!(output)
|
23
|
+
puts output
|
24
|
+
end
|
25
|
+
|
26
|
+
def overwrite!(infile, output)
|
27
|
+
raise('No file to overwrite') unless infile
|
28
|
+
File.write infile, output
|
29
|
+
end
|
30
|
+
|
31
|
+
def save!(outfile, output)
|
32
|
+
raise('No output file specified') unless outfile
|
33
|
+
File.write outfile, output
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
2
|
+
require 'active_support/core_ext/hash/deep_merge'
|
3
|
+
|
4
|
+
module Magma::Config
|
5
|
+
class MLT < ActiveSupport::HashWithIndifferentAccess
|
6
|
+
VERSION = 1
|
7
|
+
ALLOWED_KEYS = %w[
|
8
|
+
globals
|
9
|
+
scopes
|
10
|
+
type
|
11
|
+
version
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
attr_reader :data
|
15
|
+
|
16
|
+
def initialize(hash = {})
|
17
|
+
super({
|
18
|
+
globals: {},
|
19
|
+
scopes: {},
|
20
|
+
type: 'mlt',
|
21
|
+
version: VERSION,
|
22
|
+
}.merge(hash))
|
23
|
+
validate!
|
24
|
+
end
|
25
|
+
|
26
|
+
def globals
|
27
|
+
self[:globals]
|
28
|
+
end
|
29
|
+
|
30
|
+
def operations
|
31
|
+
raise 'needs to be resolved' unless resolved?
|
32
|
+
data.select { |key, _value| key[0] == '$' }
|
33
|
+
end
|
34
|
+
|
35
|
+
def resolve(context = {})
|
36
|
+
@data = context.reduce(globals.clone) do |res, (key, value)|
|
37
|
+
scope = scopes.fetch(key, {}).fetch(value, nil)
|
38
|
+
res.tap { |r| r.deep_merge!(scope) if scope }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def resolved?
|
43
|
+
data && true
|
44
|
+
end
|
45
|
+
|
46
|
+
def scopes
|
47
|
+
self[:scopes]
|
48
|
+
end
|
49
|
+
|
50
|
+
def transform(doc)
|
51
|
+
raise 'needs to be resolved' unless resolved?
|
52
|
+
operations.each do |key, obj|
|
53
|
+
# Skip if not prefixed with $
|
54
|
+
leader = key[0]
|
55
|
+
next unless leader == '$'
|
56
|
+
|
57
|
+
# Select node set
|
58
|
+
nodeset = doc.css(key[1..-1])
|
59
|
+
|
60
|
+
case obj
|
61
|
+
when Hash
|
62
|
+
obj.each do |name, value|
|
63
|
+
if value
|
64
|
+
nodeset.attr(name, value)
|
65
|
+
else
|
66
|
+
nodeset.remove_attr(name)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
else
|
70
|
+
text = obj.to_s
|
71
|
+
nodeset.each { |node| node.content = text }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
doc
|
75
|
+
end
|
76
|
+
|
77
|
+
def variables
|
78
|
+
raise 'needs to be resolved' unless resolved?
|
79
|
+
data.reject { |key, _value| key[0] == '$' }
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def validate!
|
85
|
+
extras = keys - ALLOWED_KEYS
|
86
|
+
raise "extra keys: #{extras}" unless extras.empty?
|
87
|
+
raise 'not type: mlt' unless self[:type] == 'mlt'
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/magma/config.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'magma/common'
|
2
|
+
require 'magma/utils'
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Magma
|
7
|
+
class Preparer
|
8
|
+
include Magma::Common
|
9
|
+
include Magma::Utils
|
10
|
+
|
11
|
+
def initialize(infile, options = {})
|
12
|
+
@infile = infile
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
stabilize_producers!
|
18
|
+
handle_output! xml, options.merge(infile: infile)
|
19
|
+
xml
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_accessor :infile, :options
|
25
|
+
|
26
|
+
def doc
|
27
|
+
@doc ||= Nokogiri::XML(source)
|
28
|
+
end
|
29
|
+
|
30
|
+
def log(message)
|
31
|
+
# Don't print the message if we're out-putting to STDOUT
|
32
|
+
puts message unless options[:print]
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource_path
|
36
|
+
return Dir.pwd unless infile
|
37
|
+
File.dirname File.expand_path(infile)
|
38
|
+
end
|
39
|
+
|
40
|
+
def stabilize_producers! # rubocop:disable Metrics/AbcSize
|
41
|
+
map = Hash[doc.xpath('//producer').map do |producer|
|
42
|
+
id = producer.get_attribute 'id'
|
43
|
+
next nil if uuid? id
|
44
|
+
[producer.get_attribute('id'), uuid]
|
45
|
+
end.compact]
|
46
|
+
return if map.empty?
|
47
|
+
|
48
|
+
doc.traverse do |node|
|
49
|
+
id = node.get_attribute 'id'
|
50
|
+
case node.name
|
51
|
+
when 'producer'
|
52
|
+
if map[id]
|
53
|
+
log "Re-mapping producer##{id} id to #{map[id]}"
|
54
|
+
node.set_attribute('id', map[id])
|
55
|
+
|
56
|
+
node.search('property[name="resource"]').each do |property|
|
57
|
+
if File.file?(File.join(resource_path, property.content))
|
58
|
+
log "Expanding resource '#{resource_path}/#{property.content}'"
|
59
|
+
property.content = File.join(resource_path, property.content)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
else
|
64
|
+
producer = node.get_attribute 'producer'
|
65
|
+
if map[producer]
|
66
|
+
node.set_attribute('producer', map[producer])
|
67
|
+
log "Re-mapping #{node.name}#{id ? '#' + id : ''} producer to #{map[producer]}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def source
|
74
|
+
@source ||= read_file_or_stdin(infile) || raise('Need a template')
|
75
|
+
end
|
76
|
+
|
77
|
+
def uuid
|
78
|
+
SecureRandom.uuid
|
79
|
+
end
|
80
|
+
|
81
|
+
def xml
|
82
|
+
doc.to_xml
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'magma/utils'
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
module Magma
|
5
|
+
class Renderer
|
6
|
+
SUPPORTED_FORMATS = %w[hls mp4].freeze
|
7
|
+
MELT = 'melt'.freeze
|
8
|
+
|
9
|
+
include Magma::Common
|
10
|
+
include Magma::Utils
|
11
|
+
|
12
|
+
def initialize(outfile, options)
|
13
|
+
@outfile = outfile
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
render!
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
attr_accessor :options, :outfile
|
24
|
+
|
25
|
+
# Adds sane defaults to config
|
26
|
+
def add_defaults(options)
|
27
|
+
{
|
28
|
+
hls_list_size: 0,
|
29
|
+
start_number: 0,
|
30
|
+
}.merge(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Formats keys to melt arguments
|
34
|
+
def format(options)
|
35
|
+
options.map do |key, value|
|
36
|
+
[{
|
37
|
+
format: :f,
|
38
|
+
size: :s,
|
39
|
+
length: :hls_time,
|
40
|
+
}[key] || key, value].map(&:to_s).join('=')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Finds and constructs a melt invocation string
|
45
|
+
def melt(args)
|
46
|
+
bin = which(MELT) || raise("#{MELT} not found. Is MLT installed?")
|
47
|
+
"#{bin} #{args}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# Constructs melt arguments from options hash
|
51
|
+
def melt_args(options)
|
52
|
+
[
|
53
|
+
options[:infile],
|
54
|
+
"-consumer avformat:#{outfile}",
|
55
|
+
].concat(pipe(options, %i[
|
56
|
+
symbolize_keys
|
57
|
+
add_defaults
|
58
|
+
select
|
59
|
+
transform
|
60
|
+
table
|
61
|
+
format
|
62
|
+
]))
|
63
|
+
end
|
64
|
+
|
65
|
+
# Renders the project
|
66
|
+
def render!
|
67
|
+
cmd = melt melt_args(options.merge(outfile: outfile)).join(' ')
|
68
|
+
puts "Run: #{cmd}"
|
69
|
+
puts
|
70
|
+
|
71
|
+
run(cmd) do |_stdin, _stdout, stderr|
|
72
|
+
stderr.each("\r") do |line|
|
73
|
+
STDOUT.write "\r#{line}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Selects certain options
|
79
|
+
def select(options)
|
80
|
+
# Clone original
|
81
|
+
options = options.clone
|
82
|
+
|
83
|
+
# Handle related options
|
84
|
+
options.delete(:real_time) unless options.delete(:enable_real_time)
|
85
|
+
|
86
|
+
# Reject
|
87
|
+
options.select do |key, value|
|
88
|
+
!value.nil? && %i[
|
89
|
+
format
|
90
|
+
hls_list_size
|
91
|
+
real_time
|
92
|
+
length
|
93
|
+
preset
|
94
|
+
size
|
95
|
+
start_number
|
96
|
+
vcodec
|
97
|
+
].include?(key)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Prints a table and passes through the options hash
|
102
|
+
def table(options)
|
103
|
+
lpadding = options.keys.max_by(&:length).length + 2
|
104
|
+
rpadding = options.values.max_by { |v| v.to_s.length }.to_s.length
|
105
|
+
puts 'PROPERTY'.ljust(lpadding) + 'VALUE'
|
106
|
+
puts '=' * (lpadding + rpadding)
|
107
|
+
options.keys.sort.each do |key|
|
108
|
+
puts key.to_s.ljust(lpadding) + options[key].to_s
|
109
|
+
end
|
110
|
+
puts
|
111
|
+
options
|
112
|
+
end
|
113
|
+
|
114
|
+
# Transforms certain options values
|
115
|
+
def transform(options)
|
116
|
+
options.map do |key, value|
|
117
|
+
[key, ({
|
118
|
+
real_time: ->(x) { "-#{x}" },
|
119
|
+
}[key] || proc { |x| x }).call(value)]
|
120
|
+
end.to_h
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'magma/config/mlt'
|
2
|
+
require 'magma/common'
|
3
|
+
require 'magma/utils'
|
4
|
+
require 'liquid'
|
5
|
+
require 'nokogiri'
|
6
|
+
|
7
|
+
module Magma
|
8
|
+
class Templater
|
9
|
+
include Magma::Common
|
10
|
+
include Magma::Utils
|
11
|
+
|
12
|
+
def initialize(infile, options)
|
13
|
+
@infile = infile
|
14
|
+
@options = options
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
pipe source, [
|
19
|
+
:template,
|
20
|
+
:render,
|
21
|
+
:parse,
|
22
|
+
:transform,
|
23
|
+
'.to_xml',
|
24
|
+
->(result) { handle_output!(result, options.merge(infile: infile)) },
|
25
|
+
]
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :infile, :options
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= Magma::Config::MLT.new(data).tap do |c|
|
34
|
+
c.resolve(options[:context] || {})
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def data
|
39
|
+
@data ||= options[:data_file].nil? ? {} : YAML.safe_load(File.read(options[:data_file]), [], [], true)
|
40
|
+
end
|
41
|
+
|
42
|
+
def source
|
43
|
+
@source ||= read_file_or_stdin(infile) || raise('Need a template')
|
44
|
+
end
|
45
|
+
|
46
|
+
### Pipeline
|
47
|
+
|
48
|
+
# Renders a Liquid template
|
49
|
+
def render(template)
|
50
|
+
template.render(config.variables.deep_merge(options[:globals]), strict_variables: true).tap do
|
51
|
+
if template.errors&.length&.positive?
|
52
|
+
puts template.errors
|
53
|
+
raise template.errors.map(&:to_s).join('; ')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Parses a template into a DOM
|
59
|
+
def parse(template)
|
60
|
+
Nokogiri::XML template
|
61
|
+
end
|
62
|
+
|
63
|
+
# Parses a source into a template
|
64
|
+
def template(source)
|
65
|
+
Liquid::Template.parse(source, error_mode: :strict)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Applies transformations to a document
|
69
|
+
def transform(doc)
|
70
|
+
config.transform doc
|
71
|
+
end
|
72
|
+
|
73
|
+
# Converts a docuemtn into XML
|
74
|
+
def xml(doc)
|
75
|
+
doc.to_xml
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/magma/utils.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Magma
|
4
|
+
module Utils
|
5
|
+
# Reads a file or from STDIN if piped
|
6
|
+
def read_file_or_stdin(filename = nil)
|
7
|
+
filename.nil? ? !STDIN.tty? && STDIN.read : File.read(filename)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Returns whether a string is a UUID
|
11
|
+
def uuid?(uuid)
|
12
|
+
uuid_regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/
|
13
|
+
uuid_regex =~ uuid.to_s.downcase
|
14
|
+
end
|
15
|
+
|
16
|
+
# Applies methods/procs to an object in order
|
17
|
+
def pipe(object, methods = [])
|
18
|
+
methods.reduce(object) do |acc, method|
|
19
|
+
case method
|
20
|
+
when Proc || method.respond_to?(:call)
|
21
|
+
method.call(acc)
|
22
|
+
when Symbol
|
23
|
+
send(method, acc)
|
24
|
+
when String
|
25
|
+
raise "must start with '.'" unless method[0] == '.'
|
26
|
+
acc.send(method[1..-1].to_sym)
|
27
|
+
else
|
28
|
+
raise "unexpected pipe #{method}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Delegates to Open3
|
34
|
+
def run(*args, &block)
|
35
|
+
return Open3.popen3(*args, &block) if block_given?
|
36
|
+
Open3.popen3(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Symbolizes a hash's keys
|
40
|
+
def symbolize_keys(hash)
|
41
|
+
hash.each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Cross-platform way of finding an executable in the $PATH
|
45
|
+
# Reference: https://stackoverflow.com/a/5471032/3557448
|
46
|
+
def which(cmd)
|
47
|
+
exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
|
48
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
|
49
|
+
exts.each do |ext|
|
50
|
+
exe = File.join(path, "#{cmd}#{ext}")
|
51
|
+
return exe if File.executable?(exe) && !File.directory?(exe)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/magma.rb
ADDED
data/magma.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'magma/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'magma_cli'
|
7
|
+
spec.version = Magma::VERSION
|
8
|
+
spec.authors = ['Allan Reyes']
|
9
|
+
spec.email = ['allanbreyes@users.noreply.github.com']
|
10
|
+
|
11
|
+
spec.summary = 'Magma CLI'
|
12
|
+
spec.description = 'Command-line interface for MLT templating and rendering'
|
13
|
+
spec.homepage = 'https://github.com/darbylabs/magma'
|
14
|
+
spec.license = 'LGPL-2.1'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
+
f.match(%r{^(examples|features|spec|test)/})
|
18
|
+
end
|
19
|
+
spec.executables = ['magma']
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
|
22
|
+
spec.add_dependency 'activesupport', '~> 5.2'
|
23
|
+
spec.add_dependency 'liquid', '~> 4.0'
|
24
|
+
spec.add_dependency 'nokogiri', '~> 1.8'
|
25
|
+
spec.add_dependency 'thor', '~> 0.20'
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
28
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
30
|
+
end
|