extensionator 1.0.1 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c159fab576ebc4eee9fafc8e9c67cb3c057acf9f
4
- data.tar.gz: 67917fd1539033b7bfdc84a55e80f7b58b4e71d6
3
+ metadata.gz: 3780dd6f297b930567d87ad3db41b44f43702759
4
+ data.tar.gz: 9ac68018622dff9e312be10be2cfe97522ec35e2
5
5
  SHA512:
6
- metadata.gz: 430508a2a39100f6fe3b34357d6e323bc85e8fe4d08548a967cfa6b94adc7c74c19147c0d787daf43a29ee60101258ba397a3616812297d5a5cbb8aaf13f0bf8
7
- data.tar.gz: 7dc51d6fd2646d24ea6dbc1b8eb1bfcbfb2495cf2ea592898c0e07a0636517fb7c03732fcdc8d692dd459b347af7464d32f7ce1d8b7f609d2cdb0e718dcd02c8
6
+ metadata.gz: 7806db009edfc01d8e56a9aced37d2e287b2b37293464977d49b1e748cfedef0e5da1665bd7f709cd8dffcf91cea4e6234c12b982eadabd1bcbaac8ff2368bfa
7
+ data.tar.gz: 61eb7af4225a22e2397baf1427507ece88a658c630c22d00c6cd5d53bb7ff22e28d7cbdfaba5f5e4bcd40c120661dbfe196de15eccfd69ee82500966f8c47a6c
data/README.md CHANGED
@@ -2,13 +2,12 @@
2
2
 
3
3
  Package Chrome extensions. Inspired by [crxmake][crxmake-url], but with an eye towards simplicity and supportability. Use the CLI or the Ruby API.
4
4
 
5
- [![Gem Version][gem-img]][gem-url]
6
- [![License][license-img]][license]
7
- [![Gem Downloads][gem-dl-img]][gem-url]
5
+ [![Gem Version][gem-image]][gem-url]
6
+ [![MIT License][license-image]][license]
7
+ [![Gem Downloads][gem-dl-image]][gem-url]
8
8
  [![Build Status][travis-image]][travis-url]
9
- [![Code Climate][code-climate-img]][code-climate-url]
9
+ [![Code Climate][code-climate-image]][code-climate-url]
10
10
  [![Dependencies][gemnasium-image]][gemnasium-url]
11
- ![Phasers to stun][phasers-image]
12
11
 
13
12
  ## Install
14
13
 
@@ -70,18 +69,20 @@ Extensionator.create("dir", "key.pem", "output_file.crx", exclude: /\.md$/)
70
69
 
71
70
  Copyright 2015 Zensight. Distributed under the MIT License. See the [LICENSE][] file for more details.
72
71
 
73
- [license-img]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
72
+ ![Phasers to stun][phasers-image]
73
+
74
+ [license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square
74
75
  [license]: LICENSE.md
75
76
 
76
- [code-climate-img]: https://img.shields.io/codeclimate/github/Zensight/extensionator.svg?style=flat-square
77
+ [code-climate-image]: https://img.shields.io/codeclimate/github/Zensight/extensionator.svg?style=flat-square
77
78
  [code-climate-url]: https://codeclimate.com/github/Zensight/extensionator
78
79
 
79
- [gem-img]: https://img.shields.io/gem/v/extensionator.svg?style=flat-square
80
- [gem-dl-img]: https://img.shields.io/gem/dt/extensionator.svg?style=flat-square
80
+ [gem-image]: https://img.shields.io/gem/v/extensionator.svg?style=flat-square
81
+ [gem-dl-image]: https://img.shields.io/gem/dt/extensionator.svg?style=flat-square
81
82
  [gem-url]: https://rubygems.org/gems/extensionator
82
83
 
83
- [travis-url]: http://travis-ci.org/zensight/extensionator
84
- [travis-image]: http://img.shields.io/travis/zensight/extensionator.svg?style=flat-square
84
+ [travis-url]: http://travis-ci.org/Zensight/extensionator
85
+ [travis-image]: http://img.shields.io/travis/Zensight/extensionator.svg?style=flat-square
85
86
 
86
87
  [gemnasium-url]: https://gemnasium.com/zensight/extensionator
87
88
  [gemnasium-image]: https://img.shields.io/gemnasium/Zensight/extensionator.svg?style=flat-square
@@ -2,8 +2,10 @@
2
2
 
3
3
  begin
4
4
  require_relative "../lib/extensionator"
5
+ require_relative "../lib/extensionator/cli"
5
6
  rescue LoadError
6
7
  require "extensionator"
8
+ require "../lib/extensionator/cli"
7
9
  end
8
10
 
9
11
  Extensionator::CLI.start
@@ -1,7 +1,24 @@
1
- Dir["#{__dir__}/extensionator/*.rb"].each{|f| require f}
1
+ require_relative "extensionator/error"
2
+ require_relative "extensionator/manifest"
3
+ require_relative "extensionator/crx"
4
+ require_relative "extensionator/creator"
2
5
 
3
6
  module Extensionator
4
- def self.create(dir, key_file, dest_filename, opts = {exclude: /\.crx$/})
5
- Impl.create(dir, key_file, dest_filename, opts)
7
+
8
+ def self.validate(dir, opts={})
9
+ Creator.new(dir, opts).validate
10
+ end
11
+
12
+ def self.crx(dir, identity_file, dest_filename, opts = {})
13
+ Creator.new(dir, opts.merge(identity: identity_file)).crx(dest_filename)
14
+ end
15
+
16
+ def self.zip(dir, dest_filename, opts= {})
17
+ Creator.new(dir, opts).zip(dest_filename)
18
+ end
19
+
20
+ ##deprecated, reverse compat
21
+ def self.create(dir, identity_file, dest_filename, opts = {})
22
+ crx(dir, identity_file, dest_filename, opts)
6
23
  end
7
24
  end
@@ -1,13 +1,18 @@
1
1
  require "slop"
2
+ require_relative "../extensionator"
2
3
 
3
4
  module Extensionator
4
5
  module CLI
5
6
  def self.start
6
7
  opts = Slop.parse do |o|
7
- o.string "-d", "--directory", "Directory containing the extension. (Default: .)", default: "."
8
- o.string "-i", "--identity", "Location of the pem file to sign with. (Required)"
9
- o.string "-o", "--output", "Location of the output file. (Default: 'extension.crx')", default: "extension.crx"
10
- o.string "-e", "--exclude", "Regular expression for filenames to exclude. (Default: \.crx$)", default: nil
8
+ o.string "-d", "--directory", "Directory containing the extension. (Default: .)"
9
+ o.string "-i", "--identity", "Location of the pem file to sign with."
10
+ o.string "-o", "--output", "Location of the output file. (Default: 'extension.[zip|crx]')"
11
+ o.string "-e", "--exclude", "Regular expression for filenames to exclude. (Default: \.crx$)"
12
+ o.string "-f", "--format", "Type of file to produce, either zip or crx. Defaults to crx"
13
+ o.string "--inject-version", "Inject a version number into the manifest file."
14
+ o.bool "--inject-key", "Inject a key parameter into the manifest file."
15
+ #o.string "--skip-validation", "Don't try to validate this extension."
11
16
  o.on "-v", "--version", "Extensionator version info." do
12
17
  puts Extensionator::VERSION
13
18
  exit
@@ -18,12 +23,19 @@ module Extensionator
18
23
  end
19
24
  end
20
25
 
21
- abort("No identity file specified; use --identity or -i switch.") unless opts[:identity]
26
+ creator = Creator.new(opts[:directory], opts)
27
+ if opts[:format] == "zip"
28
+ creator.zip(opts[:output] || "output.zip")
29
+ else
30
+ creator.crx(opts[:output] || "output.crx")
31
+ end
32
+
33
+ end
22
34
 
23
- Extensionator.create(opts[:directory],
24
- opts[:identity],
25
- opts[:output],
26
- exclude: opts[:exclude] ? Regexp.new(opts[:exclude]) : nil)
35
+ def self.fixup_options(in_opts)
36
+ opts = in_opts.clone
37
+ opts[:output] = output.to_sym if opts[:output]
38
+ opts[:exclude] = Regexp.new(opts[:exclude]) if opts[:exclude]
27
39
  end
28
40
  end
29
41
  end
@@ -0,0 +1,110 @@
1
+ require "find"
2
+ require "openssl"
3
+ require "pathname"
4
+ require "zip"
5
+
6
+ module Extensionator
7
+ class Creator
8
+
9
+ def initialize(dir, options)
10
+ @dir = dir
11
+ @opts = {
12
+ inject_key: false,
13
+ inject_version: nil,
14
+ skip_validation: false
15
+ }.merge(options)
16
+ end
17
+
18
+ def zip(destination)
19
+ with_files("zip") do |zip_str|
20
+ File.open(destination, "wb"){|f| f.write zip_str}
21
+ end
22
+ end
23
+
24
+ def crx(destination)
25
+ with_files("crx") do |zip_str|
26
+ CRX.create(destination, zip_str, private_key)
27
+ end
28
+ end
29
+
30
+ def validate
31
+ manifest.validate(dir_files)
32
+ end
33
+
34
+ private
35
+
36
+ def with_files(default_extension)
37
+ files = process_directory
38
+ yield zip_up(files)
39
+ end
40
+
41
+ def process_directory
42
+ new_paths = dir_files
43
+
44
+ manifest.validate(new_paths) unless @opts[:skip_validation]
45
+ manifest.inject_key(private_key) if @opts[:inject_key]
46
+ manifest.inject_version(@opts[:inject_version]) if @opts[:inject_version]
47
+
48
+ new_paths << manifest.write if manifest_updated?
49
+
50
+ new_paths
51
+ end
52
+
53
+ def zip_up(paths)
54
+ Zip.continue_on_exists_proc = true
55
+ Zip::File.add_buffer do |zip|
56
+ paths.each do |path_combo|
57
+ path_in_zip, file = path_combo
58
+ if File.directory?(file)
59
+ zip.mkdir(path_in_zip, file)
60
+ else
61
+ zip.add(path_in_zip, file)
62
+ end
63
+ end
64
+ end.string
65
+ end
66
+
67
+ def dir_files
68
+ unless @dir_files
69
+ @dir_files = []
70
+ Find.find(@dir) do |path|
71
+ case
72
+ when path == @dir
73
+ next
74
+ when (@opts[:exclude] && path =~ @opts[:exclude])
75
+ Find.prune
76
+ else
77
+ @dir_files << [relative_path(@dir, path), path]
78
+ end
79
+ end
80
+ end
81
+ @dir_files
82
+ end
83
+
84
+ def manifest
85
+ @manifest ||= Manifest.new(@dir)
86
+ end
87
+
88
+ def manifest_updated?
89
+ @manifest && @manifest.updated?
90
+ end
91
+
92
+ def private_key
93
+ @private_key ||=
94
+ if @opts[:identity]
95
+ begin
96
+ OpenSSL::PKey::RSA.new(File.read(@opts[:identity]))
97
+ rescue Error => e
98
+ raise KeyError.new("Couldn't read key file #{@opts[:identity]}: #{e.message}")
99
+ end
100
+ else
101
+ raise ArgumentError.new("You need to specify an identity file for this operation.")
102
+ end
103
+ end
104
+
105
+ def relative_path(base, target)
106
+ from, to = [base, target].map(&Pathname.method(:new))
107
+ to.relative_path_from(from).to_s
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,34 @@
1
+ require "digest/sha1"
2
+
3
+ module Extensionator
4
+ module CRX
5
+
6
+ def self.create(destination, zip_str, priv_key)
7
+ sig_bytes = sign(zip_str, priv_key)
8
+ write_crx(destination, zip_str, sig_bytes, priv_key)
9
+ end
10
+
11
+ def self.sign(zip_str, priv_key)
12
+ priv_key.sign(OpenSSL::Digest::SHA1.new, zip_str)
13
+ end
14
+
15
+ def self.write_crx(destination, zip_str, sig_bytes, priv_key)
16
+ pub_key_bytes = priv_key.public_key.to_der
17
+
18
+ #See https://developer.chrome.com/extensions/crx for the format description
19
+ File.open(destination, "wb") do |file|
20
+ file << "Cr24"
21
+ file << format_size(2)
22
+ file << format_size(pub_key_bytes.size)
23
+ file << format_size(sig_bytes.size)
24
+ file << pub_key_bytes
25
+ file << sig_bytes
26
+ file << zip_str
27
+ end
28
+ end
29
+
30
+ def self.format_size(num)
31
+ [num].pack("V")
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ module Extensionator
2
+ class Error < StandardError
3
+
4
+ ArgumentError = Class.new(self)
5
+ KeyError = Class.new(self)
6
+ ValidationError = Class.new(self)
7
+ end
8
+ end
9
+
@@ -0,0 +1,47 @@
1
+ require "json"
2
+ require "base64"
3
+
4
+ module Extensionator
5
+ class Manifest
6
+
7
+ def initialize(dir)
8
+ manifest_file = "#{dir}/manifest.json"
9
+
10
+ raise "Can't find manifest file" unless File.exists? manifest_file
11
+
12
+ begin
13
+ @manifest = JSON.parse(File.read(manifest_file))
14
+ rescue Errno::ENOTENT => e
15
+ raise ValidationError.new("Can't read manifest file: #{e.message}")
16
+ rescue JSON::ParserError => e
17
+ raise ValidationError.new("Can't parse manifest file: #{e.message}")
18
+ end
19
+ end
20
+
21
+ def validate(paths)
22
+ #todo: actual validations!
23
+ true
24
+ end
25
+
26
+ def inject_key(priv_key)
27
+ #Chrome appears to support some shorter encoding of the pub key, but I'm not sure what it is
28
+ @manifest["key"] = Base64.encode64(priv_key.public_key.to_der).gsub("\n", "")
29
+ @updated = true
30
+ end
31
+
32
+ def inject_version(version)
33
+ @manifest["version"] = version
34
+ @updated = true
35
+ end
36
+
37
+ def updated?
38
+ @updated || false
39
+ end
40
+
41
+ def write
42
+ file = Tempfile.new("crx-manifest.json")
43
+ File.open(file, "w"){|f| f.write(JSON.dump(@manifest))}
44
+ ["manifest.json", file.path]
45
+ end
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module Extensionator
2
- VERSION = "1.0.1"
2
+ VERSION = "1.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extensionator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Isaac Cambron
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-23 00:00:00.000000000 Z
11
+ date: 2015-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -65,7 +65,10 @@ files:
65
65
  - extensionator.gemspec
66
66
  - lib/extensionator.rb
67
67
  - lib/extensionator/cli.rb
68
- - lib/extensionator/impl.rb
68
+ - lib/extensionator/creator.rb
69
+ - lib/extensionator/crx.rb
70
+ - lib/extensionator/error.rb
71
+ - lib/extensionator/manifest.rb
69
72
  - lib/extensionator/version.rb
70
73
  homepage: http://github.com/zensight/extensionator/
71
74
  licenses:
@@ -1,65 +0,0 @@
1
- require "digest/sha1"
2
- require "find"
3
- require "openssl"
4
- require "pathname"
5
- require "zip"
6
-
7
- module Extensionator
8
- module Impl
9
- def self.create(dir, key_file, dest_filename, opts)
10
- priv_key = read_key(key_file)
11
- zip_str = zip(dir, opts)
12
- sig_bytes = sign(zip_str, priv_key)
13
- write(zip_str, sig_bytes, priv_key, dest_filename)
14
- end
15
-
16
- def self.read_key(key_file)
17
- OpenSSL::PKey::RSA.new(File.read(key_file))
18
- end
19
-
20
- def self.zip(dir, opts = {})
21
- Zip::File.add_buffer do |zip|
22
- Find.find(dir) do |path|
23
- case
24
- when path == dir
25
- next
26
- when (opts[:exclude] && path =~ opts[:exclude])
27
- Find.prune
28
- when File.directory?(path)
29
- zip.mkdir(relative_path(dir, path))
30
- else
31
- zip.add(relative_path(dir, path), path)
32
- end
33
- end
34
- end.string
35
- end
36
-
37
- def self.sign(zip_str, priv_key)
38
- priv_key.sign(OpenSSL::Digest::SHA1.new, zip_str)
39
- end
40
-
41
- def self.write(zip_str, sig_bytes, priv_key, dest_filename)
42
- pub_key_bytes = priv_key.public_key.to_der
43
-
44
- #See https://developer.chrome.com/extensions/crx for the format description
45
- File.open(dest_filename, "wb") do |file|
46
- file << "Cr24"
47
- file << format_size(2)
48
- file << format_size(pub_key_bytes.size)
49
- file << format_size(sig_bytes.size)
50
- file << pub_key_bytes
51
- file << sig_bytes
52
- file << zip_str
53
- end
54
- end
55
-
56
- def self.relative_path(base, target)
57
- from, to = [base, target].map(&Pathname.method(:new))
58
- to.relative_path_from(from).to_s
59
- end
60
-
61
- def self.format_size(num)
62
- [num].pack("V")
63
- end
64
- end
65
- end