pushapp 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,127 @@
1
+ require 'erb'
2
+
3
+ module Pushapp
4
+ class Hook
5
+
6
+ ANSI_COLORS = {
7
+ :reset => 0,
8
+ :black => 30,
9
+ :red => 31,
10
+ :green => 32,
11
+ :yellow => 33,
12
+ :blue => 34,
13
+ :magenta => 35,
14
+ :cyan => 36,
15
+ :white => 37,
16
+ :bright_black => 30,
17
+ :bright_red => 31,
18
+ :bright_green => 32,
19
+ :bright_yellow => 33,
20
+ :bright_blue => 34,
21
+ :bright_magenta => 35,
22
+ :bright_cyan => 36,
23
+ :bright_white => 37,
24
+ }
25
+
26
+ HOOK_COLORS = %w( cyan yellow green magenta blue bright_cyan bright_yellow
27
+ bright_magenta bright_blue red bright_green bright_red )
28
+
29
+ attr_accessor :remote
30
+
31
+ def initialize(remote)
32
+ @remote = remote
33
+ @config = remote.config
34
+ @options = remote.options
35
+
36
+ @remote_index = @config.remotes.index(@remote)
37
+ @remote_color = ANSI_COLORS[HOOK_COLORS[@remote_index % HOOK_COLORS.length].to_sym]
38
+ @echo_color = ANSI_COLORS[:bright_green]
39
+ @error_color = ANSI_COLORS[:bright_red]
40
+ end
41
+
42
+ def setup
43
+ prepare_hook
44
+ deploy_hook
45
+ end
46
+
47
+ private
48
+
49
+ def padded_max_length
50
+ if @remote.group
51
+ @config.remotes_grouped_by(@remote.group).map {|r| r.full_name}.max.length
52
+ else
53
+ @remote.name.length
54
+ end
55
+ end
56
+
57
+ def padded_name
58
+ @padden_name ||= @remote.full_name.ljust([padded_max_length, "remote:".length].max)
59
+ end
60
+
61
+ def prefix
62
+ "\e[1G\e[#{@remote_color}m#{padded_name} |\e[0m "
63
+ end
64
+
65
+ def pre
66
+ "#\{ENV[\"PAP_PRE\"]\}"
67
+ end
68
+
69
+ def colorize
70
+ %{2>&1 | ruby -pe '$_="#{pre}#\{$_\}"'}
71
+ end
72
+
73
+ def info message
74
+ %{ruby -e 'puts "#{pre}\e[#{@echo_color}m\e[1m[pushapp]\e[0m\e[#{@echo_color}m INFO: #{message}\e[0m"'}
75
+ end
76
+
77
+ def load_template(template_name)
78
+ ::ERB.new(File.read(find_template(template_name))).result(binding)
79
+ end
80
+
81
+ def find_template(template_name)
82
+ "#{Pushapp::TEMPLATE_ROOT}/#{template_name}.erb"
83
+ end
84
+
85
+ def prepare_hook
86
+ # info "Generating and uploading post-receive hook for #{@remote.name}"
87
+ hook = generate_hook
88
+ write hook
89
+ end
90
+
91
+ def deploy_hook
92
+ # debug "Copying hook for #{@remote.name} to #{@remote.location}"
93
+ copy_hook
94
+ set_hook_permissions
95
+ end
96
+
97
+ def generate_hook
98
+ load_template 'hook/base'
99
+ end
100
+
101
+ def write(hook)
102
+ File.open(Pushapp::TMP_HOOK, "wb") do |f|
103
+ f.puts hook
104
+ end
105
+ end
106
+
107
+ def set_hook_permissions
108
+ @remote.run "#{make_hook_executable}"
109
+ end
110
+
111
+ def copy_hook
112
+ # debug "Making hook executable"
113
+ # TODO: handle missing user?
114
+ if @remote.host
115
+ Pushapp::Pipe.run "scp #{Pushapp::TMP_HOOK} #{@remote.user}@#{@remote.host}:#{@remote.path}/.git/hooks/post-receive"
116
+ else
117
+ Pushapp::Pipe.run "cp #{Pushapp::TMP_HOOK} #{@remote.path}/.git/hooks/post-receive"
118
+ end
119
+ end
120
+
121
+ def make_hook_executable
122
+ # debug "Making hook executable"
123
+ "chmod +x #{@remote.path}/.git/hooks/post-receive"
124
+ end
125
+ end
126
+ end
127
+
@@ -0,0 +1,34 @@
1
+ require 'logger'
2
+ require 'thor'
3
+
4
+ module Pushapp
5
+ class Logger < ::Logger
6
+ attr_reader :shell
7
+
8
+ def initialize
9
+ super($stdout)
10
+ @shell = Thor::Shell::Color.new
11
+
12
+ self.progname = '[pushapp]'
13
+
14
+ # DEBUG INFO WARN ERROR FATAL UNKNOWN
15
+ @colors = {
16
+ 'DEBUG' => :blue,
17
+ 'INFO' => :green,
18
+ 'WARN' => :magenta,
19
+ 'ERROR' => :red,
20
+ 'FATAL' => :red,
21
+ 'UNKOWN' => :black
22
+ }
23
+
24
+ self.formatter = proc { |severity, datetime, progname, msg|
25
+ color = @colors[severity]
26
+
27
+ progname = @shell.set_color progname, color, :bold
28
+ sev = @shell.set_color "#{severity}:", color
29
+ msg = @shell.set_color msg, color
30
+ "#{progname} #{sev} #{msg}\n"
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ require 'open3'
2
+ require 'stringio'
3
+
4
+ module Pushapp
5
+ class Pipe
6
+
7
+ def self.run(command)
8
+ case command
9
+ when String
10
+ pipe(command)
11
+ when Pushapp::Tasks::Script
12
+ pipe(command.cmd)
13
+ when Pushapp::Tasks::Base
14
+ command.run
15
+ when Array
16
+ pipe(command)
17
+ else
18
+ raise "Unknown command format: '#{command.inspect}'"
19
+ end
20
+ end
21
+
22
+ def self.capture(cmd)
23
+ output, s = Open3.capture2e(cmd)
24
+ raise "Failed with status #{s.exitstatus}: #{cmd.inspect}" unless s.success?
25
+ output
26
+ end
27
+
28
+ private
29
+
30
+ def self.pipe cmd, stdin=$stdin, stdout=$stdout
31
+ s = Open3.pipeline(cmd, :in => stdin, :out => stdout).last
32
+ raise "Failed with status #{s.exitstatus}: #{cmd.inspect}" unless s.success?
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,141 @@
1
+ require 'pushapp/tasks/rake'
2
+ require 'shellwords'
3
+
4
+ class Pushapp::Remote
5
+
6
+ attr_reader :config
7
+ attr_reader :tasks
8
+ attr_reader :name
9
+ attr_reader :location
10
+ attr_reader :options
11
+ attr_reader :group
12
+
13
+ def initialize(name, group, location, config, options = {})
14
+ @name = name
15
+ @location = location
16
+ @config = config
17
+ @options = options
18
+ @group = group
19
+ @tasks = Hash.new { |hash, key| hash[key] = [] }
20
+ end
21
+
22
+ def full_name
23
+ [group, name].compact.join('-')
24
+ end
25
+
26
+ def rake(task_name, task_options={})
27
+ tasks[@event] << Pushapp::Tasks::Rake.new(task_name, merge_options(task_options))
28
+ end
29
+
30
+ def script(script_name, script_options={})
31
+ tasks[@event] << Pushapp::Tasks::Script.new(script_name, merge_options(script_options))
32
+ end
33
+
34
+ def task(task_name, task_options={})
35
+ tasks[@event] << config.known_task(task_name).new(merge_options(task_options).merge(task_name: task_name))
36
+ end
37
+
38
+ def on event, &block
39
+ @event = event.to_s
40
+ instance_eval(&block) if block_given?
41
+ end
42
+
43
+ def tasks_on event
44
+ tasks[event.to_s]
45
+ end
46
+
47
+ def path
48
+ if host
49
+ @location.match(/:(.*)$/)[1]
50
+ else
51
+ @location
52
+ end
53
+ end
54
+
55
+ def host
56
+ host = @location.match(/@(.*):/)
57
+ host[1] unless host.nil?
58
+ end
59
+
60
+ def user
61
+ user = @location.match(/(.*)@/)
62
+ user[1] unless user.nil?
63
+ end
64
+
65
+ def ssh!
66
+ exec "cd #{path} && #{shell_env} $SHELL -l"
67
+ end
68
+
69
+ #
70
+ # Set up Repositories and Hook
71
+ #
72
+ def setup!
73
+ run "#{init_repository} && #{setup_repository}"
74
+ end
75
+
76
+ #
77
+ # Update git remote and hook
78
+ #
79
+ def update!
80
+ Pushapp::Hook.new(self).setup
81
+ end
82
+
83
+ def exec cmd
84
+ if host
85
+ Kernel.exec "ssh -t #{user}@#{host} '#{cmd}'"
86
+ else
87
+ Kernel.exec cmd
88
+ end
89
+ end
90
+
91
+ def env_run cmd
92
+ if host
93
+ Pushapp::Pipe.run "ssh #{user}@#{host} 'cd #{path} && #{shell_env} $SHELL -l -c \"#{cmd}\"'"
94
+ else
95
+ Bundler.with_original_env do
96
+ Pushapp::Pipe.run "cd #{path} && #{shell_env} #{cmd}"
97
+ end
98
+ end
99
+ end
100
+
101
+ def run cmd
102
+ if host
103
+ Pushapp::Pipe.run "ssh #{user}@#{host} '#{cmd}'"
104
+ else
105
+ Bundler.with_original_env do
106
+ Pushapp::Pipe.run cmd
107
+ end
108
+ end
109
+ end
110
+
111
+ def env
112
+ (options[:env] || {})
113
+ end
114
+
115
+ private
116
+
117
+ #
118
+ # Initialize an empty repository
119
+ #
120
+ def init_repository
121
+ # Instead of git init with a path, so it does not fail on older
122
+ # git versions (https://github.com/effkay/blazing/issues/53)
123
+ "mkdir #{path}; cd #{path} && git init"
124
+ end
125
+
126
+ #
127
+ # Allow pushing to currently checked out branch
128
+ #
129
+ def setup_repository
130
+ "cd #{path} && git config receive.denyCurrentBranch ignore"
131
+ end
132
+
133
+ def shell_env
134
+ env.map {|k,v| "#{k}=\"#{Shellwords.escape(v)}\""}.join(" ")
135
+ end
136
+
137
+ def merge_options task_options={}
138
+ Pushapp.rmerge(task_options, options).merge(remote: name)
139
+ end
140
+
141
+ end
@@ -0,0 +1,27 @@
1
+ require 'pushapp/config'
2
+ require 'pushapp/logger'
3
+
4
+ module Pushapp
5
+ module Tasks
6
+ class Base
7
+ attr_reader :options
8
+ attr_reader :logger
9
+
10
+ def initialize options={}
11
+ @options = options
12
+ @logger = Pushapp::Logger.new
13
+ end
14
+
15
+ def run
16
+ end
17
+
18
+ def self.register_as name
19
+ Pushapp::Config.register_task name, self
20
+ end
21
+
22
+ def inspect
23
+ options[:task_name]
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ require 'pushapp/tasks/base'
2
+
3
+ module Pushapp
4
+ module Tasks
5
+ class ForemanExport < Base
6
+
7
+ def run
8
+ logger.info 'Forman exporting...'
9
+ end
10
+
11
+ register_as :foreman_export
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,15 @@
1
+ require 'pushapp/tasks/script'
2
+
3
+ module Pushapp
4
+ module Tasks
5
+ class Rake < Script
6
+ def cmd
7
+ [env, "rake #{script}"]
8
+ end
9
+
10
+ def inspect
11
+ "rake: #{script}"
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ require 'pushapp/tasks/base'
2
+
3
+ module Pushapp
4
+ module Tasks
5
+ class Script < Base
6
+ attr_reader :script
7
+
8
+ def initialize script, options={}
9
+ super(options)
10
+
11
+ @script = script
12
+ end
13
+
14
+ def cmd
15
+ [env, script]
16
+ end
17
+
18
+ def env
19
+ Hash[env_options.map {|k, v| [k.to_s, v.to_s] }]
20
+ end
21
+
22
+ def env_options
23
+ options[:env] || {}
24
+ end
25
+
26
+ def inspect
27
+ "script: #{script}"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Pushapp
2
+ VERSION = '0.0.1'
3
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pushapp/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'pushapp'
8
+ spec.version = Pushapp::VERSION
9
+ spec.authors = ["Yury Korolev"]
10
+ spec.email = ["yurykorolev@me.com"]
11
+ spec.description = %q{Push your App}
12
+ spec.summary = %q{Push your App}
13
+ spec.homepage = 'https://github.com/anjlab/pushapp'
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'thor'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'minitest'
26
+ end