pullcrusher 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.
- data/.gitignore +20 -0
- data/.travis.yml +9 -0
- data/CHANGELOG +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +105 -0
- data/README.rdoc +19 -0
- data/Rakefile +36 -0
- data/bin/pullcrusher +80 -0
- data/features/pullcrusher.feature +15 -0
- data/features/step_definitions/pullcrusher_steps.rb +1 -0
- data/features/support/env.rb +16 -0
- data/lib/auth/github.rb +55 -0
- data/lib/pullcrusher/version.rb +3 -0
- data/lib/pullcrusher.rb +187 -0
- data/pullcrusher.gemspec +29 -0
- data/test/test_git.rb +15 -0
- data/test/test_github.rb +12 -0
- metadata +231 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/CHANGELOG
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Matthew Rothenberg
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Pullcrusher
|
2
|
+
|
3
|
+
There are lots of great utilities out there to losslessly optimize
|
4
|
+
images, but most people forget to run them. Pullcrusher makes it easy
|
5
|
+
to optimize the images of any GitHub repository, and contribute the
|
6
|
+
optimizations back to the maintainer as a pull request.
|
7
|
+
|
8
|
+
We stand on the shoulders of giants! Thanks to: image_optim, ruby-git,
|
9
|
+
and octokit.
|
10
|
+
|
11
|
+
## Prerequisites
|
12
|
+
|
13
|
+
Get dependencies for image optimization, on MacOSX:
|
14
|
+
|
15
|
+
brew install advancecomp gifsicle jpegoptim jpeg optipng pngcrush
|
16
|
+
|
17
|
+
Debian/Ubuntu:
|
18
|
+
|
19
|
+
sudo apt-get install -y advancecomp gifsicle jpegoptim libjpeg-progs optipng pngcrush
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Install it via:
|
24
|
+
|
25
|
+
$ gem install pullcrusher
|
26
|
+
|
27
|
+
On some setups (default MacOSX) that may need a `sudo` first.
|
28
|
+
|
29
|
+
## Usage
|
30
|
+
|
31
|
+
Simply do a `pullcrush [repo_name]` using the github style short-name
|
32
|
+
for a repository, e.g. `mroth/pullcrusher`.
|
33
|
+
|
34
|
+
Pullcrusher will locate all images, compress them, and then ask you if
|
35
|
+
you want it to automatically fork on github and submit a pull request.
|
36
|
+
Simply type "Y" if you like and you are done!
|
37
|
+
|
38
|
+
### Sample output
|
39
|
+
|
40
|
+
% pullcrush waferbaby/usesthis
|
41
|
+
*** Asking Github to find us the URI for waferbaby/usesthis
|
42
|
+
*** Cloning git@github.com:waferbaby/usesthis.git to local filesystem
|
43
|
+
*** Finding and processing any candidate files
|
44
|
+
public/images/interviews/chris.ilias.knives.jpg
|
45
|
+
34415 -> 34204 (211 saved)
|
46
|
+
public/images/interviews/chris.ilias.spoons.jpg
|
47
|
+
40466 -> 40126 (340 saved)
|
48
|
+
public/images/interviews/julian.bleecker.cameras.jpg
|
49
|
+
135909 -> 133247 (2662 saved)
|
50
|
+
public/images/interviews/khoi.vinh.home.jpg
|
51
|
+
|
52
|
+
[snip]
|
53
|
+
|
54
|
+
public/images/portraits/zed.shaw.jpg
|
55
|
+
143185 -> 141055 (2130 saved)
|
56
|
+
*** 286 files processed, 136 successfully optimized for total savings of 400438 bytes.
|
57
|
+
Do you want to automatically fork and pull request? [y/N] y
|
58
|
+
*** Git branching and commiting all changed files
|
59
|
+
*** Forking the original repo on github
|
60
|
+
*** Pushing changes to your forked copy of the repo
|
61
|
+
*** Creating a pull request...
|
62
|
+
*** Done! Pull request is at https://github.com/waferbaby/usesthis/pull/5
|
63
|
+
|
64
|
+
## SSH setup and caveats
|
65
|
+
|
66
|
+
### SSH
|
67
|
+
We use standard git ssh to push changes to your github account. You'll
|
68
|
+
want to make sure you have [SSH keys properly setup](https://help.github.com/articles/generating-ssh-keys).
|
69
|
+
|
70
|
+
(Does anyone actually prefer HTTPS with credential caching as github thinks? lmk if
|
71
|
+
this is something we need to add to pullcrusher).
|
72
|
+
|
73
|
+
### GitHub Credentials
|
74
|
+
The first time you run pullcrusher, it will ask for your GitHub username
|
75
|
+
and password to obtain a oAuth token (or possibly just your password, if
|
76
|
+
it can locate your Github username in your git configuration). This is
|
77
|
+
totally cool and awesome, but if you don't want that token on your hard
|
78
|
+
drive for any reason, its located at `~/.config/pullcrusher.yml`.
|
79
|
+
|
80
|
+
We use this so we can use the Github API to handle forking
|
81
|
+
repositories.
|
82
|
+
|
83
|
+
## Contributing
|
84
|
+
|
85
|
+
1. Fork it
|
86
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
87
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
88
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
89
|
+
5. Create new Pull Request
|
90
|
+
|
91
|
+
I'm trying to learn more about using tests so bonus points if you
|
92
|
+
include a test for your new functionality or fix. (Or, if you just want
|
93
|
+
to write some tests for existing functionality, that's awesome too!)
|
94
|
+
|
95
|
+
## TODO
|
96
|
+
Things that still need to be done before v0.0.1 of this gem gets
|
97
|
+
released.
|
98
|
+
|
99
|
+
* Minimal error handling (somewhat taken care of since Methadone handles exceptions okay) √
|
100
|
+
* Handle your own repos not just others (cant do a pull request
|
101
|
+
really? I guess just leave it in branch and ask person to do
|
102
|
+
manually?) (PUNTING ON THIS TILL NEXT VERSION)
|
103
|
+
* Some very very basic tests, and setup travis-ci to ensure we run on
|
104
|
+
ruby 1.8.7 as well. √
|
105
|
+
|
data/README.rdoc
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
= pullcrusher - DESCRIBE YOUR GEM
|
2
|
+
|
3
|
+
Author:: YOUR NAME (YOUR EMAIL)
|
4
|
+
Copyright:: Copyright (c) 2012 YOUR NAME
|
5
|
+
|
6
|
+
|
7
|
+
DESCRIBE YOUR GEM HERE
|
8
|
+
|
9
|
+
== Links
|
10
|
+
|
11
|
+
* {Source on Github}[LINK TO GITHUB]
|
12
|
+
* RDoc[LINK TO RDOC.INFO]
|
13
|
+
|
14
|
+
== Install
|
15
|
+
|
16
|
+
== Examples
|
17
|
+
|
18
|
+
== Contributing
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'rake/clean'
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
|
6
|
+
require 'cucumber'
|
7
|
+
require 'cucumber/rake/task'
|
8
|
+
gem 'rdoc' # we need the installed RDoc gem, not the system one
|
9
|
+
require 'rdoc/task'
|
10
|
+
|
11
|
+
include Rake::DSL
|
12
|
+
|
13
|
+
Bundler::GemHelper.install_tasks
|
14
|
+
|
15
|
+
|
16
|
+
Rake::TestTask.new do |t|
|
17
|
+
t.pattern = 'test/test_*.rb'
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
CUKE_RESULTS = 'results.html'
|
22
|
+
CLEAN << CUKE_RESULTS
|
23
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
24
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty --no-source -x"
|
25
|
+
t.fork = false
|
26
|
+
end
|
27
|
+
|
28
|
+
Rake::RDocTask.new do |rd|
|
29
|
+
|
30
|
+
rd.main = "README.rdoc"
|
31
|
+
|
32
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => [:test,:features]
|
36
|
+
|
data/bin/pullcrusher
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'methadone'
|
5
|
+
require 'pullcrusher'
|
6
|
+
|
7
|
+
class App
|
8
|
+
include Methadone::Main
|
9
|
+
include Methadone::CLILogging
|
10
|
+
|
11
|
+
main do |repo_name| # Add args you want: |like,so|
|
12
|
+
# your program code here
|
13
|
+
# You can access CLI options via
|
14
|
+
# the options Hash
|
15
|
+
|
16
|
+
#
|
17
|
+
# create an authorized github client, handles prompting if needed
|
18
|
+
#
|
19
|
+
authclient = GitHubAuth.client
|
20
|
+
|
21
|
+
#
|
22
|
+
# instantiate a Pullcrusher object with that authclient
|
23
|
+
#
|
24
|
+
pc = Pullcrusher::Pullcrusher.new(authclient)
|
25
|
+
|
26
|
+
#
|
27
|
+
# do it!
|
28
|
+
#
|
29
|
+
# repo_name = @parse_repo_from_args
|
30
|
+
results, fs_repo = pc.process_repo( repo_name )
|
31
|
+
exit if (results.filez_optimized < 1)
|
32
|
+
|
33
|
+
input = ask "Do you want to automatically fork and pull request? [y/N] "
|
34
|
+
if input.match(/[yY]/)
|
35
|
+
pc.fork_and_pull(fs_repo, repo_name, results)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# supplemental methods here
|
40
|
+
def parse_repo_from_args
|
41
|
+
#TODO: THIS IS ALL DEPRECATED, MOVE COMMENTS OUT
|
42
|
+
format_desc = "Repo name should be in github shorthand style, e.g. 'mroth/pullcrusher'"
|
43
|
+
if ARGV.count != 1
|
44
|
+
puts "Usage: pullcrush [github_repo]"
|
45
|
+
puts format_desc
|
46
|
+
exit 0
|
47
|
+
end
|
48
|
+
|
49
|
+
#TODO: test for proper format
|
50
|
+
|
51
|
+
ARGV[0]
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# Declare command-line interface here
|
56
|
+
|
57
|
+
description "Crushes images for great justice!\nRepo name should be in github shorthand style, e.g. 'mroth/pullcrusher'"
|
58
|
+
#
|
59
|
+
# Accept flags via:
|
60
|
+
# on("--flag VAL","Some flag")
|
61
|
+
# options[flag] will contain VAL
|
62
|
+
#
|
63
|
+
# Specify switches via:
|
64
|
+
# on("--[no-]switch","Some switch")
|
65
|
+
#
|
66
|
+
# Or, just call OptionParser methods on opts
|
67
|
+
#
|
68
|
+
# Require an argument
|
69
|
+
# arg :some_arg
|
70
|
+
#
|
71
|
+
# # Make an argument optional
|
72
|
+
# arg :optional_arg, :optional
|
73
|
+
arg :repo_name
|
74
|
+
|
75
|
+
version Pullcrusher::VERSION
|
76
|
+
|
77
|
+
use_log_level_option
|
78
|
+
|
79
|
+
go!
|
80
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
Feature: My bootstrapped app kinda works
|
2
|
+
In order to get going on coding my awesome app
|
3
|
+
I want to have aruba and cucumber setup
|
4
|
+
So I don't have to do it myself
|
5
|
+
|
6
|
+
Scenario: App just runs
|
7
|
+
When I get help for "pullcrusher"
|
8
|
+
Then the exit status should be 0
|
9
|
+
And the banner should be present
|
10
|
+
And the banner should document that this app takes options
|
11
|
+
And the following options should be documented:
|
12
|
+
|--version|
|
13
|
+
And the banner should document that this app's arguments are:
|
14
|
+
|repo_name|which is required|
|
15
|
+
And there should be a one line summary of what the app does
|
@@ -0,0 +1 @@
|
|
1
|
+
# Put your step definitions here
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
require 'methadone/cucumber'
|
3
|
+
|
4
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
5
|
+
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
6
|
+
|
7
|
+
Before do
|
8
|
+
# Using "announce" causes massive warnings on 1.9.2
|
9
|
+
@puts = true
|
10
|
+
@original_rubylib = ENV['RUBYLIB']
|
11
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
12
|
+
end
|
13
|
+
|
14
|
+
After do
|
15
|
+
ENV['RUBYLIB'] = @original_rubylib
|
16
|
+
end
|
data/lib/auth/github.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#
|
2
|
+
# stating point for this was this gist: https://gist.github.com/2771702
|
3
|
+
#
|
4
|
+
|
5
|
+
# Use Octokit 1.3.0 or higher for great justice (and Authorizations)
|
6
|
+
#require 'bundler/setup' #this was causing trouble with VERSION
|
7
|
+
require 'octokit'
|
8
|
+
require 'yaml'
|
9
|
+
require 'highline/import'
|
10
|
+
|
11
|
+
class GitHubAuth
|
12
|
+
# Change NOTE, SCOPES and CREDENTIALS to match your app's needs.
|
13
|
+
NOTE = "Pullcrusher!"
|
14
|
+
SCOPES = ["user","repo"]
|
15
|
+
CREDENTIALS = File.join("#{ENV['HOME']}", ".config", "pullcrusher.yml")
|
16
|
+
|
17
|
+
def self.client
|
18
|
+
new.client
|
19
|
+
end
|
20
|
+
|
21
|
+
def client
|
22
|
+
@client ||= lambda do
|
23
|
+
unless File.exist?(CREDENTIALS)
|
24
|
+
authenticate
|
25
|
+
end
|
26
|
+
Octokit::Client.new(YAML.load_file(CREDENTIALS))
|
27
|
+
end.call
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def authenticate
|
32
|
+
login = password = token = ""
|
33
|
+
login = `git config github.user`.chomp
|
34
|
+
login = ask_login if login.empty?
|
35
|
+
password = ask_password
|
36
|
+
auth_client = Octokit::Client.new(:login => login, :password => password)
|
37
|
+
auth = auth_client.authorizations.detect { |a| a.note == NOTE }
|
38
|
+
unless auth
|
39
|
+
auth = auth_client.create_authorization(:scopes => SCOPES, :note => NOTE)
|
40
|
+
end
|
41
|
+
File.open(CREDENTIALS, 'w') do |f|
|
42
|
+
f.puts({ :login => login, :oauth_token => auth.token }.to_yaml)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def ask_login
|
47
|
+
ask("Enter you GitHub username: ")
|
48
|
+
end
|
49
|
+
|
50
|
+
def ask_password
|
51
|
+
ask("Enter your Github password (this will NOT be stored): ") { |q| q.echo = '*' }
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
data/lib/pullcrusher.rb
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
require "pullcrusher/version"
|
2
|
+
require "auth/github"
|
3
|
+
|
4
|
+
require "git"
|
5
|
+
require "image_optim"
|
6
|
+
require "octokit"
|
7
|
+
require "virtus"
|
8
|
+
require "methadone"
|
9
|
+
|
10
|
+
module Pullcrusher
|
11
|
+
|
12
|
+
class Pullcrusher
|
13
|
+
include Methadone::CLILogging
|
14
|
+
|
15
|
+
attr_accessor :ok_client, :github_username
|
16
|
+
|
17
|
+
# Class constructor
|
18
|
+
#
|
19
|
+
# authclient - An initialized Octokit client with authorization.
|
20
|
+
# If none is provided, a public client will be used (which cannot perform many actions)
|
21
|
+
#
|
22
|
+
def initialize(authclient=nil)
|
23
|
+
unless authclient.nil?
|
24
|
+
@ok_client = authclient
|
25
|
+
@github_username = authclient.login
|
26
|
+
else
|
27
|
+
@ok_client = Octokit::Client.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class Results
|
32
|
+
include Virtus
|
33
|
+
attribute :bytes_before, Integer
|
34
|
+
attribute :bytes_saved, Integer
|
35
|
+
attribute :filez_candidates, Integer
|
36
|
+
attribute :filez_optimized, Integer
|
37
|
+
end
|
38
|
+
|
39
|
+
# Looks up a github style "username/repo" repository name
|
40
|
+
#
|
41
|
+
# repo_name - the github style repository name
|
42
|
+
#
|
43
|
+
# Returns a octokit repo object
|
44
|
+
def repo_from_shortname(repo_name)
|
45
|
+
@ok_client.repo(repo_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Clones a remote git repository to the local filesystem
|
49
|
+
#
|
50
|
+
# uri - the URI of the remote git repository
|
51
|
+
#
|
52
|
+
# Returns a Git object representing the repository in the local filesystem.
|
53
|
+
def clone_repo(repo_name)
|
54
|
+
FileUtils.mkdir_p('/tmp/pullcrusher')
|
55
|
+
dirname = repo_name.gsub('/','-')
|
56
|
+
target = "/tmp/pullcrusher/#{dirname}"
|
57
|
+
uri = repo_from_shortname(repo_name).clone_url
|
58
|
+
|
59
|
+
#check if tmp directory already exists, if so, clobber it
|
60
|
+
FileUtils.remove_dir(target) if File.directory?(target)
|
61
|
+
|
62
|
+
g = Git.clone(uri,target)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Given a directory, identify any files that are possible candidates for optimization.
|
66
|
+
# This is naive, and only looks based on filename for now.
|
67
|
+
#
|
68
|
+
# dir - the directory to recursively search for candidate files
|
69
|
+
#
|
70
|
+
# Returns an array of file paths.
|
71
|
+
def get_candidate_files(dir)
|
72
|
+
Dir.chdir(dir)
|
73
|
+
Dir.glob("**/*.{jpg,png,gif}")
|
74
|
+
end
|
75
|
+
|
76
|
+
# Convenience method to take a repo by string name, and do all the processing.
|
77
|
+
#
|
78
|
+
# repo_name - the github style repository name
|
79
|
+
#
|
80
|
+
# Returns an array containing both a Results hash, and the fs_repo reference that was cloned
|
81
|
+
def process_repo(repo_name)
|
82
|
+
info "*** Asking Github to find us the URI for #{repo_name}"
|
83
|
+
orig_repo = repo_from_shortname(repo_name)
|
84
|
+
|
85
|
+
info "*** Cloning #{orig_repo.ssh_url} to local filesystem"
|
86
|
+
fs_repo = clone_repo(repo_name)
|
87
|
+
|
88
|
+
info "*** Finding and processing any candidate files"
|
89
|
+
results = process_files_from_repo( fs_repo )
|
90
|
+
|
91
|
+
info "*** #{results.filez_candidates} files processed, #{results.filez_optimized} successfully optimized for total savings of #{results.bytes_saved} bytes."
|
92
|
+
return results, fs_repo
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
# Convenience method to take a Git repository object, identify and process
|
97
|
+
#
|
98
|
+
# fs_repo - ruby-git object for the repo on filesystem
|
99
|
+
#
|
100
|
+
# Returns a Results object
|
101
|
+
def process_files_from_repo(fs_repo)
|
102
|
+
process_files( get_candidate_files(fs_repo.dir.to_s) )
|
103
|
+
end
|
104
|
+
|
105
|
+
# Given a list of files, process with image_optim to optimized file size.
|
106
|
+
# Files are modified in place in file system.
|
107
|
+
#
|
108
|
+
# filez - the list of filez to process
|
109
|
+
#
|
110
|
+
# Returns a Results object with number of files optimized and bytes saved.
|
111
|
+
def process_files(filez)
|
112
|
+
#TODO: reject any files larger than MAXSIZE
|
113
|
+
# perhaps use Enumerable reject for this!
|
114
|
+
|
115
|
+
#
|
116
|
+
# create an ImageOptim instance
|
117
|
+
#
|
118
|
+
io = ImageOptim.new(:pngout => false) #, :threads => THREADS)
|
119
|
+
|
120
|
+
filez_optimized = 0
|
121
|
+
bytes_saved = 0
|
122
|
+
bytes_total = 0
|
123
|
+
|
124
|
+
filez.each do |f|
|
125
|
+
size_before = File.size(f)
|
126
|
+
info "\t#{f}"
|
127
|
+
if (io.optimize_image!(f)) #returns true when an optimization has been made
|
128
|
+
filez_optimized += 1
|
129
|
+
size_after = File.size(f)
|
130
|
+
size_diff = size_before - size_after
|
131
|
+
bytes_total += size_before
|
132
|
+
bytes_saved += size_diff
|
133
|
+
info "\t\t#{size_before} -> #{size_after} (#{size_diff} saved)"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
Results.new(:bytes_saved => bytes_saved, :filez_optimized => filez_optimized, :filez_candidates => filez.count)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Fork and pull baby!
|
141
|
+
#
|
142
|
+
# fs_repo - git handle to the filesystem repo
|
143
|
+
# repo_name - github style name as a string
|
144
|
+
# results - a Pullcrusher::Results object from the optimization
|
145
|
+
#
|
146
|
+
# Returns nothing?
|
147
|
+
def fork_and_pull(fs_repo, repo_name, results)
|
148
|
+
#TODO: set name and email for git commits?!
|
149
|
+
#nope handle this in bot insteat
|
150
|
+
#fs_repo.config('user.name', 'PullCrusher Bot')
|
151
|
+
#fs_repo.config('user.email','mrothenberg+pullcrusher@gmail.com')
|
152
|
+
#fs_repo.config('credential.git@github.com.username', 'PullcrusherBot')
|
153
|
+
|
154
|
+
|
155
|
+
#DONE: commit changed files
|
156
|
+
info "*** Git branching and commiting all changed files"
|
157
|
+
fs_repo.branch('pullcrushed').checkout
|
158
|
+
fs_repo.add('.')
|
159
|
+
fs_repo.commit('Optimized image files via pullcrusher')
|
160
|
+
|
161
|
+
#DONE: fork original repo (via octokit)
|
162
|
+
info "*** Forking the original repo on github"
|
163
|
+
fork = @ok_client.fork(repo_name)
|
164
|
+
|
165
|
+
#DONE: add forked repo as a new remote to git repo (or just change default?)
|
166
|
+
fs_repo.add_remote('myfork', fork.ssh_url)
|
167
|
+
|
168
|
+
#DONE: push new commits to GH remote
|
169
|
+
info "*** Pushing changes to your forked copy of the repo"
|
170
|
+
fs_repo.push( fs_repo.remote('myfork'), 'pullcrushed' )
|
171
|
+
|
172
|
+
info "*** Creating a pull request..."
|
173
|
+
#def create_pull_request(repo, base, head, title, body, options={})
|
174
|
+
pr = @ok_client.create_pull_request(
|
175
|
+
repo_name,
|
176
|
+
"master", #BASE
|
177
|
+
"#{@github_username}:pullcrushed", #HEAD
|
178
|
+
"Optimized image files via pullcrusher",
|
179
|
+
"Hi there! I've used [pullcrusher](http://github.com/mroth/pullcrusher) to optimize images for this this repository losslessly.\n\n
|
180
|
+
#{results.filez_optimized} files were optimized for a total savings of #{results.bytes_saved} bytes."
|
181
|
+
)
|
182
|
+
info "*** Done! Pull request is at #{pr.html_url}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
data/pullcrusher.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/pullcrusher/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Matthew Rothenberg"]
|
6
|
+
gem.email = ["mrothenberg@gmail.com"]
|
7
|
+
gem.description = %q{Optimize all images in a GitHub repository, then easily send a pull request with the changes.}
|
8
|
+
gem.summary = %q{Optimize all images in a GitHub repository, then easily send a pull request with the changes.}
|
9
|
+
gem.homepage = "http://github.com/mroth/pullcrusher"
|
10
|
+
|
11
|
+
gem.add_development_dependency('rdoc')
|
12
|
+
gem.add_development_dependency('aruba')
|
13
|
+
gem.add_development_dependency('rake','~> 0.9.2')
|
14
|
+
gem.add_dependency('methadone', '~>1.2.1')
|
15
|
+
|
16
|
+
gem.add_runtime_dependency "git"
|
17
|
+
gem.add_runtime_dependency "octokit", ">= 1.3.0" #1.3.0 added authorizations
|
18
|
+
gem.add_runtime_dependency "virtus"
|
19
|
+
gem.add_runtime_dependency "image_optim"
|
20
|
+
gem.add_runtime_dependency "highline"
|
21
|
+
gem.add_runtime_dependency "json" #needed for ruby 1.8 #TODO: figure out how to scope to platform?
|
22
|
+
|
23
|
+
gem.files = `git ls-files`.split($\)
|
24
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
25
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
26
|
+
gem.name = "pullcrusher"
|
27
|
+
gem.require_paths = ["lib"]
|
28
|
+
gem.version = Pullcrusher::VERSION
|
29
|
+
end
|
data/test/test_git.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'pullcrusher'
|
3
|
+
|
4
|
+
class TestPullcrusherGitIntegration < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_can_clone_repo
|
7
|
+
pc = Pullcrusher::Pullcrusher.new
|
8
|
+
|
9
|
+
assert_nothing_raised do
|
10
|
+
pc.clone_repo('PullcrusherBot/pullcrusher_test_repo')
|
11
|
+
end
|
12
|
+
assert File.directory?("/tmp/pullcrusher/PullcrusherBot-pullcrusher_test_repo/.git"), "Cloned repo exists on filesystem"
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
data/test/test_github.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'pullcrusher'
|
3
|
+
|
4
|
+
class TestPullcrusherGithubIntegration < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_github_lookup
|
7
|
+
pc = Pullcrusher::Pullcrusher.new #unauthed client
|
8
|
+
|
9
|
+
assert_equal pc.repo_from_shortname('mroth/pullcrusher').clone_url, 'https://github.com/mroth/pullcrusher.git'
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
metadata
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pullcrusher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matthew Rothenberg
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-06-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rdoc
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: aruba
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rake
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.9.2
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.9.2
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: methadone
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.2.1
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 1.2.1
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: git
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: octokit
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 1.3.0
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 1.3.0
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: virtus
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: image_optim
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: highline
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :runtime
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: json
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :runtime
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
description: Optimize all images in a GitHub repository, then easily send a pull request
|
175
|
+
with the changes.
|
176
|
+
email:
|
177
|
+
- mrothenberg@gmail.com
|
178
|
+
executables:
|
179
|
+
- pullcrusher
|
180
|
+
extensions: []
|
181
|
+
extra_rdoc_files: []
|
182
|
+
files:
|
183
|
+
- .gitignore
|
184
|
+
- .travis.yml
|
185
|
+
- CHANGELOG
|
186
|
+
- Gemfile
|
187
|
+
- LICENSE
|
188
|
+
- README.md
|
189
|
+
- README.rdoc
|
190
|
+
- Rakefile
|
191
|
+
- bin/pullcrusher
|
192
|
+
- features/pullcrusher.feature
|
193
|
+
- features/step_definitions/pullcrusher_steps.rb
|
194
|
+
- features/support/env.rb
|
195
|
+
- lib/auth/github.rb
|
196
|
+
- lib/pullcrusher.rb
|
197
|
+
- lib/pullcrusher/version.rb
|
198
|
+
- pullcrusher.gemspec
|
199
|
+
- test/test_git.rb
|
200
|
+
- test/test_github.rb
|
201
|
+
homepage: http://github.com/mroth/pullcrusher
|
202
|
+
licenses: []
|
203
|
+
post_install_message:
|
204
|
+
rdoc_options: []
|
205
|
+
require_paths:
|
206
|
+
- lib
|
207
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
208
|
+
none: false
|
209
|
+
requirements:
|
210
|
+
- - ! '>='
|
211
|
+
- !ruby/object:Gem::Version
|
212
|
+
version: '0'
|
213
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
214
|
+
none: false
|
215
|
+
requirements:
|
216
|
+
- - ! '>='
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '0'
|
219
|
+
requirements: []
|
220
|
+
rubyforge_project:
|
221
|
+
rubygems_version: 1.8.23
|
222
|
+
signing_key:
|
223
|
+
specification_version: 3
|
224
|
+
summary: Optimize all images in a GitHub repository, then easily send a pull request
|
225
|
+
with the changes.
|
226
|
+
test_files:
|
227
|
+
- features/pullcrusher.feature
|
228
|
+
- features/step_definitions/pullcrusher_steps.rb
|
229
|
+
- features/support/env.rb
|
230
|
+
- test/test_git.rb
|
231
|
+
- test/test_github.rb
|