greenhouse 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/greenhouse +9 -0
- data/lib/greenhouse/cli.rb +88 -0
- data/lib/greenhouse/commands/add.rb +32 -0
- data/lib/greenhouse/commands/command.rb +88 -0
- data/lib/greenhouse/commands/configure.rb +9 -0
- data/lib/greenhouse/commands/help.rb +45 -0
- data/lib/greenhouse/commands/init.rb +93 -0
- data/lib/greenhouse/commands/launch.rb +113 -0
- data/lib/greenhouse/commands/new.rb +42 -0
- data/lib/greenhouse/commands/pull.rb +45 -0
- data/lib/greenhouse/commands/purge.rb +91 -0
- data/lib/greenhouse/commands/push.rb +45 -0
- data/lib/greenhouse/commands/remove.rb +32 -0
- data/lib/greenhouse/commands/start.rb +21 -0
- data/lib/greenhouse/commands/status.rb +50 -0
- data/lib/greenhouse/commands/sync.rb +42 -0
- data/lib/greenhouse/commands.rb +32 -0
- data/lib/greenhouse/projects/application.rb +19 -0
- data/lib/greenhouse/projects/collection.rb +23 -0
- data/lib/greenhouse/projects/engine.rb +6 -0
- data/lib/greenhouse/projects/gem.rb +6 -0
- data/lib/greenhouse/projects/project.rb +77 -0
- data/lib/greenhouse/projects/repository.rb +197 -0
- data/lib/greenhouse/projects.rb +86 -0
- data/lib/greenhouse/resources/dotenv_file.rb +69 -0
- data/lib/greenhouse/resources/file_resource.rb +50 -0
- data/lib/greenhouse/resources/ignore_file.rb +115 -0
- data/lib/greenhouse/resources/procfile.rb +144 -0
- data/lib/greenhouse/resources/projects_file.rb +44 -0
- data/lib/greenhouse/resources.rb +10 -0
- data/lib/greenhouse/scripts/argument.rb +36 -0
- data/lib/greenhouse/scripts/arguments.rb +28 -0
- data/lib/greenhouse/scripts/invalid_argument.rb +6 -0
- data/lib/greenhouse/scripts/script.rb +109 -0
- data/lib/greenhouse/scripts.rb +4 -0
- data/lib/greenhouse/tasks/add_project.rb +57 -0
- data/lib/greenhouse/tasks/generate_procfile.rb +22 -0
- data/lib/greenhouse/tasks/project_status.rb +37 -0
- data/lib/greenhouse/tasks/project_task.rb +363 -0
- data/lib/greenhouse/tasks/pull_project.rb +14 -0
- data/lib/greenhouse/tasks/purge_project.rb +13 -0
- data/lib/greenhouse/tasks/push_project.rb +14 -0
- data/lib/greenhouse/tasks/remove_greenhouse_files.rb +58 -0
- data/lib/greenhouse/tasks/remove_project.rb +15 -0
- data/lib/greenhouse/tasks/sync_project.rb +19 -0
- data/lib/greenhouse/tasks/task.rb +25 -0
- data/lib/greenhouse/tasks.rb +20 -0
- data/lib/greenhouse/version.rb +20 -0
- data/lib/greenhouse.rb +12 -0
- metadata +165 -0
@@ -0,0 +1,45 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
class Push
|
4
|
+
include Command
|
5
|
+
command_summary "Push local branches for all projects to their git remotes"
|
6
|
+
project_arguments *Projects::projects.map(&:to_arg)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def usage
|
10
|
+
puts <<USAGE
|
11
|
+
usage: #{::Greenhouse::CLI.command_name} #{command_name} [<project>] #{valid_arguments.to_s}
|
12
|
+
|
13
|
+
Projects:
|
14
|
+
#{project_arguments.to_help}
|
15
|
+
USAGE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def push_all
|
20
|
+
if Projects.projects.empty?
|
21
|
+
puts "No projects defined."
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
Projects.projects.each do |project|
|
26
|
+
Tasks::PushProject.perform(project)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def push_project(project)
|
31
|
+
unless project.exists?
|
32
|
+
puts "Project \e[36m#{project.title}\e[0m does not exist. Try initializing it with `greenhouse init`"
|
33
|
+
return
|
34
|
+
end
|
35
|
+
|
36
|
+
Tasks::PushProject.perform(project)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
project = Projects::projects.select { |project| arguments.map(&:key).include?(project.name) }.first
|
41
|
+
project.nil? ? push_all : push_project(project)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
class Remove
|
4
|
+
include Command
|
5
|
+
command_summary "Purge a project and remove it from the ecosystem .projects file"
|
6
|
+
project_arguments *Projects::projects.map(&:to_arg)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def usage
|
10
|
+
puts <<USAGE
|
11
|
+
usage: #{::Greenhouse::CLI.command_name} #{command_name} <project>
|
12
|
+
|
13
|
+
Projects:
|
14
|
+
#{project_arguments.to_help}
|
15
|
+
USAGE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
if arguments.empty?
|
21
|
+
puts "You must provide the name of the project you want to remove from your ecosystem."
|
22
|
+
usage
|
23
|
+
return
|
24
|
+
end
|
25
|
+
|
26
|
+
project = Projects::projects.select { |proj| proj.name == arguments[0].key }.first
|
27
|
+
Tasks::PurgeProject.perform(project)
|
28
|
+
Tasks::RemoveProject.perform(project)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
class Start
|
4
|
+
include Command
|
5
|
+
|
6
|
+
command_summary "Startup the entire ecosystem of apps using your Procfile (aliases `foreman start`)"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def usage
|
10
|
+
puts "usage: #{::Greenhouse::CLI.command_name} #{command_name} #{valid_arguments.to_s}"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
Dir.chdir(Projects::path) do
|
16
|
+
exec 'foreman start'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
class Status
|
4
|
+
include Command
|
5
|
+
command_summary "List projects and their current status"
|
6
|
+
valid_arguments Scripts::Argument.new("-v, --verbose", :summary => "Print out detailed information about project status (local changes, ahead/behind/diverged branches, etc.)")
|
7
|
+
project_arguments *Projects::projects.map(&:to_arg)
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def usage
|
11
|
+
puts <<USAGE
|
12
|
+
usage: #{::Greenhouse::CLI.command_name} #{command_name} [<project>] #{valid_arguments.to_s}
|
13
|
+
|
14
|
+
Arguments:
|
15
|
+
#{valid_arguments.to_help}
|
16
|
+
|
17
|
+
Projects:
|
18
|
+
#{project_arguments.to_help}
|
19
|
+
USAGE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def verbose
|
24
|
+
arguments.map(&:key).include?("-v")
|
25
|
+
end
|
26
|
+
|
27
|
+
def ecosystem_status
|
28
|
+
if Projects::projects.empty?
|
29
|
+
puts "No projects configured."
|
30
|
+
return
|
31
|
+
end
|
32
|
+
|
33
|
+
puts "The following projects are configured in your ecosystem: "
|
34
|
+
Projects::projects.each do |project|
|
35
|
+
puts
|
36
|
+
Tasks::ProjectStatus.perform(project, verbose)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def project_status(project)
|
41
|
+
Tasks::ProjectStatus.perform(project, verbose)
|
42
|
+
end
|
43
|
+
|
44
|
+
def run
|
45
|
+
project = Projects::projects.select { |project| arguments.map(&:key).include?(project.name) }.first
|
46
|
+
project.nil? ? ecosystem_status : project_status(project)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
class Sync
|
4
|
+
include Command
|
5
|
+
command_summary "Sync all projects with their git remotes"
|
6
|
+
project_arguments *Projects::projects.map(&:to_arg)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def usage
|
10
|
+
puts <<USAGE
|
11
|
+
usage: #{::Greenhouse::CLI.command_name} #{command_name} [<project>] #{valid_arguments.to_s}
|
12
|
+
|
13
|
+
Projects:
|
14
|
+
#{project_arguments.to_help}
|
15
|
+
USAGE
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def sync_all
|
20
|
+
if Projects.projects.empty?
|
21
|
+
puts "No projects defined."
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
25
|
+
Projects.projects.each do |project|
|
26
|
+
Tasks::SyncProject.perform(project)
|
27
|
+
end
|
28
|
+
Tasks::GenerateProcfile.perform
|
29
|
+
end
|
30
|
+
|
31
|
+
def sync_project(project)
|
32
|
+
Tasks::SyncProject.perform(project)
|
33
|
+
Tasks::GenerateProcfile.perform if project.type == 'application'
|
34
|
+
end
|
35
|
+
|
36
|
+
def run
|
37
|
+
project = Projects::projects.select { |project| arguments.map(&:key).include?(project.name) }.first
|
38
|
+
project.nil? ? sync_all : sync_project(project)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Commands
|
3
|
+
def self.commands
|
4
|
+
@commands ||= []
|
5
|
+
@commands
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.exists?(cmd)
|
9
|
+
commands.map(&:command_name).include?(cmd.underscore.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.command(cmd)
|
13
|
+
raise "Command does not exist: #{cmd}" unless exists?(cmd)
|
14
|
+
commands.select { |command| command.command_name == cmd.underscore.to_s }.first
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
require 'greenhouse/commands/command'
|
20
|
+
require 'greenhouse/commands/new'
|
21
|
+
require 'greenhouse/commands/init'
|
22
|
+
require 'greenhouse/commands/configure'
|
23
|
+
require 'greenhouse/commands/add'
|
24
|
+
require 'greenhouse/commands/status'
|
25
|
+
require 'greenhouse/commands/launch'
|
26
|
+
require 'greenhouse/commands/start'
|
27
|
+
require 'greenhouse/commands/push'
|
28
|
+
require 'greenhouse/commands/pull'
|
29
|
+
require 'greenhouse/commands/sync'
|
30
|
+
require 'greenhouse/commands/purge'
|
31
|
+
require 'greenhouse/commands/remove'
|
32
|
+
require 'greenhouse/commands/help'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Projects
|
3
|
+
class Application < Project
|
4
|
+
attr_reader :dotenv, :procfile
|
5
|
+
|
6
|
+
def configured?
|
7
|
+
@dotenv.exists?
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def initialize(name, args={})
|
13
|
+
super
|
14
|
+
@procfile = Resources::Procfile.new("#{path}/Procfile")
|
15
|
+
@dotenv = Resources::DotenvFile.new("#{path}/.env")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Projects
|
3
|
+
class Collection < Array
|
4
|
+
protected
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
|
9
|
+
# DEPRECATED - moving this to Projects itself
|
10
|
+
each do |project|
|
11
|
+
meth = project.class.name.pluralize.underscore.split("/").last.to_sym
|
12
|
+
next if respond_to?(meth)
|
13
|
+
|
14
|
+
self.class.instance_eval do
|
15
|
+
define_method meth do
|
16
|
+
select { |proj| proj.class.name.pluralize.underscore.split("/").last.to_sym == meth }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Projects
|
3
|
+
class Project
|
4
|
+
attr_accessor :name, :repository, :title
|
5
|
+
|
6
|
+
class << self
|
7
|
+
attr_reader :subclasses
|
8
|
+
|
9
|
+
# Keep track of inheriting classes (to use as project "types")
|
10
|
+
def inherited(subclass)
|
11
|
+
(@subclasses ||= [self]) << subclass
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(name, args={})
|
17
|
+
@name = name
|
18
|
+
@title = args.delete('title') || name.camelize
|
19
|
+
@ignored = (args.has_key?('ignore') ? [args.delete('ignore')] : []).flatten
|
20
|
+
@repository = Repository.new(name, args)
|
21
|
+
@ignore_file = Resources::IgnoreFile.new("#{path}/.ignore")
|
22
|
+
end
|
23
|
+
|
24
|
+
def ignored
|
25
|
+
Projects.ignored.concat(@ignore_file.ignored).concat(@ignored)
|
26
|
+
end
|
27
|
+
|
28
|
+
def chdir(&block)
|
29
|
+
Dir.chdir(path, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the local path to the project repo
|
33
|
+
def path
|
34
|
+
@repository.path
|
35
|
+
end
|
36
|
+
|
37
|
+
def gemfile
|
38
|
+
return nil unless gemfile?
|
39
|
+
"#{path}/Gemfile"
|
40
|
+
end
|
41
|
+
|
42
|
+
def gemfile?
|
43
|
+
chdir { return File.exists?("Gemfile") }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check if the repository exists
|
47
|
+
def exists?
|
48
|
+
@repository.exists?
|
49
|
+
end
|
50
|
+
|
51
|
+
def type
|
52
|
+
self.class.name.underscore.split('/').last
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_arg
|
56
|
+
Scripts::Argument.new(name, :summary => "#{title} (#{type.capitalize})")
|
57
|
+
end
|
58
|
+
|
59
|
+
# Go into the local directory and run Bundler
|
60
|
+
def bundle(cmd='install')
|
61
|
+
raise "Directory does not exist: #{path}" unless exists?
|
62
|
+
Dir.chdir(path) do
|
63
|
+
Bundler.with_clean_env do
|
64
|
+
# TODO look into using Bundler to install instead of executing system cmd
|
65
|
+
Greenhouse::CLI::exec "bundle #{cmd.to_s} 2>&1"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Remove the project directory
|
71
|
+
def destroy
|
72
|
+
@repository.destroy # use the repository object to destroy itself/directory
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
module Greenhouse
|
2
|
+
module Projects
|
3
|
+
class Repository
|
4
|
+
attr_accessor :local, :remote
|
5
|
+
alias_method :path, :local
|
6
|
+
|
7
|
+
def method_missing(meth, *args)
|
8
|
+
return git.send(meth, *args) if git.respond_to?(meth)
|
9
|
+
super
|
10
|
+
end
|
11
|
+
|
12
|
+
# Clone the remote into the local path
|
13
|
+
def clone
|
14
|
+
raise "Repository already exists: #{@local}" if cloned?
|
15
|
+
Git.clone(@remote, @local.split("/").last)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check if the remote has been cloned locally
|
19
|
+
def cloned?
|
20
|
+
File.exists?(@local) && @git ||= Git.open(@local)
|
21
|
+
end
|
22
|
+
alias_method :exists?, :cloned?
|
23
|
+
|
24
|
+
# Check whether there are any uncommited local changes
|
25
|
+
def changes?(untracked=true)
|
26
|
+
raise "Repository does not exist: #{@local}" unless cloned?
|
27
|
+
!changes(untracked).empty?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get a list of all local changes (modified, added, deleted & untracked)
|
31
|
+
def changes(include_untracked=true)
|
32
|
+
raise "Repository does not exist: #{@local}" unless cloned?
|
33
|
+
changes = changed.merge(git.status.added).merge(git.status.deleted)
|
34
|
+
changes.merge!(untracked) if include_untracked
|
35
|
+
changes
|
36
|
+
end
|
37
|
+
|
38
|
+
def changed?
|
39
|
+
!changed.empty?
|
40
|
+
end
|
41
|
+
|
42
|
+
def changed
|
43
|
+
git.status.changed.select { |name,file| !git.diff('HEAD', '--').path(name).to_s.empty? }
|
44
|
+
end
|
45
|
+
|
46
|
+
def untracked
|
47
|
+
git.status.untracked.select { |name,file| !name.match(/\Atmp\/.*\Z/) } # temporary hack to avoid untracked tmp files, since they're not being properly ignored(?)
|
48
|
+
end
|
49
|
+
|
50
|
+
def untracked?
|
51
|
+
!untracked.empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def staged
|
55
|
+
changed.merge(git.status.added).merge(git.status.deleted).delete_if { |name,file| file.sha_index.empty? || file.sha_index == '0000000000000000000000000000000000000000' }
|
56
|
+
end
|
57
|
+
|
58
|
+
def staged?
|
59
|
+
!staged.empty?
|
60
|
+
end
|
61
|
+
|
62
|
+
def unstaged
|
63
|
+
changed.merge(untracked).select { |name,file| file.sha_index.empty? || file.sha_index == '0000000000000000000000000000000000000000' }
|
64
|
+
end
|
65
|
+
|
66
|
+
def unstaged?
|
67
|
+
!unstaged.empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return the results of `ls-files --others` to list ignored/untracked files
|
71
|
+
def others
|
72
|
+
git.chdir do
|
73
|
+
return `git ls-files --others`.split("\n")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Check whether local branches are synced with the remotes
|
78
|
+
def synced?
|
79
|
+
unsynced.empty?
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return any unsynced branches for all remotes
|
83
|
+
def unsynced
|
84
|
+
branches = []
|
85
|
+
git.branches.local.each do |branch|
|
86
|
+
git.remotes.each do |remote|
|
87
|
+
lcommit = git.object(branch.name).log.first
|
88
|
+
rcommit = git.object("#{remote.name}/#{branch.name}").log.first
|
89
|
+
next if lcommit.sha == rcommit.sha
|
90
|
+
branches << [branch, remote]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
branches
|
94
|
+
end
|
95
|
+
|
96
|
+
# Check whether there are unpushed local changes on all branches/remotes
|
97
|
+
def ahead?
|
98
|
+
!ahead.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
# Return any unpushed local changes on all branches/remotes
|
102
|
+
def ahead
|
103
|
+
unsynced.select do |branch|
|
104
|
+
lbranch = git.object(branch[0].name)
|
105
|
+
rbranch = git.object("#{branch[1].name}/#{branch[0].name}")
|
106
|
+
lcommit = lbranch.log.first
|
107
|
+
rcommit = rbranch.log.first
|
108
|
+
|
109
|
+
!rbranch.log.map(&:sha).include?(lcommit.sha) && lbranch.log.map(&:sha).include?(rcommit.sha)
|
110
|
+
|
111
|
+
#lcommit.date <= rcommit.date
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Check if there are any unpulled changes on all branches/remotes
|
116
|
+
def behind?
|
117
|
+
!behind.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
# Return any unpulled changes on all branches/remotes
|
121
|
+
def behind
|
122
|
+
unsynced.select do |branch|
|
123
|
+
lbranch = git.object(branch[0].name)
|
124
|
+
rbranch = git.object("#{branch[1].name}/#{branch[0].name}")
|
125
|
+
lcommit = lbranch.log.first
|
126
|
+
rcommit = rbranch.log.first
|
127
|
+
|
128
|
+
rbranch.log.map(&:sha).include?(lcommit.sha) && !lbranch.log.map(&:sha).include?(rcommit.sha)
|
129
|
+
|
130
|
+
#lcommit.date >= rcommit.date
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def diverged?
|
135
|
+
!diverged.empty?
|
136
|
+
end
|
137
|
+
|
138
|
+
def diverged
|
139
|
+
unsynced.select do |branch|
|
140
|
+
lbranch = git.object(branch[0].name)
|
141
|
+
rbranch = git.object("#{branch[1].name}/#{branch[0].name}")
|
142
|
+
lcommit = lbranch.log.first
|
143
|
+
rcommit = rbranch.log.first
|
144
|
+
|
145
|
+
!rbranch.log.map(&:sha).include?(lcommit.sha) && !lbranch.log.map(&:sha).include?(rcommit.sha)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def up_to_date?
|
150
|
+
!out_of_sync?
|
151
|
+
end
|
152
|
+
|
153
|
+
def out_of_sync?
|
154
|
+
behind? || diverged?
|
155
|
+
end
|
156
|
+
|
157
|
+
def out_of_sync
|
158
|
+
unsynced.select do |branch|
|
159
|
+
lbranch = git.object(branch[0].name)
|
160
|
+
rbranch = git.object("#{branch[1].name}/#{branch[0].name}")
|
161
|
+
lcommit = lbranch.log.first
|
162
|
+
rcommit = rbranch.log.first
|
163
|
+
|
164
|
+
(rbranch.log.map(&:sha).include?(lcommit.sha) && !lbranch.log.map(&:sha).include?(rcommit.sha)) ||
|
165
|
+
(!rbranch.log.map(&:sha).include?(lcommit.sha) && !lbranch.log.map(&:sha).include?(rcommit.sha))
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def stash
|
170
|
+
git.chdir { `git stash 2>&1` }
|
171
|
+
end
|
172
|
+
|
173
|
+
def pop_stash
|
174
|
+
git.chdir { `git stash pop 2>&1` }
|
175
|
+
end
|
176
|
+
|
177
|
+
# Remove the local repository
|
178
|
+
def destroy
|
179
|
+
FileUtils.rm_rf @local
|
180
|
+
end
|
181
|
+
|
182
|
+
def git
|
183
|
+
@git ||= Git.open(@local)
|
184
|
+
@git
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
def initialize(name, args={})
|
190
|
+
raise "A git remote is required." unless args.has_key?(:remote)
|
191
|
+
@local = File.expand_path(args[:local] || name)
|
192
|
+
@remote = args[:remote]# || "git@github.com:Graphicly/#{name}.git"
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'git'
|
3
|
+
require 'greenhouse/projects/repository'
|
4
|
+
require 'greenhouse/projects/collection'
|
5
|
+
require 'greenhouse/projects/project'
|
6
|
+
require 'greenhouse/projects/engine'
|
7
|
+
require 'greenhouse/projects/application'
|
8
|
+
require 'greenhouse/projects/gem'
|
9
|
+
|
10
|
+
module Greenhouse
|
11
|
+
module Projects
|
12
|
+
@@path = nil
|
13
|
+
@@procfile = nil
|
14
|
+
@@ignore_file = nil
|
15
|
+
@@projects_file = nil
|
16
|
+
@@dotenv = nil
|
17
|
+
@@projects = nil
|
18
|
+
|
19
|
+
def self.method_missing(meth, *args)
|
20
|
+
if Project.subclasses.map { |subclass| subclass.name.pluralize.underscore.split("/").last.to_sym }.include?(meth.to_sym)
|
21
|
+
projects.select { |proj| proj.class.name.pluralize.underscore.split("/").last.to_sym == meth.to_sym }
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.projects
|
28
|
+
@@projects = Collection.new
|
29
|
+
return @@projects unless projects_file.exists?
|
30
|
+
|
31
|
+
projects_file.projects.each do |name,project|
|
32
|
+
type = (project.has_key?('type') ? project['type'] : 'project')
|
33
|
+
projargs = project.merge({:remote => (project['remote'] || project['git'])})
|
34
|
+
classname = "Greenhouse::Projects::#{type.singularize.camelize}"
|
35
|
+
@@projects << (defined?(classname.constantize) ? classname.constantize.new(name, projargs) : Greenhouse::Projects::Project.new(name, projargs))
|
36
|
+
end
|
37
|
+
@@projects
|
38
|
+
end
|
39
|
+
|
40
|
+
# Attempts to look for and returns the path of the root projects directory
|
41
|
+
#
|
42
|
+
# Looks up the tree from the current directory, currently checking for a .projects
|
43
|
+
# file (this might change in the future).
|
44
|
+
#
|
45
|
+
# If no projects path is found, the current directory is returned.
|
46
|
+
def self.path
|
47
|
+
return @@path unless @@path.nil?
|
48
|
+
dir = Dir.pwd
|
49
|
+
while dir != "" do
|
50
|
+
if File.exists?("#{dir}/.projects")
|
51
|
+
@@path = dir
|
52
|
+
return @@path
|
53
|
+
end
|
54
|
+
dir = dir.gsub(/\/[^\/]*\Z/,'')
|
55
|
+
end
|
56
|
+
@@path = Dir.pwd # if we haven't found a .projects file, this must be where they want to work
|
57
|
+
@@path
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.ignore_file
|
61
|
+
return @@ignore_file unless @@ignore_file.nil?
|
62
|
+
@@ignore_file = Resources::IgnoreFile.new("#{path}/.ignore")
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.projects_file
|
66
|
+
return @@projects_file unless @@projects_file.nil?
|
67
|
+
@@projects_file = Resources::ProjectsFile.new("#{path}/.projects")
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.procfile
|
71
|
+
return @@procfile unless @@procfile.nil?
|
72
|
+
@@procfile = Resources::Procfile.new("#{path}/Procfile")
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.dotenv
|
76
|
+
return @@dotenv unless @@dotenv.nil?
|
77
|
+
@@dotenv = Resources::DotenvFile.new("#{path}/.env")
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.ignored
|
81
|
+
return [] unless ignore_file.exists?
|
82
|
+
ignore_file.ignored
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|