alterant 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 58a70e9ec9e231c880a288c9c1ad0c0a839a2721448ed869567ab3b8b926c874
4
+ data.tar.gz: 9584c3391e717d9317a6513ec2d9e7b768d22aac33f368bf488aa3e9babed725
5
+ SHA512:
6
+ metadata.gz: 1d076a7e5e5b1949740d829545223e75095f9fdbd96d7fc7abe0ea3de459ea9ac6a326964f28a27a2f3b07f03e70378c72f2ba44b72359cd76f3370df1152e8b
7
+ data.tar.gz: f2518b91cbe170114b526e3c873906b63894efd52ddac645974cd8094a7c1c82e98713ffd9d2a87a9ca148d1e3d67a56c76390da6513b58adc0c23c79148e65e
@@ -0,0 +1,9 @@
1
+ ![Alterant Logo](https://s3.amazonaws.com/cdn.cloud66.com/images/alterant_logo.png)
2
+
3
+ # Alterant
4
+
5
+ Alterant is a tool to modify JSON and YAML configuration files.
6
+
7
+ ```bash
8
+ alterant modify --in config.yml --out output.yml --modifier script.js
9
+ ```
@@ -0,0 +1,170 @@
1
+ #!/usr/bin/env ruby
2
+ require 'thor'
3
+ require 'yaml'
4
+ require 'colorize'
5
+ require_relative '../lib/alterant'
6
+
7
+ module Alterant
8
+ class AlterantCLI < Thor
9
+ package_name "alterant"
10
+
11
+ desc "version", "Show Alterant version"
12
+ def version
13
+ say "#{::Alterant::APP_NAME} #{::Alterant::VERSION}\n#{::Alterant::COPYRIGHT_MESSAGE}"
14
+ end
15
+
16
+ desc "update", "Update Alterant to the latest version"
17
+ option :version, type: :string, desc: "Force a specific version"
18
+ def update
19
+ say "Updating Alterant..."
20
+ unless options[:version]
21
+ say `gem install alterant --no-ri --no-rdoc`
22
+ else
23
+ say `gem install alterant -v #{options[:version]} --no-ri --no-rdoc`
24
+ end
25
+ end
26
+
27
+ desc "modify", "Runs the given script against a file"
28
+ option :modifier, type: :string, desc: "Alterant JS script"
29
+ option :in, type: :string, desc: "Input configuration file"
30
+ option :out, type: :string, desc: "Output configuration file"
31
+ option :output_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Output format"
32
+ option :diff, type: :boolean, default: false, desc: "Return the diff instead of the output itself"
33
+ option :debug, type: :boolean, default: false
34
+ option :input_format, type: :string, enum: ['yaml', 'json'], default: 'yaml', desc: "Input format if it's based on a stream and not a file"
35
+ option :overwrite, type: :boolean, default: false
36
+ def modify
37
+ $debug = options[:debug] || false
38
+ overwrite = options[:overwrite]
39
+ diff = options[:diff]
40
+ output_format = options[:output_format]
41
+ input_format = options[:input_format]
42
+
43
+ in_file = options[:in]
44
+ if !in_file
45
+ STDERR.puts "No input file provided. Use --in option".red
46
+ exit(1)
47
+ end
48
+ if in_file != '-'
49
+ unless File.exists?(in_file)
50
+ STDERR.puts "Input file #{in_file} not found".red
51
+ exit(1)
52
+ end
53
+ # detect the type
54
+ input_ext = File.extname(in_file)
55
+ unless ['.yaml', '.yml', '.json'].include? input_ext
56
+ STDERR.puts "Only yaml and json files are supported for input".red
57
+ exit(1)
58
+ end
59
+ if ['.yaml', '.yml'].include?(input_ext)
60
+ input_format = 'yaml'
61
+ else
62
+ input_format = 'json'
63
+ end
64
+
65
+ output_format = input_format if !output_format
66
+ end
67
+
68
+ modifier_file = options[:modifier]
69
+ if !modifier_file
70
+ STDERR.puts "No script file provided. Use --modifier option".red
71
+ exit(1)
72
+ end
73
+ unless File.exists?(modifier_file)
74
+ STDERR.puts "Modifier file #{modifier_file} not found".red
75
+ exit(1)
76
+ end
77
+
78
+ out_file = options[:out]
79
+ if out_file
80
+ if !overwrite && File.exists?(out_file)
81
+ STDERR.puts "Output file #{out_file} already exists. Use --overwrite flag to overwrite it".red
82
+ exit(1)
83
+ end
84
+ else
85
+ out_file = '-' # output to stdout
86
+ end
87
+
88
+
89
+ data = []
90
+ if in_file == '-'
91
+ input_text = STDIN.read
92
+ else
93
+ input_text = File.read(in_file)
94
+ end
95
+
96
+ if input_format == 'yaml'
97
+ if input_text.empty?
98
+ STDERR.puts "Empty input file".red
99
+ exit(2)
100
+ end
101
+
102
+ input_text.split('---').each_with_index do |part, idx|
103
+ part_data = YAML.load(part)
104
+ data << part_data
105
+ end
106
+
107
+ else
108
+ file_data = JSON.parse(input_text)
109
+ if file_data.is_a? Array
110
+ data = file_data
111
+ else
112
+ data = [file_data]
113
+ end
114
+ end
115
+
116
+ run_context = {}
117
+ modifier = File.read(modifier_file)
118
+ if in_file != '-'
119
+ run_context[:basedir] = File.dirname(in_file)
120
+ end
121
+
122
+ run_context[:js_preload] = ::Alterant::Classes.LoadClasses
123
+ alter = ::Alterant::Alterant.new(input: data, modifier: modifier, filename: modifier_file, options: run_context)
124
+ results = alter.execute(timeout: 500)
125
+
126
+ if results.nil?
127
+ STDERR.puts "Aborting".red
128
+ exit(2)
129
+ end
130
+
131
+ if output_format == 'yaml' || output_format == 'yml'
132
+ converted_as_text = results.map { |r| r.to_yaml }.join("\n")
133
+ input = data.map { |r| r.to_yaml }.join("\n")
134
+ elsif output_format == 'json'
135
+ converted_as_text = JSON.pretty_generate(results)
136
+ input = JSON.pretty_generate(data)
137
+ end
138
+
139
+ if diff
140
+ output_as_text = Diffy::Diff.new(input, converted_as_text)
141
+ else
142
+ output_as_text = converted_as_text
143
+ end
144
+
145
+ if out_file == "-"
146
+ puts output_as_text
147
+ else
148
+ File.open(out_file, 'w') do |file|
149
+ file.write(output_as_text)
150
+ end
151
+ end
152
+ return true
153
+ rescue ::Alterant::ParseError => exc
154
+ STDERR.puts "Syntax error: #{exc.message}".red
155
+ return false
156
+ rescue ::Alterant::RuntimeError => exc
157
+ STDERR.puts "Runtime error: #{exc.message}".red
158
+ return false
159
+ rescue => exc
160
+ if $debug
161
+ raise
162
+ else
163
+ STDERR.puts exc
164
+ end
165
+ return false
166
+ end
167
+ end
168
+
169
+ AlterantCLI.start
170
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "alterant"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,6 @@
1
+ require_relative 'alterant/version'
2
+ require_relative 'alterant/utils'
3
+ require_relative 'alterant/loader'
4
+
5
+ module Alterant
6
+ end
@@ -0,0 +1,59 @@
1
+ module Alterant
2
+ class Alterant
3
+ attr_reader :basedir
4
+
5
+ # input is a hash
6
+ # filename is the modifier filename use for the backtrace
7
+ # modifier is the script in string
8
+ def initialize(input:, modifier:, filename:, options: {})
9
+ @modifier = modifier
10
+ @filename = filename
11
+ @input = input
12
+ @basedir = options[:basedir]
13
+ @js_preload = options[:js_preload] || []
14
+ end
15
+
16
+ # timeout is in ms
17
+ # returns a hash
18
+ def execute(timeout: 500, max_memory: 5000000)
19
+ jpath = ::Alterant::Helpers::Jpath.new
20
+
21
+ result = []
22
+ snapshot = MiniRacer::Snapshot.new("$$ = #{@input.to_json};\n" + @js_preload.join("\n")) # this is more efficient but we lose debug info (filename) of helper classes
23
+
24
+ isolate = MiniRacer::Isolate.new(snapshot)
25
+ @input.each_with_index do |input, idx|
26
+ ctx = ::MiniRacer::Context.new(isolate: isolate, timeout: timeout, max_memory: max_memory)
27
+ ctx.eval("$ = #{input.to_json}")
28
+ ctx.eval("$['fetch'] = function(key) { return jpath.fetch(JSON.stringify($), key); }")
29
+ ctx.attach('jpath.fetch', proc{|x, y| jpath.fetch(x, y)})
30
+ ctx.attach('console.log', proc{|x| STDERR.puts("DEBUG: #{x.inspect}") if $debug })
31
+ ctx.attach('console.exception', proc{|x| raise ::Alterant::RuntimeError, x })
32
+ ctx.attach('$$.push', proc{|x| result << x })
33
+ ctx.attach('$.index', proc{ idx })
34
+ ctx.attach('YamlReader', ::Alterant::Classes::YamlReader.new(self, ctx))
35
+ ctx.attach('JsonReader', ::Alterant::Classes::JsonReader.new(self, ctx))
36
+
37
+ ctx.eval(@modifier, filename: @filename)
38
+ pre_convert = ctx.eval("JSON.stringify($)")
39
+ converted = JSON.parse(pre_convert)
40
+ result << converted
41
+
42
+ ctx.dispose
43
+ isolate.idle_notification(100)
44
+ rescue ::MiniRacer::RuntimeError => exc
45
+ if $debug
46
+ raise
47
+ else
48
+ raise ::Alterant::ParseError, "part: #{idx} - #{exc.message}, #{exc.backtrace.first}"
49
+ end
50
+ rescue ::Alterant::AlterantError => exc
51
+ STDERR.puts exc.message.red
52
+ return nil
53
+ end
54
+
55
+ return result
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,20 @@
1
+ module Alterant
2
+ module Classes
3
+ CLASSES = {}
4
+
5
+ def self.LoadClasses
6
+ # ::Alterant::Classes.constants.map(&::Alterant::Classes.method(:const_get)).grep(Class) do |c|
7
+ # name = c.name.split('::').last
8
+ # ::Alterant::Classes::CLASSES[name] = c
9
+ # end
10
+
11
+ # load all JS files in the classes dir and construct a long text
12
+ js_preload = []
13
+ Dir["#{__dir__}/*.js"].each do |f|
14
+ js_preload << File.read(f)
15
+ end
16
+
17
+ return js_preload
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ class Containers {
2
+ constructor(containers) {
3
+ this.containers = containers
4
+ }
5
+
6
+ by_name(name) {
7
+ for (var c in this.containers) {
8
+ var item = this.containers[c]
9
+ if (item.name == name) {
10
+ return item
11
+ }
12
+ }
13
+
14
+ return null
15
+ }
16
+ }
@@ -0,0 +1,51 @@
1
+ class DockerImage {
2
+ constructor(source_image) {
3
+ var full_image = source_image.trim();
4
+ var full_name = '';
5
+ if (!full_image.includes('/')) {
6
+ full_name = "library/" + full_image;
7
+ }
8
+ if (!full_image.includes('/')) {
9
+ full_image = "library/" + full_image;
10
+ }
11
+ if (!full_image.includes(":")) {
12
+ full_image = full_image + ":latest";
13
+ }
14
+ var proto = 'https://';
15
+ if (/^https/.test(full_image)) {
16
+ proto = 'https://';
17
+ full_image = full_image.replace(/https:\/\//, '');
18
+ }
19
+ else if (/^http/.test(full_image)) {
20
+ full_image = full_image.replace(/http:\/\//, '');
21
+ }
22
+ // trim / from front and back
23
+ full_image = full_image.replace(/^\//, '').replace(/\/$/, '');
24
+ var registry
25
+ // figure out registry
26
+ if (/^library\//.test(full_image) || full_image.split('/').length < 3) {
27
+ // its docker io
28
+ registry = 'index.docker.io';
29
+ }
30
+ else {
31
+ registry = full_image.replace(/\/.*/, '');
32
+ }
33
+ // figure out image name
34
+ full_image = full_image.replace(/#{registry}(\/(v|V)(1|2)|)/i, '').replace(/^\//, '').replace(/\/$/, '');
35
+ var image_parts = full_image.split(':');
36
+ var image_name = image_parts[0];
37
+ var image_tag = image_parts[1];
38
+ // recombine for registry
39
+ var registry_url = proto + registry;
40
+ var fqin = registry_url + "/" + full_image;
41
+ this.fqin = fqin;
42
+ this.registry = registry;
43
+ this.registry_url = registry_url;
44
+ this.name = image_name;
45
+ this.tag = image_tag;
46
+ }
47
+
48
+ address() {
49
+ return this.registry + this.name + ":" + this.tag
50
+ }
51
+ }
@@ -0,0 +1,22 @@
1
+ module Alterant
2
+ module Classes
3
+ class JsonReader
4
+ attr_reader :value
5
+
6
+ def call(file)
7
+ if @alter.basedir.nil?
8
+ raise ::Alterant::RuntimeError, 'no basedir set'
9
+ end
10
+
11
+ content = File.read(File.join(@alter.basedir, file))
12
+ return ::JSON.load(content)
13
+ end
14
+
15
+ def initialize(alter, context)
16
+ @context = context
17
+ @alter = alter
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ class Ports {
2
+ constructor(container) {
3
+ this.ports = container.ports
4
+ }
5
+
6
+ containerPorts() {
7
+ var ports = this.ports;
8
+ var port_numbers = new Array();
9
+
10
+ for (var item in ports) {
11
+ port_numbers.push(ports[item]["containerPort"]);
12
+ }
13
+
14
+ return port_numbers;
15
+ }
16
+ }
@@ -0,0 +1,22 @@
1
+ module Alterant
2
+ module Classes
3
+ class YamlReader
4
+ attr_reader :value
5
+
6
+ def call(file)
7
+ if @alter.basedir.nil?
8
+ raise ::Alterant::RuntimeError, 'no basedir set'
9
+ end
10
+
11
+ content = File.read(File.join(@alter.basedir, file))
12
+ return ::YAML.safe_load(content)
13
+ end
14
+
15
+ def initialize(alter, context)
16
+ @context = context
17
+ @alter = alter
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Alterant
2
+ class AlterantError < StandardError; end
3
+ class ParseError < AlterantError; end
4
+ class RuntimeError < AlterantError; end
5
+ end
@@ -0,0 +1,12 @@
1
+ module Alterant
2
+ module Helpers
3
+ class Jpath
4
+ require 'jsonpath'
5
+
6
+ def fetch(context, key)
7
+ return ::JsonPath.on(context, key)
8
+ end
9
+
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'mini_racer'
2
+ require 'yaml'
3
+ require 'json'
4
+ require 'diffy'
5
+
6
+ Dir.glob File.join(__dir__, 'helpers', '**', '*.rb'), &method(:require)
7
+ Dir.glob File.join(__dir__, 'classes', '**', '*.rb'), &method(:require)
8
+
9
+ # Load other Alterant classes.
10
+ Dir.glob File.join(__dir__, '**', '*.rb'), &method(:require)
11
+
@@ -0,0 +1,11 @@
1
+ class String
2
+ def underscore
3
+ word = self.dup
4
+ word.gsub!(/::/, '/')
5
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
6
+ word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
7
+ word.tr!("-", "_")
8
+ word.downcase!
9
+ word
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ module Alterant
2
+ VERSION = '0.0.1'
3
+ COPYRIGHT_MESSAGE = "(c) 2018 Cloud66 Inc."
4
+ APP_NAME = 'Alterant'
5
+ end
metadata ADDED
@@ -0,0 +1,190 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: alterant
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Khash Sajadi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-10-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rerun
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.13'
55
+ - !ruby/object:Gem::Dependency
56
+ name: jsonpath
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.9'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.9'
69
+ - !ruby/object:Gem::Dependency
70
+ name: json
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.4'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: thor
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.20'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.20'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mini_racer
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: colorize
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.8'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.8'
125
+ - !ruby/object:Gem::Dependency
126
+ name: diffy
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.2'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.2'
139
+ description: Alterant is a tool to alter configuration files
140
+ email:
141
+ - khash@cloud66.com
142
+ executables:
143
+ - alterant
144
+ - console
145
+ - setup
146
+ extensions: []
147
+ extra_rdoc_files: []
148
+ files:
149
+ - README.md
150
+ - bin/alterant
151
+ - bin/console
152
+ - bin/setup
153
+ - lib/alterant.rb
154
+ - lib/alterant/alterant.rb
155
+ - lib/alterant/classes/classes.rb
156
+ - lib/alterant/classes/containers.js
157
+ - lib/alterant/classes/docker_image.js
158
+ - lib/alterant/classes/json_reader.rb
159
+ - lib/alterant/classes/ports.js
160
+ - lib/alterant/classes/yaml_reader.rb
161
+ - lib/alterant/error.rb
162
+ - lib/alterant/helpers/jpath.rb
163
+ - lib/alterant/loader.rb
164
+ - lib/alterant/utils.rb
165
+ - lib/alterant/version.rb
166
+ homepage: https://github.com/cloud66/alterant
167
+ licenses:
168
+ - Nonstandard
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: '0'
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubyforge_project:
186
+ rubygems_version: 2.7.7
187
+ signing_key:
188
+ specification_version: 4
189
+ summary: Alterant gem and command line
190
+ test_files: []