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 +4 -4
- data/README.md +12 -11
- data/bin/extensionator +2 -0
- data/lib/extensionator.rb +20 -3
- data/lib/extensionator/cli.rb +21 -9
- data/lib/extensionator/creator.rb +110 -0
- data/lib/extensionator/crx.rb +34 -0
- data/lib/extensionator/error.rb +9 -0
- data/lib/extensionator/manifest.rb +47 -0
- data/lib/extensionator/version.rb +1 -1
- metadata +6 -3
- data/lib/extensionator/impl.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3780dd6f297b930567d87ad3db41b44f43702759
|
4
|
+
data.tar.gz: 9ac68018622dff9e312be10be2cfe97522ec35e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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-
|
6
|
-
[![License][license-
|
7
|
-
[![Gem Downloads][gem-dl-
|
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-
|
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
|
-
[
|
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-
|
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-
|
80
|
-
[gem-dl-
|
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/
|
84
|
-
[travis-image]: http://img.shields.io/travis/
|
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
|
data/bin/extensionator
CHANGED
data/lib/extensionator.rb
CHANGED
@@ -1,7 +1,24 @@
|
|
1
|
-
|
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
|
-
|
5
|
-
|
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
|
data/lib/extensionator/cli.rb
CHANGED
@@ -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: .)"
|
8
|
-
o.string "-i", "--identity", "Location of the pem file to sign with.
|
9
|
-
o.string "-o", "--output", "Location of the output file. (Default: 'extension.crx')"
|
10
|
-
o.string "-e", "--exclude", "Regular expression for filenames to exclude. (Default: \.crx$)"
|
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
|
-
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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,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
|
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
|
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-
|
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/
|
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:
|
data/lib/extensionator/impl.rb
DELETED
@@ -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
|